[Mlir-commits] [mlir] c827c90 - Improving MinUI and MaxUI implementation in valuebounds (#207701)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Mon Jul 6 06:15:22 PDT 2026


Author: Hagai Lev Hacohen
Date: 2026-07-06T13:15:17Z
New Revision: c827c9011f81ef8b74143dd0ef40db5950aaec81

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

LOG: Improving MinUI and MaxUI implementation in valuebounds (#207701)

Previously, the valuebounds implementation for MinUI and MaxUI was too
conservative.
We can already make correct assumptions even when we know only one
operand is provably not negative.

Added: 
    

Modified: 
    mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
    mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
index bc15d3b22f1b3..61e84e3cdae0f 100644
--- a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -354,20 +354,34 @@ struct MinUIOpInterface
     assert(value == minOp.getResult() && "invalid value");
 
     // ValueBoundsConstraintSet models values as signed integers (e.g. an i8
-    // 0xff is treated as -1, not 255).So, we can only derive bounds for minui
-    // if both operands are provably non-negative.
+    // 0xff is treated as -1, not 255). For an unsigned minimum it is enough
+    // that a single operand is provably non-negative: minui(x, y) is in
+    // [0, y] whenever y >= 0 (and symmetrically for x).
     bool lhsNonNegative =
         ValueBoundsConstraintSet::isProvablyNonNegative(minOp.getLhs(), cstr);
     bool rhsNonNegative =
         ValueBoundsConstraintSet::isProvablyNonNegative(minOp.getRhs(), cstr);
-    if (!lhsNonNegative || !rhsNonNegative)
+    if (!lhsNonNegative && !rhsNonNegative)
       return;
 
+    // A negative signed integer bit pattern reinterpreted as an
+    // unsigned integer is greater than SIGNED_INT_MAX. If
+    // one of the operands is signed non-negative, it is smaller than
+    // or equal to SIGNED_INT_MAX in unsigned interpretation,
+    // and `minui` will choose that operand over a negative signed
+    // integer operand.
     cstr.bound(value) >= 0;
-    AffineExpr lhs = cstr.getExpr(minOp.getLhs());
-    AffineExpr rhs = cstr.getExpr(minOp.getRhs());
-    cstr.bound(value) <= lhs;
-    cstr.bound(value) <= rhs;
+    // If an operand is provably non-negative, its signed and unsigned value
+    // interpretations agree, so `minsi` and `minui` impose the same upper
+    // bound: `result <= operand`.
+    if (lhsNonNegative) {
+      AffineExpr lhs = cstr.getExpr(minOp.getLhs());
+      cstr.bound(value) <= lhs;
+    }
+    if (rhsNonNegative) {
+      AffineExpr rhs = cstr.getExpr(minOp.getRhs());
+      cstr.bound(value) <= rhs;
+    }
   }
 };
 
@@ -384,13 +398,15 @@ struct MaxUIOpInterface
         ValueBoundsConstraintSet::isProvablyNonNegative(maxOp.getLhs(), cstr);
     bool rhsNonNegative =
         ValueBoundsConstraintSet::isProvablyNonNegative(maxOp.getRhs(), cstr);
-    if (!lhsNonNegative || !rhsNonNegative)
-      return;
 
-    AffineExpr lhs = cstr.getExpr(maxOp.getLhs());
-    AffineExpr rhs = cstr.getExpr(maxOp.getRhs());
-    cstr.bound(value) >= lhs;
-    cstr.bound(value) >= rhs;
+    if (lhsNonNegative) {
+      AffineExpr lhs = cstr.getExpr(maxOp.getLhs());
+      cstr.bound(value) >= lhs;
+    }
+    if (rhsNonNegative) {
+      AffineExpr rhs = cstr.getExpr(maxOp.getRhs());
+      cstr.bound(value) >= rhs;
+    }
   }
 };
 } // namespace

