Skip to content

Commit d2d2281

Browse files
authored
Perform reachability analysis before codegen (#66967)
* Add size * Remove unreachable blocks * Ensure epoch * Add a condition in asserT * Add test case * Another shorter implementation * Add DeadBlock elimination * fgDomsComputed adjusted * Review comment
1 parent 670262a commit d2d2281

File tree

8 files changed

+242
-35
lines changed

8 files changed

+242
-35
lines changed

src/coreclr/jit/compiler.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4942,6 +4942,9 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
49424942
}
49434943
}
49444944

4945+
// Remove dead blocks
4946+
DoPhase(this, PHASE_REMOVE_DEAD_BLOCKS, &Compiler::fgRemoveDeadBlocks);
4947+
49454948
// Insert GC Polls
49464949
DoPhase(this, PHASE_INSERT_GC_POLLS, &Compiler::fgInsertGCPolls);
49474950

src/coreclr/jit/compiler.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5197,7 +5197,6 @@ class Compiler
51975197
bool fgHaveValidEdgeWeights; // true if we were successful in computing all of the edge weights
51985198
bool fgSlopUsedInEdgeWeights; // true if their was some slop used when computing the edge weights
51995199
bool fgRangeUsedInEdgeWeights; // true if some of the edgeWeight are expressed in Min..Max form
5200-
bool fgNeedsUpdateFlowGraph; // true if we need to run fgUpdateFlowGraph
52015200
weight_t fgCalledCount; // count of the number of times this method was called
52025201
// This is derived from the profile data
52035202
// or is BB_UNITY_WEIGHT when we don't have profile data
@@ -5775,10 +5774,14 @@ class Compiler
57755774

57765775
void fgComputeEnterBlocksSet(); // Compute the set of entry blocks, 'fgEnterBlks'.
57775776

5778-
bool fgRemoveUnreachableBlocks(); // Remove blocks determined to be unreachable by the bbReach sets.
5777+
// Remove blocks determined to be unreachable by the 'canRemoveBlock'.
5778+
template <typename CanRemoveBlockBody>
5779+
bool fgRemoveUnreachableBlocks(CanRemoveBlockBody canRemoveBlock);
57795780

57805781
void fgComputeReachability(); // Perform flow graph node reachability analysis.
57815782

5783+
void fgRemoveDeadBlocks(); // Identify and remove dead blocks.
5784+
57825785
BasicBlock* fgIntersectDom(BasicBlock* a, BasicBlock* b); // Intersect two immediate dominator sets.
57835786

57845787
void fgDfsInvPostOrder(); // In order to compute dominance using fgIntersectDom, the flow graph nodes must be

