開頭的碎碎念:
對接公眾平台的時候,開始有個字串排序,我接觸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) = NaNfunc Abs(x float64) float64func 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 mainimport( "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 asmfunc Asm(i int)int{ return i}
go tool 6g –S asm.go:
--- prog list "Asm" ---0000 (asm.go:3) TEXT Asm+0(SB),$0-160001 (asm.go:3) LOCALS ,$00002 (asm.go:3) TYPE i+0(FP){int},$80003 (asm.go:3) TYPE ~anon1+8(FP){int},$80004 (asm.go:4) MOVQ i+0(FP),BX0005 (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 asmfunc Asm(f float64)float64{ return f}
--- prog list "Asm" ---0000 (asm.go:3) TEXT Asm+0(SB),$0-160001 (asm.go:3) LOCALS ,$00002 (asm.go:3) TYPE i+0(FP){int},$80003 (asm.go:3) TYPE ~anon1+8(FP){float64},$80004 (asm.go:4) MOVQ i+0(FP),X00005 (asm.go:4) MOVQ X0,~anon1+8(FP)0006 (asm.go:4) RET ,
可以看出與前面用int去嘗試大致相同,只是BX寄存器變成了X0,可以推測X0就是浮點數寄存器,有X0,大膽推測會有X1、X2、X3…
試試吧
package asmfunc Asm(f1,f2 float64) float64{ return f1+f2}
--- prog list "Asm" ---0000 (asm.go:3) TEXT Asm+0(SB),$0-240001 (asm.go:3) LOCALS ,$00002 (asm.go:3) TYPE f1+0(FP){float64},$80003 (asm.go:3) TYPE f2+8(FP){float64},$80004 (asm.go:3) TYPE ~anon2+16(FP){float64},$80005 (asm.go:4) MOVSD f1+0(FP),X00006 (asm.go:4) MOVSD f2+8(FP),X10007 (asm.go:4) ADDSD X1,X00008 (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) float64TEXT ·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), BXMOVQ 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,由此完成取絕對值的任務。