Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
832f9d8
Emit wasm base globals as WASM_GLOBAL_INDEX_LEB relocations
jtschuster Jun 4, 2026
fc8462b
Cover wasm base-global relocations in WasmWebcilModule R2R test
jtschuster Jun 5, 2026
d03465e
Validate wasm base-global global.get emission in WasmWebcilModule R2R…
jtschuster Jun 17, 2026
2083974
Restrict wasm base-global emitter to global.get
jtschuster Jun 18, 2026
e45baaf
Add getWasmBaseGlobals JIT-EE API for wasm base globals
jtschuster Jun 22, 2026
a192e3a
Resolve wasm base-global relocs via WasmBaseGlobalSymbolNode.GlobalIndex
jtschuster Jun 22, 2026
9227ffb
Address PR review: append getWasmBaseGlobals last, guard wasm-only JI…
jtschuster Jun 22, 2026
29ce847
Merge remote-tracking branch 'upstream/main' into jtschuster/wasm-bas…
jtschuster Jun 23, 2026
71346df
Rename WASM base globals to well-known globals; cache symbol nodes on…
jtschuster Jun 25, 2026
6d4bd72
Merge remote-tracking branch 'upstream/main' into jtschuster/wasm-bas…
jtschuster Jun 25, 2026
9a7d534
Make WasmWellKnownGlobalNode inherit from ExternSymbolNode
jtschuster Jun 25, 2026
2def050
Make symbol name the identity of WasmWellKnownGlobalSymbol
jtschuster Jun 29, 2026
c19051f
Merge branch 'main' of https://github.com/dotnet/runtime into jtschus…
jtschuster Jun 30, 2026
2ec6045
Jit format and delete unused STACK_POINTER_GLOBAL
jtschuster Jun 30, 2026
5ef08a4
Merge branch 'main' into jtschuster/wasm-base-global-reloc-test
jtschuster Jun 30, 2026
aec85c0
Merge branch 'main' of https://github.com/dotnet/runtime into jtschus…
jtschuster Jul 7, 2026
2eb601b
Update JIT-EE interfacae Guid
jtschuster Jul 7, 2026
799f95b
Merge branch 'main' of https://github.com/dotnet/runtime into jtschus…
jtschuster Jul 8, 2026
efbd9b2
Update test to avoid optimization of try/catch and include method cal…
jtschuster Jul 8, 2026
b83daa5
Merge upstream/main into jtschuster/wasm-base-global-reloc-test
jtschuster Jul 14, 2026
dbd7484
Remove usings
jtschuster Jul 14, 2026
83ef375
Merge upstream/main into jtschuster/wasm-base-global-reloc-test
jtschuster Jul 15, 2026
1ca50fb
Merge upstream/main into jtschuster/wasm-base-global-reloc-test
jtschuster Jul 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,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;
Expand Down Expand Up @@ -1839,6 +1840,19 @@ struct CORINFO_ASYNC_INFO
CORINFO_METHOD_HANDLE finishSuspensionWithContinuationContextMethHnd;
};

// 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 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;
// 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
{
Expand Down Expand Up @@ -3209,6 +3223,12 @@ class ICorStaticInfo
virtual uint32_t getAddressAlignment(
void* address
) = 0;

// 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 getWasmWellKnownGlobals(
CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut
) = 0;
};

/*****************************************************************************
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/inc/icorjitinfoimpl_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,9 @@ CorInfoWasmType getWasmLowering(
uint32_t getAddressAlignment(
void* address) override;

void getWasmWellKnownGlobals(
CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut) override;

uint32_t getThreadTLSIndex(
void** ppIndirection) override;

Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/inc/jiteeversionguid.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@

#include <minipal/guid.h>

constexpr GUID JITEEVersionIdentifier = { /* 24c0b78a-9173-40cd-a6b3-e290fb3c0a22 */
0x24c0b78a,
0x9173,
0x40cd,
{0xa6, 0xb3, 0xe2, 0x90, 0xfb, 0x3c, 0x0a, 0x22}
constexpr GUID JITEEVersionIdentifier = { /* 65743063-e8fa-41d4-9496-c436974c00f5 */
0x65743063,
0xe8fa,
0x41d4,
{0x94, 0x96, 0xc4, 0x36, 0x97, 0x4c, 0x00, 0xf5}
};

#endif // JIT_EE_VERSIONING_GUID_H
1 change: 1 addition & 0 deletions src/coreclr/jit/ICorJitInfo_names_generated.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ DEF_CLR_API(getSwiftLowering)
DEF_CLR_API(getFpStructLowering)
DEF_CLR_API(getWasmLowering)
DEF_CLR_API(getAddressAlignment)
DEF_CLR_API(getWasmWellKnownGlobals)
DEF_CLR_API(getThreadTLSIndex)
DEF_CLR_API(getAddrOfCaptureThreadGlobal)
DEF_CLR_API(getHelperFtn)
Expand Down
8 changes: 8 additions & 0 deletions src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,14 @@ uint32_t WrapICorJitInfo::getAddressAlignment(
return temp;
}

