给定以下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是否有任何理由在1f
处插入90
/nop
,或者这只是在未打开优化时可能发生的副作用?
注意:这个问题与大多数其他问题不同,因为它询问的是函数体内部的nop
,而不是外部填充
测试的编译器版本:GCC Debian 5.3.1-14(5.3.1)和Debian 6-20160313-1(6.0.0)
这很奇怪,我以前从未注意到-O0
的asm输出中有杂散nop
s。(可能是因为我不会浪费时间查看未优化的编译器输出)。
通常nop
的内部函数是对齐分支目标,包括Brian链接的问题中的函数入口点。(另请参阅gcc文档中的-falign-loops
,在除-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
没有调试或性能优势(请参阅x86标签wiki中的性能指南链接,特别是Agner Fog的微体系结构指南,了解是什么使代码在当前CPU上快速运行。)
我的猜测是它纯粹是gcc内部构件。该nop
在gcc -S
asm输出中作为nop
存在,而不是作为.p2align
指令存在。gcc本身不计算机器代码字节数,它只是在某些点使用对齐指令来对齐重要的分支目标。只有汇编程序知道实际需要多大的nop
才能达到给定的对齐。
默认的-O0
命令告诉gcc,您希望它快速编译,而而不是生成好的代码。这意味着asm输出告诉您更多关于gcc内部的信息,而不是其他-O
级别,而很少关于如何优化或其他任何内容。
如果您正在尝试学习asm,那么查看-Og
中的代码会更有趣(针对调试进行优化)。
如果你想看看gcc或clang在编写代码方面做得有多好,你应该看看-O3 -march=native
(或-O2 -mtune=intel
,或者你构建项目时使用的任何设置)。不过,弄清楚在-O3
中进行的优化是学习一些asm技巧的好方法。如果你想看到一个完全优化的非矢量化版本,-fno-tree-vectorize
是很方便的。