Skip to content

Commit

Permalink
Fix problem with scaling general loop blocks; add dumpers (dotnet#99742)
Browse files Browse the repository at this point in the history
* Fix problem with scaling general loop blocks; add dumpers

1. Fix a problem where scaling block weights of a range of
basic blocks that compose a general loop could encounter a
block that is unreachable. This can happen for various kinds
of blocks that the JIT doesn't like to remove even if unreachable.
Simply skip scaling those blocks.

2. Add dumpers for `FlowGraphDfsTree` and `BlockToNaturalLoopMap`.
These can be called from the debugger (perhaps they should be called
to output these things to the JitDump).

3. Update `fgDfsBlocksAndRemove()` to dump any blocks that it did
not remove, even if they were unreachable.

This was found as part of fixing JitOptRepeat: dotnet#94250

* Update src/coreclr/jit/flowgraph.cpp

Co-authored-by: Jakob Botsch Nielsen <[email protected]>

* Update dump header

---------

Co-authored-by: Jakob Botsch Nielsen <[email protected]>
  • Loading branch information
BruceForstall and jakobbotsch authored Mar 14, 2024
1 parent a32586a commit 33d737a
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 4 deletions.
4 changes: 4 additions & 0 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5895,6 +5895,10 @@ void Compiler::RecomputeFlowGraphAnnotations()
fgInvalidateDfsTree();
fgDfsBlocksAndRemove();
optFindLoops();

// Should we call this using the phase method:
// DoPhase(this, PHASE_SET_BLOCK_WEIGHTS, &Compiler::optSetBlockWeights);
// ? It could be called multiple times.
optSetBlockWeights();

if (m_domTree == nullptr)
Expand Down
11 changes: 10 additions & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -1951,6 +1951,10 @@ class FlowGraphDfsTree
return m_hasCycle;
}

#ifdef DEBUG
void Dump() const;
#endif // DEBUG

bool Contains(BasicBlock* block) const;
bool IsAncestor(BasicBlock* ancestor, BasicBlock* descendant) const;
};
Expand Down Expand Up @@ -2230,7 +2234,7 @@ class FlowGraphNaturalLoops
return m_dfsTree;
}

size_t NumLoops()
size_t NumLoops() const
{
return m_loops.size();
}
Expand Down Expand Up @@ -2322,6 +2326,7 @@ class FlowGraphDominatorTree
}

static BasicBlock* IntersectDom(BasicBlock* block1, BasicBlock* block2);

public:
const FlowGraphDfsTree* GetDfsTree()
{
Expand Down Expand Up @@ -2355,6 +2360,10 @@ class BlockToNaturalLoopMap
FlowGraphNaturalLoop* GetLoop(BasicBlock* block);

static BlockToNaturalLoopMap* Build(FlowGraphNaturalLoops* loops);

#ifdef DEBUG
void Dump() const;
#endif // DEBUG
};

// Represents a data structure that can answer A -> B reachability queries in
Expand Down
22 changes: 20 additions & 2 deletions src/coreclr/jit/fgopt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5263,7 +5263,7 @@ PhaseStatus Compiler::fgDfsBlocksAndRemove()
#ifdef DEBUG
if (verbose)
{
printf("%u/%u blocks are unreachable and will be removed\n", fgBBcount - m_dfsTree->GetPostOrderCount(),
printf("%u/%u blocks are unreachable and will be removed:\n", fgBBcount - m_dfsTree->GetPostOrderCount(),
fgBBcount);
for (BasicBlock* block : Blocks())
{
Expand All @@ -5273,7 +5273,7 @@ PhaseStatus Compiler::fgDfsBlocksAndRemove()
}
}
}
#endif
#endif // DEBUG

// The DFS we run is not precise around call-finally, so
// `fgRemoveUnreachableBlocks` can expose newly unreachable blocks
Expand Down Expand Up @@ -5301,6 +5301,24 @@ PhaseStatus Compiler::fgDfsBlocksAndRemove()
m_dfsTree = fgComputeDfs();
}

