Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions eng/Subsets.props
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,10 @@
<ClrRuntimeBuildSubsets>$(ClrRuntimeBuildSubsets);ClrWasmJitSubset=true</ClrRuntimeBuildSubsets>
</PropertyGroup>

<PropertyGroup Condition="$(_subset.Contains('+clr.toolstests+')) and '$(DotNetBuildSourceOnly)' != 'true' and ('$(BuildArchitecture)' == 'x64' or '$(BuildArchitecture)' == 'arm64') and ('$(TargetArchitecture)' == 'x64' or '$(TargetArchitecture)' == 'arm64')">
<ClrRuntimeBuildSubsets>$(ClrRuntimeBuildSubsets);ClrWasmJitSubset=true</ClrRuntimeBuildSubsets>
</PropertyGroup>

<PropertyGroup Condition="$(_subset.Contains('+clr.paltests+'))">
<ClrRuntimeBuildSubsets>$(ClrRuntimeBuildSubsets);ClrPalTestsSubset=true</ClrRuntimeBuildSubsets>
</PropertyGroup>
Expand Down Expand Up @@ -394,6 +398,7 @@
<ItemGroup>
<!-- crossgen2/ILC have dependencies on the JITs, so build them if the cross component includes crossgen2/ILC. -->
<_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" />
<!-- When targeting WebAssembly, only build the wasm JIT for the cross component. -->
<_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" />
Expand Down
13 changes: 12 additions & 1 deletion src/coreclr/jit/codegenlinear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -922,7 +928,6 @@ void CodeGen::genEmitEndBlock(BasicBlock* block)
genEmitFunctionEnd();
}
#endif // defined(TARGET_WASM)

break;

case BBJ_COND:
Expand All @@ -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:
Expand Down
45 changes: 26 additions & 19 deletions src/coreclr/jit/codegenwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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;
Expand All @@ -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
{
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

PEP-specific logic should be gated by JitFlags::JIT_FLAG_PORTABLE_ENTRY_POINTS check

{
// 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)
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/emitwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 4 additions & 1 deletion src/coreclr/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 13 additions & 3 deletions src/coreclr/jit/lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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;
Expand Down
34 changes: 23 additions & 11 deletions src/coreclr/jit/lowerwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The method is called LowerPEPCall. It is odd to overload it to lower non-PEP calls.

{
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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,9 @@ public static void EmitObject(string objectFilePath, IReadOnlyCollection<Depende
ObjectWriter objectWriter =
factory.Target.IsApplePlatform ? new MachObjectWriter(factory, options) :
factory.Target.OperatingSystem == TargetOS.Windows ? new CoffObjectWriter(factory, options) :
#if !READYTORUN
factory.Target.Architecture == TargetArchitecture.Wasm32 ? new WasmRelocatableObjectWriter(factory, options) :
#endif
new ElfObjectWriter(factory, options);

using Stream outputFileStream = new FileStream(objectFilePath, FileMode.Create);
Expand Down
Loading
Loading