[DAGCombiner] Add target hook function to decide folding (mul (add x, c1), c2)
[llvm-project.git] / llvm / test / Transforms / Coroutines / coro-heap-elide.ll
blob9c6165a38511a7edb73eb6e2ceb1c46a225099bf
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
8 declare void @print(i32) nounwind
10 %f.frame = type {i32}
12 declare void @bar(i8*)
14 declare fastcc void @f.resume(%f.frame*)
15 declare fastcc void @f.destroy(%f.frame*)
16 declare fastcc void @f.cleanup(%f.frame*)
18 declare void @may_throw()
19 declare i8* @CustomAlloc(i32)
20 declare void @CustomFree(i8*)
22 @f.resumers = internal constant [3 x void (%f.frame*)*] 
23   [void (%f.frame*)* @f.resume, void (%f.frame*)* @f.destroy, void (%f.frame*)* @f.cleanup]
25 ; a coroutine start function
26 define i8* @f() personality i8* null {
27 entry:
28   %id = call token @llvm.coro.id(i32 0, i8* null,
29                       i8* bitcast (i8*()* @f to i8*),
30                       i8* bitcast ([3 x void (%f.frame*)*]* @f.resumers to i8*))
31   %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
32   br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin
33 dyn.alloc:
34   %alloc = call i8* @CustomAlloc(i32 4)
35   br label %coro.begin
36 coro.begin:
37   %phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ]
38   %hdl = call i8* @llvm.coro.begin(token %id, i8* %phi)
39   invoke void @may_throw() 
40     to label %ret unwind label %ehcleanup
41 ret:          
42   ret i8* %hdl
44 ehcleanup:
45   %tok = cleanuppad within none []
46   %mem = call i8* @llvm.coro.free(token %id, i8* %hdl)
47   %need.dyn.free = icmp ne i8* %mem, null
48   br i1 %need.dyn.free, label %dyn.free, label %if.end
49 dyn.free:
50   call void @CustomFree(i8* %mem)
51   br label %if.end
52 if.end:
53   cleanupret from %tok unwind to caller
56 ; CHECK-LABEL: @callResume(
57 define void @callResume() {
58 entry:
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 i8* @f()
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(i8* %hdl)
69 ; CHECK: tail call void @bar(
70   tail call void @bar(i8* null)
72 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.resume to void (i8*)*)(i8* %vFrame)
73   %0 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0)
74   %1 = bitcast i8* %0 to void (i8*)*
75   call fastcc void %1(i8* %hdl)
77 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.cleanup to void (i8*)*)(i8* %vFrame)
78   %2 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
79   %3 = bitcast i8* %2 to void (i8*)*
80   call fastcc void %3(i8* %hdl)
82 ; CHECK-NEXT: ret void
83   ret void
86 ; CHECK-LABEL: @callResume_with_coro_suspend_1(
87 define void @callResume_with_coro_suspend_1() {
88 entry:
89 ; CHECK: alloca [4 x i8], align 4
90 ; CHECK-NOT: coro.begin
91 ; CHECK-NOT: CustomAlloc
92 ; CHECK: call void @may_throw()
93   %hdl = call i8* @f()
95 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.resume to void (i8*)*)(i8* %vFrame)
96   %0 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0)
97   %1 = bitcast i8* %0 to void (i8*)*
98   call fastcc void %1(i8* %hdl)
99   %2 = call token @llvm.coro.save(i8* %hdl)
100   %3 = call i8 @llvm.coro.suspend(token %2, i1 false)
101   switch i8 %3, label  %coro.ret [
102     i8 0, label %final.suspend
103     i8 1, label %cleanups
104   ]
106 ; CHECK-LABEL: final.suspend:
107 final.suspend:
108 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.cleanup to void (i8*)*)(i8* %vFrame)
109   %4 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
110   %5 = bitcast i8* %4 to void (i8*)*
111   call fastcc void %5(i8* %hdl)
112   %6 = call token @llvm.coro.save(i8* %hdl)
113   %7 = call i8 @llvm.coro.suspend(token %6, i1 true)
114   switch i8 %7, label  %coro.ret [
115     i8 0, label %coro.ret
116     i8 1, label %cleanups
117   ]
119 ; CHECK-LABEL: cleanups:
120 cleanups:
121 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.cleanup to void (i8*)*)(i8* %vFrame)
122   %8 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
123   %9 = bitcast i8* %8 to void (i8*)*
124   call fastcc void %9(i8* %hdl)
125   br label %coro.ret
127 ; CHECK-LABEL: coro.ret:
128 coro.ret:
129 ; CHECK-NEXT: ret void
130   ret void
133 ; CHECK-LABEL: @callResume_with_coro_suspend_2(
134 define void @callResume_with_coro_suspend_2() personality i8* null {
135 entry:
136 ; CHECK: alloca [4 x i8], align 4
137 ; CHECK-NOT: coro.begin
138 ; CHECK-NOT: CustomAlloc
139 ; CHECK: call void @may_throw()
140   %hdl = call i8* @f()
142   %0 = call token @llvm.coro.save(i8* %hdl)
143 ; CHECK: invoke fastcc void bitcast (void (%f.frame*)* @f.resume to void (i8*)*)(i8* %vFrame)
144   %1 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0)
145   %2 = bitcast i8* %1 to void (i8*)*
146   invoke fastcc void %2(i8* %hdl)
147     to label %invoke.cont1 unwind label %lpad
149 ; CHECK-LABEL: invoke.cont1:
150 invoke.cont1:
151   %3 = call i8 @llvm.coro.suspend(token %0, i1 false)
152   switch i8 %3, label  %coro.ret [
153     i8 0, label %final.ready
154     i8 1, label %cleanups
155   ]
157 ; CHECK-LABEL: lpad:
158 lpad:
159   %4 = landingpad { i8*, i32 }
160           catch i8* null
161 ; CHECK: call fastcc void bitcast (void (%f.frame*)* @f.cleanup to void (i8*)*)(i8* %vFrame)
162   %5 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
163   %6 = bitcast i8* %5 to void (i8*)*
164   call fastcc void %6(i8* %hdl)
165   br label %final.suspend
167 ; CHECK-LABEL: final.ready:
168 final.ready:
169 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.cleanup to void (i8*)*)(i8* %vFrame)
170   %7 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
171   %8 = bitcast i8* %7 to void (i8*)*
172   call fastcc void %8(i8* %hdl)
173   br label %final.suspend
175 ; CHECK-LABEL: final.suspend:
176 final.suspend:
177   %9 = call token @llvm.coro.save(i8* %hdl)
178   %10 = call i8 @llvm.coro.suspend(token %9, i1 true)
179   switch i8 %10, label  %coro.ret [
180     i8 0, label %coro.ret
181     i8 1, label %cleanups
182   ]
184 ; CHECK-LABEL: cleanups:
185 cleanups:
186 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.cleanup to void (i8*)*)(i8* %vFrame)
187   %11 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
188   %12 = bitcast i8* %11 to void (i8*)*
189   call fastcc void %12(i8* %hdl)
190   br label %coro.ret
192 ; CHECK-LABEL: coro.ret:
193 coro.ret:
194 ; CHECK-NEXT: ret void
195   ret void
198 ; CHECK-LABEL: @callResume_with_coro_suspend_3(
199 define void @callResume_with_coro_suspend_3(i8 %cond) {
200 entry:
201 ; CHECK: alloca [4 x i8], align 4
202   switch i8 %cond, label  %coro.ret [
203     i8 0, label %init.suspend
204     i8 1, label %coro.ret
205   ]
207 init.suspend:
208 ; CHECK-NOT: llvm.coro.begin
209 ; CHECK-NOT: CustomAlloc
210 ; CHECK: call void @may_throw()
211   %hdl = call i8* @f()
212 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.resume to void (i8*)*)(i8* %vFrame)
213   %0 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0)
214   %1 = bitcast i8* %0 to void (i8*)*
215   call fastcc void %1(i8* %hdl)
216   %2 = call token @llvm.coro.save(i8* %hdl)
217   %3 = call i8 @llvm.coro.suspend(token %2, i1 false)
218   switch i8 %3, label  %coro.ret [
219     i8 0, label %final.suspend
220     i8 1, label %cleanups
221   ]
223 ; CHECK-LABEL: final.suspend:
224 final.suspend:
225 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.cleanup to void (i8*)*)(i8* %vFrame)
226   %4 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
227   %5 = bitcast i8* %4 to void (i8*)*
228   call fastcc void %5(i8* %hdl)
229   %6 = call token @llvm.coro.save(i8* %hdl)
230   %7 = call i8 @llvm.coro.suspend(token %6, i1 true)
231   switch i8 %7, label  %coro.ret [
232     i8 0, label %coro.ret
233     i8 1, label %cleanups
234   ]
236 ; CHECK-LABEL: cleanups:
237 cleanups:
238 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.cleanup to void (i8*)*)(i8* %vFrame)
239   %8 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
240   %9 = bitcast i8* %8 to void (i8*)*
241   call fastcc void %9(i8* %hdl)
242   br label %coro.ret
244 ; CHECK-LABEL: coro.ret:
245 coro.ret:
246 ; CHECK-NEXT: ret void
247   ret void
252 ; CHECK-LABEL: @callResume_PR34897_no_elision(
253 define void @callResume_PR34897_no_elision(i1 %cond) {
254 ; CHECK-LABEL: entry:
255 entry:
256 ; CHECK: call i8* @CustomAlloc(
257   %hdl = call i8* @f()
258 ; CHECK: tail call void @bar(
259   tail call void @bar(i8* %hdl)
260 ; CHECK: tail call void @bar(
261   tail call void @bar(i8* null)
262   br i1 %cond, label %if.then, label %if.else
264 ; CHECK-LABEL: if.then:
265 if.then:
266 ; CHECK: call fastcc void bitcast (void (%f.frame*)* @f.resume to void (i8*)*)(i8*
267   %0 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0)
268   %1 = bitcast i8* %0 to void (i8*)*
269   call fastcc void %1(i8* %hdl)
270 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.destroy to void (i8*)*)(i8*
271   %2 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
272   %3 = bitcast i8* %2 to void (i8*)*
273   call fastcc void %3(i8* %hdl)
274   br label %return
276 if.else:
277   br label %return
279 ; CHECK-LABEL: return:
280 return:
281 ; CHECK: ret void
282   ret void
285 ; CHECK-LABEL: @callResume_PR34897_elision(
286 define void @callResume_PR34897_elision(i1 %cond) {
287 ; CHECK-LABEL: entry:
288 entry:
289 ; CHECK: alloca [4 x i8], align 4
290 ; CHECK: tail call void @bar(
291   tail call void @bar(i8* null)
292   br i1 %cond, label %if.then, label %if.else
294 if.then:
295 ; CHECK-NOT: CustomAlloc
296 ; CHECK: call void @may_throw()
297   %hdl = call i8* @f()
298 ; CHECK: call void @bar(
299   tail call void @bar(i8* %hdl)
300 ; CHECK: call fastcc void bitcast (void (%f.frame*)* @f.resume to void (i8*)*)(i8*
301   %0 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0)
302   %1 = bitcast i8* %0 to void (i8*)*
303   call fastcc void %1(i8* %hdl)
304 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.cleanup to void (i8*)*)(i8*
305   %2 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
306   %3 = bitcast i8* %2 to void (i8*)*
307   call fastcc void %3(i8* %hdl)
308   br label %return
310 if.else:
311   br label %return
313 ; CHECK-LABEL: return:
314 return:
315 ; CHECK: ret void
316   ret void
320 ; a coroutine start function (cannot elide heap alloc, due to second argument to
321 ; coro.begin not pointint to coro.alloc)
322 define i8* @f_no_elision() personality i8* null {
323 entry:
324   %id = call token @llvm.coro.id(i32 0, i8* null,
325                       i8* bitcast (i8*()* @f_no_elision to i8*),
326                       i8* bitcast ([3 x void (%f.frame*)*]* @f.resumers to i8*))
327   %alloc = call i8* @CustomAlloc(i32 4)
328   %hdl = call i8* @llvm.coro.begin(token %id, i8* %alloc)
329   ret i8* %hdl
332 ; CHECK-LABEL: @callResume_no_elision(
333 define void @callResume_no_elision() {
334 entry:
335 ; CHECK: call i8* @CustomAlloc(
336   %hdl = call i8* @f_no_elision()
338 ; Tail call should remain tail calls
339 ; CHECK: tail call void @bar(
340   tail call void @bar(i8* %hdl)
341 ; CHECK: tail call void @bar(  
342   tail call void @bar(i8* null)
344 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.resume to void (i8*)*)(i8*
345   %0 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0)
346   %1 = bitcast i8* %0 to void (i8*)*
347   call fastcc void %1(i8* %hdl)
349 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.destroy to void (i8*)*)(i8*
350   %2 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
351   %3 = bitcast i8* %2 to void (i8*)*
352   call fastcc void %3(i8* %hdl)
354 ; CHECK-NEXT: ret void
355   ret void
358 declare token @llvm.coro.id(i32, i8*, i8*, i8*)
359 declare i1 @llvm.coro.alloc(token)
360 declare i8* @llvm.coro.free(token, i8*)
361 declare i8* @llvm.coro.begin(token, i8*)
362 declare i8* @llvm.coro.frame(token)
363 declare i8* @llvm.coro.subfn.addr(i8*, i8)
364 declare i8 @llvm.coro.suspend(token, i1)
365 declare token @llvm.coro.save(i8*)