AMD64 - nopw汇编指令?

Jef*_*and 20 c x86 assembly gcc machine-code

在这个编译器输出中,我试图理解nopw指令的机器码编码是如何工作的:

00000000004004d0 <main>:
  4004d0:       eb fe                   jmp    4004d0 <main>
  4004d2:       66 66 66 66 66 2e 0f    nopw   %cs:0x0(%rax,%rax,1)
  4004d9:       1f 84 00 00 00 00 00
Run Code Online (Sandbox Code Playgroud)

http://john.freml.in/amd64-nopl上有一些关于"nopw"的讨论.任何人都可以解释4004d2-4004e0的含义吗?从查看操作码列表,似乎66 ..代码是多字节扩展.我觉得我可能会得到一个比这更好的答案,除非我试图在几个小时内查看操作码列表.


asm输出来自C中的以下(疯狂)代码,它优化为简单的无限循环:

long i = 0;

main() {
    recurse();
}

recurse() {
    i++;
    recurse();
}
Run Code Online (Sandbox Code Playgroud)

编译时gcc -O2,编译器识别无限递归并将其转换为无限循环; 它实际上做得很好,事实上,它实际上是在main()没有调用recurse()函数的情况下循环.


编者注:带NOP的填充函数并不特定于无限循环.在Godbolt编译器资源管理器中,这是一组具有一系列NOP长度的函数.

Mic*_*urr 23

0x66字节是一个"操作数大小覆盖"前缀.拥有多个这些相当于拥有一个.

0x2e是在64位模式下的"零前缀"(这是一个CS:段重载否则-这就是为什么它在装配助记符显示出来).

0x0f 0x1f 是一个NOP的2字节操作码,它接受一个ModRM字节

0x84ModRM字节,在这种情况下编码使用5个字节的寻址模式.

一些CPU很难解码具有许多前缀的指令(例如,多于三个),因此指定SIB + disp32的ModRM字节是比使用五个以上前缀字节多出5个字节的更好方法.

Agner Fog microarch的AMD K8解码器pdf:

每个指令解码器可以在每个时钟周期处理三个前缀.这意味着可以在同一时钟周期中解码具有三个前缀的三个指令.具有4 - 6个前缀的指令需要额外的时钟周期来解码.


本质上,这些字节是一条长NOP指令,无论如何都不会被执行.它在那里确保下一个函数在16字节边界上对齐,因为编译器发出了一个.p2align 4指令,因此汇编器用NOP填充. gcc的x86默认值是
-falign-functions=16
.对于将要执行的NOP,长NOP的最佳选择取决于微体系结构.对于在许多前缀上窒息的微体系结构,如Intel Silvermont或AMD K8,两个带有3个前缀的NOP可能解码得更快.

链接到(http://john.freml.in/amd64-nopl)的问题的博客文章解释了为什么编译器使用复杂的单个NOP指令而不是一堆单字节0x90 NOP指令.

您可以在AMD的技术参考文档中找到有关指令编码的详细信息:

主要在"AMD64架构程序员手册第3卷:通用和系统指令"中.我确信英特尔对x64架构的技术参考将具有相同的信息(甚至可能更容易理解).