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