9

给定以下 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/ nopat1f或者这只是在未打开优化时可能发生的副作用?

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

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

4

1 回答 1

10

这很奇怪,我以前从未注意到nopasm 输出中的流浪 s -O0。(可能是因为我不会浪费时间查看未优化的编译器输出)。

通常nops 内部函数用于对齐分支目标,包括问题 Brian linked 中的函数入口点。(另请参阅-falign-loops gcc 文档,默认情况下在除 之外的优化级别上启用-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

在 Godbolt 编译器资源管理器中查看该代码,以便您可以检查 asm 以获取其他编译器版本和编译选项。

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


当然,这nop在任何非零优化级别都不存在。 问题中的代码没有调试或性能优势nop标签 wiki 中的性能指南链接,尤其是Agner Fog 的微架构指南,了解是什么让代码在当前 CPU 上运行得更快。)

我的猜测是它纯粹是 gcc internals 的产物。这nop是asm 输出nop中的gcc -Sa ,而不是.p2align指令。gcc 本身不计算机器码字节,它只是在某些点使用对齐指令来对齐重要的分支目标。只有汇编器知道实际需要多大的 anop才能达到给定的对齐。

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

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

如果您想了解 gcc 或 clang 在编写代码方面的表现如何,您应该查看-O3 -march=native(或-O2 -mtune=intel,或您构建项目时使用的任何设置)。不过,弄明白在 处所做的优化-O3是学习一些 asm 技巧的好方法。 -fno-tree-vectorize如果您想查看除此之外完全优化的东西的非矢量化版本,它会很方便。

于 2016-04-15T12:57:07.823 回答