Skip to content

Commit 3eb8c7f

Browse files
JIT: Add pred block iterator that tolerates pred list modifications (#99466)
Follow-up to #99362. fgRedirectTargetEdge modifies the predecessor lists of the old and new successor blocks, so if we want to be able to use it in fgReplaceJumpTarget, we need a pred block iterator that is resilient to these changes, as we frequently call the latter method while iterating predecessors.
1 parent 8bd4f6f commit 3eb8c7f

File tree

5 files changed

+64
-59
lines changed

5 files changed

+64
-59
lines changed

src/coreclr/jit/block.h

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,9 @@ class PredEdgeList
258258
// PredBlockList: adapter class for forward iteration of the predecessor edge linked list yielding
259259
// predecessor blocks, using range-based `for`, normally used via BasicBlock::PredBlocks(), e.g.:
260260
// for (BasicBlock* const predBlock : block->PredBlocks()) ...
261+
// allowEdits controls whether the iterator should be resilient to changes to the predecessor list.
261262
//
263+
template <bool allowEdits>
262264
class PredBlockList
263265
{
264266
FlowEdge* m_begin;
@@ -270,13 +272,12 @@ class PredBlockList
270272
{
271273
FlowEdge* m_pred;
272274

273-
#ifdef DEBUG
274-
// Try to guard against the user of the iterator from making changes to the IR that would invalidate
275-
// the iterator: cache the edge we think should be next, then check it when we actually do the `++`
275+
// When allowEdits=false, try to guard against the user of the iterator from modifying the predecessor list
276+
// being traversed: cache the edge we think should be next, then check it when we actually do the `++`
276277
// operation. This is a bit conservative, but attempts to protect against callers assuming too much about
277278
// this iterator implementation.
279+
// When allowEdits=true, m_next is always used to update m_pred, so changes to m_pred don't break the iterator.
278280
FlowEdge* m_next;
279-
#endif
280281

281282
public:
282283
iterator(FlowEdge* pred);
@@ -1530,9 +1531,18 @@ struct BasicBlock : private LIR::Range
15301531
// PredBlocks: convenience method for enabling range-based `for` iteration over predecessor blocks, e.g.:
15311532
// for (BasicBlock* const predBlock : block->PredBlocks()) ...
15321533
//
1533-
PredBlockList PredBlocks() const
1534+
PredBlockList<false> PredBlocks() const
1535+
{
1536+
return PredBlockList<false>(bbPreds);
1537+
}
1538+
1539+
// PredBlocksEditing: convenience method for enabling range-based `for` iteration over predecessor blocks, e.g.:
1540+
// for (BasicBlock* const predBlock : block->PredBlocksList()) ...
1541+
// This iterator tolerates modifications to bbPreds.
1542+
//
1543+
PredBlockList<true> PredBlocksEditing() const
15341544
{
1535-
return PredBlockList(bbPreds);
1545+
return PredBlockList<true>(bbPreds);
15361546
}
15371547

15381548
// Pred list maintenance
@@ -2399,29 +2409,45 @@ inline PredEdgeList::iterator& PredEdgeList::iterator::operator++()
23992409
return *this;
24002410
}
24012411

2402-
inline PredBlockList::iterator::iterator(FlowEdge* pred) : m_pred(pred)
2412+
template <bool allowEdits>
2413+
inline PredBlockList<allowEdits>::iterator::iterator(FlowEdge* pred) : m_pred(pred)
24032414
{
2404-
#ifdef DEBUG
2405-
m_next = (m_pred == nullptr) ? nullptr : m_pred->getNextPredEdge();
2406-
#endif
2415+
bool initNextPointer = allowEdits;
2416+
INDEBUG(initNextPointer = true);
2417+
if (initNextPointer)
2418+
{
2419+
m_next = (m_pred == nullptr) ? nullptr : m_pred->getNextPredEdge();
2420+
}
24072421
}
24082422

2409-
inline BasicBlock* PredBlockList::iterator::operator*() const
2423+
template <bool allowEdits>
2424+
inline BasicBlock* PredBlockList<allowEdits>::iterator::operator*() const
24102425
{
24112426
return m_pred->getSourceBlock();
24122427
}
24132428

2414-
inline PredBlockList::iterator& PredBlockList::iterator::operator++()
2429+
template <bool allowEdits>
2430+
inline typename PredBlockList<allowEdits>::iterator& PredBlockList<allowEdits>::iterator::operator++()
24152431
{
2416-
FlowEdge* next = m_pred->getNextPredEdge();
2432+
if (allowEdits)
2433+
{
2434+
// For editing iterators, m_next is always used and maintained
2435+
m_pred = m_next;
2436+
m_next = (m_next == nullptr) ? nullptr : m_next->getNextPredEdge();
2437+
}
2438+
else
2439+
{
2440+
FlowEdge* next = m_pred->getNextPredEdge();
24172441

24182442
#ifdef DEBUG
2419-
// Check that the next block is the one we expect to see.
2420-
assert(next == m_next);
2421-
m_next = (next == nullptr) ? nullptr : next->getNextPredEdge();
2443+
// If allowEdits=false, check that the next block is the one we expect to see.
2444+
assert(next == m_next);
2445+
m_next = (m_next == nullptr) ? nullptr : m_next->getNextPredEdge();
24222446
#endif // DEBUG
24232447

2424-
m_pred = next;
2448+
m_pred = next;
2449+
}
2450+
24252451
return *this;
24262452
}
24272453

src/coreclr/jit/fgbasic.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -645,17 +645,11 @@ void Compiler::fgReplaceJumpTarget(BasicBlock* block, BasicBlock* oldTarget, Bas
645645
case BBJ_EHCATCHRET:
646646
case BBJ_EHFILTERRET:
647647
case BBJ_LEAVE: // This function can be called before import, so we still have BBJ_LEAVE
648-
{
649-
// TODO: Use fgRedirectTargetEdge once pred edge iterators are resilient to bbPreds being modified.
650648
assert(block->TargetIs(oldTarget));
651-
fgRemoveRefPred(block->GetTargetEdge());
652-
FlowEdge* const newEdge = fgAddRefPred(newTarget, block, block->GetTargetEdge());
653-
block->SetTargetEdge(newEdge);
649+
fgRedirectTargetEdge(block, newTarget);
654650
break;
655-
}
656651

657652
case BBJ_COND:
658-
659653
if (block->TrueTargetIs(oldTarget))
660654
{
661655
FlowEdge* const oldEdge = block->GetTrueEdge();
@@ -5297,7 +5291,7 @@ BasicBlock* Compiler::fgRemoveBlock(BasicBlock* block, bool unreachable)
52975291

52985292
fgRemoveRefPred(block->GetTargetEdge());
52995293

5300-
for (BasicBlock* const predBlock : block->PredBlocks())
5294+
for (BasicBlock* const predBlock : block->PredBlocksEditing())
53015295
{
53025296
/* change all jumps/refs to the removed block */
53035297
switch (predBlock->GetKind())

src/coreclr/jit/fgehopt.cpp

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2002,36 +2002,30 @@ PhaseStatus Compiler::fgTailMergeThrows()
20022002

20032003
// Walk pred list of the non canonical block, updating flow to target
20042004
// the canonical block instead.
2005-
for (FlowEdge* predEdge = nonCanonicalBlock->bbPreds; predEdge != nullptr; predEdge = nextPredEdge)
2005+
for (BasicBlock* const predBlock : nonCanonicalBlock->PredBlocksEditing())
20062006
{
2007-
BasicBlock* const predBlock = predEdge->getSourceBlock();
2008-
nextPredEdge = predEdge->getNextPredEdge();
2009-
20102007
switch (predBlock->GetKind())
20112008
{
2012-
case BBJ_ALWAYS:
2013-
{
2014-
fgTailMergeThrowsJumpToHelper(predBlock, nonCanonicalBlock, canonicalBlock, predEdge);
2015-
updated = true;
2016-
}
2017-
break;
2018-
2009+
// TODO: Use fgReplaceJumpTarget once it preserves edge weights for BBJ_COND blocks
20192010
case BBJ_COND:
20202011
{
2021-
// Flow to non canonical block could be via fall through or jump or both.
2012+
// Flow to non canonical block could be via true or false target.
20222013
if (predBlock->FalseTargetIs(nonCanonicalBlock))
20232014
{
2024-
fgTailMergeThrowsFallThroughHelper(predBlock, nonCanonicalBlock, canonicalBlock, predEdge);
2015+
fgTailMergeThrowsFallThroughHelper(predBlock, nonCanonicalBlock, canonicalBlock,
2016+
predBlock->GetFalseEdge());
20252017
}
20262018

20272019
if (predBlock->TrueTargetIs(nonCanonicalBlock))
20282020
{
2029-
fgTailMergeThrowsJumpToHelper(predBlock, nonCanonicalBlock, canonicalBlock, predEdge);
2021+
fgTailMergeThrowsJumpToHelper(predBlock, nonCanonicalBlock, canonicalBlock,
2022+
predBlock->GetTrueEdge());
20302023
}
20312024
updated = true;
20322025
}
20332026
break;
20342027

2028+
case BBJ_ALWAYS:
20352029
case BBJ_SWITCH:
20362030
{
20372031
JITDUMP("*** " FMT_BB " now branching to " FMT_BB "\n", predBlock->bbNum, canonicalBlock->bbNum);
@@ -2127,21 +2121,12 @@ void Compiler::fgTailMergeThrowsJumpToHelper(BasicBlock* predBlock,
21272121
{
21282122
JITDUMP("*** " FMT_BB " now branching to " FMT_BB "\n", predBlock->bbNum, canonicalBlock->bbNum);
21292123

2130-
if (predBlock->KindIs(BBJ_ALWAYS))
2131-
{
2132-
// Update flow to new target
2133-
assert(predBlock->TargetIs(nonCanonicalBlock));
2134-
fgRedirectTargetEdge(predBlock, canonicalBlock);
2135-
}
2136-
else
2137-
{
2138-
// Remove the old flow
2139-
assert(predBlock->KindIs(BBJ_COND));
2140-
assert(predBlock->TrueTargetIs(nonCanonicalBlock));
2141-
fgRemoveRefPred(predBlock->GetTrueEdge());
2142-
2143-
// Wire up the new flow
2144-
FlowEdge* const trueEdge = fgAddRefPred(canonicalBlock, predBlock, predEdge);
2145-
predBlock->SetTrueEdge(trueEdge);
2146-
}
2124+
// Remove the old flow
2125+
assert(predBlock->KindIs(BBJ_COND));
2126+
assert(predBlock->TrueTargetIs(nonCanonicalBlock));
2127+
fgRemoveRefPred(predBlock->GetTrueEdge());
2128+
2129+
// Wire up the new flow
2130+
FlowEdge* const trueEdge = fgAddRefPred(canonicalBlock, predBlock, predEdge);
2131+
predBlock->SetTrueEdge(trueEdge);
21472132
}

src/coreclr/jit/optimizer.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2247,7 +2247,7 @@ bool Compiler::optInvertWhileLoop(BasicBlock* block)
22472247
//
22482248
unsigned const loopFirstNum = bTop->bbNum;
22492249
unsigned const loopBottomNum = bTest->bbNum;
2250-
for (BasicBlock* const predBlock : bTest->PredBlocks())
2250+
for (BasicBlock* const predBlock : bTest->PredBlocksEditing())
22512251
{
22522252
unsigned const bNum = predBlock->bbNum;
22532253
if ((loopFirstNum <= bNum) && (bNum <= loopBottomNum))
@@ -3154,7 +3154,7 @@ bool Compiler::optCanonicalizeExit(FlowGraphNaturalLoop* loop, BasicBlock* exit)
31543154
JITDUMP("Created new exit " FMT_BB " to replace " FMT_BB " for " FMT_LP "\n", newExit->bbNum, exit->bbNum,
31553155
loop->GetIndex());
31563156

3157-
for (BasicBlock* pred : exit->PredBlocks())
3157+
for (BasicBlock* pred : exit->PredBlocksEditing())
31583158
{
31593159
if (loop->ContainsBlock(pred))
31603160
{

src/coreclr/jit/redundantbranchopts.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1600,7 +1600,7 @@ bool Compiler::optJumpThreadCore(JumpThreadInfo& jti)
16001600
// If this pred is in the set that will reuse block, do nothing.
16011601
// Else revise pred to branch directly to the appropriate successor of block.
16021602
//
1603-
for (BasicBlock* const predBlock : jti.m_block->PredBlocks())
1603+
for (BasicBlock* const predBlock : jti.m_block->PredBlocksEditing())
16041604
{
16051605
// If this was an ambiguous pred, skip.
16061606
//

0 commit comments

Comments
 (0)