Skip to content

Commit c3445c7

Browse files
JIT: Consolidate block pred iterator logic (#111009)
Move most of the nested iterator class logic in `PredEdgeList`/`PredBlockList` to a base class, and implement subclasses that specify the yield type (flow edge or basic block) and whether edits are allowed. This allows us to add a pred edge iterator that tolerates edits with minimal code duplication.
1 parent f376329 commit c3445c7

File tree

1 file changed

+69
-82
lines changed

1 file changed

+69
-82
lines changed

src/coreclr/jit/block.h

Lines changed: 69 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -204,42 +204,58 @@ struct allMemoryKinds
204204
}
205205
};
206206

207+
// Base class for forward iterators over the predecessor edge linked list.
208+
// Subclasses decide what the iterator yields (edge, source block, etc.) by implementing the dereference operator.
209+
// The pred list cannot be modified during iteration unless allowEdits is true.
210+
//
211+
template <bool allowEdits>
212+
class BasePredIterator
213+
{
214+
private:
215+
// When allowEdits=false, try to guard against the user of the iterator from modifying the predecessor list
216+
// being traversed: cache the edge we think should be next, then check it when we actually do the `++`
217+
// operation. This is a bit conservative, but attempts to protect against callers assuming too much about
218+
// this iterator implementation.
219+
// When allowEdits=true, m_next is always used to update m_pred, so changes to m_pred don't break the iterator.
220+
FlowEdge* m_next;
221+
222+
protected:
223+
FlowEdge* m_pred;
224+
225+
BasePredIterator(FlowEdge* pred);
226+
227+
public:
228+
BasePredIterator& operator++();
229+
230+
bool operator!=(const BasePredIterator& i) const
231+
{
232+
return m_pred != i.m_pred;
233+
}
234+
};
235+
207236
// PredEdgeList: adapter class for forward iteration of the predecessor edge linked list using range-based `for`,
208237
// normally used via BasicBlock::PredEdges(), e.g.:
209238
// for (FlowEdge* const edge : block->PredEdges()) ...
239+
// allowEdits controls whether the iterator should be resilient to changes to the predecessor list.
210240
//
241+
template <bool allowEdits>
211242
class PredEdgeList
212243
{
213244
FlowEdge* m_begin;
214245

215246
// Forward iterator for the predecessor edges linked list.
216-
// The caller can't make changes to the preds list when using this.
217247
//
218-
class iterator
248+
class PredEdgeIterator : public BasePredIterator<allowEdits>
219249
{
220-
FlowEdge* m_pred;
221-
222-
#ifdef DEBUG
223-
// Try to guard against the user of the iterator from making changes to the IR that would invalidate
224-
// the iterator: cache the edge we think should be next, then check it when we actually do the `++`
225-
// operation. This is a bit conservative, but attempts to protect against callers assuming too much about
226-
// this iterator implementation.
227-
FlowEdge* m_next;
228-
#endif
229-
230250
public:
231-
iterator(FlowEdge* pred);
232-
233-
FlowEdge* operator*() const
251+
PredEdgeIterator(FlowEdge* pred)
252+
: BasePredIterator<allowEdits>(pred)
234253
{
235-
return m_pred;
236254
}
237255

238-
iterator& operator++();
239-
240-
bool operator!=(const iterator& i) const
256+
FlowEdge* operator*() const
241257
{
242-
return m_pred != i.m_pred;
258+
return this->m_pred;
243259
}
244260
};
245261

@@ -249,14 +265,14 @@ class PredEdgeList
249265
{
250266
}
251267

252-
iterator begin() const
268+
PredEdgeIterator begin() const
253269
{
254-
return iterator(m_begin);
270+
return PredEdgeIterator(m_begin);
255271
}
256272

257-
iterator end() const
273+
PredEdgeIterator end() const
258274
{
259-
return iterator(nullptr);
275+
return PredEdgeIterator(nullptr);
260276
}
261277
};
262278

@@ -271,30 +287,16 @@ class PredBlockList
271287
FlowEdge* m_begin;
272288

273289
// Forward iterator for the predecessor edges linked list, yielding the predecessor block, not the edge.
274-
// The caller can't make changes to the preds list when using this.
275290
//
276-
class iterator
291+
class PredBlockIterator : public BasePredIterator<allowEdits>
277292
{
278-
FlowEdge* m_pred;
279-
280-
// When allowEdits=false, try to guard against the user of the iterator from modifying the predecessor list
281-
// being traversed: cache the edge we think should be next, then check it when we actually do the `++`
282-
// operation. This is a bit conservative, but attempts to protect against callers assuming too much about
283-
// this iterator implementation.
284-
// When allowEdits=true, m_next is always used to update m_pred, so changes to m_pred don't break the iterator.
285-
FlowEdge* m_next;
286-
287293
public:
288-
iterator(FlowEdge* pred);
289-
290-
BasicBlock* operator*() const;
291-
292-
iterator& operator++();
293-
294-
bool operator!=(const iterator& i) const
294+
PredBlockIterator(FlowEdge* pred)
295+
: BasePredIterator<allowEdits>(pred)
295296
{
296-
return m_pred != i.m_pred;
297297
}
298+
299+
BasicBlock* operator*() const;
298300
};
299301