diff  --git a/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir b/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
index 41f7f18baefec..ceb8256003b1e 100644
--- a/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
+++ b/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
@@ -486,32 +486,38 @@ func.func @arith_maxui() -> index {
 
 // -----
 
-func.func @arith_maxui_unknown_sign(%a: index) -> index {
+// CHECK-LABEL: func @arith_maxui_unknown_operand(
+//       CHECK:   %[[lb:.*]] = arith.constant 4 : index
+//       CHECK:   return %[[lb]]
+func.func @arith_maxui_unknown_operand(%a: index) -> index {
   %c4 = arith.constant 4 : index
   %0 = arith.maxui %a, %c4 : index
-  // expected-error @below{{could not reify bound}}
-  %1 = "test.reify_bound"(%0) {type = "LB"} : (index) -> (index)
+  %1 = "test.reify_bound"(%0) {type = "LB", constant} : (index) -> (index)
   return %1 : index
 }
 
 // -----
 
+// CHECK-LABEL: func @arith_minui_wraparound(
+//       CHECK:   %[[ub:.*]] = arith.constant 11 : index
+//       CHECK:   return %[[ub]]
 func.func @arith_minui_wraparound() -> index {
   %c255 = arith.constant 0xFF : i8
   %c10 = arith.constant 10 : i8
   %0 = arith.minui %c255, %c10 : i8
-  // expected-error @below{{could not reify bound}}
   %1 = "test.reify_bound"(%0) {type = "UB", allow_integer_type} : (i8) -> (index)
   return %1 : index
 }
 
 // -----
 
+// CHECK-LABEL: func @arith_maxui_wraparound(
+//       CHECK:   %[[lb:.*]] = arith.constant 10 : index
+//       CHECK:   return %[[lb]]
 func.func @arith_maxui_wraparound() -> index {
   %c255 = arith.constant 0xFF : i8
   %c10 = arith.constant 10 : i8
   %0 = arith.maxui %c255, %c10 : i8
-  // expected-error @below{{could not reify bound}}
   %1 = "test.reify_bound"(%0) {type = "LB", allow_integer_type} : (i8) -> (index)
   return %1 : index
 }
@@ -547,12 +553,15 @@ func.func @arith_minui_nonneg_symbolic(%a: index) -> index {
 
 // -----
 
+// CHECK-LABEL: func @arith_minui_negative_symbolic(
+//  CHECK-SAME:     %[[a:.*]]: index
+//       CHECK:   %[[ub:.*]] = arith.constant 5 : index
+//       CHECK:   return %[[ub]]
 func.func @arith_minui_negative_symbolic(%a: index) -> index {
   %cm1 = arith.constant -1 : index
   %c4 = arith.constant 4 : index
   %neg = arith.minsi %a, %cm1 : index
   %0 = arith.minui %neg, %c4 : index
-  // expected-error @below{{could not reify bound}}
   %1 = "test.reify_bound"(%0) {type = "UB", constant} : (index) -> (index)
   return %1 : index
 }
@@ -574,12 +583,15 @@ func.func @arith_maxui_nonneg_symbolic(%a: index) -> index {
 
 // -----
 
+// CHECK-LABEL: func @arith_maxui_negative_symbolic(
+//  CHECK-SAME:     %[[a:.*]]: index
+//       CHECK:   %[[lb:.*]] = arith.constant 4 : index
+//       CHECK:   return %[[lb]]
 func.func @arith_maxui_negative_symbolic(%a: index) -> index {
   %cm1 = arith.constant -1 : index
   %c4 = arith.constant 4 : index
   %neg = arith.minsi %a, %cm1 : index
   %0 = arith.maxui %neg, %c4 : index
-  // expected-error @below{{could not reify bound}}
   %1 = "test.reify_bound"(%0) {type = "LB", constant} : (index) -> (index)
   return %1 : index
 }


        


More information about the Mlir-commits mailing list