Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ed20ac0
Checkpoint: Start wiring up relocs for call_indirect
kg Feb 10, 2026
50e19c3
Checkpoint: We now reach 'Unsupported relocation type: R_WASM_TYPE_IN…
kg Feb 10, 2026
2e4e5ee
Add wasm enum values to RelocType
kg Feb 10, 2026
eef8857
R2R runs to completion again
kg Feb 10, 2026
5f776b1
jit-format
kg Feb 10, 2026
6b57af5
Fix copy-paste error
kg Feb 10, 2026
9a41bd6
Add reloc pseudo-instructions
kg Feb 10, 2026
1f321a9
jit-format
kg Feb 10, 2026
c552071
Address PR feedback
kg Feb 10, 2026
49aee80
Move IF_ enums for relocs to be next to what they alias
kg Feb 10, 2026
eda0262
Move pseudo-instructions to preserve encoding order
kg Feb 10, 2026
d04615f
Implement AppendMangledName to generate emscripten/wabt-style signatures
kg Feb 10, 2026
5f30d64
Address PR feedback
kg Feb 10, 2026
4590336
Address PR feedback
kg Feb 10, 2026
30316e7
Address copilot feedback
kg Feb 10, 2026
dd22d0f
Drop some unnecessary reloc types
kg Feb 10, 2026
31897d1
Address PR feedback
kg Feb 10, 2026
2e47960
jit-format
kg Feb 10, 2026
c1d04cc
Address PR feedback
kg Feb 11, 2026
b446a3d
Address PR feedback
kg Feb 11, 2026
3e1f0f6
Add ULEB/SLEB readers to DwarfHelper based on the ones from the PAL
kg Feb 11, 2026
f62df4b
Address PR feedback
kg Feb 11, 2026
79b1f15
Better name
kg Feb 11, 2026
8fd0398
Address PR feedback
kg Feb 11, 2026
0984497
Formatting
kg Feb 11, 2026
1dc7b7d
Add speculative implementations of padded ULEB/SLEB writer
kg Feb 11, 2026
4d86a02
Replace speculative implementations with ones from runtimelab
kg Feb 11, 2026
4c0c5b6
Fix mistake caught by copilot
kg Feb 12, 2026
a1308b9
Address PR feedback
kg Feb 13, 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
11 changes: 11 additions & 0 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,17 @@ enum class CorInfoReloc
RISCV64_CALL_PLT, // RiscV64: auipc + jalr
RISCV64_PCREL_I, // RiscV64: auipc + I-type
RISCV64_PCREL_S, // RiscV64: auipc + S-type

// Wasm relocs
WASM_FUNCTION_INDEX_LEB, // Wasm: a function index encoded as a 5-byte varuint32. Used for the immediate argument of a call instruction.
WASM_TABLE_INDEX_SLEB, // Wasm: a function table index encoded as a 5-byte varint32. Used to refer to the immediate argument of a
// i32.const instruction, e.g. taking the address of a function.
WASM_MEMORY_ADDR_LEB, // Wasm: a linear memory index encoded as a 5-byte varuint32. Used for the immediate argument of a load or store instruction,
// e.g. directly loading from or storing to a C++ global.
WASM_MEMORY_ADDR_SLEB, // Wasm: a linear memory index encoded as a 5-byte varint32. Used for the immediate argument of a i32.const instruction,
// e.g. taking the address of a C++ global.
WASM_TYPE_INDEX_LEB, // Wasm: a type index encoded as a 5-byte varuint32, e.g. the type immediate in a call_indirect.
WASM_GLOBAL_INDEX_LEB, // Wasm: a global index encoded as a 5-byte varuint32, e.g. the index immediate in a get_global.
};

enum CorInfoGCType
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/codegenwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1580,7 +1580,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
void* pAddr = helperFunction.addr;

// Push indirection cell address onto stack for genEmitCall to dereference
GetEmitter()->emitIns_I(INS_i32_const, emitActualTypeSize(TYP_I_IMPL), (cnsval_ssize_t)pAddr);
GetEmitter()->emitIns_I(INS_i32_const_address, EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)pAddr);

