[llvm] [SCCP] Propagate non-null pointers (PR #106090)

Nikita Popov via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 26 08:23:04 PDT 2024


https://github.com/nikic created https://github.com/llvm/llvm-project/pull/106090

Add NotConstant(Null) roots for nonnull arguments and then propagate them through nuw/inbounds GEPs.

Having this functionality in SCCP is useful because it allows reliably eliminating null comparisons, independently of how deeply nested they are in selects/phis. This handles cases that would hit a cutoff in ValueTracking otherwise.

>From f8bd12b5127759018e09c7a27bf7d2410039c121 Mon Sep 17 00:00:00 2001
From: Nikita Popov <npopov at redhat.com>
Date: Mon, 26 Aug 2024 14:47:50 +0200
Subject: [PATCH] [SCCP] Propagate non-null pointers

Add NotConstant(Null) roots for nonnull arguments and the propagate
them through nuw/inbounds GEPs.

Having this functionality in SCCP is useful because it allows
reliably eliminating null comparisons, independently of how
deeply nested they are in selects/phis. This handles cases that
would hit a cutoff in ValueTracking.
---
 llvm/lib/Transforms/Utils/SCCPSolver.cpp     | 34 +++++++++++++++++++-
 llvm/test/Transforms/SCCP/pointer-nonnull.ll | 21 ++++--------
 2 files changed, 40 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/SCCPSolver.cpp b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
index c6f355a07d9c7f..c21b4c0b3a1c87 100644
--- a/llvm/lib/Transforms/Utils/SCCPSolver.cpp
+++ b/llvm/lib/Transforms/Utils/SCCPSolver.cpp
@@ -446,6 +446,12 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
     return markConstant(ValueState[V], V, C);
   }
 
+  bool markNotConstant(ValueLatticeElement &IV, Value *V, Constant *C);
+
+  bool markNotNull(ValueLatticeElement &IV, Value *V) {
+    return markNotConstant(IV, V, Constant::getNullValue(V->getType()));
+  }
+
   /// markConstantRange - Mark the object as constant range with \p CR. If the
   /// object is not a constant range with the range \p CR, add it to the
   /// instruction work list so that the users of the instruction are updated
@@ -820,7 +826,11 @@ class SCCPInstVisitor : public InstVisitor<SCCPInstVisitor> {
         return;
       }
     }
-    // Assume nothing about the incoming arguments without range.
+    if (A->hasNonNullAttr()) {
+      markNotNull(ValueState[A], A);
+      return;
+    }
+    // Assume nothing about the incoming arguments without attributes.
     markOverdefined(A);
   }
 
@@ -905,6 +915,15 @@ bool SCCPInstVisitor::markConstant(ValueLatticeElement &IV, Value *V,
   return true;
 }
 
