1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 20:51:52 +01:00

[ValueTracking] Don't assume readonly function will return

This is similar to D94106, but for the
isGuaranteedToTransferExecutionToSuccessor() helper. We should not
assume that readonly functions will return, as this is only true for
mustprogress functions (in which case we already infer willreturn).
As with the DCE change, for now continue assuming that readonly
intrinsics will return, as not all target intrinsics have been
annotated yet.

Differential Revision: https://reviews.llvm.org/D95288
This commit is contained in:
Nikita Popov 2021-01-22 22:33:55 +01:00
parent 7c6fbeb405
commit a78d45f1a3
12 changed files with 65 additions and 85 deletions

View File

@ -5040,23 +5040,10 @@ bool llvm::isGuaranteedToTransferExecutionToSuccessor(const Instruction *I) {
if (CB->hasFnAttr(Attribute::WillReturn)) if (CB->hasFnAttr(Attribute::WillReturn))
return true; return true;
// Non-throwing call sites can loop infinitely, call exit/pthread_exit // FIXME: Temporarily assume that all side-effect free intrinsics will
// etc. and thus not return. However, LLVM already assumes that // return. Remove this workaround once all intrinsics are appropriately
// // annotated.
// - Thread exiting actions are modeled as writes to memory invisible to return isa<IntrinsicInst>(CB) && CB->onlyReadsMemory();
// the program.
//
// - Loops that don't have side effects (side effects are volatile/atomic
// stores and IO) always terminate (see http://llvm.org/PR965).
// Furthermore IO itself is also modeled as writes to memory invisible to
// the program.
//
// We rely on those assumptions here, and use the memory effects of the call
// target as a proxy for checking that it always returns.
// FIXME: This isn't aggressive enough; a call which only writes to a global
// is guaranteed to return.
return CB->onlyReadsMemory() || CB->onlyAccessesArgMemory();
} }
// Other instructions return normally. // Other instructions return normally.

View File

