[InstCombine] Signed saturation patterns
[llvm-core.git] / lib / Transforms / Coroutines / CoroInternal.h
blobc151474316f908e45abc4c7be5784fb0e2b6345e
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 //===----------------------------------------------------------------------===//
11 #ifndef LLVM_LIB_TRANSFORMS_COROUTINES_COROINTERNAL_H
12 #define LLVM_LIB_TRANSFORMS_COROUTINES_COROINTERNAL_H
14 #include "CoroInstr.h"
15 #include "llvm/IR/IRBuilder.h"
16 #include "llvm/Transforms/Coroutines.h"
18 namespace llvm {
20 class CallGraph;
21 class CallGraphSCC;
22 class PassRegistry;
24 void initializeCoroEarlyPass(PassRegistry &);
25 void initializeCoroSplitPass(PassRegistry &);
26 void initializeCoroElidePass(PassRegistry &);
27 void initializeCoroCleanupPass(PassRegistry &);
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.
38 #define CORO_PRESPLIT_ATTR "coroutine.presplit"
39 #define UNPREPARED_FOR_SPLIT "0"
40 #define PREPARED_FOR_SPLIT "1"
42 #define CORO_DEVIRT_TRIGGER_FN "coro.devirt.trigger"
44 namespace coro {
46 bool declaresIntrinsics(Module &M, std::initializer_list<StringRef>);
47 void replaceAllCoroAllocs(CoroBeginInst *CB, bool Replacement);
48 void replaceAllCoroFrees(CoroBeginInst *CB, Value *Replacement);
49 void replaceCoroFree(CoroIdInst *CoroId, bool Elide);
50 void updateCallGraph(Function &Caller, ArrayRef<Function *> Funcs,
51 CallGraph &CG, CallGraphSCC &SCC);
53 // Keeps data and helper functions for lowering coroutine intrinsics.
54 struct LowererBase {
55 Module &TheModule;
56 LLVMContext &Context;
57 PointerType *const Int8Ptr;
58 FunctionType *const ResumeFnType;
59 ConstantPointerNull *const NullPtr;
61 LowererBase(Module &M);
62 Value *makeSubFnCall(Value *Arg, int Index, Instruction *InsertPt);
65 enum class ABI {
66 /// The "resume-switch" lowering, where there are separate resume and
67 /// destroy functions that are shared between all suspend points. The
68 /// coroutine frame implicitly stores the resume and destroy functions,
69 /// the current index, and any promise value.
70 Switch,
72 /// The "returned-continuation" lowering, where each suspend point creates a
73 /// single continuation function that is used for both resuming and
74 /// destroying. Does not support promises.
75 Retcon,
77 /// The "unique returned-continuation" lowering, where each suspend point
78 /// creates a single continuation function that is used for both resuming
79 /// and destroying. Does not support promises. The function is known to
80 /// suspend at most once during its execution, and the return value of
81 /// the continuation is void.
82 RetconOnce,
85 // Holds structural Coroutine Intrinsics for a particular function and other
86 // values used during CoroSplit pass.
87 struct LLVM_LIBRARY_VISIBILITY Shape {
88 CoroBeginInst *CoroBegin;
89 SmallVector<CoroEndInst *, 4> CoroEnds;
90 SmallVector<CoroSizeInst *, 2> CoroSizes;
91 SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends;
92 SmallVector<CallInst*, 2> SwiftErrorOps;
94 // Field indexes for special fields in the switch lowering.
95 struct SwitchFieldIndex {
96 enum {
97 Resume,
98 Destroy,
99 Promise,
100 Index,
101 /// The index of the first spill field.
102 FirstSpill
106 coro::ABI ABI;
108 StructType *FrameTy;
109 Instruction *FramePtr;
110 BasicBlock *AllocaSpillBlock;
112 struct SwitchLoweringStorage {
113 SwitchInst *ResumeSwitch;
114 AllocaInst *PromiseAlloca;
115 BasicBlock *ResumeEntryBlock;
116 bool HasFinalSuspend;
119 struct RetconLoweringStorage {
120 Function *ResumePrototype;
121 Function *Alloc;
122 Function *Dealloc;
123 BasicBlock *ReturnBlock;
124 bool IsFrameInlineInStorage;
127 union {
128 SwitchLoweringStorage SwitchLowering;
129 RetconLoweringStorage RetconLowering;
132 CoroIdInst *getSwitchCoroId() const {
133 assert(ABI == coro::ABI::Switch);
134 return cast<CoroIdInst>(CoroBegin->getId());
137 AnyCoroIdRetconInst *getRetconCoroId() const {
138 assert(ABI == coro::ABI::Retcon ||
139 ABI == coro::ABI::RetconOnce);
140 return cast<AnyCoroIdRetconInst>(CoroBegin->getId());
143 IntegerType *getIndexType() const {
144 assert(ABI == coro::ABI::Switch);
145 assert(FrameTy && "frame type not assigned");
146 return cast<IntegerType>(FrameTy->getElementType(SwitchFieldIndex::Index));
148 ConstantInt *getIndex(uint64_t Value) const {
149 return ConstantInt::get(getIndexType(), Value);
152 PointerType *getSwitchResumePointerType() const {
153 assert(ABI == coro::ABI::Switch);
154 assert(FrameTy && "frame type not assigned");
155 return cast<PointerType>(FrameTy->getElementType(SwitchFieldIndex::Resume));
158 FunctionType *getResumeFunctionType() const {
159 switch (ABI) {
160 case coro::ABI::Switch: {
161 auto *FnPtrTy = getSwitchResumePointerType();
162 return cast<FunctionType>(FnPtrTy->getPointerElementType());
164 case coro::ABI::Retcon:
165 case coro::ABI::RetconOnce:
166 return RetconLowering.ResumePrototype->getFunctionType();
168 llvm_unreachable("Unknown coro::ABI enum");
171 ArrayRef<Type*> getRetconResultTypes() const {
172 assert(ABI == coro::ABI::Retcon ||
173 ABI == coro::ABI::RetconOnce);
174 auto FTy = CoroBegin->getFunction()->getFunctionType();
176 // The safety of all this is checked by checkWFRetconPrototype.
177 if (auto STy = dyn_cast<StructType>(FTy->getReturnType())) {
178 return STy->elements().slice(1);
179 } else {
180 return ArrayRef<Type*>();
184 ArrayRef<Type*> getRetconResumeTypes() const {
185 assert(ABI == coro::ABI::Retcon ||
186 ABI == coro::ABI::RetconOnce);
188 // The safety of all this is checked by checkWFRetconPrototype.
189 auto FTy = RetconLowering.ResumePrototype->getFunctionType();
190 return FTy->params().slice(1);
193 CallingConv::ID getResumeFunctionCC() const {
194 switch (ABI) {
195 case coro::ABI::Switch:
196 return CallingConv::Fast;
198 case coro::ABI::Retcon:
199 case coro::ABI::RetconOnce:
200 return RetconLowering.ResumePrototype->getCallingConv();
202 llvm_unreachable("Unknown coro::ABI enum");
205 unsigned getFirstSpillFieldIndex() const {
206 switch (ABI) {
207 case coro::ABI::Switch:
208 return SwitchFieldIndex::FirstSpill;
210 case coro::ABI::Retcon:
211 case coro::ABI::RetconOnce:
212 return 0;
214 llvm_unreachable("Unknown coro::ABI enum");
217 AllocaInst *getPromiseAlloca() const {
218 if (ABI == coro::ABI::Switch)
219 return SwitchLowering.PromiseAlloca;
220 return nullptr;
223 /// Allocate memory according to the rules of the active lowering.
225 /// \param CG - if non-null, will be updated for the new call
226 Value *emitAlloc(IRBuilder<> &Builder, Value *Size, CallGraph *CG) const;
228 /// Deallocate memory according to the rules of the active lowering.
230 /// \param CG - if non-null, will be updated for the new call
231 void emitDealloc(IRBuilder<> &Builder, Value *Ptr, CallGraph *CG) const;
233 Shape() = default;
234 explicit Shape(Function &F) { buildFrom(F); }
235 void buildFrom(Function &F);
238 void buildCoroutineFrame(Function &F, Shape &Shape);
240 } // End namespace coro.
241 } // End namespace llvm
243 #endif