C++ gcc vs clang:用-fPIC内联函数
考虑以下代码:C++ gcc vs clang:用-fPIC内联函数,c++,gcc,clang,fpic,C++,Gcc,Clang,Fpic,考虑以下代码: // foo.cxx int last; int next() { return ++last; } int index(int scale) { return next() << scale; } 这将产生: next(): movq last@GOTPCREL(%rip), %rdx movl (%rdx), %eax addl $1, %eax movl %eax, (%rdx) re
// foo.cxx
int last;
int next() {
return ++last;
}
int index(int scale) {
return next() << scale;
}
这将产生:
next():
movq last@GOTPCREL(%rip), %rdx
movl (%rdx), %eax
addl $1, %eax
movl %eax, (%rdx)
ret
index(int):
pushq %rbx
movl %edi, %ebx
call next()@PLT ## next() not inlined, call through PLT
movl %ebx, %ecx
sall %cl, %eax
popq %rbx
ret
但是,当使用clang 3.9编译具有相同标志的相同代码时:
next(): # @next()
movq last@GOTPCREL(%rip), %rcx
movl (%rcx), %eax
incl %eax
movl %eax, (%rcx)
retq
index(int): # @index(int)
movq last@GOTPCREL(%rip), %rcx
movl (%rcx), %eax
incl %eax ## next() was inlined!
movl %eax, (%rcx)
movl %edi, %ecx
shll %cl, %eax
retq
gcc通过PLT调用
next()
,clang将其内联。两者仍然从GOT中查找上次的。对于linux上的编译,clang进行优化是正确的,而gcc在轻松内联上遗漏了,还是clang进行优化是错误的,还是这纯粹是一个QoI问题?我不认为该标准有太多细节。它只是粗略地说,如果符号在不同的翻译单位中有外部链接,那么它就是同一个符号。这使得clang的版本是正确的
从那时起,据我所知,我们已经超出了标准。编译器的选择在他们认为有用的<代码> -FPIC输出。
请注意,g++-c-std=c++11-O3-fPIE
输出:
0000000000000000 <_Z4nextv>:
0: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 6 <_Z4nextv+0x6>
6: 83 c0 01 add $0x1,%eax
9: 89 05 00 00 00 00 mov %eax,0x0(%rip) # f <_Z4nextv+0xf>
f: c3 retq
0000000000000010 <_Z5indexi>:
10: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 16 <_Z5indexi+0x6>
16: 89 f9 mov %edi,%ecx
18: 83 c0 01 add $0x1,%eax
1b: 89 05 00 00 00 00 mov %eax,0x0(%rip) # 21 <_Z5indexi+0x11>
21: d3 e0 shl %cl,%eax
23: c3 retq
基本上,这明确地表明,尽管使它们在全球范围内可用,但我们使用这些符号的隐藏版本,将忽略任何类型的插入。然后,两个编译器完全优化了访问,而不管传递的选项是什么。@Barry>很高兴您发现它很有用,我在研究这个问题时确实学到了一些有趣的东西。我对上次的也很好奇,做了一些测试;在帖子中添加了一些发现。alias属性相对于可见性(“隐藏”)
有什么好处?@Barry>这只是为了让属性保持可见,以便语义保持等效,但缺少插入支持。诚然,若并没有实际导出它们的意图,那个么只需去掉别名并具有可见性(“隐藏”)。顺便说一句,我没有对它进行测试,但我相信使用-fvisibility=hidden-fvisibility inlines hidden进行编译,并使用\uuuuu属性((可见性(“默认”))
手动标记相关符号,应该会产生相同的结果。@Oliv>有趣的是,在最后一次上使用受保护的可见性,clang-O2-fPIC-shared foo.cxx
失败,出现错误在创建共享对象时,无法使用针对受保护符号“last”重新定位R_X86_64_PC32
。所以我想说,clang(3.8.0)的实现是可疑的。@spectras我继续朝着这个方向挖掘。可以通过声明变量extern并将其定义为其他源文件来修复此异常错误。我已经与readelf进行了检查,这3个符号具有相同的受保护可见性。此外,clang和gcc都从动态位置加载last
:)!
0000000000000000 <_Z4nextv>:
0: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 6 <_Z4nextv+0x6>
6: 83 c0 01 add $0x1,%eax
9: 89 05 00 00 00 00 mov %eax,0x0(%rip) # f <_Z4nextv+0xf>
f: c3 retq
0000000000000010 <_Z5indexi>:
10: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 16 <_Z5indexi+0x6>
16: 89 f9 mov %edi,%ecx
18: 83 c0 01 add $0x1,%eax
1b: 89 05 00 00 00 00 mov %eax,0x0(%rip) # 21 <_Z5indexi+0x11>
21: d3 e0 shl %cl,%eax
23: c3 retq
// foo.cxx
int last_ __attribute__((visibility("hidden")));
extern int last __attribute__((alias("last_")));
int __attribute__((visibility("hidden"))) next_()
{
return ++last_;
}
// This one is ugly, because alias needs the mangled name. Could extern "C" next_ instead.
extern int next() __attribute__((alias("_Z5next_v")));
int index(int scale) {
return next_() << scale;
}