Index: cfe/trunk/test/CodeGenCoroutines/Inputs/coroutine.h =================================================================== --- cfe/trunk/test/CodeGenCoroutines/Inputs/coroutine.h +++ cfe/trunk/test/CodeGenCoroutines/Inputs/coroutine.h @@ -0,0 +1,80 @@ +#pragma once + +namespace std { namespace experimental { inline namespace coroutines_v1 { + +template struct coroutine_traits { + using promise_type = typename R::promise_type; +}; + +template struct coroutine_handle; + +template <> struct coroutine_handle { + 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 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( + __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 + bool operator==(coroutine_handle<_PromiseT> const& _Left, + coroutine_handle<_PromiseT> const& _Right) noexcept + { + return _Left.address() == _Right.address(); + } + + template + 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() {} +}; + +}}} Index: cfe/trunk/test/CodeGenCoroutines/coro-promise-dtor.cpp =================================================================== --- cfe/trunk/test/CodeGenCoroutines/coro-promise-dtor.cpp +++ cfe/trunk/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"( Index: cfe/trunk/test/CodeGenCoroutines/coro-ret-void.cpp =================================================================== --- cfe/trunk/test/CodeGenCoroutines/coro-ret-void.cpp +++ cfe/trunk/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) Index: cfe/trunk/test/CodeGenCoroutines/coro-unhandled-exception.cpp =================================================================== --- cfe/trunk/test/CodeGenCoroutines/coro-unhandled-exception.cpp +++ cfe/trunk/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{}; + 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(