300302
public:
@@ -303,14 +305,14 @@ class PredBlockList
303305
{
304306
}
305307

306-
iterator begin() const
308+
PredBlockIterator begin() const
307309
{
308-
return iterator(m_begin);
310+
return PredBlockIterator(m_begin);
309311
}
310312

311-
iterator end() const
313+
PredBlockIterator end() const
312314
{
313-
return iterator(nullptr);
315+
return PredBlockIterator(nullptr);
314316
}
315317
};
316318

@@ -1536,9 +1538,18 @@ struct BasicBlock : private LIR::Range
15361538
// PredEdges: convenience method for enabling range-based `for` iteration over predecessor edges, e.g.:
15371539
// for (FlowEdge* const edge : block->PredEdges()) ...
15381540
//
1539-
PredEdgeList PredEdges() const
1541+
PredEdgeList<false> PredEdges() const
15401542
{
1541-
return PredEdgeList(bbPreds);
1543+
return PredEdgeList<false>(bbPreds);
1544+
}
1545+
1546+
// PredEdgesEditing: convenience method for enabling range-based `for` iteration over predecessor edges, e.g.:
1547+
// for (FlowEdge* const edge : block->PredEdges()) ...
1548+
// This iterator tolerates modifications to bbPreds.
1549+
//
1550+
PredEdgeList<true> PredEdgesEditing() const
1551+
{
1552+
return PredEdgeList<true>(bbPreds);
15421553
}
15431554

15441555
// PredBlocks: convenience method for enabling range-based `for` iteration over predecessor blocks, e.g.:
@@ -2424,30 +2435,8 @@ inline BasicBlock* BBArrayIterator::operator*() const
24242435

24252436
// Pred list iterator implementations (that are required to be defined after the declaration of BasicBlock and FlowEdge)
24262437

2427-
inline PredEdgeList::iterator::iterator(FlowEdge* pred)
2428-
: m_pred(pred)
2429-
{
2430-
#ifdef DEBUG
2431-
m_next = (m_pred == nullptr) ? nullptr : m_pred->getNextPredEdge();
2432-
#endif
2433-
}
2434-
2435-
inline PredEdgeList::iterator& PredEdgeList::iterator::operator++()
2436-
{
2437-
FlowEdge* next = m_pred->getNextPredEdge();
2438-
2439-
#ifdef DEBUG
2440-
// Check that the next block is the one we expect to see.
2441-
assert(next == m_next);
2442-
m_next = (next == nullptr) ? nullptr : next->getNextPredEdge();
2443-
#endif // DEBUG
2444-
2445-
m_pred = next;
2446-
return *this;
2447-
}
2448-
24492438
template <bool allowEdits>
2450-
inline PredBlockList<allowEdits>::iterator::iterator(FlowEdge* pred)
2439+
inline BasePredIterator<allowEdits>::BasePredIterator(FlowEdge* pred)
24512440
: m_pred(pred)
24522441
{
24532442
bool initNextPointer = allowEdits;
@@ -2459,13 +2448,7 @@ inline PredBlockList<allowEdits>::iterator::iterator(FlowEdge* pred)
24592448
}
24602449

24612450
template <bool allowEdits>
2462-
inline BasicBlock* PredBlockList<allowEdits>::iterator::operator*() const
2463-
{
2464-
return m_pred->getSourceBlock();
2465-
}
2466-
2467-
template <bool allowEdits>
2468-
inline typename PredBlockList<allowEdits>::iterator& PredBlockList<allowEdits>::iterator::operator++()
2451+
inline BasePredIterator<allowEdits>& BasePredIterator<allowEdits>::operator++()
24692452
{
24702453
if (allowEdits)
24712454
{
@@ -2477,18 +2460,22 @@ inline typename PredBlockList<allowEdits>::iterator& PredBlockList<allowEdits>::
24772460
{
24782461
FlowEdge* next = m_pred->getNextPredEdge();
24792462

2480-
#ifdef DEBUG
24812463
// If allowEdits=false, check that the next block is the one we expect to see.
24822464
assert(next == m_next);
2483-
m_next = (m_next == nullptr) ? nullptr : m_next->getNextPredEdge();
2484-
#endif // DEBUG
2465+
INDEBUG(m_next = (m_next == nullptr) ? nullptr : m_next->getNextPredEdge());
24852466

24862467
m_pred = next;
24872468
}
24882469

24892470
return *this;
24902471
}
24912472

2473+
template <bool allowEdits>
2474+
inline BasicBlock* PredBlockList<allowEdits>::PredBlockIterator::operator*() const
2475+
{
2476+
return this->m_pred->getSourceBlock();
2477+
}
2478+
24922479
/*****************************************************************************
24932480
*
24942481
* The following call-backs supplied by the client; it's used by the code

0 commit comments

Comments
 (0)