Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -4352,6 +4352,19 @@ return isCapturedIn(/* Memory */ false, /* Integer */ false, /* Return */ true); + // Comparison to null should be regarded as nocapture. + if (ICmpInst *ICmp = dyn_cast(UInst)) { + const Function *F = UInst->getFunction(); + bool NullPointerIsDefined = + F ? llvm::NullPointerIsDefined( + F, U->get()->getType()->getPointerAddressSpace()) + : true; + if (!NullPointerIsDefined && + isa(ICmp->getOperand(1 - U->getOperandNo()))) + return isCapturedIn(/* Memory */ false, /* Integer */ false, + /* Return */ false); + } + // For now we only use special logic for call sites. However, the tracker // itself knows about a lot of other non-capturing cases already. CallSite CS(UInst); Index: llvm/test/Transforms/Attributor/noalias.ll =================================================================== --- llvm/test/Transforms/Attributor/noalias.ll +++ llvm/test/Transforms/Attributor/noalias.ll @@ -143,7 +143,7 @@ ; TEST 8 -; CHECK: define noalias i8* @test8(i32* %0) +; CHECK: define noalias i8* @test8(i32* nocapture readnone %0) define i8* @test8(i32* %0) nounwind uwtable { %2 = tail call noalias i8* @malloc(i64 4) %3 = icmp ne i32* %0, null Index: llvm/test/Transforms/Attributor/nocapture-1.ll =================================================================== --- llvm/test/Transforms/Attributor/nocapture-1.ll +++ llvm/test/Transforms/Attributor/nocapture-1.ll @@ -278,13 +278,13 @@ ret void } -; ATTRIBUTOR: define i1 @captureICmp(i32* nofree readnone %x) +; ATTRIBUTOR: define i1 @captureICmp(i32* nocapture nofree readnone %x) define i1 @captureICmp(i32* %x) { %1 = icmp eq i32* %x, null ret i1 %1 } -; ATTRIBUTOR: define i1 @captureICmpRev(i32* nofree readnone %x) +; ATTRIBUTOR: define i1 @captureICmpRev(i32* nocapture nofree readnone %x) define i1 @captureICmpRev(i32* %x) { %1 = icmp eq i32* null, %x ret i1 %1 Index: llvm/test/Transforms/Attributor/nocapture-2.ll =================================================================== --- llvm/test/Transforms/Attributor/nocapture-2.ll +++ llvm/test/Transforms/Attributor/nocapture-2.ll @@ -11,8 +11,7 @@ ; return p == 0; ; } ; -; FIXME: no-capture missing for %p -; CHECK: define i32 @is_null_return(i32* nofree readnone %p) +; CHECK: define i32 @is_null_return(i32* nocapture nofree readnone %p) define i32 @is_null_return(i32* %p) #0 { entry: %cmp = icmp eq i32* %p, null @@ -30,8 +29,7 @@ ; return 0; ; } ; -; FIXME: no-capture missing for %p -; CHECK: define i32 @is_null_control(i32* nofree readnone %p) +; CHECK: define i32 @is_null_control(i32* nocapture nofree readnone %p) define i32 @is_null_control(i32* %p) #0 { entry: %retval = alloca i32, align 4 @@ -353,8 +351,8 @@ ; ; Verify we do *not* assume b is returned or not captured. ; -; CHECK: define i32* @ret_arg_or_unknown(i32* readnone %b) -; CHECK: define i32* @ret_arg_or_unknown_through_phi(i32* readnone %b) +; CHECK: define i32* @ret_arg_or_unknown(i32* readnone "no-capture-maybe-returned" %b) +; CHECK: define i32* @ret_arg_or_unknown_through_phi(i32* readnone "no-capture-maybe-returned" %b) declare i32* @unknown() Index: llvm/test/Transforms/Attributor/nonnull.ll =================================================================== --- llvm/test/Transforms/Attributor/nonnull.ll +++ llvm/test/Transforms/Attributor/nonnull.ll @@ -192,7 +192,7 @@ define internal i32* @f1(i32* %arg) { ; FIXME: missing nonnull It should be nonnull @f1(i32* nonnull readonly %arg) -; ATTRIBUTOR: define internal nonnull i32* @f1(i32* nofree readonly %arg) +; ATTRIBUTOR: define internal nonnull i32* @f1(i32* nocapture nofree readonly %arg) bb: %tmp = icmp eq i32* %arg, null @@ -205,13 +205,13 @@ bb4: ; preds = %bb1 %tmp5 = getelementptr inbounds i32, i32* %arg, i64 1 -; ATTRIBUTOR: %tmp5b = tail call nonnull i32* @f3(i32* nofree nonnull readonly %tmp5) +; ATTRIBUTOR: %tmp5b = tail call nonnull i32* @f3(i32* nocapture nofree nonnull readonly %tmp5) %tmp5b = tail call i32* @f3(i32* %tmp5) %tmp5c = getelementptr inbounds i32, i32* %tmp5b, i64 -1 br label %bb9 bb6: ; preds = %bb1 -; ATTRIBUTOR: %tmp7 = tail call nonnull i32* @f2(i32* nofree nonnull readonly align 4 dereferenceable(4) %arg) +; ATTRIBUTOR: %tmp7 = tail call nonnull i32* @f2(i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) %arg) %tmp7 = tail call i32* @f2(i32* %arg) ret i32* %tmp7 @@ -221,19 +221,19 @@ } define internal i32* @f2(i32* %arg) { -; ATTRIBUTOR: define internal nonnull i32* @f2(i32* nofree nonnull readonly align 4 dereferenceable(4) %arg) +; ATTRIBUTOR: define internal nonnull i32* @f2(i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) %arg) bb: -; ATTRIBUTOR: %tmp = tail call nonnull i32* @f1(i32* nofree nonnull readonly align 4 dereferenceable(4) %arg) +; ATTRIBUTOR: %tmp = tail call nonnull i32* @f1(i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) %arg) %tmp = tail call i32* @f1(i32* %arg) ret i32* %tmp } define dso_local noalias i32* @f3(i32* %arg) { -; FIXME: missing nonnull. It should be nonnull @f3(i32* nonnull readonly %arg) -; ATTRIBUTOR: define dso_local noalias nonnull i32* @f3(i32* nofree readonly %arg) +; FIXME: missing nonnull. It should be nonnull @f3(i32* nocapture nonnull readonly %arg) +; ATTRIBUTOR: define dso_local noalias nonnull i32* @f3(i32* nocapture nofree readonly %arg) bb: ; FIXME: missing nonnull. It should be @f1(i32* nonnull readonly %arg) -; ATTRIBUTOR: %tmp = call nonnull i32* @f1(i32* nofree readonly %arg) +; ATTRIBUTOR: %tmp = call nonnull i32* @f1(i32* nocapture nofree readonly %arg) %tmp = call i32* @f1(i32* %arg) ret i32* %tmp } Index: llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll =================================================================== --- llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll +++ llvm/test/Transforms/Attributor/read_write_returned_arguments_scc.ll @@ -1,4 +1,4 @@ -; RUN: opt -functionattrs -enable-nonnull-arg-prop -attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=6 -S < %s | FileCheck %s +; RUN: opt -functionattrs -enable-nonnull-arg-prop -attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=7 -S < %s | FileCheck %s ; ; This is an evolved example to stress test SCC parameter attribute propagation. ; The SCC in this test is made up of the following six function, three of which @@ -30,7 +30,7 @@ target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" ; CHECK: Function Attrs: argmemonly nofree nosync nounwind -; CHECK-NEXT: define i32* @external_ret2_nrw(i32* nofree %n0, i32* nofree %r0, i32* nofree returned %w0) +; CHECK-NEXT: define i32* @external_ret2_nrw(i32* nocapture nofree readnone %n0, i32* nocapture nofree readonly %r0, i32* nofree returned writeonly "no-capture-maybe-returned" %w0) define i32* @external_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: %call = call i32* @internal_ret0_nw(i32* %n0, i32* %w0) @@ -41,7 +41,7 @@ } ; CHECK: Function Attrs: argmemonly nofree nosync nounwind -; CHECK-NEXT: define internal i32* @internal_ret0_nw(i32* nofree returned %n0, i32* nofree %w0) +; CHECK-NEXT: define internal i32* @internal_ret0_nw(i32* noalias nofree readnone returned "no-capture-maybe-returned" %n0, i32* nocapture nofree writeonly %w0) define internal i32* @internal_ret0_nw(i32* %n0, i32* %w0) { entry: %r0 = alloca i32, align 4 @@ -70,7 +70,7 @@ } ; CHECK: Function Attrs: argmemonly nofree nosync nounwind -; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* nofree nonnull align 4 dereferenceable(4) %r0, i32* nofree returned %r1, i32* nofree %w0) +; CHECK-NEXT: define internal i32* @internal_ret1_rrw(i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) %r0, i32* nocapture nofree readonly returned %r1, i32* nocapture nofree writeonly %w0) define internal i32* @internal_ret1_rrw(i32* %r0, i32* %r1, i32* %w0) { entry: %0 = load i32, i32* %r0, align 4 @@ -102,7 +102,7 @@ } ; CHECK: Function Attrs: argmemonly nofree norecurse nosync nounwind -; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* nofree readnone %n0, i32* nocapture nofree readonly %r0, i32* nofree returned writeonly "no-capture-maybe-returned" %w0) +; CHECK-NEXT: define i32* @external_sink_ret2_nrw(i32* nocapture nofree readnone %n0, i32* nocapture nofree readonly %r0, i32* nofree returned writeonly "no-capture-maybe-returned" %w0) define i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: %tobool = icmp ne i32* %n0, null @@ -121,7 +121,7 @@ } ; CHECK: Function Attrs: argmemonly nofree nosync nounwind -; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* nofree nonnull align 4 dereferenceable(4) %r0, i32* nofree returned %w0) +; CHECK-NEXT: define internal i32* @internal_ret1_rw(i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) %r0, i32* nofree returned writeonly "no-capture-maybe-returned" %w0) define internal i32* @internal_ret1_rw(i32* %r0, i32* %w0) { entry: %0 = load i32, i32* %r0, align 4 @@ -147,7 +147,7 @@ } ; CHECK: Function Attrs: argmemonly nofree nosync nounwind -; CHECK-NEXT: define i32* @external_source_ret2_nrw(i32* nofree %n0, i32* nofree %r0, i32* nofree returned %w0) +; CHECK-NEXT: define i32* @external_source_ret2_nrw(i32* nocapture nofree readnone %n0, i32* nocapture nofree readonly %r0, i32* nofree returned writeonly "no-capture-maybe-returned" %w0) define i32* @external_source_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) { entry: %call = call i32* @external_sink_ret2_nrw(i32* %n0, i32* %r0, i32* %w0) Index: llvm/test/Transforms/Attributor/returned.ll =================================================================== --- llvm/test/Transforms/Attributor/returned.ll +++ llvm/test/Transforms/Attributor/returned.ll @@ -276,8 +276,8 @@ ; TEST another SCC test ; -; BOTH: define i32* @rt2_helper(i32* nofree readnone returned %a) -; BOTH: define i32* @rt2(i32* nofree readnone %a, i32* nofree readnone "no-capture-maybe-returned" %b) +; BOTH: define i32* @rt2_helper(i32* nofree readnone returned "no-capture-maybe-returned" %a) +; BOTH: define i32* @rt2(i32* nofree readnone "no-capture-maybe-returned" %a, i32* nofree readnone "no-capture-maybe-returned" %b) define i32* @rt2_helper(i32* %a) #0 { entry: %call = call i32* @rt2(i32* %a, i32* %a) @@ -300,8 +300,8 @@ ; TEST another SCC test ; -; BOTH: define i32* @rt3_helper(i32* nofree readnone %a, i32* nofree readnone returned "no-capture-maybe-returned" %b) -; BOTH: define i32* @rt3(i32* nofree readnone %a, i32* nofree readnone returned "no-capture-maybe-returned" %b) +; BOTH: define i32* @rt3_helper(i32* nocapture nofree readnone %a, i32* nofree readnone returned "no-capture-maybe-returned" %b) +; BOTH: define i32* @rt3(i32* nocapture nofree readnone %a, i32* nofree readnone returned "no-capture-maybe-returned" %b) define i32* @rt3_helper(i32* %a, i32* %b) #0 { entry: %call = call i32* @rt3(i32* %a, i32* %b) @@ -501,11 +501,11 @@ ; } ; ; BOTH: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable -; BOTH-NEXT: define double* @bitcasts_select_and_phi(i32* nofree readnone returned %b) +; BOTH-NEXT: define double* @bitcasts_select_and_phi(i32* nofree readnone returned "no-capture-maybe-returned" %b) ; ; ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable -; ATTRIBUTOR-NEXT: define double* @bitcasts_select_and_phi(i32* nofree readnone returned %b) +; ATTRIBUTOR-NEXT: define double* @bitcasts_select_and_phi(i32* nofree readnone returned "no-capture-maybe-returned" %b) define double* @bitcasts_select_and_phi(i32* %b) #0 { entry: %bc0 = bitcast i32* %b to double* @@ -538,11 +538,11 @@ ; } ; ; BOTH: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable -; BOTH-NEXT: define double* @ret_arg_arg_undef(i32* nofree readnone returned %b) +; BOTH-NEXT: define double* @ret_arg_arg_undef(i32* nofree readnone returned "no-capture-maybe-returned" %b) ; ; ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable -; ATTRIBUTOR-NEXT: define double* @ret_arg_arg_undef(i32* nofree readnone returned %b) +; ATTRIBUTOR-NEXT: define double* @ret_arg_arg_undef(i32* nofree readnone returned "no-capture-maybe-returned" %b) define double* @ret_arg_arg_undef(i32* %b) #0 { entry: %bc0 = bitcast i32* %b to double* @@ -575,11 +575,11 @@ ; } ; ; BOTH: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable -; BOTH-NEXT: define double* @ret_undef_arg_arg(i32* nofree readnone returned %b) +; BOTH-NEXT: define double* @ret_undef_arg_arg(i32* nofree readnone returned "no-capture-maybe-returned" %b) ; ; ; ATTRIBUTOR: Function Attrs: nofree noinline nosync nounwind readnone uwtable -; ATTRIBUTOR-NEXT: define double* @ret_undef_arg_arg(i32* nofree readnone returned %b) +; ATTRIBUTOR-NEXT: define double* @ret_undef_arg_arg(i32* nofree readnone returned "no-capture-maybe-returned" %b) define double* @ret_undef_arg_arg(i32* %b) #0 { entry: %bc0 = bitcast i32* %b to double* @@ -612,9 +612,9 @@ ; } ; ; BOTH: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable -; BOTH-NEXT: define double* @ret_undef_arg_undef(i32* nofree readnone returned %b) +; BOTH-NEXT: define double* @ret_undef_arg_undef(i32* nofree readnone returned "no-capture-maybe-returned" %b) ; -; ATTRIBUTOR: define double* @ret_undef_arg_undef(i32* nofree readnone returned %b) +; ATTRIBUTOR: define double* @ret_undef_arg_undef(i32* nofree readnone returned "no-capture-maybe-returned" %b) define double* @ret_undef_arg_undef(i32* %b) #0 { entry: %bc0 = bitcast i32* %b to double*