[RISCV] Fix mgather -> riscv.masked.strided.load combine not extending indices (...
[llvm-project.git] / llvm / lib / Transforms / Coroutines / CoroInternal.h
blobfb16a4090689b438401cfa4eed328c1b0f7040ec
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"
17 namespace llvm {
19 class CallGraph;
21 namespace coro {
23 bool declaresAnyIntrinsic(const Module &M);
24 bool declaresIntrinsics(const Module &M,
25 const std::initializer_list<StringRef>);
26 void replaceCoroFree(CoroIdInst *CoroId, bool Elide);
28 /// Attempts to rewrite the location operand of debug intrinsics in terms of
29 /// the coroutine frame pointer, folding pointer offsets into the DIExpression
30 /// of the intrinsic.
31 /// If the frame pointer is an Argument, store it into an alloca if
32 /// OptimizeFrame is false.
33 void salvageDebugInfo(
34 SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap,
35 DbgVariableIntrinsic &DVI, bool OptimizeFrame, bool IsEntryPoint);
36 void salvageDebugInfo(
37 SmallDenseMap<Argument *, AllocaInst *, 4> &ArgToAllocaMap, DPValue &DPV,
38 bool OptimizeFrame, bool UseEntryValue);
40 // Keeps data and helper functions for lowering coroutine intrinsics.
41 struct LowererBase {
42 Module &TheModule;
43 LLVMContext &Context;
44 PointerType *const Int8Ptr;
45 FunctionType *const ResumeFnType;
46 ConstantPointerNull *const NullPtr;
48 LowererBase(Module &M);
49 Value *makeSubFnCall(Value *Arg, int Index, Instruction *InsertPt);
52 enum class ABI {
53 /// The "resume-switch" lowering, where there are separate resume and
54 /// destroy functions that are shared between all suspend points. The
55 /// coroutine frame implicitly stores the resume and destroy functions,
56 /// the current index, and any promise value.
57 Switch,
59 /// The "returned-continuation" lowering, where each suspend point creates a
60 /// single continuation function that is used for both resuming and
61 /// destroying. Does not support promises.
62 Retcon,
64 /// The "unique returned-continuation" lowering, where each suspend point
65 /// creates a single continuation function that is used for both resuming
66 /// and destroying. Does not support promises. The function is known to
67 /// suspend at most once during its execution, and the return value of
68 /// the continuation is void.
69 RetconOnce,
71 /// The "async continuation" lowering, where each suspend point creates a
72 /// single continuation function. The continuation function is available as an
73 /// intrinsic.
74 Async,
77 // Holds structural Coroutine Intrinsics for a particular function and other
78 // values used during CoroSplit pass.
79 struct LLVM_LIBRARY_VISIBILITY Shape {
80 CoroBeginInst *CoroBegin;
81 SmallVector<AnyCoroEndInst *, 4> CoroEnds;
82 SmallVector<CoroSizeInst *, 2> CoroSizes;
83 SmallVector<CoroAlignInst *, 2> CoroAligns;
84 SmallVector<AnyCoroSuspendInst *, 4> CoroSuspends;
85 SmallVector<CallInst*, 2> SwiftErrorOps;
87 // Field indexes for special fields in the switch lowering.
88 struct SwitchFieldIndex {
89 enum {
90 Resume,
91 Destroy
93 // The promise field is always at a fixed offset from the start of
94 // frame given its type, but the index isn't a constant for all
95 // possible frames.
97 // The switch-index field isn't at a fixed offset or index, either;
98 // we just work it in where it fits best.
102 coro::ABI ABI;
104 StructType *FrameTy;
105 Align FrameAlign;
106 uint64_t FrameSize;
107 Value *FramePtr;
108 BasicBlock *AllocaSpillBlock;
110 /// This would only be true if optimization are enabled.
111 bool OptimizeFrame;
113 struct SwitchLoweringStorage {
114 SwitchInst *ResumeSwitch;
115 AllocaInst *PromiseAlloca;
116 BasicBlock *ResumeEntryBlock;
117 unsigned IndexField;
118 unsigned IndexAlign;
119 unsigned IndexOffset;
120 bool HasFinalSuspend;
121 bool HasUnwindCoroEnd;
124 struct RetconLoweringStorage {
125 Function *ResumePrototype;
126 Function *Alloc;
127 Function *Dealloc;
128 BasicBlock *ReturnBlock;
129 bool IsFrameInlineInStorage;
132 struct AsyncLoweringStorage {
133 Value *Context;
134 CallingConv::ID AsyncCC;
135 unsigned ContextArgNo;
136 uint64_t ContextHeaderSize;
137 uint64_t ContextAlignment;
138 uint64_t FrameOffset; // Start of the frame.
139 uint64_t ContextSize; // Includes frame size.
140 GlobalVariable *AsyncFuncPointer;
142 Align getContextAlignment() const { return Align(ContextAlignment); }
145 union {
146 SwitchLoweringStorage SwitchLowering;
147 RetconLoweringStorage RetconLowering;
148 AsyncLoweringStorage AsyncLowering;
151 CoroIdInst *getSwitchCoroId() const {
152 assert(ABI == coro::ABI::Switch);
153 return cast<CoroIdInst>(CoroBegin->getId());
156 AnyCoroIdRetconInst *getRetconCoroId() const {
157 assert(ABI == coro::ABI::Retcon ||
158 ABI == coro::ABI::RetconOnce);
159 return cast<AnyCoroIdRetconInst>(CoroBegin->getId());
162 CoroIdAsyncInst *getAsyncCoroId() const {
163 assert(ABI == coro::ABI::Async);
164 return cast<CoroIdAsyncInst>(CoroBegin->getId());
167 unsigned getSwitchIndexField() const {
168 assert(ABI == coro::ABI::Switch);
169 assert(FrameTy && "frame type not assigned");
170 return SwitchLowering.IndexField;
172 IntegerType *getIndexType() const {
173 assert(ABI == coro::ABI::Switch);
174 assert(FrameTy && "frame type not assigned");
175 return cast<IntegerType>(FrameTy->getElementType(getSwitchIndexField()));
177 ConstantInt *getIndex(uint64_t Value) const {
178 return ConstantInt::get(getIndexType(), Value);
181 PointerType *getSwitchResumePointerType() const {
182 assert(ABI == coro::ABI::Switch);
183 assert(FrameTy && "frame type not assigned");
184 return cast<PointerType>(FrameTy->getElementType(SwitchFieldIndex::Resume));
187 FunctionType *getResumeFunctionType() const {
188 switch (ABI) {
189 case coro::ABI::Switch:
190 return FunctionType::get(Type::getVoidTy(FrameTy->getContext()),
191 PointerType::getUnqual(FrameTy->getContext()),
192 /*IsVarArg=*/false);
193 case coro::ABI::Retcon:
194 case coro::ABI::RetconOnce:
195 return RetconLowering.ResumePrototype->getFunctionType();
196 case coro::ABI::Async:
197 // Not used. The function type depends on the active suspend.
198 return nullptr;
201 llvm_unreachable("Unknown coro::ABI enum");
204 ArrayRef<Type*> getRetconResultTypes() const {
205 assert(ABI == coro::ABI::Retcon ||
206 ABI == coro::ABI::RetconOnce);
207 auto FTy = CoroBegin->getFunction()->getFunctionType();
209 // The safety of all this is checked by checkWFRetconPrototype.
210 if (auto STy = dyn_cast<StructType>(FTy->getReturnType())) {
211 return STy->elements().slice(1);
212 } else {
213 return ArrayRef<Type*>();
217 ArrayRef<Type*> getRetconResumeTypes() const {
218 assert(ABI == coro::ABI::Retcon ||
219 ABI == coro::ABI::RetconOnce);
221 // The safety of all this is checked by checkWFRetconPrototype.
222 auto FTy = RetconLowering.ResumePrototype->getFunctionType();
223 return FTy->params().slice(1);
226 CallingConv::ID getResumeFunctionCC() const {
227 switch (ABI) {
228 case coro::ABI::Switch:
229 return CallingConv::Fast;
231 case coro::ABI::Retcon:
232 case coro::ABI::RetconOnce:
233 return RetconLowering.ResumePrototype->getCallingConv();
234 case coro::ABI::Async:
235 return AsyncLowering.AsyncCC;
237 llvm_unreachable("Unknown coro::ABI enum");
240 AllocaInst *getPromiseAlloca() const {
241 if (ABI == coro::ABI::Switch)
242 return SwitchLowering.PromiseAlloca;
243 return nullptr;
246 BasicBlock::iterator getInsertPtAfterFramePtr() const {
247 if (auto *I = dyn_cast<Instruction>(FramePtr)) {
248 BasicBlock::iterator It = std::next(I->getIterator());
249 It.setHeadBit(true); // Copy pre-RemoveDIs behaviour.
250 return It;
252 return cast<Argument>(FramePtr)->getParent()->getEntryBlock().begin();
255 /// Allocate memory according to the rules of the active lowering.
257 /// \param CG - if non-null, will be updated for the new call
258 Value *emitAlloc(IRBuilder<> &Builder, Value *Size, CallGraph *CG) const;
260 /// Deallocate memory according to the rules of the active lowering.
262 /// \param CG - if non-null, will be updated for the new call
263 void emitDealloc(IRBuilder<> &Builder, Value *Ptr, CallGraph *CG) const;
265 Shape() = default;
266 explicit Shape(Function &F, bool OptimizeFrame = false)
267 : OptimizeFrame(OptimizeFrame) {
268 buildFrom(F);
270 void buildFrom(Function &F);
273 bool defaultMaterializable(Instruction &V);
274 void buildCoroutineFrame(
275 Function &F, Shape &Shape,
276 const std::function<bool(Instruction &)> &MaterializableCallback);
277 CallInst *createMustTailCall(DebugLoc Loc, Function *MustTailCallFn,
278 ArrayRef<Value *> Arguments, IRBuilder<> &);
279 } // End namespace coro.
280 } // End namespace llvm
282 #endif