You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[wasm][r2r] Jit generates code with very large numbers of wasm locals #135139
The jit for WebAssembly generates methods with tremendous numbers of wasm locals. For instance, the System.TimeZoneInfo.CacheTransitionsForYear function has 848 declared locals, and wasm-opt is able to take that down to 9 locals. While we probably don't need to capture all of those wins, its important to capture some of them, since 848 locals causes all sorts of problems.
There are 2 risks.
If functions exceed 50,000 locals+parameters combined, most WebAssembly runtimes will refuse to compile.
If functions have a large number of locals then functions will consume excessive resources on compilation backends which do not perform optimization.
Notably, initial tier compilation generally doesn't optimize locals at all, so it requires all of the locals to actually exist and be compiled and dealt with, so that's 4,676 bytes of stack frame that the WebAssembly frame actually uses, and the wasm-opt optimized variant only requires 60 bytes. Since we are very concerned about initial process startup behavior, I'd like us to focus on optimizations which should pay off nicely for this initial tier. Additionally, V8 has implemented speculative devirtualization for indirect calls. This implementation is based on a deoptimization strategy, and so requires the ability to reconstruct the state of any non-dead locals at speculative call sites. Looking at our typical implementation strategy, it appears that many locals are dead at nearly all points in the codebase, but de-opt may still be negatively impacted by the presence of a great many locals.
Cranelift, which is relevant for Wasmtime and other standalone/wasi runtimes simply has the 50,000 local limit, and appears to optimize without any issues with large numbers of locals (when most of them are dead.)
Following up on this one as I realize I never responded to this issue. Right now, our mapping of register candidate jit local <-> wasm local is essentially 1:1 for an entire method. The bottom line is, if two JIT locals that are candidates have non-overlapping lifetimes then they can be assigned the same wasm local as a home. I would guess that we could implement some kind of scheme that looks more like traditional register allocation with LSRA, where we construct live intervals and do a scan over them. Conflicting intervals can be assigned their own locals, but we should only really need to allocate an interval to a new wasm local if there isn't a free one available. This is a big change to how the current RA system works though, so this may take a bit longer to implement.
Activity
dotnet-policy-service commentedon Oct 2, 2026
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.
dotnet-policy-service commentedon Oct 2, 2026
Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
See info in area-owners.md if you want to be subscribed.
JulieLeeMSFT commentedon Oct 2, 2026
@adamperlin, PTAL.
adamperlin commentedon Oct 16, 2026
Following up on this one as I realize I never responded to this issue. Right now, our mapping of register candidate jit local <-> wasm local is essentially 1:1 for an entire method. The bottom line is, if two JIT locals that are candidates have non-overlapping lifetimes then they can be assigned the same wasm local as a home. I would guess that we could implement some kind of scheme that looks more like traditional register allocation with LSRA, where we construct live intervals and do a scan over them. Conflicting intervals can be assigned their own locals, but we should only really need to allocate an interval to a new wasm local if there isn't a free one available. This is a big change to how the current RA system works though, so this may take a bit longer to implement.
AndyAyersMS commentedon Nov 2, 2026
Yeah I think this plus similar issues like #136662 are pointing us at implementing a better RA for Wasm.
We might first look into enabling linear scan before considering alternatives.
adamperlin commentedon Nov 6, 2026
I've been reading more about speculative inlining in V8 here: https://v8.dev/blog/wasm-speculative-optimizations; It looks like having the deoptimization approach allows for more surrounding optimizations when speculative inlining happens. Interestingly, it looks like V8 has a limit of 500 locals for doing deopt: https://github.com/v8/v8/blob/41b0e42f7f004cdf8a17d10bc085792efbf9cfd4/src/wasm/turboshaft-graph-interface.cc#L7163. I think V8 can still do speculative inlining without deopt, but this is interesting to know.