From 2de1151002758ad1f8900e28cf7715d602242125 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 29 Jun 2026 13:21:26 -0700 Subject: [PATCH 1/2] wasm: report the generic context to the GC stack walk Spill the generic context to its GC slot in the prolog, and populate the frame pointer in TransitionFrame::UpdateRegDisplay so the stack walk can locate it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/jit/codegencommon.cpp | 4 +++- src/coreclr/jit/codegenwasm.cpp | 31 +++++++++++++++++++++++++++++++ src/coreclr/vm/wasm/helpers.cpp | 4 ++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 3e5f54cb2423f4..b93a07677c19d3 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -5740,9 +5740,11 @@ void CodeGen::genFnProlog() genZeroInitFrame(untrLclHi, untrLclLo, initReg, &initRegZeroed); -#ifndef TARGET_WASM // TODO-WASM: enable as needed. + // Save the generic context arg in the prolog so GetParamTypeArg can report it. genReportGenericContextArg(initReg, &initRegZeroed); +#ifndef TARGET_WASM // TODO-WASM: enable as needed. + #ifdef JIT32_GCENCODER // Initialize the LocalAllocSP slot if there is localloc in the function. if (m_compiler->lvaLocAllocSPvar != BAD_VAR_NUM) diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index 45b432c2a96537..2dc4f79bbeddf0 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -352,6 +352,37 @@ void CodeGen::genHomeRegisterParamsOutsideProlog() } } +//------------------------------------------------------------------------ +// genReportGenericContextArg: spill the generic context (or "this") into its +// GC-reportable frame slot in the prolog so the GC stack walk can locate it. +// initReg / pInitRegZeroed are unused on wasm. +// +void CodeGen::genReportGenericContextArg(regNumber initReg, bool* pInitRegZeroed) +{ + assert(m_compiler->compGeneratingProlog); + + const bool reportArg = m_compiler->lvaReportParamTypeArg(); + const bool reportThis = m_compiler->lvaKeepAliveAndReportThis(); + if (!reportArg && !reportThis) + { + return; + } + + const unsigned contextArg = reportArg ? m_compiler->info.compTypeCtxtArg : m_compiler->info.compThisArg; + noway_assert(contextArg != BAD_VAR_NUM); + + // The context arg is still in its incoming register at this point in the prolog. + const ABIPassingInformation& abiInfo = m_compiler->lvaGetParameterABIInfo(contextArg); + assert(abiInfo.HasExactlyOneRegisterSegment()); + const regNumber reg = abiInfo.Segment(0).GetRegister(); + + // [FP + lvaCachedGenericContextArgOffset()] = contextArg + emitter* emit = GetEmitter(); + emit->emitIns_I(INS_local_get, EA_PTRSIZE, GetFramePointerRegIndex()); + emit->emitIns_I(INS_local_get, EA_PTRSIZE, WasmRegToIndex(reg)); + emit->emitIns_I(ins_Store(TYP_I_IMPL), EA_PTRSIZE, m_compiler->lvaCachedGenericContextArgOffset()); +} + void CodeGen::genFnEpilog(BasicBlock* block) { #ifdef DEBUG diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp index 8abf0c736818f2..78050a8631d811 100644 --- a/src/coreclr/vm/wasm/helpers.cpp +++ b/src/coreclr/vm/wasm/helpers.cpp @@ -520,12 +520,16 @@ void FaultingExceptionFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool u PORTABILITY_ASSERT("FaultingExceptionFrame::UpdateRegDisplay_Impl is not implemented on wasm"); } +TADDR GetWasmFramePointerFromStackPointer(TADDR sp); + void TransitionFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats) { pRD->IsCallerContextValid = FALSE; pRD->pCurrentContext->InterpreterIP = GetReturnAddress(); pRD->pCurrentContext->InterpreterSP = GetSP(); + // Recover the frame pointer so GC-info readers can locate frame slots. + pRD->pCurrentContext->InterpreterFP = GetWasmFramePointerFromStackPointer(GetSP()); SyncRegDisplayToCurrentContext(pRD); From 185e153bfe8913e87b95963b4f4d63ffe9e50225 Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Mon, 29 Jun 2026 18:31:22 -0700 Subject: [PATCH 2/2] wasm: drop the runtime change, keep JIT-only The TransitionFrame fp fix will go in a separate change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/vm/wasm/helpers.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/coreclr/vm/wasm/helpers.cpp b/src/coreclr/vm/wasm/helpers.cpp index 78050a8631d811..8abf0c736818f2 100644 --- a/src/coreclr/vm/wasm/helpers.cpp +++ b/src/coreclr/vm/wasm/helpers.cpp @@ -520,16 +520,12 @@ void FaultingExceptionFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool u PORTABILITY_ASSERT("FaultingExceptionFrame::UpdateRegDisplay_Impl is not implemented on wasm"); } -TADDR GetWasmFramePointerFromStackPointer(TADDR sp); - void TransitionFrame::UpdateRegDisplay_Impl(const PREGDISPLAY pRD, bool updateFloats) { pRD->IsCallerContextValid = FALSE; pRD->pCurrentContext->InterpreterIP = GetReturnAddress(); pRD->pCurrentContext->InterpreterSP = GetSP(); - // Recover the frame pointer so GC-info readers can locate frame slots. - pRD->pCurrentContext->InterpreterFP = GetWasmFramePointerFromStackPointer(GetSP()); SyncRegDisplayToCurrentContext(pRD);