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(ptr)
14 declare fastcc void @f.resume(ptr align 4 dereferenceable(4))
15 declare fastcc void @f.destroy(ptr)
16 declare fastcc void @f.cleanup(ptr)
18 declare void @may_throw()
19 declare ptr @CustomAlloc(i32)
20 declare void @CustomFree(ptr)
22 @f.resumers = internal constant [3 x ptr]
23 [ptr @f.resume, ptr @f.destroy, ptr @f.cleanup]
25 ; a coroutine start function
26 define ptr @f() personality ptr null {
28 %id = call token @llvm.coro.id(i32 0, ptr null,
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 ptr @CustomAlloc(i32 4)
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
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
50 call void @CustomFree(ptr %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(ptr %hdl)
69 ; CHECK: tail call void @bar(
70 tail call void @bar(ptr null)
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)
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)
80 ; CHECK-NEXT: ret void
84 ; CHECK-LABEL: @callResume_with_coro_suspend_1(
85 define void @callResume_with_coro_suspend_1() {
87 ; CHECK: alloca [4 x i8], align 4
88 ; CHECK-NOT: coro.begin
89 ; CHECK-NOT: CustomAlloc
90 ; CHECK: call void @may_throw()
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
103 ; CHECK-LABEL: final.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
115 ; CHECK-LABEL: cleanups:
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)
122 ; CHECK-LABEL: coro.ret:
124 ; CHECK-NEXT: ret void
128 ; CHECK-LABEL: @callResume_with_coro_suspend_2(
129 define void @callResume_with_coro_suspend_2() personality ptr null {
131 ; CHECK: alloca [4 x i8], align 4
132 ; CHECK-NOT: coro.begin
133 ; CHECK-NOT: CustomAlloc
134 ; CHECK: call void @may_throw()
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
143 ; CHECK-LABEL: invoke.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
153 %3 = landingpad { ptr, i32 }
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
160 ; CHECK-LABEL: final.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
167 ; CHECK-LABEL: final.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
176 ; CHECK-LABEL: cleanups:
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)
183 ; CHECK-LABEL: coro.ret:
185 ; CHECK-NEXT: ret void
189 ; CHECK-LABEL: @callResume_with_coro_suspend_3(
190 define void @callResume_with_coro_suspend_3(i8 %cond) {
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
199 ; CHECK-NOT: llvm.coro.begin
200 ; CHECK-NOT: CustomAlloc
201 ; CHECK: call void @may_throw()
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
213 ; CHECK-LABEL: final.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
225 ; CHECK-LABEL: cleanups:
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)
232 ; CHECK-LABEL: coro.ret:
234 ; CHECK-NEXT: ret void
240 ; CHECK-LABEL: @callResume_PR34897_no_elision(
241 define void @callResume_PR34897_no_elision(i1 %cond) {
242 ; CHECK-LABEL: entry:
244 ; CHECK: call ptr @CustomAlloc(
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
252 ; CHECK-LABEL: if.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)
265 ; CHECK-LABEL: return:
271 ; CHECK-LABEL: @callResume_PR34897_elision(
272 define void @callResume_PR34897_elision(i1 %cond) {
273 ; CHECK-LABEL: entry:
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
281 ; CHECK-NOT: CustomAlloc
282 ; CHECK: call void @may_throw()
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)
297 ; CHECK-LABEL: return:
304 ; a coroutine start function (cannot elide heap alloc, due to second argument to
305 ; coro.begin not pointint to coro.alloc)
306 define ptr @f_no_elision() personality ptr null {
308 %id = call token @llvm.coro.id(i32 0, ptr null,
311 %alloc = call ptr @CustomAlloc(i32 4)
312 %hdl = call ptr @llvm.coro.begin(token %id, ptr %alloc)
316 ; CHECK-LABEL: @callResume_no_elision(
317 define void @callResume_no_elision() {
319 ; CHECK: call ptr @CustomAlloc(
320 %hdl = call ptr @f_no_elision()
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)
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)
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)
336 ; CHECK-NEXT: ret void
340 declare token @llvm.coro.id(i32, ptr, ptr, ptr)
341 declare i1 @llvm.coro.alloc(token)
342 declare ptr @llvm.coro.free(token, ptr)
343 declare ptr @llvm.coro.begin(token, ptr)
344 declare ptr @llvm.coro.frame(token)
345 declare ptr @llvm.coro.subfn.addr(ptr, i8)
346 declare i8 @llvm.coro.suspend(token, i1)
347 declare token @llvm.coro.save(ptr)