modocache created this revision. modocache added reviewers: GorNishanov, EricWF. Herald added a subscriber: cfe-commits.
In his review of https://reviews.llvm.org/D45860, @GorNishanov suggested avoiding generating additional exception-handling IR in the case that the resume function was marked as 'noexcept', and exceptions could not occur. This implements that suggestion. Test Plan: `check-clang` Repository: rC Clang https://reviews.llvm.org/D47673 Files: lib/CodeGen/CGCoroutine.cpp test/CodeGenCoroutines/coro-await-resume-eh.cpp
Index: test/CodeGenCoroutines/coro-await-resume-eh.cpp =================================================================== --- test/CodeGenCoroutines/coro-await-resume-eh.cpp +++ test/CodeGenCoroutines/coro-await-resume-eh.cpp @@ -18,18 +18,18 @@ void await_resume() { throw 42; } }; -struct task { +struct throwing_task { struct promise_type { - task get_return_object() { return task{}; } + auto get_return_object() { return throwing_task{}; } auto initial_suspend() { return throwing_awaitable{}; } auto final_suspend() { return coro::suspend_never{}; } void return_void() {} void unhandled_exception() {} }; }; // CHECK-LABEL: define void @_Z1fv() -task f() { +throwing_task f() { // A variable RESUMETHREW is used to keep track of whether the body // of 'await_resume' threw an exception. Exceptions thrown in // 'await_resume' are unwound to RESUMELPAD. @@ -50,7 +50,7 @@ // CHECK: [[RESUMELPAD]]: // CHECK: br label %[[RESUMECATCH:.+]] // CHECK: [[RESUMECATCH]]: - // CHECK: invoke void @_ZN4task12promise_type19unhandled_exceptionEv + // CHECK: invoke void @_ZN13throwing_task12promise_type19unhandled_exceptionEv // CHECK-NEXT: to label %[[RESUMEENDCATCH:.+]] unwind label // CHECK: [[RESUMEENDCATCH]]: // CHECK-NEXT: invoke void @__cxa_end_catch() @@ -67,15 +67,42 @@ // CHECK-NEXT: br i1 %[[RESUMETHREWLOAD]], label %[[RESUMEDCONT:.+]], label %[[RESUMEDBODY:.+]] // CHECK: [[RESUMEDBODY]]: - // CHECK: invoke void @_ZN4task12promise_type11return_voidEv + // CHECK: invoke void @_ZN13throwing_task12promise_type11return_voidEv // CHECK-NEXT: to label %[[REDUMEDBODYCONT:.+]] unwind label // CHECK: [[REDUMEDBODYCONT]]: // CHECK-NEXT: br label %[[COROFINAL:.+]] // CHECK: [[RESUMEDCONT]]: // CHECK-NEXT: br label %[[COROFINAL]] // CHECK: [[COROFINAL]]: - // CHECK-NEXT: invoke void @_ZN4task12promise_type13final_suspendEv + // CHECK-NEXT: invoke void @_ZN13throwing_task12promise_type13final_suspendEv + co_return; +} + +struct noexcept_awaitable { + bool await_ready() { return true; } + void await_suspend(coro::coroutine_handle<>) {} + void await_resume() noexcept {} +}; + +struct noexcept_task { + struct promise_type { + auto get_return_object() { return noexcept_task{}; } + auto initial_suspend() { return noexcept_awaitable{}; } + auto final_suspend() { return coro::suspend_never{}; } + void return_void() {} + void unhandled_exception() {} + }; +}; + +// CHECK-LABEL: define void @_Z1gv() +noexcept_task g() { + // If the await_resume function is marked as noexcept, none of the additional + // conditions that are present in f() above are added to the IR. + // This means that no i1 are stored before or after calling await_resume: + // CHECK: init.ready: + // CHECK-NEXT: call void @_ZN18noexcept_awaitable12await_resumeEv + // CHECK-NOT: store i1 false, i1* co_return; } Index: lib/CodeGen/CGCoroutine.cpp =================================================================== --- lib/CodeGen/CGCoroutine.cpp +++ lib/CodeGen/CGCoroutine.cpp @@ -217,8 +217,19 @@ // Emit await_resume expression. CGF.EmitBlock(ReadyBlock); + + // Exception handling requires additional IR. If the 'await_resume' function + // is marked as 'noexcept', we avoid generating this additional IR. + bool ResumeCanThrow = true; + if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(S.getResumeExpr())) + if (const auto *Proto = + MCE->getMethodDecl()->getType()->getAs<FunctionProtoType>()) + if (isNoexceptExceptionSpec(Proto->getExceptionSpecType()) && + Proto->canThrow() == CT_Cannot) + ResumeCanThrow = false; + CXXTryStmt *TryStmt = nullptr; - if (Coro.ExceptionHandler && Kind == AwaitKind::Init) { + if (Coro.ExceptionHandler && Kind == AwaitKind::Init && ResumeCanThrow) { Coro.ResumeEHVar = CGF.CreateTempAlloca(Builder.getInt1Ty(), Prefix + Twine("resume.eh")); Builder.CreateFlagStore(true, Coro.ResumeEHVar); @@ -625,12 +636,20 @@ CurCoro.Data->CurrentAwaitKind = AwaitKind::Normal; if (CurCoro.Data->ExceptionHandler) { - BasicBlock *BodyBB = createBasicBlock("coro.resumed.body"); - BasicBlock *ContBB = createBasicBlock("coro.resumed.cont"); - Value *SkipBody = - Builder.CreateFlagLoad(CurCoro.Data->ResumeEHVar, "coro.resumed.eh"); - Builder.CreateCondBr(SkipBody, ContBB, BodyBB); - EmitBlock(BodyBB); + // If we generated IR to record whether an exception was thrown from + // 'await_resume', then use that IR to determine whether the coroutine + // body should be skipped. + // If we didn't generate the IR (perhaps because 'await_resume' was marked + // as 'noexcept'), then we skip this check. + BasicBlock *ContBB = nullptr; + if (CurCoro.Data->ResumeEHVar) { + BasicBlock *BodyBB = createBasicBlock("coro.resumed.body"); + ContBB = createBasicBlock("coro.resumed.cont"); + Value *SkipBody = Builder.CreateFlagLoad(CurCoro.Data->ResumeEHVar, + "coro.resumed.eh"); + Builder.CreateCondBr(SkipBody, ContBB, BodyBB); + EmitBlock(BodyBB); + } auto Loc = S.getLocStart(); CXXCatchStmt Catch(Loc, /*exDecl=*/nullptr, @@ -642,7 +661,8 @@ emitBodyAndFallthrough(*this, S, TryStmt->getTryBlock()); ExitCXXTryStmt(*TryStmt); - EmitBlock(ContBB); + if (ContBB) + EmitBlock(ContBB); } else { emitBodyAndFallthrough(*this, S, S.getBody());
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits