Skip to content

Commit fe043b4

Browse files
JIT: Allow initializing blocks without jump targets (#93928)
...and remove BasicBlock::bbTempJumpDest, per discussion in #93415. We still assert the jump target is set appropriately whenever it is read/written, and in the majority of cases, we still initialize blocks with their jump kind and target set simultaneously. This change improves usability for the few edge cases (like in Compiler::impImportLeave) where a block's jump target isn't known at initialization.
1 parent 371d715 commit fe043b4

File tree

13 files changed

+100
-98
lines changed

13 files changed

+100
-98
lines changed

src/coreclr/jit/block.cpp

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,6 @@ size_t BasicBlock::s_Count;
2828
// The max # of tree nodes in any BB
2929
/* static */
3030
unsigned BasicBlock::s_nMaxTrees;
31-
32-
// Temporary target to initialize blocks with jumps
33-
/* static */
34-
BasicBlock BasicBlock::bbTempJumpDest;
3531
#endif // DEBUG
3632

3733
#ifdef DEBUG
@@ -1449,14 +1445,14 @@ bool BasicBlock::endsWithTailCallConvertibleToLoop(Compiler* comp, GenTree** tai
14491445
* Allocate a basic block but don't append it to the current BB list.
14501446
*/
14511447

1452-
BasicBlock* Compiler::bbNewBasicBlock()
1448+
BasicBlock* BasicBlock::bbNewBasicBlock(Compiler* compiler)
14531449
{
14541450
BasicBlock* block;
14551451

14561452
/* Allocate the block descriptor and zero it out */
1457-
assert(fgSafeBasicBlockCreation);
1453+
assert(compiler->fgSafeBasicBlockCreation);
14581454

1459-
block = new (this, CMK_BasicBlock) BasicBlock;
1455+
block = new (compiler, CMK_BasicBlock) BasicBlock;
14601456

14611457
#if MEASURE_BLOCK_SIZE
14621458
BasicBlock::s_Count += 1;
@@ -1465,7 +1461,7 @@ BasicBlock* Compiler::bbNewBasicBlock()
14651461

14661462
#ifdef DEBUG
14671463
// fgLookupBB() is invalid until fgInitBBLookup() is called again.
1468-
fgBBs = (BasicBlock**)0xCDCD;
1464+
compiler->fgInvalidateBBLookup();
14691465
#endif
14701466

14711467
// TODO-Throughput: The following memset is pretty expensive - do something else?
@@ -1479,15 +1475,15 @@ BasicBlock* Compiler::bbNewBasicBlock()
14791475
block->bbCodeOffsEnd = BAD_IL_OFFSET;
14801476

14811477
#ifdef DEBUG
1482-
block->bbID = compBasicBlockID++;
1478+
block->bbID = compiler->compBasicBlockID++;
14831479
#endif
14841480

14851481
/* Give the block a number, set the ancestor count and weight */
14861482

1487-
++fgBBcount;
1488-
block->bbNum = ++fgBBNumMax;
1483+
++compiler->fgBBcount;
1484+
block->bbNum = ++compiler->fgBBNumMax;
14891485

1490-
if (compRationalIRForm)
1486+
if (compiler->compRationalIRForm)
14911487
{
14921488
block->bbFlags |= BBF_IS_LIR;
14931489
}
@@ -1501,7 +1497,7 @@ BasicBlock* Compiler::bbNewBasicBlock()
15011497
block->bbEntryState = nullptr;
15021498

15031499
#ifdef DEBUG
1504-
if (verbose)
1500+
if (compiler->verbose)
15051501
{
15061502
printf("New Basic Block %s created.\n", block->dspToString());
15071503
}
@@ -1510,21 +1506,21 @@ BasicBlock* Compiler::bbNewBasicBlock()
15101506
// We will give all the blocks var sets after the number of tracked variables
15111507
// is determined and frozen. After that, if we dynamically create a basic block,
15121508
// we will initialize its var sets.
1513-
if (fgBBVarSetsInited)
1509+
if (compiler->fgBBVarSetsInited)
15141510
{
1515-
VarSetOps::AssignNoCopy(this, block->bbVarUse, VarSetOps::MakeEmpty(this));
1516-
VarSetOps::AssignNoCopy(this, block->bbVarDef, VarSetOps::MakeEmpty(this));
1517-
VarSetOps::AssignNoCopy(this, block->bbLiveIn, VarSetOps::MakeEmpty(this));
1518-
VarSetOps::AssignNoCopy(this, block->bbLiveOut, VarSetOps::MakeEmpty(this));
1519-
VarSetOps::AssignNoCopy(this, block->bbScope, VarSetOps::MakeEmpty(this));
1511+
VarSetOps::AssignNoCopy(compiler, block->bbVarUse, VarSetOps::MakeEmpty(compiler));
1512+
VarSetOps::AssignNoCopy(compiler, block->bbVarDef, VarSetOps::MakeEmpty(compiler));
1513+
VarSetOps::AssignNoCopy(compiler, block->bbLiveIn, VarSetOps::MakeEmpty(compiler));
1514+
VarSetOps::AssignNoCopy(compiler, block->bbLiveOut, VarSetOps::MakeEmpty(compiler));
1515+
VarSetOps::AssignNoCopy(compiler, block->bbScope, VarSetOps::MakeEmpty(compiler));
15201516
}
15211517
else
15221518
{
1523-
VarSetOps::AssignNoCopy(this, block->bbVarUse, VarSetOps::UninitVal());
1524-
VarSetOps::AssignNoCopy(this, block->bbVarDef, VarSetOps::UninitVal());
1525-
VarSetOps::AssignNoCopy(this, block->bbLiveIn, VarSetOps::UninitVal());
1526-
VarSetOps::AssignNoCopy(this, block->bbLiveOut, VarSetOps::UninitVal());
1527-
VarSetOps::AssignNoCopy(this, block->bbScope, VarSetOps::UninitVal());
1519+
VarSetOps::AssignNoCopy(compiler, block->bbVarUse, VarSetOps::UninitVal());
1520+
VarSetOps::AssignNoCopy(compiler, block->bbVarDef, VarSetOps::UninitVal());
1521+
VarSetOps::AssignNoCopy(compiler, block->bbLiveIn, VarSetOps::UninitVal());
1522+
VarSetOps::AssignNoCopy(compiler, block->bbLiveOut, VarSetOps::UninitVal());
1523+
VarSetOps::AssignNoCopy(compiler, block->bbScope, VarSetOps::UninitVal());
15281524
}
15291525

15301526
block->bbMemoryUse = emptyMemoryKindSet;
@@ -1550,10 +1546,15 @@ BasicBlock* Compiler::bbNewBasicBlock()
15501546
return block;
15511547
}
15521548

1553-
BasicBlock* Compiler::bbNewBasicBlock(BBjumpKinds jumpKind, BasicBlock* jumpDest /* = nullptr */)
1549+
BasicBlock* BasicBlock::bbNewBasicBlock(Compiler* compiler, BBjumpKinds jumpKind, BasicBlock* jumpDest /* = nullptr */)
15541550
{
1555-
BasicBlock* block = bbNewBasicBlock();
1556-
block->SetJumpKindAndTarget(jumpKind, jumpDest DEBUG_ARG(this));
1551+
BasicBlock* block = BasicBlock::bbNewBasicBlock(compiler);
1552+
1553+
// In some cases, we don't know a block's jump target during initialization, so don't check the jump kind/target
1554+
// yet.
1555+
// The checks will be done any time the jump kind/target is read or written to after initialization.
1556+
block->bbJumpKind = jumpKind;
1557+
block->bbJumpDest = jumpDest;
15571558

15581559
if (jumpKind == BBJ_THROW)
15591560
{
@@ -1563,17 +1564,19 @@ BasicBlock* Compiler::bbNewBasicBlock(BBjumpKinds jumpKind, BasicBlock* jumpDest
15631564
return block;
15641565
}
15651566

1566-
BasicBlock* Compiler::bbNewBasicBlock(BBswtDesc* jumpSwt)
1567+
BasicBlock* BasicBlock::bbNewBasicBlock(Compiler* compiler, BBswtDesc* jumpSwt)
15671568
{
1568-
BasicBlock* block = bbNewBasicBlock();
1569-
block->SetSwitchKindAndTarget(jumpSwt);
1569+
BasicBlock* block = BasicBlock::bbNewBasicBlock(compiler);
1570+
block->bbJumpKind = BBJ_SWITCH;
1571+
block->bbJumpSwt = jumpSwt;
15701572
return block;
15711573
}
15721574

1573-
BasicBlock* Compiler::bbNewBasicBlock(BBjumpKinds jumpKind, unsigned jumpOffs)
1575+
BasicBlock* BasicBlock::bbNewBasicBlock(Compiler* compiler, BBjumpKinds jumpKind, unsigned jumpOffs)
15741576
{
1575-
BasicBlock* block = bbNewBasicBlock();
1576-
block->SetJumpKindAndTarget(jumpKind, jumpOffs);
1577+
BasicBlock* block = BasicBlock::bbNewBasicBlock(compiler);
1578+
block->bbJumpKind = jumpKind;
1579+
block->bbJumpOffs = jumpOffs;
15771580
return block;
15781581
}
15791582

src/coreclr/jit/block.h

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -538,13 +538,10 @@ struct BasicBlock : private LIR::Range
538538
};
539539

540540
public:
541-
#ifdef DEBUG
542-
// When creating a block with a jump, we require its jump kind and target be initialized simultaneously.
543-
// In a few edge cases (for example, in Compiler::impImportLeave), we don't know the jump target at block creation.
544-
// In these cases, temporarily set the jump target to bbTempJumpDest, and update the jump target later.
545-
// We won't check jump targets against bbTempJumpDest in Release builds.
546-
static BasicBlock bbTempJumpDest;
547-
#endif // DEBUG
541+
static BasicBlock* bbNewBasicBlock(Compiler* compiler);
542+
static BasicBlock* bbNewBasicBlock(Compiler* compiler, BBjumpKinds jumpKind, BasicBlock* jumpDest = nullptr);
543+
static BasicBlock* bbNewBasicBlock(Compiler* compiler, BBswtDesc* jumpSwt);
544+
static BasicBlock* bbNewBasicBlock(Compiler* compiler, BBjumpKinds jumpKind, unsigned jumpOffs);
548545

549546
BBjumpKinds GetJumpKind() const
550547
{
@@ -633,10 +630,6 @@ struct BasicBlock : private LIR::Range
633630

634631
void SetJumpDest(BasicBlock* jumpDest)
635632
{
636-
// If bbJumpKind indicates this block has a jump,
637-
// bbJumpDest should have previously been set in SetJumpKindAndTarget().
638-
assert(HasJump() || !KindIs(BBJ_ALWAYS, BBJ_CALLFINALLY, BBJ_COND, BBJ_EHCATCHRET, BBJ_LEAVE));
639-
640633
// SetJumpKindAndTarget() nulls jumpDest for non-jump kinds,
641634
// so don't use SetJumpDest() to null bbJumpDest without updating bbJumpKind.
642635
bbJumpDest = jumpDest;

src/coreclr/jit/codegencommon.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ BasicBlock* CodeGen::genCreateTempLabel()
857857
#endif
858858

859859
// Label doesn't need a jump kind
860-
BasicBlock* block = compiler->bbNewBasicBlock();
860+
BasicBlock* block = BasicBlock::bbNewBasicBlock(compiler);
861861

862862
#ifdef DEBUG
863863
compiler->fgSafeBasicBlockCreation = false;

src/coreclr/jit/compiler.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3221,11 +3221,6 @@ class Compiler
32213221
bool fgSafeBasicBlockCreation;
32223222
#endif
32233223

3224-
BasicBlock* bbNewBasicBlock();
3225-
BasicBlock* bbNewBasicBlock(BBjumpKinds jumpKind, BasicBlock* jumpDest = nullptr);
3226-
BasicBlock* bbNewBasicBlock(BBswtDesc* jumpSwt);
3227-
BasicBlock* bbNewBasicBlock(BBjumpKinds jumpKind, unsigned jumpOffs);
3228-
32293224
/*
32303225
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
32313226
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
@@ -5718,6 +5713,10 @@ class Compiler
57185713
void* pCallBackData = nullptr,
57195714
bool computeStack = false);
57205715

5716+
#ifdef DEBUG
5717+
void fgInvalidateBBLookup();
5718+
#endif // DEBUG
5719+
57215720
/**************************************************************************
57225721
* PROTECTED
57235722
*************************************************************************/

src/coreclr/jit/fgbasic.cpp

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ bool Compiler::fgEnsureFirstBBisScratch()
221221

222222
assert(fgFirstBBScratch == nullptr);
223223

224-
BasicBlock* block = bbNewBasicBlock(BBJ_NONE);
224+
BasicBlock* block = BasicBlock::bbNewBasicBlock(this, BBJ_NONE);
225225

226226
if (fgFirstBB != nullptr)
227227
{
@@ -802,6 +802,17 @@ BasicBlock* Compiler::fgFirstBlockOfHandler(BasicBlock* block)
802802
return ehGetDsc(block->getHndIndex())->ebdHndBeg;
803803
}
804804

805+
#ifdef DEBUG
806+
//------------------------------------------------------------------------
807+
// fgInvalidateBBLookup: In non-Release builds, set fgBBs to a dummy value.
808+
// After calling this, fgInitBBLookup must be called before using fgBBs again.
809+
//
810+
void Compiler::fgInvalidateBBLookup()
811+
{
812+
fgBBs = (BasicBlock**)0xCDCD;
813+
}
814+
#endif // DEBUG
815+
805816
/*****************************************************************************
806817
*
807818
* The following helps find a basic block given its PC offset.
@@ -3478,18 +3489,18 @@ unsigned Compiler::fgMakeBasicBlocks(const BYTE* codeAddr, IL_OFFSET codeSize, F
34783489
switch (jmpKind)
34793490
{
34803491
case BBJ_SWITCH:
3481-
curBBdesc = bbNewBasicBlock(swtDsc);
3492+
curBBdesc = BasicBlock::bbNewBasicBlock(this, swtDsc);
34823493
break;
34833494

34843495
case BBJ_COND:
34853496
case BBJ_ALWAYS:
34863497
case BBJ_LEAVE:
34873498
noway_assert(jmpAddr != DUMMY_INIT(BAD_IL_OFFSET));
3488-
curBBdesc = bbNewBasicBlock(jmpKind, jmpAddr);
3499+
curBBdesc = BasicBlock::bbNewBasicBlock(this, jmpKind, jmpAddr);
34893500
break;
34903501

34913502
default:
3492-
curBBdesc = bbNewBasicBlock(jmpKind);
3503+
curBBdesc = BasicBlock::bbNewBasicBlock(this, jmpKind);
34933504
break;
34943505
}
34953506

@@ -4733,7 +4744,7 @@ BasicBlock* Compiler::fgSplitBlockAtEnd(BasicBlock* curr)
47334744
if (!curr->KindIs(BBJ_SWITCH))
47344745
{
47354746
// Start the new block with no refs. When we set the preds below, this will get updated correctly.
4736-
newBlock = bbNewBasicBlock(curr->GetJumpKind(), curr->GetJumpDest());
4747+
newBlock = BasicBlock::bbNewBasicBlock(this, curr->GetJumpKind(), curr->GetJumpDest());
47374748
newBlock->bbRefs = 0;
47384749

47394750
for (BasicBlock* const succ : curr->Succs(this))
@@ -4749,7 +4760,7 @@ BasicBlock* Compiler::fgSplitBlockAtEnd(BasicBlock* curr)
47494760
else
47504761
{
47514762
// Start the new block with no refs. When we set the preds below, this will get updated correctly.
4752-
newBlock = bbNewBasicBlock(curr->GetJumpSwt());
4763+
newBlock = BasicBlock::bbNewBasicBlock(this, curr->GetJumpSwt());
47534764
newBlock->bbRefs = 0;
47544765

47554766
// In the case of a switch statement there's more complicated logic in order to wire up the predecessor lists
@@ -5618,8 +5629,11 @@ BasicBlock* Compiler::fgConnectFallThrough(BasicBlock* bSrc, BasicBlock* bDst)
56185629
{
56195630
// If bSrc is an unconditional branch to the next block
56205631
// then change it to a BBJ_NONE block
5632+
// (It's possible for bSrc's jump destination to not be set yet,
5633+
// so check with BasicBlock::HasJump before calling BasicBlock::JumpsToNext)
56215634
//
5622-
if (bSrc->KindIs(BBJ_ALWAYS) && !(bSrc->bbFlags & BBF_KEEP_BBJ_ALWAYS) && bSrc->JumpsToNext())
5635+
if (bSrc->KindIs(BBJ_ALWAYS) && !(bSrc->bbFlags & BBF_KEEP_BBJ_ALWAYS) && bSrc->HasJump() &&
5636+
bSrc->JumpsToNext())
56235637
{
56245638
bSrc->SetJumpKindAndTarget(BBJ_NONE DEBUG_ARG(this));
56255639
JITDUMP("Changed an unconditional jump from " FMT_BB " to the next block " FMT_BB
@@ -6226,7 +6240,7 @@ BasicBlock* Compiler::fgNewBBbefore(BBjumpKinds jumpKind,
62266240
{
62276241
// Create a new BasicBlock and chain it in
62286242

6229-
BasicBlock* newBlk = bbNewBasicBlock(jumpKind, jumpDest);
6243+
BasicBlock* newBlk = BasicBlock::bbNewBasicBlock(this, jumpKind, jumpDest);
62306244
newBlk->bbFlags |= BBF_INTERNAL;
62316245

62326246
fgInsertBBbefore(block, newBlk);
@@ -6268,7 +6282,7 @@ BasicBlock* Compiler::fgNewBBafter(BBjumpKinds jumpKind,
62686282
{
62696283
// Create a new BasicBlock and chain it in
62706284

6271-
BasicBlock* newBlk = bbNewBasicBlock(jumpKind, jumpDest);
6285+
BasicBlock* newBlk = BasicBlock::bbNewBasicBlock(this, jumpKind, jumpDest);
62726286
newBlk->bbFlags |= BBF_INTERNAL;
62736287

62746288
fgInsertBBafter(block, newBlk);

src/coreclr/jit/fgdiagnostic.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3039,11 +3039,10 @@ void Compiler::fgDebugCheckBBlist(bool checkBBNum /* = false */, bool checkBBRef
30393039
assert(block->getTryIndex() < compHndBBtabCount);
30403040
}
30413041

3042-
// Blocks with these jump kinds must have valid (non-temporary) jump targets
3042+
// Blocks with these jump kinds must have non-null jump targets
30433043
if (block->KindIs(BBJ_ALWAYS, BBJ_CALLFINALLY, BBJ_COND, BBJ_EHCATCHRET, BBJ_LEAVE))
30443044
{
30453045
assert(block->HasJump());
3046-
assert(!block->HasJumpTo(&BasicBlock::bbTempJumpDest));
30473046
}
30483047

30493048
// A branch or fall-through to a BBJ_CALLFINALLY block must come from the `try` region associated

src/coreclr/jit/fgopt.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1661,7 +1661,7 @@ PhaseStatus Compiler::fgPostImportationCleanup()
16611661

16621662
// What follows is similar to fgNewBBInRegion, but we can't call that
16631663
// here as the oldTryEntry is no longer in the main bb list.
1664-
newTryEntry = bbNewBasicBlock(BBJ_NONE);
1664+
newTryEntry = BasicBlock::bbNewBasicBlock(this, BBJ_NONE);
16651665
newTryEntry->bbFlags |= (BBF_IMPORTED | BBF_INTERNAL);
16661666
newTryEntry->bbRefs = 0;
16671667

src/coreclr/jit/flowgraph.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2985,7 +2985,7 @@ void Compiler::fgInsertFuncletPrologBlock(BasicBlock* block)
29852985

29862986
/* Allocate a new basic block */
29872987

2988-
BasicBlock* newHead = bbNewBasicBlock(BBJ_NONE);
2988+
BasicBlock* newHead = BasicBlock::bbNewBasicBlock(this, BBJ_NONE);
29892989
newHead->bbFlags |= BBF_INTERNAL;
29902990
newHead->inheritWeight(block);
29912991
newHead->bbRefs = 0;

src/coreclr/jit/helperexpansion.cpp

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,9 @@ bool Compiler::fgExpandRuntimeLookupsForCall(BasicBlock** pBlock, Statement* stm
279279
nullcheckOp->gtFlags |= GTF_RELOP_JMP_USED;
280280

281281
// nullcheckBb conditionally jumps to fallbackBb, but we need to initialize fallbackBb last
282-
// so we can place it after nullcheckBb. So use a temporary jump target for now.
283-
BasicBlock* nullcheckBb = fgNewBBFromTreeAfter(BBJ_COND, prevBb, gtNewOperNode(GT_JTRUE, TYP_VOID, nullcheckOp),
284-
debugInfo DEBUG_ARG(&BasicBlock::bbTempJumpDest));
282+
// so we can place it after nullcheckBb. So set the jump target later.
283+
BasicBlock* nullcheckBb =
284+
fgNewBBFromTreeAfter(BBJ_COND, prevBb, gtNewOperNode(GT_JTRUE, TYP_VOID, nullcheckOp), debugInfo);
285285

286286
// Fallback basic block
287287
GenTree* fallbackValueDef = gtNewStoreLclVarNode(rtLookupLcl->GetLclNum(), call);
@@ -741,18 +741,15 @@ bool Compiler::fgExpandThreadLocalAccessForCall(BasicBlock** pBlock, Statement*
741741
// maxThreadStaticBlocksCondBB
742742

743743
// maxThreadStaticBlocksCondBB conditionally jumps to fallbackBb, but fallbackBb must be initialized last
744-
// so it can be placed after it. So use a temporary jump target for now.
745-
BasicBlock* maxThreadStaticBlocksCondBB =
746-
fgNewBBFromTreeAfter(BBJ_COND, prevBb, tlsValueDef, debugInfo DEBUG_ARG(&BasicBlock::bbTempJumpDest));
744+
// so it can be placed after it. So set the jump target later.
745+
BasicBlock* maxThreadStaticBlocksCondBB = fgNewBBFromTreeAfter(BBJ_COND, prevBb, tlsValueDef, debugInfo);
747746

748747
fgInsertStmtAfter(maxThreadStaticBlocksCondBB, maxThreadStaticBlocksCondBB->firstStmt(),
749748
fgNewStmtFromTree(maxThreadStaticBlocksCond));
750749

751-
// Similarly, give threadStaticBlockNulLCondBB a temporary jump target for now,
752-
// and update it to jump to its real target (fastPathBb) after it is initialized.
750+
// Similarly, set threadStaticBlockNulLCondBB to jump to fastPathBb once the latter exists.
753751
BasicBlock* threadStaticBlockNullCondBB =
754-
fgNewBBFromTreeAfter(BBJ_COND, maxThreadStaticBlocksCondBB, threadStaticBlockBaseDef,
755-
debugInfo DEBUG_ARG(&BasicBlock::bbTempJumpDest));
752+
fgNewBBFromTreeAfter(BBJ_COND, maxThreadStaticBlocksCondBB, threadStaticBlockBaseDef, debugInfo);
756753
fgInsertStmtAfter(threadStaticBlockNullCondBB, threadStaticBlockNullCondBB->firstStmt(),
757754
fgNewStmtFromTree(threadStaticBlockNullCond));
758755

0 commit comments

Comments
 (0)