Skip to content

Commit 33d737a

Browse files
Fix problem with scaling general loop blocks; add dumpers (#99742)
* 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: #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]>
1 parent a32586a commit 33d737a

File tree

5 files changed

+93
-4
lines changed

5 files changed

+93
-4
lines changed

src/coreclr/jit/compiler.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -5895,6 +5895,10 @@ void Compiler::RecomputeFlowGraphAnnotations()
58955895
fgInvalidateDfsTree();
58965896
fgDfsBlocksAndRemove();
58975897
optFindLoops();
5898+
5899+
// Should we call this using the phase method:
5900+
// DoPhase(this, PHASE_SET_BLOCK_WEIGHTS, &Compiler::optSetBlockWeights);
5901+
// ? It could be called multiple times.
58985902
optSetBlockWeights();
58995903

59005904
if (m_domTree == nullptr)

src/coreclr/jit/compiler.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -1951,6 +1951,10 @@ class FlowGraphDfsTree
19511951
return m_hasCycle;
19521952
}
19531953

1954+
#ifdef DEBUG
1955+
void Dump() const;
1956+
#endif // DEBUG
1957+
19541958
bool Contains(BasicBlock* block) const;
19551959
bool IsAncestor(BasicBlock* ancestor, BasicBlock* descendant) const;
19561960
};
@@ -2230,7 +2234,7 @@ class FlowGraphNaturalLoops
22302234
return m_dfsTree;
22312235
}
22322236

2233-
size_t NumLoops()
2237+
size_t NumLoops() const
22342238
{
22352239
return m_loops.size();
22362240
}
@@ -2322,6 +2326,7 @@ class FlowGraphDominatorTree
23222326
}
23232327

23242328
static BasicBlock* IntersectDom(BasicBlock* block1, BasicBlock* block2);
2329+
23252330
public:
23262331
const FlowGraphDfsTree* GetDfsTree()
23272332
{
@@ -2355,6 +2360,10 @@ class BlockToNaturalLoopMap
23552360
FlowGraphNaturalLoop* GetLoop(BasicBlock* block);
23562361

23572362
static BlockToNaturalLoopMap* Build(FlowGraphNaturalLoops* loops);
2363+
2364+
#ifdef DEBUG
2365+
void Dump() const;
2366+
#endif // DEBUG
23582367
};
23592368

23602369
// Represents a data structure that can answer A -> B reachability queries in

src/coreclr/jit/fgopt.cpp

+20-2
Original file line numberDiff line numberDiff line change
@@ -5263,7 +5263,7 @@ PhaseStatus Compiler::fgDfsBlocksAndRemove()
52635263
#ifdef DEBUG
52645264
if (verbose)
52655265
{
5266-
printf("%u/%u blocks are unreachable and will be removed\n", fgBBcount - m_dfsTree->GetPostOrderCount(),
5266+
printf("%u/%u blocks are unreachable and will be removed:\n", fgBBcount - m_dfsTree->GetPostOrderCount(),
52675267
fgBBcount);
52685268
for (BasicBlock* block : Blocks())
52695269
{
@@ -5273,7 +5273,7 @@ PhaseStatus Compiler::fgDfsBlocksAndRemove()
52735273
}
52745274
}
52755275
}
5276-
#endif
5276+
#endif // DEBUG
52775277

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

5304+
#ifdef DEBUG
5305+
// Did we actually remove all the blocks we said we were going to?
5306+
if (verbose)
5307+
{
5308+
if (m_dfsTree->GetPostOrderCount() != fgBBcount)
5309+
{
5310+
printf("%u unreachable blocks were not removed:\n", fgBBcount - m_dfsTree->GetPostOrderCount());
5311+
for (BasicBlock* block : Blocks())
5312+
{
5313+
if (!m_dfsTree->Contains(block))
5314+
{
5315+
printf(" " FMT_BB "\n", block->bbNum);
5316+
}
5317+
}
5318+
}
5319+
}
5320+
#endif // DEBUG
5321+
53045322
status = PhaseStatus::MODIFIED_EVERYTHING;
53055323
}
53065324

