[llvm] [ConstraintElim] Handle (X & Y) <s 0 as X <s 0 && Y <s 0. (PR #210033)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 16 04:10:18 PDT 2026


https://github.com/fhahn created https://github.com/llvm/llvm-project/pull/210033

InstCombine canonicalizes X <s 0 && Y <s 0 as (X & Y) <s 0. Teach
ConstraintElimination to recover the signed-negative information by
looking through compares of binary ANDs.

Alive2 Proof: https://alive2.llvm.org/ce/z/R6DWqD

Depends on https://github.com/llvm/llvm-project/pull/209743 (included in PR)

>From c933a39b1444350116f67d52a0aaa614ace66698 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Tue, 14 Jul 2026 19:24:46 +0100
Subject: [PATCH 1/5] [ConstraintElim] Handle (X | Y) >=s 0 as X >=s 0 && Y >=s
 0.

InstCombine canonicalizes X >=s 0 && Y >=s 0 as (X | Y) >=s 0. Teach
ConstraintElimination to recover the signed-positive information by
looking through compares of binary ORs.

Alive2 Proof: https://alive2.llvm.org/ce/z/ACu4gs
---
 .../Scalar/ConstraintElimination.cpp          | 24 +++++++++++++++++++
 .../ConstraintElimination/or-non-negative.ll  |  9 +++----
 .../constraint-eliminiation-interactions.ll   | 12 +++-------
 3 files changed, 30 insertions(+), 15 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index b244445c6e51f..5c064751956e2 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1996,6 +1996,30 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
                                      DFSInStack);
       }
 
