8
8
9
9
#include " llvm/Transforms/Coroutines/CoroElide.h"
10
10
#include " CoroInternal.h"
11
+ #include " llvm/ADT/DenseMap.h"
11
12
#include " llvm/Analysis/AliasAnalysis.h"
12
13
#include " llvm/Analysis/InstructionSimplify.h"
13
14
#include " llvm/IR/Dominators.h"
@@ -27,8 +28,9 @@ struct Lowerer : coro::LowererBase {
27
28
SmallVector<CoroBeginInst *, 1 > CoroBegins;
28
29
SmallVector<CoroAllocInst *, 1 > CoroAllocs;
29
30
SmallVector<CoroSubFnInst *, 4 > ResumeAddr;
30
- SmallVector<CoroSubFnInst *, 4 > DestroyAddr;
31
+ DenseMap<CoroBeginInst *, SmallVector<CoroSubFnInst *, 4 > > DestroyAddr;
31
32
SmallVector<CoroFreeInst *, 1 > CoroFrees;
33
+ CoroSuspendInst *CoroFinalSuspend;
32
34
33
35
Lowerer (Module &M) : LowererBase(M) {}
34
36
@@ -146,33 +148,62 @@ bool Lowerer::shouldElide(Function *F, DominatorTree &DT) const {
146
148
if (CoroAllocs.empty ())
147
149
return false ;
148
150
149
- // Check that for every coro.begin there is a coro.destroy directly
150
- // referencing the SSA value of that coro.begin along a non-exceptional path.
151
+ // Check that for every coro.begin there is at least one coro.destroy directly
152
+ // referencing the SSA value of that coro.begin along each
153
+ // non-exceptional path.
151
154
// If the value escaped, then coro.destroy would have been referencing a
152
155
// memory ___location storing that value and not the virtual register.
153
156
154
- // First gather all of the non-exceptional terminators for the function.
155
157
SmallPtrSet<Instruction *, 8 > Terminators;
156
- for (BasicBlock &B : *F) {
157
- auto *TI = B.getTerminator ();
158
- if (TI->getNumSuccessors () == 0 && !TI->isExceptionalTerminator () &&
159
- !isa<UnreachableInst>(TI))
160
- Terminators.insert (TI);
158
+ bool HasMultiPred = false ;
159
+ // First gather all of the non-exceptional terminators for the function.
160
+ // Consider the final coro.suspend as the real terminator when the current
161
+ // function is a coroutine.
162
+ if (CoroFinalSuspend) {
163
+ // If block of final coro.suspend has more than one predecessor,
164
+ // then there is one resume path and the others are exceptional paths,
165
+ // consider these predecessors as terminators.
166
+ BasicBlock *FinalBB = CoroFinalSuspend->getParent ();
167
+ if (FinalBB->hasNPredecessorsOrMore (2 )) {
168
+ HasMultiPred = true ;
169
+ for (auto *B : predecessors (FinalBB))
170
+ Terminators.insert (B->getTerminator ());
171
+ } else
172
+ Terminators.insert (CoroFinalSuspend);
173
+ } else {
174
+ for (BasicBlock &B : *F) {
175
+ auto *TI = B.getTerminator ();
176
+ if (TI->getNumSuccessors () == 0 && !TI->isExceptionalTerminator () &&
177
+ !isa<UnreachableInst>(TI))
178
+ Terminators.insert (TI);
179
+ }
161
180
}
162
181
163
182
// Filter out the coro.destroy that lie along exceptional paths.
164
183
SmallPtrSet<CoroSubFnInst *, 4 > DAs;
165
- for (CoroSubFnInst *DA : DestroyAddr) {
166
- for (Instruction *TI : Terminators) {
167
- if (DT.dominates (DA, TI)) {
168
- DAs.insert (DA);
169
- break ;
184
+ SmallPtrSet<Instruction *, 2 > TIs;
185
+ SmallPtrSet<CoroBeginInst *, 8 > ReferencedCoroBegins;
186
+ for (auto &It : DestroyAddr) {
187
+ for (CoroSubFnInst *DA : It.second ) {
188
+ for (Instruction *TI : Terminators) {
189
+ if (DT.dominates (DA, TI)) {
190
+ if (HasMultiPred)
191
+ TIs.insert (TI);
192
+ else
193
+ DAs.insert (DA);
194
+ break ;
195
+ }
170
196
}
171
197
}
198
+ // If all the predecessors dominate coro.destroys that reference same
199
+ // coro.begin, record the coro.begin
200
+ if (TIs.size () == Terminators.size ()) {
201
+ ReferencedCoroBegins.insert (It.first );
202
+ TIs.clear ();
203
+ }
172
204
}
173
205
174
206
// Find all the coro.begin referenced by coro.destroy along happy paths.
175
- SmallPtrSet<CoroBeginInst *, 8 > ReferencedCoroBegins;
176
207
for (CoroSubFnInst *DA : DAs) {
177
208
if (auto *CB = dyn_cast<CoroBeginInst>(DA->getFrame ()))
178
209
ReferencedCoroBegins.insert (CB);
@@ -188,12 +219,22 @@ bool Lowerer::shouldElide(Function *F, DominatorTree &DT) const {
188
219
189
220
void Lowerer::collectPostSplitCoroIds (Function *F) {
190
221
CoroIds.clear ();
191
- for (auto &I : instructions (F))
222
+ CoroFinalSuspend = nullptr ;
223
+ for (auto &I : instructions (F)) {
192
224
if (auto *CII = dyn_cast<CoroIdInst>(&I))
193
225
if (CII->getInfo ().isPostSplit ())
194
226
// If it is the coroutine itself, don't touch it.
195
227
if (CII->getCoroutine () != CII->getFunction ())
196
228
CoroIds.push_back (CII);
229
+
230
+ if (auto *CSI = dyn_cast<CoroSuspendInst>(&I))
231
+ if (CSI->isFinal ()) {
232
+ if (!CoroFinalSuspend)
233
+ CoroFinalSuspend = CSI;
234
+ else
235
+ report_fatal_error (" Only one suspend point can be marked as final" );
236
+ }
237
+ }
197
238
}
198
239
199
240
bool Lowerer::processCoroId (CoroIdInst *CoroId, AAResults &AA,
@@ -226,7 +267,7 @@ bool Lowerer::processCoroId(CoroIdInst *CoroId, AAResults &AA,
226
267
ResumeAddr.push_back (II);
227
268
break ;
228
269
case CoroSubFnInst::DestroyIndex:
229
- DestroyAddr.push_back (II);
270
+ DestroyAddr[CB] .push_back (II);
230
271
break ;
231
272
default :
232
273
llvm_unreachable (" unexpected coro.subfn.addr constant" );
@@ -249,7 +290,8 @@ bool Lowerer::processCoroId(CoroIdInst *CoroId, AAResults &AA,
249
290
Resumers,
250
291
ShouldElide ? CoroSubFnInst::CleanupIndex : CoroSubFnInst::DestroyIndex);
251
292
252
- replaceWithConstant (DestroyAddrConstant, DestroyAddr);
293
+ for (auto &It : DestroyAddr)
294
+ replaceWithConstant (DestroyAddrConstant, It.second );
253
295
254
296
if (ShouldElide) {
255
297
auto *FrameTy = getFrameType (cast<Function>(ResumeAddrConstant));
0 commit comments