[Mlir-commits] [mlir] 268d751 - [mlir][arith] Add ValueBoundsOpInterface external models for the arith integer CeilDiv, RemSI, RemUI, MaxUI, MinUI. (#204966)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Mon Jun 29 00:17:16 PDT 2026


Author: nirhersh
Date: 2026-06-29T07:17:10Z
New Revision: 268d7517922f6a22519c13cf6c138f67c5816ff9

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

LOG: [mlir][arith] Add ValueBoundsOpInterface external models for the arith integer CeilDiv, RemSI, RemUI, MaxUI, MinUI. (#204966)

Add ValueBoundsOpInterface external models for the arith integer
CeilDiv, RemSI, RemUI, MaxUI, MinUI.

Since the ValueBoundsConstraintSet infrastructure interprets unsigned
integers as signed, unsigned ops needed special handling.
In the unsigned ops we first verify that the integers can be proven as
positive, and if yes we add the appropriate constraints to the set.

The only exception for that is the RemUI, since the bound is only
dependent on the divider we don't care what's the sign of the lhs.

---------

Co-authored-by: Nir Herscovici <nir.herscovici at mobileye.com>

Added: 
    

Modified: 
    mlir/include/mlir/Interfaces/ValueBoundsOpInterface.h
    mlir/lib/Dialect/Arith/IR/ArithDialect.cpp
    mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
    mlir/lib/Interfaces/ValueBoundsOpInterface.cpp
    mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Interfaces/ValueBoundsOpInterface.h b/mlir/include/mlir/Interfaces/ValueBoundsOpInterface.h
index 434f786c161d8..f6dfa5855d011 100644
--- a/mlir/include/mlir/Interfaces/ValueBoundsOpInterface.h
+++ b/mlir/include/mlir/Interfaces/ValueBoundsOpInterface.h
@@ -314,6 +314,24 @@ class ValueBoundsConstraintSet
   areEquivalentSlices(MLIRContext *ctx, const HyperrectangularSlice &slice1,
                       const HyperrectangularSlice &slice2);
 
+  /// Return "true" if the given value is provably non-negative. If it's
+  /// provably positive, or nothing can be proven, return "false".
+  static bool isProvablyNonNegative(Value value,
+                                    ValueBoundsConstraintSet &cstr);
+
+  /// Return "true" if the given value is provably non-positive. If it's
+  /// provably negative, or nothing can be proven, return "false".
+  static bool isProvablyNonPositive(Value value,
+                                    ValueBoundsConstraintSet &cstr);
+
+  /// Return "true" if the given value is provably positive. If it's provably
+  /// non-negative, or nothing can be proven, return "false".
+  static bool isProvablyPositive(Value value, ValueBoundsConstraintSet &cstr);
+
+  /// Return "true" if the given value is provably negative. If it's provably
+  /// non-positive, or nothing can be proven, return "false".
+  static bool isProvablyNegative(Value value, ValueBoundsConstraintSet &cstr);
+
   /// Add a bound for the given index-typed value or shaped value. This function
   /// returns a builder that adds the bound.
   BoundBuilder bound(Value value) { return BoundBuilder(*this, value); }

diff  --git a/mlir/lib/Dialect/Arith/IR/ArithDialect.cpp b/mlir/lib/Dialect/Arith/IR/ArithDialect.cpp
index ec611ca7924be..5f645af274b8b 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithDialect.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ArithDialect.cpp
@@ -56,7 +56,9 @@ void arith::ArithDialect::initialize() {
   declarePromisedInterfaces<bufferization::BufferizableOpInterface, ConstantOp,
                             IndexCastOp, SelectOp>();
   declarePromisedInterfaces<ValueBoundsOpInterface, AddIOp, ConstantOp, SubIOp,
-                            MulIOp, SelectOp, FloorDivSIOp, MinSIOp, MaxSIOp>();
+                            MulIOp, SelectOp, FloorDivSIOp, CeilDivSIOp,
+                            MinSIOp, MaxSIOp, MinUIOp, MaxUIOp, RemSIOp,
+                            RemUIOp>();
 }
 
 /// Materialize an integer or floating point constant.

diff  --git a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
index 3440c60f169d3..b50eb6719b4ae 100644
--- a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -89,6 +89,84 @@ struct FloorDivSIOpInterface
   }
 };
 
