Skip to content
Tech News
← Back to articles

When can the C++ compiler devirtualize a call?

read original get C devirtualization tools → more articles
Why This Matters

This article highlights how modern C++ compilers can reliably devirtualize calls to final methods, improving performance by enabling more aggressive optimizations. Understanding when and how devirtualization occurs helps developers write more efficient code and informs compiler design, ultimately benefiting both industry and consumers through faster, more optimized applications.

Key Takeaways

Someone recently asked me about devirtualization optimizations: when do they happen? when can we rely on devirtualization? do different compilers do devirtualization differently? As usual, this led me down an experimental rabbit-hole. The answer seems to be: Modern compilers devirtualize calls to final methods pretty reliably. But there are many interesting corner cases — including some I haven’t thought of, I’m sure! — and different compilers do catch different subsets of those corner cases.

First, let’s observe that devirtualization can (probably?) be done more effectively via LTO, using whole-program analysis. I don’t know anything about the state of the art in link-time devirtualization, and it’s hard to experiment with on Compiler Explorer, so I’m not going to talk about LTO at all. We’re looking purely at what the compiler itself can do.

There are basically two situations where the compiler knows enough to devirtualize. They don’t have much in common:

When we know the instance’s dynamic type

The archetypical case here is

void test() { Apple o; o.f(); }

It doesn’t matter if Apple::f is virtual; all virtual dispatch ever does is invoke the method on the actual dynamic type of the object, and here we know the actual dynamic type is exactly Apple . Static and dynamic dispatch should give us the same result in this case.

A sufficiently smart compiler will use dataflow analysis to optimize non-trivial cases such as

Derived d; Base *p = &d; p->f();

It turns out that even this simple dodge is enough to fool MSVC and ICC. The next test case is

... continue reading