Skip to content
Merged
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
81 changes: 81 additions & 0 deletions src/coreclr/jit/codegenwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@
static const instruction INS_I_const = INS_i64_const;
static const instruction INS_I_add = INS_i64_add;
static const instruction INS_I_sub = INS_i64_sub;
static const instruction INS_I_le_u = INS_i64_le_u;
static const instruction INS_I_gt_u = INS_i64_gt_u;
#else // !TARGET_64BIT
static const instruction INS_I_const = INS_i32_const;
static const instruction INS_I_add = INS_i32_add;
static const instruction INS_I_sub = INS_i32_sub;
static const instruction INS_I_le_u = INS_i32_le_u;
static const instruction INS_I_gt_u = INS_i32_gt_u;
#endif // !TARGET_64BIT

void CodeGen::genMarkLabelsForCodegen()
Expand Down Expand Up @@ -495,6 +499,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
genCodeForNegNot(treeNode->AsOp());
break;

case GT_NULLCHECK:
genCodeForNullCheck(treeNode->AsIndir());
break;

case GT_IND:
genCodeForIndir(treeNode->AsIndir());
break;
Expand Down Expand Up @@ -922,6 +930,37 @@ void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
{
genConsumeOperands(treeNode);

// wasm stack is
// divisor (top)
// dividend (next)
// ...
// TODO-WASM: To check for exception, we will have to spill these to
// internal registers along the way, like so:
//
// ... push dividend
// tee.local $temp1
// ... push divisor
// tee.local $temp2
// ... exception checks (using $temp1 and $temp2; will introduce flow)
// div/mod op

if (!varTypeIsFloating(treeNode->TypeGet()))
{
ExceptionSetFlags exSetFlags = treeNode->OperExceptions(compiler);

// TODO-WASM:(AnyVal / 0) => DivideByZeroException
//
if ((exSetFlags & ExceptionSetFlags::DivideByZeroException) != ExceptionSetFlags::None)
{
}

// TODO-WASM: (MinInt / -1) => ArithmeticException
//
if ((exSetFlags & ExceptionSetFlags::ArithmeticException) != ExceptionSetFlags::None)
{
}
}

instruction ins;
switch (PackOperAndType(treeNode->OperGet(), treeNode->TypeGet()))
{
Expand Down Expand Up @@ -1136,6 +1175,48 @@ void CodeGen::genCodeForNegNot(GenTreeOp* tree)
genProduceReg(tree);
}

//---------------------------------------------------------------------
// genCodeForNullCheck - generate code for a GT_NULLCHECK node
//
// Arguments:
// tree - the GT_NULLCHECK node
//
// Notes:
// If throw helper calls are being emitted inline, we need
// to wrap the resulting codegen in a block/end pair.
//
void CodeGen::genCodeForNullCheck(GenTreeIndir* tree)
{
genConsumeAddress(tree->Addr());

// TODO-WASM: refactor once we have implemented other cases invoking throw helpers
if (compiler->fgUseThrowHelperBlocks())
{
Compiler::AddCodeDsc* const add = compiler->fgFindExcptnTarget(SCK_NULL_CHECK, compiler->compCurBB);

if (add == nullptr)
{
NYI_WASM("Missing null check demand");
}

assert(add != nullptr);
assert(add->acdUsed);
GetEmitter()->emitIns_I(INS_I_const, EA_PTRSIZE, compiler->compMaxUncheckedOffsetForNullObject);
GetEmitter()->emitIns(INS_I_le_u);
inst_JMP(EJ_jmpif, add->acdDstBlk);
}
else
{
GetEmitter()->emitIns_I(INS_I_const, EA_PTRSIZE, compiler->compMaxUncheckedOffsetForNullObject);
GetEmitter()->emitIns(INS_I_le_u);
GetEmitter()->emitIns(INS_if);
// TODO-WASM: codegen for the call instead of unreachable
// genEmitHelperCall(compiler->acdHelper(SCK_NULL_CHECK), 0, EA_UNKNOWN);
GetEmitter()->emitIns(INS_unreachable);
GetEmitter()->emitIns(INS_end);
}
}

//------------------------------------------------------------------------
// genCodeForLclAddr: Generates the code for GT_LCL_ADDR.
//
Expand Down
9 changes: 9 additions & 0 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,15 @@ Compiler::Compiler(ArenaAllocator* arena,

compMaxUncheckedOffsetForNullObject = eeInfo->maxUncheckedOffsetForNullObject;

#if defined(DEBUG) && defined(TARGET_WASM)
// TODO-WASM: remove once we no longer need to use x86/arm collections for wasm replay
// if we are cross-replaying wasm, override compMaxUncheckedOffsetForNullObject
if (!info.compMatchedVM)
{
compMaxUncheckedOffsetForNullObject = 1024 - 1;
}
#endif

info.compProfilerCallback = false; // Assume false until we are told to hook this method.

info.compCode = methodInfo->ILCode;
Expand Down
10 changes: 9 additions & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2841,7 +2841,15 @@ class Compiler
#endif // DEBUG

bool ehAnyFunclets(); // Are there any funclets in this function?
unsigned ehFuncletCount(); // Return the count of funclets in the function
unsigned ehFuncletCount(); // Return the count of funclets in the function.

#ifdef TARGET_WASM
// Once we have run wasm layout, try regions may no longer be contiguous.
//
bool fgTrysNotContiguous() { return fgIndexToBlockMap != nullptr; }
#else
bool fgTrysNotContiguous() { return false; }
#endif

FlowEdge* BlockPredsWithEH(BasicBlock* blk);
FlowEdge* BlockDominancePreds(BasicBlock* blk);
Expand Down
22 changes: 21 additions & 1 deletion src/coreclr/jit/fgwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,13 @@ FlowGraphDfsTree* FgWasm::WasmDfs(bool& hasBlocksOnlyReachableViaEH)
JITDUMP("Determining Wasm DFS entry points\n");

// All funclets are entries. For now we assume finallys are funclets.
// We walk from outer->inner order, so that for mutual protect trys
// the "first" handler is visited last and ends up earlier in RPO.
//
for (EHblkDsc* const ehDsc : EHClauses(comp))
for (int XTnum = comp->compHndBBtabCount - 1; XTnum >= 0; XTnum--)
{
EHblkDsc* const ehDsc = &comp->compHndBBtab[XTnum];

JITDUMP(FMT_BB " is handler entry\n", ehDsc->ebdHndBeg->bbNum);
entryBlocks.push_back(ehDsc->ebdHndBeg);
if (ehDsc->HasFilter())
Expand Down Expand Up @@ -1390,6 +1394,17 @@ PhaseStatus Compiler::fgWasmControlFlow()
{
fgUnlinkBlock(block);
fgInsertBBafter(lastBlock, block);

// If the last block was the end of a handler, we may need
// to update the enclosing region endpoint.
//
// Because we are not keeping try regions contiguous,
// we can't and don't need to do the same for a try.
//
if (ehIsBlockHndLast(lastBlock) && block->hasHndIndex() && BasicBlock::sameHndRegion(lastBlock, block))
{
fgSetHndEnd(ehGetBlockHndDsc(block), block);
}
lastBlock = block;
}

Expand Down Expand Up @@ -1441,6 +1456,11 @@ PhaseStatus Compiler::fgWasmControlFlow()
JITDUMPEXEC(fgDumpWasmControlFlow());
JITDUMPEXEC(fgDumpWasmControlFlowDot());

// By publishing the index to block map, we are also indicating
// that try regions may no longer be contiguous.
//
assert(fgTrysNotContiguous());

return PhaseStatus::MODIFIED_EVERYTHING;
}

Expand Down
42 changes: 42 additions & 0 deletions src/coreclr/jit/fgwasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,51 @@ class FgWasm
// consider exceptional successors or successors that require runtime intervention
// (eg funclet returns).
//
// For method and funclet entries we add any "ACD" blocks as successors before the
// true successors. This ensures the ACD blocks end up at the end of the funclet
// region, and that we create proper Wasm blocks so we can branch to them from
// anywhere within the method region or funclet region.
//
template <typename TFunc>
BasicBlockVisit FgWasm::VisitWasmSuccs(Compiler* comp, BasicBlock* block, TFunc func, bool useProfile)
{
// Special case throw helper blocks that are not yet connected in the flow graph.
//
Compiler::AddCodeDscMap* const acdMap = comp->fgGetAddCodeDscMap();
if (acdMap != nullptr)
{
// Behave as if these blocks have edges from their respective region entry blocks.
//
if ((block == comp->fgFirstBB) || comp->bbIsFuncletBeg(block))
{
Compiler::AcdKeyDesignator dsg;
const unsigned blockData = comp->bbThrowIndex(block, &dsg);

// We do not expect any ACDs to be mapped to try regions (only method/handler/filter)
//
assert(dsg != Compiler::AcdKeyDesignator::KD_TRY);

for (const Compiler::AddCodeDscKey& key : Compiler::AddCodeDscMap::KeyIteration(acdMap))
{
if (key.Data() == blockData)
{
// This ACD refers to a throw helper block in the right region.
// Make the block a successor.
//
Compiler::AddCodeDsc* acd = nullptr;
acdMap->Lookup(key, &acd);

// We only need to consider used ACDs... we may have demanded throw helpers that are not needed.
//
if (acd->acdUsed)
{
RETURN_ON_ABORT(func(acd->acdDstBlk));
}
}
}
}
}

switch (block->GetKind())
{
// Funclet returns have no successors
Expand Down
33 changes: 31 additions & 2 deletions src/coreclr/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3326,6 +3326,8 @@ unsigned Compiler::acdHelper(SpecialCodeKind codeKind)
return CORINFO_HELP_OVERFLOW;
case SCK_FAIL_FAST:
return CORINFO_HELP_FAIL_FAST;
case SCK_NULL_CHECK:
return CORINFO_HELP_THROWNULLREF;
default:
assert(!"Bad codeKind");
return 0;
Expand Down Expand Up @@ -3358,6 +3360,8 @@ const char* sckName(SpecialCodeKind codeKind)
return "SCK_ARITH_EXCPN";
case SCK_FAIL_FAST:
return "SCK_FAIL_FAST";
case SCK_NULL_CHECK:
return "SCK_NULL_CHECK";
default:
return "SCK_UNKNOWN";
}
Expand Down Expand Up @@ -3490,6 +3494,7 @@ PhaseStatus Compiler::fgCreateThrowHelperBlocks()
BBJ_THROW, // SCK_ARG_EXCPN
BBJ_THROW, // SCK_ARG_RNG_EXCPN
BBJ_THROW, // SCK_FAIL_FAST
BBJ_THROW, // SCK_NULL_CHECK
};

noway_assert(sizeof(jumpKinds) == SCK_COUNT); // sanity check
Expand Down Expand Up @@ -3555,6 +3560,9 @@ PhaseStatus Compiler::fgCreateThrowHelperBlocks()
case SCK_FAIL_FAST:
msg = " for FAIL_FAST";
break;
case SCK_NULL_CHECK:
msg = " for NULL_CHECK";
break;
default:
msg = " for ??";
break;
Expand Down Expand Up @@ -3676,7 +3684,13 @@ Compiler::AddCodeDsc* Compiler::fgFindExcptnTarget(SpecialCodeKind kind, BasicBl
{
JITDUMP(FMT_BB ": unexpected request for new throw helper: kind %d (%s), data 0x%08x\n", fromBlock->bbNum,
kind, sckName(kind), key.Data());

if (kind == SCK_NULL_CHECK)
{
NYI_WASM("Missing null check demand");
}
}

assert(!fgRngChkThrowAdded);
}

Expand Down Expand Up @@ -3708,13 +3722,28 @@ unsigned Compiler::bbThrowIndex(BasicBlock* blk, AcdKeyDesignator* dsg)

assert(inTry || inHnd);

#if defined(TARGET_WASM)
// The current plan for Wasm: method regions or funclets with
// trys will have a single Wasm try handle all
// resumption from catches via virtual IPs.
//
// So we do not need to consider the nesting of the throw
// in try regions, just in handlers.
//
if (!inHnd)
{
*dsg = AcdKeyDesignator::KD_NONE;
return 0;
}
#else
if (inTry && (!inHnd || (tryIndex < hndIndex)))
{
// The most enclosing region is a try body, use it
assert(tryIndex <= 0x3FFFFFFF);
*dsg = AcdKeyDesignator::KD_TRY;
return tryIndex;
}
#endif // !defined(TARGET_WASM)

// The most enclosing region is a handler which will be a funclet
// Now we have to figure out if blk is in the filter or handler
Expand All @@ -3731,7 +3760,7 @@ unsigned Compiler::bbThrowIndex(BasicBlock* blk, AcdKeyDesignator* dsg)
}

//------------------------------------------------------------------------
// AddCodedDscKey: construct from kind and block
// AddCodeDscKey: construct from kind and block
//
// Arguments:
// kind - exception kind
Expand All @@ -3755,7 +3784,7 @@ Compiler::AddCodeDscKey::AddCodeDscKey(SpecialCodeKind kind, BasicBlock* block,
}

//------------------------------------------------------------------------
// AddCodedDscKey: construct from AddCodeDsc
// AddCodeDscKey: construct from AddCodeDsc
//
// Arguments:
// add - add code dsc in querstion
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/gentree.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ enum SpecialCodeKind
SCK_ARG_EXCPN, // target on ArgumentException (currently used only for SIMD intrinsics)
SCK_ARG_RNG_EXCPN, // target on ArgumentOutOfRangeException (currently used only for SIMD intrinsics)
SCK_FAIL_FAST, // target for fail fast exception
SCK_NULL_CHECK, // target for NullReferenceException (Wasm)
SCK_COUNT
};

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/jiteh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3695,7 +3695,7 @@ void Compiler::fgVerifyHandlerTab()
// Make sure that all blocks have the right index, including those blocks that should have zero (no EH region).
for (BasicBlock* const block : Blocks())
{
assert(block->bbTryIndex == blockTryIndex[block->bbNum]);
assert(fgTrysNotContiguous() || block->bbTryIndex == blockTryIndex[block->bbNum]);
assert(block->bbHndIndex == blockHndIndex[block->bbNum]);

// Also, since we're walking the blocks, check that all blocks we didn't mark as EH handler 'begin' blocks
Expand Down
7 changes: 7 additions & 0 deletions src/coreclr/jit/stacklevelsetter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,13 @@ void StackLevelSetter::SetThrowHelperBlocks(GenTree* node, BasicBlock* block)
break;
#endif

#if defined(TARGET_WASM)
// TODO-WASM: add other opers that imply null checks
case GT_NULLCHECK:
SetThrowHelperBlock(SCK_NULL_CHECK, block);
break;
#endif // defined(TARGET_WASM)

default: // Other opers can target throw only due to overflow.
break;
}
Expand Down
15 changes: 13 additions & 2 deletions src/coreclr/tools/Common/JitInterface/CorInfoImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3394,8 +3394,19 @@ private void getEEInfo(ref CORINFO_EE_INFO pEEInfoOut)

pEEInfoOut.osPageSize = 0x1000;

pEEInfoOut.maxUncheckedOffsetForNullObject = (_compilation.NodeFactory.Target.IsWindows) ?
(32 * 1024 - 1) : (pEEInfoOut.osPageSize / 2 - 1);
if (_compilation.NodeFactory.Target.IsWasm)
{
// TODO: Set this value to 0 for Wasm
pEEInfoOut.maxUncheckedOffsetForNullObject = 1024 - 1;
}
else if (_compilation.NodeFactory.Target.IsWindows)
{
pEEInfoOut.maxUncheckedOffsetForNullObject = 32 * 1024 - 1;
}
else
{
pEEInfoOut.maxUncheckedOffsetForNullObject = pEEInfoOut.osPageSize / 2 - 1;
}

pEEInfoOut.targetAbi = TargetABI;
pEEInfoOut.osType = TargetToOs(_compilation.NodeFactory.Target);
Expand Down
Loading
Loading