1 | // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fcoroutines-ts -std=c++14 \ |
2 | // RUN: -emit-llvm %s -o - -disable-llvm-passes -Wno-coroutine -Wno-unused | FileCheck %s |
3 | |
4 | namespace std { |
5 | namespace experimental { |
6 | template <typename... T> |
7 | struct coroutine_traits; |
8 | |
9 | template <typename Promise = void> struct coroutine_handle; |
10 | |
11 | template <> |
12 | struct coroutine_handle<void> { |
13 | void *ptr; |
14 | static coroutine_handle from_address(void *); |
15 | void *address(); |
16 | }; |
17 | |
18 | template <typename Promise> |
19 | struct coroutine_handle : coroutine_handle<> { |
20 | static coroutine_handle from_address(void *); |
21 | }; |
22 | |
23 | } |
24 | } |
25 | |
26 | struct init_susp { |
27 | bool await_ready(); |
28 | void await_suspend(std::experimental::coroutine_handle<>); |
29 | void await_resume(); |
30 | }; |
31 | struct final_susp { |
32 | bool await_ready(); |
33 | void await_suspend(std::experimental::coroutine_handle<>); |
34 | void await_resume(); |
35 | }; |
36 | |
37 | struct suspend_always { |
38 | int stuff; |
39 | bool await_ready(); |
40 | void await_suspend(std::experimental::coroutine_handle<>); |
41 | void await_resume(); |
42 | }; |
43 | |
44 | template<> |
45 | struct std::experimental::coroutine_traits<void> { |
46 | struct promise_type { |
47 | void get_return_object(); |
48 | init_susp initial_suspend(); |
49 | final_susp final_suspend(); |
50 | void return_void(); |
51 | }; |
52 | }; |
53 | |
54 | // CHECK-LABEL: f0( |
55 | extern "C" void f0() { |
56 | // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin( |
57 | |
58 | // See if initial_suspend was issued: |
59 | // ---------------------------------- |
60 | // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type15initial_suspendEv( |
61 | // CHECK-NEXT: call zeroext i1 @_ZN9init_susp11await_readyEv(%struct.init_susp* |
62 | // CHECK: %[[INITSP_ID:.+]] = call token @llvm.coro.save( |
63 | // CHECK: call i8 @llvm.coro.suspend(token %[[INITSP_ID]], i1 false) |
64 | |
65 | co_await suspend_always{}; |
66 | // See if we need to suspend: |
67 | // -------------------------- |
68 | // CHECK: %[[READY:.+]] = call zeroext i1 @_ZN14suspend_always11await_readyEv(%struct.suspend_always* %[[AWAITABLE:.+]]) |
69 | // CHECK: br i1 %[[READY]], label %[[READY_BB:.+]], label %[[SUSPEND_BB:.+]] |
70 | |
71 | // If we are suspending: |
72 | // --------------------- |
73 | // CHECK: [[SUSPEND_BB]]: |
74 | // CHECK: %[[SUSPEND_ID:.+]] = call token @llvm.coro.save( |
75 | // --------------------------- |
76 | // Build the coroutine handle and pass it to await_suspend |
77 | // --------------------------- |
78 | // CHECK: call i8* @_ZNSt12experimental16coroutine_handleINS_16coroutine_traitsIJvEE12promise_typeEE12from_addressEPv(i8* %[[FRAME]]) |
79 | // ... many lines of code to coerce coroutine_handle into an i8* scalar |
80 | // CHECK: %[[CH:.+]] = load i8*, i8** %{{.+}} |
81 | // CHECK: call void @_ZN14suspend_always13await_suspendENSt12experimental16coroutine_handleIvEE(%struct.suspend_always* %[[AWAITABLE]], i8* %[[CH]]) |
82 | // ------------------------- |
83 | // Generate a suspend point: |
84 | // ------------------------- |
85 | // CHECK: %[[OUTCOME:.+]] = call i8 @llvm.coro.suspend(token %[[SUSPEND_ID]], i1 false) |
86 | // CHECK: switch i8 %[[OUTCOME]], label %[[RET_BB:.+]] [ |
87 | // CHECK: i8 0, label %[[READY_BB]] |
88 | // CHECK: i8 1, label %[[CLEANUP_BB:.+]] |
89 | // CHECK: ] |
90 | |
91 | // Cleanup code goes here: |
92 | // ----------------------- |
93 | // CHECK: [[CLEANUP_BB]]: |
94 | |
95 | // When coroutine is resumed, call await_resume |
96 | // -------------------------- |
97 | // CHECK: [[READY_BB]]: |
98 | // CHECK: call void @_ZN14suspend_always12await_resumeEv(%struct.suspend_always* %[[AWAITABLE]]) |
99 | |
100 | // See if final_suspend was issued: |
101 | // ---------------------------------- |
102 | // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type13final_suspendEv( |
103 | // CHECK-NEXT: call zeroext i1 @_ZN10final_susp11await_readyEv(%struct.final_susp* |
104 | // CHECK: %[[FINALSP_ID:.+]] = call token @llvm.coro.save( |
105 | // CHECK: call i8 @llvm.coro.suspend(token %[[FINALSP_ID]], i1 true) |
106 | } |
107 | |
108 | struct suspend_maybe { |
109 | float stuff; |
110 | ~suspend_maybe(); |
111 | bool await_ready(); |
112 | bool await_suspend(std::experimental::coroutine_handle<>); |
113 | void await_resume(); |
114 | }; |
115 | |
116 | |
117 | template<> |
118 | struct std::experimental::coroutine_traits<void,int> { |
119 | struct promise_type { |
120 | void get_return_object(); |
121 | init_susp initial_suspend(); |
122 | final_susp final_suspend(); |
123 | void return_void(); |
124 | suspend_maybe yield_value(int); |
125 | }; |
126 | }; |
127 | |
128 | // CHECK-LABEL: f1( |
129 | extern "C" void f1(int) { |
130 | // CHECK: %[[PROMISE:.+]] = alloca %"struct.std::experimental::coroutine_traits<void, int>::promise_type" |
131 | // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin( |
132 | co_yield 42; |
133 | // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJviEE12promise_type11yield_valueEi(%struct.suspend_maybe* sret %[[AWAITER:.+]], %"struct.std::experimental::coroutine_traits<void, int>::promise_type"* %[[PROMISE]], i32 42) |
134 | |
135 | // See if we need to suspend: |
136 | // -------------------------- |
137 | // CHECK: %[[READY:.+]] = call zeroext i1 @_ZN13suspend_maybe11await_readyEv(%struct.suspend_maybe* %[[AWAITABLE]]) |
138 | // CHECK: br i1 %[[READY]], label %[[READY_BB:.+]], label %[[SUSPEND_BB:.+]] |
139 | |
140 | // If we are suspending: |
141 | // --------------------- |
142 | // CHECK: [[SUSPEND_BB]]: |
143 | // CHECK: %[[SUSPEND_ID:.+]] = call token @llvm.coro.save( |
144 | // --------------------------- |
145 | // Build the coroutine handle and pass it to await_suspend |
146 | // --------------------------- |
147 | // CHECK: call i8* @_ZNSt12experimental16coroutine_handleINS_16coroutine_traitsIJviEE12promise_typeEE12from_addressEPv(i8* %[[FRAME]]) |
148 | // ... many lines of code to coerce coroutine_handle into an i8* scalar |
149 | // CHECK: %[[CH:.+]] = load i8*, i8** %{{.+}} |
150 | // CHECK: %[[YES:.+]] = call zeroext i1 @_ZN13suspend_maybe13await_suspendENSt12experimental16coroutine_handleIvEE(%struct.suspend_maybe* %[[AWAITABLE]], i8* %[[CH]]) |
151 | // ------------------------------------------- |
152 | // See if await_suspend decided not to suspend |
153 | // ------------------------------------------- |
154 | // CHECK: br i1 %[[YES]], label %[[SUSPEND_PLEASE:.+]], label %[[READY_BB]] |
155 | |
156 | // CHECK: [[SUSPEND_PLEASE]]: |
157 | // CHECK: call i8 @llvm.coro.suspend(token %[[SUSPEND_ID]], i1 false) |
158 | |
159 | // CHECK: [[READY_BB]]: |
160 | // CHECK: call void @_ZN13suspend_maybe12await_resumeEv(%struct.suspend_maybe* %[[AWAITABLE]]) |
161 | } |
162 | |
163 | struct ComplexAwaiter { |
164 | template <typename F> void await_suspend(F); |
165 | bool await_ready(); |
166 | _Complex float await_resume(); |
167 | }; |
168 | extern "C" void UseComplex(_Complex float); |
169 | |
170 | // CHECK-LABEL: @TestComplex( |
171 | extern "C" void TestComplex() { |
172 | UseComplex(co_await ComplexAwaiter{}); |
173 | // CHECK: call <2 x float> @_ZN14ComplexAwaiter12await_resumeEv(%struct.ComplexAwaiter* |
174 | // CHECK: call void @UseComplex(<2 x float> %{{.+}}) |
175 | |
176 | co_await ComplexAwaiter{}; |
177 | // CHECK: call <2 x float> @_ZN14ComplexAwaiter12await_resumeEv(%struct.ComplexAwaiter* |
178 | |
179 | _Complex float Val = co_await ComplexAwaiter{}; |
180 | // CHECK: call <2 x float> @_ZN14ComplexAwaiter12await_resumeEv(%struct.ComplexAwaiter* |
181 | } |
182 | |
183 | struct Aggr { int X, Y, Z; ~Aggr(); }; |
184 | struct AggrAwaiter { |
185 | template <typename F> void await_suspend(F); |
186 | bool await_ready(); |
187 | Aggr await_resume(); |
188 | }; |
189 | |
190 | extern "C" void Whatever(); |
191 | extern "C" void UseAggr(Aggr&&); |
192 | |
193 | // FIXME: Once the cleanup code is in, add testing that destructors for Aggr |
194 | // are invoked properly on the cleanup branches. |
195 | |
196 | // CHECK-LABEL: @TestAggr( |
197 | extern "C" void TestAggr() { |
198 | UseAggr(co_await AggrAwaiter{}); |
199 | Whatever(); |
200 | // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret %[[AwaitResume:.+]], |
201 | // CHECK: call void @UseAggr(%struct.Aggr* dereferenceable(12) %[[AwaitResume]]) |
202 | // CHECK: call void @_ZN4AggrD1Ev(%struct.Aggr* %[[AwaitResume]]) |
203 | // CHECK: call void @Whatever() |
204 | |
205 | co_await AggrAwaiter{}; |
206 | Whatever(); |
207 | // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret %[[AwaitResume2:.+]], |
208 | // CHECK: call void @_ZN4AggrD1Ev(%struct.Aggr* %[[AwaitResume2]]) |
209 | // CHECK: call void @Whatever() |
210 | |
211 | Aggr Val = co_await AggrAwaiter{}; |
212 | Whatever(); |
213 | // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret %[[AwaitResume3:.+]], |
214 | // CHECK: call void @Whatever() |
215 | // CHECK: call void @_ZN4AggrD1Ev(%struct.Aggr* %[[AwaitResume3]]) |
216 | } |
217 | |
218 | struct ScalarAwaiter { |
219 | template <typename F> void await_suspend(F); |
220 | bool await_ready(); |
221 | int await_resume(); |
222 | }; |
223 | |
224 | extern "C" void UseScalar(int); |
225 | |
226 | // CHECK-LABEL: @TestScalar( |
227 | extern "C" void TestScalar() { |
228 | UseScalar(co_await ScalarAwaiter{}); |
229 | // CHECK: %[[Result:.+]] = call i32 @_ZN13ScalarAwaiter12await_resumeEv(%struct.ScalarAwaiter* |
230 | // CHECK: call void @UseScalar(i32 %[[Result]]) |
231 | |
232 | int Val = co_await ScalarAwaiter{}; |
233 | // CHECK: %[[Result2:.+]] = call i32 @_ZN13ScalarAwaiter12await_resumeEv(%struct.ScalarAwaiter* |
234 | // CHECK: store i32 %[[Result2]], i32* %Val |
235 | |
236 | co_await ScalarAwaiter{}; |
237 | // CHECK: call i32 @_ZN13ScalarAwaiter12await_resumeEv(%struct.ScalarAwaiter* |
238 | } |
239 | |
240 | // Test operator co_await codegen. |
241 | enum class MyInt: int {}; |
242 | ScalarAwaiter operator co_await(MyInt); |
243 | |
244 | struct MyAgg { |
245 | AggrAwaiter operator co_await(); |
246 | }; |
247 | |
248 | // CHECK-LABEL: @TestOpAwait( |
249 | extern "C" void TestOpAwait() { |
250 | co_await MyInt(42); |
251 | // CHECK: call void @_Zaw5MyInt(i32 42) |
252 | // CHECK: call i32 @_ZN13ScalarAwaiter12await_resumeEv(%struct.ScalarAwaiter* % |
253 | |
254 | co_await MyAgg{}; |
255 | // CHECK: call void @_ZN5MyAggawEv(%struct.MyAgg* % |
256 | // CHECK: call void @_ZN11AggrAwaiter12await_resumeEv(%struct.Aggr* sret % |
257 | } |
258 | |
259 | // CHECK-LABEL: EndlessLoop( |
260 | extern "C" void EndlessLoop() { |
261 | // CHECK: %[[FRAME:.+]] = call i8* @llvm.coro.begin( |
262 | |
263 | // See if initial_suspend was issued: |
264 | // ---------------------------------- |
265 | // CHECK: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type15initial_suspendEv( |
266 | // CHECK-NEXT: call zeroext i1 @_ZN9init_susp11await_readyEv(%struct.init_susp* |
267 | |
268 | for (;;) |
269 | co_await suspend_always{}; |
270 | |
271 | // Verify that final_suspend was NOT issued: |
272 | // ---------------------------------- |
273 | // CHECK-NOT: call void @_ZNSt12experimental16coroutine_traitsIJvEE12promise_type13final_suspendEv( |
274 | // CHECK-NOT: call zeroext i1 @_ZN10final_susp11await_readyEv(%struct.final_susp* |
275 | } |
276 | |
277 | // Verifies that we don't crash when awaiting on an lvalue. |
278 | // CHECK-LABEL: @_Z11AwaitLValuev( |
279 | void AwaitLValue() { |
280 | suspend_always lval; |
281 | co_await lval; |
282 | } |
283 | |
284 | struct RefTag { }; |
285 | |
286 | struct AwaitResumeReturnsLValue { |
287 | bool await_ready(); |
288 | void await_suspend(std::experimental::coroutine_handle<>); |
289 | RefTag& await_resume(); |
290 | }; |
291 | |
292 | |
293 | template<> |
294 | struct std::experimental::coroutine_traits<void,double> { |
295 | struct promise_type { |
296 | void get_return_object(); |
297 | init_susp initial_suspend(); |
298 | final_susp final_suspend(); |
299 | void return_void(); |
300 | AwaitResumeReturnsLValue yield_value(int); |
301 | }; |
302 | }; |
303 | |
304 | // Verifies that we don't crash when returning an lvalue from an await_resume() |
305 | // expression. |
306 | // CHECK-LABEL: define void @_Z18AwaitReturnsLValued(double) |
307 | void AwaitReturnsLValue(double) { |
308 | AwaitResumeReturnsLValue a; |
309 | // CHECK: %[[AVAR:.+]] = alloca %struct.AwaitResumeReturnsLValue, |
310 | // CHECK: %[[XVAR:.+]] = alloca %struct.RefTag*, |
311 | |
312 | // CHECK: %[[YVAR:.+]] = alloca %struct.RefTag*, |
313 | // CHECK-NEXT: %[[TMP1:.+]] = alloca %struct.AwaitResumeReturnsLValue, |
314 | |
315 | // CHECK: %[[ZVAR:.+]] = alloca %struct.RefTag*, |
316 | // CHECK-NEXT: %[[TMP2:.+]] = alloca %struct.AwaitResumeReturnsLValue, |
317 | |
318 | // CHECK: %[[RES1:.+]] = call dereferenceable({{.*}}) %struct.RefTag* @_ZN24AwaitResumeReturnsLValue12await_resumeEv(%struct.AwaitResumeReturnsLValue* %[[AVAR]]) |
319 | // CHECK-NEXT: store %struct.RefTag* %[[RES1]], %struct.RefTag** %[[XVAR]], |
320 | RefTag& x = co_await a; |
321 | |
322 | // CHECK: %[[RES2:.+]] = call dereferenceable({{.*}}) %struct.RefTag* @_ZN24AwaitResumeReturnsLValue12await_resumeEv(%struct.AwaitResumeReturnsLValue* %[[TMP1]]) |
323 | // CHECK-NEXT: store %struct.RefTag* %[[RES2]], %struct.RefTag** %[[YVAR]], |
324 | |
325 | RefTag& y = co_await AwaitResumeReturnsLValue{}; |
326 | // CHECK: %[[RES3:.+]] = call dereferenceable({{.*}}) %struct.RefTag* @_ZN24AwaitResumeReturnsLValue12await_resumeEv(%struct.AwaitResumeReturnsLValue* %[[TMP2]]) |
327 | // CHECK-NEXT: store %struct.RefTag* %[[RES3]], %struct.RefTag** %[[ZVAR]], |
328 | RefTag& z = co_yield 42; |
329 | } |
330 | |
331 | struct TailCallAwait { |
332 | bool await_ready(); |
333 | std::experimental::coroutine_handle<> await_suspend(std::experimental::coroutine_handle<>); |
334 | void await_resume(); |
335 | }; |
336 | |
337 | // CHECK-LABEL: @TestTailcall( |
338 | extern "C" void TestTailcall() { |
339 | co_await TailCallAwait{}; |
340 | |
341 | // CHECK: %[[RESULT:.+]] = call i8* @_ZN13TailCallAwait13await_suspendENSt12experimental16coroutine_handleIvEE(%struct.TailCallAwait* |
342 | // CHECK: %[[COERCE:.+]] = getelementptr inbounds %"struct.std::experimental::coroutine_handle", %"struct.std::experimental::coroutine_handle"* %[[TMP:.+]], i32 0, i32 0 |
343 | // CHECK: store i8* %[[RESULT]], i8** %[[COERCE]] |
344 | // CHECK: %[[ADDR:.+]] = call i8* @_ZNSt12experimental16coroutine_handleIvE7addressEv(%"struct.std::experimental::coroutine_handle"* %[[TMP]]) |
345 | // CHECK: call void @llvm.coro.resume(i8* %[[ADDR]]) |
346 | } |
347 | |