forked from OSchip/llvm-project
[Coroutines] Less IR for noexcept await_resume
Summary: 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` Reviewers: GorNishanov, EricWF Reviewed By: GorNishanov Subscribers: cfe-commits, GorNishanov Differential Revision: https://reviews.llvm.org/D47673 llvm-svn: 335422
This commit is contained in:
parent
f4c82cffc6
commit
12728474b3
|
@ -130,6 +130,16 @@ static SmallString<32> buildSuspendPrefixStr(CGCoroData &Coro, AwaitKind Kind) {
|
|||
return Prefix;
|
||||
}
|
||||
|
||||
static bool memberCallExpressionCanThrow(const Expr *E) {
|
||||
if (const auto *CE = dyn_cast<CXXMemberCallExpr>(E))
|
||||
if (const auto *Proto =
|
||||
CE->getMethodDecl()->getType()->getAs<FunctionProtoType>())
|
||||
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 @@ static LValueOrRValue emitSuspendExpression(CodeGenFunction &CGF, CGCoroData &Co
|
|||
|
||||
// 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 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
|
|||
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 +664,8 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
|
|||
emitBodyAndFallthrough(*this, S, TryStmt->getTryBlock());
|
||||
ExitCXXTryStmt(*TryStmt);
|
||||
|
||||
EmitBlock(ContBB);
|
||||
if (ContBB)
|
||||
EmitBlock(ContBB);
|
||||
}
|
||||
else {
|
||||
emitBodyAndFallthrough(*this, S, S.getBody());
|
||||
|
|
|
@ -18,9 +18,9 @@ struct throwing_awaitable {
|
|||
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() {}
|
||||
|
@ -29,7 +29,7 @@ struct task {
|
|||
};
|
||||
|
||||
// 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 @@ task f() {
|
|||
// 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,7 +67,7 @@ task f() {
|
|||
// 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:.+]]
|
||||
|
@ -76,6 +76,33 @@ task f() {
|
|||
// 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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue