以下代码无法通过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属性)。模仿一些现实世界的要求也不是最终的。
答案 0 :(得分:3)
当编译器在编译时知道对象的类型时,就会发生虚拟化。在这里,您无法使用C :: C使主要人员无法知道在构造过程中哪种类型的对象实际上会导致C :: b。
答案 1 :(得分:0)
在构造函数是另一个编译单元的现实代码中,编译器将无法看到构造函数的主体(因此是noinline属性)。模仿一些现实世界的要求也不是最终的。
要进行去虚拟化,编译器通常需要能够证明类层次结构是密封的。如果对构造函数的调用是在单独的转换单元中,则编译器无法证明它。但是,使用link-time optimization可以在翻译单元之间提供优化器信息,这可以更容易地证明关于类层次结构和引用的事实。
以下是使用clang的示例。
#ifndef B_H
#define B_H
struct B {
virtual int foo();
};
#endif
#include "b.h"
int B::foo() { return 3; };
#ifndef C_H
#define C_H
#include "b.h"
struct C {
B& b;
C(B& b);
int foo();
};
#endif
#include "c.h"
C::C(B& b) : b(b) {}
int C::foo() {
return b.foo();
}
#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
。
_ZN1C3fooEv: # @_ZN1C3fooEv
.cfi_startproc
# BB#0:
movq (%rdi), %rdi
movq (%rdi), %rax
jmpq *(%rax) # TAILCALL <== pointer call
但是,给予优化器链接时间
信息(-flto
)允许它证明类层次结构与呼叫站点是密封的。
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