#ifdef DEBUG
// Did we actually remove all the blocks we said we were going to?
if (verbose)
{
if (m_dfsTree->GetPostOrderCount() != fgBBcount)
{
printf("%u unreachable blocks were not removed:\n", fgBBcount - m_dfsTree->GetPostOrderCount());
for (BasicBlock* block : Blocks())
{
if (!m_dfsTree->Contains(block))
{
printf(" " FMT_BB "\n", block->bbNum);
}
}
}
}
#endif // DEBUG

status = PhaseStatus::MODIFIED_EVERYTHING;
}

Expand Down
51 changes: 51 additions & 0 deletions src/coreclr/jit/flowgraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3908,6 +3908,26 @@ void Compiler::fgLclFldAssign(unsigned lclNum)
}
}

#ifdef DEBUG

//------------------------------------------------------------------------
// FlowGraphDfsTree::Dump: Dump a textual representation of the DFS tree.
//
void FlowGraphDfsTree::Dump() const
{
printf("DFS tree. %s.\n", HasCycle() ? "Has cycle" : "No cycle");
printf("PO RPO -> BB [pre, post]\n");
for (unsigned i = 0; i < GetPostOrderCount(); i++)
{
unsigned rpoNum = GetPostOrderCount() - i - 1;
BasicBlock* const block = GetPostOrder(i);
printf("%02u %02u -> " FMT_BB "[%u, %u]\n", i, rpoNum, block->bbNum, block->bbPreorderNum,
block->bbPostorderNum);
}
}

#endif // DEBUG

//------------------------------------------------------------------------
// FlowGraphDfsTree::Contains: Check if a block is contained in the DFS tree;
// i.e., if it is reachable.
Expand Down Expand Up @@ -6132,6 +6152,37 @@ BlockToNaturalLoopMap* BlockToNaturalLoopMap::Build(FlowGraphNaturalLoops* loops
return new (comp, CMK_Loops) BlockToNaturalLoopMap(loops, indices);
}

#ifdef DEBUG

//------------------------------------------------------------------------
// BlockToNaturalLoopMap::Dump: Dump a textual representation of the map.
//
void BlockToNaturalLoopMap::Dump() const
{
const FlowGraphDfsTree* dfs = m_loops->GetDfsTree();
unsigned blockCount = dfs->GetPostOrderCount();

printf("Block -> natural loop map: %u blocks\n", blockCount);
if (blockCount > 0)
{
printf("block : loop index\n");
for (unsigned i = 0; i < blockCount; i++)
{
if (m_indices[i] == UINT_MAX)
{
// Just leave the loop space empty if there is no enclosing loop
printf(FMT_BB " : \n", dfs->GetPostOrder(i)->bbNum);
}
else
{
printf(FMT_BB " : " FMT_LP "\n", dfs->GetPostOrder(i)->bbNum, m_indices[i]);
}
}
}
}

#endif // DEBUG

//------------------------------------------------------------------------
// BlockReachabilitySets::Build: Build the reachability sets.
//
Expand Down
9 changes: 8 additions & 1 deletion src/coreclr/jit/optimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,13 @@ void Compiler::optScaleLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk)
continue;
}

// Don't change the block weight if it's unreachable.
if (!m_reachabilitySets->GetDfsTree()->Contains(curBlk))
{
reportBlockWeight(curBlk, "; unchanged: unreachable");
continue;
}

// 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.

Expand Down Expand Up @@ -2681,7 +2688,7 @@ void Compiler::optFindAndScaleGeneralLoopBlocks()
}

//-----------------------------------------------------------------------------
// optFindLoops: find loops in the function.
// optFindLoopsPhase: find loops in the function.
//
// The JIT recognizes two types of loops in a function: natural loops and "general" (or "unnatural") loops.
// Natural loops are those which get added to Compiler::m_loops. Most downstream optimizations require
Expand Down

0 comments on commit 33d737a

Please sign in to comment.