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