1 //===- Coroutines.cpp -----------------------------------------------------===//
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 //===----------------------------------------------------------------------===//
9 // This file implements the common infrastructure for Coroutine Passes.
11 //===----------------------------------------------------------------------===//
13 #include "llvm/Transforms/Coroutines.h"
14 #include "llvm-c/Transforms/Coroutines.h"
15 #include "CoroInstr.h"
16 #include "CoroInternal.h"
17 #include "llvm/ADT/SmallVector.h"
18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Analysis/CallGraph.h"
20 #include "llvm/Analysis/CallGraphSCCPass.h"
21 #include "llvm/Transforms/Utils/Local.h"
22 #include "llvm/IR/Attributes.h"
23 #include "llvm/IR/CallSite.h"
24 #include "llvm/IR/Constants.h"
25 #include "llvm/IR/DerivedTypes.h"
26 #include "llvm/IR/Function.h"
27 #include "llvm/IR/InstIterator.h"
28 #include "llvm/IR/Instructions.h"
29 #include "llvm/IR/IntrinsicInst.h"
30 #include "llvm/IR/Intrinsics.h"
31 #include "llvm/IR/LegacyPassManager.h"
32 #include "llvm/IR/Module.h"
33 #include "llvm/IR/Type.h"
34 #include "llvm/Support/Casting.h"
35 #include "llvm/Support/ErrorHandling.h"
36 #include "llvm/Transforms/IPO.h"
37 #include "llvm/Transforms/IPO/PassManagerBuilder.h"
44 void llvm::initializeCoroutines(PassRegistry
&Registry
) {
45 initializeCoroEarlyPass(Registry
);
46 initializeCoroSplitPass(Registry
);
47 initializeCoroElidePass(Registry
);
48 initializeCoroCleanupPass(Registry
);
51 static void addCoroutineOpt0Passes(const PassManagerBuilder
&Builder
,
52 legacy::PassManagerBase
&PM
) {
53 PM
.add(createCoroSplitPass());
54 PM
.add(createCoroElidePass());
56 PM
.add(createBarrierNoopPass());
57 PM
.add(createCoroCleanupPass());
60 static void addCoroutineEarlyPasses(const PassManagerBuilder
&Builder
,
61 legacy::PassManagerBase
&PM
) {
62 PM
.add(createCoroEarlyPass());
65 static void addCoroutineScalarOptimizerPasses(const PassManagerBuilder
&Builder
,
66 legacy::PassManagerBase
&PM
) {
67 PM
.add(createCoroElidePass());
70 static void addCoroutineSCCPasses(const PassManagerBuilder
&Builder
,
71 legacy::PassManagerBase
&PM
) {
72 PM
.add(createCoroSplitPass());
75 static void addCoroutineOptimizerLastPasses(const PassManagerBuilder
&Builder
,
76 legacy::PassManagerBase
&PM
) {
77 PM
.add(createCoroCleanupPass());
80 void llvm::addCoroutinePassesToExtensionPoints(PassManagerBuilder
&Builder
) {
81 Builder
.addExtension(PassManagerBuilder::EP_EarlyAsPossible
,
82 addCoroutineEarlyPasses
);
83 Builder
.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0
,
84 addCoroutineOpt0Passes
);
85 Builder
.addExtension(PassManagerBuilder::EP_CGSCCOptimizerLate
,
86 addCoroutineSCCPasses
);
87 Builder
.addExtension(PassManagerBuilder::EP_ScalarOptimizerLate
,
88 addCoroutineScalarOptimizerPasses
);
89 Builder
.addExtension(PassManagerBuilder::EP_OptimizerLast
,
90 addCoroutineOptimizerLastPasses
);
93 // Construct the lowerer base class and initialize its members.
94 coro::LowererBase::LowererBase(Module
&M
)
95 : TheModule(M
), Context(M
.getContext()),
96 Int8Ptr(Type::getInt8PtrTy(Context
)),
97 ResumeFnType(FunctionType::get(Type::getVoidTy(Context
), Int8Ptr
,
99 NullPtr(ConstantPointerNull::get(Int8Ptr
)) {}
101 // Creates a sequence of instructions to obtain a resume function address using
102 // llvm.coro.subfn.addr. It generates the following sequence:
104 // call i8* @llvm.coro.subfn.addr(i8* %Arg, i8 %index)
105 // bitcast i8* %2 to void(i8*)*
107 Value
*coro::LowererBase::makeSubFnCall(Value
*Arg
, int Index
,
108 Instruction
*InsertPt
) {
109 auto *IndexVal
= ConstantInt::get(Type::getInt8Ty(Context
), Index
);
110 auto *Fn
= Intrinsic::getDeclaration(&TheModule
, Intrinsic::coro_subfn_addr
);
112 assert(Index
>= CoroSubFnInst::IndexFirst
&&
113 Index
< CoroSubFnInst::IndexLast
&&
114 "makeSubFnCall: Index value out of range");
115 auto *Call
= CallInst::Create(Fn
, {Arg
, IndexVal
}, "", InsertPt
);
118 new BitCastInst(Call
, ResumeFnType
->getPointerTo(), "", InsertPt
);
123 static bool isCoroutineIntrinsicName(StringRef Name
) {
124 // NOTE: Must be sorted!
125 static const char *const CoroIntrinsics
[] = {
134 "llvm.coro.id.retcon",
135 "llvm.coro.id.retcon.once",
138 "llvm.coro.prepare.retcon",
143 "llvm.coro.subfn.addr",
145 "llvm.coro.suspend.retcon",
147 return Intrinsic::lookupLLVMIntrinsicByName(CoroIntrinsics
, Name
) != -1;
151 // Verifies if a module has named values listed. Also, in debug mode verifies
152 // that names are intrinsic names.
153 bool coro::declaresIntrinsics(Module
&M
,
154 std::initializer_list
<StringRef
> List
) {
155 for (StringRef Name
: List
) {
156 assert(isCoroutineIntrinsicName(Name
) && "not a coroutine intrinsic");
157 if (M
.getNamedValue(Name
))
164 // Replace all coro.frees associated with the provided CoroId either with 'null'
165 // if Elide is true and with its frame parameter otherwise.
166 void coro::replaceCoroFree(CoroIdInst
*CoroId
, bool Elide
) {
167 SmallVector
<CoroFreeInst
*, 4> CoroFrees
;
168 for (User
*U
: CoroId
->users())
169 if (auto CF
= dyn_cast
<CoroFreeInst
>(U
))
170 CoroFrees
.push_back(CF
);
172 if (CoroFrees
.empty())
176 Elide
? ConstantPointerNull::get(Type::getInt8PtrTy(CoroId
->getContext()))
177 : CoroFrees
.front()->getFrame();
179 for (CoroFreeInst
*CF
: CoroFrees
) {
180 CF
->replaceAllUsesWith(Replacement
);
181 CF
->eraseFromParent();
185 // FIXME: This code is stolen from CallGraph::addToCallGraph(Function *F), which
186 // happens to be private. It is better for this functionality exposed by the
188 static void buildCGN(CallGraph
&CG
, CallGraphNode
*Node
) {
189 Function
*F
= Node
->getFunction();
191 // Look for calls by this function.
192 for (Instruction
&I
: instructions(F
))
193 if (auto *Call
= dyn_cast
<CallBase
>(&I
)) {
194 const Function
*Callee
= Call
->getCalledFunction();
195 if (!Callee
|| !Intrinsic::isLeaf(Callee
->getIntrinsicID()))
196 // Indirect calls of intrinsics are not allowed so no need to check.
197 // We can be more precise here by using TargetArg returned by
198 // Intrinsic::isLeaf.
199 Node
->addCalledFunction(Call
, CG
.getCallsExternalNode());
200 else if (!Callee
->isIntrinsic())
201 Node
->addCalledFunction(Call
, CG
.getOrInsertFunction(Callee
));
205 // Rebuild CGN after we extracted parts of the code from ParentFunc into
206 // NewFuncs. Builds CGNs for the NewFuncs and adds them to the current SCC.
207 void coro::updateCallGraph(Function
&ParentFunc
, ArrayRef
<Function
*> NewFuncs
,
208 CallGraph
&CG
, CallGraphSCC
&SCC
) {
209 // Rebuild CGN from scratch for the ParentFunc
210 auto *ParentNode
= CG
[&ParentFunc
];
211 ParentNode
->removeAllCalledFunctions();
212 buildCGN(CG
, ParentNode
);
214 SmallVector
<CallGraphNode
*, 8> Nodes(SCC
.begin(), SCC
.end());
216 for (Function
*F
: NewFuncs
) {
217 CallGraphNode
*Callee
= CG
.getOrInsertFunction(F
);
218 Nodes
.push_back(Callee
);
219 buildCGN(CG
, Callee
);
222 SCC
.initialize(Nodes
);
225 static void clear(coro::Shape
&Shape
) {
226 Shape
.CoroBegin
= nullptr;
227 Shape
.CoroEnds
.clear();
228 Shape
.CoroSizes
.clear();
229 Shape
.CoroSuspends
.clear();
231 Shape
.FrameTy
= nullptr;
232 Shape
.FramePtr
= nullptr;
233 Shape
.AllocaSpillBlock
= nullptr;
236 static CoroSaveInst
*createCoroSave(CoroBeginInst
*CoroBegin
,
237 CoroSuspendInst
*SuspendInst
) {
238 Module
*M
= SuspendInst
->getModule();
239 auto *Fn
= Intrinsic::getDeclaration(M
, Intrinsic::coro_save
);
241 cast
<CoroSaveInst
>(CallInst::Create(Fn
, CoroBegin
, "", SuspendInst
));
242 assert(!SuspendInst
->getCoroSave());
243 SuspendInst
->setArgOperand(0, SaveInst
);
247 // Collect "interesting" coroutine intrinsics.
248 void coro::Shape::buildFrom(Function
&F
) {
249 bool HasFinalSuspend
= false;
250 size_t FinalSuspendIndex
= 0;
252 SmallVector
<CoroFrameInst
*, 8> CoroFrames
;
253 SmallVector
<CoroSaveInst
*, 2> UnusedCoroSaves
;
255 for (Instruction
&I
: instructions(F
)) {
256 if (auto II
= dyn_cast
<IntrinsicInst
>(&I
)) {
257 switch (II
->getIntrinsicID()) {
260 case Intrinsic::coro_size
:
261 CoroSizes
.push_back(cast
<CoroSizeInst
>(II
));
263 case Intrinsic::coro_frame
:
264 CoroFrames
.push_back(cast
<CoroFrameInst
>(II
));
266 case Intrinsic::coro_save
:
267 // After optimizations, coro_suspends using this coro_save might have
268 // been removed, remember orphaned coro_saves to remove them later.
270 UnusedCoroSaves
.push_back(cast
<CoroSaveInst
>(II
));
272 case Intrinsic::coro_suspend_retcon
: {
273 auto Suspend
= cast
<CoroSuspendRetconInst
>(II
);
274 CoroSuspends
.push_back(Suspend
);
277 case Intrinsic::coro_suspend
: {
278 auto Suspend
= cast
<CoroSuspendInst
>(II
);
279 CoroSuspends
.push_back(Suspend
);
280 if (Suspend
->isFinal()) {
283 "Only one suspend point can be marked as final");
284 HasFinalSuspend
= true;
285 FinalSuspendIndex
= CoroSuspends
.size() - 1;
289 case Intrinsic::coro_begin
: {
290 auto CB
= cast
<CoroBeginInst
>(II
);
292 // Ignore coro id's that aren't pre-split.
293 auto Id
= dyn_cast
<CoroIdInst
>(CB
->getId());
294 if (Id
&& !Id
->getInfo().isPreSplit())
299 "coroutine should have exactly one defining @llvm.coro.begin");
300 CB
->addAttribute(AttributeList::ReturnIndex
, Attribute::NonNull
);
301 CB
->addAttribute(AttributeList::ReturnIndex
, Attribute::NoAlias
);
302 CB
->removeAttribute(AttributeList::FunctionIndex
,
303 Attribute::NoDuplicate
);
307 case Intrinsic::coro_end
:
308 CoroEnds
.push_back(cast
<CoroEndInst
>(II
));
309 if (CoroEnds
.back()->isFallthrough()) {
310 // Make sure that the fallthrough coro.end is the first element in the
312 if (CoroEnds
.size() > 1) {
313 if (CoroEnds
.front()->isFallthrough())
315 "Only one coro.end can be marked as fallthrough");
316 std::swap(CoroEnds
.front(), CoroEnds
.back());
324 // If for some reason, we were not able to find coro.begin, bailout.
326 // Replace coro.frame which are supposed to be lowered to the result of
327 // coro.begin with undef.
328 auto *Undef
= UndefValue::get(Type::getInt8PtrTy(F
.getContext()));
329 for (CoroFrameInst
*CF
: CoroFrames
) {
330 CF
->replaceAllUsesWith(Undef
);
331 CF
->eraseFromParent();
334 // Replace all coro.suspend with undef and remove related coro.saves if
336 for (AnyCoroSuspendInst
*CS
: CoroSuspends
) {
337 CS
->replaceAllUsesWith(UndefValue::get(CS
->getType()));
338 CS
->eraseFromParent();
339 if (auto *CoroSave
= CS
->getCoroSave())
340 CoroSave
->eraseFromParent();
343 // Replace all coro.ends with unreachable instruction.
344 for (CoroEndInst
*CE
: CoroEnds
)
345 changeToUnreachable(CE
, /*UseLLVMTrap=*/false);
350 auto Id
= CoroBegin
->getId();
351 switch (auto IdIntrinsic
= Id
->getIntrinsicID()) {
352 case Intrinsic::coro_id
: {
353 auto SwitchId
= cast
<CoroIdInst
>(Id
);
354 this->ABI
= coro::ABI::Switch
;
355 this->SwitchLowering
.HasFinalSuspend
= HasFinalSuspend
;
356 this->SwitchLowering
.ResumeSwitch
= nullptr;
357 this->SwitchLowering
.PromiseAlloca
= SwitchId
->getPromise();
358 this->SwitchLowering
.ResumeEntryBlock
= nullptr;
360 for (auto AnySuspend
: CoroSuspends
) {
361 auto Suspend
= dyn_cast
<CoroSuspendInst
>(AnySuspend
);
366 report_fatal_error("coro.id must be paired with coro.suspend");
369 if (!Suspend
->getCoroSave())
370 createCoroSave(CoroBegin
, Suspend
);
375 case Intrinsic::coro_id_retcon
:
376 case Intrinsic::coro_id_retcon_once
: {
377 auto ContinuationId
= cast
<AnyCoroIdRetconInst
>(Id
);
378 ContinuationId
->checkWellFormed();
379 this->ABI
= (IdIntrinsic
== Intrinsic::coro_id_retcon
381 : coro::ABI::RetconOnce
);
382 auto Prototype
= ContinuationId
->getPrototype();
383 this->RetconLowering
.ResumePrototype
= Prototype
;
384 this->RetconLowering
.Alloc
= ContinuationId
->getAllocFunction();
385 this->RetconLowering
.Dealloc
= ContinuationId
->getDeallocFunction();
386 this->RetconLowering
.ReturnBlock
= nullptr;
387 this->RetconLowering
.IsFrameInlineInStorage
= false;
389 // Determine the result value types, and make sure they match up with
390 // the values passed to the suspends.
391 auto ResultTys
= getRetconResultTypes();
392 auto ResumeTys
= getRetconResumeTypes();
394 for (auto AnySuspend
: CoroSuspends
) {
395 auto Suspend
= dyn_cast
<CoroSuspendRetconInst
>(AnySuspend
);
400 report_fatal_error("coro.id.retcon.* must be paired with "
401 "coro.suspend.retcon");
404 // Check that the argument types of the suspend match the results.
405 auto SI
= Suspend
->value_begin(), SE
= Suspend
->value_end();
406 auto RI
= ResultTys
.begin(), RE
= ResultTys
.end();
407 for (; SI
!= SE
&& RI
!= RE
; ++SI
, ++RI
) {
408 auto SrcTy
= (*SI
)->getType();
410 // The optimizer likes to eliminate bitcasts leading into variadic
411 // calls, but that messes with our invariants. Re-insert the
412 // bitcast and ignore this type mismatch.
413 if (CastInst::isBitCastable(SrcTy
, *RI
)) {
414 auto BCI
= new BitCastInst(*SI
, *RI
, "", Suspend
);
421 Prototype
->getFunctionType()->dump();
423 report_fatal_error("argument to coro.suspend.retcon does not "
424 "match corresponding prototype function result");
427 if (SI
!= SE
|| RI
!= RE
) {
430 Prototype
->getFunctionType()->dump();
432 report_fatal_error("wrong number of arguments to coro.suspend.retcon");
435 // Check that the result type of the suspend matches the resume types.
436 Type
*SResultTy
= Suspend
->getType();
437 ArrayRef
<Type
*> SuspendResultTys
;
438 if (SResultTy
->isVoidTy()) {
439 // leave as empty array
440 } else if (auto SResultStructTy
= dyn_cast
<StructType
>(SResultTy
)) {
441 SuspendResultTys
= SResultStructTy
->elements();
443 // forms an ArrayRef using SResultTy, be careful
444 SuspendResultTys
= SResultTy
;
446 if (SuspendResultTys
.size() != ResumeTys
.size()) {
449 Prototype
->getFunctionType()->dump();
451 report_fatal_error("wrong number of results from coro.suspend.retcon");
453 for (size_t I
= 0, E
= ResumeTys
.size(); I
!= E
; ++I
) {
454 if (SuspendResultTys
[I
] != ResumeTys
[I
]) {
457 Prototype
->getFunctionType()->dump();
459 report_fatal_error("result from coro.suspend.retcon does not "
460 "match corresponding prototype function param");
468 llvm_unreachable("coro.begin is not dependent on a coro.id call");
471 // The coro.free intrinsic is always lowered to the result of coro.begin.
472 for (CoroFrameInst
*CF
: CoroFrames
) {
473 CF
->replaceAllUsesWith(CoroBegin
);
474 CF
->eraseFromParent();
477 // Move final suspend to be the last element in the CoroSuspends vector.
478 if (ABI
== coro::ABI::Switch
&&
479 SwitchLowering
.HasFinalSuspend
&&
480 FinalSuspendIndex
!= CoroSuspends
.size() - 1)
481 std::swap(CoroSuspends
[FinalSuspendIndex
], CoroSuspends
.back());
483 // Remove orphaned coro.saves.
484 for (CoroSaveInst
*CoroSave
: UnusedCoroSaves
)
485 CoroSave
->eraseFromParent();
488 static void propagateCallAttrsFromCallee(CallInst
*Call
, Function
*Callee
) {
489 Call
->setCallingConv(Callee
->getCallingConv());
493 static void addCallToCallGraph(CallGraph
*CG
, CallInst
*Call
, Function
*Callee
){
495 (*CG
)[Call
->getFunction()]->addCalledFunction(Call
, (*CG
)[Callee
]);
498 Value
*coro::Shape::emitAlloc(IRBuilder
<> &Builder
, Value
*Size
,
499 CallGraph
*CG
) const {
501 case coro::ABI::Switch
:
502 llvm_unreachable("can't allocate memory in coro switch-lowering");
504 case coro::ABI::Retcon
:
505 case coro::ABI::RetconOnce
: {
506 auto Alloc
= RetconLowering
.Alloc
;
507 Size
= Builder
.CreateIntCast(Size
,
508 Alloc
->getFunctionType()->getParamType(0),
509 /*is signed*/ false);
510 auto *Call
= Builder
.CreateCall(Alloc
, Size
);
511 propagateCallAttrsFromCallee(Call
, Alloc
);
512 addCallToCallGraph(CG
, Call
, Alloc
);
516 llvm_unreachable("Unknown coro::ABI enum");
519 void coro::Shape::emitDealloc(IRBuilder
<> &Builder
, Value
*Ptr
,
520 CallGraph
*CG
) const {
522 case coro::ABI::Switch
:
523 llvm_unreachable("can't allocate memory in coro switch-lowering");
525 case coro::ABI::Retcon
:
526 case coro::ABI::RetconOnce
: {
527 auto Dealloc
= RetconLowering
.Dealloc
;
528 Ptr
= Builder
.CreateBitCast(Ptr
,
529 Dealloc
->getFunctionType()->getParamType(0));
530 auto *Call
= Builder
.CreateCall(Dealloc
, Ptr
);
531 propagateCallAttrsFromCallee(Call
, Dealloc
);
532 addCallToCallGraph(CG
, Call
, Dealloc
);
536 llvm_unreachable("Unknown coro::ABI enum");
539 LLVM_ATTRIBUTE_NORETURN
540 static void fail(const Instruction
*I
, const char *Reason
, Value
*V
) {
544 errs() << " Value: ";
545 V
->printAsOperand(llvm::errs());
549 report_fatal_error(Reason
);
552 /// Check that the given value is a well-formed prototype for the
553 /// llvm.coro.id.retcon.* intrinsics.
554 static void checkWFRetconPrototype(const AnyCoroIdRetconInst
*I
, Value
*V
) {
555 auto F
= dyn_cast
<Function
>(V
->stripPointerCasts());
557 fail(I
, "llvm.coro.id.retcon.* prototype not a Function", V
);
559 auto FT
= F
->getFunctionType();
561 if (isa
<CoroIdRetconInst
>(I
)) {
563 if (FT
->getReturnType()->isPointerTy()) {
565 } else if (auto SRetTy
= dyn_cast
<StructType
>(FT
->getReturnType())) {
566 ResultOkay
= (!SRetTy
->isOpaque() &&
567 SRetTy
->getNumElements() > 0 &&
568 SRetTy
->getElementType(0)->isPointerTy());
573 fail(I
, "llvm.coro.id.retcon prototype must return pointer as first "
576 if (FT
->getReturnType() !=
577 I
->getFunction()->getFunctionType()->getReturnType())
578 fail(I
, "llvm.coro.id.retcon prototype return type must be same as"
579 "current function return type", F
);
581 // No meaningful validation to do here for llvm.coro.id.unique.once.
584 if (FT
->getNumParams() == 0 || !FT
->getParamType(0)->isPointerTy())
585 fail(I
, "llvm.coro.id.retcon.* prototype must take pointer as "
586 "its first parameter", F
);
589 /// Check that the given value is a well-formed allocator.
590 static void checkWFAlloc(const Instruction
*I
, Value
*V
) {
591 auto F
= dyn_cast
<Function
>(V
->stripPointerCasts());
593 fail(I
, "llvm.coro.* allocator not a Function", V
);
595 auto FT
= F
->getFunctionType();
596 if (!FT
->getReturnType()->isPointerTy())
597 fail(I
, "llvm.coro.* allocator must return a pointer", F
);
599 if (FT
->getNumParams() != 1 ||
600 !FT
->getParamType(0)->isIntegerTy())
601 fail(I
, "llvm.coro.* allocator must take integer as only param", F
);
604 /// Check that the given value is a well-formed deallocator.
605 static void checkWFDealloc(const Instruction
*I
, Value
*V
) {
606 auto F
= dyn_cast
<Function
>(V
->stripPointerCasts());
608 fail(I
, "llvm.coro.* deallocator not a Function", V
);
610 auto FT
= F
->getFunctionType();
611 if (!FT
->getReturnType()->isVoidTy())
612 fail(I
, "llvm.coro.* deallocator must return void", F
);
614 if (FT
->getNumParams() != 1 ||
615 !FT
->getParamType(0)->isPointerTy())
616 fail(I
, "llvm.coro.* deallocator must take pointer as only param", F
);
619 static void checkConstantInt(const Instruction
*I
, Value
*V
,
620 const char *Reason
) {
621 if (!isa
<ConstantInt
>(V
)) {
626 void AnyCoroIdRetconInst::checkWellFormed() const {
627 checkConstantInt(this, getArgOperand(SizeArg
),
628 "size argument to coro.id.retcon.* must be constant");
629 checkConstantInt(this, getArgOperand(AlignArg
),
630 "alignment argument to coro.id.retcon.* must be constant");
631 checkWFRetconPrototype(this, getArgOperand(PrototypeArg
));
632 checkWFAlloc(this, getArgOperand(AllocArg
));
633 checkWFDealloc(this, getArgOperand(DeallocArg
));
636 void LLVMAddCoroEarlyPass(LLVMPassManagerRef PM
) {
637 unwrap(PM
)->add(createCoroEarlyPass());
640 void LLVMAddCoroSplitPass(LLVMPassManagerRef PM
) {
641 unwrap(PM
)->add(createCoroSplitPass());
644 void LLVMAddCoroElidePass(LLVMPassManagerRef PM
) {
645 unwrap(PM
)->add(createCoroElidePass());
648 void LLVMAddCoroCleanupPass(LLVMPassManagerRef PM
) {
649 unwrap(PM
)->add(createCoroCleanupPass());