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.
5 ; RUN: -passes='cgscc(inline,function(coro-elide,instsimplify,simplifycfg))' \
6 ; RUN: -aa-pipeline='basic-aa' | FileCheck %s
8 declare void @print(i32) nounwind
12 declare void @bar(i8*)
14 declare fastcc void @f.resume(%f.frame* align 4 dereferenceable(4))
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 {
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
34 %alloc = call i8* @CustomAlloc(i32 4)
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
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
50 call void @CustomFree(i8* %mem)
53 cleanupret from %tok unwind to caller
56 ; CHECK-LABEL: @callResume(
57 define void @callResume() {
59 ; CHECK: alloca [4 x i8], align 4
60 ; CHECK-NOT: coro.begin
61 ; CHECK-NOT: CustomAlloc
62 ; CHECK: call void @may_throw()
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
86 ; CHECK-LABEL: @callResume_with_coro_suspend_1(
87 define void @callResume_with_coro_suspend_1() {
89 ; CHECK: alloca [4 x i8], align 4
90 ; CHECK-NOT: coro.begin
91 ; CHECK-NOT: CustomAlloc
92 ; CHECK: call void @may_throw()
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
106 ; CHECK-LABEL: 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
119 ; CHECK-LABEL: 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)
127 ; CHECK-LABEL: coro.ret:
129 ; CHECK-NEXT: ret void
133 ; CHECK-LABEL: @callResume_with_coro_suspend_2(
134 define void @callResume_with_coro_suspend_2() personality i8* null {
136 ; CHECK: alloca [4 x i8], align 4
137 ; CHECK-NOT: coro.begin
138 ; CHECK-NOT: CustomAlloc
139 ; CHECK: call void @may_throw()
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:
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
159 %4 = landingpad { i8*, i32 }
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:
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:
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
184 ; CHECK-LABEL: 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)
192 ; CHECK-LABEL: coro.ret:
194 ; CHECK-NEXT: ret void
198 ; CHECK-LABEL: @callResume_with_coro_suspend_3(
199 define void @callResume_with_coro_suspend_3(i8 %cond) {
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
208 ; CHECK-NOT: llvm.coro.begin
209 ; CHECK-NOT: CustomAlloc
210 ; CHECK: call void @may_throw()
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
223 ; CHECK-LABEL: 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
236 ; CHECK-LABEL: 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)
244 ; CHECK-LABEL: coro.ret:
246 ; CHECK-NEXT: ret void
252 ; CHECK-LABEL: @callResume_PR34897_no_elision(
253 define void @callResume_PR34897_no_elision(i1 %cond) {
254 ; CHECK-LABEL: entry:
256 ; CHECK: call i8* @CustomAlloc(
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:
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)
279 ; CHECK-LABEL: return:
285 ; CHECK-LABEL: @callResume_PR34897_elision(
286 define void @callResume_PR34897_elision(i1 %cond) {
287 ; CHECK-LABEL: 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
295 ; CHECK-NOT: CustomAlloc
296 ; CHECK: call void @may_throw()
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)
313 ; CHECK-LABEL: return:
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 {
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)
332 ; CHECK-LABEL: @callResume_no_elision(
333 define void @callResume_no_elision() {
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
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*)