src/coreclr/jit/compphases.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ CompPhaseNameMacro(PHASE_OPTIMIZE_BRANCHES, "Redundant branch opts",
8484
CompPhaseNameMacro(PHASE_ASSERTION_PROP_MAIN, "Assertion prop", "AST-PROP", false, -1, false)
8585
CompPhaseNameMacro(PHASE_OPT_UPDATE_FLOW_GRAPH, "Update flow graph opt pass", "UPD-FG-O", false, -1, false)
8686
CompPhaseNameMacro(PHASE_COMPUTE_EDGE_WEIGHTS2, "Compute edge weights (2, false)","EDG-WGT2", false, -1, false)
87+
CompPhaseNameMacro(PHASE_REMOVE_DEAD_BLOCKS, "Remove dead blocks", "DEAD-BLK", false, -1, false)
8788
CompPhaseNameMacro(PHASE_INSERT_GC_POLLS, "Insert GC Polls", "GC-POLLS", false, -1, true)
8889
CompPhaseNameMacro(PHASE_DETERMINE_FIRST_COLD_BLOCK, "Determine first cold block", "COLD-BLK", false, -1, true)
8990
CompPhaseNameMacro(PHASE_RATIONALIZE, "Rationalize IR", "RAT", false, -1, false)

src/coreclr/jit/emit.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6556,7 +6556,8 @@ unsigned emitter::emitEndCodeGen(Compiler* comp,
65566556
#ifdef DEBUG
65576557
if (emitComp->opts.disAsm || emitComp->verbose)
65586558
{
6559-
printf("\t\t\t\t\t\t;; bbWeight=%s PerfScore %.2f", refCntWtd2str(ig->igWeight), ig->igPerfScore);
6559+
printf("\t\t\t\t\t\t;; size=%d bbWeight=%s PerfScore %.2f", ig->igSize, refCntWtd2str(ig->igWeight),
6560+
ig->igPerfScore);
65606561
}
65616562
*instrCount += ig->igInsCnt;
65626563
#endif // DEBUG

src/coreclr/jit/fgbasic.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ void Compiler::fgInit()
3131
fgHaveValidEdgeWeights = false;
3232
fgSlopUsedInEdgeWeights = false;
3333
fgRangeUsedInEdgeWeights = true;
34-
fgNeedsUpdateFlowGraph = false;
3534
fgCalledCount = BB_ZERO_WEIGHT;
3635

3736
/* We haven't yet computed the dominator sets */

src/coreclr/jit/fgopt.cpp

Lines changed: 134 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -367,17 +367,14 @@ void Compiler::fgComputeEnterBlocksSet()
367367
BlockSetOps::AddElemD(this, fgEnterBlks, fgFirstBB->bbNum);
368368
assert(fgFirstBB->bbNum == 1);
369369

370-
if (compHndBBtabCount > 0)
370+
/* Also 'or' in the handler basic blocks */
371+
for (EHblkDsc* const HBtab : EHClauses(this))
371372
{
372-
/* Also 'or' in the handler basic blocks */
373-
for (EHblkDsc* const HBtab : EHClauses(this))
373+
if (HBtab->HasFilter())
374374
{
375-
if (HBtab->HasFilter())
376-
{
377-
BlockSetOps::AddElemD(this, fgEnterBlks, HBtab->ebdFilter->bbNum);
378-
}
379-
BlockSetOps::AddElemD(this, fgEnterBlks, HBtab->ebdHndBeg->bbNum);
375+
BlockSetOps::AddElemD(this, fgEnterBlks, HBtab->ebdFilter->bbNum);
380376
}
377+
BlockSetOps::AddElemD(this, fgEnterBlks, HBtab->ebdHndBeg->bbNum);
381378
}
382379

383380
#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
@@ -420,16 +417,28 @@ void Compiler::fgComputeEnterBlocksSet()
420417
// are converted to `throw` blocks. Internal throw helper blocks and the single return block (if any)
421418
// are never considered unreachable.
422419
//
420+
// Arguments:
421+
// canRemoveBlock - Method that determines if a block can be removed or not. In earlier phases, it
422+
// relies on the reachability set. During final phase, it depends on the DFS walk of the flowgraph
423+
// and considering blocks that are not visited as unreachable.
424+
//
423425
// Return Value:
424426
// Return true if changes were made that may cause additional blocks to be removable.
425427
//
426-
// Assumptions:
427-
// The reachability sets must be computed and valid.
428+
// Notes:
429+
// Unreachable blocks removal phase happens twice.
430+
//
431+
// During early phases RecomputeLoopInfo, the logic to determine if a block is reachable
432+
// or not is based on the reachability sets, and hence it must be computed and valid.
433+
//
434+
// During late phase, all the reachable blocks from fgFirstBB are traversed and everything
435+
// else are marked as unreachable (with exceptions of handler/filter blocks and BBJ_ALWAYS
436+
// blocks in Arm). As such, it is not dependent on the validity of reachability sets.
428437
//
429-
bool Compiler::fgRemoveUnreachableBlocks()
438+
template <typename CanRemoveBlockBody>
439+
bool Compiler::fgRemoveUnreachableBlocks(CanRemoveBlockBody canRemoveBlock)
430440
{
431441
assert(!fgCheapPredsValid);
432-
assert(fgReachabilitySetsValid);
433442

434443
bool hasUnreachableBlocks = false;
435444
bool changed = false;
@@ -451,18 +460,10 @@ bool Compiler::fgRemoveUnreachableBlocks()
451460
}
452461
else
453462
{
454-
// If any of the entry blocks can reach this block, then we skip it.
455-
if (!BlockSetOps::IsEmptyIntersection(this, fgEnterBlks, block->bbReach))
456-
{
457-
continue;
458-
}
459-
460-
#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
461-
if (!BlockSetOps::IsEmptyIntersection(this, fgAlwaysBlks, block->bbReach))
463+
if (!canRemoveBlock(block))
462464
{
463465
continue;
464466
}
465-
#endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
466467
}
467468

468469
// Remove all the code for the block
@@ -572,6 +573,24 @@ void Compiler::fgComputeReachability()
572573
// The dominator algorithm expects that all blocks can be reached from the fgEnterBlks set.
573574
unsigned passNum = 1;
574575
bool changed;
576+
577+
auto canRemoveBlock = [&](BasicBlock* block) -> bool {
578+
// If any of the entry blocks can reach this block, then we skip it.
579+
if (!BlockSetOps::IsEmptyIntersection(this, fgEnterBlks, block->bbReach))
580+
{
581+
return false;
582+
}
583+
584+
#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
585+
if (!BlockSetOps::IsEmptyIntersection(this, fgAlwaysBlks, block->bbReach))
586+
{
587+
return false;
588+
}
589+
#endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
590+
591+
return true;
592+
};
593+
575594
do
576595
{
577596
// Just to be paranoid, avoid infinite loops; fall back to minopts.
@@ -601,7 +620,7 @@ void Compiler::fgComputeReachability()
601620
// Use reachability information to delete unreachable blocks.
602621
//
603622

604-
changed = fgRemoveUnreachableBlocks();
623+
changed = fgRemoveUnreachableBlocks(canRemoveBlock);
605624

606625
} while (changed);
607626

@@ -624,6 +643,95 @@ void Compiler::fgComputeReachability()
624643
fgComputeDoms();
625644
}
626645

646+
//------------------------------------------------------------------------
647+
// fgRemoveDeadBlocks: Identify all the unreachable blocks and remove them.
648+
// Handler and filter blocks are considered as reachable and hence won't
649+
// be removed. For Arm32, do not remove BBJ_ALWAYS block of
650+
// BBJ_CALLFINALLY/BBJ_ALWAYS pair.
651+
//
652+
void Compiler::fgRemoveDeadBlocks()
653+
{
654+
jitstd::list<BasicBlock*> worklist(jitstd::allocator<void>(getAllocator(CMK_Reachability)));
655+
worklist.push_back(fgFirstBB);
656+
657+
// Do not remove handler blocks
658+
for (EHblkDsc* const HBtab : EHClauses(this))
659+
{
660+
if (HBtab->HasFilter())
661+
{
662+
worklist.push_back(HBtab->ebdFilter);
663+
}
664+
worklist.push_back(HBtab->ebdHndBeg);
665+
}
666+
667+
#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
668+
// For ARM code, prevent creating retless calls by adding the BBJ_ALWAYS to the "fgAlwaysBlks" list.
669+
for (BasicBlock* const block : Blocks())
670+
{
671+
if (block->bbJumpKind == BBJ_CALLFINALLY)
672+
{
673+
assert(block->isBBCallAlwaysPair());
674+
675+
// Don't remove the BBJ_ALWAYS block that is only here for the unwinder.
676+
worklist.push_back(block->bbNext);
677+
}
678+
}
679+
#endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
680+
681+
unsigned prevFgCurBBEpoch = fgCurBBEpoch;
682+
EnsureBasicBlockEpoch();
683+
684+
if (prevFgCurBBEpoch != fgCurBBEpoch)
685+
{
686+
// If Epoch has changed, reset the doms computed as well because
687+
// in future, during insert gc polls or lowering, when we compact
688+
// blocks during flowgraph update, it might propagate the invalid
689+
// bbReach as well (although Epoch adjustment resets fgReachabilitySetsValid).
690+
fgDomsComputed = false;
691+
}
692+
693+
BlockSet visitedBlocks(BlockSetOps::MakeEmpty(this));
694+
695+
// Visit all the reachable blocks, everything else can be removed
696+
while (!worklist.empty())
697+
{
698+
BasicBlock* block = *(worklist.begin());
699+
worklist.pop_front();
700+
701+
if (BlockSetOps::IsMember(this, visitedBlocks, block->bbNum))
702+
{
703+
continue;
704+
}
705+
706+
BlockSetOps::AddElemD(this, visitedBlocks, block->bbNum);
707+
708+
for (BasicBlock* succ : block->Succs(this))
709+
{
710+
worklist.push_back(succ);
711+
}
712+
}
713+
714+
// A block is unreachable if no path was found from
715+
// any of the fgFirstBB, handler, filter or BBJ_ALWAYS (Arm) blocks.
716+
auto isBlockRemovable = [&](BasicBlock* block) -> bool {
717+
return !BlockSetOps::IsMember(this, visitedBlocks, block->bbNum);
718+
};
719+
720+
bool changed = fgRemoveUnreachableBlocks(isBlockRemovable);
721+
722+
#ifdef DEBUG
723+
if (verbose && changed)
724+
{
725+
printf("\nAfter dead block removal:\n");
726+
fgDispBasicBlocks(verboseTrees);
727+
printf("\n");
728+
}
729+
730+
fgVerifyHandlerTab();
731+
fgDebugCheckBBlist(false);
732+
#endif // DEBUG
733+
}
734+
627735
//-------------------------------------------------------------
628736
// fgDfsInvPostOrder: Helper function for computing dominance information.
629737
//
@@ -2163,6 +2271,7 @@ void Compiler::fgCompactBlocks(BasicBlock* block, BasicBlock* bNext)
21632271
//
21642272
if (fgDomsComputed && (block->bbNum > fgDomBBcount))
21652273
{
2274+
assert(fgReachabilitySetsValid);
21662275
BlockSetOps::Assign(this, block->bbReach, bNext->bbReach);
21672276
BlockSetOps::ClearD(this, bNext->bbReach);
21682277

@@ -2512,8 +2621,7 @@ bool Compiler::fgOptimizeBranchToEmptyUnconditional(BasicBlock* block, BasicBloc
25122621
//
25132622
if (fgIsUsingProfileWeights() && !fgEdgeWeightsComputed)
25142623
{
2515-
fgNeedsUpdateFlowGraph = true;
2516-
optimizeJump = false;
2624+
optimizeJump = false;
25172625
}
25182626

25192627
if (optimizeJump)
@@ -2875,8 +2983,7 @@ bool Compiler::fgOptimizeSwitchBranches(BasicBlock* block)
28752983
//
28762984
if (fgIsUsingProfileWeights() && !fgEdgeWeightsComputed)
28772985
{
2878-
fgNeedsUpdateFlowGraph = true;
2879-
optimizeJump = false;
2986+
optimizeJump = false;
28802987
}
28812988

28822989
if (optimizeJump)
@@ -5683,7 +5790,6 @@ bool Compiler::fgReorderBlocks()
56835790

56845791
if (changed)
56855792
{
5686-
fgNeedsUpdateFlowGraph = true;
56875793
#if DEBUG
56885794
// Make sure that the predecessor lists are accurate
56895795
if (expensiveDebugCheckLevel >= 2)
@@ -5908,8 +6014,7 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication)
59086014
// because we can't allow fall-through into the cold region.
59096015
if (!fgEdgeWeightsComputed || fgInDifferentRegions(block, bDest))
59106016
{
5911-
fgNeedsUpdateFlowGraph = true;
5912-
optimizeJump = false;
6017+
optimizeJump = false;
59136018
}
59146019
}
59156020

@@ -6194,8 +6299,6 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication)
61946299
}
61956300
} while (change);
61966301

6197-
fgNeedsUpdateFlowGraph = false;
6198-
61996302
#ifdef DEBUG
62006303
if (verbose && modified)
62016304
{

0 commit comments

Comments
 (0)