gcc - 为什么x86-64上的GCC会在函数内部插入NOP?

标签 gcc assembly x86-64 nop

给定以下C函数:

void go(char *data) {
    char name[64];
    strcpy(name, data);
}

将x86-64上的GCC 5和6编译(纯gcc -c -g -o后跟objdump),以将其转换为:
0000000000000000 <go>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 50             sub    $0x50,%rsp
   8:   48 89 7d b8             mov    %rdi,-0x48(%rbp)
   c:   48 8b 55 b8             mov    -0x48(%rbp),%rdx
  10:   48 8d 45 c0             lea    -0x40(%rbp),%rax
  14:   48 89 d6                mov    %rdx,%rsi
  17:   48 89 c7                mov    %rax,%rdi
  1a:   e8 00 00 00 00          callq  1f <go+0x1f>
  1f:   90                      nop
  20:   c9                      leaveq 
  21:   c3                      retq   

GCC是否有任何理由在90处插入nop/1f,或者仅仅是在未启用任何优化的情况下可能发生的副作用?

注意:此问题与大多数其他问题不同,因为它询问的是函数体内而不是外部填充内的nop

经过测试的编译器版本:GCC Debian 5.3.1-14(5.3.1)和Debian 6-20160313-1(6.0.0)

最佳答案

太奇怪了,我以前从没在nop的asm输出中注意到流浪的-O0。 (可能是因为我不会浪费时间在未优化的编译器输出上)。

通常,函数内部的nop用来对齐分支目标,包括the question Brian linked中的函数入口点。 (另请参见-falign-loops in the gcc docs,默认情况下在-Os之外的优化级别处于启用状态)。

在这种情况下,nop是一个空函数的编译器噪声的一部分:

void go(void) {
    //char name[64];
    //strcpy(name, data);
}
    push    rbp
    mov     rbp, rsp
    nop                     # only present for gcc5, not gcc 4.9.3
    pop     rbp
    ret

See that code in the Godbolt Compiler Explorer,因此您可以检查asm的其他编译器版本和编译选项。

(从技术上讲,这不是噪音,但-O0启用-fno-omit-frame-pointer,并且在-O0处甚至设置了空函数并拆除了堆栈框架。)

当然,在任何非零的优化级别上都不存在该nop问题代码中的nop没有调试或性能优势。 (请参阅标签Wiki,尤其是Agner Fog's microarchitecture guide中的性能指南链接,以了解如何使代码在当前CPU上快速运行。)

我的猜测是,它纯粹是gcc内部构件的产物。此nopnop asm输出中以gcc -S的形式存在,而不是在.p2align指令中。 gcc本身不计算机器代码字节,它只是在某些点使用对齐指令来对齐重要的分支目标。只有汇编程序才能知道达到给定对齐实际需要多大的nop

默认的-O0告诉gcc您希望它快速编译而不是编写良好的代码。这意味着与其他-O级别相比,asm输出可告诉您更多关于gcc内部的信息,而很少涉及如何优化或其他方面的信息。

如果您想学习asm,那么例如查看-Og上的代码会更有趣(优化调试)。

如果试图查看gcc或clang在编写代码方面做得如何,则应查看-O3 -march=native(或-O2 -mtune=intel,或用于构建项目的任何设置)。不过,迷惑-O3进行的优化是学习一些asm技巧的好方法。如果您想查看经过完全优化的东西的非矢量化版本,-fno-tree-vectorize会很方便。

关于gcc - 为什么x86-64上的GCC会在函数内部插入NOP?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36646479/

相关文章:

gcc - 使用 GCC 的 -Wpointer-arith

c - va_list 参数未按预期工作

assembly - 从Rust更改x86中的执行堆栈

assembly - 为什么不将功能参数存储在XMM向量寄存器中?

c++ - 如何拦截64位进程中的API方法调用?

assembly - Intel X86-64 组装教程或书籍

c++ - GCC 8 交叉编译器输出 ARMv7 可执行文件而不是 ARMv6

c++ - 如何让 gcc 使用 SSE4.1 pminuq/pminud/etc 操作码对代码进行矢量化?

assembly - "mov offset(%rip), %rax"是做什么的?

c - asm ("nop");作品?