@ -48,7 +48,7 @@ define void @c3(i32* %q) {
; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly
; IS__CGSCC____-LABEL: define {{[^@]+}}@c3 ; IS__CGSCC____-LABEL: define {{[^@]+}}@c3
; IS__CGSCC____-SAME: (i32* nofree writeonly [[Q:%.*]]) [[ATTR1]] { ; IS__CGSCC____-SAME: (i32* nofree writeonly [[Q:%.*]]) [[ATTR1]] {
; IS__CGSCC____-NEXT: call void @c2(i32* nofree writeonly [[Q]]) [[ATTR16:#.*]] ; IS__CGSCC____-NEXT: call void @c2(i32* nofree writeonly [[Q]]) [[ATTR17:#.*]]
; IS__CGSCC____-NEXT: ret void ; IS__CGSCC____-NEXT: ret void
; ;
call void @c2(i32* %q) call void @c2(i32* %q)
@ -215,14 +215,14 @@ define i1 @c7(i32* %q, i32 %bitno) {
; IS__TUNIT____: Function Attrs: nofree nosync nounwind readonly willreturn ; IS__TUNIT____: Function Attrs: nofree nosync nounwind readonly willreturn
; IS__TUNIT____-LABEL: define {{[^@]+}}@c7 ; IS__TUNIT____-LABEL: define {{[^@]+}}@c7
; IS__TUNIT____-SAME: (i32* nofree readonly [[Q:%.*]], i32 [[BITNO:%.*]]) [[ATTR2]] { ; IS__TUNIT____-SAME: (i32* nofree readonly [[Q:%.*]], i32 [[BITNO:%.*]]) [[ATTR2]] {
; IS__TUNIT____-NEXT: [[PTR:%.*]] = call i1* @lookup_bit(i32* noalias nofree readnone [[Q]], i32 [[BITNO]]) [[ATTR14:#.*]] ; IS__TUNIT____-NEXT: [[PTR:%.*]] = call i1* @lookup_bit(i32* noalias nofree readnone [[Q]], i32 [[BITNO]]) [[ATTR15:#.*]]
; IS__TUNIT____-NEXT: [[VAL:%.*]] = load i1, i1* [[PTR]], align 1 ; IS__TUNIT____-NEXT: [[VAL:%.*]] = load i1, i1* [[PTR]], align 1
; IS__TUNIT____-NEXT: ret i1 [[VAL]] ; IS__TUNIT____-NEXT: ret i1 [[VAL]]
; ;
; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readonly willreturn ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readonly willreturn
; IS__CGSCC____-LABEL: define {{[^@]+}}@c7 ; IS__CGSCC____-LABEL: define {{[^@]+}}@c7
; IS__CGSCC____-SAME: (i32* nofree readonly [[Q:%.*]], i32 [[BITNO:%.*]]) [[ATTR2]] { ; IS__CGSCC____-SAME: (i32* nofree readonly [[Q:%.*]], i32 [[BITNO:%.*]]) [[ATTR2]] {
; IS__CGSCC____-NEXT: [[PTR:%.*]] = call i1* @lookup_bit(i32* noalias nofree readnone [[Q]], i32 [[BITNO]]) [[ATTR17:#.*]] ; IS__CGSCC____-NEXT: [[PTR:%.*]] = call i1* @lookup_bit(i32* noalias nofree readnone [[Q]], i32 [[BITNO]]) [[ATTR18:#.*]]
; IS__CGSCC____-NEXT: [[VAL:%.*]] = load i1, i1* [[PTR]], align 1 ; IS__CGSCC____-NEXT: [[VAL:%.*]] = load i1, i1* [[PTR]], align 1
; IS__CGSCC____-NEXT: ret i1 [[VAL]] ; IS__CGSCC____-NEXT: ret i1 [[VAL]]
; ;
@ -330,7 +330,7 @@ define void @nc2(i32* %p, i32* %q) {
; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind willreturn ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind willreturn
; IS__CGSCC____-LABEL: define {{[^@]+}}@nc2 ; IS__CGSCC____-LABEL: define {{[^@]+}}@nc2
; IS__CGSCC____-SAME: (i32* nocapture nofree [[P:%.*]], i32* nofree [[Q:%.*]]) [[ATTR5]] { ; IS__CGSCC____-SAME: (i32* nocapture nofree [[P:%.*]], i32* nofree [[Q:%.*]]) [[ATTR5]] {
; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i32 @nc1(i32* nofree [[Q]], i32* nocapture nofree [[P]], i1 noundef false) [[ATTR18:#.*]] ; IS__CGSCC____-NEXT: [[TMP1:%.*]] = call i32 @nc1(i32* nofree [[Q]], i32* nocapture nofree [[P]], i1 noundef false) [[ATTR14:#.*]]
; IS__CGSCC____-NEXT: ret void ; IS__CGSCC____-NEXT: ret void
; ;
%1 = call i32 @nc1(i32* %q, i32* %p, i1 0) ; <i32> [#uses=0] %1 = call i32 @nc1(i32* %q, i32* %p, i1 0) ; <i32> [#uses=0]
@ -355,13 +355,13 @@ define void @nc4(i8* %p) {
; IS__TUNIT____: Function Attrs: argmemonly nounwind ; IS__TUNIT____: Function Attrs: argmemonly nounwind
; IS__TUNIT____-LABEL: define {{[^@]+}}@nc4 ; IS__TUNIT____-LABEL: define {{[^@]+}}@nc4
; IS__TUNIT____-SAME: (i8* [[P:%.*]]) [[ATTR6:#.*]] { ; IS__TUNIT____-SAME: (i8* [[P:%.*]]) [[ATTR6:#.*]] {
; IS__TUNIT____-NEXT: call void @external(i8* readonly [[P]]) [[ATTR11:#.*]] ; IS__TUNIT____-NEXT: call void @external(i8* readonly [[P]]) [[ATTR16:#.*]]
; IS__TUNIT____-NEXT: ret void ; IS__TUNIT____-NEXT: ret void
; ;
; IS__CGSCC____: Function Attrs: argmemonly nounwind ; IS__CGSCC____: Function Attrs: argmemonly nounwind
; IS__CGSCC____-LABEL: define {{[^@]+}}@nc4 ; IS__CGSCC____-LABEL: define {{[^@]+}}@nc4
; IS__CGSCC____-SAME: (i8* [[P:%.*]]) [[ATTR6:#.*]] { ; IS__CGSCC____-SAME: (i8* [[P:%.*]]) [[ATTR6:#.*]] {
; IS__CGSCC____-NEXT: call void @external(i8* readonly [[P]]) [[ATTR13:#.*]] ; IS__CGSCC____-NEXT: call void @external(i8* readonly [[P]]) [[ATTR19:#.*]]
; IS__CGSCC____-NEXT: ret void ; IS__CGSCC____-NEXT: ret void
; ;
call void @external(i8* %p) call void @external(i8* %p)
@ -609,7 +609,7 @@ define void @nocaptureLaunder(i8* %p) {
; IS__TUNIT____-LABEL: define {{[^@]+}}@nocaptureLaunder ; IS__TUNIT____-LABEL: define {{[^@]+}}@nocaptureLaunder
; IS__TUNIT____-SAME: (i8* nocapture nofree [[P:%.*]]) [[ATTR5]] { ; IS__TUNIT____-SAME: (i8* nocapture nofree [[P:%.*]]) [[ATTR5]] {
; IS__TUNIT____-NEXT: entry: ; IS__TUNIT____-NEXT: entry:
; IS__TUNIT____-NEXT: [[B:%.*]] = call i8* @llvm.launder.invariant.group.p0i8(i8* nofree [[P]]) [[ATTR15:#.*]] ; IS__TUNIT____-NEXT: [[B:%.*]] = call i8* @llvm.launder.invariant.group.p0i8(i8* nofree [[P]]) [[ATTR17:#.*]]
; IS__TUNIT____-NEXT: store i8 42, i8* [[B]], align 1 ; IS__TUNIT____-NEXT: store i8 42, i8* [[B]], align 1
; IS__TUNIT____-NEXT: ret void ; IS__TUNIT____-NEXT: ret void
; ;
@ -617,7 +617,7 @@ define void @nocaptureLaunder(i8* %p) {
; IS__CGSCC____-LABEL: define {{[^@]+}}@nocaptureLaunder ; IS__CGSCC____-LABEL: define {{[^@]+}}@nocaptureLaunder
; IS__CGSCC____-SAME: (i8* nocapture nofree [[P:%.*]]) [[ATTR10:#.*]] { ; IS__CGSCC____-SAME: (i8* nocapture nofree [[P:%.*]]) [[ATTR10:#.*]] {
; IS__CGSCC____-NEXT: entry: ; IS__CGSCC____-NEXT: entry:
; IS__CGSCC____-NEXT: [[B:%.*]] = call i8* @llvm.launder.invariant.group.p0i8(i8* nofree [[P]]) [[ATTR17]] ; IS__CGSCC____-NEXT: [[B:%.*]] = call i8* @llvm.launder.invariant.group.p0i8(i8* nofree [[P]]) [[ATTR18]]
; IS__CGSCC____-NEXT: store i8 42, i8* [[B]], align 1 ; IS__CGSCC____-NEXT: store i8 42, i8* [[B]], align 1
; IS__CGSCC____-NEXT: ret void ; IS__CGSCC____-NEXT: ret void
; ;
@ -632,14 +632,14 @@ define void @captureLaunder(i8* %p) {
; IS__TUNIT____: Function Attrs: nofree nosync nounwind willreturn ; IS__TUNIT____: Function Attrs: nofree nosync nounwind willreturn
; IS__TUNIT____-LABEL: define {{[^@]+}}@captureLaunder ; IS__TUNIT____-LABEL: define {{[^@]+}}@captureLaunder
; IS__TUNIT____-SAME: (i8* nofree [[P:%.*]]) [[ATTR5]] { ; IS__TUNIT____-SAME: (i8* nofree [[P:%.*]]) [[ATTR5]] {
; IS__TUNIT____-NEXT: [[B:%.*]] = call i8* @llvm.launder.invariant.group.p0i8(i8* nofree [[P]]) [[ATTR15]] ; IS__TUNIT____-NEXT: [[B:%.*]] = call i8* @llvm.launder.invariant.group.p0i8(i8* nofree [[P]]) [[ATTR17]]
; IS__TUNIT____-NEXT: store i8* [[B]], i8** @g2, align 8 ; IS__TUNIT____-NEXT: store i8* [[B]], i8** @g2, align 8
; IS__TUNIT____-NEXT: ret void ; IS__TUNIT____-NEXT: ret void
; ;
; IS__CGSCC____: Function Attrs: nofree nosync nounwind willreturn ; IS__CGSCC____: Function Attrs: nofree nosync nounwind willreturn
; IS__CGSCC____-LABEL: define {{[^@]+}}@captureLaunder ; IS__CGSCC____-LABEL: define {{[^@]+}}@captureLaunder
; IS__CGSCC____-SAME: (i8* nofree [[P:%.*]]) [[ATTR10]] { ; IS__CGSCC____-SAME: (i8* nofree [[P:%.*]]) [[ATTR10]] {
; IS__CGSCC____-NEXT: [[B:%.*]] = call i8* @llvm.launder.invariant.group.p0i8(i8* nofree [[P]]) [[ATTR17]] ; IS__CGSCC____-NEXT: [[B:%.*]] = call i8* @llvm.launder.invariant.group.p0i8(i8* nofree [[P]]) [[ATTR18]]
; IS__CGSCC____-NEXT: store i8* [[B]], i8** @g2, align 8 ; IS__CGSCC____-NEXT: store i8* [[B]], i8** @g2, align 8
; IS__CGSCC____-NEXT: ret void ; IS__CGSCC____-NEXT: ret void
; ;
@ -653,7 +653,7 @@ define void @nocaptureStrip(i8* %p) {
; IS__TUNIT____-LABEL: define {{[^@]+}}@nocaptureStrip ; IS__TUNIT____-LABEL: define {{[^@]+}}@nocaptureStrip
; IS__TUNIT____-SAME: (i8* nocapture nofree writeonly [[P:%.*]]) [[ATTR1]] { ; IS__TUNIT____-SAME: (i8* nocapture nofree writeonly [[P:%.*]]) [[ATTR1]] {
; IS__TUNIT____-NEXT: entry: ; IS__TUNIT____-NEXT: entry:
; IS__TUNIT____-NEXT: [[B:%.*]] = call i8* @llvm.strip.invariant.group.p0i8(i8* noalias nofree readnone [[P]]) [[ATTR15]] ; IS__TUNIT____-NEXT: [[B:%.*]] = call i8* @llvm.strip.invariant.group.p0i8(i8* noalias nofree readnone [[P]]) [[ATTR17]]
; IS__TUNIT____-NEXT: store i8 42, i8* [[B]], align 1 ; IS__TUNIT____-NEXT: store i8 42, i8* [[B]], align 1
; IS__TUNIT____-NEXT: ret void ; IS__TUNIT____-NEXT: ret void
; ;
@ -661,7 +661,7 @@ define void @nocaptureStrip(i8* %p) {
; IS__CGSCC____-LABEL: define {{[^@]+}}@nocaptureStrip ; IS__CGSCC____-LABEL: define {{[^@]+}}@nocaptureStrip
; IS__CGSCC____-SAME: (i8* nocapture nofree writeonly [[P:%.*]]) [[ATTR11:#.*]] { ; IS__CGSCC____-SAME: (i8* nocapture nofree writeonly [[P:%.*]]) [[ATTR11:#.*]] {
; IS__CGSCC____-NEXT: entry: ; IS__CGSCC____-NEXT: entry:
; IS__CGSCC____-NEXT: [[B:%.*]] = call i8* @llvm.strip.invariant.group.p0i8(i8* noalias nofree readnone [[P]]) [[ATTR17]] ; IS__CGSCC____-NEXT: [[B:%.*]] = call i8* @llvm.strip.invariant.group.p0i8(i8* noalias nofree readnone [[P]]) [[ATTR18]]
; IS__CGSCC____-NEXT: store i8 42, i8* [[B]], align 1 ; IS__CGSCC____-NEXT: store i8 42, i8* [[B]], align 1
; IS__CGSCC____-NEXT: ret void ; IS__CGSCC____-NEXT: ret void
; ;
@ -676,14 +676,14 @@ define void @captureStrip(i8* %p) {
; IS__TUNIT____: Function Attrs: nofree nosync nounwind willreturn writeonly ; IS__TUNIT____: Function Attrs: nofree nosync nounwind willreturn writeonly
; IS__TUNIT____-LABEL: define {{[^@]+}}@captureStrip ; IS__TUNIT____-LABEL: define {{[^@]+}}@captureStrip
; IS__TUNIT____-SAME: (i8* nofree writeonly [[P:%.*]]) [[ATTR1]] { ; IS__TUNIT____-SAME: (i8* nofree writeonly [[P:%.*]]) [[ATTR1]] {
; IS__TUNIT____-NEXT: [[B:%.*]] = call i8* @llvm.strip.invariant.group.p0i8(i8* noalias nofree readnone [[P]]) [[ATTR15]] ; IS__TUNIT____-NEXT: [[B:%.*]] = call i8* @llvm.strip.invariant.group.p0i8(i8* noalias nofree readnone [[P]]) [[ATTR17]]
; IS__TUNIT____-NEXT: store i8* [[B]], i8** @g3, align 8 ; IS__TUNIT____-NEXT: store i8* [[B]], i8** @g3, align 8
; IS__TUNIT____-NEXT: ret void ; IS__TUNIT____-NEXT: ret void
; ;
; IS__CGSCC____: Function Attrs: nofree nosync nounwind willreturn writeonly ; IS__CGSCC____: Function Attrs: nofree nosync nounwind willreturn writeonly
; IS__CGSCC____-LABEL: define {{[^@]+}}@captureStrip ; IS__CGSCC____-LABEL: define {{[^@]+}}@captureStrip
; IS__CGSCC____-SAME: (i8* nofree writeonly [[P:%.*]]) [[ATTR11]] { ; IS__CGSCC____-SAME: (i8* nofree writeonly [[P:%.*]]) [[ATTR11]] {
; IS__CGSCC____-NEXT: [[B:%.*]] = call i8* @llvm.strip.invariant.group.p0i8(i8* noalias nofree readnone [[P]]) [[ATTR17]] ; IS__CGSCC____-NEXT: [[B:%.*]] = call i8* @llvm.strip.invariant.group.p0i8(i8* noalias nofree readnone [[P]]) [[ATTR18]]
; IS__CGSCC____-NEXT: store i8* [[B]], i8** @g3, align 8 ; IS__CGSCC____-NEXT: store i8* [[B]], i8** @g3, align 8
; IS__CGSCC____-NEXT: ret void ; IS__CGSCC____-NEXT: ret void
; ;
@ -840,17 +840,15 @@ entry:
ret i8* %p ret i8* %p
} }
declare i8* @maybe_returned_ptr(i8* readonly %ptr) readonly nounwind declare i8* @maybe_returned_ptr(i8* readonly %ptr) readonly nounwind willreturn
declare i8 @maybe_returned_val(i8* %ptr) readonly nounwind declare i8 @maybe_returned_val(i8* %ptr) readonly nounwind willreturn
declare void @val_use(i8 %ptr) readonly nounwind declare void @val_use(i8 %ptr) readonly nounwind willreturn
; FIXME: Both pointers should be nocapture ; FIXME: Both pointers should be nocapture
define void @ptr_uses(i8* %ptr, i8* %wptr) { define void @ptr_uses(i8* %ptr, i8* %wptr) {
; CHECK: Function Attrs: nounwind ; CHECK: Function Attrs: nounwind willreturn
; CHECK-LABEL: define {{[^@]+}}@ptr_uses ; CHECK-LABEL: define {{[^@]+}}@ptr_uses
; CHECK-SAME: (i8* [[PTR:%.*]], i8* nocapture nonnull writeonly dereferenceable(1) [[WPTR:%.*]]) [[ATTR13:#.*]] { ; CHECK-SAME: (i8* [[PTR:%.*]], i8* nocapture nonnull writeonly dereferenceable(1) [[WPTR:%.*]]) [[ATTR14:#.*]] {
; CHECK-NEXT: [[CALL_PTR:%.*]] = call i8* @maybe_returned_ptr(i8* readonly [[PTR]]) [[ATTR4]]
; CHECK-NEXT: [[CALL_VAL:%.*]] = call i8 @maybe_returned_val(i8* readonly [[CALL_PTR]]) [[ATTR4]]
; CHECK-NEXT: store i8 0, i8* [[WPTR]], align 1 ; CHECK-NEXT: store i8 0, i8* [[WPTR]], align 1
; CHECK-NEXT: ret void ; CHECK-NEXT: ret void
; ;

View File

@ -146,34 +146,22 @@ define i8* @test3(i1 %c) {
; nonnull if neither can ever return null. (In this case, they ; nonnull if neither can ever return null. (In this case, they
; just never return period.) ; just never return period.)
define i8* @test4_helper() { define i8* @test4_helper() {
; NOT_CGSCC_NPM: Function Attrs: nofree noreturn nosync nounwind readnone ; CHECK: Function Attrs: nofree noreturn nosync nounwind readnone
; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@test4_helper ; CHECK-LABEL: define {{[^@]+}}@test4_helper
; NOT_CGSCC_NPM-SAME: () [[ATTR2:#.*]] { ; CHECK-SAME: () [[ATTR2:#.*]] {
; NOT_CGSCC_NPM-NEXT: [[RET:%.*]] = call i8* @test4() ; CHECK-NEXT: [[RET:%.*]] = call i8* @test4() [[ATTR2]]
; NOT_CGSCC_NPM-NEXT: unreachable ; CHECK-NEXT: unreachable
;
; IS__CGSCC_NPM: Function Attrs: nofree noreturn nosync nounwind readnone
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@test4_helper
; IS__CGSCC_NPM-SAME: () [[ATTR2:#.*]] {
; IS__CGSCC_NPM-NEXT: [[RET:%.*]] = call i8* @test4()
; IS__CGSCC_NPM-NEXT: unreachable
; ;
%ret = call i8* @test4() %ret = call i8* @test4()
ret i8* %ret ret i8* %ret
} }
define i8* @test4() { define i8* @test4() {
; NOT_CGSCC_NPM: Function Attrs: nofree noreturn nosync nounwind readnone ; CHECK: Function Attrs: nofree noreturn nosync nounwind readnone
; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@test4 ; CHECK-LABEL: define {{[^@]+}}@test4
; NOT_CGSCC_NPM-SAME: () [[ATTR2]] { ; CHECK-SAME: () [[ATTR2]] {
; NOT_CGSCC_NPM-NEXT: [[RET:%.*]] = call i8* @test4_helper() ; CHECK-NEXT: [[RET:%.*]] = call i8* @test4_helper() [[ATTR2]]
; NOT_CGSCC_NPM-NEXT: unreachable ; CHECK-NEXT: unreachable
;
; IS__CGSCC_NPM: Function Attrs: nofree noreturn nosync nounwind readnone
; IS__CGSCC_NPM-LABEL: define {{[^@]+}}@test4
; IS__CGSCC_NPM-SAME: () [[ATTR2]] {
; IS__CGSCC_NPM-NEXT: [[RET:%.*]] = call i8* @test4_helper()
; IS__CGSCC_NPM-NEXT: unreachable
; ;
%ret = call i8* @test4_helper() %ret = call i8* @test4_helper()
ret i8* %ret ret i8* %ret
@ -802,7 +790,7 @@ declare void @use1nonnull(i8* nonnull %x);
declare void @use2nonnull(i8* nonnull %x, i8* nonnull %y); declare void @use2nonnull(i8* nonnull %x, i8* nonnull %y);
declare void @use3nonnull(i8* nonnull %x, i8* nonnull %y, i8* nonnull %z); declare void @use3nonnull(i8* nonnull %x, i8* nonnull %y, i8* nonnull %z);
declare i8 @use1safecall(i8* %x) readonly nounwind ; readonly+nounwind guarantees that execution continues to successor declare i8 @use1safecall(i8* %x) readonly nounwind willreturn ; nounwind+willreturn guarantees that execution continues to successor
; Can't extend non-null to parent for any argument because the 2nd call is not guaranteed to execute. ; Can't extend non-null to parent for any argument because the 2nd call is not guaranteed to execute.
@ -1524,7 +1512,7 @@ define i8* @mybasename(i8* nofree readonly %str) {
; NOT_CGSCC_OPM: Function Attrs: nofree nounwind readonly willreturn ; NOT_CGSCC_OPM: Function Attrs: nofree nounwind readonly willreturn
; NOT_CGSCC_OPM-LABEL: define {{[^@]+}}@mybasename ; NOT_CGSCC_OPM-LABEL: define {{[^@]+}}@mybasename
; NOT_CGSCC_OPM-SAME: (i8* nofree readonly [[STR:%.*]]) [[ATTR11:#.*]] { ; NOT_CGSCC_OPM-SAME: (i8* nofree readonly [[STR:%.*]]) [[ATTR11:#.*]] {
; NOT_CGSCC_OPM-NEXT: [[CALL:%.*]] = call i8* @strrchr(i8* nofree readonly [[STR]], i32 noundef 47) [[ATTR15:#.*]] ; NOT_CGSCC_OPM-NEXT: [[CALL:%.*]] = call i8* @strrchr(i8* nofree readonly [[STR]], i32 noundef 47) [[ATTR14]]
; NOT_CGSCC_OPM-NEXT: [[TOBOOL:%.*]] = icmp ne i8* [[CALL]], null ; NOT_CGSCC_OPM-NEXT: [[TOBOOL:%.*]] = icmp ne i8* [[CALL]], null
; NOT_CGSCC_OPM-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, i8* [[CALL]], i64 1 ; NOT_CGSCC_OPM-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, i8* [[CALL]], i64 1
; NOT_CGSCC_OPM-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i8* [[ADD_PTR]], i8* [[STR]] ; NOT_CGSCC_OPM-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i8* [[ADD_PTR]], i8* [[STR]]
@ -1533,7 +1521,7 @@ define i8* @mybasename(i8* nofree readonly %str) {
; IS__CGSCC_OPM: Function Attrs: nofree nounwind readonly willreturn ; IS__CGSCC_OPM: Function Attrs: nofree nounwind readonly willreturn
; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@mybasename ; IS__CGSCC_OPM-LABEL: define {{[^@]+}}@mybasename
; IS__CGSCC_OPM-SAME: (i8* nofree readonly [[STR:%.*]]) [[ATTR12:#.*]] { ; IS__CGSCC_OPM-SAME: (i8* nofree readonly [[STR:%.*]]) [[ATTR12:#.*]] {
; IS__CGSCC_OPM-NEXT: [[CALL:%.*]] = call i8* @strrchr(i8* nofree readonly [[STR]], i32 noundef 47) [[ATTR16:#.*]] ; IS__CGSCC_OPM-NEXT: [[CALL:%.*]] = call i8* @strrchr(i8* nofree readonly [[STR]], i32 noundef 47) [[ATTR15]]
; IS__CGSCC_OPM-NEXT: [[TOBOOL:%.*]] = icmp ne i8* [[CALL]], null ; IS__CGSCC_OPM-NEXT: [[TOBOOL:%.*]] = icmp ne i8* [[CALL]], null
; IS__CGSCC_OPM-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, i8* [[CALL]], i64 1 ; IS__CGSCC_OPM-NEXT: [[ADD_PTR:%.*]] = getelementptr inbounds i8, i8* [[CALL]], i64 1
; IS__CGSCC_OPM-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i8* [[ADD_PTR]], i8* [[STR]] ; IS__CGSCC_OPM-NEXT: [[COND:%.*]] = select i1 [[TOBOOL]], i8* [[ADD_PTR]], i8* [[STR]]

View File

@ -47,7 +47,7 @@ define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
; ;
; IS__CGSCC____: Function Attrs: argmemonly nofree nosync nounwind ; IS__CGSCC____: Function Attrs: argmemonly nofree nosync nounwind
; IS__CGSCC____-LABEL: define {{[^@]+}}@external_ret2_nrw ; IS__CGSCC____-LABEL: define {{[^@]+}}@external_ret2_nrw
; IS__CGSCC____-SAME: (i32* nofree [[N0:%.*]], i32* nofree nonnull align 4 dereferenceable(4) [[R0:%.*]], i32* nofree returned [[W0:%.*]]) [[ATTR0:#.*]] { ; IS__CGSCC____-SAME: (i32* nofree [[N0:%.*]], i32* nofree [[R0:%.*]], i32* nofree returned [[W0:%.*]]) [[ATTR0:#.*]] {
; IS__CGSCC____-NEXT: entry: ; IS__CGSCC____-NEXT: entry:
; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @internal_ret0_nw(i32* nofree [[N0]], i32* nofree [[W0]]) [[ATTR2:#.*]] ; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @internal_ret0_nw(i32* nofree [[N0]], i32* nofree [[W0]]) [[ATTR2:#.*]]
; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32* @internal_ret1_rrw(i32* nofree nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[R0]], i32* nofree [[W0]]) [[ATTR2]] ; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32* @internal_ret1_rrw(i32* nofree nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[R0]], i32* nofree [[W0]]) [[ATTR2]]
@ -179,7 +179,7 @@ define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) {
; IS__CGSCC____: if.then: ; IS__CGSCC____: if.then:
; IS__CGSCC____-NEXT: br label [[RETURN:%.*]] ; IS__CGSCC____-NEXT: br label [[RETURN:%.*]]
; IS__CGSCC____: if.end: ; IS__CGSCC____: if.end:
; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @internal_ret1_rw(i32* nofree nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) [[ATTR2]] ; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @internal_ret1_rw(i32* nofree nonnull align 4 dereferenceable(4) [[R0]], i32* nofree [[W0]]) [[ATTR2]]
; IS__CGSCC____-NEXT: [[TMP1:%.*]] = load i32, i32* [[R0]], align 4 ; IS__CGSCC____-NEXT: [[TMP1:%.*]] = load i32, i32* [[R0]], align 4
; IS__CGSCC____-NEXT: [[TMP2:%.*]] = load i32, i32* [[R1]], align 4 ; IS__CGSCC____-NEXT: [[TMP2:%.*]] = load i32, i32* [[R1]], align 4
; IS__CGSCC____-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP1]], [[TMP2]] ; IS__CGSCC____-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP1]], [[TMP2]]
@ -306,7 +306,7 @@ define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) {
; IS__CGSCC____: if.then: ; IS__CGSCC____: if.then:
; IS__CGSCC____-NEXT: br label [[RETURN:%.*]] ; IS__CGSCC____-NEXT: br label [[RETURN:%.*]]
; IS__CGSCC____: if.end: ; IS__CGSCC____: if.end:
; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @internal_ret1_rrw(i32* nofree nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) [[ATTR2]] ; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @internal_ret1_rrw(i32* nofree nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[R0]], i32* nofree [[W0]]) [[ATTR2]]
; IS__CGSCC____-NEXT: [[TMP1:%.*]] = load i32, i32* [[R0]], align 4 ; IS__CGSCC____-NEXT: [[TMP1:%.*]] = load i32, i32* [[R0]], align 4
; IS__CGSCC____-NEXT: store i32 [[TMP1]], i32* [[W0]], align 4 ; IS__CGSCC____-NEXT: store i32 [[TMP1]], i32* [[W0]], align 4
; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32* @internal_ret0_nw(i32* nofree nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) [[ATTR2]] ; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32* @internal_ret0_nw(i32* nofree nonnull align 4 dereferenceable(4) [[R0]], i32* nofree nonnull align 4 dereferenceable(4) [[W0]]) [[ATTR2]]
@ -352,10 +352,10 @@ define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) {
; ;
; IS__CGSCC____: Function Attrs: argmemonly nofree nosync nounwind ; IS__CGSCC____: Function Attrs: argmemonly nofree nosync nounwind
; IS__CGSCC____-LABEL: define {{[^@]+}}@external_source_ret2_nrw ; IS__CGSCC____-LABEL: define {{[^@]+}}@external_source_ret2_nrw
; IS__CGSCC____-SAME: (i32* nofree [[N0:%.*]], i32* nofree align 4 [[R0:%.*]], i32* nofree returned [[W0:%.*]]) [[ATTR0]] { ; IS__CGSCC____-SAME: (i32* nofree [[N0:%.*]], i32* nofree [[R0:%.*]], i32* nofree returned [[W0:%.*]]) [[ATTR0]] {
; IS__CGSCC____-NEXT: entry: ; IS__CGSCC____-NEXT: entry:
; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @external_sink_ret2_nrw(i32* nofree [[N0]], i32* nocapture nofree readonly align 4 [[R0]], i32* nofree writeonly "no-capture-maybe-returned" [[W0]]) [[ATTR4:#.*]] ; IS__CGSCC____-NEXT: [[CALL:%.*]] = call i32* @external_sink_ret2_nrw(i32* nofree [[N0]], i32* nocapture nofree readonly [[R0]], i32* nofree writeonly "no-capture-maybe-returned" [[W0]]) [[ATTR4:#.*]]
; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32* @external_ret2_nrw(i32* nofree [[N0]], i32* nofree align 4 [[R0]], i32* nofree [[W0]]) [[ATTR3]] ; IS__CGSCC____-NEXT: [[CALL1:%.*]] = call i32* @external_ret2_nrw(i32* nofree [[N0]], i32* nofree [[R0]], i32* nofree [[W0]]) [[ATTR3]]
; IS__CGSCC____-NEXT: ret i32* [[CALL1]] ; IS__CGSCC____-NEXT: ret i32* [[CALL1]]
; ;
entry: entry:

View File

@ -332,7 +332,7 @@ declare void @use1nonnull_without_noundef(i8* nonnull %x);
declare void @use2nonnull(i8* nonnull noundef %x, i8* nonnull noundef %y); declare void @use2nonnull(i8* nonnull noundef %x, i8* nonnull noundef %y);
declare void @use3nonnull(i8* nonnull noundef %x, i8* nonnull noundef %y, i8* nonnull noundef %z); declare void @use3nonnull(i8* nonnull noundef %x, i8* nonnull noundef %y, i8* nonnull noundef %z);
declare i8 @use1safecall(i8* %x) readonly nounwind ; readonly+nounwind guarantees that execution continues to successor declare i8 @use1safecall(i8* %x) nounwind willreturn ; nounwind+willreturn guarantees that execution continues to successor
; Without noundef, nonnull cannot be propagated to the parent ; Without noundef, nonnull cannot be propagated to the parent

View File

@ -89,5 +89,5 @@ if.end:
declare float @convergent_func(float, float) #0 declare float @convergent_func(float, float) #0
declare float @func(float, float) #1 declare float @func(float, float) #1
attributes #0 = { nounwind readnone convergent } attributes #0 = { nounwind readnone convergent willreturn }
attributes #1 = { nounwind readnone } attributes #1 = { nounwind readnone willreturn }

View File

@ -52,7 +52,7 @@ declare void @useit1(float)
declare void @useit2(float) declare void @useit2(float)
attributes #0 = { noinline nounwind readnone uwtable } attributes #0 = { noinline nounwind readnone uwtable }
attributes #1 = { nounwind readnone } attributes #1 = { nounwind readnone willreturn }
attributes #2 = { noinline nounwind uwtable } attributes #2 = { noinline nounwind uwtable }
!llvm.dbg.cu = !{!0} !llvm.dbg.cu = !{!0}

View File

@ -2,7 +2,7 @@
; RUN: opt < %s -inline-threshold=0 -always-inline -S | FileCheck %s ; RUN: opt < %s -inline-threshold=0 -always-inline -S | FileCheck %s
; RUN: opt < %s -passes=always-inline -S | FileCheck %s ; RUN: opt < %s -passes=always-inline -S | FileCheck %s
declare i8* @foo(i8*) argmemonly nounwind declare i8* @foo(i8*) nounwind willreturn
define i8* @callee(i8 *%p) alwaysinline { define i8* @callee(i8 *%p) alwaysinline {
; CHECK-LABEL: @callee( ; CHECK-LABEL: @callee(
@ -120,7 +120,7 @@ define i8* @test4(i8* %ptr, i64 %x) {
ret i8* %p ret i8* %p
} }
declare i8* @baz(i8*) nounwind readonly declare i8* @baz(i8*) nounwind willreturn
define internal i8* @callee5(i8* %p) alwaysinline { define internal i8* @callee5(i8* %p) alwaysinline {
%r = call i8* @foo(i8* %p) %r = call i8* @foo(i8* %p)
%v = call i8* @baz(i8* %p) %v = call i8* @baz(i8* %p)

View File

@ -100,7 +100,7 @@ error:
ret void ret void
} }
declare void @dummy(i1) nounwind argmemonly declare void @dummy(i1) nounwind willreturn
define void @can_fold_some_use_before_assume(i32* %array) { define void @can_fold_some_use_before_assume(i32* %array) {
; CHECK-LABEL:@can_fold_some_use_before_assume ; CHECK-LABEL:@can_fold_some_use_before_assume

View File

@ -206,7 +206,7 @@ BB2:
ret void ret void
} }
declare void @dummy(i1) nounwind argmemonly declare void @dummy(i1) nounwind willreturn
; same as dont_fold_guard1 but there's a use immediately after guard and before ; same as dont_fold_guard1 but there's a use immediately after guard and before
; branch. We can fold that use. ; branch. We can fold that use.
define void @dont_fold_guard2(i8* %addr, i32 %i, i32 %length) { define void @dont_fold_guard2(i8* %addr, i32 %i, i32 %length) {

View File

@ -286,7 +286,7 @@ declare void @__kmpc_end_master(%struct.ident_t*, i32)
define internal void @.omp_outlined..5(i32* noalias %.global_tid., i32* noalias %.bound_tid., i32* dereferenceable(4) %a) { define internal void @.omp_outlined..5(i32* noalias %.global_tid., i32* noalias %.bound_tid., i32* dereferenceable(4) %a) {
; CHECK-LABEL: define {{[^@]+}}@.omp_outlined..5 ; CHECK-LABEL: define {{[^@]+}}@.omp_outlined..5
; CHECK-SAME: (i32* noalias nocapture nonnull readonly align 4 dereferenceable(4) [[DOTGLOBAL_TID_:%.*]], i32* noalias nocapture nofree readnone [[DOTBOUND_TID_:%.*]], i32* nocapture noundef nonnull align 4 dereferenceable(4) [[A:%.*]]) { ; CHECK-SAME: (i32* noalias nocapture readonly [[DOTGLOBAL_TID_:%.*]], i32* noalias nocapture nofree readnone [[DOTBOUND_TID_:%.*]], i32* nocapture noundef nonnull align 4 dereferenceable(4) [[A:%.*]]) {
; CHECK-NEXT: entry: ; CHECK-NEXT: entry:
; CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* noundef nonnull [[GLOB0]]) [[ATTR12]] ; CHECK-NEXT: [[OMP_GLOBAL_THREAD_NUM:%.*]] = call i32 @__kmpc_global_thread_num(%struct.ident_t* noundef nonnull [[GLOB0]]) [[ATTR12]]
; CHECK-NEXT: [[TMP:%.*]] = load i32, i32* [[DOTGLOBAL_TID_]], align 4 ; CHECK-NEXT: [[TMP:%.*]] = load i32, i32* [[DOTGLOBAL_TID_]], align 4

View File

@ -614,22 +614,26 @@ TEST(ValueTracking, GuaranteedToTransferExecutionToSuccessor) {
StringRef Assembly = StringRef Assembly =
"declare void @nounwind_readonly(i32*) nounwind readonly " "declare void @nounwind_readonly(i32*) nounwind readonly "
"declare void @nounwind_argmemonly(i32*) nounwind argmemonly " "declare void @nounwind_argmemonly(i32*) nounwind argmemonly "
"declare void @nounwind_willreturn(i32*) nounwind willreturn "
"declare void @throws_but_readonly(i32*) readonly " "declare void @throws_but_readonly(i32*) readonly "
"declare void @throws_but_argmemonly(i32*) argmemonly " "declare void @throws_but_argmemonly(i32*) argmemonly "
"declare void @nounwind_willreturn(i32*) nounwind willreturn" "declare void @throws_but_willreturn(i32*) willreturn "
" " " "
"declare void @unknown(i32*) " "declare void @unknown(i32*) "
" " " "
"define void @f(i32* %p) { " "define void @f(i32* %p) { "
" call void @nounwind_readonly(i32* %p) " " call void @nounwind_readonly(i32* %p) "
" call void @nounwind_argmemonly(i32* %p) " " call void @nounwind_argmemonly(i32* %p) "
" call void @nounwind_willreturn(i32* %p)"
" call void @throws_but_readonly(i32* %p) " " call void @throws_but_readonly(i32* %p) "
" call void @throws_but_argmemonly(i32* %p) " " call void @throws_but_argmemonly(i32* %p) "
" call void @throws_but_willreturn(i32* %p) "
" call void @unknown(i32* %p) nounwind readonly " " call void @unknown(i32* %p) nounwind readonly "
" call void @unknown(i32* %p) nounwind argmemonly " " call void @unknown(i32* %p) nounwind argmemonly "
" call void @unknown(i32* %p) nounwind willreturn "
" call void @unknown(i32* %p) readonly " " call void @unknown(i32* %p) readonly "
" call void @unknown(i32* %p) argmemonly " " call void @unknown(i32* %p) argmemonly "
" call void @nounwind_willreturn(i32* %p)" " call void @unknown(i32* %p) willreturn "
" ret void " " ret void "
"} "; "} ";
@ -643,15 +647,18 @@ TEST(ValueTracking, GuaranteedToTransferExecutionToSuccessor) {
auto &BB = F->getEntryBlock(); auto &BB = F->getEntryBlock();
bool ExpectedAnswers[] = { bool ExpectedAnswers[] = {
true, // call void @nounwind_readonly(i32* %p) false, // call void @nounwind_readonly(i32* %p)
true, // call void @nounwind_argmemonly(i32* %p) false, // call void @nounwind_argmemonly(i32* %p)
true, // call void @nounwind_willreturn(i32* %p)
false, // call void @throws_but_readonly(i32* %p) false, // call void @throws_but_readonly(i32* %p)
false, // call void @throws_but_argmemonly(i32* %p) false, // call void @throws_but_argmemonly(i32* %p)
true, // call void @unknown(i32* %p) nounwind readonly false, // call void @throws_but_willreturn(i32* %p)
true, // call void @unknown(i32* %p) nounwind argmemonly false, // call void @unknown(i32* %p) nounwind readonly
false, // call void @unknown(i32* %p) nounwind argmemonly
true, // call void @unknown(i32* %p) nounwind willreturn
false, // call void @unknown(i32* %p) readonly false, // call void @unknown(i32* %p) readonly
false, // call void @unknown(i32* %p) argmemonly false, // call void @unknown(i32* %p) argmemonly
true, // call void @nounwind_willreturn(i32* %p) false, // call void @unknown(i32* %p) willreturn
false, // ret void false, // ret void
}; };