summaryrefslogtreecommitdiff
path: root/test/CodeGenCoroutines
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2017-05-29 16:25:46 +0000
committerDimitry Andric <dim@FreeBSD.org>2017-05-29 16:25:46 +0000
commitb5aee35cc5d62f11d98539f62e4fe63f0ac9edc6 (patch)
tree3e6ab962dbc73cfe1445a60d2eb4dfba7c939a22 /test/CodeGenCoroutines
parentaa803409c3bd3930126db630c29f63d42f255153 (diff)
Notes
Diffstat (limited to 'test/CodeGenCoroutines')
-rw-r--r--test/CodeGenCoroutines/Inputs/coroutine.h80
-rw-r--r--test/CodeGenCoroutines/coro-alloc.cpp40
-rw-r--r--test/CodeGenCoroutines/coro-await.cpp64
-rw-r--r--test/CodeGenCoroutines/coro-builtins.c22
-rw-r--r--test/CodeGenCoroutines/coro-cleanup.cpp67
-rw-r--r--test/CodeGenCoroutines/coro-eh-cleanup.cpp16
-rw-r--r--test/CodeGenCoroutines/coro-gro.cpp86
-rw-r--r--test/CodeGenCoroutines/coro-lambda.cpp58
-rw-r--r--test/CodeGenCoroutines/coro-params.cpp95
-rw-r--r--test/CodeGenCoroutines/coro-promise-dtor.cpp47
-rw-r--r--test/CodeGenCoroutines/coro-ret-void.cpp39
-rw-r--r--test/CodeGenCoroutines/coro-unhandled-exception.cpp72
12 files changed, 634 insertions, 52 deletions
diff --git a/test/CodeGenCoroutines/Inputs/coroutine.h b/test/CodeGenCoroutines/Inputs/coroutine.h
new file mode 100644
index 0000000000000..d58212b1d5280
--- /dev/null
+++ b/test/CodeGenCoroutines/Inputs/coroutine.h
@@ -0,0 +1,80 @@
+#pragma once
+
+namespace std { namespace experimental { inline namespace coroutines_v1 {
+
+template <typename R, typename...> struct coroutine_traits {
+ using promise_type = typename R::promise_type;
+};
+
+template <typename Promise = void> struct coroutine_handle;
+
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *addr) noexcept {
+ coroutine_handle me;
+ me.ptr = addr;
+ return me;
+ }
+ void operator()() { resume(); }
+ void *address() const { return ptr; }
+ void resume() const { __builtin_coro_resume(ptr); }
+ void destroy() const { __builtin_coro_destroy(ptr); }
+ bool done() const { return __builtin_coro_done(ptr); }
+ coroutine_handle &operator=(decltype(nullptr)) {
+ ptr = nullptr;
+ return *this;
+ }
+ coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}
+ coroutine_handle() : ptr(nullptr) {}
+// void reset() { ptr = nullptr; } // add to P0057?
+ explicit operator bool() const { return ptr; }
+
+protected:
+ void *ptr;
+};
+
+template <typename Promise> struct coroutine_handle : coroutine_handle<> {
+ using coroutine_handle<>::operator=;
+
+ static coroutine_handle from_address(void *addr) noexcept {
+ coroutine_handle me;
+ me.ptr = addr;
+ return me;
+ }
+
+ Promise &promise() const {
+ return *reinterpret_cast<Promise *>(
+ __builtin_coro_promise(ptr, alignof(Promise), false));
+ }
+ static coroutine_handle from_promise(Promise &promise) {
+ coroutine_handle p;
+ p.ptr = __builtin_coro_promise(&promise, alignof(Promise), true);
+ return p;
+ }
+};
+
+ template <typename _PromiseT>
+ bool operator==(coroutine_handle<_PromiseT> const& _Left,
+ coroutine_handle<_PromiseT> const& _Right) noexcept
+ {
+ return _Left.address() == _Right.address();
+ }
+
+ template <typename _PromiseT>
+ bool operator!=(coroutine_handle<_PromiseT> const& _Left,
+ coroutine_handle<_PromiseT> const& _Right) noexcept
+ {
+ return !(_Left == _Right);
+ }
+
+struct suspend_always {
+ bool await_ready() { return false; }
+ void await_suspend(coroutine_handle<>) {}
+ void await_resume() {}
+};
+struct suspend_never {
+ bool await_ready() { return true; }
+ void await_suspend(coroutine_handle<>) {}
+ void await_resume() {}
+};
+
+}}}
diff --git a/test/CodeGenCoroutines/coro-alloc.cpp b/test/CodeGenCoroutines/coro-alloc.cpp
index 3a65736f6c03a..820201db357fd 100644
--- a/test/CodeGenCoroutines/coro-alloc.cpp
+++ b/test/CodeGenCoroutines/coro-alloc.cpp
@@ -56,12 +56,28 @@ struct std::experimental::coroutine_traits<void, global_new_delete_tag> {
// CHECK-LABEL: f0(
extern "C" void f0(global_new_delete_tag) {
// CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
+ // CHECK: %[[NeedAlloc:.+]] = call i1 @llvm.coro.alloc(token %[[ID]])
+ // CHECK: br i1 %[[NeedAlloc]], label %[[AllocBB:.+]], label %[[InitBB:.+]]
+
+ // CHECK: [[AllocBB]]:
// CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
- // CHECK: call i8* @_Znwm(i64 %[[SIZE]])
+ // CHECK: %[[MEM:.+]] = call i8* @_Znwm(i64 %[[SIZE]])
+ // CHECK: br label %[[InitBB]]
+
+ // CHECK: [[InitBB]]:
+ // CHECK: %[[PHI:.+]] = phi i8* [ null, %{{.+}} ], [ %call, %[[AllocBB]] ]
+ // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(token %[[ID]], i8* %[[PHI]])
- // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame()
// CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
+ // CHECK: %[[NeedDealloc:.+]] = icmp ne i8* %[[MEM]], null
+ // CHECK: br i1 %[[NeedDealloc]], label %[[FreeBB:.+]], label %[[Afterwards:.+]]
+
+ // CHECK: [[FreeBB]]:
// CHECK: call void @_ZdlPv(i8* %[[MEM]])
+ // CHECK: br label %[[Afterwards]]
+
+ // CHECK: [[Afterwards]]:
+ // CHECK: ret void
co_return;
}
@@ -84,7 +100,7 @@ extern "C" void f1(promise_new_tag ) {
// CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
// CHECK: call i8* @_ZNSt12experimental16coroutine_traitsIJv15promise_new_tagEE12promise_typenwEm(i64 %[[SIZE]])
- // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame()
+ // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
// CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
// CHECK: call void @_ZdlPv(i8* %[[MEM]])
co_return;
@@ -109,7 +125,7 @@ extern "C" void f2(promise_delete_tag) {
// CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
// CHECK: call i8* @_Znwm(i64 %[[SIZE]])
- // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame()
+ // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
// CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
// CHECK: call void @_ZNSt12experimental16coroutine_traitsIJv18promise_delete_tagEE12promise_typedlEPv(i8* %[[MEM]])
co_return;
@@ -134,7 +150,7 @@ extern "C" void f3(promise_sized_delete_tag) {
// CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
// CHECK: call i8* @_Znwm(i64 %[[SIZE]])
- // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame()
+ // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
// CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
// CHECK: %[[SIZE2:.+]] = call i64 @llvm.coro.size.i64()
// CHECK: call void @_ZNSt12experimental16coroutine_traitsIJv24promise_sized_delete_tagEE12promise_typedlEPvm(i8* %[[MEM]], i64 %[[SIZE2]])
@@ -156,6 +172,7 @@ struct std::experimental::coroutine_traits<int, promise_on_alloc_failure_tag> {
// CHECK-LABEL: f4(
extern "C" int f4(promise_on_alloc_failure_tag) {
+ // CHECK: %[[RetVal:.+]] = alloca i32
// CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
// CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
// CHECK: %[[MEM:.+]] = call i8* @_ZnwmRKSt9nothrow_t(i64 %[[SIZE]], %"struct.std::nothrow_t"* dereferenceable(1) @_ZStL7nothrow)
@@ -163,7 +180,16 @@ extern "C" int f4(promise_on_alloc_failure_tag) {
// CHECK: br i1 %[[OK]], label %[[OKBB:.+]], label %[[ERRBB:.+]]
// CHECK: [[ERRBB]]:
- // CHECK: %[[RETVAL:.+]] = call i32 @_ZNSt12experimental16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type39get_return_object_on_allocation_failureEv(
- // CHECK: ret i32 %[[RETVAL]]
+ // CHECK: %[[FailRet:.+]] = call i32 @_ZNSt12experimental16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type39get_return_object_on_allocation_failureEv(
+ // CHECK: store i32 %[[FailRet]], i32* %[[RetVal]]
+ // CHECK: br label %[[RetBB:.+]]
+
+ // CHECK: [[OKBB]]:
+ // CHECK: %[[OkRet:.+]] = call i32 @_ZNSt12experimental16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type17get_return_objectEv(
+ // CHECK: store i32 %[[OkRet]], i32* %[[RetVal]]
+
+ // CHECK: [[RetBB]]:
+ // CHECK: %[[LoadRet:.+]] = load i32, i32* %[[RetVal]], align 4
+ // CHECK: ret i32 %[[LoadRet]]
co_return;
}
diff --git a/test/CodeGenCoroutines/coro-await.cpp b/test/CodeGenCoroutines/coro-await.cpp
index b0fd0129de34a..1e2deaa8f59a7 100644
--- a/test/CodeGenCoroutines/coro-await.cpp
+++ b/test/CodeGenCoroutines/coro-await.cpp
@@ -21,6 +21,17 @@ struct coroutine_handle : coroutine_handle<> {
}
}
+struct init_susp {
+ bool await_ready();
+ void await_suspend(std::experimental::coroutine_handle<>);
+ void await_resume();
+};
+struct final_susp {
+ bool await_ready();
+ void await_suspend(std::experimental::coroutine_handle<>);
+ void await_resume();
+};
+
struct suspend_always {
int stuff;
bool await_ready();
@@ -32,14 +43,22 @@ template<>
struct std::experimental::coroutine_traits<void> {
struct promise_type {
void get_return_object();
- suspend_always initial_suspend();
- suspend_always final_suspend();
+ init_susp initial_suspend();
+ final_susp final_suspend();
void return_void();
};
};
// CHECK-LABEL: f0(
extern "C" void f0() {
+ // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
+
+ // See if initial_suspend was issued:
+ // ----------------------------------
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type15initial_suspendEv(
+ // CHECK-NEXT: call zeroext i1 @_ZN9init_susp11await_readyEv(%struct.init_susp*
+ // CHECK: %[[INITSP_ID:.+]] = call token @llvm.coro.save(
+ // CHECK: call i8 @llvm.coro.suspend(token %[[INITSP_ID]], i1 false)
co_await suspend_always{};
// See if we need to suspend:
@@ -54,7 +73,6 @@ extern "C" void f0() {
// ---------------------------
// Build the coroutine handle and pass it to await_suspend
// ---------------------------
- // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame()
// CHECK: call i8* @_ZNSt12experimental16coroutine_handleINS_16coroutine_traitsIJvEE12promise_typeEE12from_addressEPv(i8* %[[FRAME]])
// ... many lines of code to coerce coroutine_handle into an i8* scalar
// CHECK: %[[CH:.+]] = load i8*, i8** %{{.+}}
@@ -76,6 +94,13 @@ extern "C" void f0() {
// --------------------------
// CHECK: [[READY_BB]]:
// CHECK: call void @_ZN14suspend_always12await_resumeEv(%struct.suspend_always* %[[AWAITABLE]])
+
+ // See if final_suspend was issued:
+ // ----------------------------------
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type13final_suspendEv(
+ // CHECK-NEXT: call zeroext i1 @_ZN10final_susp11await_readyEv(%struct.final_susp*
+ // CHECK: %[[FINALSP_ID:.+]] = call token @llvm.coro.save(
+ // CHECK: call i8 @llvm.coro.suspend(token %[[FINALSP_ID]], i1 true)
}
struct suspend_maybe {
@@ -91,8 +116,8 @@ template<>
struct std::experimental::coroutine_traits<void,int> {
struct promise_type {
void get_return_object();
- suspend_always initial_suspend();
- suspend_always final_suspend();
+ init_susp initial_suspend();
+ final_susp final_suspend();
void return_void();
suspend_maybe yield_value(int);
};
@@ -100,8 +125,9 @@ struct std::experimental::coroutine_traits<void,int> {
// CHECK-LABEL: f1(
extern "C" void f1(int) {
- co_yield 42;
// CHECK: %[[PROMISE:.+]] = alloca %"struct.std::experimental::coroutine_traits<void, int>::promise_type"
+ // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
+ co_yield 42;
// CHECK: call void @_ZNSt12experimental16coroutine_traitsIJviEE12promise_type11yield_valueEi(%struct.suspend_maybe* sret %[[AWAITER:.+]], %"struct.std::experimental::coroutine_traits<void, int>::promise_type"* %[[PROMISE]], i32 42)
// See if we need to suspend:
@@ -116,7 +142,6 @@ extern "C" void f1(int) {
// ---------------------------
// Build the coroutine handle and pass it to await_suspend
// ---------------------------
- // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame()
// CHECK: call i8* @_ZNSt12experimental16coroutine_handleINS_16coroutine_traitsIJviEE12promise_typeEE12from_addressEPv(i8* %[[FRAME]])
// ... many lines of code to coerce coroutine_handle into an i8* scalar
// CHECK: %[[CH:.+]] = load i8*, i8** %{{.+}}
@@ -228,3 +253,28 @@ extern "C" void TestOpAwait() {
// CHECK: call void @_ZN5MyAggawEv(%struct.MyAgg* %
// CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret %
}
+
+// CHECK-LABEL: EndlessLoop(
+extern "C" void EndlessLoop() {
+ // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
+
+ // See if initial_suspend was issued:
+ // ----------------------------------
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type15initial_suspendEv(
+ // CHECK-NEXT: call zeroext i1 @_ZN9init_susp11await_readyEv(%struct.init_susp*
+
+ for (;;)
+ co_await suspend_always{};
+
+ // Verify that final_suspend was NOT issued:
+ // ----------------------------------
+ // CHECK-NOT: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type13final_suspendEv(
+ // CHECK-NOT: call zeroext i1 @_ZN10final_susp11await_readyEv(%struct.final_susp*
+}
+
+// Verifies that we don't crash when awaiting on an lvalue.
+// CHECK-LABEL: @_Z11AwaitLValuev(
+void AwaitLValue() {
+ suspend_always lval;
+ co_await lval;
+}
diff --git a/test/CodeGenCoroutines/coro-builtins.c b/test/CodeGenCoroutines/coro-builtins.c
index d18d5e730f911..9ec2147642588 100644
--- a/test/CodeGenCoroutines/coro-builtins.c
+++ b/test/CodeGenCoroutines/coro-builtins.c
@@ -2,7 +2,7 @@
void *myAlloc(long long);
-// CHECK-LABEL: f(
+// CHECK-LABEL: f(
void f(int n) {
// CHECK: %n.addr = alloca i32
// CHECK: %n_copy = alloca i32
@@ -19,31 +19,25 @@ void f(int n) {
// CHECK-NEXT: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
// CHECK-NEXT: %[[MEM:.+]] = call i8* @myAlloc(i64 %[[SIZE]])
- // CHECK-NEXT: %[[BEG:.+]] = call i8* @llvm.coro.begin(token %[[COROID]], i8* %[[MEM]])
+ // CHECK-NEXT: %[[FRAME:.+]] = call i8* @llvm.coro.begin(token %[[COROID]], i8* %[[MEM]])
__builtin_coro_begin(myAlloc(__builtin_coro_size()));
- // CHECK-NEXT: %[[FRAME1:.+]] = call i8* @llvm.coro.frame()
- // CHECK-NEXT: call void @llvm.coro.resume(i8* %[[FRAME1]])
+ // CHECK-NEXT: call void @llvm.coro.resume(i8* %[[FRAME]])
__builtin_coro_resume(__builtin_coro_frame());
- // CHECK-NEXT: %[[FRAME2:.+]] = call i8* @llvm.coro.frame()
- // CHECK-NEXT: call void @llvm.coro.destroy(i8* %[[FRAME2]])
+ // CHECK-NEXT: call void @llvm.coro.destroy(i8* %[[FRAME]])
__builtin_coro_destroy(__builtin_coro_frame());
- // CHECK-NEXT: %[[FRAME3:.+]] = call i8* @llvm.coro.frame()
- // CHECK-NEXT: call i1 @llvm.coro.done(i8* %[[FRAME3]])
+ // CHECK-NEXT: call i1 @llvm.coro.done(i8* %[[FRAME]])
__builtin_coro_done(__builtin_coro_frame());
- // CHECK-NEXT: %[[FRAME4:.+]] = call i8* @llvm.coro.frame()
- // CHECK-NEXT: call i8* @llvm.coro.promise(i8* %[[FRAME4]], i32 48, i1 false)
+ // CHECK-NEXT: call i8* @llvm.coro.promise(i8* %[[FRAME]], i32 48, i1 false)
__builtin_coro_promise(__builtin_coro_frame(), 48, 0);
- // CHECK-NEXT: %[[FRAME5:.+]] = call i8* @llvm.coro.frame()
- // CHECK-NEXT: call i8* @llvm.coro.free(token %[[COROID]], i8* %[[FRAME5]])
+ // CHECK-NEXT: call i8* @llvm.coro.free(token %[[COROID]], i8* %[[FRAME]])
__builtin_coro_free(__builtin_coro_frame());
- // CHECK-NEXT: %[[FRAME6:.+]] = call i8* @llvm.coro.frame()
- // CHECK-NEXT: call i1 @llvm.coro.end(i8* %[[FRAME6]], i1 false)
+ // CHECK-NEXT: call i1 @llvm.coro.end(i8* %[[FRAME]], i1 false)
__builtin_coro_end(__builtin_coro_frame(), 0);
// CHECK-NEXT: call i8 @llvm.coro.suspend(token none, i1 true)
diff --git a/test/CodeGenCoroutines/coro-cleanup.cpp b/test/CodeGenCoroutines/coro-cleanup.cpp
index 7f6f35cfe26c4..ba1246979e522 100644
--- a/test/CodeGenCoroutines/coro-cleanup.cpp
+++ b/test/CodeGenCoroutines/coro-cleanup.cpp
@@ -6,38 +6,38 @@ template <typename... T> struct coroutine_traits;
template <class Promise = void> struct coroutine_handle {
coroutine_handle() = default;
- static coroutine_handle from_address(void *) { return {}; }
+ static coroutine_handle from_address(void *) noexcept;
};
template <> struct coroutine_handle<void> {
- static coroutine_handle from_address(void *) { return {}; }
+ static coroutine_handle from_address(void *) noexcept;
coroutine_handle() = default;
template <class PromiseType>
- coroutine_handle(coroutine_handle<PromiseType>) {}
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
};
}
struct suspend_always {
- bool await_ready();
- void await_suspend(std::experimental::coroutine_handle<>);
- void await_resume();
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
};
template <> struct std::experimental::coroutine_traits<void> {
struct promise_type {
- void get_return_object();
- suspend_always initial_suspend();
- suspend_always final_suspend();
- void return_void();
+ void get_return_object() noexcept;
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void return_void() noexcept;
promise_type();
~promise_type();
- void unhandled_exception();
+ void unhandled_exception() noexcept;
};
};
struct Cleanup { ~Cleanup(); };
void may_throw();
-// CHECK: define void @_Z1fv(
+// CHECK-LABEL: define void @_Z1fv(
void f() {
// CHECK: call i8* @_Znwm(i64
@@ -46,25 +46,40 @@ void f() {
// CHECK: invoke void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_typeC1Ev(
// CHECK-NEXT: to label %{{.+}} unwind label %[[DeallocPad:.+]]
+ // CHECK: [[DeallocPad]]:
+ // CHECK-NEXT: landingpad
+ // CHECK-NEXT: cleanup
+ // CHECK: br label %[[Dealloc:.+]]
+
Cleanup cleanup;
may_throw();
// if may_throw throws, check that we destroy the promise and free the memory.
// CHECK: invoke void @_Z9may_throwv(
- // CHECK-NEXT: to label %{{.+}} unwind label %[[PromDtorPad:.+]]
+ // CHECK-NEXT: to label %{{.+}} unwind label %[[CatchPad:.+]]
- // CHECK: [[DeallocPad]]:
- // CHECK-NEXT: landingpad
- // CHECK-NEXT: cleanup
- // CHECK: br label %[[Dealloc:.+]]
+ // CHECK: [[CatchPad]]:
+ // CHECK-NEXT: landingpad
+ // CHECK-NEXT: catch i8* null
+ // CHECK: call void @_ZN7CleanupD1Ev(
+ // CHECK: br label %[[Catch:.+]]
- // CHECK: [[PromDtorPad]]:
- // CHECK-NEXT: landingpad
- // CHECK-NEXT: cleanup
- // CHECK: call void @_ZN7CleanupD1Ev(%struct.Cleanup*
+ // CHECK: [[Catch]]:
+ // CHECK: call i8* @__cxa_begin_catch(
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type19unhandled_exceptionEv(
+ // CHECK: invoke void @__cxa_end_catch()
+ // CHECK-NEXT: to label %[[Cont:.+]] unwind
+
+ // CHECK: [[Cont]]:
+ // CHECK-NEXT: br label %[[Cont2:.+]]
+ // CHECK: [[Cont2]]:
+ // CHECK-NEXT: br label %[[Cleanup:.+]]
+
+ // CHECK: [[Cleanup]]:
// CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_typeD1Ev(
- // CHECK: br label %[[Dealloc]]
+ // CHECK: %[[Mem0:.+]] = call i8* @llvm.coro.free(
+ // CHECK: call void @_ZdlPv(i8* %[[Mem0]]
// CHECK: [[Dealloc]]:
// CHECK: %[[Mem:.+]] = call i8* @llvm.coro.free(
@@ -72,3 +87,11 @@ void f() {
co_return;
}
+
+// CHECK-LABEL: define void @_Z1gv(
+void g() {
+ for (;;)
+ co_await suspend_always{};
+ // Since this is the endless loop there should be no fallthrough handler (call to 'return_void').
+ // CHECK-NOT: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type11return_voidEv
+}
diff --git a/test/CodeGenCoroutines/coro-eh-cleanup.cpp b/test/CodeGenCoroutines/coro-eh-cleanup.cpp
index 578d57fbab28a..5a3ecd1da4949 100644
--- a/test/CodeGenCoroutines/coro-eh-cleanup.cpp
+++ b/test/CodeGenCoroutines/coro-eh-cleanup.cpp
@@ -51,7 +51,13 @@ coro_t f() {
// CHECK: [[EHCLEANUP]]:
// CHECK: %[[INNERPAD:.+]] = cleanuppad within none []
// CHECK: call void @"\01??_DCleanup@@QEAAXXZ"(
-// CHECK: cleanupret from %[[INNERPAD]] unwind label %[[COROENDBB:.+]]
+// CHECK: cleanupret from %{{.+}} unwind label %[[CATCHDISPATCH:.+]]
+
+// CHECK: [[CATCHDISPATCH]]:
+// CHECK: catchswitch within none [label %[[CATCHPAD:.+]]] unwind label %[[COROENDBB:.+]]
+// CHECK: [[CATCHPAD]]:
+// CHECK: call void @"\01?unhandled_exception@promise_type@coro_t@@QEAAXXZ"
+
// CHECK: [[COROENDBB]]:
// CHECK-NEXT: %[[CLPAD:.+]] = cleanuppad within none
// CHECK-NEXT: call i1 @llvm.coro.end(i8* null, i1 true) [ "funclet"(token %[[CLPAD]]) ]
@@ -62,8 +68,14 @@ coro_t f() {
// CHECK-LPAD: to label %[[CONT:.+]] unwind label %[[EHCLEANUP:.+]]
// CHECK-LPAD: [[EHCLEANUP]]:
// CHECK-LPAD: landingpad { i8*, i32 }
-// CHECK-LPAD: cleanup
+// CHECK-LPAD: catch
// CHECK-LPAD: call void @_ZN7CleanupD1Ev(
+// CHECK-LPAD: call i8* @__cxa_begin_catch
+// CHECK-LPAD: call void @_ZN6coro_t12promise_type19unhandled_exceptionEv
+// CHECK-LPAD: invoke void @__cxa_end_catch()
+// CHECK-LPAD: to label %{{.+}} unwind label %[[UNWINDBB:.+]]
+
+// CHECK-LPAD: [[UNWINDBB]]:
// CHECK-LPAD: %[[I1RESUME:.+]] = call i1 @llvm.coro.end(i8* null, i1 true)
// CHECK-LPAD: br i1 %[[I1RESUME]], label %[[EHRESUME:.+]], label
// CHECK-LPAD: [[EHRESUME]]:
diff --git a/test/CodeGenCoroutines/coro-gro.cpp b/test/CodeGenCoroutines/coro-gro.cpp
new file mode 100644
index 0000000000000..95bf2069722a2
--- /dev/null
+++ b/test/CodeGenCoroutines/coro-gro.cpp
@@ -0,0 +1,86 @@
+// Verifies lifetime of __gro local variable
+// Verify that coroutine promise and allocated memory are freed up on exception.
+// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -disable-llvm-passes | FileCheck %s
+
+namespace std::experimental {
+template <typename... T> struct coroutine_traits;
+
+template <class Promise = void> struct coroutine_handle {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+};
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) noexcept;
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+};
+}
+
+struct suspend_always {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+struct GroType {
+ ~GroType();
+ operator int() noexcept;
+};
+
+template <> struct std::experimental::coroutine_traits<int> {
+ struct promise_type {
+ GroType get_return_object() noexcept;
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void return_void() noexcept;
+ promise_type();
+ ~promise_type();
+ void unhandled_exception() noexcept;
+ };
+};
+
+struct Cleanup { ~Cleanup(); };
+void doSomething() noexcept;
+
+// CHECK: define i32 @_Z1fv(
+int f() {
+ // CHECK: %[[RetVal:.+]] = alloca i32
+ // CHECK: %[[GroActive:.+]] = alloca i1
+
+ // CHECK: %[[Size:.+]] = call i64 @llvm.coro.size.i64()
+ // CHECK: call i8* @_Znwm(i64 %[[Size]])
+ // CHECK: store i1 false, i1* %[[GroActive]]
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_typeC1Ev(
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type17get_return_objectEv(
+ // CHECK: store i1 true, i1* %[[GroActive]]
+
+ Cleanup cleanup;
+ doSomething();
+ co_return;
+
+ // CHECK: call void @_Z11doSomethingv(
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type11return_voidEv(
+ // CHECK: call void @_ZN7CleanupD1Ev(
+
+ // Destroy promise and free the memory.
+
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_typeD1Ev(
+ // CHECK: %[[Mem:.+]] = call i8* @llvm.coro.free(
+ // CHECK: call void @_ZdlPv(i8* %[[Mem]])
+
+ // Initialize retval from Gro and destroy Gro
+
+ // CHECK: %[[Conv:.+]] = call i32 @_ZN7GroTypecviEv(
+ // CHECK: store i32 %[[Conv]], i32* %[[RetVal]]
+ // CHECK: %[[IsActive:.+]] = load i1, i1* %[[GroActive]]
+ // CHECK: br i1 %[[IsActive]], label %[[CleanupGro:.+]], label %[[Done:.+]]
+
+ // CHECK: [[CleanupGro]]:
+ // CHECK: call void @_ZN7GroTypeD1Ev(
+ // CHECK: br label %[[Done]]
+
+ // CHECK: [[Done]]:
+ // CHECK: %[[LoadRet:.+]] = load i32, i32* %[[RetVal]]
+ // CHECK: ret i32 %[[LoadRet]]
+}
diff --git a/test/CodeGenCoroutines/coro-lambda.cpp b/test/CodeGenCoroutines/coro-lambda.cpp
new file mode 100644
index 0000000000000..cd3256dc07eff
--- /dev/null
+++ b/test/CodeGenCoroutines/coro-lambda.cpp
@@ -0,0 +1,58 @@
+// Verify that we synthesized the coroutine for a lambda inside of a function template.
+// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
+
+namespace std::experimental {
+template <typename R, typename... T> struct coroutine_traits {
+ using promise_type = typename R::promise_type;
+};
+
+template <class Promise = void> struct coroutine_handle;
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) noexcept;
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+};
+template <class Promise> struct coroutine_handle : coroutine_handle<void> {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+};
+}
+
+struct suspend_always {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+struct Task {
+ struct promise_type {
+ Task get_return_object();
+ void return_void() {}
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void unhandled_exception() noexcept;
+ };
+};
+
+template <typename _AwrT> auto SyncAwait(_AwrT &&A) {
+ if (!A.await_ready()) {
+ auto AwaitAsync = [&]() -> Task {
+ try { (void)(co_await A); } catch (...) {}
+ };
+ Task t = AwaitAsync();
+ }
+ return A.await_resume();
+}
+
+void f() {
+ suspend_always test;
+ SyncAwait(test);
+}
+
+// Verify that we synthesized the coroutine for a lambda inside SyncAwait
+// CHECK-LABEL: define linkonce_odr void @_ZZ9SyncAwaitIR14suspend_alwaysEDaOT_ENKUlvE_clEv(
+// CHECK: alloca %"struct.Task::promise_type"
+// CHECK: call token @llvm.coro.id(
+// CHECK: call i8 @llvm.coro.suspend(
+// CHECK: call i1 @llvm.coro.end(
diff --git a/test/CodeGenCoroutines/coro-params.cpp b/test/CodeGenCoroutines/coro-params.cpp
new file mode 100644
index 0000000000000..c88e503a651c0
--- /dev/null
+++ b/test/CodeGenCoroutines/coro-params.cpp
@@ -0,0 +1,95 @@
+// Verifies that parameters are copied with move constructors
+// Verifies that parameter copies are destroyed
+// Vefifies that parameter copies are used in the body of the coroutine
+// RUN: %clang_cc1 -std=c++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -disable-llvm-passes -fexceptions | FileCheck %s
+
+namespace std::experimental {
+template <typename... T> struct coroutine_traits;
+
+template <class Promise = void> struct coroutine_handle {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+};
+template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) noexcept;
+ coroutine_handle() = default;
+ template <class PromiseType>
+ coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+};
+}
+
+struct suspend_always {
+ bool await_ready() noexcept;
+ void await_suspend(std::experimental::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+template <typename... Args> struct std::experimental::coroutine_traits<void, Args...> {
+ struct promise_type {
+ void get_return_object() noexcept;
+ suspend_always initial_suspend() noexcept;
+ suspend_always final_suspend() noexcept;
+ void return_void() noexcept;
+ promise_type();
+ ~promise_type() noexcept;
+ void unhandled_exception() noexcept;
+ };
+};
+
+// TODO: Not supported yet
+struct CopyOnly {
+ int val;
+ CopyOnly(const CopyOnly&) noexcept;
+ CopyOnly(CopyOnly&&) = delete;
+ ~CopyOnly();
+};
+
+struct MoveOnly {
+ int val;
+ MoveOnly(const MoveOnly&) = delete;
+ MoveOnly(MoveOnly&&) noexcept;
+ ~MoveOnly();
+};
+
+struct MoveAndCopy {
+ int val;
+ MoveAndCopy(const MoveAndCopy&)noexcept;
+ MoveAndCopy(MoveAndCopy&&) noexcept;
+ ~MoveAndCopy();
+};
+
+void consume(int,int,int) noexcept;
+
+// TODO: Add support for CopyOnly params
+// CHECK: define void @_Z1fi8MoveOnly11MoveAndCopy(i32 %val, %struct.MoveOnly* %[[MoParam:.+]], %struct.MoveAndCopy* %[[McParam:.+]]) #0 personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*
+void f(int val, MoveOnly moParam, MoveAndCopy mcParam) {
+ // CHECK: %[[MoCopy:.+]] = alloca %struct.MoveOnly
+ // CHECK: %[[McCopy:.+]] = alloca %struct.MoveAndCopy
+ // CHECK: store i32 %val, i32* %[[ValAddr:.+]]
+
+ // CHECK: call i8* @llvm.coro.begin(
+ // CHECK-NEXT: call void @_ZN8MoveOnlyC1EOS_(%struct.MoveOnly* %[[MoCopy]], %struct.MoveOnly* dereferenceable(4) %[[MoParam]])
+ // CHECK-NEXT: call void @_ZN11MoveAndCopyC1EOS_(%struct.MoveAndCopy* %[[McCopy]], %struct.MoveAndCopy* dereferenceable(4) %[[McParam]]) #
+ // CHECK-NEXT: invoke void @_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeC1Ev(
+
+ // CHECK: call void @_ZN14suspend_always12await_resumeEv(
+ // CHECK: %[[IntParam:.+]] = load i32, i32* %val.addr
+ // CHECK: %[[MoGep:.+]] = getelementptr inbounds %struct.MoveOnly, %struct.MoveOnly* %[[MoCopy]], i32 0, i32 0
+ // CHECK: %[[MoVal:.+]] = load i32, i32* %[[MoGep]]
+ // CHECK: %[[McGep:.+]] = getelementptr inbounds %struct.MoveAndCopy, %struct.MoveAndCopy* %[[McCopy]], i32 0, i32 0
+ // CHECK: %[[McVal:.+]] = load i32, i32* %[[McGep]]
+ // CHECK: call void @_Z7consumeiii(i32 %[[IntParam]], i32 %[[MoVal]], i32 %[[McVal]])
+
+ consume(val, moParam.val, mcParam.val);
+ co_return;
+
+ // Skip to final suspend:
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_type13final_suspendEv(
+ // CHECK: call void @_ZN14suspend_always12await_resumeEv(
+
+ // Destroy promise, then parameter copies:
+ // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvi8MoveOnly11MoveAndCopyEE12promise_typeD1Ev(%"struct.std::experimental::coroutine_traits<void, int, MoveOnly, MoveAndCopy>::promise_type"* %__promise) #2
+ // CHECK-NEXT: call void @_ZN11MoveAndCopyD1Ev(%struct.MoveAndCopy* %[[McCopy]])
+ // CHECK-NEXT: call void @_ZN8MoveOnlyD1Ev(%struct.MoveOnly* %[[MoCopy]]
+ // CHECK-NEXT: call i8* @llvm.coro.free(
+}
diff --git a/test/CodeGenCoroutines/coro-promise-dtor.cpp b/test/CodeGenCoroutines/coro-promise-dtor.cpp
new file mode 100644
index 0000000000000..4142cebe6132f
--- /dev/null
+++ b/test/CodeGenCoroutines/coro-promise-dtor.cpp
@@ -0,0 +1,47 @@
+// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -triple=x86_64-pc-windows-msvc18.0.0 -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck %s
+// -triple=x86_64-unknown-linux-gnu
+
+#include "Inputs/coroutine.h"
+
+namespace coro = std::experimental::coroutines_v1;
+
+struct coro_t {
+ void* p;
+ ~coro_t();
+ struct promise_type {
+ coro_t get_return_object();
+ coro::suspend_never initial_suspend();
+ coro::suspend_never final_suspend();
+ void return_void();
+ promise_type();
+ ~promise_type();
+ void unhandled_exception();
+ };
+};
+
+struct Cleanup { ~Cleanup(); };
+void may_throw();
+
+coro_t f() {
+ Cleanup cleanup;
+ may_throw();
+ co_return;
+}
+
+// CHECK-LABEL: define void @"\01?f@@YA?AUcoro_t@@XZ"(
+// CHECK: %gro.active = alloca i1
+// CHECK: store i1 false, i1* %gro.active
+
+// CHECK: invoke %"struct.coro_t::promise_type"* @"\01??0promise_type@coro_t@@QEAA@XZ"(
+// CHECK: invoke void @"\01?get_return_object@promise_type@coro_t@@QEAA?AU2@XZ"(
+// CHECK: store i1 true, i1* %gro.active
+
+// CHECK: %[[IS_ACTIVE:.+]] = load i1, i1* %gro.active
+// CHECK: br i1 %[[IS_ACTIVE]], label %[[CLEANUP1:.+]], label
+
+// CHECK: [[CLEANUP1]]:
+// CHECK: %[[NRVO:.+]] = load i1, i1* %nrvo
+// CHECK: br i1 %[[NRVO]], label %{{.+}}, label %[[DTOR:.+]]
+
+// CHECK: [[DTOR]]:
+// CHECK: call void @"\01??_Dcoro_t@@QEAAXXZ"(
diff --git a/test/CodeGenCoroutines/coro-ret-void.cpp b/test/CodeGenCoroutines/coro-ret-void.cpp
new file mode 100644
index 0000000000000..6b07f6ea1d642
--- /dev/null
+++ b/test/CodeGenCoroutines/coro-ret-void.cpp
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -std=c++14 -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s
+
+#include "Inputs/coroutine.h"
+
+namespace coro = std::experimental::coroutines_v1;
+
+struct coro1 {
+ struct promise_type {
+ coro1 get_return_object();
+ coro::suspend_never initial_suspend();
+ coro::suspend_never final_suspend();
+ void return_void();
+ };
+};
+
+coro1 f() {
+ co_await coro::suspend_never{};
+}
+
+// CHECK-LABEL: define void @_Z1fv(
+// CHECK: call void @_ZNSt12experimental13coroutines_v113suspend_never12await_resumeEv(%"struct.std::experimental::coroutines_v1::suspend_never"*
+// CHECK: call void @_ZN5coro112promise_type11return_voidEv(%"struct.coro1::promise_type"* %__promise)
+
+struct coro2 {
+ struct promise_type {
+ coro2 get_return_object();
+ coro::suspend_never initial_suspend();
+ coro::suspend_never final_suspend();
+ void return_value(int);
+ };
+};
+
+coro2 g() {
+ co_return 42;
+}
+
+// CHECK-LABEL: define void @_Z1gv(
+// CHECK: call void @_ZNSt12experimental13coroutines_v113suspend_never12await_resumeEv(%"struct.std::experimental::coroutines_v1::suspend_never"*
+// CHECK: call void @_ZN5coro212promise_type12return_valueEi(%"struct.coro2::promise_type"* %__promise, i32 42)
diff --git a/test/CodeGenCoroutines/coro-unhandled-exception.cpp b/test/CodeGenCoroutines/coro-unhandled-exception.cpp
new file mode 100644
index 0000000000000..e26a51861d9f2
--- /dev/null
+++ b/test/CodeGenCoroutines/coro-unhandled-exception.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-LPAD %s
+
+#include "Inputs/coroutine.h"
+
+namespace coro = std::experimental::coroutines_v1;
+
+namespace std {
+ using exception_ptr = int;
+ exception_ptr current_exception();
+}
+
+struct coro_t {
+ struct promise_type {
+ coro_t get_return_object() {
+ coro::coroutine_handle<promise_type>{};
+ return {};
+ }
+ coro::suspend_never initial_suspend() { return {}; }
+ coro::suspend_never final_suspend() { return {}; }
+ void return_void(){}
+ void unhandled_exception() noexcept;
+ };
+};
+
+struct Cleanup { ~Cleanup(); };
+void may_throw();
+
+coro_t f() {
+ Cleanup x;
+ may_throw();
+ co_return;
+}
+
+// CHECK: @"\01?f@@YA?AUcoro_t@@XZ"(
+// CHECK: invoke void @"\01?may_throw@@YAXXZ"()
+// CHECK: to label %{{.+}} unwind label %[[EHCLEANUP:.+]]
+// CHECK: [[EHCLEANUP]]:
+// CHECK: %[[INNERPAD:.+]] = cleanuppad within none []
+// CHECK: call void @"\01??_DCleanup@@QEAAXXZ"(
+// CHECK: cleanupret from %[[INNERPAD]] unwind label %[[CATCHSW:.+]]
+// CHECK: [[CATCHSW]]:
+// CHECK: %[[CATCHSWTOK:.+]] = catchswitch within none [label %[[CATCH:.+]]] unwind label
+// CHECK: [[CATCH]]:
+// CHECK: %[[CATCHTOK:.+]] = catchpad within [[CATCHSWTOK:.+]]
+// CHECK: call void @"\01?unhandled_exception@promise_type@coro_t@@QEAAXXZ"
+// CHECK: catchret from %[[CATCHTOK]] to label %[[CATCHRETDEST:.+]]
+// CHECK: [[CATCHRETDEST]]:
+// CHECK-NEXT: br label %[[TRYCONT:.+]]
+// CHECK: [[TRYCONT]]:
+// CHECK-NEXT: br label %[[COROFIN:.+]]
+// CHECK: [[COROFIN]]:
+// CHECK-NEXT: invoke void @"\01?final_suspend@promise_type@coro_t@@QEAA?AUsuspend_never@coroutines_v1@experimental@std@@XZ"(
+
+// CHECK-LPAD: @_Z1fv(
+// CHECK-LPAD: invoke void @_Z9may_throwv()
+// CHECK-LPAD: to label %[[CONT:.+]] unwind label %[[CLEANUP:.+]]
+// CHECK-LPAD: [[CLEANUP]]:
+// CHECK-LPAD: call void @_ZN7CleanupD1Ev(%struct.Cleanup* %x) #2
+// CHECK-LPAD: br label %[[CATCH:.+]]
+
+// CHECK-LPAD: [[CATCH]]:
+// CHECK-LPAD: call i8* @__cxa_begin_catch
+// CHECK-LPAD: call void @_ZN6coro_t12promise_type19unhandled_exceptionEv(%"struct.coro_t::promise_type"* %__promise) #2
+// CHECK-LPAD: invoke void @__cxa_end_catch()
+// CHECK-LPAD-NEXT: to label %[[CATCHRETDEST:.+]] unwind label
+// CHECK-LPAD: [[CATCHRETDEST]]:
+// CHECK-LPAD-NEXT: br label %[[TRYCONT:.+]]
+// CHECK-LPAD: [[TRYCONT]]:
+// CHECK-LPAD-NEXT: br label %[[COROFIN:.+]]
+// CHECK-LPAD: [[COROFIN]]:
+// CHECK-LPAD-NEXT: invoke void @_ZN6coro_t12promise_type13final_suspendEv(