Skip to content

Commit e1efa6b

Browse files
authored
JIT: Enable downwards optimization for multi-exit loops (#103181)
As long as an exiting block dominates all backedges it is ok to consider it to be converted to a downwards test, provided that the normal heuristics pass (i.e. it should be on a primary IV that is unused in other places in the loop). Previously we required there to be only one exiting block.
1 parent c417e3b commit e1efa6b

File tree

2 files changed

+51
-46
lines changed

2 files changed

+51
-46
lines changed

src/coreclr/jit/compiler.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7574,6 +7574,10 @@ class Compiler
75747574
bool optMakeLoopDownwardsCounted(ScalarEvolutionContext& scevContext,
75757575
FlowGraphNaturalLoop* loop,
75767576
LoopLocalOccurrences* loopLocals);
7577+
bool optMakeExitTestDownwardsCounted(ScalarEvolutionContext& scevContext,
7578+
FlowGraphNaturalLoop* loop,
7579+
BasicBlock* exiting,
7580+
LoopLocalOccurrences* loopLocals);
75777581
bool optWidenPrimaryIV(FlowGraphNaturalLoop* loop,
75787582
unsigned lclNum,
75797583
ScevAddRec* addRec,

src/coreclr/jit/inductionvariableopts.cpp

Lines changed: 47 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -879,21 +879,58 @@ bool Compiler::optMakeLoopDownwardsCounted(ScalarEvolutionContext& scevContext,
879879
{
880880
JITDUMP("Checking if we should make " FMT_LP " downwards counted\n", loop->GetIndex());
881881

882-
if (loop->ExitEdges().size() != 1)
882+
BasicBlock* dominates = nullptr;
883+
884+
for (FlowEdge* backEdge : loop->BackEdges())
883885
{
884-
// With multiple exits we generally can only compute an upper bound on
885-
// the backedge count.
886-
JITDUMP(" No; has multiple exits\n");
887-
return false;
886+
if (dominates == nullptr)
887+
{
888+
dominates = backEdge->getSourceBlock();
889+
}
890+
else
891+
{
892+
dominates = m_domTree->Intersect(dominates, backEdge->getSourceBlock());
893+
}
888894
}
889895

890-
BasicBlock* exiting = loop->ExitEdge(0)->getSourceBlock();
891-
if (!exiting->KindIs(BBJ_COND))
896+
bool changed = false;
897+
while ((dominates != nullptr) && loop->ContainsBlock(dominates))
892898
{
893-
JITDUMP(" No; exit is not BBJ_COND\n");
894-
return false;
899+
if (dominates->KindIs(BBJ_COND) &&
900+
(!loop->ContainsBlock(dominates->GetTrueTarget()) || !loop->ContainsBlock(dominates->GetFalseTarget())))
901+
{
902+
JITDUMP(" Considering exiting block " FMT_BB "\n", dominates->bbNum);
903+
// 'dominates' is an exiting block that dominates all backedges.
904+
changed |= optMakeExitTestDownwardsCounted(scevContext, loop, dominates, loopLocals);
905+
}
906+
907+
dominates = dominates->bbIDom;
895908
}
896909

910+
return changed;
911+
}
912+
913+
//------------------------------------------------------------------------
914+
// optMakeExitTestDownwardsCounted:
915+
// Try to modify the condition of a specific BBJ_COND exiting block to be on
916+
// a downwards counted IV if profitable.
917+
//
918+
// Parameters:
919+
// scevContext - SCEV context
920+
// loop - The specific loop
921+
// exiting - Exiting block
922+
// loopLocals - Data structure tracking local uses
923+
//
924+
// Returns:
925+
// True if any modification was made.
926+
//
927+
bool Compiler::optMakeExitTestDownwardsCounted(ScalarEvolutionContext& scevContext,
928+
FlowGraphNaturalLoop* loop,
929+
BasicBlock* exiting,
930+
LoopLocalOccurrences* loopLocals)
931+
{
932+
assert(exiting->KindIs(BBJ_COND));
933+
897934
Statement* jtrueStmt = exiting->lastStmt();
898935
GenTree* jtrue = jtrueStmt->GetRootNode();
899936
assert(jtrue->OperIs(GT_JTRUE));
@@ -1016,43 +1053,6 @@ bool Compiler::optMakeLoopDownwardsCounted(ScalarEvolutionContext& scevContext,
10161053
return false;
10171054
}
10181055

1019-
// Commonly there is only a single shared exit and backedge. In that case
1020-
// we do not even need to compute dominators since all backedges are
1021-
// definitely dominated by the exit.
1022-
if ((loop->BackEdges().size() == 1) && loop->BackEdge(0)->getSourceBlock() == loop->ExitEdge(0)->getSourceBlock())
1023-
{
1024-
// Exit definitely dominates the latch since they are the same block.
1025-
// Fall through.
1026-
JITDUMP(" Loop exit is also the only backedge\n");
1027-
}
1028-
else
1029-
{
1030-
for (FlowEdge* backedge : loop->BackEdges())
1031-
{
1032-
// We know dom(x, y) => ancestor(x, y), so we can utilize the
1033-
// contrapositive !ancestor(x, y) => !dom(x, y) to avoid computing
1034-
// dominators in some cases.
1035-
if (!m_dfsTree->IsAncestor(exiting, backedge->getSourceBlock()))
1036-
{
1037-
JITDUMP(" No; exiting block " FMT_BB " is not an ancestor of backedge source " FMT_BB "\n",
1038-
exiting->bbNum, backedge->getSourceBlock()->bbNum);
1039-
return false;
1040-
}
1041-
1042-
if (m_domTree == nullptr)
1043-
{
1044-
m_domTree = FlowGraphDominatorTree::Build(m_dfsTree);
1045-
}
1046-
1047-
if (!m_domTree->Dominates(exiting, backedge->getSourceBlock()))
1048-
{
1049-
JITDUMP(" No; exiting block " FMT_BB " does not dominate backedge source " FMT_BB "\n", exiting->bbNum,
1050-
backedge->getSourceBlock()->bbNum);
1051-
return false;
1052-
}
1053-
}
1054-
}
1055-
10561056
// At this point we know that the single exit dominates all backedges.
10571057
JITDUMP(" All backedges are dominated by exiting block " FMT_BB "\n", exiting->bbNum);
10581058

@@ -1177,6 +1177,7 @@ PhaseStatus Compiler::optInductionVariables()
11771177

11781178
optReachableBitVecTraits = nullptr;
11791179
m_dfsTree = fgComputeDfs();
1180+
m_domTree = FlowGraphDominatorTree::Build(m_dfsTree);
11801181
m_loops = FlowGraphNaturalLoops::Find(m_dfsTree);
11811182

11821183
LoopLocalOccurrences loopLocals(m_loops);

0 commit comments

Comments
 (0)