Go/Goのプログラムがどんなアセンブリにコンパイルされるか?
< Go
Goのプログラムがどんなアセンブリにコンパイルされるか?
編集Goは簡素化された構文とキャッシュの使用により、非常に高速にコンパイルされますが、(中間コードではなく)ネイティブな機械語を生成します。
では、実際にどのようなコードが生成されるかを検証してみましょう。
Hello, World を逆アセンブル
編集- hello.go
package main import "fmt" func main() { fmt.Println("Hello, World") }
上のような単純で完全なプログラム hello.go を用意します。
% go tool compile -o hello.o hello.go
hello.o
にコンパイル結果が出力されますが、これは。% file fibo.o fibo.o: current ar archive
ar(1) のアーカイブです。
% ar tv hello.o rw-r--r-- 0/0 92 Jan 1 09:00 1970 __.PKGDEF rw-r--r-- 0/0 6447 Jan 1 09:00 1970 _go_.o
展開してみても
% ar xv hello.o x - __.PKGDEF x - _go_.o % file __.PKGDEF _go_.o __.PKGDEF: data _go_.o: data
- と、素性はわかりませんが
- go tool objdump という Go の逆アセンブラーは、この形式を理解できます(-S はソースも併せて表示するオプションです)。
% go tool objdump -S hello.o > hello.objdump
- hello.objdump
func main() { 0x14f1 493b6610 CMPQ 0x10(R14), SP [2:2]R_USEIFACE:type.string [2:2]R_USEIFACE:type.*os.File 0x14f5 7656 JBE 0x154d 0x14f7 4883ec40 SUBQ $0x40, SP 0x14fb 48896c2438 MOVQ BP, 0x38(SP) 0x1500 488d6c2438 LEAQ 0x38(SP), BP fmt.Println("Hello, World") 0x1505 440f117c2428 MOVUPS X15, 0x28(SP) 0x150b 488d1500000000 LEAQ 0(IP), DX [3:7]R_PCREL:type.string 0x1512 4889542428 MOVQ DX, 0x28(SP) 0x1517 488d1500000000 LEAQ 0(IP), DX [3:7]R_PCREL:main..stmp_0<1> 0x151e 4889542430 MOVQ DX, 0x30(SP) return Fprintln(os.Stdout, a...) 0x1523 488b1d00000000 MOVQ 0(IP), BX [3:7]R_PCREL:os.Stdout 0x152a 488d0500000000 LEAQ 0(IP), AX [3:7]R_PCREL:go.itab.*os.File,io.Writer 0x1531 488d4c2428 LEAQ 0x28(SP), CX 0x1536 bf01000000 MOVL $0x1, DI 0x153b 4889fe MOVQ DI, SI 0x153e e800000000 CALL 0x1543 [1:5]R_CALL:fmt.Fprintln } 0x1543 488b6c2438 MOVQ 0x38(SP), BP 0x1548 4883c440 ADDQ $0x40, SP 0x154c c3 RET func main() { 0x154d 0f1f4000 NOPL 0(AX) 0x1551 e800000000 CALL 0x1556 [1:5]R_CALL:runtime.morestack_noctxt 0x1556 eb99 JMP main.main(SB)
- ターゲットは AMD64 なのですが、あまり見慣れないニーモニックだと思います。
- これは、Plan9 のアセンブラフォーマットでIntelともAT&Tとも違います。
- レジスタは8086の頃にあったものならば8086当時の名前で、AMD64で追加されたレジスタはRnnの形式で表示されます。
- 演算や転送の幅は SUBQ や MOVL のようにオペレーションの末尾の文字で指定されます。
よく見ると、ソースの fmt.Println("Hello, World") が、fmt.Fprintln(os.Stdout,) に置き換えられておりインライン展開が行われていることがわかります。
Goはコンパイラーですが、コンパイラー自体がGoで書かれ、ソースコードとともに配布されており、他にも
go tool nm
や
go tool pack
の様なハウスキーピング用のコマンドがあります(機会を見て紹介します)。
脚註
編集
参考文献
編集- “A Quick Guide to Go's Assembler”. 2021年10月22日閲覧。