汇编分析 Golang 循环(推荐)
女主宣言
今天小编为大家分享一篇关于Golang循环汇编分析的文章,文章中介绍了golang循环的汇编层面的处理,通过分析,我们可以更了解循环的实现。希望能对大家有所帮助。
PS:丰富的一线技术、多元化的表现形式,尽在“360云计算”,点关注哦!
循环是编程中很强大的一个概念,而且非常容易处理。但是,必须将其翻译成机器可理解的基本指令。它的编译方式也可能影响标准库中的其他组件。让我们开始分析一下范围循环。
1循环汇编
范围循环可以迭代数组,切片或通道。下面函数展示了,对分片进行循环并将数字相加:
funcmain(){ l:=[]int{9,45,23,67,78} t:=0 for_,v:=rangel{ t+=v } println(t) }
执行gotoolcompile-Smain.go可以转储生成汇编代码,下面为范围循环的相关代码。
0x004100065(main.go:4)XORLAX,AX 0x004300067(main.go:4)XORLCX,CX 0x004500069(main.go:7)JMP82 0x004700071(main.go:7)MOVQ""..autotmp_5+16(SP)(AX*8),DX 0x004c00076(main.go:7)INCQAX 0x004f00079(main.go:8)ADDQDX,CX 0x005200082(main.go:7)CMPQAX,$5 0x005600086(main.go:7)JLT71 0x005800088(main.go:11)MOVQCX,"".t+8(SP)
我们把指令分为两部分:初始化及循环本身。最开始两行指令用来初始化两个寄存器为0。
0x004100065(main.go:4)XORLAX,AX 0x004300067(main.go:4)XORLCX,CX
寄存器AX包含循环中的当前位置,而CX包含变量t的值。下面是带有指令和通用寄存器的直观表示:
该循环指令JMP82开始,表示跳转到指令82。可以通过第二列来标识此目标指令:
下一条指令CMPQAX,$5表示“比较寄存器AX和数值5”。它实际上是从AX中减去寄存器DX的值,并将结果存储到另一个寄存器中。现在,可以在下一条指令JLT71中使用该值,该指令表示“如果小于0,则跳转到指令71。”下面是更新后的图:
如果条件不满足,则程序将不会跳转执行循环后面的下一条指令。
因此,我们现在有了循环的结构。下面是转换回Go的循环:
gotoend start: ? end: ifi<5{ gotostart } println(t)
该循环的主体是缺失的,下面是指令:
0x004700071(main.go:7)MOVQ""..autotmp_5+16(SP)(AX*8),DX 0x004c00076(main.go:7)INCQAX 0x004f00079(main.go:8)ADDQDX,CX
第一个指令MOVQ""..autotmp_5+16(SP)(AX*8),DX表示“将内存从源移动到目标”。由以下内容组成:
- 片段""..autotmp_5+16(SP)其中SP是堆栈指针(我们当前的内存栈帧),而autotmp_*是自动生成的变量名称。
- 偏移量8(在64位架构上,int为8位)乘以寄存器AX的值,即循环中的当前位置。
- 由寄存器DX表示的,目标现在包含循环的当前值。
然后,INCQ代表“递增”,并将递增循环的当前位置:
循环体的最后一条指令是ADDQDX,CX表示“将DX添加到CX”。之前我们已经看到DX包含循环的当前值,而CX是包含变量t内容的寄存器:
它将一直循环直到循环计数器到达5。然后,循环之后的指令显示寄存器CX将其值移至t:
0x005800088(main.go:11)MOVQCX,"".t+8(SP)
这是处于最终状态的图:
我们还可以在Go中完成循环的翻译:
funcmain(){ l:=[]int{9,45,23,67,78} t:=0 i:=0 vartmpint gotoend start: tmp=l[i] i++ t+=tmp end: ifi<5{ gotostart } println(t) }
为这个新程序生成汇编代码,将提供完全相同的输出。
2改进
内部转换循环的方式可能会对其他功能(例如Go调度程序)产生影响。在Go1.10之前,编译的循环类似于以下代码:
funcmain(){ l:=[]int{9,45,23,67,78} t:=0 i:=0 vartmpint p:=uintptr(unsafe.Pointer(&l[0])) ifi>=5{ gotoend } body: tmp=*(*int)(unsafe.Pointer(p)) p+=unsafe.Sizeof(l[0]) i++ t+=tmp ifi<5{ gotobody } end: println(t) }
这种实现方式的问题是,当达到5时,指针p超过了分配的末尾。这个问题使循环不容易被抢占,因为它的主体不安全。循环编译的优化确保它不会创建任何过去的指针。为准备Go调度程序中的非合作式抢占而进行了此改进。
总结
以上所述是小编给大家介绍的汇编分析Golang循环,希望对大家有所帮助,也非常感谢大家对毛票票网站的支持!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。