1 // An end-to-end test to make sure things get processed correctly. 2 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm -o - %s -O3 | \ 3 // RUN: FileCheck %s 4 5 #include "Inputs/coroutine.h" 6 7 struct SomeAwaitable { 8 // Resume the supplied handle once the awaitable becomes ready, 9 // returning a handle that should be resumed now for the sake of symmetric transfer. 10 // If the awaitable is already ready, return an empty handle without doing anything. 11 // 12 // Defined in another translation unit. Note that this may contain 13 // code that synchronizees with another thread. 14 std::coroutine_handle<> Register(std::coroutine_handle<>); 15 }; 16 17 // Defined in another translation unit. 18 void DidntSuspend(); 19 20 struct Awaiter { 21 SomeAwaitable&& awaitable; 22 bool suspended; 23 await_readyAwaiter24 bool await_ready() { return false; } 25 await_suspendAwaiter26 std::coroutine_handle<> await_suspend(const std::coroutine_handle<> h) { 27 // Assume we will suspend unless proven otherwise below. We must do 28 // this *before* calling Register, since we may be destroyed by another 29 // thread asynchronously as soon as we have registered. 30 suspended = true; 31 32 // Attempt to hand off responsibility for resuming/destroying the coroutine. 33 const auto to_resume = awaitable.Register(h); 34 35 if (!to_resume) { 36 // The awaitable is already ready. In this case we know that Register didn't 37 // hand off responsibility for the coroutine. So record the fact that we didn't 38 // actually suspend, and tell the compiler to resume us inline. 39 suspended = false; 40 return h; 41 } 42 43 // Resume whatever Register wants us to resume. 44 return to_resume; 45 } 46 await_resumeAwaiter47 void await_resume() { 48 // If we didn't suspend, make note of that fact. 49 if (!suspended) { 50 DidntSuspend(); 51 } 52 } 53 }; 54 55 struct MyTask{ 56 struct promise_type { get_return_objectMyTask::promise_type57 MyTask get_return_object() { return {}; } initial_suspendMyTask::promise_type58 std::suspend_never initial_suspend() { return {}; } final_suspendMyTask::promise_type59 std::suspend_always final_suspend() noexcept { return {}; } 60 void unhandled_exception(); 61 await_transformMyTask::promise_type62 Awaiter await_transform(SomeAwaitable&& awaitable) { 63 return Awaiter{static_cast<SomeAwaitable&&>(awaitable)}; 64 } 65 }; 66 }; 67 FooBar()68MyTask FooBar() { 69 co_await SomeAwaitable(); 70 } 71 72 // CHECK-LABEL: @_Z6FooBarv 73 // CHECK: %[[to_resume:.*]] = {{.*}}call ptr @_ZN13SomeAwaitable8RegisterESt16coroutine_handleIvE 74 // CHECK-NEXT: %[[to_bool:.*]] = icmp eq ptr %[[to_resume]], null 75 // CHECK-NEXT: br i1 %[[to_bool]], label %[[then:.*]], label %[[else:.*]] 76 77 // CHECK: [[then]]: 78 // We only access the coroutine frame conditionally as the sources did. 79 // CHECK: store i8 0, 80 // CHECK-NEXT: br label %[[else]] 81 82 // CHECK: [[else]]: 83 // No more access to the coroutine frame until suspended. 84 // CHECK-NOT: store 85 // CHECK: } 86