1; Test no suspend coroutines 2; RUN: opt < %s -coro-split -S | FileCheck %s 3; RUN: opt < %s -passes=coro-split -S | FileCheck %s 4 5; Coroutine with no-suspends will turn into: 6; 7; CHECK-LABEL: define void @no_suspends( 8; CHECK-NEXT: entry: 9; CHECK-NEXT: alloca 10; CHECK-NEXT: bitcast 11; CHECK-NEXT: call void @print(i32 %n) 12; CHECK-NEXT: ret void 13; 14define void @no_suspends(i32 %n) "coroutine.presplit"="1" { 15entry: 16 %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) 17 %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) 18 br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin 19dyn.alloc: 20 %size = call i32 @llvm.coro.size.i32() 21 %alloc = call i8* @malloc(i32 %size) 22 br label %coro.begin 23coro.begin: 24 %phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ] 25 %hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi) 26 br label %body 27body: 28 call void @print(i32 %n) 29 br label %cleanup 30cleanup: 31 %mem = call i8* @llvm.coro.free(token %id, i8* %hdl) 32 %need.dyn.free = icmp ne i8* %mem, null 33 br i1 %need.dyn.free, label %dyn.free, label %suspend 34dyn.free: 35 call void @free(i8* %mem) 36 br label %suspend 37suspend: 38 call i1 @llvm.coro.end(i8* %hdl, i1 false) 39 ret void 40} 41 42; SimplifySuspendPoint will detect that coro.resume resumes itself and will 43; replace suspend with a jump to %resume label turning it into no-suspend 44; coroutine. 45; 46; CHECK-LABEL: define void @simplify_resume( 47; CHECK-NEXT: entry: 48; CHECK-NEXT: alloca 49; CHECK-NEXT: bitcast 50; CHECK-NEXT: call void @llvm.memcpy 51; CHECK-NEXT: call void @print(i32 0) 52; CHECK-NEXT: ret void 53; 54define void @simplify_resume(i8* %src, i8* %dst) "coroutine.presplit"="1" { 55entry: 56 %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) 57 %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) 58 br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin 59dyn.alloc: 60 %size = call i32 @llvm.coro.size.i32() 61 %alloc = call i8* @malloc(i32 %size) 62 br label %coro.begin 63coro.begin: 64 %phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ] 65 %hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi) 66 br label %body 67body: 68 %save = call token @llvm.coro.save(i8* %hdl) 69 ; memcpy intrinsics should not prevent simplification. 70 call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, i64 1, i1 false) 71 %subfn = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0) 72 %bres = bitcast i8* %subfn to void (i8*)* 73 call fastcc void %bres(i8* %hdl) 74 %0 = call i8 @llvm.coro.suspend(token %save, i1 false) 75 switch i8 %0, label %suspend [i8 0, label %resume 76 i8 1, label %pre.cleanup] 77resume: 78 call void @print(i32 0) 79 br label %cleanup 80 81pre.cleanup: 82 call void @print(i32 1) 83 br label %cleanup 84 85cleanup: 86 %mem = call i8* @llvm.coro.free(token %id, i8* %hdl) 87 call void @free(i8* %mem) 88 br label %suspend 89suspend: 90 call i1 @llvm.coro.end(i8* %hdl, i1 false) 91 ret void 92} 93 94; SimplifySuspendPoint will detect that coroutine destroys itself and will 95; replace suspend with a jump to %cleanup label turning it into no-suspend 96; coroutine. 97; 98; CHECK-LABEL: define void @simplify_destroy( 99; CHECK-NEXT: entry: 100; CHECK-NEXT: alloca 101; CHECK-NEXT: bitcast 102; CHECK-NEXT: call void @print(i32 1) 103; CHECK-NEXT: ret void 104; 105define void @simplify_destroy() "coroutine.presplit"="1" personality i32 0 { 106entry: 107 %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) 108 %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) 109 br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin 110dyn.alloc: 111 %size = call i32 @llvm.coro.size.i32() 112 %alloc = call i8* @malloc(i32 %size) 113 br label %coro.begin 114coro.begin: 115 %phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ] 116 %hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi) 117 br label %body 118body: 119 %save = call token @llvm.coro.save(i8* %hdl) 120 %subfn = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1) 121 %bcast = bitcast i8* %subfn to void (i8*)* 122 invoke fastcc void %bcast(i8* %hdl) to label %real_susp unwind label %lpad 123 124real_susp: 125 %0 = call i8 @llvm.coro.suspend(token %save, i1 false) 126 switch i8 %0, label %suspend [i8 0, label %resume 127 i8 1, label %pre.cleanup] 128resume: 129 call void @print(i32 0) 130 br label %cleanup 131 132pre.cleanup: 133 call void @print(i32 1) 134 br label %cleanup 135 136cleanup: 137 %mem = call i8* @llvm.coro.free(token %id, i8* %hdl) 138 call void @free(i8* %mem) 139 br label %suspend 140suspend: 141 call i1 @llvm.coro.end(i8* %hdl, i1 false) 142 ret void 143lpad: 144 %lpval = landingpad { i8*, i32 } 145 cleanup 146 147 call void @print(i32 2) 148 resume { i8*, i32 } %lpval 149} 150 151; SimplifySuspendPoint will detect that coro.resume resumes itself and will 152; replace suspend with a jump to %resume label turning it into no-suspend 153; coroutine. 154; 155; CHECK-LABEL: define void @simplify_resume_with_inlined_if( 156; CHECK-NEXT: entry: 157; CHECK-NEXT: alloca 158; CHECK-NEXT: bitcast 159; CHECK-NEXT: br i1 160; CHECK: call void @print(i32 0) 161; CHECK-NEXT: ret void 162; 163define void @simplify_resume_with_inlined_if(i8* %src, i8* %dst, i1 %cond) "coroutine.presplit"="1" { 164entry: 165 %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) 166 %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) 167 br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin 168dyn.alloc: 169 %size = call i32 @llvm.coro.size.i32() 170 %alloc = call i8* @malloc(i32 %size) 171 br label %coro.begin 172coro.begin: 173 %phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ] 174 %hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi) 175 br label %body 176body: 177 %save = call token @llvm.coro.save(i8* %hdl) 178 br i1 %cond, label %if.then, label %if.else 179if.then: 180 call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, i64 1, i1 false) 181 br label %if.end 182if.else: 183 call void @llvm.memcpy.p0i8.p0i8.i64(i8* %src, i8* %dst, i64 1, i1 false) 184 br label %if.end 185if.end: 186 %subfn = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0) 187 %bres = bitcast i8* %subfn to void (i8*)* 188 call fastcc void %bres(i8* %hdl) 189 %0 = call i8 @llvm.coro.suspend(token %save, i1 false) 190 switch i8 %0, label %suspend [i8 0, label %resume 191 i8 1, label %pre.cleanup] 192resume: 193 call void @print(i32 0) 194 br label %cleanup 195 196pre.cleanup: 197 call void @print(i32 1) 198 br label %cleanup 199 200cleanup: 201 %mem = call i8* @llvm.coro.free(token %id, i8* %hdl) 202 call void @free(i8* %mem) 203 br label %suspend 204suspend: 205 call i1 @llvm.coro.end(i8* %hdl, i1 false) 206 ret void 207} 208 209 210 211; SimplifySuspendPoint won't be able to simplify if it detects that there are 212; other calls between coro.save and coro.suspend. They potentially can call 213; resume or destroy, so we should not simplify this suspend point. 214; 215; CHECK-LABEL: define void @cannot_simplify_other_calls( 216; CHECK-NEXT: entry: 217; CHECK-NEXT: llvm.coro.id 218 219define void @cannot_simplify_other_calls() "coroutine.presplit"="1" { 220entry: 221 %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) 222 %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) 223 br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin 224dyn.alloc: 225 %size = call i32 @llvm.coro.size.i32() 226 %alloc = call i8* @malloc(i32 %size) 227 br label %coro.begin 228coro.begin: 229 %phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ] 230 %hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi) 231 br label %body 232body: 233 %save = call token @llvm.coro.save(i8* %hdl) 234 br label %body1 235 236body1: 237 call void @foo() 238 br label %body2 239 240body2: 241 %subfn = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1) 242 %bcast = bitcast i8* %subfn to void (i8*)* 243 call fastcc void %bcast(i8* %hdl) 244 %0 = call i8 @llvm.coro.suspend(token %save, i1 false) 245 switch i8 %0, label %suspend [i8 0, label %resume 246 i8 1, label %pre.cleanup] 247resume: 248 call void @print(i32 0) 249 br label %cleanup 250 251pre.cleanup: 252 call void @print(i32 1) 253 br label %cleanup 254 255cleanup: 256 %mem = call i8* @llvm.coro.free(token %id, i8* %hdl) 257 call void @free(i8* %mem) 258 br label %suspend 259suspend: 260 call i1 @llvm.coro.end(i8* %hdl, i1 false) 261 ret void 262} 263 264; SimplifySuspendPoint won't be able to simplify if it detects that there are 265; other calls between coro.save and coro.suspend. They potentially can call 266; resume or destroy, so we should not simplify this suspend point. 267; 268; CHECK-LABEL: define void @cannot_simplify_calls_in_terminator( 269; CHECK-NEXT: entry: 270; CHECK-NEXT: llvm.coro.id 271 272define void @cannot_simplify_calls_in_terminator() "coroutine.presplit"="1" personality i32 0 { 273entry: 274 %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) 275 %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) 276 br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin 277dyn.alloc: 278 %size = call i32 @llvm.coro.size.i32() 279 %alloc = call i8* @malloc(i32 %size) 280 br label %coro.begin 281coro.begin: 282 %phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ] 283 %hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi) 284 br label %body 285body: 286 %save = call token @llvm.coro.save(i8* %hdl) 287 invoke void @foo() to label %resume_cont unwind label %lpad 288resume_cont: 289 %subfn = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1) 290 %bcast = bitcast i8* %subfn to void (i8*)* 291 call fastcc void %bcast(i8* %hdl) 292 %0 = call i8 @llvm.coro.suspend(token %save, i1 false) 293 switch i8 %0, label %suspend [i8 0, label %resume 294 i8 1, label %pre.cleanup] 295resume: 296 call void @print(i32 0) 297 br label %cleanup 298 299pre.cleanup: 300 call void @print(i32 1) 301 br label %cleanup 302 303cleanup: 304 %mem = call i8* @llvm.coro.free(token %id, i8* %hdl) 305 call void @free(i8* %mem) 306 br label %suspend 307suspend: 308 call i1 @llvm.coro.end(i8* %hdl, i1 false) 309 ret void 310lpad: 311 %lpval = landingpad { i8*, i32 } 312 cleanup 313 314 call void @print(i32 2) 315 resume { i8*, i32 } %lpval 316} 317 318; SimplifySuspendPoint won't be able to simplify if it detects that resume or 319; destroy does not immediately preceed coro.suspend. 320; 321; CHECK-LABEL: define void @cannot_simplify_not_last_instr( 322; CHECK-NEXT: entry: 323; CHECK-NEXT: llvm.coro.id 324 325define void @cannot_simplify_not_last_instr(i8* %dst, i8* %src) "coroutine.presplit"="1" { 326entry: 327 %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) 328 %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) 329 br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin 330dyn.alloc: 331 %size = call i32 @llvm.coro.size.i32() 332 %alloc = call i8* @malloc(i32 %size) 333 br label %coro.begin 334coro.begin: 335 %phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ] 336 %hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi) 337 br label %body 338body: 339 %save = call token @llvm.coro.save(i8* %hdl) 340 %subfn = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1) 341 %bcast = bitcast i8* %subfn to void (i8*)* 342 call fastcc void %bcast(i8* %hdl) 343 ; memcpy separates destory from suspend, therefore cannot simplify. 344 call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, i64 1, i1 false) 345 %0 = call i8 @llvm.coro.suspend(token %save, i1 false) 346 switch i8 %0, label %suspend [i8 0, label %resume 347 i8 1, label %pre.cleanup] 348resume: 349 call void @print(i32 0) 350 br label %cleanup 351 352pre.cleanup: 353 call void @print(i32 1) 354 br label %cleanup 355 356cleanup: 357 %mem = call i8* @llvm.coro.free(token %id, i8* %hdl) 358 call void @free(i8* %mem) 359 br label %suspend 360suspend: 361 call i1 @llvm.coro.end(i8* %hdl, i1 false) 362 ret void 363} 364 365; SimplifySuspendPoint should not simplify final suspend point 366; 367; CHECK-LABEL: define void @cannot_simplify_final_suspend( 368; CHECK-NEXT: entry: 369; CHECK-NEXT: llvm.coro.id 370; 371define void @cannot_simplify_final_suspend() "coroutine.presplit"="1" personality i32 0 { 372entry: 373 %id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* null) 374 %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) 375 br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin 376dyn.alloc: 377 %size = call i32 @llvm.coro.size.i32() 378 %alloc = call i8* @malloc(i32 %size) 379 br label %coro.begin 380coro.begin: 381 %phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ] 382 %hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi) 383 br label %body 384body: 385 %save = call token @llvm.coro.save(i8* %hdl) 386 %subfn = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1) 387 %bcast = bitcast i8* %subfn to void (i8*)* 388 invoke fastcc void %bcast(i8* %hdl) to label %real_susp unwind label %lpad 389 390real_susp: 391 %0 = call i8 @llvm.coro.suspend(token %save, i1 1) 392 switch i8 %0, label %suspend [i8 0, label %resume 393 i8 1, label %pre.cleanup] 394resume: 395 call void @print(i32 0) 396 br label %cleanup 397 398pre.cleanup: 399 call void @print(i32 1) 400 br label %cleanup 401 402cleanup: 403 %mem = call i8* @llvm.coro.free(token %id, i8* %hdl) 404 call void @free(i8* %mem) 405 br label %suspend 406suspend: 407 call i1 @llvm.coro.end(i8* %hdl, i1 false) 408 ret void 409lpad: 410 %lpval = landingpad { i8*, i32 } 411 cleanup 412 413 call void @print(i32 2) 414 resume { i8*, i32 } %lpval 415} 416 417declare i8* @malloc(i32) 418declare void @free(i8*) 419declare void @print(i32) 420declare void @foo() 421 422declare token @llvm.coro.id(i32, i8*, i8*, i8*) 423declare i1 @llvm.coro.alloc(token) 424declare i32 @llvm.coro.size.i32() 425declare i8* @llvm.coro.begin(token, i8*) 426declare token @llvm.coro.save(i8* %hdl) 427declare i8 @llvm.coro.suspend(token, i1) 428declare i8* @llvm.coro.free(token, i8*) 429declare i1 @llvm.coro.end(i8*, i1) 430 431declare i8* @llvm.coro.subfn.addr(i8*, i8) 432 433declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1) 434