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