diff options
Diffstat (limited to 'test/CodeGenCoroutines')
-rw-r--r-- | test/CodeGenCoroutines/coro-alloc.cpp | 60 | ||||
-rw-r--r-- | test/CodeGenCoroutines/coro-await.cpp | 230 | ||||
-rw-r--r-- | test/CodeGenCoroutines/coro-builtins.c | 2 | ||||
-rw-r--r-- | test/CodeGenCoroutines/coro-cleanup.cpp | 74 | ||||
-rw-r--r-- | test/CodeGenCoroutines/coro-eh-cleanup.cpp | 74 | ||||
-rw-r--r-- | test/CodeGenCoroutines/coro-return.cpp | 59 |
6 files changed, 489 insertions, 10 deletions
diff --git a/test/CodeGenCoroutines/coro-alloc.cpp b/test/CodeGenCoroutines/coro-alloc.cpp index 7dbbdd31be085..f0a600eabe9a2 100644 --- a/test/CodeGenCoroutines/coro-alloc.cpp +++ b/test/CodeGenCoroutines/coro-alloc.cpp @@ -4,12 +4,27 @@ namespace std { namespace experimental { template <typename... T> struct coroutine_traits; // expected-note {{declared here}} + +template <class Promise = void> +struct coroutine_handle { + coroutine_handle() = default; + static coroutine_handle from_address(void *) { return {}; } +}; + +template <> +struct coroutine_handle<void> { + static coroutine_handle from_address(void *) { return {}; } + coroutine_handle() = default; + template <class PromiseType> + coroutine_handle(coroutine_handle<PromiseType>) {} +}; + } } struct suspend_always { bool await_ready() { return false; } - void await_suspend() {} + void await_suspend(std::experimental::coroutine_handle<>) {} void await_resume() {} }; @@ -25,7 +40,7 @@ struct std::experimental::coroutine_traits<void, global_new_delete_tag> { }; }; -// CHECK-LABEL: f0( +// CHECK-LABEL: f0( extern "C" void f0(global_new_delete_tag) { // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16 // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() @@ -34,7 +49,7 @@ extern "C" void f0(global_new_delete_tag) { // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame() // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]]) // CHECK: call void @_ZdlPv(i8* %[[MEM]]) - co_await suspend_always{}; + co_return; } struct promise_new_tag {}; @@ -50,7 +65,7 @@ struct std::experimental::coroutine_traits<void, promise_new_tag> { }; }; -// CHECK-LABEL: f1( +// CHECK-LABEL: f1( extern "C" void f1(promise_new_tag ) { // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16 // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() @@ -59,7 +74,7 @@ extern "C" void f1(promise_new_tag ) { // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame() // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]]) // CHECK: call void @_ZdlPv(i8* %[[MEM]]) - co_await suspend_always{}; + co_return; } struct promise_delete_tag {}; @@ -75,7 +90,7 @@ struct std::experimental::coroutine_traits<void, promise_delete_tag> { }; }; -// CHECK-LABEL: f2( +// CHECK-LABEL: f2( extern "C" void f2(promise_delete_tag) { // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16 // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() @@ -84,7 +99,7 @@ extern "C" void f2(promise_delete_tag) { // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.frame() // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]]) // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJv18promise_delete_tagEE12promise_typedlEPv(i8* %[[MEM]]) - co_await suspend_always{}; + co_return; } struct promise_sized_delete_tag {}; @@ -100,7 +115,7 @@ struct std::experimental::coroutine_traits<void, promise_sized_delete_tag> { }; }; -// CHECK-LABEL: f3( +// CHECK-LABEL: f3( extern "C" void f3(promise_sized_delete_tag) { // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16 // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() @@ -110,5 +125,32 @@ extern "C" void f3(promise_sized_delete_tag) { // 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]]) - co_await suspend_always{}; + co_return; +} + +struct promise_on_alloc_failure_tag {}; + +template<> +struct std::experimental::coroutine_traits<int, promise_on_alloc_failure_tag> { + struct promise_type { + int get_return_object() {} + suspend_always initial_suspend() { return {}; } + suspend_always final_suspend() { return {}; } + void return_void() {} + static int get_return_object_on_allocation_failure() { return -1; } + }; +}; + +// CHECK-LABEL: f4( +extern "C" int f4(promise_on_alloc_failure_tag) { + // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16 + // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() + // CHECK: %[[MEM:.+]] = call i8* @_Znwm(i64 %[[SIZE]]) + // CHECK: %[[OK:.+]] = icmp ne i8* %[[MEM]], null + // 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]] + co_return; } diff --git a/test/CodeGenCoroutines/coro-await.cpp b/test/CodeGenCoroutines/coro-await.cpp new file mode 100644 index 0000000000000..b0fd0129de34a --- /dev/null +++ b/test/CodeGenCoroutines/coro-await.cpp @@ -0,0 +1,230 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 -emit-llvm %s -o - -disable-llvm-passes | FileCheck %s + +namespace std { +namespace experimental { +template <typename... T> +struct coroutine_traits; + +template <typename Promise = void> struct coroutine_handle; + +template <> +struct coroutine_handle<void> { + void *ptr; + static coroutine_handle from_address(void *); +}; + +template <typename Promise> +struct coroutine_handle : coroutine_handle<> { + static coroutine_handle from_address(void *); +}; + +} +} + +struct suspend_always { + int stuff; + bool await_ready(); + void await_suspend(std::experimental::coroutine_handle<>); + void await_resume(); +}; + +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(); + }; +}; + +// CHECK-LABEL: f0( +extern "C" void f0() { + + co_await suspend_always{}; + // See if we need to suspend: + // -------------------------- + // CHECK: %[[READY:.+]] = call zeroext i1 @_ZN14suspend_always11await_readyEv(%struct.suspend_always* %[[AWAITABLE:.+]]) + // CHECK: br i1 %[[READY]], label %[[READY_BB:.+]], label %[[SUSPEND_BB:.+]] + + // If we are suspending: + // --------------------- + // CHECK: [[SUSPEND_BB]]: + // CHECK: %[[SUSPEND_ID:.+]] = call token @llvm.coro.save( + // --------------------------- + // 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** %{{.+}} + // CHECK: call void @_ZN14suspend_always13await_suspendENSt12experimental16coroutine_handleIvEE(%struct.suspend_always* %[[AWAITABLE]], i8* %[[CH]]) + // ------------------------- + // Generate a suspend point: + // ------------------------- + // CHECK: %[[OUTCOME:.+]] = call i8 @llvm.coro.suspend(token %[[SUSPEND_ID]], i1 false) + // CHECK: switch i8 %[[OUTCOME]], label %[[RET_BB:.+]] [ + // CHECK: i8 0, label %[[READY_BB]] + // CHECK: i8 1, label %[[CLEANUP_BB:.+]] + // CHECK: ] + + // Cleanup code goes here: + // ----------------------- + // CHECK: [[CLEANUP_BB]]: + + // When coroutine is resumed, call await_resume + // -------------------------- + // CHECK: [[READY_BB]]: + // CHECK: call void @_ZN14suspend_always12await_resumeEv(%struct.suspend_always* %[[AWAITABLE]]) +} + +struct suspend_maybe { + float stuff; + ~suspend_maybe(); + bool await_ready(); + bool await_suspend(std::experimental::coroutine_handle<>); + void await_resume(); +}; + + +template<> +struct std::experimental::coroutine_traits<void,int> { + struct promise_type { + void get_return_object(); + suspend_always initial_suspend(); + suspend_always final_suspend(); + void return_void(); + suspend_maybe yield_value(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: 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: + // -------------------------- + // CHECK: %[[READY:.+]] = call zeroext i1 @_ZN13suspend_maybe11await_readyEv(%struct.suspend_maybe* %[[AWAITABLE]]) + // CHECK: br i1 %[[READY]], label %[[READY_BB:.+]], label %[[SUSPEND_BB:.+]] + + // If we are suspending: + // --------------------- + // CHECK: [[SUSPEND_BB]]: + // CHECK: %[[SUSPEND_ID:.+]] = call token @llvm.coro.save( + // --------------------------- + // 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** %{{.+}} + // CHECK: %[[YES:.+]] = call zeroext i1 @_ZN13suspend_maybe13await_suspendENSt12experimental16coroutine_handleIvEE(%struct.suspend_maybe* %[[AWAITABLE]], i8* %[[CH]]) + // ------------------------------------------- + // See if await_suspend decided not to suspend + // ------------------------------------------- + // CHECK: br i1 %[[YES]], label %[[SUSPEND_PLEASE:.+]], label %[[READY_BB]] + + // CHECK: [[SUSPEND_PLEASE]]: + // CHECK: call i8 @llvm.coro.suspend(token %[[SUSPEND_ID]], i1 false) + + // CHECK: [[READY_BB]]: + // CHECK: call void @_ZN13suspend_maybe12await_resumeEv(%struct.suspend_maybe* %[[AWAITABLE]]) +} + +struct ComplexAwaiter { + template <typename F> void await_suspend(F); + bool await_ready(); + _Complex float await_resume(); +}; +extern "C" void UseComplex(_Complex float); + +// CHECK-LABEL: @TestComplex( +extern "C" void TestComplex() { + UseComplex(co_await ComplexAwaiter{}); + // CHECK: call <2 x float> @_ZN14ComplexAwaiter12await_resumeEv(%struct.ComplexAwaiter* + // CHECK: call void @UseComplex(<2 x float> %{{.+}}) + + co_await ComplexAwaiter{}; + // CHECK: call <2 x float> @_ZN14ComplexAwaiter12await_resumeEv(%struct.ComplexAwaiter* + + _Complex float Val = co_await ComplexAwaiter{}; + // CHECK: call <2 x float> @_ZN14ComplexAwaiter12await_resumeEv(%struct.ComplexAwaiter* +} + +struct Aggr { int X, Y, Z; ~Aggr(); }; +struct AggrAwaiter { + template <typename F> void await_suspend(F); + bool await_ready(); + Aggr await_resume(); +}; + +extern "C" void Whatever(); +extern "C" void UseAggr(Aggr&&); + +// FIXME: Once the cleanup code is in, add testing that destructors for Aggr +// are invoked properly on the cleanup branches. + +// CHECK-LABEL: @TestAggr( +extern "C" void TestAggr() { + UseAggr(co_await AggrAwaiter{}); + Whatever(); + // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret %[[AwaitResume:.+]], + // CHECK: call void @UseAggr(%struct.Aggr* dereferenceable(12) %[[AwaitResume]]) + // CHECK: call void @_ZN4AggrD1Ev(%struct.Aggr* %[[AwaitResume]]) + // CHECK: call void @Whatever() + + co_await AggrAwaiter{}; + Whatever(); + // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret %[[AwaitResume2:.+]], + // CHECK: call void @_ZN4AggrD1Ev(%struct.Aggr* %[[AwaitResume2]]) + // CHECK: call void @Whatever() + + Aggr Val = co_await AggrAwaiter{}; + Whatever(); + // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret %[[AwaitResume3:.+]], + // CHECK: call void @Whatever() + // CHECK: call void @_ZN4AggrD1Ev(%struct.Aggr* %[[AwaitResume3]]) +} + +struct ScalarAwaiter { + template <typename F> void await_suspend(F); + bool await_ready(); + int await_resume(); +}; + +extern "C" void UseScalar(int); + +// CHECK-LABEL: @TestScalar( +extern "C" void TestScalar() { + UseScalar(co_await ScalarAwaiter{}); + // CHECK: %[[Result:.+]] = call i32 @_ZN13ScalarAwaiter12await_resumeEv(%struct.ScalarAwaiter* + // CHECK: call void @UseScalar(i32 %[[Result]]) + + int Val = co_await ScalarAwaiter{}; + // CHECK: %[[Result2:.+]] = call i32 @_ZN13ScalarAwaiter12await_resumeEv(%struct.ScalarAwaiter* + // CHECK: store i32 %[[Result2]], i32* %Val + + co_await ScalarAwaiter{}; + // CHECK: call i32 @_ZN13ScalarAwaiter12await_resumeEv(%struct.ScalarAwaiter* +} + +// Test operator co_await codegen. +enum class MyInt: int {}; +ScalarAwaiter operator co_await(MyInt); + +struct MyAgg { + AggrAwaiter operator co_await(); +}; + +// CHECK-LABEL: @TestOpAwait( +extern "C" void TestOpAwait() { + co_await MyInt(42); + // CHECK: call void @_Zaw5MyInt(i32 42) + // CHECK: call i32 @_ZN13ScalarAwaiter12await_resumeEv(%struct.ScalarAwaiter* % + + co_await MyAgg{}; + // CHECK: call void @_ZN5MyAggawEv(%struct.MyAgg* % + // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret % +} diff --git a/test/CodeGenCoroutines/coro-builtins.c b/test/CodeGenCoroutines/coro-builtins.c index 5c3e6c3a6bdff..d18d5e730f911 100644 --- a/test/CodeGenCoroutines/coro-builtins.c +++ b/test/CodeGenCoroutines/coro-builtins.c @@ -43,7 +43,7 @@ void f(int n) { __builtin_coro_free(__builtin_coro_frame()); // CHECK-NEXT: %[[FRAME6:.+]] = call i8* @llvm.coro.frame() - // CHECK-NEXT: call void @llvm.coro.end(i8* %[[FRAME6]], i1 false) + // CHECK-NEXT: call i1 @llvm.coro.end(i8* %[[FRAME6]], 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 new file mode 100644 index 0000000000000..7f6f35cfe26c4 --- /dev/null +++ b/test/CodeGenCoroutines/coro-cleanup.cpp @@ -0,0 +1,74 @@ +// 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 -fexceptions -fcxx-exceptions -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 *) { return {}; } +}; +template <> struct coroutine_handle<void> { + static coroutine_handle from_address(void *) { return {}; } + coroutine_handle() = default; + template <class PromiseType> + coroutine_handle(coroutine_handle<PromiseType>) {} +}; +} + +struct suspend_always { + bool await_ready(); + void await_suspend(std::experimental::coroutine_handle<>); + void await_resume(); +}; + +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(); + promise_type(); + ~promise_type(); + void unhandled_exception(); + }; +}; + +struct Cleanup { ~Cleanup(); }; +void may_throw(); + +// CHECK: define void @_Z1fv( +void f() { + // CHECK: call i8* @_Znwm(i64 + + // If promise constructor throws, check that we free the memory. + + // CHECK: invoke void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_typeC1Ev( + // CHECK-NEXT: to label %{{.+}} unwind label %[[DeallocPad:.+]] + + 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: [[DeallocPad]]: + // CHECK-NEXT: landingpad + // CHECK-NEXT: cleanup + // CHECK: br label %[[Dealloc:.+]] + + // CHECK: [[PromDtorPad]]: + // CHECK-NEXT: landingpad + // CHECK-NEXT: cleanup + // CHECK: call void @_ZN7CleanupD1Ev(%struct.Cleanup* + // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_typeD1Ev( + // CHECK: br label %[[Dealloc]] + + // CHECK: [[Dealloc]]: + // CHECK: %[[Mem:.+]] = call i8* @llvm.coro.free( + // CHECK: call void @_ZdlPv(i8* %[[Mem]]) + + co_return; +} diff --git a/test/CodeGenCoroutines/coro-eh-cleanup.cpp b/test/CodeGenCoroutines/coro-eh-cleanup.cpp new file mode 100644 index 0000000000000..578d57fbab28a --- /dev/null +++ b/test/CodeGenCoroutines/coro-eh-cleanup.cpp @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -std=c++1z -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++1z -fcoroutines-ts -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s -fexceptions -fcxx-exceptions -disable-llvm-passes | FileCheck --check-prefix=CHECK-LPAD %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 coro_t { + struct promise_type { + coro_t get_return_object() noexcept; + suspend_always initial_suspend() noexcept; + suspend_always final_suspend() noexcept; + void return_void() noexcept; + 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 %[[CONT:.+]] unwind label %[[EHCLEANUP:.+]] +// CHECK: [[EHCLEANUP]]: +// CHECK: %[[INNERPAD:.+]] = cleanuppad within none [] +// CHECK: call void @"\01??_DCleanup@@QEAAXXZ"( +// CHECK: cleanupret from %[[INNERPAD]] unwind label %[[COROENDBB:.+]] +// CHECK: [[COROENDBB]]: +// CHECK-NEXT: %[[CLPAD:.+]] = cleanuppad within none +// CHECK-NEXT: call i1 @llvm.coro.end(i8* null, i1 true) [ "funclet"(token %[[CLPAD]]) ] +// CHECK-NEXT: cleanupret from %[[CLPAD]] unwind label + +// CHECK-LPAD: @_Z1fv( +// CHECK-LPAD: invoke void @_Z9may_throwv() +// CHECK-LPAD: to label %[[CONT:.+]] unwind label %[[EHCLEANUP:.+]] +// CHECK-LPAD: [[EHCLEANUP]]: +// CHECK-LPAD: landingpad { i8*, i32 } +// CHECK-LPAD: cleanup +// CHECK-LPAD: call void @_ZN7CleanupD1Ev( +// CHECK-LPAD: %[[I1RESUME:.+]] = call i1 @llvm.coro.end(i8* null, i1 true) +// CHECK-LPAD: br i1 %[[I1RESUME]], label %[[EHRESUME:.+]], label +// CHECK-LPAD: [[EHRESUME]]: +// CHECK-LPAD-NEXT: %[[exn:.+]] = load i8*, i8** %exn.slot, align 8 +// CHECK-LPAD-NEXT: %[[sel:.+]] = load i32, i32* %ehselector.slot, align 4 +// CHECK-LPAD-NEXT: %[[val1:.+]] = insertvalue { i8*, i32 } undef, i8* %[[exn]], 0 +// CHECK-LPAD-NEXT: %[[val2:.+]] = insertvalue { i8*, i32 } %[[val1]], i32 %[[sel]], 1 +// CHECK-LPAD-NEXT: resume { i8*, i32 } %[[val2]] diff --git a/test/CodeGenCoroutines/coro-return.cpp b/test/CodeGenCoroutines/coro-return.cpp new file mode 100644 index 0000000000000..7577725909d0f --- /dev/null +++ b/test/CodeGenCoroutines/coro-return.cpp @@ -0,0 +1,59 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++1z -emit-llvm %s -o - -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 *) { return {}; } +}; +template <> struct coroutine_handle<void> { + static coroutine_handle from_address(void *) { return {}; } + coroutine_handle() = default; + template <class PromiseType> + coroutine_handle(coroutine_handle<PromiseType>) {} +}; +} + +struct suspend_always { + bool await_ready(); + void await_suspend(std::experimental::coroutine_handle<>); + void await_resume(); +}; + +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(); + }; +}; + +// CHECK-LABEL: f0( +extern "C" void f0() { + // CHECK: %__promise = alloca %"struct.std::experimental::coroutine_traits<void>::promise_type" + // CHECK: %call = call i8* @_Znwm( + // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type11return_voidEv(%"struct.std::experimental::coroutine_traits<void>::promise_type"* %__promise) + // CHECK: call void @_ZdlPv + co_return; +} + +template<> +struct std::experimental::coroutine_traits<int> { + struct promise_type { + int get_return_object(); + suspend_always initial_suspend(); + suspend_always final_suspend(); + void return_value(int); + }; +}; + +// CHECK-LABEL: f1( +extern "C" int f1() { + // CHECK: %__promise = alloca %"struct.std::experimental::coroutine_traits<int>::promise_type" + // CHECK: %call = call i8* @_Znwm( + // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJiEE12promise_type12return_valueEi(%"struct.std::experimental::coroutine_traits<int>::promise_type"* %__promise, i32 42) + // CHECK: call void @_ZdlPv + co_return 42; +} |