[clang] [llvm] [RFC] Better devirtualization for non-virtual interface (PR #185087)

Nikita Taranov via cfe-commits cfe-commits at lists.llvm.org
Sat Mar 7 09:08:47 PST 2026


nickitat wrote:

Thanks for your comments. AFAIK, the general problem that prevents a lot of devirtualization currently is that we should assume that code like this exists in the program:

``` cpp
#include <new>
#include <cstdio>

struct Base {
    virtual void hello() { printf("Base\n"); }
    virtual ~Base() = default;
};

struct Derived : Base {
    virtual void hello() override { printf("Derived\n"); }
};

static_assert(sizeof(Base) == sizeof(Derived)); // same layout

void clobber(Base* p) {
    p->~Base();                  // explicitly destroy the Base object
    ::new (p) Derived();         // construct a Derived in the same storage
    // vptr now points to Derived's vtable
}

int main() {
    alignas(Derived) unsigned char buf[sizeof(Derived)];
    Base* p = ::new (buf) Base();

    p->hello();   // "Base"
    clobber(p);
    p->hello();   // "Derived"

    p->~Base();   // calls ~Derived() via vtable — correct
}
```

I.e., a simple fact that we have a `T*` and that `T` is final or whatever does not guarantee anything by itself. This is why the current implementation is able to devirtualize when it can find the relevant store of vptr:

https://github.com/llvm/llvm-project/blob/8e702735090388a3231a863e343f880d0f96fecb/llvm/lib/Transforms/Utils/CallPromotionUtils.cpp#L709-L712

And a case like this works in the current clang:

``` cpp
void h() { 
    Impl impl; // here is the traceable vptr store
    impl.foo();
}
```

But not the case from the description. So, I don't aim for a really generic solution here. Rather, I'd like to extend the current logic tracking vptr stores to include `static_cast` as another source of a guarantee on the dynamic type.

How to properly propagate this knowledge from the `static_cast` to all subsequent calls is another good question. Probably with alias analysis or some clever invariant groups that were described [here](https://youtu.be/Dt4UehzzcsE?si=t-OPPNrz8ogCYUBN). For now I've added a simple AA-based solution. 

https://github.com/llvm/llvm-project/pull/185087


More information about the cfe-commits mailing list