[llvm] [InstSimplify] Simplify 'x u>= 1' to true when x is known non-zero (PR #145204)

Iris Shi via llvm-commits llvm-commits at lists.llvm.org
Sat Jun 21 21:02:55 PDT 2025


https://github.com/el-ev created https://github.com/llvm/llvm-project/pull/145204

There is a common pattern where `umax(x, 1)` is generated in loop preheaders. When we can prove that `x` is non-zero, such call could be eliminated.

Godbolt Example: https://godbolt.org/z/zGzTTYe57
Bench: https://github.com/dtcxzyw/llvm-opt-benchmark/pull/2484

>From 1021b0a06db50c08da0c6f628a4ca88427976d28 Mon Sep 17 00:00:00 2001
From: Iris Shi <0.0 at owo.li>
Date: Sun, 22 Jun 2025 10:33:14 +0800
Subject: [PATCH 1/3] [InstSimplify] Simplify 'x u>= 1' to true when x is known
 non-zero

---
 llvm/lib/Analysis/InstructionSimplify.cpp | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index d1ac8d9fbdfd1..cb1dae92faf92 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -2981,7 +2981,7 @@ static Value *simplifyICmpWithZero(CmpPredicate Pred, Value *LHS, Value *RHS,
 }
 
 static Value *simplifyICmpWithConstant(CmpPredicate Pred, Value *LHS,
-                                       Value *RHS, const InstrInfoQuery &IIQ) {
+                                       Value *RHS, const SimplifyQuery &Q) {
   Type *ITy = getCompareTy(RHS); // The return type.
 
   Value *X;
@@ -3007,7 +3007,7 @@ static Value *simplifyICmpWithConstant(CmpPredicate Pred, Value *LHS,
     return ConstantInt::getTrue(ITy);
 
   ConstantRange LHS_CR =
-      computeConstantRange(LHS, CmpInst::isSigned(Pred), IIQ.UseInstrInfo);
+      computeConstantRange(LHS, CmpInst::isSigned(Pred), Q.IIQ.UseInstrInfo);
   if (!LHS_CR.isFullSet()) {
     if (RHS_CR.contains(LHS_CR))
       return ConstantInt::getTrue(ITy);
@@ -3018,13 +3018,16 @@ static Value *simplifyICmpWithConstant(CmpPredicate Pred, Value *LHS,
   // (mul nuw/nsw X, MulC) != C --> true  (if C is not a multiple of MulC)
   // (mul nuw/nsw X, MulC) == C --> false (if C is not a multiple of MulC)
   const APInt *MulC;
-  if (IIQ.UseInstrInfo && ICmpInst::isEquality(Pred) &&
+  if (Q.IIQ.UseInstrInfo && ICmpInst::isEquality(Pred) &&
       ((match(LHS, m_NUWMul(m_Value(), m_APIntAllowPoison(MulC))) &&
         *MulC != 0 && C->urem(*MulC) != 0) ||
        (match(LHS, m_NSWMul(m_Value(), m_APIntAllowPoison(MulC))) &&
         *MulC != 0 && C->srem(*MulC) != 0)))
     return ConstantInt::get(ITy, Pred == ICmpInst::ICMP_NE);
 
+  if (Pred == ICmpInst::ICMP_UGE && C->isOne() && isKnownNonZero(LHS, Q))
+    return ConstantInt::getTrue(ITy);
+
   return nullptr;
 }
 
@@ -3776,7 +3779,7 @@ static Value *simplifyICmpInst(CmpPredicate Pred, Value *LHS, Value *RHS,
   if (Value *V = simplifyICmpWithZero(Pred, LHS, RHS, Q))
     return V;
 
-  if (Value *V = simplifyICmpWithConstant(Pred, LHS, RHS, Q.IIQ))
+  if (Value *V = simplifyICmpWithConstant(Pred, LHS, RHS, Q))
     return V;
 
   // If both operands have range metadata, use the metadata

>From aca7a434543beb2321a8c3567fbc1587fad15799 Mon Sep 17 00:00:00 2001
From: Iris Shi <0.0 at owo.li>
Date: Sun, 22 Jun 2025 10:47:59 +0800
Subject: [PATCH 2/3] pre-commit tests

---
 llvm/test/Transforms/InstSimplify/umax-1.ll | 123 ++++++++++++++++++++
 1 file changed, 123 insertions(+)
 create mode 100644 llvm/test/Transforms/InstSimplify/umax-1.ll

diff --git a/llvm/test/Transforms/InstSimplify/umax-1.ll b/llvm/test/Transforms/InstSimplify/umax-1.ll
new file mode 100644
index 0000000000000..7d5b0c12c3378
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/umax-1.ll
@@ -0,0 +1,123 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instsimplify -S | FileCheck %s
+
+define i32 @known_non_zero_by_or(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @known_non_zero_by_or(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[COND:%.*]] = icmp ne i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[VAL:%.*]] = or i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[MAX:%.*]] = call i32 @llvm.umax.i32(i32 [[VAL]], i32 1)
+; CHECK-NEXT:    ret i32 [[MAX]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    ret i32 0
+;
+  %cond = icmp ne i32 %x, 0
+  br i1 %cond, label %if.then, label %if.else
+
+if.then:
+  %val = or i32 %x, %y
+  %max = call i32 @llvm.umax.i32(i32 %val, i32 1)
+  ret i32 %max
+
+if.else:
+  ret i32 0
+}
+
+define i32 @known_non_zero_by_mul(i32 %x) {
+; CHECK-LABEL: define i32 @known_non_zero_by_mul(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[COND:%.*]] = icmp ne i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[NONZERO1:%.*]] = mul nuw i32 [[X]], 3
+; CHECK-NEXT:    [[MAX:%.*]] = call i32 @llvm.umax.i32(i32 [[NONZERO1]], i32 1)
+; CHECK-NEXT:    ret i32 [[MAX]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    ret i32 0
+;
+  %cond = icmp ne i32 %x, 0
+  br i1 %cond, label %if.then, label %if.else
+
+if.then:
+  %nonzero = mul nuw i32 %x, 3
+  %max = call i32 @llvm.umax.i32(i32 %nonzero, i32 1)
+  ret i32 %max
+
+if.else:
+  ret i32 0
+}
+
+define i32 @known_non_zero_commute(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @known_non_zero_commute(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[COND:%.*]] = icmp ne i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[VAL:%.*]] = or i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[MAX:%.*]] = call i32 @llvm.umax.i32(i32 1, i32 [[VAL]])
+; CHECK-NEXT:    ret i32 [[MAX]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    ret i32 0
+;
+  %cond = icmp ne i32 %x, 0
+  br i1 %cond, label %if.then, label %if.else
+
+if.then:
+  %val = or i32 %x, %y
+  %max = call i32 @llvm.umax.i32(i32 1, i32 %val)
+  ret i32 %max
+
+if.else:
+  ret i32 0
+}
+
+; Negative
+define i32 @umax_ge_2(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @umax_ge_2(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[COND:%.*]] = icmp ne i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[VAL:%.*]] = or i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[MAX:%.*]] = call i32 @llvm.umax.i32(i32 [[VAL]], i32 2)
+; CHECK-NEXT:    ret i32 [[MAX]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    ret i32 0
+;
+  %cond = icmp ne i32 %x, 0
+  br i1 %cond, label %if.then, label %if.else
+
+if.then:
+  %val = or i32 %x, %y
+  %max = call i32 @llvm.umax.i32(i32 %val, i32 2)
+  ret i32 %max
+
+if.else:
+  ret i32 0
+}
+
+define i32 @unknown_by_and(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @unknown_by_and(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT:    [[COND:%.*]] = icmp ne i32 [[X]], 0
+; CHECK-NEXT:    br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[VAL:%.*]] = and i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[MAX:%.*]] = call i32 @llvm.umax.i32(i32 [[VAL]], i32 1)
+; CHECK-NEXT:    ret i32 [[MAX]]
+; CHECK:       [[IF_ELSE]]:
+; CHECK-NEXT:    ret i32 0
+;
+  %cond = icmp ne i32 %x, 0
+  br i1 %cond, label %if.then, label %if.else
+
+if.then:
+  %val = and i32 %x, %y
+  %max = call i32 @llvm.umax.i32(i32 %val, i32 1)
+  ret i32 %max
+
+if.else:
+  ret i32 0
+}

>From c7813a6478771a050ca8b46ec7a62cfe95b0ce63 Mon Sep 17 00:00:00 2001
From: Iris Shi <0.0 at owo.li>
Date: Sun, 22 Jun 2025 10:48:27 +0800
Subject: [PATCH 3/3] update tests

---
 llvm/test/Transforms/InstSimplify/umax-1.ll | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/llvm/test/Transforms/InstSimplify/umax-1.ll b/llvm/test/Transforms/InstSimplify/umax-1.ll
index 7d5b0c12c3378..77863acb02327 100644
--- a/llvm/test/Transforms/InstSimplify/umax-1.ll
+++ b/llvm/test/Transforms/InstSimplify/umax-1.ll
@@ -8,8 +8,7 @@ define i32 @known_non_zero_by_or(i32 %x, i32 %y) {
 ; CHECK-NEXT:    br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
 ; CHECK:       [[IF_THEN]]:
 ; CHECK-NEXT:    [[VAL:%.*]] = or i32 [[X]], [[Y]]
-; CHECK-NEXT:    [[MAX:%.*]] = call i32 @llvm.umax.i32(i32 [[VAL]], i32 1)
-; CHECK-NEXT:    ret i32 [[MAX]]
+; CHECK-NEXT:    ret i32 [[VAL]]
 ; CHECK:       [[IF_ELSE]]:
 ; CHECK-NEXT:    ret i32 0
 ;
@@ -32,8 +31,7 @@ define i32 @known_non_zero_by_mul(i32 %x) {
 ; CHECK-NEXT:    br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
 ; CHECK:       [[IF_THEN]]:
 ; CHECK-NEXT:    [[NONZERO1:%.*]] = mul nuw i32 [[X]], 3
-; CHECK-NEXT:    [[MAX:%.*]] = call i32 @llvm.umax.i32(i32 [[NONZERO1]], i32 1)
-; CHECK-NEXT:    ret i32 [[MAX]]
+; CHECK-NEXT:    ret i32 [[NONZERO1]]
 ; CHECK:       [[IF_ELSE]]:
 ; CHECK-NEXT:    ret i32 0
 ;
@@ -56,8 +54,7 @@ define i32 @known_non_zero_commute(i32 %x, i32 %y) {
 ; CHECK-NEXT:    br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_ELSE:.*]]
 ; CHECK:       [[IF_THEN]]:
 ; CHECK-NEXT:    [[VAL:%.*]] = or i32 [[X]], [[Y]]
-; CHECK-NEXT:    [[MAX:%.*]] = call i32 @llvm.umax.i32(i32 1, i32 [[VAL]])
-; CHECK-NEXT:    ret i32 [[MAX]]
+; CHECK-NEXT:    ret i32 [[VAL]]
 ; CHECK:       [[IF_ELSE]]:
 ; CHECK-NEXT:    ret i32 0
 ;



More information about the llvm-commits mailing list