void WrapICorJitInfo::getWasmWellKnownGlobals(
CORINFO_WASM_WELLKNOWN_GLOBALS* pWellKnownGlobalsOut)
{
API_ENTER(getWasmWellKnownGlobals);
wrapHnd->getWasmWellKnownGlobals(pWellKnownGlobalsOut);
API_LEAVE(getWasmWellKnownGlobals);
}

uint32_t WrapICorJitInfo::getThreadTLSIndex(
void** ppIndirection)
{
Expand Down
5 changes: 2 additions & 3 deletions src/coreclr/jit/codegenwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -165,7 +163,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_PTRSIZE, 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);
Comment thread
jtschuster marked this conversation as resolved.
}
else
Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -9516,6 +9516,13 @@ class Compiler

CORINFO_ASYNC_INFO* eeGetAsyncInfo();

#if defined(TARGET_WASM)
CORINFO_WASM_WELLKNOWN_GLOBALS wasmWellKnownGlobals;
bool wasmWellKnownGlobalsInitialized = false;

CORINFO_WASM_WELLKNOWN_GLOBALS* eeGetWasmWellKnownGlobals();
#endif // defined(TARGET_WASM)

// Gets the offset of a SDArray's first element
static unsigned eeGetArrayDataOffset();

Expand Down
13 changes: 13 additions & 0 deletions src/coreclr/jit/ee_il_dll.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,19 @@ inline CORINFO_ASYNC_INFO* Compiler::eeGetAsyncInfo()
return &asyncInfo;
}

#if defined(TARGET_WASM)
inline CORINFO_WASM_WELLKNOWN_GLOBALS* Compiler::eeGetWasmWellKnownGlobals()
{
if (!wasmWellKnownGlobalsInitialized)
{
info.compCompHnd->getWasmWellKnownGlobals(&wasmWellKnownGlobals);
wasmWellKnownGlobalsInitialized = true;
}

return &wasmWellKnownGlobals;
}
#endif // defined(TARGET_WASM)

/*****************************************************************************
*
* Convert the type returned from the VM to a var_type.
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/emitfmtswasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ IF_DEF(RAW_ULEB128, IS_NONE, NONE) // <ULEB128 immediate>
IF_DEF(CODE_SIZE, IS_NONE, NONE)
IF_DEF(ULEB128, IS_NONE, NONE) // <opcode> <ULEB128 immediate>
IF_DEF(FUNCIDX, IS_NONE, NONE) // <opcode> <ULEB128 immediate (function index reloc)>
IF_DEF(GLOBALIDX, IS_NONE, NONE) // <opcode> <ULEB128 immediate (global index reloc)>
IF_DEF(SLEB128, IS_NONE, NONE) // <opcode> <LEB128 immediate (signed)>
IF_DEF(MEMADDR, IS_NONE, NONE) // <opcode> <SLEB128 immediate (memory address reloc)>
IF_DEF(FUNCPTR, IS_NONE, NONE) // <opcode> <SLEB128 immediate (function pointer reloc)>
Expand Down
19 changes: 14 additions & 5 deletions src/coreclr/jit/emitwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,20 +184,21 @@ 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 __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_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);
}

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_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);
}
Expand Down Expand Up @@ -670,6 +671,7 @@ unsigned emitter::instrDesc::idCodeSize() const
}
case IF_FUNCIDX:
case IF_ULEB128:
case IF_GLOBALIDX:
size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this));
break;
case IF_MEMADDR:
Expand Down Expand Up @@ -957,6 +959,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);
Expand Down Expand Up @@ -1241,6 +1249,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);
Expand Down
38 changes: 19 additions & 19 deletions src/coreclr/jit/instrswasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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.Collections.Frozen;
using System.Threading;
using Internal.Text;

namespace ILCompiler.DependencyAnalysis
{
public partial class NodeFactory
{
// 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.
private FrozenDictionary<Utf8String, WasmWellKnownGlobalSymbolNode> _wasmWellKnownGlobals;

public WasmWellKnownGlobalSymbolNode GetWellKnownWasmGlobalSymbol(Utf8String symbolName)
{
FrozenDictionary<Utf8String, WasmWellKnownGlobalSymbolNode> globals = _wasmWellKnownGlobals;
if (globals is null)
{
globals = FrozenDictionary.Create<Utf8String, WasmWellKnownGlobalSymbolNode>([
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[symbolName];
}
Comment thread
jtschuster marked this conversation as resolved.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Internal.Text;

namespace ILCompiler.DependencyAnalysis
{
/// <summary>
/// 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.
/// </summary>
public class WasmWellKnownGlobalSymbolNode(string symbolName) : ExternDataSymbolNode(new Utf8String(symbolName))
{
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()}";
}
}
Loading
Loading