Skip to content

Commit d7c8198

Browse files
Improve EH dead code elimination (#93428)
* Improve EH dead code elimination Update `fgRemoveDeadBlocks()` to only treat EH handlers as reachable if their corresponding `try` block entry is reachable. This causes more dead code to be eliminated in handlers that are not reachable. There is a little more work to do to completely clean these up: 1. If a `try` is not reachable, we should get rid of its entire EH table entry. Currently, its block is generated as `int3` (on xarch). 2. Even though the handler is not reachable, we still generate the prolog followed by `int3` for the body. Contributes to #82335 * Fix BBJ_EHFILTERRET handling in fgRemoveBlockAsPred fgRemoveBlockAsPred had special handling to not decrease the bbRefs count of the filter-handler targeted by BBJ_EHFILTERRET. This is incorrect, as the filter-handler already has an extra "beginning of handler" extra ref count. Possibly this code was never invoked before as filters were not previously removed. Also, fix the JitDump output of the filter ret block in block dumps. * Put back arm32 BBJ_CALLFINALLY/BBJ_ALWAYS pair check
1 parent 92907e2 commit d7c8198

File tree

6 files changed

+58
-41
lines changed

6 files changed

+58
-41
lines changed

src/coreclr/jit/block.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ void BasicBlock::dspJumpKind()
637637
break;
638638

639639
case BBJ_EHFILTERRET:
640-
printf(" (fltret)");
640+
printf(" -> " FMT_BB " (fltret)", bbJumpDest->bbNum);
641641
break;
642642

643643
case BBJ_EHCATCHRET:

src/coreclr/jit/clrjit.natvis

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Documentation for VS debugger format specifiers: https://docs.microsoft.com/en-u
2121
</Type>
2222

2323
<Type Name="BasicBlock">
24-
<DisplayString Condition="bbJumpKind==BBJ_COND || bbJumpKind==BBJ_ALWAYS || bbJumpKind==BBJ_LEAVE || bbJumpKind==BBJ_EHCATCHRET || bbJumpKind==BBJ_CALLFINALLY">BB{bbNum,d}->BB{bbJumpDest->bbNum,d}; {bbJumpKind,en}</DisplayString>
24+
<DisplayString Condition="bbJumpKind==BBJ_COND || bbJumpKind==BBJ_ALWAYS || bbJumpKind==BBJ_LEAVE || bbJumpKind==BBJ_EHCATCHRET || bbJumpKind==BBJ_CALLFINALLY || bbJumpKind==BBJ_EHFILTERRET">BB{bbNum,d}->BB{bbJumpDest->bbNum,d}; {bbJumpKind,en}</DisplayString>
2525
<DisplayString Condition="bbJumpKind==BBJ_SWITCH">BB{bbNum,d}; {bbJumpKind,en}; {bbJumpSwt->bbsCount} cases</DisplayString>
2626
<DisplayString Condition="bbJumpKind==BBJ_EHFINALLYRET">BB{bbNum,d}; {bbJumpKind,en}; {bbJumpEhf->bbeCount} succs</DisplayString>
2727
<DisplayString>BB{bbNum,d}; {bbJumpKind,en}</DisplayString>

src/coreclr/jit/fgbasic.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ bool Compiler::fgEnsureFirstBBisScratch()
268268
}
269269

270270
// The first block has an implicit ref count which we must
271-
// remove. Note the ref count could be greater that one, if
271+
// remove. Note the ref count could be greater than one, if
272272
// the first block is not scratch and is targeted by a
273273
// branch.
274274
assert(fgFirstBB->bbRefs >= 1);

src/coreclr/jit/fgdiagnostic.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2077,7 +2077,8 @@ void Compiler::fgTableDispBasicBlock(BasicBlock* block, int ibcColWidth /* = 0 *
20772077
break;
20782078

20792079
case BBJ_EHFILTERRET:
2080-
printf("%*s (fltret)", maxBlockNumWidth - 2, "");
2080+
printf("-> " FMT_BB "%*s (fltret)", block->GetJumpDest()->bbNum,
2081+
maxBlockNumWidth - max(CountDigits(block->GetJumpDest()->bbNum), 2), "");
20812082
break;
20822083

20832084
case BBJ_EHCATCHRET:

src/coreclr/jit/fgflow.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ void Compiler::fgRemoveBlockAsPred(BasicBlock* block)
346346
case BBJ_CALLFINALLY:
347347
case BBJ_ALWAYS:
348348
case BBJ_EHCATCHRET:
349+
case BBJ_EHFILTERRET:
349350
fgRemoveRefPred(block->GetJumpDest(), block);
350351
break;
351352

@@ -358,11 +359,6 @@ void Compiler::fgRemoveBlockAsPred(BasicBlock* block)
358359
fgRemoveRefPred(block->Next(), block);
359360
break;
360361

361-
case BBJ_EHFILTERRET:
362-
block->GetJumpDest()->bbRefs++; // To compensate the bbRefs-- inside fgRemoveRefPred
363-
fgRemoveRefPred(block->GetJumpDest(), block);
364-
break;
365-
366362
case BBJ_EHFINALLYRET:
367363
for (BasicBlock* const succ : block->EHFinallyRetSuccs())
368364
{

src/coreclr/jit/fgopt.cpp

Lines changed: 52 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,11 @@ bool Compiler::fgRemoveUnreachableBlocks(CanRemoveBlockBody canRemoveBlock)
437437
// to properly set the info.compProfilerCallback flag.
438438
continue;
439439
}
440+
else if ((block->bbFlags & BBF_DONT_REMOVE) && block->isEmpty() && block->KindIs(BBJ_THROW))
441+
{
442+
// We already converted a non-removable block to a throw; don't bother processing it again.
443+
continue;
444+
}
440445
else if (!canRemoveBlock(block))
441446
{
442447
continue;
@@ -458,6 +463,8 @@ bool Compiler::fgRemoveUnreachableBlocks(CanRemoveBlockBody canRemoveBlock)
458463

459464
// Unmark the block as removed, clear BBF_INTERNAL, and set BBJ_IMPORTED
460465

466+
JITDUMP("Converting BBF_DONT_REMOVE block " FMT_BB " to BBJ_THROW\n", block->bbNum);
467+
461468
// The successors may be unreachable after this change.
462469
changed |= block->NumSucc() > 0;
463470

@@ -631,34 +638,6 @@ bool Compiler::fgRemoveDeadBlocks()
631638
{
632639
JITDUMP("\n*************** In fgRemoveDeadBlocks()");
633640

634-
jitstd::list<BasicBlock*> worklist(jitstd::allocator<void>(getAllocator(CMK_Reachability)));
635-
636-
worklist.push_back(fgFirstBB);
637-
638-
// Do not remove handler blocks
639-
for (EHblkDsc* const HBtab : EHClauses(this))
640-
{
641-
if (HBtab->HasFilter())
642-
{
643-
worklist.push_back(HBtab->ebdFilter);
644-
}
645-
worklist.push_back(HBtab->ebdHndBeg);
646-
}
647-
648-
#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
649-
// For ARM code, prevent creating retless calls by adding the BBJ_ALWAYS to the "fgAlwaysBlks" list.
650-
for (BasicBlock* const block : Blocks())
651-
{
652-
if (block->KindIs(BBJ_CALLFINALLY))
653-
{
654-
assert(block->isBBCallAlwaysPair());
655-
656-
// Don't remove the BBJ_ALWAYS block that is only here for the unwinder.
657-
worklist.push_back(block->Next());
658-
}
659-
}
660-
#endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
661-
662641
unsigned prevFgCurBBEpoch = fgCurBBEpoch;
663642
EnsureBasicBlockEpoch();
664643

@@ -673,6 +652,9 @@ bool Compiler::fgRemoveDeadBlocks()
673652

674653
BlockSet visitedBlocks(BlockSetOps::MakeEmpty(this));
675654

655+
jitstd::list<BasicBlock*> worklist(jitstd::allocator<void>(getAllocator(CMK_Reachability)));
656+
worklist.push_back(fgFirstBB);
657+
676658
// Visit all the reachable blocks, everything else can be removed
677659
while (!worklist.empty())
678660
{
@@ -690,6 +672,43 @@ bool Compiler::fgRemoveDeadBlocks()
690672
{
691673
worklist.push_back(succ);
692674
}
675+
676+
// Add all the "EH" successors. For every `try`, add its handler (including filter) to the worklist.
677+
if (bbIsTryBeg(block))
678+
{
679+
// Due to EH normalization, a block can only be the start of a single `try` region, with the exception
680+
// of mutually-protect regions.
681+
assert(block->hasTryIndex());
682+
unsigned tryIndex = block->getTryIndex();
683+
EHblkDsc* ehDsc = ehGetDsc(tryIndex);
684+
for (;;)
685+
{
686+
worklist.push_back(ehDsc->ebdHndBeg);
687+
if (ehDsc->HasFilter())
688+
{
689+
worklist.push_back(ehDsc->ebdFilter);
690+
}
691+
tryIndex = ehDsc->ebdEnclosingTryIndex;
692+
if (tryIndex == EHblkDsc::NO_ENCLOSING_INDEX)
693+
{
694+
break;
695+
}
696+
ehDsc = ehGetDsc(tryIndex);
697+
if (ehDsc->ebdTryBeg != block)
698+
{
699+
break;
700+
}
701+
}
702+
}
703+
704+
#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
705+
// For ARM code, always keep BBJ_CALLFINALLY/BBJ_ALWAYS as a pair
706+
if (block->KindIs(BBJ_CALLFINALLY))
707+
{
708+
assert(block->isBBCallAlwaysPair());
709+
worklist.push_back(block->Next());
710+
}
711+
#endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
693712
}
694713

695714
// Track if there is any unreachable block. Even if it is marked with
@@ -702,14 +721,14 @@ bool Compiler::fgRemoveDeadBlocks()
702721
// any of the fgFirstBB, handler, filter or BBJ_ALWAYS (Arm) blocks.
703722
auto isBlockRemovable = [&](BasicBlock* block) -> bool {
704723
bool isVisited = BlockSetOps::IsMember(this, visitedBlocks, block->bbNum);
705-
bool isRemovable = (!isVisited || block->bbRefs == 0);
724+
bool isRemovable = !isVisited || (block->bbRefs == 0);
706725

707726
#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
708-
isRemovable &=
709-
!block->isBBCallAlwaysPairTail(); // can't remove the BBJ_ALWAYS of a BBJ_CALLFINALLY / BBJ_ALWAYS pair
727+
// Can't remove the BBJ_ALWAYS of a BBJ_CALLFINALLY / BBJ_ALWAYS pair, even if bbRefs == 0.
728+
isRemovable &= !block->isBBCallAlwaysPairTail();
710729
#endif
711-
hasUnreachableBlock |= isRemovable;
712730

731+
hasUnreachableBlock |= isRemovable;
713732
return isRemovable;
714733
};
715734

@@ -738,6 +757,7 @@ bool Compiler::fgRemoveDeadBlocks()
738757
fgVerifyHandlerTab();
739758
fgDebugCheckBBlist(false);
740759
#endif // DEBUG
760+
741761
return hasUnreachableBlock;
742762
}
743763

0 commit comments

Comments
 (0)