我正在尝试理解编译器完成的x64
程序集优化。
我在Windows 8.1上使用Release
IDE编译了一个小型C ++项目Visual Studio 2008 SP1
。
其中一行包含以下汇编代码:
B8 31 00 00 00 mov eax,31h
0F 1F 44 00 00 nop dword ptr [rax+rax]
这是截图:
据我所知nop
本身就是do nothing
,但我从未见过像这样的操作数。
有人可以解释它的作用吗?
答案 0 :(得分:7)
在此页面其他地方的comment中,Michael Petch指向web page,其中描述了 Intel x86 多字节NOP操作码。该页面有一个有用的信息表,但遗憾的是HTML搞砸了,所以你无法读取它。以下是该页面的一些信息,另外该表格提供了一个可读的表格:
多字节NOP
http://www.felixcloutier.com/x86/NOP.html
单字节NOP指令是XCHG(E)AX,(E)AX指令的别名助记符。多字节NOP指令不对受支持的处理器执行任何操作,并在不支持多字节NOP指令的处理器上生成未定义的操作码异常。
指令的存储器操作数形式允许软件将“无操作”的字节序列创建为一条指令。
对于需要多字节NOP的情况,推荐的操作(32位模式
和64位模式)是:[我的编辑:在64位模式下,写入rax
代替eax
。]Length Assembly Byte Sequence ------- ------------------------------------------ -------------------------- 1 byte nop 90 2 bytes 66 nop 66 90 3 bytes nop dword ptr [eax] 0F 1F 00 4 bytes nop dword ptr [eax + 00h] 0F 1F 40 00 5 bytes nop dword ptr [eax + eax*1 + 00h] 0F 1F 44 00 00 6 bytes 66 nop word ptr [eax + eax*1 + 00h] 66 0F 1F 44 00 00 7 bytes nop dword ptr [eax + 00000000h] 0F 1F 80 00 00 00 00 8 bytes nop dword ptr [eax + eax*1 + 00000000h] 0F 1F 84 00 00 00 00 00 9 bytes 66 nop word ptr [eax + eax*1 + 00000000h] 66 0F 1F 84 00 00 00 00 00
请注意,选择正确字节序列的技术 - 以及所需的总大小 - 可能因您使用的汇编程序而异。
例如,从表中取出的以下两行汇编表面上相似:
nop dword ptr [eax + 00h]
nop dword ptr [eax + 00000000h]
这些只在前导零的数量上有所不同,并且一些汇编程序可能难以禁用它们总是编码最短字节序列的“有用”功能,这可能使第二个表达式无法访问。
对于多字节NOP情况,您不需要此“帮助”,因为您需要确保实际获得所需的字节数。所以问题是如何指定 mod 和 r / m 位的精确组合,最终得到所需的 disp 大小 - 但是通过单独的指令助记符。这个主题很复杂,当然超出了我的知识范围,但Scaled Indexing,MOD+R/M和SIB可能是一个起点。
现在我知道你只是想,如果你发现很难或不可能通过指令助记符来强迫你的汇编程序的合作,你总是可以只使用db
(“定义字节”)作为一个简单的麻烦保证工作的替代方案。
答案 1 :(得分:4)
正如注释中所指出的,当该指令是循环中的第一条指令时,它通常用于将后续指令与16字节边界对齐。
这种对齐可以帮助获取指令带宽,因为指令获取通常以16字节为单位进行,因此对齐循环的顶部可以最大程度地实现解码而不会出现瓶颈。
通过引入循环缓冲区和 uop缓存对比对敏感性较低,这种对齐的重要性可能不像以前那么重要。在某些情况下,这种优化甚至可能是一种悲观,特别是当循环执行很少次时。
答案 2 :(得分:1)
当使用跳转指令执行从较大地址到较低地址(0EBh XX-jmp short)和(0E9h XX XX XX XX-jmp near)的跳转指令时,完成此代码对齐,其中两种情况下的XX是负号数。因此,编译器将需要跳转的代码块对齐到10h字节边界。这样可以优化并加快代码执行速度。