// Emit suspend expression which roughly looks like: // // auto && x = CommonExpr(); // if (!x.await_ready()) { // llvm_coro_save(); // x.await_suspend(...); (*) // llvm_coro_suspend(); (**) // } // x.await_resume(); // // where the result of the entire expression is the result of x.await_resume() // // (*) If x.await_suspend return type is bool, it allows to veto a suspend: // if (x.await_suspend(...)) // llvm_coro_suspend(); // // (**) llvm_coro_suspend() encodes three possible continuations as // a switch instruction: // // %where-to = call i8 @llvm.coro.suspend(...) // switch i8 %where-to, label %coro.ret [ ; jump to epilogue to suspend // i8 0, label %yield.ready ; go here when resumed // i8 1, label %yield.cleanup ; go here when destroyed // ] // // See llvm's docs/Coroutines.rst for more details. // static RValue emitSuspendExpression(CodeGenFunction &CGF, CGCoroData &Coro, CoroutineSuspendExpr const &S, AwaitKind Kind, AggValueSlot aggSlot, bool ignoreResult) { auto *E = S.getCommonExpr(); auto Binder = CodeGenFunction::OpaqueValueMappingData::bind(CGF, S.getOpaqueValue(), E); auto UnbindOnExit = llvm::make_scope_exit([&] { Binder.unbind(CGF); }); auto Prefix = buildSuspendPrefixStr(Coro, Kind); BasicBlock *ReadyBlock = CGF.createBasicBlock(Prefix + Twine(".ready")); BasicBlock *SuspendBlock = CGF.createBasicBlock(Prefix + Twine(".suspend")); BasicBlock *CleanupBlock = CGF.createBasicBlock(Prefix + Twine(".cleanup")); // If expression is ready, no need to suspend. CGF.EmitBranchOnBoolExpr(S.getReadyExpr(), ReadyBlock, SuspendBlock, 0); // Otherwise, emit suspend logic. CGF.EmitBlock(SuspendBlock); auto &Builder = CGF.Builder; llvm::Function *CoroSave = CGF.CGM.getIntrinsic(llvm::Intrinsic::coro_save); auto *NullPtr = llvm::ConstantPointerNull::get(CGF.CGM.Int8PtrTy); auto *SaveCall = Builder.CreateCall(CoroSave, {NullPtr}); auto *SuspendRet = CGF.EmitScalarExpr(S.getSuspendExpr()); if (SuspendRet != nullptr) { // Veto suspension if requested by bool returning await_suspend. assert(SuspendRet->getType()->isIntegerTy(1) && "Sema should have already checked that it is void or bool"); BasicBlock *RealSuspendBlock = CGF.createBasicBlock(Prefix + Twine(".suspend.bool")); CGF.Builder.CreateCondBr(SuspendRet, RealSuspendBlock, ReadyBlock); SuspendBlock = RealSuspendBlock; CGF.EmitBlock(RealSuspendBlock); } // Emit the suspend point. const bool IsFinalSuspend = (Kind == AwaitKind::Final); llvm::Function *CoroSuspend = CGF.CGM.getIntrinsic(llvm::Intrinsic::coro_suspend); auto *SuspendResult = Builder.CreateCall( CoroSuspend, {SaveCall, Builder.getInt1(IsFinalSuspend)}); // Create a switch capturing three possible continuations. auto *Switch = Builder.CreateSwitch(SuspendResult, Coro.SuspendBB, 2); Switch->addCase(Builder.getInt8(0), ReadyBlock); Switch->addCase(Builder.getInt8(1), CleanupBlock); // Emit cleanup for this suspend point. CGF.EmitBlock(CleanupBlock); CGF.EmitBranchThroughCleanup(Coro.CleanupJD); // Emit await_resume expression. CGF.EmitBlock(ReadyBlock); return CGF.EmitAnyExpr(S.getResumeExpr(), aggSlot, ignoreResult); }