diff --git a/src/coreclr/jit/block.cpp b/src/coreclr/jit/block.cpp index 60dbce6aaf00a..ecee292264ec8 100644 --- a/src/coreclr/jit/block.cpp +++ b/src/coreclr/jit/block.cpp @@ -137,14 +137,17 @@ void FlowEdge::addLikelihood(weight_t addedLikelihood) // AllSuccessorEnumerator: Construct an instance of the enumerator. // // Arguments: -// comp - Compiler instance -// block - The block whose successors are to be iterated +// comp - Compiler instance +// block - The block whose successors are to be iterated +// useProfile - If true, determines the order of successors visited using profile data // -AllSuccessorEnumerator::AllSuccessorEnumerator(Compiler* comp, BasicBlock* block) +AllSuccessorEnumerator::AllSuccessorEnumerator(Compiler* comp, BasicBlock* block, const bool useProfile /* = false */) : m_block(block) { m_numSuccs = 0; - block->VisitAllSuccs(comp, [this](BasicBlock* succ) { + block->VisitAllSuccs( + comp, + [this](BasicBlock* succ) { if (m_numSuccs < ArrLen(m_successors)) { m_successors[m_numSuccs] = succ; @@ -152,18 +155,22 @@ AllSuccessorEnumerator::AllSuccessorEnumerator(Compiler* comp, BasicBlock* block m_numSuccs++; return BasicBlockVisit::Continue; - }); + }, + useProfile); if (m_numSuccs > ArrLen(m_successors)) { m_pSuccessors = new (comp, CMK_BasicBlock) BasicBlock*[m_numSuccs]; unsigned numSuccs = 0; - block->VisitAllSuccs(comp, [this, &numSuccs](BasicBlock* succ) { + block->VisitAllSuccs( + comp, + [this, &numSuccs](BasicBlock* succ) { assert(numSuccs < m_numSuccs); m_pSuccessors[numSuccs++] = succ; return BasicBlockVisit::Continue; - }); + }, + useProfile); assert(numSuccs == m_numSuccs); } diff --git a/src/coreclr/jit/block.h b/src/coreclr/jit/block.h index 168f29cca084d..1c7d5c92a72f0 100644 --- a/src/coreclr/jit/block.h +++ b/src/coreclr/jit/block.h @@ -1820,7 +1820,7 @@ struct BasicBlock : private LIR::Range BasicBlockVisit VisitEHEnclosedHandlerSecondPassSuccs(Compiler* comp, TFunc func); template - BasicBlockVisit VisitAllSuccs(Compiler* comp, TFunc func); + BasicBlockVisit VisitAllSuccs(Compiler* comp, TFunc func, const bool useProfile = false); template BasicBlockVisit VisitEHSuccs(Compiler* comp, TFunc func); @@ -2518,7 +2518,7 @@ class AllSuccessorEnumerator public: // Constructs an enumerator of all `block`'s successors. - AllSuccessorEnumerator(Compiler* comp, BasicBlock* block); + AllSuccessorEnumerator(Compiler* comp, BasicBlock* block, const bool useProfile = false); // Gets the block whose successors are enumerated. BasicBlock* Block() diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index 7949058066494..21914eebea90a 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -2793,6 +2793,9 @@ class Compiler EHblkDsc* ehIsBlockHndLast(BasicBlock* block); bool ehIsBlockEHLast(BasicBlock* block); + template + void ehUpdateTryLasts(GetTryLast getTryLast, SetTryLast setTryLast); + bool ehBlockHasExnFlowDsc(BasicBlock* block); // Return the region index of the most nested EH region this block is in. @@ -6054,6 +6057,8 @@ class Compiler bool fgComputeCalledCount(weight_t returnWeight); bool fgReorderBlocks(bool useProfile); + void fgDoReversePostOrderLayout(); + void fgMoveColdBlocks(); bool fgFuncletsAreCold(); @@ -6074,9 +6079,10 @@ class Compiler PhaseStatus fgSetBlockOrder(); bool fgHasCycleWithoutGCSafePoint(); - template + template unsigned fgRunDfs(VisitPreorder assignPreorder, VisitPostorder assignPostorder, VisitEdge visitEdge); + template FlowGraphDfsTree* fgComputeDfs(); void fgInvalidateDfsTree(); diff --git a/src/coreclr/jit/compiler.hpp b/src/coreclr/jit/compiler.hpp index 022a92bd740a3..d8844c2e35afb 100644 --- a/src/coreclr/jit/compiler.hpp +++ b/src/coreclr/jit/compiler.hpp @@ -623,12 +623,13 @@ BasicBlockVisit BasicBlock::VisitEHSuccs(Compiler* comp, TFunc func) // Arguments: // comp - Compiler instance // func - Callback +// useProfile - If true, determines the order of successors visited using profile data // // Returns: // Whether or not the visiting was aborted. // template -BasicBlockVisit BasicBlock::VisitAllSuccs(Compiler* comp, TFunc func) +BasicBlockVisit BasicBlock::VisitAllSuccs(Compiler* comp, TFunc func, const bool useProfile /* = false */) { switch (bbKind) { @@ -662,10 +663,22 @@ BasicBlockVisit BasicBlock::VisitAllSuccs(Compiler* comp, TFunc func) return VisitEHSuccs(comp, func); case BBJ_COND: - RETURN_ON_ABORT(func(GetFalseTarget())); - - if (!TrueEdgeIs(GetFalseEdge())) + if (TrueEdgeIs(GetFalseEdge())) { + RETURN_ON_ABORT(func(GetFalseTarget())); + } + else if (useProfile && (GetTrueEdge()->getLikelihood() < GetFalseEdge()->getLikelihood())) + { + // When building an RPO-based block layout, we want to visit the unlikely successor first + // so that in the DFS computation, the likely successor will be processed right before this block, + // meaning the RPO-based layout will enable fall-through into the likely successor. + // + RETURN_ON_ABORT(func(GetTrueTarget())); + RETURN_ON_ABORT(func(GetFalseTarget())); + } + else + { + RETURN_ON_ABORT(func(GetFalseTarget())); RETURN_ON_ABORT(func(GetTrueTarget())); } @@ -696,8 +709,8 @@ BasicBlockVisit BasicBlock::VisitAllSuccs(Compiler* comp, TFunc func) // VisitRegularSuccs: Visit regular successors of this block. // // Arguments: -// comp - Compiler instance -// func - Callback +// comp - Compiler instance +// func - Callback // // Returns: // Whether or not the visiting was aborted. @@ -4745,6 +4758,7 @@ inline bool Compiler::compCanHavePatchpoints(const char** reason) // VisitPreorder - Functor type that takes a BasicBlock* and its preorder number // VisitPostorder - Functor type that takes a BasicBlock* and its postorder number // VisitEdge - Functor type that takes two BasicBlock*. +// useProfile - If true, determines order of successors visited using profile data // // Parameters: // visitPreorder - Functor to visit block in its preorder @@ -4755,7 +4769,7 @@ inline bool Compiler::compCanHavePatchpoints(const char** reason) // Returns: // Number of blocks visited. // -template +template unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPostorder, VisitEdge visitEdge) { BitVecTraits traits(fgBBNumMax + 1, this); @@ -4768,7 +4782,7 @@ unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPos auto dfsFrom = [&](BasicBlock* firstBB) { BitVecOps::AddElemD(&traits, visited, firstBB->bbNum); - blocks.Emplace(this, firstBB); + blocks.Emplace(this, firstBB, useProfile); visitPreorder(firstBB, preOrderIndex++); while (!blocks.Empty()) @@ -4780,7 +4794,7 @@ unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPos { if (BitVecOps::TryAddElemD(&traits, visited, succ->bbNum)) { - blocks.Emplace(this, succ); + blocks.Emplace(this, succ, useProfile); visitPreorder(succ, preOrderIndex++); } diff --git a/src/coreclr/jit/fgopt.cpp b/src/coreclr/jit/fgopt.cpp index ac008eb140f9d..88dd717fab693 100644 --- a/src/coreclr/jit/fgopt.cpp +++ b/src/coreclr/jit/fgopt.cpp @@ -3412,9 +3412,22 @@ bool Compiler::fgReorderBlocks(bool useProfile) } } - // If we will be reordering blocks, ensure the false target of a BBJ_COND block is its next block if (useProfile) { + if (JitConfig.JitDoReversePostOrderLayout()) + { + fgDoReversePostOrderLayout(); + fgMoveColdBlocks(); + + // Renumber blocks to facilitate LSRA's order of block visitation + // TODO: Consider removing this, and using traversal order in lSRA + // + fgRenumberBlocks(); + + return true; + } + + // We will be reordering blocks, so ensure the false target of a BBJ_COND block is its next block for (BasicBlock* block = fgFirstBB; block != nullptr; block = block->Next()) { if (block->KindIs(BBJ_COND) && !block->NextIs(block->GetFalseTarget())) @@ -4522,6 +4535,505 @@ bool Compiler::fgReorderBlocks(bool useProfile) #pragma warning(pop) #endif +//----------------------------------------------------------------------------- +// fgDoReversePostOrderLayout: Reorder blocks using a greedy RPO traversal. +// +void Compiler::fgDoReversePostOrderLayout() +{ +#ifdef DEBUG + if (verbose) + { + printf("*************** In fgDoReversePostOrderLayout()\n"); + + printf("\nInitial BasicBlocks"); + fgDispBasicBlocks(verboseTrees); + printf("\n"); + } +#endif // DEBUG + + // Compute DFS of all blocks in the method, using profile data to determine the order successors are visited in + // + FlowGraphDfsTree* const dfsTree = fgComputeDfs(); + + // Fast path: We don't have any EH regions, so just reorder the blocks + // + if (compHndBBtabCount == 0) + { + for (unsigned i = dfsTree->GetPostOrderCount() - 1; i != 0; i--) + { + BasicBlock* const block = dfsTree->GetPostOrder(i); + BasicBlock* const blockToMove = dfsTree->GetPostOrder(i - 1); + fgUnlinkBlock(blockToMove); + fgInsertBBafter(block, blockToMove); + } + + return; + } + + // The RPO will scramble the EH regions, requiring us to correct their state. + // To do so, we will need to determine the new end blocks of each region. + // + struct EHLayoutInfo + { + BasicBlock* tryRegionEnd; + BasicBlock* hndRegionEnd; + bool tryRegionInMainBody; + + // Default constructor provided so we can call ArrayStack::Emplace + // + EHLayoutInfo() = default; + }; + + ArrayStack regions(getAllocator(CMK_ArrayStack), compHndBBtabCount); + + // The RPO will break up call-finally pairs, so save them before re-ordering + // + struct CallFinallyPair + { + BasicBlock* callFinally; + BasicBlock* callFinallyRet; + + // Constructor provided so we can call ArrayStack::Emplace + // + CallFinallyPair(BasicBlock* first, BasicBlock* second) + : callFinally(first) + , callFinallyRet(second) + { + } + }; + + ArrayStack callFinallyPairs(getAllocator()); + + for (EHblkDsc* const HBtab : EHClauses(this)) + { + // Default-initialize a EHLayoutInfo for each EH clause + regions.Emplace(); + + if (HBtab->HasFinallyHandler()) + { + for (BasicBlock* const pred : HBtab->ebdHndBeg->PredBlocks()) + { + assert(pred->KindIs(BBJ_CALLFINALLY)); + if (pred->isBBCallFinallyPair()) + { + callFinallyPairs.Emplace(pred, pred->Next()); + } + } + } + } + + // Reorder blocks + // + for (unsigned i = dfsTree->GetPostOrderCount() - 1; i != 0; i--) + { + BasicBlock* const block = dfsTree->GetPostOrder(i); + BasicBlock* const blockToMove = dfsTree->GetPostOrder(i - 1); + + // Only reorder blocks within the same EH region -- we don't want to make them non-contiguous + // + if (BasicBlock::sameEHRegion(block, blockToMove)) + { + // Don't reorder EH regions with filter handlers -- we want the filter to come first + // + if (block->hasHndIndex() && ehGetDsc(block->getHndIndex())->HasFilter()) + { + continue; + } + + fgUnlinkBlock(blockToMove); + fgInsertBBafter(block, blockToMove); + } + } + + // Fix up call-finally pairs + // + for (int i = 0; i < callFinallyPairs.Height(); i++) + { + const CallFinallyPair& pair = callFinallyPairs.BottomRef(i); + fgUnlinkBlock(pair.callFinallyRet); + fgInsertBBafter(pair.callFinally, pair.callFinallyRet); + } + + // The RPO won't change the entry blocks of any EH regions, but reordering can change the last block in a region + // (for example, by pushing throw blocks unreachable via normal flow to the end of the region). + // First, determine the new EH region ends. + // + + for (BasicBlock* const block : Blocks(fgFirstBB, fgLastBBInMainFunction())) + { + if (block->hasTryIndex()) + { + EHLayoutInfo& layoutInfo = regions.BottomRef(block->getTryIndex()); + layoutInfo.tryRegionEnd = block; + layoutInfo.tryRegionInMainBody = true; + } + + if (block->hasHndIndex()) + { + regions.BottomRef(block->getHndIndex()).hndRegionEnd = block; + } + } + + for (BasicBlock* const block : Blocks(fgFirstFuncletBB)) + { + if (block->hasHndIndex()) + { + regions.BottomRef(block->getHndIndex()).hndRegionEnd = block; + } + + if (block->hasTryIndex()) + { + EHLayoutInfo& layoutInfo = regions.BottomRef(block->getTryIndex()); + + if (!layoutInfo.tryRegionInMainBody) + { + layoutInfo.tryRegionEnd = block; + } + } + } + + // Now, update the EH descriptors, starting with the try regions + // + auto getTryLast = [®ions](const unsigned index) -> BasicBlock* { + return regions.BottomRef(index).tryRegionEnd; + }; + + auto setTryLast = [®ions](const unsigned index, BasicBlock* const block) { + regions.BottomRef(index).tryRegionEnd = block; + }; + + ehUpdateTryLasts(getTryLast, setTryLast); + + // Now, do the handler regions + // + unsigned XTnum = 0; + for (EHblkDsc* const HBtab : EHClauses(this)) + { + // The end of each handler region should have been visited by iterating the blocklist above + // + BasicBlock* const hndEnd = regions.BottomRef(XTnum++).hndRegionEnd; + assert(hndEnd != nullptr); + + // Update the end pointer of this handler region to the new last block + // + HBtab->ebdHndLast = hndEnd; + const unsigned enclosingHndIndex = HBtab->ebdEnclosingHndIndex; + + // If this handler region is nested in another one, we might need to update its enclosing region's end block + // + if (enclosingHndIndex != EHblkDsc::NO_ENCLOSING_INDEX) + { + BasicBlock* const enclosingHndEnd = regions.BottomRef(enclosingHndIndex).hndRegionEnd; + assert(enclosingHndEnd != nullptr); + + // If the enclosing region ends right before the nested region begins, + // extend the enclosing region's last block to the end of the nested region. + // + BasicBlock* const hndBeg = HBtab->HasFilter() ? HBtab->ebdFilter : HBtab->ebdHndBeg; + if (enclosingHndEnd->NextIs(hndBeg)) + { + regions.BottomRef(enclosingHndIndex).hndRegionEnd = hndEnd; + } + } + } +} + +//----------------------------------------------------------------------------- +// fgMoveColdBlocks: Move rarely-run blocks to the end of their respective regions. +// +// Notes: +// Exception handlers are assumed to be cold, so we won't move blocks within them. +// +void Compiler::fgMoveColdBlocks() +{ +#ifdef DEBUG + if (verbose) + { + printf("*************** In fgMoveColdBlocks()\n"); + + printf("\nInitial BasicBlocks"); + fgDispBasicBlocks(verboseTrees); + printf("\n"); + } +#endif // DEBUG + + auto moveColdMainBlocks = [this]() { + // Find the last block in the main body that isn't part of an EH region + // + BasicBlock* lastMainBB; + for (lastMainBB = this->fgLastBBInMainFunction(); lastMainBB != nullptr; lastMainBB = lastMainBB->Prev()) + { + if (!lastMainBB->hasTryIndex() && !lastMainBB->hasHndIndex()) + { + break; + } + } + + // Nothing to do if there are two or fewer non-EH blocks + // + if ((lastMainBB == nullptr) || lastMainBB->IsFirst() || lastMainBB->PrevIs(fgFirstBB)) + { + return; + } + + // Search the main method body for rarely-run blocks to move + // + BasicBlock* prev; + for (BasicBlock* block = lastMainBB->Prev(); block != fgFirstBB; block = prev) + { + prev = block->Prev(); + + // We only want to move cold blocks. + // Also, don't consider blocks in EH regions for now; only move blocks in the main method body. + // Finally, don't move block if it is the beginning of a call-finally pair, + // as we want to keep these pairs contiguous + // (if we encounter the end of a pair below, we'll move the whole pair). + // + if (!block->isRunRarely() || block->hasTryIndex() || block->hasHndIndex() || block->isBBCallFinallyPair()) + { + continue; + } + + this->fgUnlinkBlock(block); + this->fgInsertBBafter(lastMainBB, block); + + // If block is the end of a call-finally pair, prev is the beginning of the pair. + // Move prev to before block to keep the pair contiguous. + // + if (block->KindIs(BBJ_CALLFINALLYRET)) + { + BasicBlock* const callFinally = prev; + prev = prev->Prev(); + assert(callFinally->KindIs(BBJ_CALLFINALLY)); + assert(!callFinally->HasFlag(BBF_RETLESS_CALL)); + this->fgUnlinkBlock(callFinally); + this->fgInsertBBafter(lastMainBB, callFinally); + } + } + + // We have moved all cold main blocks before lastMainBB to after lastMainBB. + // If lastMainBB itself is cold, move it to the end of the method to restore its relative ordering. + // + if (lastMainBB->isRunRarely()) + { + BasicBlock* const newLastMainBB = this->fgLastBBInMainFunction(); + if (lastMainBB != newLastMainBB) + { + BasicBlock* const prev = lastMainBB->Prev(); + this->fgUnlinkBlock(lastMainBB); + this->fgInsertBBafter(newLastMainBB, lastMainBB); + + // Call-finally check + // + if (lastMainBB->KindIs(BBJ_CALLFINALLYRET)) + { + assert(prev->KindIs(BBJ_CALLFINALLY)); + assert(!prev->HasFlag(BBF_RETLESS_CALL)); + assert(prev != newLastMainBB); + this->fgUnlinkBlock(prev); + this->fgInsertBBafter(newLastMainBB, prev); + } + } + } + }; + + moveColdMainBlocks(); + + // No EH regions + // + if (compHndBBtabCount == 0) + { + return; + } + + // We assume exception handlers are cold, so we won't bother moving blocks within them. + // We will move blocks only within try regions. + // First, determine where each try region ends, without considering nested regions. + // We will use these end blocks as insertion points. + // + BasicBlock** const tryRegionEnds = new (this, CMK_Generic) BasicBlock* [compHndBBtabCount] {}; + + for (BasicBlock* const block : Blocks(fgFirstBB, fgLastBBInMainFunction())) + { + if (block->hasTryIndex()) + { + tryRegionEnds[block->getTryIndex()] = block; + } + } + + // Search all try regions in the main method body for cold blocks to move + // + BasicBlock* prev; + for (BasicBlock* block = fgLastBBInMainFunction(); block != fgFirstBB; block = prev) + { + prev = block->Prev(); + + // Only consider rarely-run blocks in try regions. + // If we have such a block that is also part of an exception handler, don't bother moving it. + // Finally, don't move block if it is the beginning of a call-finally pair, + // as we want to keep these pairs contiguous + // (if we encounter the end of a pair below, we'll move the whole pair). + // + if (!block->hasTryIndex() || !block->isRunRarely() || block->hasHndIndex() || block->isBBCallFinallyPair()) + { + continue; + } + + const unsigned tryIndex = block->getTryIndex(); + EHblkDsc* const HBtab = ehGetDsc(tryIndex); + + // Don't move the beginning of a try region. + // Also, if this try region's entry is cold, don't bother moving its blocks. + // + if ((HBtab->ebdTryBeg == block) || (HBtab->ebdTryBeg->isRunRarely())) + { + continue; + } + + BasicBlock* const insertionPoint = tryRegionEnds[tryIndex]; + assert(insertionPoint != nullptr); + + // Don't move the end of this try region + // + if (block == insertionPoint) + { + continue; + } + + fgUnlinkBlock(block); + fgInsertBBafter(insertionPoint, block); + + // Keep call-finally pairs contiguous + // + if (block->KindIs(BBJ_CALLFINALLYRET)) + { + BasicBlock* const callFinally = prev; + prev = prev->Prev(); + assert(callFinally->KindIs(BBJ_CALLFINALLY)); + assert(!callFinally->HasFlag(BBF_RETLESS_CALL)); + fgUnlinkBlock(callFinally); + fgInsertBBafter(insertionPoint, callFinally); + } + } + + // Before updating EH descriptors, find the new try region ends + // + for (unsigned XTnum = 0; XTnum < compHndBBtabCount; XTnum++) + { + BasicBlock* const tryEnd = tryRegionEnds[XTnum]; + + // This try region isn't in the main method body + // + if (tryEnd == nullptr) + { + continue; + } + + // If we moved cold blocks to the end of this try region, + // search for the new end block + // + BasicBlock* newTryEnd = tryEnd; + for (BasicBlock* const block : Blocks(tryEnd, fgLastBBInMainFunction())) + { + if (!BasicBlock::sameTryRegion(tryEnd, block)) + { + break; + } + + newTryEnd = block; + } + + // We moved cold blocks to the end of this try region, but the old end block is cold, too. + // Move the old end block to the end of the region to preserve its relative ordering. + // + if ((tryEnd != newTryEnd) && tryEnd->isRunRarely() && !tryEnd->hasHndIndex()) + { + BasicBlock* const prev = tryEnd->Prev(); + fgUnlinkBlock(tryEnd); + fgInsertBBafter(newTryEnd, tryEnd); + + // Keep call-finally pairs contiguous + // + if (tryEnd->KindIs(BBJ_CALLFINALLYRET)) + { + assert(prev->KindIs(BBJ_CALLFINALLY)); + assert(!prev->HasFlag(BBF_RETLESS_CALL)); + fgUnlinkBlock(prev); + fgInsertBBafter(newTryEnd, prev); + } + } + else + { + // Otherwise, just update the try region end + // + tryRegionEnds[XTnum] = newTryEnd; + } + } + + // Now, update EH descriptors + // + auto getTryLast = [tryRegionEnds](const unsigned index) -> BasicBlock* { + return tryRegionEnds[index]; + }; + + auto setTryLast = [tryRegionEnds](const unsigned index, BasicBlock* const block) { + tryRegionEnds[index] = block; + }; + + ehUpdateTryLasts(getTryLast, setTryLast); +} + +//------------------------------------------------------------- +// ehUpdateTryLasts: Iterates EH descriptors, updating each try region's +// end block as determined by getTryLast. +// +// Type parameters: +// GetTryLast - Functor type that takes an EH index, +// and returns the corresponding region's new try end block +// SetTryLast - Functor type that takes an EH index and a BasicBlock*, +// and updates some internal state tracking the new try end block of each EH region +// +// Parameters: +// getTryLast - Functor to get new try end block for an EH region +// setTryLast - Functor to update the new try end block for an EH region +// +template +void Compiler::ehUpdateTryLasts(GetTryLast getTryLast, SetTryLast setTryLast) +{ + unsigned XTnum = 0; + for (EHblkDsc* const HBtab : EHClauses(this)) + { + BasicBlock* const tryEnd = getTryLast(XTnum++); + + if (tryEnd == nullptr) + { + continue; + } + + // Update the end pointer of this try region to the new last block + // + HBtab->ebdTryLast = tryEnd; + const unsigned enclosingTryIndex = HBtab->ebdEnclosingTryIndex; + + // If this try region is nested in another one, we might need to update its enclosing region's end block + // + if (enclosingTryIndex != EHblkDsc::NO_ENCLOSING_INDEX) + { + BasicBlock* const enclosingTryEnd = getTryLast(enclosingTryIndex); + + // If multiple EH descriptors map to the same try region, + // then the enclosing region's last block might be null in the table, so set it here. + // Similarly, if the enclosing region ends right before the nested region begins, + // extend the enclosing region's last block to the end of the nested region. + // + if ((enclosingTryEnd == nullptr) || enclosingTryEnd->NextIs(HBtab->ebdTryBeg)) + { + setTryLast(enclosingTryIndex, tryEnd); + } + } + } +} + //------------------------------------------------------------- // fgUpdateFlowGraphPhase: run flow graph optimization as a // phase, with no tail duplication diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 6ab0977c55c25..457a3cde82fff 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -4043,8 +4043,8 @@ bool FlowGraphDfsTree::Contains(BasicBlock* block) const // block `descendant` // // Arguments: -// ancestor -- block that is possible ancestor -// descendant -- block that is possible descendant +// ancestor - block that is possible ancestor +// descendant - block that is possible descendant // // Returns: // True if `ancestor` is ancestor of `descendant` in the depth first spanning @@ -4063,6 +4063,9 @@ bool FlowGraphDfsTree::IsAncestor(BasicBlock* ancestor, BasicBlock* descendant) //------------------------------------------------------------------------ // fgComputeDfs: Compute a depth-first search tree for the flow graph. // +// Type parameters: +// useProfile - If true, determines order of successors visited using profile data +// // Returns: // The tree. // @@ -4070,6 +4073,7 @@ bool FlowGraphDfsTree::IsAncestor(BasicBlock* ancestor, BasicBlock* descendant) // Preorder and postorder numbers are assigned into the BasicBlock structure. // The tree returned contains a postorder of the basic blocks. // +template FlowGraphDfsTree* Compiler::fgComputeDfs() { BasicBlock** postOrder = new (this, CMK_DepthFirstSearch) BasicBlock*[fgBBcount]; @@ -4095,10 +4099,17 @@ FlowGraphDfsTree* Compiler::fgComputeDfs() } }; - unsigned numBlocks = fgRunDfs(visitPreorder, visitPostorder, visitEdge); + unsigned numBlocks = + fgRunDfs(visitPreorder, + visitPostorder, + visitEdge); return new (this, CMK_DepthFirstSearch) FlowGraphDfsTree(this, postOrder, numBlocks, hasCycle); } +// Add explicit instantiations. +template FlowGraphDfsTree* Compiler::fgComputeDfs(); +template FlowGraphDfsTree* Compiler::fgComputeDfs(); + //------------------------------------------------------------------------ // fgInvalidateDfsTree: Invalidate computed DFS tree and dependent annotations // (like loops, dominators and SSA). diff --git a/src/coreclr/jit/jitconfigvalues.h b/src/coreclr/jit/jitconfigvalues.h index 700cd33bd6bf2..81345191ce5f8 100644 --- a/src/coreclr/jit/jitconfigvalues.h +++ b/src/coreclr/jit/jitconfigvalues.h @@ -754,6 +754,9 @@ RELEASE_CONFIG_INTEGER(JitEnablePhysicalPromotion, W("JitEnablePhysicalPromotion // Enable cross-block local assertion prop RELEASE_CONFIG_INTEGER(JitEnableCrossBlockLocalAssertionProp, W("JitEnableCrossBlockLocalAssertionProp"), 1) +// Do greedy RPO-based layout in Compiler::fgReorderBlocks. +RELEASE_CONFIG_INTEGER(JitDoReversePostOrderLayout, W("JitDoReversePostOrderLayout"), 0); + // JitFunctionFile: Name of a file that contains a list of functions. If the currently compiled function is in the // file, certain other JIT config variables will be active. If the currently compiled function is not in the file, // the specific JIT config variables will not be active.