開頭的碎碎念:
對接微信公眾平台的時候,開始有個字符串排序,我接觸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,由此完成取絕對值的任務。
