[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