[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:19:43 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