Hacker News new | ask | show | jobs
by Spex_guy 1650 days ago
It depends a lot, but in practice in Zig devirtualization is effectively constant propagation. The compiler needs to see the place where the vtable is created, and follow that to the place where virtual functions are called, ensuring along the way that nothing modifies the vtable. This is not possible for all uses of interfaces, but it is possible for many of them, especially ones where the interface is sort of "temporary" and you are usually passing around the implementation. These are the cases targeted by this change.

The difference in results has to do with pointer provenance tracking and aliasing. With both approaches, the first call to an interface function will almost definitely be devirtualized. The problem is that that first call will also modify implementation state. If the implementation function is not inlined (which is common), this is tracked as a modification to the memory region containing the implementation state. But with the fieldParentPtr model, that's the same memory region containing the vtable! So this breaks constant propagation on the vtable and any later calls must always be fully virtual, even if the optimizer can see the whole way from vtable creation to virtual call.

1 comments

So does this optimization only work when the declaration of the allocator and its uses are in the same codegen unit? Otherwise the vtable of the allocator can’t be known at compile time.
Zig's build model is mostly to combine everything into one compilation unit (from multiple files). Since Zig doesn't have a preprocessor, the compiler can reason about incremental compilation and avoid rebuilding everything for every change. Incremental optimization is an open question that we will need to tackle at some point, but the focus for now is on getting the language to a point where it can be stable first.

You can introduce intentional graph cuts if you want, and build multiple objects, but you are limited to the C ABI at these boundaries.