Skip to content

[WASM] Use relocs for tableBase / imageBase / stackPointer globals#129717

Open
jtschuster wants to merge 7 commits into
dotnet:mainfrom
jtschuster:jtschuster/wasm-base-global-reloc-test
Open

[WASM] Use relocs for tableBase / imageBase / stackPointer globals#129717
jtschuster wants to merge 7 commits into
dotnet:mainfrom
jtschuster:jtschuster/wasm-base-global-reloc-test

Conversation

@jtschuster

Copy link
Copy Markdown
Member

For NativeAOT, we need to use relocatable tableBase / imageBase / stackPointer globals rather than hard-coded indices.

Add a new JIT-EE API to get handles for the globals needed. The JIT emits the maximum LEB size for the relocation, and the crossgen2 ObjectWriter fixes up the relocations to insert the index of the global. This does increase code size, but should have no other behavioral changes.

The NativeAOT ObjectWriter will leave emit WASM relocations into the emitted object for wasm-ld to fix up when linking to the NativeAOT WASM runtime.

This was implemented with a new JIT interface method, but I think it also could be implemented as ReadyToRun helpers instead if that's preferred.

Fixes #129712.

jtschuster and others added 6 commits June 22, 2026 11:51
The wasm JIT previously referenced the three ABI "base globals" (shadow
stack pointer, image base, table base) with bare `global.get <fixedIdx>`
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>
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>
… 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>
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>
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>
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>
@jtschuster jtschuster self-assigned this Jun 22, 2026
Copilot AI review requested due to automatic review settings June 22, 2026 20:46
@github-actions github-actions Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jun 22, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the WASM JIT/codegen and related toolchain plumbing so that references to the well-known WASM “base globals” (stack pointer, image base, table base) flow through relocations/symbol handles instead of relying on hard-coded global indices. It wires a new JIT-EE query (getWasmBaseGlobals) end-to-end (JIT ↔ EE ↔ SuperPMI ↔ thunks) and updates the WASM object writer plus ReadyToRun tests to validate expected encoding.

Changes:

  • Add a new JIT-EE interface method getWasmBaseGlobals and thread it through the managed JIT interface, wrappers, and SuperPMI record/replay.
  • Teach the WASM emitter to generate global.get for base globals via WASM_GLOBAL_INDEX_LEB relocations, and update the WASM object writer to self-resolve these in ReadyToRun.
  • Add a small R2R WASM test case and a byte-pattern smoke check to ensure the resolved global.get encodings are present.

Reviewed changes

Copilot reviewed 32 out of 32 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/coreclr/inc/corinfo.h Adds CORINFO_WASM_BASE_GLOBALS and the getWasmBaseGlobals interface method.
src/coreclr/inc/icorjitinfoimpl_generated.h Declares the VM-side override for the new interface method.
src/coreclr/inc/jiteeversionguid.h Bumps the JIT-EE version GUID to reflect the interface change.
src/coreclr/vm/jitinterface.cpp Adds the VM implementation stub for CEEInfo::getWasmBaseGlobals.
src/coreclr/jit/compiler.h Adds JIT-side caching fields and accessor declaration for base globals.
src/coreclr/jit/ee_il_dll.hpp Adds inline Compiler::eeGetWasmBaseGlobals helper.
src/coreclr/jit/emitfmtswasm.h Introduces a new WASM emitter format for global-index relocations.
src/coreclr/jit/emitwasm.h Declares emitIns_BaseGlobalGet to emit relocatable base-global reads.
src/coreclr/jit/emitwasm.cpp Implements emitIns_BaseGlobalGet and routes image/table base users through it.
src/coreclr/jit/codegenwasm.cpp Switches stack-pointer global.get emission to the new base-global path.
src/coreclr/jit/ICorJitInfo_names_generated.h Adds the new API name to the generated API name list.
src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp Adds wrapper forwarding for getWasmBaseGlobals.
src/coreclr/tools/Common/JitInterface/ThunkGenerator/ThunkInput.txt Updates thunk generator inputs for the new struct and API.
src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs Adds managed projections for the new handle and base-globals struct.
src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs Implements managed-side callback filling CORINFO_WASM_BASE_GLOBALS.
src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs Wires the callback trampoline for getWasmBaseGlobals.
src/coreclr/tools/Common/Compiler/DependencyAnalysis/WasmBaseGlobalSymbolNode.cs New node type representing well-known base-global symbols.
src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs Self-resolves WASM_GLOBAL_INDEX_LEB relocations for base globals in R2R output.
src/coreclr/tools/aot/jitinterface/jitinterface_generated.h Adds wrapper support for the new callback in the AOT jitinterface layer.
src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj Links in the new WasmBaseGlobalSymbolNode source file.
src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj Links in the new WasmBaseGlobalSymbolNode source file.
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCasesRunner/R2RResultChecker.cs Adds a WASM byte-pattern scan helper for padded base-global global.get.
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/Webcil/WasmWebcilModule.cs Adds methods designed to force image-base/table-base materialization in codegen.
src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs Extends the WASM WebCIL test to assert expected base-global global.get encodings exist.
src/coreclr/tools/superpmi/superpmi/icorjitinfo.cpp Adds SuperPMI replay hook for getWasmBaseGlobals.
src/coreclr/tools/superpmi/superpmi-shim-simple/icorjitinfo_generated.cpp Adds passthrough for the new API in the “simple” shim.
src/coreclr/tools/superpmi/superpmi-shim-counter/icorjitinfo_generated.cpp Adds call-counting support for the new API in the “counter” shim.
src/coreclr/tools/superpmi/superpmi-shim-collector/icorjitinfo.cpp Records the new API call and its outputs during collection.
src/coreclr/tools/superpmi/superpmi-shared/agnostic.h Adds agnostic serialization struct for CORINFO_WASM_BASE_GLOBALS.
src/coreclr/tools/superpmi/superpmi-shared/lwmlist.h Registers a new lightweight map for recording/replaying the call.
src/coreclr/tools/superpmi/superpmi-shared/methodcontext.h Adds record/dump/replay declarations and a new packet id.
src/coreclr/tools/superpmi/superpmi-shared/methodcontext.cpp Implements record/dump/replay for getWasmBaseGlobals.

Comment thread src/coreclr/jit/compiler.h
Comment thread src/coreclr/jit/ee_il_dll.hpp
Comment thread src/coreclr/inc/corinfo.h Outdated
…T 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[RyuJit Wasm] Emit relocations for WebCIL globals rather than hardcoding global table indices

2 participants