Warm tip: This article is reproduced from serverfault.com, please click

assembly-x86 LOOP指令到底如何工作?

(assembly - How exactly does the x86 LOOP instruction work?)

发布于 2017-10-23 02:49:51
            mov    ecx, 16
looptop:    .
            .
            .
            loop looptop

此循环执行多少次?

如果ecx = 0开始,会发生什么loop在这种情况下跳还是掉下去?

Questioner
Hannah Duncan
Viewed
11
Peter Cordes 2019-10-10 04:39:59

loop完全一样dec ecx / jnz,只是它没有设置标志

就像do{} while(--ecx != 0);C中a的底部。如果执行使用进入循环ecx = 0,则环绕表示循环将运行2 ^ 32次。(或者在64位模式下为2 ^ 64倍,因为它使用RCX)。

与不同rep movsb/stosb/etc.,它不会在递减之前(仅在递减之后)检查ECX = 0。

地址大小确定它是否使用CX,ECX或RCX。因此,在64位代码中,addr32 loop就像dec ecx / jnz,而常规代码loop一样dec rcx / jnz或在16位代码中,它通常使用CX,但地址大小前缀(0x67)将使其使用ecx正如英特尔手册所说,它忽略了REX.W,因为它设置了操作数大小,而不是地址大小。

相关:为什么循环总是被编译成“ do ... while”风格(尾跳)?更多有关循环结构,ASM,while(){}do{}while()和如何将它们布置。


额外的调试技巧

如果你想了解指令的详细信息,请查看该手册:英特尔的官方vol.2 PDF指令集参考手册,或html摘录,其中每个条目都位于不同的页面上(http://felixcloutier.com/x86/)。但是请注意,HTML省略了介绍和附录,其中包含有关如何解释内容的详细信息,例如对于诸如的说明说“根据结果设置了标志”时add

你也可以(而且应该)在调试器中尝试一些操作:单步执行,并观察寄存器的变化。使用一个较小的起始值,ecx这样你就可以ecx=1更快地到达有趣的部分。另请参见x86标签Wiki,以获取底部的手册,指南和asm调试提示的链接。


顺便说一句,如果未显示的循环内指令进行了修改ecx,则它可以循环任意次。为了使问题有一个简单而独特的答案,你需要保证标签和说明之间的loop说明不被修改ecx(他们可以保存/恢复,但如果你打算这样做,通常最好是只使用一个不同的寄存器作为循环计数器。 push/pop内循环,使你的代码难以阅读。)


LOOP即使你已经需要在循环中增加其他内容也要过度使用LOOP不是循环的唯一方法,通常是最糟糕的方法。

通常不要使用循环指令,除非以速度为代价来优化代码大小,因为它很慢编译器不使用它。(因此,CPU供应商不必费心将其加快;赶上22。)使用dec / jnz或完全不同的循环条件。(另请参见http://agner.org/optimize/,以了解更多有关效率的信息。)

循环甚至不必使用计数器。比较指针和结束地址或检查其他条件通常更好,甚至更好。(无意义的使用loop是我的烦恼之一,特别是当你已经在另一个寄存器中有某些东西可以用作循环计数器时。)cx用作循环计数器时,当你可以使用cmp/时,通常只会占用你宝贵的几个寄存器之一jcc在另一个寄存器上,你无论如何都要递增。

IMO,loop应该被认为是那些不适合初学者的晦涩的x86指令之一。stosd(不带rep前缀)aamxlatb不过,在优化代码大小时,它确实具有实际用途。(这在现实生活中有时对机器代码(例如引导扇区)有用,而不仅仅是像代码高尔夫之类的东西。)

IMO,只是教/学条件分支的工作原理,以及如何使它们循环。这样你就不会陷入思考使用循环的特殊情况loop我已经看到一个SO问题或评论,上面写着“我认为你必须声明循环”之类的内容,却没有意识到那loop只是一条指令。

</rant>就像我说的,loop是我的宠儿之一。除非你针对实际的8086进行了优化,否则这是一个晦涩的代码查询指令。