+      // (X | Y) >=s 0 implies X >=s 0 and Y >=s 0, because the sign bit of an
+      // OR is the OR of the operand sign bits. Look through this
+      // canonicalization by InstCombine. disjunct so it is available to the
+      // solver.
+      if ((Pred == CmpInst::ICMP_SGE && match(B, m_Zero())) ||
+          (Pred == CmpInst::ICMP_SGT && match(B, m_AllOnes()))) {
+        SmallVector<Value *> OrWorklist = {A};
+        SmallPtrSet<Value *, 4> SeenOr;
+        Value *X, *Y;
+        while (!OrWorklist.empty()) {
+          Value *Cur = OrWorklist.pop_back_val();
+          if (!match(Cur, m_Or(m_Value(X), m_Value(Y))))
+            continue;
+          for (Value *Op : {X, Y}) {
+            if (!SeenOr.insert(Op).second)
+              continue;
+            OrWorklist.push_back(Op);
+            Info.addFact(CmpInst::ICMP_SGE, Op,
+                         ConstantInt::getNullValue(Op->getType()), CB.NumIn,
+                         CB.NumOut, DFSInStack);
+          }
+        }
+      }
+
       if (ReproducerModule && DFSInStack.size() > ReproducerCondStack.size()) {
         // Add dummy entries to ReproducerCondStack to keep it in sync with
         // DFSInStack.
diff --git a/llvm/test/Transforms/ConstraintElimination/or-non-negative.ll b/llvm/test/Transforms/ConstraintElimination/or-non-negative.ll
index b8413acbbf2cf..92b80e5459499 100644
--- a/llvm/test/Transforms/ConstraintElimination/or-non-negative.ll
+++ b/llvm/test/Transforms/ConstraintElimination/or-non-negative.ll
@@ -17,8 +17,7 @@ define i1 @or_non_negative_bounds(i64 %a, i64 %b, i64 %c, i64 %d) {
 ; CHECK-NEXT:    br i1 [[AND_3]], label %[[THEN:.*]], label %[[ELSE:.*]]
 ; CHECK:       [[THEN]]:
 ; CHECK-NEXT:    [[IDX:%.*]] = add nsw i64 [[D]], [[B]]
-; CHECK-NEXT:    [[RES:%.*]] = icmp ult i64 [[IDX]], [[A]]
-; CHECK-NEXT:    ret i1 [[RES]]
+; CHECK-NEXT:    ret i1 true
 ; CHECK:       [[ELSE]]:
 ; CHECK-NEXT:    ret i1 false
 ;
@@ -50,8 +49,7 @@ define i1 @or_non_negative_sge(i64 %a, i64 %b) {
 ; CHECK-NEXT:    [[C:%.*]] = icmp sge i64 [[O]], 0
 ; CHECK-NEXT:    br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
 ; CHECK:       [[THEN]]:
-; CHECK-NEXT:    [[RES:%.*]] = icmp sge i64 [[B]], 0
-; CHECK-NEXT:    ret i1 [[RES]]
+; CHECK-NEXT:    ret i1 true
 ; CHECK:       [[ELSE]]:
 ; CHECK-NEXT:    ret i1 false
 ;
@@ -76,8 +74,7 @@ define i1 @or_non_negative_nested(i64 %a, i64 %b, i64 %c) {
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp sgt i64 [[O2]], -1
 ; CHECK-NEXT:    br i1 [[CMP]], label %[[THEN:.*]], label %[[ELSE:.*]]
 ; CHECK:       [[THEN]]:
-; CHECK-NEXT:    [[RES:%.*]] = icmp sge i64 [[C]], 0
-; CHECK-NEXT:    ret i1 [[RES]]
+; CHECK-NEXT:    ret i1 true
 ; CHECK:       [[ELSE]]:
 ; CHECK-NEXT:    ret i1 false
 ;
diff --git a/llvm/test/Transforms/PhaseOrdering/constraint-eliminiation-interactions.ll b/llvm/test/Transforms/PhaseOrdering/constraint-eliminiation-interactions.ll
index a5a1fcb97541a..375649d8790e9 100644
--- a/llvm/test/Transforms/PhaseOrdering/constraint-eliminiation-interactions.ll
+++ b/llvm/test/Transforms/PhaseOrdering/constraint-eliminiation-interactions.ll
@@ -70,16 +70,10 @@ define noundef i64 @or_sge_canonicalization(ptr noundef %p, i64 noundef %a, i64
 ; CHECK-NEXT:    [[TMP2:%.*]] = icmp sgt i64 [[TMP1]], -1
 ; CHECK-NEXT:    [[AND_3:%.*]] = and i1 [[CMP_3]], [[TMP2]]
 ; CHECK-NEXT:    [[AND_4:%.*]] = and i1 [[CMP_5]], [[AND_3]]
-; CHECK-NEXT:    br i1 [[AND_4]], label %[[THEN:.*]], label %[[ELSE:.*]]
-; CHECK:       [[THEN]]:
-; CHECK-NEXT:    [[IDX:%.*]] = add nuw nsw i64 [[D]], [[B]]
-; CHECK-NEXT:    [[OOB_NOT:%.*]] = icmp ult i64 [[IDX]], [[A]]
-; CHECK-NEXT:    br i1 [[OOB_NOT]], label %[[CONT:.*]], label %[[TRAP:.*]]
-; CHECK:       [[TRAP]]:
-; CHECK-NEXT:    tail call void @llvm.trap()
-; CHECK-NEXT:    unreachable
+; CHECK-NEXT:    br i1 [[AND_4]], label %[[CONT:.*]], label %[[ELSE:.*]]
 ; CHECK:       [[CONT]]:
-; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [8 x i8], ptr [[P]], i64 [[IDX]]
+; CHECK-NEXT:    [[TMP3:%.*]] = getelementptr inbounds nuw [8 x i8], ptr [[P]], i64 [[D]]
+; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [8 x i8], ptr [[TMP3]], i64 [[B]]
 ; CHECK-NEXT:    [[V:%.*]] = load i64, ptr [[ARRAYIDX]], align 8
 ; CHECK-NEXT:    br label %[[ELSE]]
 ; CHECK:       [[ELSE]]:

>From 1577efba165bcc5bd341fe5c6982d59f604edff2 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Wed, 15 Jul 2026 14:50:53 +0100
Subject: [PATCH 2/5] !fixup strip stray comment, remove sge 0 support

---
 llvm/lib/Transforms/Scalar/ConstraintElimination.cpp      | 8 +++-----
 .../Transforms/ConstraintElimination/or-non-negative.ll   | 3 ++-
 2 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 5c064751956e2..12a0792fd4148 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1996,12 +1996,10 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
                                      DFSInStack);
       }
 
-      // (X | Y) >=s 0 implies X >=s 0 and Y >=s 0, because the sign bit of an
+      // (X | Y) >s -1 implies X >s -1 and Y >s -1, because the sign bit of an
       // OR is the OR of the operand sign bits. Look through this
-      // canonicalization by InstCombine. disjunct so it is available to the
-      // solver.
-      if ((Pred == CmpInst::ICMP_SGE && match(B, m_Zero())) ||
-          (Pred == CmpInst::ICMP_SGT && match(B, m_AllOnes()))) {
+      // canonicalization by InstCombine.
+      if (Pred == CmpInst::ICMP_SGT && match(B, m_AllOnes())) {
         SmallVector<Value *> OrWorklist = {A};
         SmallPtrSet<Value *, 4> SeenOr;
         Value *X, *Y;
diff --git a/llvm/test/Transforms/ConstraintElimination/or-non-negative.ll b/llvm/test/Transforms/ConstraintElimination/or-non-negative.ll
index 92b80e5459499..138ea41198b91 100644
--- a/llvm/test/Transforms/ConstraintElimination/or-non-negative.ll
+++ b/llvm/test/Transforms/ConstraintElimination/or-non-negative.ll
@@ -49,7 +49,8 @@ define i1 @or_non_negative_sge(i64 %a, i64 %b) {
 ; CHECK-NEXT:    [[C:%.*]] = icmp sge i64 [[O]], 0
 ; CHECK-NEXT:    br i1 [[C]], label %[[THEN:.*]], label %[[ELSE:.*]]
 ; CHECK:       [[THEN]]:
-; CHECK-NEXT:    ret i1 true
+; CHECK-NEXT:    [[RES:%.*]] = icmp sge i64 [[B]], 0
+; CHECK-NEXT:    ret i1 [[RES]]
 ; CHECK:       [[ELSE]]:
 ; CHECK-NEXT:    ret i1 false
 ;

>From f8e81cd7c33189d63ae02b8bdcad2ca0aa6af7a3 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 16 Jul 2026 11:58:29 +0100
Subject: [PATCH 3/5] !fixup use Pred instead of spelling out predicate.

---
 llvm/lib/Transforms/Scalar/ConstraintElimination.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 12a0792fd4148..f1f800cd69d60 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -2011,9 +2011,7 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
             if (!SeenOr.insert(Op).second)
               continue;
             OrWorklist.push_back(Op);
-            Info.addFact(CmpInst::ICMP_SGE, Op,
-                         ConstantInt::getNullValue(Op->getType()), CB.NumIn,
-                         CB.NumOut, DFSInStack);
+            Info.addFact(Pred, Op, B, CB.NumIn, CB.NumOut, DFSInStack);
           }
         }
       }

>From 6763d1efe2ec85c7e852990b320c01cf61f8b87d Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Wed, 15 Jul 2026 21:26:11 +0100
Subject: [PATCH 4/5] [ConstraintElim] Precommit tests.

---
 .../ConstraintElimination/and-negative.ll     | 89 +++++++++++++++++++
 .../constraint-eliminiation-interactions.ll   | 54 +++++++++++
 2 files changed, 143 insertions(+)
 create mode 100644 llvm/test/Transforms/ConstraintElimination/and-negative.ll

diff --git a/llvm/test/Transforms/ConstraintElimination/and-negative.ll b/llvm/test/Transforms/ConstraintElimination/and-negative.ll
new file mode 100644
index 0000000000000..954eb4c81f82c
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/and-negative.ll
@@ -0,0 +1,89 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
+
+; (%a & %b) <s 0 implies both %a <s 0 and %b <s 0.
+define i1 @and_negative_slt(i64 %a, i64 %b) {
+; CHECK-LABEL: define i1 @and_negative_slt(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:    [[O:%.*]] = and i64 [[A]], [[B]]
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[O]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[RES:%.*]] = icmp slt i64 [[B]], 0
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %o = and i64 %a, %b
+  %c = icmp slt i64 %o, 0
+  call void @llvm.assume(i1 %c)
+  %res = icmp slt i64 %b, 0
+  ret i1 %res
+}
+
+; sle -1 form of the same fact.
+define i1 @and_negative_sle(i64 %a, i64 %b) {
+; CHECK-LABEL: define i1 @and_negative_sle(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:    [[O:%.*]] = and i64 [[A]], [[B]]
+; CHECK-NEXT:    [[C:%.*]] = icmp sle i64 [[O]], -1
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[RES:%.*]] = icmp slt i64 [[A]], 0
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %o = and i64 %a, %b
+  %c = icmp sle i64 %o, -1
+  call void @llvm.assume(i1 %c)
+  %res = icmp slt i64 %a, 0
+  ret i1 %res
+}
+
+; Nested ANDs: (a & b & c) <s 0 implies c <s 0.
+define i1 @and_negative_nested(i64 %a, i64 %b, i64 %c) {
+; CHECK-LABEL: define i1 @and_negative_nested(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]], i64 [[C:%.*]]) {
+; CHECK-NEXT:    [[O1:%.*]] = and i64 [[A]], [[B]]
+; CHECK-NEXT:    [[O2:%.*]] = and i64 [[O1]], [[C]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i64 [[O2]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[CMP]])
+; CHECK-NEXT:    [[RES:%.*]] = icmp slt i64 [[C]], 0
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %o1 = and i64 %a, %b
+  %o2 = and i64 %o1, %c
+  %cmp = icmp slt i64 %o2, 0
+  call void @llvm.assume(i1 %cmp)
+  %res = icmp slt i64 %c, 0
+  ret i1 %res
+}
+
+; Negative: (a & b) <s 0 does not imply any ordering between a and b.
+define i1 @and_negative_no_order(i64 %a, i64 %b) {
+; CHECK-LABEL: define i1 @and_negative_no_order(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:    [[O:%.*]] = and i64 [[A]], [[B]]
+; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[O]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[RES:%.*]] = icmp sge i64 [[A]], [[B]]
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %o = and i64 %a, %b
+  %c = icmp slt i64 %o, 0
+  call void @llvm.assume(i1 %c)
+  %res = icmp sge i64 %a, %b
+  ret i1 %res
+}
+
+; The non-negative fact does not imply the operands are negative.
+define i1 @and_non_negative(i64 %a, i64 %b) {
+; CHECK-LABEL: define i1 @and_non_negative(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:    [[O:%.*]] = and i64 [[A]], [[B]]
+; CHECK-NEXT:    [[C:%.*]] = icmp sge i64 [[O]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
+; CHECK-NEXT:    [[RES:%.*]] = icmp slt i64 [[A]], 0
+; CHECK-NEXT:    ret i1 [[RES]]
+;
+  %o = and i64 %a, %b
+  %c = icmp sge i64 %o, 0
+  call void @llvm.assume(i1 %c)
+  %res = icmp slt i64 %a, 0
+  ret i1 %res
+}
diff --git a/llvm/test/Transforms/PhaseOrdering/constraint-eliminiation-interactions.ll b/llvm/test/Transforms/PhaseOrdering/constraint-eliminiation-interactions.ll
index 375649d8790e9..91ac853a1d593 100644
--- a/llvm/test/Transforms/PhaseOrdering/constraint-eliminiation-interactions.ll
+++ b/llvm/test/Transforms/PhaseOrdering/constraint-eliminiation-interactions.ll
@@ -111,3 +111,57 @@ else:
   %res = phi i64 [ %v, %cont ], [ 0, %entry ]
   ret i64 %res
 }
+
+; InstCombine canonicalizes the sign checks %b <s 0, %c <s 0 and %d <s 0 into a
+; single (%b & %c & %d) <s 0 test.
+define noundef i64 @and_slt_canonicalization(ptr noundef %p, i64 noundef %a, i64 noundef %b, i64 noundef %c, i64 noundef %d) {
+; CHECK-LABEL: define noundef i64 @and_slt_canonicalization(
+; CHECK-SAME: ptr nofree noundef readonly captures(none) [[P:%.*]], i64 noundef [[A:%.*]], i64 noundef [[B:%.*]], i64 noundef [[C:%.*]], i64 noundef [[D:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[CMP_3:%.*]] = icmp slt i64 [[A]], [[B]]
+; CHECK-NEXT:    [[TMP0:%.*]] = and i64 [[C]], [[B]]
+; CHECK-NEXT:    [[TMP1:%.*]] = and i64 [[TMP0]], [[D]]
+; CHECK-NEXT:    [[TMP2:%.*]] = icmp slt i64 [[TMP1]], 0
+; CHECK-NEXT:    [[AND_3:%.*]] = and i1 [[CMP_3]], [[TMP2]]
+; CHECK-NEXT:    br i1 [[AND_3]], label %[[CONT:.*]], label %[[ELSE:.*]]
+; CHECK:       [[CONT]]:
+; CHECK-NEXT:    [[OOB:%.*]] = icmp sgt i64 [[A]], -1
+; CHECK-NEXT:    br i1 [[OOB]], label %[[TRAP:.*]], label %[[CONT1:.*]]
+; CHECK:       [[TRAP]]:
+; CHECK-NEXT:    tail call void @llvm.trap()
+; CHECK-NEXT:    unreachable
+; CHECK:       [[CONT1]]:
+; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [8 x i8], ptr [[P]], i64 [[D]]
+; CHECK-NEXT:    [[V:%.*]] = load i64, ptr [[ARRAYIDX]], align 8
+; CHECK-NEXT:    br label %[[ELSE]]
+; CHECK:       [[ELSE]]:
+; CHECK-NEXT:    [[RES:%.*]] = phi i64 [ [[V]], %[[CONT1]] ], [ 0, %[[ENTRY]] ]
+; CHECK-NEXT:    ret i64 [[RES]]
+;
+entry:
+  %cmp.1 = icmp slt i64 %b, 0
+  %cmp.2 = icmp slt i64 %c, 0
+  %cmp.3 = icmp slt i64 %a, %b
+  %cmp.4 = icmp slt i64 %d, 0
+  %and.1 = and i1 %cmp.1, %cmp.2
+  %and.2 = and i1 %and.1, %cmp.3
+  %and.3 = and i1 %and.2, %cmp.4
+  br i1 %and.3, label %then, label %else
+
+then:
+  %oob = icmp sge i64 %a, 0
+  br i1 %oob, label %trap, label %cont
+
+trap:
+  call void @llvm.trap()
+  br label %cont
+
+cont:
+  %arrayidx = getelementptr inbounds i64, ptr %p, i64 %d
+  %v = load i64, ptr %arrayidx, align 8
+  br label %else
+
+else:
+  %res = phi i64 [ %v, %cont ], [ 0, %entry ]
+  ret i64 %res
+}

>From 86d3f32cf2334a73f319bf9d0c7b514116c664ec Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 16 Jul 2026 11:23:34 +0100
Subject: [PATCH 5/5] [ConstraintElim] Handle (X & Y) <s 0 as X <s 0 && Y <s 0.

InstCombine canonicalizes X <s 0 && Y <s 0 as (X & Y) <s 0. Teach
ConstraintElimination to recover the signed-negative information by
looking through compares of binary ANDs.

Alive2 Proof: https://alive2.llvm.org/ce/z/R6DWqD
---
 .../Scalar/ConstraintElimination.cpp          | 29 +++++++++++--------
 .../ConstraintElimination/and-negative.ll     |  6 ++--
 .../constraint-eliminiation-interactions.ll   | 10 ++-----
 3 files changed, 21 insertions(+), 24 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index f1f800cd69d60..cafec46e6345e 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1997,20 +1997,25 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
       }
 
       // (X | Y) >s -1 implies X >s -1 and Y >s -1, because the sign bit of an
-      // OR is the OR of the operand sign bits. Look through this
-      // canonicalization by InstCombine.
-      if (Pred == CmpInst::ICMP_SGT && match(B, m_AllOnes())) {
-        SmallVector<Value *> OrWorklist = {A};
-        SmallPtrSet<Value *, 4> SeenOr;
-        Value *X, *Y;
-        while (!OrWorklist.empty()) {
-          Value *Cur = OrWorklist.pop_back_val();
-          if (!match(Cur, m_Or(m_Value(X), m_Value(Y))))
+      // OR is the OR of the operand sign bits. Similarly, (X & Y) <s 0 implies
+      // X <s 0 and Y <s 0. Look through these canonical forms produced by
+      // InstCombine so the sign facts on the operands are available to the
+      // solver.
+      if ((Pred == CmpInst::ICMP_SGT && match(B, m_AllOnes())) ||
+          (Pred == CmpInst::ICMP_SLT && match(B, m_Zero()))) {
+        unsigned Opc =
+            Pred == CmpInst::ICMP_SGT ? Instruction::Or : Instruction::And;
+        SmallVector<Value *> Worklist = {A};
+        SmallPtrSet<Value *, 4> Seen;
+        while (!Worklist.empty()) {
+          Value *Cur = Worklist.pop_back_val();
+          auto *BO = dyn_cast<BinaryOperator>(Cur);
+          if (!BO || BO->getOpcode() != Opc)
             continue;
-          for (Value *Op : {X, Y}) {
-            if (!SeenOr.insert(Op).second)
+          for (Value *Op : {BO->getOperand(0), BO->getOperand(1)}) {
+            if (!Seen.insert(Op).second)
               continue;
-            OrWorklist.push_back(Op);
+            Worklist.push_back(Op);
             Info.addFact(Pred, Op, B, CB.NumIn, CB.NumOut, DFSInStack);
           }
         }
diff --git a/llvm/test/Transforms/ConstraintElimination/and-negative.ll b/llvm/test/Transforms/ConstraintElimination/and-negative.ll
index 954eb4c81f82c..1bc94de7969cf 100644
--- a/llvm/test/Transforms/ConstraintElimination/and-negative.ll
+++ b/llvm/test/Transforms/ConstraintElimination/and-negative.ll
@@ -8,8 +8,7 @@ define i1 @and_negative_slt(i64 %a, i64 %b) {
 ; CHECK-NEXT:    [[O:%.*]] = and i64 [[A]], [[B]]
 ; CHECK-NEXT:    [[C:%.*]] = icmp slt i64 [[O]], 0
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[C]])
-; CHECK-NEXT:    [[RES:%.*]] = icmp slt i64 [[B]], 0
-; CHECK-NEXT:    ret i1 [[RES]]
+; CHECK-NEXT:    ret i1 true
 ;
   %o = and i64 %a, %b
   %c = icmp slt i64 %o, 0
@@ -43,8 +42,7 @@ define i1 @and_negative_nested(i64 %a, i64 %b, i64 %c) {
 ; CHECK-NEXT:    [[O2:%.*]] = and i64 [[O1]], [[C]]
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i64 [[O2]], 0
 ; CHECK-NEXT:    call void @llvm.assume(i1 [[CMP]])
-; CHECK-NEXT:    [[RES:%.*]] = icmp slt i64 [[C]], 0
-; CHECK-NEXT:    ret i1 [[RES]]
+; CHECK-NEXT:    ret i1 true
 ;
   %o1 = and i64 %a, %b
   %o2 = and i64 %o1, %c
diff --git a/llvm/test/Transforms/PhaseOrdering/constraint-eliminiation-interactions.ll b/llvm/test/Transforms/PhaseOrdering/constraint-eliminiation-interactions.ll
index 91ac853a1d593..ce7485ba98cfb 100644
--- a/llvm/test/Transforms/PhaseOrdering/constraint-eliminiation-interactions.ll
+++ b/llvm/test/Transforms/PhaseOrdering/constraint-eliminiation-interactions.ll
@@ -116,20 +116,14 @@ else:
 ; single (%b & %c & %d) <s 0 test.
 define noundef i64 @and_slt_canonicalization(ptr noundef %p, i64 noundef %a, i64 noundef %b, i64 noundef %c, i64 noundef %d) {
 ; CHECK-LABEL: define noundef i64 @and_slt_canonicalization(
-; CHECK-SAME: ptr nofree noundef readonly captures(none) [[P:%.*]], i64 noundef [[A:%.*]], i64 noundef [[B:%.*]], i64 noundef [[C:%.*]], i64 noundef [[D:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
+; CHECK-SAME: ptr nofree noundef readonly captures(none) [[P:%.*]], i64 noundef [[A:%.*]], i64 noundef [[B:%.*]], i64 noundef [[C:%.*]], i64 noundef [[D:%.*]]) local_unnamed_addr #[[ATTR1]] {
 ; CHECK-NEXT:  [[ENTRY:.*]]:
 ; CHECK-NEXT:    [[CMP_3:%.*]] = icmp slt i64 [[A]], [[B]]
 ; CHECK-NEXT:    [[TMP0:%.*]] = and i64 [[C]], [[B]]
 ; CHECK-NEXT:    [[TMP1:%.*]] = and i64 [[TMP0]], [[D]]
 ; CHECK-NEXT:    [[TMP2:%.*]] = icmp slt i64 [[TMP1]], 0
 ; CHECK-NEXT:    [[AND_3:%.*]] = and i1 [[CMP_3]], [[TMP2]]
-; CHECK-NEXT:    br i1 [[AND_3]], label %[[CONT:.*]], label %[[ELSE:.*]]
-; CHECK:       [[CONT]]:
-; CHECK-NEXT:    [[OOB:%.*]] = icmp sgt i64 [[A]], -1
-; CHECK-NEXT:    br i1 [[OOB]], label %[[TRAP:.*]], label %[[CONT1:.*]]
-; CHECK:       [[TRAP]]:
-; CHECK-NEXT:    tail call void @llvm.trap()
-; CHECK-NEXT:    unreachable
+; CHECK-NEXT:    br i1 [[AND_3]], label %[[CONT1:.*]], label %[[ELSE:.*]]
 ; CHECK:       [[CONT1]]:
 ; CHECK-NEXT:    [[ARRAYIDX:%.*]] = getelementptr inbounds [8 x i8], ptr [[P]], i64 [[D]]
 ; CHECK-NEXT:    [[V:%.*]] = load i64, ptr [[ARRAYIDX]], align 8



More information about the llvm-commits mailing list