[llvm] a2ba4e8 - [ConstraintElimination] Handle solving-only `ICMP_NE` predicates

Antonio Frighetto via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 29 12:26:40 PDT 2023


Author: Antonio Frighetto
Date: 2023-06-29T21:22:48+02:00
New Revision: a2ba4e8075140a97680edf1878535c4c2c651b19

URL: https://github.com/llvm/llvm-project/commit/a2ba4e8075140a97680edf1878535c4c2c651b19
DIFF: https://github.com/llvm/llvm-project/commit/a2ba4e8075140a97680edf1878535c4c2c651b19.diff

LOG: [ConstraintElimination] Handle solving-only `ICMP_NE` predicates

Simplification of non-equality predicates for solving constraint
systems is now supported by checking the validity of related
inequalities and equalities.

Differential Revision: https://reviews.llvm.org/D152684

Added: 
    

Modified: 
    llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
    llvm/test/Transforms/ConstraintElimination/constants-unsigned-predicates.ll
    llvm/test/Transforms/ConstraintElimination/ne.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 864f0a1326cee..55e4503ff8aa0 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -192,8 +192,10 @@ struct ConstraintTy {
 
   ConstraintTy() = default;
 
-  ConstraintTy(SmallVector<int64_t, 8> Coefficients, bool IsSigned, bool IsEq)
-      : Coefficients(Coefficients), IsSigned(IsSigned), IsEq(IsEq) {}
+  ConstraintTy(SmallVector<int64_t, 8> Coefficients, bool IsSigned, bool IsEq,
+               bool IsNe)
+      : Coefficients(Coefficients), IsSigned(IsSigned), IsEq(IsEq), IsNe(IsNe) {
+  }
 
   unsigned size() const { return Coefficients.size(); }
 
@@ -205,6 +207,8 @@ struct ConstraintTy {
 
   bool isEq() const { return IsEq; }
 
+  bool isNe() const { return IsNe; }
+
   /// Check if the current constraint is implied by the given ConstraintSystem.
   ///
   /// \return true or false if the constraint is proven to be respectively true,
@@ -214,6 +218,7 @@ struct ConstraintTy {
 
 private:
   bool IsEq = false;
+  bool IsNe = false;
 };
 
 /// Wrapper encapsulating separate constraint systems and corresponding value
@@ -502,6 +507,8 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
                               SmallVectorImpl<Value *> &NewVariables) const {
   assert(NewVariables.empty() && "NewVariables must be empty when passed in");
   bool IsEq = false;
+  bool IsNe = false;
+
   // Try to convert Pred to one of ULE/SLT/SLE/SLT.
   switch (Pred) {
   case CmpInst::ICMP_UGT:
@@ -521,10 +528,13 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
     }
     break;
   case CmpInst::ICMP_NE:
-    if (!match(Op1, m_Zero()))
-      return {};
-    Pred = CmpInst::getSwappedPredicate(CmpInst::ICMP_UGT);
-    std::swap(Op0, Op1);
+    if (match(Op1, m_Zero())) {
+      Pred = CmpInst::getSwappedPredicate(CmpInst::ICMP_UGT);
+      std::swap(Op0, Op1);
+    } else {
+      IsNe = true;
+      Pred = CmpInst::ICMP_ULE;
+    }
     break;
   default:
     break;
@@ -571,7 +581,7 @@ ConstraintInfo::getConstraint(CmpInst::Predicate Pred, Value *Op0, Value *Op1,
   // subtracting all coefficients from B.
   ConstraintTy Res(
       SmallVector<int64_t, 8>(Value2Index.size() + NewVariables.size() + 1, 0),
-      IsSigned, IsEq);
+      IsSigned, IsEq, IsNe);
   // Collect variables that are known to be positive in all uses in the
   // constraint.
   DenseMap<Value *, bool> KnownNonNegativeVariables;
@@ -653,15 +663,16 @@ std::optional<bool>
 ConstraintTy::isImpliedBy(const ConstraintSystem &CS) const {
   bool IsConditionImplied = CS.isConditionImplied(Coefficients);
 
-  if (IsEq) {
+  if (IsEq || IsNe) {
     auto NegatedOrEqual = ConstraintSystem::negateOrEqual(Coefficients);
     bool IsNegatedOrEqualImplied =
         !NegatedOrEqual.empty() && CS.isConditionImplied(NegatedOrEqual);
 
-    // In order to check that `%a == %b` is true, we want to check that `%a >=
-    // %b` and `%a <= %b` must hold.
+    // In order to check that `%a == %b` is true (equality), both conditions `%a
+    // >= %b` and `%a <= %b` must hold true. When checking for equality (`IsEq`
+    // is true), we return true if they both hold, false in the other cases.
     if (IsConditionImplied && IsNegatedOrEqualImplied)
-      return true;
+      return IsEq;
 
     auto Negated = ConstraintSystem::negate(Coefficients);
     bool IsNegatedImplied = !Negated.empty() && CS.isConditionImplied(Negated);
@@ -670,10 +681,12 @@ ConstraintTy::isImpliedBy(const ConstraintSystem &CS) const {
     bool IsStrictLessThanImplied =
         !StrictLessThan.empty() && CS.isConditionImplied(StrictLessThan);
 
-    // In order to check that `%a == %b` is false, we want to check whether
-    // either `%a > %b` or `%a < %b` holds.
+    // In order to check that `%a != %b` is true (non-equality), either
+    // condition `%a > %b` or `%a < %b` must hold true. When checking for
+    // non-equality (`IsNe` is true), we return true if one of the two holds,
+    // false in the other cases.
     if (IsNegatedImplied || IsStrictLessThanImplied)
-      return false;
+      return IsNe;
 
     return std::nullopt;
   }
@@ -1141,7 +1154,9 @@ void ConstraintInfo::addFact(CmpInst::Predicate Pred, Value *A, Value *B,
   // hold.
   SmallVector<Value *> NewVariables;
   auto R = getConstraint(Pred, A, B, NewVariables);
-  if (!R.isValid(*this))
+
+  // TODO: Support non-equality for facts as well.
+  if (!R.isValid(*this) || R.isNe())
     return;
 
   LLVM_DEBUG(dbgs() << "Adding '" << Pred << " ";

diff  --git a/llvm/test/Transforms/ConstraintElimination/constants-unsigned-predicates.ll b/llvm/test/Transforms/ConstraintElimination/constants-unsigned-predicates.ll
index 102303c03ed47..99ff34e901fda 100644
--- a/llvm/test/Transforms/ConstraintElimination/constants-unsigned-predicates.ll
+++ b/llvm/test/Transforms/ConstraintElimination/constants-unsigned-predicates.ll
@@ -94,9 +94,9 @@ define i1 @test_ne() {
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[T_0:%.*]] = icmp ne i8 10, 11
 ; CHECK-NEXT:    [[F_0:%.*]] = icmp ne i8 10, 10
-; CHECK-NEXT:    [[RES_1:%.*]] = xor i1 [[T_0]], [[F_0]]
+; CHECK-NEXT:    [[RES_1:%.*]] = xor i1 true, false
 ; CHECK-NEXT:    [[T_1:%.*]] = icmp ne i8 10, 9
-; CHECK-NEXT:    [[RES_2:%.*]] = xor i1 [[RES_1]], [[T_1]]
+; CHECK-NEXT:    [[RES_2:%.*]] = xor i1 [[RES_1]], true
 ; CHECK-NEXT:    ret i1 [[RES_2]]
 ;
 entry:

diff  --git a/llvm/test/Transforms/ConstraintElimination/ne.ll b/llvm/test/Transforms/ConstraintElimination/ne.ll
index 2f6a54b7441ac..33fe6b6db6eb4 100644
--- a/llvm/test/Transforms/ConstraintElimination/ne.ll
+++ b/llvm/test/Transforms/ConstraintElimination/ne.ll
@@ -1,6 +1,8 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes
 ; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
 
+declare void @llvm.assume(i1)
+
 define i1 @test_eq_ne_0(i8 %a, i8 %b) {
 ; CHECK-LABEL: @test_eq_ne_0(
 ; CHECK-NEXT:  entry:
@@ -10,7 +12,7 @@ define i1 @test_eq_ne_0(i8 %a, i8 %b) {
 ; CHECK-NEXT:    [[F_1:%.*]] = icmp ne i8 [[A]], 0
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ne i8 [[A]], 1
 ; CHECK-NEXT:    [[C_2:%.*]] = icmp ne i8 [[A]], [[B:%.*]]
-; CHECK-NEXT:    [[RES_1:%.*]] = xor i1 false, [[C_1]]
+; CHECK-NEXT:    [[RES_1:%.*]] = xor i1 false, true
 ; CHECK-NEXT:    [[RES_2:%.*]] = xor i1 [[RES_1]], [[C_2]]
 ; CHECK-NEXT:    ret i1 [[RES_2]]
 ; CHECK:       else:
@@ -69,7 +71,7 @@ define i1 @test_ne_eq_0(i8 %a, i8 %b) {
 ; CHECK:       else:
 ; CHECK-NEXT:    [[F_1:%.*]] = icmp ne i8 [[A]], 0
 ; CHECK-NEXT:    [[C_7:%.*]] = icmp ne i8 [[A]], 1
-; CHECK-NEXT:    [[RES_9:%.*]] = xor i1 false, [[C_7]]
+; CHECK-NEXT:    [[RES_9:%.*]] = xor i1 false, true
 ; CHECK-NEXT:    [[C_8:%.*]] = icmp ne i8 [[A]], [[B]]
 ; CHECK-NEXT:    [[RES_10:%.*]] = xor i1 [[RES_9]], [[C_8]]
 ; CHECK-NEXT:    [[C_9:%.*]] = icmp eq i8 [[A]], [[B]]
@@ -156,7 +158,7 @@ define i1 @test_eq_ne_1(i8 %a, i8 %b) {
 ; CHECK-NEXT:    [[F_1:%.*]] = icmp ne i8 [[A]], 0
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ne i8 [[A]], 1
 ; CHECK-NEXT:    [[C_2:%.*]] = icmp ne i8 [[A]], [[B:%.*]]
-; CHECK-NEXT:    [[RES_1:%.*]] = xor i1 true, [[C_1]]
+; CHECK-NEXT:    [[RES_1:%.*]] = xor i1 true, false
 ; CHECK-NEXT:    [[RES_2:%.*]] = xor i1 [[RES_1]], [[C_2]]
 ; CHECK-NEXT:    ret i1 [[RES_2]]
 ; CHECK:       else:
@@ -215,7 +217,7 @@ define i1 @test_ne_eq_1(i8 %a, i8 %b) {
 ; CHECK:       else:
 ; CHECK-NEXT:    [[T_2:%.*]] = icmp ne i8 [[A]], 0
 ; CHECK-NEXT:    [[C_9:%.*]] = icmp ne i8 [[A]], 1
-; CHECK-NEXT:    [[RES_9:%.*]] = xor i1 true, [[C_9]]
+; CHECK-NEXT:    [[RES_9:%.*]] = xor i1 true, false
 ; CHECK-NEXT:    [[C_10:%.*]] = icmp ne i8 [[A]], [[B]]
 ; CHECK-NEXT:    [[RES_10:%.*]] = xor i1 [[RES_9]], [[C_10]]
 ; CHECK-NEXT:    [[C_11:%.*]] = icmp eq i8 [[A]], [[B]]
@@ -292,3 +294,157 @@ else:
 
   ret i1 %res.16
 }
+
+define i1 @assume_b_plus_1_ult_a(i64 %a, i64 %b) {
+; CHECK-LABEL: @assume_b_plus_1_ult_a(
+; CHECK-NEXT:    [[TMP1:%.*]] = add nuw i64 [[B:%.*]], 1
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ult i64 [[TMP1]], [[A:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[TMP2]])
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp ne i64 [[A]], [[B]]
+; CHECK-NEXT:    ret i1 true
+;
+  %1 = add nuw i64 %b, 1
+  %2 = icmp ult i64 %1, %a
+  tail call void @llvm.assume(i1 %2)
+  %3 = icmp ne i64 %a, %b
+  ret i1 %3
+}
+
+define i1 @assume_a_gt_b_and_b_ge_c(i64 %a, i64 %b, i64 %c) {
+; CHECK-LABEL: @assume_a_gt_b_and_b_ge_c(
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ugt i64 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[TMP1]])
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp uge i64 [[B]], [[C:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[TMP2]])
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp ne i64 [[A]], [[C]]
+; CHECK-NEXT:    ret i1 true
+;
+  %1 = icmp ugt i64 %a, %b
+  tail call void @llvm.assume(i1 %1)
+  %2 = icmp uge i64 %b, %c
+  tail call void @llvm.assume(i1 %2)
+  %3 = icmp ne i64 %a, %c
+  ret i1 %3
+}
+
+define i1 @assume_a_ne_b_and_b_ne_c(i64 %a, i64 %b, i64 %c) {
+; CHECK-LABEL: @assume_a_ne_b_and_b_ne_c(
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ne i64 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[TMP1]])
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp ne i64 [[B]], [[C:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[TMP2]])
+; CHECK-NEXT:    [[TMP3:%.*]] = icmp ne i64 [[A]], [[C]]
+; CHECK-NEXT:    ret i1 [[TMP3]]
+;
+  %1 = icmp ne i64 %a, %b
+  tail call void @llvm.assume(i1 %1)
+  %2 = icmp ne i64 %b, %c
+  tail call void @llvm.assume(i1 %2)
+  %3 = icmp ne i64 %a, %c
+  ret i1 %3
+}
+
+define i1 @assume_1a(i64 %a, i64 %b) {
+; CHECK-LABEL: @assume_1a(
+; CHECK-NEXT:    [[NE:%.*]] = icmp ne i64 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[NE]])
+; CHECK-NEXT:    [[RET:%.*]] = icmp ugt i64 [[A]], [[B]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %ne = icmp ne i64 %a, %b
+  tail call void @llvm.assume(i1 %ne)
+  %ret = icmp ugt i64 %a, %b
+  ret i1 %ret
+}
+
+define i1 @assume_1b(i64 %a, i64 %b) {
+; CHECK-LABEL: @assume_1b(
+; CHECK-NEXT:    [[NE:%.*]] = icmp ne i64 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[NE]])
+; CHECK-NEXT:    [[RET:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %ne = icmp ne i64 %a, %b
+  tail call void @llvm.assume(i1 %ne)
+  %ret = icmp uge i64 %a, %b
+  ret i1 %ret
+}
+
+define i1 @assume_2a(i64 %a, i64 %b) {
+; CHECK-LABEL: @assume_2a(
+; CHECK-NEXT:    [[NE:%.*]] = icmp ne i64 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[NE]])
+; CHECK-NEXT:    [[RET:%.*]] = icmp ult i64 [[A]], [[B]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %ne = icmp ne i64 %a, %b
+  tail call void @llvm.assume(i1 %ne)
+  %ret = icmp ult i64 %a, %b
+  ret i1 %ret
+}
+
+define i1 @assume_2b(i64 %a, i64 %b) {
+; CHECK-LABEL: @assume_2b(
+; CHECK-NEXT:    [[NE:%.*]] = icmp ne i64 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[NE]])
+; CHECK-NEXT:    [[RET:%.*]] = icmp ule i64 [[A]], [[B]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %ne = icmp ne i64 %a, %b
+  tail call void @llvm.assume(i1 %ne)
+  %ret = icmp ule i64 %a, %b
+  ret i1 %ret
+}
+
+; TODO: extend to support signed comparisons
+define i1 @assume_3a(i64 %a, i64 %b) {
+; CHECK-LABEL: @assume_3a(
+; CHECK-NEXT:    [[NE:%.*]] = icmp ne i64 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[NE]])
+; CHECK-NEXT:    [[RET:%.*]] = icmp sgt i64 [[A]], [[B]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %ne = icmp ne i64 %a, %b
+  tail call void @llvm.assume(i1 %ne)
+  %ret = icmp sgt i64 %a, %b
+  ret i1 %ret
+}
+
+define i1 @assume_3b(i64 %a, i64 %b) {
+; CHECK-LABEL: @assume_3b(
+; CHECK-NEXT:    [[NE:%.*]] = icmp ne i64 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[NE]])
+; CHECK-NEXT:    [[RET:%.*]] = icmp sge i64 [[A]], [[B]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %ne = icmp ne i64 %a, %b
+  tail call void @llvm.assume(i1 %ne)
+  %ret = icmp sge i64 %a, %b
+  ret i1 %ret
+}
+
+define i1 @assume_4a(i64 %a, i64 %b) {
+; CHECK-LABEL: @assume_4a(
+; CHECK-NEXT:    [[NE:%.*]] = icmp ne i64 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[NE]])
+; CHECK-NEXT:    [[RET:%.*]] = icmp slt i64 [[A]], [[B]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %ne = icmp ne i64 %a, %b
+  tail call void @llvm.assume(i1 %ne)
+  %ret = icmp slt i64 %a, %b
+  ret i1 %ret
+}
+
+define i1 @assume_4b(i64 %a, i64 %b) {
+; CHECK-LABEL: @assume_4b(
+; CHECK-NEXT:    [[NE:%.*]] = icmp ne i64 [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    tail call void @llvm.assume(i1 [[NE]])
+; CHECK-NEXT:    [[RET:%.*]] = icmp sle i64 [[A]], [[B]]
+; CHECK-NEXT:    ret i1 [[RET]]
+;
+  %ne = icmp ne i64 %a, %b
+  tail call void @llvm.assume(i1 %ne)
+  %ret = icmp sle i64 %a, %b
+  ret i1 %ret
+}


        


More information about the llvm-commits mailing list