// 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 // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm -disable-llvm-passes %s -o %t-cir.ll // RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=OGCG 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); }; template struct optional { optional(); optional(const T&); T &operator*() &; T &&operator*() &&; T &value() &; T &&value() &&; }; } // 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; template T blockingWait(Task&& awaitable) { return T(); } 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 // OGCG-DAG: %[[VoidPromisse:"struct.folly::coro::Task::promise_type"]] = type { i8 } // OGCG-DAG: %[[VoidTask:"struct.folly::coro::Task"]] = type { i8 } // OGCG-DAG: %[[SuspendAlways:"struct.std::suspend_always"]] = type { i8 } // 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"] // CIR: %[[SuspendAlwaysAddr:.*]] = cir.alloca ![[SuspendAlways]], {{.*}} ["ref.tmp0"] // CIR: %[[CoroHandleVoidAddr:.*]] = cir.alloca ![[CoroHandleVoid]], {{.*}} ["agg.tmp0"] // CIR: %[[CoroHandlePromiseAddr:.*]] = cir.alloca ![[CoroHandlePromiseVoid]], {{.*}} ["agg.tmp1"] // OGCG: %[[VoidPromisseAddr:.*]] = alloca %[[VoidPromisse]], align 1 // OGCG: %[[VoidTaskAddr:.*]] = alloca %[[VoidTask]], align 1 // OGCG: %[[SuspendAlwaysAddr:.*]] = alloca %[[SuspendAlways]], align 1 // 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]]) // OGCG: %[[CoroId:.*]] = call token @llvm.coro.id(i32 16, ptr %[[VoidPromisseAddr]], ptr null, ptr null) // 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 {llvm.noundef}) // CIR: %[[AllocAddr:.*]] = cir.call @_Znwm(%[[CoroSize]]) {allocsize = array} : (!u64i {llvm.noundef}) -> (!cir.ptr {llvm.nonnull, llvm.noundef}) // 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]]) // OGCG: %[[ShouldAlloc:.*]] = call i1 @llvm.coro.alloc(token %[[CoroId]]) // OGCG: br i1 %[[ShouldAlloc]], label %coro.alloc, label %coro.init // OGCG: coro.alloc: // OGCG: %[[CoroSize:.*]] = call i64 @llvm.coro.size.i64() // OGCG: %[[Alloc_Frame:.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef %[[CoroSize]]) // OGCG: br label %coro.init // OGCG: coro.init: // OGCG: %[[PtrToFramr:.*]] = phi ptr [ null, %entry ], [ %[[Alloc_Frame]], %coro.alloc ] // OGCG: %[[CoroFrameAddr:.*]] = call ptr @llvm.coro.begin(token %[[CoroId]], ptr %[[PtrToFramr]]) // Call promise.get_return_object() to retrieve the task object. // CIR: cir.cleanup.scope { // CIR: %[[RetObj:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type17get_return_objectEv(%[[VoidPromisseAddr]]) nothrow : {{.*}} -> ![[VoidTask]] // CIR: cir.store{{.*}} %[[RetObj]], %[[VoidTaskAddr]] : ![[VoidTask]] // OGCG: call void @llvm.lifetime.start.p0(ptr %[[VoidPromisseAddr]]) // OGCG: call void @_ZN5folly4coro4TaskIvE12promise_type17get_return_objectEv(ptr noundef nonnull align 1 dereferenceable(1) %[[VoidPromisseAddr]]) // OGCG: call void @llvm.lifetime.start.p0(ptr %[[SuspendAlwaysAddr]]) // Start a new scope for the actual codegen for co_await, create temporary allocas for // holding coroutine handle and the suspend_always struct. // 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]] // OGCG: call void @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(ptr noundef nonnull align 1 dereferenceable(1) %[[VoidPromisseAddr]]) // // 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.call @_ZNSt14suspend_always11await_readyEv(%[[SuspendAlwaysAddr]]) // CIR: cir.condition(%[[ReadyVeto]]) // OGCG: %[[Tmp0:.*]] = call noundef zeroext i1 @_ZNSt14suspend_always11await_readyEv(ptr noundef nonnull align 1 dereferenceable(1) %[[SuspendAlwaysAddr]]) // OGCG: br i1 %[[Tmp0]], label %init.ready, label %init.suspend // 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 // OGCG: init.suspend: // OGCG: %[[Save:.*]] = call token @llvm.coro.save(ptr null) // OGCG: call void @llvm.coro.await.suspend.void(ptr %[[SuspendAlwaysAddr]], ptr %[[CoroFrameAddr]], ptr @_Z10silly_taskv.__await_suspend_wrapper__init) // OGCG: %[[TMP1:.*]] = call i8 @llvm.coro.suspend(token %[[Save]], i1 false) // OGCG: switch i8 %[[TMP1]], label %coro.ret [ // OGCG: i8 0, label %init.ready // OGCG: i8 1, label %init.cleanup // OGCG: ] // Third region `resume` handles coroutine resuming logic. // CIR: }, resume : { // CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SuspendAlwaysAddr]]) // CIR: cir.yield // CIR: },) // OGCG: init.ready: // OGCG: call void @_ZNSt14suspend_always12await_resumeEv(ptr noundef nonnull align 1 dereferenceable(1) %[[SuspendAlwaysAddr]] // OGCG: br label %cleanup // 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 // CIR: cir.coro.body { // The actual user written co_await // CIR: cir.await(user, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // OGCG: cleanup.cont // OGCG: await.suspend: // OGCG: await.ready: // The promise call // CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv(%[[VoidPromisseAddr]]) // CIR: cir.co_return // CIR: } // OGCG: call void @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv(ptr noundef nonnull align 1 dereferenceable(1) %[[VoidPromisseAddr]]) // The final suspend co_await // CIR: cir.await(final, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: cir.yield // OGCG: coro.final: // OGCG: final.suspend: // OGCG: final.ready: // Cleanup of coroutine frame. // // `__builtin_coro_free` returns the frame pointer or null. // If null, no dynamic allocation happened, so nothing to free. // The `if` ensures we only call delete on non-null. // CIR: } cleanup normal { // CIR: %[[FreeMem:.*]] = cir.call @__builtin_coro_free(%[[CoroId]], %[[CoroFrameAddr]]) // CIR: %[[NullPtr2:.*]] = cir.const #cir.ptr // CIR: %[[Cond:.*]] = cir.cmp ne %[[FreeMem]], %[[NullPtr2]] // CIR: cir.if %[[Cond]] { // CIR: %[[Size:.*]] = cir.call @__builtin_coro_size() // CIR: cir.call @_ZdlPvm(%[[FreeMem]], %[[Size]]) // CIR: } // CIR: cir.yield // CIR: } // OGCG: %[[FreeMem:.*]] = call ptr @llvm.coro.free(token %[[CoroId]], ptr %[[CoroFrameAddr]]) // OGCG: %[[Cond:.*]] = icmp ne ptr %[[FreeMem]], null // OGCG: br i1 %[[Cond]], label %coro.free, label %after.coro.free // OGCG: coro.free: // OGCG: %[[Size:.*]] = call i64 @llvm.coro.size.i64() // OGCG: call void @_ZdlPvm(ptr {{.*}} %[[FreeMem]], i64 {{.*}} %[[Size]]) // OGCG: br label %after.coro.free // OGCG: after.coro.free: // Call builtin coro end and return // CIR: %[[CoroEndArg0:.*]] = cir.const #cir.ptr : !cir.ptr // CIR: %[[CoroEndArg1:.*]] = cir.const #false // CIR: = cir.call @__builtin_coro_end(%[[CoroEndArg0]], %[[CoroEndArg1]]) // CIR: %[[Tmp1:.*]] = cir.load{{.*}} %[[VoidTaskAddr]] // CIR: cir.return %[[Tmp1]] // CIR: } // OGCG: coro.ret: // OGCG: call void @llvm.coro.end(ptr null, i1 false, token none) // OGCG: ret void 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: %[[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: cir.store %[[ARG]], %[[AllocaParam]] : !cir.ptr, {{.*}} // CIR: cir.cleanup.scope { // 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(%[[IntPromisseAddr]]) nothrow : {{.*}} -> ![[IntTask]] // CIR: cir.store {{.*}} %[[RetObj]], %[[IntTaskAddr]] : ![[IntTask]] // CIR: %[[Tmp0:.*]] = cir.call @_ZN5folly4coro4TaskIiE12promise_type15initial_suspendEv(%[[IntPromisseAddr]]) // CIR: cir.store{{.*}} %[[Tmp0]], %[[SuspendAlwaysAddr]] // CIR: cir.await(init, ready : { // CIR: %[[TmpCallRes:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SuspendAlwaysAddr]]) // CIR: cir.condition(%[[TmpCallRes]]) // 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: cir.coro.body { // can't fallthrough // CIR-NOT: cir.await(user // CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[IntPromisseAddr]], %[[STRING_SIZE:.*]]) //CIR: cir.co_return // CIR: } // The final suspend co_await // CIR: cir.await(final, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: cir.yield // CIR: } cleanup normal { // CIR: } folly::coro::Task silly_coro() { std::optional> task; { std::string s = "yolo"; task = byRef(s); } folly::coro::blockingWait(std::move(task.value())); co_return; } // Make sure we properly handle OnFallthrough coro body sub stmt and // check there are not multiple co_returns emitted. // CIR: cir.func coroutine {{.*}} @_Z10silly_corov() {{.*}} ![[VoidTask]] // CIR: cir.cleanup.scope { // CIR: cir.await(init, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: cir.coro.body { // CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv // CIR: cir.co_return // CIR: } // CIR: cir.await(final, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: cir.yield // CIR: } cleanup normal { // CIR: } folly::coro::Task yield(); folly::coro::Task yield1() { auto t = yield(); co_yield t; } // CIR: cir.func coroutine {{.*}} @_Z6yield1v() -> !rec_folly3A3Acoro3A3ATask3Cvoid3E // Prologue allocas (still present in output) // CIR-DAG: %[[RETVAL:.*]] = cir.alloca ![[VoidTask]], {{.*}} ["__retval"] // CIR-DAG: %[[FRAME:.*]] = cir.alloca !cir.ptr, !cir.ptr>, ["__coro_frame_addr"] // CIR-DAG: %[[PROMISE:.*]] = cir.alloca ![[VoidPromisse]], {{.*}} ["__promise"] // CIR-DAG: %[[SUSP0:.*]] = cir.alloca ![[SuspendAlways]], {{.*}} ["ref.tmp0"] // CIR-DAG: %[[CH_VOID0:.*]] = cir.alloca ![[CoroHandleVoid]], {{.*}} ["agg.tmp0"] // CIR-DAG: %[[CH_PROM0:.*]] = cir.alloca ![[CoroHandlePromiseVoid]], {{.*}} ["agg.tmp1"] // CIR-DAG: %[[T_ADDR:.*]] = cir.alloca ![[VoidTask]], {{.*}} ["t", init] // CIR-DAG: %[[SUSP1:.*]] = cir.alloca ![[SuspendAlways]], {{.*}} ["ref.tmp1"] // CIR-DAG: %[[AWAITER_COPY_ADDR:.*]] = cir.alloca ![[VoidTask]], {{.*}} ["agg.tmp2"] // CIR-DAG: %[[CH_VOID1:.*]] = cir.alloca ![[CoroHandleVoid]], {{.*}} ["agg.tmp3"] // CIR-DAG: %[[CH_PROM1:.*]] = cir.alloca ![[CoroHandlePromiseVoid]], {{.*}} ["agg.tmp4"] // CIR-DAG: %[[SUSP2:.*]] = cir.alloca ![[SuspendAlways]], {{.*}} ["ref.tmp2"] // CIR-DAG: %[[CH_VOID2:.*]] = cir.alloca ![[CoroHandleVoid]], {{.*}} ["agg.tmp5"] // CIR-DAG: %[[CH_PROM2:.*]] = cir.alloca ![[CoroHandlePromiseVoid]], {{.*}} ["agg.tmp6"] // CIR: cir.cleanup.scope { // initial_suspend + await(init) // CIR: %[[INIT_SUSP:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(%[[PROMISE]]){{.*}} // CIR: cir.store{{.*}} %[[INIT_SUSP]], %[[SUSP0]] // CIR: cir.await(init, ready : { // CIR: %[[READY0:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SUSP0]]){{.*}} // CIR: cir.condition(%[[READY0]]) // CIR: }, suspend : { // CIR: %[[FROMADDR0:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}} // CIR: cir.store{{.*}} %[[FROMADDR0]], %[[CH_PROM0]] // CIR: %[[PROM_RELOAD0:.*]] = cir.load{{.*}} %[[CH_PROM0]] // CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID0]], %[[PROM_RELOAD0]]){{.*}} // CIR: %[[VOID_RELOAD0:.*]] = cir.load{{.*}} %[[CH_VOID0]] // CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP0]], %[[VOID_RELOAD0]]){{.*}} // CIR: cir.yield // CIR: }, resume : { // CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP0]]){{.*}} // CIR: cir.yield // CIR: },) // yield_value + await(yield) // CIR: cir.coro.body { // CIR: %[[YIELD_TASK:.*]] = cir.call @_Z5yieldv(){{.*}} // CIR: cir.store{{.*}} %[[YIELD_TASK]], %[[T_ADDR]] // CIR: %[[AWAITER:.*]] = cir.load{{.*}} %[[AWAITER_COPY_ADDR]] // CIR: %[[YIELD_SUSP:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type11yield_valueES2_(%[[PROMISE]], %[[AWAITER]]){{.*}} // CIR: cir.store{{.*}} %[[YIELD_SUSP]], %[[SUSP1]] // CIR: cir.await(yield, ready : { // CIR: %[[READY1:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SUSP1]]){{.*}} // CIR: cir.condition(%[[READY1]]) // CIR: }, suspend : { // CIR: %[[FROMADDR1:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}} // CIR: cir.store{{.*}} %[[FROMADDR1]], %[[CH_PROM1]] // CIR: %[[PROM_RELOAD1:.*]] = cir.load{{.*}} %[[CH_PROM1]] // CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID1]], %[[PROM_RELOAD1]]){{.*}} // CIR: %[[VOID_RELOAD1:.*]] = cir.load{{.*}} %[[CH_VOID1]] // CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP1]], %[[VOID_RELOAD1]]){{.*}} // CIR: cir.yield // CIR: }, resume : { // CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP1]]){{.*}} // CIR: cir.yield // CIR: },) // CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv(%[[PROMISE]]) // CIR: cir.co_return // CIR: } // return_void + await(final) // CIR: %[[FINAL_SUSP:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type13final_suspendEv(%[[PROMISE]]){{.*}} // CIR: cir.store{{.*}} %[[FINAL_SUSP]], %[[SUSP2]] // CIR: cir.await(final, ready : { // CIR: %[[READY2:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SUSP2]]){{.*}} // CIR: cir.condition(%[[READY2]]) // CIR: }, suspend : { // CIR: %[[FROMADDR2:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}} // CIR: cir.store{{.*}} %[[FROMADDR2]], %[[CH_PROM2]] // CIR: %[[PROM_RELOAD2:.*]] = cir.load{{.*}} %[[CH_PROM2]] // CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID2]], %[[PROM_RELOAD2]]){{.*}} // CIR: %[[VOID_RELOAD2:.*]] = cir.load{{.*}} %[[CH_VOID2]] // CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP2]], %[[VOID_RELOAD2]]){{.*}} // CIR: cir.yield // CIR: }, resume : { // CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP2]]){{.*}} // CIR: cir.yield // CIR: },) // CIR: cir.yield // CIR: } cleanup normal { // CIR: } // CIR: = cir.call @__builtin_coro_end(%{{.*}}, %{{.*}}){{.*}} // CIR: %[[RETLOAD:.*]] = cir.load{{.*}} %[[RETVAL]] // CIR: cir.return %[[RETLOAD]] // CIR: } // CHECK: } folly::coro::Task go(int const& val); folly::coro::Task go1() { auto task = go(1); co_return co_await task; } // CIR: cir.func coroutine {{.*}} @_Z3go1v() {{.*}} ![[IntTask]] // CIR: %[[IntTaskAddr:.*]] = cir.alloca ![[IntTask]], !cir.ptr, ["task", init] // CIR: %[[OneAddr:.*]] = cir.alloca !s32i, !cir.ptr, ["ref.tmp1", init] {alignment = 4 : i64} // CIR: cir.cleanup.scope { // CIR: cir.await(init, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: cir.coro.body { // CIR: %[[One:.*]] = cir.const #cir.int<1> : !s32i // CIR: cir.store{{.*}} %[[One]], %[[OneAddr]] : !s32i, !cir.ptr // CIR: %[[IntTaskTmp:.*]] = cir.call @_Z2goRKi(%[[OneAddr]]) : (!cir.ptr{{.*}}) -> ![[IntTask]] // CIR: cir.store{{.*}} %[[IntTaskTmp]], %[[IntTaskAddr]] : ![[IntTask]], !cir.ptr // CIR: cir.await(user, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: %[[ResumeVal:.*]] = cir.call @_ZN5folly4coro4TaskIiE12await_resumeEv(%[[IntTaskAddr]]) // CIR: cir.store{{.*}} %[[ResumeVal]], %[[CoReturnValAddr:.*]] : !s32i, !cir.ptr // CIR: },) // CIR: %[[V:.*]] = cir.load{{.*}} %[[CoReturnValAddr]] : !cir.ptr, !s32i // CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi({{.*}}, %[[V]]) // CIR: cir.co_return // CIR: } // CIR: cir.await(final, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: cir.yield // CIR: } cleanup normal { // CIR: } folly::coro::Task go1_lambda() { auto task = []() -> folly::coro::Task { co_return 1; }(); co_return co_await task; } // CIR: cir.func coroutine {{.*}} @_ZZ10go1_lambdavENK3$_0clEv{{.*}} ![[IntTask]] // CIR: cir.cleanup.scope { // CIR: cir.await(init, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: cir.coro.body { // CIR: %[[ONE:.*]] = cir.const #cir.int<1> // CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE:.*]], %[[ONE]]) // CIR: cir.co_return // CIR: } // CIR: cir.await(final, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: } cleanup normal { // CIR: cir.func coroutine {{.*}} @_Z10go1_lambdav() {{.*}} ![[IntTask]] // CIR: cir.cleanup.scope { // CIR: cir.await(init, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: cir.coro.body { // CIR: cir.call @_ZZ10go1_lambdavENK3$_0clEv // CIR: cir.await(user, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: %[[RESUME_RES:.*]] = cir.call @_ZN5folly4coro4TaskIiE12await_resumeEv(%[[TASK:.*]]) // CIR: cir.store %[[RESUME_RES]], %[[resume_rval:.*]] : !s32i, !cir.ptr // CIR: },) // CIR: %[[TMP1:.*]] = cir.load %[[resume_rval:.*]] : !cir.ptr // CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE:.*]], %[[TMP1]]) // CIR: cir.co_return // CIR: } // CIR: cir.await(final, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: } cleanup normal { folly::coro::Task go4() { auto* fn = +[](int const& i) -> folly::coro::Task { co_return i; }; auto task = fn(3); co_return co_await std::move(task); } // CIR: cir.func coroutine{{.*}} @_ZZ3go4vENK3$_0clERKi( // CIR: cir.cleanup.scope { // CIR: cir.await(init, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: cir.coro.body { // CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE:.*]], %[[I:.*]]) // CIR: cir.co_return // CIR: } // CIR: cir.await(final, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: } cleanup normal { // CIR: cir.func coroutine {{.*}} @_Z3go4v() {{.*}} ![[IntTask]] // CIR: cir.cleanup.scope { // CIR: cir.await(init, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // Get the lambda invoker ptr via `lambda operator folly::coro::Task (*)(int const&)()` // CIR: %[[INVOKER:.*]] = cir.call @_ZZ3go4vENK3$_0cvPFN5folly4coro4TaskIiEERKiEEv(%{{.*}}) nothrow : {{.*}} -> (!cir.ptr) -> ![[IntTask]]>> {llvm.noundef}) // CIR: cir.store{{.*}} %[[INVOKER]], %[[FN_ADDR:.*]] : !cir.ptr) -> ![[IntTask]]>>, !cir.ptr) -> ![[IntTask]]>>> // CIR: %[[FN:.*]] = cir.load{{.*}} %[[FN_ADDR]] : !cir.ptr) -> ![[IntTask]]>>>, !cir.ptr) -> ![[IntTask]]>> // CIR: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i // CIR: cir.store{{.*}} %[[THREE]], %[[ARG:.*]] : !s32i, !cir.ptr // Call invoker, which calls operator() indirectly. // CIR: %[[CALLRES:.*]] = cir.call %[[FN]](%[[ARG]]) : (!cir.ptr) -> ![[IntTask]]>>, !cir.ptr {{.*}}) -> ![[IntTask]] // CIR: cir.store{{.*}} %[[CALLRES]], %[[TASK_ADDR:.*]] : ![[IntTask]], !cir.ptr // CIR: cir.await(user, ready : { // CIR: = cir.call @_ZN5folly4coro4TaskIiE11await_readyEv(%[[TASK_ADDR]]) // CIR: cir.condition( // CIR: }, suspend : { // CIR: cir.yield // CIR: }, resume : { // CIR: cir.yield // CIR: },) // CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi // CIR: cir.co_return // CIR: } // CIR: cir.await(final, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: } cleanup normal { // OGCG: define {{.*}}__await_suspend_wrapper__init(ptr noundef nonnull %[[Awaiter:.*]], ptr noundef %[[Handle:.*]]) // OGCG: entry: // OGCG: %[[AwaiterAddr:.*]] = alloca ptr // OGCG: %[[HandleAddr:.*]] = alloca ptr // OGCG: %[[CoroHandleVoidAddr:.*]] = alloca %"struct.std::coroutine_handle" // OGCG: store ptr %[[Awaiter:.*]], ptr %[[AwaiterAddr]] // OGCG: store ptr %[[Handle:.*]], ptr %[[HandleAddr]] // OGCG: %[[AwaiterReload:.*]] = load ptr, ptr %[[AwaiterAddr]] // OGCG: %[[CoroFrameAddr:.*]] = load ptr, ptr %[[HandleAddr]] // OGCG: call void @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(ptr noundef %[[CoroFrameAddr]]) // OGCG: call void @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(ptr noundef nonnull align 1 dereferenceable(1) %[[CoroHandleVoidAddr]]) // OGCG: call void @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(ptr noundef nonnull align 1 dereferenceable(1) %[[AwaiterReload]]) // OGCG: ret void // OGCG: } folly::coro::Task co_returns(int x) { if (x < 0) co_return -1; else if (x == 1) co_return -2; co_await std::suspend_always(); co_return x * 2; } // CIR: cir.func coroutine {{.*}} @_Z10co_returnsi // CIR: cir.cleanup.scope { // CIR: cir.await(init, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: cir.coro.body { // CIR: cir.scope { // CIR: cir.if {{.*}} { // CIR: %[[MINUS_ONE:.*]] = cir.const #cir.int<-1> // CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE:.*]], %[[MINUS_ONE]]) // CIR: cir.co_return // CIR: } else { // CIR: cir.if {{.*}} { // CIR: %[[MINUS_TWO:.*]] = cir.const #cir.int<-2> // CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE]], %[[MINUS_TWO]]) // CIR: cir.co_return // CIR: } // CIR: } // CIR: } // CIR: cir.await(user, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: %[[X_LOAD:.*]] = cir.load {{.*}} %[[X:.*]] // CIR: %[[TWO:.*]] = cir.const #cir.int<2> // CIR: %[[RES:.*]] = cir.mul nsw %[[X_LOAD]], %[[TWO]] // CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE]], %[[RES]]) // CIR: cir.co_return // CIR: } // CIR: cir.await(final, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: cir.yield // CIR: } cleanup normal { // OGCG: define {{.*}} @_Z10co_returnsi // OGCG: coro.init: // OGCG: init.suspend: // OGCG: init.ready: // OGCG: if.then: // OGCG: call void @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi({{.*}} %[[promise:.*]], {{.*}} -1) // OGCG: br label %coro.final // OGCG: if.else: // OGCG: if.then5: // OGCG: call void @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi({{.*}} %[[promise:.*]], {{.*}} -2) // OGCG: br label %coro.final // OGCG: await.suspend: // OGCG: await.ready: // OGCG: coro.final: // OGCG: final.suspend: // OGCG: final.ready: folly::coro::Task implicit_yield_terminator(int x) { if (x < 0) co_return -1; else co_return 2; } // CIR: cir.func coroutine {{.*}} @_Z25implicit_yield_terminatori // CIR: cir.coro.body { // CIR: cir.if %{{.*}} { // CIR: cir.co_return // CIR: } else { // CIR: cir.co_return // CIR: } // CIR: cir.yield // CIR: } struct HasDtor { ~ HasDtor(); }; folly::coro::Task co_return_with_dtor(int flag) { HasDtor local; if (flag) co_return 1; // local dtor must run here co_return 2; } // CIR: cir.func coroutine {{.*}} @_Z19co_return_with_dtori // CIR: %[[LOCAL:.*]] = cir.alloca !rec_HasDtor, !cir.ptr, ["local"] // CIR: cir.cleanup.scope { // CIR: cir.await(init, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: cir.coro.body { // CIR: cir.cleanup.scope { // CIR: cir.scope { // CIR: %[[CAST_FLAG:.*]] = cir.cast int_to_bool %[[FLAG:.*]] // CIR: cir.if %[[CAST_FLAG]] { // CIR: %[[ONE:.*]] = cir.const #cir.int<1> // CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[promise:.*]], %[[ONE]]) // CIR: cir.co_return // CIR: } // CIR: } // CIR: %[[TWO:.*]] = cir.const #cir.int<2> // CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[promise]], %[[TWO]]) // CIR: cir.co_return // CIR: } cleanup normal { // CIR: cir.call @_ZN7HasDtorD1Ev(%[[LOCAL]]) // CIR: cir.yield // CIR: } // CIR: cir.yield // CIR: } // CIR: cir.await(final, ready : { // CIR: }, suspend : { // CIR: }, resume : { // CIR: },) // CIR: cir.yield // CIR: } cleanup normal { // CIR: cir.yield // CIR: } // OGCG: define {{.*}} void @_Z19co_return_with_dtori // OGCG: %[[LOCAL:.*]] = alloca %struct.HasDtor // OGCG: coro.init: // OGCG: init.suspend: // OGCG: init.ready: // OGCG: if.then: // OGCG: call void @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi({{.*}} %[[promise:.*]], {{.*}} 1) // OGCG: br label %cleanup4 // OGCG: if.end: // OGCG: call void @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi({{.*}} %[[promise]], {{.*}} 2) // OGCG: br label %cleanup4 // OGCG: cleanup4: // OGCG: call void @_ZN7HasDtorD1Ev({{.*}} %[[LOCAL]]) // OGCG: br label %coro.final