Skip to content

Commit 70b91f5

Browse files
ChuanqiXu9eymay
authored andcommitted
Revert "[Coroutines] Add an O(n) algorithm for computing the cross suspend point"
This reverts commit bb4121e. Sorry for forgetting adding Differential Revision information. It may worth reverting this one and commit it again given this is a relative big patch.
1 parent fa61199 commit 70b91f5

File tree

1 file changed

+68
-150
lines changed

1 file changed

+68
-150
lines changed

llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Lines changed: 68 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -98,57 +98,24 @@ class SuspendCrossingInfo {
9898
bool Suspend = false;
9999
bool End = false;
100100
bool KillLoop = false;
101+
bool Changed = false;
101102
};
102103
SmallVector<BlockData, SmallVectorThreshold> Block;
103104

104105
iterator_range<pred_iterator> predecessors(BlockData const &BD) const {
105106
BasicBlock *BB = Mapping.indexToBlock(&BD - &Block[0]);
106107
return llvm::predecessors(BB);
107108
}
108-
size_t pred_size(BlockData const &BD) const {
109-
BasicBlock *BB = Mapping.indexToBlock(&BD - &Block[0]);
110-
return llvm::pred_size(BB);
111-
}
112-
iterator_range<succ_iterator> successors(BlockData const &BD) const {
113-
BasicBlock *BB = Mapping.indexToBlock(&BD - &Block[0]);
114-
return llvm::successors(BB);
115-
}
116109

117110
BlockData &getBlockData(BasicBlock *BB) {
118111
return Block[Mapping.blockToIndex(BB)];
119112
}
120113

121-
/// This algorithm is based on topological sorting. As we know, topological
122-
/// sorting is typically used on Directed Acyclic Graph (DAG). However, a
123-
/// Control Flow Graph (CFG) may not always be a DAG, as it can contain back
124-
/// edges or loops. To handle this, we need to break the back edge when we
125-
/// encounter it in order to ensure a valid topological sorting.
126-
/// Why do we need an extra traversal when a CFG contains a back edge?
127-
/// Firstly, we need to figure out how the Consumes information propagates
128-
/// along the back edge. For example,
129-
///
130-
/// A -> B -> C -> D -> H
131-
/// ^ |
132-
/// | v
133-
/// G <- F <- E
134-
///
135-
/// Following the direction of the arrow, we can obtain the traversal
136-
/// sequences: A, B, C, D, H, E, F, G or A, B, C, D, E, H, F, G. We know that
137-
/// there is a path from C to G after the first traversal. However, we are
138-
/// uncertain about the existence of a path from G to C, as the Consumes info
139-
/// of G has not yet propagated to C (via B). Therefore, we need a second
140-
/// traversal to propagate G's Consumes info to C (via B) and its successors.
141-
/// The second traversal allows us to obtain the complete Consumes info. Since
142-
/// the computation of the Kills info depends on the Consumes info.
143-
144-
/// The parameter "EntryNo" represents the index associated with the entry
145-
/// block.
146-
/// The parameter "BlockPredecessorsNum" represents the number of predecessors
147-
/// for each block.
148-
/// Returns true if there exists back edges in CFG.
149-
template <bool HasBackEdge = false>
150-
bool collectConsumeKillInfo(size_t EntryNo,
151-
const SmallVector<size_t> &BlockPredecessorsNum);
114+
/// Compute the BlockData for the current function in one iteration.
115+
/// Returns whether the BlockData changes in this iteration.
116+
/// Initialize - Whether this is the first iteration, we can optimize
117+
/// the initial case a little bit by manual loop switch.
118+
template <bool Initialize = false> bool computeBlockData();
152119

153120
public:
154121
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
@@ -256,132 +223,84 @@ LLVM_DUMP_METHOD void SuspendCrossingInfo::dump() const {
256223
}
257224
#endif
258225

