diff --git a/eng/Subsets.props b/eng/Subsets.props
index 337bca6fa63caf..8201b66e9fe187 100644
--- a/eng/Subsets.props
+++ b/eng/Subsets.props
@@ -338,6 +338,10 @@
$(ClrRuntimeBuildSubsets);ClrWasmJitSubset=true
+
+ $(ClrRuntimeBuildSubsets);ClrWasmJitSubset=true
+
+
$(ClrRuntimeBuildSubsets);ClrPalTestsSubset=true
@@ -394,6 +398,7 @@
<_CrossToolSubset Condition="'$(_BuildCrossComponents)' == 'true' and '$(TargetArchitecture)' != 'wasm' and $(_subset.Contains('+clr.toolstests+'))" Include="ClrAllJitsSubset=true" />
+ <_CrossToolSubset Condition="'$(_BuildCrossComponents)' == 'true' and ('$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'arm64') and $(_subset.Contains('+clr.toolstests+')) and ('$(BuildArchitecture)' == 'x64' or '$(BuildArchitecture)' == 'arm64')" Include="ClrWasmJitSubset=true" />
<_CrossToolSubset Condition="'$(_BuildCrossComponents)' == 'true' and '$(TargetArchitecture)' != 'wasm' and ($(_subset.Contains('+clr.tools+')) or $(_subset.Contains('+clr.nativecorelib+')) or $(_subset.Contains('+clr.crossarchtools+')))" Include="ClrJitSubset=true" />
<_CrossToolSubset Condition="'$(_BuildCrossComponents)' == 'true' and '$(TargetArchitecture)' == 'wasm' and ($(_subset.Contains('+clr.tools+')) or $(_subset.Contains('+clr.nativecorelib+')) or $(_subset.Contains('+clr.crossarchtools+')))" Include="ClrWasmJitSubset=true" />
diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp
index c9dc081a0d4b23..b9a50148748e8a 100644
--- a/src/coreclr/jit/codegenlinear.cpp
+++ b/src/coreclr/jit/codegenlinear.cpp
@@ -872,6 +872,12 @@ void CodeGen::genEmitEndBlock(BasicBlock* block)
break;
case BBJ_SWITCH:
+#if defined(TARGET_WASM)
+ if (block->IsLast() || m_compiler->bbIsFuncletBeg(block->Next()))
+ {
+ genEmitFunctionEnd();
+ }
+#endif
break;
case BBJ_ALWAYS:
@@ -922,7 +928,6 @@ void CodeGen::genEmitEndBlock(BasicBlock* block)
genEmitFunctionEnd();
}
#endif // defined(TARGET_WASM)
-
break;
case BBJ_COND:
@@ -933,6 +938,12 @@ void CodeGen::genEmitEndBlock(BasicBlock* block)
SetLoopAlignBackEdge(block, block->GetFalseTarget());
#endif // FEATURE_LOOP_ALIGN
+#if defined(TARGET_WASM)
+ if (block->IsLast() || m_compiler->bbIsFuncletBeg(block->Next()))
+ {
+ genEmitFunctionEnd();
+ }
+#endif
break;
default:
diff --git a/src/coreclr/jit/codegenwasm.cpp b/src/coreclr/jit/codegenwasm.cpp
index 96cf252708742b..1ce06482125015 100644
--- a/src/coreclr/jit/codegenwasm.cpp
+++ b/src/coreclr/jit/codegenwasm.cpp
@@ -416,17 +416,17 @@ void CodeGen::genFnEpilog(BasicBlock* block)
{
if (block->IsLast() || m_compiler->bbIsFuncletBeg(block->Next()))
{
- instGen(INS_end);
+ genEmitFunctionEnd(/* emitTerminalUnreachable */ false);
}
return;
}
// TODO-WASM: shadow stack maintenance
- // TODO-WASM: we need to handle the end-of-function case if we reach the end of a codegen for a function
- // and do NOT have an epilog. In those cases we currently will not emit an end instruction.
+ // Close the root function before the first funclet starts. Other returns
+ // within the root function leave the remaining root blocks reachable.
if (block->IsLast() || m_compiler->bbIsFuncletBeg(block->Next()))
{
- instGen(INS_end);
+ genEmitFunctionEnd(/* emitTerminalUnreachable */ false);
}
else
{
@@ -3184,7 +3184,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
if (target != nullptr)
{
// Codegen should have already evaluated our target node (last) and pushed it onto the stack,
- // ready for call_indirect. Consume it.
+ // ready for call_indirect. Consume it.
genConsumeReg(target);
params.callType = EC_INDIR_R;
@@ -3195,16 +3195,22 @@ void CodeGen::genCallInstruction(GenTreeCall* call)
// Generate a direct call to a non-virtual user defined or helper method
assert(call->IsHelperCall() || (call->gtCallType == CT_USER_FUNC));
- assert(call->gtEntryPoint.addr == NULL);
-
if (call->IsHelperCall())
{
assert(!call->IsFastTailCall());
- CorInfoHelpFunc helperNum = m_compiler->eeGetHelperNum(params.methHnd);
- noway_assert(helperNum != CORINFO_HELP_UNDEF);
- CORINFO_CONST_LOOKUP helperLookup = m_compiler->compGetHelperFtn(helperNum);
- assert(helperLookup.accessType == IAT_VALUE);
- params.addr = helperLookup.addr;
+
+ if (call->gtDirectCallAddress != nullptr)
+ {
+ params.addr = call->gtDirectCallAddress;
+ }
+ else
+ {
+ CorInfoHelpFunc helperNum = m_compiler->eeGetHelperNum(params.methHnd);
+ noway_assert(helperNum != CORINFO_HELP_UNDEF);
+ CORINFO_CONST_LOOKUP helperLookup = m_compiler->compGetHelperFtn(helperNum);
+ assert(helperLookup.accessType == IAT_VALUE);
+ params.addr = helperLookup.addr;
+ }
}
else
{
@@ -3247,9 +3253,8 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
}
else
{
- params.addr = nullptr;
assert(helperFunction.accessType == IAT_PVALUE);
-
+ params.addr = nullptr;
params.callType = EC_INDIR_R;
}
@@ -3309,12 +3314,14 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize,
if (helperIsManaged)
{
- // Push PEP onto the stack because we are calling a managed helper that expects it as the last parameter.
- // The helper function address is the address of an indirection cell, so we load from the cell to get the PEP
- // address to push.
- assert(helperFunction.accessType == IAT_PVALUE);
GetEmitter()->emitAddressConstant(helperFunction.addr);
- GetEmitter()->emitIns_I(INS_I_load, EA_PTRSIZE, 0);
+ if (helperFunction.accessType != IAT_VALUE)
+ {
+ // Push PEP onto the stack because we are calling a managed helper that expects it as the last parameter.
+ // The helper function address is the address of an indirection cell, so load the PEP from it.
+ assert(helperFunction.accessType == IAT_PVALUE);
+ GetEmitter()->emitIns_I(INS_I_load, EA_PTRSIZE, 0);
+ }
}
if (params.callType == EC_INDIR_R)
diff --git a/src/coreclr/jit/emitwasm.cpp b/src/coreclr/jit/emitwasm.cpp
index cced3becef1a9b..bf7eb773a948ee 100644
--- a/src/coreclr/jit/emitwasm.cpp
+++ b/src/coreclr/jit/emitwasm.cpp
@@ -320,7 +320,7 @@ void emitter::emitIns_Call(const EmitCallParams& params)
{
case EC_FUNC_TOKEN:
ins = params.isJump ? INS_return_call : INS_call;
- id = emitNewInstrSC(EA_HANDLE_CNS_RELOC, 0 /* FIXME-WASM: function index reloc */);
+ id = emitNewInstrSC(EA_HANDLE_CNS_RELOC, (cnsval_ssize_t)params.addr);
id->idIns(ins);
id->idInsFmt(IF_FUNCIDX);
break;
diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp
index 85bd63121d5559..3c4068e88c85d3 100644
--- a/src/coreclr/jit/flowgraph.cpp
+++ b/src/coreclr/jit/flowgraph.cpp
@@ -866,7 +866,10 @@ GenTreeCall* Compiler::fgGetSharedCCtor(CORINFO_CLASS_HANDLE cls)
{
#if defined(TARGET_WASM)
// Wasm does not support dynamically created helpers
- return fgGetStaticsCCtorHelper(cls, CORINFO_HELP_INITCLASS);
+ if (!IsNativeAot())
+ {
+ return fgGetStaticsCCtorHelper(cls, CORINFO_HELP_INITCLASS);
+ }
#endif
#ifdef FEATURE_READYTORUN
diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp
index 099e0400e1794c..2174e99f68d12a 100644
--- a/src/coreclr/jit/lower.cpp
+++ b/src/coreclr/jit/lower.cpp
@@ -7384,21 +7384,27 @@ GenTree* Lowering::LowerVirtualVtableCall(GenTreeCall* call)
{
noway_assert(call->gtCallType == CT_USER_FUNC);
- GenTree* thisArgNode;
+ CallArg* thisArg;
if (call->IsTailCallViaJitHelper())
{
assert(call->gtArgs.CountArgs() > 0);
- thisArgNode = call->gtArgs.GetArgByIndex(0)->GetNode();
+ thisArg = call->gtArgs.GetArgByIndex(0);
}
else
{
assert(call->gtArgs.HasThisPointer());
- thisArgNode = call->gtArgs.GetThisArg()->GetNode();
+ thisArg = call->gtArgs.GetThisArg();
}
+ GenTree* thisArgNode = thisArg->GetNode();
// get a reference to the thisPtr being passed
+#if HAS_FIXED_REGISTER_SET
assert(thisArgNode->OperIs(GT_PUTARG_REG));
GenTree* thisPtr = thisArgNode->AsUnOp()->gtGetOp1();
+#else
+ // On platforms without fixed registers (e.g., WASM), PUTARG nodes are not inserted.
+ GenTree* thisPtr = thisArgNode;
+#endif
// If what we are passing as the thisptr is not already a local, make a new local to place it in
// because we will be creating expressions based on it.
@@ -7415,7 +7421,11 @@ GenTree* Lowering::LowerVirtualVtableCall(GenTreeCall* call)
vtableCallTemp = m_compiler->lvaGrabTemp(true DEBUGARG("virtual vtable call"));
}
+#if HAS_FIXED_REGISTER_SET
LIR::Use thisPtrUse(BlockRange(), &thisArgNode->AsUnOp()->gtOp1, thisArgNode);
+#else
+ LIR::Use thisPtrUse(BlockRange(), &thisArg->NodeRef(), call);
+#endif
ReplaceWithLclVar(thisPtrUse, vtableCallTemp);
lclNum = vtableCallTemp;
diff --git a/src/coreclr/jit/lowerwasm.cpp b/src/coreclr/jit/lowerwasm.cpp
index 5f73d554c8bf99..44d45ea6fa1aa1 100644
--- a/src/coreclr/jit/lowerwasm.cpp
+++ b/src/coreclr/jit/lowerwasm.cpp
@@ -66,22 +66,28 @@ void Lowering::LowerPEPCall(GenTreeCall* call)
JITDUMP("Begin lowering PEP call\n");
DISPTREERANGE(BlockRange(), call);
- // PEP call must always have a control expression
- assert(call->gtControlExpr != nullptr);
- LIR::Use callTargetUse(BlockRange(), &call->gtControlExpr, call);
+ GenTree* callTargetForArg;
+ if (call->gtControlExpr != nullptr)
+ {
+ LIR::Use callTargetUse(BlockRange(), &call->gtControlExpr, call);
- JITDUMP("Creating new local variable for PEP");
- unsigned int callTargetLclNum = callTargetUse.ReplaceWithLclVar(m_compiler);
- GenTreeLclVar* callTargetLclForArg = m_compiler->gtNewLclvNode(callTargetLclNum, TYP_I_IMPL);
+ JITDUMP("Creating new local variable for PEP");
+ unsigned int callTargetLclNum = callTargetUse.ReplaceWithLclVar(m_compiler);
+ callTargetForArg = m_compiler->gtNewLclvNode(callTargetLclNum, TYP_I_IMPL);
+ }
+ else
+ {
+ assert(call->gtDirectCallAddress != nullptr);
+ callTargetForArg = AddrGen(call->gtDirectCallAddress);
+ }
DISPTREE(call);
JITDUMP("Add new arg to call arg list corresponding to PEP target");
- NewCallArg pepTargetArg =
- NewCallArg::Primitive(callTargetLclForArg).WellKnown(WellKnownArg::WasmPortableEntryPoint);
- CallArg* pepArg = call->gtArgs.PushBack(m_compiler, pepTargetArg);
+ NewCallArg pepTargetArg = NewCallArg::Primitive(callTargetForArg).WellKnown(WellKnownArg::WasmPortableEntryPoint);
+ CallArg* pepArg = call->gtArgs.PushBack(m_compiler, pepTargetArg);
pepArg->SetEarlyNode(nullptr);
- pepArg->SetLateNode(callTargetLclForArg);
+ pepArg->SetLateNode(callTargetForArg);
call->gtArgs.PushLateBack(pepArg);
// Set up ABI information for this arg; PEP's should be passed as the last param to a wasm function
@@ -90,12 +96,18 @@ void Lowering::LowerPEPCall(GenTreeCall* call)
pepArg->AbiInfo =
ABIPassingInformation::FromSegmentByValue(m_compiler,
ABIPassingSegment::InRegister(pepReg, 0, TARGET_POINTER_SIZE));
- BlockRange().InsertBefore(call, callTargetLclForArg);
+ BlockRange().InsertBefore(call, callTargetForArg);
// Lower the new PEP arg now that the call abi info is updated and lcl var is inserted
LowerArg(call, pepArg);
DISPTREE(call);
+ if (call->gtControlExpr == nullptr)
+ {
+ JITDUMP("Finished lowering direct PEP call\n");
+ return;
+ }
+
JITDUMP("Rewrite PEP call's control expression to indirect through the new local variable\n");
// Rewrite the call's control expression to have an additional load from the PEP local
diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_Wasm/WasmEmitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_Wasm/WasmEmitter.cs
index 900b7a089812e4..50ee58b73eb434 100644
--- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_Wasm/WasmEmitter.cs
+++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_Wasm/WasmEmitter.cs
@@ -13,14 +13,26 @@
namespace ILCompiler.DependencyAnalysis.Wasm
{
- public struct WasmEmitter(NodeFactory factory, bool relocsOnly)
+ public struct WasmEmitter
{
#if READYTORUN
public WasmFunctionBody FunctionBody = null;
#endif
- public bool Is64Bit => factory.Target.PointerSize == 8;
- public bool RelocsOnly => relocsOnly;
+ private readonly NodeFactory _factory;
+ private readonly bool _relocsOnly;
+
+ public WasmEmitter(NodeFactory factory, bool relocsOnly)
+ {
+ _factory = factory;
+ _relocsOnly = relocsOnly;
+#if READYTORUN
+ FunctionBody = null;
+#endif
+ }
+
+ public bool Is64Bit => _factory.Target.PointerSize == 8;
+ public bool RelocsOnly => _relocsOnly;
public ObjectNode.ObjectData Encode(ISymbolDefinitionNode symbolDefinitionNode)
{
@@ -33,7 +45,7 @@ public ObjectNode.ObjectData Encode(ISymbolDefinitionNode symbolDefinitionNode)
return new ObjectNode.ObjectData(encodedThunk, relocs, 1, new ISymbolDefinitionNode[] { symbolDefinitionNode });
#else
- return default(ObjectNode.ObjectData);
+ throw new PlatformNotSupportedException("NativeAOT WebAssembly assembly stubs are not supported.");
#endif
}
}
diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs
index c2cf6b7fdf4505..a6b608fe79d602 100644
--- a/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs
+++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/ObjectWriter.cs
@@ -713,6 +713,9 @@ public static void EmitObject(string objectFilePath, IReadOnlyCollection buffer)
public abstract class WasmExpr : IWasmEncodable
{
- WasmExprKind _kind;
+ private WasmExprKind _kind;
public WasmExpr(WasmExprKind kind)
{
_kind = kind;
@@ -303,7 +303,7 @@ public static void OffsetRelocationsByOffset(Span buffer, int offset
}
}
- readonly struct WasmEncodableULong : IWasmEncodable
+ internal readonly struct WasmEncodableULong : IWasmEncodable
{
private readonly ulong _value;
public WasmEncodableULong(ulong value)
@@ -322,7 +322,7 @@ public int Encode(Span buffer)
public int EncodeRelocations(Span buffer) => 0;
}
- readonly struct WasmEncodableSymbol : IWasmEncodable
+ internal readonly struct WasmEncodableSymbol : IWasmEncodable
{
private readonly ISymbolNode _symbol;
private readonly RelocType _relocType;
@@ -374,10 +374,10 @@ public int EncodeRelocations(Span buffer)
}
}
- class WasmMemoryArgInstruction : WasmExpr where TOffset : IWasmEncodable
+ internal sealed class WasmMemoryArgInstruction : WasmExpr where TOffset : IWasmEncodable
{
- readonly uint _align;
- readonly TOffset _offset;
+ private readonly uint _align;
+ private readonly TOffset _offset;
public WasmMemoryArgInstruction(WasmExprKind kind, uint align, TOffset offset) : base(kind)
{
@@ -418,9 +418,9 @@ public override int EncodeRelocations(Span buffer)
}
// Represents a constant expression (e.g., (i32.const ))
- class WasmConstExpr : WasmExpr
+ internal sealed class WasmConstExpr : WasmExpr
{
- readonly long ConstValue;
+ private readonly long ConstValue;
public WasmConstExpr(WasmExprKind kind, long value) : base(kind)
{
@@ -448,10 +448,10 @@ public override int Encode(Span buffer)
}
}
- sealed class WasmIndirectCallInstruction : WasmExpr
+ internal sealed class WasmIndirectCallInstruction : WasmExpr
{
- ISymbolNode _type;
- uint _tableIndex;
+ private ISymbolNode _type;
+ private uint _tableIndex;
public WasmIndirectCallInstruction(WasmExprKind kind, ISymbolNode type, uint tableIndex) : base(kind)
{
@@ -484,9 +484,9 @@ public override int EncodeRelocations(Span buffer)
}
}
- sealed class WasmLEBConstantReloc : WasmExpr
+ internal sealed class WasmLEBConstantReloc : WasmExpr
{
- readonly WasmEncodableSymbol _symbol;
+ private readonly WasmEncodableSymbol _symbol;
public WasmLEBConstantReloc(WasmExprKind kind, ISymbolNode symbol, RelocType relocType) : base(kind)
{
@@ -511,7 +511,7 @@ public override int EncodeRelocations(Span buffer)
}
// Represents a local variable expression (e.g., (local.get ))
- class WasmLocalVarExpr : WasmExpr
+ internal sealed class WasmLocalVarExpr : WasmExpr
{
public readonly int LocalIndex;
public WasmLocalVarExpr(WasmExprKind kind, int localIndex) : base(kind)
@@ -536,7 +536,7 @@ public override int EncodeSize()
}
// Represents a global variable expression (e.g., (global.get ()
- class WasmMemoryCopyExpr : WasmExpr
+ internal sealed class WasmMemoryCopyExpr : WasmExpr
{
public readonly int DstMemoryIndex;
public readonly int SrcMemoryIndex;
@@ -614,7 +614,7 @@ public override int EncodeSize()
// Represents a memory.fill expression.
// Binary encoding: 0xFC prefix + u32(11) sub-opcode + u32(memoryIndex)
// Stack operands: (dst: i32, val: i32, len: i32) -> ()
- class WasmMemoryFillExpr : WasmExpr
+ internal sealed class WasmMemoryFillExpr : WasmExpr
{
public readonly int MemoryIndex;
@@ -640,7 +640,7 @@ public override int EncodeSize()
}
// Represents a memory.init expression.
- class WasmMemoryInitExpr : WasmExpr
+ internal sealed class WasmMemoryInitExpr : WasmExpr
{
public readonly int DataSegmentIndex;
public readonly int MemoryIndex;
@@ -672,7 +672,7 @@ public override int EncodeSize()
// Represents a table.init expression.
// Binary encoding: 0xFC prefix + u32(12) sub-opcode + u32(elemidx) + u32(tableidx)
- class WasmTableInitExpr : WasmExpr
+ internal sealed class WasmTableInitExpr : WasmExpr
{
public readonly int ElemIndex;
public readonly int TableIndex;
@@ -702,7 +702,7 @@ public override int EncodeSize()
}
}
- class WasmTableGrowExpr : WasmExpr
+ internal sealed class WasmTableGrowExpr : WasmExpr
{
public readonly uint TableIndex;
@@ -723,14 +723,14 @@ public override int EncodeSize()
}
}
- enum WasmAbsHeapType : byte
+ internal enum WasmAbsHeapType : byte
{
Func = 0x70,
}
- class WasmRefNullExpr : WasmExpr
+ internal sealed class WasmRefNullExpr : WasmExpr
{
- WasmAbsHeapType absheaptype;
+ private WasmAbsHeapType absheaptype;
public WasmRefNullExpr(WasmAbsHeapType heapType) : base(WasmExprKind.RefNull)
{
@@ -749,7 +749,7 @@ public override int EncodeSize()
}
}
- enum WasmBlockType : byte
+ internal enum WasmBlockType : byte
{
Empty = 0x40,
I32 = 0x7F,
@@ -758,9 +758,9 @@ enum WasmBlockType : byte
F64 = 0x7C,
V128 = 0x7B,
}
- class WasmBlockStartExpr : WasmExpr
+ internal sealed class WasmBlockStartExpr : WasmExpr
{
- WasmBlockType BlockType;
+ private WasmBlockType BlockType;
public WasmBlockStartExpr(WasmExprKind kind, WasmBlockType blockType) : base(kind)
{
BlockType = blockType;
@@ -780,7 +780,7 @@ public override int EncodeSize()
// ************************************************
// Simple DSL wrapper for creating Wasm expressions
// ************************************************
- static class Local
+ internal static class Local
{
public static WasmExpr Get(int index)
{
@@ -796,7 +796,7 @@ public static WasmExpr Tee(int index)
}
}
- static class Global
+ internal static class Global
{
public static WasmExpr Get(int index)
{
@@ -808,7 +808,7 @@ public static WasmExpr Set(int index)
}
}
- static class I32
+ internal static class I32
{
public static WasmExpr Const(long value)
{
@@ -827,7 +827,7 @@ public static WasmExpr ConstRVA(ISymbolNode symbolNode)
public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.I32Store, 4, new WasmEncodableULong(offset));
}
- static class I64
+ internal static class I64
{
public static WasmExpr Const(long value)
{
@@ -837,25 +837,25 @@ public static WasmExpr Const(long value)
public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.I64Store, 8, new WasmEncodableULong(offset));
}
- static class F32
+ internal static class F32
{
public static WasmExpr Load(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.F32Load, 4, new WasmEncodableULong(offset));
public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.F32Store, 4, new WasmEncodableULong(offset));
}
- static class F64
+ internal static class F64
{
public static WasmExpr Load(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.F64Load, 8, new WasmEncodableULong(offset));
public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.F64Store, 8, new WasmEncodableULong(offset));
}
- static class V128
+ internal static class V128
{
public static WasmExpr Load(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.V128Load, 16, new WasmEncodableULong(offset));
public static WasmExpr Store(ulong offset) => new WasmMemoryArgInstruction(WasmExprKind.V128Store, 16, new WasmEncodableULong(offset));
}
- static class Memory
+ internal static class Memory
{
public static WasmExpr Copy(int dstMemoryIndex = 0, int srcMemoryIndex = 0)
{
@@ -872,20 +872,20 @@ public static WasmExpr Init(int dataSegmentIndex, int memoryIndex = 0)
return new WasmMemoryInitExpr(dataSegmentIndex, memoryIndex);
}
}
- static class ControlFlow
+ internal static class ControlFlow
{
public static WasmExpr CallIndirect(ISymbolNode funcType, uint tableIndex) => new WasmIndirectCallInstruction(WasmExprKind.CallIndirect, funcType, tableIndex);
}
- static class Table
+ internal static class Table
{
public static WasmExpr Grow(uint tableIndex) => new WasmTableGrowExpr(tableIndex);
public static WasmExpr Init(int elemSegmentIndex, int tableIndex = 0) => new WasmTableInitExpr(elemSegmentIndex, tableIndex);
}
- static class Ref
+ internal static class Ref
{
public static WasmExpr NullFuncRef => new WasmRefNullExpr(WasmAbsHeapType.Func);
}
- static class Block
+ internal static class Block
{
public static WasmExpr If(WasmBlockType blockType) => new WasmBlockStartExpr(WasmExprKind.If, blockType);
public static WasmExpr End => new WasmUnaryExpr(WasmExprKind.End);
diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs
index 6441eef518f560..9bc77de3a9f201 100644
--- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs
+++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmNative.cs
@@ -85,8 +85,8 @@ internal enum WasmExportKind : byte
public class WasmGlobalImportType : WasmImportType
{
- WasmValueType _valueType;
- WasmMutabilityType _mutability;
+ private readonly WasmValueType _valueType;
+ private readonly WasmMutabilityType _mutability;
public WasmGlobalImportType(WasmValueType valueType, WasmMutabilityType mutability) : base (WasmExternalKind.Global)
{
@@ -115,7 +115,7 @@ public WasmTableImportType() : base (WasmExternalKind.Table)
public override int Encode(Span buffer)
{
int pos = 0;
- buffer[pos++] = (byte)0x70; // element type: funcref
+ buffer[pos++] = (byte)0x70; // element type: funcref
buffer[pos++] = (byte)0; // table limits: flags (0 = min-only, 1 = min+max)
pos += DwarfHelper.WriteULEB128(buffer.Slice(pos), 1); // Requires 1 table entry
return pos;
@@ -131,12 +131,12 @@ public enum WasmLimitType : byte
HasMin = 0x00,
HasMinAndMax = 0x01
}
-
+
public class WasmMemoryImportType : WasmImportType
{
- WasmLimitType _limitType;
- uint _min;
- uint? _max;
+ private readonly WasmLimitType _limitType;
+ private readonly uint _min;
+ private readonly uint? _max;
public WasmMemoryImportType(WasmLimitType limitType, uint min, uint? max = null) : base(WasmExternalKind.Memory)
{
diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs
index 29f33411a947f4..58f386655f7e40 100644
--- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs
+++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmObjectWriter.cs
@@ -21,11 +21,6 @@ namespace ILCompiler.ObjectWriter
///
internal abstract partial class WasmObjectWriter : ObjectWriter
{
- public const int StackPointerGlobalIndex = WasmGlobalImports.StackPointerGlobalIndex;
- public const int ImageBaseGlobalIndex = WasmGlobalImports.ImageBaseGlobalIndex;
- public const int TableBaseGlobalIndex = WasmGlobalImports.TableBaseGlobalIndex;
- public const int AsyncContinuationGlobalIndex = WasmGlobalImports.AsyncContinuationGlobalIndex;
-
private readonly Dictionary _sectionToType = new()
{
{ WasmObjectNodeSection.MemorySection, WasmSectionType.Memory },
@@ -74,13 +69,10 @@ private protected int[] SectionEmitOrder
{
get
{
- if (_sectionEmitOrder is null)
- {
- _sectionEmitOrder = _sectionOrder
- .Where(name => _sections.Contains(name))
- .Select(name => _sections.GetSectionIndex(name))
- .ToArray();
- }
+ _sectionEmitOrder ??= _sectionOrder
+ .Where(_sections.Contains)
+ .Select(_sections.GetSectionIndex)
+ .ToArray();
return _sectionEmitOrder;
}
@@ -160,7 +152,15 @@ private protected override void RecordMethodDeclaration(INodeWithTypeSignature n
flags |= WasmLowering.LoweringFlags.IsUnmanagedCallersOnly;
}
WriteSignatureIndexForFunction(node.Signature, flags, node);
- RegisterFunctionSymbol(new Utf8String(node.GetMangledName(_nodeFactory.NameMangler)));
+ Utf8String functionName = GetMangledName(node);
+ RegisterFunctionSymbol(functionName);
+
+ Utf8String alternateName = _nodeFactory.GetSymbolAlternateName(node, out _);
+ if (!alternateName.IsNull)
+ {
+ _wasmSymbolManager.AddAlias(ExternCName(alternateName), functionName);
+ }
+
if (node is INodeWithFunclets nodeWithFunclets)
{
RecordFunclets(nodeWithFunclets);
@@ -400,9 +400,9 @@ private protected void FinalizeSectionEntryCounts()
_sections.GetSection(ObjectNodeSection.WasmCodeSection.Name)
.SetEntryCount(MethodCount);
- Debug.Assert(_sections.GetSection(WasmObjectNodeSection.FunctionSection.Name).EntryCount == MethodCount);
- Debug.Assert(_sections.GetSection(WasmObjectNodeSection.ImportSection.Name).EntryCount == _wasmSymbolManager.GetImportCount());
- Debug.Assert(_sections.GetSection(WasmObjectNodeSection.GlobalSection.Name).EntryCount == _wasmSymbolManager.GetDefinitionCount(WasmIndexSpace.Global));
+ Debug.Assert(GetOrCreateSection(WasmObjectNodeSection.ImportSection, out _).EntryCount == _wasmSymbolManager.GetImportCount());
+ Debug.Assert(GetOrCreateSection(WasmObjectNodeSection.FunctionSection, out _).EntryCount == MethodCount);
+ Debug.Assert(GetOrCreateSection(WasmObjectNodeSection.GlobalSection, out _).EntryCount == _wasmSymbolManager.GetDefinitionCount(WasmIndexSpace.Global));
}
}
diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmRelocatableObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmRelocatableObjectWriter.cs
new file mode 100644
index 00000000000000..bc267f37baed92
--- /dev/null
+++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmRelocatableObjectWriter.cs
@@ -0,0 +1,214 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Diagnostics;
+using System.Collections.Generic;
+using System.IO;
+using ILCompiler.DependencyAnalysis;
+using Internal.JitInterface;
+using Internal.Text;
+using Internal.TypeSystem.TypesDebugInfo;
+using ILCompiler.DependencyAnalysis.Wasm;
+
+namespace ILCompiler.ObjectWriter
+{
+ internal sealed partial class WasmRelocatableObjectWriter : WasmObjectWriter
+ {
+ public WasmRelocatableObjectWriter(NodeFactory factory, ObjectWritingOptions options, OutputInfoBuilder outputInfoBuilder = null) : base(factory, options, outputInfoBuilder)
+ {
+ }
+
+ private protected override void EmitObjectFile(Stream outputFileStream)
+ {
+ Debug.Assert(outputFileStream.CanSeek, $"EmitObjectFile requires seekable output stream");
+
+ FinalizeSectionEntryCounts();
+
+ EmitWasmHeader(outputFileStream);
+
+ foreach (int index in SectionEmitOrder)
+ {
+ SectionDataEmitter section = _sections[index];
+ if (_resolvableRelocations.TryGetValue(index, out List relocations) &&
+ section is WasmSection)
+ {
+ using (Stream originalStream = section.ContentReadStream)
+ {
+ MemoryStream stream = new MemoryStream((int)originalStream.Length);
+ originalStream.Position = 0;
+ originalStream.CopyTo(stream);
+ ResolveRelocations(index, stream, relocations, sectionStart: 0);
+ section.ContentReadStream = stream;
+ // originalStream may be disposed, section.Stream now points to resolved stream
+ }
+ }
+
+ section.EmitToStream(outputFileStream);
+ }
+ }
+
+ private Dictionary> _resolvableRelocations = new();
+ private protected override void EmitRelocations(int sectionIndex, List relocationList)
+ {
+ foreach (var reloc in relocationList)
+ {
+ if (!_resolvableRelocations.TryGetValue(sectionIndex, out List resolvable))
+ {
+ _resolvableRelocations[sectionIndex] = resolvable = new List();
+ }
+ // Unconditionally add the reloc to our resolvable list; we do some amount of relocation resolution
+ // for all relocation types.
+ resolvable.Add(reloc);
+ }
+ }
+
+ private unsafe void ResolveRelocations(int sectionIndex, MemoryStream sectionStream, List relocs, long sectionStart = 0)
+ {
+ byte[] relocScratchBuffer = new byte[Relocation.MaxSize];
+
+ foreach (SymbolicRelocation reloc in relocs)
+ {
+ int size = Relocation.GetSize(reloc.Type);
+ if (size > relocScratchBuffer.Length)
+ {
+ throw new InvalidOperationException($"Unsupported relocation size for relocation: {reloc.Type}");
+ }
+
+ SymbolDefinition definedSymbol = _definedSymbols[reloc.SymbolName];
+
+ // We need a pinned raw pointer here for manipulation with Relocation.WriteValue
+ fixed (byte* pData = ReadRelocToDataSpan(reloc, relocScratchBuffer, sectionStart))
+ {
+ long addend = Relocation.ReadValue(reloc.Type, pData);
+ int relocLength = Relocation.GetSize(reloc.Type);
+
+ 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:
+ case RelocType.WASM_MEMORY_ADDR_REL_SLEB when _sections.GetSection(definedSymbol.SectionIndex).Type == WasmSectionType.Code:
+ {
+ // 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 (!_wasmSymbolManager.TryGetSymbol(reloc.SymbolName, out WasmSymbol symbol))
+ {
+ throw new InvalidOperationException($"Symbol '{reloc.SymbolName}' was not registered. Relocation type {reloc.Type}.");
+ }
+ Relocation.WriteValue(reloc.Type, pData, symbol.Index + addend);
+ break;
+ }
+
+ default:
+ // TODO-WASM: add other cases as needed;
+ // ignoring other reloc types for now
+ throw new NotSupportedException($"Relocation type {reloc.Type} not yet implemented");
+ }
+
+ WriteRelocFromDataSpan(reloc, pData, sectionStart);
+ }
+ }
+
+ Span ReadRelocToDataSpan(SymbolicRelocation reloc, byte[] buffer, long sectionStart)
+ {
+ Span relocContents = buffer.AsSpan(0, Relocation.GetSize(reloc.Type));
+ sectionStream.Position = reloc.Offset + sectionStart;
+ sectionStream.ReadExactly(relocContents);
+ return relocContents;
+ }
+
+ void WriteRelocFromDataSpan(SymbolicRelocation reloc, byte* pData, long sectionStart)
+ {
+ sectionStream.Position = reloc.Offset + sectionStart;
+ sectionStream.Write(new Span(pData, Relocation.GetSize(reloc.Type)));
+ }
+ }
+
+ private protected override SectionDataEmitter CreateDataSection(
+ ObjectNodeSection section,
+ int sectionIndex,
+ Stream sectionStream)
+ {
+ return new WasmSection(WasmSectionType.Data, sectionStream, new Utf8String("data"), sectionIndex);
+ }
+
+ protected internal override void UpdateSectionAlignment(int sectionIndex, int alignment)
+ {
+ }
+ private protected override void WriteGlobalSection()
+ {
+ }
+
+ private const int RtlRestoreContextTagIndex = 0;
+ private static readonly WasmFuncType RtlRestoreContextTagSignature = new(
+ new([]),
+ new([]));
+ private const int StackPointerGlobalIndex = 0;
+ private const int ImageBaseGlobalIndex = 1;
+ private const int TableBaseGlobalIndex = 2;
+ private const int AsyncContinuationGlobalIndex = 3;
+ private static readonly Utf8String RtlRestoreContextTagName = new Utf8String("RtlRestoreContextTag");
+ private WasmImport[] CreateDefaultGlobalImports()
+ {
+ int rtlRestoreContextTagTypeIndex = RegisterSignature(RtlRestoreContextTagSignature);
+
+ return
+ [
+ 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", WasmWellKnownGlobalSymbolNode.AsyncContinuationName, import: new WasmGlobalImportType(WasmValueType.I32, WasmMutabilityType.Mut), index: AsyncContinuationGlobalIndex),
+ new WasmImport("webcil", "table", import: new WasmTableImportType(), index: 0),
+ new WasmImport("webcil", RtlRestoreContextTagName.ToString(), import: new WasmTagImportType(rtlRestoreContextTagTypeIndex), index: RtlRestoreContextTagIndex),
+ new WasmImport("webcil", "memory", import: new WasmMemoryImportType(WasmLimitType.HasMin, /* TODO: This is an arbitrary number */ 32))
+ ];
+ }
+
+ private protected override void WriteImports()
+ {
+ foreach (WasmImport import in CreateDefaultGlobalImports())
+ {
+ WriteImport(import);
+ }
+ }
+
+ private protected override void WriteExports()
+ {
+ }
+
+ private protected override void WriteElements()
+ {
+ }
+ }
+
+ // AOT
+ internal sealed partial class WasmRelocatableObjectWriter : WasmObjectWriter
+ {
+ private protected override void EmitUnwindInfo(SectionWriter sectionWriter, INodeWithCodeInfo nodeWithCodeInfo, Utf8String currentSymbolName)
+ {
+ }
+
+ private protected override ITypesDebugInfoWriter CreateDebugInfoBuilder()
+ {
+ return null;
+ }
+
+ private protected override void EmitDebugFunctionInfo(uint methodTypeIndex, Utf8String methodName, SymbolDefinition methodSymbol, INodeWithDebugInfo debugNode, bool hasSequencePoints)
+ {
+ }
+
+ private protected override void EmitDebugSections(IDictionary definedSymbols)
+ {
+ }
+
+ private protected override void CreateEhSections()
+ {
+ }
+ }
+}
diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmSymbolManager.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmSymbolManager.cs
index 1fe51a1d5eb0f7..b53d4ad06dd570 100644
--- a/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmSymbolManager.cs
+++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/WasmSymbolManager.cs
@@ -56,6 +56,7 @@ public T this[WasmIndexSpace indexSpace]
}
private readonly Dictionary _entries = new();
+ private readonly Dictionary _aliases = new();
private IndexSpaceArray _importCounts = new IndexSpaceArray();
private IndexSpaceArray _definitionCounts = new IndexSpaceArray();
private IndexSpaceArray _importsFrozen = new IndexSpaceArray();
@@ -76,14 +77,21 @@ public void AddDefinition(Utf8String name, WasmIndexSpace indexSpace)
_definitionCounts[indexSpace]++;
}
+ public void AddAlias(Utf8String alias, Utf8String target)
+ {
+ Entry entry = _entries[target];
+ _aliases.Add(alias, entry with { Name = alias });
+ }
+
public WasmSymbol GetSymbol(Utf8String name)
{
- return ResolveAndFreeze(_entries[name]);
+ return ResolveAndFreeze(GetEntry(name));
}
public bool TryGetSymbol(Utf8String name, out WasmSymbol symbol)
{
- if (!_entries.TryGetValue(name, out Entry entry))
+ if (!_entries.TryGetValue(name, out Entry entry) &&
+ !_aliases.TryGetValue(name, out entry))
{
symbol = default;
return false;
@@ -93,6 +101,9 @@ public bool TryGetSymbol(Utf8String name, out WasmSymbol symbol)
return true;
}
+ private Entry GetEntry(Utf8String name) =>
+ _entries.TryGetValue(name, out Entry entry) ? entry : _aliases[name];
+
public int GetImportCount() => _importCounts.Values.Sum();
public int GetDefinitionCount(WasmIndexSpace indexSpace) =>
diff --git a/src/coreclr/tools/Common/JitInterface/WasmLowering.cs b/src/coreclr/tools/Common/JitInterface/WasmLowering.cs
index 592ec82427ad16..1d7bdd03e6d639 100644
--- a/src/coreclr/tools/Common/JitInterface/WasmLowering.cs
+++ b/src/coreclr/tools/Common/JitInterface/WasmLowering.cs
@@ -394,7 +394,7 @@ public static WasmSignature GetSignature(MethodSignature signature, LoweringFlag
{
if (!flags.HasFlag(LoweringFlags.IsUnmanagedCallersOnly) && signature.Flags.HasFlag(MethodSignatureFlags.UnmanagedCallingConvention))
{
- flags = flags | LoweringFlags.IsUnmanagedCallersOnly;
+ flags |= LoweringFlags.IsUnmanagedCallersOnly;
}
TypeDesc returnType = signature.ReturnType;
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/ILCompiler.Compiler.Tests.Assets.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/ILCompiler.Compiler.Tests.Assets.csproj
index 2e0b217160f958..2bd02d648b64e7 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/ILCompiler.Compiler.Tests.Assets.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/ILCompiler.Compiler.Tests.Assets.csproj
@@ -15,4 +15,8 @@
+
+
+
+
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/XunitStubs.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/XunitStubs.cs
new file mode 100644
index 00000000000000..4d4f9ef801ffa6
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/XunitStubs.cs
@@ -0,0 +1,9 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Xunit
+{
+ internal sealed class FactAttribute : System.Attribute
+ {
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.csproj
index 69e9d87637f92d..ef24d8566e4d3b 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.csproj
@@ -26,6 +26,9 @@
+
+ Configuration=$(CoreCLRConfiguration)
+
false
@@ -43,5 +46,22 @@
+
+
+
+
+ <_NativeAotWasmTestSupported Condition="('$(BuildArchitecture)' == 'x64' or '$(BuildArchitecture)' == 'arm64') and ('$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'arm64')">true
+
+
+
+
+ $(_NativeAotWasmTestSupported)
+
+
+ $(BuildArchitecture)
+
+
+ $(CoreCLRArtifactsPath)
+
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/WasmSingleMethodTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/WasmSingleMethodTests.cs
new file mode 100644
index 00000000000000..0eb70da110a9e8
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/WasmSingleMethodTests.cs
@@ -0,0 +1,195 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Threading.Tasks;
+
+using Microsoft.DotNet.XUnitExtensions;
+
+using Xunit;
+
+namespace ILCompiler.Compiler.Tests
+{
+ public class WasmSingleMethodTests
+ {
+ private const string ExportName = "ILCompiler_Compiler_Tests_Assets_SwitchTest__TestEntryPoint";
+ private static readonly byte[] WasmHeader = [0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00];
+
+ public static bool IsWasmCompilationSupported =>
+ string.Equals(
+ AppContext.GetData("NativeAotWasmTest.IsSupported") as string,
+ "true",
+ StringComparison.OrdinalIgnoreCase);
+
+ public static bool IsWasmExecutionSupported =>
+ IsWasmCompilationSupported &&
+ RunProcess(
+ "node",
+ ["-e", "process.exit(typeof WebAssembly.Tag === 'function' ? 0 : 1)"],
+ throwOnError: false).ExitCode == 0;
+
+ [ConditionalFact(nameof(IsWasmCompilationSupported))]
+ public void NativeAotWasmSingleMethodCompiles()
+ {
+ string outputPath = CompileSwitchTest();
+ try
+ {
+ byte[] output = File.ReadAllBytes(outputPath);
+ Assert.True(output.Length >= WasmHeader.Length);
+ Assert.Equal(WasmHeader, output.AsSpan(0, WasmHeader.Length).ToArray());
+ }
+ finally
+ {
+ File.Delete(outputPath);
+ }
+ }
+
+ [ConditionalFact(nameof(IsWasmExecutionSupported))]
+ public void NativeAotWasmSingleMethodExecutes()
+ {
+ string outputPath = CompileSwitchTest();
+ string scriptPath = Path.ChangeExtension(outputPath, ".js");
+ try
+ {
+ File.WriteAllText(scriptPath,
+ $$"""
+ const fs = require("fs");
+ const bytes = fs.readFileSync({{ToJavaScriptString(outputPath)}});
+ if (!WebAssembly.validate(bytes)) {
+ throw new Error("NativeAOT produced an invalid WebAssembly module.");
+ }
+ const webcil = {
+ stackPointer: new WebAssembly.Global({ value: "i32", mutable: true }, 65000),
+ imageBase: new WebAssembly.Global({ value: "i32", mutable: false }, 0),
+ tableBase: new WebAssembly.Global({ value: "i32", mutable: false }, 0),
+ asyncContinuation: new WebAssembly.Global({ value: "i32", mutable: true }, 0),
+ table: new WebAssembly.Table({ initial: 4096, element: "anyfunc" }),
+ rtlRestoreContextTag: new WebAssembly.Tag({ parameters: [] }),
+ memory: new WebAssembly.Memory({ initial: 16 }),
+ };
+ WebAssembly.instantiate(bytes, { webcil }).then(({ instance }) => {
+ const result = instance.exports.{{ExportName}}(65000, 0);
+ if (result !== 100) {
+ throw new Error(`Expected 100, got ${result}.`);
+ }
+ });
+ """);
+
+ ProcessResult result = RunProcess("node", [scriptPath], throwOnError: false);
+ Assert.True(result.ExitCode == 0, result.Output);
+ }
+ finally
+ {
+ File.Delete(scriptPath);
+ File.Delete(outputPath);
+ }
+ }
+
+ private static string CompileSwitchTest()
+ {
+ string coreClrArtifactsDir = Assert.IsType(AppContext.GetData("NativeAotWasmTest.CoreCLRArtifactsDir"));
+ string buildArchitecture = Assert.IsType(AppContext.GetData("NativeAotWasmTest.BuildArchitecture"));
+ string ilcPath = Path.Combine(
+ coreClrArtifactsDir,
+ buildArchitecture,
+ "ilc",
+ OperatingSystem.IsWindows() ? "ilc.exe" : "ilc");
+ string jitFileName = OperatingSystem.IsWindows()
+ ? $"clrjit_universal_wasm_{buildArchitecture}.dll"
+ : OperatingSystem.IsMacOS()
+ ? $"libclrjit_universal_wasm_{buildArchitecture}.dylib"
+ : $"libclrjit_universal_wasm_{buildArchitecture}.so";
+ string jitPath = Path.Combine(coreClrArtifactsDir, jitFileName);
+ if (!File.Exists(jitPath))
+ {
+ jitPath = Path.Combine(coreClrArtifactsDir, buildArchitecture, jitFileName);
+ }
+
+ Assert.True(File.Exists(jitPath), $"WASM JIT not found at '{jitPath}'.");
+
+ string outputPath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():N}.wasm");
+ try
+ {
+ RunProcess(
+ ilcPath,
+ [
+ "--singlemethodtypename", "SwitchTest, ILCompiler.Compiler.Tests.Assets",
+ "--singlemethodname", "TestEntryPoint",
+ Path.Combine(AppContext.BaseDirectory, "ILCompiler.Compiler.Tests.Assets.dll"),
+ $"-r:{Path.Combine(AppContext.BaseDirectory, "Test.CoreLib.dll")}",
+ "--systemmodule:Test.CoreLib",
+ $"-o:{outputPath}",
+ "--targetarch:wasm",
+ "--targetos:browser",
+ $"--jitpath:{jitPath}",
+ "--stacktracedata:none",
+ "--reflectiondata:none",
+ ],
+ throwOnError: true);
+
+ return outputPath;
+ }
+ catch
+ {
+ File.Delete(outputPath);
+ throw;
+ }
+ }
+
+ private static ProcessResult RunProcess(string fileName, IEnumerable arguments, bool throwOnError)
+ {
+ var startInfo = new ProcessStartInfo(fileName)
+ {
+ RedirectStandardError = true,
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ };
+ foreach (string argument in arguments)
+ {
+ startInfo.ArgumentList.Add(argument);
+ }
+
+ try
+ {
+ using Process process = Process.Start(startInfo) ??
+ throw new InvalidOperationException($"Failed to start '{fileName}'.");
+ Task standardOutput = process.StandardOutput.ReadToEndAsync();
+ Task standardError = process.StandardError.ReadToEndAsync();
+ process.WaitForExit();
+
+ var result = new ProcessResult(
+ process.ExitCode,
+ standardOutput.GetAwaiter().GetResult() + standardError.GetAwaiter().GetResult());
+ if (throwOnError && result.ExitCode != 0)
+ {
+ throw new InvalidOperationException(result.Output);
+ }
+
+ return result;
+ }
+ catch (Exception ex) when (!throwOnError && ex is Win32Exception or InvalidOperationException)
+ {
+ return new ProcessResult(-1, ex.ToString());
+ }
+ }
+
+ private static string ToJavaScriptString(string value) =>
+ '"' + value.Replace("\\", "\\\\", StringComparison.Ordinal).Replace("\"", "\\\"", StringComparison.Ordinal) + '"';
+
+ private readonly struct ProcessResult
+ {
+ public ProcessResult(int exitCode, string output)
+ {
+ ExitCode = exitCode;
+ Output = output;
+ }
+
+ public int ExitCode { get; }
+ public string Output { get; }
+ }
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs
index 636bf28595a23f..035042b3d012ff 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/NodeFactory.cs
@@ -11,6 +11,7 @@
using ILCompiler.DependencyAnalysisFramework;
using Internal.IL;
+using Internal.JitInterface;
using Internal.NativeFormat;
using Internal.Runtime;
using Internal.Text;
@@ -1618,9 +1619,14 @@ public AnalysisCharacteristicNode AnalysisCharacteristic(string ch)
// memory efficiency on lookup
public WasmTypeNode WasmTypeNode(MethodDesc desc)
{
- // TODO-Wasm: Construct proper function type based on the passed in MethodDesc
- // once we have defined lowering rules for signatures in NativeAOT.
- throw new NotImplementedException("NAOT wasm type signature lowering not yet implemented");
+ WasmFuncType funcType = WasmLowering.GetSignature(desc).FuncType;
+ return _wasmTypeNodes.GetOrAdd(funcType);
+ }
+
+ public WasmTypeNode WasmTypeNode(CorInfoWasmType[] types)
+ {
+ WasmFuncType funcType = WasmFuncType.FromCorInfoSignature(types);
+ return _wasmTypeNodes.GetOrAdd(funcType);
}
///
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs
index ec6fa5d049cd3f..a8e07d0f528fca 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ReadyToRunGenericHelperNode.cs
@@ -13,6 +13,10 @@
namespace ILCompiler.DependencyAnalysis
{
+ ///
+ /// Represents a NativeAOT runtime generic dictionary lookup helper.
+ /// "ReadyToRun" refers to the JIT helper ABI used to request the lookup, not to the ReadyToRun compiler.
+ ///
public abstract partial class ReadyToRunGenericHelperNode : AssemblyStubNode, INodeWithRuntimeDeterminedDependencies
{
private readonly ReadyToRunHelperId _id;
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmJumpStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmJumpStubNode.cs
index 0adaef32572541..eca26b3792cc9a 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmJumpStubNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmJumpStubNode.cs
@@ -11,7 +11,7 @@ public partial class JumpStubNode
{
protected override void EmitCode(NodeFactory factory, ref WasmEmitter encoder, bool relocsOnly)
{
- throw new NotImplementedException();
+ throw new PlatformNotSupportedException("NativeAOT WebAssembly jump stubs are not supported.");
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmReadyToRunGenericHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmReadyToRunGenericHelperNode.cs
index c5c13718eb873c..dfed6e0e5c3eb9 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmReadyToRunGenericHelperNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmReadyToRunGenericHelperNode.cs
@@ -11,12 +11,14 @@ public partial class ReadyToRunGenericHelperNode
{
protected override void EmitCode(NodeFactory factory, ref WasmEmitter encoder, bool relocsOnly)
{
- throw new NotImplementedException();
+ throw new PlatformNotSupportedException(
+ "NativeAOT WebAssembly does not support runtime generic dictionary lookup helpers.");
}
protected virtual void EmitLoadGenericContext(NodeFactory factory, ref WasmEmitter encoder, bool relocsOnly)
{
- throw new NotImplementedException();
+ throw new PlatformNotSupportedException(
+ "NativeAOT WebAssembly runtime generic dictionary context loading is not supported.");
}
}
@@ -24,7 +26,8 @@ public partial class ReadyToRunGenericLookupFromTypeNode
{
protected override void EmitLoadGenericContext(NodeFactory factory, ref WasmEmitter encoder, bool relocsOnly)
{
- throw new NotImplementedException();
+ throw new PlatformNotSupportedException(
+ "NativeAOT WebAssembly runtime generic dictionary context loading from a type is not supported.");
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmReadyToRunHelperNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmReadyToRunHelperNode.cs
index 310f7e22c3153d..09702fc27ce327 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmReadyToRunHelperNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmReadyToRunHelperNode.cs
@@ -11,7 +11,7 @@ public partial class ReadyToRunHelperNode
{
protected override void EmitCode(NodeFactory factory, ref WasmEmitter encoder, bool relocsOnly)
{
- throw new NotImplementedException();
+ throw new PlatformNotSupportedException("NativeAOT WebAssembly ReadyToRun helpers are not supported.");
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmTentativeMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmTentativeMethodNode.cs
index 7596bdeec2a9dd..f20476c7d37649 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmTentativeMethodNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmTentativeMethodNode.cs
@@ -11,7 +11,7 @@ public partial class TentativeMethodNode
{
protected override void EmitCode(NodeFactory factory, ref WasmEmitter encoder, bool relocsOnly)
{
- throw new NotImplementedException();
+ throw new PlatformNotSupportedException("NativeAOT WebAssembly tentative method stubs are not supported.");
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmUnboxingStubNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmUnboxingStubNode.cs
index 0ed23ac753b3c3..253053cd051ea0 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmUnboxingStubNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/Target_Wasm/WasmUnboxingStubNode.cs
@@ -11,7 +11,7 @@ public partial class UnboxingStubNode
{
protected override void EmitCode(NodeFactory factory, ref WasmEmitter encoder, bool relocsOnly)
{
- throw new NotImplementedException();
+ throw new PlatformNotSupportedException("NativeAOT WebAssembly unboxing stubs are not supported.");
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
index e26214457ac8c3..014d87408377bc 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
@@ -377,6 +377,14 @@
+
+
+
+
+
+
+
+
diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
index e96e97d7c8f03e..2fad7885d05cc8 100644
--- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
@@ -201,7 +201,6 @@
-
diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs
index f8418a8d67befc..1d5c713c5596d8 100644
--- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/DependencyAnalysis/MethodCodeNode.cs
@@ -50,7 +50,10 @@ public void SetCode(ObjectData data)
public override ObjectNodeSection GetSection(NodeFactory factory)
{
return factory.Target.IsWindows ?
- ObjectNodeSection.ManagedCodeWindowsContentSection : ObjectNodeSection.ManagedCodeUnixContentSection;
+ ObjectNodeSection.ManagedCodeWindowsContentSection :
+ factory.Target.IsWasm ?
+ ObjectNodeSection.WasmCodeSection :
+ ObjectNodeSection.ManagedCodeUnixContentSection;
}
public override bool StaticDependenciesAreComputed => _methodCode != null;
@@ -119,7 +122,7 @@ public ISymbolNode GetUnboxingThunkTarget(NodeFactory factory)
public MethodExceptionHandlingInfoNode EHInfo => _ehInfo;
// TODO-WASM: Appropriately extract funclet kinds from eh clause info
- public FuncletKind[] GetFuncletKinds() => throw new NotImplementedException();
+ public FuncletKind[] GetFuncletKinds() => [];
public ISymbolNode GetAssociatedDataNode(NodeFactory factory)
{
diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs
index 5c635bf69e3b4c..a1b006eba54336 100644
--- a/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs
+++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/Compiler/RyuJitCompilation.cs
@@ -110,8 +110,11 @@ protected override void CompileInternal(string outputFile, ObjectDumper dumper)
if ((_compilationOptions & RyuJitCompilationOptions.UseDwarf5) != 0)
options |= ObjectWritingOptions.UseDwarf5;
- if (_debugInformationProvider is not NullDebugInformationProvider)
+ if (_debugInformationProvider is not NullDebugInformationProvider &&
+ NodeFactory.Target.Architecture != TargetArchitecture.Wasm32)
+ {
options |= ObjectWritingOptions.GenerateDebugInfo;
+ }
if ((_compilationOptions & RyuJitCompilationOptions.ControlFlowGuardAnnotations) != 0)
options |= ObjectWritingOptions.ControlFlowGuard;
diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj
index 12164a8e937f9f..c80cac8a2dfc77 100644
--- a/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/ILCompiler.RyuJit.csproj
@@ -50,12 +50,6 @@
Compiler\JitHelper.cs
-
- ObjectWriter\WasmNative.cs
-
-
- ObjectWriter\WasmInstructions.cs
-
IL\HelperExtensions.cs
@@ -95,9 +89,7 @@
JitInterface\SwiftPhysicalLowering.cs
-
- JitInterface\WasmLowering.cs
-
+
Pgo\TypeSystemEntityOrUnknown.cs
diff --git a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs
index 14f0f9b6c1e33a..4a74c65d1c0073 100644
--- a/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs
+++ b/src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs
@@ -15,6 +15,7 @@
using ILCompiler;
using ILCompiler.DependencyAnalysis;
+using ILCompiler.DependencyAnalysis.Wasm;
using System.Runtime.CompilerServices;
#if SUPPORT_JIT
@@ -2518,7 +2519,10 @@ private void getThreadLocalStaticInfo_NativeAOT(CORINFO_THREAD_STATIC_INFO_NATIV
private CORINFO_WASM_TYPE_SYMBOL_STRUCT_* getWasmTypeSymbol(CorInfoWasmType* types, nuint typesSize)
{
- throw new NotImplementedException();
+ CorInfoWasmType[] typeArray = new ReadOnlySpan(types, (int)typesSize).ToArray();
+
+ WasmTypeNode typeNode = _compilation.NodeFactory.WasmTypeNode(typeArray);
+ return (CORINFO_WASM_TYPE_SYMBOL_STRUCT_*)ObjectToHandle(typeNode);
}
#pragma warning disable CA1822 // Mark members as static
diff --git a/src/coreclr/tools/aot/ILCompiler/ILCompiler.props b/src/coreclr/tools/aot/ILCompiler/ILCompiler.props
index e901d56df167fb..84855fc2f189ad 100644
--- a/src/coreclr/tools/aot/ILCompiler/ILCompiler.props
+++ b/src/coreclr/tools/aot/ILCompiler/ILCompiler.props
@@ -52,5 +52,6 @@
+
diff --git a/src/coreclr/tools/aot/ILCompiler/reproZero/Program.cs b/src/coreclr/tools/aot/ILCompiler/reproZero/Program.cs
new file mode 100644
index 00000000000000..a9f7df1193567b
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler/reproZero/Program.cs
@@ -0,0 +1,129 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime;
+using System.Runtime.InteropServices;
+
+#region A couple very basic things
+namespace System
+{
+ public class Object
+ {
+#pragma warning disable 169
+ // The layout of object is a contract with the compiler.
+ private IntPtr m_pMethodTable;
+#pragma warning restore 169
+ }
+ public struct Void { }
+
+ // The layout of primitive types is special cased because it would be recursive.
+ // These really don't need any fields to work.
+ public struct Boolean { }
+ public struct Char { }
+ public struct SByte { }
+ public struct Byte { }
+ public struct Int16 { }
+ public struct UInt16 { }
+ public struct Int32 { }
+ public struct UInt32 { }
+ public struct Int64 { }
+ public struct UInt64 { }
+ public struct IntPtr { }
+ public struct UIntPtr { }
+ public struct Single { }
+ public struct Double { }
+
+ public abstract class ValueType { }
+ public abstract class Enum : ValueType { }
+
+ public struct Nullable where T : struct { }
+
+ public sealed class String { public readonly int Length; }
+ public abstract class Array { }
+ public abstract class Delegate { }
+ public abstract class MulticastDelegate : Delegate { }
+
+ public struct RuntimeTypeHandle { }
+ public struct RuntimeMethodHandle { }
+ public struct RuntimeFieldHandle { }
+
+ public class Attribute { }
+
+ public enum AttributeTargets { }
+
+ public sealed class AttributeUsageAttribute : Attribute
+ {
+ public AttributeUsageAttribute(AttributeTargets validOn) { }
+ public bool AllowMultiple { get; set; }
+ public bool Inherited { get; set; }
+ }
+
+ public class AppContext
+ {
+ public static void SetData(string s, object o) { }
+ }
+
+ namespace Runtime.CompilerServices
+ {
+ public class RuntimeHelpers
+ {
+ public static unsafe int OffsetToStringData => sizeof(IntPtr) + sizeof(int);
+ }
+ }
+}
+namespace System.Runtime.InteropServices
+{
+ public sealed class DllImportAttribute : Attribute
+ {
+ public DllImportAttribute(string dllName) { }
+ }
+}
+#endregion
+
+#region Things needed by ILC
+namespace System
+{
+ namespace Runtime
+ {
+ internal sealed class RuntimeExportAttribute : Attribute
+ {
+ public RuntimeExportAttribute(string entry) { }
+ }
+ }
+
+ class Array : Array { }
+}
+
+namespace Internal.Runtime.CompilerHelpers
+{
+ // A class that the compiler looks for that has helpers to initialize the
+ // process. The compiler can gracefully handle the helpers not being present,
+ // but the class itself being absent is unhandled. Let's add an empty class.
+ class StartupCodeHelpers
+ {
+ // A couple symbols the generated code will need we park them in this class
+ // for no particular reason. These aid in transitioning to/from managed code.
+ // Since we don't have a GC, the transition is a no-op.
+ [RuntimeExport("RhpReversePInvoke")]
+ static void RhpReversePInvoke(IntPtr frame) { }
+ [RuntimeExport("RhpReversePInvokeReturn")]
+ static void RhpReversePInvokeReturn(IntPtr frame) { }
+ [RuntimeExport("RhpPInvoke")]
+ static void RhpPInvoke(IntPtr frame) { }
+ [RuntimeExport("RhpPInvokeReturn")]
+ static void RhpPInvokeReturn(IntPtr frame) { }
+
+ [RuntimeExport("RhpFallbackFailFast")]
+ static void RhpFallbackFailFast() { while (true) ; }
+ }
+}
+#endregion
+
+unsafe class Program
+{
+ static int Main()
+ {
+ return 42;
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler/reproZero/reproZero.csproj b/src/coreclr/tools/aot/ILCompiler/reproZero/reproZero.csproj
new file mode 100644
index 00000000000000..9a501e553cfbcd
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler/reproZero/reproZero.csproj
@@ -0,0 +1,64 @@
+
+
+
+ $(NetCoreAppToolCurrent)
+ Exe
+ x64;x86;wasm
+ AnyCPU
+ false
+ false
+ Debug;Release;Checked
+ true
+ false
+ true
+ true
+ false
+ false
+ false
+ v4.0.30319
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+