params.callType = EC_INDIR_R;
}
Expand Down
3 changes: 3 additions & 0 deletions src/coreclr/jit/emitfmtswasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ IF_DEF(OPCODE, IS_NONE, NONE) // <opcode>
IF_DEF(BLOCK, IS_NONE, NONE) // <opcode> <0x40>
IF_DEF(RAW_ULEB128, IS_NONE, NONE) // <ULEB128 immediate>
IF_DEF(ULEB128, IS_NONE, NONE) // <opcode> <ULEB128 immediate>
IF_DEF(FUNCIDX, IS_NONE, NONE) // <opcode> <ULEB128 immediate (function 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)>
IF_DEF(F32, IS_NONE, NONE) // <opcode> <f32 immediate (stored as 64-bit integer constant)>
IF_DEF(F64, IS_NONE, NONE) // <opcode> <f64 immediate (stored as 64-bit integer constant)>
IF_DEF(MEMARG, IS_NONE, NONE) // <opcode> <memarg> (<align> <offset>)
Expand Down
88 changes: 74 additions & 14 deletions src/coreclr/jit/emitwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,9 @@ void emitter::emitIns_Call(const EmitCallParams& params)
{
case EC_FUNC_TOKEN:
ins = params.isJump ? INS_return_call : INS_call;
id = emitNewInstrSC(EA_8BYTE, 0 /* FIXME-WASM: function index reloc */);
id = emitNewInstrSC(EA_HANDLE_CNS_RELOC, 0 /* FIXME-WASM: function index reloc */);
id->idIns(ins);
id->idInsFmt(IF_ULEB128);
id->idInsFmt(IF_FUNCIDX);
break;
case EC_INDIR_R:
{
Expand All @@ -167,10 +167,11 @@ void emitter::emitIns_Call(const EmitCallParams& params)

// TODO-WASM: Generate actual list of types and generate reloc
// This is here to exercise the new JIT-EE API
CorInfoWasmType types[] = {CORINFO_WASM_TYPE_VOID};
codeGen->GetCompiler()->info.compCompHnd->getWasmTypeSymbol(types, 1);
CorInfoWasmType types[] = {CORINFO_WASM_TYPE_VOID};
CORINFO_WASM_TYPE_SYMBOL_HANDLE typeHandle =
codeGen->GetCompiler()->info.compCompHnd->getWasmTypeSymbol(types, 1);

id = emitNewInstrSC(EA_8BYTE, 0 /* FIXME-WASM: type index reloc */);
id = emitNewInstrSC(EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)(void*)typeHandle);
id->idIns(ins);
id->idInsFmt(IF_CALL_INDIRECT);
break;
Expand Down Expand Up @@ -368,14 +369,15 @@ static uint8_t GetWasmValueTypeCode(WasmValueType type)
return typecode_mapping[static_cast<unsigned>(type)];
}

unsigned emitter::instrDesc::idCodeSize() const
{
#ifdef TARGET_WASM32
static const unsigned PADDED_RELOC_SIZE = 5;
// NOTE: Keep in sync with Relocation.cs
static const unsigned PADDED_RELOC_SIZE = 5;
#else
#error WASM64
#endif

unsigned emitter::instrDesc::idCodeSize() const
{
unsigned int opcode = GetInsOpcode(idIns());

// Currently, all our instructions have 1 or 2 byte opcodes.
Expand All @@ -399,15 +401,18 @@ unsigned emitter::instrDesc::idCodeSize() const
size = SizeOfULEB128(emitGetLclVarDeclCount(this)) + sizeof(typeCode);
break;
}
case IF_FUNCIDX:
case IF_ULEB128:
size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this));
break;
case IF_MEMADDR:
case IF_FUNCPTR:
case IF_SLEB128:
size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfSLEB128(emitGetInsSC(this));
break;
case IF_CALL_INDIRECT:
{
size += SizeOfULEB128(emitGetInsSC(this));
size += idIsCnsReloc() ? PADDED_RELOC_SIZE : SizeOfULEB128(emitGetInsSC(this));
size += SizeOfULEB128(0);
break;
}
Expand Down Expand Up @@ -506,8 +511,42 @@ size_t emitter::emitOutputOpcode(BYTE* dst, instruction ins)
return sz;
}

