diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index e3e700b754a9e6..ba8156be7a35dd 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -398,10 +398,7 @@ enum BasicBlockFlags : unsigned __int64 BBF_LOOP_ALIGN = MAKE_BBFLAG(16), // Block is lexically the first block in a loop we intend to align. BBF_HAS_ALIGN = MAKE_BBFLAG(17), // BB ends with 'align' instruction BBF_HAS_JMP = MAKE_BBFLAG(18), // BB executes a JMP instruction (instead of return) - BBF_GC_SAFE_POINT = MAKE_BBFLAG(19), // BB has a GC safe point (a call). More abstractly, BB does not require a - // (further) poll -- this may be because this BB has a call, or, in some - // cases, because the BB occurs in a loop, and we've determined that all - // paths in the loop body leading to BB include a call. + BBF_GC_SAFE_POINT = MAKE_BBFLAG(19), // BB has a GC safe point (e.g. a call) BBF_HAS_IDX_LEN = MAKE_BBFLAG(20), // BB contains simple index or length expressions on an SD array local var. BBF_HAS_MD_IDX_LEN = MAKE_BBFLAG(21), // BB contains simple index, length, or lower bound expressions on an MD array local var. BBF_HAS_MDARRAYREF = MAKE_BBFLAG(22), // Block has a multi-dimensional array reference @@ -1296,8 +1293,6 @@ struct BasicBlock : private LIR::Range void ensurePredListOrder(Compiler* compiler); void reorderPredList(Compiler* compiler); - BlockSet bbReach; // Set of all blocks that can reach this one - union { BasicBlock* bbIDom; // Represent the closest dominator to this block (called the Immediate // Dominator) used to compute the dominance tree. diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index fcc8da7b792cf7..c13091a84ead35 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -293,9 +293,6 @@ Histogram domsChangedIterationTable(domsChangedIterationBuckets); unsigned computeReachabilitySetsIterationBuckets[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0}; Histogram computeReachabilitySetsIterationTable(computeReachabilitySetsIterationBuckets); -unsigned computeReachabilityIterationBuckets[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0}; -Histogram computeReachabilityIterationTable(computeReachabilityIterationBuckets); - #endif // COUNT_BASIC_BLOCKS /***************************************************************************** @@ -1577,12 +1574,6 @@ void Compiler::compShutdown() computeReachabilitySetsIterationTable.dump(jitstdout()); jitprintf("--------------------------------------------------\n"); - jitprintf("--------------------------------------------------\n"); - jitprintf("fgComputeReachability `while (change)` iterations:\n"); - jitprintf("--------------------------------------------------\n"); - computeReachabilityIterationTable.dump(jitstdout()); - jitprintf("--------------------------------------------------\n"); - #endif // COUNT_BASIC_BLOCKS #if COUNT_LOOPS @@ -5854,6 +5845,7 @@ void Compiler::RecomputeFlowGraphAnnotations() optSetBlockWeights(); optFindLoops(); + fgInvalidateDfsTree(); m_dfsTree = fgComputeDfs(); optFindNewLoops(); @@ -9650,7 +9642,14 @@ JITDBGAPI void __cdecl cReach(Compiler* comp) { static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called printf("===================================================================== *Reach %u\n", sequenceNumber++); - comp->fgDispReach(); + if (comp->m_reachabilitySets != nullptr) + { + comp->m_reachabilitySets->Dump(); + } + else + { + printf(" Not computed\n"); + } } JITDBGAPI void __cdecl cDoms(Compiler* comp) diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 543f1c24938172..521aa32777b4be 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -1570,10 +1570,8 @@ enum API_ICorJitInfo_Names enum class FlowGraphUpdates { - COMPUTE_BASICS = 0, // renumber blocks, reachability, etc - COMPUTE_DOMS = 1 << 0, // recompute dominators - COMPUTE_RETURNS = 1 << 1, // recompute return blocks - COMPUTE_LOOPS = 1 << 2, // recompute loop table + COMPUTE_BASICS = 0, // renumber blocks, reachability, etc + COMPUTE_DOMS = 1 << 0, // recompute dominators }; inline constexpr FlowGraphUpdates operator|(FlowGraphUpdates a, FlowGraphUpdates b) @@ -2392,6 +2390,30 @@ class BlockToNaturalLoopMap static BlockToNaturalLoopMap* Build(FlowGraphNaturalLoops* loops); }; +// Represents a data structure that can answer A -> B reachability queries in +// O(1) time. Only takes regular flow into account; if A -> B requires +// exceptional flow, then CanReach returns false. +class BlockReachabilitySets +{ + FlowGraphDfsTree* m_dfsTree; + BitVec* m_reachabilitySets; + + BlockReachabilitySets(FlowGraphDfsTree* dfsTree, BitVec* reachabilitySets) + : m_dfsTree(dfsTree) + , m_reachabilitySets(reachabilitySets) + { + } + +public: + bool CanReach(BasicBlock* from, BasicBlock* to); + +#ifdef DEBUG + void Dump(); +#endif + + static BlockReachabilitySets* Build(FlowGraphDfsTree* dfsTree); +}; + enum class FieldKindForVN { SimpleStatic, @@ -4988,6 +5010,7 @@ class Compiler // Dominator tree used by SSA construction and copy propagation (the two are expected to use the same tree // in order to avoid the need for SSA reconstruction and an "out of SSA" phase). FlowGraphDominatorTree* m_domTree; + BlockReachabilitySets* m_reachabilitySets; // After the dominance tree is computed, we cache a DFS preorder number and DFS postorder number to compute // dominance queries in O(1). fgDomTreePreOrder and fgDomTreePostOrder are arrays giving the block's preorder and @@ -5045,10 +5068,6 @@ class Compiler roundUp(fgCurBBEpochSize, (unsigned)(sizeof(size_t) * 8)) / unsigned(sizeof(size_t) * 8); #ifdef DEBUG - // All BlockSet objects are now invalid! - fgReachabilitySetsValid = false; // the bbReach sets are now invalid! - fgEnterBlksSetValid = false; // the fgEnterBlks set is now invalid! - if (verbose) { unsigned epochArrSize = BasicBlockBitSetTraits::GetArrSize(this); @@ -5128,14 +5147,6 @@ class Compiler bool fgHasSwitch; // any BBJ_SWITCH jumps? - BlockSet fgEnterBlks; // Set of blocks which have a special transfer of control; the "entry" blocks plus EH handler - // begin blocks. - -#ifdef DEBUG - bool fgReachabilitySetsValid; // Are the bbReach sets valid? - bool fgEnterBlksSetValid; // Is the fgEnterBlks set valid? -#endif // DEBUG - bool fgRemoveRestOfBlock; // true if we know that we will throw bool fgStmtRemoved; // true if we remove statements -> need new DFA @@ -5782,8 +5793,6 @@ class Compiler // Dominator computation member functions // Not exposed outside Compiler protected: - bool fgReachable(BasicBlock* b1, BasicBlock* b2); // Returns true if block b1 can reach block b2 - // Compute immediate dominators, the dominator tree and and its pre/post-order travsersal numbers. void fgComputeDoms(); @@ -5791,12 +5800,8 @@ class Compiler // Note: this is relatively slow compared to calling fgDominate(), // especially if dealing with a single block versus block check. - void fgComputeReachabilitySets(); // Compute bbReach sets. (Also sets BBF_GC_SAFE_POINT flag on blocks.) - void fgComputeReturnBlocks(); // Initialize fgReturnBlocks to a list of BBJ_RETURN blocks. - void fgComputeEnterBlocksSet(); // Compute the set of entry blocks, 'fgEnterBlks'. - // Remove blocks determined to be unreachable by the 'canRemoveBlock'. template bool fgRemoveUnreachableBlocks(CanRemoveBlockBody canRemoveBlock); @@ -6120,7 +6125,6 @@ class Compiler #ifdef DEBUG void fgDispDoms(); - void fgDispReach(); void fgDispBBLiveness(BasicBlock* block); void fgDispBBLiveness(); void fgTableDispBasicBlock(BasicBlock* block, int ibcColWidth = 0); @@ -12302,7 +12306,6 @@ extern Histogram bbCntTable; extern Histogram bbOneBBSizeTable; extern Histogram domsChangedIterationTable; extern Histogram computeReachabilitySetsIterationTable; -extern Histogram computeReachabilityIterationTable; #endif /***************************************************************************** diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index eee2ab4f2c603f..0a22fb43cecdc0 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -34,10 +34,6 @@ void Compiler::fgInit() fgDomsComputed = false; fgReturnBlocksComputed = false; -#ifdef DEBUG - fgReachabilitySetsValid = false; -#endif // DEBUG - /* Initialize the basic block list */ fgFirstBB = nullptr; @@ -66,10 +62,12 @@ void Compiler::fgInit() fgBBVarSetsInited = false; fgReturnCount = 0; - m_dfsTree = nullptr; - m_loops = nullptr; - m_loopSideEffects = nullptr; - m_blockToLoop = nullptr; + m_dfsTree = nullptr; + m_loops = nullptr; + m_loopSideEffects = nullptr; + m_blockToLoop = nullptr; + m_domTree = nullptr; + m_reachabilitySets = nullptr; // Initialize BlockSet data. fgCurBBEpoch = 0; @@ -118,15 +116,8 @@ void Compiler::fgInit() /* We will record a list of all BBJ_RETURN blocks here */ fgReturnBlocks = nullptr; - /* This is set by fgComputeReachability */ - fgEnterBlks = BlockSetOps::UninitVal(); - fgUsedSharedTemps = nullptr; -#ifdef DEBUG - fgEnterBlksSetValid = false; -#endif // DEBUG - #if !defined(FEATURE_EH_FUNCLETS) ehMaxHndNestingCount = 0; #endif // !FEATURE_EH_FUNCLETS diff --git a/src/coreclr/jit/fgdiagnostic.cpp b/src/coreclr/jit/fgdiagnostic.cpp index d0c0bb39e04633..f77cca453519a7 100644 --- a/src/coreclr/jit/fgdiagnostic.cpp +++ b/src/coreclr/jit/fgdiagnostic.cpp @@ -1860,25 +1860,6 @@ void Compiler::fgDumpFlowGraphLoops(FILE* file) /*****************************************************************************/ #ifdef DEBUG -void Compiler::fgDispReach() -{ - printf("------------------------------------------------\n"); - printf("BBnum Reachable by \n"); - printf("------------------------------------------------\n"); - - for (BasicBlock* const block : Blocks()) - { - printf(FMT_BB " : ", block->bbNum); - BlockSetOps::Iter iter(this, block->bbReach); - unsigned bbNum = 0; - while (iter.NextElem(&bbNum)) - { - printf(FMT_BB " ", bbNum); - } - printf("\n"); - } -} - void Compiler::fgDispDoms() { // Don't bother printing this when we have a large number of BasicBlocks in the method diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index 5fbb9be3dd3a53..a6dfd2b2a733d0 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -82,74 +82,6 @@ bool Compiler::fgDominate(const BasicBlock* b1, const BasicBlock* b2) return treeDom; } -//------------------------------------------------------------------------ -// fgReachable: Returns true if block `b1` can reach block `b2`. -// -// Arguments: -// b1, b2 -- Two blocks to compare. -// -// Return Value: -// true if `b1` can reach `b2` via some path. If either b1 or b2 were created after dominators were calculated, -// but the dominator information still exists, try to determine if we can make a statement about -// b1 reaching b2 based on existing reachability information and other information, such as -// predecessor lists. -// -// Assumptions: -// -- Dominators have been calculated (`fgDomsComputed` is true). -// -- Reachability information has been calculated (`fgReachabilitySetsValid` is true). -// -bool Compiler::fgReachable(BasicBlock* b1, BasicBlock* b2) -{ - noway_assert(fgDomsComputed); - - // - // If the fgModified flag is false then we made some modifications to - // the flow graph, like adding a new block or changing a conditional branch - // into an unconditional branch. - // - // We can continue to use the dominator and reachable information to - // unmark loops as long as we haven't renumbered the blocks or we aren't - // asking for information about a new block - // - - if (b2->bbNum > fgDomBBcount) - { - if (b1 == b2) - { - return true; - } - - for (BasicBlock* const predBlock : b2->PredBlocks()) - { - if (fgReachable(b1, predBlock)) - { - return true; - } - } - - return false; - } - - if (b1->bbNum > fgDomBBcount) - { - noway_assert(b1->KindIs(BBJ_ALWAYS, BBJ_COND)); - - if (b1->KindIs(BBJ_COND)) - { - return fgReachable(b1->GetFalseTarget(), b2) || fgReachable(b1->GetTrueTarget(), b2); - } - else - { - return fgReachable(b1->GetTarget(), b2); - } - } - - /* Check if b1 can reach b2 */ - assert(fgReachabilitySetsValid); - assert(BasicBlockBitSetTraits::GetSize(this) == fgDomBBcount + 1); - return BlockSetOps::IsMember(this, b2->bbReach, b1->bbNum); -} - //------------------------------------------------------------------------ // fgUpdateChangedFlowGraph: Update changed flow graph information. // @@ -167,113 +99,17 @@ bool Compiler::fgReachable(BasicBlock* b1, BasicBlock* b2) void Compiler::fgUpdateChangedFlowGraph(FlowGraphUpdates updates) { const bool computeDoms = ((updates & FlowGraphUpdates::COMPUTE_DOMS) == FlowGraphUpdates::COMPUTE_DOMS); - const bool computeReturnBlocks = - ((updates & FlowGraphUpdates::COMPUTE_RETURNS) == FlowGraphUpdates::COMPUTE_RETURNS); - const bool computeLoops = ((updates & FlowGraphUpdates::COMPUTE_LOOPS) == FlowGraphUpdates::COMPUTE_LOOPS); // We need to clear this so we don't hit an assert calling fgRenumberBlocks(). fgDomsComputed = false; - if (computeReturnBlocks) - { - fgComputeReturnBlocks(); - } - JITDUMP("\nRenumbering the basic blocks for fgUpdateChangeFlowGraph\n"); fgRenumberBlocks(); - fgComputeEnterBlocksSet(); fgDfsReversePostorder(); - fgComputeReachabilitySets(); if (computeDoms) { fgComputeDoms(); } - if (computeLoops) - { - // Reset the loop info annotations and find the loops again. - // Note: this is similar to `RecomputeLoopInfo`. - optResetLoopInfo(); - optSetBlockWeights(); - optFindLoops(); - } -} - -//------------------------------------------------------------------------ -// fgComputeReachabilitySets: Compute the bbReach sets. -// -// This can be called to recompute the bbReach sets after the flow graph changes, such as when the -// number of BasicBlocks change (and thus, the BlockSet epoch changes). -// -// This also sets the BBF_GC_SAFE_POINT flag on blocks. -// -// This depends on `fgBBReversePostorder` being correct. -// -// TODO-Throughput: This algorithm consumes O(n^2) because we're using dense bitsets to -// represent reachability. While this yields O(1) time queries, it bloats the memory usage -// for large code. We can do better if we try to approach reachability by -// computing the strongly connected components of the flow graph. That way we only need -// linear memory to label every block with its SCC. -// -void Compiler::fgComputeReachabilitySets() -{ - assert(fgPredsComputed); - assert(fgBBReversePostorder != nullptr); - -#ifdef DEBUG - fgReachabilitySetsValid = false; -#endif // DEBUG - - for (BasicBlock* const block : Blocks()) - { - // Initialize the per-block bbReach sets. It creates a new empty set, - // because the block epoch could change since the previous initialization - // and the old set could have wrong size. - block->bbReach = BlockSetOps::MakeEmpty(this); - - /* Mark block as reaching itself */ - BlockSetOps::AddElemD(this, block->bbReach, block->bbNum); - } - - // Find the reachable blocks. Also, set BBF_GC_SAFE_POINT. - - bool change; - unsigned changedIterCount = 1; - do - { - change = false; - - for (unsigned i = 1; i <= fgBBNumMax; ++i) - { - BasicBlock* const block = fgBBReversePostorder[i]; - - if (block->bbPreds != nullptr) - { - BasicBlockFlags predGcFlags = BBF_GC_SAFE_POINT; // Do all of our predecessor blocks have a GC safe bit? - for (BasicBlock* const predBlock : block->PredBlocks()) - { - change |= BlockSetOps::UnionDChanged(this, block->bbReach, predBlock->bbReach); - predGcFlags &= predBlock->GetFlagsRaw(); - } - block->SetFlags(predGcFlags); - } - } - - ++changedIterCount; - } while (change); - -#if COUNT_BASIC_BLOCKS - computeReachabilitySetsIterationTable.record(changedIterCount); -#endif // COUNT_BASIC_BLOCKS - -#ifdef DEBUG - if (verbose) - { - printf("\nAfter computing reachability sets:\n"); - fgDispReach(); - } - - fgReachabilitySetsValid = true; -#endif // DEBUG } //------------------------------------------------------------------------ @@ -317,56 +153,6 @@ void Compiler::fgComputeReturnBlocks() #endif // DEBUG } -//------------------------------------------------------------------------ -// fgComputeEnterBlocksSet: Compute the entry blocks set. -// -// Initialize fgEnterBlks to the set of blocks for which we don't have explicit control -// flow edges. These are the entry basic block and each of the EH handler blocks. -// -void Compiler::fgComputeEnterBlocksSet() -{ -#ifdef DEBUG - fgEnterBlksSetValid = false; -#endif // DEBUG - - fgEnterBlks = BlockSetOps::MakeEmpty(this); - - /* Now set the entry basic block */ - BlockSetOps::AddElemD(this, fgEnterBlks, fgFirstBB->bbNum); - assert(fgFirstBB->bbNum == 1); - - /* Also 'or' in the handler basic blocks */ - if (!compIsForInlining()) - { - for (EHblkDsc* const HBtab : EHClauses(this)) - { - if (HBtab->HasFilter()) - { - BlockSetOps::AddElemD(this, fgEnterBlks, HBtab->ebdFilter->bbNum); - } - BlockSetOps::AddElemD(this, fgEnterBlks, HBtab->ebdHndBeg->bbNum); - } - } - -#ifdef DEBUG - if (verbose) - { - printf("Enter blocks: "); - BlockSetOps::Iter iter(this, fgEnterBlks); - unsigned bbNum = 0; - while (iter.NextElem(&bbNum)) - { - printf(FMT_BB " ", bbNum); - } - printf("\n"); - } -#endif // DEBUG - -#ifdef DEBUG - fgEnterBlksSetValid = true; -#endif // DEBUG -} - //------------------------------------------------------------------------ // fgRemoveUnreachableBlocks: Remove unreachable blocks. // @@ -493,70 +279,21 @@ bool Compiler::fgRemoveUnreachableBlocks(CanRemoveBlockBody canRemoveBlock) // // Assumes the predecessor lists are computed and correct. // -// Use `fgReachable()` to check reachability. -// Use `fgDominate()` to check dominance. -// PhaseStatus Compiler::fgComputeReachability() { assert(fgPredsComputed); - fgComputeReturnBlocks(); - - // Compute reachability and then delete blocks determined to be unreachable. If we delete blocks, we - // need to loop, as that might have caused more blocks to become unreachable. This can happen in the - // case where a call to a finally is unreachable and deleted (maybe the call to the finally is - // preceded by a throw or an infinite loop), making the blocks following the finally unreachable. - // However, all EH entry blocks are considered global entry blocks, causing the blocks following the - // call to the finally to stay rooted, until a second round of reachability is done. - // The dominator algorithm expects that all blocks can be reached from the fgEnterBlks set. - unsigned passNum = 1; - bool changed; - - auto canRemoveBlock = [&](BasicBlock* block) -> bool { - // If any of the entry blocks can reach this block, then we skip it. - if (!BlockSetOps::IsEmptyIntersection(this, fgEnterBlks, block->bbReach)) - { - return false; - } - - return true; - }; - - bool madeChanges = false; + bool madeChanges = fgDfsBlocksAndRemove() != PhaseStatus::MODIFIED_NOTHING; - do - { - // Just to be paranoid, avoid infinite loops; fall back to minopts. - if (passNum > 10) - { - noway_assert(!"Too many unreachable block removal loops"); - } - - // Walk the flow graph, reassign block numbers to keep them in ascending order. - JITDUMP("\nRenumbering the basic blocks for fgComputeReachability pass #%u\n", passNum); - passNum++; - madeChanges |= fgRenumberBlocks(); - - // - // Compute fgEnterBlks, reverse post-order, and bbReach. - // + madeChanges |= fgRenumberBlocks(); - fgComputeEnterBlocksSet(); - fgDfsReversePostorder(); - fgComputeReachabilitySets(); - - // - // Use reachability information to delete unreachable blocks. - // - - changed = fgRemoveUnreachableBlocks(canRemoveBlock); - madeChanges |= changed; + fgDfsReversePostorder(); - } while (changed); + // fgDfsReversePostorder reassigns preorder numbers, so recompute the DFS. + m_dfsTree = fgComputeDfs(); -#if COUNT_BASIC_BLOCKS - computeReachabilityIterationTable.record(passNum - 1); -#endif // COUNT_BASIC_BLOCKS + fgComputeReturnBlocks(); + m_reachabilitySets = BlockReachabilitySets::Build(m_dfsTree); // // Now, compute the dominators @@ -6418,6 +6155,7 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication, bool isPhase) // PhaseStatus Compiler::fgDfsBlocksAndRemove() { + fgInvalidateDfsTree(); m_dfsTree = fgComputeDfs(); PhaseStatus status = PhaseStatus::MODIFIED_NOTHING; diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index fdf58c20855ead..8503328a8d2b38 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -4025,10 +4025,11 @@ FlowGraphDfsTree* Compiler::fgComputeDfs() // void Compiler::fgInvalidateDfsTree() { - m_dfsTree = nullptr; - m_loops = nullptr; - m_domTree = nullptr; - fgSsaValid = false; + m_dfsTree = nullptr; + m_loops = nullptr; + m_domTree = nullptr; + m_reachabilitySets = nullptr; + fgSsaValid = false; } //------------------------------------------------------------------------ @@ -5778,3 +5779,125 @@ BlockToNaturalLoopMap* BlockToNaturalLoopMap::Build(FlowGraphNaturalLoops* loops return new (comp, CMK_Loops) BlockToNaturalLoopMap(loops, indices); } + +//------------------------------------------------------------------------ +// BlockReachabilitySets::Build: Build the reachability sets. +// +// Parameters: +// dfsTree - DFS tree +// +// Returns: +// The sets. +// +// Remarks: +// This algorithm consumes O(n^2) memory because we're using dense +// bitsets to represent reachability. +// +BlockReachabilitySets* BlockReachabilitySets::Build(FlowGraphDfsTree* dfsTree) +{ + Compiler* comp = dfsTree->GetCompiler(); + BitVecTraits postOrderTraits = dfsTree->PostOrderTraits(); + BitVec* sets = new (comp, CMK_Reachability) BitVec[dfsTree->GetPostOrderCount()]; + + for (unsigned i = 0; i < dfsTree->GetPostOrderCount(); i++) + { + sets[i] = BitVecOps::MakeSingleton(&postOrderTraits, i); + } + + // Find the reachable blocks. Also, set BBF_GC_SAFE_POINT. + bool change; + unsigned changedIterCount = 1; + do + { + change = false; + + for (unsigned i = dfsTree->GetPostOrderCount(); i != 0; i--) + { + BasicBlock* const block = dfsTree->GetPostOrder(i - 1); + + for (BasicBlock* const predBlock : block->PredBlocks()) + { + change |= BitVecOps::UnionDChanged(&postOrderTraits, sets[block->bbNewPostorderNum], + sets[predBlock->bbNewPostorderNum]); + } + } + + ++changedIterCount; + } while (change); + +#if COUNT_BASIC_BLOCKS + computeReachabilitySetsIterationTable.record(changedIterCount); +#endif // COUNT_BASIC_BLOCKS + + BlockReachabilitySets* reachabilitySets = new (comp, CMK_Reachability) BlockReachabilitySets(dfsTree, sets); + +#ifdef DEBUG + if (comp->verbose) + { + printf("\nAfter computing reachability sets:\n"); + reachabilitySets->Dump(); + } +#endif + + return reachabilitySets; +} + +//------------------------------------------------------------------------ +// BlockReachabilitySets::CanReach: Check if "from" can flow to "to" through +// only regular control flow edges. +// +// Parameters: +// from - Start block +// to - Candidate destination block +// +// Returns: +// True if so. +// +bool BlockReachabilitySets::CanReach(BasicBlock* from, BasicBlock* to) +{ + assert(m_dfsTree->Contains(from)); + + if (!m_dfsTree->Contains(to)) + { + return false; + } + + BitVecTraits poTraits = m_dfsTree->PostOrderTraits(); + return BitVecOps::IsMember(&poTraits, m_reachabilitySets[to->bbNewPostorderNum], from->bbNewPostorderNum); +} + +#ifdef DEBUG +//------------------------------------------------------------------------ +// BlockReachabilitySets::Dump: Dump the reachability sets to stdout. +// +void BlockReachabilitySets::Dump() +{ + printf("------------------------------------------------\n"); + printf("BBnum Reachable by \n"); + printf("------------------------------------------------\n"); + + Compiler* comp = m_dfsTree->GetCompiler(); + BitVecTraits postOrderTraits = m_dfsTree->PostOrderTraits(); + + for (BasicBlock* const block : comp->Blocks()) + { + printf(FMT_BB " : ", block->bbNum); + if (m_dfsTree->Contains(block)) + { + BitVecOps::Iter iter(&postOrderTraits, m_reachabilitySets[block->bbNewPostorderNum]); + unsigned poNum = 0; + const char* sep = ""; + while (iter.NextElem(&poNum)) + { + printf("%s" FMT_BB, sep, m_dfsTree->GetPostOrder(poNum)->bbNum); + sep = " "; + } + } + else + { + printf("[unreachable]"); + } + printf("\n"); + } +} +#endif diff --git a/src/coreclr/jit/loopcloning.cpp b/src/coreclr/jit/loopcloning.cpp index 1290e47d5dbcf1..bd73a898d22387 100644 --- a/src/coreclr/jit/loopcloning.cpp +++ b/src/coreclr/jit/loopcloning.cpp @@ -3168,6 +3168,7 @@ PhaseStatus Compiler::optCloneLoops() fgDomsComputed = false; fgRenumberBlocks(); + fgInvalidateDfsTree(); m_dfsTree = fgComputeDfs(); m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); } diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index 51e4de72a0c90e..b70188c434dd96 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -13213,7 +13213,8 @@ Compiler::FoldResult Compiler::fgFoldConditional(BasicBlock* block) /* dest block must also be marked as a loop head and */ /* We must be able to reach the backedge block */ if (optLoopTableValid && block->GetTrueTarget()->isLoopHead() && - (block->GetTrueTarget()->bbNum <= block->bbNum) && fgReachable(block->GetTrueTarget(), block)) + (block->GetTrueTarget()->bbNum <= block->bbNum) && + m_reachabilitySets->CanReach(block->GetTrueTarget(), block)) { optUnmarkLoopBlocks(block->GetTrueTarget(), block); } diff --git a/src/coreclr/jit/optimizer.cpp b/src/coreclr/jit/optimizer.cpp index 7c6f19beb8bf0d..a65cc93c472728 100644 --- a/src/coreclr/jit/optimizer.cpp +++ b/src/coreclr/jit/optimizer.cpp @@ -75,7 +75,7 @@ PhaseStatus Compiler::optSetBlockWeights() for (BasicBlock* const block : Blocks()) { /* Blocks that can't be reached via the first block are rarely executed */ - if (!fgReachable(fgFirstBB, block) && !block->isRunRarely()) + if (!m_reachabilitySets->CanReach(fgFirstBB, block) && !block->isRunRarely()) { madeChanges = true; block->bbSetRunRarely(); @@ -155,7 +155,7 @@ void Compiler::optScaleLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk) { noway_assert(begBlk->bbNum <= endBlk->bbNum); noway_assert(begBlk->isLoopHead()); - noway_assert(fgReachable(begBlk, endBlk)); + noway_assert(m_reachabilitySets->CanReach(begBlk, endBlk)); noway_assert(!opts.MinOpts()); #ifdef DEBUG @@ -213,7 +213,7 @@ void Compiler::optScaleLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk) // For curBlk to be part of a loop that starts at begBlk, curBlk must be reachable from begBlk and // (since this is a loop) begBlk must likewise be reachable from curBlk. - if (fgReachable(curBlk, begBlk) && fgReachable(begBlk, curBlk)) + if (m_reachabilitySets->CanReach(curBlk, begBlk) && m_reachabilitySets->CanReach(begBlk, curBlk)) { // If `curBlk` reaches any of the back edge blocks we set `reachable`. // If `curBlk` dominates any of the back edge blocks we set `dominates`. @@ -224,7 +224,7 @@ void Compiler::optScaleLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk) { BasicBlock* backedge = tmp->getSourceBlock(); - reachable |= fgReachable(curBlk, backedge); + reachable |= m_reachabilitySets->CanReach(curBlk, backedge); dominates |= fgDominate(curBlk, backedge); if (dominates && reachable) @@ -319,7 +319,7 @@ void Compiler::optUnmarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk) #endif return; } - noway_assert(fgReachable(begBlk, endBlk)); + noway_assert(m_reachabilitySets->CanReach(begBlk, endBlk)); #ifdef DEBUG if (verbose) @@ -357,7 +357,7 @@ void Compiler::optUnmarkLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk) // For curBlk to be part of a loop that starts at begBlk, curBlk must be reachable from begBlk and // (since this is a loop) begBlk must likewise be reachable from curBlk. // - if (fgReachable(curBlk, begBlk) && fgReachable(begBlk, curBlk)) + if (m_reachabilitySets->CanReach(curBlk, begBlk) && m_reachabilitySets->CanReach(begBlk, curBlk)) { weight_t scale = 1.0 / BB_LOOP_WEIGHT_SCALE; @@ -503,17 +503,18 @@ void Compiler::optUpdateLoopsBeforeRemoveBlock(BasicBlock* block, bool skipUnmar { // This block must reach conditionally or always - if (block->KindIs(BBJ_ALWAYS) && // This block always reaches - block->GetTarget()->isLoopHead() && // to a loop head... - (block->GetTarget()->bbNum <= block->bbNum) && // This is a backedge... - fgReachable(block->GetTarget(), block)) // Block's back edge target can reach block... + if (block->KindIs(BBJ_ALWAYS) && // This block always reaches + block->GetTarget()->isLoopHead() && // to a loop head... + (block->GetTarget()->bbNum <= block->bbNum) && // This is a backedge... + m_reachabilitySets->CanReach(block->GetTarget(), block)) // Block's back edge target can reach block... { optUnmarkLoopBlocks(block->GetTarget(), block); // Unscale the blocks in such loop. } - else if (block->KindIs(BBJ_COND) && // This block conditionally reaches - block->GetTrueTarget()->isLoopHead() && // to a loop head... - (block->GetTrueTarget()->bbNum <= block->bbNum) && // This is a backedge... - fgReachable(block->GetTrueTarget(), block)) // Block's back edge target can reach block... + else if (block->KindIs(BBJ_COND) && // This block conditionally reaches + block->GetTrueTarget()->isLoopHead() && // to a loop head... + (block->GetTrueTarget()->bbNum <= block->bbNum) && // This is a backedge... + m_reachabilitySets->CanReach(block->GetTrueTarget(), block)) // Block's back edge target can reach + // block... { optUnmarkLoopBlocks(block->GetTrueTarget(), block); // Unscale the blocks in such loop. } @@ -2546,6 +2547,7 @@ void Compiler::optFindNaturalLoops() if (mod) { + fgInvalidateDfsTree(); fgUpdateChangedFlowGraph(FlowGraphUpdates::COMPUTE_DOMS); } @@ -3943,6 +3945,7 @@ PhaseStatus Compiler::optUnrollLoops() { fgDomsComputed = false; fgRenumberBlocks(); // For proper lexical visit + fgInvalidateDfsTree(); m_dfsTree = fgComputeDfs(); m_loops = FlowGraphNaturalLoops::Find(m_dfsTree); passes++; @@ -4653,7 +4656,7 @@ void Compiler::optMarkLoopHeads() printf("*************** In optMarkLoopHeads()\n"); } - assert(fgReachabilitySetsValid); + assert(m_reachabilitySets != nullptr); fgDebugCheckBBNumIncreasing(); int loopHeadsMarked = 0; @@ -4665,10 +4668,9 @@ void Compiler::optMarkLoopHeads() { // Set BBF_LOOP_HEAD if we have backwards branches to this block. - unsigned blockNum = block->bbNum; for (BasicBlock* const predBlock : block->PredBlocks()) { - if (blockNum <= predBlock->bbNum) + if (block->bbNum <= predBlock->bbNum) { if (predBlock->KindIs(BBJ_CALLFINALLY)) { @@ -4677,7 +4679,7 @@ void Compiler::optMarkLoopHeads() } // If block can reach predBlock then we have a loop head - if (BlockSetOps::IsMember(this, predBlock->bbReach, blockNum)) + if (m_reachabilitySets->CanReach(block, predBlock)) { hasLoops = true; block->SetFlags(BBF_LOOP_HEAD); @@ -4746,6 +4748,12 @@ void Compiler::optFindAndScaleGeneralLoopBlocks() // This code depends on block number ordering. INDEBUG(fgDebugCheckBBNumIncreasing()); + assert(m_dfsTree != nullptr); + if (m_reachabilitySets == nullptr) + { + m_reachabilitySets = BlockReachabilitySets::Build(m_dfsTree); + } + unsigned generalLoopCount = 0; // We will use the following terminology: @@ -4780,7 +4788,7 @@ void Compiler::optFindAndScaleGeneralLoopBlocks() } /* the top block must be able to reach the bottom block */ - if (!fgReachable(top, bottom)) + if (!m_reachabilitySets->CanReach(top, bottom)) { continue; } @@ -4891,6 +4899,7 @@ void Compiler::optFindNewLoops() if (optCanonicalizeLoops(m_loops)) { + fgInvalidateDfsTree(); fgUpdateChangedFlowGraph(FlowGraphUpdates::COMPUTE_DOMS); m_dfsTree = fgComputeDfs(); m_loops = FlowGraphNaturalLoops::Find(m_dfsTree);