Uh oh!
There was an error while loading. Please reload this page.
[Wasm RyuJIT] Spill live ref/byref values to pinned stack slots at calls - #129059
Conversation
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
There was a problem hiding this comment.
Pull request overview
This PR introduces a Wasm-specific mechanism intended to make ref/byref values GC-visible across call sites by injecting a new IR node (GT_WASM_SPILL_REF) and a new Wasm phase (WasmSpillRefs) that inserts these nodes and allocates pinned stack spill slots used during Wasm codegen.
Changes:
- Add
GT_WASM_SPILL_REFnode kind and operand iteration support. - Add
Compiler::WasmSpillRefsphase to insert spill nodes around calls and allocate spill locals. - Extend Wasm codegen/regalloc to track a spill index and (temporarily) force-enregister a scratch “splash zone” local.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/coreclr/jit/regallocwasm.cpp | Forces the “splash zone” local to be treated as a reg candidate. |
| src/coreclr/jit/gtlist.h | Adds new Wasm node WASM_SPILL_REF. |
| src/coreclr/jit/gentree.cpp | Updates operand-edge iterator to treat GT_WASM_SPILL_REF as unary. |
| src/coreclr/jit/fgwasm.cpp | Implements Compiler::WasmSpillRefs to insert spill nodes and allocate spill locals. |
| src/coreclr/jit/compphases.h | Adds PHASE_WASM_SPILL_REFS. |
| src/coreclr/jit/compmemkind.h | Adds WasmSpillRefs memory kind. |
| src/coreclr/jit/compiler.h | Adds m_wasmSpillSlots field and WasmSpillRefs declaration. |
| src/coreclr/jit/compiler.cpp | Wires WasmSpillRefs into the Wasm compilation pipeline. |
| src/coreclr/jit/codegenwasm.cpp | Emits Wasm for GT_WASM_SPILL_REF and resets spill index at calls. |
| src/coreclr/jit/codegenlinear.cpp | Resets spill index at block boundaries on Wasm. |
| src/coreclr/jit/codegen.h | Adds wasmSpillRefIndex state. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
AndyAyersMS
left a comment
There was a problem hiding this comment.
Generally looks good.
I'm curious how this intersects/overlaps with LSRA's spill temp mechanism. Not saying we should use that here, but it likely serves a similar purpose.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
kg
commented
Jun 8, 2026
Still working on this, going to try moving to STORE_LCL_VAR/LCL_VAR like Jakob suggested. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
kg
commented
Jun 8, 2026
Updated to use STORE_LCL_VAR and LCL_VAR instead, removing the splash zone var. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
kg
commented
Jun 9, 2026
I had to come up with a different test program to get failures without this PR's changes - the previous test program was only failing because of some other bugs that AndyA has since fixed. publicclassC{publicstringS="";publicbyte[]?Trash;[MethodImpl(MethodImplOptions.NoInlining)]publicvoidAppendToS(stringvalue){Trash=newbyte[32768];S+=value;}}[MethodImpl(MethodImplOptions.NoInlining)]publicstaticintMain(){Console.WriteLine("Hello world!");strings="";for(inti=0;i<1000;i++){Cc=newC();for(intj=0;j<2000;j++)c.AppendToS(j.ToString());s=c.S;}Console.WriteLine(s);return42;}With the spilling pass disabled this asserts inside the GC during execution, i.e. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
AndyAyersMS
left a comment
There was a problem hiding this comment.
Think this is close, though we probably still need slot zeroing.
Another option would be to try and report these slots as tracked, since we know their lifetimes, but that can happen later.
Interested to see the code size impact.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
AndyAyersMS
left a comment
There was a problem hiding this comment.
LGTM
@jakobbotsch you want to take another look?
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
jakobbotsch
left a comment
There was a problem hiding this comment.
In general I would have preferred to keep the slot reuse and pinning optimization separate if a simpler "just create a new local for the LIR edge" would have sufficed for correctness.
That aside this looks good to me, I left mainly nits as comments. Feel free to ignore them.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Resolve conflict in compiler.h: keep WasmSpillSlot struct added by this branch alongside the blank line added on main. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
AndyAyersMS
commented
Jun 16, 2026
Had to fix a merge conflict. |
AndyAyersMS
commented
Jun 16, 2026
@jakobbotsch can you re-approve (new repo policy) |
AndyAyersMS
commented
Jun 16, 2026
/ba-g persistent timeouts in some arm64 legs |
Uh oh!
There was an error while loading. Please reload this page.
…lls (#129059) GC refs/byrefs can end up temporarily living on the Wasm shadow stack - where the GC can't see them - across calls, which creates a GC hole. This PR adds a pass that 'spills' these live refs/byrefs to dedicated pinned locals so that the GC won't move or collect those refs. --------- Co-authored-by: Andy Ayers <andya@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
GC refs/byrefs can end up temporarily living on the Wasm shadow stack - where the GC can't see them - across calls, which creates a GC hole. This PR adds a pass that 'spills' these live refs/byrefs to dedicated pinned locals so that the GC won't move or collect those refs.