/src/pkg/math/abs.go源碼閱讀兼談golang與匯編


開頭的碎碎念:

對接微信公眾平台的時候,開始有個字符串排序,我接觸golang畢竟時間尚淺,很多東西都是能從網上找到就直接從網上找,結果就是找了好幾個示例代碼都不好用,好容易一個好用的,從頭開始實現的,代碼太多了。我就想,google應該把這些玩意都封裝好了吧,不然一個新出的語言只有基礎語法,沒有強大的標准庫,誰用這玩意啊。也就是那時候第一次接觸src文件夾,后來發現pkg里的那些go文件是絕好的學習資料。

那么多文件、文件夾從哪開始看呢,我的原則,先找沒有依賴性的,也就是沒有import的,這么尋摸着就找到了math文件夾。笨方法,從a開始按順序來唄,這不就碰到了abs.go

難以理解的第12行:

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package math

// Abs returns the absolute value of x.
//
// Special cases are:
//    Abs(±Inf) = +Inf
//    Abs(NaN) = NaN
func Abs(x float64) float64

func abs(x float64) float64 {
    switch {
    case x < 0:
        return -x
    case x == 0:
        return 0 // return correctly abs(-0)
    }
    return x
}

孤零零的一行,一個方法聲明,別的啥都沒有,完全不解其意。

下面那個abs()不用我說了吧,很簡單的,取絕對值。

不管了,先到math文件夾看看,abs_386.s、abs_amd64.s、abs_arm.s有這三個文件估計跟那行不知道哪來的代碼有關系,.s結尾的,匯編語言文件,繼續發動google的威力,golang、匯編混編,如此便找到了http://www.mikespook.com/2013/02/%E7%BF%BB%E8%AF%91go-%E5%92%8C%E6%B1%87%E7%BC%96/

讓程序跑起來:

先讓這段程序跑起來吧,因為我的機器是64位的,所以我把abs.go、abs_amd64.s兩個文件拷貝到別處,abs.go的包改成了mymath,另外寫了一個簡單的測試程序

package main

import(
    "fmt"
    "mymath"
)

func main(){
    fmt.Println(mymath.Abs(-12.00))
}

在/pkg/tool/windows_amd64下有很多有用的工具,6g、6l啥的,不過常用的都被go命令給封裝了,直接go build、go install等命令就解決了。

涉及到匯編的,主要是6a,上面的代碼按如下順序編譯:

6a abs_amd64.s(生成abs_amd64.6)

6g –o _go_.6 abs.go(生成_go_.6)

pack grc abs.a  _go_.6 abs_amd64.6(生成abs.a)

本來是想直接讓主程序調用目錄下的庫的,import “./mymath”,不過,windows下提示出錯import path contains invalid characte ‘:’:”c:/xxx/xxx”,所以只得將abs.a扔到pkg/windows_amd64文件夾中。

剩下的就是:

6g test.go(生成test.6)

6l test.6(生成6.out.exe)

從簡單的golang編譯器自動生成的匯編入手:

先看一個新的命令,golang編譯器自動生成匯編中間代碼的命令,go tool 6g –S XXX.go,其實上面的那些命令也都可以用go tool XXX來代替,go命令把那些命令都封裝進去了。

來個最簡單的代碼吧:

package asm

func Asm(i int)int{
    return i
}

go tool 6g –S asm.go:

--- prog list "Asm" ---
0000 (asm.go:3) TEXT    Asm+0(SB),$0-16
0001 (asm.go:3) LOCALS  ,$0
0002 (asm.go:3) TYPE    i+0(FP){int},$8
0003 (asm.go:3) TYPE    ~anon1+8(FP){int},$8
0004 (asm.go:4) MOVQ    i+0(FP),BX
0005 (asm.go:4) MOVQ    BX,~anon1+8(FP)
0006 (asm.go:4) RET     ,

plan9匯編,語法跟AT&T頗為類似,傳值是前面是源,后面是目的,這點跟masm、nasm啥的都是反的。

