x86 LOOP指令到底如何工作?

Han*_*can 0 x86 assembly loops

            mov    ecx, 16
looptop:    .
            .
            .
            loop looptop
Run Code Online (Sandbox Code Playgroud)

此循环执行多少次?

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

Pet*_*des 9

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进行了优化,否则这是一个晦涩的代码查询指令。

  • 我决定将其发布,以便将来对任何“循环工作原理”问题有一个规范的答案。 (2认同)
  • @ineedahero:然后在第一句话或第一段之后停止阅读。那正好描述了它的正常运行。 (2认同)