@@ -367,17 +367,14 @@ void Compiler::fgComputeEnterBlocksSet()
367
367
BlockSetOps::AddElemD (this , fgEnterBlks, fgFirstBB->bbNum );
368
368
assert (fgFirstBB->bbNum == 1 );
369
369
370
- if (compHndBBtabCount > 0 )
370
+ /* Also 'or' in the handler basic blocks */
371
+ for (EHblkDsc* const HBtab : EHClauses (this ))
371
372
{
372
- /* Also 'or' in the handler basic blocks */
373
- for (EHblkDsc* const HBtab : EHClauses (this ))
373
+ if (HBtab->HasFilter ())
374
374
{
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 );
380
376
}
377
+ BlockSetOps::AddElemD (this , fgEnterBlks, HBtab->ebdHndBeg ->bbNum );
381
378
}
382
379
383
380
#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
@@ -420,16 +417,28 @@ void Compiler::fgComputeEnterBlocksSet()
420
417
// are converted to `throw` blocks. Internal throw helper blocks and the single return block (if any)
421
418
// are never considered unreachable.
422
419
//
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
+ //
423
425
// Return Value:
424
426
// Return true if changes were made that may cause additional blocks to be removable.
425
427
//
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.
428
437
//
429
- bool Compiler::fgRemoveUnreachableBlocks ()
438
+ template <typename CanRemoveBlockBody>
439
+ bool Compiler::fgRemoveUnreachableBlocks (CanRemoveBlockBody canRemoveBlock)
430
440
{
431
441
assert (!fgCheapPredsValid);
432
- assert (fgReachabilitySetsValid);
433
442
434
443
bool hasUnreachableBlocks = false ;
435
444
bool changed = false ;
@@ -451,18 +460,10 @@ bool Compiler::fgRemoveUnreachableBlocks()
451
460
}
452
461
else
453
462
{
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))
462
464
{
463
465
continue ;
464
466
}
465
- #endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
466
467
}
467
468
468
469
// Remove all the code for the block
@@ -572,6 +573,24 @@ void Compiler::fgComputeReachability()
572
573
// The dominator algorithm expects that all blocks can be reached from the fgEnterBlks set.
573
574
unsigned passNum = 1 ;
574
575
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
+
575
594
do
576
595
{
577
596
// Just to be paranoid, avoid infinite loops; fall back to minopts.
@@ -601,7 +620,7 @@ void Compiler::fgComputeReachability()
601
620
// Use reachability information to delete unreachable blocks.
602
621
//
603
622
604
- changed = fgRemoveUnreachableBlocks ();
623
+ changed = fgRemoveUnreachableBlocks (canRemoveBlock );
605
624
606
625
} while (changed);
607
626
@@ -624,6 +643,95 @@ void Compiler::fgComputeReachability()
624
643
fgComputeDoms ();
625
644
}
626
645
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 (" \n After dead block removal:\n " );
726
+ fgDispBasicBlocks (verboseTrees);
727
+ printf (" \n " );
728
+ }
729
+
730
+ fgVerifyHandlerTab ();
731
+ fgDebugCheckBBlist (false );
732
+ #endif // DEBUG
733
+ }
734
+
627
735
// -------------------------------------------------------------
628
736
// fgDfsInvPostOrder: Helper function for computing dominance information.
629
737
//
@@ -2163,6 +2271,7 @@ void Compiler::fgCompactBlocks(BasicBlock* block, BasicBlock* bNext)
2163
2271
//
2164
2272
if (fgDomsComputed && (block->bbNum > fgDomBBcount))
2165
2273
{
2274
+ assert (fgReachabilitySetsValid);
2166
2275
BlockSetOps::Assign (this , block->bbReach , bNext->bbReach );
2167
2276
BlockSetOps::ClearD (this , bNext->bbReach );
2168
2277
@@ -2512,8 +2621,7 @@ bool Compiler::fgOptimizeBranchToEmptyUnconditional(BasicBlock* block, BasicBloc
2512
2621
//
2513
2622
if (fgIsUsingProfileWeights () && !fgEdgeWeightsComputed)
2514
2623
{
2515
- fgNeedsUpdateFlowGraph = true ;
2516
- optimizeJump = false ;
2624
+ optimizeJump = false ;
2517
2625
}
2518
2626
2519
2627
if (optimizeJump)
@@ -2875,8 +2983,7 @@ bool Compiler::fgOptimizeSwitchBranches(BasicBlock* block)
2875
2983
//
2876
2984
if (fgIsUsingProfileWeights () && !fgEdgeWeightsComputed)
2877
2985
{
2878
- fgNeedsUpdateFlowGraph = true ;
2879
- optimizeJump = false ;
2986
+ optimizeJump = false ;
2880
2987
}
2881
2988
2882
2989
if (optimizeJump)
@@ -5683,7 +5790,6 @@ bool Compiler::fgReorderBlocks()
5683
5790
5684
5791
if (changed)
5685
5792
{
5686
- fgNeedsUpdateFlowGraph = true ;
5687
5793
#if DEBUG
5688
5794
// Make sure that the predecessor lists are accurate
5689
5795
if (expensiveDebugCheckLevel >= 2 )
@@ -5908,8 +6014,7 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication)
5908
6014
// because we can't allow fall-through into the cold region.
5909
6015
if (!fgEdgeWeightsComputed || fgInDifferentRegions (block, bDest))
5910
6016
{
5911
- fgNeedsUpdateFlowGraph = true ;
5912
- optimizeJump = false ;
6017
+ optimizeJump = false ;
5913
6018
}
5914
6019
}
5915
6020
@@ -6194,8 +6299,6 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication)
6194
6299
}
6195
6300
} while (change);
6196
6301
6197
- fgNeedsUpdateFlowGraph = false ;
6198
-
6199
6302
#ifdef DEBUG
6200
6303
if (verbose && modified)
6201
6304
{
0 commit comments