106f32e7eSjoerg //===- CoroInternal.h - Internal Coroutine interfaces ---------*- C++ -*---===// 206f32e7eSjoerg // 306f32e7eSjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 406f32e7eSjoerg // See https://llvm.org/LICENSE.txt for license information. 506f32e7eSjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 606f32e7eSjoerg // 706f32e7eSjoerg //===----------------------------------------------------------------------===// 806f32e7eSjoerg // Common definitions/declarations used internally by coroutine lowering passes. 906f32e7eSjoerg //===----------------------------------------------------------------------===// 1006f32e7eSjoerg 1106f32e7eSjoerg #ifndef LLVM_LIB_TRANSFORMS_COROUTINES_COROINTERNAL_H 1206f32e7eSjoerg #define LLVM_LIB_TRANSFORMS_COROUTINES_COROINTERNAL_H 1306f32e7eSjoerg 1406f32e7eSjoerg #include "CoroInstr.h" 1506f32e7eSjoerg #include "llvm/IR/IRBuilder.h" 1606f32e7eSjoerg #include "llvm/Transforms/Coroutines.h" 1706f32e7eSjoerg 1806f32e7eSjoerg namespace llvm { 1906f32e7eSjoerg 2006f32e7eSjoerg class CallGraph; 2106f32e7eSjoerg class CallGraphSCC; 2206f32e7eSjoerg class PassRegistry; 2306f32e7eSjoerg 24*da58b97aSjoerg void initializeCoroEarlyLegacyPass(PassRegistry &); 25*da58b97aSjoerg void initializeCoroSplitLegacyPass(PassRegistry &); 26*da58b97aSjoerg void initializeCoroElideLegacyPass(PassRegistry &); 27*da58b97aSjoerg void initializeCoroCleanupLegacyPass(PassRegistry &); 2806f32e7eSjoerg 2906f32e7eSjoerg // CoroEarly pass marks every function that has coro.begin with a string 3006f32e7eSjoerg // attribute "coroutine.presplit"="0". CoroSplit pass processes the coroutine 3106f32e7eSjoerg // twice. First, it lets it go through complete IPO optimization pipeline as a 3206f32e7eSjoerg // single function. It forces restart of the pipeline by inserting an indirect 3306f32e7eSjoerg // call to an empty function "coro.devirt.trigger" which is devirtualized by 3406f32e7eSjoerg // CoroElide pass that triggers a restart of the pipeline by CGPassManager. 3506f32e7eSjoerg // When CoroSplit pass sees the same coroutine the second time, it splits it up, 3606f32e7eSjoerg // adds coroutine subfunctions to the SCC to be processed by IPO pipeline. 37*da58b97aSjoerg // Async lowering similarily triggers a restart of the pipeline after it has 38*da58b97aSjoerg // split the coroutine. 3906f32e7eSjoerg #define CORO_PRESPLIT_ATTR "coroutine.presplit" 4006f32e7eSjoerg #define UNPREPARED_FOR_SPLIT "0" 4106f32e7eSjoerg #define PREPARED_FOR_SPLIT "1" 42*da58b97aSjoerg #define ASYNC_RESTART_AFTER_SPLIT "2" 4306f32e7eSjoerg 4406f32e7eSjoerg #define CORO_DEVIRT_TRIGGER_FN "coro.devirt.trigger" 4506f32e7eSjoerg 4606f32e7eSjoerg namespace coro { 4706f32e7eSjoerg 48*da58b97aSjoerg bool declaresIntrinsics(const Module &M, 49*da58b97aSjoerg const std::initializer_list<StringRef>); 5006f32e7eSjoerg void replaceCoroFree(CoroIdInst *CoroId, bool Elide); 5106f32e7eSjoerg void updateCallGraph(Function &Caller, ArrayRef<Function *> Funcs, 5206f32e7eSjoerg CallGraph &CG, CallGraphSCC &SCC); 53*da58b97aSjoerg /// Recover a dbg.declare prepared by the frontend and emit an alloca 54*da58b97aSjoerg /// holding a pointer to the coroutine frame. 55*da58b97aSjoerg void salvageDebugInfo( 56*da58b97aSjoerg SmallDenseMap<llvm::Value *, llvm::AllocaInst *, 4> &DbgPtrAllocaCache, 57*da58b97aSjoerg DbgVariableIntrinsic *DVI, bool ReuseFrameSlot); 5806f32e7eSjoerg 5906f32e7eSjoerg // Keeps data and helper functions for lowering coroutine intrinsics. 6006f32e7eSjoerg struct LowererBase { 6106f32e7eSjoerg Module &TheModule; 6206f32e7eSjoerg LLVMContext &Context; 6306f32e7eSjoerg PointerType *const Int8Ptr; 6406f32e7eSjoerg FunctionType *const ResumeFnType; 6506f32e7eSjoerg ConstantPointerNull *const NullPtr; 6606f32e7eSjoerg 6706f32e7eSjoerg LowererBase(Module &M); 6806f32e7eSjoerg Value *makeSubFnCall(Value *Arg, int Index, Instruction *InsertPt); 6906f32e7eSjoerg }; 7006f32e7eSjoerg 7106f32e7eSjoerg enum class ABI { 7206f32e7eSjoerg /// The "resume-switch" lowering, where there are separate resume and 7306f32e7eSjoerg /// destroy functions that are shared between all suspend points. The 7406f32e7eSjoerg /// coroutine frame implicitly stores the resume and destroy functions, 7506f32e7eSjoerg /// the current index, and any promise value. 7606f32e7eSjoerg Switch, 7706f32e7eSjoerg 7806f32e7eSjoerg /// The "returned-continuation" lowering, where each suspend point creates a 7906f32e7eSjoerg /// single continuation function that is used for both resuming and 8006f32e7eSjoerg /// destroying. Does not support promises. 8106f32e7eSjoerg Retcon, 8206f32e7eSjoerg 8306f32e7eSjoerg /// The "unique returned-continuation" lowering, where each suspend point 8406f32e7eSjoerg /// creates a single continuation function that is used for both resuming 8506f32e7eSjoerg /// and destroying. Does not support promises. The function is known to 8606f32e7eSjoerg /// suspend at most once during its execution, and the return value of 8706f32e7eSjoerg /// the continuation is void. 8806f32e7eSjoerg RetconOnce, 89*da58b97aSjoerg 90*da58b97aSjoerg /// The "async continuation" lowering, where each suspend point creates a 91*da58b97aSjoerg /// single continuation function. The continuation function is available as an 92*da58b97aSjoerg /// intrinsic. 93*da58b97aSjoerg Async, 9406f32e7eSjoerg }; 9506f32e7eSjoerg 9606f32e7eSjoerg // Holds structural Coroutine Intrinsics for a particular function and other 9706f32e7eSjoerg // values used during CoroSplit pass. 9806f32e7eSjoerg struct LLVM_LIBRARY_VISIBILITY Shape { 9906f32e7eSjoerg CoroBeginInst *CoroBegin; 100*da58b97aSjoerg SmallVector<AnyCoroEndInst *, 4> CoroEnds; 10106f32e7eSjoerg SmallVector<CoroSizeInst *, 2> CoroSizes; 10206f32e7eSjoerg SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends; 10306f32e7eSjoerg SmallVector<CallInst*, 2> SwiftErrorOps; 10406f32e7eSjoerg 10506f32e7eSjoerg // Field indexes for special fields in the switch lowering. 10606f32e7eSjoerg struct SwitchFieldIndex { 10706f32e7eSjoerg enum { 10806f32e7eSjoerg Resume, 109*da58b97aSjoerg Destroy 110*da58b97aSjoerg 111*da58b97aSjoerg // The promise field is always at a fixed offset from the start of 112*da58b97aSjoerg // frame given its type, but the index isn't a constant for all 113*da58b97aSjoerg // possible frames. 114*da58b97aSjoerg 115*da58b97aSjoerg // The switch-index field isn't at a fixed offset or index, either; 116*da58b97aSjoerg // we just work it in where it fits best. 11706f32e7eSjoerg }; 11806f32e7eSjoerg }; 11906f32e7eSjoerg 12006f32e7eSjoerg coro::ABI ABI; 12106f32e7eSjoerg 12206f32e7eSjoerg StructType *FrameTy; 123*da58b97aSjoerg Align FrameAlign; 124*da58b97aSjoerg uint64_t FrameSize; 12506f32e7eSjoerg Instruction *FramePtr; 12606f32e7eSjoerg BasicBlock *AllocaSpillBlock; 12706f32e7eSjoerg 128*da58b97aSjoerg /// This would only be true if optimization are enabled. 129*da58b97aSjoerg bool ReuseFrameSlot; 130*da58b97aSjoerg 13106f32e7eSjoerg struct SwitchLoweringStorage { 13206f32e7eSjoerg SwitchInst *ResumeSwitch; 13306f32e7eSjoerg AllocaInst *PromiseAlloca; 13406f32e7eSjoerg BasicBlock *ResumeEntryBlock; 135*da58b97aSjoerg unsigned IndexField; 136*da58b97aSjoerg unsigned IndexAlign; 137*da58b97aSjoerg unsigned IndexOffset; 13806f32e7eSjoerg bool HasFinalSuspend; 13906f32e7eSjoerg }; 14006f32e7eSjoerg 14106f32e7eSjoerg struct RetconLoweringStorage { 14206f32e7eSjoerg Function *ResumePrototype; 14306f32e7eSjoerg Function *Alloc; 14406f32e7eSjoerg Function *Dealloc; 14506f32e7eSjoerg BasicBlock *ReturnBlock; 14606f32e7eSjoerg bool IsFrameInlineInStorage; 14706f32e7eSjoerg }; 14806f32e7eSjoerg 149*da58b97aSjoerg struct AsyncLoweringStorage { 150*da58b97aSjoerg FunctionType *AsyncFuncTy; 151*da58b97aSjoerg Value *Context; 152*da58b97aSjoerg CallingConv::ID AsyncCC; 153*da58b97aSjoerg unsigned ContextArgNo; 154*da58b97aSjoerg uint64_t ContextHeaderSize; 155*da58b97aSjoerg uint64_t ContextAlignment; 156*da58b97aSjoerg uint64_t FrameOffset; // Start of the frame. 157*da58b97aSjoerg uint64_t ContextSize; // Includes frame size. 158*da58b97aSjoerg GlobalVariable *AsyncFuncPointer; 159*da58b97aSjoerg getContextAlignmentShape::AsyncLoweringStorage160*da58b97aSjoerg Align getContextAlignment() const { return Align(ContextAlignment); } 161*da58b97aSjoerg }; 162*da58b97aSjoerg 16306f32e7eSjoerg union { 16406f32e7eSjoerg SwitchLoweringStorage SwitchLowering; 16506f32e7eSjoerg RetconLoweringStorage RetconLowering; 166*da58b97aSjoerg AsyncLoweringStorage AsyncLowering; 16706f32e7eSjoerg }; 16806f32e7eSjoerg getSwitchCoroIdShape16906f32e7eSjoerg CoroIdInst *getSwitchCoroId() const { 17006f32e7eSjoerg assert(ABI == coro::ABI::Switch); 17106f32e7eSjoerg return cast<CoroIdInst>(CoroBegin->getId()); 17206f32e7eSjoerg } 17306f32e7eSjoerg getRetconCoroIdShape17406f32e7eSjoerg AnyCoroIdRetconInst *getRetconCoroId() const { 17506f32e7eSjoerg assert(ABI == coro::ABI::Retcon || 17606f32e7eSjoerg ABI == coro::ABI::RetconOnce); 17706f32e7eSjoerg return cast<AnyCoroIdRetconInst>(CoroBegin->getId()); 17806f32e7eSjoerg } 17906f32e7eSjoerg getAsyncCoroIdShape180*da58b97aSjoerg CoroIdAsyncInst *getAsyncCoroId() const { 181*da58b97aSjoerg assert(ABI == coro::ABI::Async); 182*da58b97aSjoerg return cast<CoroIdAsyncInst>(CoroBegin->getId()); 183*da58b97aSjoerg } 184*da58b97aSjoerg getSwitchIndexFieldShape185*da58b97aSjoerg unsigned getSwitchIndexField() const { 186*da58b97aSjoerg assert(ABI == coro::ABI::Switch); 187*da58b97aSjoerg assert(FrameTy && "frame type not assigned"); 188*da58b97aSjoerg return SwitchLowering.IndexField; 189*da58b97aSjoerg } getIndexTypeShape19006f32e7eSjoerg IntegerType *getIndexType() const { 19106f32e7eSjoerg assert(ABI == coro::ABI::Switch); 19206f32e7eSjoerg assert(FrameTy && "frame type not assigned"); 193*da58b97aSjoerg return cast<IntegerType>(FrameTy->getElementType(getSwitchIndexField())); 19406f32e7eSjoerg } getIndexShape19506f32e7eSjoerg ConstantInt *getIndex(uint64_t Value) const { 19606f32e7eSjoerg return ConstantInt::get(getIndexType(), Value); 19706f32e7eSjoerg } 19806f32e7eSjoerg getSwitchResumePointerTypeShape19906f32e7eSjoerg PointerType *getSwitchResumePointerType() const { 20006f32e7eSjoerg assert(ABI == coro::ABI::Switch); 20106f32e7eSjoerg assert(FrameTy && "frame type not assigned"); 20206f32e7eSjoerg return cast<PointerType>(FrameTy->getElementType(SwitchFieldIndex::Resume)); 20306f32e7eSjoerg } 20406f32e7eSjoerg getResumeFunctionTypeShape20506f32e7eSjoerg FunctionType *getResumeFunctionType() const { 20606f32e7eSjoerg switch (ABI) { 20706f32e7eSjoerg case coro::ABI::Switch: { 20806f32e7eSjoerg auto *FnPtrTy = getSwitchResumePointerType(); 20906f32e7eSjoerg return cast<FunctionType>(FnPtrTy->getPointerElementType()); 21006f32e7eSjoerg } 21106f32e7eSjoerg case coro::ABI::Retcon: 21206f32e7eSjoerg case coro::ABI::RetconOnce: 21306f32e7eSjoerg return RetconLowering.ResumePrototype->getFunctionType(); 214*da58b97aSjoerg case coro::ABI::Async: 215*da58b97aSjoerg // Not used. The function type depends on the active suspend. 216*da58b97aSjoerg return nullptr; 21706f32e7eSjoerg } 218*da58b97aSjoerg 21906f32e7eSjoerg llvm_unreachable("Unknown coro::ABI enum"); 22006f32e7eSjoerg } 22106f32e7eSjoerg getRetconResultTypesShape22206f32e7eSjoerg ArrayRef<Type*> getRetconResultTypes() const { 22306f32e7eSjoerg assert(ABI == coro::ABI::Retcon || 22406f32e7eSjoerg ABI == coro::ABI::RetconOnce); 22506f32e7eSjoerg auto FTy = CoroBegin->getFunction()->getFunctionType(); 22606f32e7eSjoerg 22706f32e7eSjoerg // The safety of all this is checked by checkWFRetconPrototype. 22806f32e7eSjoerg if (auto STy = dyn_cast<StructType>(FTy->getReturnType())) { 22906f32e7eSjoerg return STy->elements().slice(1); 23006f32e7eSjoerg } else { 23106f32e7eSjoerg return ArrayRef<Type*>(); 23206f32e7eSjoerg } 23306f32e7eSjoerg } 23406f32e7eSjoerg getRetconResumeTypesShape23506f32e7eSjoerg ArrayRef<Type*> getRetconResumeTypes() const { 23606f32e7eSjoerg assert(ABI == coro::ABI::Retcon || 23706f32e7eSjoerg ABI == coro::ABI::RetconOnce); 23806f32e7eSjoerg 23906f32e7eSjoerg // The safety of all this is checked by checkWFRetconPrototype. 24006f32e7eSjoerg auto FTy = RetconLowering.ResumePrototype->getFunctionType(); 24106f32e7eSjoerg return FTy->params().slice(1); 24206f32e7eSjoerg } 24306f32e7eSjoerg getResumeFunctionCCShape24406f32e7eSjoerg CallingConv::ID getResumeFunctionCC() const { 24506f32e7eSjoerg switch (ABI) { 24606f32e7eSjoerg case coro::ABI::Switch: 24706f32e7eSjoerg return CallingConv::Fast; 24806f32e7eSjoerg 24906f32e7eSjoerg case coro::ABI::Retcon: 25006f32e7eSjoerg case coro::ABI::RetconOnce: 25106f32e7eSjoerg return RetconLowering.ResumePrototype->getCallingConv(); 252*da58b97aSjoerg case coro::ABI::Async: 253*da58b97aSjoerg return AsyncLowering.AsyncCC; 25406f32e7eSjoerg } 25506f32e7eSjoerg llvm_unreachable("Unknown coro::ABI enum"); 25606f32e7eSjoerg } 25706f32e7eSjoerg getPromiseAllocaShape25806f32e7eSjoerg AllocaInst *getPromiseAlloca() const { 25906f32e7eSjoerg if (ABI == coro::ABI::Switch) 26006f32e7eSjoerg return SwitchLowering.PromiseAlloca; 26106f32e7eSjoerg return nullptr; 26206f32e7eSjoerg } 26306f32e7eSjoerg 26406f32e7eSjoerg /// Allocate memory according to the rules of the active lowering. 26506f32e7eSjoerg /// 26606f32e7eSjoerg /// \param CG - if non-null, will be updated for the new call 26706f32e7eSjoerg Value *emitAlloc(IRBuilder<> &Builder, Value *Size, CallGraph *CG) const; 26806f32e7eSjoerg 26906f32e7eSjoerg /// Deallocate memory according to the rules of the active lowering. 27006f32e7eSjoerg /// 27106f32e7eSjoerg /// \param CG - if non-null, will be updated for the new call 27206f32e7eSjoerg void emitDealloc(IRBuilder<> &Builder, Value *Ptr, CallGraph *CG) const; 27306f32e7eSjoerg 27406f32e7eSjoerg Shape() = default; 275*da58b97aSjoerg explicit Shape(Function &F, bool ReuseFrameSlot = false) ReuseFrameSlotShape276*da58b97aSjoerg : ReuseFrameSlot(ReuseFrameSlot) { 277*da58b97aSjoerg buildFrom(F); 278*da58b97aSjoerg } 27906f32e7eSjoerg void buildFrom(Function &F); 28006f32e7eSjoerg }; 28106f32e7eSjoerg 28206f32e7eSjoerg void buildCoroutineFrame(Function &F, Shape &Shape); 283*da58b97aSjoerg CallInst *createMustTailCall(DebugLoc Loc, Function *MustTailCallFn, 284*da58b97aSjoerg ArrayRef<Value *> Arguments, IRBuilder<> &); 28506f32e7eSjoerg } // End namespace coro. 28606f32e7eSjoerg } // End namespace llvm 28706f32e7eSjoerg 28806f32e7eSjoerg #endif 289