Skip to content

Commit

Permalink
JIT: Factor reachability sets and base them on new DFS (#96892)
Browse files Browse the repository at this point in the history
- Introduce `BlockReachabilitySets` which encapsulates a data structure
  that can answer "A -> B" reachability queries in `O(1)`. It factors
  the old `fgComputeReachabilitySets` and `fgReachable`.
- Base the new reachability sets on the new DFS instead of using the old
  DFS. Switch to postorder num indexing instead of bbNum based indexing.
- Remove `BasicBlock::bbReach`; store the reachability sets within
  `BlockReachabilitySets` instead. This costs some extra memory in
  FullOpts if we end up computing it twice, but reduces memory in
  MinOpts.
- We no longer propagate `BBF_GC_SAFE_POINT` during the reachability
  computation. The cycle-based GC safe point algorithm introduced in
  #95299 does not require this propagation.
- Switch the dead block elimination in `fgComputeReachability` to use
  `fgDfsBlocksAndRemove`, which has more general handling of EH and does
  not require as much iteration to closure
- Remove the compuation of "enter blocks". With recent work the
  reachability computation was the last remaining use.

Minor diffs expected due to the more general dead block removal capacity
of `fgDfsBlocksAndRemove`.
  • Loading branch information
jakobbotsch authored Jan 12, 2024
1 parent 59fb3be commit 9fde15e
Show file tree
Hide file tree
Showing 10 changed files with 209 additions and 368 deletions.
7 changes: 1 addition & 6 deletions src/coreclr/jit/block.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,10 +398,7 @@ enum BasicBlockFlags : unsigned __int64
BBF_LOOP_ALIGN = MAKE_BBFLAG(16), // Block is lexically the first block in a loop we intend to align.
BBF_HAS_ALIGN = MAKE_BBFLAG(17), // BB ends with 'align' instruction
BBF_HAS_JMP = MAKE_BBFLAG(18), // BB executes a JMP instruction (instead of return)
BBF_GC_SAFE_POINT = MAKE_BBFLAG(19), // BB has a GC safe point (a call). More abstractly, BB does not require a
// (further) poll -- this may be because this BB has a call, or, in some
// cases, because the BB occurs in a loop, and we've determined that all
// paths in the loop body leading to BB include a call.
BBF_GC_SAFE_POINT = MAKE_BBFLAG(19), // BB has a GC safe point (e.g. a call)
BBF_HAS_IDX_LEN = MAKE_BBFLAG(20), // BB contains simple index or length expressions on an SD array local var.
BBF_HAS_MD_IDX_LEN = MAKE_BBFLAG(21), // BB contains simple index, length, or lower bound expressions on an MD array local var.
BBF_HAS_MDARRAYREF = MAKE_BBFLAG(22), // Block has a multi-dimensional array reference
Expand Down Expand Up @@ -1296,8 +1293,6 @@ struct BasicBlock : private LIR::Range
void ensurePredListOrder(Compiler* compiler);
void reorderPredList(Compiler* compiler);

BlockSet bbReach; // Set of all blocks that can reach this one

union {
BasicBlock* bbIDom; // Represent the closest dominator to this block (called the Immediate
// Dominator) used to compute the dominance tree.
Expand Down
19 changes: 9 additions & 10 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,6 @@ Histogram domsChangedIterationTable(domsChangedIterationBuckets);
unsigned computeReachabilitySetsIterationBuckets[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0};
Histogram computeReachabilitySetsIterationTable(computeReachabilitySetsIterationBuckets);

unsigned computeReachabilityIterationBuckets[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0};
Histogram computeReachabilityIterationTable(computeReachabilityIterationBuckets);

#endif // COUNT_BASIC_BLOCKS

/*****************************************************************************
Expand Down Expand Up @@ -1577,12 +1574,6 @@ void Compiler::compShutdown()
computeReachabilitySetsIterationTable.dump(jitstdout());
jitprintf("--------------------------------------------------\n");

jitprintf("--------------------------------------------------\n");
jitprintf("fgComputeReachability `while (change)` iterations:\n");
jitprintf("--------------------------------------------------\n");
computeReachabilityIterationTable.dump(jitstdout());
jitprintf("--------------------------------------------------\n");

#endif // COUNT_BASIC_BLOCKS

#if COUNT_LOOPS
Expand Down Expand Up @@ -5830,6 +5821,7 @@ void Compiler::RecomputeFlowGraphAnnotations()
optSetBlockWeights();
optFindLoops();

fgInvalidateDfsTree();
m_dfsTree = fgComputeDfs();
optFindNewLoops();

Expand Down Expand Up @@ -9626,7 +9618,14 @@ JITDBGAPI void __cdecl cReach(Compiler* comp)
{
static unsigned sequenceNumber = 0; // separate calls with a number to indicate this function has been called
printf("===================================================================== *Reach %u\n", sequenceNumber++);
comp->fgDispReach();
if (comp->m_reachabilitySets != nullptr)
{
comp->m_reachabilitySets->Dump();
}
else
{
printf(" Not computed\n");
}
}

JITDBGAPI void __cdecl cDoms(Compiler* comp)
Expand Down
51 changes: 27 additions & 24 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -1570,10 +1570,8 @@ enum API_ICorJitInfo_Names

enum class FlowGraphUpdates
{
COMPUTE_BASICS = 0, // renumber blocks, reachability, etc
COMPUTE_DOMS = 1 << 0, // recompute dominators
COMPUTE_RETURNS = 1 << 1, // recompute return blocks
COMPUTE_LOOPS = 1 << 2, // recompute loop table
COMPUTE_BASICS = 0, // renumber blocks, reachability, etc
COMPUTE_DOMS = 1 << 0, // recompute dominators
};

inline constexpr FlowGraphUpdates operator|(FlowGraphUpdates a, FlowGraphUpdates b)
Expand Down Expand Up @@ -2392,6 +2390,30 @@ class BlockToNaturalLoopMap
static BlockToNaturalLoopMap* Build(FlowGraphNaturalLoops* loops);
};

// Represents a data structure that can answer A -> B reachability queries in
// O(1) time. Only takes regular flow into account; if A -> B requires
// exceptional flow, then CanReach returns false.
class BlockReachabilitySets
{
FlowGraphDfsTree* m_dfsTree;
BitVec* m_reachabilitySets;

BlockReachabilitySets(FlowGraphDfsTree* dfsTree, BitVec* reachabilitySets)
: m_dfsTree(dfsTree)
, m_reachabilitySets(reachabilitySets)
{
}

public:
bool CanReach(BasicBlock* from, BasicBlock* to);

#ifdef DEBUG
void Dump();
#endif

static BlockReachabilitySets* Build(FlowGraphDfsTree* dfsTree);
};

enum class FieldKindForVN
{
SimpleStatic,
Expand Down Expand Up @@ -4988,6 +5010,7 @@ class Compiler
// Dominator tree used by SSA construction and copy propagation (the two are expected to use the same tree
// in order to avoid the need for SSA reconstruction and an "out of SSA" phase).
FlowGraphDominatorTree* m_domTree;
BlockReachabilitySets* m_reachabilitySets;

// After the dominance tree is computed, we cache a DFS preorder number and DFS postorder number to compute
// dominance queries in O(1). fgDomTreePreOrder and fgDomTreePostOrder are arrays giving the block's preorder and
Expand Down Expand Up @@ -5045,10 +5068,6 @@ class Compiler
roundUp(fgCurBBEpochSize, (unsigned)(sizeof(size_t) * 8)) / unsigned(sizeof(size_t) * 8);

#ifdef DEBUG
// All BlockSet objects are now invalid!
fgReachabilitySetsValid = false; // the bbReach sets are now invalid!
fgEnterBlksSetValid = false; // the fgEnterBlks set is now invalid!

if (verbose)
{
unsigned epochArrSize = BasicBlockBitSetTraits::GetArrSize(this);
Expand Down Expand Up @@ -5128,14 +5147,6 @@ class Compiler

bool fgHasSwitch; // any BBJ_SWITCH jumps?

BlockSet fgEnterBlks; // Set of blocks which have a special transfer of control; the "entry" blocks plus EH handler
// begin blocks.

#ifdef DEBUG
bool fgReachabilitySetsValid; // Are the bbReach sets valid?
bool fgEnterBlksSetValid; // Is the fgEnterBlks set valid?
#endif // DEBUG

bool fgRemoveRestOfBlock; // true if we know that we will throw
bool fgStmtRemoved; // true if we remove statements -> need new DFA

Expand Down Expand Up @@ -5782,21 +5793,15 @@ class Compiler
// Dominator computation member functions
// Not exposed outside Compiler
protected:
bool fgReachable(BasicBlock* b1, BasicBlock* b2); // Returns true if block b1 can reach block b2

// Compute immediate dominators, the dominator tree and and its pre/post-order travsersal numbers.
void fgComputeDoms();

BlockSet_ValRet_T fgGetDominatorSet(BasicBlock* block); // Returns a set of blocks that dominate the given block.
// Note: this is relatively slow compared to calling fgDominate(),
// especially if dealing with a single block versus block check.

void fgComputeReachabilitySets(); // Compute bbReach sets. (Also sets BBF_GC_SAFE_POINT flag on blocks.)

void fgComputeReturnBlocks(); // Initialize fgReturnBlocks to a list of BBJ_RETURN blocks.

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

// Remove blocks determined to be unreachable by the 'canRemoveBlock'.
template <typename CanRemoveBlockBody>
bool fgRemoveUnreachableBlocks(CanRemoveBlockBody canRemoveBlock);
Expand Down Expand Up @@ -6120,7 +6125,6 @@ class Compiler
#ifdef DEBUG

void fgDispDoms();
void fgDispReach();
void fgDispBBLiveness(BasicBlock* block);
void fgDispBBLiveness();
void fgTableDispBasicBlock(BasicBlock* block, int ibcColWidth = 0);
Expand Down Expand Up @@ -12302,7 +12306,6 @@ extern Histogram bbCntTable;
extern Histogram bbOneBBSizeTable;
extern Histogram domsChangedIterationTable;
extern Histogram computeReachabilitySetsIterationTable;
extern Histogram computeReachabilityIterationTable;
#endif

/*****************************************************************************
Expand Down
21 changes: 6 additions & 15 deletions src/coreclr/jit/fgbasic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ void Compiler::fgInit()
fgDomsComputed = false;
fgReturnBlocksComputed = false;

#ifdef DEBUG
fgReachabilitySetsValid = false;
#endif // DEBUG

/* Initialize the basic block list */

fgFirstBB = nullptr;
Expand Down Expand Up @@ -66,10 +62,12 @@ void Compiler::fgInit()
fgBBVarSetsInited = false;
fgReturnCount = 0;

m_dfsTree = nullptr;
m_loops = nullptr;
m_loopSideEffects = nullptr;
m_blockToLoop = nullptr;
m_dfsTree = nullptr;
m_loops = nullptr;
m_loopSideEffects = nullptr;
m_blockToLoop = nullptr;
m_domTree = nullptr;
m_reachabilitySets = nullptr;

// Initialize BlockSet data.
fgCurBBEpoch = 0;
Expand Down Expand Up @@ -118,15 +116,8 @@ void Compiler::fgInit()
/* We will record a list of all BBJ_RETURN blocks here */
fgReturnBlocks = nullptr;

/* This is set by fgComputeReachability */
fgEnterBlks = BlockSetOps::UninitVal();

fgUsedSharedTemps = nullptr;

#ifdef DEBUG
fgEnterBlksSetValid = false;
#endif // DEBUG

#if !defined(FEATURE_EH_FUNCLETS)
ehMaxHndNestingCount = 0;
#endif // !FEATURE_EH_FUNCLETS
Expand Down
19 changes: 0 additions & 19 deletions src/coreclr/jit/fgdiagnostic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1860,25 +1860,6 @@ void Compiler::fgDumpFlowGraphLoops(FILE* file)
/*****************************************************************************/
#ifdef DEBUG

void Compiler::fgDispReach()
{
printf("------------------------------------------------\n");
printf("BBnum Reachable by \n");
printf("------------------------------------------------\n");

for (BasicBlock* const block : Blocks())
{
printf(FMT_BB " : ", block->bbNum);
BlockSetOps::Iter iter(this, block->bbReach);
unsigned bbNum = 0;
while (iter.NextElem(&bbNum))
{
printf(FMT_BB " ", bbNum);
}
printf("\n");
}
}

void Compiler::fgDispDoms()
{
// Don't bother printing this when we have a large number of BasicBlocks in the method
Expand Down
Loading

0 comments on commit 9fde15e

Please sign in to comment.