From 4854223ef9442c7d790336fa3e05a9f2ff6cd01c Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 9 Jul 2026 11:27:30 -0700 Subject: [PATCH 1/4] Make entries in wasm tables "symbol definitions" And use the symbol definitions for reloc lookups. This creates a single location for all named entries in each section of the module, and makes _uniqueSignatures, _uniqueSymbols, and _methodCount unnecessary. --- .../WasmWellKnownGlobalSymbolNode.cs | 6 + .../Compiler/ObjectWriter/ObjectWriter.cs | 2 +- .../Compiler/ObjectWriter/WasmObjectWriter.cs | 213 ++++++++++-------- 3 files changed, 120 insertions(+), 101 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs index 26f70551b642de..a934cddf07a8a3 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs @@ -13,9 +13,15 @@ namespace ILCompiler.DependencyAnalysis /// public class WasmWellKnownGlobalSymbolNode(string symbolName) : ExternDataSymbolNode(new Utf8String(symbolName)) { +#if READYTORUN + public const string StackPointerName = "stackPointer"; + public const string ImageBaseName = "memoryBase"; + public const string TableBaseName = "tableBase"; +#else public const string StackPointerName = "__stack_pointer"; public const string ImageBaseName = "__memory_base"; public const string TableBaseName = "__table_base"; +#endif public override int ClassCode => 0x79046cf9; diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs index c2cf6b7fdf4505..3d043038bce227 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs @@ -262,7 +262,7 @@ private protected virtual void EmitReferencedMethod(Utf8String symbolName) { } /// the order of the symbols so any necessary sorting is done when the /// symbol table is created. /// - protected internal void EmitSymbolDefinition( + protected internal virtual void EmitSymbolDefinition( int sectionIndex, Utf8String symbolName, long offset = 0, diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index d50888e0fdf309..f2a5e1f4ee94c1 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -3,13 +3,13 @@ using System; using System.Buffers.Binary; -using System.Collections.Frozen; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Numerics; using System.Text; +using System.Threading; using ILCompiler.DependencyAnalysis; using ILCompiler.DependencyAnalysis.Wasm; using ILCompiler.DependencyAnalysisFramework; @@ -77,9 +77,13 @@ private void EmitWasmHeader(Stream outputFileStream) outputFileStream.Write([0x1, 0x0, 0x0, 0x0]); } - private Dictionary _uniqueSignatures = new(); - private Dictionary _uniqueSymbols = new(); - private int _methodCount = 0; + // The wasm function index space. Every function (method, funclet, or stub) is registered + // into the function section via RegisterFunctionSymbol, so the number of symbols in that + // section is both the count of emitted functions and the next function index to assign. + private int MethodCount => + _sectionNameToIndex.TryGetValue(WasmObjectNodeSection.FunctionSection.Name, out int functionSectionIndex) + ? SectionSymbolCount(functionSectionIndex) + : 0; private Dictionary _wellKnownSymbols = new(); private protected override void RecordWellKnownSymbol(Utf8String currentSymbolName, SortableDependencyNode.ObjectNodeOrder classCode) @@ -99,9 +103,9 @@ private protected override void RecordMethodSignature(WasmTypeNode signature) var mangledNameBuilder = new Utf8StringBuilder(); signature.AppendMangledName(_nodeFactory.NameMangler, mangledNameBuilder); Utf8String mangledName = mangledNameBuilder.ToUtf8String(); - // Note that we do not expect duplicates here, crossgen should deduplicate signatures already - // using the node cache, so we can simply add the new signature with the next available index. - _uniqueSignatures.Add(mangledName, _uniqueSignatures.Count); + // Record the signature's wasm type index in the shared symbol table. The signature bytes + // are emitted by the node's own data; here we only assign its index. + EnsureSymbolIndex(mangledName, GetOrCreateSection(ObjectNodeSection.WasmTypeSection).SectionIndex); } private protected override void RecordMethodDeclaration(INodeWithTypeSignature node) @@ -120,8 +124,7 @@ private protected override void RecordMethodDeclaration(INodeWithTypeSignature n flags |= WasmLowering.LoweringFlags.IsUnmanagedCallersOnly; } WriteSignatureIndexForFunction(node.Signature, flags, node); - _uniqueSymbols.Add(node.GetMangledName(_nodeFactory.NameMangler), _methodCount); - _methodCount++; + RegisterFunctionSymbol(new Utf8String(node.GetMangledName(_nodeFactory.NameMangler))); if (node is INodeWithFunclets nodeWithFunclets) { RecordFunclets(nodeWithFunclets); @@ -142,8 +145,7 @@ private void RecordFunclets(INodeWithFunclets nodeWithFunclets) for (int i = 0; i < funcletKinds.Length; i++) { WasmFuncType funcletSignature = GetFuncletType(funcletKinds[i], pointerType); - _uniqueSymbols.Add($"{mangledNodeName}_funclet_{i}", _methodCount); - _methodCount++; + RegisterFunctionSymbol(new Utf8String($"{mangledNodeName}_funclet_{i}")); RegisterStubIndexAndSignature(funcletSignature); } } @@ -164,12 +166,12 @@ private void WriteSignatureIndexForFunction(MethodSignature managedSignature, Wa WasmFuncType signature = WasmLowering.GetSignature(managedSignature, flags).FuncType; Utf8String key = signature.GetMangledName(_nodeFactory.NameMangler); - if (!_uniqueSignatures.TryGetValue(key, out int signatureIndex)) + if (!_symbolDefinitions.TryGetValue(key, out var signatureSymbol)) { throw new InvalidOperationException($"Signature index of {key} not found for function: {node.ToString()}"); } - writer.WriteULEB128((ulong)signatureIndex); + writer.WriteULEB128((ulong)signatureSymbol.Index); } private int _numImports; @@ -189,6 +191,8 @@ private SectionWriter WriteImport(WasmImport import) Debug.Assert(bytesWritten == encodeSize); writer.Buffer.Advance((int)bytesWritten); + writer.EmitSymbolDefinition(new Utf8String(import.Name)); + _numImports++; return writer; } @@ -384,8 +388,7 @@ private void InsertWasmStub(Utf8String name, WasmFunctionBody body) body.Encode(data); codeWriter.EmitData(data); - _uniqueSymbols.Add(name.ToString(), _methodCount); - _methodCount++; + RegisterFunctionSymbol(name); RegisterStubIndexAndSignature(body.Signature); @@ -534,15 +537,15 @@ private void WriteDataCountSection() private WebcilSegment _webcilSegment = null; private protected override void EmitSectionsAndLayout() { - int totalMethodCount = _methodCount + 3; + int totalMethodCount = MethodCount + 3; InsertWasmStub(new Utf8String("getWebcilSize"), GetWebcilSize); InsertWasmStub(new Utf8String("getWebcilPayload"), GetWebcilPayload); InsertWasmStub(new Utf8String("fillWebcilTable"), FillWebcilTable(totalMethodCount)); - Debug.Assert(_methodCount == totalMethodCount); + Debug.Assert(MethodCount == totalMethodCount); WriteDataCountSection(); - PrependCount(SectionByName(ObjectNodeSection.WasmCodeSection.Name), _methodCount); + PrependCount(SectionByName(ObjectNodeSection.WasmCodeSection.Name), MethodCount); } private int _numDefinedGlobals = 0; @@ -759,7 +762,7 @@ private protected override void EmitObjectFile(Stream outputFileStream) // Create passive data segment for encoding the size of the webcil payload (size must fit in 32-bit uint) byte[] lengthBuffer = new byte[sizeof(uint) * 2]; BinaryPrimitives.WriteUInt32LittleEndian(lengthBuffer, (uint)_webcilSegment.GetFlatMappedSize()); - BinaryPrimitives.WriteUInt32LittleEndian(lengthBuffer.AsSpan().Slice(4), (uint)_uniqueSymbols.Count); + BinaryPrimitives.WriteUInt32LittleEndian(lengthBuffer.AsSpan().Slice(4), (uint)MethodCount); MemoryStream webcilSizeSegmentStream = new MemoryStream(lengthBuffer); WasmDataSegment webcilSizeSegment = new WasmDataSegment(webcilSizeSegmentStream, new Utf8String("webcilCount"), WasmDataSectionType.Passive, null); @@ -809,6 +812,46 @@ private protected override void EmitRelocations(int sectionIndex, List _sections[sectionIndex].GetNewSymbolIndex(); + + private int SectionSymbolCount(int sectionIndex) => _sections[sectionIndex].IndexCount; + + Dictionary _symbolDefinitions = new(); + record struct WasmIndexBasedSymbol(int SectionIndex, long Index, Utf8String Name); + + // Assigns (or returns the existing) wasm index for a symbol within the index space of the + // given object-writer section. Wasm functions, types and globals each live in a distinct + // index space, tracked here by the section the symbol is first registered in. Keeping the + // first registration ensures a later definition of the same symbol (e.g. the code-section + // body of a function already registered in the function section) does not reassign its index. + private int EnsureSymbolIndex(Utf8String symbolName, int sectionIndex) + { + // Keep-first: a symbol legitimately appears in two sections - its index-space section + // (function/type/import), where it is registered first, and later its definition section + // (e.g. the code section body of a function), which must NOT reassign its index. The kept + // section is therefore the index space the symbol belongs to. This relies on the wasm index + // spaces having disjoint symbol names (mangled method names vs mangled type keys vs the fixed + // well-known import names), so distinct symbols never collide on a name. + if (!_symbolDefinitions.TryGetValue(symbolName, out WasmIndexBasedSymbol symbol)) + { + symbol = new WasmIndexBasedSymbol(sectionIndex, GetNextIndexForSection(sectionIndex), symbolName); + _symbolDefinitions.Add(symbolName, symbol); + } + + return (int)symbol.Index; + } + + private int RegisterFunctionSymbol(Utf8String name) => + EnsureSymbolIndex(name, GetOrCreateSection(WasmObjectNodeSection.FunctionSection).SectionIndex); + + protected internal override void EmitSymbolDefinition(int sectionIndex, Utf8String symbolName, long offset = 0, int size = 0, bool global = false) + { + EnsureSymbolIndex(symbolName, sectionIndex); + base.EmitSymbolDefinition(sectionIndex, symbolName, offset, size, global); + } + /// /// Processes the deferred file-level relocations after webcil section VirtualAddresses /// have been assigned. Populates with page-grouped base reloc @@ -873,26 +916,6 @@ 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 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 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)) - { - Relocation.WriteValue(reloc.Type, pData, (int)globalIndex); - WriteRelocFromDataSpan(reloc, pData, sectionStart); - } - - continue; - } - SymbolDefinition definedSymbol = _definedSymbols[reloc.SymbolName]; // The virtual address of the relocation we are resolving @@ -926,16 +949,22 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr switch (reloc.Type) { case RelocType.WASM_TYPE_INDEX_LEB: + case RelocType.WASM_GLOBAL_INDEX_LEB: + case RelocType.WASM_TABLE_INDEX_I32: + case RelocType.WASM_TABLE_INDEX_I64: + case RelocType.WASM_TABLE_INDEX_SLEB: + case RelocType.WASM_TABLE_INDEX_REL_I32: + case RelocType.WASM_FUNCTION_INDEX_LEB: { - if (_uniqueSignatures.TryGetValue(reloc.SymbolName, out int index)) - { - Relocation.WriteValue(reloc.Type, pData, index); - } - else + // These relocations reference a wasm structural index (function, type, + // table entry, or well-known global). For R2R we self-resolve them here to + // the index assigned when the symbol was registered into its index space. + if (!_symbolDefinitions.TryGetValue(reloc.SymbolName, out var symbol)) { - throw new InvalidDataException($"Type signature symbol definition '{reloc.SymbolName}' not found"); + throw new NotImplementedException($"No wasm index registered for symbol '{reloc.SymbolName}' (relocation {reloc.Type})."); } + Relocation.WriteValue(reloc.Type, pData, (int)symbol.Index + addend); break; } @@ -998,30 +1027,6 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr Relocation.WriteValue(reloc.Type, pData, virtualSymbolImageOffset + addend); break; } - case RelocType.WASM_TABLE_INDEX_I32: - case RelocType.WASM_TABLE_INDEX_I64: - case RelocType.WASM_TABLE_INDEX_SLEB: - case RelocType.WASM_TABLE_INDEX_REL_I32: - { - string symbolName = reloc.SymbolName.ToString(); - int index = _uniqueSymbols[symbolName]; - // Here, we are effectively writing a table offset relative to the table_base. - // These will need to be fixed up by the runtime after load by adding tableBase - // except for WASM_TABLE_INDEX_REL_I32 and WASM_TABLE_INDEX_SLEB which are relative - // to the start of the table. - Relocation.WriteValue(reloc.Type, pData, index + addend); - break; - } - case RelocType.WASM_FUNCTION_INDEX_LEB: - { - string symbolName = reloc.SymbolName.ToString(); - int index = _uniqueSymbols[symbolName]; - - // These are module-local function pointer indices, so we can simply write out the assigned function index - // for this particular symbol - Relocation.WriteValue(reloc.Type, pData, index + addend); - break; - } case RelocType.WASM_CLR_RESTORE_CONTEXT_EXCEPTION_TAG_LEB: { Relocation.WriteValue(reloc.Type, pData, RtlRestoreContextTagIndex + addend); @@ -1062,21 +1067,15 @@ 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: 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", WasmWellKnownGlobalSymbolNode.StackPointerName, import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Mut), index: StackPointerGlobalIndex), + new WasmImport("webcil", WasmWellKnownGlobalSymbolNode.ImageBaseName, import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Const), index: ImageBaseGlobalIndex), + new WasmImport("webcil", WasmWellKnownGlobalSymbolNode.TableBaseName, 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), ]; @@ -1085,20 +1084,20 @@ private WasmImport[] CreateDefaultGlobalImports() private int RegisterSignature(WasmFuncType signature) { Utf8String signatureKey = signature.GetMangledName(_nodeFactory.NameMangler); - if (_uniqueSignatures.TryGetValue(signatureKey, out int signatureIndex)) + if (_symbolDefinitions.TryGetValue(signatureKey, out var signatureIndex)) { - return signatureIndex; + return (int)signatureIndex.Index; } - signatureIndex = _uniqueSignatures.Count; - _uniqueSignatures.Add(signatureKey, signatureIndex); - SectionWriter typeSectionWriter = GetOrCreateSection(ObjectNodeSection.WasmTypeSection); byte[] encodedSignature = new byte[signature.EncodeSize()]; signature.Encode(encodedSignature); typeSectionWriter.EmitData(encodedSignature); + // Assigns the type index (via the EmitSymbolDefinition override) and registers the + // signature in the defined-symbol table used during relocation resolution. + typeSectionWriter.EmitSymbolDefinition(signatureKey); - return signatureIndex; + return (int)_symbolDefinitions[signatureKey].Index; } private void WriteImports() @@ -1134,29 +1133,29 @@ private void WriteExports() Debug.Assert(_definedGlobals.ContainsKey("webcilVersion")); WriteGlobalExport("webcilVersion", _definedGlobals["webcilVersion"].Index); - string[] functionExports = _uniqueSymbols.Keys.ToArray(); + int functionSectionIndex = _sectionNameToIndex[WasmObjectNodeSection.FunctionSection.Name]; // TODO-WASM: Handle exports better (e.g., only export public methods, etc.) - // Also, see if we could leverage definedSymbols for this instead of doing our own bookkeeping in _uniqueSymbols. - foreach (string name in functionExports.OrderBy(name => name)) + IEnumerable functionSymbols = + _symbolDefinitions.Values.Where(symbol => symbol.SectionIndex == functionSectionIndex); + foreach (WasmIndexBasedSymbol symbol in functionSymbols.OrderBy(symbol => symbol.Name.ToString())) { - WriteFunctionExport(name, _uniqueSymbols[name]); + WriteFunctionExport(symbol.Name.ToString(), (int)symbol.Index); } } private Dictionary _definedSymbols; private void WriteElements() { - // Generate the function pointer table element that contains function pointers for all of our functions - int[] functionIndices = new int[_uniqueSymbols.Count]; - // NOTE: This relies on items in _uniqueSymbols being assigned sequentially and that iteration over Values is order-preserving. - // BCL Dictionary preserves insertion order so as long as we keep using it, we would get the function indices in the order they were added. - _uniqueSymbols.Values.CopyTo(functionIndices, 0); - // Enforce that the function pointers are sequential so that (image_function_pointer_base + 0) == ftn index 0 -#if DEBUG - for (int i = 0; i < _uniqueSymbols.Count; i++) { - Debug.Assert(functionIndices[i] == i); + // Generate the function pointer table element that contains function pointers for all of our functions. + // Function indices are assigned sequentially (0..MethodCount-1) so that + // (image_function_pointer_base + 0) == function index 0. + int methodCount = MethodCount; + int[] functionIndices = new int[methodCount]; + for (int i = 0; i < methodCount; i++) + { + functionIndices[i] = i; } -#endif + WriteRefFuncFunctionElement(functionIndices); } @@ -1175,10 +1174,10 @@ private protected override void EmitSymbolTable(IDictionary + /// The number of wasm indices allocated in this section's index space so far. + /// + public int IndexCount => _nextSymbolIndex; + + /// + /// Returns the next wasm index in this section's index space, and increments the counter for future calls. + /// + public int GetNewSymbolIndex() => Interlocked.Increment(ref _nextSymbolIndex) - 1; + public int? PrependCount = null; public int PrependCountSize => PrependCount.HasValue ? (int)DwarfHelper.SizeOfULEB128((ulong)PrependCount.Value) : 0; From 82c4ebc42fb68fd2adad97d631e30ff48c615d7e Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 15 Jul 2026 14:38:39 -0700 Subject: [PATCH 2/4] Rename wasm symbol index helper Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2b88048c-7bbb-40bb-ae53-e990e71779c0 --- .../Common/Compiler/ObjectWriter/WasmObjectWriter.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index f2a5e1f4ee94c1..3824fe8c0ec5fb 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -105,7 +105,7 @@ private protected override void RecordMethodSignature(WasmTypeNode signature) Utf8String mangledName = mangledNameBuilder.ToUtf8String(); // Record the signature's wasm type index in the shared symbol table. The signature bytes // are emitted by the node's own data; here we only assign its index. - EnsureSymbolIndex(mangledName, GetOrCreateSection(ObjectNodeSection.WasmTypeSection).SectionIndex); + GetOrCreateSymbolIndex(mangledName, GetOrCreateSection(ObjectNodeSection.WasmTypeSection).SectionIndex); } private protected override void RecordMethodDeclaration(INodeWithTypeSignature node) @@ -826,7 +826,7 @@ record struct WasmIndexBasedSymbol(int SectionIndex, long Index, Utf8String Name // index space, tracked here by the section the symbol is first registered in. Keeping the // first registration ensures a later definition of the same symbol (e.g. the code-section // body of a function already registered in the function section) does not reassign its index. - private int EnsureSymbolIndex(Utf8String symbolName, int sectionIndex) + private int GetOrCreateSymbolIndex(Utf8String symbolName, int sectionIndex) { // Keep-first: a symbol legitimately appears in two sections - its index-space section // (function/type/import), where it is registered first, and later its definition section @@ -844,11 +844,11 @@ private int EnsureSymbolIndex(Utf8String symbolName, int sectionIndex) } private int RegisterFunctionSymbol(Utf8String name) => - EnsureSymbolIndex(name, GetOrCreateSection(WasmObjectNodeSection.FunctionSection).SectionIndex); + GetOrCreateSymbolIndex(name, GetOrCreateSection(WasmObjectNodeSection.FunctionSection).SectionIndex); protected internal override void EmitSymbolDefinition(int sectionIndex, Utf8String symbolName, long offset = 0, int size = 0, bool global = false) { - EnsureSymbolIndex(symbolName, sectionIndex); + GetOrCreateSymbolIndex(symbolName, sectionIndex); base.EmitSymbolDefinition(sectionIndex, symbolName, offset, size, global); } From 2b84bf9f36d1b006c8d11aa8059fb9b53108981f Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 16 Jul 2026 13:06:06 -0700 Subject: [PATCH 3/4] "memoryBase" -> "imageBase" --- .../DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs index a934cddf07a8a3..27221d977228ca 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmWellKnownGlobalSymbolNode.cs @@ -15,7 +15,7 @@ public class WasmWellKnownGlobalSymbolNode(string symbolName) : ExternDataSymbol { #if READYTORUN public const string StackPointerName = "stackPointer"; - public const string ImageBaseName = "memoryBase"; + public const string ImageBaseName = "imageBase"; public const string TableBaseName = "tableBase"; #else public const string StackPointerName = "__stack_pointer"; From 4c55e3db5100d02acc572b210cb96faff700a33f Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 16 Jul 2026 16:46:09 -0700 Subject: [PATCH 4/4] Renames and doc comments --- .../Compiler/ObjectWriter/WasmObjectWriter.cs | 45 ++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs index 3824fe8c0ec5fb..241d700a79c76e 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs @@ -77,9 +77,9 @@ private void EmitWasmHeader(Stream outputFileStream) outputFileStream.Write([0x1, 0x0, 0x0, 0x0]); } - // The wasm function index space. Every function (method, funclet, or stub) is registered - // into the function section via RegisterFunctionSymbol, so the number of symbols in that - // section is both the count of emitted functions and the next function index to assign. + /// + /// The number of methods in the Function section. + /// private int MethodCount => _sectionNameToIndex.TryGetValue(WasmObjectNodeSection.FunctionSection.Name, out int functionSectionIndex) ? SectionSymbolCount(functionSectionIndex) @@ -105,7 +105,7 @@ private protected override void RecordMethodSignature(WasmTypeNode signature) Utf8String mangledName = mangledNameBuilder.ToUtf8String(); // Record the signature's wasm type index in the shared symbol table. The signature bytes // are emitted by the node's own data; here we only assign its index. - GetOrCreateSymbolIndex(mangledName, GetOrCreateSection(ObjectNodeSection.WasmTypeSection).SectionIndex); + GetOrCreateWasmSymbolDefinition(mangledName, GetOrCreateSection(ObjectNodeSection.WasmTypeSection).SectionIndex); } private protected override void RecordMethodDeclaration(INodeWithTypeSignature node) @@ -166,7 +166,7 @@ private void WriteSignatureIndexForFunction(MethodSignature managedSignature, Wa WasmFuncType signature = WasmLowering.GetSignature(managedSignature, flags).FuncType; Utf8String key = signature.GetMangledName(_nodeFactory.NameMangler); - if (!_symbolDefinitions.TryGetValue(key, out var signatureSymbol)) + if (!_wasmSymbolDefinitions.TryGetValue(key, out var signatureSymbol)) { throw new InvalidOperationException($"Signature index of {key} not found for function: {node.ToString()}"); } @@ -812,13 +812,18 @@ private protected override void EmitRelocations(int sectionIndex, List _sections[sectionIndex].GetNewSymbolIndex(); + private int GetNewIndexForSection(int sectionIndex) => _sections[sectionIndex].GetNewSymbolIndex(); private int SectionSymbolCount(int sectionIndex) => _sections[sectionIndex].IndexCount; - Dictionary _symbolDefinitions = new(); + private Dictionary _wasmSymbolDefinitions = new(); + + /// + /// A symbol definition that has a unique name and is assigned to an index within the index space of a specific section. + /// + /// The index of the section (index space) the symbol belongs to. + /// The index of the symbol within the section. + /// The unique name of the symbol. record struct WasmIndexBasedSymbol(int SectionIndex, long Index, Utf8String Name); // Assigns (or returns the existing) wasm index for a symbol within the index space of the @@ -826,7 +831,7 @@ record struct WasmIndexBasedSymbol(int SectionIndex, long Index, Utf8String Name // index space, tracked here by the section the symbol is first registered in. Keeping the // first registration ensures a later definition of the same symbol (e.g. the code-section // body of a function already registered in the function section) does not reassign its index. - private int GetOrCreateSymbolIndex(Utf8String symbolName, int sectionIndex) + private WasmIndexBasedSymbol GetOrCreateWasmSymbolDefinition(Utf8String symbolName, int sectionIndex) { // Keep-first: a symbol legitimately appears in two sections - its index-space section // (function/type/import), where it is registered first, and later its definition section @@ -834,21 +839,21 @@ private int GetOrCreateSymbolIndex(Utf8String symbolName, int sectionIndex) // section is therefore the index space the symbol belongs to. This relies on the wasm index // spaces having disjoint symbol names (mangled method names vs mangled type keys vs the fixed // well-known import names), so distinct symbols never collide on a name. - if (!_symbolDefinitions.TryGetValue(symbolName, out WasmIndexBasedSymbol symbol)) + if (!_wasmSymbolDefinitions.TryGetValue(symbolName, out WasmIndexBasedSymbol symbol)) { - symbol = new WasmIndexBasedSymbol(sectionIndex, GetNextIndexForSection(sectionIndex), symbolName); - _symbolDefinitions.Add(symbolName, symbol); + symbol = new WasmIndexBasedSymbol(sectionIndex, GetNewIndexForSection(sectionIndex), symbolName); + _wasmSymbolDefinitions.Add(symbolName, symbol); } - return (int)symbol.Index; + return symbol; } private int RegisterFunctionSymbol(Utf8String name) => - GetOrCreateSymbolIndex(name, GetOrCreateSection(WasmObjectNodeSection.FunctionSection).SectionIndex); + (int)GetOrCreateWasmSymbolDefinition(name, GetOrCreateSection(WasmObjectNodeSection.FunctionSection).SectionIndex).Index; protected internal override void EmitSymbolDefinition(int sectionIndex, Utf8String symbolName, long offset = 0, int size = 0, bool global = false) { - GetOrCreateSymbolIndex(symbolName, sectionIndex); + GetOrCreateWasmSymbolDefinition(symbolName, sectionIndex); base.EmitSymbolDefinition(sectionIndex, symbolName, offset, size, global); } @@ -959,7 +964,7 @@ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStr // These relocations reference a wasm structural index (function, type, // table entry, or well-known global). For R2R we self-resolve them here to // the index assigned when the symbol was registered into its index space. - if (!_symbolDefinitions.TryGetValue(reloc.SymbolName, out var symbol)) + if (!_wasmSymbolDefinitions.TryGetValue(reloc.SymbolName, out var symbol)) { throw new NotImplementedException($"No wasm index registered for symbol '{reloc.SymbolName}' (relocation {reloc.Type})."); } @@ -1084,7 +1089,7 @@ private WasmImport[] CreateDefaultGlobalImports() private int RegisterSignature(WasmFuncType signature) { Utf8String signatureKey = signature.GetMangledName(_nodeFactory.NameMangler); - if (_symbolDefinitions.TryGetValue(signatureKey, out var signatureIndex)) + if (_wasmSymbolDefinitions.TryGetValue(signatureKey, out var signatureIndex)) { return (int)signatureIndex.Index; } @@ -1097,7 +1102,7 @@ private int RegisterSignature(WasmFuncType signature) // signature in the defined-symbol table used during relocation resolution. typeSectionWriter.EmitSymbolDefinition(signatureKey); - return (int)_symbolDefinitions[signatureKey].Index; + return (int)_wasmSymbolDefinitions[signatureKey].Index; } private void WriteImports() @@ -1136,7 +1141,7 @@ private void WriteExports() int functionSectionIndex = _sectionNameToIndex[WasmObjectNodeSection.FunctionSection.Name]; // TODO-WASM: Handle exports better (e.g., only export public methods, etc.) IEnumerable functionSymbols = - _symbolDefinitions.Values.Where(symbol => symbol.SectionIndex == functionSectionIndex); + _wasmSymbolDefinitions.Values.Where(symbol => symbol.SectionIndex == functionSectionIndex); foreach (WasmIndexBasedSymbol symbol in functionSymbols.OrderBy(symbol => symbol.Name.ToString())) { WriteFunctionExport(symbol.Name.ToString(), (int)symbol.Index);