[Mlir-commits] [mlir] [mlir][arith] Implement ValueBoundsOpInterface for min/max ops (PR #203269)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Thu Jun 11 06:53:22 PDT 2026
https://github.com/nirhersh created https://github.com/llvm/llvm-project/pull/203269
Add ValueBoundsOpInterface external models for the arith integer
min/max operations: arith.minui, arith.minsi, arith.maxui and
arith.maxsi.
>From 3e86a6d10f95462090dec812bfe9c4cbaa718944 Mon Sep 17 00:00:00 2001
From: Nir Herscovici <nir.herscovici at mobileye.com>
Date: Thu, 11 Jun 2026 16:12:36 +0300
Subject: [PATCH] [mlir][arith] Implement ValueBoundsOpInterface for min/max
ops
Add ValueBoundsOpInterface external models for the arith integer
min/max operations: arith.minui, arith.minsi, arith.maxui and
arith.maxsi.
---
.../Arith/IR/ValueBoundsOpInterfaceImpl.cpp | 68 ++++++++++++++++
.../Arith/value-bounds-op-interface-impl.mlir | 79 +++++++++++++++++++
2 files changed, 147 insertions(+)
diff --git a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
index bd0c262f932e3..03e45aa46d0cf 100644
--- a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -160,6 +160,70 @@ struct SelectOpInterface
populateBounds(cast<SelectOp>(op), dim, cstr);
}
};
+
+struct MinUIOpInterface
+ : public ValueBoundsOpInterface::ExternalModel<MinUIOpInterface,
+ arith::MinUIOp> {
+ void populateBoundsForIndexValue(Operation *op, Value value,
+ ValueBoundsConstraintSet &cstr) const {
+ auto minOp = cast<arith::MinUIOp>(op);
+ assert(value == minOp.getResult() && "invalid value");
+ AffineExpr lhs = cstr.getExpr(minOp.getLhs());
+ AffineExpr rhs = cstr.getExpr(minOp.getRhs());
+ AffineExpr zero = cstr.getExpr(0);
+ cstr.bound(value) <= lhs;
+ cstr.bound(value) <= rhs;
+ cstr.bound(value) >= zero;
+ }
+};
+
+struct MinSIOpInterface
+ : public ValueBoundsOpInterface::ExternalModel<MinSIOpInterface,
+ arith::MinSIOp> {
+ void populateBoundsForIndexValue(Operation *op, Value value,
+ ValueBoundsConstraintSet &cstr) const {
+ auto minOp = cast<arith::MinSIOp>(op);
+ assert(value == minOp.getResult() && "invalid value");
+
+ AffineExpr lhs = cstr.getExpr(minOp.getLhs());
+ AffineExpr rhs = cstr.getExpr(minOp.getRhs());
+
+ cstr.bound(value) <= lhs;
+ cstr.bound(value) <= rhs;
+ }
+};
+
+struct MaxUIOpInterface
+ : public ValueBoundsOpInterface::ExternalModel<MaxUIOpInterface,
+ arith::MaxUIOp> {
+ void populateBoundsForIndexValue(Operation *op, Value value,
+ ValueBoundsConstraintSet &cstr) const {
+ auto maxOp = cast<arith::MaxUIOp>(op);
+ assert(value == maxOp.getResult() && "invalid value");
+ AffineExpr lhs = cstr.getExpr(maxOp.getLhs());
+ AffineExpr rhs = cstr.getExpr(maxOp.getRhs());
+ AffineExpr zero = cstr.getExpr(0);
+
+ cstr.bound(value) <= lhs + rhs;
+ cstr.bound(value) >= lhs;
+ cstr.bound(value) >= rhs;
+ cstr.bound(value) >= zero;
+ }
+};
+
+struct MaxSIOpInterface
+ : public ValueBoundsOpInterface::ExternalModel<MaxSIOpInterface,
+ arith::MaxSIOp> {
+ void populateBoundsForIndexValue(Operation *op, Value value,
+ ValueBoundsConstraintSet &cstr) const {
+ auto maxOp = cast<arith::MaxSIOp>(op);
+ assert(value == maxOp.getResult() && "invalid value");
+ AffineExpr lhs = cstr.getExpr(maxOp.getLhs());
+ AffineExpr rhs = cstr.getExpr(maxOp.getRhs());
+ cstr.bound(value) >= lhs;
+ cstr.bound(value) >= rhs;
+ }
+};
} // namespace
} // namespace arith
} // namespace mlir
@@ -173,5 +237,9 @@ void mlir::arith::registerValueBoundsOpInterfaceExternalModels(
arith::MulIOp::attachInterface<arith::MulIOpInterface>(*ctx);
arith::FloorDivSIOp::attachInterface<arith::FloorDivSIOpInterface>(*ctx);
arith::SelectOp::attachInterface<arith::SelectOpInterface>(*ctx);
+ arith::MinUIOp::attachInterface<arith::MinUIOpInterface>(*ctx);
+ arith::MinSIOp::attachInterface<arith::MinSIOpInterface>(*ctx);
+ arith::MaxUIOp::attachInterface<arith::MaxUIOpInterface>(*ctx);
+ arith::MaxSIOp::attachInterface<arith::MaxSIOpInterface>(*ctx);
});
}
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 78802a282f096..62d8497f180ab 100644
--- a/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
+++ b/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
@@ -168,3 +168,82 @@ func.func @arith_select_elementwise(%a: tensor<?xf32>, %b: tensor<?xf32>, %c: te
// CHECK: return %[[dim]]
return %0 : index
}
+
+// -----
+
+// CHECK-LABEL: func @arith_minui(
+// CHECK-SAME: %[[a:.*]]: index
+// CHECK: %[[lb:.*]] = arith.constant 0 : index
+// CHECK: %[[ub:.*]] = arith.constant 5 : index
+// CHECK: return %[[lb]], %[[ub]]
+func.func @arith_minui(%a: index) -> (index, index) {
+ %c4 = arith.constant 4 : index
+ %0 = arith.minui %a, %c4 : index
+ %1 = "test.reify_bound"(%0) {type = "LB"} : (index) -> (index)
+ %2 = "test.reify_bound"(%0) {type = "UB"} : (index) -> (index)
+ return %1, %2 : index, index
+}
+
+// -----
+
+// CHECK-LABEL: func @arith_minsi(
+// CHECK-SAME: %[[a:.*]]: index
+// CHECK: %[[ub:.*]] = arith.constant 5 : index
+// CHECK: return %[[ub]]
+func.func @arith_minsi(%a: index) -> index {
+ %c4 = arith.constant 4 : index
+ %0 = arith.minsi %a, %c4 : index
+ %1 = "test.reify_bound"(%0) {type = "UB"} : (index) -> (index)
+ return %1 : index
+}
+
+// -----
+
+func.func @arith_minsi_lb(%a: index) -> index {
+ %c4 = arith.constant 4 : index
+ %0 = arith.minsi %a, %c4 : index
+ // Signed min has no lower bound.
+ // expected-error @below{{could not reify bound}}
+ %1 = "test.reify_bound"(%0) {type = "LB"} : (index) -> (index)
+ return %1 : index
+}
+
+// -----
+
+// CHECK: #[[$map:.*]] = affine_map<()[s0, s1] -> (s0 + s1 + 1)>
+// CHECK-LABEL: func @arith_maxui(
+// CHECK-SAME: %[[a:.*]]: index, %[[b:.*]]: index
+// CHECK: %[[lb:.*]] = arith.constant 0 : index
+// CHECK: %[[ub:.*]] = affine.apply #[[$map]]()[%[[a]], %[[b]]]
+// CHECK: return %[[lb]], %[[ub]]
+func.func @arith_maxui(%a: index, %b: index) -> (index, index) {
+ %0 = arith.maxui %a, %b : index
+ %1 = "test.reify_bound"(%0) {type = "LB"} : (index) -> (index)
+ %2 = "test.reify_bound"(%0) {type = "UB"} : (index) -> (index)
+ return %1, %2 : index, index
+}
+
+// -----
+
+// CHECK-LABEL: func @arith_maxsi(
+// CHECK-SAME: %[[a:.*]]: index
+// CHECK: arith.constant 4 : index
+// CHECK: %[[lb:.*]] = arith.constant 4 : index
+// CHECK: return %[[lb]]
+func.func @arith_maxsi(%a: index) -> index {
+ %c4 = arith.constant 4 : index
+ %0 = arith.maxsi %a, %c4 : index
+ %1 = "test.reify_bound"(%0) {type = "LB"} : (index) -> (index)
+ return %1 : index
+}
+
+// -----
+
+func.func @arith_maxsi_ub(%a: index) -> index {
+ %c4 = arith.constant 4 : index
+ %0 = arith.maxsi %a, %c4 : index
+ // Signed max has no upper bound.
+ // expected-error @below{{could not reify bound}}
+ %1 = "test.reify_bound"(%0) {type = "UB"} : (index) -> (index)
+ return %1 : index
+}
More information about the Mlir-commits
mailing list