From 515b2bdabb73e4f8862c34c452bdaa933c48461a Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Tue, 6 Oct 2026 19:00:28 +0200 Subject: [PATCH 1/4] [wasm][JIT] Don't spill the shadow stack pointer arg for store interference When a call argument contains many stores (e.g. a long chain of calls sharing temps), gtMayHaveStoreInterference gives up and conservatively reports interference, which caused the wasm shadow stack pointer argument to be evaluated into a temp. For a fast tail call the register allocator expects that argument to be a GT_PHYSREG and asserted. The SP is never stored to, so skip it when marking earlier args as needing temps. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 12580074-dce3-426d-bcc2-8c83aa5a4a7d --- src/coreclr/jit/morph.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 1395f930160cc4..75f9b214b15fbd 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -807,6 +807,14 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call) continue; } +#ifdef TARGET_WASM + // The shadow stack pointer is never stored to, so it cannot interfere. + if (prevArg.GetWellKnownArg() == WellKnownArg::WasmShadowStackPointer) + { + continue; + } +#endif + if (((prevArg.GetEarlyNode()->gtFlags & GTF_ALL_EFFECT) != 0) || comp->gtMayHaveStoreInterference(argx, prevArg.GetEarlyNode())) { From d1721c47626e81b6277aad18758892573f0703a7 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Thu, 8 Oct 2026 11:29:47 +0200 Subject: [PATCH 2/4] Revert "[wasm][JIT] Don't spill the shadow stack pointer arg for store interference" The regalloc no longer assumes the SP arg is a GT_PHYSREG, so avoiding the spill is just an optimization and not needed for correctness. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 12580074-dce3-426d-bcc2-8c83aa5a4a7d --- src/coreclr/jit/regallocwasm.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/coreclr/jit/regallocwasm.cpp b/src/coreclr/jit/regallocwasm.cpp index 43c60ed1d13ac6..0deace337382cd 100644 --- a/src/coreclr/jit/regallocwasm.cpp +++ b/src/coreclr/jit/regallocwasm.cpp @@ -578,7 +578,7 @@ void WasmRegAlloc::CollectReferencesForCall(GenTreeCall* callNode) // For a fast tail call, wrap the SP arg with ADD(SP, FRAME_SIZE) so codegen // undoes the prolog's SP adjustment and the callee sees the incoming SP. - // The arg has been rewritten to GT_PHYSREG above (args are visited before the call). + // The arg is either a GT_PHYSREG (args are visited before the call) or a temp that holds the SP value. if (callNode->IsFastTailCall()) { CallArg* const spArg = callNode->gtArgs.FindWellKnownArg(WellKnownArg::WasmShadowStackPointer); @@ -586,8 +586,9 @@ void WasmRegAlloc::CollectReferencesForCall(GenTreeCall* callNode) { GenTree* const physReg = spArg->GetNode(); assert(physReg != nullptr); - assert(physReg->OperIs(GT_PHYSREG)); - assert(physReg->AsPhysReg()->gtSrcReg == m_perFuncletData[m_currentFunclet]->m_spReg); + assert(physReg->OperIs(GT_PHYSREG, GT_LCL_VAR)); + assert(!physReg->OperIs(GT_PHYSREG) || + (physReg->AsPhysReg()->gtSrcReg == m_perFuncletData[m_currentFunclet]->m_spReg)); // Fast tail calls from funclets are not supported. assert(m_currentFunclet == ROOT_FUNC_IDX); From 2b4b2a89bed5a717aabf2874ec4f2a08107eea79 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Thu, 8 Oct 2026 14:05:21 +0200 Subject: [PATCH 3/4] Revert the morph change that avoided spilling the wasm SP arg The regalloc no longer assumes the SP arg is a GT_PHYSREG, so avoiding the spill is only an optimization and not needed for correctness. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 12580074-dce3-426d-bcc2-8c83aa5a4a7d --- src/coreclr/jit/morph.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 75f9b214b15fbd..1395f930160cc4 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -807,14 +807,6 @@ void CallArgs::ArgsComplete(Compiler* comp, GenTreeCall* call) continue; } -#ifdef TARGET_WASM - // The shadow stack pointer is never stored to, so it cannot interfere. - if (prevArg.GetWellKnownArg() == WellKnownArg::WasmShadowStackPointer) - { - continue; - } -#endif - if (((prevArg.GetEarlyNode()->gtFlags & GTF_ALL_EFFECT) != 0) || comp->gtMayHaveStoreInterference(argx, prevArg.GetEarlyNode())) { From 8fc0f5c559acc26a1a60fef146ceba89448daf97 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 12 Oct 2026 11:50:27 +0200 Subject: [PATCH 4/4] Delete the SP arg asserts in WasmRegAlloc fast tail call handling Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 12580074-dce3-426d-bcc2-8c83aa5a4a7d --- src/coreclr/jit/regallocwasm.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/coreclr/jit/regallocwasm.cpp b/src/coreclr/jit/regallocwasm.cpp index 0deace337382cd..9f62e7057f5407 100644 --- a/src/coreclr/jit/regallocwasm.cpp +++ b/src/coreclr/jit/regallocwasm.cpp @@ -578,17 +578,12 @@ void WasmRegAlloc::CollectReferencesForCall(GenTreeCall* callNode) // For a fast tail call, wrap the SP arg with ADD(SP, FRAME_SIZE) so codegen // undoes the prolog's SP adjustment and the callee sees the incoming SP. - // The arg is either a GT_PHYSREG (args are visited before the call) or a temp that holds the SP value. if (callNode->IsFastTailCall()) { CallArg* const spArg = callNode->gtArgs.FindWellKnownArg(WellKnownArg::WasmShadowStackPointer); if (spArg != nullptr) { GenTree* const physReg = spArg->GetNode(); - assert(physReg != nullptr); - assert(physReg->OperIs(GT_PHYSREG, GT_LCL_VAR)); - assert(!physReg->OperIs(GT_PHYSREG) || - (physReg->AsPhysReg()->gtSrcReg == m_perFuncletData[m_currentFunclet]->m_spReg)); // Fast tail calls from funclets are not supported. assert(m_currentFunclet == ROOT_FUNC_IDX);