"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" 因为您需要确保您实际获得了所需的字节数。所以问题是如何指定 mod 和 r/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 字节边界。这将提供优化和代码执行加速。
我正在尝试了解编译器完成的 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 操作码的
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, writerax
instead ofeax
.]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" 因为您需要确保您实际获得了所需的字节数。所以问题是如何指定 mod 和 r/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 字节边界。这将提供优化和代码执行加速。