src/coreclr/jit/flowgraph.cpp

+51
Original file line numberDiff line numberDiff line change
@@ -3908,6 +3908,26 @@ void Compiler::fgLclFldAssign(unsigned lclNum)
39083908
}
39093909
}
39103910

3911+
#ifdef DEBUG
3912+
3913+
//------------------------------------------------------------------------
3914+
// FlowGraphDfsTree::Dump: Dump a textual representation of the DFS tree.
3915+
//
3916+
void FlowGraphDfsTree::Dump() const
3917+
{
3918+
printf("DFS tree. %s.\n", HasCycle() ? "Has cycle" : "No cycle");
3919+
printf("PO RPO -> BB [pre, post]\n");
3920+
for (unsigned i = 0; i < GetPostOrderCount(); i++)
3921+
{
3922+
unsigned rpoNum = GetPostOrderCount() - i - 1;
3923+
BasicBlock* const block = GetPostOrder(i);
3924+
printf("%02u %02u -> " FMT_BB "[%u, %u]\n", i, rpoNum, block->bbNum, block->bbPreorderNum,
3925+
block->bbPostorderNum);
3926+
}
3927+
}
3928+
3929+
#endif // DEBUG
3930+
39113931
//------------------------------------------------------------------------
39123932
// FlowGraphDfsTree::Contains: Check if a block is contained in the DFS tree;
39133933
// i.e., if it is reachable.
@@ -6132,6 +6152,37 @@ BlockToNaturalLoopMap* BlockToNaturalLoopMap::Build(FlowGraphNaturalLoops* loops
61326152
return new (comp, CMK_Loops) BlockToNaturalLoopMap(loops, indices);
61336153
}
61346154

6155+
#ifdef DEBUG
6156+
6157+
//------------------------------------------------------------------------
6158+
// BlockToNaturalLoopMap::Dump: Dump a textual representation of the map.
6159+
//
6160+
void BlockToNaturalLoopMap::Dump() const
6161+
{
6162+
const FlowGraphDfsTree* dfs = m_loops->GetDfsTree();
6163+
unsigned blockCount = dfs->GetPostOrderCount();
6164+
6165+
printf("Block -> natural loop map: %u blocks\n", blockCount);
6166+
if (blockCount > 0)
6167+
{
6168+
printf("block : loop index\n");
6169+
for (unsigned i = 0; i < blockCount; i++)
6170+
{
6171+
if (m_indices[i] == UINT_MAX)
6172+
{
6173+
// Just leave the loop space empty if there is no enclosing loop
6174+
printf(FMT_BB " : \n", dfs->GetPostOrder(i)->bbNum);
6175+
}
6176+
else
6177+
{
6178+
printf(FMT_BB " : " FMT_LP "\n", dfs->GetPostOrder(i)->bbNum, m_indices[i]);
6179+
}
6180+
}
6181+
}
6182+
}
6183+
6184+
#endif // DEBUG
6185+
61356186
//------------------------------------------------------------------------
61366187
// BlockReachabilitySets::Build: Build the reachability sets.
61376188
//

src/coreclr/jit/optimizer.cpp

+8-1
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,13 @@ void Compiler::optScaleLoopBlocks(BasicBlock* begBlk, BasicBlock* endBlk)
246246
continue;
247247
}
248248

249+
// Don't change the block weight if it's unreachable.
250+
if (!m_reachabilitySets->GetDfsTree()->Contains(curBlk))
251+
{
252+
reportBlockWeight(curBlk, "; unchanged: unreachable");
253+
continue;
254+
}
255+
249256
// For curBlk to be part of a loop that starts at begBlk, curBlk must be reachable from begBlk and
250257
// (since this is a loop) begBlk must likewise be reachable from curBlk.
251258

@@ -2681,7 +2688,7 @@ void Compiler::optFindAndScaleGeneralLoopBlocks()
26812688
}
26822689

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

0 commit comments

Comments
 (0)