Skip to content

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

Draft
kg wants to merge 1 commit into
dotnet:mainfrom
kg:wasm-spill-refs
Draft

[Wasm RyuJIT] Spill live ref/byref values to pinned stack slots at calls#129059
kg wants to merge 1 commit into
dotnet:mainfrom
kg:wasm-spill-refs

Conversation

@kg
Copy link
Copy Markdown
Member

@kg kg commented Jun 5, 2026

No description provided.

Copilot AI review requested due to automatic review settings June 5, 2026 22:58
@kg kg 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.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

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
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.

Comment on lines +1670 to +1673
if (defs.size())
{
JITDUMP("Spilling %d live ref(s) for call\n", defs.size());
DISPNODE(tree);
Comment on lines +1724 to +1727
JITDUMP("High water mark for refs was %d\n", highWaterMark);
if (highWaterMark == 0)
return PhaseStatus::MODIFIED_NOTHING;

Comment on lines +1715 to +1720
if (tree->IsValue() && tree->TypeIs(TYP_REF, TYP_BYREF) && !tree->OperIs(GT_WASM_SPILL_REF))
{
// TODO: Can we skip this for GT_LCL_VAR when it lives in memory? Or is it possible
// that the LCL_VAR has been modified since it was loaded onto the Wasm stack?
defs.push_back(tree);
}
Comment on lines +1736 to +1740
varDsc->lvHasExplicitInit = true;
varDsc->lvImplicitlyReferenced = true;
// If we don't make this var tracked, regalloc will crash when allocating a register for it
varDsc->lvTracked = true;
m_wasmSpillSlots->at(0) = varNum;
Comment on lines +193 to +197
// HACK: Ensure that we always enregister the splash zone, even if we are not enregistering other locals
if (m_compiler->m_wasmSpillSlots && m_compiler->m_wasmSpillSlots->size() && m_compiler->m_wasmSpillSlots->at(0) == lclNum)
{
varIsRegCandidate = true;
}
Copy link
Copy Markdown
Member

@AndyAyersMS AndyAyersMS left a comment

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.

return GenTree::VisitResult::Continue;
});

if (tree->IsValue() && tree->TypeIs(TYP_REF, TYP_BYREF) && !tree->OperIs(GT_WASM_SPILL_REF))
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.

Check for unused values here?

if (!op->TypeIs(TYP_REF, TYP_BYREF))
return GenTree::VisitResult::Continue;

for (size_t i = defs.size(); i > 0; i--)
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.

Probably worth commenting what this is doing (removing active defs once we find their use, and keeping the defs collection compact).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-CodeGen-coreclr CLR 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.

3 participants