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 initializeCoroEarlyLegacyPass(PassRegistry
&);
25 void initializeCoroSplitLegacyPass(PassRegistry
&);
26 void initializeCoroElideLegacyPass(PassRegistry
&);
27 void initializeCoroCleanupLegacyPass(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(const Module
&M
,
47 const std::initializer_list
<StringRef
>);
48 void replaceAllCoroAllocs(CoroBeginInst
*CB
, bool Replacement
);
49 void replaceAllCoroFrees(CoroBeginInst
*CB
, Value
*Replacement
);
50 void replaceCoroFree(CoroIdInst
*CoroId
, bool Elide
);
51 void updateCallGraph(Function
&Caller
, ArrayRef
<Function
*> Funcs
,
52 CallGraph
&CG
, CallGraphSCC
&SCC
);
54 // Keeps data and helper functions for lowering coroutine intrinsics.
58 PointerType
*const Int8Ptr
;
59 FunctionType
*const ResumeFnType
;
60 ConstantPointerNull
*const NullPtr
;
62 LowererBase(Module
&M
);
63 Value
*makeSubFnCall(Value
*Arg
, int Index
, Instruction
*InsertPt
);
67 /// The "resume-switch" lowering, where there are separate resume and
68 /// destroy functions that are shared between all suspend points. The
69 /// coroutine frame implicitly stores the resume and destroy functions,
70 /// the current index, and any promise value.
73 /// The "returned-continuation" lowering, where each suspend point creates a
74 /// single continuation function that is used for both resuming and
75 /// destroying. Does not support promises.
78 /// The "unique returned-continuation" lowering, where each suspend point
79 /// creates a single continuation function that is used for both resuming
80 /// and destroying. Does not support promises. The function is known to
81 /// suspend at most once during its execution, and the return value of
82 /// the continuation is void.
86 // Holds structural Coroutine Intrinsics for a particular function and other
87 // values used during CoroSplit pass.
88 struct LLVM_LIBRARY_VISIBILITY Shape
{
89 CoroBeginInst
*CoroBegin
;
90 SmallVector
<CoroEndInst
*, 4> CoroEnds
;
91 SmallVector
<CoroSizeInst
*, 2> CoroSizes
;
92 SmallVector
<AnyCoroSuspendInst
*, 4> CoroSuspends
;
93 SmallVector
<CallInst
*, 2> SwiftErrorOps
;
95 // Field indexes for special fields in the switch lowering.
96 struct SwitchFieldIndex
{
102 /// The index of the first spill field.
110 Instruction
*FramePtr
;
111 BasicBlock
*AllocaSpillBlock
;
113 struct SwitchLoweringStorage
{
114 SwitchInst
*ResumeSwitch
;
115 AllocaInst
*PromiseAlloca
;
116 BasicBlock
*ResumeEntryBlock
;
117 bool HasFinalSuspend
;
120 struct RetconLoweringStorage
{
121 Function
*ResumePrototype
;
124 BasicBlock
*ReturnBlock
;
125 bool IsFrameInlineInStorage
;
129 SwitchLoweringStorage SwitchLowering
;
130 RetconLoweringStorage RetconLowering
;
133 CoroIdInst
*getSwitchCoroId() const {
134 assert(ABI
== coro::ABI::Switch
);
135 return cast
<CoroIdInst
>(CoroBegin
->getId());
138 AnyCoroIdRetconInst
*getRetconCoroId() const {
139 assert(ABI
== coro::ABI::Retcon
||
140 ABI
== coro::ABI::RetconOnce
);
141 return cast
<AnyCoroIdRetconInst
>(CoroBegin
->getId());
144 IntegerType
*getIndexType() const {
145 assert(ABI
== coro::ABI::Switch
);
146 assert(FrameTy
&& "frame type not assigned");
147 return cast
<IntegerType
>(FrameTy
->getElementType(SwitchFieldIndex::Index
));
149 ConstantInt
*getIndex(uint64_t Value
) const {
150 return ConstantInt::get(getIndexType(), Value
);
153 PointerType
*getSwitchResumePointerType() const {
154 assert(ABI
== coro::ABI::Switch
);
155 assert(FrameTy
&& "frame type not assigned");
156 return cast
<PointerType
>(FrameTy
->getElementType(SwitchFieldIndex::Resume
));
159 FunctionType
*getResumeFunctionType() const {
161 case coro::ABI::Switch
: {
162 auto *FnPtrTy
= getSwitchResumePointerType();
163 return cast
<FunctionType
>(FnPtrTy
->getPointerElementType());
165 case coro::ABI::Retcon
:
166 case coro::ABI::RetconOnce
:
167 return RetconLowering
.ResumePrototype
->getFunctionType();
169 llvm_unreachable("Unknown coro::ABI enum");
172 ArrayRef
<Type
*> getRetconResultTypes() const {
173 assert(ABI
== coro::ABI::Retcon
||
174 ABI
== coro::ABI::RetconOnce
);
175 auto FTy
= CoroBegin
->getFunction()->getFunctionType();
177 // The safety of all this is checked by checkWFRetconPrototype.
178 if (auto STy
= dyn_cast
<StructType
>(FTy
->getReturnType())) {
179 return STy
->elements().slice(1);
181 return ArrayRef
<Type
*>();
185 ArrayRef
<Type
*> getRetconResumeTypes() const {
186 assert(ABI
== coro::ABI::Retcon
||
187 ABI
== coro::ABI::RetconOnce
);
189 // The safety of all this is checked by checkWFRetconPrototype.
190 auto FTy
= RetconLowering
.ResumePrototype
->getFunctionType();
191 return FTy
->params().slice(1);
194 CallingConv::ID
getResumeFunctionCC() const {
196 case coro::ABI::Switch
:
197 return CallingConv::Fast
;
199 case coro::ABI::Retcon
:
200 case coro::ABI::RetconOnce
:
201 return RetconLowering
.ResumePrototype
->getCallingConv();
203 llvm_unreachable("Unknown coro::ABI enum");
206 unsigned getFirstSpillFieldIndex() const {
208 case coro::ABI::Switch
:
209 return SwitchFieldIndex::FirstSpill
;
211 case coro::ABI::Retcon
:
212 case coro::ABI::RetconOnce
:
215 llvm_unreachable("Unknown coro::ABI enum");
218 AllocaInst
*getPromiseAlloca() const {
219 if (ABI
== coro::ABI::Switch
)
220 return SwitchLowering
.PromiseAlloca
;
224 /// Allocate memory according to the rules of the active lowering.
226 /// \param CG - if non-null, will be updated for the new call
227 Value
*emitAlloc(IRBuilder
<> &Builder
, Value
*Size
, CallGraph
*CG
) const;
229 /// Deallocate memory according to the rules of the active lowering.
231 /// \param CG - if non-null, will be updated for the new call
232 void emitDealloc(IRBuilder
<> &Builder
, Value
*Ptr
, CallGraph
*CG
) const;
235 explicit Shape(Function
&F
) { buildFrom(F
); }
236 void buildFrom(Function
&F
);
239 void buildCoroutineFrame(Function
&F
, Shape
&Shape
);
241 } // End namespace coro.
242 } // End namespace llvm