blob: 46d7cde18b047c862f03699fcbdc0c3d01477553 [file]
// 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<typename T> struct remove_reference { typedef T type; };
template<typename T> struct remove_reference<T &> { typedef T type; };
template<typename T> struct remove_reference<T &&> { typedef T type; };
template<typename T>
typename remove_reference<T>::type &&move(T &&t) noexcept;
template <class Ret, typename... T>
struct coroutine_traits { using promise_type = typename Ret::promise_type; };
template <class Promise = void>
struct coroutine_handle {
static coroutine_handle from_address(void *) noexcept;
};
template <>
struct coroutine_handle<void> {
template <class PromiseType>
coroutine_handle(coroutine_handle<PromiseType>) 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<typename T>
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<class T>
struct Task {
struct promise_type {
Task<T> 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<T>) noexcept { return final_suspend(); }
};
bool await_ready() noexcept { return false; }
void await_suspend(coroutine_handle<>) noexcept {}
T await_resume();
};
template<>
struct Task<void> {
struct promise_type {
Task<void> 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<void>) 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 <typename T>
// T operator()(Task<T>&& awaitable) const {
// return T();
// }
// };
// inline constexpr blocking_wait_fn blocking_wait{};
// static constexpr blocking_wait_fn const& blockingWait = blocking_wait;
template <typename T>
T blockingWait(Task<T>&& awaitable) {
return T();
}
struct co_invoke_fn {
template <typename F, typename... A>
Task<void> operator()(F&& f, A&&... a) const {
return Task<void>();
}
};
co_invoke_fn co_invoke;
}} // namespace folly::coro
// CIR-DAG: ![[VoidTask:.*]] = !cir.record<struct "folly::coro::Task<void>" padded {!u8i}>
// CIR-DAG: ![[IntTask:.*]] = !cir.record<struct "folly::coro::Task<int>" padded {!u8i}>
// CIR-DAG: ![[VoidPromisse:.*]] = !cir.record<struct "folly::coro::Task<void>::promise_type" padded {!u8i}>
// CIR-DAG: ![[IntPromisse:.*]] = !cir.record<struct "folly::coro::Task<int>::promise_type" padded {!u8i}>
// CIR-DAG: ![[StdString:.*]] = !cir.record<struct "std::string" padded {!u8i}>
// CIR-DAG: ![[CoroHandleVoid:.*]] = !cir.record<struct "std::coroutine_handle<void>" padded {!u8i}>
// CIR-DAG: ![[CoroHandlePromiseVoid:rec_.*]] = !cir.record<struct "std::coroutine_handle<folly::coro::Task<void>::promise_type>" padded {!u8i}>
// CIR-DAG: ![[CoroHandlePromiseInt:rec_.*]] = !cir.record<struct "std::coroutine_handle<folly::coro::Task<int>::promise_type>" padded {!u8i}>
// CIR-DAG: ![[SuspendAlways:.*]] = !cir.record<struct "std::suspend_always" padded {!u8i}>
// OGCG-DAG: %[[VoidPromisse:"struct.folly::coro::Task<void>::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<!void>, !cir.ptr<!void>, !cir.ptr<!void>) -> !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<!void>) -> !cir.ptr<!void>
using VoidTask = folly::coro::Task<void>;
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<!void>, !cir.ptr<!cir.ptr<!void>>, ["__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<null> : !cir.ptr<!void>
// 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<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR: cir.if %[[ShouldAlloc]] {
// CIR: %[[CoroSize:.*]] = cir.call @__builtin_coro_size() : () -> (!u64i {llvm.noundef})
// CIR: %[[AllocAddr:.*]] = cir.call @_Znwm(%[[CoroSize]]) {allocsize = array<i32: 0>} : (!u64i {llvm.noundef}) -> (!cir.ptr<!void> {llvm.nonnull, llvm.noundef})
// CIR: cir.store{{.*}} %[[AllocAddr]], %[[SavedFrameAddr]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>
// CIR: }
// CIR: %[[Load0:.*]] = cir.load{{.*}} %[[SavedFrameAddr]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>
// 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]]>, ![[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<null>
// 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<null> : !cir.ptr<!void>
// 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<int> byRef(const std::string& s) {
co_return s.size();
}
// CIR: cir.func coroutine {{.*}} @_Z5byRefRKSt6string(%[[ARG:.*]]: !cir.ptr<![[StdString]]> {{.*}}) -> ![[IntTask]]
// CIR: %[[AllocaParam:.*]] = cir.alloca !cir.ptr<![[StdString]]>, {{.*}}, ["s", init, const]
// CIR: %[[IntTaskAddr:.*]] = cir.alloca ![[IntTask]], {{.*}}, ["__retval"]
// CIR: %[[SavedFrameAddr:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__coro_frame_addr"]
// CIR: %[[AllocaFnUse:.*]] = cir.alloca !cir.ptr<![[StdString]]>, {{.*}}, ["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<![[StdString]]>, {{.*}}
// CIR: cir.cleanup.scope {
// Call promise.get_return_object() to retrieve the task object.
// CIR: %[[LOAD:.*]] = cir.load %[[AllocaParam]] : !cir.ptr<!cir.ptr<![[StdString]]>>, !cir.ptr<![[StdString]]>
// CIR: cir.store {{.*}} %[[LOAD]], %[[AllocaFnUse]] : !cir.ptr<![[StdString]]>, !cir.ptr<!cir.ptr<![[StdString]]>>
// 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]]>, ![[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<void> silly_coro() {
std::optional<folly::coro::Task<int>> 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<void> yield();
folly::coro::Task<void> 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<!void>, !cir.ptr<!cir.ptr<!void>>, ["__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<int> go(int const& val);
folly::coro::Task<int> go1() {
auto task = go(1);
co_return co_await task;
}
// CIR: cir.func coroutine {{.*}} @_Z3go1v() {{.*}} ![[IntTask]]
// CIR: %[[IntTaskAddr:.*]] = cir.alloca ![[IntTask]], !cir.ptr<![[IntTask]]>, ["task", init]
// CIR: %[[OneAddr:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["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<!s32i>
// CIR: %[[IntTaskTmp:.*]] = cir.call @_Z2goRKi(%[[OneAddr]]) : (!cir.ptr<!s32i>{{.*}}) -> ![[IntTask]]
// CIR: cir.store{{.*}} %[[IntTaskTmp]], %[[IntTaskAddr]] : ![[IntTask]], !cir.ptr<![[IntTask]]>
// CIR: cir.await(user, ready : {
// CIR: }, suspend : {
// CIR: }, resume : {
// CIR: %[[ResumeVal:.*]] = cir.call @_ZN5folly4coro4TaskIiE12await_resumeEv(%[[IntTaskAddr]])
// CIR: cir.store{{.*}} %[[ResumeVal]], %[[CoReturnValAddr:.*]] : !s32i, !cir.ptr<!s32i>
// CIR: },)
// CIR: %[[V:.*]] = cir.load{{.*}} %[[CoReturnValAddr]] : !cir.ptr<!s32i>, !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<int> go1_lambda() {
auto task = []() -> folly::coro::Task<int> {
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<!s32i>
// CIR: },)
// CIR: %[[TMP1:.*]] = cir.load %[[resume_rval:.*]] : !cir.ptr<!s32i>
// 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<int> go4() {
auto* fn = +[](int const& i) -> folly::coro::Task<int> { 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> (*)(int const&)()`
// CIR: %[[INVOKER:.*]] = cir.call @_ZZ3go4vENK3$_0cvPFN5folly4coro4TaskIiEERKiEEv(%{{.*}}) nothrow : {{.*}} -> (!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>> {llvm.noundef})
// CIR: cir.store{{.*}} %[[INVOKER]], %[[FN_ADDR:.*]] : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>>
// CIR: %[[FN:.*]] = cir.load{{.*}} %[[FN_ADDR]] : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>>, !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>
// CIR: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i
// CIR: cir.store{{.*}} %[[THREE]], %[[ARG:.*]] : !s32i, !cir.ptr<!s32i>
// Call invoker, which calls operator() indirectly.
// CIR: %[[CALLRES:.*]] = cir.call %[[FN]](%[[ARG]]) : (!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!s32i> {{.*}}) -> ![[IntTask]]
// CIR: cir.store{{.*}} %[[CALLRES]], %[[TASK_ADDR:.*]] : ![[IntTask]], !cir.ptr<![[IntTask]]>
// 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<int> 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<int> 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<int> 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<!rec_HasDtor>, ["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
folly::coro::Task<int __complex__> fetchData() noexcept {
int __complex__ a;
co_return a;
}
folly::coro::Task<int __complex__> complex_co_await() noexcept {
co_await fetchData();
}
// CIR: cir.func coroutine {{.*}} @_Z16complex_co_awaitv
// CIR: %[[COMPLEX_ADDR:.*]] = cir.alloca !rec_folly3A3Acoro3A3ATask3C_Complex_int3E, !cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E>, ["ref.tmp1"]
// CIR: %[[RESUME_VAL_ADDR:.*]] = cir.alloca !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>, ["__coawait_resume_rval"]
// CIR: cir.cleanup.scope {
// CIR: cir.await(init, ready : {
// CIR: }, suspend : {
// CIR: }, resume : {
// CIR: },)
// CIR: cir.coro.body {
// CIR: %[[CALL:.*]] = cir.call @_Z9fetchDatav() nothrow : () -> !rec_folly3A3Acoro3A3ATask3C_Complex_int3E
// CIR: cir.store {{.*}} %[[CALL]], %[[COMPLEX_ADDR]] : !rec_folly3A3Acoro3A3ATask3C_Complex_int3E, !cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E>
// CIR: cir.await(user, ready : {
// CIR: }, suspend : {
// CIR: }, resume : {
// CIR: %[[RESUME_VAL:.*]] = cir.call @_ZN5folly4coro4TaskICiE12await_resumeEv(%[[COMPLEX_ADDR]]) : (!cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E> {llvm.align = 1 : i64, llvm.dereferenceable = 1 : i64, llvm.nonnull, llvm.noundef}) -> (!cir.complex<!s32i> {llvm.noundef})
// CIR: cir.store %[[RESUME_VAL]], %[[RESUME_VAL_ADDR]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>
// CIR: },)
// CIR: %[[V:.*]] = cir.load %[[RESUME_VAL_ADDR]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
// CIR: cir.yield
// CIR: }
// CIR: } cleanup normal {
// CIR: }
// OGCG: define dso_local void @_Z16complex_co_awaitv()
// OGCG: %[[RESUME_VAL_ADDR:.*]] = alloca { i32, i32 }, align 4
// OGCG: %[[RESUME_REAL_ADDR:.*]] = alloca i32, align 4
// OGCG: %[[RESUME_IMAG_ADDR:.*]] = alloca i32, align 4
// OGCG: coro.init:
// OGCG: await.ready:
// OGCG: %[[RESUME_VAL:.*]] = call noundef i64 @_ZN5folly4coro4TaskICiE12await_resumeEv(ptr noundef nonnull align 1 dereferenceable(1) %{{.*}})
// OGCG: store i64 %[[RESUME_VAL]], ptr %[[RESUME_VAL_ADDR]], align 4
// OGCG: %[[RESUME_REAL_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[RESUME_VAL_ADDR]], i32 0, i32 0
// OGCG: %[[RESUME_REAL:.*]] = load i32, ptr %[[RESUME_REAL_PTR]], align 4
// OGCG: store i32 %[[RESUME_REAL]], ptr %[[RESUME_REAL_ADDR]], align 4
// OGCG: %[[RESUME_IMAG_PTR:.*]] = getelementptr inbounds nuw { i32, i32 }, ptr %[[RESUME_VAL_ADDR]], i32 0, i32 1
// OGCG: %[[RESUME_IMAG:.*]] = load i32, ptr %[[RESUME_IMAG_PTR]], align 4
// OGCG: store i32 %[[RESUME_IMAG]], ptr %[[RESUME_IMAG_ADDR]], align 4
// OGCG: br label %[[CLEANUP_FROM_AWAIT_READY:.*]]
// OGCG: [[CLEANUP_CONT:.*]]:
// OGCG: %[[RESUME_REAL:.*]] = load i32, ptr %[[RESUME_REAL_ADDR]], align 4
// OGCG: %[[RESUME_IMAG:.*]] = load i32, ptr %[[RESUME_IMAG_ADDR]], align 4