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;
}