c - AMD64——nopw 汇编指令?

标签 c gcc assembly x86 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

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


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

long i = 0;

main() {
    recurse();
}

recurse() {
    i++;
    recurse();
}

当用gcc -O2编译时,编译器识别出无限递归并将其变成无限循环;它做得非常好,事实上,它实际上在 main() 中循环而不调用 recurse() 函数。


编者注:带有 NOP 的填充函数并不特定于无限循环。这是一组具有一系列 NOP 长度的函数,on the Godbolt compiler explorer.

最佳答案

0x66 字节是“Operand-Size Override”前缀。多了一个就等于有一个。

0x2e 是 64 位模式下的“空前缀”(否则它是 CS: 段覆盖 - 这就是它出现在程序集助记符中的原因)。

0x0f 0x1f 是 NOP 的 2 字节操作码,它采用 ModRM 字节

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

某些 CPU 解码带有许多前缀(例如超过三个)的指令的速度很慢,因此指定 SIB + disp32 的 ModRM 字节比使用五个额外的前缀字节更好地使用额外的 5 个字节。

AMD K8 decoders in Agner Fog's microarch pdf:

Each of the instruction decoders can handle three prefixes per clock cycle. This means that three instructions with three prefixes each can be decoded in the same clock cycle. An instruction with 4 - 6 prefixes takes an extra clock cycle to decode.


本质上,这些字节是一条永远不会执行的长 NOP 指令。它在那里确保下一个函数在 16 字节边界上对齐,因为编译器发出了 .p2align 4 指令,所以汇编器用 NOP 填充。 gcc's default for x86 is
-falign-functions=16
.对于将要执行的 NOP,长 NOP 的最佳选择取决于微体系结构。对于因许多前缀而阻塞的微架构,如 Intel Silvermont 或 AMD K8,两个带有 3 个前缀的 NOP 可能解码得更快。

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

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

主要在《AMD64架构程序员手册第3卷:通用和系统指令》中。我确信英特尔针对 x64 架构的技术引用资料将包含相同的信息(甚至可能更容易理解)。

关于c - AMD64——nopw 汇编指令?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4798356/

相关文章:

c - Ubuntu 上 C 语言的类似 shell 的应用程序

c++ - 在生产中使用 GCC 的 C++0x 模式?

c - 为什么没有类型转换警告?

c - x86 上交换与比较和交换锁的相对性能

assembly - AVX512 舍入模式如何工作(或者 NDISASM 只是混淆了)?

c - 将带有 typedef 的结构数组传递给函数

c - switch case在c语言中与数组一起使用

c - C 中指针的类型转换错误

c++ - GCC 7 C++ 17 对折叠表达式的支持

assembly - USB 硬盘模拟导致磁盘读取失败(BIOS int 13)?