Skip to content

Commit

Permalink
JIT: account for newly unreachable blocks in morph (#109394)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
AndyAyersMS authored Nov 1, 2024
1 parent 65d6ef6 commit bf369fd
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 4 deletions.
14 changes: 13 additions & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5400,8 +5400,20 @@ class Compiler

FoldResult fgFoldConditional(BasicBlock* block);

struct MorphUnreachableInfo
{
MorphUnreachableInfo(Compiler* comp);
void SetUnreachable(BasicBlock* block);
bool IsUnreachable(BasicBlock* block);

private:

BitVecTraits m_traits;
BitVec m_vec;
};

PhaseStatus fgMorphBlocks();
void fgMorphBlock(BasicBlock* block);
void fgMorphBlock(BasicBlock* block, MorphUnreachableInfo* unreachableInfo = nullptr);
void fgMorphStmts(BasicBlock* block);

void fgMergeBlockReturn(BasicBlock* block);
Expand Down
90 changes: 87 additions & 3 deletions src/coreclr/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13207,13 +13207,49 @@ void Compiler::fgMorphStmts(BasicBlock* block)
fgRemoveRestOfBlock = false;
}

//------------------------------------------------------------------------
// MorphUnreachbleInfo: construct info for unreachability tracking during morph
//
// Arguments:
// comp - compiler object
//
Compiler::MorphUnreachableInfo::MorphUnreachableInfo(Compiler* comp)
: m_traits(comp->m_dfsTree->GetPostOrderCount(), comp)
, m_vec(BitVecOps::MakeEmpty(&m_traits)){};

//------------------------------------------------------------------------
// SetUnreachable: during morph, mark a block as unreachable
//
// Arguments:
// block - block in question
//
void Compiler::MorphUnreachableInfo::SetUnreachable(BasicBlock* block)
{
BitVecOps::AddElemD(&m_traits, m_vec, block->bbPostorderNum);
}

//------------------------------------------------------------------------
// IsUnreachable: during morph, see if a block is now known to be unreachable
//
// Arguments:
// block - block in question
//
// Returns:
// true if so
//
bool Compiler::MorphUnreachableInfo::IsUnreachable(BasicBlock* block)
{
return BitVecOps::IsMember(&m_traits, m_vec, block->bbPostorderNum);
}

//------------------------------------------------------------------------
// fgMorphBlock: Morph a basic block
//
// Arguments:
// block - block in question
// unreachableInfo - [optional] info on blocks proven unreachable
//
void Compiler::fgMorphBlock(BasicBlock* block)
void Compiler::fgMorphBlock(BasicBlock* block, MorphUnreachableInfo* unreachableInfo)
{
JITDUMP("\nMorphing " FMT_BB "\n", block->bbNum);

Expand Down Expand Up @@ -13243,6 +13279,8 @@ void Compiler::fgMorphBlock(BasicBlock* block)
else
{
bool hasPredAssertions = false;
bool isReachable =
(block == fgFirstBB) || (block == genReturnBB) || (opts.IsOSR() && (block == fgEntryBB));

for (BasicBlock* const pred : block->PredBlocks())
{
Expand All @@ -13257,10 +13295,27 @@ void Compiler::fgMorphBlock(BasicBlock* block)
JITDUMP(FMT_BB " pred " FMT_BB " not processed; clearing assertions in\n", block->bbNum,
pred->bbNum);
hasPredAssertions = false;

// Generally we must assume this pred will be reachable.
//
isReachable = true;
break;
}

// Yes, pred assertions are available.
// This pred was reachable in the pre-morph DFS, but might have
// become unreachable during morph. If so, we can ignore its assertion state.
//
if (unreachableInfo->IsUnreachable(pred))
{
JITDUMP("Pred " FMT_BB " is no longer reachable\n", pred->bbNum);
continue;
}

// Since we have a reachable pred, this blocks is also reachable.
//
isReachable = true;

// This pred is reachable and has available assertions.
// If the pred is (a non-degenerate) BBJ_COND, fetch the appropriate out set.
//
ASSERT_TP assertionsOut;
Expand Down Expand Up @@ -13312,6 +13367,31 @@ void Compiler::fgMorphBlock(BasicBlock* block)
//
canUsePredAssertions = false;
}

// If there wasn't any reachable pred, this block is also not
// reachable. Note we exclude handler entries above, since we don't
// do the correct assertion tracking for handlers. Thus there is
// no need to consider reachable EH preds.
//
if (!isReachable)
{
JITDUMP(FMT_BB " has no reachable preds, marking as unreachable\n", block->bbNum);
unreachableInfo->SetUnreachable(block);

// Remove the block's IR and flow edges but don't mark the block as removed.
// Convert to BBJ_THROW. But leave CALLFINALLY alone.
//
// If we clear out the block, there is nothing to morph, so just return.
//
bool const isCallFinally = block->KindIs(BBJ_CALLFINALLY);
if (!isCallFinally)
{
fgUnreachableBlock(block);
block->RemoveFlags(BBF_REMOVED);
block->SetKindAndTargetEdge(BBJ_THROW);
return;
}
}
}

if (!canUsePredAssertions)
Expand Down Expand Up @@ -13430,6 +13510,10 @@ PhaseStatus Compiler::fgMorphBlocks()
INDEBUG(fgSafeBasicBlockCreation = false;);
INDEBUG(fgSafeFlowEdgeCreation = false;);

// We will track which blocks become unreachable during morph
//
MorphUnreachableInfo unreachableInfo(this);

// Allow edge creation to genReturnBB (target of return merging)
// and the scratch block successor (target for tail call to loop).
// This will also disallow dataflow into these blocks.
Expand All @@ -13452,7 +13536,7 @@ PhaseStatus Compiler::fgMorphBlocks()
for (unsigned i = m_dfsTree->GetPostOrderCount(); i != 0; i--)
{
BasicBlock* const block = m_dfsTree->GetPostOrder(i - 1);
fgMorphBlock(block);
fgMorphBlock(block, &unreachableInfo);
}
assert(bbNumMax == fgBBNumMax);

Expand Down

0 comments on commit bf369fd

Please sign in to comment.