+struct CeilDivSIOpInterface
+    : public ValueBoundsOpInterface::ExternalModel<CeilDivSIOpInterface,
+                                                   CeilDivSIOp> {
+  void populateBoundsForIndexValue(Operation *op, Value value,
+                                   ValueBoundsConstraintSet &cstr) const {
+    auto divSIOp = cast<CeilDivSIOp>(op);
+    assert(value == divSIOp.getResult() && "invalid value");
+
+    AffineExpr lhs = cstr.getExpr(divSIOp.getLhs());
+    AffineExpr rhs = cstr.getExpr(divSIOp.getRhs());
+    cstr.bound(value) == lhs.ceilDiv(rhs);
+  }
+};
+
+struct RemSIOpInterface
+    : public ValueBoundsOpInterface::ExternalModel<RemSIOpInterface, RemSIOp> {
+  void populateBoundsForIndexValue(Operation *op, Value value,
+                                   ValueBoundsConstraintSet &cstr) const {
+    auto remSIOp = cast<RemSIOp>(op);
+    assert(value == remSIOp.getResult() && "invalid value");
+
+    Value lhsValue = remSIOp.getLhs();
+    Value rhsValue = remSIOp.getRhs();
+    AffineExpr rhs = cstr.getExpr(rhsValue);
+    bool rhsPositive =
+        ValueBoundsConstraintSet::isProvablyPositive(rhsValue, cstr);
+    bool rhsNegative =
+        ValueBoundsConstraintSet::isProvablyNegative(rhsValue, cstr);
+
+    // The result of remsi has the same sign as the dividend (lhs) and also
+    // fulfills |result| < |rhs|. The sign of lhs does not need to be a
+    // compile-time constant: it is sufficient if the constraint set can prove
+    // it. For lhs == 0 both branches may fire, which is consistent since the
+    // result is then 0. f.e:
+    //   lhs   rhs   result   bounds
+    //   ----  ----  ------   --------------------------------------------------
+    //    7     3      1      0 <= val && val <= rhs-1 = 2      -> [0, 2]
+    //    7    -3      1      0 <= val && val <= -rhs-1 = 2     -> [0, 2]
+    //   -7     3     -1      val <= 0 && val >= 1-rhs = -2     -> [-2, 0]
+    //   -7    -3     -1      val <= 0 && val >= rhs+1 = -2     -> [-2, 0]
+    //    0     3      0      both lhs branches fire (0<=val and val<=0) -> val
+    //    == 0
+    if (ValueBoundsConstraintSet::isProvablyNonPositive(lhsValue, cstr)) {
+      cstr.bound(value) <= 0;
+      if (rhsPositive)
+        cstr.bound(value) >= 1 - rhs;
+      if (rhsNegative)
+        cstr.bound(value) >= rhs + 1;
+    }
+    if (ValueBoundsConstraintSet::isProvablyNonNegative(lhsValue, cstr)) {
+      cstr.bound(value) >= 0;
+      if (rhsPositive)
+        cstr.bound(value) <= rhs - 1;
+      if (rhsNegative)
+        cstr.bound(value) <= -rhs - 1;
+    }
+  }
+};
+
+struct RemUIOpInterface
+    : public ValueBoundsOpInterface::ExternalModel<RemUIOpInterface, RemUIOp> {
+  void populateBoundsForIndexValue(Operation *op, Value value,
+                                   ValueBoundsConstraintSet &cstr) const {
+    auto remUIOp = cast<RemUIOp>(op);
+    assert(value == remUIOp.getResult() && "invalid value");
+
+    Value rhsValue = remUIOp.getRhs();
+    AffineExpr rhs = cstr.getExpr(rhsValue);
+
+    // remui computes an unsigned remainder, so for a provably positive divisor
+    // the result is always in [0, rhs - 1].
+    if (ValueBoundsConstraintSet::isProvablyPositive(rhsValue, cstr)) {
+      cstr.bound(value) >= 0;
+      cstr.bound(value) <= rhs - 1;
+    }
+  }
+};
+
 struct SelectOpInterface
     : public ValueBoundsOpInterface::ExternalModel<SelectOpInterface,
                                                    SelectOp> {
@@ -190,6 +268,55 @@ struct MaxSIOpInterface
     cstr.bound(value) >= rhs;
   }
 };
+
+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");
+
+    // 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.
+    bool lhsNonNegative =
+        ValueBoundsConstraintSet::isProvablyNonNegative(minOp.getLhs(), cstr);
+    bool rhsNonNegative =
+        ValueBoundsConstraintSet::isProvablyNonNegative(minOp.getRhs(), cstr);
+    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;
+  }
+};
+
+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");
+
+    // See MinUIOpInterface comment
+    bool lhsNonNegative =
+        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;
+  }
+};
 } // namespace
 } // namespace arith
 } // namespace mlir
