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 -inline -coro-elide -instsimplify -simplifycfg | FileCheck %s
6 declare void @print(i32) nounwind
10 declare void @bar(i8*)
12 declare fastcc void @f.resume(%f.frame*)
13 declare fastcc void @f.destroy(%f.frame*)
14 declare fastcc void @f.cleanup(%f.frame*)
16 declare void @may_throw()
17 declare i8* @CustomAlloc(i32)
18 declare void @CustomFree(i8*)
20 @f.resumers = internal constant [3 x void (%f.frame*)*]
21 [void (%f.frame*)* @f.resume, void (%f.frame*)* @f.destroy, void (%f.frame*)* @f.cleanup]
23 ; a coroutine start function
24 define i8* @f() personality i8* null {
26 %id = call token @llvm.coro.id(i32 0, i8* null,
27 i8* bitcast (i8*()* @f to i8*),
28 i8* bitcast ([3 x void (%f.frame*)*]* @f.resumers to i8*))
29 %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id)
30 br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin
32 %alloc = call i8* @CustomAlloc(i32 4)
35 %phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ]
36 %hdl = call i8* @llvm.coro.begin(token %id, i8* %phi)
37 invoke void @may_throw()
38 to label %ret unwind label %ehcleanup
43 %tok = cleanuppad within none []
44 %mem = call i8* @llvm.coro.free(token %id, i8* %hdl)
45 %need.dyn.free = icmp ne i8* %mem, null
46 br i1 %need.dyn.free, label %dyn.free, label %if.end
48 call void @CustomFree(i8* %mem)
51 cleanupret from %tok unwind to caller
54 ; CHECK-LABEL: @callResume(
55 define void @callResume() {
57 ; CHECK: alloca %f.frame
58 ; CHECK-NOT: coro.begin
59 ; CHECK-NOT: CustomAlloc
60 ; CHECK: call void @may_throw()
63 ; Need to remove 'tail' from the first call to @bar
64 ; CHECK-NOT: tail call void @bar(
65 ; CHECK: call void @bar(
66 tail call void @bar(i8* %hdl)
67 ; CHECK: tail call void @bar(
68 tail call void @bar(i8* null)
70 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.resume to void (i8*)*)(i8* %vFrame)
71 %0 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0)
72 %1 = bitcast i8* %0 to void (i8*)*
73 call fastcc void %1(i8* %hdl)
75 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.cleanup to void (i8*)*)(i8* %vFrame)
76 %2 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
77 %3 = bitcast i8* %2 to void (i8*)*
78 call fastcc void %3(i8* %hdl)
80 ; CHECK-NEXT: ret void
84 ; CHECK-LABEL: @callResume_PR34897_no_elision(
85 define void @callResume_PR34897_no_elision(i1 %cond) {
88 ; CHECK: call i8* @CustomAlloc(
90 ; CHECK: tail call void @bar(
91 tail call void @bar(i8* %hdl)
92 ; CHECK: tail call void @bar(
93 tail call void @bar(i8* null)
94 br i1 %cond, label %if.then, label %if.else
96 ; CHECK-LABEL: if.then:
98 ; CHECK: call fastcc void bitcast (void (%f.frame*)* @f.resume to void (i8*)*)(i8*
99 %0 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0)
100 %1 = bitcast i8* %0 to void (i8*)*
101 call fastcc void %1(i8* %hdl)
102 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.destroy to void (i8*)*)(i8*
103 %2 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
104 %3 = bitcast i8* %2 to void (i8*)*
105 call fastcc void %3(i8* %hdl)
111 ; CHECK-LABEL: return:
117 ; a coroutine start function (cannot elide heap alloc, due to second argument to
118 ; coro.begin not pointint to coro.alloc)
119 define i8* @f_no_elision() personality i8* null {
121 %id = call token @llvm.coro.id(i32 0, i8* null,
122 i8* bitcast (i8*()* @f_no_elision to i8*),
123 i8* bitcast ([3 x void (%f.frame*)*]* @f.resumers to i8*))
124 %alloc = call i8* @CustomAlloc(i32 4)
125 %hdl = call i8* @llvm.coro.begin(token %id, i8* %alloc)
129 ; CHECK-LABEL: @callResume_no_elision(
130 define void @callResume_no_elision() {
132 ; CHECK: call i8* @CustomAlloc(
133 %hdl = call i8* @f_no_elision()
135 ; Tail call should remain tail calls
136 ; CHECK: tail call void @bar(
137 tail call void @bar(i8* %hdl)
138 ; CHECK: tail call void @bar(
139 tail call void @bar(i8* null)
141 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.resume to void (i8*)*)(i8*
142 %0 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0)
143 %1 = bitcast i8* %0 to void (i8*)*
144 call fastcc void %1(i8* %hdl)
146 ; CHECK-NEXT: call fastcc void bitcast (void (%f.frame*)* @f.destroy to void (i8*)*)(i8*
147 %2 = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
148 %3 = bitcast i8* %2 to void (i8*)*
149 call fastcc void %3(i8* %hdl)
151 ; CHECK-NEXT: ret void
155 declare token @llvm.coro.id(i32, i8*, i8*, i8*)
156 declare i1 @llvm.coro.alloc(token)
157 declare i8* @llvm.coro.free(token, i8*)
158 declare i8* @llvm.coro.begin(token, i8*)
159 declare i8* @llvm.coro.frame(token)
160 declare i8* @llvm.coro.subfn.addr(i8*, i8)