xref: /freebsd/contrib/llvm-project/llvm/include/llvm/Transforms/Coroutines/CoroShape.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===- CoroShape.h - Coroutine info for lowering --------------*- C++ -*---===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 // This file declares the shape info struct that is required by many coroutine
9 // utility methods.
10 //===----------------------------------------------------------------------===//
11 
12 #ifndef LLVM_TRANSFORMS_COROUTINES_COROSHAPE_H
13 #define LLVM_TRANSFORMS_COROUTINES_COROSHAPE_H
14 
15 #include "llvm/IR/IRBuilder.h"
16 #include "llvm/IR/PassManager.h"
17 #include "llvm/Support/Compiler.h"
18 #include "llvm/Transforms/Coroutines/CoroInstr.h"
19 
20 namespace llvm {
21 
22 class CallGraph;
23 
24 namespace coro {
25 
26 enum class ABI {
27   /// The "resume-switch" lowering, where there are separate resume and
28   /// destroy functions that are shared between all suspend points.  The
29   /// coroutine frame implicitly stores the resume and destroy functions,
30   /// the current index, and any promise value.
31   Switch,
32 
33   /// The "returned-continuation" lowering, where each suspend point creates a
34   /// single continuation function that is used for both resuming and
35   /// destroying.  Does not support promises.
36   Retcon,
37 
38   /// The "unique returned-continuation" lowering, where each suspend point
39   /// creates a single continuation function that is used for both resuming
40   /// and destroying.  Does not support promises.  The function is known to
41   /// suspend at most once during its execution, and the return value of
42   /// the continuation is void.
43   RetconOnce,
44 
45   /// The "async continuation" lowering, where each suspend point creates a
46   /// single continuation function. The continuation function is available as an
47   /// intrinsic.
48   Async,
49 };
50 
51 // Holds structural Coroutine Intrinsics for a particular function and other
52 // values used during CoroSplit pass.
53 struct Shape {
54   CoroBeginInst *CoroBegin = nullptr;
55   SmallVector<AnyCoroEndInst *, 4> CoroEnds;
56   SmallVector<CoroSizeInst *, 2> CoroSizes;
57   SmallVector<CoroAlignInst *, 2> CoroAligns;
58   SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends;
59   SmallVector<CoroAwaitSuspendInst *, 4> CoroAwaitSuspends;
60   SmallVector<CallInst *, 2> SymmetricTransfers;
61 
62   // Values invalidated by replaceSwiftErrorOps()
63   SmallVector<CallInst *, 2> SwiftErrorOps;
64 
clearShape65   void clear() {
66     CoroBegin = nullptr;
67     CoroEnds.clear();
68     CoroSizes.clear();
69     CoroAligns.clear();
70     CoroSuspends.clear();
71     CoroAwaitSuspends.clear();
72     SymmetricTransfers.clear();
73 
74     SwiftErrorOps.clear();
75 
76     FrameTy = nullptr;
77     FramePtr = nullptr;
78     AllocaSpillBlock = nullptr;
79   }
80 
81   // Scan the function and collect the above intrinsics for later processing
82   LLVM_ABI void analyze(Function &F,
83                         SmallVectorImpl<CoroFrameInst *> &CoroFrames,
84                         SmallVectorImpl<CoroSaveInst *> &UnusedCoroSaves,
85                         CoroPromiseInst *&CoroPromise);
86   // If for some reason, we were not able to find coro.begin, bailout.
87   LLVM_ABI void
88   invalidateCoroutine(Function &F,
89                       SmallVectorImpl<CoroFrameInst *> &CoroFrames);
90   // Perform ABI related initial transformation
91   LLVM_ABI void initABI();
92   // Remove orphaned and unnecessary intrinsics
93   LLVM_ABI void cleanCoroutine(SmallVectorImpl<CoroFrameInst *> &CoroFrames,
94                                SmallVectorImpl<CoroSaveInst *> &UnusedCoroSaves,
95                                CoroPromiseInst *CoroPromise);
96 
97   // Field indexes for special fields in the switch lowering.
98   struct SwitchFieldIndex {
99     enum {
100       Resume,
101       Destroy
102 
103       // The promise field is always at a fixed offset from the start of
104       // frame given its type, but the index isn't a constant for all
105       // possible frames.
106 
107       // The switch-index field isn't at a fixed offset or index, either;
108       // we just work it in where it fits best.
109     };
110   };
111 
112   coro::ABI ABI;
113 
114   StructType *FrameTy = nullptr;
115   Align FrameAlign;
116   uint64_t FrameSize = 0;
117   Value *FramePtr = nullptr;
118   BasicBlock *AllocaSpillBlock = nullptr;
119 
120   struct SwitchLoweringStorage {
121     SwitchInst *ResumeSwitch;
122     AllocaInst *PromiseAlloca;
123     BasicBlock *ResumeEntryBlock;
124     unsigned IndexField;
125     unsigned IndexAlign;
126     unsigned IndexOffset;
127     bool HasFinalSuspend;
128     bool HasUnwindCoroEnd;
129   };
130 
131   struct RetconLoweringStorage {
132     Function *ResumePrototype;
133     Function *Alloc;
134     Function *Dealloc;
135     BasicBlock *ReturnBlock;
136     bool IsFrameInlineInStorage;
137   };
138 
139   struct AsyncLoweringStorage {
140     Value *Context;
141     CallingConv::ID AsyncCC;
142     unsigned ContextArgNo;
143     uint64_t ContextHeaderSize;
144     uint64_t ContextAlignment;
145     uint64_t FrameOffset; // Start of the frame.
146     uint64_t ContextSize; // Includes frame size.
147     GlobalVariable *AsyncFuncPointer;
148 
getContextAlignmentShape::AsyncLoweringStorage149     Align getContextAlignment() const { return Align(ContextAlignment); }
150   };
151 
152   union {
153     SwitchLoweringStorage SwitchLowering;
154     RetconLoweringStorage RetconLowering;
155     AsyncLoweringStorage AsyncLowering;
156   };
157 
getSwitchCoroIdShape158   CoroIdInst *getSwitchCoroId() const {
159     assert(ABI == coro::ABI::Switch);
160     return cast<CoroIdInst>(CoroBegin->getId());
161   }
162 
getRetconCoroIdShape163   AnyCoroIdRetconInst *getRetconCoroId() const {
164     assert(ABI == coro::ABI::Retcon || ABI == coro::ABI::RetconOnce);
165     return cast<AnyCoroIdRetconInst>(CoroBegin->getId());
166   }
167 
getAsyncCoroIdShape168   CoroIdAsyncInst *getAsyncCoroId() const {
169     assert(ABI == coro::ABI::Async);
170     return cast<CoroIdAsyncInst>(CoroBegin->getId());
171   }
172 
getSwitchIndexFieldShape173   unsigned getSwitchIndexField() const {
174     assert(ABI == coro::ABI::Switch);
175     assert(FrameTy && "frame type not assigned");
176     return SwitchLowering.IndexField;
177   }
getIndexTypeShape178   IntegerType *getIndexType() const {
179     assert(ABI == coro::ABI::Switch);
180     assert(FrameTy && "frame type not assigned");
181     return cast<IntegerType>(FrameTy->getElementType(getSwitchIndexField()));
182   }
getIndexShape183   ConstantInt *getIndex(uint64_t Value) const {
184     return ConstantInt::get(getIndexType(), Value);
185   }
186 
getSwitchResumePointerTypeShape187   PointerType *getSwitchResumePointerType() const {
188     assert(ABI == coro::ABI::Switch);
189     assert(FrameTy && "frame type not assigned");
190     return cast<PointerType>(FrameTy->getElementType(SwitchFieldIndex::Resume));
191   }
192 
getResumeFunctionTypeShape193   FunctionType *getResumeFunctionType() const {
194     switch (ABI) {
195     case coro::ABI::Switch:
196       return FunctionType::get(Type::getVoidTy(FrameTy->getContext()),
197                                PointerType::getUnqual(FrameTy->getContext()),
198                                /*IsVarArg=*/false);
199     case coro::ABI::Retcon:
200     case coro::ABI::RetconOnce:
201       return RetconLowering.ResumePrototype->getFunctionType();
202     case coro::ABI::Async:
203       // Not used. The function type depends on the active suspend.
204       return nullptr;
205     }
206 
207     llvm_unreachable("Unknown coro::ABI enum");
208   }
209 
getRetconResultTypesShape210   ArrayRef<Type *> getRetconResultTypes() const {
211     assert(ABI == coro::ABI::Retcon || ABI == coro::ABI::RetconOnce);
212     auto FTy = CoroBegin->getFunction()->getFunctionType();
213 
214     // The safety of all this is checked by checkWFRetconPrototype.
215     if (auto STy = dyn_cast<StructType>(FTy->getReturnType())) {
216       return STy->elements().slice(1);
217     } else {
218       return ArrayRef<Type *>();
219     }
220   }
221 
getRetconResumeTypesShape222   ArrayRef<Type *> getRetconResumeTypes() const {
223     assert(ABI == coro::ABI::Retcon || ABI == coro::ABI::RetconOnce);
224 
225     // The safety of all this is checked by checkWFRetconPrototype.
226     auto FTy = RetconLowering.ResumePrototype->getFunctionType();
227     return FTy->params().slice(1);
228   }
229 
getResumeFunctionCCShape230   CallingConv::ID getResumeFunctionCC() const {
231     switch (ABI) {
232     case coro::ABI::Switch:
233       return CallingConv::Fast;
234 
235     case coro::ABI::Retcon:
236     case coro::ABI::RetconOnce:
237       return RetconLowering.ResumePrototype->getCallingConv();
238     case coro::ABI::Async:
239       return AsyncLowering.AsyncCC;
240     }
241     llvm_unreachable("Unknown coro::ABI enum");
242   }
243 
getPromiseAllocaShape244   AllocaInst *getPromiseAlloca() const {
245     if (ABI == coro::ABI::Switch)
246       return SwitchLowering.PromiseAlloca;
247     return nullptr;
248   }
249 
getInsertPtAfterFramePtrShape250   BasicBlock::iterator getInsertPtAfterFramePtr() const {
251     if (auto *I = dyn_cast<Instruction>(FramePtr)) {
252       BasicBlock::iterator It = std::next(I->getIterator());
253       It.setHeadBit(true); // Copy pre-RemoveDIs behaviour.
254       return It;
255     }
256     return cast<Argument>(FramePtr)->getParent()->getEntryBlock().begin();
257   }
258 
259   /// Allocate memory according to the rules of the active lowering.
260   ///
261   /// \param CG - if non-null, will be updated for the new call
262   LLVM_ABI Value *emitAlloc(IRBuilder<> &Builder, Value *Size,
263                             CallGraph *CG) const;
264 
265   /// Deallocate memory according to the rules of the active lowering.
266   ///
267   /// \param CG - if non-null, will be updated for the new call
268   LLVM_ABI void emitDealloc(IRBuilder<> &Builder, Value *Ptr,
269                             CallGraph *CG) const;
270 
271   Shape() = default;
ShapeShape272   explicit Shape(Function &F) {
273     SmallVector<CoroFrameInst *, 8> CoroFrames;
274     SmallVector<CoroSaveInst *, 2> UnusedCoroSaves;
275     CoroPromiseInst *CoroPromise = nullptr;
276 
277     analyze(F, CoroFrames, UnusedCoroSaves, CoroPromise);
278     if (!CoroBegin) {
279       invalidateCoroutine(F, CoroFrames);
280       return;
281     }
282     cleanCoroutine(CoroFrames, UnusedCoroSaves, CoroPromise);
283   }
284 };
285 
286 } // end namespace coro
287 
288 } // end namespace llvm
289 
290 #endif // LLVM_TRANSFORMS_COROUTINES_COROSHAPE_H
291