Skip to content

Perform reachability analysis before codegen #66967

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

Merged
merged 9 commits into from
Mar 29, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
3 changes: 3 additions & 0 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4944,6 +4944,9 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
}
}

// Remove dead blocks
DoPhase(this, PHASE_REMOVE_DEAD_BLOCKS, &Compiler::fgRemoveDeadBlocks);

// Insert GC Polls
DoPhase(this, PHASE_INSERT_GC_POLLS, &Compiler::fgInsertGCPolls);

Expand Down
7 changes: 5 additions & 2 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5196,7 +5196,6 @@ class Compiler
bool fgHaveValidEdgeWeights; // true if we were successful in computing all of the edge weights
bool fgSlopUsedInEdgeWeights; // true if their was some slop used when computing the edge weights
bool fgRangeUsedInEdgeWeights; // true if some of the edgeWeight are expressed in Min..Max form
bool fgNeedsUpdateFlowGraph; // true if we need to run fgUpdateFlowGraph
weight_t fgCalledCount; // count of the number of times this method was called
// This is derived from the profile data
// or is BB_UNITY_WEIGHT when we don't have profile data
Expand Down Expand Up @@ -5774,10 +5773,14 @@ class Compiler

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

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

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

void fgRemoveDeadBlocks(); // Identify and remove dead blocks.

BasicBlock* fgIntersectDom(BasicBlock* a, BasicBlock* b); // Intersect two immediate dominator sets.

void fgDfsInvPostOrder(); // In order to compute dominance using fgIntersectDom, the flow graph nodes must be
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/compphases.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ CompPhaseNameMacro(PHASE_OPTIMIZE_BRANCHES, "Redundant branch opts",
CompPhaseNameMacro(PHASE_ASSERTION_PROP_MAIN, "Assertion prop", "AST-PROP", false, -1, false)
CompPhaseNameMacro(PHASE_OPT_UPDATE_FLOW_GRAPH, "Update flow graph opt pass", "UPD-FG-O", false, -1, false)
CompPhaseNameMacro(PHASE_COMPUTE_EDGE_WEIGHTS2, "Compute edge weights (2, false)","EDG-WGT2", false, -1, false)
CompPhaseNameMacro(PHASE_REMOVE_DEAD_BLOCKS, "Remove dead blocks", "DEAD-BLK", false, -1, false)
CompPhaseNameMacro(PHASE_INSERT_GC_POLLS, "Insert GC Polls", "GC-POLLS", false, -1, true)
CompPhaseNameMacro(PHASE_DETERMINE_FIRST_COLD_BLOCK, "Determine first cold block", "COLD-BLK", false, -1, true)
CompPhaseNameMacro(PHASE_RATIONALIZE, "Rationalize IR", "RAT", false, -1, false)
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/jit/emit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6556,7 +6556,8 @@ unsigned emitter::emitEndCodeGen(Compiler* comp,
#ifdef DEBUG
if (emitComp->opts.disAsm || emitComp->verbose)
{
printf("\t\t\t\t\t\t;; bbWeight=%s PerfScore %.2f", refCntWtd2str(ig->igWeight), ig->igPerfScore);
printf("\t\t\t\t\t\t;; size=%d bbWeight=%s PerfScore %.2f", ig->igSize, refCntWtd2str(ig->igWeight),
ig->igPerfScore);
}
*instrCount += ig->igInsCnt;
#endif // DEBUG
Expand Down
1 change: 0 additions & 1 deletion src/coreclr/jit/fgbasic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ void Compiler::fgInit()
fgHaveValidEdgeWeights = false;
fgSlopUsedInEdgeWeights = false;
fgRangeUsedInEdgeWeights = true;
fgNeedsUpdateFlowGraph = false;
fgCalledCount = BB_ZERO_WEIGHT;

/* We haven't yet computed the dominator sets */
Expand Down
165 changes: 134 additions & 31 deletions src/coreclr/jit/fgopt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,17 +367,14 @@ void Compiler::fgComputeEnterBlocksSet()
BlockSetOps::AddElemD(this, fgEnterBlks, fgFirstBB->bbNum);
assert(fgFirstBB->bbNum == 1);

if (compHndBBtabCount > 0)
/* Also 'or' in the handler basic blocks */
for (EHblkDsc* const HBtab : EHClauses(this))
{
/* Also 'or' in the handler basic blocks */
for (EHblkDsc* const HBtab : EHClauses(this))
if (HBtab->HasFilter())
{
if (HBtab->HasFilter())
{
BlockSetOps::AddElemD(this, fgEnterBlks, HBtab->ebdFilter->bbNum);
}
BlockSetOps::AddElemD(this, fgEnterBlks, HBtab->ebdHndBeg->bbNum);
BlockSetOps::AddElemD(this, fgEnterBlks, HBtab->ebdFilter->bbNum);
}
BlockSetOps::AddElemD(this, fgEnterBlks, HBtab->ebdHndBeg->bbNum);
}

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

bool hasUnreachableBlocks = false;
bool changed = false;
Expand All @@ -451,18 +460,10 @@ bool Compiler::fgRemoveUnreachableBlocks()
}
else
{
// If any of the entry blocks can reach this block, then we skip it.
if (!BlockSetOps::IsEmptyIntersection(this, fgEnterBlks, block->bbReach))
{
continue;
}

#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
if (!BlockSetOps::IsEmptyIntersection(this, fgAlwaysBlks, block->bbReach))
if (!canRemoveBlock(block))
{
continue;
}
#endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
}

// Remove all the code for the block
Expand Down Expand Up @@ -572,6 +573,24 @@ void Compiler::fgComputeReachability()
// The dominator algorithm expects that all blocks can be reached from the fgEnterBlks set.
unsigned passNum = 1;
bool changed;

auto canRemoveBlock = [&](BasicBlock* block) -> bool {
// If any of the entry blocks can reach this block, then we skip it.
if (!BlockSetOps::IsEmptyIntersection(this, fgEnterBlks, block->bbReach))
{
return false;
}

#if defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)
if (!BlockSetOps::IsEmptyIntersection(this, fgAlwaysBlks, block->bbReach))
{
return false;
}
#endif // defined(FEATURE_EH_FUNCLETS) && defined(TARGET_ARM)

return true;
};

do
{
// Just to be paranoid, avoid infinite loops; fall back to minopts.
Expand Down Expand Up @@ -601,7 +620,7 @@ void Compiler::fgComputeReachability()
// Use reachability information to delete unreachable blocks.
//

changed = fgRemoveUnreachableBlocks();
changed = fgRemoveUnreachableBlocks(canRemoveBlock);

} while (changed);

Expand All @@ -624,6 +643,95 @@ void Compiler::fgComputeReachability()
fgComputeDoms();
}