259-
template <bool HasBackEdge>
260-
bool SuspendCrossingInfo::collectConsumeKillInfo(
261-
size_t EntryNo, const SmallVector<size_t> &BlockPredecessorsNum) {
262-
bool FoundBackEdge = false;
263-
SmallVector<size_t> UnvisitedBlockPredNum = BlockPredecessorsNum;
264-
// BlockNo Queue with BlockPredNum[BlockNo] equal to zero.
265-
std::queue<size_t> CandidateQueue;
266-
// For blocks that maybe has a back edge.
267-
DenseSet<size_t> MaybeBackEdgeSet;
268-
// Visit BlockNo
269-
auto visit = [&](size_t BlockNo) {
270-
switch (UnvisitedBlockPredNum[BlockNo]) {
271-
// Already visited, not visit again.
272-
case 0:
273-
break;
274-
// If predecessors number of BlockNo is 1, it means all predecessors of
275-
// BlockNo have propagated its info to BlockNo. So add BlockNo to
276-
// CandidateQueue.
277-
case 1: {
278-
CandidateQueue.push(BlockNo);
279-
MaybeBackEdgeSet.erase(BlockNo);
280-
UnvisitedBlockPredNum[BlockNo] = 0;
281-
break;
282-
}
283-
// If predecessors number of BlockNo bigger than 1, it means BlockNo not
284-
// collect full Consumes/Kills info yet. So decrease
285-
// UnvisitedBlockPredNum[BlockNo] and insert BlockNo into MaybeBackEdgeSet.
286-
default: {
287-
UnvisitedBlockPredNum[BlockNo]--;
288-
MaybeBackEdgeSet.insert(BlockNo);
289-
break;
290-
}
291-
}
292-
};
226+
template <bool Initialize> bool SuspendCrossingInfo::computeBlockData() {
227+
const size_t N = Mapping.size();
228+
bool Changed = false;
293229

294-
CandidateQueue.push(EntryNo);
295-
296-
// Topological sorting.
297-
while (!CandidateQueue.empty()) {
298-
auto &B = Block[CandidateQueue.front()];
299-
CandidateQueue.pop();
300-
for (BasicBlock *SI : successors(B)) {
301-
auto SuccNo = Mapping.blockToIndex(SI);
302-
auto &S = Block[SuccNo];
303-
304-
// Propagate Kills and Consumes from predecessors into S.
305-
S.Consumes |= B.Consumes;
306-
S.Kills |= B.Kills;
307-
308-
if (B.Suspend)
309-
S.Kills |= B.Consumes;
310-
311-
if (S.Suspend) {
312-
// If block S is a suspend block, it should kill all of the blocks
313-
// it consumes.
314-
S.Kills |= S.Consumes;
315-
} else if (S.End) {
316-
// If block S is an end block, it should not propagate kills as the
317-
// blocks following coro.end() are reached during initial invocation
318-
// of the coroutine while all the data are still available on the
319-
// stack or in the registers.
320-
S.Kills.reset();
321-
} else {
322-
// This is reached when S block it not Suspend nor coro.end and it
323-
// need to make sure that it is not in the kill set.
324-
S.KillLoop |= S.Kills[SuccNo];
325-
S.Kills.reset(SuccNo);
230+
for (size_t I = 0; I < N; ++I) {
231+
auto &B = Block[I];
232+
233+
// We don't need to count the predecessors when initialization.
234+
if constexpr (!Initialize)
235+
// If all the predecessors of the current Block don't change,
236+
// the BlockData for the current block must not change too.
237+
if (all_of(predecessors(B), [this](BasicBlock *BB) {
238+
return !Block[Mapping.blockToIndex(BB)].Changed;
239+
})) {
240+
B.Changed = false;
241+
continue;
326242
}
327-
// visit SuccNo.
328-
visit(SuccNo);
329-
}
330243

331-
// If the CandidateQueue is empty but the MaybeBackEdgeSet is not, it
332-
// indicates the presence of a back edge that needs to be addressed. In such
333-
// cases, it is necessary to break the back edge.
334-
if (CandidateQueue.empty() && !MaybeBackEdgeSet.empty()) {
335-
FoundBackEdge = true;
336-
size_t CandidateNo = -1;
337-
if constexpr (HasBackEdge) {
338-
auto IsCandidate = [this](size_t I) {
339-
for (BasicBlock *PI : llvm::predecessors(Mapping.indexToBlock(I))) {
340-
auto PredNo = Mapping.blockToIndex(PI);
341-
auto &P = Block[PredNo];
342-
// The node I can reach its predecessor. So we found a loop.
343-
if (P.Consumes[I])
344-
return true;
345-
}
244+
// Saved Consumes and Kills bitsets so that it is easy to see
245+
// if anything changed after propagation.
246+
auto SavedConsumes = B.Consumes;
247+
auto SavedKills = B.Kills;
346248

347-
return false;
348-
};
249+
for (BasicBlock *PI : predecessors(B)) {
250+
auto PrevNo = Mapping.blockToIndex(PI);
251+
auto &P = Block[PrevNo];
349252

350-
for (auto I : MaybeBackEdgeSet) {
351-
if (IsCandidate(I)) {
352-
CandidateNo = I;
353-
break;
354-
}
355-
}
356-
assert(CandidateNo != size_t(-1) && "We collected the wrong backegdes");
357-
} else
358-
// When the value of HasBackEdge is false and we don't have any
359-
// information about back edges, we can simply select one block from the
360-
// MaybeBackEdgeSet.
361-
CandidateNo = *(MaybeBackEdgeSet.begin());
362-
CandidateQueue.push(CandidateNo);
363-
MaybeBackEdgeSet.erase(CandidateNo);
364-
UnvisitedBlockPredNum[CandidateNo] = 0;
253+
// Propagate Kills and Consumes from predecessors into B.
254+
B.Consumes |= P.Consumes;
255+
B.Kills |= P.Kills;
256+
257+
// If block P is a suspend block, it should propagate kills into block
258+
// B for every block P consumes.
259+
if (P.Suspend)
260+
B.Kills |= P.Consumes;
261+
}
262+
263+
if (B.Suspend) {
264+
// If block S is a suspend block, it should kill all of the blocks it
265+
// consumes.
266+
B.Kills |= B.Consumes;
267+
} else if (B.End) {
268+
// If block B is an end block, it should not propagate kills as the
269+
// blocks following coro.end() are reached during initial invocation
270+
// of the coroutine while all the data are still available on the
271+
// stack or in the registers.
272+
B.Kills.reset();
273+
} else {
274+
// This is reached when B block it not Suspend nor coro.end and it
275+
// need to make sure that it is not in the kill set.
276+
B.KillLoop |= B.Kills[I];
277+
B.Kills.reset(I);
278+
}
279+
280+
if constexpr (!Initialize) {
281+
B.Changed = (B.Kills != SavedKills) || (B.Consumes != SavedConsumes);
282+
Changed |= B.Changed;
365283
}
366284
}
367-
return FoundBackEdge;
285+
286+
if constexpr (Initialize)
287+
return true;
288+
289+
return Changed;
368290
}
369291

370292
SuspendCrossingInfo::SuspendCrossingInfo(Function &F, coro::Shape &Shape)
371293
: Mapping(F) {
372294
const size_t N = Mapping.size();
373295
Block.resize(N);
374296

375-
size_t EntryNo = Mapping.blockToIndex(&(F.getEntryBlock()));
376-
SmallVector<size_t> BlockPredecessorsNum(N, 0);
377-
378297
// Initialize every block so that it consumes itself
379298
for (size_t I = 0; I < N; ++I) {
380299
auto &B = Block[I];
381300
B.Consumes.resize(N);
382301
B.Kills.resize(N);
383302
B.Consumes.set(I);
384-
BlockPredecessorsNum[I] = pred_size(B);
303+
B.Changed = true;
385304
}
386305

387306
// Mark all CoroEnd Blocks. We do not propagate Kills beyond coro.ends as
@@ -406,11 +325,10 @@ SuspendCrossingInfo::SuspendCrossingInfo(Function &F, coro::Shape &Shape)
406325
markSuspendBlock(Save);
407326
}
408327

409-
// We should collect the Consumes and Kills information initially. If there is
410-
// a back edge present, it is necessary to perform the collection process
411-
// again.
412-
if (collectConsumeKillInfo(EntryNo, BlockPredecessorsNum))
413-
collectConsumeKillInfo</*HasBackEdge*/ true>(EntryNo, BlockPredecessorsNum);
328+
computeBlockData</*Initialize=*/true>();
329+
330+
while (computeBlockData())
331+
;
414332

415333
LLVM_DEBUG(dump());
416334
}

0 commit comments

Comments
 (0)