1 //===- CoroInternal.h - Internal Coroutine interfaces ---------*- C++ -*---===//
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
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"
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
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.
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
);
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.
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.
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.
71 /// The "async continuation" lowering, where each suspend point creates a
72 /// single continuation function. The continuation function is available as an
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
{
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
97 // The switch-index field isn't at a fixed offset or index, either;
98 // we just work it in where it fits best.
108 BasicBlock
*AllocaSpillBlock
;
110 /// This would only be true if optimization are enabled.
113 struct SwitchLoweringStorage
{
114 SwitchInst
*ResumeSwitch
;
115 AllocaInst
*PromiseAlloca
;
116 BasicBlock
*ResumeEntryBlock
;
119 unsigned IndexOffset
;
120 bool HasFinalSuspend
;
121 bool HasUnwindCoroEnd
;
124 struct RetconLoweringStorage
{
125 Function
*ResumePrototype
;
128 BasicBlock
*ReturnBlock
;
129 bool IsFrameInlineInStorage
;
132 struct AsyncLoweringStorage
{
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
); }
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 {
189 case coro::ABI::Switch
:
190 return FunctionType::get(Type::getVoidTy(FrameTy
->getContext()),
191 PointerType::getUnqual(FrameTy
->getContext()),
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.
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);
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 {
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
;
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.
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;
266 explicit Shape(Function
&F
, bool OptimizeFrame
= false)
267 : OptimizeFrame(OptimizeFrame
) {
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