Skip to content

[SCCP] Propagate non-null pointers #106090

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 27, 2024
Merged

[SCCP] Propagate non-null pointers #106090

merged 1 commit into from
Aug 27, 2024

Conversation

nikic
Copy link
Contributor

@nikic nikic commented Aug 26, 2024

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.

The implementation is something of a MVP, there are a number of obvious extensions (e.g. allocas are also non-null).

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.
@llvmbot
Copy link
Member

llvmbot commented Aug 26, 2024

@llvm/pr-subscribers-function-specialization

@llvm/pr-subscribers-llvm-transforms

Author: Nikita Popov (nikic)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/106090.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/Utils/SCCPSolver.cpp (+33-1)
  • (modified) llvm/test/Transforms/SCCP/pointer-nonnull.ll (+7-14)
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

Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dtcxzyw
Copy link
Member

dtcxzyw commented Aug 26, 2024

Can you have a look at https://github.com/dtcxzyw/llvm-opt-benchmark/pull/1247/files#r1731703928? This patch seems to incorrectly remove the null pointer checking in https://github.com/abseil/abseil-cpp/blob/master/absl/time/internal/cctz/src/time_zone_format.cc#L623-L646.

Oh, ignore me. This function is internal :)

Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thank you!

@nikic nikic merged commit 1cea5c2 into llvm:main Aug 27, 2024
11 checks passed
@nikic nikic deleted the ipsccp-nonnull branch August 27, 2024 07:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants