| 1 | // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 \ |
| 2 | // RUN: -Wno-coroutine-missing-unhandled-exception -emit-llvm %s -o - -disable-llvm-passes \ |
| 3 | // RUN: | FileCheck %s |
| 4 | |
| 5 | namespace std { |
| 6 | namespace experimental { |
| 7 | template <typename... T> |
| 8 | struct coroutine_traits; // expected-note {{declared here}} |
| 9 | |
| 10 | template <class Promise = void> |
| 11 | struct coroutine_handle { |
| 12 | coroutine_handle() = default; |
| 13 | static coroutine_handle from_address(void *) { return {}; } |
| 14 | }; |
| 15 | |
| 16 | template <> |
| 17 | struct coroutine_handle<void> { |
| 18 | static coroutine_handle from_address(void *) { return {}; } |
| 19 | coroutine_handle() = default; |
| 20 | template <class PromiseType> |
| 21 | coroutine_handle(coroutine_handle<PromiseType>) {} |
| 22 | }; |
| 23 | |
| 24 | } // end namespace experimental |
| 25 | |
| 26 | struct nothrow_t {}; |
| 27 | constexpr nothrow_t nothrow = {}; |
| 28 | |
| 29 | } // end namespace std |
| 30 | |
| 31 | // Required when get_return_object_on_allocation_failure() is defined by |
| 32 | // the promise. |
| 33 | using SizeT = decltype(sizeof(int)); |
| 34 | void* operator new(SizeT __sz, const std::nothrow_t&) noexcept; |
| 35 | void operator delete(void* __p, const std::nothrow_t&) noexcept; |
| 36 | |
| 37 | |
| 38 | struct suspend_always { |
| 39 | bool await_ready() { return false; } |
| 40 | void await_suspend(std::experimental::coroutine_handle<>) {} |
| 41 | void await_resume() {} |
| 42 | }; |
| 43 | |
| 44 | struct global_new_delete_tag {}; |
| 45 | |
| 46 | template<> |
| 47 | struct std::experimental::coroutine_traits<void, global_new_delete_tag> { |
| 48 | struct promise_type { |
| 49 | void get_return_object() {} |
| 50 | suspend_always initial_suspend() { return {}; } |
| 51 | suspend_always final_suspend() { return {}; } |
| 52 | void return_void() {} |
| 53 | }; |
| 54 | }; |
| 55 | |
| 56 | // CHECK-LABEL: f0( |
| 57 | extern "C" void f0(global_new_delete_tag) { |
| 58 | // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16 |
| 59 | // CHECK: %[[NeedAlloc:.+]] = call i1 @llvm.coro.alloc(token %[[ID]]) |
| 60 | // CHECK: br i1 %[[NeedAlloc]], label %[[AllocBB:.+]], label %[[InitBB:.+]] |
| 61 | |
| 62 | // CHECK: [[AllocBB]]: |
| 63 | // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() |
| 64 | // CHECK: %[[MEM:.+]] = call i8* @_Znwm(i64 %[[SIZE]]) |
| 65 | // CHECK: br label %[[InitBB]] |
| 66 | |
| 67 | // CHECK: [[InitBB]]: |
| 68 | // CHECK: %[[PHI:.+]] = phi i8* [ null, %{{.+}} ], [ %call, %[[AllocBB]] ] |
| 69 | // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin(token %[[ID]], i8* %[[PHI]]) |
| 70 | |
| 71 | // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]]) |
| 72 | // CHECK: %[[NeedDealloc:.+]] = icmp ne i8* %[[MEM]], null |
| 73 | // CHECK: br i1 %[[NeedDealloc]], label %[[FreeBB:.+]], label %[[Afterwards:.+]] |
| 74 | |
| 75 | // CHECK: [[FreeBB]]: |
| 76 | // CHECK: call void @_ZdlPv(i8* %[[MEM]]) |
| 77 | // CHECK: br label %[[Afterwards]] |
| 78 | |
| 79 | // CHECK: [[Afterwards]]: |
| 80 | // CHECK: ret void |
| 81 | co_return; |
| 82 | } |
| 83 | |
| 84 | struct promise_new_tag {}; |
| 85 | |
| 86 | template<> |
| 87 | struct std::experimental::coroutine_traits<void, promise_new_tag> { |
| 88 | struct promise_type { |
| 89 | void *operator new(unsigned long); |
| 90 | void get_return_object() {} |
| 91 | suspend_always initial_suspend() { return {}; } |
| 92 | suspend_always final_suspend() { return {}; } |
| 93 | void return_void() {} |
| 94 | }; |
| 95 | }; |
| 96 | |
| 97 | // CHECK-LABEL: f1( |
| 98 | extern "C" void f1(promise_new_tag ) { |
| 99 | // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16 |
| 100 | // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() |
| 101 | // CHECK: call i8* @_ZNSt12experimental16coroutine_traitsIJv15promise_new_tagEE12promise_typenwEm(i64 %[[SIZE]]) |
| 102 | |
| 103 | // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin( |
| 104 | // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]]) |
| 105 | // CHECK: call void @_ZdlPv(i8* %[[MEM]]) |
| 106 | co_return; |
| 107 | } |
| 108 | |
| 109 | struct promise_matching_placement_new_tag {}; |
| 110 | |
| 111 | template<> |
| 112 | struct std::experimental::coroutine_traits<void, promise_matching_placement_new_tag, int, float, double> { |
| 113 | struct promise_type { |
| 114 | void *operator new(unsigned long, promise_matching_placement_new_tag, |
| 115 | int, float, double); |
| 116 | void get_return_object() {} |
| 117 | suspend_always initial_suspend() { return {}; } |
| 118 | suspend_always final_suspend() { return {}; } |
| 119 | void return_void() {} |
| 120 | }; |
| 121 | }; |
| 122 | |
| 123 | // CHECK-LABEL: f1a( |
| 124 | extern "C" void f1a(promise_matching_placement_new_tag, int x, float y , double z) { |
| 125 | // CHECK: store i32 %x, i32* %x.addr, align 4 |
| 126 | // CHECK: store float %y, float* %y.addr, align 4 |
| 127 | // CHECK: store double %z, double* %z.addr, align 8 |
| 128 | // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16 |
| 129 | // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() |
| 130 | // CHECK: %[[INT:.+]] = load i32, i32* %x.addr, align 4 |
| 131 | // CHECK: %[[FLOAT:.+]] = load float, float* %y.addr, align 4 |
| 132 | // CHECK: %[[DOUBLE:.+]] = load double, double* %z.addr, align 8 |
| 133 | // CHECK: call i8* @_ZNSt12experimental16coroutine_traitsIJv34promise_matching_placement_new_tagifdEE12promise_typenwEmS1_ifd(i64 %[[SIZE]], i32 %[[INT]], float %[[FLOAT]], double %[[DOUBLE]]) |
| 134 | co_return; |
| 135 | } |
| 136 | |
| 137 | // Declare a placement form operator new, such as the one described in |
| 138 | // C++ 18.6.1.3.1, which takes a void* argument. |
| 139 | void* operator new(SizeT __sz, void *__p) noexcept; |
| 140 | |
| 141 | struct promise_matching_global_placement_new_tag {}; |
| 142 | struct dummy {}; |
| 143 | template<> |
| 144 | struct std::experimental::coroutine_traits<void, promise_matching_global_placement_new_tag, dummy*> { |
| 145 | struct promise_type { |
| 146 | void get_return_object() {} |
| 147 | suspend_always initial_suspend() { return {}; } |
| 148 | suspend_always final_suspend() { return {}; } |
| 149 | void return_void() {} |
| 150 | }; |
| 151 | }; |
| 152 | |
| 153 | // A coroutine that takes a single pointer argument should not invoke this |
| 154 | // placement form operator. [dcl.fct.def.coroutine]/7 dictates that lookup for |
| 155 | // allocation functions matching the coroutine function's signature be done |
| 156 | // within the scope of the promise type's class. |
| 157 | // CHECK-LABEL: f1b( |
| 158 | extern "C" void f1b(promise_matching_global_placement_new_tag, dummy *) { |
| 159 | // CHECK: call i8* @_Znwm(i64 |
| 160 | co_return; |
| 161 | } |
| 162 | |
| 163 | struct promise_delete_tag {}; |
| 164 | |
| 165 | template<> |
| 166 | struct std::experimental::coroutine_traits<void, promise_delete_tag> { |
| 167 | struct promise_type { |
| 168 | void operator delete(void*); |
| 169 | void get_return_object() {} |
| 170 | suspend_always initial_suspend() { return {}; } |
| 171 | suspend_always final_suspend() { return {}; } |
| 172 | void return_void() {} |
| 173 | }; |
| 174 | }; |
| 175 | |
| 176 | // CHECK-LABEL: f2( |
| 177 | extern "C" void f2(promise_delete_tag) { |
| 178 | // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16 |
| 179 | // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() |
| 180 | // CHECK: call i8* @_Znwm(i64 %[[SIZE]]) |
| 181 | |
| 182 | // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin( |
| 183 | // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]]) |
| 184 | // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJv18promise_delete_tagEE12promise_typedlEPv(i8* %[[MEM]]) |
| 185 | co_return; |
| 186 | } |
| 187 | |
| 188 | struct promise_sized_delete_tag {}; |
| 189 | |
| 190 | template<> |
| 191 | struct std::experimental::coroutine_traits<void, promise_sized_delete_tag> { |
| 192 | struct promise_type { |
| 193 | void operator delete(void*, unsigned long); |
| 194 | void get_return_object() {} |
| 195 | suspend_always initial_suspend() { return {}; } |
| 196 | suspend_always final_suspend() { return {}; } |
| 197 | void return_void() {} |
| 198 | }; |
| 199 | }; |
| 200 | |
| 201 | // CHECK-LABEL: f3( |
| 202 | extern "C" void f3(promise_sized_delete_tag) { |
| 203 | // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16 |
| 204 | // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() |
| 205 | // CHECK: call i8* @_Znwm(i64 %[[SIZE]]) |
| 206 | |
| 207 | // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin( |
| 208 | // CHECK: %[[MEM:.+]] = call i8* @llvm.coro.free(token %[[ID]], i8* %[[FRAME]]) |
| 209 | // CHECK: %[[SIZE2:.+]] = call i64 @llvm.coro.size.i64() |
| 210 | // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJv24promise_sized_delete_tagEE12promise_typedlEPvm(i8* %[[MEM]], i64 %[[SIZE2]]) |
| 211 | co_return; |
| 212 | } |
| 213 | |
| 214 | struct promise_on_alloc_failure_tag {}; |
| 215 | |
| 216 | template<> |
| 217 | struct std::experimental::coroutine_traits<int, promise_on_alloc_failure_tag> { |
| 218 | struct promise_type { |
| 219 | int get_return_object() { return 0; } |
| 220 | suspend_always initial_suspend() { return {}; } |
| 221 | suspend_always final_suspend() { return {}; } |
| 222 | void return_void() {} |
| 223 | static int get_return_object_on_allocation_failure() { return -1; } |
| 224 | }; |
| 225 | }; |
| 226 | |
| 227 | // CHECK-LABEL: f4( |
| 228 | extern "C" int f4(promise_on_alloc_failure_tag) { |
| 229 | // CHECK: %[[RetVal:.+]] = alloca i32 |
| 230 | // CHECK: %[[Gro:.+]] = alloca i32 |
| 231 | // CHECK: %[[ID:.+]] = call token @llvm.coro.id(i32 16 |
| 232 | // CHECK: %[[SIZE:.+]] = call i64 @llvm.coro.size.i64() |
| 233 | // CHECK: %[[MEM:.+]] = call i8* @_ZnwmRKSt9nothrow_t(i64 %[[SIZE]], %"struct.std::nothrow_t"* dereferenceable(1) @_ZStL7nothrow) |
| 234 | // CHECK: %[[OK:.+]] = icmp ne i8* %[[MEM]], null |
| 235 | // CHECK: br i1 %[[OK]], label %[[OKBB:.+]], label %[[ERRBB:.+]] |
| 236 | |
| 237 | // CHECK: [[ERRBB]]: |
| 238 | // CHECK: %[[FailRet:.+]] = call i32 @_ZNSt12experimental16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type39get_return_object_on_allocation_failureEv( |
| 239 | // CHECK: store i32 %[[FailRet]], i32* %[[RetVal]] |
| 240 | // CHECK: br label %[[RetBB:.+]] |
| 241 | |
| 242 | // CHECK: [[OKBB]]: |
| 243 | // CHECK: %[[OkRet:.+]] = call i32 @_ZNSt12experimental16coroutine_traitsIJi28promise_on_alloc_failure_tagEE12promise_type17get_return_objectEv( |
| 244 | // CHECK: store i32 %[[OkRet]], i32* %[[Gro]] |
| 245 | |
| 246 | // CHECK: %[[Tmp1:.*]] = load i32, i32* %[[Gro]] |
| 247 | // CHECK-NEXT: store i32 %[[Tmp1]], i32* %[[RetVal]] |
| 248 | // CHECK-NEXT: br label %[[RetBB]] |
| 249 | |
| 250 | // CHECK: [[RetBB]]: |
| 251 | // CHECK: %[[LoadRet:.+]] = load i32, i32* %[[RetVal]], align 4 |
| 252 | // CHECK: ret i32 %[[LoadRet]] |
| 253 | co_return; |
| 254 | } |
| 255 | |