Skip to content

Commit

Permalink
JIT: Canonicalize loop exits (#98096)
Browse files Browse the repository at this point in the history
This adds another canonicalization requirement for loops during the
optimization phases, namely that all regular loop exit blocks have only
loop predecessors. This gives a natural place to insert IR that we want
to run only when we know the loop was entered.

Exceptional loop exit blocks can still have non-loop predecessors, so
these must still be accounted for by optimizations.
  • Loading branch information
jakobbotsch authored Feb 14, 2024
1 parent 0ce39a1 commit d464313
Show file tree
Hide file tree
Showing 6 changed files with 403 additions and 36 deletions.
2 changes: 1 addition & 1 deletion src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5036,7 +5036,7 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
}
}

optLoopsRequirePreHeaders = false;
optLoopsCanonical = false;

#ifdef DEBUG
DoPhase(this, PHASE_STRESS_SPLIT_TREE, &Compiler::StressSplitTree);
Expand Down
17 changes: 15 additions & 2 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2184,6 +2184,9 @@ class FlowGraphNaturalLoop
template<typename TFunc>
BasicBlockVisit VisitLoopBlocksLexical(TFunc func);

template<typename TFunc>
BasicBlockVisit VisitRegularExitBlocks(TFunc func);

BasicBlock* GetLexicallyTopMostBlock();
BasicBlock* GetLexicallyBottomMostBlock();

Expand Down Expand Up @@ -4976,7 +4979,11 @@ class Compiler
FlowGraphDominatorTree* m_domTree;
BlockReachabilitySets* m_reachabilitySets;

bool optLoopsRequirePreHeaders; // Do we require that all loops (in m_loops) have pre-headers?
// Do we require loops to be in canonical form? The canonical form ensures that:
// 1. All loops have preheaders (single entry blocks that always enter the loop)
// 2. All loop exits where bbIsHandlerBeg(exit) is false have only loop predecessors.
//
bool optLoopsCanonical;
unsigned optNumNaturalLoopsFound; // Number of natural loops found in the loop finding phase

bool fgBBVarSetsInited;
Expand Down Expand Up @@ -5914,7 +5921,7 @@ class Compiler

PhaseStatus fgCanonicalizeFirstBB();

void fgSetEHRegionForNewPreheader(BasicBlock* preheader);
void fgSetEHRegionForNewPreheaderOrExit(BasicBlock* preheader);

void fgUnreachableBlock(BasicBlock* block);

Expand Down Expand Up @@ -6793,13 +6800,19 @@ class Compiler

void optFindLoops();
bool optCanonicalizeLoops();

void optCompactLoops();
void optCompactLoop(FlowGraphNaturalLoop* loop);
BasicBlock* optFindLoopCompactionInsertionPoint(FlowGraphNaturalLoop* loop, BasicBlock* top);
BasicBlock* optTryAdvanceLoopCompactionInsertionPoint(FlowGraphNaturalLoop* loop, BasicBlock* insertionPoint, BasicBlock* top, BasicBlock* bottom);
bool optCreatePreheader(FlowGraphNaturalLoop* loop);
void optSetPreheaderWeight(FlowGraphNaturalLoop* loop, BasicBlock* preheader);

bool optCanonicalizeExits(FlowGraphNaturalLoop* loop);
bool optCanonicalizeExit(FlowGraphNaturalLoop* loop, BasicBlock* exit);
weight_t optEstimateEdgeLikelihood(BasicBlock* from, BasicBlock* to, bool* fromProfile);
void optSetExitWeight(FlowGraphNaturalLoop* loop, BasicBlock* exit);

PhaseStatus optCloneLoops();
void optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* context);
PhaseStatus optUnrollLoops(); // Unrolls loops (needs to have cost info)
Expand Down
54 changes: 54 additions & 0 deletions src/coreclr/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4983,6 +4983,60 @@ BasicBlockVisit FlowGraphNaturalLoop::VisitLoopBlocksLexical(TFunc func)
return BasicBlockVisit::Continue;
}

//------------------------------------------------------------------------------
// FlowGraphNaturalLoop::VisitRegularExitBlocks: Visit non-handler blocks that
// are outside the loop but that may have regular predecessors inside the loop.
//
// Type parameters:
// TFunc - Callback functor type
//
// Arguments:
// func - Callback functor that takes a BasicBlock* and returns a
// BasicBlockVisit.
//
// Returns:
// BasicBlockVisit that indicated whether the visit was aborted by the
// callback or whether all blocks were visited.
//
// Remarks:
// Note that no handler begins are visited by this function, even if they
// have regular predecessors inside the loop (for example, finally handlers
// can have regular BBJ_CALLFINALLY predecessors inside the loop). This
// choice is motivated by the fact that such handlers will also show up as
// exceptional exit blocks that must always be handled specially by client
// code regardless.
//
template <typename TFunc>
BasicBlockVisit FlowGraphNaturalLoop::VisitRegularExitBlocks(TFunc func)
{
Compiler* comp = m_dfsTree->GetCompiler();

BitVecTraits traits = m_dfsTree->PostOrderTraits();
BitVec visited(BitVecOps::MakeEmpty(&traits));

for (FlowEdge* edge : ExitEdges())
{
BasicBlockVisit result = edge->getSourceBlock()->VisitRegularSuccs(comp, [&](BasicBlock* succ) {
assert(m_dfsTree->Contains(succ));
if (!comp->bbIsHandlerBeg(succ) && !ContainsBlock(succ) &&
BitVecOps::TryAddElemD(&traits, visited, succ->bbPostorderNum) &&
(func(succ) == BasicBlockVisit::Abort))
{
return BasicBlockVisit::Abort;
}

return BasicBlockVisit::Continue;
});

if (result == BasicBlockVisit::Abort)
{
return BasicBlockVisit::Abort;
}
}

return BasicBlockVisit::Continue;
}

/*****************************************************************************/
#endif //_COMPILER_HPP_
/*****************************************************************************/
10 changes: 9 additions & 1 deletion src/coreclr/jit/fgdiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4723,12 +4723,20 @@ void Compiler::fgDebugCheckLoops()
{
return;
}
if (optLoopsRequirePreHeaders)
if (optLoopsCanonical)
{
for (FlowGraphNaturalLoop* loop : m_loops->InReversePostOrder())
{
assert(loop->EntryEdges().size() == 1);
assert(loop->EntryEdge(0)->getSourceBlock()->KindIs(BBJ_ALWAYS));

loop->VisitRegularExitBlocks([=](BasicBlock* exit) {
for (BasicBlock* pred : exit->PredBlocks())
{
assert(loop->ContainsBlock(pred));
}
return BasicBlockVisit::Continue;
});
}
}
}
Expand Down
11 changes: 9 additions & 2 deletions src/coreclr/jit/loopcloning.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2969,11 +2969,18 @@ PhaseStatus Compiler::optCloneLoops()

if (optLoopsCloned > 0)
{
fgRenumberBlocks();

fgInvalidateDfsTree();
m_dfsTree = fgComputeDfs();
m_loops = FlowGraphNaturalLoops::Find(m_dfsTree);

if (optCanonicalizeLoops())
{
fgInvalidateDfsTree();
m_dfsTree = fgComputeDfs();
m_loops = FlowGraphNaturalLoops::Find(m_dfsTree);
}

fgRenumberBlocks();
}

#ifdef DEBUG
Expand Down
Loading

0 comments on commit d464313

Please sign in to comment.