xref: /aosp_15_r20/external/swiftshader/third_party/llvm-16.0/llvm/lib/Transforms/Coroutines/CoroInternal.h (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
1 //===- CoroInternal.h - Internal Coroutine interfaces ---------*- 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 // Common definitions/declarations used internally by coroutine lowering passes.
9 //===----------------------------------------------------------------------===//
10 
11 #ifndef LLVM_LIB_TRANSFORMS_COROUTINES_COROINTERNAL_H
12 #define LLVM_LIB_TRANSFORMS_COROUTINES_COROINTERNAL_H
13 
14 #include "CoroInstr.h"
15 #include "llvm/IR/IRBuilder.h"
16 
17 namespace llvm {
18 
19 class CallGraph;
20 
21 namespace coro {
22 
23 bool declaresAnyIntrinsic(const Module &M);
24 bool declaresIntrinsics(const Module &M,
25                         const std::initializer_list<StringRef>);
26 void replaceCoroFree(CoroIdInst *CoroId, bool Elide);
27 
28 /// Recover a dbg.declare prepared by the frontend and emit an alloca
29 /// holding a pointer to the coroutine frame.
30 void salvageDebugInfo(
31     SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> &DbgPtrAllocaCache,
32     DbgVariableIntrinsic *DVI, bool OptimizeFrame);
33 
34 // Keeps data and helper functions for lowering coroutine intrinsics.
35 struct LowererBase {
36   Module &TheModule;
37   LLVMContext &Context;
38   PointerType *const Int8Ptr;
39   FunctionType *const ResumeFnType;
40   ConstantPointerNull *const NullPtr;
41 
42   LowererBase(Module &M);
43   Value *makeSubFnCall(Value *Arg, int Index, Instruction *InsertPt);
44 };
45 
46 enum class ABI {
47   /// The "resume-switch" lowering, where there are separate resume and
48   /// destroy functions that are shared between all suspend points.  The
49   /// coroutine frame implicitly stores the resume and destroy functions,
50   /// the current index, and any promise value.
51   Switch,
52 
53   /// The "returned-continuation" lowering, where each suspend point creates a
54   /// single continuation function that is used for both resuming and
55   /// destroying.  Does not support promises.
56   Retcon,
57 
58   /// The "unique returned-continuation" lowering, where each suspend point
59   /// creates a single continuation function that is used for both resuming
60   /// and destroying.  Does not support promises.  The function is known to
61   /// suspend at most once during its execution, and the return value of
62   /// the continuation is void.
63   RetconOnce,
64 
65   /// The "async continuation" lowering, where each suspend point creates a
66   /// single continuation function. The continuation function is available as an
67   /// intrinsic.
68   Async,
69 };
70 
71 // Holds structural Coroutine Intrinsics for a particular function and other
72 // values used during CoroSplit pass.
73 struct LLVM_LIBRARY_VISIBILITY Shape {
74   CoroBeginInst *CoroBegin;
75   SmallVector<AnyCoroEndInst *, 4> CoroEnds;
76   SmallVector<CoroSizeInst *, 2> CoroSizes;
77   SmallVector<CoroAlignInst *, 2> CoroAligns;
78   SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends;
79   SmallVector<CallInst*, 2> SwiftErrorOps;
80 
81   // Field indexes for special fields in the switch lowering.
82   struct SwitchFieldIndex {
83     enum {
84       Resume,
85       Destroy
86 
87       // The promise field is always at a fixed offset from the start of
88       // frame given its type, but the index isn't a constant for all
89       // possible frames.
90 
91       // The switch-index field isn't at a fixed offset or index, either;
92       // we just work it in where it fits best.
93     };
94   };
95 
96   coro::ABI ABI;
97 
98   StructType *FrameTy;
99   Align FrameAlign;
100   uint64_t FrameSize;
101   Value *FramePtr;
102   BasicBlock *AllocaSpillBlock;
103 
104   /// This would only be true if optimization are enabled.
105   bool OptimizeFrame;
106 
107   struct SwitchLoweringStorage {
108     SwitchInst *ResumeSwitch;
109     AllocaInst *PromiseAlloca;
110     BasicBlock *ResumeEntryBlock;
111     unsigned IndexField;
112     unsigned IndexAlign;
113     unsigned IndexOffset;
114     bool HasFinalSuspend;
115     bool HasUnwindCoroEnd;
116   };
117 
118   struct RetconLoweringStorage {
119     Function *ResumePrototype;
120     Function *Alloc;
121     Function *Dealloc;
122     BasicBlock *ReturnBlock;
123     bool IsFrameInlineInStorage;
124   };
125 
126   struct AsyncLoweringStorage {
127     FunctionType *AsyncFuncTy;
128     Value *Context;
129     CallingConv::ID AsyncCC;
130     unsigned ContextArgNo;
131     uint64_t ContextHeaderSize;
132     uint64_t ContextAlignment;
133     uint64_t FrameOffset; // Start of the frame.
134     uint64_t ContextSize; // Includes frame size.
135     GlobalVariable *AsyncFuncPointer;
136 
getContextAlignmentShape::AsyncLoweringStorage137     Align getContextAlignment() const { return Align(ContextAlignment); }
138   };
139 
140   union {
141     SwitchLoweringStorage SwitchLowering;
142     RetconLoweringStorage RetconLowering;
143     AsyncLoweringStorage AsyncLowering;
144   };
145 
getSwitchCoroIdShape146   CoroIdInst *getSwitchCoroId() const {
147     assert(ABI == coro::ABI::Switch);
148     return cast<CoroIdInst>(CoroBegin->getId());
149   }
150 
getRetconCoroIdShape151   AnyCoroIdRetconInst *getRetconCoroId() const {
152     assert(ABI == coro::ABI::Retcon ||
153            ABI == coro::ABI::RetconOnce);
154     return cast<AnyCoroIdRetconInst>(CoroBegin->getId());
155   }
156 
getAsyncCoroIdShape157   CoroIdAsyncInst *getAsyncCoroId() const {
158     assert(ABI == coro::ABI::Async);
159     return cast<CoroIdAsyncInst>(CoroBegin->getId());
160   }
161 
getSwitchIndexFieldShape162   unsigned getSwitchIndexField() const {
163     assert(ABI == coro::ABI::Switch);
164     assert(FrameTy && "frame type not assigned");
165     return SwitchLowering.IndexField;
166   }
getIndexTypeShape167   IntegerType *getIndexType() const {
168     assert(ABI == coro::ABI::Switch);
169     assert(FrameTy && "frame type not assigned");
170     return cast<IntegerType>(FrameTy->getElementType(getSwitchIndexField()));
171   }
getIndexShape172   ConstantInt *getIndex(uint64_t Value) const {
173     return ConstantInt::get(getIndexType(), Value);
174   }
175 
getSwitchResumePointerTypeShape176   PointerType *getSwitchResumePointerType() const {
177     assert(ABI == coro::ABI::Switch);
178   assert(FrameTy && "frame type not assigned");
179   return cast<PointerType>(FrameTy->getElementType(SwitchFieldIndex::Resume));
180   }
181 
getResumeFunctionTypeShape182   FunctionType *getResumeFunctionType() const {
183     switch (ABI) {
184     case coro::ABI::Switch:
185       return FunctionType::get(Type::getVoidTy(FrameTy->getContext()),
186                                FrameTy->getPointerTo(), /*IsVarArg*/false);
187     case coro::ABI::Retcon:
188     case coro::ABI::RetconOnce:
189       return RetconLowering.ResumePrototype->getFunctionType();
190     case coro::ABI::Async:
191       // Not used. The function type depends on the active suspend.
192       return nullptr;
193     }
194 
195     llvm_unreachable("Unknown coro::ABI enum");
196   }
197 
getRetconResultTypesShape198   ArrayRef<Type*> getRetconResultTypes() const {
199     assert(ABI == coro::ABI::Retcon ||
200            ABI == coro::ABI::RetconOnce);
201     auto FTy = CoroBegin->getFunction()->getFunctionType();
202 
203     // The safety of all this is checked by checkWFRetconPrototype.
204     if (auto STy = dyn_cast<StructType>(FTy->getReturnType())) {
205       return STy->elements().slice(1);
206     } else {
207       return ArrayRef<Type*>();
208     }
209   }
210 
getRetconResumeTypesShape211   ArrayRef<Type*> getRetconResumeTypes() const {
212     assert(ABI == coro::ABI::Retcon ||
213            ABI == coro::ABI::RetconOnce);
214 
215     // The safety of all this is checked by checkWFRetconPrototype.
216     auto FTy = RetconLowering.ResumePrototype->getFunctionType();
217     return FTy->params().slice(1);
218   }
219 
getResumeFunctionCCShape220   CallingConv::ID getResumeFunctionCC() const {
221     switch (ABI) {
222     case coro::ABI::Switch:
223       return CallingConv::Fast;
224 
225     case coro::ABI::Retcon:
226     case coro::ABI::RetconOnce:
227       return RetconLowering.ResumePrototype->getCallingConv();
228     case coro::ABI::Async:
229       return AsyncLowering.AsyncCC;
230     }
231     llvm_unreachable("Unknown coro::ABI enum");
232   }
233 
getPromiseAllocaShape234   AllocaInst *getPromiseAlloca() const {
235     if (ABI == coro::ABI::Switch)
236       return SwitchLowering.PromiseAlloca;
237     return nullptr;
238   }
239 
getInsertPtAfterFramePtrShape240   Instruction *getInsertPtAfterFramePtr() const {
241     if (auto *I = dyn_cast<Instruction>(FramePtr))
242       return I->getNextNode();
243     return &cast<Argument>(FramePtr)->getParent()->getEntryBlock().front();
244   }
245 
246   /// Allocate memory according to the rules of the active lowering.
247   ///
248   /// \param CG - if non-null, will be updated for the new call
249   Value *emitAlloc(IRBuilder<> &Builder, Value *Size, CallGraph *CG) const;
250 
251   /// Deallocate memory according to the rules of the active lowering.
252   ///
253   /// \param CG - if non-null, will be updated for the new call
254   void emitDealloc(IRBuilder<> &Builder, Value *Ptr, CallGraph *CG) const;
255 
256   Shape() = default;
257   explicit Shape(Function &F, bool OptimizeFrame = false)
OptimizeFrameShape258       : OptimizeFrame(OptimizeFrame) {
259     buildFrom(F);
260   }
261   void buildFrom(Function &F);
262 };
263 
264 void buildCoroutineFrame(Function &F, Shape &Shape);
265 CallInst *createMustTailCall(DebugLoc Loc, Function *MustTailCallFn,
266                              ArrayRef<Value *> Arguments, IRBuilder<> &);
267 } // End namespace coro.
268 } // End namespace llvm
269 
270 #endif
271