123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 \
- // RUN: -emit-llvm %s -o - -disable-llvm-passes -Wno-coroutine -Wno-unused | 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 *);
- void *address();
- };
- template <typename Promise>
- struct coroutine_handle : coroutine_handle<> {
- static coroutine_handle from_address(void *);
- };
- }
- }
- 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();
- void await_suspend(std::experimental::coroutine_handle<>);
- void await_resume();
- };
- template<>
- struct std::experimental::coroutine_traits<void> {
- struct promise_type {
- void get_return_object();
- 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:
- // --------------------------
- // 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: 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]])
- // 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 {
- 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();
- init_susp initial_suspend();
- final_susp final_suspend();
- void return_void();
- suspend_maybe yield_value(int);
- };
- };
- // CHECK-LABEL: f1(
- extern "C" void f1(int) {
- // 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:
- // --------------------------
- // 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: 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 %
- }
- // 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;
- }
- struct RefTag { };
- struct AwaitResumeReturnsLValue {
- bool await_ready();
- void await_suspend(std::experimental::coroutine_handle<>);
- RefTag& await_resume();
- };
- template<>
- struct std::experimental::coroutine_traits<void,double> {
- struct promise_type {
- void get_return_object();
- init_susp initial_suspend();
- final_susp final_suspend();
- void return_void();
- AwaitResumeReturnsLValue yield_value(int);
- };
- };
- // Verifies that we don't crash when returning an lvalue from an await_resume()
- // expression.
- // CHECK-LABEL: define void @_Z18AwaitReturnsLValued(double %0)
- void AwaitReturnsLValue(double) {
- AwaitResumeReturnsLValue a;
- // CHECK: %[[AVAR:.+]] = alloca %struct.AwaitResumeReturnsLValue,
- // CHECK: %[[XVAR:.+]] = alloca %struct.RefTag*,
- // CHECK: %[[YVAR:.+]] = alloca %struct.RefTag*,
- // CHECK-NEXT: %[[TMP1:.+]] = alloca %struct.AwaitResumeReturnsLValue,
- // CHECK: %[[ZVAR:.+]] = alloca %struct.RefTag*,
- // CHECK-NEXT: %[[TMP2:.+]] = alloca %struct.AwaitResumeReturnsLValue,
- // CHECK: %[[RES1:.+]] = call dereferenceable({{.*}}) %struct.RefTag* @_ZN24AwaitResumeReturnsLValue12await_resumeEv(%struct.AwaitResumeReturnsLValue* %[[AVAR]])
- // CHECK-NEXT: store %struct.RefTag* %[[RES1]], %struct.RefTag** %[[XVAR]],
- RefTag& x = co_await a;
- // CHECK: %[[RES2:.+]] = call dereferenceable({{.*}}) %struct.RefTag* @_ZN24AwaitResumeReturnsLValue12await_resumeEv(%struct.AwaitResumeReturnsLValue* %[[TMP1]])
- // CHECK-NEXT: store %struct.RefTag* %[[RES2]], %struct.RefTag** %[[YVAR]],
- RefTag& y = co_await AwaitResumeReturnsLValue{};
- // CHECK: %[[RES3:.+]] = call dereferenceable({{.*}}) %struct.RefTag* @_ZN24AwaitResumeReturnsLValue12await_resumeEv(%struct.AwaitResumeReturnsLValue* %[[TMP2]])
- // CHECK-NEXT: store %struct.RefTag* %[[RES3]], %struct.RefTag** %[[ZVAR]],
- RefTag& z = co_yield 42;
- }
- struct TailCallAwait {
- bool await_ready();
- std::experimental::coroutine_handle<> await_suspend(std::experimental::coroutine_handle<>);
- void await_resume();
- };
- // CHECK-LABEL: @TestTailcall(
- extern "C" void TestTailcall() {
- co_await TailCallAwait{};
- // CHECK: %[[RESULT:.+]] = call i8* @_ZN13TailCallAwait13await_suspendENSt12experimental16coroutine_handleIvEE(%struct.TailCallAwait*
- // CHECK: %[[COERCE:.+]] = getelementptr inbounds %"struct.std::experimental::coroutine_handle", %"struct.std::experimental::coroutine_handle"* %[[TMP:.+]], i32 0, i32 0
- // CHECK: store i8* %[[RESULT]], i8** %[[COERCE]]
- // CHECK: %[[ADDR:.+]] = call i8* @_ZNSt12experimental16coroutine_handleIvE7addressEv(%"struct.std::experimental::coroutine_handle"* %[[TMP]])
- // CHECK: call void @llvm.coro.resume(i8* %[[ADDR]])
- }
|