[Wasm RyuJIT] Spill live ref/byref values to pinned stack slots at calls#129059
[Wasm RyuJIT] Spill live ref/byref values to pinned stack slots at calls#129059kg wants to merge 1 commit into
Conversation
…the GC won't move them
|
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. |
| if (defs.size()) | ||
| { | ||
| JITDUMP("Spilling %d live ref(s) for call\n", defs.size()); | ||
| DISPNODE(tree); |
| JITDUMP("High water mark for refs was %d\n", highWaterMark); | ||
| if (highWaterMark == 0) | ||
| return PhaseStatus::MODIFIED_NOTHING; | ||
|
|
| 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); | ||
| } |
| 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; |
| // 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; | ||
| } |
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.
| return GenTree::VisitResult::Continue; | ||
| }); | ||
|
|
||
| if (tree->IsValue() && tree->TypeIs(TYP_REF, TYP_BYREF) && !tree->OperIs(GT_WASM_SPILL_REF)) |
There was a problem hiding this comment.
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--) |
There was a problem hiding this comment.
Probably worth commenting what this is doing (removing active defs once we find their use, and keeping the defs collection compact).
No description provided.