"nop dword ptr [rax+rax]" x64 汇编指令有什么作用?

What does "nop dword ptr [rax+rax]" x64 assembly instruction do?

我正在尝试了解编译器完成的 x64 汇编优化。

我在 Windows 8.1.

上使用 Visual Studio 2008 SP1 IDE 编译了一个小型 C++ 项目 Release

其中一行包含以下汇编代码:

B8 31 00 00 00   mov         eax,31h
0F 1F 44 00 00   nop         dword ptr [rax+rax]

这是截图:

据我所知 nop 本身就是 do nothing,但我从未见过它有这样的操作数。

谁能解释一下它的作用?

正如评论中指出的那样,它是一个 multi-byte NOP 通常用于将后续指令对齐到 16 字节边界,当该指令是循环中的第一条指令时。

这种对齐可以帮助提高指令获取带宽,因为指令获取通常以 16 字节为单位发生,因此对齐循环顶部可以最大程度地实现无瓶颈解码。

随着 循环缓冲区uop 缓存 的引入,这种对齐的重要性可以说不如以前那么重要了对对齐不太敏感。在某些情况下,这种优化甚至可能是一种悲观化,尤其是当循环执行次数很少时。

在描述 Intel x86 多字节 NOP 操作码的 elsewhere on this page, Michael Petch points to a web page 中。该页面有 table 个有用的信息,但不幸的是 HTML 被弄乱了,所以您无法阅读。这是该页面的一些信息,另外 table 呈现了一种可读的形式:

Multi-Byte NOP
http://www.felixcloutier.com/x86/NOP.html
The one-byte NOP instruction is an alias mnemonic for the XCHG (E)AX, (E)AX instruction.

The multi-byte NOP instruction performs no operation on supported processors and generates undefined opcode exception on processors that do not support the multi-byte NOP instruction.

The memory operand form of the instruction allows software to create a byte sequence of “no operation” as one instruction.

For situations where multiple-byte NOPs are needed, the recommended operations (32-bit mode and 64-bit mode) are:     [my edit: in 64-bit mode, write rax instead of 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


请注意,选择正确字节序列的技术——以及所需的总大小——可能会因您使用的汇编程序而异。

例如,从 table 中截取的以下两行汇编在表面上是相似的:

nop dword ptr [eax + 00h]
nop dword ptr [eax + 00000000h]

它们仅在前导零的数量上有所不同,一些汇编程序可能很难禁用它们的 "helpful" 始终对尽可能短的字节序列进行编码的功能,这可能会使第二个表达式无法访问。

对于多字节 NOP 情况,您不希望这样 "help" 因为您需要确保您实际获得了所需的字节数。所以问题是如何指定 modr/m 位的精确组合,最终得到所需的 disp size——但仅通过指令助记符。这个话题很复杂,当然超出了我的知识范围,但 Scaled Indexing, MOD+R/M and SIB 可能是一个起点。

现在我知道你只是在想,如果你发现很难或不可能通过指令助记符强制你的汇编程序合作,你总是可以求助于 db ("define bytes") 作为一个简单的没有大惊小怪的选择,嗯,保证工作。

当使用跳转指令执行从较大地址到较低地址的跳转(0EBh XX - jmp short)和(0E9h XX XX XX XX - jmp near)时,将完成此代码对齐,其中 XX 在两种情况下都是有符号的负数。因此,编译器将需要执行跳转的代码块对齐到 10h 字节边界。这将提供优化和代码执行加速。