Index: clang/include/clang/AST/StmtCXX.h =================================================================== --- clang/include/clang/AST/StmtCXX.h +++ clang/include/clang/AST/StmtCXX.h @@ -385,6 +385,12 @@ VarDecl *getPromiseDecl() const { return cast(cast(getPromiseDeclStmt())->getSingleDecl()); } + /// Return true if the constructor of the promise wouldn't throw, + /// return false other wise. + bool isPromiseCtorNothrow() const; + /// Return true if the promise_type::unhandled_exception wouldn't throw, + /// return false other wise. + bool isOnExceptionNothrow() const; Stmt *getInitSuspendStmt() const { return getStoredStmts()[SubStmt::InitSuspend]; Index: clang/lib/AST/StmtCXX.cpp =================================================================== --- clang/lib/AST/StmtCXX.cpp +++ clang/lib/AST/StmtCXX.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/StmtCXX.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/ASTContext.h" @@ -125,3 +126,30 @@ std::copy(Args.ParamMoves.begin(), Args.ParamMoves.end(), const_cast(getParamMoves().data())); } + +bool CoroutineBodyStmt::isPromiseCtorNothrow() const { + Expr *CtorExpr = getPromiseDecl()->getInit(); + if (isa(CtorExpr)) + CtorExpr = cast(CtorExpr)->getSubExpr(); + if (!isa(CtorExpr)) + // Unimaged pattern. Return false conservatively. + return false; + + auto *Ctor = cast(CtorExpr)->getConstructor(); + return isNoexceptExceptionSpec(Ctor->getExceptionSpecType()); +} +bool CoroutineBodyStmt::isOnExceptionNothrow() const { + Stmt *ExceptionHandler = getExceptionHandler(); + if (!ExceptionHandler) + return true; + Expr *OnExceptionCall = cast(ExceptionHandler); + if (isa(OnExceptionCall)) + OnExceptionCall = cast(OnExceptionCall)->getSubExpr(); + if (!isa(OnExceptionCall)) + // Unimaged pattern. Return false conservatively. + return false; + auto *UnhandledException = cast(OnExceptionCall)->getDirectCallee(); + assert(UnhandledException && + "The Call to promise_type::unhandled_exception is not direct call.\n"); + return isNoexceptExceptionSpec(UnhandledException->getExceptionSpecType()); +} \ No newline at end of file Index: clang/lib/CodeGen/CGCoroutine.cpp =================================================================== --- clang/lib/CodeGen/CGCoroutine.cpp +++ clang/lib/CodeGen/CGCoroutine.cpp @@ -541,6 +541,23 @@ } void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) { + // A coroutine would generate following codes: + // ``` + // promise-type promise promise-constructor-arguments ; + // try { + // co_await promise.initial_suspend(); + // function-body + // } catch (...) { + // promise.unhandled_exception(); + // } + // co_await promise.final_suspend(); + // ``` + // promise_type::final_suspend is guranteed to be noexcept. So if the + // constructor of promise_type and unhandled_exception() are guranteed + // to be no throw, we could mark the coroutine as no throw. + if (S.isPromiseCtorNothrow() && S.isOnExceptionNothrow()) + CurFn->addFnAttr(llvm::Attribute::NoUnwind); + auto *NullPtr = llvm::ConstantPointerNull::get(Builder.getInt8PtrTy()); auto &TI = CGM.getContext().getTargetInfo(); unsigned NewAlign = TI.getNewAlign() / TI.getCharWidth(); Index: clang/test/CodeGenCoroutines/coro-nounwind.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCoroutines/coro-nounwind.cpp @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -triple=x86_64-pc-windows-msvc18.0.0 -emit-llvm %s -o - -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s +// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck --check-prefix=CHECK-LINUX %s + +#include "Inputs/coroutine.h" + +namespace coro = std::experimental::coroutines_v1; + +struct coro_nothrow_t { + struct promise_type { + coro_nothrow_t get_return_object() { + coro::coroutine_handle{}; + return {}; + } + coro::suspend_never initial_suspend() { return {}; } + coro::suspend_never final_suspend() noexcept { return {}; } + void return_void() {} + void unhandled_exception() noexcept; + }; +}; + +// CHECK: define dso_local i8 @"?foo@@YA?AUcoro_nothrow_t{{.*}}#[[FOOATTRNUM:[0-9]+]] +// CHECK-LINUX: define dso_local void @_Z3foov{{.*}}#[[FOOATTRNUM:[0-9]+]] + +coro_nothrow_t foo() { + co_return; +} + +struct coro_ctor_maythrow_t { + struct promise_type { + coro_ctor_maythrow_t get_return_object() { + coro::coroutine_handle{}; + return {}; + } + coro::suspend_never initial_suspend() { return {}; } + coro::suspend_never final_suspend() noexcept { return {}; } + void return_void() {} + void unhandled_exception() noexcept; + promise_type(); + }; +}; + +// CHECK: define dso_local i8 @"?bar@@YA?AUcoro_ctor_maythrow_t{{.*}}#[[BARATTRNUM:[0-9]+]] +// CHECK-LINUX: define dso_local void @_Z3barv() #[[BARATTRNUM:[0-9]+]] +coro_ctor_maythrow_t bar() { + co_return; +} + +struct coro_onexception_maythrow_t { + struct promise_type { + coro_onexception_maythrow_t get_return_object() { + coro::coroutine_handle{}; + return {}; + } + coro::suspend_never initial_suspend() { return {}; } + coro::suspend_never final_suspend() noexcept { return {}; } + void return_void() {} + void unhandled_exception(); + }; +}; + +// CHECK: define dso_local i8 @"?baz@@YA?AUcoro_onexception_maythrow_t{{.*}}#[[BAZATTRNUM:[0-9]+]] +// CHECK-LINUX: define dso_local void @_Z3bazv() #[[BAZATTRNUM:[0-9]+]] + +// CHECK: attributes #[[FOOATTRNUM]] = {{.*}}nounwind +// CHECK-NOT: attributes #[[BARATTRNUM]] = {{.*}}nounwind +// CHECK-NOT: attributes #[[BAZATTRNUM]] = {{.*}}nounwind +// CHECK-LINUX: attributes #[[FOOATTRNUM]] = {{.*}}nounwind +// CHECK-LINUX-NOT: attributes #[[BARATTRNUM]] = {{.*}}nounwind +// CHECK-LINUX-NOT: attributes #[[BAZATTRNUM]] = {{.*}}nounwind +coro_onexception_maythrow_t baz() { + co_return; +} \ No newline at end of file