Skip to content

Commit bf369fd

Browse files
authored
JIT: account for newly unreachable blocks in morph (#109394)
Morph can alter control flow, if (for instance) it can prove a block's conditional branch must go a certain way. Take advantage of this to improve morph's cross-block assertion prop, by not considering unreachable predecessors when computing the incoming assertion state. This is similar to something we already do in value numbering, but there control flow isn't being altered. Also, when we can prove that a block has become unreachable, remove the block IR and alter its jump kind, so we are not spending time morphing IR we'll subsequently just throw away. However, leave callfinallys as is, since removing their IR and flow is more involved.
1 parent 65d6ef6 commit bf369fd

File tree

2 files changed

+100
-4
lines changed

2 files changed

+100
-4
lines changed

src/coreclr/jit/compiler.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5400,8 +5400,20 @@ class Compiler
54005400

54015401
FoldResult fgFoldConditional(BasicBlock* block);
54025402

5403+
struct MorphUnreachableInfo
5404+
{
5405+
MorphUnreachableInfo(Compiler* comp);
5406+
void SetUnreachable(BasicBlock* block);
5407+
bool IsUnreachable(BasicBlock* block);
5408+
5409+
private:
5410+
5411+
BitVecTraits m_traits;
5412+
BitVec m_vec;
5413+
};
5414+
54035415
PhaseStatus fgMorphBlocks();
5404-
void fgMorphBlock(BasicBlock* block);
5416+
void fgMorphBlock(BasicBlock* block, MorphUnreachableInfo* unreachableInfo = nullptr);
54055417
void fgMorphStmts(BasicBlock* block);
54065418

54075419
void fgMergeBlockReturn(BasicBlock* block);

src/coreclr/jit/morph.cpp

Lines changed: 87 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13207,13 +13207,49 @@ void Compiler::fgMorphStmts(BasicBlock* block)
1320713207
fgRemoveRestOfBlock = false;
1320813208
}
1320913209

