Skip to content

Commit

Permalink
JIT: Add loop-aware RPO, and use as LSRA's block sequence (dotnet#108086
Browse files Browse the repository at this point in the history
)

Part of dotnet#107749, and follow-up to dotnet#107927. When computing a RPO of the flow graph, ensuring that the entirety of a loop body is visited before any of the loop's successors has the benefit of keeping the loop body compact in the traversal. This is certainly ideal when computing an initial block layout, and may be preferable for register allocation, too. Thus, this change formalizes loop-aware RPO creation as part of the flowgraph API surface, and uses it for LSRA's block sequence.
  • Loading branch information
amanasifkhalid authored and rzikm committed Oct 11, 2024
1 parent 98af00b commit cc640cd
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 3 deletions.
3 changes: 3 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -6286,6 +6286,9 @@ class Compiler
FlowGraphDfsTree* fgComputeDfs();
void fgInvalidateDfsTree();

template <typename TFunc>
void fgVisitBlocksInLoopAwareRPO(FlowGraphDfsTree* dfsTree, FlowGraphNaturalLoops* loops, TFunc func);

void fgRemoveReturnBlock(BasicBlock* block);

void fgConvertBBToThrowBB(BasicBlock* block);
Expand Down
69 changes: 69 additions & 0 deletions src/coreclr/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4974,6 +4974,75 @@ unsigned Compiler::fgRunDfs(VisitPreorder visitPreorder, VisitPostorder visitPos
return preOrderIndex;
}

//------------------------------------------------------------------------
// fgVisitBlocksInLoopAwareRPO: Visit the blocks in 'dfsTree' in reverse post-order,
// but ensure loop bodies are visited before loop successors.
//
// Type parameters:
// TFunc - Callback functor type
//
// Parameters:
// dfsTree - The DFS tree of the flow graph
// loops - A collection of the loops in the flow graph
// func - Callback functor that operates on a BasicBlock*
//
// Returns:
// A postorder traversal with compact loop bodies.
//
template <typename TFunc>
void Compiler::fgVisitBlocksInLoopAwareRPO(FlowGraphDfsTree* dfsTree, FlowGraphNaturalLoops* loops, TFunc func)
{
assert(dfsTree != nullptr);
assert(loops != nullptr);

// We will start by visiting blocks in reverse post-order.
// If we encounter the header of a loop, we will visit the loop's remaining blocks next
// to keep the loop body compact in the visitation order.
// We have to do this recursively to handle nested loops.
// Since the presence of loops implies we will try to visit some blocks more than once,
// we need to track visited blocks.
struct LoopAwareVisitor
{
BitVecTraits traits;
BitVec visitedBlocks;
FlowGraphNaturalLoops* loops;
TFunc func;

LoopAwareVisitor(FlowGraphDfsTree* dfsTree, FlowGraphNaturalLoops* loops, TFunc func)
: traits(dfsTree->PostOrderTraits())
, visitedBlocks(BitVecOps::MakeEmpty(&traits))
, loops(loops)
, func(func)
{
}

void VisitBlock(BasicBlock* block)
{
if (BitVecOps::TryAddElemD(&traits, visitedBlocks, block->bbPostorderNum))
{
func(block);

FlowGraphNaturalLoop* const loop = loops->GetLoopByHeader(block);
if (loop != nullptr)
{
loop->VisitLoopBlocksReversePostOrder([&](BasicBlock* block) {
VisitBlock(block);
return BasicBlockVisit::Continue;
});
}
}
}
};

LoopAwareVisitor visitor(dfsTree, loops, func);

for (unsigned i = dfsTree->GetPostOrderCount(); i != 0; i--)
{
BasicBlock* const block = dfsTree->GetPostOrder(i - 1);
visitor.VisitBlock(block);
}
}

//------------------------------------------------------------------------------
// FlowGraphNaturalLoop::VisitLoopBlocksReversePostOrder: Visit all of the
// loop's blocks in reverse post order.
Expand Down
26 changes: 23 additions & 3 deletions src/coreclr/jit/lsra.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -953,9 +953,29 @@ void LinearScan::setBlockSequence()

assert((blockSequence == nullptr) && (bbSeqCount == 0));
FlowGraphDfsTree* const dfsTree = compiler->fgComputeDfs</* useProfile */ true>();
blockSequence = dfsTree->GetPostOrder();
bbNumMaxBeforeResolution = compiler->fgBBNumMax;
blockInfo = new (compiler, CMK_LSRA) LsraBlockInfo[bbNumMaxBeforeResolution + 1];

if (compiler->opts.OptimizationEnabled() && dfsTree->HasCycle())
{
// Ensure loop bodies are compact in the visitation order
FlowGraphNaturalLoops* const loops = FlowGraphNaturalLoops::Find(dfsTree);
blockSequence = new (compiler, CMK_LSRA) BasicBlock*[compiler->fgBBcount];
unsigned index = dfsTree->GetPostOrderCount();

auto addToSequence = [this, &index](BasicBlock* block) {
assert(index != 0);
blockSequence[--index] = block;
};

compiler->fgVisitBlocksInLoopAwareRPO(dfsTree, loops, addToSequence);
}
else
{
// TODO: Just use lexical block order in MinOpts
blockSequence = dfsTree->GetPostOrder();
}

bbNumMaxBeforeResolution = compiler->fgBBNumMax;
blockInfo = new (compiler, CMK_LSRA) LsraBlockInfo[bbNumMaxBeforeResolution + 1];

// Flip the DFS traversal to get the reverse post-order traversal
// (this is the order in which blocks will be allocated)
Expand Down

0 comments on commit cc640cd

Please sign in to comment.