Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JIT: account for newly unreachable blocks in morph #109394

Merged
merged 4 commits into from
Nov 1, 2024
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
14 changes: 13 additions & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5450,8 +5450,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
Loading