1*532dc62bSNikita Popov // RUN: %clang_cc1 -no-opaque-pointers -triple x86_64-unknown-linux-gnu -std=c++20 \
2a9b3d097SEric Fiselier // RUN:    -Wno-coroutine-missing-unhandled-exception -emit-llvm %s -o - -disable-llvm-passes \
3a9b3d097SEric Fiselier // RUN:   | FileCheck %s
48df64e94SGor Nishanov 
58df64e94SGor Nishanov namespace std {
68df64e94SGor Nishanov template <typename... T>
78df64e94SGor Nishanov struct coroutine_traits; // expected-note {{declared here}}
86dcb0eb3SGor Nishanov 
96dcb0eb3SGor Nishanov template <class Promise = void>
106dcb0eb3SGor Nishanov struct coroutine_handle {
116dcb0eb3SGor Nishanov   coroutine_handle() = default;
from_addressstd::coroutine_handle12516803dcSXun Li   static coroutine_handle from_address(void *) noexcept { return {}; }
136dcb0eb3SGor Nishanov };
146dcb0eb3SGor Nishanov 
156dcb0eb3SGor Nishanov template <>
166dcb0eb3SGor Nishanov struct coroutine_handle<void> {
from_addressstd::coroutine_handle176dcb0eb3SGor Nishanov   static coroutine_handle from_address(void *) { return {}; }
186dcb0eb3SGor Nishanov   coroutine_handle() = default;
196dcb0eb3SGor Nishanov   template <class PromiseType>
coroutine_handlestd::coroutine_handle20516803dcSXun Li   coroutine_handle(coroutine_handle<PromiseType>) noexcept {}
216dcb0eb3SGor Nishanov };
226dcb0eb3SGor Nishanov 
23f692e7dbSEric Fiselier struct nothrow_t {};
24f692e7dbSEric Fiselier constexpr nothrow_t nothrow = {};
25f692e7dbSEric Fiselier 
26f692e7dbSEric Fiselier } // end namespace std
27f692e7dbSEric Fiselier 
28f692e7dbSEric Fiselier // Required when get_return_object_on_allocation_failure() is defined by
29f692e7dbSEric Fiselier // the promise.
30f692e7dbSEric Fiselier using SizeT = decltype(sizeof(int));
31f692e7dbSEric Fiselier void* operator new(SizeT __sz, const std::nothrow_t&) noexcept;
32f692e7dbSEric Fiselier void  operator delete(void* __p, const std::nothrow_t&) noexcept;
33f692e7dbSEric Fiselier 
348df64e94SGor Nishanov 
358df64e94SGor Nishanov struct suspend_always {
await_readysuspend_always36516803dcSXun Li   bool await_ready() noexcept { return false; }
await_suspendsuspend_always37ec117158SChuanqi Xu   void await_suspend(std::coroutine_handle<>) noexcept {}
await_resumesuspend_always38516803dcSXun Li   void await_resume() noexcept {}
398df64e94SGor Nishanov };
408df64e94SGor Nishanov 
418df64e94SGor Nishanov struct global_new_delete_tag {};
428df64e94SGor Nishanov 
438df64e94SGor Nishanov template <>
44ec117158SChuanqi Xu struct std::coroutine_traits<void, global_new_delete_tag> {
458df64e94SGor Nishanov   struct promise_type {
get_return_objectstd::coroutine_traits::promise_type468df64e94SGor Nishanov     void get_return_object() {}
initial_suspendstd::coroutine_traits::promise_type478df64e94SGor Nishanov     suspend_always initial_suspend() { return {}; }
final_suspendstd::coroutine_traits::promise_type48516803dcSXun Li     suspend_always final_suspend() noexcept { return {}; }
return_voidstd::coroutine_traits::promise_type498df64e94SGor Nishanov     void return_void() {}
508df64e94SGor Nishanov   };
518df64e94SGor Nishanov };
528df64e94SGor Nishanov 
538df64e94SGor Nishanov // CHECK-LABEL: f0(
f0(global_new_delete_tag)548df64e94SGor Nishanov extern "C" void f0(global_new_delete_tag) {
558df64e94SGor Nishanov   // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
56aa6e9a99SGor Nishanov   // CHECK: %[[NeedAlloc:.+]] = call i1 @llvm.coro.alloc(token %[[ID]])
57aa6e9a99SGor Nishanov   // CHECK: br i1 %[[NeedAlloc]], label %[[AllocBB:.+]], label %[[InitBB:.+]]
58aa6e9a99SGor Nishanov 
59aa6e9a99SGor Nishanov   // CHECK: [[AllocBB]]:
608df64e94SGor Nishanov   // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
611b1c8d83Shyeongyu kim   // CHECK: %[[MEM:.+]] = call noalias noundef nonnull i8* @_Znwm(i64 noundef %[[SIZE]])
62aa6e9a99SGor Nishanov   // CHECK: br label %[[InitBB]]
63aa6e9a99SGor Nishanov 
64aa6e9a99SGor Nishanov   // CHECK: [[InitBB]]:
65aa6e9a99SGor Nishanov   // CHECK: %[[PHI:.+]] = phi i8* [ null, %{{.+}} ], [ %call, %[[AllocBB]] ]
6668fe6ee7SGor Nishanov   // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(token %[[ID]], i8* %[[PHI]])
678df64e94SGor Nishanov 
688df64e94SGor Nishanov   // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
696c4530c6SGor Nishanov   // CHECK: %[[NeedDealloc:.+]] = icmp ne i8* %[[MEM]], null
706c4530c6SGor Nishanov   // CHECK: br i1 %[[NeedDealloc]], label %[[FreeBB:.+]], label %[[Afterwards:.+]]
716c4530c6SGor Nishanov 
726c4530c6SGor Nishanov   // CHECK: [[FreeBB]]:
731b1c8d83Shyeongyu kim   // CHECK: call void @_ZdlPv(i8* noundef %[[MEM]])
746c4530c6SGor Nishanov   // CHECK: br label %[[Afterwards]]
756c4530c6SGor Nishanov 
766c4530c6SGor Nishanov   // CHECK: [[Afterwards]]:
776c4530c6SGor Nishanov   // CHECK: ret void
7890be1213SGor Nishanov   co_return;
798df64e94SGor Nishanov }
808df64e94SGor Nishanov 
818df64e94SGor Nishanov struct promise_new_tag {};
828df64e94SGor Nishanov 
838df64e94SGor Nishanov template <>
84ec117158SChuanqi Xu struct std::coroutine_traits<void, promise_new_tag> {
858df64e94SGor Nishanov   struct promise_type {
868df64e94SGor Nishanov     void *operator new(unsigned long);
get_return_objectstd::coroutine_traits::promise_type878df64e94SGor Nishanov     void get_return_object() {}
initial_suspendstd::coroutine_traits::promise_type888df64e94SGor Nishanov     suspend_always initial_suspend() { return {}; }
final_suspendstd::coroutine_traits::promise_type89516803dcSXun Li     suspend_always final_suspend() noexcept { return {}; }
return_voidstd::coroutine_traits::promise_type908df64e94SGor Nishanov     void return_void() {}
918df64e94SGor Nishanov   };
928df64e94SGor Nishanov };
938df64e94SGor Nishanov 
948df64e94SGor Nishanov // CHECK-LABEL: f1(
f1(promise_new_tag)958df64e94SGor Nishanov extern "C" void f1(promise_new_tag ) {
968df64e94SGor Nishanov   // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
978df64e94SGor Nishanov   // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
981b1c8d83Shyeongyu kim   // CHECK: call noundef i8* @_ZNSt16coroutine_traitsIJv15promise_new_tagEE12promise_typenwEm(i64 noundef %[[SIZE]])
998df64e94SGor Nishanov 
10068fe6ee7SGor Nishanov   // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
1018df64e94SGor Nishanov   // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
1021b1c8d83Shyeongyu kim   // CHECK: call void @_ZdlPv(i8* noundef %[[MEM]])
10390be1213SGor Nishanov   co_return;
1048df64e94SGor Nishanov }
1058df64e94SGor Nishanov 
10698606221SBrian Gesiak struct promise_matching_placement_new_tag {};
10798606221SBrian Gesiak 
10898606221SBrian Gesiak template <>
109ec117158SChuanqi Xu struct std::coroutine_traits<void, promise_matching_placement_new_tag, int, float, double> {
11098606221SBrian Gesiak   struct promise_type {
11198606221SBrian Gesiak     void *operator new(unsigned long, promise_matching_placement_new_tag,
11298606221SBrian Gesiak                        int, float, double);
get_return_objectstd::coroutine_traits::promise_type11398606221SBrian Gesiak     void get_return_object() {}
initial_suspendstd::coroutine_traits::promise_type11498606221SBrian Gesiak     suspend_always initial_suspend() { return {}; }
final_suspendstd::coroutine_traits::promise_type115516803dcSXun Li     suspend_always final_suspend() noexcept { return {}; }
return_voidstd::coroutine_traits::promise_type11698606221SBrian Gesiak     void return_void() {}
11798606221SBrian Gesiak   };
11898606221SBrian Gesiak };
11998606221SBrian Gesiak 
12098606221SBrian Gesiak // CHECK-LABEL: f1a(
f1a(promise_matching_placement_new_tag,int x,float y,double z)12198606221SBrian Gesiak extern "C" void f1a(promise_matching_placement_new_tag, int x, float y , double z) {
12298606221SBrian Gesiak   // CHECK: store i32 %x, i32* %x.addr, align 4
12398606221SBrian Gesiak   // CHECK: store float %y, float* %y.addr, align 4
12498606221SBrian Gesiak   // CHECK: store double %z, double* %z.addr, align 8
12598606221SBrian Gesiak   // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
12698606221SBrian Gesiak   // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
12798606221SBrian Gesiak   // CHECK: %[[INT:.+]] = load i32, i32* %x.addr, align 4
12898606221SBrian Gesiak   // CHECK: %[[FLOAT:.+]] = load float, float* %y.addr, align 4
12998606221SBrian Gesiak   // CHECK: %[[DOUBLE:.+]] = load double, double* %z.addr, align 8
1301b1c8d83Shyeongyu kim   // CHECK: call noundef i8* @_ZNSt16coroutine_traitsIJv34promise_matching_placement_new_tagifdEE12promise_typenwEmS0_ifd(i64 noundef %[[SIZE]], i32 noundef %[[INT]], float noundef %[[FLOAT]], double noundef %[[DOUBLE]])
13198606221SBrian Gesiak   co_return;
13298606221SBrian Gesiak }
13398606221SBrian Gesiak 
134cb024024SBrian Gesiak // Declare a placement form operator new, such as the one described in
135cb024024SBrian Gesiak // C++ 18.6.1.3.1, which takes a void* argument.
136cb024024SBrian Gesiak void* operator new(SizeT __sz, void *__p) noexcept;
137cb024024SBrian Gesiak 
138cb024024SBrian Gesiak struct promise_matching_global_placement_new_tag {};
139cb024024SBrian Gesiak struct dummy {};
140cb024024SBrian Gesiak template <>
141ec117158SChuanqi Xu struct std::coroutine_traits<void, promise_matching_global_placement_new_tag, dummy *> {
142cb024024SBrian Gesiak   struct promise_type {
get_return_objectstd::coroutine_traits::promise_type143cb024024SBrian Gesiak     void get_return_object() {}
initial_suspendstd::coroutine_traits::promise_type144cb024024SBrian Gesiak     suspend_always initial_suspend() { return {}; }
final_suspendstd::coroutine_traits::promise_type145516803dcSXun Li     suspend_always final_suspend() noexcept { return {}; }
return_voidstd::coroutine_traits::promise_type146cb024024SBrian Gesiak     void return_void() {}
147cb024024SBrian Gesiak   };
148cb024024SBrian Gesiak };
149cb024024SBrian Gesiak 
150cb024024SBrian Gesiak // A coroutine that takes a single pointer argument should not invoke this
151cb024024SBrian Gesiak // placement form operator. [dcl.fct.def.coroutine]/7 dictates that lookup for
152cb024024SBrian Gesiak // allocation functions matching the coroutine function's signature be done
153cb024024SBrian Gesiak // within the scope of the promise type's class.
154cb024024SBrian Gesiak // CHECK-LABEL: f1b(
f1b(promise_matching_global_placement_new_tag,dummy *)155cb024024SBrian Gesiak extern "C" void f1b(promise_matching_global_placement_new_tag, dummy *) {
1561b1c8d83Shyeongyu kim   // CHECK: call noalias noundef nonnull i8* @_Znwm(i64
157cb024024SBrian Gesiak   co_return;
158cb024024SBrian Gesiak }
159cb024024SBrian Gesiak 
1608df64e94SGor Nishanov struct promise_delete_tag {};
1618df64e94SGor Nishanov 
1628df64e94SGor Nishanov template <>
163ec117158SChuanqi Xu struct std::coroutine_traits<void, promise_delete_tag> {
1648df64e94SGor Nishanov   struct promise_type {
1658df64e94SGor Nishanov     void operator delete(void*);
get_return_objectstd::coroutine_traits::promise_type1668df64e94SGor Nishanov     void get_return_object() {}
initial_suspendstd::coroutine_traits::promise_type1678df64e94SGor Nishanov     suspend_always initial_suspend() { return {}; }
final_suspendstd::coroutine_traits::promise_type168516803dcSXun Li     suspend_always final_suspend() noexcept { return {}; }
return_voidstd::coroutine_traits::promise_type1698df64e94SGor Nishanov     void return_void() {}
1708df64e94SGor Nishanov   };
1718df64e94SGor Nishanov };
1728df64e94SGor Nishanov 
1738df64e94SGor Nishanov // CHECK-LABEL: f2(
f2(promise_delete_tag)1748df64e94SGor Nishanov extern "C" void f2(promise_delete_tag) {
1758df64e94SGor Nishanov   // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
1768df64e94SGor Nishanov   // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
1771b1c8d83Shyeongyu kim   // CHECK: call noalias noundef nonnull i8* @_Znwm(i64 noundef %[[SIZE]])
1788df64e94SGor Nishanov 
17968fe6ee7SGor Nishanov   // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
1808df64e94SGor Nishanov   // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
1811b1c8d83Shyeongyu kim   // CHECK: call void @_ZNSt16coroutine_traitsIJv18promise_delete_tagEE12promise_typedlEPv(i8* noundef %[[MEM]])
18290be1213SGor Nishanov   co_return;
1838df64e94SGor Nishanov }
1848df64e94SGor Nishanov 
1858df64e94SGor Nishanov struct promise_sized_delete_tag {};
1868df64e94SGor Nishanov 
1878df64e94SGor Nishanov template <>
188ec117158SChuanqi Xu struct std::coroutine_traits<void, promise_sized_delete_tag> {
1898df64e94SGor Nishanov   struct promise_type {
1908df64e94SGor Nishanov     void operator delete(void*, unsigned long);
get_return_objectstd::coroutine_traits::promise_type1918df64e94SGor Nishanov     void get_return_object() {}
initial_suspendstd::coroutine_traits::promise_type1928df64e94SGor Nishanov     suspend_always initial_suspend() { return {}; }
final_suspendstd::coroutine_traits::promise_type193516803dcSXun Li     suspend_always final_suspend() noexcept { return {}; }
return_voidstd::coroutine_traits::promise_type1948df64e94SGor Nishanov     void return_void() {}
1958df64e94SGor Nishanov   };
1968df64e94SGor Nishanov };
1978df64e94SGor Nishanov 
1988df64e94SGor Nishanov // CHECK-LABEL: f3(
f3(promise_sized_delete_tag)1998df64e94SGor Nishanov extern "C" void f3(promise_sized_delete_tag) {
2008df64e94SGor Nishanov   // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
2018df64e94SGor Nishanov   // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
2021b1c8d83Shyeongyu kim   // CHECK: call noalias noundef nonnull i8* @_Znwm(i64 noundef %[[SIZE]])
2038df64e94SGor Nishanov 
20468fe6ee7SGor Nishanov   // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(
2058df64e94SGor Nishanov   // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]])
2068df64e94SGor Nishanov   // CHECK: %[[SIZE2:.+]] = call i64 @llvm.coro.size.i64()
2071b1c8d83Shyeongyu kim   // CHECK: call void @_ZNSt16coroutine_traitsIJv24promise_sized_delete_tagEE12promise_typedlEPvm(i8* noundef %[[MEM]], i64 noundef %[[SIZE2]])
2083aa9eb38SGor Nishanov   co_return;
2093aa9eb38SGor Nishanov }
2103aa9eb38SGor Nishanov 
2113aa9eb38SGor Nishanov struct promise_on_alloc_failure_tag {};
2123aa9eb38SGor Nishanov 
2133aa9eb38SGor Nishanov template <>
214ec117158SChuanqi Xu struct std::coroutine_traits<int, promise_on_alloc_failure_tag> {
2153aa9eb38SGor Nishanov   struct promise_type {
get_return_objectstd::coroutine_traits::promise_type216a9b3d097SEric Fiselier     int get_return_object() { return 0; }
initial_suspendstd::coroutine_traits::promise_type2173aa9eb38SGor Nishanov     suspend_always initial_suspend() { return {}; }
final_suspendstd::coroutine_traits::promise_type218516803dcSXun Li     suspend_always final_suspend() noexcept { return {}; }
return_voidstd::coroutine_traits::promise_type2193aa9eb38SGor Nishanov     void return_void() {}
get_return_object_on_allocation_failurestd::coroutine_traits::promise_type2203aa9eb38SGor Nishanov     static int get_return_object_on_allocation_failure() { return -1; }
2213aa9eb38SGor Nishanov   };
2223aa9eb38SGor Nishanov };
2233aa9eb38SGor Nishanov 
2243aa9eb38SGor Nishanov // CHECK-LABEL: f4(
f4(promise_on_alloc_failure_tag)2253aa9eb38SGor Nishanov extern "C" int f4(promise_on_alloc_failure_tag) {
2266a470689SGor Nishanov   // CHECK: %[[RetVal:.+]] = alloca i32
2273aa9eb38SGor Nishanov   // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16
2283aa9eb38SGor Nishanov   // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64()
2291b1c8d83Shyeongyu kim   // CHECK: %[[MEM:.+]] = call noalias noundef i8* @_ZnwmRKSt9nothrow_t(i64 noundef %[[SIZE]], %"struct.std::nothrow_t"* noundef nonnull align 1 dereferenceable(1) @_ZStL7nothrow)
2303aa9eb38SGor Nishanov   // CHECK: %[[OK:.+]] = icmp ne i8* %[[MEM]], null
2313aa9eb38SGor Nishanov   // CHECK: br i1 %[[OK]], label %[[OKBB:.+]], label %[[ERRBB:.+]]
2323aa9eb38SGor Nishanov 
2333aa9eb38SGor Nishanov   // CHECK: [[ERRBB]]:
2341b1c8d83Shyeongyu kim   // CHECK:   %[[FailRet:.+]] = call noundef i32 @_ZNSt16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type39get_return_object_on_allocation_failureEv(
2356a470689SGor Nishanov   // CHECK:   store i32 %[[FailRet]], i32* %[[RetVal]]
2366a470689SGor Nishanov   // CHECK:   br label %[[RetBB:.+]]
2376a470689SGor Nishanov 
2386a470689SGor Nishanov   // CHECK: [[OKBB]]:
2391b1c8d83Shyeongyu kim   // CHECK:   %[[OkRet:.+]] = call noundef i32 @_ZNSt16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type17get_return_objectEv(
2406a470689SGor Nishanov 
2416a470689SGor Nishanov   // CHECK: [[RetBB]]:
2426a470689SGor Nishanov   // CHECK:   %[[LoadRet:.+]] = load i32, i32* %[[RetVal]], align 4
2436a470689SGor Nishanov   // CHECK:   ret i32 %[[LoadRet]]
24490be1213SGor Nishanov   co_return;
2458df64e94SGor Nishanov }
246