Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve EH dead code elimination #93428

Merged
merged 3 commits into from
Oct 14, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/coreclr/jit/block.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,7 @@ void BasicBlock::dspJumpKind()
break;

case BBJ_EHFILTERRET:
printf(" (fltret)");
printf(" -> " FMT_BB " (fltret)", bbJumpDest->bbNum);
break;

case BBJ_EHCATCHRET:
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/clrjit.natvis
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Documentation for VS debugger format specifiers: https://docs.microsoft.com/en-u
</Type>

<Type Name="BasicBlock">
<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>
<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>
<DisplayString Condition="bbJumpKind==BBJ_SWITCH">BB{bbNum,d}; {bbJumpKind,en}; {bbJumpSwt->bbsCount} cases</DisplayString>
<DisplayString Condition="bbJumpKind==BBJ_EHFINALLYRET">BB{bbNum,d}; {bbJumpKind,en}; {bbJumpEhf->bbeCount} succs</DisplayString>
<DisplayString>BB{bbNum,d}; {bbJumpKind,en}</DisplayString>
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/fgbasic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ bool Compiler::fgEnsureFirstBBisScratch()
}

// The first block has an implicit ref count which we must
// remove. Note the ref count could be greater that one, if
// remove. Note the ref count could be greater than one, if
// the first block is not scratch and is targeted by a
// branch.
assert(fgFirstBB->bbRefs >= 1);
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/jit/fgdiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2077,7 +2077,8 @@ void Compiler::fgTableDispBasicBlock(BasicBlock* block, int ibcColWidth /* = 0 *
break;

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

case BBJ_EHCATCHRET:
Expand Down
6 changes: 1 addition & 5 deletions src/coreclr/jit/fgflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ void Compiler::fgRemoveBlockAsPred(BasicBlock* block)
case BBJ_CALLFINALLY:
case BBJ_ALWAYS:
case BBJ_EHCATCHRET:
case BBJ_EHFILTERRET:
fgRemoveRefPred(block->GetJumpDest(), block);
break;

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

case BBJ_EHFILTERRET:
block->GetJumpDest()->bbRefs++; // To compensate the bbRefs-- inside fgRemoveRefPred
fgRemoveRefPred(block->GetJumpDest(), block);
break;

case BBJ_EHFINALLYRET:
for (BasicBlock* const succ : block->EHFinallyRetSuccs())
{
Expand Down
84 changes: 49 additions & 35 deletions src/coreclr/jit/fgopt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,11 @@ bool Compiler::fgRemoveUnreachableBlocks(CanRemoveBlockBody canRemoveBlock)
// to properly set the info.compProfilerCallback flag.
continue;
}
else if ((block->bbFlags & BBF_DONT_REMOVE) && block->isEmpty() && block->KindIs(BBJ_THROW))
{
// We already converted a non-removable block to a throw; don't bother processing it again.
continue;
}
else if (!canRemoveBlock(block))
{
continue;
Expand All @@ -458,6 +463,8 @@ bool Compiler::fgRemoveUnreachableBlocks(CanRemoveBlockBody canRemoveBlock)

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

JITDUMP("Converting BBF_DONT_REMOVE block " FMT_BB " to BBJ_THROW\n", block->bbNum);

// The successors may be unreachable after this change.
changed |= block->NumSucc() > 0;

Expand Down Expand Up @@ -631,34 +638,6 @@ bool Compiler::fgRemoveDeadBlocks()
{
JITDUMP("\n*************** In fgRemoveDeadBlocks()");

jitstd::list<BasicBlock*> worklist(jitstd::allocator<void>(getAllocator(CMK_Reachability)));

worklist.push_back(fgFirstBB);

// Do not remove handler blocks
for (EHblkDsc* const HBtab : EHClauses(this))
{
if (HBtab->HasFilter())
{
worklist.push_back(HBtab->ebdFilter);
}
worklist.push_back(HBtab->ebdHndBeg);
}

#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
// For ARM code, prevent creating retless calls by adding the BBJ_ALWAYS to the "fgAlwaysBlks" list.
for (BasicBlock* const block : Blocks())
{
if (block->KindIs(BBJ_CALLFINALLY))
{
assert(block->isBBCallAlwaysPair());

// Don't remove the BBJ_ALWAYS block that is only here for the unwinder.
worklist.push_back(block->Next());
}
}
#endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)

unsigned prevFgCurBBEpoch = fgCurBBEpoch;
EnsureBasicBlockEpoch();

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

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

jitstd::list<BasicBlock*> worklist(jitstd::allocator<void>(getAllocator(CMK_Reachability)));
worklist.push_back(fgFirstBB);

// Visit all the reachable blocks, everything else can be removed
while (!worklist.empty())
{
Expand All @@ -690,6 +672,43 @@ bool Compiler::fgRemoveDeadBlocks()
{
worklist.push_back(succ);
}

// Add all the "EH" successors. For every `try`, add its handler (including filter) to the worklist.
if (bbIsTryBeg(block))
{
// Due to EH normalization, a block can only be the start of a single `try` region, with the exception
// of mutually-protect regions.
assert(block->hasTryIndex());
unsigned tryIndex = block->getTryIndex();
EHblkDsc* ehDsc = ehGetDsc(tryIndex);
for (;;)
{
worklist.push_back(ehDsc->ebdHndBeg);
if (ehDsc->HasFilter())
{
worklist.push_back(ehDsc->ebdFilter);
}
tryIndex = ehDsc->ebdEnclosingTryIndex;
if (tryIndex == EHblkDsc::NO_ENCLOSING_INDEX)
{
break;
}
ehDsc = ehGetDsc(tryIndex);
if (ehDsc->ebdTryBeg != block)
{
break;
}
}
}

#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
// For ARM code, always keep BBJ_CALLFINALLY/BBJ_ALWAYS as a pair
if (block->KindIs(BBJ_CALLFINALLY))
{
assert(block->isBBCallAlwaysPair());
worklist.push_back(block->Next());
}
#endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
}

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

#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
isRemovable &=
!block->isBBCallAlwaysPairTail(); // can't remove the BBJ_ALWAYS of a BBJ_CALLFINALLY / BBJ_ALWAYS pair
#endif
bool isRemovable = !isVisited || (block->bbRefs == 0);
hasUnreachableBlock |= isRemovable;

return isRemovable;
};

Expand Down Expand Up @@ -738,6 +751,7 @@ bool Compiler::fgRemoveDeadBlocks()
fgVerifyHandlerTab();
fgDebugCheckBBlist(false);
#endif // DEBUG

return hasUnreachableBlock;
}

Expand Down