GCC去简化类的虚拟化

问题描述 投票:1回答:2

以下代码未通过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属性)。模仿一些现实世界的要求也不是最终的。

c++ gcc optimization vtable devirtualization
2个回答
3
投票

当编译器在编译时知道对象的类型时,就会发生虚拟化。在这里,您无法使用C :: C使得main无法知道什么类型的对象在构造期间实际上最终到达C :: b。


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(构造函数)的调用站点一无所知,因此它对B的运行时类型一无所知。所以,它无法去除B::foo的虚拟化。

C::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 

main

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
© www.soinside.com 2019 - 2024. All rights reserved.