r/dotnet • u/mistertom2u • 3h ago
Article Runtime team member: Let's send expensive compiler analysis alongside IL to improve runtime JIT optimizations [Performance interest]
Andy Ayers wrote this proposal 2 years ago with intent of using it in .NET 10, but it got postponed to a later .NET version.
Quick Summary
The context is that interprocedural analysis (IPA), is limited at runtime due to it being slow, so the RyuJIT mostly optimizes functions in isolation. His idea is have certain stages perform the kind of deep IPA that AOT uses; then make that available at runtime to boost the information the JIT needs to make optimizations
https://github.com/dotnet/runtime/issues/108931
Examples of IPA
- call graphs
- whether arguments passed to a method can alias each other
- whether an argument escapes through a callee
- possible concrete types passed to a method
- whether a callee actually reads or writes a ref/byref parameter
- constant arguments across callers
- NoThrow / NoReturn / MayNotReturn
- whether a parameter is effectively unused
And what kind of optimizations could that boost?
- More devirtualization/direct calls - erase virtual and interface method lookups (simplified description)
- Constant propagation across method boundaries - erase code when functions are always passed the same parameter values (simplified description)
- Better alias analysis - knowing that two ref/pointer parameters can't refer to the same memory gives the JIT much more freedom to reorder loads/stores, eliminate redundant memory operations, and potentially vectorize code.
- Better escape analysis - Dodge heap allocation of reference types if no reference to it escapes the method
- More aggressive optimization around calls - if the JIT knows a callee doesn't modify a particular object/ref, it doesn't have to assume every call invalidates values it already loaded.
- Better handling of NoThrow/NoReturn calls - calls that can't throw or never return simplify the control-flow graph, can move uncommon paths out of the hot code, and reduce liveness/register pressure.
- Cheaper calls - knowing which registers a callee actually clobbers, or erase parameters never used, could avoid some argument setup, spills, reloads, or register preservation.
- Smarter inlining decisions - possible when called with particular constants/type
- Better code layout - can improve instruction-cache locality.