HN
Today

When can the C++ compiler devirtualize a call?

This C++ deep dive meticulously explores the complex world of devirtualization, an optimization where virtual function calls are converted to direct calls. It meticulously details when compilers like GCC, Clang, MSVC, and ICC can perform this magic, dissecting various scenarios from known dynamic types to intricate "proofs of leafness." The article appeals to Hacker News's love for low-level performance insights and detailed compiler behavior.

5
Score
1
Comments
#16
Highest Rank
3h
on Front Page
First Seen
May 19, 1:00 AM
Last Seen
May 19, 3:00 AM
Rank Over Time
161818

The Lowdown

Devirtualization is a crucial compiler optimization in C++ that transforms expensive virtual function calls into faster direct calls. This article dives into the conditions under which modern compilers can perform this optimization, focusing exclusively on what the compiler itself can deduce without link-time optimization (LTO).

The author identifies two primary scenarios where compilers can achieve devirtualization:

  • When the instance's dynamic type is known:
    • This is straightforward when an object's concrete type is directly instantiated and used (e.g., Apple o; o.f();).
    • More complex cases involve dataflow analysis, such as when a base pointer points to a derived object (Base *p = &d; p->f();). Compilers like MSVC and ICC can be fooled by even this simple indirection.
    • Conditional assignments (cond ? &da : &db;) further challenge compilers; GCC shows more advanced analysis but also has limits.
  • When a "proof of leafness" exists for its static type:
    • Proof-by-final: Marking a class (struct Derived final) or a specific virtual method (int f() override final) as final guarantees no further inheritance or overrides, allowing devirtualization. While most compilers handle this, ICC shows some weaknesses with inherited final methods.
    • "Silly" final destructor: Clang uniquely uses final destructors as a proof of leafness, warning about them but also optimizing.
    • Proof-by-internal-linkage: Classes whose names have internal linkage (e.g., in anonymous namespaces) cannot be derived from in other translation units. If such a class has no local children overriding methods, its virtual calls can be devirtualized. This can apply to template instantiations or types with internal linkage members, though compiler support varies (GCC detects more cases here).
    • The author also mentions a "silly old-school trick" involving virtual bases with private constructors, which effectively prevents derivation, though no compiler currently optimizes based on this logic.

The article concludes with a comprehensive table comparing how GCC, Clang, MSVC, and ICC perform across the detailed test cases, highlighting their strengths and weaknesses in applying devirtualization optimizations.