Skip to content

Commit 50a1b96

Browse files
authored
Merge branch 'main' into copilot/add-idnmapping-span-apis-again
2 parents ab4609c + 5c23265 commit 50a1b96

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+2163
-369
lines changed

src/coreclr/jit/codegenwasm.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@
1313
static const instruction INS_I_const = INS_i64_const;
1414
static const instruction INS_I_add = INS_i64_add;
1515
static const instruction INS_I_sub = INS_i64_sub;
16+
static const instruction INS_I_le_u = INS_i64_le_u;
17+
static const instruction INS_I_gt_u = INS_i64_gt_u;
1618
#else // !TARGET_64BIT
1719
static const instruction INS_I_const = INS_i32_const;
1820
static const instruction INS_I_add = INS_i32_add;
1921
static const instruction INS_I_sub = INS_i32_sub;
22+
static const instruction INS_I_le_u = INS_i32_le_u;
23+
static const instruction INS_I_gt_u = INS_i32_gt_u;
2024
#endif // !TARGET_64BIT
2125

2226
void CodeGen::genMarkLabelsForCodegen()
@@ -495,6 +499,10 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
495499
genCodeForNegNot(treeNode->AsOp());
496500
break;
497501

502+
case GT_NULLCHECK:
503+
genCodeForNullCheck(treeNode->AsIndir());
504+
break;
505+
498506
case GT_IND:
499507
genCodeForIndir(treeNode->AsIndir());
500508
break;
@@ -922,6 +930,37 @@ void CodeGen::genCodeForDivMod(GenTreeOp* treeNode)
922930
{
923931
genConsumeOperands(treeNode);
924932

933+
// wasm stack is
934+
// divisor (top)
935+
// dividend (next)
936+
// ...
937+
// TODO-WASM: To check for exception, we will have to spill these to
938+
// internal registers along the way, like so:
939+
//
940+
// ... push dividend
941+
// tee.local $temp1
942+
// ... push divisor
943+
// tee.local $temp2
944+
// ... exception checks (using $temp1 and $temp2; will introduce flow)
945+
// div/mod op
946+
947+
if (!varTypeIsFloating(treeNode->TypeGet()))
948+
{
949+
ExceptionSetFlags exSetFlags = treeNode->OperExceptions(compiler);
950+
951+
// TODO-WASM:(AnyVal / 0) => DivideByZeroException
952+
//
953+
if ((exSetFlags & ExceptionSetFlags::DivideByZeroException) != ExceptionSetFlags::None)
954+
{
955+
}
956+
957+
// TODO-WASM: (MinInt / -1) => ArithmeticException
958+
//
959+
if ((exSetFlags & ExceptionSetFlags::ArithmeticException) != ExceptionSetFlags::None)
960+
{
961+
}
962+
}
963+
925964
instruction ins;
926965
switch (PackOperAndType(treeNode->OperGet(), treeNode->TypeGet()))
927966
{
@@ -1136,6 +1175,48 @@ void CodeGen::genCodeForNegNot(GenTreeOp* tree)
11361175
genProduceReg(tree);
11371176
}
11381177

1178+
//---------------------------------------------------------------------
1179+
// genCodeForNullCheck - generate code for a GT_NULLCHECK node
1180+
//
1181+
// Arguments:
1182+
// tree - the GT_NULLCHECK node
1183+
//
1184+
// Notes:
1185+
// If throw helper calls are being emitted inline, we need
1186+
// to wrap the resulting codegen in a block/end pair.
1187+
//
1188+
void CodeGen::genCodeForNullCheck(GenTreeIndir* tree)
1189+
{
1190+
genConsumeAddress(tree->Addr());
1191+
1192+
// TODO-WASM: refactor once we have implemented other cases invoking throw helpers
1193+
if (compiler->fgUseThrowHelperBlocks())
1194+
{
1195+
Compiler::AddCodeDsc* const add = compiler->fgFindExcptnTarget(SCK_NULL_CHECK, compiler->compCurBB);
1196+
1197+
if (add == nullptr)
1198+
{
1199+
NYI_WASM("Missing null check demand");
1200+
}
1201+
1202+
assert(add != nullptr);
1203+
assert(add->acdUsed);
1204+
GetEmitter()->emitIns_I(INS_I_const, EA_PTRSIZE, compiler->compMaxUncheckedOffsetForNullObject);
1205+
GetEmitter()->emitIns(INS_I_le_u);
1206+
inst_JMP(EJ_jmpif, add->acdDstBlk);
1207+
}
1208+
else
1209+
{
1210+
GetEmitter()->emitIns_I(INS_I_const, EA_PTRSIZE, compiler->compMaxUncheckedOffsetForNullObject);
1211+
GetEmitter()->emitIns(INS_I_le_u);
1212+
GetEmitter()->emitIns(INS_if);
1213+
// TODO-WASM: codegen for the call instead of unreachable
1214+
// genEmitHelperCall(compiler->acdHelper(SCK_NULL_CHECK), 0, EA_UNKNOWN);
1215+
GetEmitter()->emitIns(INS_unreachable);
1216+
GetEmitter()->emitIns(INS_end);
1217+
}
1218+
}
1219+
11391220
//------------------------------------------------------------------------
11401221
// genCodeForLclAddr: Generates the code for GT_LCL_ADDR.
11411222
//

src/coreclr/jit/compiler.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,15 @@ Compiler::Compiler(ArenaAllocator* arena,
438438

439439
compMaxUncheckedOffsetForNullObject = eeInfo->maxUncheckedOffsetForNullObject;
440440

441+
#if defined(DEBUG) && defined(TARGET_WASM)
442+
// TODO-WASM: remove once we no longer need to use x86/arm collections for wasm replay
443+
// if we are cross-replaying wasm, override compMaxUncheckedOffsetForNullObject
444+
if (!info.compMatchedVM)
445+
{
446+
compMaxUncheckedOffsetForNullObject = 1024 - 1;
447+
}
448+
#endif
449+
441450
info.compProfilerCallback = false; // Assume false until we are told to hook this method.
442451

443452
info.compCode = methodInfo->ILCode;

src/coreclr/jit/compiler.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2841,7 +2841,15 @@ class Compiler
28412841
#endif // DEBUG
28422842

28432843
bool ehAnyFunclets(); // Are there any funclets in this function?
2844-
unsigned ehFuncletCount(); // Return the count of funclets in the function
2844+
unsigned ehFuncletCount(); // Return the count of funclets in the function.
2845+
2846+
#ifdef TARGET_WASM
2847+
// Once we have run wasm layout, try regions may no longer be contiguous.
2848+
//
2849+
bool fgTrysNotContiguous() { return fgIndexToBlockMap != nullptr; }
2850+
#else
2851+
bool fgTrysNotContiguous() { return false; }
2852+
#endif
28452853

28462854
FlowEdge* BlockPredsWithEH(BasicBlock* blk);
28472855
FlowEdge* BlockDominancePreds(BasicBlock* blk);

src/coreclr/jit/fgwasm.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,13 @@ FlowGraphDfsTree* FgWasm::WasmDfs(bool& hasBlocksOnlyReachableViaEH)
107107
JITDUMP("Determining Wasm DFS entry points\n");
108108

109109
// All funclets are entries. For now we assume finallys are funclets.
110+
// We walk from outer->inner order, so that for mutual protect trys
111+
// the "first" handler is visited last and ends up earlier in RPO.
110112
//
111-
for (EHblkDsc* const ehDsc : EHClauses(comp))
113+
for (int XTnum = comp->compHndBBtabCount - 1; XTnum >= 0; XTnum--)
112114
{
115+
EHblkDsc* const ehDsc = &comp->compHndBBtab[XTnum];
116+
113117
JITDUMP(FMT_BB " is handler entry\n", ehDsc->ebdHndBeg->bbNum);
114118
entryBlocks.push_back(ehDsc->ebdHndBeg);
115119
if (ehDsc->HasFilter())
@@ -1390,6 +1394,17 @@ PhaseStatus Compiler::fgWasmControlFlow()
13901394
{
13911395
fgUnlinkBlock(block);
13921396
fgInsertBBafter(lastBlock, block);
1397+
1398+
// If the last block was the end of a handler, we may need
1399+
// to update the enclosing region endpoint.
1400+
//
1401+
// Because we are not keeping try regions contiguous,
1402+
// we can't and don't need to do the same for a try.
1403+
//
1404+
if (ehIsBlockHndLast(lastBlock) && block->hasHndIndex() && BasicBlock::sameHndRegion(lastBlock, block))
1405+
{
1406+
fgSetHndEnd(ehGetBlockHndDsc(block), block);
1407+
}
13931408
lastBlock = block;
13941409
}
13951410

@@ -1441,6 +1456,11 @@ PhaseStatus Compiler::fgWasmControlFlow()
14411456
JITDUMPEXEC(fgDumpWasmControlFlow());
14421457
JITDUMPEXEC(fgDumpWasmControlFlowDot());
14431458

1459+
// By publishing the index to block map, we are also indicating
1460+
// that try regions may no longer be contiguous.
1461+
//
1462+
assert(fgTrysNotContiguous());
1463+
14441464
return PhaseStatus::MODIFIED_EVERYTHING;
14451465
}
14461466

src/coreclr/jit/fgwasm.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,9 +318,51 @@ class FgWasm
318318
// consider exceptional successors or successors that require runtime intervention
319319
// (eg funclet returns).
320320
//
321+
// For method and funclet entries we add any "ACD" blocks as successors before the
322+
// true successors. This ensures the ACD blocks end up at the end of the funclet
323+
// region, and that we create proper Wasm blocks so we can branch to them from
324+
// anywhere within the method region or funclet region.
325+
//
321326
template <typename TFunc>
322327
BasicBlockVisit FgWasm::VisitWasmSuccs(Compiler* comp, BasicBlock* block, TFunc func, bool useProfile)
323328
{
329+
// Special case throw helper blocks that are not yet connected in the flow graph.
330+
//
331+
Compiler::AddCodeDscMap* const acdMap = comp->fgGetAddCodeDscMap();
332+
if (acdMap != nullptr)
333+
{
334+
// Behave as if these blocks have edges from their respective region entry blocks.
335+
//
336+
if ((block == comp->fgFirstBB) || comp->bbIsFuncletBeg(block))
337+
{
338+
Compiler::AcdKeyDesignator dsg;
339+
const unsigned blockData = comp->bbThrowIndex(block, &dsg);
340+
341+
// We do not expect any ACDs to be mapped to try regions (only method/handler/filter)
342+
//
343+
assert(dsg != Compiler::AcdKeyDesignator::KD_TRY);
344+
345+
for (const Compiler::AddCodeDscKey& key : Compiler::AddCodeDscMap::KeyIteration(acdMap))
346+
{
347+
if (key.Data() == blockData)
348+
{
349+
// This ACD refers to a throw helper block in the right region.
350+
// Make the block a successor.
351+
//
352+
Compiler::AddCodeDsc* acd = nullptr;
353+
acdMap->Lookup(key, &acd);
354+
355+
// We only need to consider used ACDs... we may have demanded throw helpers that are not needed.
356+
//
357+
if (acd->acdUsed)
358+
{
359+
RETURN_ON_ABORT(func(acd->acdDstBlk));
360+
}
361+
}
362+
}
363+
}
364+
}
365+
324366
switch (block->GetKind())
325367
{
326368
// Funclet returns have no successors

src/coreclr/jit/flowgraph.cpp

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3326,6 +3326,8 @@ unsigned Compiler::acdHelper(SpecialCodeKind codeKind)
33263326
return CORINFO_HELP_OVERFLOW;
33273327
case SCK_FAIL_FAST:
33283328
return CORINFO_HELP_FAIL_FAST;
3329+
case SCK_NULL_CHECK:
3330+
return CORINFO_HELP_THROWNULLREF;
33293331
default:
33303332
assert(!"Bad codeKind");
33313333
return 0;
@@ -3358,6 +3360,8 @@ const char* sckName(SpecialCodeKind codeKind)
33583360
return "SCK_ARITH_EXCPN";
33593361
case SCK_FAIL_FAST:
33603362
return "SCK_FAIL_FAST";
3363+
case SCK_NULL_CHECK:
3364+
return "SCK_NULL_CHECK";
33613365
default:
33623366
return "SCK_UNKNOWN";
33633367
}
@@ -3490,6 +3494,7 @@ PhaseStatus Compiler::fgCreateThrowHelperBlocks()
34903494
BBJ_THROW, // SCK_ARG_EXCPN
34913495
BBJ_THROW, // SCK_ARG_RNG_EXCPN
34923496
BBJ_THROW, // SCK_FAIL_FAST
3497+
BBJ_THROW, // SCK_NULL_CHECK
34933498
};
34943499

34953500
noway_assert(sizeof(jumpKinds) == SCK_COUNT); // sanity check
@@ -3555,6 +3560,9 @@ PhaseStatus Compiler::fgCreateThrowHelperBlocks()
35553560
case SCK_FAIL_FAST:
35563561
msg = " for FAIL_FAST";
35573562
break;
3563+
case SCK_NULL_CHECK:
3564+
msg = " for NULL_CHECK";
3565+
break;
35583566
default:
35593567
msg = " for ??";
35603568
break;
@@ -3676,7 +3684,13 @@ Compiler::AddCodeDsc* Compiler::fgFindExcptnTarget(SpecialCodeKind kind, BasicBl
36763684
{
36773685
JITDUMP(FMT_BB ": unexpected request for new throw helper: kind %d (%s), data 0x%08x\n", fromBlock->bbNum,
36783686
kind, sckName(kind), key.Data());
3687+
3688+
if (kind == SCK_NULL_CHECK)
3689+
{
3690+
NYI_WASM("Missing null check demand");
3691+
}
36793692
}
3693+
36803694
assert(!fgRngChkThrowAdded);
36813695
}
36823696

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

37093723
assert(inTry || inHnd);
37103724

3725+
#if defined(TARGET_WASM)
3726+
// The current plan for Wasm: method regions or funclets with
3727+
// trys will have a single Wasm try handle all
3728+
// resumption from catches via virtual IPs.
3729+
//
3730+
// So we do not need to consider the nesting of the throw
3731+
// in try regions, just in handlers.
3732+
//
3733+
if (!inHnd)
3734+
{
3735+
*dsg = AcdKeyDesignator::KD_NONE;
3736+
return 0;
3737+
}
3738+
#else
37113739
if (inTry && (!inHnd || (tryIndex < hndIndex)))
37123740
{
37133741
// The most enclosing region is a try body, use it
37143742
assert(tryIndex <= 0x3FFFFFFF);
37153743
*dsg = AcdKeyDesignator::KD_TRY;
37163744
return tryIndex;
37173745
}
3746+
#endif // !defined(TARGET_WASM)
37183747

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

37333762
//------------------------------------------------------------------------
3734-
// AddCodedDscKey: construct from kind and block
3763+
// AddCodeDscKey: construct from kind and block
37353764
//
37363765
// Arguments:
37373766
// kind - exception kind
@@ -3755,7 +3784,7 @@ Compiler::AddCodeDscKey::AddCodeDscKey(SpecialCodeKind kind, BasicBlock* block,
37553784
}
37563785

37573786
//------------------------------------------------------------------------
3758-
// AddCodedDscKey: construct from AddCodeDsc
3787+
// AddCodeDscKey: construct from AddCodeDsc
37593788
//
37603789
// Arguments:
37613790
// add - add code dsc in querstion

src/coreclr/jit/gentree.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ enum SpecialCodeKind
6262
SCK_ARG_EXCPN, // target on ArgumentException (currently used only for SIMD intrinsics)
6363
SCK_ARG_RNG_EXCPN, // target on ArgumentOutOfRangeException (currently used only for SIMD intrinsics)
6464
SCK_FAIL_FAST, // target for fail fast exception
65+
SCK_NULL_CHECK, // target for NullReferenceException (Wasm)
6566
SCK_COUNT
6667
};
6768

src/coreclr/jit/importercalls.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7438,6 +7438,7 @@ bool Compiler::isCompatibleMethodGDV(GenTreeCall* call, CORINFO_METHOD_HANDLE gd
74387438
{
74397439
case WellKnownArg::RetBuffer:
74407440
case WellKnownArg::ThisPointer:
7441+
case WellKnownArg::AsyncContinuation:
74417442
// Not part of signature but we still expect to see it here
74427443
continue;
74437444
case WellKnownArg::None:

src/coreclr/jit/jiteh.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3695,7 +3695,7 @@ void Compiler::fgVerifyHandlerTab()
36953695
// Make sure that all blocks have the right index, including those blocks that should have zero (no EH region).
36963696
for (BasicBlock* const block : Blocks())
36973697
{
3698-
assert(block->bbTryIndex == blockTryIndex[block->bbNum]);
3698+
assert(fgTrysNotContiguous() || block->bbTryIndex == blockTryIndex[block->bbNum]);
36993699
assert(block->bbHndIndex == blockHndIndex[block->bbNum]);
37003700

37013701
// Also, since we're walking the blocks, check that all blocks we didn't mark as EH handler 'begin' blocks

src/coreclr/jit/lower.cpp

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3209,20 +3209,6 @@ void Lowering::LowerFastTailCall(GenTreeCall* call)
32093209
// We expect to see a call that meets the following conditions
32103210
assert(call->IsFastTailCall());
32113211

3212-
// VM cannot use return address hijacking when A() and B() tail call each
3213-
// other in mutual recursion. Therefore, this block is reachable through
3214-
// a GC-safe point or the whole method is marked as fully interruptible.
3215-
//
3216-
// TODO-Cleanup:
3217-
// optReachWithoutCall() depends on the fact that loop headers blocks
3218-
// will have a block number > fgLastBB. These loop headers gets added
3219-
// after dominator computation and get skipped by OptReachWithoutCall().
3220-
// The below condition cannot be asserted in lower because fgSimpleLowering()
3221-
// can add a new basic block for range check failure which becomes
3222-
// fgLastBB with block number > loop header block number.
3223-
// assert(comp->compCurBB->HasFlag(BBF_GC_SAFE_POINT) ||
3224-
// !comp->optReachWithoutCall(comp->fgFirstBB, comp->compCurBB) || comp->GetInterruptible());
3225-
32263212
// If PInvokes are in-lined, we have to remember to execute PInvoke method epilog anywhere that
32273213
// a method returns. This is a case of caller method has both PInvokes and tail calls.
32283214
if (comp->compMethodRequiresPInvokeFrame())
@@ -3525,12 +3511,6 @@ GenTree* Lowering::LowerTailCallViaJitHelper(GenTreeCall* call, GenTree* callTar
35253511
assert(call->IsTailCallViaJitHelper());
35263512
assert(callTarget != nullptr);
35273513

3528-
// The TailCall helper call never returns to the caller and is not GC interruptible.
3529-
// Therefore the block containing the tail call should be a GC safe point to avoid
3530-
// GC starvation. It is legal for the block to be unmarked iff the entry block is a
3531-
// GC safe point, as the entry block trivially dominates every reachable block.
3532-
assert(comp->compCurBB->HasFlag(BBF_GC_SAFE_POINT) || comp->fgFirstBB->HasFlag(BBF_GC_SAFE_POINT));
3533-
35343514
// If PInvokes are in-lined, we have to remember to execute PInvoke method epilog anywhere that
35353515
// a method returns. This is a case of caller method has both PInvokes and tail calls.
35363516
if (comp->compMethodRequiresPInvokeFrame())

0 commit comments

Comments
 (0)