Files
clang-p2996/llvm/test/Transforms/Coroutines/no-suspend.ll
Florian Hahn 292077072e [Local] Treat calls that may not return as being alive.
With the addition of the `willreturn` attribute, functions that may
not return (e.g. due to an infinite loop) are well defined, if they are
not marked as `willreturn`.

This patch updates `wouldInstructionBeTriviallyDead` to not consider
calls that may not return as dead.

This patch still provides an escape hatch for intrinsics, which are
still assumed as willreturn unconditionally. It will be removed once
all intrinsics definitions have been reviewed and updated.

Reviewed By: jdoerfert

Differential Revision: https://reviews.llvm.org/D94106
2021-01-23 16:05:14 +00:00

434 lines
13 KiB
LLVM

; Test no suspend coroutines
; RUN: opt < %s -coro-split -S | FileCheck %s
; RUN: opt < %s -passes=coro-split -S | FileCheck %s
; Coroutine with no-suspends will turn into:
;
; CHECK-LABEL: define void @no_suspends(
; CHECK-NEXT: entry:
; CHECK-NEXT: alloca
; CHECK-NEXT: bitcast
; CHECK-NEXT: call void @print(i32 %n)
; CHECK-NEXT: ret void
;
define void @no_suspends(i32 %n) "coroutine.presplit"="1" {
entry:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* 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 i8* @malloc(i32 %size)
br label %coro.begin
coro.begin:
%phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ]
%hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi)
br label %body
body:
call void @print(i32 %n)
br label %cleanup
cleanup:
%mem = call i8* @llvm.coro.free(token %id, i8* %hdl)
%need.dyn.free = icmp ne i8* %mem, null
br i1 %need.dyn.free, label %dyn.free, label %suspend
dyn.free:
call void @free(i8* %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 false)
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: alloca
; CHECK-NEXT: bitcast
; CHECK-NEXT: call void @llvm.memcpy
; CHECK-NEXT: call void @print(i32 0)
; CHECK-NEXT: ret void
;
define void @simplify_resume(i8* %src, i8* %dst) "coroutine.presplit"="1" {
entry:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* 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 i8* @malloc(i32 %size)
br label %coro.begin
coro.begin:
%phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ]
%hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi)
br label %body
body:
%save = call token @llvm.coro.save(i8* %hdl)
; memcpy intrinsics should not prevent simplification.
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, i64 1, i1 false)
%subfn = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0)
%bres = bitcast i8* %subfn to void (i8*)*
call fastcc void %bres(i8* %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 i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 false)
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: alloca
; CHECK-NEXT: bitcast
; CHECK-NEXT: call void @print(i32 1)
; CHECK-NEXT: ret void
;
define void @simplify_destroy() "coroutine.presplit"="1" personality i32 0 {
entry:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* 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 i8* @malloc(i32 %size)
br label %coro.begin
coro.begin:
%phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ]
%hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi)
br label %body
body:
%save = call token @llvm.coro.save(i8* %hdl)
%subfn = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
%bcast = bitcast i8* %subfn to void (i8*)*
invoke fastcc void %bcast(i8* %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 i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 false)
ret void
lpad:
%lpval = landingpad { i8*, i32 }
cleanup
call void @print(i32 2)
resume { i8*, 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: alloca
; CHECK-NEXT: bitcast
; CHECK-NEXT: br i1
; CHECK: call void @print(i32 0)
; CHECK-NEXT: ret void
;
define void @simplify_resume_with_inlined_if(i8* %src, i8* %dst, i1 %cond) "coroutine.presplit"="1" {
entry:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* 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 i8* @malloc(i32 %size)
br label %coro.begin
coro.begin:
%phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ]
%hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi)
br label %body
body:
%save = call token @llvm.coro.save(i8* %hdl)
br i1 %cond, label %if.then, label %if.else
if.then:
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, i64 1, i1 false)
br label %if.end
if.else:
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %src, i8* %dst, i64 1, i1 false)
br label %if.end
if.end:
%subfn = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 0)
%bres = bitcast i8* %subfn to void (i8*)*
call fastcc void %bres(i8* %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 i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 false)
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() "coroutine.presplit"="1" {
entry:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* 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 i8* @malloc(i32 %size)
br label %coro.begin
coro.begin:
%phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ]
%hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi)
br label %body
body:
%save = call token @llvm.coro.save(i8* %hdl)
br label %body1
body1:
call void @foo()
br label %body2
body2:
%subfn = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
%bcast = bitcast i8* %subfn to void (i8*)*
call fastcc void %bcast(i8* %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 i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 false)
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() "coroutine.presplit"="1" personality i32 0 {
entry:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* 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 i8* @malloc(i32 %size)
br label %coro.begin
coro.begin:
%phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ]
%hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi)
br label %body
body:
%save = call token @llvm.coro.save(i8* %hdl)
invoke void @foo() to label %resume_cont unwind label %lpad
resume_cont:
%subfn = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
%bcast = bitcast i8* %subfn to void (i8*)*
call fastcc void %bcast(i8* %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 i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 false)
ret void
lpad:
%lpval = landingpad { i8*, i32 }
cleanup
call void @print(i32 2)
resume { i8*, 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(i8* %dst, i8* %src) "coroutine.presplit"="1" {
entry:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* 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 i8* @malloc(i32 %size)
br label %coro.begin
coro.begin:
%phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ]
%hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi)
br label %body
body:
%save = call token @llvm.coro.save(i8* %hdl)
%subfn = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
%bcast = bitcast i8* %subfn to void (i8*)*
call fastcc void %bcast(i8* %hdl)
; memcpy separates destory from suspend, therefore cannot simplify.
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %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 i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 false)
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() "coroutine.presplit"="1" personality i32 0 {
entry:
%id = call token @llvm.coro.id(i32 0, i8* null, i8* null, i8* 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 i8* @malloc(i32 %size)
br label %coro.begin
coro.begin:
%phi = phi i8* [ null, %entry ], [ %alloc, %dyn.alloc ]
%hdl = call noalias i8* @llvm.coro.begin(token %id, i8* %phi)
br label %body
body:
%save = call token @llvm.coro.save(i8* %hdl)
%subfn = call i8* @llvm.coro.subfn.addr(i8* %hdl, i8 1)
%bcast = bitcast i8* %subfn to void (i8*)*
invoke fastcc void %bcast(i8* %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 i8* @llvm.coro.free(token %id, i8* %hdl)
call void @free(i8* %mem)
br label %suspend
suspend:
call i1 @llvm.coro.end(i8* %hdl, i1 false)
ret void
lpad:
%lpval = landingpad { i8*, i32 }
cleanup
call void @print(i32 2)
resume { i8*, i32 } %lpval
}
declare i8* @malloc(i32)
declare void @free(i8*) willreturn
declare void @print(i32)
declare void @foo()
declare token @llvm.coro.id(i32, i8*, i8*, i8*)
declare i1 @llvm.coro.alloc(token)
declare i32 @llvm.coro.size.i32()
declare i8* @llvm.coro.begin(token, i8*)
declare token @llvm.coro.save(i8* %hdl)
declare i8 @llvm.coro.suspend(token, i1)
declare i8* @llvm.coro.free(token, i8*)
declare i1 @llvm.coro.end(i8*, i1)
declare i8* @llvm.coro.subfn.addr(i8*, i8)
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1)