+bool SCCPInstVisitor::markNotConstant(ValueLatticeElement &IV, Value *V,
+                                      Constant *C) {
+  if (!IV.markNotConstant(C))
+    return false;
+  LLVM_DEBUG(dbgs() << "markNotConstant: " << *C << ": " << *V << '\n');
+  pushToWorkList(IV, V);
+  return true;
+}
+
 bool SCCPInstVisitor::markConstantRange(ValueLatticeElement &IV, Value *V,
                                         const ConstantRange &CR) {
   if (!IV.markConstantRange(CR))
@@ -1574,6 +1593,19 @@ void SCCPInstVisitor::visitGetElementPtrInst(GetElementPtrInst &I) {
   if (ValueState[&I].isOverdefined())
     return (void)markOverdefined(&I);
 
+  const ValueLatticeElement &PtrState = getValueState(I.getPointerOperand());
+  if (PtrState.isUnknownOrUndef())
+    return;
+
+  // gep inbounds/nuw of non-null is non-null.
+  if (PtrState.isNotConstant() && PtrState.getNotConstant()->isNullValue()) {
+    if (I.hasNoUnsignedWrap() ||
+        (I.isInBounds() &&
+         !NullPointerIsDefined(I.getFunction(), I.getAddressSpace())))
+      return (void)markNotNull(ValueState[&I], &I);
+    return (void)markOverdefined(&I);
+  }
+
   SmallVector<Constant *, 8> Operands;
   Operands.reserve(I.getNumOperands());
 
diff --git a/llvm/test/Transforms/SCCP/pointer-nonnull.ll b/llvm/test/Transforms/SCCP/pointer-nonnull.ll
index 85367d8a56765e..a88f08ab25fe1d 100644
--- a/llvm/test/Transforms/SCCP/pointer-nonnull.ll
+++ b/llvm/test/Transforms/SCCP/pointer-nonnull.ll
@@ -14,8 +14,7 @@ define i1 @test_no_attr(ptr %p) {
 define i1 @test_nonnull(ptr nonnull %p) {
 ; CHECK-LABEL: define i1 @test_nonnull(
 ; CHECK-SAME: ptr nonnull [[P:%.*]]) {
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne ptr [[P]], null
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
   %cmp = icmp ne ptr %p, null
   ret i1 %cmp
@@ -24,8 +23,7 @@ define i1 @test_nonnull(ptr nonnull %p) {
 define i1 @test_nonnull_eq(ptr nonnull %p) {
 ; CHECK-LABEL: define i1 @test_nonnull_eq(
 ; CHECK-SAME: ptr nonnull [[P:%.*]]) {
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq ptr [[P]], null
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %cmp = icmp eq ptr %p, null
   ret i1 %cmp
@@ -34,8 +32,7 @@ define i1 @test_nonnull_eq(ptr nonnull %p) {
 define i1 @test_dereferenceable(ptr dereferenceable(4) %p) {
 ; CHECK-LABEL: define i1 @test_dereferenceable(
 ; CHECK-SAME: ptr dereferenceable(4) [[P:%.*]]) {
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne ptr [[P]], null
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
   %cmp = icmp ne ptr %p, null
   ret i1 %cmp
@@ -57,8 +54,7 @@ define i1 @test_gep_nuw(ptr nonnull %p, i64 %x) {
 ; CHECK-LABEL: define i1 @test_gep_nuw(
 ; CHECK-SAME: ptr nonnull [[P:%.*]], i64 [[X:%.*]]) {
 ; CHECK-NEXT:    [[GEP:%.*]] = getelementptr nuw i8, ptr [[P]], i64 [[X]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne ptr [[GEP]], null
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
   %gep = getelementptr nuw i8, ptr %p, i64 %x
   %cmp = icmp ne ptr %gep, null
@@ -69,8 +65,7 @@ define i1 @test_gep_inbounds(ptr nonnull %p, i64 %x) {
 ; CHECK-LABEL: define i1 @test_gep_inbounds(
 ; CHECK-SAME: ptr nonnull [[P:%.*]], i64 [[X:%.*]]) {
 ; CHECK-NEXT:    [[GEP:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 [[X]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne ptr [[GEP]], null
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
   %gep = getelementptr inbounds i8, ptr %p, i64 %x
   %cmp = icmp ne ptr %gep, null
@@ -94,8 +89,7 @@ define i1 @test_select(i1 %c, ptr nonnull %p, i64 %x) {
 ; CHECK-SAME: i1 [[C:%.*]], ptr nonnull [[P:%.*]], i64 [[X:%.*]]) {
 ; CHECK-NEXT:    [[GEP:%.*]] = getelementptr nuw i8, ptr [[P]], i64 [[X]]
 ; CHECK-NEXT:    [[SEL:%.*]] = select i1 [[C]], ptr [[P]], ptr [[GEP]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne ptr [[SEL]], null
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
   %gep = getelementptr nuw i8, ptr %p, i64 %x
   %sel = select i1 %c, ptr %p, ptr %gep
@@ -127,8 +121,7 @@ define i1 @test_phi(i1 %c, ptr nonnull %p, i64 %x) {
 ; CHECK-NEXT:    br label %[[JOIN]]
 ; CHECK:       [[JOIN]]:
 ; CHECK-NEXT:    [[PHI:%.*]] = phi ptr [ [[P]], %[[ENTRY]] ], [ [[GEP]], %[[IF]] ]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ne ptr [[PHI]], null
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 true
 ;
 entry:
   br i1 %c, label %if, label %join



More information about the llvm-commits mailing list