Clang Project

clang_source_code/test/CodeGenCoroutines/coro-alloc.cpp
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
5namespace std {
6namespace experimental {
7template <typename... T>
8struct coroutine_traits; // expected-note {{declared here}}
9
10template <class Promise = void>
11struct coroutine_handle {
12  coroutine_handle() = default;
13  static coroutine_handle from_address(void *) { return {}; }
14};
15
16template <>
17struct 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
26struct nothrow_t {};
27constexpr nothrow_t nothrow = {};
28
29} // end namespace std
30
31// Required when get_return_object_on_allocation_failure() is defined by
32// the promise.
33using SizeT = decltype(sizeof(int));
34void* operator new(SizeT __sz, const std::nothrow_t&) noexcept;
35void  operator delete(void* __p, const std::nothrow_t&) noexcept;
36
37
38struct suspend_always {
39  bool await_ready() { return false; }
40  void await_suspend(std::experimental::coroutine_handle<>) {}
41  void await_resume() {}
42};
43
44struct global_new_delete_tag {};
45
46template<>
47struct 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(
57extern "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
84struct promise_new_tag {};
85
86template<>
87struct 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(
98extern "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
109struct promise_matching_placement_new_tag {};
110
111template<>
112struct 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(
124extern "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.
139void* operator new(SizeT __sz, void *__p) noexcept;
140
141struct promise_matching_global_placement_new_tag {};
142struct dummy {};
143template<>
144struct 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(
158extern "C" void f1b(promise_matching_global_placement_new_tag, dummy *) {
159  // CHECK: call i8* @_Znwm(i64
160  co_return;
161}
162
163struct promise_delete_tag {};
164
165template<>
166struct 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(
177extern "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
188struct promise_sized_delete_tag {};
189
190template<>
191struct 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(
202extern "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
214struct promise_on_alloc_failure_tag {};
215
216template<>
217struct 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(
228extern "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