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"
16 #include "llvm/Transforms/Coroutines.h"
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"
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.
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
);
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.
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.
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.
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
{
101 /// The index of the first spill field.
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
;
123 BasicBlock
*ReturnBlock
;
124 bool IsFrameInlineInStorage
;
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 {
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);
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 {
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 {
207 case coro::ABI::Switch
:
208 return SwitchFieldIndex::FirstSpill
;
210 case coro::ABI::Retcon
:
211 case coro::ABI::RetconOnce
:
214 llvm_unreachable("Unknown coro::ABI enum");
217 AllocaInst
*getPromiseAlloca() const {
218 if (ABI
== coro::ABI::Switch
)
219 return SwitchLowering
.PromiseAlloca
;
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;
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