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 #include "llvm/Transforms/Coroutines.h" 17 18 namespace llvm { 19 20 class CallGraph; 21 class CallGraphSCC; 22 class PassRegistry; 23 24 void initializeCoroEarlyLegacyPass(PassRegistry &); 25 void initializeCoroSplitLegacyPass(PassRegistry &); 26 void initializeCoroElideLegacyPass(PassRegistry &); 27 void initializeCoroCleanupLegacyPass(PassRegistry &); 28 29 // CoroEarly pass marks every function that has coro.begin with a string 30 // attribute "coroutine.presplit"="0". CoroSplit pass processes the coroutine 31 // twice. First, it lets it go through complete IPO optimization pipeline as a 32 // single function. It forces restart of the pipeline by inserting an indirect 33 // call to an empty function "coro.devirt.trigger" which is devirtualized by 34 // CoroElide pass that triggers a restart of the pipeline by CGPassManager. 35 // When CoroSplit pass sees the same coroutine the second time, it splits it up, 36 // adds coroutine subfunctions to the SCC to be processed by IPO pipeline. 37 38 #define CORO_PRESPLIT_ATTR "coroutine.presplit" 39 #define UNPREPARED_FOR_SPLIT "0" 40 #define PREPARED_FOR_SPLIT "1" 41 42 #define CORO_DEVIRT_TRIGGER_FN "coro.devirt.trigger" 43 44 namespace coro { 45 46 bool declaresIntrinsics(const Module &M, 47 const std::initializer_list<StringRef>); 48 void replaceAllCoroAllocs(CoroBeginInst *CB, bool Replacement); 49 void replaceAllCoroFrees(CoroBeginInst *CB, Value *Replacement); 50 void replaceCoroFree(CoroIdInst *CoroId, bool Elide); 51 void updateCallGraph(Function &Caller, ArrayRef<Function *> Funcs, 52 CallGraph &CG, CallGraphSCC &SCC); 53 54 // Keeps data and helper functions for lowering coroutine intrinsics. 55 struct LowererBase { 56 Module &TheModule; 57 LLVMContext &Context; 58 PointerType *const Int8Ptr; 59 FunctionType *const ResumeFnType; 60 ConstantPointerNull *const NullPtr; 61 62 LowererBase(Module &M); 63 Value *makeSubFnCall(Value *Arg, int Index, Instruction *InsertPt); 64 }; 65 66 enum class ABI { 67 /// The "resume-switch" lowering, where there are separate resume and 68 /// destroy functions that are shared between all suspend points. The 69 /// coroutine frame implicitly stores the resume and destroy functions, 70 /// the current index, and any promise value. 71 Switch, 72 73 /// The "returned-continuation" lowering, where each suspend point creates a 74 /// single continuation function that is used for both resuming and 75 /// destroying. Does not support promises. 76 Retcon, 77 78 /// The "unique returned-continuation" lowering, where each suspend point 79 /// creates a single continuation function that is used for both resuming 80 /// and destroying. Does not support promises. The function is known to 81 /// suspend at most once during its execution, and the return value of 82 /// the continuation is void. 83 RetconOnce, 84 }; 85 86 // Holds structural Coroutine Intrinsics for a particular function and other 87 // values used during CoroSplit pass. 88 struct LLVM_LIBRARY_VISIBILITY Shape { 89 CoroBeginInst *CoroBegin; 90 SmallVector<CoroEndInst *, 4> CoroEnds; 91 SmallVector<CoroSizeInst *, 2> CoroSizes; 92 SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends; 93 SmallVector<CallInst*, 2> SwiftErrorOps; 94 95 // Field indexes for special fields in the switch lowering. 96 struct SwitchFieldIndex { 97 enum { 98 Resume, 99 Destroy, 100 Promise, 101 Index, 102 /// The index of the first spill field. 103 FirstSpill 104 }; 105 }; 106 107 coro::ABI ABI; 108 109 StructType *FrameTy; 110 Instruction *FramePtr; 111 BasicBlock *AllocaSpillBlock; 112 113 struct SwitchLoweringStorage { 114 SwitchInst *ResumeSwitch; 115 AllocaInst *PromiseAlloca; 116 BasicBlock *ResumeEntryBlock; 117 bool HasFinalSuspend; 118 }; 119 120 struct RetconLoweringStorage { 121 Function *ResumePrototype; 122 Function *Alloc; 123 Function *Dealloc; 124 BasicBlock *ReturnBlock; 125 bool IsFrameInlineInStorage; 126 }; 127 128 union { 129 SwitchLoweringStorage SwitchLowering; 130 RetconLoweringStorage RetconLowering; 131 }; 132 getSwitchCoroIdShape133 CoroIdInst *getSwitchCoroId() const { 134 assert(ABI == coro::ABI::Switch); 135 return cast<CoroIdInst>(CoroBegin->getId()); 136 } 137 getRetconCoroIdShape138 AnyCoroIdRetconInst *getRetconCoroId() const { 139 assert(ABI == coro::ABI::Retcon || 140 ABI == coro::ABI::RetconOnce); 141 return cast<AnyCoroIdRetconInst>(CoroBegin->getId()); 142 } 143 getIndexTypeShape144 IntegerType *getIndexType() const { 145 assert(ABI == coro::ABI::Switch); 146 assert(FrameTy && "frame type not assigned"); 147 return cast<IntegerType>(FrameTy->getElementType(SwitchFieldIndex::Index)); 148 } getIndexShape149 ConstantInt *getIndex(uint64_t Value) const { 150 return ConstantInt::get(getIndexType(), Value); 151 } 152 getSwitchResumePointerTypeShape153 PointerType *getSwitchResumePointerType() const { 154 assert(ABI == coro::ABI::Switch); 155 assert(FrameTy && "frame type not assigned"); 156 return cast<PointerType>(FrameTy->getElementType(SwitchFieldIndex::Resume)); 157 } 158 getResumeFunctionTypeShape159 FunctionType *getResumeFunctionType() const { 160 switch (ABI) { 161 case coro::ABI::Switch: { 162 auto *FnPtrTy = getSwitchResumePointerType(); 163 return cast<FunctionType>(FnPtrTy->getPointerElementType()); 164 } 165 case coro::ABI::Retcon: 166 case coro::ABI::RetconOnce: 167 return RetconLowering.ResumePrototype->getFunctionType(); 168 } 169 llvm_unreachable("Unknown coro::ABI enum"); 170 } 171 getRetconResultTypesShape172 ArrayRef<Type*> getRetconResultTypes() const { 173 assert(ABI == coro::ABI::Retcon || 174 ABI == coro::ABI::RetconOnce); 175 auto FTy = CoroBegin->getFunction()->getFunctionType(); 176 177 // The safety of all this is checked by checkWFRetconPrototype. 178 if (auto STy = dyn_cast<StructType>(FTy->getReturnType())) { 179 return STy->elements().slice(1); 180 } else { 181 return ArrayRef<Type*>(); 182 } 183 } 184 getRetconResumeTypesShape185 ArrayRef<Type*> getRetconResumeTypes() const { 186 assert(ABI == coro::ABI::Retcon || 187 ABI == coro::ABI::RetconOnce); 188 189 // The safety of all this is checked by checkWFRetconPrototype. 190 auto FTy = RetconLowering.ResumePrototype->getFunctionType(); 191 return FTy->params().slice(1); 192 } 193 getResumeFunctionCCShape194 CallingConv::ID getResumeFunctionCC() const { 195 switch (ABI) { 196 case coro::ABI::Switch: 197 return CallingConv::Fast; 198 199 case coro::ABI::Retcon: 200 case coro::ABI::RetconOnce: 201 return RetconLowering.ResumePrototype->getCallingConv(); 202 } 203 llvm_unreachable("Unknown coro::ABI enum"); 204 } 205 getFirstSpillFieldIndexShape206 unsigned getFirstSpillFieldIndex() const { 207 switch (ABI) { 208 case coro::ABI::Switch: 209 return SwitchFieldIndex::FirstSpill; 210 211 case coro::ABI::Retcon: 212 case coro::ABI::RetconOnce: 213 return 0; 214 } 215 llvm_unreachable("Unknown coro::ABI enum"); 216 } 217 getPromiseAllocaShape218 AllocaInst *getPromiseAlloca() const { 219 if (ABI == coro::ABI::Switch) 220 return SwitchLowering.PromiseAlloca; 221 return nullptr; 222 } 223 224 /// Allocate memory according to the rules of the active lowering. 225 /// 226 /// \param CG - if non-null, will be updated for the new call 227 Value *emitAlloc(IRBuilder<> &Builder, Value *Size, CallGraph *CG) const; 228 229 /// Deallocate memory according to the rules of the active lowering. 230 /// 231 /// \param CG - if non-null, will be updated for the new call 232 void emitDealloc(IRBuilder<> &Builder, Value *Ptr, CallGraph *CG) const; 233 234 Shape() = default; ShapeShape235 explicit Shape(Function &F) { buildFrom(F); } 236 void buildFrom(Function &F); 237 }; 238 239 void buildCoroutineFrame(Function &F, Shape &Shape); 240 241 } // End namespace coro. 242 } // End namespace llvm 243 244 #endif 245