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