r331519 - [Coroutines] Catch exceptions in await_resume
Brian Gesiak via cfe-commits
cfe-commits at lists.llvm.org
Fri May 4 07:02:38 PDT 2018
Author: modocache
Date: Fri May 4 07:02:37 2018
New Revision: 331519
URL: http://llvm.org/viewvc/llvm-project?rev=331519&view=rev
Log:
[Coroutines] Catch exceptions in await_resume
Summary:
http://wg21.link/P0664r2 section "Evolution/Core Issues 24" describes a
proposed change to Coroutines TS that would have any exceptions thrown
after the initial suspend point of a coroutine be caught by the handler
specified by the promise type's 'unhandled_exception' member function.
This commit provides a sample implementation of the specified behavior.
Test Plan: `check-clang`
Reviewers: GorNishanov, EricWF
Reviewed By: GorNishanov
Subscribers: cfe-commits, lewissbaker, eric_niebler
Differential Revision: https://reviews.llvm.org/D45860
Added:
cfe/trunk/test/CodeGenCoroutines/coro-await-resume-eh.cpp
Modified:
cfe/trunk/lib/CodeGen/CGCoroutine.cpp
cfe/trunk/test/CodeGenCoroutines/coro-unhandled-exception.cpp
Modified: cfe/trunk/lib/CodeGen/CGCoroutine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCoroutine.cpp?rev=331519&r1=331518&r2=331519&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGCoroutine.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGCoroutine.cpp Fri May 4 07:02:37 2018
@@ -44,6 +44,15 @@ struct clang::CodeGen::CGCoroData {
// A branch to this block is emitted when coroutine needs to suspend.
llvm::BasicBlock *SuspendBB = nullptr;
+ // The promise type's 'unhandled_exception' handler, if it defines one.
+ Stmt *ExceptionHandler = nullptr;
+
+ // A temporary i1 alloca that stores whether 'await_resume' threw an
+ // exception. If it did, 'true' is stored in this variable, and the coroutine
+ // body must be skipped. If the promise type does not define an exception
+ // handler, this is null.
+ llvm::Value *ResumeEHVar = nullptr;
+
// Stores the jump destination just before the coroutine memory is freed.
// This is the destination that every suspend point jumps to for the cleanup
// branch.
@@ -208,11 +217,32 @@ static LValueOrRValue emitSuspendExpress
// Emit await_resume expression.
CGF.EmitBlock(ReadyBlock);
+ CXXTryStmt *TryStmt = nullptr;
+ if (Coro.ExceptionHandler && Kind == AwaitKind::Init) {
+ Coro.ResumeEHVar =
+ CGF.CreateTempAlloca(Builder.getInt1Ty(), Prefix + Twine("resume.eh"));
+ Builder.CreateFlagStore(true, Coro.ResumeEHVar);
+
+ auto Loc = S.getResumeExpr()->getExprLoc();
+ auto *Catch = new (CGF.getContext())
+ CXXCatchStmt(Loc, /*exDecl=*/nullptr, Coro.ExceptionHandler);
+ auto *TryBody =
+ CompoundStmt::Create(CGF.getContext(), S.getResumeExpr(), Loc, Loc);
+ TryStmt = CXXTryStmt::Create(CGF.getContext(), Loc, TryBody, Catch);
+ CGF.EnterCXXTryStmt(*TryStmt);
+ }
+
LValueOrRValue Res;
if (forLValue)
Res.LV = CGF.EmitLValue(S.getResumeExpr());
else
Res.RV = CGF.EmitAnyExpr(S.getResumeExpr(), aggSlot, ignoreResult);
+
+ if (TryStmt) {
+ Builder.CreateFlagStore(false, Coro.ResumeEHVar);
+ CGF.ExitCXXTryStmt(*TryStmt);
+ }
+
return Res;
}
@@ -588,19 +618,31 @@ void CodeGenFunction::EmitCoroutineBody(
EHStack.pushCleanup<CallCoroEnd>(EHCleanup);
CurCoro.Data->CurrentAwaitKind = AwaitKind::Init;
+ CurCoro.Data->ExceptionHandler = S.getExceptionHandler();
EmitStmt(S.getInitSuspendStmt());
CurCoro.Data->FinalJD = getJumpDestInCurrentScope(FinalBB);
CurCoro.Data->CurrentAwaitKind = AwaitKind::Normal;
- if (auto *OnException = S.getExceptionHandler()) {
+ 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);
+
auto Loc = S.getLocStart();
- CXXCatchStmt Catch(Loc, /*exDecl=*/nullptr, OnException);
- auto *TryStmt = CXXTryStmt::Create(getContext(), Loc, S.getBody(), &Catch);
+ CXXCatchStmt Catch(Loc, /*exDecl=*/nullptr,
+ CurCoro.Data->ExceptionHandler);
+ auto *TryStmt =
+ CXXTryStmt::Create(getContext(), Loc, S.getBody(), &Catch);
EnterCXXTryStmt(*TryStmt);
emitBodyAndFallthrough(*this, S, TryStmt->getTryBlock());
ExitCXXTryStmt(*TryStmt);
+
+ EmitBlock(ContBB);
}
else {
emitBodyAndFallthrough(*this, S, S.getBody());
Added: cfe/trunk/test/CodeGenCoroutines/coro-await-resume-eh.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCoroutines/coro-await-resume-eh.cpp?rev=331519&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCoroutines/coro-await-resume-eh.cpp (added)
+++ cfe/trunk/test/CodeGenCoroutines/coro-await-resume-eh.cpp Fri May 4 07:02:37 2018
@@ -0,0 +1,81 @@
+// Test the behavior of http://wg21.link/P0664, a proposal to catch any
+// exceptions thrown after the initial suspend point of a coroutine by
+// executing the handler specified by the promise type's 'unhandled_exception'
+// member function.
+//
+// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts \
+// RUN: -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s \
+// RUN: -fexceptions -fcxx-exceptions -disable-llvm-passes \
+// RUN: | FileCheck %s
+
+#include "Inputs/coroutine.h"
+
+namespace coro = std::experimental::coroutines_v1;
+
+struct throwing_awaitable {
+ bool await_ready() { return true; }
+ void await_suspend(coro::coroutine_handle<>) {}
+ void await_resume() { throw 42; }
+};
+
+struct task {
+ struct promise_type {
+ task get_return_object() { return 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() {
+ // 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.
+ // CHECK: init.ready:
+ // CHECK-NEXT: store i1 true, i1* %[[RESUMETHREW:.+]], align 1
+ // CHECK-NEXT: invoke void @_ZN18throwing_awaitable12await_resumeEv
+ // CHECK-NEXT: to label %[[RESUMECONT:.+]] unwind label %[[RESUMELPAD:.+]]
+
+ // If 'await_resume' does not throw an exception, 'false' is stored in
+ // variable RESUMETHREW.
+ // CHECK: [[RESUMECONT]]:
+ // CHECK-NEXT: store i1 false, i1* %[[RESUMETHREW]]
+ // CHECK-NEXT: br label %[[RESUMETRYCONT:.+]]
+
+ // 'unhandled_exception' is called for the exception thrown in
+ // 'await_resume'. The variable RESUMETHREW is never set to false,
+ // and a jump is made to RESUMETRYCONT.
+ // CHECK: [[RESUMELPAD]]:
+ // CHECK: br label %[[RESUMECATCH:.+]]
+ // CHECK: [[RESUMECATCH]]:
+ // CHECK: invoke void @_ZN4task12promise_type19unhandled_exceptionEv
+ // CHECK-NEXT: to label %[[RESUMEENDCATCH:.+]] unwind label
+ // CHECK: [[RESUMEENDCATCH]]:
+ // CHECK-NEXT: invoke void @__cxa_end_catch()
+ // CHECK-NEXT: to label %[[RESUMEENDCATCHCONT:.+]] unwind label
+ // CHECK: [[RESUMEENDCATCHCONT]]:
+ // CHECK-NEXT: br label %[[RESUMETRYCONT]]
+
+ // The variable RESUMETHREW is loaded and if true, then 'await_resume'
+ // threw an exception and the coroutine body is skipped, and the final
+ // suspend is executed immediately. Otherwise, the coroutine body is
+ // executed, and then the final suspend.
+ // CHECK: [[RESUMETRYCONT]]:
+ // CHECK-NEXT: %[[RESUMETHREWLOAD:.+]] = load i1, i1* %[[RESUMETHREW]]
+ // CHECK-NEXT: br i1 %[[RESUMETHREWLOAD]], label %[[RESUMEDCONT:.+]], label %[[RESUMEDBODY:.+]]
+
+ // CHECK: [[RESUMEDBODY]]:
+ // CHECK: invoke void @_ZN4task12promise_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
+ co_return;
+}
Modified: cfe/trunk/test/CodeGenCoroutines/coro-unhandled-exception.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCoroutines/coro-unhandled-exception.cpp?rev=331519&r1=331518&r2=331519&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCoroutines/coro-unhandled-exception.cpp (original)
+++ cfe/trunk/test/CodeGenCoroutines/coro-unhandled-exception.cpp Fri May 4 07:02:37 2018
@@ -48,6 +48,8 @@ coro_t f() {
// CHECK: [[CATCHRETDEST]]:
// CHECK-NEXT: br label %[[TRYCONT:.+]]
// CHECK: [[TRYCONT]]:
+// CHECK-NEXT: br label %[[RESUMECONT:.+]]
+// CHECK: [[RESUMECONT]]:
// CHECK-NEXT: br label %[[COROFIN:.+]]
// CHECK: [[COROFIN]]:
// CHECK-NEXT: invoke void @"?final_suspend at promise_type@coro_t@@QEAA?AUsuspend_never at coroutines_v1@experimental at std@@XZ"(
@@ -67,6 +69,8 @@ coro_t f() {
// CHECK-LPAD: [[CATCHRETDEST]]:
// CHECK-LPAD-NEXT: br label %[[TRYCONT:.+]]
// CHECK-LPAD: [[TRYCONT]]:
+// CHECK-LPAD: br label %[[RESUMECONT:.+]]
+// CHECK-LPAD: [[RESUMECONT]]:
// CHECK-LPAD-NEXT: br label %[[COROFIN:.+]]
// CHECK-LPAD: [[COROFIN]]:
// CHECK-LPAD-NEXT: invoke void @_ZN6coro_t12promise_type13final_suspendEv(
More information about the cfe-commits
mailing list