@@ -202,8 +329,13 @@ void mlir::arith::registerValueBoundsOpInterfaceExternalModels(
     arith::SubIOp::attachInterface<arith::SubIOpInterface>(*ctx);
     arith::MulIOp::attachInterface<arith::MulIOpInterface>(*ctx);
     arith::FloorDivSIOp::attachInterface<arith::FloorDivSIOpInterface>(*ctx);
+    arith::CeilDivSIOp::attachInterface<arith::CeilDivSIOpInterface>(*ctx);
+    arith::RemSIOp::attachInterface<arith::RemSIOpInterface>(*ctx);
+    arith::RemUIOp::attachInterface<arith::RemUIOpInterface>(*ctx);
     arith::SelectOp::attachInterface<arith::SelectOpInterface>(*ctx);
     arith::MinSIOp::attachInterface<arith::MinSIOpInterface>(*ctx);
     arith::MaxSIOp::attachInterface<arith::MaxSIOpInterface>(*ctx);
+    arith::MinUIOp::attachInterface<arith::MinUIOpInterface>(*ctx);
+    arith::MaxUIOp::attachInterface<arith::MaxUIOpInterface>(*ctx);
   });
 }

diff  --git a/mlir/lib/Interfaces/ValueBoundsOpInterface.cpp b/mlir/lib/Interfaces/ValueBoundsOpInterface.cpp
index b2b33b7441c37..02780048f7556 100644
--- a/mlir/lib/Interfaces/ValueBoundsOpInterface.cpp
+++ b/mlir/lib/Interfaces/ValueBoundsOpInterface.cpp
@@ -29,6 +29,34 @@ namespace mlir {
 #include "mlir/Interfaces/ValueBoundsOpInterface.cpp.inc"
 } // namespace mlir
 
