From 99fdef64146e3b234228fbee2e337bebd4c24af3 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Tue, 14 Apr 2026 19:38:45 -0700 Subject: [PATCH 1/2] Checkpoint gcinfo wire-up Checkpoint Checkpoint Comment out some overly sensitive asserts that can be hit when things are working as designed Funclet start blocks need labels on Wasm for some reason. Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Cleanup jit-format Compile the gc info decoder too, since we'll need it Add comment --- src/coreclr/gcinfo/CMakeLists.txt | 1 + src/coreclr/gcinfo/gcinfoencoder.cpp | 2 - src/coreclr/inc/gcinfotypes.h | 51 ++++++++++++++++++- src/coreclr/jit/CMakeLists.txt | 4 +- src/coreclr/jit/codegencommon.cpp | 18 ++++--- src/coreclr/jit/codegenlinear.cpp | 20 +++++--- src/coreclr/jit/codegenwasm.cpp | 37 +++++++++++++- src/coreclr/jit/emit.cpp | 16 +++--- src/coreclr/jit/gcencode.cpp | 2 + src/coreclr/jit/gcinfo.cpp | 4 +- src/coreclr/jit/targetwasm.h | 2 +- .../JitInterface/CorInfoImpl.ReadyToRun.cs | 11 +++- src/coreclr/vm/eetwain.cpp | 5 +- src/coreclr/vm/gcinfodecoder.cpp | 2 - 14 files changed, 140 insertions(+), 35 deletions(-) diff --git a/src/coreclr/gcinfo/CMakeLists.txt b/src/coreclr/gcinfo/CMakeLists.txt index a652648f32a689..05521608a09a73 100644 --- a/src/coreclr/gcinfo/CMakeLists.txt +++ b/src/coreclr/gcinfo/CMakeLists.txt @@ -66,6 +66,7 @@ endif() if (CLR_CMAKE_TARGET_ARCH_ARM64 OR CLR_CMAKE_TARGET_ARCH_AMD64) create_gcinfo_lib(TARGET gcinfo_universal_arm64 OS universal ARCH arm64) create_gcinfo_lib(TARGET gcinfo_unix_x64 OS unix ARCH x64) + create_gcinfo_lib(TARGET gcinfo_universal_wasm OS universal ARCH wasm) if (CLR_CMAKE_BUILD_COMMUNITY_ALTJITS EQUAL 1) create_gcinfo_lib(TARGET gcinfo_unix_loongarch64 OS unix ARCH loongarch64) create_gcinfo_lib(TARGET gcinfo_unix_riscv64 OS unix ARCH riscv64) diff --git a/src/coreclr/gcinfo/gcinfoencoder.cpp b/src/coreclr/gcinfo/gcinfoencoder.cpp index e877fb534cb163..2a0636e11dbb7e 100644 --- a/src/coreclr/gcinfo/gcinfoencoder.cpp +++ b/src/coreclr/gcinfo/gcinfoencoder.cpp @@ -2634,10 +2634,8 @@ int BitStreamWriter::EncodeVarLengthSigned( SSIZE_T n, UINT32 base ) } } -#ifndef TARGET_WASM // Instantiate the encoder so other files can use it template class TGcInfoEncoder; -#endif // !TARGET_WASM #ifdef FEATURE_INTERPRETER template class TGcInfoEncoder; diff --git a/src/coreclr/inc/gcinfotypes.h b/src/coreclr/inc/gcinfotypes.h index dc56c94477db0e..68aa15eae549df 100644 --- a/src/coreclr/inc/gcinfotypes.h +++ b/src/coreclr/inc/gcinfotypes.h @@ -909,13 +909,60 @@ struct X86GcInfoEncoding { static const bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA = true; }; -#elif defined(TARGET_WASM) +#elif defined(TARGET_WASM) && !defined(TARGET_64BIT) #ifndef TARGET_POINTER_SIZE #define TARGET_POINTER_SIZE 4 // equal to sizeof(void*) and the managed pointer size in bytes for this target #endif -#define TargetGcInfoEncoding InterpreterGcInfoEncoding +#define TargetGcInfoEncoding Wasm32GcInfoEncoding + +struct Wasm32GcInfoEncoding { + static const uint32_t NUM_NORM_CODE_OFFSETS_PER_CHUNK = (64); + static const uint32_t NUM_NORM_CODE_OFFSETS_PER_CHUNK_LOG2 = (6); + static inline constexpr int32_t NORMALIZE_STACK_SLOT (int32_t x) { return (x); } + static inline constexpr int32_t DENORMALIZE_STACK_SLOT (int32_t x) { return (x); } + static inline constexpr uint32_t NORMALIZE_CODE_LENGTH (uint32_t x) { return (x); } + static inline constexpr uint32_t DENORMALIZE_CODE_LENGTH (uint32_t x) { return (x); } + static inline constexpr uint32_t NORMALIZE_STACK_BASE_REGISTER (uint32_t x) { return (x); } + static inline constexpr uint32_t DENORMALIZE_STACK_BASE_REGISTER (uint32_t x) { return (x); } + static inline constexpr uint32_t NORMALIZE_SIZE_OF_STACK_AREA (uint32_t x) { return (x); } + static inline constexpr uint32_t DENORMALIZE_SIZE_OF_STACK_AREA (uint32_t x) { return (x); } + static const bool CODE_OFFSETS_NEED_NORMALIZATION = false; + static inline constexpr uint32_t NORMALIZE_CODE_OFFSET (uint32_t x) { return (x); } + static inline constexpr uint32_t DENORMALIZE_CODE_OFFSET (uint32_t x) { return (x); } + + static const int PSP_SYM_STACK_SLOT_ENCBASE = 6; + static const int GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE = 6; + static const int SECURITY_OBJECT_STACK_SLOT_ENCBASE = 6; + static const int GS_COOKIE_STACK_SLOT_ENCBASE = 6; + static const int CODE_LENGTH_ENCBASE = 6; + static const int SIZE_OF_RETURN_KIND_IN_SLIM_HEADER = 2; + static const int SIZE_OF_RETURN_KIND_IN_FAT_HEADER = 2; + static const int STACK_BASE_REGISTER_ENCBASE = 3; + static const int SIZE_OF_STACK_AREA_ENCBASE = 6; + static const int SIZE_OF_EDIT_AND_CONTINUE_PRESERVED_AREA_ENCBASE = 3; + static const int REVERSE_PINVOKE_FRAME_ENCBASE = 6; + static const int NUM_REGISTERS_ENCBASE = 3; + static const int NUM_STACK_SLOTS_ENCBASE = 5; + static const int NUM_UNTRACKED_SLOTS_ENCBASE = 5; + static const int NORM_PROLOG_SIZE_ENCBASE = 4; + static const int NORM_EPILOG_SIZE_ENCBASE = 3; + static const int NORM_CODE_OFFSET_DELTA_ENCBASE = 3; + static const int INTERRUPTIBLE_RANGE_DELTA1_ENCBASE = 5; + static const int INTERRUPTIBLE_RANGE_DELTA2_ENCBASE = 5; + static const int REGISTER_ENCBASE = 3; + static const int REGISTER_DELTA_ENCBASE = REGISTER_ENCBASE; + static const int STACK_SLOT_ENCBASE = 6; + static const int STACK_SLOT_DELTA_ENCBASE = 4; + static const int NUM_SAFE_POINTS_ENCBASE = 4; + static const int NUM_INTERRUPTIBLE_RANGES_ENCBASE = 1; + static const int NUM_EH_CLAUSES_ENCBASE = 2; + static const int POINTER_SIZE_ENCBASE = 3; + static const int LIVESTATE_RLE_RUN_ENCBASE = 2; + static const int LIVESTATE_RLE_SKIP_ENCBASE = 4; + static const bool HAS_FIXED_STACK_PARAMETER_SCRATCH_AREA = false; +}; #else // No target defined diff --git a/src/coreclr/jit/CMakeLists.txt b/src/coreclr/jit/CMakeLists.txt index e7e69887486830..fd88b263fd5946 100644 --- a/src/coreclr/jit/CMakeLists.txt +++ b/src/coreclr/jit/CMakeLists.txt @@ -23,7 +23,7 @@ function(create_standalone_jit) if(TARGETDETAILS_OS STREQUAL "unix_osx" OR TARGETDETAILS_OS STREQUAL "unix_anyos") set(JIT_ARCH_LINK_LIBRARIES gcinfo_unix_${TARGETDETAILS_ARCH}) - elseif(NOT ${TARGETDETAILS_ARCH} MATCHES "wasm") + else() set(JIT_ARCH_LINK_LIBRARIES gcinfo_${TARGETDETAILS_OS}_${TARGETDETAILS_ARCH}) endif() @@ -292,6 +292,8 @@ set( JIT_RISCV64_SOURCES ) set( JIT_WASM_SOURCES + gcdecode.cpp + gcencode.cpp codegenwasm.cpp emitwasm.cpp fgwasm.cpp diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index d7dc1e38aedfd6..9da4c8a1e9286a 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -854,7 +854,9 @@ bool CodeGen::genIsSameLocalVar(GenTree* op1, GenTree* op2) // inline void CodeGenInterface::genUpdateRegLife(const LclVarDsc* varDsc, bool isBorn, bool isDying DEBUGARG(GenTree* tree)) { -#if EMIT_GENERATE_GCINFO // The regset being updated here is only needed for codegen-level GCness tracking +#if EMIT_GENERATE_GCINFO && !defined(TARGET_WASM) + // The regset being updated here is only needed for codegen-level GCness tracking, + // and Wasm does not have registers regMaskTP regMask = genGetRegMask(varDsc); #ifdef DEBUG @@ -884,7 +886,7 @@ void CodeGenInterface::genUpdateRegLife(const LclVarDsc* varDsc, bool isBorn, bo assert(varDsc->IsAlwaysAliveInMemory() || ((regSet.GetMaskVars() & regMask) == 0)); regSet.AddMaskVars(regMask); } -#endif // EMIT_GENERATE_GCINFO +#endif // EMIT_GENERATE_GCINFO && !TARGET_WASM } #ifndef TARGET_WASM @@ -1032,6 +1034,7 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife) bool isByRef = varDsc->TypeIs(TYP_BYREF); bool isInReg = varDsc->lvIsInReg(); bool isInMemory = !isInReg || varDsc->IsAlwaysAliveInMemory(); +#ifndef TARGET_WASM if (isInReg) { // TODO-Cleanup: Move the code from compUpdateLifeVar to genUpdateRegLife that updates the @@ -1047,7 +1050,8 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife) } codeGen->genUpdateRegLife(varDsc, false /*isBorn*/, true /*isDying*/ DEBUGARG(nullptr)); } - // Update the gcVarPtrSetCur if it is in memory. +#endif // !TARGET_WASM + // Update the gcVarPtrSetCur if it is in memory. if (isInMemory && (isGCRef || isByRef)) { VarSetOps::RemoveElemD(this, codeGen->gcInfo.gcVarPtrSetCur, deadVarIndex); @@ -1070,6 +1074,7 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife) bool isByRef = varDsc->TypeIs(TYP_BYREF); if (varDsc->lvIsInReg()) { +#ifndef TARGET_WASM // If this variable is going live in a register, it is no longer live on the stack, // unless it is an EH/"spill at single-def" var, which always remains live on the stack. if (!varDsc->IsAlwaysAliveInMemory()) @@ -1092,6 +1097,7 @@ void Compiler::compChangeLife(VARSET_VALARG_TP newLife) { codeGen->gcInfo.gcRegByrefSetCur |= regMask; } +#endif // !TARGET_WASM } else if (lvaIsGCTracked(varDsc)) { @@ -1769,7 +1775,7 @@ void CodeGen::genExitCode(BasicBlock* block) genIPmappingAdd(IPmappingDscKind::Epilog, DebugInfo(), true); -#if EMIT_GENERATE_GCINFO && defined(DEBUG) +#if EMIT_GENERATE_GCINFO && defined(DEBUG) && !defined(TARGET_WASM) // For returnining epilogs do some validation that the GC info looks right. if (!block->HasFlag(BBF_HAS_JMP)) { @@ -1791,7 +1797,7 @@ void CodeGen::genExitCode(BasicBlock* block) } } } -#endif // EMIT_GENERATE_GCINFO && defined(DEBUG) +#endif // EMIT_GENERATE_GCINFO && defined(DEBUG) && !defined(TARGET_WASM) if (m_compiler->getNeedsGSSecurityCookie()) { @@ -7210,7 +7216,7 @@ void CodeGen::genReturn(GenTree* treeNode) } } -#if EMIT_GENERATE_GCINFO +#if EMIT_GENERATE_GCINFO && !defined(TARGET_WASM) if (treeNode->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET)) { genMarkReturnGCInfo(); diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 5386aa65a5d74b..a5a0bdf982b46b 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -259,7 +259,7 @@ void CodeGen::genCodeForBlock(BasicBlock* block) // and before first of the current block is emitted genUpdateLife(block->bbLiveIn); -#if EMIT_GENERATE_GCINFO +#if EMIT_GENERATE_GCINFO && !defined(TARGET_WASM) // Even if liveness didn't change, we need to update the registers containing GC references. // genUpdateLife will update the registers live due to liveness changes. But what about registers that didn't // change? We cleared them out above. Maybe we should just not clear them out, but update the ones that change @@ -353,7 +353,7 @@ void CodeGen::genCodeForBlock(BasicBlock* block) } } } -#endif // EMIT_GENERATE_GCINFO +#endif // EMIT_GENERATE_GCINFO && !TARGET_WASM /* Start a new code output block */ @@ -405,6 +405,14 @@ void CodeGen::genCodeForBlock(BasicBlock* block) } #endif +#ifdef TARGET_WASM + // FIXME-WASM: Why is this only necessary on Wasm? + if (m_compiler->bbIsFuncletBeg(block)) + { + needLabel = true; + } +#endif + if (needLabel) { // Mark a label and update the current set of live GC refs @@ -569,7 +577,7 @@ void CodeGen::genCodeForBlock(BasicBlock* block) regSet.rsSpillChk(); -#if EMIT_GENERATE_GCINFO +#if EMIT_GENERATE_GCINFO && !defined(TARGET_WASM) // Make sure we didn't bungle pointer register tracking regMaskTP ptrRegs = gcInfo.gcRegGCrefSetCur | gcInfo.gcRegByrefSetCur; regMaskTP nonVarPtrRegs = ptrRegs & ~regSet.GetMaskVars(); @@ -618,7 +626,7 @@ void CodeGen::genCodeForBlock(BasicBlock* block) } noway_assert(nonVarPtrRegs == RBM_NONE); -#endif // EMIT_GENERATE_GCINFO +#endif // EMIT_GENERATE_GCINFO && !TARGET_WASM #endif // DEBUG #if defined(DEBUG) @@ -1601,7 +1609,7 @@ regNumber CodeGen::genConsumeReg(GenTree* tree) // genUpdateLife() will also spill local var if marked as GTF_SPILL by calling CodeGen::genSpillVar genUpdateLife(tree); -#if EMIT_GENERATE_GCINFO +#if EMIT_GENERATE_GCINFO && !defined(TARGET_WASM) // there are three cases where consuming a reg means clearing the bit in the live mask // 1. it was not produced by a local // 2. it was produced by a local that is going dead @@ -1659,7 +1667,7 @@ regNumber CodeGen::genConsumeReg(GenTree* tree) { gcInfo.gcMarkRegSetNpt(tree->gtGetRegMask()); } -#endif // EMIT_GENERATE_GCINFO +#endif // EMIT_GENERATE_GCINFO && !TARGET_WASM genCheckConsumeNode(tree); return tree->GetRegNum(); diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index a9d5308d63ee41..0c7a08475637ef 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -9,6 +9,8 @@ #include "codegen.h" #include "regallocwasm.h" #include "fgwasm.h" +#include "gcinfo.h" +#include "gcinfoencoder.h" static const int LINEAR_MEMORY_INDEX = 0; @@ -3366,7 +3368,40 @@ void CodeGen::inst_JMP(emitJumpKind jmp, BasicBlock* tgtBlock) void CodeGen::genCreateAndStoreGCInfo(unsigned codeSize, unsigned prologSize, unsigned epilogSize DEBUGARG(void* code)) { - // GCInfo not captured/created by codegen. + IAllocator* allowZeroAlloc = new (m_compiler, CMK_GC) CompIAllocator(m_compiler->getAllocatorGC()); + GcInfoEncoder* gcInfoEncoder = new (m_compiler, CMK_GC) + GcInfoEncoder(m_compiler->info.compCompHnd, m_compiler->info.compMethodInfo, allowZeroAlloc, NOMEM); + assert(gcInfoEncoder != nullptr); + + // Follow the code pattern of the x86 gc info encoder (genCreateAndStoreGCInfoJIT32). + gcInfo.gcInfoBlockHdrSave(gcInfoEncoder, codeSize, prologSize); + + // We keep the call count for the second call to gcMakeRegPtrTable() below. + unsigned callCnt = 0; + + // First we figure out the encoder ID's for the stack slots and registers. + gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_ASSIGN_SLOTS, &callCnt); + + // Now we've requested all the slots we'll need; "finalize" these (make more compact data structures for them). + gcInfoEncoder->FinalizeSlotIds(); + + // Now we can actually use those slot ID's to declare live ranges. + gcInfo.gcMakeRegPtrTable(gcInfoEncoder, codeSize, prologSize, GCInfo::MAKE_REG_PTR_MODE_DO_WORK, &callCnt); + + if (m_compiler->opts.IsReversePInvoke()) + { + unsigned reversePInvokeFrameVarNumber = m_compiler->lvaReversePInvokeFrameVar; + assert(reversePInvokeFrameVarNumber != BAD_VAR_NUM); + const LclVarDsc* reversePInvokeFrameVar = m_compiler->lvaGetDesc(reversePInvokeFrameVarNumber); + gcInfoEncoder->SetReversePInvokeFrameSlot(reversePInvokeFrameVar->GetStackOffset()); + } + + gcInfoEncoder->Build(); + + // GC Encoder automatically puts the GC info in the right spot using ICorJitInfo::allocGCInfo(size_t) + // let's save the values anyway for debugging purposes + m_compiler->compInfoBlkAddr = gcInfoEncoder->Emit(); + m_compiler->compInfoBlkSize = gcInfoEncoder->GetEncodedGCInfoSize(); } //--------------------------------------------------------------------- diff --git a/src/coreclr/jit/emit.cpp b/src/coreclr/jit/emit.cpp index 98f74b755e1408..5a01409ce41a8a 100644 --- a/src/coreclr/jit/emit.cpp +++ b/src/coreclr/jit/emit.cpp @@ -9076,7 +9076,7 @@ void emitter::emitUpdateLiveGCregs(GCtype gcType, regMaskTP regs, BYTE* addr) return; } -#if EMIT_GENERATE_GCINFO +#if EMIT_GENERATE_GCINFO && !defined(TARGET_WASM) regMaskTP life; regMaskTP dead; regMaskTP chg; @@ -9131,7 +9131,7 @@ void emitter::emitUpdateLiveGCregs(GCtype gcType, regMaskTP regs, BYTE* addr) // The 2 GC reg masks can't be overlapping assert((emitThisGCrefRegs & emitThisByrefRegs) == 0); -#endif // EMIT_GENERATE_GCINFO +#endif // EMIT_GENERATE_GCINFO && !TARGET_WASM } /***************************************************************************** @@ -9434,7 +9434,7 @@ void emitter::emitGCregLiveUpd(GCtype gcType, regNumber reg, BYTE* addr) { assert(emitIssuing); -#if EMIT_GENERATE_GCINFO +#if EMIT_GENERATE_GCINFO && !defined(TARGET_WASM) // Don't track GC changes in epilogs if (emitIGisInEpilog(emitCurIG)) { @@ -9476,7 +9476,7 @@ void emitter::emitGCregLiveUpd(GCtype gcType, regNumber reg, BYTE* addr) // The 2 GC reg masks can't be overlapping assert((emitThisGCrefRegs & emitThisByrefRegs) == 0); -#endif // EMIT_GENERATE_GCINFO +#endif // EMIT_GENERATE_GCINFO && !TARGET_WASM } /***************************************************************************** @@ -9494,7 +9494,7 @@ void emitter::emitGCregDeadUpdMask(regMaskTP regs, BYTE* addr) return; } -#if EMIT_GENERATE_GCINFO +#if EMIT_GENERATE_GCINFO && !defined(TARGET_WASM) // First, handle the gcref regs going dead regMaskTP gcrefRegs = emitThisGCrefRegs & regs; @@ -9530,7 +9530,7 @@ void emitter::emitGCregDeadUpdMask(regMaskTP regs, BYTE* addr) emitThisByrefRegs &= ~byrefRegs; } -#endif // EMIT_GENERATE_GCINFO +#endif // EMIT_GENERATE_GCINFO && !TARGET_WASM } /***************************************************************************** @@ -9542,7 +9542,7 @@ void emitter::emitGCregDeadUpd(regNumber reg, BYTE* addr) { assert(emitIssuing); -#if EMIT_GENERATE_GCINFO +#if EMIT_GENERATE_GCINFO && !defined(TARGET_WASM) // Don't track GC changes in epilogs if (emitIGisInEpilog(emitCurIG)) { @@ -9571,7 +9571,7 @@ void emitter::emitGCregDeadUpd(regNumber reg, BYTE* addr) emitThisByrefRegs &= ~regMask; } -#endif // EMIT_GENERATE_GCINFO +#endif // EMIT_GENERATE_GCINFO && !TARGET_WASM } /***************************************************************************** diff --git a/src/coreclr/jit/gcencode.cpp b/src/coreclr/jit/gcencode.cpp index 40908eccefa695..1f270c59a976a2 100644 --- a/src/coreclr/jit/gcencode.cpp +++ b/src/coreclr/jit/gcencode.cpp @@ -4646,6 +4646,7 @@ void GCInfo::gcInfoRecordGCRegStateChange(GcInfoEncoder* gcInfoEncoder, regMaskSmall byRefMask, regMaskSmall* pPtrRegs) { +#ifndef TARGET_WASM // Precondition: byRefMask is a subset of regMask. assert((byRefMask & ~regMask) == 0); @@ -4703,6 +4704,7 @@ void GCInfo::gcInfoRecordGCRegStateChange(GcInfoEncoder* gcInfoEncoder, // Turn the bit we've just generated off and continue. regMask ^= tmpMask; // EAX,ECX,EDX,EBX,---,EBP,ESI,EDI } +#endif // !TARGET_WASM } /************************************************************************** diff --git a/src/coreclr/jit/gcinfo.cpp b/src/coreclr/jit/gcinfo.cpp index e687e169e2a049..f26c87cbcbeeee 100644 --- a/src/coreclr/jit/gcinfo.cpp +++ b/src/coreclr/jit/gcinfo.cpp @@ -196,7 +196,7 @@ void GCInfo::gcMarkRegSetNpt(regMaskTP regMask DEBUGARG(bool forceOutput)) void GCInfo::gcMarkRegPtrVal(regNumber reg, var_types type) { -#if EMIT_GENERATE_GCINFO +#if EMIT_GENERATE_GCINFO && !defined(TARGET_WASM) regMaskTP regMask = genRegMask(reg); switch (type) @@ -211,7 +211,7 @@ void GCInfo::gcMarkRegPtrVal(regNumber reg, var_types type) gcMarkRegSetNpt(regMask); break; } -#endif // EMIT_GENERATE_GCINFO +#endif // EMIT_GENERATE_GCINFO && !TARGET_WASM } //------------------------------------------------------------------------ diff --git a/src/coreclr/jit/targetwasm.h b/src/coreclr/jit/targetwasm.h index ba00eb9624900d..1262df809f9d3b 100644 --- a/src/coreclr/jit/targetwasm.h +++ b/src/coreclr/jit/targetwasm.h @@ -50,7 +50,7 @@ #define CSE_CONSTS 1 // Enable if we want to CSE constants #define LOWER_DECOMPOSE_LONGS 0 // Decompose TYP_LONG operations into (typically two) TYP_INT ones #define EMIT_TRACK_STACK_DEPTH 0 // No need to track arg pushes/pops -#define EMIT_GENERATE_GCINFO 0 // Codegen and emit not responsible for GC liveness tracking and GCInfo generation +#define EMIT_GENERATE_GCINFO 1 // Codegen and emit generate GC info; on WASM this enables stack slot GC info encoding without fixed-register GC tracking // Since we don't have a fixed register set on WASM, we set most of the following register defines to 'none'-like values. #define REG_FP_FIRST REG_NA diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs index 0be908211636f7..ce51f68858eaee 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/JitInterface/CorInfoImpl.ReadyToRun.cs @@ -1581,10 +1581,15 @@ private ObjectNode.ObjectData EncodeEHInfo() /// private void setVars(CORINFO_METHOD_STRUCT_* ftn, uint cVars, NativeVarInfo* vars) { - Debug.Assert(_debugVarInfos == null); + // FIXME: This can erroneously trigger when a method fails compilation late and we + // successfully retry compilation after the failure. + // Debug.Assert(_debugVarInfos == null); if (cVars == 0) + { + _debugVarInfos = null; return; + } _debugVarInfos = new NativeVarInfo[cVars]; for (int i = 0; i < cVars; i++) @@ -1601,7 +1606,9 @@ private void setVars(CORINFO_METHOD_STRUCT_* ftn, uint cVars, NativeVarInfo* var /// private void setBoundaries(CORINFO_METHOD_STRUCT_* ftn, uint cMap, OffsetMapping* pMap) { - Debug.Assert(_debugLocInfos == null); + // FIXME: This can erroneously trigger when a method fails compilation late and we + // successfully retry compilation after the failure. + // Debug.Assert(_debugLocInfos == null); _debugLocInfos = new OffsetMapping[cMap]; for (int i = 0; i < cMap; i++) diff --git a/src/coreclr/vm/eetwain.cpp b/src/coreclr/vm/eetwain.cpp index a1ec17c0002fa9..85d9d07b731faf 100644 --- a/src/coreclr/vm/eetwain.cpp +++ b/src/coreclr/vm/eetwain.cpp @@ -712,6 +712,7 @@ bool EECodeManager::IsGcSafe( EECodeInfo *pCodeInfo, return false; } +// FIXME-WASM: Add TARGET_WASM once we implement tail calls on Wasm. #if defined(TARGET_ARM) || defined(TARGET_ARM64) || defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64) bool EECodeManager::HasTailCalls( EECodeInfo *pCodeInfo) { @@ -1941,7 +1942,7 @@ void InterpreterCodeManager::ResumeAfterCatch(CONTEXT *pContext, size_t targetSS #if defined(HOST_AMD64) && defined(HOST_WINDOWS) targetSSP = pInterpreterFrame->GetInterpExecMethodSSP(); -#endif +#endif ExecuteFunctionBelowContext((PCODE)ThrowResumeAfterCatchException, pContext, targetSSP, resumeSP, resumeIP); #endif // TARGET_WASM } @@ -2111,7 +2112,7 @@ static void VirtualUnwindInterpreterCallFrame(TADDR sp, T_CONTEXT *pContext) else { // This indicates that there are no more interpreter frames to unwind in the current InterpExecMethod - // The stack walker will not find any code manager for the address InterpreterFrame::DummyCallerIP (0) + // The stack walker will not find any code manager for the address InterpreterFrame::DummyCallerIP (0) // and move on to the next explicit frame which is the InterpreterFrame. // The SP is set to the address of the InterpreterFrame. For the case of interpreted exception handling // funclets, this matches the pExInfo->m_csfEHClause.SP that the CallFunclet sets. diff --git a/src/coreclr/vm/gcinfodecoder.cpp b/src/coreclr/vm/gcinfodecoder.cpp index 82842705d217f7..681411ae521851 100644 --- a/src/coreclr/vm/gcinfodecoder.cpp +++ b/src/coreclr/vm/gcinfodecoder.cpp @@ -2274,10 +2274,8 @@ template void TGcInfoDecoder::ReportSt pCallBack(hCallBack, pObjRef, gcFlags DAC_ARG(DacSlotLocation(GetStackReg(spBase), spOffset, true))); } -#ifndef TARGET_WASM // Instantiate the decoder so other files can use it template class TGcInfoDecoder; -#endif // !TARGET_WASM #ifdef FEATURE_INTERPRETER template class TGcInfoDecoder; From ce54c49dce27efdb9f05ceb52bd18bb79dfa8629 Mon Sep 17 00:00:00 2001 From: Katelyn Gadd Date: Mon, 27 Apr 2026 23:18:45 -0700 Subject: [PATCH 2/2] Always disable tracked GC slots for Wasm --- src/coreclr/jit/gcencode.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/coreclr/jit/gcencode.cpp b/src/coreclr/jit/gcencode.cpp index 1f270c59a976a2..c5d5f6b458b8cb 100644 --- a/src/coreclr/jit/gcencode.cpp +++ b/src/coreclr/jit/gcencode.cpp @@ -4091,7 +4091,12 @@ void GCInfo::gcMakeRegPtrTable( { GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder); + // TODO-WASM: Enable tracked GC slots for precise GC +#ifdef TARGET_WASM + const bool noTrackedGCSlots = true; +#else const bool noTrackedGCSlots = m_compiler->opts.MinOpts(); +#endif if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS) {