@@ -98,57 +98,24 @@ class SuspendCrossingInfo {
98
98
bool Suspend = false ;
99
99
bool End = false ;
100
100
bool KillLoop = false ;
101
+ bool Changed = false ;
101
102
};
102
103
SmallVector<BlockData, SmallVectorThreshold> Block;
103
104
104
105
iterator_range<pred_iterator> predecessors (BlockData const &BD) const {
105
106
BasicBlock *BB = Mapping.indexToBlock (&BD - &Block[0 ]);
106
107
return llvm::predecessors (BB);
107
108
}
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
- }
116
109
117
110
BlockData &getBlockData (BasicBlock *BB) {
118
111
return Block[Mapping.blockToIndex (BB)];
119
112
}
120
113
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 ();
152
119
153
120
public:
154
121
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
@@ -256,132 +223,84 @@ LLVM_DUMP_METHOD void SuspendCrossingInfo::dump() const {
256
223
}
257
224
#endif
258
225
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 ;
293
229
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 ;
326
242
}
327
- // visit SuccNo.
328
- visit (SuccNo);
329
- }
330
243
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 ;
346
248
347
- return false ;
348
- };
249
+ for (BasicBlock *PI : predecessors (B)) {
250
+ auto PrevNo = Mapping.blockToIndex (PI);
251
+ auto &P = Block[PrevNo];
349
252
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 ;
365
283
}
366
284
}
367
- return FoundBackEdge;
285
+
286
+ if constexpr (Initialize)
287
+ return true ;
288
+
289
+ return Changed;
368
290
}
369
291
370
292
SuspendCrossingInfo::SuspendCrossingInfo (Function &F, coro::Shape &Shape)
371
293
: Mapping(F) {
372
294
const size_t N = Mapping.size ();
373
295
Block.resize (N);
374
296
375
- size_t EntryNo = Mapping.blockToIndex (&(F.getEntryBlock ()));
376
- SmallVector<size_t > BlockPredecessorsNum (N, 0 );
377
-
378
297
// Initialize every block so that it consumes itself
379
298
for (size_t I = 0 ; I < N; ++I) {
380
299
auto &B = Block[I];
381
300
B.Consumes .resize (N);
382
301
B.Kills .resize (N);
383
302
B.Consumes .set (I);
384
- BlockPredecessorsNum[I] = pred_size (B) ;
303
+ B. Changed = true ;
385
304
}
386
305
387
306
// Mark all CoroEnd Blocks. We do not propagate Kills beyond coro.ends as
@@ -406,11 +325,10 @@ SuspendCrossingInfo::SuspendCrossingInfo(Function &F, coro::Shape &Shape)
406
325
markSuspendBlock (Save);
407
326
}
408
327
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
+ ;
414
332
415
333
LLVM_DEBUG (dump ());
416
334
}
0 commit comments