[llvm] [ValueTracking] Infer non-zero from shr (add nuw A, B), C (PR #203039)

Marina Taylor via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 11 09:18:38 PDT 2026


https://github.com/citymarina updated https://github.com/llvm/llvm-project/pull/203039

>From 8712ee3fb4171316144ad07a8f1cd95ab3bec442 Mon Sep 17 00:00:00 2001
From: Marina Taylor <marina_taylor at apple.com>
Date: Wed, 10 Jun 2026 17:08:45 +0100
Subject: [PATCH 1/3] [ValueTracking] Pre-commit tests for known-non-zero
 shr-add (NFC)

Assisted-by: claude
---
 .../ValueTracking/known-non-zero-shr-add.ll   | 139 ++++++++++++++++++
 1 file changed, 139 insertions(+)
 create mode 100644 llvm/test/Analysis/ValueTracking/known-non-zero-shr-add.ll

diff --git a/llvm/test/Analysis/ValueTracking/known-non-zero-shr-add.ll b/llvm/test/Analysis/ValueTracking/known-non-zero-shr-add.ll
new file mode 100644
index 0000000000000..acfc2aefec9e5
--- /dev/null
+++ b/llvm/test/Analysis/ValueTracking/known-non-zero-shr-add.ll
@@ -0,0 +1,139 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -passes=instsimplify < %s -S | FileCheck %s
+
+define i1 @lshr_add_nuw_lhs_has_high_bit(i32 %x, i32 %y) {
+; CHECK-LABEL: @lshr_add_nuw_lhs_has_high_bit(
+; CHECK-NEXT:    [[X_HIGH:%.*]] = or i32 [[X:%.*]], 256
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw i32 [[X_HIGH]], [[Y:%.*]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[ADD]], 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[SHR]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %x_high = or i32 %x, 256
+  %add = add nuw i32 %x_high, %y
+  %shr = lshr i32 %add, 4
+  %cmp = icmp eq i32 %shr, 0
+  ret i1 %cmp
+}
+
+define i1 @lshr_add_nuw_rhs_has_high_bit(i32 %x, i32 %y) {
+; CHECK-LABEL: @lshr_add_nuw_rhs_has_high_bit(
+; CHECK-NEXT:    [[Y_HIGH:%.*]] = or i32 [[Y:%.*]], 256
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw i32 [[X:%.*]], [[Y_HIGH]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[ADD]], 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[SHR]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %y_high = or i32 %y, 256
+  %add = add nuw i32 %x, %y_high
+  %shr = lshr i32 %add, 4
+  %cmp = icmp eq i32 %shr, 0
+  ret i1 %cmp
+}
+
+define i1 @lshr_add_nuw_rhs_has_high_bit_i13(i13 %x, i13 %y) {
+; CHECK-LABEL: @lshr_add_nuw_rhs_has_high_bit_i13(
+; CHECK-NEXT:    [[Y_HIGH:%.*]] = or i13 [[Y:%.*]], 256
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw i13 [[X:%.*]], [[Y_HIGH]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i13 [[ADD]], 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i13 [[SHR]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %y_high = or i13 %y, 256
+  %add = add nuw i13 %x, %y_high
+  %shr = lshr i13 %add, 4
+  %cmp = icmp eq i13 %shr, 0
+  ret i1 %cmp
+}
+
+define <2 x i1> @lshr_add_nuw_vec(<2 x i32> %x, <2 x i32> %y) {
+; CHECK-LABEL: @lshr_add_nuw_vec(
+; CHECK-NEXT:    [[X_HIGH:%.*]] = or <2 x i32> [[X:%.*]], splat (i32 256)
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw <2 x i32> [[X_HIGH]], [[Y:%.*]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr <2 x i32> [[ADD]], splat (i32 4)
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i32> [[SHR]], zeroinitializer
+; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+;
+  %x_high = or <2 x i32> %x, splat (i32 256)
+  %add = add nuw <2 x i32> %x_high, %y
+  %shr = lshr <2 x i32> %add, splat (i32 4)
+  %cmp = icmp eq <2 x i32> %shr, zeroinitializer
+  ret <2 x i1> %cmp
+}
+
+define i1 @ashr_add_nuw_lhs_has_high_bit(i32 %x, i32 %y) {
+; CHECK-LABEL: @ashr_add_nuw_lhs_has_high_bit(
+; CHECK-NEXT:    [[X_HIGH:%.*]] = or i32 [[X:%.*]], 256
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw i32 [[X_HIGH]], [[Y:%.*]]
+; CHECK-NEXT:    [[SHR:%.*]] = ashr i32 [[ADD]], 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[SHR]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %x_high = or i32 %x, 256
+  %add = add nuw i32 %x_high, %y
+  %shr = ashr i32 %add, 4
+  %cmp = icmp eq i32 %shr, 0
+  ret i1 %cmp
+}
+
+; negative tests
+
+define i1 @lshr_add_nuw_bit_shifted_out_fail(i32 %x, i32 %y) {
+; CHECK-LABEL: @lshr_add_nuw_bit_shifted_out_fail(
+; CHECK-NEXT:    [[X_HIGH:%.*]] = or i32 [[X:%.*]], 16
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw i32 [[X_HIGH]], [[Y:%.*]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[ADD]], 8
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[SHR]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %x_high = or i32 %x, 16
+  %add = add nuw i32 %x_high, %y
+  %shr = lshr i32 %add, 8
+  %cmp = icmp eq i32 %shr, 0
+  ret i1 %cmp
+}
+
+define i1 @lshr_add_no_nuw_fail(i32 %x, i32 %y) {
+; CHECK-LABEL: @lshr_add_no_nuw_fail(
+; CHECK-NEXT:    [[X_HIGH:%.*]] = or i32 [[X:%.*]], 256
+; CHECK-NEXT:    [[ADD:%.*]] = add i32 [[X_HIGH]], [[Y:%.*]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[ADD]], 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[SHR]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %x_high = or i32 %x, 256
+  %add = add i32 %x_high, %y
+  %shr = lshr i32 %add, 4
+  %cmp = icmp eq i32 %shr, 0
+  ret i1 %cmp
+}
+
+define i1 @lshr_add_nsw_fail(i32 %x, i32 %y) {
+; CHECK-LABEL: @lshr_add_nsw_fail(
+; CHECK-NEXT:    [[X_HIGH:%.*]] = or i32 [[X:%.*]], 256
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[X_HIGH]], [[Y:%.*]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[ADD]], 4
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[SHR]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %x_high = or i32 %x, 256
+  %add = add nsw i32 %x_high, %y
+  %shr = lshr i32 %add, 4
+  %cmp = icmp eq i32 %shr, 0
+  ret i1 %cmp
+}
+
+define i1 @lshr_add_nuw_variable_shift_fail(i32 %x, i32 %y, i32 %c) {
+; CHECK-LABEL: @lshr_add_nuw_variable_shift_fail(
+; CHECK-NEXT:    [[X_HIGH:%.*]] = or i32 [[X:%.*]], 256
+; CHECK-NEXT:    [[ADD:%.*]] = add nuw i32 [[X_HIGH]], [[Y:%.*]]
+; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[ADD]], [[C:%.*]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[SHR]], 0
+; CHECK-NEXT:    ret i1 [[CMP]]
+;
+  %x_high = or i32 %x, 256
+  %add = add nuw i32 %x_high, %y
+  %shr = lshr i32 %add, %c
+  %cmp = icmp eq i32 %shr, 0
+  ret i1 %cmp
+}

>From 39fed24abdc8b6fa85ac75755a485e195232cb9f Mon Sep 17 00:00:00 2001
From: Marina Taylor <marina_taylor at apple.com>
Date: Wed, 10 Jun 2026 17:10:57 +0100
Subject: [PATCH 2/3] [ValueTracking] Infer non-zero from shr (add nuw A, B), C

...if either A or B has a known-one bit at position >= C.

https://alive2.llvm.org/ce/z/ELYTjh

This eliminates null checks in some internal workloads.

Assisted-by: claude
---
 llvm/lib/Analysis/ValueTracking.cpp           | 20 +++++++++++++
 .../ValueTracking/known-non-zero-shr-add.ll   | 30 ++++---------------
 2 files changed, 25 insertions(+), 25 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 0dc9f91964e5a..6918bd9cffbbc 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -3337,6 +3337,26 @@ static bool isKnownNonZeroFromOperator(const Operator *I,
     if (Known.isNegative())
       return true;
 
+    // shr (add nuw A, B), C is non-zero if A or B has a known-one bit at
+    // position >= C, because the sum >= max(A, B).
+    const APInt *ShAmtC;
+    if (Depth + 1 < MaxAnalysisRecursionDepth &&
+        match(I->getOperand(1), m_APInt(ShAmtC)) && ShAmtC->ult(BitWidth)) {
+      Value *ShiftIn = I->getOperand(0);
+      if (auto *Add = dyn_cast<OverflowingBinaryOperator>(ShiftIn);
+          Add && Add->getOpcode() == Instruction::Add &&
+          Add->hasNoUnsignedWrap()) {
+        KnownBits KnownA =
+            computeKnownBits(Add->getOperand(0), DemandedElts, Q, Depth + 1);
+        if (!KnownA.One.lshr(*ShAmtC).isZero())
+          return true;
+        KnownBits KnownB =
+            computeKnownBits(Add->getOperand(1), DemandedElts, Q, Depth + 1);
+        if (!KnownB.One.lshr(*ShAmtC).isZero())
+          return true;
+      }
+    }
+
     return isNonZeroShift(I, DemandedElts, Q, Known, Depth);
   }
   case Instruction::UDiv:
diff --git a/llvm/test/Analysis/ValueTracking/known-non-zero-shr-add.ll b/llvm/test/Analysis/ValueTracking/known-non-zero-shr-add.ll
index acfc2aefec9e5..577ad5409d1fb 100644
--- a/llvm/test/Analysis/ValueTracking/known-non-zero-shr-add.ll
+++ b/llvm/test/Analysis/ValueTracking/known-non-zero-shr-add.ll
@@ -3,11 +3,7 @@
 
 define i1 @lshr_add_nuw_lhs_has_high_bit(i32 %x, i32 %y) {
 ; CHECK-LABEL: @lshr_add_nuw_lhs_has_high_bit(
-; CHECK-NEXT:    [[X_HIGH:%.*]] = or i32 [[X:%.*]], 256
-; CHECK-NEXT:    [[ADD:%.*]] = add nuw i32 [[X_HIGH]], [[Y:%.*]]
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[ADD]], 4
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[SHR]], 0
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %x_high = or i32 %x, 256
   %add = add nuw i32 %x_high, %y
@@ -18,11 +14,7 @@ define i1 @lshr_add_nuw_lhs_has_high_bit(i32 %x, i32 %y) {
 
 define i1 @lshr_add_nuw_rhs_has_high_bit(i32 %x, i32 %y) {
 ; CHECK-LABEL: @lshr_add_nuw_rhs_has_high_bit(
-; CHECK-NEXT:    [[Y_HIGH:%.*]] = or i32 [[Y:%.*]], 256
-; CHECK-NEXT:    [[ADD:%.*]] = add nuw i32 [[X:%.*]], [[Y_HIGH]]
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i32 [[ADD]], 4
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[SHR]], 0
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %y_high = or i32 %y, 256
   %add = add nuw i32 %x, %y_high
@@ -33,11 +25,7 @@ define i1 @lshr_add_nuw_rhs_has_high_bit(i32 %x, i32 %y) {
 
 define i1 @lshr_add_nuw_rhs_has_high_bit_i13(i13 %x, i13 %y) {
 ; CHECK-LABEL: @lshr_add_nuw_rhs_has_high_bit_i13(
-; CHECK-NEXT:    [[Y_HIGH:%.*]] = or i13 [[Y:%.*]], 256
-; CHECK-NEXT:    [[ADD:%.*]] = add nuw i13 [[X:%.*]], [[Y_HIGH]]
-; CHECK-NEXT:    [[SHR:%.*]] = lshr i13 [[ADD]], 4
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i13 [[SHR]], 0
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %y_high = or i13 %y, 256
   %add = add nuw i13 %x, %y_high
@@ -48,11 +36,7 @@ define i1 @lshr_add_nuw_rhs_has_high_bit_i13(i13 %x, i13 %y) {
 
 define <2 x i1> @lshr_add_nuw_vec(<2 x i32> %x, <2 x i32> %y) {
 ; CHECK-LABEL: @lshr_add_nuw_vec(
-; CHECK-NEXT:    [[X_HIGH:%.*]] = or <2 x i32> [[X:%.*]], splat (i32 256)
-; CHECK-NEXT:    [[ADD:%.*]] = add nuw <2 x i32> [[X_HIGH]], [[Y:%.*]]
-; CHECK-NEXT:    [[SHR:%.*]] = lshr <2 x i32> [[ADD]], splat (i32 4)
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq <2 x i32> [[SHR]], zeroinitializer
-; CHECK-NEXT:    ret <2 x i1> [[CMP]]
+; CHECK-NEXT:    ret <2 x i1> zeroinitializer
 ;
   %x_high = or <2 x i32> %x, splat (i32 256)
   %add = add nuw <2 x i32> %x_high, %y
@@ -63,11 +47,7 @@ define <2 x i1> @lshr_add_nuw_vec(<2 x i32> %x, <2 x i32> %y) {
 
 define i1 @ashr_add_nuw_lhs_has_high_bit(i32 %x, i32 %y) {
 ; CHECK-LABEL: @ashr_add_nuw_lhs_has_high_bit(
-; CHECK-NEXT:    [[X_HIGH:%.*]] = or i32 [[X:%.*]], 256
-; CHECK-NEXT:    [[ADD:%.*]] = add nuw i32 [[X_HIGH]], [[Y:%.*]]
-; CHECK-NEXT:    [[SHR:%.*]] = ashr i32 [[ADD]], 4
-; CHECK-NEXT:    [[CMP:%.*]] = icmp eq i32 [[SHR]], 0
-; CHECK-NEXT:    ret i1 [[CMP]]
+; CHECK-NEXT:    ret i1 false
 ;
   %x_high = or i32 %x, 256
   %add = add nuw i32 %x_high, %y

>From 8f3368ab6d85d5a11cff6df1e26e67987064a28b Mon Sep 17 00:00:00 2001
From: Marina Taylor <marina_taylor at apple.com>
Date: Thu, 11 Jun 2026 17:16:57 +0100
Subject: [PATCH 3/3] Refactor with m_NUWAdd

---
 llvm/lib/Analysis/ValueTracking.cpp | 25 ++++++++++---------------
 1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 6918bd9cffbbc..238055c4a023e 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -3339,22 +3339,17 @@ static bool isKnownNonZeroFromOperator(const Operator *I,
 
     // shr (add nuw A, B), C is non-zero if A or B has a known-one bit at
     // position >= C, because the sum >= max(A, B).
-    const APInt *ShAmtC;
+    Value *A, *B;
+    const APInt *C;
     if (Depth + 1 < MaxAnalysisRecursionDepth &&
-        match(I->getOperand(1), m_APInt(ShAmtC)) && ShAmtC->ult(BitWidth)) {
-      Value *ShiftIn = I->getOperand(0);
-      if (auto *Add = dyn_cast<OverflowingBinaryOperator>(ShiftIn);
-          Add && Add->getOpcode() == Instruction::Add &&
-          Add->hasNoUnsignedWrap()) {
-        KnownBits KnownA =
-            computeKnownBits(Add->getOperand(0), DemandedElts, Q, Depth + 1);
-        if (!KnownA.One.lshr(*ShAmtC).isZero())
-          return true;
-        KnownBits KnownB =
-            computeKnownBits(Add->getOperand(1), DemandedElts, Q, Depth + 1);
-        if (!KnownB.One.lshr(*ShAmtC).isZero())
-          return true;
-      }
+        match(I->getOperand(0), m_NUWAdd(m_Value(A), m_Value(B))) &&
+        match(I->getOperand(1), m_APInt(C)) && C->ult(BitWidth)) {
+      KnownBits KnownA = computeKnownBits(A, DemandedElts, Q, Depth + 1);
+      if (!KnownA.One.lshr(*C).isZero())
+        return true;
+      KnownBits KnownB = computeKnownBits(B, DemandedElts, Q, Depth + 1);
+      if (!KnownB.One.lshr(*C).isZero())
+        return true;
     }
 
     return isNonZeroShift(I, DemandedElts, Q, Known, Depth);



More information about the llvm-commits mailing list