给出以下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)
答案 0 :(得分:9)
这很奇怪,我以前从来没有注意到nop
的asm输出中的迷路-O0
s。 (可能是因为我不会浪费时间查看未优化的编译器输出)。
通常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中的效果指南链接,尤其是{ {3}}了解在当前CPU上使代码快速运行的原因。)
我的猜测是它纯粹是gcc内部的神器。此nop
在nop
asm输出中为gcc -S
,而不是.p2align
指令。 gcc本身并不计算机器代码字节,它只是在某些点使用对齐指令来对齐重要的分支目标。只有汇编程序知道实际需要nop
达到给定对齐的大小。
默认-O0
告诉gcc您希望它快速编译并且不制作好的代码。这意味着asm输出比其他-O
级别更多地告诉您gcc内部结构,而关于如何优化或其他任何内容的信息很少。
如果您正在尝试学习asm,那么查看-Og
处的代码会更有趣(例如,优化调试)。
如果您正在尝试查看gcc或clang在制作代码时的表现,那么您应该查看-O3 -march=native
(或-O2 -mtune=intel
,或者您构建项目的任何设置)。尽管如此,在-O3
中进行优化是一种很好的方法,但也是学习一些asm技巧的好方法。 -fno-tree-vectorize
如果您希望看到除此之外的完全优化的非矢量化版本,则非常方便。