[PATCH] D47673: [Coroutines] Less IR for noexcept await_resume
This revision was automatically updated to reflect the committed changes. Closed by commit rL335422: [Coroutines] Less IR for noexcept await_resume (authored by modocache, committed by ). Repository: rL LLVM https://reviews.llvm.org/D47673 Files: cfe/trunk/lib/CodeGen/CGCoroutine.cpp cfe/trunk/test/CodeGenCoroutines/coro-await-resume-eh.cpp Index: cfe/trunk/test/CodeGenCoroutines/coro-await-resume-eh.cpp === --- cfe/trunk/test/CodeGenCoroutines/coro-await-resume-eh.cpp +++ cfe/trunk/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: cfe/trunk/lib/CodeGen/CGCoroutine.cpp === --- cfe/trunk/lib/CodeGen/CGCoroutine.cpp +++ cfe/trunk/lib/CodeGen/CGCoroutine.cpp @@ -130,6 +130,16 @@ return Prefix; } +static bool memberCallExpressionCanThrow(const Expr *E) { + if (const auto *CE = dyn_cast(E)) +if (const auto *Proto = +CE->getMethodDecl()->getType()->getAs()) + if (isNoexceptExceptionSpec(Proto->getExceptionSpecType()) && + Proto->canThrow() == CT_Cannot) +return false; + return true; +} + // Emit suspend expression which roughly looks like: // // auto && x = CommonExpr(); @@ -217,8 +227,12 @@ // 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. CXXTryStmt *TryStmt = nullptr; - if (Coro.ExceptionHandler && Kind == AwaitKind::Init) { + if (Coro.ExceptionHandler && Kind == AwaitKind::Init && + memberCallExpressionCanThrow(S.getResumeExpr())) { Coro.ResumeEHVar = CGF.CreateTempAlloca(Builder.getInt1Ty(), Prefix + Twine("resume.eh")); Builder.CreateFlagStore(true, Coro.ResumeEHVar); @@ -625,12 +639,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,
[PATCH] D47673: [Coroutines] Less IR for noexcept await_resume
This revision was automatically updated to reflect the committed changes. Closed by commit rC335422: [Coroutines] Less IR for noexcept await_resume (authored by modocache, committed by ). Changed prior to commit: https://reviews.llvm.org/D47673?vs=152599=152602#toc 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 @@ -130,6 +130,16 @@ return Prefix; } +static bool memberCallExpressionCanThrow(const Expr *E) { + if (const auto *CE = dyn_cast(E)) +if (const auto *Proto = +CE->getMethodDecl()->getType()->getAs()) + if (isNoexceptExceptionSpec(Proto->getExceptionSpecType()) && + Proto->canThrow() == CT_Cannot) +return false; + return true; +} + // Emit suspend expression which roughly looks like: // // auto && x = CommonExpr(); @@ -217,8 +227,12 @@ // 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. CXXTryStmt *TryStmt = nullptr; - if (Coro.ExceptionHandler && Kind == AwaitKind::Init) { + if (Coro.ExceptionHandler && Kind == AwaitKind::Init && + memberCallExpressionCanThrow(S.getResumeExpr())) { Coro.ResumeEHVar = CGF.CreateTempAlloca(Builder.getInt1Ty(), Prefix + Twine("resume.eh")); Builder.CreateFlagStore(true, Coro.ResumeEHVar); @@ -625,12 +639,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,
[PATCH] D47673: [Coroutines] Less IR for noexcept await_resume
modocache updated this revision to Diff 152599. modocache added a comment. Great, thanks @GorNishanov! I moved the 'can throw' logic into a function called 'memberCallExpressionCanThrow', to convey that some dyn_cast'ing is going on. 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 @@ -130,6 +130,16 @@ return Prefix; } +static bool memberCallExpressionCanThrow(const Expr *E) { + if (const auto *CE = dyn_cast(E)) +if (const auto *Proto = +CE->getMethodDecl()->getType()->getAs()) + if (isNoexceptExceptionSpec(Proto->getExceptionSpecType()) && + Proto->canThrow() == CT_Cannot) +return false; + return true; +} + // Emit suspend expression which roughly looks like: // // auto && x = CommonExpr(); @@ -217,8 +227,12 @@ // 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. CXXTryStmt *TryStmt = nullptr; - if (Coro.ExceptionHandler && Kind == AwaitKind::Init) { + if (Coro.ExceptionHandler && Kind == AwaitKind::Init && + memberCallExpressionCanThrow(S.getResumeExpr())) { Coro.ResumeEHVar = CGF.CreateTempAlloca(Builder.getInt1Ty(), Prefix + Twine("resume.eh")); Builder.CreateFlagStore(true, Coro.ResumeEHVar); @@ -625,12 +639,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); -
[PATCH] D47673: [Coroutines] Less IR for noexcept await_resume
GorNishanov accepted this revision. GorNishanov added a comment. This revision is now accepted and ready to land. LGTM with some suggestions. Comment at: lib/CodeGen/CGCoroutine.cpp:224 + bool ResumeCanThrow = true; + if (const auto *MCE = dyn_cast(S.getResumeExpr())) +if (const auto *Proto = This long sequence of if statements seems to be asking to be put into its own predicate function: expressionCanThrow or something like that. Repository: rC Clang https://reviews.llvm.org/D47673 ___ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D47673: [Coroutines] Less IR for noexcept await_resume
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(S.getResumeExpr())) +if (const auto *Proto = +MCE->getMethodDecl()->getType()->getAs()) + 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