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