Skip to content

Commit 9469471

Browse files
authored
JIT: Optimize data flow used in assertion prop/CSE (#94701)
The data flow used by assertion prop and CSE utilize "all-succs" which includes handler successors. However, in reality neither analysis needs full treatment of handlers; we do not CSE variables that are live-in to handlers, and assertion prop never kills assertions that would need to be propagated to handlers. Additionally, the data flow framework used conflates end-of-block propagation of facts with reachability of the handler. If no facts changed at the ends of the preds of the handler, then the handler is not visited. This is despite the fact that the handler in the general case is also reachable with facts from the beginning of every enclosed basic block (or more generally with facts that were invalidated in the middle of enclosed basic blocks). This ends up working out today only because of the fake successor edges we have from preds of the try to the handler, in addition to the restrictions on the data flows described above. Since I'm removing these fake edges, we need a more explicit solution. The change here has such a solution, by making it more explicit what exactly is needed in the data flow for CSE and AP here: they only need to consider regular successors, with the added catch that they need to consider reachability of handlers once we see the corresponding 'try' being reachable.
1 parent 9deded1 commit 9469471

File tree

2 files changed

+40
-8
lines changed

2 files changed

+40
-8
lines changed

src/coreclr/jit/assertionprop.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5488,8 +5488,14 @@ class AssertionPropFlowCallback
54885488
// lastTryBlock - the last block of the try for "block" handler;.
54895489
//
54905490
// Notes:
5491-
// We can jump to the handler from any instruction in the try region.
5492-
// It means we can propagate only assertions that are valid for the whole try region.
5491+
// We can jump to the handler from any instruction in the try region. It
5492+
// means we can propagate only assertions that are valid for the whole
5493+
// try region.
5494+
//
5495+
// It suffices to intersect with only the head 'try' block's assertions,
5496+
// since that block dominates all other blocks in the try, and since
5497+
// assertions are VN-based and can never become false.
5498+
//
54935499
void MergeHandler(BasicBlock* block, BasicBlock* firstTryBlock, BasicBlock* lastTryBlock)
54945500
{
54955501
if (VerboseDataflow())
@@ -5498,11 +5504,8 @@ class AssertionPropFlowCallback
54985504
Compiler::optDumpAssertionIndices("in -> ", block->bbAssertionIn, "; ");
54995505
JITDUMP("firstTryBlock " FMT_BB " ", firstTryBlock->bbNum);
55005506
Compiler::optDumpAssertionIndices("in -> ", firstTryBlock->bbAssertionIn, "; ");
5501-
JITDUMP("lastTryBlock " FMT_BB " ", lastTryBlock->bbNum);
5502-
Compiler::optDumpAssertionIndices("out -> ", lastTryBlock->bbAssertionOut, "\n");
55035507
}
55045508
BitVecOps::IntersectionD(apTraits, block->bbAssertionIn, firstTryBlock->bbAssertionIn);
5505-
BitVecOps::IntersectionD(apTraits, block->bbAssertionIn, lastTryBlock->bbAssertionOut);
55065509
}
55075510

55085511
// At the end of the merge store results of the dataflow equations, in a postmerge state.

src/coreclr/jit/dataflow.h

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,48 @@ void DataFlow::ForwardAnalysis(TCallback& callback)
5858
}
5959
else
6060
{
61-
FlowEdge* preds = m_pCompiler->BlockPredsWithEH(block);
62-
for (FlowEdge* pred = preds; pred; pred = pred->getNextPredEdge())
61+
for (FlowEdge* pred : block->PredEdges())
6362
{
6463
callback.Merge(block, pred->getSourceBlock(), pred->getDupCount());
6564
}
6665
}
6766

6867
if (callback.EndMerge(block))
6968
{
70-
block->VisitAllSuccs(m_pCompiler, [&worklist](BasicBlock* succ) {
69+
// The clients using DataFlow (CSE, assertion prop) currently do
70+
// not need EH successors here:
71+
//
72+
// 1. CSE does not CSE into handlers, so it considers no
73+
// expressions available at the beginning of handlers;
74+
//
75+
// 2. Facts in global assertion prop are VN-based and can only
76+
// become false because of control flow, so it is sufficient to
77+
// propagate facts available into the 'try' head block, since that
78+
// block dominates all other blocks in the 'try'. That will happen
79+
// as part of processing handlers below.
80+
//
81+
block->VisitRegularSuccs(m_pCompiler, [&worklist](BasicBlock* succ) {
7182
worklist.insert(worklist.end(), succ);
7283
return BasicBlockVisit::Continue;
7384
});
7485
}
86+
87+
if (m_pCompiler->bbIsTryBeg(block))
88+
{
89+
// Handlers of the try are reachable (and may require special
90+
// handling compared to the normal "at-the-end" propagation above).
91+
EHblkDsc* eh = m_pCompiler->ehGetDsc(block->getTryIndex());
92+
do
93+
{
94+
worklist.insert(worklist.end(), eh->ExFlowBlock());
95+
96+
if (eh->ebdEnclosingTryIndex == EHblkDsc::NO_ENCLOSING_INDEX)
97+
{
98+
break;
99+
}
100+
101+
eh = m_pCompiler->ehGetDsc(eh->ebdEnclosingTryIndex);
102+
} while (eh->ebdTryBeg == block);
103+
}
75104
}
76105
}

0 commit comments

Comments
 (0)