🐰

Go言語のfor文が動く仕組みをアセンブリレベルで見てみよう

2021/06/09に公開

for文がCPU上でどのように実行されているか、想像したことはあるでしょうか?
実際にコンパイルさせてみて出力結果を読んでみましょう。

なんの変哲もないfor文

package main

func main() {
	for i := 1; i <= 10; i++ {
		print(i)
	}
}

コンパイルしてアセンブリに変換

上のコードをアセンブリに変換してみましょう。
出力結果が読みやすくなるように、最適化とインライン化をoffにします。(-N -l オプション)

$ go tool compile -S -N -l min.go

出力されたアセンブリコード ( //のコメントは筆者 )

00029 (min.go:6) MOVQ    $1, "".i+8(SP)        // i = 1
00038 (min.go:6) JMP     40                    // goto 40番地
00040 (min.go:6) CMPQ    "".i+8(SP), $10       // compare i <=> 10
00046 (min.go:6) JLE     50                    // goto 50番地 if less than or equal to
00048 (min.go:6) JMP     91                    // goto 91番地(for loopの出口へ)
00050 (min.go:7) PCDATA  $1, $0
00050 (min.go:7) CALL    runtime.printlock(SB)
00055 (min.go:7) MOVQ    "".i+8(SP), AX        // $AX = i
00060 (min.go:7) MOVQ    AX, (SP)              // args[0] = $AX
00064 (min.go:7) CALL    runtime.printint(SB)  // printint()
00069 (min.go:7) CALL    runtime.printunlock(SB)
00074 (min.go:7) JMP     76                    // goto 76番地
00076 (min.go:6) MOVQ    "".i+8(SP), AX        // $AX = i
00081 (min.go:6) INCQ    AX                    // $AX++
00084 (min.go:6) MOVQ    AX, "".i+8(SP)        // i = $AX
00089 (min.go:6) JMP     40                    // goto 40番地

解説

Go言語でも goto文を使えば 上のアセンブリに近いコードを書くことができます。

package main

func main() {
	{
		var i int
		i = 1
		goto CONDITON

	CONDITON:
		is_le := (i <= 10)
		if is_le {
			goto BLOCK_START
		}
		goto EXIT_LOOP

	BLOCK_START:
		println(i)
		goto POST_STMT

	POST_STMT:
		i++
		goto CONDITON

	EXIT_LOOP:
	}
}

実行する → https://play.golang.org/p/SSPxmu-pfg1

実際に Goコンパイラを作るときも、このような構造を経てからアセンブリに変換すると for文をうまくコンパイルすることができます。

Discussion