diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -38,6 +38,7 @@ #include "llvm/IR/Constant.h" #include "llvm/IR/ConstantRange.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Dominators.h" @@ -2092,11 +2093,14 @@ } static bool isKnownNonNullFromDominatingCondition(const Value *V, - const Instruction *CtxI, - const DominatorTree *DT) { + unsigned Depth, + const Query &Q) { if (isa(V)) return false; + const Instruction *CtxI = Q.CxtI; + const DominatorTree *DT = Q.DT; + if (!CtxI || !DT) return false; @@ -2126,6 +2130,28 @@ return true; } + if (const GEPOperator *GEP = dyn_cast(U)) { + const Instruction *I = cast(U); + if (DT->dominates(I, CtxI) && GEP->getType()->isPointerTy() && + V == GEP->getPointerOperand() && + isGEPKnownNonNull(GEP, Depth + 1, Q)) { + unsigned GEPUsesExplored = 0; + for (auto *UGEP : GEP->users()) { + if (GEPUsesExplored >= DomConditionsMaxUses) + break; + GEPUsesExplored++; + if (U == getLoadStorePointerOperand(UGEP)) { + const Instruction *IGEP = cast(UGEP); + if (!NullPointerIsDefined(IGEP->getFunction(), + U->getType()->getPointerAddressSpace()) && + DT->dominates(IGEP, CtxI)) { + return true; + } + } + } + } + } + // Consider only compare instructions uniquely controlling a branch Value *RHS; CmpInst::Predicate Pred; @@ -2333,7 +2359,7 @@ } } - if (isKnownNonNullFromDominatingCondition(V, Q.CxtI, Q.DT)) + if (isKnownNonNullFromDominatingCondition(V, Depth, Q)) return true; // Check for recursive pointer simplifications. diff --git a/llvm/test/Analysis/ValueTracking/known-nonnull-at.ll b/llvm/test/Analysis/ValueTracking/known-nonnull-at.ll --- a/llvm/test/Analysis/ValueTracking/known-nonnull-at.ll +++ b/llvm/test/Analysis/ValueTracking/known-nonnull-at.ll @@ -220,3 +220,123 @@ %retval.0 = phi i8* [ %1, %if.end ], [ null, %entry ] ret i8* %retval.0 } + + +define i32 @test_null_after_gep_load(i32* %0) { +; CHECK-LABEL: @test_null_after_gep_load( +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, i32* [[TMP0:%.*]], i64 1 +; CHECK-NEXT: [[TMP3:%.*]] = load i32, i32* [[TMP2]], align 4 +; CHECK-NEXT: ret i32 [[TMP3]] +; + %2 = getelementptr inbounds i32, i32* %0, i64 1 + %3 = load i32, i32* %2, align 4 + %4 = icmp eq i32* %0, null + %5 = select i1 %4, i32 42, i32 %3 + ret i32 %5 +} + +define i32 @test_null_after_gep_store(i32* %0) { +; CHECK-LABEL: @test_null_after_gep_store( +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, i32* [[TMP0:%.*]], i64 1 +; CHECK-NEXT: store i32 42, i32* [[TMP2]], align 4 +; CHECK-NEXT: ret i32 42 +; + %2 = getelementptr inbounds i32, i32* %0, i64 1 + store i32 42, i32* %2, align 4 + %3 = icmp eq i32* %0, null + %4 = select i1 %3, i32 4242, i32 42 + ret i32 %4 +} + +define i32 @test_null_after_gep_load_addrspace(i32 addrspace(1)* %0) { +; CHECK-LABEL: @test_null_after_gep_load_addrspace( +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, i32 addrspace(1)* [[TMP0:%.*]], i64 1 +; CHECK-NEXT: [[TMP3:%.*]] = load i32, i32 addrspace(1)* [[TMP2]], align 4 +; CHECK-NEXT: [[TMP4:%.*]] = icmp eq i32 addrspace(1)* [[TMP0]], null +; CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i32 42, i32 [[TMP3]] +; CHECK-NEXT: ret i32 [[TMP5]] +; + %2 = getelementptr inbounds i32, i32 addrspace(1)* %0, i64 1 + %3 = load i32, i32 addrspace(1)* %2, align 4 + %4 = icmp eq i32 addrspace(1)* %0, null + %5 = select i1 %4, i32 42, i32 %3 + ret i32 %5 +} + +define i32 @test_null_after_gep_store_addrspace(i32 addrspace(1)* %0) { +; CHECK-LABEL: @test_null_after_gep_store_addrspace( +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, i32 addrspace(1)* [[TMP0:%.*]], i64 1 +; CHECK-NEXT: store i32 42, i32 addrspace(1)* [[TMP2]], align 4 +; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32 addrspace(1)* [[TMP0]], null +; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP3]], i32 4242, i32 42 +; CHECK-NEXT: ret i32 [[TMP4]] +; + %2 = getelementptr inbounds i32, i32 addrspace(1)* %0, i64 1 + store i32 42, i32 addrspace(1)* %2, align 4 + %3 = icmp eq i32 addrspace(1)* %0, null + %4 = select i1 %3, i32 4242, i32 42 + ret i32 %4 +} + +define i32* @test_null_gep_without_load(i32* %0) { +; CHECK-LABEL: @test_null_gep_without_load( +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, i32* [[TMP0:%.*]], i64 1 +; CHECK-NEXT: [[TMP3:%.*]] = icmp eq i32* [[TMP0]], null +; CHECK-NEXT: [[TMP4:%.*]] = select i1 [[TMP3]], i32* null, i32* [[TMP2]] +; CHECK-NEXT: ret i32* [[TMP4]] +; + %2 = getelementptr inbounds i32, i32* %0, i64 1 + %3 = icmp eq i32* %0, null + %4 = select i1 %3, i32* null, i32* %2 + ret i32* %4 +} + + +%struct.X = type { i32, i32 } + +define i32 @test_gep_struct_load(%struct.X* %0) { +; CHECK-LABEL: @test_gep_struct_load( +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_X:%.*]], %struct.X* [[TMP0:%.*]], i64 0, i32 1 +; CHECK-NEXT: [[TMP3:%.*]] = load i32, i32* [[TMP2]], align 4 +; CHECK-NEXT: ret i32 [[TMP3]] +; + %2 = getelementptr inbounds %struct.X, %struct.X* %0, i64 0, i32 1 + %3 = load i32, i32* %2, align 4 + %4 = icmp eq %struct.X* %0, null + %5 = select i1 %4, i32 0, i32 %3 + ret i32 %5 +} + +define i32 @test_gep_struct_load_zero(%struct.X* %0) { +; CHECK-LABEL: @test_gep_struct_load_zero( +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_X:%.*]], %struct.X* [[TMP0:%.*]], i64 0, i32 0 +; CHECK-NEXT: [[TMP3:%.*]] = load i32, i32* [[TMP2]], align 4 +; CHECK-NEXT: [[TMP4:%.*]] = icmp eq %struct.X* [[TMP0]], null +; CHECK-NEXT: [[TMP5:%.*]] = select i1 [[TMP4]], i32 0, i32 [[TMP3]] +; CHECK-NEXT: ret i32 [[TMP5]] +; + %2 = getelementptr inbounds %struct.X, %struct.X* %0, i64 0, i32 0 + %3 = load i32, i32* %2, align 4 + %4 = icmp eq %struct.X* %0, null + %5 = select i1 %4, i32 0, i32 %3 + ret i32 %5 +} + +define dso_local i32 @test_null_gep_from_assume(i32* %0, i64 %1) { +; CHECK-LABEL: @test_null_gep_from_assume( +; CHECK-NEXT: [[TMP3:%.*]] = icmp ne i64 [[TMP1:%.*]], 0 +; CHECK-NEXT: tail call void @llvm.assume(i1 [[TMP3]]) +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i32, i32* [[TMP0:%.*]], i64 [[TMP1]] +; CHECK-NEXT: [[TMP5:%.*]] = load i32, i32* [[TMP4]], align 4 +; CHECK-NEXT: ret i32 [[TMP5]] +; + %3 = icmp ne i64 %1, 0 + tail call void @llvm.assume(i1 %3) + %4 = getelementptr inbounds i32, i32* %0, i64 %1 + %5 = load i32, i32* %4, align 4 + %6 = icmp eq i32* %0, null + %7 = select i1 %6, i32 0, i32 %5 + ret i32 %7 +} + +declare void @llvm.assume(i1) diff --git a/llvm/test/Transforms/InstCombine/select-cmp-br.ll b/llvm/test/Transforms/InstCombine/select-cmp-br.ll --- a/llvm/test/Transforms/InstCombine/select-cmp-br.ll +++ b/llvm/test/Transforms/InstCombine/select-cmp-br.ll @@ -15,21 +15,19 @@ ; CHECK-NEXT: [[M:%.*]] = load i64*, i64** [[TMP]], align 8 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[C]], %C* [[ARG]], i64 1, i32 0, i32 0 ; CHECK-NEXT: [[N:%.*]] = load i64*, i64** [[TMP1]], align 8 -; CHECK-NEXT: [[TMP5:%.*]] = icmp ne i64* [[M]], [[N]] -; CHECK-NEXT: [[TMP71:%.*]] = icmp eq %C* [[ARG]], null -; CHECK-NEXT: [[TMP7:%.*]] = select i1 [[TMP5]], i1 true, i1 [[TMP71]] -; CHECK-NEXT: br i1 [[TMP7]], label [[BB10:%.*]], label [[BB8:%.*]] +; CHECK-NEXT: [[TMP5_NOT:%.*]] = icmp eq i64* [[M]], [[N]] +; CHECK-NEXT: br i1 [[TMP5_NOT]], label [[BB8:%.*]], label [[BB10:%.*]] ; CHECK: bb: ; CHECK-NEXT: ret void ; CHECK: bb8: ; CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds [[C]], %C* [[ARG]], i64 0, i32 0 -; CHECK-NEXT: tail call void @bar(%struct.S* [[TMP9]]) +; CHECK-NEXT: tail call void @bar(%struct.S* nonnull [[TMP9]]) ; CHECK-NEXT: br label [[BB:%.*]] ; CHECK: bb10: ; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i64, i64* [[M]], i64 9 ; CHECK-NEXT: [[TMP3:%.*]] = bitcast i64* [[TMP2]] to i64 (%C*)** ; CHECK-NEXT: [[TMP4:%.*]] = load i64 (%C*)*, i64 (%C*)** [[TMP3]], align 8 -; CHECK-NEXT: [[TMP11:%.*]] = tail call i64 [[TMP4]](%C* [[ARG]]) +; CHECK-NEXT: [[TMP11:%.*]] = tail call i64 [[TMP4]](%C* nonnull [[ARG]]) ; CHECK-NEXT: br label [[BB]] ; entry: @@ -66,20 +64,18 @@ ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[C]], %C* [[ARG]], i64 1, i32 0, i32 0 ; CHECK-NEXT: [[N:%.*]] = load i64*, i64** [[TMP1]], align 8 ; CHECK-NEXT: [[TMP5:%.*]] = icmp eq i64* [[M]], [[N]] -; CHECK-NEXT: [[TMP71:%.*]] = icmp eq %C* [[ARG]], null -; CHECK-NEXT: [[TMP7:%.*]] = select i1 [[TMP5]], i1 true, i1 [[TMP71]] -; CHECK-NEXT: br i1 [[TMP7]], label [[BB10:%.*]], label [[BB8:%.*]] +; CHECK-NEXT: br i1 [[TMP5]], label [[BB10:%.*]], label [[BB8:%.*]] ; CHECK: bb: ; CHECK-NEXT: ret void ; CHECK: bb8: ; CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds [[C]], %C* [[ARG]], i64 0, i32 0 -; CHECK-NEXT: tail call void @bar(%struct.S* [[TMP9]]) +; CHECK-NEXT: tail call void @bar(%struct.S* nonnull [[TMP9]]) ; CHECK-NEXT: br label [[BB:%.*]] ; CHECK: bb10: ; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i64, i64* [[M]], i64 9 ; CHECK-NEXT: [[TMP3:%.*]] = bitcast i64* [[TMP2]] to i64 (%C*)** ; CHECK-NEXT: [[TMP4:%.*]] = load i64 (%C*)*, i64 (%C*)** [[TMP3]], align 8 -; CHECK-NEXT: [[TMP11:%.*]] = tail call i64 [[TMP4]](%C* [[ARG]]) +; CHECK-NEXT: [[TMP11:%.*]] = tail call i64 [[TMP4]](%C* nonnull [[ARG]]) ; CHECK-NEXT: br label [[BB]] ; entry: @@ -115,21 +111,19 @@ ; CHECK-NEXT: [[M:%.*]] = load i64*, i64** [[TMP]], align 8 ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[C]], %C* [[ARG]], i64 1, i32 0, i32 0 ; CHECK-NEXT: [[N:%.*]] = load i64*, i64** [[TMP1]], align 8 -; CHECK-NEXT: [[TMP5:%.*]] = icmp ne i64* [[M]], [[N]] -; CHECK-NEXT: [[TMP7_NOT1:%.*]] = icmp eq %C* [[ARG]], null -; CHECK-NEXT: [[TMP7_NOT:%.*]] = select i1 [[TMP5]], i1 true, i1 [[TMP7_NOT1]] -; CHECK-NEXT: br i1 [[TMP7_NOT]], label [[BB10:%.*]], label [[BB8:%.*]] +; CHECK-NEXT: [[TMP5:%.*]] = icmp eq i64* [[M]], [[N]] +; CHECK-NEXT: br i1 [[TMP5]], label [[BB8:%.*]], label [[BB10:%.*]] ; CHECK: bb: ; CHECK-NEXT: ret void ; CHECK: bb8: ; CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds [[C]], %C* [[ARG]], i64 0, i32 0 -; CHECK-NEXT: tail call void @bar(%struct.S* [[TMP9]]) +; CHECK-NEXT: tail call void @bar(%struct.S* nonnull [[TMP9]]) ; CHECK-NEXT: br label [[BB:%.*]] ; CHECK: bb10: ; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i64, i64* [[M]], i64 9 ; CHECK-NEXT: [[TMP3:%.*]] = bitcast i64* [[TMP2]] to i64 (%C*)** ; CHECK-NEXT: [[TMP4:%.*]] = load i64 (%C*)*, i64 (%C*)** [[TMP3]], align 8 -; CHECK-NEXT: [[TMP11:%.*]] = tail call i64 [[TMP4]](%C* [[ARG]]) +; CHECK-NEXT: [[TMP11:%.*]] = tail call i64 [[TMP4]](%C* nonnull [[ARG]]) ; CHECK-NEXT: br label [[BB]] ; entry: @@ -166,20 +160,18 @@ ; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[C]], %C* [[ARG]], i64 1, i32 0, i32 0 ; CHECK-NEXT: [[N:%.*]] = load i64*, i64** [[TMP1]], align 8 ; CHECK-NEXT: [[TMP5:%.*]] = icmp eq i64* [[M]], [[N]] -; CHECK-NEXT: [[TMP7_NOT1:%.*]] = icmp eq %C* [[ARG]], null -; CHECK-NEXT: [[TMP7_NOT:%.*]] = select i1 [[TMP5]], i1 true, i1 [[TMP7_NOT1]] -; CHECK-NEXT: br i1 [[TMP7_NOT]], label [[BB10:%.*]], label [[BB8:%.*]] +; CHECK-NEXT: br i1 [[TMP5]], label [[BB10:%.*]], label [[BB8:%.*]] ; CHECK: bb: ; CHECK-NEXT: ret void ; CHECK: bb8: ; CHECK-NEXT: [[TMP9:%.*]] = getelementptr inbounds [[C]], %C* [[ARG]], i64 0, i32 0 -; CHECK-NEXT: tail call void @bar(%struct.S* [[TMP9]]) +; CHECK-NEXT: tail call void @bar(%struct.S* nonnull [[TMP9]]) ; CHECK-NEXT: br label [[BB:%.*]] ; CHECK: bb10: ; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i64, i64* [[M]], i64 9 ; CHECK-NEXT: [[TMP3:%.*]] = bitcast i64* [[TMP2]] to i64 (%C*)** ; CHECK-NEXT: [[TMP4:%.*]] = load i64 (%C*)*, i64 (%C*)** [[TMP3]], align 8 -; CHECK-NEXT: [[TMP11:%.*]] = tail call i64 [[TMP4]](%C* [[ARG]]) +; CHECK-NEXT: [[TMP11:%.*]] = tail call i64 [[TMP4]](%C* nonnull [[ARG]]) ; CHECK-NEXT: br label [[BB]] ; entry: