Skip to content
Draft
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
e59945e
[Wasm RyuJit] ABI classifier and Wasm stack arg preliminaries
AndyAyersMS Jan 16, 2026
677018d
Merge branch 'main' into WasmAbiClassifier
AndyAyersMS Jan 16, 2026
fc97403
fix x86, it really wants the orginal arg0 here
AndyAyersMS Jan 20, 2026
b847871
Merge branch 'main' into WasmAbiClassifier
AndyAyersMS Jan 20, 2026
4eac135
use the new sp arg var in wasm codegen
AndyAyersMS Jan 20, 2026
2dec621
pass sp arg to managed callees
AndyAyersMS Jan 20, 2026
2c5b37f
sp is in a reg; don't spill sp in prolog; don't emit placeholder loca…
AndyAyersMS Jan 20, 2026
e2541c8
one more undo fix
AndyAyersMS Jan 21, 2026
2130664
copilot feedback
AndyAyersMS Jan 21, 2026
a339c42
review feedback
AndyAyersMS Jan 21, 2026
ebc2e48
stop generating GT_PUTARG_REG
AndyAyersMS Jan 21, 2026
f6123de
review feedback: SP related fixes
AndyAyersMS Jan 21, 2026
39a0811
add comment headers
AndyAyersMS Jan 21, 2026
debffb2
undo classifier changes for now; tweak comment
AndyAyersMS Jan 21, 2026
f20cef5
remove inadvertent blank line
AndyAyersMS Jan 22, 2026
aff4126
fix comment; remove now-unused method declaration
AndyAyersMS Jan 22, 2026
4b8c389
jit host wasm ABI classifier
AndyAyersMS Jan 22, 2026
d37e0f0
jit side
AndyAyersMS Jan 22, 2026
00d4c7f
add wasm target to disassembly header
AndyAyersMS Jan 23, 2026
21d5b5f
format
AndyAyersMS Jan 23, 2026
929037b
Merge branch 'main' into WasmAbiClassifier
AndyAyersMS Feb 2, 2026
4dfc9f7
post-merge fixes
AndyAyersMS Feb 2, 2026
62f5166
Merge remote-tracking branch 'origin/WasmAbiClassifier' into WasmAbiC…
AndyAyersMS Feb 2, 2026
adee2a8
Merge branch 'main' into WasmABIClassifier
AndyAyersMS Feb 3, 2026
9a7291b
resolve jit guid conflict
AndyAyersMS Feb 3, 2026
105a04b
Merge branch 'main' into WasmABIClassifier
AndyAyersMS Feb 9, 2026
9f6ee69
revise
AndyAyersMS Feb 10, 2026
0c5d93b
fixes
AndyAyersMS Feb 10, 2026
cfd372d
fix spmi dump formatting
AndyAyersMS Feb 10, 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
4 changes: 4 additions & 0 deletions src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -3185,6 +3185,10 @@ class ICorStaticInfo
// Returns lowering info for fields of a RISC-V/LoongArch struct passed in registers according to
// hardware floating-point calling convention.
virtual void getFpStructLowering(CORINFO_CLASS_HANDLE structHnd, CORINFO_FPSTRUCT_LOWERING* pLowering) = 0;

// 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;
};

/*****************************************************************************
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 @@ -535,6 +535,9 @@ void getFpStructLowering(
CORINFO_CLASS_HANDLE structHnd,
CORINFO_FPSTRUCT_LOWERING* pLowering) override;

CorInfoWasmType getWasmLowering(
CORINFO_CLASS_HANDLE structHnd) 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 = { /* 868b8ae2-0000-410b-a9dc-38d9eda37d66 */
0x868b8ae2,
0x0000,
0x410b,
{0xa9, 0xdc, 0x38, 0xd9, 0xed, 0xa3, 0x7d, 0x66}
constexpr GUID JITEEVersionIdentifier = { /* 1774da53-2dcd-42cd-bb0f-ae7f66c79e8e */
0x1774da53,
0x2dcd,
0x42cd,
{0xbb, 0x0f, 0xae, 0x7f, 0x66, 0xc7, 0x9e, 0x8e}
};

#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 @@ -132,6 +132,7 @@ DEF_CLR_API(getMethodHash)
DEF_CLR_API(getSystemVAmd64PassStructInRegisterDescriptor)
DEF_CLR_API(getSwiftLowering)
DEF_CLR_API(getFpStructLowering)
DEF_CLR_API(getWasmLowering)
DEF_CLR_API(getThreadTLSIndex)
DEF_CLR_API(getAddrOfCaptureThreadGlobal)
DEF_CLR_API(getHelperFtn)
Expand Down
9 changes: 9 additions & 0 deletions src/coreclr/jit/ICorJitInfo_wrapper_generated.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1262,6 +1262,15 @@ void WrapICorJitInfo::getFpStructLowering(
API_LEAVE(getFpStructLowering);
}

CorInfoWasmType WrapICorJitInfo::getWasmLowering(
CORINFO_CLASS_HANDLE structHnd)
{
API_ENTER(getWasmLowering);
CorInfoWasmType temp = wrapHnd->getWasmLowering(structHnd);
API_LEAVE(getWasmLowering);
return temp;
}

uint32_t WrapICorJitInfo::getThreadTLSIndex(
void** ppIndirection)
{
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/jit/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ class WasmClassifier
return 0;
}

static var_types ToJitType(CorInfoWasmType wasmType);

