
本文基于 Go 1.13 版本
循环在编程中是一个重要的概念,且易于上手。但是,循环必须被翻译成计算机能理解的底层指令。它的编译方式也会在一定程度上影响到标准库中的其他组件。让我们开始分析循环吧。
循环的汇编代码
使用循坏迭代 array , slice , channel ,以下是一个使用循环对 slice 计算总和的例子。
func main() {
l := []int{9, 45, 23, 67, 78}
t := 0
for _, v := range l {
t += v
}
println(t)
}
使用 go tool compile -S main.go 生成的汇编代码,以下为相关输出:
0x0041 00065 (main.go:4) XORL AX, AX
0x0043 00067 (main.go:4) XORL CX, CX0x0045 00069 (main.go:7) JMP 82
0x0047 00071 (main.go:7) MOVQ ""..autotmp_5+16(SP)(AX*8), DX
0x004c 00076 (main.go:7) INCQ AX
0x004f 00079 (main.go:8) ADDQ DX, CX
0x0052 00082 (main.go:7) CMPQ AX, $5
0x0056 00086 (main.go:7) JLT 71
0x0058 00088 (main.go:11) MOVQ CX, "".t+8(SP)
我把这些指令分为了两个部分,初始化部分和循环主体。前两条指令,将两个寄存器初始化为零值。
0x0041 00065 (main.go:4) XORL AX, AX
0x0043 00067 (main.go:4) XORL CX, CX
寄存器 AX 包含着当前循环所处位置,而 CX 包含着变量 t 的值,下面为带有指令和通用寄存器的直观表示:

循环从表示「跳转到指令 82 」的 JMP 82 开始,这条指令的作用可以通过第二行来判断:

接下来的指令 CMPQ AX,$5 表示「比较寄存器 AX 和 5 」,事实上,这个操作是把 AX 中的值减去 5 ,然后储存在另一个寄存器中,这个值可以被用在下一条指令 JLT 71 中,它的含义是 「如果值小于 0 则跳转到指令 71 」,以下是更新后的直观表示:

如果不满足条件,则程序将会跳转到循环体之后的下一条指令执行。
所以,我们现在有了对循环的基本框架,以下是转换后的 Go 循环:
goto end
start:
?
end:
if i < 5 {
goto start
}
println(t)










