Skip to content

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

Draft
kg wants to merge 6 commits 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 6 commits into
dotnet:mainfrom
kg:wasm-spill-refs

Conversation

@kg

@kg kg commented Jun 5, 2026

Copy link
Copy Markdown
Member

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.

Copilot AI 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
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 thread src/coreclr/jit/fgwasm.cpp
Comment thread src/coreclr/jit/fgwasm.cpp Outdated
Comment thread src/coreclr/jit/fgwasm.cpp Outdated
Comment thread src/coreclr/jit/fgwasm.cpp Outdated
Comment thread src/coreclr/jit/regallocwasm.cpp Outdated

@AndyAyersMS AndyAyersMS 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 thread src/coreclr/jit/fgwasm.cpp Outdated
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).

Comment thread src/coreclr/jit/gtlist.h Outdated
Comment thread src/coreclr/jit/regallocwasm.cpp Outdated
Comment thread src/coreclr/jit/fgwasm.cpp Outdated
@kg kg force-pushed the wasm-spill-refs branch from b614c8f to 9a2f3da Compare June 7, 2026 17:07
Copilot AI review requested due to automatic review settings June 8, 2026 18:24
@kg

kg commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

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

Copilot AI 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 thread src/coreclr/jit/fgwasm.cpp Outdated
Comment on lines +1796 to +1800
varDsc->lvType = TYP_BYREF;
varDsc->lvPinned = true;
varDsc->lvImplicitlyReferenced = true;
varDsc->lvMustInit = true;
lvaSetVarDoNotEnregister(varNum, DoNotEnregisterReason::WasmGCVisibility);

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's true that this could be a problem, but I don't know if we need to do anything about it other than add a TODO-WASM comment for now

Comment thread src/coreclr/jit/fgwasm.cpp Outdated
Comment on lines +1702 to +1713
GenTreeUnOp* spill = gtNewOperNode(GT_WASM_SPILL_REF, def->TypeGet(), def);
LIR::Use use;
noway_assert(LIR::AsRange(block).TryGetUse(def, &use));
use.ReplaceWith(spill);
LIR::AsRange(block).InsertAfter(def, spill);
if (def->gtLIRFlags & LIR::Flags::MultiplyUsed)
{
JITDUMP("Transferring multiply-used flag from [%06u] to [%06u] for spill\n", Compiler::dspTreeID(def), Compiler::dspTreeID(spill));
def->gtLIRFlags &= ~LIR::Flags::MultiplyUsed;
spill->gtLIRFlags |= LIR::Flags::MultiplyUsed;
}
anyChanges = true;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Is this the right way to do it? @AndyAyersMS @jakobbotsch

Comment thread src/coreclr/jit/fgwasm.cpp Outdated
Comment thread src/coreclr/jit/codegenwasm.cpp Outdated
@kg

kg commented Jun 8, 2026

Copy link
Copy Markdown
Member Author

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

Copilot AI 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 on lines +1729 to +1741
GenTreeLclVar *spill = gtNewStoreLclVarNode(spillSlot, def);
GenTreeLclVar *reload = gtNewLclVarNode(spillSlot);
LIR::Use use;
noway_assert(LIR::AsRange(block).TryGetUse(def, &use));
use.ReplaceWith(reload);
LIR::AsRange(block).InsertAfter(def, spill);
LIR::AsRange(block).InsertAfter(spill, reload);
if (def->gtLIRFlags & LIR::Flags::MultiplyUsed)
{
JITDUMP("Transferring multiply-used flag from [%06u] to [%06u] for spill\n", Compiler::dspTreeID(def), Compiler::dspTreeID(reload));
def->gtLIRFlags &= ~LIR::Flags::MultiplyUsed;
reload->gtLIRFlags |= LIR::Flags::MultiplyUsed;
}
Comment on lines +1719 to +1723
LclVarDsc* const varDsc = lvaGetDesc(spillSlot);
varDsc->lvType = TYP_BYREF;
varDsc->lvPinned = true;
varDsc->lvImplicitlyReferenced = true;
varDsc->lvMustInit = true;

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Isn't it fine to store a REF in a pinned BYREF? Or no?

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.

Storing is fine, it's the reloading that is a bit iffy, we have to trust the thing stored there was really a ref. In your case we know this but the IR doesn't.

I would probably just keep two pools for now like copilot suggests. Possibly reporting refs as refs is a bit cheaper for GC.

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.

4 participants