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