From 832f9d82ad34feff2f88d81a986eeb30121cbdd3 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 4 Jun 2026 16:08:41 -0700 Subject: [PATCH 01/14] Emit wasm base globals as WASM_GLOBAL_INDEX_LEB relocations The wasm JIT previously referenced the three ABI "base globals" (shadow stack pointer, image base, table base) with bare `global.get ` immediates and no relocation. That prevents a relocatable NativeAOT object from letting wasm-ld renumber the global index space. Make the shared wasm JIT emit these three sites as relocatable, maximally padded `global.get` instructions carrying WASM_GLOBAL_INDEX_LEB relocations against the well-known symbols __stack_pointer/__memory_base/__table_base. This is uniform for both R2R (crossgen2) and NativeAOT: - JIT: new IF_GLOBALIDX instruction format and emitIns_BaseGlobal emitter; the three emit sites (codegenwasm prologue stack pointer, emitAddressConstant image base, emitFuncletAddressConstant table base) now emit the relocatable form. - recordRelocation maps the encoded fixed base-global index to a new shared WasmBaseGlobalSymbolNode so the relocation has a symbol target. - The R2R WasmObjectWriter self-resolves WASM_GLOBAL_INDEX_LEB back to the fixed global index (0/1/2) before the defined-symbol lookup, so existing R2R output stays functionally identical (validated: WebAssembly.validate is true, relocations section empty, base-global gets resolve to the same indices, only widened to the padded encoding). The NativeAOT object writer does not exist yet; the AOT path only needs to compile here and will emit these as undefined imported global symbols once that writer lands. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/jit/codegenwasm.cpp | 2 +- src/coreclr/jit/emitfmtswasm.h | 1 + src/coreclr/jit/emitwasm.cpp | 52 +++++++++++- src/coreclr/jit/emitwasm.h | 5 ++ .../WasmBaseGlobalSymbolNode.cs | 85 +++++++++++++++++++ .../Compiler/ObjectWriter/WasmObjectWriter.cs | 23 +++++ .../tools/Common/JitInterface/CorInfoImpl.cs | 74 +++++++++------- .../ILCompiler.Compiler.csproj | 1 + .../ILCompiler.ReadyToRun.csproj | 1 + 9 files changed, 209 insertions(+), 35 deletions(-) create mode 100644 src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmBaseGlobalSymbolNode.cs diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index 18995ab417850d..c6ce5f727e1214 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -153,7 +153,7 @@ void CodeGen::genAllocLclFrame(unsigned frameSize, regNumber initReg, bool* pIni if (!m_compiler->lvaGetDesc(m_compiler->lvaWasmSpArg)->lvIsParam) { initialSPLclIndex = spLclIndex; - GetEmitter()->emitIns_I(INS_global_get, EA_PTRSIZE, STACK_POINTER_GLOBAL); + GetEmitter()->emitIns_BaseGlobal(INS_global_get, STACK_POINTER_GLOBAL); GetEmitter()->emitIns_I(INS_local_set, EA_PTRSIZE, initialSPLclIndex); } else diff --git a/src/coreclr/jit/emitfmtswasm.h b/src/coreclr/jit/emitfmtswasm.h index 0f052e198f2f07..2321cbb71ad883 100644 --- a/src/coreclr/jit/emitfmtswasm.h +++ b/src/coreclr/jit/emitfmtswasm.h @@ -33,6 +33,7 @@ IF_DEF(RAW_ULEB128, IS_NONE, NONE) // IF_DEF(CODE_SIZE, IS_NONE, NONE) IF_DEF(ULEB128, IS_NONE, NONE) // IF_DEF(FUNCIDX, IS_NONE, NONE) // +IF_DEF(GLOBALIDX, IS_NONE, NONE) // IF_DEF(SLEB128, IS_NONE, NONE) // IF_DEF(MEMADDR, IS_NONE, NONE) // IF_DEF(FUNCPTR, IS_NONE, NONE) // diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index e9144e0b2ded5d..8d5b22baefec3e 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -8,6 +8,13 @@ #include "codegen.h" +// Well-known wasm base globals, referenced by the JIT via WASM_GLOBAL_INDEX_LEB relocations. +// These fixed indices match the ABI shared with the object writer (see WasmAbiConstants / +// WasmObjectWriter): 0 = stack pointer, 1 = image base (__memory_base), 2 = table base (__table_base). +static const unsigned WASM_STACK_POINTER_GLOBAL = 0; +static const unsigned WASM_IMAGE_BASE_GLOBAL = 1; +static const unsigned WASM_TABLE_BASE_GLOBAL = 2; + // clang-format off /*static*/ const BYTE CodeGenInterface::instInfo[] = { @@ -99,6 +106,33 @@ void emitter::emitIns_I(instruction ins, emitAttr attr, cnsval_ssize_t imm) appendToCurIG(id); } +//------------------------------------------------------------------------ +// emitIns_BaseGlobal: Emit a 'global.get'/'global.set' of a well-known base global as a +// WASM_GLOBAL_INDEX_LEB relocation against that base global's symbol. +// +// Arguments: +// ins - INS_global_get or INS_global_set +// baseGlobalIndex - the fixed wasm global index of the base global (0/1/2) +// +// Notes: +// The bare immediate is replaced by a maximally-padded, relocatable global index. The object +// writer resolves the relocation to the final global index: crossgen2/R2R self-resolves it back +// to the same fixed index, while a relocatable NativeAOT object leaves it for wasm-ld to assign. +// The relocation target is encoded as the fixed index; the EE (recordRelocation) maps it to the +// corresponding base global symbol. +// +void emitter::emitIns_BaseGlobal(instruction ins, unsigned baseGlobalIndex) +{ + assert((ins == INS_global_get) || (ins == INS_global_set)); + + instrDesc* id = emitNewInstrSC(EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)baseGlobalIndex); + id->idIns(ins); + id->idInsFmt(IF_GLOBALIDX); + + dispIns(id); + appendToCurIG(id); +} + //------------------------------------------------------------------------ // emitIns_J: Emit a jump instruction with an immediate operand. // @@ -187,9 +221,8 @@ bool emitter::emitInsIsStore(instruction ins) // This will automatically make use of relocations and the module base (__r2r_start). void emitter::emitAddressConstant(void* address) { - // Load our module base from __r2r_start, then load our address constant, then sum them. - // FIXME-WASM: Make this a named constant or a reloc that crossgen2 fills in. - emitIns_I(INS_global_get, EA_4BYTE, 1 /* __r2r_start */); + // Load our module base from the image base global, then load our address constant, then sum them. + emitIns_BaseGlobal(INS_global_get, WASM_IMAGE_BASE_GLOBAL); emitIns_I(INS_i32_const_address, EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG), (cnsval_ssize_t)address); emitIns(INS_i32_add); } @@ -197,7 +230,7 @@ void emitter::emitAddressConstant(void* address) void emitter::emitFuncletAddressConstant(cnsval_ssize_t funcletId) { // Load our table base, then load our funclet pointer offset, then sum them. - emitIns_I(INS_global_get, EA_4BYTE, 2 /* __table_start */); + emitIns_BaseGlobal(INS_global_get, WASM_TABLE_BASE_GLOBAL); emitIns_I(INS_i32_const_funcletptr, EA_PTRSIZE, (cnsval_ssize_t)funcletId); emitIns(INS_i32_add); } @@ -670,6 +703,10 @@ unsigned emitter::instrDesc::idCodeSize() const case IF_ULEB128: size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this)); break; + case IF_GLOBALIDX: + // Base global references are always emitted as relocations. + size += PADDED_RELOC_SIZE; + break; case IF_MEMADDR: case IF_FUNCPTR: case IF_SLEB128: @@ -955,6 +992,12 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) dst += emitOutputConstant(dst, id, UNSIGNED, CorInfoReloc::WASM_FUNCTION_INDEX_LEB); break; } + case IF_GLOBALIDX: + { + dst += emitOutputOpcode(dst, ins); + dst += emitOutputConstant(dst, id, UNSIGNED, CorInfoReloc::WASM_GLOBAL_INDEX_LEB); + break; + } case IF_CALL_INDIRECT: { dst += emitOutputOpcode(dst, ins); @@ -1239,6 +1282,7 @@ void emitter::emitDispIns( case IF_RAW_ULEB128: case IF_ULEB128: case IF_FUNCIDX: + case IF_GLOBALIDX: { cnsval_ssize_t imm = emitGetInsSC(id); printf(" %llu", (uint64_t)imm); diff --git a/src/coreclr/jit/emitwasm.h b/src/coreclr/jit/emitwasm.h index b5bbff12e7cc8d..7c02fcbf5ee0dc 100644 --- a/src/coreclr/jit/emitwasm.h +++ b/src/coreclr/jit/emitwasm.h @@ -40,6 +40,11 @@ void emitIns_MemargLane(instruction ins, emitAttr attr, cnsval_ssize_t offset, u void emitAddressConstant(void* address); void emitFuncletAddressConstant(cnsval_ssize_t funcletId); +// Emit a 'global.get'/'global.set' of one of the well-known base globals (stack pointer, image base, +// table base) as a WASM_GLOBAL_INDEX_LEB relocation against that base global's symbol, rather than a +// bare immediate. This lets the object writer (crossgen2/NativeAOT) resolve the final global index. +void emitIns_BaseGlobal(instruction ins, unsigned baseGlobalIndex); + static unsigned SizeOfULEB128(uint64_t value); static unsigned SizeOfSLEB128(int64_t value); static uint8_t GetWasmValueTypeCode(WasmValueType type); diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmBaseGlobalSymbolNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmBaseGlobalSymbolNode.cs new file mode 100644 index 00000000000000..5bb58a8b0cb05c --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmBaseGlobalSymbolNode.cs @@ -0,0 +1,85 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; + +using ILCompiler.DependencyAnalysisFramework; + +using Internal.Text; + +namespace ILCompiler.DependencyAnalysis +{ + // + // Represents one of the well-known wasm "base globals" referenced by JIT-generated code: + // the shadow stack pointer, the image base (__memory_base) and the table base (__table_base). + // These are imported globals whose final index is assigned by the linker, so the JIT references + // them via WASM_GLOBAL_INDEX_LEB relocations rather than bare immediates. This node is the + // relocation target carrying the well-known symbol name; the object writer maps it to the final + // wasm global index (crossgen2/R2R self-resolves it back to the fixed index, while a relocatable + // NativeAOT object emits it as an undefined imported global for wasm-ld to resolve). + // + public sealed class WasmBaseGlobalSymbolNode : SortableDependencyNode, ISortableSymbolNode + { + // Fixed wasm global indices, matching the ABI shared with the object writer + // (see WasmAbiConstants / WasmObjectWriter and the JIT's emitwasm.cpp). + public const int StackPointerGlobalIndex = 0; + public const int ImageBaseGlobalIndex = 1; + public const int TableBaseGlobalIndex = 2; + + // Well-known symbol names for the base globals (standard wasm tool-conventions names). + public const string StackPointerSymbolName = "__stack_pointer"; + public const string ImageBaseSymbolName = "__memory_base"; + public const string TableBaseSymbolName = "__table_base"; + + private static readonly WasmBaseGlobalSymbolNode s_stackPointer = new(StackPointerGlobalIndex); + private static readonly WasmBaseGlobalSymbolNode s_imageBase = new(ImageBaseGlobalIndex); + private static readonly WasmBaseGlobalSymbolNode s_tableBase = new(TableBaseGlobalIndex); + + private readonly int _globalIndex; + + private WasmBaseGlobalSymbolNode(int globalIndex) + { + _globalIndex = globalIndex; + } + + public static WasmBaseGlobalSymbolNode GetForIndex(int globalIndex) => globalIndex switch + { + StackPointerGlobalIndex => s_stackPointer, + ImageBaseGlobalIndex => s_imageBase, + TableBaseGlobalIndex => s_tableBase, + _ => throw new ArgumentOutOfRangeException(nameof(globalIndex)) + }; + + public int GlobalIndex => _globalIndex; + + public string SymbolName => _globalIndex switch + { + StackPointerGlobalIndex => StackPointerSymbolName, + ImageBaseGlobalIndex => ImageBaseSymbolName, + TableBaseGlobalIndex => TableBaseSymbolName, + _ => throw new InvalidOperationException() + }; + + public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) => sb.Append(SymbolName); + + public int Offset => 0; + public bool RepresentsIndirectionCell => false; + + public override int ClassCode => 0x57_42_47_53; // "WBGS" + + public override bool InterestingForDynamicDependencyAnalysis => false; + public override bool HasDynamicDependencies => false; + public override bool HasConditionalStaticDependencies => false; + public override bool StaticDependenciesAreComputed => true; + + protected override string GetName(NodeFactory factory) => $"Wasm Base Global: {SymbolName}"; + + public override IEnumerable GetStaticDependencies(NodeFactory factory) => null; + public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; + public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + + public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) + => _globalIndex.CompareTo(((WasmBaseGlobalSymbolNode)other)._globalIndex); + } +} diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index 9f3dcd332202c5..ee9cf89ceb5b0d 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -872,6 +872,29 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr throw new InvalidOperationException($"Unsupported relocation size for relocation: {reloc.Type}"); } + if (reloc.Type == RelocType.WASM_GLOBAL_INDEX_LEB) + { + // The JIT references the well-known wasm base globals (stack pointer / image base / + // table base) via WASM_GLOBAL_INDEX_LEB relocations against undefined imported symbols. + // For R2R these globals live at fixed indices supplied by the runtime loader, so we + // self-resolve them here. They are intentionally absent from _definedSymbols. + int globalIndex = reloc.SymbolName.ToString() switch + { + WasmBaseGlobalSymbolNode.StackPointerSymbolName => StackPointerGlobalIndex, + WasmBaseGlobalSymbolNode.ImageBaseSymbolName => ImageBaseGlobalIndex, + WasmBaseGlobalSymbolNode.TableBaseSymbolName => TableBaseGlobalIndex, + _ => throw new InvalidDataException($"Unexpected wasm base global symbol '{reloc.SymbolName}'") + }; + + fixed (byte* pData = ReadRelocToDataSpan(reloc, relocScratchBuffer, sectionStart)) + { + Relocation.WriteValue(reloc.Type, pData, globalIndex); + WriteRelocFromDataSpan(reloc, pData, sectionStart); + } + + continue; + } + SymbolDefinition definedSymbol = _definedSymbols[reloc.SymbolName]; // The virtual address of the relocation we are resolving diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index c98f4085d61da3..394e53dd83aab5 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -4252,52 +4252,66 @@ private void recordRelocation(void* location, void* locationRW, void* target, Co int length; ref ArrayBuilder sourceBlock = ref findRelocBlock(locationBlock, out length); - int relocDelta; - BlockType targetBlock = findKnownBlock(target, out relocDelta); - + int relocDelta = 0; ISymbolNode relocTarget; - switch (targetBlock) + + if (fRelocType == CorInfoReloc.WASM_GLOBAL_INDEX_LEB) { - case BlockType.Code: - relocTarget = _methodCodeNode; - break; + // The JIT references the well-known wasm base globals (stack pointer / image base / + // table base) via WASM_GLOBAL_INDEX_LEB relocations, encoding the fixed base-global + // index as the relocation target. Map that index to the corresponding base-global + // symbol; the object writer resolves it to the final wasm global index. The inline + // value is target-only (no addend). + Debug.Assert(addlDelta == 0); + relocTarget = WasmBaseGlobalSymbolNode.GetForIndex(checked((int)(nint)target)); + } + else + { + BlockType targetBlock = findKnownBlock(target, out relocDelta); - case BlockType.ColdCode: + switch (targetBlock) + { + case BlockType.Code: + relocTarget = _methodCodeNode; + break; + + case BlockType.ColdCode: #if READYTORUN - Debug.Assert(_methodColdCodeNode != null); - relocTarget = _methodColdCodeNode; - break; + Debug.Assert(_methodColdCodeNode != null); + relocTarget = _methodColdCodeNode; + break; #else - throw new NotImplementedException("ColdCode relocs"); + throw new NotImplementedException("ColdCode relocs"); #endif - case BlockType.ROData: - relocTarget = _roDataBlob; - break; + case BlockType.ROData: + relocTarget = _roDataBlob; + break; - case BlockType.RWData: - relocTarget = _rwDataBlob; - break; + case BlockType.RWData: + relocTarget = _rwDataBlob; + break; #if READYTORUN - case BlockType.BBCounts: - relocTarget = null; - break; + case BlockType.BBCounts: + relocTarget = null; + break; #endif - default: - // Reloc points to something outside of the generated blocks - var targetObject = HandleToObject(target); + default: + // Reloc points to something outside of the generated blocks + var targetObject = HandleToObject(target); #if READYTORUN - if (targetObject is RequiresRuntimeJitIfUsedSymbol requiresRuntimeSymbol) - { - throw new RequiresRuntimeJitException(requiresRuntimeSymbol.Message); - } + if (targetObject is RequiresRuntimeJitIfUsedSymbol requiresRuntimeSymbol) + { + throw new RequiresRuntimeJitException(requiresRuntimeSymbol.Message); + } #endif - relocTarget = (ISymbolNode)targetObject; - break; + relocTarget = (ISymbolNode)targetObject; + break; + } } RelocType relocType = GetRelocType(fRelocType); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 67602a68ac221c..ef3458a905326c 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -327,6 +327,7 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 215a6bf805d571..7dc6c30641b3f4 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -105,6 +105,7 @@ + From fc8462b827dce0a2add9d39149239208cf79d0ad Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 4 Jun 2026 17:54:21 -0700 Subject: [PATCH 02/14] Cover wasm base-global relocations in WasmWebcilModule R2R test Add a static-data-reading method (SumStaticData) to the WasmWebcilModule test case. Reading static data makes the wasm JIT materialize the image base and table base via 'global.get' of the base globals, which are now emitted as WASM_GLOBAL_INDEX_LEB relocations that the R2R object writer must self-resolve. This exercises the base-global relocation path end-to-end: with the self-resolution in WasmObjectWriter.ResolveRelocations, crossgen2 emits the method and the test passes; without it, crossgen2 throws KeyNotFoundException for the undefined '__table_base'/'__memory_base' symbols and the test fails. Verified the test fails when the self-resolution is disabled and passes when it is restored. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../TestCases/R2RTestSuites.cs | 8 +++++++- .../TestCases/Webcil/WasmWebcilModule.cs | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs index 3615cc3e986071..3a96991dbb4cc2 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs @@ -91,8 +91,14 @@ static void Validate(ReadyToRunReader reader) var webcilReader = Assert.IsType(reader.CompositeReader); Assert.True(webcilReader.IsWasmWrapped); Assert.Equal(WasmMachine.Wasm32, reader.Machine); - Assert.True(R2RAssert.GetAllMethods(reader).Exists(method => + List methods = R2RAssert.GetAllMethods(reader); + Assert.True(methods.Exists(method => method.SignatureString.Contains("AddIntegers", StringComparison.Ordinal))); + // Exercises the wasm base-global (image-base/table-base) relocation path: this method + // reads static data, so the JIT emits WASM_GLOBAL_INDEX_LEB relocations that the R2R + // object writer self-resolves. Its presence confirms crossgen2 emitted it successfully. + Assert.True(methods.Exists(method => + method.SignatureString.Contains("SumStaticData", StringComparison.Ordinal))); } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs index 209a61a56155ed..91e5899a895cb8 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs @@ -5,8 +5,22 @@ namespace Webcil; public static class WasmWebcilModule { + private static readonly int[] s_primes = new int[] { 3, 5, 7, 11, 13 }; + private static int s_counter; + public static int AddIntegers(int left, int right) { return left + right; } + + // Reads static data, which forces the JIT to materialize the image-base and table-base + // addresses via 'global.get' of the wasm base globals. Those are emitted as + // WASM_GLOBAL_INDEX_LEB relocations that the R2R object writer must self-resolve back to + // their fixed global indices; if that resolution regresses, crossgen2 throws while emitting + // this method. + public static int SumStaticData(int index) + { + s_counter += 1; + return s_primes[index] + s_counter; + } } From d03465e24a584d4f8a3b3d250a8439ec28bfc45b Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 17 Jun 2026 12:37:01 -0700 Subject: [PATCH 03/14] Validate wasm base-global global.get emission in WasmWebcilModule R2R test Previously the WasmWebcilModule R2R test only checked that methods were present in the R2R metadata; it never inspected the emitted wasm bytecode, so a regression in base-global relocation self-resolution would not be caught by the assertions. - Add SumWithFinally, whose try/finally drives genCallFinally to emit the table-base 'global.get'. (A function pointer would not exercise this: it emits i32.const_funcptr / WASM_TABLE_INDEX_SLEB, not the table-base global.) - Add R2RAssert.WasmImageContainsBaseGlobalGet, which scans wasm function bodies for a maximally padded 'global.get' (opcode 0x23 + 5-byte padded ULEB128) of a given base global. - Assert the image-base (1) and table-base (2) global.get patterns are present, confirming the R2R object writer self-resolved the WASM_GLOBAL_INDEX_LEB relocations to the fixed indices. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../TestCases/R2RTestSuites.cs | 23 +++++++-- .../TestCases/Webcil/WasmWebcilModule.cs | 30 ++++++++++-- .../TestCasesRunner/R2RResultChecker.cs | 49 +++++++++++++++++++ 3 files changed, 94 insertions(+), 8 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs index 3a96991dbb4cc2..62fad716247cf4 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs @@ -91,14 +91,31 @@ static void Validate(ReadyToRunReader reader) var webcilReader = Assert.IsType(reader.CompositeReader); Assert.True(webcilReader.IsWasmWrapped); Assert.Equal(WasmMachine.Wasm32, reader.Machine); + List methods = R2RAssert.GetAllMethods(reader); Assert.True(methods.Exists(method => method.SignatureString.Contains("AddIntegers", StringComparison.Ordinal))); - // Exercises the wasm base-global (image-base/table-base) relocation path: this method - // reads static data, so the JIT emits WASM_GLOBAL_INDEX_LEB relocations that the R2R - // object writer self-resolves. Its presence confirms crossgen2 emitted it successfully. + // Reads static data, so the JIT materializes the image base via a base-global global.get. Assert.True(methods.Exists(method => method.SignatureString.Contains("SumStaticData", StringComparison.Ordinal))); + // Has a try/finally, so the JIT materializes the table base via a base-global global.get. + Assert.True(methods.Exists(method => + method.SignatureString.Contains("SumWithFinally", StringComparison.Ordinal))); + + // The wasm JIT references the ABI base globals via maximally padded WASM_GLOBAL_INDEX_LEB + // relocations that the R2R object writer must self-resolve back to the fixed global + // indices. Verify the emitted code contains a correctly self-resolved 'global.get' for the + // image base (1, materialized by static-data reads in SumStaticData) and the table base + // (2, materialized by the try/finally funclet path in SumWithFinally). Each pattern encodes + // the exact resolved index, so a regression in self-resolution changes it (or makes + // crossgen2 throw while emitting the method). The stack-pointer base global is passed to + // managed methods as a parameter in R2R, so it is not referenced via 'global.get' here. + const int ImageBaseGlobal = 1; + const int TableBaseGlobal = 2; + Assert.True(R2RAssert.WasmImageContainsBaseGlobalGet(webcilReader, ImageBaseGlobal), + "Expected a 'global.get' of the wasm image-base base global in the emitted code."); + Assert.True(R2RAssert.WasmImageContainsBaseGlobalGet(webcilReader, TableBaseGlobal), + "Expected a 'global.get' of the wasm table-base base global in the emitted code."); } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs index 91e5899a895cb8..b04f8c360289e3 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs @@ -13,14 +13,34 @@ public static int AddIntegers(int left, int right) return left + right; } - // Reads static data, which forces the JIT to materialize the image-base and table-base - // addresses via 'global.get' of the wasm base globals. Those are emitted as - // WASM_GLOBAL_INDEX_LEB relocations that the R2R object writer must self-resolve back to - // their fixed global indices; if that resolution regresses, crossgen2 throws while emitting - // this method. + // Reads static data, which forces the JIT to materialize the image-base address via a + // 'global.get' of the wasm image-base base global. That global is referenced through a + // WASM_GLOBAL_INDEX_LEB relocation the R2R object writer must self-resolve back to the fixed + // image-base global index; if that resolution regresses, the emitted 'global.get' encoding + // changes (or crossgen2 throws while emitting this method). public static int SumStaticData(int index) { s_counter += 1; return s_primes[index] + s_counter; } + + // A try/finally makes the JIT emit a call to the 'finally' funclet (genCallFinally), which + // computes the funclet's address from the wasm table-base base global via a 'global.get'. + // Like the image base, that table-base global is referenced through a WASM_GLOBAL_INDEX_LEB + // relocation the R2R object writer must self-resolve back to the fixed table-base global + // index; if that resolution regresses, the emitted 'global.get' encoding changes (or + // crossgen2 throws while emitting this method). + public static int SumWithFinally(int index) + { + int total = 0; + try + { + total = s_primes[index]; + } + finally + { + s_counter++; + } + return total + s_counter; + } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs index 2b543617fe14b4..4d04d97eeff3f3 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs @@ -4,6 +4,7 @@ using System; using System.Buffers.Binary; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection.Metadata; @@ -35,6 +36,54 @@ public static List GetAllMethods(ReadyToRunReader reader) return methods; } + /// + /// Returns true if any WASM function body in the image contains a global.get of the + /// given ABI base-global index, emitted as a maximally padded 5-byte + /// WASM_GLOBAL_INDEX_LEB reference (the global.get opcode 0x23 followed + /// by the 5-byte padded ULEB128 of the index). + /// + /// + /// The wasm JIT references only the three ABI base globals (0 = stack pointer, 1 = image base, + /// 2 = table base) in this padded form; ordinary global.get instructions use the minimal + /// LEB128 encoding. The R2R object writer self-resolves the relocation in place, so after + /// compilation the padded slot holds the fixed index, e.g. image base -> + /// 23 81 80 80 80 00 and table base -> 23 82 80 80 80 00. This is a regression + /// smoke check for that self-resolution: it scans raw instruction bytes and does not decode + /// wasm instruction boundaries. + /// + public static bool WasmImageContainsBaseGlobalGet(WebcilImageReader reader, int baseGlobalIndex) + { + // The base globals are 0/1/2, which all fit in a single ULEB128 payload byte. The padded + // encoding below only writes that single payload byte, so it is correct for indices <= 0x7F. + Debug.Assert((uint)baseGlobalIndex <= 0x7F, + $"Only single-byte base-global indices are supported; got {baseGlobalIndex}."); + + // global.get (0x23) followed by the 5-byte padded ULEB128 of baseGlobalIndex. Padding sets + // the continuation bit on the first four bytes and clears the last, so a small index N + // encodes as (N | 0x80), 0x80, 0x80, 0x80, 0x00. + Span pattern = stackalloc byte[6]; + pattern[0] = 0x23; + pattern[1] = (byte)((baseGlobalIndex & 0x7F) | 0x80); + pattern[2] = 0x80; + pattern[3] = 0x80; + pattern[4] = 0x80; + pattern[5] = 0x00; + + for (int functionIndex = 0; ; functionIndex++) + { + WebcilImageReader.WasmFunctionInfo? body = reader.GetWasmFunctionBody(functionIndex); + if (body is null) + break; + + ReadOnlySpan instructions = body.Value.Image.AsSpan().Slice( + body.Value.InstructionOffset, body.Value.InstructionLength); + if (instructions.IndexOf(pattern) >= 0) + return true; + } + + return false; + } + /// /// Returns true if the R2R image contains a manifest or MSIL assembly reference with the given name. /// From 2083974b9788373df679f51ad8fdca6d662c3177 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 18 Jun 2026 16:40:47 -0700 Subject: [PATCH 04/14] Restrict wasm base-global emitter to global.get The JIT only ever reads the three wasm ABI base globals: the shadow stack pointer is read once at the root frame and then threaded through locals and the SP argument, while the image base and table base are immutable relocation bases supplied by the loader. No code path emits global.set on any of them (INS_global_set is never constructed anywhere in the JIT). Narrow emitIns_BaseGlobal to emitIns_BaseGlobalGet: drop the instruction parameter, always emit INS_global_get, and assert the index is one of the known base globals (<= table base). The emitted code is unchanged since every caller already passed INS_global_get. This keeps the relocation behavior (WASM_GLOBAL_INDEX_LEB) intact; if a future design needs to write a base global back, both a global.set emit path and this helper would be widened. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/jit/codegenwasm.cpp | 2 +- src/coreclr/jit/emitwasm.cpp | 16 +++++++++------- src/coreclr/jit/emitwasm.h | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index c6ce5f727e1214..698dde294801d0 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -153,7 +153,7 @@ void CodeGen::genAllocLclFrame(unsigned frameSize, regNumber initReg, bool* pIni if (!m_compiler->lvaGetDesc(m_compiler->lvaWasmSpArg)->lvIsParam) { initialSPLclIndex = spLclIndex; - GetEmitter()->emitIns_BaseGlobal(INS_global_get, STACK_POINTER_GLOBAL); + GetEmitter()->emitIns_BaseGlobalGet(STACK_POINTER_GLOBAL); GetEmitter()->emitIns_I(INS_local_set, EA_PTRSIZE, initialSPLclIndex); } else diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 8d5b22baefec3e..52082005a080cf 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -107,26 +107,28 @@ void emitter::emitIns_I(instruction ins, emitAttr attr, cnsval_ssize_t imm) } //------------------------------------------------------------------------ -// emitIns_BaseGlobal: Emit a 'global.get'/'global.set' of a well-known base global as a +// emitIns_BaseGlobalGet: Emit a 'global.get' of a well-known base global as a // WASM_GLOBAL_INDEX_LEB relocation against that base global's symbol. // // Arguments: -// ins - INS_global_get or INS_global_set // baseGlobalIndex - the fixed wasm global index of the base global (0/1/2) // // Notes: +// The JIT only ever reads these base globals (the shadow stack pointer is read once at the root +// frame and then threaded through locals; the image base and table base are immutable relocation +// bases set by the loader), so only 'global.get' is supported. // The bare immediate is replaced by a maximally-padded, relocatable global index. The object // writer resolves the relocation to the final global index: crossgen2/R2R self-resolves it back // to the same fixed index, while a relocatable NativeAOT object leaves it for wasm-ld to assign. // The relocation target is encoded as the fixed index; the EE (recordRelocation) maps it to the // corresponding base global symbol. // -void emitter::emitIns_BaseGlobal(instruction ins, unsigned baseGlobalIndex) +void emitter::emitIns_BaseGlobalGet(unsigned baseGlobalIndex) { - assert((ins == INS_global_get) || (ins == INS_global_set)); + assert(baseGlobalIndex <= WASM_TABLE_BASE_GLOBAL); instrDesc* id = emitNewInstrSC(EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)baseGlobalIndex); - id->idIns(ins); + id->idIns(INS_global_get); id->idInsFmt(IF_GLOBALIDX); dispIns(id); @@ -222,7 +224,7 @@ bool emitter::emitInsIsStore(instruction ins) void emitter::emitAddressConstant(void* address) { // Load our module base from the image base global, then load our address constant, then sum them. - emitIns_BaseGlobal(INS_global_get, WASM_IMAGE_BASE_GLOBAL); + emitIns_BaseGlobalGet(WASM_IMAGE_BASE_GLOBAL); emitIns_I(INS_i32_const_address, EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG), (cnsval_ssize_t)address); emitIns(INS_i32_add); } @@ -230,7 +232,7 @@ void emitter::emitAddressConstant(void* address) void emitter::emitFuncletAddressConstant(cnsval_ssize_t funcletId) { // Load our table base, then load our funclet pointer offset, then sum them. - emitIns_BaseGlobal(INS_global_get, WASM_TABLE_BASE_GLOBAL); + emitIns_BaseGlobalGet(WASM_TABLE_BASE_GLOBAL); emitIns_I(INS_i32_const_funcletptr, EA_PTRSIZE, (cnsval_ssize_t)funcletId); emitIns(INS_i32_add); } diff --git a/src/coreclr/jit/emitwasm.h b/src/coreclr/jit/emitwasm.h index 7c02fcbf5ee0dc..2ea3fad0837c78 100644 --- a/src/coreclr/jit/emitwasm.h +++ b/src/coreclr/jit/emitwasm.h @@ -40,10 +40,10 @@ void emitIns_MemargLane(instruction ins, emitAttr attr, cnsval_ssize_t offset, u void emitAddressConstant(void* address); void emitFuncletAddressConstant(cnsval_ssize_t funcletId); -// Emit a 'global.get'/'global.set' of one of the well-known base globals (stack pointer, image base, -// table base) as a WASM_GLOBAL_INDEX_LEB relocation against that base global's symbol, rather than a -// bare immediate. This lets the object writer (crossgen2/NativeAOT) resolve the final global index. -void emitIns_BaseGlobal(instruction ins, unsigned baseGlobalIndex); +// Emit a 'global.get' of one of the well-known base globals (stack pointer, image base, table +// base) as a WASM_GLOBAL_INDEX_LEB relocation against that base global's symbol. The JIT only +// reads these globals, so only 'global.get' is supported. +void emitIns_BaseGlobalGet(unsigned baseGlobalIndex); static unsigned SizeOfULEB128(uint64_t value); static unsigned SizeOfSLEB128(int64_t value); From e45baaf51327c91a28e1a9db6c3c57910731ef0c Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 22 Jun 2026 13:23:39 -0700 Subject: [PATCH 05/14] Add getWasmBaseGlobals JIT-EE API for wasm base globals Introduce a new JIT-EE interface method getWasmBaseGlobals that returns symbol handles for the three wasm ABI base globals (shadow stack pointer, image base, and table base) in a single out-struct, modeled on getAsyncInfo and cached in the JIT after the first call. The wasm emitter now targets these real WasmBaseGlobalSymbolNode handles in WASM_GLOBAL_INDEX_LEB relocations instead of having crossgen2 special-case a bare global index in CorInfoImpl.recordRelocation. The base-global reloc now flows through the normal out-of-block symbol resolution path, producing byte-identical R2R output. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/inc/corinfo.h | 20 +++++ src/coreclr/inc/icorjitinfoimpl_generated.h | 3 + src/coreclr/inc/jiteeversionguid.h | 10 +-- src/coreclr/jit/ICorJitInfo_names_generated.h | 1 + .../jit/ICorJitInfo_wrapper_generated.hpp | 8 ++ src/coreclr/jit/compiler.h | 5 ++ src/coreclr/jit/ee_il_dll.hpp | 11 +++ src/coreclr/jit/emitwasm.cpp | 25 +++++- .../tools/Common/JitInterface/CorInfoImpl.cs | 78 +++++++++---------- .../JitInterface/CorInfoImpl_generated.cs | 16 ++++ .../tools/Common/JitInterface/CorInfoTypes.cs | 17 ++++ .../ThunkGenerator/ThunkInput.txt | 2 + .../aot/jitinterface/jitinterface_generated.h | 9 +++ .../tools/superpmi/superpmi-shared/agnostic.h | 7 ++ .../tools/superpmi/superpmi-shared/lwmlist.h | 1 + .../superpmi-shared/methodcontext.cpp | 29 +++++++ .../superpmi/superpmi-shared/methodcontext.h | 5 ++ .../superpmi-shim-collector/icorjitinfo.cpp | 7 ++ .../icorjitinfo_generated.cpp | 7 ++ .../icorjitinfo_generated.cpp | 6 ++ .../tools/superpmi/superpmi/icorjitinfo.cpp | 6 ++ src/coreclr/vm/jitinterface.cpp | 6 ++ 22 files changed, 229 insertions(+), 50 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 8ed87902e29a15..f3d407eaa211e0 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -995,6 +995,7 @@ typedef struct CORINFO_JUST_MY_CODE_HANDLE_*CORINFO_JUST_MY_CODE_HANDLE; typedef struct CORINFO_PROFILING_STRUCT_* CORINFO_PROFILING_HANDLE; // a handle guaranteed to be unique per process typedef struct CORINFO_GENERIC_STRUCT_* CORINFO_GENERIC_HANDLE; // a generic handle (could be any of the above) typedef struct CORINFO_WASM_TYPE_SYMBOL_STRUCT_* CORINFO_WASM_TYPE_SYMBOL_HANDLE; // a handle for WASM type symbols +typedef struct CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_* CORINFO_WASM_GLOBAL_SYMBOL_HANDLE; // a handle for a WASM global symbol // what is actually passed on the varargs call typedef struct CORINFO_VarArgInfo * CORINFO_VARARGS_HANDLE; @@ -1834,6 +1835,19 @@ struct CORINFO_ASYNC_INFO CORINFO_METHOD_HANDLE finishSuspensionWithContinuationContextMethHnd; }; +// The well-known wasm "base globals" that JIT-generated code references via +// WASM_GLOBAL_INDEX_LEB relocations. Each handle is the relocation target for the +// corresponding base global; the object writer resolves it to the final wasm global index. +struct CORINFO_WASM_BASE_GLOBALS +{ + // Shadow stack pointer global (read at the root frame, then threaded through locals). + CORINFO_WASM_GLOBAL_SYMBOL_HANDLE stackPointer; + // Image base global (__memory_base), added to static data offsets. + CORINFO_WASM_GLOBAL_SYMBOL_HANDLE imageBase; + // Table base global (__table_base), added to funclet pointer offsets. + CORINFO_WASM_GLOBAL_SYMBOL_HANDLE tableBase; +}; + // Flags passed from JIT to runtime. enum CORINFO_GET_TAILCALL_HELPERS_FLAGS { @@ -3124,6 +3138,12 @@ class ICorStaticInfo CORINFO_ASYNC_INFO* pAsyncInfoOut ) = 0; + // Get the well-known wasm base-global symbols (shadow stack pointer, image base, table base) + // that JIT-generated wasm code references via WASM_GLOBAL_INDEX_LEB relocations. + virtual void getWasmBaseGlobals( + CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut + ) = 0; + /*********************************************************************************/ // // Diagnostic methods diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 09d1608bfa134f..5c7e5fbfb9b0dd 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -498,6 +498,9 @@ void getEEInfo( void getAsyncInfo( CORINFO_ASYNC_INFO* pAsyncInfoOut) override; +void getWasmBaseGlobals( + CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) override; + mdMethodDef getMethodDefFromMethod( CORINFO_METHOD_HANDLE hMethod) override; diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index bc9e29979e6989..acc230fea0befe 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* 14ca1721-a3ba-40a0-b8a6-92a939719e66 */ - 0x14ca1721, - 0xa3ba, - 0x40a0, - {0xb8, 0xa6, 0x92, 0xa9, 0x39, 0x71, 0x9e, 0x66} +constexpr GUID JITEEVersionIdentifier = { /* 80b02eb1-6f17-4e00-99c0-63a7db273a4c */ + 0x80b02eb1, + 0x6f17, + 0x4e00, + {0x99, 0xc0, 0x63, 0xa7, 0xdb, 0x27, 0x3a, 0x4c} }; #endif // JIT_EE_VERSIONING_GUID_H diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index 32abc2cd9596d2..57f3e9abfac682 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -124,6 +124,7 @@ DEF_CLR_API(runWithErrorTrap) DEF_CLR_API(runWithSPMIErrorTrap) DEF_CLR_API(getEEInfo) DEF_CLR_API(getAsyncInfo) +DEF_CLR_API(getWasmBaseGlobals) DEF_CLR_API(getMethodDefFromMethod) DEF_CLR_API(printMethodName) DEF_CLR_API(getMethodNameFromMetadata) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index c775460f0c1bbc..3c21aac6dada11 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -1179,6 +1179,14 @@ void WrapICorJitInfo::getAsyncInfo( API_LEAVE(getAsyncInfo); } +void WrapICorJitInfo::getWasmBaseGlobals( + CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +{ + API_ENTER(getWasmBaseGlobals); + wrapHnd->getWasmBaseGlobals(pBaseGlobalsOut); + API_LEAVE(getWasmBaseGlobals); +} + mdMethodDef WrapICorJitInfo::getMethodDefFromMethod( CORINFO_METHOD_HANDLE hMethod) { diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index c0e852bb737a8e..d02396b87c7104 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9475,6 +9475,11 @@ class Compiler CORINFO_ASYNC_INFO* eeGetAsyncInfo(); + CORINFO_WASM_BASE_GLOBALS wasmBaseGlobals; + bool wasmBaseGlobalsInitialized = false; + + CORINFO_WASM_BASE_GLOBALS* eeGetWasmBaseGlobals(); + // Gets the offset of a SDArray's first element static unsigned eeGetArrayDataOffset(); diff --git a/src/coreclr/jit/ee_il_dll.hpp b/src/coreclr/jit/ee_il_dll.hpp index a64c9ad9698acb..9d33171f60f628 100644 --- a/src/coreclr/jit/ee_il_dll.hpp +++ b/src/coreclr/jit/ee_il_dll.hpp @@ -187,6 +187,17 @@ inline CORINFO_ASYNC_INFO* Compiler::eeGetAsyncInfo() return &asyncInfo; } +inline CORINFO_WASM_BASE_GLOBALS* Compiler::eeGetWasmBaseGlobals() +{ + if (!wasmBaseGlobalsInitialized) + { + info.compCompHnd->getWasmBaseGlobals(&wasmBaseGlobals); + wasmBaseGlobalsInitialized = true; + } + + return &wasmBaseGlobals; +} + /***************************************************************************** * * Convert the type returned from the VM to a var_type. diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 52082005a080cf..eb9d9717e25d88 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -120,14 +120,33 @@ void emitter::emitIns_I(instruction ins, emitAttr attr, cnsval_ssize_t imm) // The bare immediate is replaced by a maximally-padded, relocatable global index. The object // writer resolves the relocation to the final global index: crossgen2/R2R self-resolves it back // to the same fixed index, while a relocatable NativeAOT object leaves it for wasm-ld to assign. -// The relocation target is encoded as the fixed index; the EE (recordRelocation) maps it to the -// corresponding base global symbol. +// The relocation target is the base global's symbol handle, obtained from the EE via +// getWasmBaseGlobals; the object writer maps that symbol to the corresponding global index. // void emitter::emitIns_BaseGlobalGet(unsigned baseGlobalIndex) { assert(baseGlobalIndex <= WASM_TABLE_BASE_GLOBAL); - instrDesc* id = emitNewInstrSC(EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)baseGlobalIndex); + // Resolve the base global to its symbol handle, which becomes the relocation target. + CORINFO_WASM_BASE_GLOBALS* baseGlobals = m_compiler->eeGetWasmBaseGlobals(); + + CORINFO_WASM_GLOBAL_SYMBOL_HANDLE symbol; + switch (baseGlobalIndex) + { + case WASM_STACK_POINTER_GLOBAL: + symbol = baseGlobals->stackPointer; + break; + case WASM_IMAGE_BASE_GLOBAL: + symbol = baseGlobals->imageBase; + break; + case WASM_TABLE_BASE_GLOBAL: + symbol = baseGlobals->tableBase; + break; + default: + unreached(); + } + + instrDesc* id = emitNewInstrSC(EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)(size_t)symbol); id->idIns(INS_global_get); id->idInsFmt(IF_GLOBALIDX); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 394e53dd83aab5..925b50d13db2d9 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3464,6 +3464,13 @@ private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut) pAsyncInfoOut.finishSuspensionWithContinuationContextMethHnd = ObjectToHandle(asyncHelpers.GetKnownMethod("FinishSuspensionWithContinuationContext"u8, null)); } + private void getWasmBaseGlobals(ref CORINFO_WASM_BASE_GLOBALS pBaseGlobalsOut) + { + pBaseGlobalsOut.stackPointer = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(WasmBaseGlobalSymbolNode.GetForIndex(WasmBaseGlobalSymbolNode.StackPointerGlobalIndex)); + pBaseGlobalsOut.imageBase = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(WasmBaseGlobalSymbolNode.GetForIndex(WasmBaseGlobalSymbolNode.ImageBaseGlobalIndex)); + pBaseGlobalsOut.tableBase = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(WasmBaseGlobalSymbolNode.GetForIndex(WasmBaseGlobalSymbolNode.TableBaseGlobalIndex)); + } + private CORINFO_CLASS_STRUCT_* getContinuationType(nuint dataSize, ref bool objRefs, nuint objRefsSize) { Debug.Assert(objRefsSize == (dataSize + (nuint)(PointerSize - 1)) / (nuint)PointerSize); @@ -4255,63 +4262,50 @@ private void recordRelocation(void* location, void* locationRW, void* target, Co int relocDelta = 0; ISymbolNode relocTarget; - if (fRelocType == CorInfoReloc.WASM_GLOBAL_INDEX_LEB) - { - // The JIT references the well-known wasm base globals (stack pointer / image base / - // table base) via WASM_GLOBAL_INDEX_LEB relocations, encoding the fixed base-global - // index as the relocation target. Map that index to the corresponding base-global - // symbol; the object writer resolves it to the final wasm global index. The inline - // value is target-only (no addend). - Debug.Assert(addlDelta == 0); - relocTarget = WasmBaseGlobalSymbolNode.GetForIndex(checked((int)(nint)target)); - } - else - { - BlockType targetBlock = findKnownBlock(target, out relocDelta); + BlockType targetBlock = findKnownBlock(target, out relocDelta); - switch (targetBlock) - { - case BlockType.Code: - relocTarget = _methodCodeNode; - break; + switch (targetBlock) + { + case BlockType.Code: + relocTarget = _methodCodeNode; + break; - case BlockType.ColdCode: + case BlockType.ColdCode: #if READYTORUN - Debug.Assert(_methodColdCodeNode != null); - relocTarget = _methodColdCodeNode; - break; + Debug.Assert(_methodColdCodeNode != null); + relocTarget = _methodColdCodeNode; + break; #else - throw new NotImplementedException("ColdCode relocs"); + throw new NotImplementedException("ColdCode relocs"); #endif - case BlockType.ROData: - relocTarget = _roDataBlob; - break; + case BlockType.ROData: + relocTarget = _roDataBlob; + break; - case BlockType.RWData: - relocTarget = _rwDataBlob; - break; + case BlockType.RWData: + relocTarget = _rwDataBlob; + break; #if READYTORUN - case BlockType.BBCounts: - relocTarget = null; - break; + case BlockType.BBCounts: + relocTarget = null; + break; #endif - default: - // Reloc points to something outside of the generated blocks - var targetObject = HandleToObject(target); + default: + // Reloc points to something outside of the generated blocks. + var targetObject = HandleToObject(target); #if READYTORUN - if (targetObject is RequiresRuntimeJitIfUsedSymbol requiresRuntimeSymbol) - { - throw new RequiresRuntimeJitException(requiresRuntimeSymbol.Message); - } + if (targetObject is RequiresRuntimeJitIfUsedSymbol requiresRuntimeSymbol) + { + throw new RequiresRuntimeJitException(requiresRuntimeSymbol.Message); + } #endif - relocTarget = (ISymbolNode)targetObject; - break; - } + relocTarget = (ISymbolNode)targetObject; + break; } RelocType relocType = GetRelocType(fRelocType); diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 74da04da013a53..81bdec53fbc310 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -140,6 +140,7 @@ static ICorJitInfoCallbacks() s_callbacks.runWithSPMIErrorTrap = &_runWithSPMIErrorTrap; s_callbacks.getEEInfo = &_getEEInfo; s_callbacks.getAsyncInfo = &_getAsyncInfo; + s_callbacks.getWasmBaseGlobals = &_getWasmBaseGlobals; s_callbacks.getMethodDefFromMethod = &_getMethodDefFromMethod; s_callbacks.printMethodName = &_printMethodName; s_callbacks.getMethodNameFromMetadata = &_getMethodNameFromMetadata; @@ -321,6 +322,7 @@ static ICorJitInfoCallbacks() public delegate* unmanaged runWithSPMIErrorTrap; public delegate* unmanaged getEEInfo; public delegate* unmanaged getAsyncInfo; + public delegate* unmanaged getWasmBaseGlobals; public delegate* unmanaged getMethodDefFromMethod; public delegate* unmanaged printMethodName; public delegate* unmanaged getMethodNameFromMetadata; @@ -2157,6 +2159,20 @@ private static void _getAsyncInfo(IntPtr thisHandle, IntPtr* ppException, CORINF } } + [UnmanagedCallersOnly] + private static void _getWasmBaseGlobals(IntPtr thisHandle, IntPtr* ppException, CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) + { + var _this = GetThis(thisHandle); + try + { + _this.getWasmBaseGlobals(ref *pBaseGlobalsOut); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + } + } + [UnmanagedCallersOnly] private static mdToken _getMethodDefFromMethod(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* hMethod) { diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index d94780842175e7..cfd1eed21368c7 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -64,6 +64,10 @@ public struct CORINFO_WASM_TYPE_SYMBOL_STRUCT_ { } + public struct CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_ + { + } + public struct CORINFO_JUST_MY_CODE_HANDLE_ { } @@ -968,6 +972,19 @@ public unsafe struct CORINFO_ASYNC_INFO public CORINFO_METHOD_STRUCT_* finishSuspensionWithContinuationContextMethHnd; } + // The well-known wasm "base globals" referenced by JIT-generated code via + // WASM_GLOBAL_INDEX_LEB relocations. Each handle is the relocation target for the + // corresponding base global; the object writer resolves it to the final wasm global index. + public unsafe struct CORINFO_WASM_BASE_GLOBALS + { + // Shadow stack pointer global (read at the root frame, then threaded through locals). + public CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_* stackPointer; + // Image base global (__memory_base), added to static data offsets. + public CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_* imageBase; + // Table base global (__table_base), added to funclet pointer offsets. + public CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_* tableBase; + } + // Flags passed from JIT to runtime. public enum CORINFO_GET_TAILCALL_HELPERS_FLAGS { diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index fe14f4d9bc0e5c..24f7ea288fd0b5 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -84,6 +84,7 @@ CORINFO_RESOLVED_TOKEN*,ref CORINFO_RESOLVED_TOKEN CORINFO_RESOLVED_TOKEN_PTR,CORINFO_RESOLVED_TOKEN*,CORINFO_RESOLVED_TOKEN*,CORINFO_RESOLVED_TOKEN* CORINFO_EE_INFO*,ref CORINFO_EE_INFO CORINFO_ASYNC_INFO*,ref CORINFO_ASYNC_INFO +CORINFO_WASM_BASE_GLOBALS*,ref CORINFO_WASM_BASE_GLOBALS CORINFO_TAILCALL_HELPERS*,ref CORINFO_TAILCALL_HELPERS CORINFO_SWIFT_LOWERING*,ref CORINFO_SWIFT_LOWERING CORINFO_FPSTRUCT_LOWERING*,ref CORINFO_FPSTRUCT_LOWERING @@ -293,6 +294,7 @@ FUNCTIONS [ManualNativeWrapper] bool runWithSPMIErrorTrap(ICorJitInfo::errorTrapFunction function, void* parameter); void getEEInfo(CORINFO_EE_INFO* pEEInfoOut); void getAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfoOut); + void getWasmBaseGlobals(CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut); mdMethodDef getMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod); size_t printMethodName(CORINFO_METHOD_HANDLE ftn, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize) const char* getMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn, const char **className, const char **namespaceName, const char **enclosingClassNames, size_t maxEnclosingClassNames); diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 0049a99f1e1dd8..aa0f2f4665634d 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -131,6 +131,7 @@ struct JitInterfaceCallbacks bool (* runWithSPMIErrorTrap)(void * thisHandle, CorInfoExceptionClass** ppException, ICorJitInfo::errorTrapFunction function, void* parameter); void (* getEEInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_EE_INFO* pEEInfoOut); void (* getAsyncInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_ASYNC_INFO* pAsyncInfoOut); + void (* getWasmBaseGlobals)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut); mdMethodDef (* getMethodDefFromMethod)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE hMethod); size_t (* printMethodName)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize); const char* (* getMethodNameFromMetadata)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, const char** className, const char** namespaceName, const char** enclosingClassNames, size_t maxEnclosingClassNames); @@ -1360,6 +1361,14 @@ class JitInterfaceWrapper : public ICorJitInfo if (pException != nullptr) throw pException; } + virtual void getWasmBaseGlobals( + CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +{ + CorInfoExceptionClass* pException = nullptr; + _callbacks->getWasmBaseGlobals(_thisHandle, &pException, pBaseGlobalsOut); + if (pException != nullptr) throw pException; +} + virtual mdMethodDef getMethodDefFromMethod( CORINFO_METHOD_HANDLE hMethod) { diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h index 443fad294e244e..de185198a262b4 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h @@ -208,6 +208,13 @@ struct Agnostic_CORINFO_ASYNC_INFO DWORDLONG finishSuspensionWithContinuationContextMethHnd; }; +struct Agnostic_CORINFO_WASM_BASE_GLOBALS +{ + DWORDLONG stackPointer; + DWORDLONG imageBase; + DWORDLONG tableBase; +}; + struct Agnostic_GetOSRInfo { DWORD index; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index 957c92a4aa30f7..b74d505d9545d6 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -172,6 +172,7 @@ LWM(GetUnmanagedCallConv, MethodOrSigInfoValue, DD) LWM(DoesFieldBelongToClass, DLDL, DWORD) DENSELWM(SigInstHandleMap, DWORDLONG) LWM(GetWasmTypeSymbol, Agnostic_GetWasmTypeSymbol, DWORDLONG) +LWM(GetWasmBaseGlobals, DWORD, Agnostic_CORINFO_WASM_BASE_GLOBALS) #undef LWM #undef DENSELWM diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 1c28e7fae95391..dfb2d577c4f9b6 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -4425,6 +4425,35 @@ void MethodContext::repGetAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfoOut) DEBUG_REP(dmpGetAsyncInfo(0, value)); } +void MethodContext::recGetWasmBaseGlobals(const CORINFO_WASM_BASE_GLOBALS* pBaseGlobals) +{ + if (GetWasmBaseGlobals == nullptr) + GetWasmBaseGlobals = new LightWeightMap(); + + Agnostic_CORINFO_WASM_BASE_GLOBALS value; + ZeroMemory(&value, sizeof(value)); + + value.stackPointer = CastHandle(pBaseGlobals->stackPointer); + value.imageBase = CastHandle(pBaseGlobals->imageBase); + value.tableBase = CastHandle(pBaseGlobals->tableBase); + + GetWasmBaseGlobals->Add(0, value); + DEBUG_REC(dmpGetWasmBaseGlobals(0, value)); +} +void MethodContext::dmpGetWasmBaseGlobals(DWORD key, const Agnostic_CORINFO_WASM_BASE_GLOBALS& value) +{ + printf("GetWasmBaseGlobals key %u value stackPointer-%016" PRIX64 " imageBase-%016" PRIX64 " tableBase-%016" PRIX64, + key, value.stackPointer, value.imageBase, value.tableBase); +} +void MethodContext::repGetWasmBaseGlobals(CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +{ + Agnostic_CORINFO_WASM_BASE_GLOBALS value = LookupByKeyOrMissNoMessage(GetWasmBaseGlobals, 0); + pBaseGlobalsOut->stackPointer = (CORINFO_WASM_GLOBAL_SYMBOL_HANDLE)value.stackPointer; + pBaseGlobalsOut->imageBase = (CORINFO_WASM_GLOBAL_SYMBOL_HANDLE)value.imageBase; + pBaseGlobalsOut->tableBase = (CORINFO_WASM_GLOBAL_SYMBOL_HANDLE)value.tableBase; + DEBUG_REP(dmpGetWasmBaseGlobals(0, value)); +} + void MethodContext::recGetGSCookie(GSCookie* pCookieVal, GSCookie** ppCookieVal) { if (GetGSCookie == nullptr) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index e4cef62c08791a..60ec0d1738de96 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -559,6 +559,10 @@ class MethodContext void dmpGetAsyncInfo(DWORD key, const Agnostic_CORINFO_ASYNC_INFO& value); void repGetAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfoOut); + void recGetWasmBaseGlobals(const CORINFO_WASM_BASE_GLOBALS* pBaseGlobals); + void dmpGetWasmBaseGlobals(DWORD key, const Agnostic_CORINFO_WASM_BASE_GLOBALS& value); + void repGetWasmBaseGlobals(CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut); + void recGetGSCookie(GSCookie* pCookieVal, GSCookie** ppCookieVal); void dmpGetGSCookie(DWORD key, DLDL value); void repGetGSCookie(GSCookie* pCookieVal, GSCookie** ppCookieVal); @@ -1209,6 +1213,7 @@ enum mcPackets Packet_GetWasmTypeSymbol = 235, Packet_GetWasmLowering = 236, Packet_GetAsyncOtherVariant = 237, + Packet_GetWasmBaseGlobals = 238, }; void SetDebugDumpVariables(); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 04082c6ac0c406..2ce4c1bd815412 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1382,6 +1382,13 @@ void interceptor_ICJI::getAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfo) mc->recGetAsyncInfo(pAsyncInfo); } +void interceptor_ICJI::getWasmBaseGlobals(CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +{ + mc->cr->AddCall("getWasmBaseGlobals"); + original_ICorJitInfo->getWasmBaseGlobals(pBaseGlobalsOut); + mc->recGetWasmBaseGlobals(pBaseGlobalsOut); +} + /*********************************************************************************/ // // Diagnostic methods diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index 3b3ad4933853be..0b5c124b8db116 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -969,6 +969,13 @@ void interceptor_ICJI::getAsyncInfo( original_ICorJitInfo->getAsyncInfo(pAsyncInfoOut); } +void interceptor_ICJI::getWasmBaseGlobals( + CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +{ + mcs->AddCall("getWasmBaseGlobals"); + original_ICorJitInfo->getWasmBaseGlobals(pBaseGlobalsOut); +} + mdMethodDef interceptor_ICJI::getMethodDefFromMethod( CORINFO_METHOD_HANDLE hMethod) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index 5e9dbe609bde3b..07c192e8f758cd 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -849,6 +849,12 @@ void interceptor_ICJI::getAsyncInfo( original_ICorJitInfo->getAsyncInfo(pAsyncInfoOut); } +void interceptor_ICJI::getWasmBaseGlobals( + CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +{ + original_ICorJitInfo->getWasmBaseGlobals(pBaseGlobalsOut); +} + mdMethodDef interceptor_ICJI::getMethodDefFromMethod( CORINFO_METHOD_HANDLE hMethod) { diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index c2743ff9524bb4..847995426de7a7 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1197,6 +1197,12 @@ void MyICJI::getAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfo) jitInstance->mc->repGetAsyncInfo(pAsyncInfo); } +void MyICJI::getWasmBaseGlobals(CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +{ + jitInstance->mc->cr->AddCall("getWasmBaseGlobals"); + jitInstance->mc->repGetWasmBaseGlobals(pBaseGlobalsOut); +} + /*********************************************************************************/ // // Diagnostic methods diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index d73d250b409c2e..83af8e05b4a13e 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -10123,6 +10123,12 @@ CORINFO_WASM_TYPE_SYMBOL_HANDLE CEEInfo::getWasmTypeSymbol( UNREACHABLE_RET(); } +void CEEInfo::getWasmBaseGlobals(CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +{ + LIMITED_METHOD_CONTRACT; + UNREACHABLE(); +} + CORINFO_METHOD_HANDLE CEEInfo::getSpecialCopyHelper(CORINFO_CLASS_HANDLE type) { CONTRACTL { From a192e3a32b387d9d5063fc6279a87e90fc11f621 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 22 Jun 2026 13:36:10 -0700 Subject: [PATCH 06/14] Resolve wasm base-global relocs via WasmBaseGlobalSymbolNode.GlobalIndex Have WasmObjectWriter look up the global index from the WasmBaseGlobalSymbolNode mapping instead of switching over the symbol name, and revert unrelated churn in recordRelocation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../DependencyAnalysis/WasmBaseGlobalSymbolNode.cs | 14 +++++++++++++- .../Compiler/ObjectWriter/WasmObjectWriter.cs | 14 ++++++-------- .../tools/Common/JitInterface/CorInfoImpl.cs | 7 +++---- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmBaseGlobalSymbolNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmBaseGlobalSymbolNode.cs index 5bb58a8b0cb05c..cd1fd17f37e5ca 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmBaseGlobalSymbolNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmBaseGlobalSymbolNode.cs @@ -22,7 +22,7 @@ namespace ILCompiler.DependencyAnalysis public sealed class WasmBaseGlobalSymbolNode : SortableDependencyNode, ISortableSymbolNode { // Fixed wasm global indices, matching the ABI shared with the object writer - // (see WasmAbiConstants / WasmObjectWriter and the JIT's emitwasm.cpp). + // (see WasmAbiConstants / WasmObjectWriter and the JIT's emitwasm.cpp, as well as the WebCIL spec). public const int StackPointerGlobalIndex = 0; public const int ImageBaseGlobalIndex = 1; public const int TableBaseGlobalIndex = 2; @@ -43,6 +43,13 @@ private WasmBaseGlobalSymbolNode(int globalIndex) _globalIndex = globalIndex; } + private static readonly Dictionary s_symbolNameToGlobalIndex = new() + { + { StackPointerSymbolName, StackPointerGlobalIndex }, + { ImageBaseSymbolName, ImageBaseGlobalIndex }, + { TableBaseSymbolName, TableBaseGlobalIndex }, + }; + public static WasmBaseGlobalSymbolNode GetForIndex(int globalIndex) => globalIndex switch { StackPointerGlobalIndex => s_stackPointer, @@ -51,6 +58,11 @@ private WasmBaseGlobalSymbolNode(int globalIndex) _ => throw new ArgumentOutOfRangeException(nameof(globalIndex)) }; + // Maps a well-known base-global symbol name back to its fixed wasm global index. This is the + // authoritative mapping used by the object writer to resolve WASM_GLOBAL_INDEX_LEB relocations. + public static bool TryGetGlobalIndexForSymbol(string symbolName, out int globalIndex) + => s_symbolNameToGlobalIndex.TryGetValue(symbolName, out globalIndex); + public int GlobalIndex => _globalIndex; public string SymbolName => _globalIndex switch diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index ee9cf89ceb5b0d..3fa894ae32e1a1 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -875,16 +875,14 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr if (reloc.Type == RelocType.WASM_GLOBAL_INDEX_LEB) { // The JIT references the well-known wasm base globals (stack pointer / image base / - // table base) via WASM_GLOBAL_INDEX_LEB relocations against undefined imported symbols. + // table base) via WASM_GLOBAL_INDEX_LEB relocations against the WasmBaseGlobalSymbolNode. // For R2R these globals live at fixed indices supplied by the runtime loader, so we - // self-resolve them here. They are intentionally absent from _definedSymbols. - int globalIndex = reloc.SymbolName.ToString() switch + // self-resolve them to the node's GlobalIndex here. They are intentionally absent from + // _definedSymbols. + if (!WasmBaseGlobalSymbolNode.TryGetGlobalIndexForSymbol(reloc.SymbolName.ToString(), out int globalIndex)) { - WasmBaseGlobalSymbolNode.StackPointerSymbolName => StackPointerGlobalIndex, - WasmBaseGlobalSymbolNode.ImageBaseSymbolName => ImageBaseGlobalIndex, - WasmBaseGlobalSymbolNode.TableBaseSymbolName => TableBaseGlobalIndex, - _ => throw new InvalidDataException($"Unexpected wasm base global symbol '{reloc.SymbolName}'") - }; + throw new InvalidDataException($"Unexpected wasm base global symbol '{reloc.SymbolName}'"); + } fixed (byte* pData = ReadRelocToDataSpan(reloc, relocScratchBuffer, sectionStart)) { diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 925b50d13db2d9..2fedda2a63a08f 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -4259,11 +4259,10 @@ private void recordRelocation(void* location, void* locationRW, void* target, Co int length; ref ArrayBuilder sourceBlock = ref findRelocBlock(locationBlock, out length); - int relocDelta = 0; - ISymbolNode relocTarget; - + int relocDelta; BlockType targetBlock = findKnownBlock(target, out relocDelta); + ISymbolNode relocTarget; switch (targetBlock) { case BlockType.Code: @@ -4294,7 +4293,7 @@ private void recordRelocation(void* location, void* locationRW, void* target, Co #endif default: - // Reloc points to something outside of the generated blocks. + // Reloc points to something outside of the generated blocks var targetObject = HandleToObject(target); #if READYTORUN From 9227ffb607d13ded4f953bb920d7db5ed0c65ff7 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 22 Jun 2026 14:04:18 -0700 Subject: [PATCH 07/14] Address PR review: append getWasmBaseGlobals last, guard wasm-only JIT fields - Move getWasmBaseGlobals to the end of ICorStaticInfo (after getWasmLowering) to preserve vtable slot ordering and minimize generated-wrapper churn; regenerated the thunk wrapper files via the ThunkGenerator. - Guard the wasm-only base-globals cache fields and inline accessor in Compiler with #if defined(TARGET_WASM), matching existing wasm-only fields, so non-wasm JIT builds don't carry the extra per-compilation state. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/inc/corinfo.h | 12 +++---- src/coreclr/inc/icorjitinfoimpl_generated.h | 6 ++-- src/coreclr/jit/ICorJitInfo_names_generated.h | 2 +- .../jit/ICorJitInfo_wrapper_generated.hpp | 16 +++++----- src/coreclr/jit/compiler.h | 2 ++ src/coreclr/jit/ee_il_dll.hpp | 2 ++ .../JitInterface/CorInfoImpl_generated.cs | 32 +++++++++---------- .../ThunkGenerator/ThunkInput.txt | 2 +- .../aot/jitinterface/jitinterface_generated.h | 18 +++++------ .../icorjitinfo_generated.cpp | 14 ++++---- .../icorjitinfo_generated.cpp | 12 +++---- 11 files changed, 61 insertions(+), 57 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index f3d407eaa211e0..275e09888f9d78 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -3138,12 +3138,6 @@ class ICorStaticInfo CORINFO_ASYNC_INFO* pAsyncInfoOut ) = 0; - // Get the well-known wasm base-global symbols (shadow stack pointer, image base, table base) - // that JIT-generated wasm code references via WASM_GLOBAL_INDEX_LEB relocations. - virtual void getWasmBaseGlobals( - CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut - ) = 0; - /*********************************************************************************/ // // Diagnostic methods @@ -3204,6 +3198,12 @@ class ICorStaticInfo // Returns the primitive type for passing/returning a Wasm struct by value, // or CORINFO_WASM_TYPE_VOID if passing/returning must be by reference. virtual CorInfoWasmType getWasmLowering(CORINFO_CLASS_HANDLE structHnd) = 0; + + // Get the well-known wasm base-global symbols (shadow stack pointer, image base, table base) + // that JIT-generated wasm code references via WASM_GLOBAL_INDEX_LEB relocations. + virtual void getWasmBaseGlobals( + CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut + ) = 0; }; /***************************************************************************** diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 5c7e5fbfb9b0dd..6670ca20b0c95e 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -498,9 +498,6 @@ void getEEInfo( void getAsyncInfo( CORINFO_ASYNC_INFO* pAsyncInfoOut) override; -void getWasmBaseGlobals( - CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) override; - mdMethodDef getMethodDefFromMethod( CORINFO_METHOD_HANDLE hMethod) override; @@ -535,6 +532,9 @@ void getFpStructLowering( CorInfoWasmType getWasmLowering( CORINFO_CLASS_HANDLE structHnd) override; +void getWasmBaseGlobals( + CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) override; + uint32_t getThreadTLSIndex( void** ppIndirection) override; diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index 57f3e9abfac682..190adc0d269f7b 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -124,7 +124,6 @@ DEF_CLR_API(runWithErrorTrap) DEF_CLR_API(runWithSPMIErrorTrap) DEF_CLR_API(getEEInfo) DEF_CLR_API(getAsyncInfo) -DEF_CLR_API(getWasmBaseGlobals) DEF_CLR_API(getMethodDefFromMethod) DEF_CLR_API(printMethodName) DEF_CLR_API(getMethodNameFromMetadata) @@ -133,6 +132,7 @@ DEF_CLR_API(getSystemVAmd64PassStructInRegisterDescriptor) DEF_CLR_API(getSwiftLowering) DEF_CLR_API(getFpStructLowering) DEF_CLR_API(getWasmLowering) +DEF_CLR_API(getWasmBaseGlobals) DEF_CLR_API(getThreadTLSIndex) DEF_CLR_API(getAddrOfCaptureThreadGlobal) DEF_CLR_API(getHelperFtn) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index 3c21aac6dada11..9a21b9c4c3465a 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -1179,14 +1179,6 @@ void WrapICorJitInfo::getAsyncInfo( API_LEAVE(getAsyncInfo); } -void WrapICorJitInfo::getWasmBaseGlobals( - CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) -{ - API_ENTER(getWasmBaseGlobals); - wrapHnd->getWasmBaseGlobals(pBaseGlobalsOut); - API_LEAVE(getWasmBaseGlobals); -} - mdMethodDef WrapICorJitInfo::getMethodDefFromMethod( CORINFO_METHOD_HANDLE hMethod) { @@ -1267,6 +1259,14 @@ CorInfoWasmType WrapICorJitInfo::getWasmLowering( return temp; } +void WrapICorJitInfo::getWasmBaseGlobals( + CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +{ + API_ENTER(getWasmBaseGlobals); + wrapHnd->getWasmBaseGlobals(pBaseGlobalsOut); + API_LEAVE(getWasmBaseGlobals); +} + uint32_t WrapICorJitInfo::getThreadTLSIndex( void** ppIndirection) { diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index d02396b87c7104..81f63d588a8c6b 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9475,10 +9475,12 @@ class Compiler CORINFO_ASYNC_INFO* eeGetAsyncInfo(); +#if defined(TARGET_WASM) CORINFO_WASM_BASE_GLOBALS wasmBaseGlobals; bool wasmBaseGlobalsInitialized = false; CORINFO_WASM_BASE_GLOBALS* eeGetWasmBaseGlobals(); +#endif // defined(TARGET_WASM) // Gets the offset of a SDArray's first element static unsigned eeGetArrayDataOffset(); diff --git a/src/coreclr/jit/ee_il_dll.hpp b/src/coreclr/jit/ee_il_dll.hpp index 9d33171f60f628..f2294d48e4c1ba 100644 --- a/src/coreclr/jit/ee_il_dll.hpp +++ b/src/coreclr/jit/ee_il_dll.hpp @@ -187,6 +187,7 @@ inline CORINFO_ASYNC_INFO* Compiler::eeGetAsyncInfo() return &asyncInfo; } +#if defined(TARGET_WASM) inline CORINFO_WASM_BASE_GLOBALS* Compiler::eeGetWasmBaseGlobals() { if (!wasmBaseGlobalsInitialized) @@ -197,6 +198,7 @@ inline CORINFO_WASM_BASE_GLOBALS* Compiler::eeGetWasmBaseGlobals() return &wasmBaseGlobals; } +#endif // defined(TARGET_WASM) /***************************************************************************** * diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 81bdec53fbc310..66a83195c61c43 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -140,7 +140,6 @@ static ICorJitInfoCallbacks() s_callbacks.runWithSPMIErrorTrap = &_runWithSPMIErrorTrap; s_callbacks.getEEInfo = &_getEEInfo; s_callbacks.getAsyncInfo = &_getAsyncInfo; - s_callbacks.getWasmBaseGlobals = &_getWasmBaseGlobals; s_callbacks.getMethodDefFromMethod = &_getMethodDefFromMethod; s_callbacks.printMethodName = &_printMethodName; s_callbacks.getMethodNameFromMetadata = &_getMethodNameFromMetadata; @@ -149,6 +148,7 @@ static ICorJitInfoCallbacks() s_callbacks.getSwiftLowering = &_getSwiftLowering; s_callbacks.getFpStructLowering = &_getFpStructLowering; s_callbacks.getWasmLowering = &_getWasmLowering; + s_callbacks.getWasmBaseGlobals = &_getWasmBaseGlobals; s_callbacks.getThreadTLSIndex = &_getThreadTLSIndex; s_callbacks.getAddrOfCaptureThreadGlobal = &_getAddrOfCaptureThreadGlobal; s_callbacks.getHelperFtn = &_getHelperFtn; @@ -322,7 +322,6 @@ static ICorJitInfoCallbacks() public delegate* unmanaged runWithSPMIErrorTrap; public delegate* unmanaged getEEInfo; public delegate* unmanaged getAsyncInfo; - public delegate* unmanaged getWasmBaseGlobals; public delegate* unmanaged getMethodDefFromMethod; public delegate* unmanaged printMethodName; public delegate* unmanaged getMethodNameFromMetadata; @@ -331,6 +330,7 @@ static ICorJitInfoCallbacks() public delegate* unmanaged getSwiftLowering; public delegate* unmanaged getFpStructLowering; public delegate* unmanaged getWasmLowering; + public delegate* unmanaged getWasmBaseGlobals; public delegate* unmanaged getThreadTLSIndex; public delegate* unmanaged getAddrOfCaptureThreadGlobal; public delegate* unmanaged getHelperFtn; @@ -2159,20 +2159,6 @@ private static void _getAsyncInfo(IntPtr thisHandle, IntPtr* ppException, CORINF } } - [UnmanagedCallersOnly] - private static void _getWasmBaseGlobals(IntPtr thisHandle, IntPtr* ppException, CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) - { - var _this = GetThis(thisHandle); - try - { - _this.getWasmBaseGlobals(ref *pBaseGlobalsOut); - } - catch (Exception ex) - { - *ppException = _this.AllocException(ex); - } - } - [UnmanagedCallersOnly] private static mdToken _getMethodDefFromMethod(IntPtr thisHandle, IntPtr* ppException, CORINFO_METHOD_STRUCT_* hMethod) { @@ -2291,6 +2277,20 @@ private static CorInfoWasmType _getWasmLowering(IntPtr thisHandle, IntPtr* ppExc } } + [UnmanagedCallersOnly] + private static void _getWasmBaseGlobals(IntPtr thisHandle, IntPtr* ppException, CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) + { + var _this = GetThis(thisHandle); + try + { + _this.getWasmBaseGlobals(ref *pBaseGlobalsOut); + } + catch (Exception ex) + { + *ppException = _this.AllocException(ex); + } + } + [UnmanagedCallersOnly] private static uint _getThreadTLSIndex(IntPtr thisHandle, IntPtr* ppException, void** ppIndirection) { diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index 24f7ea288fd0b5..c67d4e011434a7 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -294,7 +294,6 @@ FUNCTIONS [ManualNativeWrapper] bool runWithSPMIErrorTrap(ICorJitInfo::errorTrapFunction function, void* parameter); void getEEInfo(CORINFO_EE_INFO* pEEInfoOut); void getAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfoOut); - void getWasmBaseGlobals(CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut); mdMethodDef getMethodDefFromMethod(CORINFO_METHOD_HANDLE hMethod); size_t printMethodName(CORINFO_METHOD_HANDLE ftn, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize) const char* getMethodNameFromMetadata(CORINFO_METHOD_HANDLE ftn, const char **className, const char **namespaceName, const char **enclosingClassNames, size_t maxEnclosingClassNames); @@ -303,6 +302,7 @@ FUNCTIONS void getSwiftLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering); void getFpStructLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering); CorInfoWasmType getWasmLowering(CORINFO_CLASS_HANDLE structHnd); + void getWasmBaseGlobals(CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut); uint32_t getThreadTLSIndex(void **ppIndirection); int32_t * getAddrOfCaptureThreadGlobal(void **ppIndirection); void getHelperFtn (CorInfoHelpFunc ftnNum, CORINFO_CONST_LOOKUP* pNativeEntrypoint, CORINFO_METHOD_HANDLE *pMethod); diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index aa0f2f4665634d..9695a40d5bfbd5 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -131,7 +131,6 @@ struct JitInterfaceCallbacks bool (* runWithSPMIErrorTrap)(void * thisHandle, CorInfoExceptionClass** ppException, ICorJitInfo::errorTrapFunction function, void* parameter); void (* getEEInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_EE_INFO* pEEInfoOut); void (* getAsyncInfo)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_ASYNC_INFO* pAsyncInfoOut); - void (* getWasmBaseGlobals)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut); mdMethodDef (* getMethodDefFromMethod)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE hMethod); size_t (* printMethodName)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, char* buffer, size_t bufferSize, size_t* pRequiredBufferSize); const char* (* getMethodNameFromMetadata)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_METHOD_HANDLE ftn, const char** className, const char** namespaceName, const char** enclosingClassNames, size_t maxEnclosingClassNames); @@ -140,6 +139,7 @@ struct JitInterfaceCallbacks void (* getSwiftLowering)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering); void (* getFpStructLowering)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering); CorInfoWasmType (* getWasmLowering)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd); + void (* getWasmBaseGlobals)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut); uint32_t (* getThreadTLSIndex)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); int32_t* (* getAddrOfCaptureThreadGlobal)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); void (* getHelperFtn)(void * thisHandle, CorInfoExceptionClass** ppException, CorInfoHelpFunc ftnNum, CORINFO_CONST_LOOKUP* pNativeEntrypoint, CORINFO_METHOD_HANDLE* pMethod); @@ -1361,14 +1361,6 @@ class JitInterfaceWrapper : public ICorJitInfo if (pException != nullptr) throw pException; } - virtual void getWasmBaseGlobals( - CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) -{ - CorInfoExceptionClass* pException = nullptr; - _callbacks->getWasmBaseGlobals(_thisHandle, &pException, pBaseGlobalsOut); - if (pException != nullptr) throw pException; -} - virtual mdMethodDef getMethodDefFromMethod( CORINFO_METHOD_HANDLE hMethod) { @@ -1449,6 +1441,14 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } + virtual void getWasmBaseGlobals( + CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +{ + CorInfoExceptionClass* pException = nullptr; + _callbacks->getWasmBaseGlobals(_thisHandle, &pException, pBaseGlobalsOut); + if (pException != nullptr) throw pException; +} + virtual uint32_t getThreadTLSIndex( void** ppIndirection) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index 0b5c124b8db116..fb681401a79b3c 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -969,13 +969,6 @@ void interceptor_ICJI::getAsyncInfo( original_ICorJitInfo->getAsyncInfo(pAsyncInfoOut); } -void interceptor_ICJI::getWasmBaseGlobals( - CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) -{ - mcs->AddCall("getWasmBaseGlobals"); - original_ICorJitInfo->getWasmBaseGlobals(pBaseGlobalsOut); -} - mdMethodDef interceptor_ICJI::getMethodDefFromMethod( CORINFO_METHOD_HANDLE hMethod) { @@ -1042,6 +1035,13 @@ CorInfoWasmType interceptor_ICJI::getWasmLowering( return original_ICorJitInfo->getWasmLowering(structHnd); } +void interceptor_ICJI::getWasmBaseGlobals( + CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +{ + mcs->AddCall("getWasmBaseGlobals"); + original_ICorJitInfo->getWasmBaseGlobals(pBaseGlobalsOut); +} + uint32_t interceptor_ICJI::getThreadTLSIndex( void** ppIndirection) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index 07c192e8f758cd..af608de11944af 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -849,12 +849,6 @@ void interceptor_ICJI::getAsyncInfo( original_ICorJitInfo->getAsyncInfo(pAsyncInfoOut); } -void interceptor_ICJI::getWasmBaseGlobals( - CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) -{ - original_ICorJitInfo->getWasmBaseGlobals(pBaseGlobalsOut); -} - mdMethodDef interceptor_ICJI::getMethodDefFromMethod( CORINFO_METHOD_HANDLE hMethod) { @@ -913,6 +907,12 @@ CorInfoWasmType interceptor_ICJI::getWasmLowering( return original_ICorJitInfo->getWasmLowering(structHnd); } +void interceptor_ICJI::getWasmBaseGlobals( + CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +{ + original_ICorJitInfo->getWasmBaseGlobals(pBaseGlobalsOut); +} + uint32_t interceptor_ICJI::getThreadTLSIndex( void** ppIndirection) { From 71346dfeed7d7349327ae140ddf34a757a6d467d Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 25 Jun 2026 10:34:06 -0700 Subject: [PATCH 08/14] Rename WASM base globals to well-known globals; cache symbol nodes on NodeFactory Rename the JIT-EE getWasmBaseGlobals API and CORINFO_WASM_BASE_GLOBALS to getWasmWellKnownGlobals / CORINFO_WASM_WELLKNOWN_GLOBALS, and rename WasmBaseGlobalSymbolNode to WasmWellKnownGlobalSymbolNode. Replace the static symbol-node instances with a lazily-initialized, thread-safe cache on NodeFactory exposed via GetWasmGlobal(WasmWellKnownGlobal), so non-wasm compilations don't allocate the nodes. Simplify the JIT emitter by folding emitIns_BaseGlobalGet into emitIns_I with the IF_GLOBALIDX format on global.get/global.set. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/coreclr/inc/corinfo.h | 12 ++-- src/coreclr/inc/icorjitinfoimpl_generated.h | 4 +- src/coreclr/jit/ICorJitInfo_names_generated.h | 2 +- .../jit/ICorJitInfo_wrapper_generated.hpp | 10 +-- src/coreclr/jit/codegenwasm.cpp | 2 +- src/coreclr/jit/compiler.h | 6 +- src/coreclr/jit/ee_il_dll.hpp | 10 +-- src/coreclr/jit/emitwasm.cpp | 59 ++--------------- src/coreclr/jit/emitwasm.h | 5 -- src/coreclr/jit/instrswasm.h | 38 +++++------ .../DependencyAnalysis/NodeFactory.Wasm.cs | 34 ++++++++++ ...de.cs => WasmWellKnownGlobalSymbolNode.cs} | 64 ++++++++----------- .../Compiler/ObjectWriter/WasmObjectWriter.cs | 24 +++---- .../tools/Common/JitInterface/CorInfoImpl.cs | 9 +-- .../JitInterface/CorInfoImpl_generated.cs | 8 +-- .../tools/Common/JitInterface/CorInfoTypes.cs | 6 +- .../ThunkGenerator/ThunkInput.txt | 4 +- .../ILCompiler.Compiler.csproj | 3 +- .../TestCases/R2RTestSuites.cs | 16 ++--- .../TestCases/Webcil/WasmWebcilModule.cs | 4 +- .../TestCasesRunner/R2RResultChecker.cs | 16 ++--- .../ReadyToRunCodegenNodeFactory.cs | 2 +- .../ILCompiler.ReadyToRun.csproj | 3 +- .../aot/jitinterface/jitinterface_generated.h | 8 +-- .../tools/superpmi/superpmi-shared/agnostic.h | 2 +- .../tools/superpmi/superpmi-shared/lwmlist.h | 2 +- .../superpmi-shared/methodcontext.cpp | 28 ++++---- .../superpmi/superpmi-shared/methodcontext.h | 8 +-- .../superpmi-shim-collector/icorjitinfo.cpp | 8 +-- .../icorjitinfo_generated.cpp | 8 +-- .../icorjitinfo_generated.cpp | 6 +- .../tools/superpmi/superpmi/icorjitinfo.cpp | 6 +- src/coreclr/vm/jitinterface.cpp | 2 +- 33 files changed, 196 insertions(+), 223 deletions(-) create mode 100644 src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs rename src/coreclr/tools/Common/Compiler/DependencyAnalysis/{WasmBaseGlobalSymbolNode.cs => WasmWellKnownGlobalSymbolNode.cs} (55%) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index e40e682925e135..984757919bca55 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -1836,10 +1836,10 @@ struct CORINFO_ASYNC_INFO CORINFO_METHOD_HANDLE finishSuspensionWithContinuationContextMethHnd; }; -// The well-known wasm "base globals" that JIT-generated code references via +// The well-known wasm globals that JIT-generated code references via // WASM_GLOBAL_INDEX_LEB relocations. Each handle is the relocation target for the -// corresponding base global; the object writer resolves it to the final wasm global index. -struct CORINFO_WASM_BASE_GLOBALS +// corresponding well-known global; the object writer resolves it to the final wasm global index. +struct CORINFO_WASM_WELLKNOWN_GLOBALS { // Shadow stack pointer global (read at the root frame, then threaded through locals). CORINFO_WASM_GLOBAL_SYMBOL_HANDLE stackPointer; @@ -3204,10 +3204,10 @@ class ICorStaticInfo // or CORINFO_WASM_TYPE_VOID if passing/returning must be by reference. virtual CorInfoWasmType getWasmLowering(CORINFO_CLASS_HANDLE structHnd) = 0; - // Get the well-known wasm base-global symbols (shadow stack pointer, image base, table base) + // Get the well-known wasm global symbols (shadow stack pointer, image base, table base) // that JIT-generated wasm code references via WASM_GLOBAL_INDEX_LEB relocations. - virtual void getWasmBaseGlobals( - CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut + virtual void getWasmWellKnownGlobals( + CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut ) = 0; }; diff --git a/src/coreclr/inc/icorjitinfoimpl_generated.h b/src/coreclr/inc/icorjitinfoimpl_generated.h index 43b1a9d7e4fdc6..ae52be3e37e6c0 100644 --- a/src/coreclr/inc/icorjitinfoimpl_generated.h +++ b/src/coreclr/inc/icorjitinfoimpl_generated.h @@ -536,8 +536,8 @@ void getFpStructLowering( CorInfoWasmType getWasmLowering( CORINFO_CLASS_HANDLE structHnd) override; -void getWasmBaseGlobals( - CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) override; +void getWasmWellKnownGlobals( + CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut) override; uint32_t getThreadTLSIndex( void** ppIndirection) override; diff --git a/src/coreclr/jit/ICorJitInfo_names_generated.h b/src/coreclr/jit/ICorJitInfo_names_generated.h index 72182978da7c4a..31bbd02097b770 100644 --- a/src/coreclr/jit/ICorJitInfo_names_generated.h +++ b/src/coreclr/jit/ICorJitInfo_names_generated.h @@ -133,7 +133,7 @@ DEF_CLR_API(getSystemVAmd64PassStructInRegisterDescriptor) DEF_CLR_API(getSwiftLowering) DEF_CLR_API(getFpStructLowering) DEF_CLR_API(getWasmLowering) -DEF_CLR_API(getWasmBaseGlobals) +DEF_CLR_API(getWasmWellKnownGlobals) DEF_CLR_API(getThreadTLSIndex) DEF_CLR_API(getAddrOfCaptureThreadGlobal) DEF_CLR_API(getHelperFtn) diff --git a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp index 2642965cb1e40e..9c8da9f9d82dd8 100644 --- a/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp +++ b/src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp @@ -1269,12 +1269,12 @@ CorInfoWasmType WrapICorJitInfo::getWasmLowering( return temp; } -void WrapICorJitInfo::getWasmBaseGlobals( - CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +void WrapICorJitInfo::getWasmWellKnownGlobals( + CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut) { - API_ENTER(getWasmBaseGlobals); - wrapHnd->getWasmBaseGlobals(pBaseGlobalsOut); - API_LEAVE(getWasmBaseGlobals); + API_ENTER(getWasmWellKnownGlobals); + wrapHnd->getWasmWellKnownGlobals(pWellKnownGlobalsOut); + API_LEAVE(getWasmWellKnownGlobals); } uint32_t WrapICorJitInfo::getThreadTLSIndex( diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index b58c60ce20641d..5ff203b1ca74dd 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -153,7 +153,7 @@ void CodeGen::genAllocLclFrame(unsigned frameSize, regNumber initReg, bool* pIni if (!m_compiler->lvaGetDesc(m_compiler->lvaWasmSpArg)->lvIsParam) { initialSPLclIndex = spLclIndex; - GetEmitter()->emitIns_BaseGlobalGet(STACK_POINTER_GLOBAL); + GetEmitter()->emitIns_I(INS_global_get, EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)(size_t)m_compiler->eeGetWasmWellKnownGlobals()->stackPointer); GetEmitter()->emitIns_I(INS_local_set, EA_PTRSIZE, initialSPLclIndex); } else diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index e609d3fa4f8ba8..714817b9a741c0 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9474,10 +9474,10 @@ class Compiler CORINFO_ASYNC_INFO* eeGetAsyncInfo(); #if defined(TARGET_WASM) - CORINFO_WASM_BASE_GLOBALS wasmBaseGlobals; - bool wasmBaseGlobalsInitialized = false; + CORINFO_WASM_WELLKNOWN_GLOBALS wasmWellKnownGlobals; + bool wasmWellKnownGlobalsInitialized = false; - CORINFO_WASM_BASE_GLOBALS* eeGetWasmBaseGlobals(); + CORINFO_WASM_WELLKNOWN_GLOBALS* eeGetWasmWellKnownGlobals(); #endif // defined(TARGET_WASM) // Gets the offset of a SDArray's first element diff --git a/src/coreclr/jit/ee_il_dll.hpp b/src/coreclr/jit/ee_il_dll.hpp index f2294d48e4c1ba..9784b39b5b7157 100644 --- a/src/coreclr/jit/ee_il_dll.hpp +++ b/src/coreclr/jit/ee_il_dll.hpp @@ -188,15 +188,15 @@ inline CORINFO_ASYNC_INFO* Compiler::eeGetAsyncInfo() } #if defined(TARGET_WASM) -inline CORINFO_WASM_BASE_GLOBALS* Compiler::eeGetWasmBaseGlobals() +inline CORINFO_WASM_WELLKNOWN_GLOBALS* Compiler::eeGetWasmWellKnownGlobals() { - if (!wasmBaseGlobalsInitialized) + if (!wasmWellKnownGlobalsInitialized) { - info.compCompHnd->getWasmBaseGlobals(&wasmBaseGlobals); - wasmBaseGlobalsInitialized = true; + info.compCompHnd->getWasmWellKnownGlobals(&wasmWellKnownGlobals); + wasmWellKnownGlobalsInitialized = true; } - return &wasmBaseGlobals; + return &wasmWellKnownGlobals; } #endif // defined(TARGET_WASM) diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index eb9d9717e25d88..749a03b938da15 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -8,7 +8,7 @@ #include "codegen.h" -// Well-known wasm base globals, referenced by the JIT via WASM_GLOBAL_INDEX_LEB relocations. +// Well-known wasm globals, referenced by the JIT via WASM_GLOBAL_INDEX_LEB relocations. // These fixed indices match the ABI shared with the object writer (see WasmAbiConstants / // WasmObjectWriter): 0 = stack pointer, 1 = image base (__memory_base), 2 = table base (__table_base). static const unsigned WASM_STACK_POINTER_GLOBAL = 0; @@ -106,54 +106,6 @@ void emitter::emitIns_I(instruction ins, emitAttr attr, cnsval_ssize_t imm) appendToCurIG(id); } -//------------------------------------------------------------------------ -// emitIns_BaseGlobalGet: Emit a 'global.get' of a well-known base global as a -// WASM_GLOBAL_INDEX_LEB relocation against that base global's symbol. -// -// Arguments: -// baseGlobalIndex - the fixed wasm global index of the base global (0/1/2) -// -// Notes: -// The JIT only ever reads these base globals (the shadow stack pointer is read once at the root -// frame and then threaded through locals; the image base and table base are immutable relocation -// bases set by the loader), so only 'global.get' is supported. -// The bare immediate is replaced by a maximally-padded, relocatable global index. The object -// writer resolves the relocation to the final global index: crossgen2/R2R self-resolves it back -// to the same fixed index, while a relocatable NativeAOT object leaves it for wasm-ld to assign. -// The relocation target is the base global's symbol handle, obtained from the EE via -// getWasmBaseGlobals; the object writer maps that symbol to the corresponding global index. -// -void emitter::emitIns_BaseGlobalGet(unsigned baseGlobalIndex) -{ - assert(baseGlobalIndex <= WASM_TABLE_BASE_GLOBAL); - - // Resolve the base global to its symbol handle, which becomes the relocation target. - CORINFO_WASM_BASE_GLOBALS* baseGlobals = m_compiler->eeGetWasmBaseGlobals(); - - CORINFO_WASM_GLOBAL_SYMBOL_HANDLE symbol; - switch (baseGlobalIndex) - { - case WASM_STACK_POINTER_GLOBAL: - symbol = baseGlobals->stackPointer; - break; - case WASM_IMAGE_BASE_GLOBAL: - symbol = baseGlobals->imageBase; - break; - case WASM_TABLE_BASE_GLOBAL: - symbol = baseGlobals->tableBase; - break; - default: - unreached(); - } - - instrDesc* id = emitNewInstrSC(EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)(size_t)symbol); - id->idIns(INS_global_get); - id->idInsFmt(IF_GLOBALIDX); - - dispIns(id); - appendToCurIG(id); -} - //------------------------------------------------------------------------ // emitIns_J: Emit a jump instruction with an immediate operand. // @@ -243,7 +195,7 @@ bool emitter::emitInsIsStore(instruction ins) void emitter::emitAddressConstant(void* address) { // Load our module base from the image base global, then load our address constant, then sum them. - emitIns_BaseGlobalGet(WASM_IMAGE_BASE_GLOBAL); + emitIns_I(INS_global_get, EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)(size_t)m_compiler->eeGetWasmWellKnownGlobals()->imageBase); emitIns_I(INS_i32_const_address, EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG), (cnsval_ssize_t)address); emitIns(INS_i32_add); } @@ -251,7 +203,7 @@ void emitter::emitAddressConstant(void* address) void emitter::emitFuncletAddressConstant(cnsval_ssize_t funcletId) { // Load our table base, then load our funclet pointer offset, then sum them. - emitIns_BaseGlobalGet(WASM_TABLE_BASE_GLOBAL); + emitIns_I(INS_global_get, EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)(size_t)m_compiler->eeGetWasmWellKnownGlobals()->tableBase); emitIns_I(INS_i32_const_funcletptr, EA_PTRSIZE, (cnsval_ssize_t)funcletId); emitIns(INS_i32_add); } @@ -722,11 +674,8 @@ unsigned emitter::instrDesc::idCodeSize() const } case IF_FUNCIDX: case IF_ULEB128: - size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this)); - break; case IF_GLOBALIDX: - // Base global references are always emitted as relocations. - size += PADDED_RELOC_SIZE; + size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this)); break; case IF_MEMADDR: case IF_FUNCPTR: diff --git a/src/coreclr/jit/emitwasm.h b/src/coreclr/jit/emitwasm.h index 2ea3fad0837c78..b5bbff12e7cc8d 100644 --- a/src/coreclr/jit/emitwasm.h +++ b/src/coreclr/jit/emitwasm.h @@ -40,11 +40,6 @@ void emitIns_MemargLane(instruction ins, emitAttr attr, cnsval_ssize_t offset, u void emitAddressConstant(void* address); void emitFuncletAddressConstant(cnsval_ssize_t funcletId); -// Emit a 'global.get' of one of the well-known base globals (stack pointer, image base, table -// base) as a WASM_GLOBAL_INDEX_LEB relocation against that base global's symbol. The JIT only -// reads these globals, so only 'global.get' is supported. -void emitIns_BaseGlobalGet(unsigned baseGlobalIndex); - static unsigned SizeOfULEB128(uint64_t value); static unsigned SizeOfSLEB128(int64_t value); static uint8_t GetWasmValueTypeCode(WasmValueType type); diff --git a/src/coreclr/jit/instrswasm.h b/src/coreclr/jit/instrswasm.h index 90e87b177e7cba..34c60c1517e518 100644 --- a/src/coreclr/jit/instrswasm.h +++ b/src/coreclr/jit/instrswasm.h @@ -56,25 +56,25 @@ INST(call_funclet, "call.funclet", 0, IF_FUNCLETIDX, 0x10) INST(drop, "drop", 0, IF_OPCODE, 0x1A) INST(try_table, "try_table", 0, IF_TRY_TABLE, 0x1F) -INST(local_get, "local.get", 0, IF_ULEB128, 0x20) -INST(local_set, "local.set", 0, IF_ULEB128, 0x21) -INST(local_tee, "local.tee", 0, IF_ULEB128, 0x22) -INST(global_get, "global.get", 0, IF_ULEB128, 0x23) -INST(global_set, "global.set", 0, IF_ULEB128, 0x24) -INST(i32_load, "i32.load", 0, IF_MEMARG, 0x28) -INST(i64_load, "i64.load", 0, IF_MEMARG, 0x29) -INST(f32_load, "f32.load", 0, IF_MEMARG, 0x2A) -INST(f64_load, "f64.load", 0, IF_MEMARG, 0x2B) -INST(i32_load8_s, "i32.load8_s", 0, IF_MEMARG, 0x2C) -INST(i32_load8_u, "i32.load8_u", 0, IF_MEMARG, 0x2D) -INST(i32_load16_s, "i32.load16_s", 0, IF_MEMARG, 0x2E) -INST(i32_load16_u, "i32.load16_u", 0, IF_MEMARG, 0x2F) -INST(i64_load8_s, "i64.load8_s", 0, IF_MEMARG, 0x30) -INST(i64_load8_u, "i64.load8_u", 0, IF_MEMARG, 0x31) -INST(i64_load16_s, "i64.load16_s", 0, IF_MEMARG, 0x32) -INST(i64_load16_u, "i64.load16_u", 0, IF_MEMARG, 0x33) -INST(i64_load32_s, "i64.load32_s", 0, IF_MEMARG, 0x34) -INST(i64_load32_u, "i64.load32_u", 0, IF_MEMARG, 0x35) +INST(local_get, "local.get", 0, IF_ULEB128, 0x20) +INST(local_set, "local.set", 0, IF_ULEB128, 0x21) +INST(local_tee, "local.tee", 0, IF_ULEB128, 0x22) +INST(global_get, "global.get", 0, IF_GLOBALIDX, 0x23) +INST(global_set, "global.set", 0, IF_GLOBALIDX, 0x24) +INST(i32_load, "i32.load", 0, IF_MEMARG, 0x28) +INST(i64_load, "i64.load", 0, IF_MEMARG, 0x29) +INST(f32_load, "f32.load", 0, IF_MEMARG, 0x2A) +INST(f64_load, "f64.load", 0, IF_MEMARG, 0x2B) +INST(i32_load8_s, "i32.load8_s", 0, IF_MEMARG, 0x2C) +INST(i32_load8_u, "i32.load8_u", 0, IF_MEMARG, 0x2D) +INST(i32_load16_s, "i32.load16_s", 0, IF_MEMARG, 0x2E) +INST(i32_load16_u, "i32.load16_u", 0, IF_MEMARG, 0x2F) +INST(i64_load8_s, "i64.load8_s", 0, IF_MEMARG, 0x30) +INST(i64_load8_u, "i64.load8_u", 0, IF_MEMARG, 0x31) +INST(i64_load16_s, "i64.load16_s", 0, IF_MEMARG, 0x32) +INST(i64_load16_u, "i64.load16_u", 0, IF_MEMARG, 0x33) +INST(i64_load32_s, "i64.load32_s", 0, IF_MEMARG, 0x34) +INST(i64_load32_u, "i64.load32_u", 0, IF_MEMARG, 0x35) INST(i32_store, "i32.store", 0, IF_MEMARG, 0x36) INST(i64_store, "i64.store", 0, IF_MEMARG, 0x37) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs new file mode 100644 index 00000000000000..15672fa83c490b --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs @@ -0,0 +1,34 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Threading; + +namespace ILCompiler.DependencyAnalysis +{ + public partial class NodeFactory + { + // The well-known wasm globals are canonical, immutable relocation targets. They are created + // lazily on first access (only wasm targets ever request them) so non-wasm compilations don't + // pay the allocation. Indexed by WasmWellKnownGlobal. + private WasmWellKnownGlobalSymbolNode[] _wasmWellKnownGlobals; + + public WasmWellKnownGlobalSymbolNode GetWasmGlobal(WasmWellKnownGlobal global) + { + WasmWellKnownGlobalSymbolNode[] globals = _wasmWellKnownGlobals; + if (globals is null) + { + globals = + [ + new WasmWellKnownGlobalSymbolNode(WasmWellKnownGlobal.StackPointer), + new WasmWellKnownGlobalSymbolNode(WasmWellKnownGlobal.ImageBase), + new WasmWellKnownGlobalSymbolNode(WasmWellKnownGlobal.TableBase), + ]; + + // Ensure a single canonical array wins across threads; a loser's nodes are never handed out. + globals = Interlocked.CompareExchange(ref _wasmWellKnownGlobals, globals, null) ?? globals; + } + + return globals[(int)global]; + } + } +} diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmBaseGlobalSymbolNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs similarity index 55% rename from src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmBaseGlobalSymbolNode.cs rename to src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs index cd1fd17f37e5ca..96346ff8577fc3 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmBaseGlobalSymbolNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs @@ -10,8 +10,18 @@ namespace ILCompiler.DependencyAnalysis { + // Fixed wasm global indices, matching the ABI shared with the object writer + // (see WasmAbiConstants / WasmObjectWriter and the JIT's emitwasm.cpp, as well as the WebCIL spec). + + public enum WasmWellKnownGlobal + { + StackPointer = 0, + ImageBase = 1, + TableBase = 2, + } + // - // Represents one of the well-known wasm "base globals" referenced by JIT-generated code: + // Represents one of the well-known wasm globals referenced by JIT-generated code: // the shadow stack pointer, the image base (__memory_base) and the table base (__table_base). // These are imported globals whose final index is assigned by the linker, so the JIT references // them via WASM_GLOBAL_INDEX_LEB relocations rather than bare immediates. This node is the @@ -19,57 +29,39 @@ namespace ILCompiler.DependencyAnalysis // wasm global index (crossgen2/R2R self-resolves it back to the fixed index, while a relocatable // NativeAOT object emits it as an undefined imported global for wasm-ld to resolve). // - public sealed class WasmBaseGlobalSymbolNode : SortableDependencyNode, ISortableSymbolNode + public sealed class WasmWellKnownGlobalSymbolNode : SortableDependencyNode, ISortableSymbolNode { - // Fixed wasm global indices, matching the ABI shared with the object writer - // (see WasmAbiConstants / WasmObjectWriter and the JIT's emitwasm.cpp, as well as the WebCIL spec). - public const int StackPointerGlobalIndex = 0; - public const int ImageBaseGlobalIndex = 1; - public const int TableBaseGlobalIndex = 2; - - // Well-known symbol names for the base globals (standard wasm tool-conventions names). + // Well-known symbol names for the globals (standard wasm tool-conventions names). public const string StackPointerSymbolName = "__stack_pointer"; public const string ImageBaseSymbolName = "__memory_base"; public const string TableBaseSymbolName = "__table_base"; - private static readonly WasmBaseGlobalSymbolNode s_stackPointer = new(StackPointerGlobalIndex); - private static readonly WasmBaseGlobalSymbolNode s_imageBase = new(ImageBaseGlobalIndex); - private static readonly WasmBaseGlobalSymbolNode s_tableBase = new(TableBaseGlobalIndex); + private readonly WasmWellKnownGlobal _globalId; - private readonly int _globalIndex; - - private WasmBaseGlobalSymbolNode(int globalIndex) + internal WasmWellKnownGlobalSymbolNode(WasmWellKnownGlobal global) { - _globalIndex = globalIndex; + _globalId = global; } private static readonly Dictionary s_symbolNameToGlobalIndex = new() { - { StackPointerSymbolName, StackPointerGlobalIndex }, - { ImageBaseSymbolName, ImageBaseGlobalIndex }, - { TableBaseSymbolName, TableBaseGlobalIndex }, - }; - - public static WasmBaseGlobalSymbolNode GetForIndex(int globalIndex) => globalIndex switch - { - StackPointerGlobalIndex => s_stackPointer, - ImageBaseGlobalIndex => s_imageBase, - TableBaseGlobalIndex => s_tableBase, - _ => throw new ArgumentOutOfRangeException(nameof(globalIndex)) + { StackPointerSymbolName, (int)WasmWellKnownGlobal.StackPointer }, + { ImageBaseSymbolName, (int)WasmWellKnownGlobal.ImageBase }, + { TableBaseSymbolName, (int)WasmWellKnownGlobal.TableBase }, }; - // Maps a well-known base-global symbol name back to its fixed wasm global index. This is the + // Maps a well-known global symbol name back to its fixed wasm global index. This is the // authoritative mapping used by the object writer to resolve WASM_GLOBAL_INDEX_LEB relocations. public static bool TryGetGlobalIndexForSymbol(string symbolName, out int globalIndex) => s_symbolNameToGlobalIndex.TryGetValue(symbolName, out globalIndex); - public int GlobalIndex => _globalIndex; + public WasmWellKnownGlobal GlobalId => _globalId; - public string SymbolName => _globalIndex switch + public string SymbolName => _globalId switch { - StackPointerGlobalIndex => StackPointerSymbolName, - ImageBaseGlobalIndex => ImageBaseSymbolName, - TableBaseGlobalIndex => TableBaseSymbolName, + WasmWellKnownGlobal.StackPointer => StackPointerSymbolName, + WasmWellKnownGlobal.ImageBase => ImageBaseSymbolName, + WasmWellKnownGlobal.TableBase => TableBaseSymbolName, _ => throw new InvalidOperationException() }; @@ -78,20 +70,20 @@ public static bool TryGetGlobalIndexForSymbol(string symbolName, out int globalI public int Offset => 0; public bool RepresentsIndirectionCell => false; - public override int ClassCode => 0x57_42_47_53; // "WBGS" + public override int ClassCode => 0x57_57_47_53; // "WWGS" public override bool InterestingForDynamicDependencyAnalysis => false; public override bool HasDynamicDependencies => false; public override bool HasConditionalStaticDependencies => false; public override bool StaticDependenciesAreComputed => true; - protected override string GetName(NodeFactory factory) => $"Wasm Base Global: {SymbolName}"; + protected override string GetName(NodeFactory factory) => $"Wasm Well-Known Global: {SymbolName}"; public override IEnumerable GetStaticDependencies(NodeFactory factory) => null; public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) - => _globalIndex.CompareTo(((WasmBaseGlobalSymbolNode)other)._globalIndex); + => _globalId.CompareTo(((WasmWellKnownGlobalSymbolNode)other)._globalId); } } diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index 3fa894ae32e1a1..818c626354033e 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -156,7 +156,7 @@ private WasmFuncType GetFuncletType(FuncletKind funcletKind, WasmValueType point _ => new WasmFuncType(new([pointerType, pointerType]), new([])), // (FP, SP) -> void }; } - + private void WriteSignatureIndexForFunction(MethodSignature managedSignature, WasmLowering.LoweringFlags flags, ISymbolNode node) { SectionWriter writer = GetOrCreateSection(WasmObjectNodeSection.FunctionSection); @@ -556,7 +556,7 @@ private int NextGlobalIndex() private Dictionary _definedGlobals = new(); // TODO-Wasm: In the future, we may want to consider representing Wasm globals in the dependency graph so that they - // can be referenced by other nodes and we can make effective use of them. + // can be referenced by other nodes and we can make effective use of them. private void WriteGlobal(SectionWriter writer, string name, WasmValueType valueType, WasmMutabilityType mutability, WasmInstructionGroup initExpr) { WasmGlobal global = new WasmGlobal( @@ -767,7 +767,7 @@ private protected override void EmitObjectFile(Stream outputFileStream) WasmDataSegment webcilContentsSegment = new WasmDataSegment(webcilStream, new Utf8String("webcilPayload"), WasmDataSectionType.Passive, null); - // Create combined data section and emit + // Create combined data section and emit WasmDataSection dataSection = new WasmDataSection([webcilSizeSegment, webcilContentsSegment], new Utf8String("data"), contentAlign: 4); dataSection.Emit(outputFileStream); #endif @@ -874,14 +874,14 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr if (reloc.Type == RelocType.WASM_GLOBAL_INDEX_LEB) { - // The JIT references the well-known wasm base globals (stack pointer / image base / - // table base) via WASM_GLOBAL_INDEX_LEB relocations against the WasmBaseGlobalSymbolNode. + // The JIT references the well-known wasm globals (stack pointer / image base / + // table base) via WASM_GLOBAL_INDEX_LEB relocations against the WasmWellKnownGlobalSymbolNode. // For R2R these globals live at fixed indices supplied by the runtime loader, so we // self-resolve them to the node's GlobalIndex here. They are intentionally absent from // _definedSymbols. - if (!WasmBaseGlobalSymbolNode.TryGetGlobalIndexForSymbol(reloc.SymbolName.ToString(), out int globalIndex)) + if (!WasmWellKnownGlobalSymbolNode.TryGetGlobalIndexForSymbol(reloc.SymbolName.ToString(), out int globalIndex)) { - throw new InvalidDataException($"Unexpected wasm base global symbol '{reloc.SymbolName}'"); + throw new InvalidDataException($"Unexpected wasm well-known global symbol '{reloc.SymbolName}'"); } fixed (byte* pData = ReadRelocToDataSpan(reloc, relocScratchBuffer, sectionStart)) @@ -971,7 +971,7 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr // i32.const // i32.add // i32.load 0 - // So, the relocated address value should always represent an offset relative to image base. + // So, the relocated address value should always represent an offset relative to image base. // This offset should ALWAYS be equal to the actual offset from image base at runtime, due to Webcil's // flag mapping if (symbolWebcilSection is null) @@ -987,7 +987,7 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr // These relocs should be for cases of the form: // global.get $imageBase // i32.load - // So, the relocated address value should always represent an offset relative to image base. + // So, the relocated address value should always represent an offset relative to image base. // This offset should ALWAYS be equal to the actual offset from image base at runtime, due to Webcil's // flag mapping if (symbolWebcilSection is null) @@ -1068,9 +1068,9 @@ private WasmImport[] CreateDefaultGlobalImports() return [ - new WasmImport("webcil", "stackPointer", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Mut), index: StackPointerGlobalIndex), - new WasmImport("webcil", "imageBase", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: ImageBaseGlobalIndex), - new WasmImport("webcil", "tableBase", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: TableBaseGlobalIndex), + new WasmImport("webcil", "stackPointer", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Mut), index: (int)WasmWellKnownGlobal.StackPointer), + new WasmImport("webcil", "imageBase", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: (int)WasmWellKnownGlobal.ImageBase), + new WasmImport("webcil", "tableBase", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: (int)WasmWellKnownGlobal.TableBase), new WasmImport("webcil", "table", import: new WasmTableImportType(), index: 0), new WasmImport("webcil", "rtlRestoreContextTag", import: new WasmTagImportType(rtlRestoreContextTagTypeIndex), index: RtlRestoreContextTagIndex), ]; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 7270a5378050ec..5859a0b914ee52 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3473,11 +3473,12 @@ private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut) pAsyncInfoOut.finishSuspensionWithContinuationContextMethHnd = ObjectToHandle(asyncHelpers.GetKnownMethod("FinishSuspensionWithContinuationContext"u8, null)); } - private void getWasmBaseGlobals(ref CORINFO_WASM_BASE_GLOBALS pBaseGlobalsOut) + private void getWasmWellKnownGlobals(ref CORINFO_WASM_WELLKNOWN_GLOBALS pWellKnownGlobalsOut) { - pBaseGlobalsOut.stackPointer = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(WasmBaseGlobalSymbolNode.GetForIndex(WasmBaseGlobalSymbolNode.StackPointerGlobalIndex)); - pBaseGlobalsOut.imageBase = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(WasmBaseGlobalSymbolNode.GetForIndex(WasmBaseGlobalSymbolNode.ImageBaseGlobalIndex)); - pBaseGlobalsOut.tableBase = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(WasmBaseGlobalSymbolNode.GetForIndex(WasmBaseGlobalSymbolNode.TableBaseGlobalIndex)); + NodeFactory factory = _compilation.NodeFactory; + pWellKnownGlobalsOut.stackPointer = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWasmGlobal(WasmWellKnownGlobal.StackPointer)); + pWellKnownGlobalsOut.imageBase = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWasmGlobal(WasmWellKnownGlobal.ImageBase)); + pWellKnownGlobalsOut.tableBase = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWasmGlobal(WasmWellKnownGlobal.TableBase)); } private CORINFO_METHOD_STRUCT_* getAwaitReturnCall(CORINFO_METHOD_STRUCT_* callerHandle, ref CORINFO_LOOKUP instArg) { diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs index 2238e006ba3c61..ade7cf1abf0224 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs @@ -149,7 +149,7 @@ static ICorJitInfoCallbacks() s_callbacks.getSwiftLowering = &_getSwiftLowering; s_callbacks.getFpStructLowering = &_getFpStructLowering; s_callbacks.getWasmLowering = &_getWasmLowering; - s_callbacks.getWasmBaseGlobals = &_getWasmBaseGlobals; + s_callbacks.getWasmWellKnownGlobals = &_getWasmWellKnownGlobals; s_callbacks.getThreadTLSIndex = &_getThreadTLSIndex; s_callbacks.getAddrOfCaptureThreadGlobal = &_getAddrOfCaptureThreadGlobal; s_callbacks.getHelperFtn = &_getHelperFtn; @@ -332,7 +332,7 @@ static ICorJitInfoCallbacks() public delegate* unmanaged getSwiftLowering; public delegate* unmanaged getFpStructLowering; public delegate* unmanaged getWasmLowering; - public delegate* unmanaged getWasmBaseGlobals; + public delegate* unmanaged getWasmWellKnownGlobals; public delegate* unmanaged getThreadTLSIndex; public delegate* unmanaged getAddrOfCaptureThreadGlobal; public delegate* unmanaged getHelperFtn; @@ -2295,12 +2295,12 @@ private static CorInfoWasmType _getWasmLowering(IntPtr thisHandle, IntPtr* ppExc } [UnmanagedCallersOnly] - private static void _getWasmBaseGlobals(IntPtr thisHandle, IntPtr* ppException, CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) + private static void _getWasmWellKnownGlobals(IntPtr thisHandle, IntPtr* ppException, CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut) { var _this = GetThis(thisHandle); try { - _this.getWasmBaseGlobals(ref *pBaseGlobalsOut); + _this.getWasmWellKnownGlobals(ref *pWellKnownGlobalsOut); } catch (Exception ex) { diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index d61e7e24a5c4b2..bbc265af07b3f0 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -973,10 +973,10 @@ public unsafe struct CORINFO_ASYNC_INFO public CORINFO_METHOD_STRUCT_* finishSuspensionWithContinuationContextMethHnd; } - // The well-known wasm "base globals" referenced by JIT-generated code via + // The well-known wasm globals referenced by JIT-generated code via // WASM_GLOBAL_INDEX_LEB relocations. Each handle is the relocation target for the - // corresponding base global; the object writer resolves it to the final wasm global index. - public unsafe struct CORINFO_WASM_BASE_GLOBALS + // corresponding well-known global; the object writer resolves it to the final wasm global index. + public unsafe struct CORINFO_WASM_WELLKNOWN_GLOBALS { // Shadow stack pointer global (read at the root frame, then threaded through locals). public CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_* stackPointer; diff --git a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt index b3a0ed4c4bce19..f208aac47f8eed 100644 --- a/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt +++ b/src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt @@ -84,7 +84,7 @@ CORINFO_RESOLVED_TOKEN*,ref CORINFO_RESOLVED_TOKEN CORINFO_RESOLVED_TOKEN_PTR,CORINFO_RESOLVED_TOKEN*,CORINFO_RESOLVED_TOKEN*,CORINFO_RESOLVED_TOKEN* CORINFO_EE_INFO*,ref CORINFO_EE_INFO CORINFO_ASYNC_INFO*,ref CORINFO_ASYNC_INFO -CORINFO_WASM_BASE_GLOBALS*,ref CORINFO_WASM_BASE_GLOBALS +CORINFO_WASM_WELLKNOWN_GLOBALS*,ref CORINFO_WASM_WELLKNOWN_GLOBALS CORINFO_TAILCALL_HELPERS*,ref CORINFO_TAILCALL_HELPERS CORINFO_SWIFT_LOWERING*,ref CORINFO_SWIFT_LOWERING CORINFO_FPSTRUCT_LOWERING*,ref CORINFO_FPSTRUCT_LOWERING @@ -303,7 +303,7 @@ FUNCTIONS void getSwiftLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering); void getFpStructLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering); CorInfoWasmType getWasmLowering(CORINFO_CLASS_HANDLE structHnd); - void getWasmBaseGlobals(CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut); + void getWasmWellKnownGlobals(CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut); uint32_t getThreadTLSIndex(void **ppIndirection); int32_t * getAddrOfCaptureThreadGlobal(void **ppIndirection); void getHelperFtn (CorInfoHelpFunc ftnNum, CORINFO_CONST_LOOKUP* pNativeEntrypoint, CORINFO_METHOD_HANDLE *pMethod); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index ef3458a905326c..3c383e1469f90e 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -327,7 +327,8 @@ - + + diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs index d6c5e3e18a6640..1880cb129d404a 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs @@ -95,27 +95,27 @@ static void Validate(ReadyToRunReader reader) List methods = R2RAssert.GetAllMethods(reader); Assert.True(methods.Exists(method => method.SignatureString.Contains("AddIntegers", StringComparison.Ordinal))); - // Reads static data, so the JIT materializes the image base via a base-global global.get. + // Reads static data, so the JIT materializes the image base via a well-known-global global.get. Assert.True(methods.Exists(method => method.SignatureString.Contains("SumStaticData", StringComparison.Ordinal))); - // Has a try/finally, so the JIT materializes the table base via a base-global global.get. + // Has a try/finally, so the JIT materializes the table base via a well-known-global global.get. Assert.True(methods.Exists(method => method.SignatureString.Contains("SumWithFinally", StringComparison.Ordinal))); - // The wasm JIT references the ABI base globals via maximally padded WASM_GLOBAL_INDEX_LEB + // The wasm JIT references the ABI well-known globals via maximally padded WASM_GLOBAL_INDEX_LEB // relocations that the R2R object writer must self-resolve back to the fixed global // indices. Verify the emitted code contains a correctly self-resolved 'global.get' for the // image base (1, materialized by static-data reads in SumStaticData) and the table base // (2, materialized by the try/finally funclet path in SumWithFinally). Each pattern encodes // the exact resolved index, so a regression in self-resolution changes it (or makes - // crossgen2 throw while emitting the method). The stack-pointer base global is passed to + // crossgen2 throw while emitting the method). The stack-pointer well-known global is passed to // managed methods as a parameter in R2R, so it is not referenced via 'global.get' here. const int ImageBaseGlobal = 1; const int TableBaseGlobal = 2; - Assert.True(R2RAssert.WasmImageContainsBaseGlobalGet(webcilReader, ImageBaseGlobal), - "Expected a 'global.get' of the wasm image-base base global in the emitted code."); - Assert.True(R2RAssert.WasmImageContainsBaseGlobalGet(webcilReader, TableBaseGlobal), - "Expected a 'global.get' of the wasm table-base base global in the emitted code."); + Assert.True(R2RAssert.WasmImageContainsWellKnownGlobalGet(webcilReader, ImageBaseGlobal), + "Expected a 'global.get' of the wasm image-base well-known global in the emitted code."); + Assert.True(R2RAssert.WasmImageContainsWellKnownGlobalGet(webcilReader, TableBaseGlobal), + "Expected a 'global.get' of the wasm table-base well-known global in the emitted code."); } } diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs index b04f8c360289e3..cb92e6501f9f71 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs @@ -14,7 +14,7 @@ public static int AddIntegers(int left, int right) } // Reads static data, which forces the JIT to materialize the image-base address via a - // 'global.get' of the wasm image-base base global. That global is referenced through a + // 'global.get' of the wasm image-base well-known global. That global is referenced through a // WASM_GLOBAL_INDEX_LEB relocation the R2R object writer must self-resolve back to the fixed // image-base global index; if that resolution regresses, the emitted 'global.get' encoding // changes (or crossgen2 throws while emitting this method). @@ -25,7 +25,7 @@ public static int SumStaticData(int index) } // A try/finally makes the JIT emit a call to the 'finally' funclet (genCallFinally), which - // computes the funclet's address from the wasm table-base base global via a 'global.get'. + // computes the funclet's address from the wasm table-base well-known global via a 'global.get'. // Like the image base, that table-base global is referenced through a WASM_GLOBAL_INDEX_LEB // relocation the R2R object writer must self-resolve back to the fixed table-base global // index; if that resolution regresses, the emitted 'global.get' encoding changes (or diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs index 4d04d97eeff3f3..2b99eb39691e1d 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs @@ -38,12 +38,12 @@ public static List GetAllMethods(ReadyToRunReader reader) /// /// Returns true if any WASM function body in the image contains a global.get of the - /// given ABI base-global index, emitted as a maximally padded 5-byte + /// given ABI well-known-global index, emitted as a maximally padded 5-byte /// WASM_GLOBAL_INDEX_LEB reference (the global.get opcode 0x23 followed /// by the 5-byte padded ULEB128 of the index). /// /// - /// The wasm JIT references only the three ABI base globals (0 = stack pointer, 1 = image base, + /// The wasm JIT references only the three ABI well-known globals (0 = stack pointer, 1 = image base, /// 2 = table base) in this padded form; ordinary global.get instructions use the minimal /// LEB128 encoding. The R2R object writer self-resolves the relocation in place, so after /// compilation the padded slot holds the fixed index, e.g. image base -> @@ -51,19 +51,19 @@ public static List GetAllMethods(ReadyToRunReader reader) /// smoke check for that self-resolution: it scans raw instruction bytes and does not decode /// wasm instruction boundaries. /// - public static bool WasmImageContainsBaseGlobalGet(WebcilImageReader reader, int baseGlobalIndex) + public static bool WasmImageContainsWellKnownGlobalGet(WebcilImageReader reader, int wellKnownGlobalIndex) { - // The base globals are 0/1/2, which all fit in a single ULEB128 payload byte. The padded + // The well-known globals are 0/1/2, which all fit in a single ULEB128 payload byte. The padded // encoding below only writes that single payload byte, so it is correct for indices <= 0x7F. - Debug.Assert((uint)baseGlobalIndex <= 0x7F, - $"Only single-byte base-global indices are supported; got {baseGlobalIndex}."); + Debug.Assert((uint)wellKnownGlobalIndex <= 0x7F, + $"Only single-byte well-known-global indices are supported; got {wellKnownGlobalIndex}."); - // global.get (0x23) followed by the 5-byte padded ULEB128 of baseGlobalIndex. Padding sets + // global.get (0x23) followed by the 5-byte padded ULEB128 of wellKnownGlobalIndex. Padding sets // the continuation bit on the first four bytes and clears the last, so a small index N // encodes as (N | 0x80), 0x80, 0x80, 0x80, 0x00. Span pattern = stackalloc byte[6]; pattern[0] = 0x23; - pattern[1] = (byte)((baseGlobalIndex & 0x7F) | 0x80); + pattern[1] = (byte)((wellKnownGlobalIndex & 0x7F) | 0x80); pattern[2] = 0x80; pattern[3] = 0x80; pattern[4] = 0x80; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs index 5a5a45b5240c38..9ffa090773ed39 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/DependencyAnalysis/ReadyToRunCodegenNodeFactory.cs @@ -73,7 +73,7 @@ public struct NodeFactoryOptimizationFlags // To make the code future compatible to the composite R2R story // do NOT attempt to pass and store _inputModule here - public sealed class NodeFactory + public sealed partial class NodeFactory { private bool _markingComplete; diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 7dc6c30641b3f4..73232818673c43 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -105,7 +105,8 @@ - + + diff --git a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h index 0ba357b12336ab..ba3e3ab363fd69 100644 --- a/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h +++ b/src/coreclr/tools/aot/jitinterface/jitinterface_generated.h @@ -140,7 +140,7 @@ struct JitInterfaceCallbacks void (* getSwiftLowering)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd, CORINFO_SWIFT_LOWERING* pLowering); void (* getFpStructLowering)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering); CorInfoWasmType (* getWasmLowering)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE structHnd); - void (* getWasmBaseGlobals)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut); + void (* getWasmWellKnownGlobals)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut); uint32_t (* getThreadTLSIndex)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); int32_t* (* getAddrOfCaptureThreadGlobal)(void * thisHandle, CorInfoExceptionClass** ppException, void** ppIndirection); void (* getHelperFtn)(void * thisHandle, CorInfoExceptionClass** ppException, CorInfoHelpFunc ftnNum, CORINFO_CONST_LOOKUP* pNativeEntrypoint, CORINFO_METHOD_HANDLE* pMethod); @@ -1452,11 +1452,11 @@ class JitInterfaceWrapper : public ICorJitInfo return temp; } - virtual void getWasmBaseGlobals( - CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) + virtual void getWasmWellKnownGlobals( + CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut) { CorInfoExceptionClass* pException = nullptr; - _callbacks->getWasmBaseGlobals(_thisHandle, &pException, pBaseGlobalsOut); + _callbacks->getWasmWellKnownGlobals(_thisHandle, &pException, pWellKnownGlobalsOut); if (pException != nullptr) throw pException; } diff --git a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h index cc9eaa0cdc5978..0eca7864dc2eee 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/agnostic.h @@ -241,7 +241,7 @@ struct Agnostic_CORINFO_ASYNC_INFO DWORDLONG finishSuspensionWithContinuationContextMethHnd; }; -struct Agnostic_CORINFO_WASM_BASE_GLOBALS +struct Agnostic_CORINFO_WASM_WELLKNOWN_GLOBALS { DWORDLONG stackPointer; DWORDLONG imageBase; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h index f5370457f1134b..6e5efcda345c2c 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h @@ -173,7 +173,7 @@ LWM(GetUnmanagedCallConv, MethodOrSigInfoValue, DD) LWM(DoesFieldBelongToClass, DLDL, DWORD) DENSELWM(SigInstHandleMap, DWORDLONG) LWM(GetWasmTypeSymbol, Agnostic_GetWasmTypeSymbol, DWORDLONG) -LWM(GetWasmBaseGlobals, DWORD, Agnostic_CORINFO_WASM_BASE_GLOBALS) +LWM(GetWasmWellKnownGlobals, DWORD, Agnostic_CORINFO_WASM_WELLKNOWN_GLOBALS) #undef LWM #undef DENSELWM diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp index 94a8dfd8d7f8e1..d0527e9bc102a2 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp @@ -4425,33 +4425,33 @@ void MethodContext::repGetAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfoOut) DEBUG_REP(dmpGetAsyncInfo(0, value)); } -void MethodContext::recGetWasmBaseGlobals(const CORINFO_WASM_BASE_GLOBALS* pBaseGlobals) +void MethodContext::recGetWasmWellKnownGlobals(const CORINFO_WASM_WELLKNOWN_GLOBALS* pBaseGlobals) { - if (GetWasmBaseGlobals == nullptr) - GetWasmBaseGlobals = new LightWeightMap(); + if (GetWasmWellKnownGlobals == nullptr) + GetWasmWellKnownGlobals = new LightWeightMap(); - Agnostic_CORINFO_WASM_BASE_GLOBALS value; + Agnostic_CORINFO_WASM_WELLKNOWN_GLOBALS value; ZeroMemory(&value, sizeof(value)); value.stackPointer = CastHandle(pBaseGlobals->stackPointer); value.imageBase = CastHandle(pBaseGlobals->imageBase); value.tableBase = CastHandle(pBaseGlobals->tableBase); - GetWasmBaseGlobals->Add(0, value); - DEBUG_REC(dmpGetWasmBaseGlobals(0, value)); + GetWasmWellKnownGlobals->Add(0, value); + DEBUG_REC(dmpGetWasmWellKnownGlobals(0, value)); } -void MethodContext::dmpGetWasmBaseGlobals(DWORD key, const Agnostic_CORINFO_WASM_BASE_GLOBALS& value) +void MethodContext::dmpGetWasmWellKnownGlobals(DWORD key, const Agnostic_CORINFO_WASM_WELLKNOWN_GLOBALS& value) { - printf("GetWasmBaseGlobals key %u value stackPointer-%016" PRIX64 " imageBase-%016" PRIX64 " tableBase-%016" PRIX64, + printf("GetWasmWellKnownGlobals key %u value stackPointer-%016" PRIX64 " imageBase-%016" PRIX64 " tableBase-%016" PRIX64, key, value.stackPointer, value.imageBase, value.tableBase); } -void MethodContext::repGetWasmBaseGlobals(CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +void MethodContext::repGetWasmWellKnownGlobals(CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut) { - Agnostic_CORINFO_WASM_BASE_GLOBALS value = LookupByKeyOrMissNoMessage(GetWasmBaseGlobals, 0); - pBaseGlobalsOut->stackPointer = (CORINFO_WASM_GLOBAL_SYMBOL_HANDLE)value.stackPointer; - pBaseGlobalsOut->imageBase = (CORINFO_WASM_GLOBAL_SYMBOL_HANDLE)value.imageBase; - pBaseGlobalsOut->tableBase = (CORINFO_WASM_GLOBAL_SYMBOL_HANDLE)value.tableBase; - DEBUG_REP(dmpGetWasmBaseGlobals(0, value)); + Agnostic_CORINFO_WASM_WELLKNOWN_GLOBALS value = LookupByKeyOrMissNoMessage(GetWasmWellKnownGlobals, 0); + pWellKnownGlobalsOut->stackPointer = (CORINFO_WASM_GLOBAL_SYMBOL_HANDLE)value.stackPointer; + pWellKnownGlobalsOut->imageBase = (CORINFO_WASM_GLOBAL_SYMBOL_HANDLE)value.imageBase; + pWellKnownGlobalsOut->tableBase = (CORINFO_WASM_GLOBAL_SYMBOL_HANDLE)value.tableBase; + DEBUG_REP(dmpGetWasmWellKnownGlobals(0, value)); } void MethodContext::recGetAwaitReturnCall(CORINFO_METHOD_HANDLE callerHnd, CORINFO_LOOKUP* instArg, CORINFO_METHOD_HANDLE methHnd) { diff --git a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h index fe4eba65e8e38a..d3f0828f8acd01 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h @@ -563,9 +563,9 @@ class MethodContext void dmpGetAwaitReturnCall(DWORDLONG key, Agnostic_GetAwaitReturnCallResult& value); CORINFO_METHOD_HANDLE repGetAwaitReturnCall(CORINFO_METHOD_HANDLE callerHnd, CORINFO_LOOKUP* instArg); - void recGetWasmBaseGlobals(const CORINFO_WASM_BASE_GLOBALS* pBaseGlobals); - void dmpGetWasmBaseGlobals(DWORD key, const Agnostic_CORINFO_WASM_BASE_GLOBALS& value); - void repGetWasmBaseGlobals(CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut); + void recGetWasmWellKnownGlobals(const CORINFO_WASM_WELLKNOWN_GLOBALS* pBaseGlobals); + void dmpGetWasmWellKnownGlobals(DWORD key, const Agnostic_CORINFO_WASM_WELLKNOWN_GLOBALS& value); + void repGetWasmWellKnownGlobals(CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut); void recGetGSCookie(GSCookie* pCookieVal, GSCookie** ppCookieVal); void dmpGetGSCookie(DWORD key, DLDL value); @@ -1218,7 +1218,7 @@ enum mcPackets Packet_GetWasmLowering = 236, Packet_GetAsyncOtherVariant = 237, Packet_GetAwaitReturnCall = 238, - Packet_GetWasmBaseGlobals = 239, + Packet_GetWasmWellKnownGlobals = 239, }; void SetDebugDumpVariables(); diff --git a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp index 2d8108db985fec..495c8e32863226 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp @@ -1382,11 +1382,11 @@ void interceptor_ICJI::getAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfo) mc->recGetAsyncInfo(pAsyncInfo); } -void interceptor_ICJI::getWasmBaseGlobals(CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +void interceptor_ICJI::getWasmWellKnownGlobals(CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut) { - mc->cr->AddCall("getWasmBaseGlobals"); - original_ICorJitInfo->getWasmBaseGlobals(pBaseGlobalsOut); - mc->recGetWasmBaseGlobals(pBaseGlobalsOut); + mc->cr->AddCall("getWasmWellKnownGlobals"); + original_ICorJitInfo->getWasmWellKnownGlobals(pWellKnownGlobalsOut); + mc->recGetWasmWellKnownGlobals(pWellKnownGlobalsOut); } CORINFO_METHOD_HANDLE interceptor_ICJI::getAwaitReturnCall(CORINFO_METHOD_HANDLE callerHandle, CORINFO_LOOKUP* instArg) { diff --git a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp index b7fe3b848b4af6..8daad8b656c174 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp @@ -1043,11 +1043,11 @@ CorInfoWasmType interceptor_ICJI::getWasmLowering( return original_ICorJitInfo->getWasmLowering(structHnd); } -void interceptor_ICJI::getWasmBaseGlobals( - CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +void interceptor_ICJI::getWasmWellKnownGlobals( + CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut) { - mcs->AddCall("getWasmBaseGlobals"); - original_ICorJitInfo->getWasmBaseGlobals(pBaseGlobalsOut); + mcs->AddCall("getWasmWellKnownGlobals"); + original_ICorJitInfo->getWasmWellKnownGlobals(pWellKnownGlobalsOut); } uint32_t interceptor_ICJI::getThreadTLSIndex( diff --git a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp index d78f9eb90d4422..58d1251f556ad9 100644 --- a/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp @@ -914,10 +914,10 @@ CorInfoWasmType interceptor_ICJI::getWasmLowering( return original_ICorJitInfo->getWasmLowering(structHnd); } -void interceptor_ICJI::getWasmBaseGlobals( - CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +void interceptor_ICJI::getWasmWellKnownGlobals( + CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut) { - original_ICorJitInfo->getWasmBaseGlobals(pBaseGlobalsOut); + original_ICorJitInfo->getWasmWellKnownGlobals(pWellKnownGlobalsOut); } uint32_t interceptor_ICJI::getThreadTLSIndex( diff --git a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp index 25081e679c50f4..b4b59f5f23f1f1 100644 --- a/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp +++ b/src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp @@ -1197,10 +1197,10 @@ void MyICJI::getAsyncInfo(CORINFO_ASYNC_INFO* pAsyncInfo) jitInstance->mc->repGetAsyncInfo(pAsyncInfo); } -void MyICJI::getWasmBaseGlobals(CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +void MyICJI::getWasmWellKnownGlobals(CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut) { - jitInstance->mc->cr->AddCall("getWasmBaseGlobals"); - jitInstance->mc->repGetWasmBaseGlobals(pBaseGlobalsOut); + jitInstance->mc->cr->AddCall("getWasmWellKnownGlobals"); + jitInstance->mc->repGetWasmWellKnownGlobals(pWellKnownGlobalsOut); } CORINFO_METHOD_HANDLE MyICJI::getAwaitReturnCall(CORINFO_METHOD_HANDLE callerHandle, CORINFO_LOOKUP* instArg) { diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 3b3abc8a1275f5..cf6eadf7960c1b 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -10142,7 +10142,7 @@ CORINFO_WASM_TYPE_SYMBOL_HANDLE CEEInfo::getWasmTypeSymbol( UNREACHABLE_RET(); } -void CEEInfo::getWasmBaseGlobals(CORINFO_WASM_BASE_GLOBALS* pBaseGlobalsOut) +void CEEInfo::getWasmWellKnownGlobals(CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut) { LIMITED_METHOD_CONTRACT; UNREACHABLE(); From 9a7d534484911dd2433cac920762bf5fcf212c0e Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 25 Jun 2026 16:13:46 -0700 Subject: [PATCH 09/14] Make WasmWellKnownGlobalNode inherit from ExternSymbolNode --- .../DependencyAnalysis/ExternSymbolNode.cs | 0 .../DependencyAnalysis/NodeFactory.Wasm.cs | 3 +- .../WasmWellKnownGlobalSymbolNode.cs | 114 ++++++++---------- .../Compiler/ObjectWriter/WasmObjectWriter.cs | 7 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 6 +- .../ILCompiler.Compiler.csproj | 2 +- .../ILCompiler.ReadyToRun.csproj | 1 + 7 files changed, 59 insertions(+), 74 deletions(-) rename src/coreclr/tools/{aot/ILCompiler.Compiler => Common}/Compiler/DependencyAnalysis/ExternSymbolNode.cs (100%) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternSymbolNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ExternSymbolNode.cs similarity index 100% rename from src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ExternSymbolNode.cs rename to src/coreclr/tools/Common/Compiler/DependencyAnalysis/ExternSymbolNode.cs diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs index 15672fa83c490b..25551e8f839d43 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs @@ -12,7 +12,7 @@ public partial class NodeFactory // pay the allocation. Indexed by WasmWellKnownGlobal. private WasmWellKnownGlobalSymbolNode[] _wasmWellKnownGlobals; - public WasmWellKnownGlobalSymbolNode GetWasmGlobal(WasmWellKnownGlobal global) + public WasmWellKnownGlobalSymbolNode GetWellKnownWasmGlobal(WasmWellKnownGlobal global) { WasmWellKnownGlobalSymbolNode[] globals = _wasmWellKnownGlobals; if (globals is null) @@ -24,7 +24,6 @@ public WasmWellKnownGlobalSymbolNode GetWasmGlobal(WasmWellKnownGlobal global) new WasmWellKnownGlobalSymbolNode(WasmWellKnownGlobal.TableBase), ]; - // Ensure a single canonical array wins across threads; a loser's nodes are never handed out. globals = Interlocked.CompareExchange(ref _wasmWellKnownGlobals, globals, null) ?? globals; } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs index 96346ff8577fc3..869ee94ca4e5e0 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs @@ -3,16 +3,14 @@ using System; using System.Collections.Generic; - -using ILCompiler.DependencyAnalysisFramework; - +using System.Diagnostics; +using ILCompiler.DependencyAnalysis; using Internal.Text; namespace ILCompiler.DependencyAnalysis { // Fixed wasm global indices, matching the ABI shared with the object writer // (see WasmAbiConstants / WasmObjectWriter and the JIT's emitwasm.cpp, as well as the WebCIL spec). - public enum WasmWellKnownGlobal { StackPointer = 0, @@ -20,70 +18,60 @@ public enum WasmWellKnownGlobal TableBase = 2, } - // - // Represents one of the well-known wasm globals referenced by JIT-generated code: - // the shadow stack pointer, the image base (__memory_base) and the table base (__table_base). - // These are imported globals whose final index is assigned by the linker, so the JIT references - // them via WASM_GLOBAL_INDEX_LEB relocations rather than bare immediates. This node is the - // relocation target carrying the well-known symbol name; the object writer maps it to the final - // wasm global index (crossgen2/R2R self-resolves it back to the fixed index, while a relocatable - // NativeAOT object emits it as an undefined imported global for wasm-ld to resolve). - // - public sealed class WasmWellKnownGlobalSymbolNode : SortableDependencyNode, ISortableSymbolNode + public static class WasmWellKnownGlobalExtensions { - // Well-known symbol names for the globals (standard wasm tool-conventions names). - public const string StackPointerSymbolName = "__stack_pointer"; - public const string ImageBaseSymbolName = "__memory_base"; - public const string TableBaseSymbolName = "__table_base"; + private static Utf8String StackPointerUtf8String = new Utf8String("__stack_pointer"u8); + private static Utf8String MemoryBaseUtf8String = new Utf8String("__memory_base"u8); + private static Utf8String TableBaseUtf8String = new Utf8String("__table_base"u8); - private readonly WasmWellKnownGlobal _globalId; - - internal WasmWellKnownGlobalSymbolNode(WasmWellKnownGlobal global) + extension(WasmWellKnownGlobal global) { - _globalId = global; + public string ToSymbolString() + { + return global switch + { + WasmWellKnownGlobal.StackPointer => "__stack_pointer", + WasmWellKnownGlobal.ImageBase => "__memory_base", + WasmWellKnownGlobal.TableBase => "__table_base", + _ => throw new UnreachableException() + }; + } + + public Utf8String ToSymbolName() + { + return global switch + { + WasmWellKnownGlobal.StackPointer => StackPointerUtf8String, + WasmWellKnownGlobal.ImageBase => MemoryBaseUtf8String, + WasmWellKnownGlobal.TableBase => TableBaseUtf8String, + _ => throw new UnreachableException() + }; + } + + public static WasmWellKnownGlobal FromSymbolName(string symbolName) + { + return symbolName switch + { + "__stack_pointer" => WasmWellKnownGlobal.StackPointer, + "__memory_base" => WasmWellKnownGlobal.ImageBase, + "__table_base" => WasmWellKnownGlobal.TableBase, + _ => throw new UnreachableException() + }; + } + public static WasmWellKnownGlobal FromSymbolName(Utf8String symbolName) => FromSymbolName(symbolName.ToString()); } + } - private static readonly Dictionary s_symbolNameToGlobalIndex = new() - { - { StackPointerSymbolName, (int)WasmWellKnownGlobal.StackPointer }, - { ImageBaseSymbolName, (int)WasmWellKnownGlobal.ImageBase }, - { TableBaseSymbolName, (int)WasmWellKnownGlobal.TableBase }, - }; - - // Maps a well-known global symbol name back to its fixed wasm global index. This is the - // authoritative mapping used by the object writer to resolve WASM_GLOBAL_INDEX_LEB relocations. - public static bool TryGetGlobalIndexForSymbol(string symbolName, out int globalIndex) - => s_symbolNameToGlobalIndex.TryGetValue(symbolName, out globalIndex); - - public WasmWellKnownGlobal GlobalId => _globalId; - - public string SymbolName => _globalId switch - { - WasmWellKnownGlobal.StackPointer => StackPointerSymbolName, - WasmWellKnownGlobal.ImageBase => ImageBaseSymbolName, - WasmWellKnownGlobal.TableBase => TableBaseSymbolName, - _ => throw new InvalidOperationException() - }; - - public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb) => sb.Append(SymbolName); - - public int Offset => 0; - public bool RepresentsIndirectionCell => false; - - public override int ClassCode => 0x57_57_47_53; // "WWGS" - - public override bool InterestingForDynamicDependencyAnalysis => false; - public override bool HasDynamicDependencies => false; - public override bool HasConditionalStaticDependencies => false; - public override bool StaticDependenciesAreComputed => true; - - protected override string GetName(NodeFactory factory) => $"Wasm Well-Known Global: {SymbolName}"; - - public override IEnumerable GetStaticDependencies(NodeFactory factory) => null; - public override IEnumerable GetConditionalStaticDependencies(NodeFactory factory) => null; - public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) => null; + /// + /// Represents one of the well-known wasm globals referenced by JIT-generated code. + /// These are imported globals whose final index is assigned by the ObjectWriter / wasm linker. + /// crossgen2/R2R resolves it back to the fixed index defined in the WebCIL format, while a relocatable + /// NativeAOT object emits it as an undefined imported global for wasm-ld to resolve. + /// + public class WasmWellKnownGlobalSymbolNode(WasmWellKnownGlobal _global) : ExternSymbolNode(_global.ToSymbolName()) + { + public override int ClassCode => -767379803; - public override int CompareToImpl(ISortableNode other, CompilerComparer comparer) - => _globalId.CompareTo(((WasmWellKnownGlobalSymbolNode)other)._globalId); + protected override string GetName(NodeFactory factory) => $"WasmWellKnownGlobal {this.ToString()}"; } } diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index 818c626354033e..b4b129c149564d 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -879,14 +879,11 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr // For R2R these globals live at fixed indices supplied by the runtime loader, so we // self-resolve them to the node's GlobalIndex here. They are intentionally absent from // _definedSymbols. - if (!WasmWellKnownGlobalSymbolNode.TryGetGlobalIndexForSymbol(reloc.SymbolName.ToString(), out int globalIndex)) - { - throw new InvalidDataException($"Unexpected wasm well-known global symbol '{reloc.SymbolName}'"); - } + var globalIndex = WasmWellKnownGlobal.FromSymbolName(reloc.SymbolName); fixed (byte* pData = ReadRelocToDataSpan(reloc, relocScratchBuffer, sectionStart)) { - Relocation.WriteValue(reloc.Type, pData, globalIndex); + Relocation.WriteValue(reloc.Type, pData, (int)globalIndex); WriteRelocFromDataSpan(reloc, pData, sectionStart); } diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 55ccc40977807a..03171d8d07c7f7 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3495,9 +3495,9 @@ private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut) private void getWasmWellKnownGlobals(ref CORINFO_WASM_WELLKNOWN_GLOBALS pWellKnownGlobalsOut) { NodeFactory factory = _compilation.NodeFactory; - pWellKnownGlobalsOut.stackPointer = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWasmGlobal(WasmWellKnownGlobal.StackPointer)); - pWellKnownGlobalsOut.imageBase = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWasmGlobal(WasmWellKnownGlobal.ImageBase)); - pWellKnownGlobalsOut.tableBase = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWasmGlobal(WasmWellKnownGlobal.TableBase)); + pWellKnownGlobalsOut.stackPointer = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWellKnownWasmGlobal(WasmWellKnownGlobal.StackPointer)); + pWellKnownGlobalsOut.imageBase = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWellKnownWasmGlobal(WasmWellKnownGlobal.ImageBase)); + pWellKnownGlobalsOut.tableBase = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWellKnownWasmGlobal(WasmWellKnownGlobal.TableBase)); } private CORINFO_METHOD_STRUCT_* getAwaitReturnCall(CORINFO_METHOD_STRUCT_* callerHandle, ref CORINFO_LOOKUP instArg) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index 3c383e1469f90e..ffc775dbaa2af3 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -305,6 +305,7 @@ + @@ -611,7 +612,6 @@ - diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index 73232818673c43..0f7a9800ca4519 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -83,6 +83,7 @@ + From 2def0509cb4e15ec92dce55d196f0f592be6d613 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 29 Jun 2026 14:49:11 -0700 Subject: [PATCH 10/14] Make symbol name the identity of WasmWellKnownGlobalSymbol Rather than use an enum defining the global index in WebCIL as the identity of the symbol and converting to and from symbol names, use the symbol name as the identity. WasmObjectWriter then handles the mapping from symbol to global table index, keeping that functionality encapsulated there. --- src/coreclr/jit/emitwasm.cpp | 9 +-- .../DependencyAnalysis/NodeFactory.Wasm.cs | 26 ++++---- .../WasmWellKnownGlobalSymbolNode.cs | 63 ++----------------- .../Compiler/ObjectWriter/WasmObjectWriter.cs | 21 +++++-- .../tools/Common/JitInterface/CorInfoImpl.cs | 6 +- 5 files changed, 39 insertions(+), 86 deletions(-) diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index 749a03b938da15..d57bb176e33724 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -8,13 +8,6 @@ #include "codegen.h" -// Well-known wasm globals, referenced by the JIT via WASM_GLOBAL_INDEX_LEB relocations. -// These fixed indices match the ABI shared with the object writer (see WasmAbiConstants / -// WasmObjectWriter): 0 = stack pointer, 1 = image base (__memory_base), 2 = table base (__table_base). -static const unsigned WASM_STACK_POINTER_GLOBAL = 0; -static const unsigned WASM_IMAGE_BASE_GLOBAL = 1; -static const unsigned WASM_TABLE_BASE_GLOBAL = 2; - // clang-format off /*static*/ const BYTE CodeGenInterface::instInfo[] = { @@ -191,7 +184,7 @@ bool emitter::emitInsIsStore(instruction ins) //------------------------------------------------------------------------ // emitAddressConstant: Emit a memory address constant, like an indirection cell. -// This will automatically make use of relocations and the module base (__r2r_start). +// This will automatically make use of relocations and the module base (imageBase). void emitter::emitAddressConstant(void* address) { // Load our module base from the image base global, then load our address constant, then sum them. diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs index 25551e8f839d43..f98d784bd0b4f6 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs @@ -1,33 +1,35 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Collections.Frozen; +using System.Collections.Generic; using System.Threading; +using Internal.Text; namespace ILCompiler.DependencyAnalysis { public partial class NodeFactory { - // The well-known wasm globals are canonical, immutable relocation targets. They are created + // The well-known wasm globals are immutable relocation targets. They are created // lazily on first access (only wasm targets ever request them) so non-wasm compilations don't - // pay the allocation. Indexed by WasmWellKnownGlobal. - private WasmWellKnownGlobalSymbolNode[] _wasmWellKnownGlobals; + // pay the allocation. + private FrozenDictionary _wasmWellKnownGlobals; - public WasmWellKnownGlobalSymbolNode GetWellKnownWasmGlobal(WasmWellKnownGlobal global) + public WasmWellKnownGlobalSymbolNode GetWellKnownWasmGlobalSymbol(Utf8String symbolName) { - WasmWellKnownGlobalSymbolNode[] globals = _wasmWellKnownGlobals; + FrozenDictionary globals = _wasmWellKnownGlobals; if (globals is null) { - globals = - [ - new WasmWellKnownGlobalSymbolNode(WasmWellKnownGlobal.StackPointer), - new WasmWellKnownGlobalSymbolNode(WasmWellKnownGlobal.ImageBase), - new WasmWellKnownGlobalSymbolNode(WasmWellKnownGlobal.TableBase), - ]; + globals = FrozenDictionary.Create([ + new(new(WasmWellKnownGlobalSymbolNode.StackPointerName), new WasmWellKnownGlobalSymbolNode(WasmWellKnownGlobalSymbolNode.StackPointerName)), + new(new(WasmWellKnownGlobalSymbolNode.ImageBaseName), new WasmWellKnownGlobalSymbolNode(WasmWellKnownGlobalSymbolNode.ImageBaseName)), + new(new(WasmWellKnownGlobalSymbolNode.TableBaseName), new WasmWellKnownGlobalSymbolNode(WasmWellKnownGlobalSymbolNode.TableBaseName)) + ]); globals = Interlocked.CompareExchange(ref _wasmWellKnownGlobals, globals, null) ?? globals; } - return globals[(int)global]; + return globals[symbolName]; } } } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs index 869ee94ca4e5e0..ee1bb5a3a5dac0 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs @@ -2,75 +2,24 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.Diagnostics; -using ILCompiler.DependencyAnalysis; using Internal.Text; namespace ILCompiler.DependencyAnalysis { - // Fixed wasm global indices, matching the ABI shared with the object writer - // (see WasmAbiConstants / WasmObjectWriter and the JIT's emitwasm.cpp, as well as the WebCIL spec). - public enum WasmWellKnownGlobal - { - StackPointer = 0, - ImageBase = 1, - TableBase = 2, - } - - public static class WasmWellKnownGlobalExtensions - { - private static Utf8String StackPointerUtf8String = new Utf8String("__stack_pointer"u8); - private static Utf8String MemoryBaseUtf8String = new Utf8String("__memory_base"u8); - private static Utf8String TableBaseUtf8String = new Utf8String("__table_base"u8); - - extension(WasmWellKnownGlobal global) - { - public string ToSymbolString() - { - return global switch - { - WasmWellKnownGlobal.StackPointer => "__stack_pointer", - WasmWellKnownGlobal.ImageBase => "__memory_base", - WasmWellKnownGlobal.TableBase => "__table_base", - _ => throw new UnreachableException() - }; - } - - public Utf8String ToSymbolName() - { - return global switch - { - WasmWellKnownGlobal.StackPointer => StackPointerUtf8String, - WasmWellKnownGlobal.ImageBase => MemoryBaseUtf8String, - WasmWellKnownGlobal.TableBase => TableBaseUtf8String, - _ => throw new UnreachableException() - }; - } - - public static WasmWellKnownGlobal FromSymbolName(string symbolName) - { - return symbolName switch - { - "__stack_pointer" => WasmWellKnownGlobal.StackPointer, - "__memory_base" => WasmWellKnownGlobal.ImageBase, - "__table_base" => WasmWellKnownGlobal.TableBase, - _ => throw new UnreachableException() - }; - } - public static WasmWellKnownGlobal FromSymbolName(Utf8String symbolName) => FromSymbolName(symbolName.ToString()); - } - } - /// /// Represents one of the well-known wasm globals referenced by JIT-generated code. /// These are imported globals whose final index is assigned by the ObjectWriter / wasm linker. /// crossgen2/R2R resolves it back to the fixed index defined in the WebCIL format, while a relocatable /// NativeAOT object emits it as an undefined imported global for wasm-ld to resolve. /// - public class WasmWellKnownGlobalSymbolNode(WasmWellKnownGlobal _global) : ExternSymbolNode(_global.ToSymbolName()) + public class WasmWellKnownGlobalSymbolNode(string symbolName) : ExternDataSymbolNode(new Utf8String(symbolName)) { - public override int ClassCode => -767379803; + public const string StackPointerName = "__stack_pointer"; + public const string ImageBaseName = "__memory_base"; + public const string TableBaseName = "__table_base"; + + public override int ClassCode => 0x79046cf9; protected override string GetName(NodeFactory factory) => $"WasmWellKnownGlobal {this.ToString()}"; } diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index b4b129c149564d..d50888e0fdf309 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -3,6 +3,7 @@ using System; using System.Buffers.Binary; +using System.Collections.Frozen; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -877,9 +878,11 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr // The JIT references the well-known wasm globals (stack pointer / image base / // table base) via WASM_GLOBAL_INDEX_LEB relocations against the WasmWellKnownGlobalSymbolNode. // For R2R these globals live at fixed indices supplied by the runtime loader, so we - // self-resolve them to the node's GlobalIndex here. They are intentionally absent from - // _definedSymbols. - var globalIndex = WasmWellKnownGlobal.FromSymbolName(reloc.SymbolName); + // self-resolve them here to the global indices defined in the WebCIL specification. + if (!_globalSymbolNameToGlobalIndex.TryGetValue(reloc.SymbolName, out var globalIndex)) + { + throw new NotImplementedException($"Unexpected global symbol: {reloc.SymbolName}"); + } fixed (byte* pData = ReadRelocToDataSpan(reloc, relocScratchBuffer, sectionStart)) { @@ -1059,15 +1062,21 @@ void WriteRelocFromDataSpan(SymbolicRelocation reloc, byte* pData, long sectionS new([]), new([])); + private static readonly FrozenDictionary _globalSymbolNameToGlobalIndex = FrozenDictionary.Create([ + new(new(WasmWellKnownGlobalSymbolNode.StackPointerName), StackPointerGlobalIndex), + new(new(WasmWellKnownGlobalSymbolNode.ImageBaseName), ImageBaseGlobalIndex), + new(new(WasmWellKnownGlobalSymbolNode.TableBaseName), TableBaseGlobalIndex) + ]); + private WasmImport[] CreateDefaultGlobalImports() { int rtlRestoreContextTagTypeIndex = RegisterSignature(RtlRestoreContextTagSignature); return [ - new WasmImport("webcil", "stackPointer", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Mut), index: (int)WasmWellKnownGlobal.StackPointer), - new WasmImport("webcil", "imageBase", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: (int)WasmWellKnownGlobal.ImageBase), - new WasmImport("webcil", "tableBase", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: (int)WasmWellKnownGlobal.TableBase), + new WasmImport("webcil", "stackPointer", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Mut), index: StackPointerGlobalIndex), + new WasmImport("webcil", "imageBase", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: ImageBaseGlobalIndex), + new WasmImport("webcil", "tableBase", import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: TableBaseGlobalIndex), new WasmImport("webcil", "table", import: new WasmTableImportType(), index: 0), new WasmImport("webcil", "rtlRestoreContextTag", import: new WasmTagImportType(rtlRestoreContextTagTypeIndex), index: RtlRestoreContextTagIndex), ]; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs index 03171d8d07c7f7..10d454710fa8f4 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs @@ -3495,9 +3495,9 @@ private void getAsyncInfo(ref CORINFO_ASYNC_INFO pAsyncInfoOut) private void getWasmWellKnownGlobals(ref CORINFO_WASM_WELLKNOWN_GLOBALS pWellKnownGlobalsOut) { NodeFactory factory = _compilation.NodeFactory; - pWellKnownGlobalsOut.stackPointer = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWellKnownWasmGlobal(WasmWellKnownGlobal.StackPointer)); - pWellKnownGlobalsOut.imageBase = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWellKnownWasmGlobal(WasmWellKnownGlobal.ImageBase)); - pWellKnownGlobalsOut.tableBase = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWellKnownWasmGlobal(WasmWellKnownGlobal.TableBase)); + pWellKnownGlobalsOut.stackPointer = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWellKnownWasmGlobalSymbol(new(WasmWellKnownGlobalSymbolNode.StackPointerName))); + pWellKnownGlobalsOut.imageBase = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWellKnownWasmGlobalSymbol(new(WasmWellKnownGlobalSymbolNode.ImageBaseName))); + pWellKnownGlobalsOut.tableBase = (CORINFO_WASM_GLOBAL_SYMBOL_STRUCT_*)ObjectToHandle(factory.GetWellKnownWasmGlobalSymbol(new(WasmWellKnownGlobalSymbolNode.TableBaseName))); } private CORINFO_METHOD_STRUCT_* getAwaitReturnCall(CORINFO_METHOD_STRUCT_* callerHandle, ref CORINFO_LOOKUP instArg) { From 2ec60458683f37955c7f8ec7aae7db5ba22752f7 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 30 Jun 2026 10:25:08 -0700 Subject: [PATCH 11/14] Jit format and delete unused STACK_POINTER_GLOBAL --- src/coreclr/jit/codegenwasm.cpp | 5 ++--- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/emitwasm.cpp | 6 ++++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp index ef122952ac01f4..061bb5f77e904a 100644 --- a/src/coreclr/jit/codegenwasm.cpp +++ b/src/coreclr/jit/codegenwasm.cpp @@ -13,8 +13,6 @@ #include "gcinfoencoder.h" static const int LINEAR_MEMORY_INDEX = 0; -// stackPointer is the 0th global in our generated Wasm modules -static const int STACK_POINTER_GLOBAL = 0; #ifdef TARGET_64BIT static const instruction INS_I_load = INS_i64_load; @@ -153,7 +151,8 @@ void CodeGen::genAllocLclFrame(unsigned frameSize, regNumber initReg, bool* pIni if (!m_compiler->lvaGetDesc(m_compiler->lvaWasmSpArg)->lvIsParam) { initialSPLclIndex = spLclIndex; - GetEmitter()->emitIns_I(INS_global_get, EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)(size_t)m_compiler->eeGetWasmWellKnownGlobals()->stackPointer); + GetEmitter()->emitIns_I(INS_global_get, EA_HANDLE_CNS_RELOC, + (cnsval_ssize_t)(size_t)m_compiler->eeGetWasmWellKnownGlobals()->stackPointer); GetEmitter()->emitIns_I(INS_local_set, EA_PTRSIZE, initialSPLclIndex); } else diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index d4377ed3d1c0c3..6402922a4e52b5 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -9497,7 +9497,7 @@ class Compiler #if defined(TARGET_WASM) CORINFO_WASM_WELLKNOWN_GLOBALS wasmWellKnownGlobals; - bool wasmWellKnownGlobalsInitialized = false; + bool wasmWellKnownGlobalsInitialized = false; CORINFO_WASM_WELLKNOWN_GLOBALS* eeGetWasmWellKnownGlobals(); #endif // defined(TARGET_WASM) diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp index d57bb176e33724..96021e7a895551 100644 --- a/src/coreclr/jit/emitwasm.cpp +++ b/src/coreclr/jit/emitwasm.cpp @@ -188,7 +188,8 @@ bool emitter::emitInsIsStore(instruction ins) void emitter::emitAddressConstant(void* address) { // Load our module base from the image base global, then load our address constant, then sum them. - emitIns_I(INS_global_get, EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)(size_t)m_compiler->eeGetWasmWellKnownGlobals()->imageBase); + emitIns_I(INS_global_get, EA_HANDLE_CNS_RELOC, + (cnsval_ssize_t)(size_t)m_compiler->eeGetWasmWellKnownGlobals()->imageBase); emitIns_I(INS_i32_const_address, EA_SET_FLG(EA_PTRSIZE, EA_CNS_RELOC_FLG), (cnsval_ssize_t)address); emitIns(INS_i32_add); } @@ -196,7 +197,8 @@ void emitter::emitAddressConstant(void* address) void emitter::emitFuncletAddressConstant(cnsval_ssize_t funcletId) { // Load our table base, then load our funclet pointer offset, then sum them. - emitIns_I(INS_global_get, EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)(size_t)m_compiler->eeGetWasmWellKnownGlobals()->tableBase); + emitIns_I(INS_global_get, EA_HANDLE_CNS_RELOC, + (cnsval_ssize_t)(size_t)m_compiler->eeGetWasmWellKnownGlobals()->tableBase); emitIns_I(INS_i32_const_funcletptr, EA_PTRSIZE, (cnsval_ssize_t)funcletId); emitIns(INS_i32_add); } From 2eb601b573b585d10c02252aa54b15dde51f21d7 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 7 Jul 2026 08:58:16 -0700 Subject: [PATCH 12/14] Update JIT-EE interfacae Guid --- src/coreclr/inc/jiteeversionguid.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/coreclr/inc/jiteeversionguid.h b/src/coreclr/inc/jiteeversionguid.h index e144d039afbcbb..3b40209c848edb 100644 --- a/src/coreclr/inc/jiteeversionguid.h +++ b/src/coreclr/inc/jiteeversionguid.h @@ -37,11 +37,11 @@ #include -constexpr GUID JITEEVersionIdentifier = { /* e35797c5-6e52-4457-82d8-344dde179269 */ - 0xe35797c5, - 0x6e52, - 0x4457, - {0x82, 0xd8, 0x34, 0x4d, 0xde, 0x17, 0x92, 0x69} +constexpr GUID JITEEVersionIdentifier = { /* 43ad2994-e0f8-4b2e-9501-d45fa7d8c18a */ + 0x43ad2994, + 0xe0f8, + 0x4b2e, + {0x95, 0x01, 0xd4, 0x5f, 0xa7, 0xd8, 0xc1, 0x8a} }; #endif // JIT_EE_VERSIONING_GUID_H From efbd9b27a6e3a82db98082f83ab12948a27887c5 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 8 Jul 2026 08:55:18 -0700 Subject: [PATCH 13/14] Update test to avoid optimization of try/catch and include method call in SumStaticData --- .../TestCases/Webcil/WasmWebcilModule.cs | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs index cb92e6501f9f71..f0822dba5f8518 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace Webcil; public static class WasmWebcilModule @@ -8,28 +10,29 @@ public static class WasmWebcilModule private static readonly int[] s_primes = new int[] { 3, 5, 7, 11, 13 }; private static int s_counter; + [MethodImpl(MethodImplOptions.NoInlining)] public static int AddIntegers(int left, int right) { return left + right; } - // Reads static data, which forces the JIT to materialize the image-base address via a - // 'global.get' of the wasm image-base well-known global. That global is referenced through a - // WASM_GLOBAL_INDEX_LEB relocation the R2R object writer must self-resolve back to the fixed - // image-base global index; if that resolution regresses, the emitted 'global.get' encoding - // changes (or crossgen2 throws while emitting this method). + // Reads static data, which forces the JIT to materialize the imageBase address via a + // 'global.get' of the wasm imageBase well-known global. public static int SumStaticData(int index) { s_counter += 1; - return s_primes[index] + s_counter; + return AddIntegers(s_primes[index], s_counter); } - // A try/finally makes the JIT emit a call to the 'finally' funclet (genCallFinally), which - // computes the funclet's address from the wasm table-base well-known global via a 'global.get'. - // Like the image base, that table-base global is referenced through a WASM_GLOBAL_INDEX_LEB - // relocation the R2R object writer must self-resolve back to the fixed table-base global - // index; if that resolution regresses, the emitted 'global.get' encoding changes (or - // crossgen2 throws while emitting this method). + // An unoptimized try/finally makes the JIT emit a call to the 'finally' + // funclet (genCallFinally), which computes the funclet's address from the + // wasm table-base well-known global via a 'global.get'. Like the image + // base, that table-base global is referenced through a + // WASM_GLOBAL_INDEX_LEB relocation the R2R object writer must self-resolve + // back to the fixed table-base global index; if that resolution regresses, + // the emitted 'global.get' encoding changes (or crossgen2 throws while + // emitting this method). + [MethodImpl(MethodImplOptions.NoOptimization)] public static int SumWithFinally(int index) { int total = 0; From dbd7484824026a4fd81e9d286014fbdf769081f7 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 14 Jul 2026 15:53:14 -0700 Subject: [PATCH 14/14] Remove usings Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs | 1 - .../DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs | 2 -- 2 files changed, 3 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs index f98d784bd0b4f6..6cb6190471d7b8 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/NodeFactory.Wasm.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Frozen; -using System.Collections.Generic; using System.Threading; using Internal.Text; diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs index ee1bb5a3a5dac0..26f70551b642de 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Diagnostics; using Internal.Text; namespace ILCompiler.DependencyAnalysis