1 //===- Coroutines.cpp -----------------------------------------------------===//
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 //
9 // This file implements the common infrastructure for Coroutine Passes.
10 //
11 //===----------------------------------------------------------------------===//
12
13 #include "llvm/Transforms/Coroutines.h"
14 #include "CoroInstr.h"
15 #include "CoroInternal.h"
16 #include "llvm-c/Transforms/Coroutines.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/IR/Attributes.h"
22 #include "llvm/IR/CallSite.h"
23 #include "llvm/IR/Constants.h"
24 #include "llvm/IR/DerivedTypes.h"
25 #include "llvm/IR/Function.h"
26 #include "llvm/IR/InstIterator.h"
27 #include "llvm/IR/Instructions.h"
28 #include "llvm/IR/IntrinsicInst.h"
29 #include "llvm/IR/Intrinsics.h"
30 #include "llvm/IR/LegacyPassManager.h"
31 #include "llvm/IR/Module.h"
32 #include "llvm/IR/Type.h"
33 #include "llvm/InitializePasses.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"
38 #include "llvm/Transforms/Utils/Local.h"
39 #include <cassert>
40 #include <cstddef>
41 #include <utility>
42
43 using namespace llvm;
44
initializeCoroutines(PassRegistry & Registry)45 void llvm::initializeCoroutines(PassRegistry &Registry) {
46 initializeCoroEarlyLegacyPass(Registry);
47 initializeCoroSplitLegacyPass(Registry);
48 initializeCoroElideLegacyPass(Registry);
49 initializeCoroCleanupLegacyPass(Registry);
50 }
51
addCoroutineOpt0Passes(const PassManagerBuilder & Builder,legacy::PassManagerBase & PM)52 static void addCoroutineOpt0Passes(const PassManagerBuilder &Builder,
53 legacy::PassManagerBase &PM) {
54 PM.add(createCoroSplitLegacyPass());
55 PM.add(createCoroElideLegacyPass());
56
57 PM.add(createBarrierNoopPass());
58 PM.add(createCoroCleanupLegacyPass());
59 }
60
addCoroutineEarlyPasses(const PassManagerBuilder & Builder,legacy::PassManagerBase & PM)61 static void addCoroutineEarlyPasses(const PassManagerBuilder &Builder,
62 legacy::PassManagerBase &PM) {
63 PM.add(createCoroEarlyLegacyPass());
64 }
65
addCoroutineScalarOptimizerPasses(const PassManagerBuilder & Builder,legacy::PassManagerBase & PM)66 static void addCoroutineScalarOptimizerPasses(const PassManagerBuilder &Builder,
67 legacy::PassManagerBase &PM) {
68 PM.add(createCoroElideLegacyPass());
69 }
70
addCoroutineSCCPasses(const PassManagerBuilder & Builder,legacy::PassManagerBase & PM)71 static void addCoroutineSCCPasses(const PassManagerBuilder &Builder,
72 legacy::PassManagerBase &PM) {
73 PM.add(createCoroSplitLegacyPass());
74 }
75
addCoroutineOptimizerLastPasses(const PassManagerBuilder & Builder,legacy::PassManagerBase & PM)76 static void addCoroutineOptimizerLastPasses(const PassManagerBuilder &Builder,
77 legacy::PassManagerBase &PM) {
78 PM.add(createCoroCleanupLegacyPass());
79 }
80
addCoroutinePassesToExtensionPoints(PassManagerBuilder & Builder)81 void llvm::addCoroutinePassesToExtensionPoints(PassManagerBuilder &Builder) {
82 Builder.addExtension(PassManagerBuilder::EP_EarlyAsPossible,
83 addCoroutineEarlyPasses);
84 Builder.addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
85 addCoroutineOpt0Passes);
86 Builder.addExtension(PassManagerBuilder::EP_CGSCCOptimizerLate,
87 addCoroutineSCCPasses);
88 Builder.addExtension(PassManagerBuilder::EP_ScalarOptimizerLate,
89 addCoroutineScalarOptimizerPasses);
90 Builder.addExtension(PassManagerBuilder::EP_OptimizerLast,
91 addCoroutineOptimizerLastPasses);
92 }
93
94 // Construct the lowerer base class and initialize its members.
LowererBase(Module & M)95 coro::LowererBase::LowererBase(Module &M)
96 : TheModule(M), Context(M.getContext()),
97 Int8Ptr(Type::getInt8PtrTy(Context)),
98 ResumeFnType(FunctionType::get(Type::getVoidTy(Context), Int8Ptr,
99 /*isVarArg=*/false)),
100 NullPtr(ConstantPointerNull::get(Int8Ptr)) {}
101
102 // Creates a sequence of instructions to obtain a resume function address using
103 // llvm.coro.subfn.addr. It generates the following sequence:
104 //
105 // call i8* @llvm.coro.subfn.addr(i8* %Arg, i8 %index)
106 // bitcast i8* %2 to void(i8*)*
107
makeSubFnCall(Value * Arg,int Index,Instruction * InsertPt)108 Value *coro::LowererBase::makeSubFnCall(Value *Arg, int Index,
109 Instruction *InsertPt) {
110 auto *IndexVal = ConstantInt::get(Type::getInt8Ty(Context), Index);
111 auto *Fn = Intrinsic::getDeclaration(&TheModule, Intrinsic::coro_subfn_addr);
112
113 assert(Index >= CoroSubFnInst::IndexFirst &&
114 Index < CoroSubFnInst::IndexLast &&
115 "makeSubFnCall: Index value out of range");
116 auto *Call = CallInst::Create(Fn, {Arg, IndexVal}, "", InsertPt);
117
118 auto *Bitcast =
119 new BitCastInst(Call, ResumeFnType->getPointerTo(), "", InsertPt);
120 return Bitcast;
121 }
122
123 #ifndef NDEBUG
isCoroutineIntrinsicName(StringRef Name)124 static bool isCoroutineIntrinsicName(StringRef Name) {
125 // NOTE: Must be sorted!
126 static const char *const CoroIntrinsics[] = {
127 "llvm.coro.alloc",
128 "llvm.coro.begin",
129 "llvm.coro.destroy",
130 "llvm.coro.done",
131 "llvm.coro.end",
132 "llvm.coro.frame",
133 "llvm.coro.free",
134 "llvm.coro.id",
135 "llvm.coro.id.retcon",
136 "llvm.coro.id.retcon.once",
137 "llvm.coro.noop",
138 "llvm.coro.param",
139 "llvm.coro.prepare.retcon",
140 "llvm.coro.promise",
141 "llvm.coro.resume",
142 "llvm.coro.save",
143 "llvm.coro.size",
144 "llvm.coro.subfn.addr",
145 "llvm.coro.suspend",
146 "llvm.coro.suspend.retcon",
147 };
148 return Intrinsic::lookupLLVMIntrinsicByName(CoroIntrinsics, Name) != -1;
149 }
150 #endif
151
152 // Verifies if a module has named values listed. Also, in debug mode verifies
153 // that names are intrinsic names.
declaresIntrinsics(const Module & M,const std::initializer_list<StringRef> List)154 bool coro::declaresIntrinsics(const Module &M,
155 const std::initializer_list<StringRef> List) {
156 for (StringRef Name : List) {
157 assert(isCoroutineIntrinsicName(Name) && "not a coroutine intrinsic");
158 if (M.getNamedValue(Name))
159 return true;
160 }
161
162 return false;
163 }
164
165 // Replace all coro.frees associated with the provided CoroId either with 'null'
166 // if Elide is true and with its frame parameter otherwise.
replaceCoroFree(CoroIdInst * CoroId,bool Elide)167 void coro::replaceCoroFree(CoroIdInst *CoroId, bool Elide) {
168 SmallVector<CoroFreeInst *, 4> CoroFrees;
169 for (User *U : CoroId->users())
170 if (auto CF = dyn_cast<CoroFreeInst>(U))
171 CoroFrees.push_back(CF);
172
173 if (CoroFrees.empty())
174 return;
175
176 Value *Replacement =
177 Elide ? ConstantPointerNull::get(Type::getInt8PtrTy(CoroId->getContext()))
178 : CoroFrees.front()->getFrame();
179
180 for (CoroFreeInst *CF : CoroFrees) {
181 CF->replaceAllUsesWith(Replacement);
182 CF->eraseFromParent();
183 }
184 }
185
186 // FIXME: This code is stolen from CallGraph::addToCallGraph(Function *F), which
187 // happens to be private. It is better for this functionality exposed by the
188 // CallGraph.
buildCGN(CallGraph & CG,CallGraphNode * Node)189 static void buildCGN(CallGraph &CG, CallGraphNode *Node) {
190 Function *F = Node->getFunction();
191
192 // Look for calls by this function.
193 for (Instruction &I : instructions(F))
194 if (auto *Call = dyn_cast<CallBase>(&I)) {
195 const Function *Callee = Call->getCalledFunction();
196 if (!Callee || !Intrinsic::isLeaf(Callee->getIntrinsicID()))
197 // Indirect calls of intrinsics are not allowed so no need to check.
198 // We can be more precise here by using TargetArg returned by
199 // Intrinsic::isLeaf.
200 Node->addCalledFunction(Call, CG.getCallsExternalNode());
201 else if (!Callee->isIntrinsic())
202 Node->addCalledFunction(Call, CG.getOrInsertFunction(Callee));
203 }
204 }
205
206 // Rebuild CGN after we extracted parts of the code from ParentFunc into
207 // NewFuncs. Builds CGNs for the NewFuncs and adds them to the current SCC.
updateCallGraph(Function & ParentFunc,ArrayRef<Function * > NewFuncs,CallGraph & CG,CallGraphSCC & SCC)208 void coro::updateCallGraph(Function &ParentFunc, ArrayRef<Function *> NewFuncs,
209 CallGraph &CG, CallGraphSCC &SCC) {
210 // Rebuild CGN from scratch for the ParentFunc
211 auto *ParentNode = CG[&ParentFunc];
212 ParentNode->removeAllCalledFunctions();
213 buildCGN(CG, ParentNode);
214
215 SmallVector<CallGraphNode *, 8> Nodes(SCC.begin(), SCC.end());
216
217 for (Function *F : NewFuncs) {
218 CallGraphNode *Callee = CG.getOrInsertFunction(F);
219 Nodes.push_back(Callee);
220 buildCGN(CG, Callee);
221 }
222
223 SCC.initialize(Nodes);
224 }
225
clear(coro::Shape & Shape)226 static void clear(coro::Shape &Shape) {
227 Shape.CoroBegin = nullptr;
228 Shape.CoroEnds.clear();
229 Shape.CoroSizes.clear();
230 Shape.CoroSuspends.clear();
231
232 Shape.FrameTy = nullptr;
233 Shape.FramePtr = nullptr;
234 Shape.AllocaSpillBlock = nullptr;
235 }
236
createCoroSave(CoroBeginInst * CoroBegin,CoroSuspendInst * SuspendInst)237 static CoroSaveInst *createCoroSave(CoroBeginInst *CoroBegin,
238 CoroSuspendInst *SuspendInst) {
239 Module *M = SuspendInst->getModule();
240 auto *Fn = Intrinsic::getDeclaration(M, Intrinsic::coro_save);
241 auto *SaveInst =
242 cast<CoroSaveInst>(CallInst::Create(Fn, CoroBegin, "", SuspendInst));
243 assert(!SuspendInst->getCoroSave());
244 SuspendInst->setArgOperand(0, SaveInst);
245 return SaveInst;
246 }
247
248 // Collect "interesting" coroutine intrinsics.
buildFrom(Function & F)249 void coro::Shape::buildFrom(Function &F) {
250 bool HasFinalSuspend = false;
251 size_t FinalSuspendIndex = 0;
252 clear(*this);
253 SmallVector<CoroFrameInst *, 8> CoroFrames;
254 SmallVector<CoroSaveInst *, 2> UnusedCoroSaves;
255
256 for (Instruction &I : instructions(F)) {
257 if (auto II = dyn_cast<IntrinsicInst>(&I)) {
258 switch (II->getIntrinsicID()) {
259 default:
260 continue;
261 case Intrinsic::coro_size:
262 CoroSizes.push_back(cast<CoroSizeInst>(II));
263 break;
264 case Intrinsic::coro_frame:
265 CoroFrames.push_back(cast<CoroFrameInst>(II));
266 break;
267 case Intrinsic::coro_save:
268 // After optimizations, coro_suspends using this coro_save might have
269 // been removed, remember orphaned coro_saves to remove them later.
270 if (II->use_empty())
271 UnusedCoroSaves.push_back(cast<CoroSaveInst>(II));
272 break;
273 case Intrinsic::coro_suspend_retcon: {
274 auto Suspend = cast<CoroSuspendRetconInst>(II);
275 CoroSuspends.push_back(Suspend);
276 break;
277 }
278 case Intrinsic::coro_suspend: {
279 auto Suspend = cast<CoroSuspendInst>(II);
280 CoroSuspends.push_back(Suspend);
281 if (Suspend->isFinal()) {
282 if (HasFinalSuspend)
283 report_fatal_error(
284 "Only one suspend point can be marked as final");
285 HasFinalSuspend = true;
286 FinalSuspendIndex = CoroSuspends.size() - 1;
287 }
288 break;
289 }
290 case Intrinsic::coro_begin: {
291 auto CB = cast<CoroBeginInst>(II);
292
293 // Ignore coro id's that aren't pre-split.
294 auto Id = dyn_cast<CoroIdInst>(CB->getId());
295 if (Id && !Id->getInfo().isPreSplit())
296 break;
297
298 if (CoroBegin)
299 report_fatal_error(
300 "coroutine should have exactly one defining @llvm.coro.begin");
301 CB->addAttribute(AttributeList::ReturnIndex, Attribute::NonNull);
302 CB->addAttribute(AttributeList::ReturnIndex, Attribute::NoAlias);
303 CB->removeAttribute(AttributeList::FunctionIndex,
304 Attribute::NoDuplicate);
305 CoroBegin = CB;
306 break;
307 }
308 case Intrinsic::coro_end:
309 CoroEnds.push_back(cast<CoroEndInst>(II));
310 if (CoroEnds.back()->isFallthrough()) {
311 // Make sure that the fallthrough coro.end is the first element in the
312 // CoroEnds vector.
313 if (CoroEnds.size() > 1) {
314 if (CoroEnds.front()->isFallthrough())
315 report_fatal_error(
316 "Only one coro.end can be marked as fallthrough");
317 std::swap(CoroEnds.front(), CoroEnds.back());
318 }
319 }
320 break;
321 }
322 }
323 }
324
325 // If for some reason, we were not able to find coro.begin, bailout.
326 if (!CoroBegin) {
327 // Replace coro.frame which are supposed to be lowered to the result of
328 // coro.begin with undef.
329 auto *Undef = UndefValue::get(Type::getInt8PtrTy(F.getContext()));
330 for (CoroFrameInst *CF : CoroFrames) {
331 CF->replaceAllUsesWith(Undef);
332 CF->eraseFromParent();
333 }
334
335 // Replace all coro.suspend with undef and remove related coro.saves if
336 // present.
337 for (AnyCoroSuspendInst *CS : CoroSuspends) {
338 CS->replaceAllUsesWith(UndefValue::get(CS->getType()));
339 CS->eraseFromParent();
340 if (auto *CoroSave = CS->getCoroSave())
341 CoroSave->eraseFromParent();
342 }
343
344 // Replace all coro.ends with unreachable instruction.
345 for (CoroEndInst *CE : CoroEnds)
346 changeToUnreachable(CE, /*UseLLVMTrap=*/false);
347
348 return;
349 }
350
351 auto Id = CoroBegin->getId();
352 switch (auto IdIntrinsic = Id->getIntrinsicID()) {
353 case Intrinsic::coro_id: {
354 auto SwitchId = cast<CoroIdInst>(Id);
355 this->ABI = coro::ABI::Switch;
356 this->SwitchLowering.HasFinalSuspend = HasFinalSuspend;
357 this->SwitchLowering.ResumeSwitch = nullptr;
358 this->SwitchLowering.PromiseAlloca = SwitchId->getPromise();
359 this->SwitchLowering.ResumeEntryBlock = nullptr;
360
361 for (auto AnySuspend : CoroSuspends) {
362 auto Suspend = dyn_cast<CoroSuspendInst>(AnySuspend);
363 if (!Suspend) {
364 #ifndef NDEBUG
365 AnySuspend->dump();
366 #endif
367 report_fatal_error("coro.id must be paired with coro.suspend");
368 }
369
370 if (!Suspend->getCoroSave())
371 createCoroSave(CoroBegin, Suspend);
372 }
373 break;
374 }
375
376 case Intrinsic::coro_id_retcon:
377 case Intrinsic::coro_id_retcon_once: {
378 auto ContinuationId = cast<AnyCoroIdRetconInst>(Id);
379 ContinuationId->checkWellFormed();
380 this->ABI = (IdIntrinsic == Intrinsic::coro_id_retcon
381 ? coro::ABI::Retcon
382 : coro::ABI::RetconOnce);
383 auto Prototype = ContinuationId->getPrototype();
384 this->RetconLowering.ResumePrototype = Prototype;
385 this->RetconLowering.Alloc = ContinuationId->getAllocFunction();
386 this->RetconLowering.Dealloc = ContinuationId->getDeallocFunction();
387 this->RetconLowering.ReturnBlock = nullptr;
388 this->RetconLowering.IsFrameInlineInStorage = false;
389
390 // Determine the result value types, and make sure they match up with
391 // the values passed to the suspends.
392 auto ResultTys = getRetconResultTypes();
393 auto ResumeTys = getRetconResumeTypes();
394
395 for (auto AnySuspend : CoroSuspends) {
396 auto Suspend = dyn_cast<CoroSuspendRetconInst>(AnySuspend);
397 if (!Suspend) {
398 #ifndef NDEBUG
399 AnySuspend->dump();
400 #endif
401 report_fatal_error("coro.id.retcon.* must be paired with "
402 "coro.suspend.retcon");
403 }
404
405 // Check that the argument types of the suspend match the results.
406 auto SI = Suspend->value_begin(), SE = Suspend->value_end();
407 auto RI = ResultTys.begin(), RE = ResultTys.end();
408 for (; SI != SE && RI != RE; ++SI, ++RI) {
409 auto SrcTy = (*SI)->getType();
410 if (SrcTy != *RI) {
411 // The optimizer likes to eliminate bitcasts leading into variadic
412 // calls, but that messes with our invariants. Re-insert the
413 // bitcast and ignore this type mismatch.
414 if (CastInst::isBitCastable(SrcTy, *RI)) {
415 auto BCI = new BitCastInst(*SI, *RI, "", Suspend);
416 SI->set(BCI);
417 continue;
418 }
419
420 #ifndef NDEBUG
421 Suspend->dump();
422 Prototype->getFunctionType()->dump();
423 #endif
424 report_fatal_error("argument to coro.suspend.retcon does not "
425 "match corresponding prototype function result");
426 }
427 }
428 if (SI != SE || RI != RE) {
429 #ifndef NDEBUG
430 Suspend->dump();
431 Prototype->getFunctionType()->dump();
432 #endif
433 report_fatal_error("wrong number of arguments to coro.suspend.retcon");
434 }
435
436 // Check that the result type of the suspend matches the resume types.
437 Type *SResultTy = Suspend->getType();
438 ArrayRef<Type*> SuspendResultTys;
439 if (SResultTy->isVoidTy()) {
440 // leave as empty array
441 } else if (auto SResultStructTy = dyn_cast<StructType>(SResultTy)) {
442 SuspendResultTys = SResultStructTy->elements();
443 } else {
444 // forms an ArrayRef using SResultTy, be careful
445 SuspendResultTys = SResultTy;
446 }
447 if (SuspendResultTys.size() != ResumeTys.size()) {
448 #ifndef NDEBUG
449 Suspend->dump();
450 Prototype->getFunctionType()->dump();
451 #endif
452 report_fatal_error("wrong number of results from coro.suspend.retcon");
453 }
454 for (size_t I = 0, E = ResumeTys.size(); I != E; ++I) {
455 if (SuspendResultTys[I] != ResumeTys[I]) {
456 #ifndef NDEBUG
457 Suspend->dump();
458 Prototype->getFunctionType()->dump();
459 #endif
460 report_fatal_error("result from coro.suspend.retcon does not "
461 "match corresponding prototype function param");
462 }
463 }
464 }
465 break;
466 }
467
468 default:
469 llvm_unreachable("coro.begin is not dependent on a coro.id call");
470 }
471
472 // The coro.free intrinsic is always lowered to the result of coro.begin.
473 for (CoroFrameInst *CF : CoroFrames) {
474 CF->replaceAllUsesWith(CoroBegin);
475 CF->eraseFromParent();
476 }
477
478 // Move final suspend to be the last element in the CoroSuspends vector.
479 if (ABI == coro::ABI::Switch &&
480 SwitchLowering.HasFinalSuspend &&
481 FinalSuspendIndex != CoroSuspends.size() - 1)
482 std::swap(CoroSuspends[FinalSuspendIndex], CoroSuspends.back());
483
484 // Remove orphaned coro.saves.
485 for (CoroSaveInst *CoroSave : UnusedCoroSaves)
486 CoroSave->eraseFromParent();
487 }
488
propagateCallAttrsFromCallee(CallInst * Call,Function * Callee)489 static void propagateCallAttrsFromCallee(CallInst *Call, Function *Callee) {
490 Call->setCallingConv(Callee->getCallingConv());
491 // TODO: attributes?
492 }
493
addCallToCallGraph(CallGraph * CG,CallInst * Call,Function * Callee)494 static void addCallToCallGraph(CallGraph *CG, CallInst *Call, Function *Callee){
495 if (CG)
496 (*CG)[Call->getFunction()]->addCalledFunction(Call, (*CG)[Callee]);
497 }
498
emitAlloc(IRBuilder<> & Builder,Value * Size,CallGraph * CG) const499 Value *coro::Shape::emitAlloc(IRBuilder<> &Builder, Value *Size,
500 CallGraph *CG) const {
501 switch (ABI) {
502 case coro::ABI::Switch:
503 llvm_unreachable("can't allocate memory in coro switch-lowering");
504
505 case coro::ABI::Retcon:
506 case coro::ABI::RetconOnce: {
507 auto Alloc = RetconLowering.Alloc;
508 Size = Builder.CreateIntCast(Size,
509 Alloc->getFunctionType()->getParamType(0),
510 /*is signed*/ false);
511 auto *Call = Builder.CreateCall(Alloc, Size);
512 propagateCallAttrsFromCallee(Call, Alloc);
513 addCallToCallGraph(CG, Call, Alloc);
514 return Call;
515 }
516 }
517 llvm_unreachable("Unknown coro::ABI enum");
518 }
519
emitDealloc(IRBuilder<> & Builder,Value * Ptr,CallGraph * CG) const520 void coro::Shape::emitDealloc(IRBuilder<> &Builder, Value *Ptr,
521 CallGraph *CG) const {
522 switch (ABI) {
523 case coro::ABI::Switch:
524 llvm_unreachable("can't allocate memory in coro switch-lowering");
525
526 case coro::ABI::Retcon:
527 case coro::ABI::RetconOnce: {
528 auto Dealloc = RetconLowering.Dealloc;
529 Ptr = Builder.CreateBitCast(Ptr,
530 Dealloc->getFunctionType()->getParamType(0));
531 auto *Call = Builder.CreateCall(Dealloc, Ptr);
532 propagateCallAttrsFromCallee(Call, Dealloc);
533 addCallToCallGraph(CG, Call, Dealloc);
534 return;
535 }
536 }
537 llvm_unreachable("Unknown coro::ABI enum");
538 }
539
540 LLVM_ATTRIBUTE_NORETURN
fail(const Instruction * I,const char * Reason,Value * V)541 static void fail(const Instruction *I, const char *Reason, Value *V) {
542 #ifndef NDEBUG
543 I->dump();
544 if (V) {
545 errs() << " Value: ";
546 V->printAsOperand(llvm::errs());
547 errs() << '\n';
548 }
549 #endif
550 report_fatal_error(Reason);
551 }
552
553 /// Check that the given value is a well-formed prototype for the
554 /// llvm.coro.id.retcon.* intrinsics.
checkWFRetconPrototype(const AnyCoroIdRetconInst * I,Value * V)555 static void checkWFRetconPrototype(const AnyCoroIdRetconInst *I, Value *V) {
556 auto F = dyn_cast<Function>(V->stripPointerCasts());
557 if (!F)
558 fail(I, "llvm.coro.id.retcon.* prototype not a Function", V);
559
560 auto FT = F->getFunctionType();
561
562 if (isa<CoroIdRetconInst>(I)) {
563 bool ResultOkay;
564 if (FT->getReturnType()->isPointerTy()) {
565 ResultOkay = true;
566 } else if (auto SRetTy = dyn_cast<StructType>(FT->getReturnType())) {
567 ResultOkay = (!SRetTy->isOpaque() &&
568 SRetTy->getNumElements() > 0 &&
569 SRetTy->getElementType(0)->isPointerTy());
570 } else {
571 ResultOkay = false;
572 }
573 if (!ResultOkay)
574 fail(I, "llvm.coro.id.retcon prototype must return pointer as first "
575 "result", F);
576
577 if (FT->getReturnType() !=
578 I->getFunction()->getFunctionType()->getReturnType())
579 fail(I, "llvm.coro.id.retcon prototype return type must be same as"
580 "current function return type", F);
581 } else {
582 // No meaningful validation to do here for llvm.coro.id.unique.once.
583 }
584
585 if (FT->getNumParams() == 0 || !FT->getParamType(0)->isPointerTy())
586 fail(I, "llvm.coro.id.retcon.* prototype must take pointer as "
587 "its first parameter", F);
588 }
589
590 /// Check that the given value is a well-formed allocator.
checkWFAlloc(const Instruction * I,Value * V)591 static void checkWFAlloc(const Instruction *I, Value *V) {
592 auto F = dyn_cast<Function>(V->stripPointerCasts());
593 if (!F)
594 fail(I, "llvm.coro.* allocator not a Function", V);
595
596 auto FT = F->getFunctionType();
597 if (!FT->getReturnType()->isPointerTy())
598 fail(I, "llvm.coro.* allocator must return a pointer", F);
599
600 if (FT->getNumParams() != 1 ||
601 !FT->getParamType(0)->isIntegerTy())
602 fail(I, "llvm.coro.* allocator must take integer as only param", F);
603 }
604
605 /// Check that the given value is a well-formed deallocator.
checkWFDealloc(const Instruction * I,Value * V)606 static void checkWFDealloc(const Instruction *I, Value *V) {
607 auto F = dyn_cast<Function>(V->stripPointerCasts());
608 if (!F)
609 fail(I, "llvm.coro.* deallocator not a Function", V);
610
611 auto FT = F->getFunctionType();
612 if (!FT->getReturnType()->isVoidTy())
613 fail(I, "llvm.coro.* deallocator must return void", F);
614
615 if (FT->getNumParams() != 1 ||
616 !FT->getParamType(0)->isPointerTy())
617 fail(I, "llvm.coro.* deallocator must take pointer as only param", F);
618 }
619
checkConstantInt(const Instruction * I,Value * V,const char * Reason)620 static void checkConstantInt(const Instruction *I, Value *V,
621 const char *Reason) {
622 if (!isa<ConstantInt>(V)) {
623 fail(I, Reason, V);
624 }
625 }
626
checkWellFormed() const627 void AnyCoroIdRetconInst::checkWellFormed() const {
628 checkConstantInt(this, getArgOperand(SizeArg),
629 "size argument to coro.id.retcon.* must be constant");
630 checkConstantInt(this, getArgOperand(AlignArg),
631 "alignment argument to coro.id.retcon.* must be constant");
632 checkWFRetconPrototype(this, getArgOperand(PrototypeArg));
633 checkWFAlloc(this, getArgOperand(AllocArg));
634 checkWFDealloc(this, getArgOperand(DeallocArg));
635 }
636
LLVMAddCoroEarlyPass(LLVMPassManagerRef PM)637 void LLVMAddCoroEarlyPass(LLVMPassManagerRef PM) {
638 unwrap(PM)->add(createCoroEarlyLegacyPass());
639 }
640
LLVMAddCoroSplitPass(LLVMPassManagerRef PM)641 void LLVMAddCoroSplitPass(LLVMPassManagerRef PM) {
642 unwrap(PM)->add(createCoroSplitLegacyPass());
643 }
644
LLVMAddCoroElidePass(LLVMPassManagerRef PM)645 void LLVMAddCoroElidePass(LLVMPassManagerRef PM) {
646 unwrap(PM)->add(createCoroElideLegacyPass());
647 }
648
LLVMAddCoroCleanupPass(LLVMPassManagerRef PM)649 void LLVMAddCoroCleanupPass(LLVMPassManagerRef PM) {
650 unwrap(PM)->add(createCoroCleanupLegacyPass());
651 }
652