[Mlir-commits] [mlir] Improving MinUI and MaxUI implementation in valuebounds (PR #207701)
Hagai Lev Hacohen
llvmlistbot at llvm.org
Mon Jul 6 06:03:45 PDT 2026
https://github.com/HagaiLevHacohen updated https://github.com/llvm/llvm-project/pull/207701
>From f83e31c0d0c939bbd784507a45591474676bcbc6 Mon Sep 17 00:00:00 2001
From: Hagai Lev Hacohen <hagai4000 at gmail.com>
Date: Mon, 6 Jul 2026 11:27:19 +0300
Subject: [PATCH 1/5] Improving MinUI and MaxUI implementation in valuebounds
---
.../Arith/IR/ValueBoundsOpInterfaceImpl.cpp | 34 ++++++++++++-------
.../Arith/value-bounds-op-interface-impl.mlir | 26 ++++++++++----
2 files changed, 40 insertions(+), 20 deletions(-)
diff --git a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
index bc15d3b22f1b3..074f7efffc81b 100644
--- a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -354,20 +354,24 @@ 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;
- 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 (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 +388,17 @@ struct MaxUIOpInterface
ValueBoundsConstraintSet::isProvablyNonNegative(maxOp.getLhs(), cstr);
bool rhsNonNegative =
ValueBoundsConstraintSet::isProvablyNonNegative(maxOp.getRhs(), cstr);
- if (!lhsNonNegative || !rhsNonNegative)
+ 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
}
>From 3637788c5a8dcc58ccbbcd58481fbf2fe2931d2e Mon Sep 17 00:00:00 2001
From: Hagai Lev Hacohen <hagai4000 at gmail.com>
Date: Mon, 6 Jul 2026 14:09:10 +0300
Subject: [PATCH 2/5] adding non negative bound for MinUI
---
mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
index 074f7efffc81b..d2d55b8b1562f 100644
--- a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -363,7 +363,8 @@ struct MinUIOpInterface
ValueBoundsConstraintSet::isProvablyNonNegative(minOp.getRhs(), cstr);
if (!lhsNonNegative && !rhsNonNegative)
return;
-
+
+ cstr.bound(value) >= 0;
if (lhsNonNegative) {
AffineExpr lhs = cstr.getExpr(minOp.getLhs());
cstr.bound(value) <= lhs;
>From 0a18237baeee085e681c8be534d0cc90a71306e4 Mon Sep 17 00:00:00 2001
From: Hagai Lev Hacohen <hagai4000 at gmail.com>
Date: Mon, 6 Jul 2026 14:10:09 +0300
Subject: [PATCH 3/5] lint
---
mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
index d2d55b8b1562f..6c6fc6fa128a1 100644
--- a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -363,7 +363,7 @@ struct MinUIOpInterface
ValueBoundsConstraintSet::isProvablyNonNegative(minOp.getRhs(), cstr);
if (!lhsNonNegative && !rhsNonNegative)
return;
-
+
cstr.bound(value) >= 0;
if (lhsNonNegative) {
AffineExpr lhs = cstr.getExpr(minOp.getLhs());
>From 0ccd18bcb88be2e27caa65ec38be934e670555aa Mon Sep 17 00:00:00 2001
From: Hagai Lev Hacohen <hagai4000 at gmail.com>
Date: Mon, 6 Jul 2026 16:01:19 +0300
Subject: [PATCH 4/5] added documentation
---
.../Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
index 6c6fc6fa128a1..87fb2bdc619ff 100644
--- a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -364,7 +364,15 @@ struct MinUIOpInterface
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;
+ // 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;
@@ -389,8 +397,6 @@ struct MaxUIOpInterface
ValueBoundsConstraintSet::isProvablyNonNegative(maxOp.getLhs(), cstr);
bool rhsNonNegative =
ValueBoundsConstraintSet::isProvablyNonNegative(maxOp.getRhs(), cstr);
- if (!lhsNonNegative && !rhsNonNegative)
- return;
if (lhsNonNegative) {
AffineExpr lhs = cstr.getExpr(maxOp.getLhs());
>From 8ce0e0c8b6a82c1d2ab198265a4bd9df58cd6036 Mon Sep 17 00:00:00 2001
From: Hagai Lev Hacohen <hagai4000 at gmail.com>
Date: Mon, 6 Jul 2026 16:02:43 +0300
Subject: [PATCH 5/5] lint
---
mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
index 87fb2bdc619ff..61e84e3cdae0f 100644
--- a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -364,15 +364,16 @@ struct MinUIOpInterface
if (!lhsNonNegative && !rhsNonNegative)
return;
- // A negative signed integer bit pattern reinterpreted as an
+ // 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;
- // 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 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;
More information about the Mlir-commits
mailing list