xref: /llvm-project/llvm/test/Transforms/Coroutines/no-suspend.ll (revision 25f4ead96618dd5d54072689d2f399b8189b574f)
1; Test no suspend coroutines
2; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s
3
4; Coroutine with no-suspends will turn into:
5;
6; CHECK-LABEL: define void @no_suspends(
7; CHECK-NEXT:  entry:
8; CHECK-NEXT:    call void @print(i32 %n)
9; CHECK-NEXT:    ret void
10;
11define void @no_suspends(i32 %n) presplitcoroutine {
12entry:
13  %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
14  %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
15  br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin
16dyn.alloc:
17  %size = call i32 @llvm.coro.size.i32()
18  %alloc = call ptr @malloc(i32 %size)
19  br label %coro.begin
20coro.begin:
21  %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
22  %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi)
23  br label %body
24body:
25  call void @print(i32 %n)
26  br label %cleanup
27cleanup:
28  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
29  %need.dyn.free = icmp ne ptr %mem, null
30  br i1 %need.dyn.free, label %dyn.free, label %suspend
31dyn.free:
32  call void @free(ptr %mem)
33  br label %suspend
34suspend:
35  call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
36  ret void
37}
38
39; SimplifySuspendPoint will detect that coro.resume resumes itself and will
40; replace suspend with a jump to %resume label turning it into no-suspend
41; coroutine.
42;
43; CHECK-LABEL: define void @simplify_resume(
44; CHECK-NEXT:  entry:
45; CHECK-NEXT:    call void @llvm.memcpy
46; CHECK-NEXT:    call void @print(i32 0)
47; CHECK-NEXT:    ret void
48;
49define void @simplify_resume(ptr %src, ptr %dst) presplitcoroutine {
50entry:
51  %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
52  %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
53  br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin
54dyn.alloc:
55  %size = call i32 @llvm.coro.size.i32()
56  %alloc = call ptr @malloc(i32 %size)
57  br label %coro.begin
58coro.begin:
59  %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
60  %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi)
61  br label %body
62body:
63  %save = call token @llvm.coro.save(ptr %hdl)
64  ; memcpy intrinsics should not prevent simplification.
65  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 1, i1 false)
66  %subfn = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 0)
67  call fastcc void %subfn(ptr %hdl)
68  %0 = call i8 @llvm.coro.suspend(token %save, i1 false)
69  switch i8 %0, label %suspend [i8 0, label %resume
70                                i8 1, label %pre.cleanup]
71resume:
72  call void @print(i32 0)
73  br label %cleanup
74
75pre.cleanup:
76  call void @print(i32 1)
77  br label %cleanup
78
79cleanup:
80  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
81  call void @free(ptr %mem)
82  br label %suspend
83suspend:
84  call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
85  ret void
86}
87
88; SimplifySuspendPoint will detect that coroutine destroys itself and will
89; replace suspend with a jump to %cleanup label turning it into no-suspend
90; coroutine.
91;
92; CHECK-LABEL: define void @simplify_destroy(
93; CHECK-NEXT:  entry:
94; CHECK-NEXT:    call void @print(i32 1)
95; CHECK-NEXT:    ret void
96;
97define void @simplify_destroy() presplitcoroutine personality i32 0 {
98entry:
99  %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
100  %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
101  br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin
102dyn.alloc:
103  %size = call i32 @llvm.coro.size.i32()
104  %alloc = call ptr @malloc(i32 %size)
105  br label %coro.begin
106coro.begin:
107  %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
108  %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi)
109  br label %body
110body:
111  %save = call token @llvm.coro.save(ptr %hdl)
112  %subfn = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
113  invoke fastcc void %subfn(ptr %hdl) to label %real_susp unwind label %lpad
114
115real_susp:
116  %0 = call i8 @llvm.coro.suspend(token %save, i1 false)
117  switch i8 %0, label %suspend [i8 0, label %resume
118                                i8 1, label %pre.cleanup]
119resume:
120  call void @print(i32 0)
121  br label %cleanup
122
123pre.cleanup:
124  call void @print(i32 1)
125  br label %cleanup
126
127cleanup:
128  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
129  call void @free(ptr %mem)
130  br label %suspend
131suspend:
132  call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
133  ret void
134lpad:
135  %lpval = landingpad { ptr, i32 }
136     cleanup
137
138  call void @print(i32 2)
139  resume { ptr, i32 } %lpval
140}
141
142; SimplifySuspendPoint will detect that coro.resume resumes itself and will
143; replace suspend with a jump to %resume label turning it into no-suspend
144; coroutine.
145;
146; CHECK-LABEL: define void @simplify_resume_with_inlined_if(
147; CHECK-NEXT:  entry:
148; CHECK-NEXT:    br i1
149; CHECK:         call void @print(i32 0)
150; CHECK-NEXT:    ret void
151;
152define void @simplify_resume_with_inlined_if(ptr %src, ptr %dst, i1 %cond) presplitcoroutine {
153entry:
154  %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
155  %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
156  br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin
157dyn.alloc:
158  %size = call i32 @llvm.coro.size.i32()
159  %alloc = call ptr @malloc(i32 %size)
160  br label %coro.begin
161coro.begin:
162  %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
163  %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi)
164  br label %body
165body:
166  %save = call token @llvm.coro.save(ptr %hdl)
167  br i1 %cond, label %if.then, label %if.else
168if.then:
169  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 1, i1 false)
170  br label %if.end
171if.else:
172  call void @llvm.memcpy.p0.p0.i64(ptr %src, ptr %dst, i64 1, i1 false)
173  br label %if.end
174if.end:
175  %subfn = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 0)
176  call fastcc void %subfn(ptr %hdl)
177  %0 = call i8 @llvm.coro.suspend(token %save, i1 false)
178  switch i8 %0, label %suspend [i8 0, label %resume
179                                i8 1, label %pre.cleanup]
180resume:
181  call void @print(i32 0)
182  br label %cleanup
183
184pre.cleanup:
185  call void @print(i32 1)
186  br label %cleanup
187
188cleanup:
189  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
190  call void @free(ptr %mem)
191  br label %suspend
192suspend:
193  call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
194  ret void
195}
196
197
198
199; SimplifySuspendPoint won't be able to simplify if it detects that there are
200; other calls between coro.save and coro.suspend. They potentially can call
201; resume or destroy, so we should not simplify this suspend point.
202;
203; CHECK-LABEL: define void @cannot_simplify_other_calls(
204; CHECK-NEXT:  entry:
205; CHECK-NEXT:     llvm.coro.id
206
207define void @cannot_simplify_other_calls() presplitcoroutine {
208entry:
209  %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
210  %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
211  br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin
212dyn.alloc:
213  %size = call i32 @llvm.coro.size.i32()
214  %alloc = call ptr @malloc(i32 %size)
215  br label %coro.begin
216coro.begin:
217  %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
218  %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi)
219  br label %body
220body:
221  %save = call token @llvm.coro.save(ptr %hdl)
222  br label %body1
223
224body1:
225  call void @foo()
226  br label %body2
227
228body2:
229  %subfn = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
230  call fastcc void %subfn(ptr %hdl)
231  %0 = call i8 @llvm.coro.suspend(token %save, i1 false)
232  switch i8 %0, label %suspend [i8 0, label %resume
233                                i8 1, label %pre.cleanup]
234resume:
235  call void @print(i32 0)
236  br label %cleanup
237
238pre.cleanup:
239  call void @print(i32 1)
240  br label %cleanup
241
242cleanup:
243  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
244  call void @free(ptr %mem)
245  br label %suspend
246suspend:
247  call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
248  ret void
249}
250
251; SimplifySuspendPoint won't be able to simplify if it detects that there are
252; other calls between coro.save and coro.suspend. They potentially can call
253; resume or destroy, so we should not simplify this suspend point.
254;
255; CHECK-LABEL: define void @cannot_simplify_calls_in_terminator(
256; CHECK-NEXT:  entry:
257; CHECK-NEXT:     llvm.coro.id
258
259define void @cannot_simplify_calls_in_terminator() presplitcoroutine personality i32 0 {
260entry:
261  %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
262  %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
263  br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin
264dyn.alloc:
265  %size = call i32 @llvm.coro.size.i32()
266  %alloc = call ptr @malloc(i32 %size)
267  br label %coro.begin
268coro.begin:
269  %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
270  %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi)
271  br label %body
272body:
273  %save = call token @llvm.coro.save(ptr %hdl)
274  invoke void @foo() to label %resume_cont unwind label %lpad
275resume_cont:
276  %subfn = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
277  call fastcc void %subfn(ptr %hdl)
278  %0 = call i8 @llvm.coro.suspend(token %save, i1 false)
279  switch i8 %0, label %suspend [i8 0, label %resume
280                                i8 1, label %pre.cleanup]
281resume:
282  call void @print(i32 0)
283  br label %cleanup
284
285pre.cleanup:
286  call void @print(i32 1)
287  br label %cleanup
288
289cleanup:
290  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
291  call void @free(ptr %mem)
292  br label %suspend
293suspend:
294  call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
295  ret void
296lpad:
297  %lpval = landingpad { ptr, i32 }
298     cleanup
299
300  call void @print(i32 2)
301  resume { ptr, i32 } %lpval
302}
303
304; SimplifySuspendPoint won't be able to simplify if it detects that resume or
305; destroy does not immediately preceed coro.suspend.
306;
307; CHECK-LABEL: define void @cannot_simplify_not_last_instr(
308; CHECK-NEXT:  entry:
309; CHECK-NEXT:     llvm.coro.id
310
311define void @cannot_simplify_not_last_instr(ptr %dst, ptr %src) presplitcoroutine {
312entry:
313  %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
314  %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
315  br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin
316dyn.alloc:
317  %size = call i32 @llvm.coro.size.i32()
318  %alloc = call ptr @malloc(i32 %size)
319  br label %coro.begin
320coro.begin:
321  %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
322  %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi)
323  br label %body
324body:
325  %save = call token @llvm.coro.save(ptr %hdl)
326  %subfn = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
327  call fastcc void %subfn(ptr %hdl)
328  ; memcpy separates destroy from suspend, therefore cannot simplify.
329  call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 1, i1 false)
330  %0 = call i8 @llvm.coro.suspend(token %save, i1 false)
331  switch i8 %0, label %suspend [i8 0, label %resume
332                                i8 1, label %pre.cleanup]
333resume:
334  call void @print(i32 0)
335  br label %cleanup
336
337pre.cleanup:
338  call void @print(i32 1)
339  br label %cleanup
340
341cleanup:
342  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
343  call void @free(ptr %mem)
344  br label %suspend
345suspend:
346  call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
347  ret void
348}
349
350; SimplifySuspendPoint should not simplify final suspend point
351;
352; CHECK-LABEL: define void @cannot_simplify_final_suspend(
353; CHECK-NEXT:  entry:
354; CHECK-NEXT:     llvm.coro.id
355;
356define void @cannot_simplify_final_suspend() presplitcoroutine personality i32 0 {
357entry:
358  %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
359  %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
360  br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin
361dyn.alloc:
362  %size = call i32 @llvm.coro.size.i32()
363  %alloc = call ptr @malloc(i32 %size)
364  br label %coro.begin
365coro.begin:
366  %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
367  %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi)
368  br label %body
369body:
370  %save = call token @llvm.coro.save(ptr %hdl)
371  %subfn = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
372  invoke fastcc void %subfn(ptr %hdl) to label %real_susp unwind label %lpad
373
374real_susp:
375  %0 = call i8 @llvm.coro.suspend(token %save, i1 1)
376  switch i8 %0, label %suspend [i8 0, label %resume
377                                i8 1, label %pre.cleanup]
378resume:
379  call void @print(i32 0)
380  br label %cleanup
381
382pre.cleanup:
383  call void @print(i32 1)
384  br label %cleanup
385
386cleanup:
387  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
388  call void @free(ptr %mem)
389  br label %suspend
390suspend:
391  call i1 @llvm.coro.end(ptr %hdl, i1 false, token none)
392  ret void
393lpad:
394  %lpval = landingpad { ptr, i32 }
395     cleanup
396
397  call void @print(i32 2)
398  resume { ptr, i32 } %lpval
399}
400
401declare ptr @malloc(i32) allockind("alloc,uninitialized") allocsize(0)
402declare void @free(ptr) willreturn allockind("free")
403declare void @print(i32)
404declare void @foo()
405
406declare token @llvm.coro.id(i32, ptr, ptr, ptr)
407declare i1 @llvm.coro.alloc(token)
408declare i32 @llvm.coro.size.i32()
409declare ptr @llvm.coro.begin(token, ptr)
410declare token @llvm.coro.save(ptr %hdl)
411declare i8 @llvm.coro.suspend(token, i1)
412declare ptr @llvm.coro.free(token, ptr)
413declare i1 @llvm.coro.end(ptr, i1, token)
414
415declare ptr @llvm.coro.subfn.addr(ptr, i8)
416
417declare void @llvm.memcpy.p0.p0.i64(ptr nocapture writeonly, ptr nocapture readonly, i64, i1)
418