//------------------------------------------------------------------------
// fgRemoveDeadBlocks: Identify all the unreachable blocks and remove them.
// Handler and filter blocks are considered as reachable and hence won't
// be removed. For Arm32, do not remove BBJ_ALWAYS block of
// BBJ_CALLFINALLY/BBJ_ALWAYS pair.
//
void Compiler::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->bbJumpKind == BBJ_CALLFINALLY)
{
assert(block->isBBCallAlwaysPair());

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

unsigned prevFgCurBBEpoch = fgCurBBEpoch;
EnsureBasicBlockEpoch();

if (prevFgCurBBEpoch != fgCurBBEpoch)
{
// If Epoch has changed, reset the doms computed as well because
// in future, during insert gc polls or lowering, when we compact
// blocks during flowgraph update, it might propagate the invalid
// bbReach as well (although Epoch adjustment resets fgReachabilitySetsValid).
fgDomsComputed = false;
}

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

// Visit all the reachable blocks, everything else can be removed
while (!worklist.empty())
{
BasicBlock* block = *(worklist.begin());
worklist.pop_front();

if (BlockSetOps::IsMember(this, visitedBlocks, block->bbNum))
{
continue;
}

BlockSetOps::AddElemD(this, visitedBlocks, block->bbNum);

for (BasicBlock* succ : block->Succs(this))
{
worklist.push_back(succ);
}
}

// A block is unreachable if no path was found from
// any of the fgFirstBB, handler, filter or BBJ_ALWAYS (Arm) blocks.
auto isBlockRemovable = [&](BasicBlock* block) -> bool {
return !BlockSetOps::IsMember(this, visitedBlocks, block->bbNum);
};

bool changed = fgRemoveUnreachableBlocks(isBlockRemovable);

#ifdef DEBUG
if (verbose && changed)
{
printf("\nAfter dead block removal:\n");
fgDispBasicBlocks(verboseTrees);
printf("\n");
}

fgVerifyHandlerTab();
fgDebugCheckBBlist(false);
#endif // DEBUG
}

//-------------------------------------------------------------
// fgDfsInvPostOrder: Helper function for computing dominance information.
//
Expand Down Expand Up @@ -2163,6 +2271,7 @@ void Compiler::fgCompactBlocks(BasicBlock* block, BasicBlock* bNext)
//
if (fgDomsComputed && (block->bbNum > fgDomBBcount))
{
assert(fgReachabilitySetsValid);
BlockSetOps::Assign(this, block->bbReach, bNext->bbReach);
BlockSetOps::ClearD(this, bNext->bbReach);

Expand Down Expand Up @@ -2512,8 +2621,7 @@ bool Compiler::fgOptimizeBranchToEmptyUnconditional(BasicBlock* block, BasicBloc
//
if (fgIsUsingProfileWeights() && !fgEdgeWeightsComputed)
{
fgNeedsUpdateFlowGraph = true;
optimizeJump = false;
optimizeJump = false;
}

if (optimizeJump)
Expand Down Expand Up @@ -2875,8 +2983,7 @@ bool Compiler::fgOptimizeSwitchBranches(BasicBlock* block)
//
if (fgIsUsingProfileWeights() && !fgEdgeWeightsComputed)
{
fgNeedsUpdateFlowGraph = true;
optimizeJump = false;
optimizeJump = false;
}

if (optimizeJump)
Expand Down Expand Up @@ -5683,7 +5790,6 @@ bool Compiler::fgReorderBlocks()

if (changed)
{
fgNeedsUpdateFlowGraph = true;
#if DEBUG
// Make sure that the predecessor lists are accurate
if (expensiveDebugCheckLevel >= 2)
Expand Down Expand Up @@ -5908,8 +6014,7 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication)
// because we can't allow fall-through into the cold region.
if (!fgEdgeWeightsComputed || fgInDifferentRegions(block, bDest))
{
fgNeedsUpdateFlowGraph = true;
optimizeJump = false;
optimizeJump = false;
}
}

Expand Down Expand Up @@ -6194,8 +6299,6 @@ bool Compiler::fgUpdateFlowGraph(bool doTailDuplication)
}
} while (change);

fgNeedsUpdateFlowGraph = false;

#ifdef DEBUG
if (verbose && modified)
{
Expand Down
Loading