[llvm-branch-commits] [llvm] [SCEV] Prove signed/unsigned implications in either operand order (PR #234208)

Dmitry Makogon via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Oct 21 06:58:58 PDT 2026


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

>From c4fe4042bbf75517b303b496518abc532fbbea10 Mon Sep 17 00:00:00 2001
From: Dmitrii Makogon <dmakogon at azul.com>
Date: Wed, 21 Oct 2026 11:41:07 +0000
Subject: [PATCH] [SCEV] Prove signed/unsigned implications in either operand
 order

SCEV could prove some signed conditions from unsigned ones and the
other way around, but only with the operands in the same order. For
example, it proved a <s b from a <u b and b >=s 0, but not b >s a.

Handle both orders.

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

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 76d76bc4c5266..28a5d36d38cc2 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -12243,19 +12243,19 @@ bool ScalarEvolution::isImpliedCondBalancedTypes(
   // the query "x >s -1" becomes "-1 <s x" for the found condition "x <u y".
   CmpPredicate SignFlippedPred = Pred;
   const SCEV *SignFlippedLHS = LHS, *SignFlippedRHS = RHS;
-  bool IsSwapped = false;
   if (!IsSignFlippedPredicate(Pred, FoundPred) &&
       IsSignFlippedPredicate(ICmpInst::getSwappedCmpPredicate(Pred),
                              FoundPred)) {
     SignFlippedPred = ICmpInst::getSwappedCmpPredicate(Pred);
     std::swap(SignFlippedLHS, SignFlippedRHS);
-    IsSwapped = true;
   }
   if (IsSignFlippedPredicate(SignFlippedPred, FoundPred)) {
     // Unsigned comparison is the same as signed comparison when both the
     // operands are non-negative or negative.
-    if (!IsSwapped && haveSameSign(FoundLHS, FoundRHS))
-      return isImpliedCondOperands(Pred, LHS, RHS, FoundLHS, FoundRHS, CtxI);
+    if (haveSameSign(FoundLHS, FoundRHS) &&
+        isImpliedCondOperands(SignFlippedPred, SignFlippedLHS, SignFlippedRHS,
+                              FoundLHS, FoundRHS, CtxI))
+      return true;
     // Create local copies that we can freely swap and canonicalize our
     // conditions to "le/lt".
     CmpPredicate CanonicalPred = SignFlippedPred,
@@ -12298,22 +12298,22 @@ bool ScalarEvolution::isImpliedCondBalancedTypes(
           return true;
       }
     }
-    if (!IsSwapped && 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 (!IsSwapped && 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 b01af67ddb7b3..c8551aae63a1c 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



More information about the llvm-branch-commits mailing list