251 lines · cpp
1// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir2// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR3 4namespace std {5 6template<typename T> struct remove_reference { typedef T type; };7template<typename T> struct remove_reference<T &> { typedef T type; };8template<typename T> struct remove_reference<T &&> { typedef T type; };9 10template<typename T>11typename remove_reference<T>::type &&move(T &&t) noexcept;12 13template <class Ret, typename... T>14struct coroutine_traits { using promise_type = typename Ret::promise_type; };15 16template <class Promise = void>17struct coroutine_handle {18 static coroutine_handle from_address(void *) noexcept;19};20template <>21struct coroutine_handle<void> {22 template <class PromiseType>23 coroutine_handle(coroutine_handle<PromiseType>) noexcept;24 static coroutine_handle from_address(void *);25};26 27struct suspend_always {28 bool await_ready() noexcept { return false; }29 void await_suspend(coroutine_handle<>) noexcept {}30 void await_resume() noexcept {}31};32 33struct suspend_never {34 bool await_ready() noexcept { return true; }35 void await_suspend(coroutine_handle<>) noexcept {}36 void await_resume() noexcept {}37};38 39struct string {40 int size() const;41 string();42 string(char const *s);43};44 45} // namespace std46 47namespace folly {48namespace coro {49 50using std::suspend_always;51using std::suspend_never;52using std::coroutine_handle;53 54using SemiFuture = int;55 56template<class T>57struct Task {58 struct promise_type {59 Task<T> get_return_object() noexcept;60 suspend_always initial_suspend() noexcept;61 suspend_always final_suspend() noexcept;62 void return_value(T);63 void unhandled_exception();64 auto yield_value(Task<T>) noexcept { return final_suspend(); }65 };66 bool await_ready() noexcept { return false; }67 void await_suspend(coroutine_handle<>) noexcept {}68 T await_resume();69};70 71template<>72struct Task<void> {73 struct promise_type {74 Task<void> get_return_object() noexcept;75 suspend_always initial_suspend() noexcept;76 suspend_always final_suspend() noexcept;77 void return_void() noexcept;78 void unhandled_exception() noexcept;79 auto yield_value(Task<void>) noexcept { return final_suspend(); }80 };81 bool await_ready() noexcept { return false; }82 void await_suspend(coroutine_handle<>) noexcept {}83 void await_resume() noexcept {}84 SemiFuture semi();85};86 87// FIXME: add CIRGen support here.88// struct blocking_wait_fn {89// template <typename T>90// T operator()(Task<T>&& awaitable) const {91// return T();92// }93// };94 95// inline constexpr blocking_wait_fn blocking_wait{};96// static constexpr blocking_wait_fn const& blockingWait = blocking_wait;97 98struct co_invoke_fn {99 template <typename F, typename... A>100 Task<void> operator()(F&& f, A&&... a) const {101 return Task<void>();102 }103};104 105co_invoke_fn co_invoke;106 107}} // namespace folly::coro108 109// CIR-DAG: ![[VoidTask:.*]] = !cir.record<struct "folly::coro::Task<void>" padded {!u8i}>110// CIR-DAG: ![[IntTask:.*]] = !cir.record<struct "folly::coro::Task<int>" padded {!u8i}>111// CIR-DAG: ![[VoidPromisse:.*]] = !cir.record<struct "folly::coro::Task<void>::promise_type" padded {!u8i}>112// CIR-DAG: ![[IntPromisse:.*]] = !cir.record<struct "folly::coro::Task<int>::promise_type" padded {!u8i}>113// CIR-DAG: ![[StdString:.*]] = !cir.record<struct "std::string" padded {!u8i}>114// CIR-DAG: ![[CoroHandleVoid:.*]] = !cir.record<struct "std::coroutine_handle<void>" padded {!u8i}>115// CIR-DAG: ![[CoroHandlePromiseVoid:rec_.*]] = !cir.record<struct "std::coroutine_handle<folly::coro::Task<void>::promise_type>" padded {!u8i}>116// CIR-DAG: ![[CoroHandlePromiseInt:rec_.*]] = !cir.record<struct "std::coroutine_handle<folly::coro::Task<int>::promise_type>" padded {!u8i}>117// CIR-DAG: ![[SuspendAlways:.*]] = !cir.record<struct "std::suspend_always" padded {!u8i}>118 119// CIR: module {{.*}} {120// CIR-NEXT: cir.global external @_ZN5folly4coro9co_invokeE = #cir.zero : !rec_folly3A3Acoro3A3Aco_invoke_fn121 122// CIR: cir.func builtin private @__builtin_coro_id(!u32i, !cir.ptr<!void>, !cir.ptr<!void>, !cir.ptr<!void>) -> !u32i123// CIR: cir.func builtin private @__builtin_coro_alloc(!u32i) -> !cir.bool124// CIR: cir.func builtin private @__builtin_coro_size() -> !u64i125// CIR: cir.func builtin private @__builtin_coro_begin(!u32i, !cir.ptr<!void>) -> !cir.ptr<!void>126 127using VoidTask = folly::coro::Task<void>;128 129VoidTask silly_task() {130 co_await std::suspend_always();131}132 133// CIR: cir.func coroutine dso_local @_Z10silly_taskv() -> ![[VoidTask]]134// CIR: %[[VoidTaskAddr:.*]] = cir.alloca ![[VoidTask]], {{.*}}, ["__retval"]135// CIR: %[[SavedFrameAddr:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__coro_frame_addr"]136// CIR: %[[VoidPromisseAddr:.*]] = cir.alloca ![[VoidPromisse]], {{.*}}, ["__promise"]137 138// Get coroutine id with __builtin_coro_id.139 140// CIR: %[[NullPtr:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>141// CIR: %[[Align:.*]] = cir.const #cir.int<16> : !u32i142// CIR: %[[CoroId:.*]] = cir.call @__builtin_coro_id(%[[Align]], %[[NullPtr]], %[[NullPtr]], %[[NullPtr]])143 144// Perform allocation calling operator 'new' depending on __builtin_coro_alloc and145// call __builtin_coro_begin for the final coroutine frame address.146 147// CIR: %[[ShouldAlloc:.*]] = cir.call @__builtin_coro_alloc(%[[CoroId]]) : (!u32i) -> !cir.bool148// CIR: cir.store{{.*}} %[[NullPtr]], %[[SavedFrameAddr]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>149// CIR: cir.if %[[ShouldAlloc]] {150// CIR: %[[CoroSize:.*]] = cir.call @__builtin_coro_size() : () -> !u64i151// CIR: %[[AllocAddr:.*]] = cir.call @_Znwm(%[[CoroSize]]) : (!u64i) -> !cir.ptr<!void>152// CIR: cir.store{{.*}} %[[AllocAddr]], %[[SavedFrameAddr]] : !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>153// CIR: }154// CIR: %[[Load0:.*]] = cir.load{{.*}} %[[SavedFrameAddr]] : !cir.ptr<!cir.ptr<!void>>, !cir.ptr<!void>155// CIR: %[[CoroFrameAddr:.*]] = cir.call @__builtin_coro_begin(%[[CoroId]], %[[Load0]])156 157// Call promise.get_return_object() to retrieve the task object.158 159// CIR: %[[RetObj:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type17get_return_objectEv(%[[VoidPromisseAddr]]) nothrow : {{.*}} -> ![[VoidTask]]160// CIR: cir.store{{.*}} %[[RetObj]], %[[VoidTaskAddr]] : ![[VoidTask]]161// Start a new scope for the actual codegen for co_await, create temporary allocas for162// holding coroutine handle and the suspend_always struct.163 164// CIR: cir.scope {165// CIR: %[[SuspendAlwaysAddr:.*]] = cir.alloca ![[SuspendAlways]], {{.*}} ["ref.tmp0"] {alignment = 1 : i64}166// CIR: %[[CoroHandleVoidAddr:.*]] = cir.alloca ![[CoroHandleVoid]], {{.*}} ["agg.tmp0"] {alignment = 1 : i64}167// CIR: %[[CoroHandlePromiseAddr:.*]] = cir.alloca ![[CoroHandlePromiseVoid]], {{.*}} ["agg.tmp1"] {alignment = 1 : i64}168 169// Effectively execute `coawait promise_type::initial_suspend()` by calling initial_suspend() and getting170// the suspend_always struct to use for cir.await. Note that we return by-value since we defer ABI lowering171// to later passes, same is done elsewhere.172 173// CIR: %[[Tmp0:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(%[[VoidPromisseAddr]])174// CIR: cir.store{{.*}} %[[Tmp0:.*]], %[[SuspendAlwaysAddr]]175 176//177// Here we start mapping co_await to cir.await.178//179 180// First regions `ready` has a special cir.yield code to veto suspension.181 182// CIR: cir.await(init, ready : {183// CIR: %[[ReadyVeto:.*]] = cir.scope {184// CIR: %[[TmpCallRes:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SuspendAlwaysAddr]])185// CIR: cir.yield %[[TmpCallRes:.*]] : !cir.bool186// CIR: }187// CIR: cir.condition(%[[ReadyVeto]])188 189// Second region `suspend` contains the actual suspend logic.190//191// - Start by getting the coroutine handle using from_address().192// - Implicit convert coroutine handle from task specific promisse193// specialization to a void one.194// - Call suspend_always::await_suspend() passing the handle.195//196// FIXME: add veto support for non-void await_suspends.197 198// CIR: }, suspend : {199// CIR: %[[FromAddrRes:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%[[CoroFrameAddr]])200// CIR: cir.store{{.*}} %[[FromAddrRes]], %[[CoroHandlePromiseAddr]] : ![[CoroHandlePromiseVoid]]201// CIR: %[[CoroHandlePromiseReload:.*]] = cir.load{{.*}} %[[CoroHandlePromiseAddr]]202// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CoroHandleVoidAddr]], %[[CoroHandlePromiseReload]])203// CIR: %[[CoroHandleVoidReload:.*]] = cir.load{{.*}} %[[CoroHandleVoidAddr]] : !cir.ptr<![[CoroHandleVoid]]>, ![[CoroHandleVoid]]204// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]], %[[CoroHandleVoidReload]])205// CIR: cir.yield206// CIR: }, resume : {207// CIR: cir.yield208// CIR: },)209// CIR: }210 211folly::coro::Task<int> byRef(const std::string& s) {212 co_return s.size();213}214 215// CIR: cir.func coroutine dso_local @_Z5byRefRKSt6string(%[[ARG:.*]]: !cir.ptr<![[StdString]]> {{.*}}) -> ![[IntTask]]216// CIR: %[[AllocaParam:.*]] = cir.alloca !cir.ptr<![[StdString]]>, {{.*}}, ["s", init, const]217// CIR: %[[IntTaskAddr:.*]] = cir.alloca ![[IntTask]], {{.*}}, ["__retval"]218// CIR: %[[SavedFrameAddr:.*]] = cir.alloca !cir.ptr<!void>, !cir.ptr<!cir.ptr<!void>>, ["__coro_frame_addr"]219// CIR: %[[AllocaFnUse:.*]] = cir.alloca !cir.ptr<![[StdString]]>, {{.*}}, ["s", init, const]220// CIR: %[[IntPromisseAddr:.*]] = cir.alloca ![[IntPromisse]], {{.*}}, ["__promise"]221// CIR: cir.store %[[ARG]], %[[AllocaParam]] : !cir.ptr<![[StdString]]>, {{.*}}222 223// Call promise.get_return_object() to retrieve the task object.224 225// CIR: %[[LOAD:.*]] = cir.load %[[AllocaParam]] : !cir.ptr<!cir.ptr<![[StdString]]>>, !cir.ptr<![[StdString]]>226// CIR: cir.store {{.*}} %[[LOAD]], %[[AllocaFnUse]] : !cir.ptr<![[StdString]]>, !cir.ptr<!cir.ptr<![[StdString]]>>227// CIR: %[[RetObj:.*]] = cir.call @_ZN5folly4coro4TaskIiE12promise_type17get_return_objectEv(%4) nothrow : {{.*}} -> ![[IntTask]]228// CIR: cir.store {{.*}} %[[RetObj]], %[[IntTaskAddr]] : ![[IntTask]]229// CIR: cir.scope {230// CIR: %[[SuspendAlwaysAddr:.*]] = cir.alloca ![[SuspendAlways]], {{.*}} ["ref.tmp0"] {alignment = 1 : i64}231// CIR: %[[CoroHandleVoidAddr:.*]] = cir.alloca ![[CoroHandleVoid]], {{.*}} ["agg.tmp0"] {alignment = 1 : i64}232// CIR: %[[CoroHandlePromiseAddr:.*]] = cir.alloca ![[CoroHandlePromiseInt]], {{.*}} ["agg.tmp1"] {alignment = 1 : i64}233// CIR: %[[Tmp0:.*]] = cir.call @_ZN5folly4coro4TaskIiE12promise_type15initial_suspendEv(%[[IntPromisseAddr]])234// CIR: cir.await(init, ready : {235// CIR: %[[ReadyVeto:.*]] = cir.scope {236// CIR: %[[TmpCallRes:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SuspendAlwaysAddr]])237// CIR: cir.yield %[[TmpCallRes:.*]] : !cir.bool238// CIR: }239// CIR: cir.condition(%[[ReadyVeto]])240// CIR: }, suspend : {241// CIR: %[[FromAddrRes:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIiE12promise_typeEE12from_addressEPv(%[[CoroFrameAddr:.*]])242// CIR: cir.store{{.*}} %[[FromAddrRes]], %[[CoroHandlePromiseAddr]] : ![[CoroHandlePromiseInt]]243// CIR: %[[CoroHandlePromiseReload:.*]] = cir.load{{.*}} %[[CoroHandlePromiseAddr]]244// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIiE12promise_typeEEES_IT_E(%[[CoroHandleVoidAddr]], %[[CoroHandlePromiseReload]])245// CIR: %[[CoroHandleVoidReload:.*]] = cir.load{{.*}} %[[CoroHandleVoidAddr]] : !cir.ptr<![[CoroHandleVoid]]>, ![[CoroHandleVoid]]246// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]], %[[CoroHandleVoidReload]])247// CIR: cir.yield248// CIR: }, resume : {249// CIR: cir.yield250// CIR: },)251