给定以下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
没有调试或性能优势。 (请参阅x86标签Wiki,尤其是Agner Fog's microarchitecture guide中的性能指南链接,以了解如何使代码在当前CPU上快速运行。)我的猜测是,它纯粹是gcc内部构件的产物。此
nop
在nop
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/