Skip to content

Commit bd753d1

Browse files
committed
standardize BasicBlockWorkqueue
The pattern of using a breadth-first traversal over a control-flow graph shows up in a few places within the SIL optimizer, so this data-structure unifies those manually-implemented traversals into a common utility.
1 parent 9d342f1 commit bd753d1

File tree

1 file changed

+70
-3
lines changed

1 file changed

+70
-3
lines changed

include/swift/SIL/BasicBlockDatastructures.h

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#include "swift/SIL/StackList.h"
2121
#include "swift/SIL/BasicBlockBits.h"
22+
#include <deque>
2223

2324
namespace swift {
2425

@@ -61,10 +62,12 @@ class BasicBlockSetVector {
6162
}
6263
};
6364

64-
/// A utility for processing basic blocks in a worklist.
65+
/// A utility for processing basic blocks in a worklist that uses
66+
/// last-in-first-out order.
6567
///
66-
/// It is basically a combination of a block vector and a block set. It can be
67-
/// used for typical worklist-processing algorithms.
68+
/// It is basically a combination of a stack list and a block set. It can be
69+
/// used for typical worklist-processing algorithms and is recommended in
70+
/// nearly all situations as its implementation is well-optimized.
6871
class BasicBlockWorklist {
6972
StackList<SILBasicBlock *> worklist;
7073
BasicBlockSet visited;
@@ -119,6 +122,70 @@ class BasicBlockWorklist {
119122
bool isVisited(SILBasicBlock *block) const { return visited.contains(block); }
120123
};
121124

125+
126+
/// A utility for processing basic blocks in a work-queue that uses
127+
/// first-in-first-out order.
128+
///
129+
/// It is basically a combination of a std::deque and a block set. It can be
130+
/// used for certain kinds of worklist-processing algorithms.
131+
///
132+
/// When in doubt, you should reach for a BasicBlockWorklist instead.
133+
class BasicBlockWorkqueue {
134+
std::deque<SILBasicBlock*> workqueue;
135+
BasicBlockSet visited;
136+
137+
public:
138+
/// Construct an empty workqueue.
139+
BasicBlockWorkqueue(SILFunction *function)
140+
: workqueue(), visited(function) {}
141+
142+
/// Initialize the workqueue with \p initialBlock.
143+
BasicBlockWorkqueue(SILBasicBlock *initialBlock)
144+
: BasicBlockWorkqueue(initialBlock->getParent()) {
145+
push(initialBlock);
146+
}
147+
148+
/// Pops the next element from the front of the workqueue or returns null,
149+
/// if the workqueue is empty.
150+
SILBasicBlock *pop() {
151+
if (workqueue.empty())
152+
return nullptr;
153+
SILBasicBlock *block = workqueue.front();
154+
workqueue.pop_front();
155+
return block;
156+
}
157+
158+
/// Pushes \p block onto the end of the workqueue if \p block has never been
159+
/// pushed before.
160+
bool pushIfNotVisited(SILBasicBlock *block) {
161+
if (visited.insert(block)) {
162+
workqueue.push_back(block);
163+
return true;
164+
}
165+
return false;
166+
}
167+
168+
/// Like `pushIfNotVisited`, but requires that \p block has never been on the
169+
/// workqueue before.
170+
void push(SILBasicBlock *block) {
171+
assert(!visited.contains(block));
172+
visited.insert(block);
173+
workqueue.push_back(block);
174+
}
175+
176+
/// Like `pop`, but marks the returned block as "unvisited". This means, that
177+
/// the block can be pushed onto the queue again.
178+
SILBasicBlock *popAndForget() {
179+
SILBasicBlock *block = pop();
180+
if (block)
181+
visited.erase(block);
182+
return block;
183+
}
184+
185+
/// Returns true if \p block was visited, i.e. has been added to the workqueue
186+
bool isVisited(SILBasicBlock *block) const { return visited.contains(block); }
187+
};
188+
122189
} // namespace swift
123190

124191
#endif

0 commit comments

Comments
 (0)