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