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