diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 56410dfd3035a9..b0fe932cb435eb 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -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); diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 1e8f2d7c45ef80..252dd0bedfecca 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -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); @@ -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()) { @@ -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; @@ -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) @@ -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. @@ -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);