ABIPassingInformation Classify(Compiler* comp,
var_types type,
ClassLayout* structLayout,
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1915,6 +1915,10 @@ void CodeGen::genGenerateMachineCode()
printf("generic LOONGARCH64");
#elif defined(TARGET_RISCV64)
printf("generic RISCV64");
#elif defined(TARGET_WASM32)
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: this could use Target::g_tgtCPUName.

printf("wasm32");
#elif defined(TARGET_WASM64)
printf("wasm64");
#else
printf("unknown architecture");
#endif
Expand Down
32 changes: 32 additions & 0 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,38 @@ var_types Compiler::getReturnTypeForStruct(CORINFO_CLASS_HANDLE clsHnd,
}
assert(structSize > 0);

#if defined(TARGET_WASM)
CorInfoWasmType abiType = info.compCompHnd->getWasmLowering(clsHnd);

if (abiType == CORINFO_WASM_TYPE_VOID)
{
howToReturnStruct = SPK_ByReference;
useType = TYP_UNKNOWN;
}
else
{
howToReturnStruct = SPK_ByValue;
useType = WasmClassifier::ToJitType(abiType);
}

if (wbReturnStruct != nullptr)
{
*wbReturnStruct = howToReturnStruct;
}

return useType;
#else
#ifdef DEBUG
// Extra query to facilitate wasm replay of native collections.
// TODO-WASM: delete once we can get a wasm collection.
//
if (JitConfig.EnableExtraSuperPmiQueries() && IsReadyToRun())
{
info.compCompHnd->getWasmLowering(clsHnd);
}
#endif // DEBUG
#endif // defined(TARGET_WASM)

#ifdef SWIFT_SUPPORT
if (callConv == CorInfoCallConvExtension::Swift)
{
Expand Down
14 changes: 14 additions & 0 deletions src/coreclr/jit/lclvars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,20 @@ void Compiler::lvaClassifyParameterABI(Classifier& classifier)

dsc->lvIsRegArg = numRegisters > 0;
dsc->lvIsMultiRegArg = numRegisters > 1;

#ifdef DEBUG
// Extra query to facilitate wasm replay of native collections.
// TODO-WASM: delete once we can get a wasm collection.
//
if (JitConfig.EnableExtraSuperPmiQueries() && IsReadyToRun() && (structLayout != nullptr))
{
CORINFO_CLASS_HANDLE clsHnd = structLayout->GetClassHandle();
if (clsHnd != NO_CLASS_HANDLE)
{
info.compCompHnd->getWasmLowering(clsHnd);
}
}
#endif // DEBUG
}

lvaParameterStackSize = classifier.StackSize();
Expand Down
46 changes: 45 additions & 1 deletion src/coreclr/jit/targetwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,32 @@ WasmClassifier::WasmClassifier(const ClassifierInfo& info)
{
}

//-----------------------------------------------------------------------------
// ToJitType: translate CorInfoWasmType to var_types
//
// Parameters:
// wasmType -- wasm type to translate
//
var_types WasmClassifier::ToJitType(CorInfoWasmType wasmType)
{
switch (wasmType)
{
case CORINFO_WASM_TYPE_I32:
return TYP_INT;
case CORINFO_WASM_TYPE_I64:
return TYP_LONG;
case CORINFO_WASM_TYPE_F32:
return TYP_FLOAT;
case CORINFO_WASM_TYPE_F64:
return TYP_DOUBLE;
case CORINFO_WASM_TYPE_V128:
// TODO-WASM: Simd support
unreached();
default:
unreached();
}
}

//-----------------------------------------------------------------------------
// Classify:
// Classify a parameter for the Wasm ABI.
Expand All @@ -48,7 +74,25 @@ ABIPassingInformation WasmClassifier::Classify(Compiler* comp,
{
if (type == TYP_STRUCT)
{
NYI_WASM("WasmClassifier::Classify - structs");
CORINFO_CLASS_HANDLE clsHnd = structLayout->GetClassHandle();
assert(clsHnd != NO_CLASS_HANDLE);
CorInfoWasmType wasmAbiType = comp->info.compCompHnd->getWasmLowering(clsHnd);
bool passByRef = false;
var_types abiType = TYP_UNDEF;

if (wasmAbiType == CORINFO_WASM_TYPE_VOID)
{
abiType = TYP_I_IMPL;
passByRef = true;
}
else
{
abiType = ToJitType(wasmAbiType);
}

regNumber reg = MakeWasmReg(m_localIndex++, genActualType(abiType));
ABIPassingSegment seg = ABIPassingSegment::InRegister(reg, 0, genTypeSize(abiType));
return ABIPassingInformation::FromSegment(comp, passByRef, seg);
}

regNumber reg = MakeWasmReg(m_localIndex++, genActualType(type));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -385,15 +385,21 @@ private protected override void EmitSymbolTable(IDictionary<Utf8String, SymbolDe
}
}

// TODO-WASM: The logic here isn't comprehensive yet. It should cover primitive types and references,
// but by-value structs + nullable types aren't handled yet.
public static class WasmAbiContext
{
private static WasmValueType LowerType(TypeDesc type)
{
if ((type.IsValueType && !type.IsPrimitive) || type.IsNullable)
WasmValueType pointerType = (type.Context.Target.PointerSize == 4) ? WasmValueType.I32 : WasmValueType.I64;

if (type.IsValueType && !type.IsPrimitiveNumeric)
{
throw new NotImplementedException($"By-value struct types are not yet supported: {type}");
type = Internal.JitInterface.WasmLowering.LowerTypeForWasm(type);

if (type == null)
{
// pass by-ref
return pointerType;
}
Comment on lines +392 to +402
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The type.IsValueType && !type.IsPrimitiveNumeric check treats bool/char as “non-primitive” and routes them through WasmLowering.LowerTypeForWasm, which returns null and causes them to be lowered as pointerType (by-ref). This breaks signature lowering for boolean/char parameters/returns. The struct-lowering path should exclude all primitives (e.g., use !type.IsPrimitive or an equivalent check) so that bool/char fall through to the switch cases that map them to I32.

Copilot uses AI. Check for mistakes.
}

switch (type.UnderlyingType.Category)
Expand Down Expand Up @@ -428,7 +434,7 @@ private static WasmValueType LowerType(TypeDesc type)
case TypeFlags.ByRef:
case TypeFlags.Pointer:
case TypeFlags.FunctionPointer:
return WasmValueType.I32;
return pointerType;

default:
throw new NotSupportedException($"Unknown wasm mapping for type: {type.UnderlyingType.Category}");
Expand All @@ -449,7 +455,6 @@ private static WasmValueType LowerType(TypeDesc type)
public static WasmFuncType GetSignature(MethodDesc method)
{
// TODO-WASM: handle struct by-value return (extra parameter pointing to buffer must be in signature)
// TODO-WASM: handle seemingly by-value struct arguments that are actually passed implicitly by reference

MethodSignature signature = method.Signature;
TypeDesc returnType = signature.ReturnType;
Expand Down
19 changes: 19 additions & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3638,6 +3638,25 @@ private void getFpStructLowering(CORINFO_CLASS_STRUCT_* structHnd, ref CORINFO_F
}
}

private CorInfoWasmType getWasmLowering(CORINFO_CLASS_STRUCT_* structHnd)
{
// Call getClassSize to make sure we record the proper dependence
_ = getClassSize(structHnd);

TypeDesc type = HandleToObject(structHnd);
CorInfoType corType = asCorInfoType(type);
Debug.Assert(corType == CorInfoType.CORINFO_TYPE_VALUECLASS);
TypeDesc abiType = WasmLowering.LowerTypeForWasm(type);

if (abiType == null)
{
// Indicate type should be passed by-ref.
return CorInfoWasmType.CORINFO_WASM_TYPE_VOID;
}

return WasmLowering.WasmTypeClassification(abiType);
}

private uint getThreadTLSIndex(ref void* ppIndirection)
{ throw new NotImplementedException("getThreadTLSIndex"); }

Expand Down
17 changes: 17 additions & 0 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl_generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ static ICorJitInfoCallbacks()
s_callbacks.getSystemVAmd64PassStructInRegisterDescriptor = &_getSystemVAmd64PassStructInRegisterDescriptor;
s_callbacks.getSwiftLowering = &_getSwiftLowering;
s_callbacks.getFpStructLowering = &_getFpStructLowering;
s_callbacks.getWasmLowering = &_getWasmLowering;
s_callbacks.getThreadTLSIndex = &_getThreadTLSIndex;
s_callbacks.getAddrOfCaptureThreadGlobal = &_getAddrOfCaptureThreadGlobal;
s_callbacks.getHelperFtn = &_getHelperFtn;
Expand Down Expand Up @@ -328,6 +329,7 @@ static ICorJitInfoCallbacks()
public delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR*, byte> getSystemVAmd64PassStructInRegisterDescriptor;
public delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_SWIFT_LOWERING*, void> getSwiftLowering;
public delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CORINFO_FPSTRUCT_LOWERING*, void> getFpStructLowering;
public delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoWasmType> getWasmLowering;
public delegate* unmanaged<IntPtr, IntPtr*, void**, uint> getThreadTLSIndex;
public delegate* unmanaged<IntPtr, IntPtr*, void**, int*> getAddrOfCaptureThreadGlobal;
public delegate* unmanaged<IntPtr, IntPtr*, CorInfoHelpFunc, CORINFO_CONST_LOOKUP*, CORINFO_METHOD_STRUCT_**, void> getHelperFtn;
Expand Down Expand Up @@ -2273,6 +2275,21 @@ private static void _getFpStructLowering(IntPtr thisHandle, IntPtr* ppException,
}
}

[UnmanagedCallersOnly]
private static CorInfoWasmType _getWasmLowering(IntPtr thisHandle, IntPtr* ppException, CORINFO_CLASS_STRUCT_* structHnd)
{
var _this = GetThis(thisHandle);
try
{
return _this.getWasmLowering(structHnd);
}
catch (Exception ex)
{
*ppException = _this.AllocException(ex);
return default;
}
}

[UnmanagedCallersOnly]
private static uint _getThreadTLSIndex(IntPtr thisHandle, IntPtr* ppException, void** ppIndirection)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ FUNCTIONS
bool getSystemVAmd64PassStructInRegisterDescriptor(CORINFO_CLASS_HANDLE structHnd, SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR* structPassInRegDescPtr);
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);
uint32_t getThreadTLSIndex(void **ppIndirection);
int32_t * getAddrOfCaptureThreadGlobal(void **ppIndirection);
void getHelperFtn (CorInfoHelpFunc ftnNum, CORINFO_CONST_LOOKUP* pNativeEntrypoint, CORINFO_METHOD_HANDLE *pMethod);
Expand Down
Loading
Loading