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