13210+
//------------------------------------------------------------------------
13211+
// MorphUnreachbleInfo: construct info for unreachability tracking during morph
13212+
//
13213+
// Arguments:
13214+
// comp - compiler object
13215+
//
13216+
Compiler::MorphUnreachableInfo::MorphUnreachableInfo(Compiler* comp)
13217+
: m_traits(comp->m_dfsTree->GetPostOrderCount(), comp)
13218+
, m_vec(BitVecOps::MakeEmpty(&m_traits)){};
13219+
13220+
//------------------------------------------------------------------------
13221+
// SetUnreachable: during morph, mark a block as unreachable
13222+
//
13223+
// Arguments:
13224+
// block - block in question
13225+
//
13226+
void Compiler::MorphUnreachableInfo::SetUnreachable(BasicBlock* block)
13227+
{
13228+
BitVecOps::AddElemD(&m_traits, m_vec, block->bbPostorderNum);
13229+
}
13230+
13231+
//------------------------------------------------------------------------
13232+
// IsUnreachable: during morph, see if a block is now known to be unreachable
13233+
//
13234+
// Arguments:
13235+
// block - block in question
13236+
//
13237+
// Returns:
13238+
// true if so
13239+
//
13240+
bool Compiler::MorphUnreachableInfo::IsUnreachable(BasicBlock* block)
13241+
{
13242+
return BitVecOps::IsMember(&m_traits, m_vec, block->bbPostorderNum);
13243+
}
13244+
1321013245
//------------------------------------------------------------------------
1321113246
// fgMorphBlock: Morph a basic block
1321213247
//
1321313248
// Arguments:
1321413249
// block - block in question
13250+
// unreachableInfo - [optional] info on blocks proven unreachable
1321513251
//
13216-
void Compiler::fgMorphBlock(BasicBlock* block)
13252+
void Compiler::fgMorphBlock(BasicBlock* block, MorphUnreachableInfo* unreachableInfo)
1321713253
{
1321813254
JITDUMP("\nMorphing " FMT_BB "\n", block->bbNum);
1321913255

@@ -13243,6 +13279,8 @@ void Compiler::fgMorphBlock(BasicBlock* block)
1324313279
else
1324413280
{
1324513281
bool hasPredAssertions = false;
13282+
bool isReachable =
13283+
(block == fgFirstBB) || (block == genReturnBB) || (opts.IsOSR() && (block == fgEntryBB));
1324613284

1324713285
for (BasicBlock* const pred : block->PredBlocks())
1324813286
{
@@ -13257,10 +13295,27 @@ void Compiler::fgMorphBlock(BasicBlock* block)
1325713295
JITDUMP(FMT_BB " pred " FMT_BB " not processed; clearing assertions in\n", block->bbNum,
1325813296
pred->bbNum);
1325913297
hasPredAssertions = false;
13298+
13299+
// Generally we must assume this pred will be reachable.
13300+
//
13301+
isReachable = true;
1326013302
break;
1326113303
}
1326213304

13263-
// Yes, pred assertions are available.
13305+
// This pred was reachable in the pre-morph DFS, but might have
13306+
// become unreachable during morph. If so, we can ignore its assertion state.
13307+
//
13308+
if (unreachableInfo->IsUnreachable(pred))
13309+
{
13310+
JITDUMP("Pred " FMT_BB " is no longer reachable\n", pred->bbNum);
13311+
continue;
13312+
}
13313+
13314+
// Since we have a reachable pred, this blocks is also reachable.
13315+
//
13316+
isReachable = true;
13317+
13318+
// This pred is reachable and has available assertions.
1326413319
// If the pred is (a non-degenerate) BBJ_COND, fetch the appropriate out set.
1326513320
//
1326613321
ASSERT_TP assertionsOut;
@@ -13312,6 +13367,31 @@ void Compiler::fgMorphBlock(BasicBlock* block)
1331213367
//
1331313368
canUsePredAssertions = false;
1331413369
}
13370+
13371+
// If there wasn't any reachable pred, this block is also not
13372+
// reachable. Note we exclude handler entries above, since we don't
13373+
// do the correct assertion tracking for handlers. Thus there is
13374+
// no need to consider reachable EH preds.
13375+
//
13376+
if (!isReachable)
13377+
{
13378+
JITDUMP(FMT_BB " has no reachable preds, marking as unreachable\n", block->bbNum);
13379+
unreachableInfo->SetUnreachable(block);
13380+
13381+
// Remove the block's IR and flow edges but don't mark the block as removed.
13382+
// Convert to BBJ_THROW. But leave CALLFINALLY alone.
13383+
//
13384+
// If we clear out the block, there is nothing to morph, so just return.
13385+
//
13386+
bool const isCallFinally = block->KindIs(BBJ_CALLFINALLY);
13387+
if (!isCallFinally)
13388+
{
13389+
fgUnreachableBlock(block);
13390+
block->RemoveFlags(BBF_REMOVED);
13391+
block->SetKindAndTargetEdge(BBJ_THROW);
13392+
return;
13393+
}
13394+
}
1331513395
}
1331613396

1331713397
if (!canUsePredAssertions)
@@ -13430,6 +13510,10 @@ PhaseStatus Compiler::fgMorphBlocks()
1343013510
INDEBUG(fgSafeBasicBlockCreation = false;);
1343113511
INDEBUG(fgSafeFlowEdgeCreation = false;);
1343213512

13513+
// We will track which blocks become unreachable during morph
13514+
//
13515+
MorphUnreachableInfo unreachableInfo(this);
13516+
1343313517
// Allow edge creation to genReturnBB (target of return merging)
1343413518
// and the scratch block successor (target for tail call to loop).
1343513519
// This will also disallow dataflow into these blocks.
@@ -13452,7 +13536,7 @@ PhaseStatus Compiler::fgMorphBlocks()
1345213536
for (unsigned i = m_dfsTree->GetPostOrderCount(); i != 0; i--)
1345313537
{
1345413538
BasicBlock* const block = m_dfsTree->GetPostOrder(i - 1);
13455-
fgMorphBlock(block);
13539+
fgMorphBlock(block, &unreachableInfo);
1345613540
}
1345713541
assert(bbNumMax == fgBBNumMax);
1345813542

0 commit comments

Comments
 (0)