000行:TEXT相當於定義了一個函數,Asm函數名,+0(SB)golang生成的都有這玩意;$0-16,經過我的反復嘗試,起碼對於int、float64這兩者而言,是(參數個數+返回值個數)*8(這都是我自己驗證的,沒啥科學依據,相關文檔我也翻閱過一些,不過鳥語不過關,將把能看懂的東西里沒有我需要的,大膽假設,小心論證現在還做不到)。

001行:我估計是執行指令的位置,不過這都不重要,關鍵是后頭的。

002、003行:i是變量名,~anon1其實也是變量名(系統自動生成的)

稍微修改下

func Asm(i int) (j int){
    j=i
    return
}

則003行,就變成了j+8(FP)

至於0(FP)、8(FP),對於int來說,每個數字占8個字節(64位下),所以傳入的參數,第一個就是+0(FP),第二個+8(FP),第三個+16(FP),第四個+24(FP)…

返回值,如果有多個返回值,第一個+(8+最后一個傳入參數的數值)(FP),后面都是依次+8

{int}標明了數據類型,$8表明占據8個字節

004行:將參數值傳給寄存器BX,MOVQ,傳遞四字

005行:將BX中的值傳給返回值

006行:RET

看看float64又是啥樣的:

package asm

func Asm(f float64)float64{
    return f
}
--- prog list "Asm" ---
0000 (asm.go:3) TEXT    Asm+0(SB),$0-16
0001 (asm.go:3) LOCALS  ,$0
0002 (asm.go:3) TYPE    i+0(FP){int},$8
0003 (asm.go:3) TYPE    ~anon1+8(FP){float64},$8
0004 (asm.go:4) MOVQ    i+0(FP),X0
0005 (asm.go:4) MOVQ    X0,~anon1+8(FP)
0006 (asm.go:4) RET     ,

可以看出與前面用int去嘗試大致相同,只是BX寄存器變成了X0,可以推測X0就是浮點數寄存器,有X0,大膽推測會有X1、X2、X3…

試試吧

package asm

func Asm(f1,f2 float64) float64{
    return f1+f2
}
--- prog list "Asm" ---
0000 (asm.go:3) TEXT    Asm+0(SB),$0-24
0001 (asm.go:3) LOCALS  ,$0
0002 (asm.go:3) TYPE    f1+0(FP){float64},$8
0003 (asm.go:3) TYPE    f2+8(FP){float64},$8
0004 (asm.go:3) TYPE    ~anon2+16(FP){float64},$8
0005 (asm.go:4) MOVSD   f1+0(FP),X0
0006 (asm.go:4) MOVSD   f2+8(FP),X1
0007 (asm.go:4) ADDSD   X1,X0
0008 (asm.go:4) MOVSD   X0,~anon2+16(FP)
0009 (asm.go:4) RET     ,

abs_amd64.s:

// Copyright 2010 The Go Authors.  All rights reserved.

// Use of this source code is governed by a BSD-style

// license that can be found in the LICENSE file.


// func Abs(x float64) float64

TEXT ·Abs(SB),7,$0
    
    MOVQ   $(1<<63), BX
    
    MOVQ   BX, X0 // movsd $(-0.0), x0
    
    MOVSD  x+0(FP), X1

    ANDNPD X1, X0

    MOVSD  X0, ret+8(FP)
    
RET

折騰一番終於到這了。

第一行就當固定格式吧,函數名替換下就好。

MOVQ   $(1<<63), BX
MOVQ   BX, X0

1右移動63位,傳給X0,此時X0二進制表示是1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

MOVSD  x+0(FP), X1

MOVSD移動標量雙精度浮點值,將參數x的值傳給X1

ANDNPD X1, X0

ANDNPD壓縮雙精度浮點值邏輯位與非,將目標操作數的取反,再與源操作數執行邏輯位“與”操作,結果存儲到目標操作數

即對X0取反,再與X1相與,最后結果存儲到X0中

以上操作所完成的也就是將符號位置0,由此完成取絕對值的任務。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM