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