[llvm] [ValueTracking] Add support for `sub` in `isKnownNonEqual` (PR #87704)

via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 5 08:55:56 PDT 2024


https://github.com/goldsteinn updated https://github.com/llvm/llvm-project/pull/87704

>From 5fa1239ccfa83db3491c1329748bd65ab407ae40 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Wed, 3 Apr 2024 21:41:37 -0500
Subject: [PATCH 1/2] [ValueTracking] Add tests for `sub` in `isKnownNonEqual`;
 NFC

---
 llvm/test/Transforms/InstSimplify/icmp.ll | 142 +++++++++++++++++++++-
 1 file changed, 139 insertions(+), 3 deletions(-)

diff --git a/llvm/test/Transforms/InstSimplify/icmp.ll b/llvm/test/Transforms/InstSimplify/icmp.ll
index 3109768bdfe005..264fdfa4bb2742 100644
--- a/llvm/test/Transforms/InstSimplify/icmp.ll
+++ b/llvm/test/Transforms/InstSimplify/icmp.ll
@@ -68,6 +68,7 @@ define i1 @mul_div_cmp_greater(i8 %x) {
   %cmp = icmp ule i8 %div, %x
   ret i1 %cmp
 }
+
 define i1 @mul_div_cmp_ugt(i8 %x) {
 ; CHECK-LABEL: @mul_div_cmp_ugt(
 ; CHECK-NEXT:    ret i1 false
@@ -263,18 +264,153 @@ define i1 @load_ptr(ptr %p) {
 ; CHECK-LABEL: @load_ptr(
 ; CHECK-NEXT:    ret i1 true
 ;
-  %load_p = load ptr, ptr %p, !dereferenceable !{i64 8}
+  %load_p = load ptr, ptr %p, !dereferenceable ! { i64 8}
   %r = icmp ne ptr %load_p, null
   ret i1 %r
 }
 
 define i1 @load_ptr_null_valid(ptr %p) null_pointer_is_valid {
 ; CHECK-LABEL: @load_ptr_null_valid(
-; CHECK-NEXT:    [[LOAD_P:%.*]] = load ptr, ptr [[P:%.*]], align 8, !dereferenceable !0
+; CHECK-NEXT:    [[LOAD_P:%.*]] = load ptr, ptr [[P:%.*]], align 8, !dereferenceable [[META0:![0-9]+]]
 ; CHECK-NEXT:    [[R:%.*]] = icmp ne ptr [[LOAD_P]], null
 ; CHECK-NEXT:    ret i1 [[R]]
 ;
-  %load_p = load ptr, ptr %p, !dereferenceable !{i64 8}
+  %load_p = load ptr, ptr %p, !dereferenceable ! { i64 8}
   %r = icmp ne ptr %load_p, null
   ret i1 %r
 }
+
+define i1 @non_eq_sub_neg_case_nsw(i8 %x, i8 %yy) {
+; CHECK-LABEL: @non_eq_sub_neg_case_nsw(
+; CHECK-NEXT:    [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT:    [[LHS:%.*]] = add i8 [[X:%.*]], [[Y]]
+; CHECK-NEXT:    [[SUBY:%.*]] = sub nsw i8 0, [[Y]]
+; CHECK-NEXT:    [[RHS:%.*]] = add i8 [[X]], [[SUBY]]
+; CHECK-NEXT:    [[R:%.*]] = icmp eq i8 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %y = add nuw i8 %yy, 1
+  %lhs = add i8 %x, %y
+  %suby = sub nsw i8 0, %y
+  %rhs = add i8 %x, %suby
+  %r = icmp eq i8 %lhs, %rhs
+  ret i1 %r
+}
+
+define i1 @non_eq_sub_neg_case_no_intmin(i8 %x, i8 %yy) {
+; CHECK-LABEL: @non_eq_sub_neg_case_no_intmin(
+; CHECK-NEXT:    [[Y0:%.*]] = and i8 [[YY:%.*]], 63
+; CHECK-NEXT:    [[Y:%.*]] = add nuw i8 [[Y0]], 1
+; CHECK-NEXT:    [[LHS:%.*]] = add i8 [[X:%.*]], [[Y]]
+; CHECK-NEXT:    [[SUBY:%.*]] = sub i8 0, [[Y]]
+; CHECK-NEXT:    [[RHS:%.*]] = add i8 [[X]], [[SUBY]]
+; CHECK-NEXT:    [[R:%.*]] = icmp eq i8 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %y0 = and i8 %yy, 63
+  %y = add nuw i8 %y0, 1
+  %lhs = add i8 %x, %y
+  %suby = sub i8 0, %y
+  %rhs = add i8 %x, %suby
+  %r = icmp eq i8 %lhs, %rhs
+  ret i1 %r
+}
+
+define i1 @non_eq_sub_neg_case_no_intmin2(i8 %x, i8 %yy) {
+; CHECK-LABEL: @non_eq_sub_neg_case_no_intmin2(
+; CHECK-NEXT:    ret i1 false
+;
+  %y = or i8 %yy, -64
+  %lhs = add i8 %x, %y
+  %suby = sub i8 0, %y
+  %rhs = add i8 %x, %suby
+  %r = icmp eq i8 %lhs, %rhs
+  ret i1 %r
+}
+
+define i1 @non_eq_sub_neg_case_fail(i8 %x, i8 %yy) {
+; CHECK-LABEL: @non_eq_sub_neg_case_fail(
+; CHECK-NEXT:    [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT:    [[LHS:%.*]] = add i8 [[X:%.*]], [[Y]]
+; CHECK-NEXT:    [[SUBY:%.*]] = sub i8 0, [[Y]]
+; CHECK-NEXT:    [[RHS:%.*]] = add i8 [[X]], [[SUBY]]
+; CHECK-NEXT:    [[R:%.*]] = icmp eq i8 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %y = add nuw i8 %yy, 1
+  %lhs = add i8 %x, %y
+  %suby = sub i8 0, %y
+  %rhs = add i8 %x, %suby
+  %r = icmp eq i8 %lhs, %rhs
+  ret i1 %r
+}
+
+define i1 @non_eq_sub(i8 %x, i8 %yy, i8 %z) {
+; CHECK-LABEL: @non_eq_sub(
+; CHECK-NEXT:    [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT:    [[LHS:%.*]] = add i8 [[X:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    [[SUBY:%.*]] = sub i8 [[Z]], [[Y]]
+; CHECK-NEXT:    [[RHS:%.*]] = add i8 [[X]], [[SUBY]]
+; CHECK-NEXT:    [[R:%.*]] = icmp eq i8 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %y = add nuw i8 %yy, 1
+  %lhs = add i8 %x, %z
+  %suby = sub i8 %z, %y
+  %rhs = add i8 %x, %suby
+  %r = icmp eq i8 %lhs, %rhs
+  ret i1 %r
+}
+
+define i1 @non_eq_sub2_fail_maybe_2x(i8 %x, i8 %yy, i8 %z) {
+; CHECK-LABEL: @non_eq_sub2_fail_maybe_2x(
+; CHECK-NEXT:    [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
+; CHECK-NEXT:    [[LHS:%.*]] = add i8 [[X:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    [[SUBY:%.*]] = sub i8 [[Y]], [[Z]]
+; CHECK-NEXT:    [[RHS:%.*]] = add i8 [[X]], [[SUBY]]
+; CHECK-NEXT:    [[R:%.*]] = icmp eq i8 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %y = add nuw i8 %yy, 1
+  %lhs = add i8 %x, %z
+  %suby = sub i8 %y, %z
+  %rhs = add i8 %x, %suby
+  %r = icmp eq i8 %lhs, %rhs
+  ret i1 %r
+}
+
+define i1 @non_eq_sub2_okay_odd(i8 %x, i8 %yy, i8 %z) {
+; CHECK-LABEL: @non_eq_sub2_okay_odd(
+; CHECK-NEXT:    [[Y:%.*]] = or i8 [[YY:%.*]], 1
+; CHECK-NEXT:    [[LHS:%.*]] = add i8 [[X:%.*]], [[Z:%.*]]
+; CHECK-NEXT:    [[SUBY:%.*]] = sub i8 [[Y]], [[Z]]
+; CHECK-NEXT:    [[RHS:%.*]] = add i8 [[X]], [[SUBY]]
+; CHECK-NEXT:    [[R:%.*]] = icmp eq i8 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %y = or i8 %yy, 1
+  %lhs = add i8 %x, %z
+  %suby = sub i8 %y, %z
+  %rhs = add i8 %x, %suby
+  %r = icmp eq i8 %lhs, %rhs
+  ret i1 %r
+}
+
+define i1 @non_eq_sub2_okay_known_not_2x(i8 %x, i8 %yy, i8 %zz) {
+; CHECK-LABEL: @non_eq_sub2_okay_known_not_2x(
+; CHECK-NEXT:    [[Y:%.*]] = or i8 [[YY:%.*]], 64
+; CHECK-NEXT:    [[Z:%.*]] = and i8 [[ZZ:%.*]], -97
+; CHECK-NEXT:    [[LHS:%.*]] = add i8 [[X:%.*]], [[Z]]
+; CHECK-NEXT:    [[SUBY:%.*]] = sub i8 [[Y]], [[Z]]
+; CHECK-NEXT:    [[RHS:%.*]] = add i8 [[X]], [[SUBY]]
+; CHECK-NEXT:    [[R:%.*]] = icmp eq i8 [[LHS]], [[RHS]]
+; CHECK-NEXT:    ret i1 [[R]]
+;
+  %y = or i8 %yy, 64
+  %z = and i8 %zz, 159
+  %lhs = add i8 %x, %z
+  %suby = sub i8 %y, %z
+  %rhs = add i8 %x, %suby
+  %r = icmp eq i8 %lhs, %rhs
+  ret i1 %r
+}

>From bae80c839b295a7162fca2cc7aa7a8c055468a64 Mon Sep 17 00:00:00 2001
From: Noah Goldstein <goldstein.w.n at gmail.com>
Date: Wed, 3 Apr 2024 17:38:48 -0500
Subject: [PATCH 2/2] [ValueTracking] Add support for `sub` in
 `isKnownNonEqual`

There are several cases we can handle.

    1) `-X != X` if `X != 0 && X != INT_MIN`
    2) `X - Y != X` if `Y != 0`
    3) `Y - X != X` if `Y != 2 * X`

Proofs: https://alive2.llvm.org/ce/z/cuvYV3
---
 llvm/lib/Analysis/ValueTracking.cpp       | 56 +++++++++++++++++++++--
 llvm/test/Transforms/InstSimplify/icmp.ll | 37 ++-------------
 2 files changed, 57 insertions(+), 36 deletions(-)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 5ad4da43bca7db..c06b84c39e6a97 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -2456,10 +2456,11 @@ static bool isNonZeroAdd(const APInt &DemandedElts, unsigned Depth,
 static bool isNonZeroSub(const APInt &DemandedElts, unsigned Depth,
                          const SimplifyQuery &Q, unsigned BitWidth, Value *X,
                          Value *Y) {
-  // TODO: Move this case into isKnownNonEqual().
-  if (auto *C = dyn_cast<Constant>(X))
-    if (C->isNullValue() && isKnownNonZero(Y, DemandedElts, Depth, Q))
-      return true;
+  // We duplicate this case with isKnownNonEqual because we have DemandedElts
+  // here but not in isKnownNonEqual.
+  // If we add support for DemandedElts in isKnownNonEqual, drop this case.
+  if (match(X, m_Zero()))
+    return isKnownNonZero(Y, DemandedElts, Depth, Q);
 
   return ::isKnownNonEqual(X, Y, Depth, Q);
 }
@@ -3134,6 +3135,50 @@ static bool isNonEqualShl(const Value *V1, const Value *V2, unsigned Depth,
   return false;
 }
 
+/// Return true if V1 == V2 - X or V1 == X - V2 implies V2 != V1
+static bool isNonEqualSub(const Value *V1, const Value *V2, unsigned Depth,
+                          const SimplifyQuery &Q) {
+  const BinaryOperator *BO = dyn_cast<BinaryOperator>(V1);
+  if (!BO || BO->getOpcode() != Instruction::Sub)
+    return false;
+
+  // -V2 != V2 iff V2 != 0 and V2 != INT_MIN
+  if (match(BO, m_Sub(m_Zero(), m_Specific(V2)))) {
+    const OverflowingBinaryOperator *OBO = cast<OverflowingBinaryOperator>(V1);
+    // nsw implies no INT_MIN case.
+    if (OBO->hasNoSignedWrap())
+      return isKnownNonZero(V2, Depth + 1, Q);
+    // Otherwise check for INT_MIN case directly.
+    KnownBits V2Known = computeKnownBits(V2, Depth + 1, Q);
+    if (V2Known.isNonNegative() ||
+        (!V2Known.One.isZero() && !V2Known.One.isMinSignedValue()))
+      return V2Known.isNonZero() || isKnownNonZero(V2, Depth + 1, Q);
+  }
+  // V2 - X != V2 if X != 0
+  if (V2 == BO->getOperand(0))
+    return isKnownNonZero(BO->getOperand(1), Depth + 1, Q);
+
+  // X - V2 != V2 if X != 2 * V2
+  if (V2 == BO->getOperand(1)) {
+    KnownBits XKnown = computeKnownBits(BO->getOperand(0), Depth + 1, Q);
+    // 2 * V2 implies non-odd.
+    if (XKnown.One[0])
+        return true;
+
+    if (!XKnown.isUnknown()) {
+      KnownBits V2Known = computeKnownBits(V2, Depth + 1, Q);
+      KnownBits One = KnownBits::makeConstant(
+          APInt::getOneBitSet(V2Known.getBitWidth(), 0));
+      V2Known = KnownBits::shl(V2Known, One);
+
+      if (KnownBits::ne(XKnown, V2Known).value_or(false))
+        return true;
+    }
+  }
+  return false;
+}
+
+
 static bool isNonEqualPHIs(const PHINode *PN1, const PHINode *PN2,
                            unsigned Depth, const SimplifyQuery &Q) {
   // Check two PHIs are in same block.
@@ -3274,6 +3319,9 @@ static bool isKnownNonEqual(const Value *V1, const Value *V2, unsigned Depth,
   if (isNonEqualShl(V1, V2, Depth, Q) || isNonEqualShl(V2, V1, Depth, Q))
     return true;
 
+  if (isNonEqualSub(V1, V2, Depth, Q) || isNonEqualSub(V2, V1, Depth, Q))
+    return true;
+
   if (V1->getType()->isIntOrIntVectorTy()) {
     // Are any known bits in V1 contradictory to known bits in V2? If V1
     // has a known zero where V2 has a known one, they must not be equal.
diff --git a/llvm/test/Transforms/InstSimplify/icmp.ll b/llvm/test/Transforms/InstSimplify/icmp.ll
index 264fdfa4bb2742..e652e10954b3dc 100644
--- a/llvm/test/Transforms/InstSimplify/icmp.ll
+++ b/llvm/test/Transforms/InstSimplify/icmp.ll
@@ -282,12 +282,7 @@ define i1 @load_ptr_null_valid(ptr %p) null_pointer_is_valid {
 
 define i1 @non_eq_sub_neg_case_nsw(i8 %x, i8 %yy) {
 ; CHECK-LABEL: @non_eq_sub_neg_case_nsw(
-; CHECK-NEXT:    [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
-; CHECK-NEXT:    [[LHS:%.*]] = add i8 [[X:%.*]], [[Y]]
-; CHECK-NEXT:    [[SUBY:%.*]] = sub nsw i8 0, [[Y]]
-; CHECK-NEXT:    [[RHS:%.*]] = add i8 [[X]], [[SUBY]]
-; CHECK-NEXT:    [[R:%.*]] = icmp eq i8 [[LHS]], [[RHS]]
-; CHECK-NEXT:    ret i1 [[R]]
+; CHECK-NEXT:    ret i1 false
 ;
   %y = add nuw i8 %yy, 1
   %lhs = add i8 %x, %y
@@ -299,13 +294,7 @@ define i1 @non_eq_sub_neg_case_nsw(i8 %x, i8 %yy) {
 
 define i1 @non_eq_sub_neg_case_no_intmin(i8 %x, i8 %yy) {
 ; CHECK-LABEL: @non_eq_sub_neg_case_no_intmin(
-; CHECK-NEXT:    [[Y0:%.*]] = and i8 [[YY:%.*]], 63
-; CHECK-NEXT:    [[Y:%.*]] = add nuw i8 [[Y0]], 1
-; CHECK-NEXT:    [[LHS:%.*]] = add i8 [[X:%.*]], [[Y]]
-; CHECK-NEXT:    [[SUBY:%.*]] = sub i8 0, [[Y]]
-; CHECK-NEXT:    [[RHS:%.*]] = add i8 [[X]], [[SUBY]]
-; CHECK-NEXT:    [[R:%.*]] = icmp eq i8 [[LHS]], [[RHS]]
-; CHECK-NEXT:    ret i1 [[R]]
+; CHECK-NEXT:    ret i1 false
 ;
   %y0 = and i8 %yy, 63
   %y = add nuw i8 %y0, 1
@@ -347,12 +336,7 @@ define i1 @non_eq_sub_neg_case_fail(i8 %x, i8 %yy) {
 
 define i1 @non_eq_sub(i8 %x, i8 %yy, i8 %z) {
 ; CHECK-LABEL: @non_eq_sub(
-; CHECK-NEXT:    [[Y:%.*]] = add nuw i8 [[YY:%.*]], 1
-; CHECK-NEXT:    [[LHS:%.*]] = add i8 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT:    [[SUBY:%.*]] = sub i8 [[Z]], [[Y]]
-; CHECK-NEXT:    [[RHS:%.*]] = add i8 [[X]], [[SUBY]]
-; CHECK-NEXT:    [[R:%.*]] = icmp eq i8 [[LHS]], [[RHS]]
-; CHECK-NEXT:    ret i1 [[R]]
+; CHECK-NEXT:    ret i1 false
 ;
   %y = add nuw i8 %yy, 1
   %lhs = add i8 %x, %z
@@ -381,12 +365,7 @@ define i1 @non_eq_sub2_fail_maybe_2x(i8 %x, i8 %yy, i8 %z) {
 
 define i1 @non_eq_sub2_okay_odd(i8 %x, i8 %yy, i8 %z) {
 ; CHECK-LABEL: @non_eq_sub2_okay_odd(
-; CHECK-NEXT:    [[Y:%.*]] = or i8 [[YY:%.*]], 1
-; CHECK-NEXT:    [[LHS:%.*]] = add i8 [[X:%.*]], [[Z:%.*]]
-; CHECK-NEXT:    [[SUBY:%.*]] = sub i8 [[Y]], [[Z]]
-; CHECK-NEXT:    [[RHS:%.*]] = add i8 [[X]], [[SUBY]]
-; CHECK-NEXT:    [[R:%.*]] = icmp eq i8 [[LHS]], [[RHS]]
-; CHECK-NEXT:    ret i1 [[R]]
+; CHECK-NEXT:    ret i1 false
 ;
   %y = or i8 %yy, 1
   %lhs = add i8 %x, %z
@@ -398,13 +377,7 @@ define i1 @non_eq_sub2_okay_odd(i8 %x, i8 %yy, i8 %z) {
 
 define i1 @non_eq_sub2_okay_known_not_2x(i8 %x, i8 %yy, i8 %zz) {
 ; CHECK-LABEL: @non_eq_sub2_okay_known_not_2x(
-; CHECK-NEXT:    [[Y:%.*]] = or i8 [[YY:%.*]], 64
-; CHECK-NEXT:    [[Z:%.*]] = and i8 [[ZZ:%.*]], -97
-; CHECK-NEXT:    [[LHS:%.*]] = add i8 [[X:%.*]], [[Z]]
-; CHECK-NEXT:    [[SUBY:%.*]] = sub i8 [[Y]], [[Z]]
-; CHECK-NEXT:    [[RHS:%.*]] = add i8 [[X]], [[SUBY]]
-; CHECK-NEXT:    [[R:%.*]] = icmp eq i8 [[LHS]], [[RHS]]
-; CHECK-NEXT:    ret i1 [[R]]
+; CHECK-NEXT:    ret i1 false
 ;
   %y = or i8 %yy, 64
   %z = and i8 %zz, 159



More information about the llvm-commits mailing list