// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir // RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR namespace std { template struct remove_reference { typedef T type; }; template struct remove_reference { typedef T type; }; template struct remove_reference { typedef T type; }; template typename remove_reference::type &&move(T &&t) noexcept; template struct coroutine_traits { using promise_type = typename Ret::promise_type; }; template struct coroutine_handle { static coroutine_handle from_address(void *) noexcept; }; template <> struct coroutine_handle { template coroutine_handle(coroutine_handle) noexcept; static coroutine_handle from_address(void *); }; struct suspend_always { bool await_ready() noexcept { return false; } void await_suspend(coroutine_handle<>) noexcept {} void await_resume() noexcept {} }; struct suspend_never { bool await_ready() noexcept { return true; } void await_suspend(coroutine_handle<>) noexcept {} void await_resume() noexcept {} }; struct string { int size() const; string(); string(char const *s); }; } // namespace std namespace folly { namespace coro { using std::suspend_always; using std::suspend_never; using std::coroutine_handle; using SemiFuture = int; template struct Task { struct promise_type { Task get_return_object() noexcept; suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_value(T); void unhandled_exception(); auto yield_value(Task) noexcept { return final_suspend(); } }; bool await_ready() noexcept { return false; } void await_suspend(coroutine_handle<>) noexcept {} T await_resume(); }; template<> struct Task { struct promise_type { Task get_return_object() noexcept; suspend_always initial_suspend() noexcept; suspend_always final_suspend() noexcept; void return_void() noexcept; void unhandled_exception() noexcept; auto yield_value(Task) noexcept { return final_suspend(); } }; bool await_ready() noexcept { return false; } void await_suspend(coroutine_handle<>) noexcept {} void await_resume() noexcept {} SemiFuture semi(); }; // FIXME: add CIRGen support here. // struct blocking_wait_fn { // template // T operator()(Task&& awaitable) const { // return T(); // } // }; // inline constexpr blocking_wait_fn blocking_wait{}; // static constexpr blocking_wait_fn const& blockingWait = blocking_wait; struct co_invoke_fn { template Task operator()(F&& f, A&&... a) const { return Task(); } }; co_invoke_fn co_invoke; }} // namespace folly::coro // CIR-DAG: ![[VoidTask:.*]] = !cir.record" padded {!u8i}> // CIR-DAG: ![[IntTask:.*]] = !cir.record" padded {!u8i}> // CIR-DAG: ![[VoidPromisse:.*]] = !cir.record::promise_type" padded {!u8i}> // CIR-DAG: ![[IntPromisse:.*]] = !cir.record::promise_type" padded {!u8i}> // CIR-DAG: ![[StdString:.*]] = !cir.record // CIR-DAG: ![[CoroHandleVoid:.*]] = !cir.record" padded {!u8i}> // CIR-DAG: ![[CoroHandlePromiseVoid:rec_.*]] = !cir.record::promise_type>" padded {!u8i}> // CIR-DAG: ![[CoroHandlePromiseInt:rec_.*]] = !cir.record::promise_type>" padded {!u8i}> // CIR-DAG: ![[SuspendAlways:.*]] = !cir.record // CIR: module {{.*}} { // CIR-NEXT: cir.global external @_ZN5folly4coro9co_invokeE = #cir.zero : !rec_folly3A3Acoro3A3Aco_invoke_fn // CIR: cir.func builtin private @__builtin_coro_id(!u32i, !cir.ptr, !cir.ptr, !cir.ptr) -> !u32i // CIR: cir.func builtin private @__builtin_coro_alloc(!u32i) -> !cir.bool // CIR: cir.func builtin private @__builtin_coro_size() -> !u64i // CIR: cir.func builtin private @__builtin_coro_begin(!u32i, !cir.ptr) -> !cir.ptr using VoidTask = folly::coro::Task; VoidTask silly_task() { co_await std::suspend_always(); } // CIR: cir.func coroutine {{.*}} @_Z10silly_taskv() -> ![[VoidTask]] // CIR: %[[VoidTaskAddr:.*]] = cir.alloca ![[VoidTask]], {{.*}}, ["__retval"] // CIR: %[[SavedFrameAddr:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["__coro_frame_addr"] // CIR: %[[VoidPromisseAddr:.*]] = cir.alloca ![[VoidPromisse]], {{.*}}, ["__promise"] // Get coroutine id with __builtin_coro_id. // CIR: %[[NullPtr:.*]] = cir.const #cir.ptr : !cir.ptr // CIR: %[[Align:.*]] = cir.const #cir.int<16> : !u32i // CIR: %[[CoroId:.*]] = cir.call @__builtin_coro_id(%[[Align]], %[[NullPtr]], %[[NullPtr]], %[[NullPtr]]) // Perform allocation calling operator 'new' depending on __builtin_coro_alloc and // call __builtin_coro_begin for the final coroutine frame address. // CIR: %[[ShouldAlloc:.*]] = cir.call @__builtin_coro_alloc(%[[CoroId]]) : (!u32i) -> !cir.bool // CIR: cir.store{{.*}} %[[NullPtr]], %[[SavedFrameAddr]] : !cir.ptr, !cir.ptr> // CIR: cir.if %[[ShouldAlloc]] { // CIR: %[[CoroSize:.*]] = cir.call @__builtin_coro_size() : () -> !u64i // CIR: %[[AllocAddr:.*]] = cir.call @_Znwm(%[[CoroSize]]) : (!u64i) -> !cir.ptr // CIR: cir.store{{.*}} %[[AllocAddr]], %[[SavedFrameAddr]] : !cir.ptr, !cir.ptr> // CIR: } // CIR: %[[Load0:.*]] = cir.load{{.*}} %[[SavedFrameAddr]] : !cir.ptr>, !cir.ptr // CIR: %[[CoroFrameAddr:.*]] = cir.call @__builtin_coro_begin(%[[CoroId]], %[[Load0]]) // Call promise.get_return_object() to retrieve the task object. // CIR: %[[RetObj:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type17get_return_objectEv(%[[VoidPromisseAddr]]) nothrow : {{.*}} -> ![[VoidTask]] // CIR: cir.store{{.*}} %[[RetObj]], %[[VoidTaskAddr]] : ![[VoidTask]] // Start a new scope for the actual codegen for co_await, create temporary allocas for // holding coroutine handle and the suspend_always struct. // CIR: cir.scope { // CIR: %[[SuspendAlwaysAddr:.*]] = cir.alloca ![[SuspendAlways]], {{.*}} ["ref.tmp0"] {alignment = 1 : i64} // CIR: %[[CoroHandleVoidAddr:.*]] = cir.alloca ![[CoroHandleVoid]], {{.*}} ["agg.tmp0"] {alignment = 1 : i64} // CIR: %[[CoroHandlePromiseAddr:.*]] = cir.alloca ![[CoroHandlePromiseVoid]], {{.*}} ["agg.tmp1"] {alignment = 1 : i64} // Effectively execute `coawait promise_type::initial_suspend()` by calling initial_suspend() and getting // the suspend_always struct to use for cir.await. Note that we return by-value since we defer ABI lowering // to later passes, same is done elsewhere. // CIR: %[[Tmp0:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(%[[VoidPromisseAddr]]) // CIR: cir.store{{.*}} %[[Tmp0:.*]], %[[SuspendAlwaysAddr]] // // Here we start mapping co_await to cir.await. // // First regions `ready` has a special cir.yield code to veto suspension. // CIR: cir.await(init, ready : { // CIR: %[[ReadyVeto:.*]] = cir.scope { // CIR: %[[TmpCallRes:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SuspendAlwaysAddr]]) // CIR: cir.yield %[[TmpCallRes:.*]] : !cir.bool // CIR: } // CIR: cir.condition(%[[ReadyVeto]]) // Second region `suspend` contains the actual suspend logic. // // - Start by getting the coroutine handle using from_address(). // - Implicit convert coroutine handle from task specific promisse // specialization to a void one. // - Call suspend_always::await_suspend() passing the handle. // // FIXME: add veto support for non-void await_suspends. // CIR: }, suspend : { // CIR: %[[FromAddrRes:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%[[CoroFrameAddr]]) // CIR: cir.store{{.*}} %[[FromAddrRes]], %[[CoroHandlePromiseAddr]] : ![[CoroHandlePromiseVoid]] // CIR: %[[CoroHandlePromiseReload:.*]] = cir.load{{.*}} %[[CoroHandlePromiseAddr]] // CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CoroHandleVoidAddr]], %[[CoroHandlePromiseReload]]) // CIR: %[[CoroHandleVoidReload:.*]] = cir.load{{.*}} %[[CoroHandleVoidAddr]] : !cir.ptr, ![[CoroHandleVoid]] // CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]], %[[CoroHandleVoidReload]]) // CIR: cir.yield // Third region `resume` handles coroutine resuming logic. // CIR: }, resume : { // CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SuspendAlwaysAddr]]) // CIR: cir.yield // CIR: },) // CIR: } // Since we already tested cir.await guts above, the remaining checks for: // - The actual user written co_await // - The promise call // - The final suspend co_await // - Return folly::coro::Task byRef(const std::string& s) { co_return s.size(); } // CIR: cir.func coroutine {{.*}} @_Z5byRefRKSt6string(%[[ARG:.*]]: !cir.ptr {{.*}}) -> ![[IntTask]] // CIR: %[[AllocaParam:.*]] = cir.alloca !cir.ptr, {{.*}}, ["s", init, const] // CIR: %[[IntTaskAddr:.*]] = cir.alloca ![[IntTask]], {{.*}}, ["__retval"] // CIR: %[[SavedFrameAddr:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["__coro_frame_addr"] // CIR: %[[AllocaFnUse:.*]] = cir.alloca !cir.ptr, {{.*}}, ["s", init, const] // CIR: %[[IntPromisseAddr:.*]] = cir.alloca ![[IntPromisse]], {{.*}}, ["__promise"] // CIR: cir.store %[[ARG]], %[[AllocaParam]] : !cir.ptr, {{.*}} // Call promise.get_return_object() to retrieve the task object. // CIR: %[[LOAD:.*]] = cir.load %[[AllocaParam]] : !cir.ptr>, !cir.ptr // CIR: cir.store {{.*}} %[[LOAD]], %[[AllocaFnUse]] : !cir.ptr, !cir.ptr> // CIR: %[[RetObj:.*]] = cir.call @_ZN5folly4coro4TaskIiE12promise_type17get_return_objectEv(%4) nothrow : {{.*}} -> ![[IntTask]] // CIR: cir.store {{.*}} %[[RetObj]], %[[IntTaskAddr]] : ![[IntTask]] // CIR: cir.scope { // CIR: %[[SuspendAlwaysAddr:.*]] = cir.alloca ![[SuspendAlways]], {{.*}} ["ref.tmp0"] {alignment = 1 : i64} // CIR: %[[CoroHandleVoidAddr:.*]] = cir.alloca ![[CoroHandleVoid]], {{.*}} ["agg.tmp0"] {alignment = 1 : i64} // CIR: %[[CoroHandlePromiseAddr:.*]] = cir.alloca ![[CoroHandlePromiseInt]], {{.*}} ["agg.tmp1"] {alignment = 1 : i64} // CIR: %[[Tmp0:.*]] = cir.call @_ZN5folly4coro4TaskIiE12promise_type15initial_suspendEv(%[[IntPromisseAddr]]) // CIR: cir.await(init, ready : { // CIR: %[[ReadyVeto:.*]] = cir.scope { // CIR: %[[TmpCallRes:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SuspendAlwaysAddr]]) // CIR: cir.yield %[[TmpCallRes:.*]] : !cir.bool // CIR: } // CIR: cir.condition(%[[ReadyVeto]]) // CIR: }, suspend : { // CIR: %[[FromAddrRes:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIiE12promise_typeEE12from_addressEPv(%[[CoroFrameAddr:.*]]) // CIR: cir.store{{.*}} %[[FromAddrRes]], %[[CoroHandlePromiseAddr]] : ![[CoroHandlePromiseInt]] // CIR: %[[CoroHandlePromiseReload:.*]] = cir.load{{.*}} %[[CoroHandlePromiseAddr]] // CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIiE12promise_typeEEES_IT_E(%[[CoroHandleVoidAddr]], %[[CoroHandlePromiseReload]]) // CIR: %[[CoroHandleVoidReload:.*]] = cir.load{{.*}} %[[CoroHandleVoidAddr]] : !cir.ptr, ![[CoroHandleVoid]] // CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]], %[[CoroHandleVoidReload]]) // CIR: cir.yield // CIR: }, resume : { // CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SuspendAlwaysAddr]]) // CIR: cir.yield // CIR: },) // CIR: }