size_t emitter::emitOutputPaddedReloc(uint8_t* destination)
{
static_assert(PADDED_RELOC_SIZE > 1);
// Write zeroes with the bit set that indicates another byte is coming
for (unsigned i = 0; i < PADDED_RELOC_SIZE - 1; i++)
{
destination += emitOutputByte(destination, 0x80);
}

emitOutputByte(destination, 0x0);
return PADDED_RELOC_SIZE;
}

size_t emitter::emitOutputConstant(uint8_t* destination, const instrDesc* id, bool isSigned, CorInfoReloc relocType)
{
if (id->idIsCnsReloc())
{
emitRecordRelocation(destination, (void*)emitGetInsSC(id), relocType);
return emitOutputPaddedReloc(destination);
}

if (isSigned)
{
return emitOutputSLEB128(destination, (int64_t)emitGetInsSC(id));
}
else
{
return emitOutputULEB128(destination, (uint64_t)emitGetInsSC(id));
}
}

size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
{
const bool SIGNED = true;
const bool UNSIGNED = false;

BYTE* dst = *dp;
size_t sz = emitSizeOfInsDsc(id);
instruction ins = id->idIns();
Expand All @@ -527,22 +566,40 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
break;
case IF_ULEB128:
{
assert(!id->idIsCnsReloc());
dst += emitOutputOpcode(dst, ins);
cnsval_ssize_t constant = emitGetInsSC(id);
dst += emitOutputULEB128(dst, (uint64_t)constant);
dst += emitOutputULEB128(dst, (uint64_t)emitGetInsSC(id));
break;
}
case IF_SLEB128:
{
assert(!id->idIsCnsReloc());
dst += emitOutputOpcode(dst, ins);
cnsval_ssize_t constant = emitGetInsSC(id);
dst += emitOutputSLEB128(dst, (int64_t)constant);
dst += emitOutputSLEB128(dst, (int64_t)emitGetInsSC(id));
break;
}
case IF_MEMADDR:
{
dst += emitOutputOpcode(dst, ins);
dst += emitOutputConstant(dst, id, SIGNED, CorInfoReloc::WASM_MEMORY_ADDR_SLEB);
break;
}
case IF_FUNCPTR:
{
dst += emitOutputOpcode(dst, ins);
dst += emitOutputConstant(dst, id, SIGNED, CorInfoReloc::WASM_TABLE_INDEX_SLEB);
break;
}
case IF_FUNCIDX:
{
dst += emitOutputOpcode(dst, ins);
dst += emitOutputConstant(dst, id, UNSIGNED, CorInfoReloc::WASM_FUNCTION_INDEX_LEB);
break;
}
case IF_CALL_INDIRECT:
{
dst += emitOutputByte(dst, opcode);
dst += emitOutputULEB128(dst, (uint64_t)emitGetInsSC(id));
dst += emitOutputConstant(dst, id, UNSIGNED, CorInfoReloc::WASM_TYPE_INDEX_LEB);
dst += emitOutputULEB128(dst, 0);
break;
}
Expand Down Expand Up @@ -719,6 +776,7 @@ void emitter::emitDispIns(

case IF_RAW_ULEB128:
case IF_ULEB128:
case IF_FUNCIDX:
{
cnsval_ssize_t imm = emitGetInsSC(id);
printf(" %llu", (uint64_t)imm);
Expand Down Expand Up @@ -762,6 +820,8 @@ void emitter::emitDispIns(
}
break;

case IF_MEMADDR:
case IF_FUNCPTR:
case IF_SLEB128:
{
cnsval_ssize_t imm = emitGetInsSC(id);
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/emitwasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,5 @@ size_t emitOutputULEB128(uint8_t* destination, uint64_t value);
size_t emitOutputSLEB128(uint8_t* destination, int64_t value);
size_t emitRawBytes(uint8_t* destination, const void* source, size_t count);
size_t emitOutputOpcode(BYTE* dst, instruction ins);
size_t emitOutputPaddedReloc(uint8_t* destination);
size_t emitOutputConstant(uint8_t* destination, const instrDesc* id, bool isSigned, CorInfoReloc relocType);
16 changes: 10 additions & 6 deletions src/coreclr/jit/instrswasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ INST(br, "br", 0, IF_ULEB128, 0x0C)
INST(br_if, "br_if", 0, IF_ULEB128, 0x0D)
INST(br_table, "br_table", 0, IF_ULEB128, 0x0E)
INST(return, "return", 0, IF_OPCODE, 0x0F)
INST(call, "call", 0, IF_ULEB128, 0x10)
INST(call, "call", 0, IF_FUNCIDX, 0x10)
INST(call_indirect, "call_indirect", 0, IF_CALL_INDIRECT, 0x11)
INST(return_call, "return_call", 0, IF_ULEB128, 0x12)
INST(return_call, "return_call", 0, IF_FUNCIDX, 0x12)
INST(return_call_indirect, "return_call_indirect", 0, IF_CALL_INDIRECT, 0x13)

INST(drop, "drop", 0, IF_OPCODE, 0x1A)
Expand Down Expand Up @@ -72,10 +72,14 @@ INST(i32_store16, "i32.store16", 0, IF_MEMARG, 0x3B)

// 5.4.7 Numeric Instructions
// Constants
INST(i32_const, "i32.const", 0, IF_SLEB128, 0x41)
INST(i64_const, "i64.const", 0, IF_SLEB128, 0x42)
INST(f32_const, "f32.const", 0, IF_F32, 0x43)
INST(f64_const, "f64.const", 0, IF_F64, 0x44)
INST(i32_const, "i32.const", 0, IF_SLEB128, 0x41)
// Pseudo-instructions for relocations
INST(i32_const_address, "i32.const_address", 0, IF_MEMADDR, 0x41)
INST(i32_const_funcptr, "i32.const_funcptr", 0, IF_FUNCPTR, 0x41)
// Constants, continued
INST(i64_const, "i64.const", 0, IF_SLEB128, 0x42)
INST(f32_const, "f32.const", 0, IF_F32, 0x43)
INST(f64_const, "f64.const", 0, IF_F64, 0x44)
// Integer comparisons
INST(i32_eqz, "i32.eqz", 0, IF_OPCODE, 0x45)
INST(i32_eq, "i32.eq", 0, IF_OPCODE, 0x46)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Diagnostics;
using ILCompiler.ObjectWriter;

namespace ILCompiler.DependencyAnalysis
{
Expand Down Expand Up @@ -38,6 +39,17 @@ public enum RelocType
IMAGE_REL_BASED_ARM64_PAGEOFFSET_12A = 0x82, // ADD/ADDS (immediate) with zero shift, for page offset
IMAGE_REL_BASED_ARM64_PAGEOFFSET_12L = 0x83, // LDR (indexed, unsigned immediate), for page offset

// Wasm relocs
WASM_FUNCTION_INDEX_LEB = 0x200, // Wasm: a function index encoded as a 5-byte varuint32. Used for the immediate argument of a call instruction.
WASM_TABLE_INDEX_SLEB = 0x201, // Wasm: a function table index encoded as a 5-byte varint32. Used to refer to the immediate argument of a
// i32.const instruction, e.g. taking the address of a function.
WASM_MEMORY_ADDR_LEB = 0x202, // Wasm: a linear memory index encoded as a 5-byte varuint32. Used for the immediate argument of a load or store instruction,
// e.g. directly loading from or storing to a C++ global.
WASM_MEMORY_ADDR_SLEB = 0x203, // Wasm: a linear memory index encoded as a 5-byte varint32. Used for the immediate argument of a i32.const instruction,
// e.g. taking the address of a C++ global.
WASM_TYPE_INDEX_LEB = 0x204, // Wasm: a type index encoded as a 5-byte varuint32, e.g. the type immediate in a call_indirect.
WASM_GLOBAL_INDEX_LEB = 0x205, // Wasm: a global index encoded as a 5-byte varuint32, e.g. the index immediate in a get_global.

//
// Relocation operators related to TLS access
//
Expand Down Expand Up @@ -81,6 +93,9 @@ public enum RelocType

public struct Relocation
{
// NOTE: Keep in sync with emitwasm.cpp
private const int WASM_PADDED_RELOC_SIZE_32 = 5;

public readonly RelocType RelocType;
public readonly int Offset;
public readonly ISymbolNode Target;
Expand Down Expand Up @@ -621,6 +636,22 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v
bool isStype = (relocType is RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S);
PutRiscV64AuipcCombo((uint*)location, value, isStype);
break;

case RelocType.WASM_FUNCTION_INDEX_LEB:
case RelocType.WASM_TABLE_INDEX_SLEB:
case RelocType.WASM_TYPE_INDEX_LEB:
case RelocType.WASM_GLOBAL_INDEX_LEB:
// These wasm relocs do not have offsets, just targets
return;

case RelocType.WASM_MEMORY_ADDR_LEB:
DwarfHelper.WritePaddedULEB128(new Span<byte>((byte*)location, WASM_PADDED_RELOC_SIZE_32), checked((ulong)value));
return;

case RelocType.WASM_MEMORY_ADDR_SLEB:
DwarfHelper.WritePaddedSLEB128(new Span<byte>((byte*)location, WASM_PADDED_RELOC_SIZE_32), value);
return;

default:
Debug.Fail("Invalid RelocType: " + relocType);
break;
Expand Down Expand Up @@ -709,6 +740,19 @@ public static unsafe long ReadValue(RelocType relocType, void* location)
case RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S:
bool isStype = (relocType is RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S);
return GetRiscV64AuipcCombo((uint*)location, isStype);
case RelocType.WASM_FUNCTION_INDEX_LEB:
case RelocType.WASM_TABLE_INDEX_SLEB:
case RelocType.WASM_TYPE_INDEX_LEB:
case RelocType.WASM_GLOBAL_INDEX_LEB:
// These wasm relocs do not have offsets, just targets
return 0;

case RelocType.WASM_MEMORY_ADDR_LEB:
return checked((long)DwarfHelper.ReadULEB128(new ReadOnlySpan<byte>(location, WASM_PADDED_RELOC_SIZE_32)));

case RelocType.WASM_MEMORY_ADDR_SLEB:
return DwarfHelper.ReadSLEB128(new ReadOnlySpan<byte>(location, WASM_PADDED_RELOC_SIZE_32));

default:
Debug.Fail("Invalid RelocType: " + relocType);
return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace ILCompiler.DependencyAnalysis.Wasm
// Represents a WASM type signature, e.g. "(i32, i32) -> (i64)". Used as a relocation target for things like 'call_indirect'.
// Does not currently support multiple return values; the return type is always the first type in the array and may be Void.
//
public class WasmTypeNode : ObjectNode
public class WasmTypeNode : ObjectNode, ISymbolNode
{
private readonly CorInfoWasmType[] _types;

Expand Down Expand Up @@ -49,5 +49,24 @@ public override int CompareToImpl(ISortableNode other, CompilerComparer comparer
result = MemoryExtensions.SequenceCompareTo(lhs, rhs);
return result;
}

public void AppendMangledName(NameMangler nameMangler, Internal.Text.Utf8StringBuilder sb)
{
sb.Append(nameMangler.CompilationUnitPrefix);
sb.Append("__wasmtype_"u8);

foreach (var type in _types)
sb.Append(type switch {
CorInfoWasmType.CORINFO_WASM_TYPE_VOID => 'v',
CorInfoWasmType.CORINFO_WASM_TYPE_V128 => 'V',
CorInfoWasmType.CORINFO_WASM_TYPE_F64 => 'd',
CorInfoWasmType.CORINFO_WASM_TYPE_F32 => 'f',
CorInfoWasmType.CORINFO_WASM_TYPE_I64 => 'j',
CorInfoWasmType.CORINFO_WASM_TYPE_I32 => 'i',
_ => throw new NotImplementedException($"Unknown CorInfoWasmType: {type}"),
});
}

public int Offset => 0;
}
}
Loading
Loading