Yes, and in several ways. You can see some examples of devirtualization in this letter. I posted to the Clang mailing list about 2 years ago.
Like all optimizations, this expects the compiler to eliminate alternatives: if it can prove that the virtual call is always allowed in Derived::func , then it can call it directly.
There are various situations, we will start first with semantic evidence:
SomeDerived& d , where SomeDerived is final , allows you to devirtualize all method callsSomeDerived& d , d.foo() , where foo is final , also allows the virtualization of this particular call
Then there are situations when you know the dynamic type of an object:
SomeDerived d; => dynamic type d required SomeDerivedSomeDerived d; Base& b; => dynamic type b required SomeDerived
These 4 devirtualization situations are usually resolved by the compiler because they require fundamental knowledge about the semantics of the language. I can confirm that all 4 are implemented in Clang, and I think they are also implemented in gcc.
However, there are many situations where this breaks down:
struct Base { virtual void foo() = 0; }; struct Derived: Base { virtual void foo() { std::cout << "Hello, World!\n"; }; void opaque(Base& b); void print(Base& b) { b.foo(); } int main() { Derived d; opaque(d); print(d); }
Although it is obvious here that the foo call is resolved before Derived::foo , Clang / LLVM will not optimize it. The problem is that:
- Clang (front-end) does not perform inlining, so it cannot replace
print(d) with d.foo() and devirtualize the call - LLVM (back-end) does not know the semantics of the language, so even after replacing
print(d) with d.foo() it assumes that the virtual pointer d could be changed to opaque (the definition is opaque, as the name implies)
I watched the efforts on the Clang and LLVM mailing lists, as both sets of developers talked about losing information and how to get Clang to tell LLVM: "Everything is fine," but unfortunately the problem is not trivial and has not yet been resolved. .. thus, half-inspired devirtualization in the interface in order to try to get all the obvious cases, and some not so obvious (although, by agreement, the interface is not where you implement them).
For reference, the code for devirtualization in Clang can be found in CGExprCXX.cpp in a function called canDevirtualizeMemberFunctionCalls . It is only ~ 64 lines (right now) and carefully commented out.
Matthieu M.
source share