LLVM: lib/Transforms/Coroutines/SpillUtils.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
18
19namespace llvm {
20
21namespace coro {
22
23namespace {
24
25typedef SmallPtrSet<BasicBlock *, 8> VisitedBlocksSet;
26
27
28
29static bool isCoroutineStructureIntrinsic(Instruction &I) {
30 return isa(&I) || isa(&I) ||
31 isa(&I);
32}
33
34
35
36static bool isSuspendReachableFrom(BasicBlock *From,
37 VisitedBlocksSet &VisitedOrFreeBBs) {
38
39
40
41 if (!VisitedOrFreeBBs.insert(From).second)
42 return false;
43
44
46 return true;
47
48
50 if (isSuspendReachableFrom(Succ, VisitedOrFreeBBs))
51 return true;
52 }
53
54 return false;
55}
56
57
58
59static bool isLocalAlloca(CoroAllocaAllocInst *AI) {
60
61
62 VisitedBlocksSet VisitedOrFreeBBs;
63 for (auto *User : AI->users()) {
64 if (auto FI = dyn_cast(User))
65 VisitedOrFreeBBs.insert(FI->getParent());
66 }
67
68 return !isSuspendReachableFrom(AI->getParent(), VisitedOrFreeBBs);
69}
70
71
72
73
74static Instruction *
75lowerNonLocalAlloca(CoroAllocaAllocInst *AI, const coro::Shape &Shape,
76 SmallVectorImpl<Instruction *> &DeadInsts) {
77 IRBuilder<> Builder(AI);
78 auto Alloc = Shape.emitAlloc(Builder, AI->getSize(), nullptr);
79
80 for (User *U : AI->users()) {
81 if (isa(U)) {
82 U->replaceAllUsesWith(Alloc);
83 } else {
84 auto FI = cast(U);
85 Builder.SetInsertPoint(FI);
86 Shape.emitDealloc(Builder, Alloc, nullptr);
87 }
88 DeadInsts.push_back(cast(U));
89 }
90
91
92 DeadInsts.push_back(AI);
93
94
95 return cast(Alloc);
96}
97
98
99
100
101
102
103
104
105
106
107static Instruction *splitBeforeCatchSwitch(CatchSwitchInst *CatchSwitch) {
108 BasicBlock *CurrentBlock = CatchSwitch->getParent();
109 BasicBlock *NewBlock = CurrentBlock->splitBasicBlock(CatchSwitch);
110 CurrentBlock->getTerminator()->eraseFromParent();
111
112 auto *CleanupPad =
114 auto *CleanupRet =
116 return CleanupRet;
117}
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150namespace {
151struct AllocaUseVisitor : PtrUseVisitor {
152 using Base = PtrUseVisitor;
153 AllocaUseVisitor(const DataLayout &DL, const DominatorTree &DT,
154 const coro::Shape &CoroShape,
155 const SuspendCrossingInfo &Checker,
156 bool ShouldUseLifetimeStartInfo)
157 : PtrUseVisitor(DL), DT(DT), CoroShape(CoroShape), Checker(Checker),
158 ShouldUseLifetimeStartInfo(ShouldUseLifetimeStartInfo) {
159 for (AnyCoroSuspendInst *SuspendInst : CoroShape.CoroSuspends)
160 CoroSuspendBBs.insert(SuspendInst->getParent());
161 }
162
163 void visit(Instruction &I) {
165 Base::visit(I);
166
167
168 if (PI.isEscaped() &&
169 !DT.dominates(CoroShape.CoroBegin, PI.getEscapingInst())) {
170 MayWriteBeforeCoroBegin = true;
171 }
172 }
173
174
175 void visit(Instruction *I) { return visit(*I); }
176
177 void visitPHINode(PHINode &I) {
178 enqueueUsers(I);
179 handleAlias(I);
180 }
181
182 void visitSelectInst(SelectInst &I) {
183 enqueueUsers(I);
184 handleAlias(I);
185 }
186
187 void visitStoreInst(StoreInst &SI) {
188
189
190 handleMayWrite(SI);
191
192 if (SI.getValueOperand() != U->get())
193 return;
194
195
196
197
198
199
200
201
202
203
204
205 auto IsSimpleStoreThenLoad = [&]() {
206 auto *AI = dyn_cast(SI.getPointerOperand());
207
208
209
210 if (!AI)
211 return false;
212
213 SmallVector<Instruction *, 4> StoreAliases = {AI};
214 while (!StoreAliases.empty()) {
215 Instruction *I = StoreAliases.pop_back_val();
216 for (User *U : I->users()) {
217
218
219 if (auto *LI = dyn_cast(U)) {
220 enqueueUsers(*LI);
221 handleAlias(*LI);
222 continue;
223 }
224
225
226 if (auto *S = dyn_cast(U))
227 if (S->getPointerOperand() == I)
228 continue;
229 if (auto *II = dyn_cast(U))
230 if (II->isLifetimeStartOrEnd())
231 continue;
232
233
234 if (auto *BI = dyn_cast(U)) {
235 StoreAliases.push_back(BI);
236 continue;
237 }
238 return false;
239 }
240 }
241
242 return true;
243 };
244
245 if (!IsSimpleStoreThenLoad())
246 PI.setEscaped(&SI);
247 }
248
249
250 void visitMemIntrinsic(MemIntrinsic &MI) { handleMayWrite(MI); }
251
252 void visitBitCastInst(BitCastInst &BC) {
253 Base::visitBitCastInst(BC);
254 handleAlias(BC);
255 }
256
257 void visitAddrSpaceCastInst(AddrSpaceCastInst &ASC) {
258 Base::visitAddrSpaceCastInst(ASC);
259 handleAlias(ASC);
260 }
261
262 void visitGetElementPtrInst(GetElementPtrInst &GEPI) {
263
264 Base::visitGetElementPtrInst(GEPI);
265 handleAlias(GEPI);
266 }
267
268 void visitIntrinsicInst(IntrinsicInst &II) {
269
270
271
272 if (!IsOffsetKnown || .isZero())
273 return Base::visitIntrinsicInst(II);
274 switch (II.getIntrinsicID()) {
275 default:
276 return Base::visitIntrinsicInst(II);
277 case Intrinsic::lifetime_start:
278 LifetimeStarts.insert(&II);
279 LifetimeStartBBs.push_back(II.getParent());
280 break;
281 case Intrinsic::lifetime_end:
282 LifetimeEndBBs.insert(II.getParent());
283 break;
284 }
285 }
286
287 void visitCallBase(CallBase &CB) {
288 for (unsigned Op = 0, OpCount = CB.arg_size(); Op < OpCount; ++Op)
289 if (U->get() == CB.getArgOperand(Op) && !CB.doesNotCapture(Op))
290 PI.setEscaped(&CB);
291 handleMayWrite(CB);
292 }
293
294 bool getShouldLiveOnFrame() const {
295 if (!ShouldLiveOnFrame)
296 ShouldLiveOnFrame = computeShouldLiveOnFrame();
297 return *ShouldLiveOnFrame;
298 }
299
300 bool getMayWriteBeforeCoroBegin() const { return MayWriteBeforeCoroBegin; }
301
302 DenseMap<Instruction *, std::optional> getAliasesCopy() const {
303 assert(getShouldLiveOnFrame() && "This method should only be called if the "
304 "alloca needs to live on the frame.");
305 for (const auto &P : AliasOffetMap)
306 if (.second)
308 "created before CoroBegin.");
309 return AliasOffetMap;
310 }
311
312private:
313 const DominatorTree &DT;
314 const coro::Shape &CoroShape;
315 const SuspendCrossingInfo &Checker;
316
317
318
319 DenseMap<Instruction *, std::optional> AliasOffetMap{};
320 SmallPtrSet<Instruction *, 4> Users{};
321 SmallPtrSet<IntrinsicInst *, 2> LifetimeStarts{};
322 SmallVector<BasicBlock *> LifetimeStartBBs{};
323 SmallPtrSet<BasicBlock *, 2> LifetimeEndBBs{};
324 SmallPtrSet<const BasicBlock *, 2> CoroSuspendBBs{};
325 bool MayWriteBeforeCoroBegin{false};
326 bool ShouldUseLifetimeStartInfo{true};
327
328 mutable std::optional ShouldLiveOnFrame{};
329
330 bool computeShouldLiveOnFrame() const {
331
332
333
334
335 if (ShouldUseLifetimeStartInfo && !LifetimeStarts.empty()) {
336
337
338 if (LifetimeEndBBs.empty())
339 return true;
340
341
342
343
346 &LifetimeEndBBs, &DT))
347 return true;
348
349
350
351
352
353 if (PI.isEscaped()) {
354 for (auto *A : LifetimeStarts) {
355 for (auto *B : LifetimeStarts) {
356 if (Checker.hasPathOrLoopCrossingSuspendPoint(A->getParent(),
357 B->getParent()))
358 return true;
359 }
360 }
361 }
362 return false;
363 }
364
365
366
367
368
369
370
371
372
373
374 if (PI.isEscaped())
375 return true;
376
377 for (auto *U1 : Users)
378 for (auto *U2 : Users)
379 if (Checker.isDefinitionAcrossSuspend(*U1, U2))
380 return true;
381
382 return false;
383 }
384
385 void handleMayWrite(const Instruction &I) {
386 if (!DT.dominates(CoroShape.CoroBegin, &I))
387 MayWriteBeforeCoroBegin = true;
388 }
389
390 bool usedAfterCoroBegin(Instruction &I) {
391 for (auto &U : I.uses())
392 if (DT.dominates(CoroShape.CoroBegin, U))
393 return true;
394 return false;
395 }
396
397 void handleAlias(Instruction &I) {
398
399
400
401 if (DT.dominates(CoroShape.CoroBegin, &I) || !usedAfterCoroBegin(I))
402 return;
403
404 if (!IsOffsetKnown) {
405 AliasOffetMap[&I].reset();
406 } else {
407 auto [Itr, Inserted] = AliasOffetMap.try_emplace(&I, Offset);
408 if (!Inserted && Itr->second && *Itr->second != Offset) {
409
410
411 Itr->second.reset();
412 }
413 }
414 }
415};
416}
417
418static void collectFrameAlloca(AllocaInst *AI, const coro::Shape &Shape,
419 const SuspendCrossingInfo &Checker,
420 SmallVectorImpl &Allocas,
421 const DominatorTree &DT) {
422 if (Shape.CoroSuspends.empty())
423 return;
424
425
426
427 if (AI == Shape.SwitchLowering.PromiseAlloca)
428 return;
429
430
431
432 if (AI->hasMetadata(LLVMContext::MD_coro_outside_frame))
433 return;
434
435
436
437
438 bool ShouldUseLifetimeStartInfo =
441 AllocaUseVisitor Visitor{AI->getDataLayout(), DT, Shape, Checker,
442 ShouldUseLifetimeStartInfo};
443 Visitor.visitPtr(*AI);
444 if (!Visitor.getShouldLiveOnFrame())
445 return;
446 Allocas.emplace_back(AI, Visitor.getAliasesCopy(),
447 Visitor.getMayWriteBeforeCoroBegin());
448}
449
450}
451
454
457 if (Checker.isDefinitionAcrossSuspend(A, U))
458 Spills[&A].push_back(cast(U));
459}
460
467
469
470
471 if (isCoroutineStructureIntrinsic(I) || &I == Shape.CoroBegin)
472 continue;
473
474
475 if (auto AI = dyn_cast(&I)) {
476
477 if (isLocalAlloca(AI)) {
479 continue;
480 }
481
482
483
484
485
486
487 auto Alloc = lowerNonLocalAlloca(AI, Shape, DeadInstructions);
488
490 if (Checker.isDefinitionAcrossSuspend(*Alloc, U))
491 Spills[Alloc].push_back(cast(U));
492 }
493 continue;
494 }
495
496
497 if (isa(I))
498 continue;
499
500 if (auto *AI = dyn_cast(&I)) {
501 collectFrameAlloca(AI, Shape, Checker, Allocas, DT);
502 continue;
503 }
504
506 if (Checker.isDefinitionAcrossSuspend(I, U)) {
507
508 if (I.getType()->isTokenTy())
510 "token definition is separated from the use by a suspend point");
511 Spills[&I].push_back(cast(U));
512 }
513 }
514}
515
518
519
520
521
522 for (auto &Iter : Spills) {
523 auto *V = Iter.first;
528 if (Checker.isDefinitionAcrossSuspend(*V, DVI))
529 Spills[V].push_back(DVI);
530
532 if (Checker.isDefinitionAcrossSuspend(*V, DVR->Marker->MarkedInstr))
533 Spills[V].push_back(DVR->Marker->MarkedInstr);
534 }
535}
536
537
538
545
546
547 auto collectUsers = [&](Value *Def) {
548 for (User *U : Def->users()) {
549 auto Inst = cast(U);
550 if (Inst->getParent() != CoroBegin->getParent() ||
552 continue;
553 if (ToMove.insert(Inst))
555 }
556 };
557 std::for_each(Spills.begin(), Spills.end(),
558 [&](auto &I) { collectUsers(I.first); });
559 std::for_each(Allocas.begin(), Allocas.end(),
560 [&](auto &I) { collectUsers(I.Alloca); });
561
562
563 while (!Worklist.empty()) {
565 for (User *U : Def->users()) {
566 auto Inst = cast(U);
567 if (Dom.dominates(CoroBegin, Inst))
568 continue;
569 if (ToMove.insert(Inst))
571 }
572 }
573
574
577
579 });
580
584}
585
589 if (auto *Arg = dyn_cast(Def)) {
590
591
593
594
595
596 Arg->getParent()->removeParamAttr(Arg->getArgNo(), Attribute::NoCapture);
597 } else if (auto *CSI = dyn_cast(Def)) {
598
599
600 InsertPt = CSI->getParent()->getSingleSuccessor()->getFirstNonPHIIt();
601 } else {
602 auto *I = cast(Def);
604
605
607 } else if (auto *II = dyn_cast(I)) {
608
609
610 auto *NewBB = SplitEdge(II->getParent(), II->getNormalDest());
611 InsertPt = NewBB->getTerminator()->getIterator();
612 } else if (isa(I)) {
613
615 if (auto *CSI = dyn_cast(DefBlock->getTerminator()))
616 InsertPt = splitBeforeCatchSwitch(CSI)->getIterator();
617 else
619 } else {
620 assert(->isTerminator() && "unexpected terminator");
621
622
623 InsertPt = I->getNextNode()->getIterator();
624 }
625 }
626
627 return InsertPt;
628}
629
630}
631
632}
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
Expand Atomic instructions
BlockVerifier::State From
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
This file provides various utilities for inspecting and working with the control flow graph in LLVM I...
iv Induction Variable Users
uint64_t IntrinsicInst * II
StandardInstrumentations SI(Mod->getContext(), Debug, VerifyEach)
This file provides a collection of visitors which walk the (instruction) uses of a pointer.
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
void visit(MachineFunction &MF, MachineBasicBlock &Start, std::function< void(MachineBasicBlock *)> op)
This class represents an incoming formal argument to a Function.
LLVM Basic Block Representation.
const_iterator getFirstInsertionPt() const
Returns an iterator to the first instruction in this block that is suitable for inserting a non-PHI i...
InstListType::iterator iterator
Instruction iterators...
const Instruction * getTerminator() const LLVM_READONLY
Returns the terminator instruction if the block is well formed or null if the block is not well forme...
static CleanupPadInst * Create(Value *ParentPad, ArrayRef< Value * > Args={}, const Twine &NameStr="", InsertPosition InsertBefore=nullptr)
static CleanupReturnInst * Create(Value *CleanupPad, BasicBlock *UnwindBB=nullptr, InsertPosition InsertBefore=nullptr)
This class represents the llvm.coro.begin or llvm.coro.begin.custom.abi instructions.
This represents the llvm.dbg.value instruction.
Record of a variable value-assignment, aka a non instruction representation of the dbg....
Concrete subclass of DominatorTreeBase that is used to compute a normal dominator tree.
bool dominates(const BasicBlock *BB, const Use &U) const
Return true if the (end of the) basic block BB dominates the use U.
void moveBefore(Instruction *MovePos)
Unlink this instruction from its current basic block and insert it into the basic block that MovePos ...
iterator end()
Get an iterator to the end of the SetVector.
iterator begin()
Get an iterator to the beginning of the SetVector.
bool insert(const value_type &X)
Insert a new element into the SetVector.
A SetVector that performs no allocations if smaller than a certain size.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
LLVM Value Representation.
const ParentTy * getParent() const
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
@ BasicBlock
Various leaf nodes.
@ Async
The "async continuation" lowering, where each suspend point creates a single continuation function.
@ RetconOnce
The "unique returned-continuation" lowering, where each suspend point creates a single continuation f...
@ Retcon
The "returned-continuation" lowering, where each suspend point creates a single continuation function...
BasicBlock::iterator getSpillInsertionPt(const coro::Shape &, Value *Def, const DominatorTree &DT)
bool isSuspendBlock(BasicBlock *BB)
void sinkSpillUsesAfterCoroBegin(const DominatorTree &DT, CoroBeginInst *CoroBegin, coro::SpillInfo &Spills, SmallVectorImpl< coro::AllocaInfo > &Allocas)
Async and Retcon{Once} conventions assume that all spill uses can be sunk after the coro....
void collectSpillsFromArgs(SpillInfo &Spills, Function &F, const SuspendCrossingInfo &Checker)
void collectSpillsFromDbgInfo(SpillInfo &Spills, Function &F, const SuspendCrossingInfo &Checker)
void collectSpillsAndAllocasFromInsts(SpillInfo &Spills, SmallVector< AllocaInfo, 8 > &Allocas, SmallVector< Instruction *, 4 > &DeadInstructions, SmallVector< CoroAllocaAllocInst *, 4 > &LocalAllocas, Function &F, const SuspendCrossingInfo &Checker, const DominatorTree &DT, const coro::Shape &Shape)
This is an optimization pass for GlobalISel generic memory operations.
auto successors(const MachineBasicBlock *BB)
void findDbgValues(SmallVectorImpl< DbgValueInst * > &DbgValues, Value *V, SmallVectorImpl< DbgVariableRecord * > *DbgVariableRecords=nullptr)
Finds the llvm.dbg.value intrinsics describing a value.
void sort(IteratorTy Start, IteratorTy End)
void report_fatal_error(Error Err, bool gen_crash_diag=true)
Report a serious error, calling any installed error handler.
bool isManyPotentiallyReachableFromMany(SmallVectorImpl< BasicBlock * > &Worklist, const SmallPtrSetImpl< const BasicBlock * > &StopSet, const SmallPtrSetImpl< BasicBlock * > *ExclusionSet, const DominatorTree *DT=nullptr, const LoopInfo *LI=nullptr)
Determine whether there is a potentially a path from at least one block in 'Worklist' to at least one...
BasicBlock * SplitEdge(BasicBlock *From, BasicBlock *To, DominatorTree *DT=nullptr, LoopInfo *LI=nullptr, MemorySSAUpdater *MSSAU=nullptr, const Twine &BBName="")
Split the edge connecting the specified blocks, and return the newly created basic block between From...
A MapVector that performs no allocations if smaller than a certain size.
CoroBeginInst * CoroBegin
BasicBlock::iterator getInsertPtAfterFramePtr() const