GCC去简化类的虚拟化

时间:2015-11-19 02:55:29

标签: c++ gcc optimization vtable devirtualization

以下代码无法通过gcc进行虚拟化。我有什么想法可以说服gcc去虚拟化?

struct B /* final */ {
    virtual int foo() { return 3; }
};

struct C {
    B& b;

    __attribute__((noinline))
    C( B& b ) : b(b) {
    }

    int foo() {
        return b.foo();
    }
};

int main() {
    B b;
    C c(b);

    int res = c.foo();
    return res;
}

我天真地认为这将是虚拟化的(至少是推测性的)并且内联。

在构造函数是另一个编译单元的现实代码中,编译器将无法看到构造函数的主体(因此是noinline属性)。模仿一些现实世界的要求也不是最终的。

2 个答案:

答案 0 :(得分:3)

当编译器在编译时知道对象的类型时,就会发生虚拟化。在这里,您无法使用C :: C使主要人员无法知道在构造过程中哪种类型的对象实际上会导致C :: b。

答案 1 :(得分:0)

  

在构造函数是另一个编译单元的现实代码中,编译器将无法看到构造函数的主体(因此是noinline属性)。模仿一些现实世界的要求也不是最终的。

要进行去虚拟化,编译器通常需要能够证明类层次结构是密封的。如果对构造函数的调用是在单独的转换单元中,则编译器无法证明它。但是,使用link-time optimization可以在翻译单元之间提供优化器信息,这可以更容易地证明关于类层次结构和引用的事实。

以下是使用clang的示例。

b.hpp

#ifndef B_H
#define B_H

struct B {

  virtual int foo();

};

#endif

b.cpp

#include "b.h"

int B::foo() { return 3; };

c.hpp

#ifndef C_H
#define C_H

#include "b.h"

struct C {

  B& b;

  C(B& b);

  int foo();

};

#endif

c.cpp

#include "c.h"

C::C(B& b) : b(b) {}

int C::foo() {

    return b.foo();
}

的main.cpp

#include <iostream>

#include "b.h"
#include "c.h"

int main(const int argc, const char* argv[argc]) {

  B b;
  C c(b);

  std::cout << c.foo() << std::endl;

  return 0;
}

由于优化器对C::C({1}}的呼叫站点一无所知 构造函数)它对B的运行时类型一无所知。所以,它不能 去虚拟化B::foo

ç:: foo的

_ZN1C3fooEv:                            # @_ZN1C3fooEv
    .cfi_startproc
# BB#0:
    movq    (%rdi), %rdi
    movq    (%rdi), %rax
    jmpq    *(%rax)                 # TAILCALL  <== pointer call

但是,给予优化器链接时间 信息(-flto)允许它证明类层次结构与呼叫站点是密封的。

B :: foo的

0000000000400960 <_ZN1B3fooEv>:
  400960:   b8 03 00 00 00          mov    $0x3,%eax
  400965:   c3                      retq   
  400966:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)
  40096d:   00 00 00 

0000000000400970 <main>:
  400970:   41 56                   push   %r14
  400972:   53                      push   %rbx
  400973:   50                      push   %rax
  400974:   48 c7 04 24 78 0a 40    movq   $0x400a78,(%rsp)
  40097b:   00 
  40097c:   48 8d 3c 24             lea    (%rsp),%rdi
  400980:   e8 db ff ff ff          callq  400960 <_ZN1B3fooEv> # <== direct call