[llvm-branch-commits] [llvm] [SCEV] Derive x >=s 0 from x <u y and y <=u 2^(n-1) (PR #231631)

Dmitry Makogon via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Oct 22 04:45:58 PDT 2026


https://github.com/d-makogon updated https://github.com/llvm/llvm-project/pull/231631

>From e037d7d51c3fe8569bc3a438445c9b9a50e9e020 Mon Sep 17 00:00:00 2001
From: Dmitrii Makogon <dmakogon at azul.com>
Date: Thu, 22 Oct 2026 11:25:52 +0000
Subject: [PATCH 1/4] [SCEV] Try other implications when sign-flipped ones fail

SCEV's isImpliedCondBalancedTypes tries to prove an implication
through several rules. There are rules for when a predicate to prove
is sign-flipped from the found predicate, and also there are other
rules, which, for example, try to prove the implication through
ranges.

Before, if a proof for a sign-flipped rule implication returned false,
we wouldn't check other rules and just bail out with false result too.

Now make those rules fall-through - if one rule couldn't be proven,
try other rules in the function.

An example query is x - 5 <s 5 with x <u 10 known.
The implication "a <u b and b >=s 0 --> a <s b" matches it,
but x - 5 <u 5 with x <u 10 known does not hold (e.g. x = 0),
so SCEV gave up.

Now it goes on to trying to prove the query from ranges.
x <u 10 gives x in [0, 10), so x - 5 is in [-5, 5), and x - 5 <s 5
holds.

Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
 llvm/lib/Analysis/ScalarEvolution.cpp         | 23 +++++++++++--------
 .../implied-cond-different-signedness.ll      |  4 +---
 2 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index fed69ea33fab74..0b53f7906e6ae9 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -12242,8 +12242,9 @@ bool ScalarEvolution::isImpliedCondBalancedTypes(
   if (IsSignFlippedPredicate(Pred, FoundPred)) {
     // Unsigned comparison is the same as signed comparison when both the
     // operands are non-negative or negative.
-    if (haveSameSign(FoundLHS, FoundRHS))
-      return isImpliedCondOperands(Pred, LHS, RHS, FoundLHS, FoundRHS, CtxI);
+    if (haveSameSign(FoundLHS, FoundRHS) &&
+        isImpliedCondOperands(Pred, LHS, RHS, FoundLHS, FoundRHS, CtxI))
+      return true;
     // Create local copies that we can freely swap and canonicalize our
     // conditions to "le/lt".
     CmpPredicate CanonicalPred = Pred, CanonicalFoundPred = FoundPred;
@@ -12260,20 +12261,22 @@ bool ScalarEvolution::isImpliedCondBalancedTypes(
     assert((ICmpInst::isLT(CanonicalFoundPred) ||
             ICmpInst::isLE(CanonicalFoundPred)) &&
            "Must be!");
-    if (ICmpInst::isSigned(CanonicalPred) && isKnownNonNegative(CanonicalRHS))
+    if (ICmpInst::isSigned(CanonicalPred) && isKnownNonNegative(CanonicalRHS)) {
       // Use implication:
       // x <u y && y >=s 0 --> x <s y.
       // If we can prove the left part, the right part is also proven.
-      return isImpliedCondOperands(CanonicalFoundPred, CanonicalLHS,
-                                   CanonicalRHS, CanonicalFoundLHS,
-                                   CanonicalFoundRHS);
-    if (ICmpInst::isUnsigned(CanonicalPred) && isKnownNegative(CanonicalRHS))
+      if (isImpliedCondOperands(CanonicalFoundPred, CanonicalLHS, CanonicalRHS,
+                                CanonicalFoundLHS, CanonicalFoundRHS))
+        return true;
+    }
+    if (ICmpInst::isUnsigned(CanonicalPred) && isKnownNegative(CanonicalRHS)) {
       // Use implication:
       // x <s y && y <s 0 --> x <u y.
       // If we can prove the left part, the right part is also proven.
-      return isImpliedCondOperands(CanonicalFoundPred, CanonicalLHS,
-                                   CanonicalRHS, CanonicalFoundLHS,
-                                   CanonicalFoundRHS);
+      if (isImpliedCondOperands(CanonicalFoundPred, CanonicalLHS, CanonicalRHS,
+                                CanonicalFoundLHS, CanonicalFoundRHS))
+        return true;
+    }
   }
 
   // Check if we can make progress by sharpening ranges.
diff --git a/llvm/test/Transforms/IndVarSimplify/implied-cond-different-signedness.ll b/llvm/test/Transforms/IndVarSimplify/implied-cond-different-signedness.ll
index ebbec2d0fdf94b..fda6bc18dd78b0 100644
--- a/llvm/test/Transforms/IndVarSimplify/implied-cond-different-signedness.ll
+++ b/llvm/test/Transforms/IndVarSimplify/implied-cond-different-signedness.ll
@@ -159,9 +159,7 @@ define void @ult_const_query_slt_offset(i32 %x) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[X_M5:%.*]] = add i32 [[X]], -5
-; CHECK-NEXT:    [[Q:%.*]] = icmp sge i32 [[X_M5]], 5
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1

>From 7f6955007637ab6728a087cdc9eeea30d64ab22c Mon Sep 17 00:00:00 2001
From: Dmitrii Makogon <dmakogon at azul.com>
Date: Thu, 22 Oct 2026 09:20:43 +0000
Subject: [PATCH 2/4] [SCEV][NFC] Factor isImpliedCondViaSignFlippedPred out of
 isImpliedCondBalancedTypes

Move the code that proves a condition from a found condition with the
other signedness into a new private function.

Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
 llvm/include/llvm/Analysis/ScalarEvolution.h |  8 +++
 llvm/lib/Analysis/ScalarEvolution.cpp        | 74 ++++++++++----------
 2 files changed, 46 insertions(+), 36 deletions(-)

diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 17bfec22d65ca7..dc74802a639398 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -2323,6 +2323,14 @@ class ScalarEvolution {
                                       const SCEV *FoundLHS,
                                       const SCEV *FoundRHS);
 
+  /// Test whether the condition described by Pred, LHS, and RHS is true
+  /// whenever the condition described by FoundLHS, FoundRHS, and Pred with the
+  /// other signedness is true.
+  bool isImpliedCondViaSignFlippedPred(CmpPredicate Pred, const SCEV *LHS,
+                                       const SCEV *RHS, const SCEV *FoundLHS,
+                                       const SCEV *FoundRHS,
+                                       const Instruction *CtxI);
+
   /// Return true if the condition denoted by \p LHS \p Pred \p RHS is implied
   /// by a call to @llvm.experimental.guard in \p BB.
   bool isImpliedViaGuard(const BasicBlock *BB, CmpPredicate Pred,
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 0b53f7906e6ae9..9444e397b303d2 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -12240,43 +12240,9 @@ bool ScalarEvolution::isImpliedCondBalancedTypes(
            P1 == ICmpInst::getFlippedSignednessPredicate(P2);
   };
   if (IsSignFlippedPredicate(Pred, FoundPred)) {
-    // Unsigned comparison is the same as signed comparison when both the
-    // operands are non-negative or negative.
-    if (haveSameSign(FoundLHS, FoundRHS) &&
-        isImpliedCondOperands(Pred, LHS, RHS, FoundLHS, FoundRHS, CtxI))
+    if (isImpliedCondViaSignFlippedPred(Pred, LHS, RHS, FoundLHS, FoundRHS,
+                                        CtxI))
       return true;
-    // Create local copies that we can freely swap and canonicalize our
-    // conditions to "le/lt".
-    CmpPredicate CanonicalPred = Pred, CanonicalFoundPred = FoundPred;
-    const SCEV *CanonicalLHS = LHS, *CanonicalRHS = RHS,
-               *CanonicalFoundLHS = FoundLHS, *CanonicalFoundRHS = FoundRHS;
-    if (ICmpInst::isGT(CanonicalPred) || ICmpInst::isGE(CanonicalPred)) {
-      CanonicalPred = ICmpInst::getSwappedCmpPredicate(CanonicalPred);
-      CanonicalFoundPred = ICmpInst::getSwappedCmpPredicate(CanonicalFoundPred);
-      std::swap(CanonicalLHS, CanonicalRHS);
-      std::swap(CanonicalFoundLHS, CanonicalFoundRHS);
-    }
-    assert((ICmpInst::isLT(CanonicalPred) || ICmpInst::isLE(CanonicalPred)) &&
-           "Must be!");
-    assert((ICmpInst::isLT(CanonicalFoundPred) ||
-            ICmpInst::isLE(CanonicalFoundPred)) &&
-           "Must be!");
-    if (ICmpInst::isSigned(CanonicalPred) && isKnownNonNegative(CanonicalRHS)) {
-      // Use implication:
-      // x <u y && y >=s 0 --> x <s y.
-      // If we can prove the left part, the right part is also proven.
-      if (isImpliedCondOperands(CanonicalFoundPred, CanonicalLHS, CanonicalRHS,
-                                CanonicalFoundLHS, CanonicalFoundRHS))
-        return true;
-    }
-    if (ICmpInst::isUnsigned(CanonicalPred) && isKnownNegative(CanonicalRHS)) {
-      // Use implication:
-      // x <s y && y <s 0 --> x <u y.
-      // If we can prove the left part, the right part is also proven.
-      if (isImpliedCondOperands(CanonicalFoundPred, CanonicalLHS, CanonicalRHS,
-                                CanonicalFoundLHS, CanonicalFoundRHS))
-        return true;
-    }
   }
 
   // Check if we can make progress by sharpening ranges.
@@ -12373,6 +12339,42 @@ bool ScalarEvolution::isImpliedCondBalancedTypes(
   return false;
 }
 
+bool ScalarEvolution::isImpliedCondViaSignFlippedPred(
+    CmpPredicate Pred, const SCEV *LHS, const SCEV *RHS, const SCEV *FoundLHS,
+    const SCEV *FoundRHS, const Instruction *CtxI) {
+  CmpPredicate FoundPred = ICmpInst::getFlippedSignednessPredicate(Pred);
+  // Unsigned comparison is the same as signed comparison when both the
+  // operands are non-negative or negative.
+  if (haveSameSign(FoundLHS, FoundRHS) &&
+      isImpliedCondOperands(Pred, LHS, RHS, FoundLHS, FoundRHS, CtxI))
+    return true;
+  // Canonicalize both conditions to "le/lt".
+  if (ICmpInst::isGT(Pred) || ICmpInst::isGE(Pred)) {
+    Pred = ICmpInst::getSwappedCmpPredicate(Pred);
+    FoundPred = ICmpInst::getSwappedCmpPredicate(FoundPred);
+    std::swap(LHS, RHS);
+    std::swap(FoundLHS, FoundRHS);
+  }
+  assert((ICmpInst::isLT(Pred) || ICmpInst::isLE(Pred)) && "Must be!");
+  assert((ICmpInst::isLT(FoundPred) || ICmpInst::isLE(FoundPred)) &&
+         "Must be!");
+  if (ICmpInst::isSigned(Pred) && isKnownNonNegative(RHS)) {
+    // Use implication:
+    // x <u y && y >=s 0 --> x <s y.
+    // If we can prove the left part, the right part is also proven.
+    if (isImpliedCondOperands(FoundPred, LHS, RHS, FoundLHS, FoundRHS))
+      return true;
+  }
+  if (ICmpInst::isUnsigned(Pred) && isKnownNegative(RHS)) {
+    // Use implication:
+    // x <s y && y <s 0 --> x <u y.
+    // If we can prove the left part, the right part is also proven.
+    if (isImpliedCondOperands(FoundPred, LHS, RHS, FoundLHS, FoundRHS))
+      return true;
+  }
+  return false;
+}
+
 bool ScalarEvolution::splitBinaryAdd(SCEVUse Expr, SCEVUse &L, SCEVUse &R,
                                      SCEVFlags &Flags) {
   if (!match(Expr, m_scev_Add(m_SCEV(L), m_SCEV(R))))

>From eeb6c11ea90a1bfaf30eff0fbe3c1b3efcf0403e Mon Sep 17 00:00:00 2001
From: Dmitrii Makogon <dmakogon at azul.com>
Date: Thu, 22 Oct 2026 09:26:01 +0000
Subject: [PATCH 3/4] [SCEV] Prove signed/unsigned implications in either
 operand order

SCEV could prove these implications only with the operands in the same
order:
- If a <u b and a, b have the same sign, then a <s b.
- If a <s b and a, b have the same sign, then a <u b.
- If a <u b and b >=s 0, then a <s b.
- If a <s b and b <s 0, then a <u b.

For example, it proved a <s b from a <u b and b >=s 0, but not b >s a.
Now it proves them in either order.

Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
 llvm/lib/Analysis/ScalarEvolution.cpp           | 16 ++++++++++------
 .../implied-cond-different-signedness.ll        | 17 +++--------------
 2 files changed, 13 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 9444e397b303d2..0d632b857158dc 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -12233,17 +12233,21 @@ bool ScalarEvolution::isImpliedCondBalancedTypes(
     return false;
   }
 
+  // Try the query as is and with its operands swapped. E.g. "x >s -1" is
+  // tried as "-1 <s x" too, to match the found condition "x <u y".
   auto IsSignFlippedPredicate = [](CmpInst::Predicate P1,
                                    CmpInst::Predicate P2) {
-    assert(P1 != P2 && "Handled earlier!");
     return CmpInst::isRelational(P2) &&
            P1 == ICmpInst::getFlippedSignednessPredicate(P2);
   };
-  if (IsSignFlippedPredicate(Pred, FoundPred)) {
-    if (isImpliedCondViaSignFlippedPred(Pred, LHS, RHS, FoundLHS, FoundRHS,
-                                        CtxI))
-      return true;
-  }
+  if (IsSignFlippedPredicate(Pred, FoundPred) &&
+      isImpliedCondViaSignFlippedPred(Pred, LHS, RHS, FoundLHS, FoundRHS, CtxI))
+    return true;
+  CmpPredicate SwappedPred = ICmpInst::getSwappedCmpPredicate(Pred);
+  if (IsSignFlippedPredicate(SwappedPred, FoundPred) &&
+      isImpliedCondViaSignFlippedPred(SwappedPred, RHS, LHS, FoundLHS, FoundRHS,
+                                      CtxI))
+    return true;
 
   // Check if we can make progress by sharpening ranges.
   if (FoundPred == ICmpInst::ICMP_NE &&
diff --git a/llvm/test/Transforms/IndVarSimplify/implied-cond-different-signedness.ll b/llvm/test/Transforms/IndVarSimplify/implied-cond-different-signedness.ll
index fda6bc18dd78b0..710840c0f2bd0b 100644
--- a/llvm/test/Transforms/IndVarSimplify/implied-cond-different-signedness.ll
+++ b/llvm/test/Transforms/IndVarSimplify/implied-cond-different-signedness.ll
@@ -3,8 +3,6 @@
 
 ; Check that an exit condition is proven from a guard with a predicate of
 ; different signedness.
-;
-; FIXME: SCEV cannot prove these yet, fold the exits to false.
 
 target datalayout = "e-m:e-i64:64-n8:16:32:64-S128"
 
@@ -22,10 +20,7 @@ define void @same_sign(ptr %px, ptr %py, i32 %w) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smin.i32(i32 [[X]], i32 [[W]])
-; CHECK-NEXT:    [[Y_1:%.*]] = add nsw i32 [[Y]], 1
-; CHECK-NEXT:    [[Q:%.*]] = icmp sle i32 [[Y_1]], [[M]]
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -69,10 +64,7 @@ define void @ult_nonneg(i32 %x, ptr %py, i32 %w) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.umin.i32(i32 [[X]], i32 [[W]])
-; CHECK-NEXT:    [[Y_1:%.*]] = add nuw nsw i32 [[Y]], 1
-; CHECK-NEXT:    [[Q:%.*]] = icmp sle i32 [[Y_1]], [[M]]
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -115,10 +107,7 @@ define void @slt_neg(i32 %x, ptr %py, i32 %w) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smin.i32(i32 [[X]], i32 [[W]])
-; CHECK-NEXT:    [[Y_1:%.*]] = add nsw i32 [[Y]], 1
-; CHECK-NEXT:    [[Q:%.*]] = icmp ule i32 [[Y_1]], [[M]]
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1

>From 617c3a719c345ed320faea6c3e73a91ae8b876a1 Mon Sep 17 00:00:00 2001
From: Dmitrii Makogon <dmakogon at azul.com>
Date: Thu, 22 Oct 2026 09:27:52 +0000
Subject: [PATCH 4/4] [SCEV] Derive x >=s 0 from x <u y and y <=u 2^(n-1)

Let n be the bit width of x and y. If x <u y and y <=u 2^(n-1), then
x <u 2^(n-1), so x >=s 0. Here y <=u 2^(n-1) means y >=s 0 or
y == INT_MIN. In the same way, if x <=u y and y <u 2^(n-1), then
x >=s 0.

This lets a guard like x <u len prove x >=s 0 when len is non-negative.

Co-Authored-By: Claude Opus 5.5 <noreply at anthropic.com>
---
 llvm/lib/Analysis/ScalarEvolution.cpp         | 21 +++++++
 .../implied-nonneg-from-unsigned-bound.ll     | 57 +++++++------------
 2 files changed, 43 insertions(+), 35 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 0d632b857158dc..79bc895a5ec61b 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -12362,6 +12362,27 @@ bool ScalarEvolution::isImpliedCondViaSignFlippedPred(
   assert((ICmpInst::isLT(Pred) || ICmpInst::isLE(Pred)) && "Must be!");
   assert((ICmpInst::isLT(FoundPred) || ICmpInst::isLE(FoundPred)) &&
          "Must be!");
+  if (ICmpInst::isSigned(Pred)) {
+    // Use implication:
+    // x <u y && y <=u 2^(n-1) --> x >=s 0,
+    // x <=u y && y <u 2^(n-1) --> x >=s 0,
+    // where n is the bit width of x and y.
+    // In both cases x <u 2^(n-1), so x >=s 0.
+    // y <=u 2^(n-1) means y >=s 0 or y == INT_MIN.
+    // If the implication's left part holds, try proving the query from x >=s
+    // 0.
+    Type *Ty = FoundLHS->getType();
+    unsigned BitWidth = getTypeSizeInBits(Ty);
+    const SCEV *SignedMin = getConstant(APInt::getSignedMinValue(BitWidth));
+    CmpPredicate BoundPred =
+        ICmpInst::isLT(FoundPred) ? ICmpInst::ICMP_ULE : ICmpInst::ICMP_ULT;
+    if (isKnownViaNonRecursiveReasoning(BoundPred, FoundRHS, SignedMin)) {
+      // x >=s 0, written as 0 <=s x, or as -1 <s x to match a strict query.
+      const SCEV *Bound = ICmpInst::isLE(Pred) ? getZero(Ty) : getMinusOne(Ty);
+      if (isImpliedCondOperands(Pred, LHS, RHS, Bound, FoundLHS, CtxI))
+        return true;
+    }
+  }
   if (ICmpInst::isSigned(Pred) && isKnownNonNegative(RHS)) {
     // Use implication:
     // x <u y && y >=s 0 --> x <s y.
diff --git a/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound.ll b/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound.ll
index c760662d9394ba..9189e0455dc7c4 100644
--- a/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound.ll
+++ b/llvm/test/Transforms/IndVarSimplify/implied-nonneg-from-unsigned-bound.ll
@@ -4,8 +4,6 @@
 ; If x <u y and y <=u INT_MIN, then x >=s 0.
 ; If x <=u y and y <=u INT_MAX, then x >=s 0.
 ; The guards and the exit conditions are written with different predicates.
-;
-; FIXME: SCEV cannot prove this yet, fold the exits to constants where possible.
 
 target datalayout = "e-m:e-i64:64-n8:16:32:64-S128"
 
@@ -21,8 +19,7 @@ define void @ult(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -62,8 +59,7 @@ define void @ult_smin(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -103,8 +99,7 @@ define void @ule(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -144,8 +139,7 @@ define void @ugt(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -185,8 +179,7 @@ define void @ugt_smin(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -226,8 +219,7 @@ define void @uge(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -267,8 +259,7 @@ define void @ult_query_sle(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp sle i32 [[X]], -1
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -308,8 +299,7 @@ define void @ult_query_sgt_swapped(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp sgt i32 0, [[X]]
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -349,8 +339,7 @@ define void @ult_query_sge(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp sge i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -390,8 +379,7 @@ define void @ult_query_sgt(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp sgt i32 [[X]], -1
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -431,8 +419,7 @@ define void @ult_query_slt_swapped(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 -1, [[X]]
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -472,8 +459,7 @@ define void @ult_query_sle_swapped(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp sle i32 0, [[X]]
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -513,8 +499,7 @@ define void @ugt_query_sge(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp sge i32 [[X]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -554,8 +539,7 @@ define void @uge_query_sgt(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp sgt i32 [[X]], -1
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 true, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -585,6 +569,8 @@ exit:
 
 ; If x <u y and y <u INT_MAX, then x >=s 0, which implies x >=s z
 ; for any z <=s 0.
+; TODO: fold the exit. This needs x >=s z to be proven from x >=s 0 and
+; z <=s 0, where z may be INT_MIN.
 define void @ult_query_nonpos(i32 %x, ptr %p, ptr %pz) {
 ; CHECK-LABEL: define void @ult_query_nonpos(
 ; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]], ptr [[PZ:%.*]]) {
@@ -639,8 +625,7 @@ define void @ult_query_const(i32 %x, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[X]], -5
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
@@ -670,6 +655,8 @@ exit:
 
 ; If x <u y and y <u 1000, then x >=s 0. Here y <u 1000 is known only from
 ; a dominating guard.
+; TODO: fold the exit. This needs the bound on y to be proven from the guard,
+; not only from the range of y.
 define void @ult_y_guarded(i32 %x, i32 %y) {
 ; CHECK-LABEL: define void @ult_y_guarded(
 ; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
@@ -715,6 +702,8 @@ exit:
 }
 
 ; If x <u y and y <u INT_MAX, then x >=s 0, which implies x + 1 >=s 1.
+; TODO: fold the exit. This needs x + 1 >=s 1 to be proven from x >=s 0 for
+; the nsw add.
 define void @ult_query_offset(i32 %x, ptr %p) {
 ; CHECK-LABEL: define void @ult_query_offset(
 ; CHECK-SAME: i32 [[X:%.*]], ptr [[P:%.*]]) {
@@ -769,9 +758,7 @@ define void @ugt_smin_query_smax(i32 %x, i32 %w, ptr %p) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_NEXT:%.*]], %[[LATCH:.*]] ], [ 0, %[[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[M:%.*]] = call i32 @llvm.smax.i32(i32 [[X]], i32 [[W]])
-; CHECK-NEXT:    [[Q:%.*]] = icmp slt i32 [[M]], 0
-; CHECK-NEXT:    br i1 [[Q]], label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
+; CHECK-NEXT:    br i1 false, label %[[EXIT_LOOPEXIT:.*]], label %[[LATCH]]
 ; CHECK:       [[LATCH]]:
 ; CHECK-NEXT:    call void @side()
 ; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1



More information about the llvm-branch-commits mailing list