| ; Test no suspend coroutines | 
 | ; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse,simplifycfg' -S | FileCheck %s | 
 |  | 
 | ; Coroutine with no-suspends will turn into: | 
 | ; | 
 | ; CHECK-LABEL: define void @no_suspends( | 
 | ; CHECK-NEXT:  entry: | 
 | ; CHECK-NEXT:    call void @print(i32 %n) | 
 | ; CHECK-NEXT:    ret void | 
 | ; | 
 | define void @no_suspends(i32 %n) presplitcoroutine { | 
 | entry: | 
 |   %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null) | 
 |   %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) | 
 |   br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin | 
 | dyn.alloc: | 
 |   %size = call i32 @llvm.coro.size.i32() | 
 |   %alloc = call ptr @malloc(i32 %size) | 
 |   br label %coro.begin | 
 | coro.begin: | 
 |   %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ] | 
 |   %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi) | 
 |   br label %body | 
 | body: | 
 |   call void @print(i32 %n) | 
 |   br label %cleanup | 
 | cleanup: | 
 |   %mem = call ptr @llvm.coro.free(token %id, ptr %hdl) | 
 |   %need.dyn.free = icmp ne ptr %mem, null | 
 |   br i1 %need.dyn.free, label %dyn.free, label %suspend | 
 | dyn.free: | 
 |   call void @free(ptr %mem) | 
 |   br label %suspend | 
 | suspend: | 
 |   call i1 @llvm.coro.end(ptr %hdl, i1 false, token none) | 
 |   ret void | 
 | } | 
 |  | 
 | ; SimplifySuspendPoint will detect that coro.resume resumes itself and will | 
 | ; replace suspend with a jump to %resume label turning it into no-suspend | 
 | ; coroutine. | 
 | ; | 
 | ; CHECK-LABEL: define void @simplify_resume( | 
 | ; CHECK-NEXT:  entry: | 
 | ; CHECK-NEXT:    call void @llvm.memcpy | 
 | ; CHECK-NEXT:    call void @print(i32 0) | 
 | ; CHECK-NEXT:    ret void | 
 | ; | 
 | define void @simplify_resume(ptr %src, ptr %dst) presplitcoroutine { | 
 | entry: | 
 |   %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null) | 
 |   %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) | 
 |   br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin | 
 | dyn.alloc: | 
 |   %size = call i32 @llvm.coro.size.i32() | 
 |   %alloc = call ptr @malloc(i32 %size) | 
 |   br label %coro.begin | 
 | coro.begin: | 
 |   %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ] | 
 |   %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi) | 
 |   br label %body | 
 | body: | 
 |   %save = call token @llvm.coro.save(ptr %hdl) | 
 |   ; memcpy intrinsics should not prevent simplification. | 
 |   call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 1, i1 false) | 
 |   %subfn = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 0) | 
 |   call fastcc void %subfn(ptr %hdl) | 
 |   %0 = call i8 @llvm.coro.suspend(token %save, i1 false) | 
 |   switch i8 %0, label %suspend [i8 0, label %resume | 
 |                                 i8 1, label %pre.cleanup] | 
 | resume: | 
 |   call void @print(i32 0) | 
 |   br label %cleanup | 
 |  | 
 | pre.cleanup: | 
 |   call void @print(i32 1) | 
 |   br label %cleanup | 
 |  | 
 | cleanup: | 
 |   %mem = call ptr @llvm.coro.free(token %id, ptr %hdl) | 
 |   call void @free(ptr %mem) | 
 |   br label %suspend | 
 | suspend: | 
 |   call i1 @llvm.coro.end(ptr %hdl, i1 false, token none) | 
 |   ret void | 
 | } | 
 |  | 
 | ; SimplifySuspendPoint will detect that coroutine destroys itself and will | 
 | ; replace suspend with a jump to %cleanup label turning it into no-suspend | 
 | ; coroutine. | 
 | ; | 
 | ; CHECK-LABEL: define void @simplify_destroy( | 
 | ; CHECK-NEXT:  entry: | 
 | ; CHECK-NEXT:    call void @print(i32 1) | 
 | ; CHECK-NEXT:    ret void | 
 | ; | 
 | define void @simplify_destroy() presplitcoroutine personality i32 0 { | 
 | entry: | 
 |   %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null) | 
 |   %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) | 
 |   br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin | 
 | dyn.alloc: | 
 |   %size = call i32 @llvm.coro.size.i32() | 
 |   %alloc = call ptr @malloc(i32 %size) | 
 |   br label %coro.begin | 
 | coro.begin: | 
 |   %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ] | 
 |   %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi) | 
 |   br label %body | 
 | body: | 
 |   %save = call token @llvm.coro.save(ptr %hdl) | 
 |   %subfn = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1) | 
 |   invoke fastcc void %subfn(ptr %hdl) to label %real_susp unwind label %lpad | 
 |  | 
 | real_susp: | 
 |   %0 = call i8 @llvm.coro.suspend(token %save, i1 false) | 
 |   switch i8 %0, label %suspend [i8 0, label %resume | 
 |                                 i8 1, label %pre.cleanup] | 
 | resume: | 
 |   call void @print(i32 0) | 
 |   br label %cleanup | 
 |  | 
 | pre.cleanup: | 
 |   call void @print(i32 1) | 
 |   br label %cleanup | 
 |  | 
 | cleanup: | 
 |   %mem = call ptr @llvm.coro.free(token %id, ptr %hdl) | 
 |   call void @free(ptr %mem) | 
 |   br label %suspend | 
 | suspend: | 
 |   call i1 @llvm.coro.end(ptr %hdl, i1 false, token none) | 
 |   ret void | 
 | lpad: | 
 |   %lpval = landingpad { ptr, i32 } | 
 |      cleanup | 
 |  | 
 |   call void @print(i32 2) | 
 |   resume { ptr, i32 } %lpval | 
 | } | 
 |  | 
 | ; SimplifySuspendPoint will detect that coro.resume resumes itself and will | 
 | ; replace suspend with a jump to %resume label turning it into no-suspend | 
 | ; coroutine. | 
 | ; | 
 | ; CHECK-LABEL: define void @simplify_resume_with_inlined_if( | 
 | ; CHECK-NEXT:  entry: | 
 | ; CHECK-NEXT:    br i1 | 
 | ; CHECK:         call void @print(i32 0) | 
 | ; CHECK-NEXT:    ret void | 
 | ; | 
 | define void @simplify_resume_with_inlined_if(ptr %src, ptr %dst, i1 %cond) presplitcoroutine { | 
 | entry: | 
 |   %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null) | 
 |   %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) | 
 |   br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin | 
 | dyn.alloc: | 
 |   %size = call i32 @llvm.coro.size.i32() | 
 |   %alloc = call ptr @malloc(i32 %size) | 
 |   br label %coro.begin | 
 | coro.begin: | 
 |   %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ] | 
 |   %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi) | 
 |   br label %body | 
 | body: | 
 |   %save = call token @llvm.coro.save(ptr %hdl) | 
 |   br i1 %cond, label %if.then, label %if.else | 
 | if.then: | 
 |   call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 1, i1 false) | 
 |   br label %if.end | 
 | if.else: | 
 |   call void @llvm.memcpy.p0.p0.i64(ptr %src, ptr %dst, i64 1, i1 false) | 
 |   br label %if.end | 
 | if.end: | 
 |   %subfn = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 0) | 
 |   call fastcc void %subfn(ptr %hdl) | 
 |   %0 = call i8 @llvm.coro.suspend(token %save, i1 false) | 
 |   switch i8 %0, label %suspend [i8 0, label %resume | 
 |                                 i8 1, label %pre.cleanup] | 
 | resume: | 
 |   call void @print(i32 0) | 
 |   br label %cleanup | 
 |  | 
 | pre.cleanup: | 
 |   call void @print(i32 1) | 
 |   br label %cleanup | 
 |  | 
 | cleanup: | 
 |   %mem = call ptr @llvm.coro.free(token %id, ptr %hdl) | 
 |   call void @free(ptr %mem) | 
 |   br label %suspend | 
 | suspend: | 
 |   call i1 @llvm.coro.end(ptr %hdl, i1 false, token none) | 
 |   ret void | 
 | } | 
 |  | 
 |  | 
 |  | 
 | ; SimplifySuspendPoint won't be able to simplify if it detects that there are | 
 | ; other calls between coro.save and coro.suspend. They potentially can call | 
 | ; resume or destroy, so we should not simplify this suspend point. | 
 | ; | 
 | ; CHECK-LABEL: define void @cannot_simplify_other_calls( | 
 | ; CHECK-NEXT:  entry: | 
 | ; CHECK-NEXT:     llvm.coro.id | 
 |  | 
 | define void @cannot_simplify_other_calls() presplitcoroutine { | 
 | entry: | 
 |   %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null) | 
 |   %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) | 
 |   br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin | 
 | dyn.alloc: | 
 |   %size = call i32 @llvm.coro.size.i32() | 
 |   %alloc = call ptr @malloc(i32 %size) | 
 |   br label %coro.begin | 
 | coro.begin: | 
 |   %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ] | 
 |   %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi) | 
 |   br label %body | 
 | body: | 
 |   %save = call token @llvm.coro.save(ptr %hdl) | 
 |   br label %body1 | 
 |  | 
 | body1: | 
 |   call void @foo() | 
 |   br label %body2 | 
 |  | 
 | body2: | 
 |   %subfn = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1) | 
 |   call fastcc void %subfn(ptr %hdl) | 
 |   %0 = call i8 @llvm.coro.suspend(token %save, i1 false) | 
 |   switch i8 %0, label %suspend [i8 0, label %resume | 
 |                                 i8 1, label %pre.cleanup] | 
 | resume: | 
 |   call void @print(i32 0) | 
 |   br label %cleanup | 
 |  | 
 | pre.cleanup: | 
 |   call void @print(i32 1) | 
 |   br label %cleanup | 
 |  | 
 | cleanup: | 
 |   %mem = call ptr @llvm.coro.free(token %id, ptr %hdl) | 
 |   call void @free(ptr %mem) | 
 |   br label %suspend | 
 | suspend: | 
 |   call i1 @llvm.coro.end(ptr %hdl, i1 false, token none) | 
 |   ret void | 
 | } | 
 |  | 
 | ; SimplifySuspendPoint won't be able to simplify if it detects that there are | 
 | ; other calls between coro.save and coro.suspend. They potentially can call | 
 | ; resume or destroy, so we should not simplify this suspend point. | 
 | ; | 
 | ; CHECK-LABEL: define void @cannot_simplify_calls_in_terminator( | 
 | ; CHECK-NEXT:  entry: | 
 | ; CHECK-NEXT:     llvm.coro.id | 
 |  | 
 | define void @cannot_simplify_calls_in_terminator() presplitcoroutine personality i32 0 { | 
 | entry: | 
 |   %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null) | 
 |   %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) | 
 |   br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin | 
 | dyn.alloc: | 
 |   %size = call i32 @llvm.coro.size.i32() | 
 |   %alloc = call ptr @malloc(i32 %size) | 
 |   br label %coro.begin | 
 | coro.begin: | 
 |   %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ] | 
 |   %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi) | 
 |   br label %body | 
 | body: | 
 |   %save = call token @llvm.coro.save(ptr %hdl) | 
 |   invoke void @foo() to label %resume_cont unwind label %lpad | 
 | resume_cont: | 
 |   %subfn = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1) | 
 |   call fastcc void %subfn(ptr %hdl) | 
 |   %0 = call i8 @llvm.coro.suspend(token %save, i1 false) | 
 |   switch i8 %0, label %suspend [i8 0, label %resume | 
 |                                 i8 1, label %pre.cleanup] | 
 | resume: | 
 |   call void @print(i32 0) | 
 |   br label %cleanup | 
 |  | 
 | pre.cleanup: | 
 |   call void @print(i32 1) | 
 |   br label %cleanup | 
 |  | 
 | cleanup: | 
 |   %mem = call ptr @llvm.coro.free(token %id, ptr %hdl) | 
 |   call void @free(ptr %mem) | 
 |   br label %suspend | 
 | suspend: | 
 |   call i1 @llvm.coro.end(ptr %hdl, i1 false, token none) | 
 |   ret void | 
 | lpad: | 
 |   %lpval = landingpad { ptr, i32 } | 
 |      cleanup | 
 |  | 
 |   call void @print(i32 2) | 
 |   resume { ptr, i32 } %lpval | 
 | } | 
 |  | 
 | ; SimplifySuspendPoint won't be able to simplify if it detects that resume or | 
 | ; destroy does not immediately preceed coro.suspend. | 
 | ; | 
 | ; CHECK-LABEL: define void @cannot_simplify_not_last_instr( | 
 | ; CHECK-NEXT:  entry: | 
 | ; CHECK-NEXT:     llvm.coro.id | 
 |  | 
 | define void @cannot_simplify_not_last_instr(ptr %dst, ptr %src) presplitcoroutine { | 
 | entry: | 
 |   %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null) | 
 |   %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) | 
 |   br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin | 
 | dyn.alloc: | 
 |   %size = call i32 @llvm.coro.size.i32() | 
 |   %alloc = call ptr @malloc(i32 %size) | 
 |   br label %coro.begin | 
 | coro.begin: | 
 |   %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ] | 
 |   %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi) | 
 |   br label %body | 
 | body: | 
 |   %save = call token @llvm.coro.save(ptr %hdl) | 
 |   %subfn = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1) | 
 |   call fastcc void %subfn(ptr %hdl) | 
 |   ; memcpy separates destory from suspend, therefore cannot simplify. | 
 |   call void @llvm.memcpy.p0.p0.i64(ptr %dst, ptr %src, i64 1, i1 false) | 
 |   %0 = call i8 @llvm.coro.suspend(token %save, i1 false) | 
 |   switch i8 %0, label %suspend [i8 0, label %resume | 
 |                                 i8 1, label %pre.cleanup] | 
 | resume: | 
 |   call void @print(i32 0) | 
 |   br label %cleanup | 
 |  | 
 | pre.cleanup: | 
 |   call void @print(i32 1) | 
 |   br label %cleanup | 
 |  | 
 | cleanup: | 
 |   %mem = call ptr @llvm.coro.free(token %id, ptr %hdl) | 
 |   call void @free(ptr %mem) | 
 |   br label %suspend | 
 | suspend: | 
 |   call i1 @llvm.coro.end(ptr %hdl, i1 false, token none) | 
 |   ret void | 
 | } | 
 |  | 
 | ; SimplifySuspendPoint should not simplify final suspend point | 
 | ; | 
 | ; CHECK-LABEL: define void @cannot_simplify_final_suspend( | 
 | ; CHECK-NEXT:  entry: | 
 | ; CHECK-NEXT:     llvm.coro.id | 
 | ; | 
 | define void @cannot_simplify_final_suspend() presplitcoroutine personality i32 0 { | 
 | entry: | 
 |   %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null) | 
 |   %need.dyn.alloc = call i1 @llvm.coro.alloc(token %id) | 
 |   br i1 %need.dyn.alloc, label %dyn.alloc, label %coro.begin | 
 | dyn.alloc: | 
 |   %size = call i32 @llvm.coro.size.i32() | 
 |   %alloc = call ptr @malloc(i32 %size) | 
 |   br label %coro.begin | 
 | coro.begin: | 
 |   %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ] | 
 |   %hdl = call noalias ptr @llvm.coro.begin(token %id, ptr %phi) | 
 |   br label %body | 
 | body: | 
 |   %save = call token @llvm.coro.save(ptr %hdl) | 
 |   %subfn = call ptr @llvm.coro.subfn.addr(ptr %hdl, i8 1) | 
 |   invoke fastcc void %subfn(ptr %hdl) to label %real_susp unwind label %lpad | 
 |  | 
 | real_susp: | 
 |   %0 = call i8 @llvm.coro.suspend(token %save, i1 1) | 
 |   switch i8 %0, label %suspend [i8 0, label %resume | 
 |                                 i8 1, label %pre.cleanup] | 
 | resume: | 
 |   call void @print(i32 0) | 
 |   br label %cleanup | 
 |  | 
 | pre.cleanup: | 
 |   call void @print(i32 1) | 
 |   br label %cleanup | 
 |  | 
 | cleanup: | 
 |   %mem = call ptr @llvm.coro.free(token %id, ptr %hdl) | 
 |   call void @free(ptr %mem) | 
 |   br label %suspend | 
 | suspend: | 
 |   call i1 @llvm.coro.end(ptr %hdl, i1 false, token none) | 
 |   ret void | 
 | lpad: | 
 |   %lpval = landingpad { ptr, i32 } | 
 |      cleanup | 
 |  | 
 |   call void @print(i32 2) | 
 |   resume { ptr, i32 } %lpval | 
 | } | 
 |  | 
 | declare ptr @malloc(i32) allockind("alloc,uninitialized") allocsize(0) | 
 | declare void @free(ptr) willreturn allockind("free") | 
 | declare void @print(i32) | 
 | declare void @foo() | 
 |  | 
 | declare token @llvm.coro.id(i32, ptr, ptr, ptr) | 
 | declare i1 @llvm.coro.alloc(token) | 
 | declare i32 @llvm.coro.size.i32() | 
 | declare ptr @llvm.coro.begin(token, ptr) | 
 | declare token @llvm.coro.save(ptr %hdl) | 
 | declare i8 @llvm.coro.suspend(token, i1) | 
 | declare ptr @llvm.coro.free(token, ptr) | 
 | declare i1 @llvm.coro.end(ptr, i1, token) | 
 |  | 
 | declare ptr @llvm.coro.subfn.addr(ptr, i8) | 
 |  | 
 | declare void @llvm.memcpy.p0.p0.i64(ptr nocapture writeonly, ptr nocapture readonly, i64, i1) |