Skip to content

[Wasm RyuJIT] Spill live ref/byref values to pinned stack slots at calls - #129059

Merged
AndyAyersMS merged 25 commits into
dotnet:mainfrom
kg:wasm-spill-refs
Jun 16, 2026
Merged

[Wasm RyuJIT] Spill live ref/byref values to pinned stack slots at calls#129059
AndyAyersMS merged 25 commits into
dotnet:mainfrom
kg:wasm-spill-refs

Conversation

@kg

@kgkg commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

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.

CopilotAI review requested due to automatic review settings June 5, 2026 22:58
@kgkg added arch-wasm WebAssembly architecture area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI labels Jun 5, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_REF node kind and operand iteration support.
  • Add Compiler::WasmSpillRefs phase 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
FileDescription
src/coreclr/jit/regallocwasm.cppForces the “splash zone” local to be treated as a reg candidate.
src/coreclr/jit/gtlist.hAdds new Wasm node WASM_SPILL_REF.
src/coreclr/jit/gentree.cppUpdates operand-edge iterator to treat GT_WASM_SPILL_REF as unary.
src/coreclr/jit/fgwasm.cppImplements Compiler::WasmSpillRefs to insert spill nodes and allocate spill locals.
src/coreclr/jit/compphases.hAdds PHASE_WASM_SPILL_REFS.
src/coreclr/jit/compmemkind.hAdds WasmSpillRefs memory kind.
src/coreclr/jit/compiler.hAdds m_wasmSpillSlots field and WasmSpillRefs declaration.
src/coreclr/jit/compiler.cppWires WasmSpillRefs into the Wasm compilation pipeline.
src/coreclr/jit/codegenwasm.cppEmits Wasm for GT_WASM_SPILL_REF and resets spill index at calls.
src/coreclr/jit/codegenlinear.cppResets spill index at block boundaries on Wasm.
src/coreclr/jit/codegen.hAdds wasmSpillRefIndex state.

Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/regallocwasm.cpp Outdated

@AndyAyersMSAndyAyersMS left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/fgwasm.cpp
Comment threadsrc/coreclr/jit/gtlist.h Outdated
Comment threadsrc/coreclr/jit/regallocwasm.cpp Outdated
Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
@kg
kgforce-pushed the wasm-spill-refs branch from b614c8f to 9a2f3daCompareJune 7, 2026 17:07
CopilotAI review requested due to automatic review settings June 8, 2026 18:24
@kg

kg commented Jun 8, 2026

Copy link
Copy Markdown
ContributorAuthor

Still working on this, going to try moving to STORE_LCL_VAR/LCL_VAR like Jakob suggested.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/codegenwasm.cpp Outdated
@kg

kg commented Jun 8, 2026

Copy link
Copy Markdown
ContributorAuthor

Updated to use STORE_LCL_VAR and LCL_VAR instead, removing the splash zone var.

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/fgwasm.cpp
Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
@kg

kg commented Jun 9, 2026

Copy link
Copy Markdown
ContributorAuthor

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.

Hello world!
ASSERT FAILED
Expression: xl <= end
Location: Z:/runtime/src/coreclr/gc\plan_phase.cpp:3194
Function: find_next_marked
Process: 42
Frame (DynamicHelperFrame): 0x4fc938
Skipping 0x4fc938
Frame (InterpreterFrame): 0x4fdeb0
0) System.String::FastAllocateString, IR_001d
1) System.String::Concat, IR_007f
Frame (InterpreterFrame): 0x4ff360
2) System.Environment::CallEntryPoint, IR_0097

CopilotAI review requested due to automatic review settings June 9, 2026 18:42

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
@kg
kg marked this pull request as ready for review June 12, 2026 17:30

@AndyAyersMSAndyAyersMS left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated

@AndyAyersMSAndyAyersMS left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jakobbotsch you want to take another look?

Comment threadsrc/coreclr/jit/fgwasm.cpp
Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/fgwasm.cpp

@jakobbotschjakobbotsch left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
Comment threadsrc/coreclr/jit/fgwasm.cpp Outdated
kgand others added 3 commits June 15, 2026 16:40
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

Copy link
Copy Markdown
Member

Had to fix a merge conflict.

@AndyAyersMS

Copy link
Copy Markdown
Member

@jakobbotsch can you re-approve (new repo policy)

@AndyAyersMS

Copy link
Copy Markdown
Member

/ba-g persistent timeouts in some arm64 legs

@AndyAyersMS
AndyAyersMS merged commit c3e1a29 into dotnet:mainJun 16, 2026
135 of 139 checks passed
@dotnet-milestone-botdotnet-milestone-botBot added this to the 11.0-preview6 milestone Jun 17, 2026
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
…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>
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Jul 18, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

arch-wasmWebAssembly architecturearea-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@kg@AndyAyersMS@jakobbotsch