xref: /llvm-project/llvm/test/Transforms/Coroutines/coro-heap-elide.ll (revision a0d2fc126efd85a4712681e7ec04a04171557475)
1; Tests that the dynamic allocation and deallocation of the coroutine frame is
2; elided and any tail calls referencing the coroutine frame has the tail
3; call attribute removed.
4; RUN: opt < %s -S \
5; RUN: -passes='cgscc(inline,function(coro-elide,instsimplify,simplifycfg))' \
6; RUN:   -aa-pipeline='basic-aa' | FileCheck %s
7
8declare void @print(i32) nounwind
9
10%f.frame = type {i32}
11
12declare void @bar(ptr)
13
14declare fastcc void @f.resume(ptr align 4 dereferenceable(4))
15declare fastcc void @f.destroy(ptr)
16declare fastcc void @f.cleanup(ptr)
17
18declare void @may_throw()
19declare ptr @CustomAlloc(i32)
20declare void @CustomFree(ptr)
21
22@f.resumers = internal constant [3 x ptr]
23  [ptr @f.resume, ptr @f.destroy, ptr @f.cleanup]
24
25; a coroutine start function
26define ptr @f() personality ptr null {
27entry:
28  %id = call token @llvm.coro.id(i32 0, ptr null,
29                      ptr @f,
30                      ptr @f.resumers)
31  %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
32  br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin
33dyn.alloc:
34  %alloc = call ptr @CustomAlloc(i32 4)
35  br label %coro.begin
36coro.begin:
37  %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
38  %hdl = call ptr @llvm.coro.begin(token %id, ptr %phi)
39  invoke void @may_throw()
40    to label %ret unwind label %ehcleanup
41ret:
42  ret ptr %hdl
43
44ehcleanup:
45  %tok = cleanuppad within none []
46  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
47  %need.dyn.free = icmp ne ptr %mem, null
48  br i1 %need.dyn.free, label %dyn.free, label %if.end
49dyn.free:
50  call void @CustomFree(ptr %mem)
51  br label %if.end
52if.end:
53  cleanupret from %tok unwind to caller
54}
55
56; CHECK-LABEL: @callResume(
57define void @callResume() {
58entry:
59; CHECK: alloca [4 x i8], align 4
60; CHECK-NOT: coro.begin
61; CHECK-NOT: CustomAlloc
62; CHECK: call void @may_throw()
63  %hdl = call ptr @f()
64
65; Need to remove 'tail' from the first call to @bar
66; CHECK-NOT: tail call void @bar(
67; CHECK: call void @bar(
68  tail call void @bar(ptr %hdl)
69; CHECK: tail call void @bar(
70  tail call void @bar(ptr null)
71
72; CHECK-NEXT: call fastcc void @f.resume(ptr %0)
73  %0 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 0)
74  call fastcc void %0(ptr %hdl)
75
76; CHECK-NEXT: call fastcc void @f.cleanup(ptr %0)
77  %1 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
78  call fastcc void %1(ptr %hdl)
79
80; CHECK-NEXT: ret void
81  ret void
82}
83
84; CHECK-LABEL: @callResume_with_coro_suspend_1(
85define void @callResume_with_coro_suspend_1() {
86entry:
87; CHECK: alloca [4 x i8], align 4
88; CHECK-NOT: coro.begin
89; CHECK-NOT: CustomAlloc
90; CHECK: call void @may_throw()
91  %hdl = call ptr @f()
92
93; CHECK-NEXT: call fastcc void @f.resume(ptr %0)
94  %0 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 0)
95  call fastcc void %0(ptr %hdl)
96  %1 = call token @llvm.coro.save(ptr %hdl)
97  %2 = call i8 @llvm.coro.suspend(token %1, i1 false)
98  switch i8 %2, label  %coro.ret [
99    i8 0, label %final.suspend
100    i8 1, label %cleanups
101  ]
102
103; CHECK-LABEL: final.suspend:
104final.suspend:
105; CHECK-NEXT: call fastcc void @f.cleanup(ptr %0)
106  %3 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
107  call fastcc void %3(ptr %hdl)
108  %4 = call token @llvm.coro.save(ptr %hdl)
109  %5 = call i8 @llvm.coro.suspend(token %4, i1 true)
110  switch i8 %5, label  %coro.ret [
111    i8 0, label %coro.ret
112    i8 1, label %cleanups
113  ]
114
115; CHECK-LABEL: cleanups:
116cleanups:
117; CHECK-NEXT: call fastcc void @f.cleanup(ptr %0)
118  %6 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
119  call fastcc void %6(ptr %hdl)
120  br label %coro.ret
121
122; CHECK-LABEL: coro.ret:
123coro.ret:
124; CHECK-NEXT: ret void
125  ret void
126}
127
128; CHECK-LABEL: @callResume_with_coro_suspend_2(
129define void @callResume_with_coro_suspend_2() personality ptr null {
130entry:
131; CHECK: alloca [4 x i8], align 4
132; CHECK-NOT: coro.begin
133; CHECK-NOT: CustomAlloc
134; CHECK: call void @may_throw()
135  %hdl = call ptr @f()
136
137  %0 = call token @llvm.coro.save(ptr %hdl)
138; CHECK: invoke fastcc void @f.resume(ptr %0)
139  %1 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 0)
140  invoke fastcc void %1(ptr %hdl)
141    to label %invoke.cont1 unwind label %lpad
142
143; CHECK-LABEL: invoke.cont1:
144invoke.cont1:
145  %2 = call i8 @llvm.coro.suspend(token %0, i1 false)
146  switch i8 %2, label  %coro.ret [
147    i8 0, label %final.ready
148    i8 1, label %cleanups
149  ]
150
151; CHECK-LABEL: lpad:
152lpad:
153  %3 = landingpad { ptr, i32 }
154          catch ptr null
155; CHECK: call fastcc void @f.cleanup(ptr %0)
156  %4 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
157  call fastcc void %4(ptr %hdl)
158  br label %final.suspend
159
160; CHECK-LABEL: final.ready:
161final.ready:
162; CHECK-NEXT: call fastcc void @f.cleanup(ptr %0)
163  %5 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
164  call fastcc void %5(ptr %hdl)
165  br label %final.suspend
166
167; CHECK-LABEL: final.suspend:
168final.suspend:
169  %6 = call token @llvm.coro.save(ptr %hdl)
170  %7 = call i8 @llvm.coro.suspend(token %6, i1 true)
171  switch i8 %7, label  %coro.ret [
172    i8 0, label %coro.ret
173    i8 1, label %cleanups
174  ]
175
176; CHECK-LABEL: cleanups:
177cleanups:
178; CHECK-NEXT: call fastcc void @f.cleanup(ptr %0)
179  %8 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
180  call fastcc void %8(ptr %hdl)
181  br label %coro.ret
182
183; CHECK-LABEL: coro.ret:
184coro.ret:
185; CHECK-NEXT: ret void
186  ret void
187}
188
189; CHECK-LABEL: @callResume_with_coro_suspend_3(
190define void @callResume_with_coro_suspend_3(i8 %cond) {
191entry:
192; CHECK: alloca [4 x i8], align 4
193  switch i8 %cond, label  %coro.ret [
194    i8 0, label %init.suspend
195    i8 1, label %coro.ret
196  ]
197
198init.suspend:
199; CHECK-NOT: llvm.coro.begin
200; CHECK-NOT: CustomAlloc
201; CHECK: call void @may_throw()
202  %hdl = call ptr @f()
203; CHECK-NEXT: call fastcc void @f.resume(ptr %0)
204  %0 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 0)
205  call fastcc void %0(ptr %hdl)
206  %1 = call token @llvm.coro.save(ptr %hdl)
207  %2 = call i8 @llvm.coro.suspend(token %1, i1 false)
208  switch i8 %2, label  %coro.ret [
209    i8 0, label %final.suspend
210    i8 1, label %cleanups
211  ]
212
213; CHECK-LABEL: final.suspend:
214final.suspend:
215; CHECK-NEXT: call fastcc void @f.cleanup(ptr %0)
216  %3 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
217  call fastcc void %3(ptr %hdl)
218  %4 = call token @llvm.coro.save(ptr %hdl)
219  %5 = call i8 @llvm.coro.suspend(token %4, i1 true)
220  switch i8 %5, label  %coro.ret [
221    i8 0, label %coro.ret
222    i8 1, label %cleanups
223  ]
224
225; CHECK-LABEL: cleanups:
226cleanups:
227; CHECK-NEXT: call fastcc void @f.cleanup(ptr %0)
228  %6 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
229  call fastcc void %6(ptr %hdl)
230  br label %coro.ret
231
232; CHECK-LABEL: coro.ret:
233coro.ret:
234; CHECK-NEXT: ret void
235  ret void
236}
237
238
239
240; CHECK-LABEL: @callResume_PR34897_no_elision(
241define void @callResume_PR34897_no_elision(i1 %cond) {
242; CHECK-LABEL: entry:
243entry:
244; CHECK: call ptr @CustomAlloc(
245  %hdl = call ptr @f()
246; CHECK: tail call void @bar(
247  tail call void @bar(ptr %hdl)
248; CHECK: tail call void @bar(
249  tail call void @bar(ptr null)
250  br i1 %cond, label %if.then, label %if.else
251
252; CHECK-LABEL: if.then:
253if.then:
254; CHECK: call fastcc void @f.resume(ptr
255  %0 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 0)
256  call fastcc void %0(ptr %hdl)
257; CHECK-NEXT: call fastcc void @f.destroy(ptr
258  %1 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
259  call fastcc void %1(ptr %hdl)
260  br label %return
261
262if.else:
263  br label %return
264
265; CHECK-LABEL: return:
266return:
267; CHECK: ret void
268  ret void
269}
270
271; CHECK-LABEL: @callResume_PR34897_elision(
272define void @callResume_PR34897_elision(i1 %cond) {
273; CHECK-LABEL: entry:
274entry:
275; CHECK: alloca [4 x i8], align 4
276; CHECK: tail call void @bar(
277  tail call void @bar(ptr null)
278  br i1 %cond, label %if.then, label %if.else
279
280if.then:
281; CHECK-NOT: CustomAlloc
282; CHECK: call void @may_throw()
283  %hdl = call ptr @f()
284; CHECK: call void @bar(
285  tail call void @bar(ptr %hdl)
286; CHECK: call fastcc void @f.resume(ptr
287  %0 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 0)
288  call fastcc void %0(ptr %hdl)
289; CHECK-NEXT: call fastcc void @f.cleanup(ptr
290  %1 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
291  call fastcc void %1(ptr %hdl)
292  br label %return
293
294if.else:
295  br label %return
296
297; CHECK-LABEL: return:
298return:
299; CHECK: ret void
300  ret void
301}
302
303
304; a coroutine start function (cannot elide heap alloc, due to second argument to
305; coro.begin not pointint to coro.alloc)
306define ptr @f_no_elision() personality ptr null {
307entry:
308  %id = call token @llvm.coro.id(i32 0, ptr null,
309                      ptr @f_no_elision,
310                      ptr @f.resumers)
311  %alloc = call ptr @CustomAlloc(i32 4)
312  %hdl = call ptr @llvm.coro.begin(token %id, ptr %alloc)
313  ret ptr %hdl
314}
315
316; CHECK-LABEL: @callResume_no_elision(
317define void @callResume_no_elision() {
318entry:
319; CHECK: call ptr @CustomAlloc(
320  %hdl = call ptr @f_no_elision()
321
322; Tail call should remain tail calls
323; CHECK: tail call void @bar(
324  tail call void @bar(ptr %hdl)
325; CHECK: tail call void @bar(
326  tail call void @bar(ptr null)
327
328; CHECK-NEXT: call fastcc void @f.resume(ptr
329  %0 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 0)
330  call fastcc void %0(ptr %hdl)
331
332; CHECK-NEXT: call fastcc void @f.destroy(ptr
333  %1 = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1)
334  call fastcc void %1(ptr %hdl)
335
336; CHECK-NEXT: ret void
337  ret void
338}
339
340declare token @llvm.coro.id(i32, ptr, ptr, ptr)
341declare i1 @llvm.coro.alloc(token)
342declare ptr @llvm.coro.free(token, ptr)
343declare ptr @llvm.coro.begin(token, ptr)
344declare ptr @llvm.coro.frame(token)
345declare ptr @llvm.coro.subfn.addr(ptr, i8)
346declare i8 @llvm.coro.suspend(token, i1)
347declare token @llvm.coro.save(ptr)
348