[ValueTracking] Don't assume readonly function will return
authorNikita Popov <nikita.ppv@gmail.com>
Fri, 22 Jan 2021 21:33:55 +0000 (22:33 +0100)
committerNikita Popov <nikita.ppv@gmail.com>
Sun, 24 Jan 2021 09:40:21 +0000 (10:40 +0100)
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

12 files changed:
llvm/lib/Analysis/ValueTracking.cpp
llvm/test/Transforms/Attributor/nocapture-1.ll
llvm/test/Transforms/Attributor/nonnull.ll
llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll
llvm/test/Transforms/FunctionAttrs/nonnull.ll
llvm/test/Transforms/GVNHoist/hoist-convergent.ll
llvm/test/Transforms/GVNHoist/hoist-pr31891.ll
llvm/test/Transforms/Inline/ret_attr_update.ll
llvm/test/Transforms/JumpThreading/assume.ll
llvm/test/Transforms/JumpThreading/guards.ll
llvm/test/Transforms/OpenMP/parallel_deletion.ll
llvm/unittests/Analysis/ValueTrackingTest.cpp

index 672497d..5600a3b 100644 (file)
@@ -5040,23 +5040,10 @@ bool llvm::isGuaranteedToTransferExecutionToSuccessor(const Instruction *I) {
     if (CB->hasFnAttr(Attribute::WillReturn))
       return true;
 
-    // Non-throwing call sites can loop infinitely, call exit/pthread_exit
-    // etc. and thus not return.  However, LLVM already assumes that
-    //
-    //  - Thread exiting actions are modeled as writes to memory invisible to
-    //    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();
+    // FIXME: Temporarily assume that all side-effect free intrinsics will
+    // return. Remove this workaround once all intrinsics are appropriately
+    // annotated.
+    return isa<IntrinsicInst>(CB) && CB->onlyReadsMemory();
   }
 
   // Other instructions return normally.
index 74b39d7..1124c65 100644 (file)
@@ -48,7 +48,7 @@ define void @c3(i32* %q) {
 ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind willreturn writeonly
 ; IS__CGSCC____-LABEL: define {{[^@]+}}@c3
 ; 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
 ;
   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____-LABEL: define {{[^@]+}}@c7
 ; 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:    ret i1 [[VAL]]
 ;
 ; IS__CGSCC____: Function Attrs: nofree norecurse nosync nounwind readonly willreturn
 ; IS__CGSCC____-LABEL: define {{[^@]+}}@c7
 ; 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:    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____-LABEL: define {{[^@]+}}@nc2
 ; 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
 ;
   %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____-LABEL: define {{[^@]+}}@nc4
 ; 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__CGSCC____: Function Attrs: argmemonly nounwind
 ; IS__CGSCC____-LABEL: define {{[^@]+}}@nc4
 ; 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
 ;
   call void @external(i8* %p)
@@ -609,7 +609,7 @@ define void @nocaptureLaunder(i8* %p) {
 ; IS__TUNIT____-LABEL: define {{[^@]+}}@nocaptureLaunder
 ; IS__TUNIT____-SAME: (i8* nocapture nofree [[P:%.*]]) [[ATTR5]] {
 ; 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:    ret void
 ;
@@ -617,7 +617,7 @@ define void @nocaptureLaunder(i8* %p) {
 ; IS__CGSCC____-LABEL: define {{[^@]+}}@nocaptureLaunder
 ; IS__CGSCC____-SAME: (i8* nocapture nofree [[P:%.*]]) [[ATTR10:#.*]] {
 ; 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:    ret void
 ;
@@ -632,14 +632,14 @@ define void @captureLaunder(i8* %p) {
 ; IS__TUNIT____: Function Attrs: nofree nosync nounwind willreturn
 ; IS__TUNIT____-LABEL: define {{[^@]+}}@captureLaunder
 ; 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:    ret void
 ;
 ; IS__CGSCC____: Function Attrs: nofree nosync nounwind willreturn
 ; IS__CGSCC____-LABEL: define {{[^@]+}}@captureLaunder
 ; 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:    ret void
 ;
@@ -653,7 +653,7 @@ define void @nocaptureStrip(i8* %p) {
 ; IS__TUNIT____-LABEL: define {{[^@]+}}@nocaptureStrip
 ; IS__TUNIT____-SAME: (i8* nocapture nofree writeonly [[P:%.*]]) [[ATTR1]] {
 ; 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:    ret void
 ;
@@ -661,7 +661,7 @@ define void @nocaptureStrip(i8* %p) {
 ; IS__CGSCC____-LABEL: define {{[^@]+}}@nocaptureStrip
 ; IS__CGSCC____-SAME: (i8* nocapture nofree writeonly [[P:%.*]]) [[ATTR11:#.*]] {
 ; 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:    ret void
 ;
@@ -676,14 +676,14 @@ define void @captureStrip(i8* %p) {
 ; IS__TUNIT____: Function Attrs: nofree nosync nounwind willreturn writeonly
 ; IS__TUNIT____-LABEL: define {{[^@]+}}@captureStrip
 ; 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:    ret void
 ;
 ; IS__CGSCC____: Function Attrs: nofree nosync nounwind willreturn writeonly
 ; IS__CGSCC____-LABEL: define {{[^@]+}}@captureStrip
 ; 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:    ret void
 ;
@@ -840,17 +840,15 @@ entry:
   ret i8* %p
 }
 
-declare i8* @maybe_returned_ptr(i8* readonly %ptr) readonly nounwind
-declare i8 @maybe_returned_val(i8* %ptr) readonly nounwind
-declare void @val_use(i8 %ptr) readonly nounwind
+declare i8* @maybe_returned_ptr(i8* readonly %ptr) readonly nounwind willreturn
+declare i8 @maybe_returned_val(i8* %ptr) readonly nounwind willreturn
+declare void @val_use(i8 %ptr) readonly nounwind willreturn
 
 ; FIXME: Both pointers should be nocapture
 define void @ptr_uses(i8* %ptr, i8* %wptr) {
-; CHECK: Function Attrs: nounwind
+; CHECK: Function Attrs: nounwind willreturn
 ; CHECK-LABEL: define {{[^@]+}}@ptr_uses
-; CHECK-SAME: (i8* [[PTR:%.*]], i8* nocapture nonnull writeonly dereferenceable(1) [[WPTR:%.*]]) [[ATTR13:#.*]] {
-; 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-SAME: (i8* [[PTR:%.*]], i8* nocapture nonnull writeonly dereferenceable(1) [[WPTR:%.*]]) [[ATTR14:#.*]] {
 ; CHECK-NEXT:    store i8 0, i8* [[WPTR]], align 1
 ; CHECK-NEXT:    ret void
 ;
index b54ab77..f5062eb 100644 (file)
@@ -146,34 +146,22 @@ define i8* @test3(i1 %c) {
 ; nonnull if neither can ever return null.  (In this case, they
 ; just never return period.)
 define i8* @test4_helper() {
-; NOT_CGSCC_NPM: Function Attrs: nofree noreturn nosync nounwind readnone
-; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@test4_helper
-; NOT_CGSCC_NPM-SAME: () [[ATTR2:#.*]] {
-; NOT_CGSCC_NPM-NEXT:    [[RET:%.*]] = call i8* @test4()
-; NOT_CGSCC_NPM-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
+; CHECK: Function Attrs: nofree noreturn nosync nounwind readnone
+; CHECK-LABEL: define {{[^@]+}}@test4_helper
+; CHECK-SAME: () [[ATTR2:#.*]] {
+; CHECK-NEXT:    [[RET:%.*]] = call i8* @test4() [[ATTR2]]
+; CHECK-NEXT:    unreachable
 ;
   %ret = call i8* @test4()
   ret i8* %ret
 }
 
 define i8* @test4() {
-; NOT_CGSCC_NPM: Function Attrs: nofree noreturn nosync nounwind readnone
-; NOT_CGSCC_NPM-LABEL: define {{[^@]+}}@test4
-; NOT_CGSCC_NPM-SAME: () [[ATTR2]] {
-; NOT_CGSCC_NPM-NEXT:    [[RET:%.*]] = call i8* @test4_helper()
-; NOT_CGSCC_NPM-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
+; CHECK: Function Attrs: nofree noreturn nosync nounwind readnone
+; CHECK-LABEL: define {{[^@]+}}@test4
+; CHECK-SAME: () [[ATTR2]] {
+; CHECK-NEXT:    [[RET:%.*]] = call i8* @test4_helper() [[ATTR2]]
+; CHECK-NEXT:    unreachable
 ;
   %ret = call i8* @test4_helper()
   ret i8* %ret
@@ -802,7 +790,7 @@ declare void @use1nonnull(i8* nonnull %x);
 declare void @use2nonnull(i8* nonnull %x, i8* nonnull %y);
 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.
 
@@ -1524,7 +1512,7 @@ define i8* @mybasename(i8* nofree readonly %str) {
 ; NOT_CGSCC_OPM: Function Attrs: nofree nounwind readonly willreturn
 ; NOT_CGSCC_OPM-LABEL: define {{[^@]+}}@mybasename
 ; 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:    [[ADD_PTR:%.*]] = getelementptr inbounds i8, i8* [[CALL]], i64 1
 ; 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-LABEL: define {{[^@]+}}@mybasename
 ; 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:    [[ADD_PTR:%.*]] = getelementptr inbounds i8, i8* [[CALL]], i64 1
 ; IS__CGSCC_OPM-NEXT:    [[COND:%.*]] = select i1 [[TOBOOL]], i8* [[ADD_PTR]], i8* [[STR]]
index 32c5970..f444dac 100644 (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____-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:    [[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]]
@@ -179,7 +179,7 @@ define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) {
 ; IS__CGSCC____:       if.then:
 ; IS__CGSCC____-NEXT:    br label [[RETURN:%.*]]
 ; 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:    [[TMP2:%.*]] = load i32, i32* [[R1]], align 4
 ; 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____-NEXT:    br label [[RETURN:%.*]]
 ; 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:    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]]
@@ -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____-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:    [[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:    [[CALL1:%.*]] = call i32* @external_ret2_nrw(i32* nofree [[N0]], i32* nofree align 4 [[R0]], i32* nofree [[W0]]) [[ATTR3]]
+; 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 [[R0]], i32* nofree [[W0]]) [[ATTR3]]
 ; IS__CGSCC____-NEXT:    ret i32* [[CALL1]]
 ;
 entry:
index 56457b3..ae5245e 100644 (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 @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
 
index 73d923c..f5bf03f 100644 (file)
@@ -89,5 +89,5 @@ if.end:
 declare float @convergent_func(float, float) #0
 declare float @func(float, float) #1
 
-attributes #0 = { nounwind readnone convergent }
-attributes #1 = { nounwind readnone }
+attributes #0 = { nounwind readnone convergent willreturn }
+attributes #1 = { nounwind readnone willreturn }
index 0c667d7..380c197 100644 (file)
@@ -52,7 +52,7 @@ declare void @useit1(float)
 declare void @useit2(float)
 
 attributes #0 = { noinline nounwind readnone uwtable }
-attributes #1 = { nounwind readnone }
+attributes #1 = { nounwind readnone willreturn }
 attributes #2 = { noinline nounwind uwtable }
 
 !llvm.dbg.cu = !{!0}
index 647029e..bb10920 100644 (file)
@@ -2,7 +2,7 @@
 ; RUN: opt < %s -inline-threshold=0 -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 {
 ; CHECK-LABEL: @callee(
@@ -120,7 +120,7 @@ define i8* @test4(i8* %ptr, i64 %x) {
   ret i8* %p
 }
 
-declare i8* @baz(i8*) nounwind readonly
+declare i8* @baz(i8*) nounwind willreturn
 define internal i8* @callee5(i8* %p) alwaysinline {
   %r = call i8* @foo(i8* %p)
   %v = call i8* @baz(i8* %p)
index f58ee29..08775b0 100644 (file)
@@ -100,7 +100,7 @@ error:
   ret void
 }
 
-declare void @dummy(i1) nounwind argmemonly
+declare void @dummy(i1) nounwind willreturn
 define void @can_fold_some_use_before_assume(i32* %array) {
 
 ; CHECK-LABEL:@can_fold_some_use_before_assume
index c760283..911eec8 100644 (file)
@@ -206,7 +206,7 @@ BB2:
   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
 ; branch. We can fold that use.
 define void @dont_fold_guard2(i8* %addr, i32 %i, i32 %length) {
index 1a6d158..76c6317 100644 (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) {
 ; 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:    [[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
index f3240e0..18fb154 100644 (file)
@@ -614,22 +614,26 @@ TEST(ValueTracking, GuaranteedToTransferExecutionToSuccessor) {
   StringRef Assembly =
       "declare void @nounwind_readonly(i32*) nounwind readonly "
       "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_argmemonly(i32*) argmemonly "
-      "declare void @nounwind_willreturn(i32*) nounwind willreturn"
+      "declare void @throws_but_willreturn(i32*) willreturn "
       " "
       "declare void @unknown(i32*) "
       " "
       "define void @f(i32* %p) { "
       "  call void @nounwind_readonly(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_argmemonly(i32* %p) "
+      "  call void @throws_but_willreturn(i32* %p) "
       "  call void @unknown(i32* %p) nounwind readonly "
       "  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) argmemonly "
-      "  call void @nounwind_willreturn(i32* %p)"
+      "  call void @unknown(i32* %p) willreturn "
       "  ret void "
       "} ";
 
@@ -643,15 +647,18 @@ TEST(ValueTracking, GuaranteedToTransferExecutionToSuccessor) {
 
   auto &BB = F->getEntryBlock();
   bool ExpectedAnswers[] = {
-      true,  // call void @nounwind_readonly(i32* %p)
-      true,  // call void @nounwind_argmemonly(i32* %p)
+      false, // call void @nounwind_readonly(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_argmemonly(i32* %p)
-      true,  // call void @unknown(i32* %p) nounwind readonly
-      true,  // call void @unknown(i32* %p) nounwind argmemonly
+      false, // call void @throws_but_willreturn(i32* %p)
+      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) argmemonly
-      true,  // call void @nounwind_willreturn(i32* %p)
+      false, // call void @unknown(i32* %p) willreturn
       false, // ret void
   };