+bool ValueBoundsConstraintSet::isProvablyNonNegative(
+    Value value, ValueBoundsConstraintSet &cstr) {
+  return cstr.populateAndCompare(
+      /*lhs=*/{value}, ValueBoundsConstraintSet::ComparisonOperator::GE,
+      /*rhs=*/{OpFoldResult(Builder(value.getContext()).getIndexAttr(0))});
+}
+
+bool ValueBoundsConstraintSet::isProvablyNonPositive(
+    Value value, ValueBoundsConstraintSet &cstr) {
+  return cstr.populateAndCompare(
+      /*lhs=*/{value}, ValueBoundsConstraintSet::ComparisonOperator::LE,
+      /*rhs=*/{OpFoldResult(Builder(value.getContext()).getIndexAttr(0))});
+}
+
+bool ValueBoundsConstraintSet::isProvablyPositive(
+    Value value, ValueBoundsConstraintSet &cstr) {
+  return cstr.populateAndCompare(
+      /*lhs=*/{value}, ValueBoundsConstraintSet::ComparisonOperator::GT,
+      /*rhs=*/{OpFoldResult(Builder(value.getContext()).getIndexAttr(0))});
+}
+
+bool ValueBoundsConstraintSet::isProvablyNegative(
+    Value value, ValueBoundsConstraintSet &cstr) {
+  return cstr.populateAndCompare(
+      /*lhs=*/{value}, ValueBoundsConstraintSet::ComparisonOperator::LT,
+      /*rhs=*/{OpFoldResult(Builder(value.getContext()).getIndexAttr(0))});
+}
+
 static Operation *getOwnerOfValue(Value value) {
   if (auto bbArg = dyn_cast<BlockArgument>(value))
     return bbArg.getOwner()->getParentOp();

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 3f55037f46f09..eb67998c52990 100644
--- a/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
+++ b/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
@@ -112,6 +112,157 @@ func.func @arith_floordivsi_non_pure(%a: index, %b: index) -> index {
 
 // -----
 
+// CHECK: #[[$map:.*]] = affine_map<()[s0] -> ((s0 + 4) floordiv 5)>
+// CHECK-LABEL: func @arith_ceildivsi(
+//  CHECK-SAME:     %[[a:.*]]: index
+//       CHECK:   %[[apply:.*]] = affine.apply #[[$map]]()[%[[a]]]
+//       CHECK:   return %[[apply]]
+func.func @arith_ceildivsi(%a: index) -> index {
+  %0 = arith.constant 5 : index
+  %1 = arith.ceildivsi %a, %0 : index
+  %2 = "test.reify_bound"(%1) : (index) -> (index)
+  return %2 : index
+}
+
+// -----
+
+func.func @arith_ceildivsi_non_pure(%a: index, %b: index) -> index {
+  %0 = arith.ceildivsi %a, %b : index
+  // Semi-affine expressions (such as "symbol * symbol") are not supported.
+  // expected-error @below{{could not reify bound}}
+  %1 = "test.reify_bound"(%0) : (index) -> (index)
+  return %1 : index
+}
+
+// -----
+
+// CHECK-LABEL: func @arith_remsi_positive_positive()
+//       CHECK:   %[[c0:.*]] = arith.constant 0 : index
+//       CHECK:   %[[c5:.*]] = arith.constant 5 : index
+//       CHECK:   return %[[c0]], %[[c5]]
+func.func @arith_remsi_positive_positive() -> (index, index) {
+  %c7 = arith.constant 7 : index
+  %c5 = arith.constant 5 : index
+  %0 = arith.remsi %c7, %c5 : 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_remsi_negative_positive()
+//       CHECK:   %[[cm4:.*]] = arith.constant -4 : index
+//       CHECK:   %[[c1:.*]] = arith.constant 1 : index
+//       CHECK:   return %[[cm4]], %[[c1]]
+func.func @arith_remsi_negative_positive() -> (index, index) {
+  %cm7 = arith.constant -7 : index
+  %c5 = arith.constant 5 : index
+  %0 = arith.remsi %cm7, %c5 : 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_remsi_positive_negative()
+//       CHECK:   %[[c0:.*]] = arith.constant 0 : index
+//       CHECK:   %[[c5:.*]] = arith.constant 5 : index
+//       CHECK:   return %[[c0]], %[[c5]]
+func.func @arith_remsi_positive_negative() -> (index, index) {
+  %c7 = arith.constant 7 : index
+  %cm5 = arith.constant -5 : index
+  %0 = arith.remsi %c7, %cm5 : 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_remsi_negative_negative()
+//       CHECK:   %[[cm4:.*]] = arith.constant -4 : index
+//       CHECK:   %[[c1:.*]] = arith.constant 1 : index
+//       CHECK:   return %[[cm4]], %[[c1]]
+func.func @arith_remsi_negative_negative() -> (index, index) {
+  %cm7 = arith.constant -7 : index
+  %cm5 = arith.constant -5 : index
+  %0 = arith.remsi %cm7, %cm5 : 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_remsi_positive_lhs_symbolic_positive_rhs(
+//  CHECK-SAME:     %[[a:.*]]: index
+//       CHECK:   %[[lb:.*]] = arith.constant 0 : index
+//       CHECK:   return %[[lb]]
+func.func @arith_remsi_positive_lhs_symbolic_positive_rhs(%a: index) -> index {
+  %c1 = arith.constant 1 : index
+  %c7 = arith.constant 7 : index
+  %rhs = arith.maxsi %a, %c1 : index
+  %0 = arith.remsi %c7, %rhs : index
+  %1 = "test.reify_bound"(%0) {type = "LB", constant} : (index) -> (index)
+  return %1 : index
+}
+
+// -----
+
+// CHECK-LABEL: func @arith_remsi_negative_lhs_symbolic_positive_rhs(
+//  CHECK-SAME:     %[[a:.*]]: index
+//       CHECK:   %[[ub:.*]] = arith.constant 1 : index
+//       CHECK:   return %[[ub]]
+func.func @arith_remsi_negative_lhs_symbolic_positive_rhs(%a: index) -> index {
+  %c1 = arith.constant 1 : index
+  %cm7 = arith.constant -7 : index
+  %rhs = arith.maxsi %a, %c1 : index
+  %0 = arith.remsi %cm7, %rhs : index
+  %1 = "test.reify_bound"(%0) {type = "UB", constant} : (index) -> (index)
+  return %1 : index
+}
+
+// -----
+
+// CHECK-LABEL: func @arith_remui_constant()
+//       CHECK:   %[[c0:.*]] = arith.constant 0 : index
+//       CHECK:   %[[c5:.*]] = arith.constant 5 : index
+//       CHECK:   return %[[c0]], %[[c5]]
+func.func @arith_remui_constant() -> (index, index) {
+  %c7 = arith.constant 7 : index
+  %c5 = arith.constant 5 : index
+  %0 = arith.remui %c7, %c5 : 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_remui_symbolic_dividend(
+//  CHECK-SAME:     %[[a:.*]]: index
+//       CHECK:   %[[ub:.*]] = arith.constant 5 : index
+//       CHECK:   return %[[ub]]
+func.func @arith_remui_symbolic_dividend(%a: index) -> index {
+  %c5 = arith.constant 5 : index
+  %0 = arith.remui %a, %c5 : index
+  %1 = "test.reify_bound"(%0) {type = "UB", constant} : (index) -> (index)
+  return %1 : index
+}
+
+// -----
+
+func.func @arith_remui_unknown_divisor(%a: index, %b: index) -> index {
+  %0 = arith.remui %a, %b : index
+  // expected-error @below{{could not reify bound}}
+  %1 = "test.reify_bound"(%0) {type = "UB", constant} : (index) -> (index)
+  return %1 : index
+}
+
+// -----
+
 // CHECK-LABEL: func @arith_const()
 //       CHECK:   %[[c5:.*]] = arith.constant 5 : index
 //       CHECK:   %[[c5:.*]] = arith.constant 5 : index
@@ -217,3 +368,129 @@ func.func @arith_maxsi_ub(%a: index) -> index {
   %1 = "test.reify_bound"(%0) {type = "UB"} : (index) -> (index)
   return %1 : index
 }
+
+// -----
+
+// CHECK-LABEL: func @arith_minui(
+//       CHECK:   %[[ub:.*]] = arith.constant 5 : index
+//       CHECK:   return %[[ub]]
+func.func @arith_minui() -> index {
+  %c4 = arith.constant 4 : index
+  %c10 = arith.constant 10 : index
+  %0 = arith.minui %c10, %c4 : index
+  %1 = "test.reify_bound"(%0) {type = "UB"} : (index) -> (index)
+  return %1 : index
+}
+
+// -----
+
+// CHECK-LABEL: func @arith_maxui(
+//       CHECK:   %[[lb:.*]] = arith.constant 10 : index
+//       CHECK:   return %[[lb]]
+func.func @arith_maxui() -> index {
+  %c4 = arith.constant 4 : index
+  %c10 = arith.constant 10 : index
+  %0 = arith.maxui %c10, %c4 : index
+  %1 = "test.reify_bound"(%0) {type = "LB"} : (index) -> (index)
+  return %1 : index
+}
+
+// -----
+
+func.func @arith_maxui_unknown_sign(%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)
+  return %1 : index
+}
+
+// -----
+
+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
+}
+
+// -----
+
+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
+}
+
+// -----
+
+// CHECK-LABEL: func @arith_maxui_addi(
+//       CHECK:   %[[lb:.*]] = arith.constant 14 : index
+//       CHECK:   return %[[lb]]
+func.func @arith_maxui_addi() -> index {
+  %c4 = arith.constant 4 : index
+  %c10 = arith.constant 10 : index
+  %sum = arith.addi %c4, %c10 : index
+  %0 = arith.maxui %sum, %c4 : index
+  %1 = "test.reify_bound"(%0) {type = "LB", constant} : (index) -> (index)
+  return %1 : index
+}
+
+// -----
+
+// CHECK-LABEL: func @arith_minui_nonneg_symbolic(
+//  CHECK-SAME:     %[[a:.*]]: index
+//       CHECK:   %[[ub:.*]] = arith.constant 5 : index
+//       CHECK:   return %[[ub]]
+func.func @arith_minui_nonneg_symbolic(%a: index) -> index {
+  %c0 = arith.constant 0 : index
+  %c4 = arith.constant 4 : index
+  %nn = arith.maxsi %a, %c0 : index
+  %0 = arith.minui %nn, %c4 : index
+  %1 = "test.reify_bound"(%0) {type = "UB", constant} : (index) -> (index)
+  return %1 : index
+}
+
+// -----
+
+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
+}
+
+// -----
+
+// CHECK-LABEL: func @arith_maxui_nonneg_symbolic(
+//  CHECK-SAME:     %[[a:.*]]: index
+//       CHECK:   %[[lb:.*]] = arith.constant 4 : index
+//       CHECK:   return %[[lb]]
+func.func @arith_maxui_nonneg_symbolic(%a: index) -> index {
+  %c0 = arith.constant 0 : index
+  %c4 = arith.constant 4 : index
+  %nn = arith.maxsi %a, %c0 : index
+  %0 = arith.maxui %nn, %c4 : index
+  %1 = "test.reify_bound"(%0) {type = "LB", constant} : (index) -> (index)
+  return %1 : index
+}
+
+// -----
+
+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