[Mlir-commits] [mlir] [mlir][arith] Implement ValueBoundsOpInterface for ExtSI op (PR #204476)
Max Graey
llvmlistbot at llvm.org
Wed Jun 24 05:15:59 PDT 2026
https://github.com/MaxGraey updated https://github.com/llvm/llvm-project/pull/204476
>From 2ab4060e783a65d7c7d84f8ea833a328e8ae7ffd Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 18 Jun 2026 01:07:25 +0300
Subject: [PATCH 1/5] add ExtSI and IndexCastUI ops for pressburger's value
bound
---
mlir/lib/Dialect/Arith/IR/ArithDialect.cpp | 5 +-
.../Arith/IR/ValueBoundsOpInterfaceImpl.cpp | 68 +++++++++++++------
.../Arith/value-bounds-op-interface-impl.mlir | 48 +++++++++++++
3 files changed, 97 insertions(+), 24 deletions(-)
diff --git a/mlir/lib/Dialect/Arith/IR/ArithDialect.cpp b/mlir/lib/Dialect/Arith/IR/ArithDialect.cpp
index ec611ca7924be..ea16839d43a5e 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithDialect.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ArithDialect.cpp
@@ -55,8 +55,9 @@ void arith::ArithDialect::initialize() {
SelectOp>();
declarePromisedInterfaces<bufferization::BufferizableOpInterface, ConstantOp,
IndexCastOp, SelectOp>();
- declarePromisedInterfaces<ValueBoundsOpInterface, AddIOp, ConstantOp, SubIOp,
- MulIOp, SelectOp, FloorDivSIOp, MinSIOp, MaxSIOp>();
+ declarePromisedInterfaces<ValueBoundsOpInterface, ConstantOp, IndexCastUIOp,
+ ExtSIOp, AddIOp, SubIOp, MulIOp, SelectOp,
+ FloorDivSIOp, MinSIOp, MaxSIOp>();
}
/// 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..572e5a149112b 100644
--- a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -17,6 +17,43 @@ namespace mlir {
namespace arith {
namespace {
+struct ConstantOpInterface
+ : public ValueBoundsOpInterface::ExternalModel<ConstantOpInterface,
+ ConstantOp> {
+ void populateBoundsForIndexValue(Operation *op, Value value,
+ ValueBoundsConstraintSet &cstr) const {
+ auto constantOp = cast<ConstantOp>(op);
+ assert(value == constantOp.getResult() && "invalid value");
+
+ if (auto attr = llvm::dyn_cast<IntegerAttr>(constantOp.getValue()))
+ cstr.bound(value) == attr.getInt();
+ }
+};
+
+struct IndexCastUIOpInterface
+ : public ValueBoundsOpInterface::ExternalModel<IndexCastUIOpInterface,
+ IndexCastUIOp> {
+ void populateBoundsForIndexValue(Operation *op, Value value,
+ ValueBoundsConstraintSet &cstr) const {
+ auto indexCastOp = cast<IndexCastUIOp>(op);
+ assert(value == indexCastOp.getOut() && "invalid value");
+ cstr.bound(value) >= 0;
+ }
+};
+
+struct ExtSIOpInterface
+ : public ValueBoundsOpInterface::ExternalModel<ExtSIOpInterface, ExtSIOp> {
+ void populateBoundsForIndexValue(Operation *op, Value value,
+ ValueBoundsConstraintSet &cstr) const {
+ auto extSIOp = cast<ExtSIOp>(op);
+ assert(value == extSIOp.getOut() && "invalid value");
+
+ // Sign extension preserves the signed value (unlike zero extension where
+ // the result may be negative), so the bound is an exact equality.
+ cstr.bound(value) == cstr.getExpr(extSIOp.getIn());
+ }
+};
+
struct AddIOpInterface
: public ValueBoundsOpInterface::ExternalModel<AddIOpInterface, AddIOp> {
void populateBoundsForIndexValue(Operation *op, Value value,
@@ -36,19 +73,6 @@ struct AddIOpInterface
}
};
-struct ConstantOpInterface
- : public ValueBoundsOpInterface::ExternalModel<ConstantOpInterface,
- ConstantOp> {
- void populateBoundsForIndexValue(Operation *op, Value value,
- ValueBoundsConstraintSet &cstr) const {
- auto constantOp = cast<ConstantOp>(op);
- assert(value == constantOp.getResult() && "invalid value");
-
- if (auto attr = llvm::dyn_cast<IntegerAttr>(constantOp.getValue()))
- cstr.bound(value) == attr.getInt();
- }
-};
-
struct SubIOpInterface
: public ValueBoundsOpInterface::ExternalModel<SubIOpInterface, SubIOp> {
void populateBoundsForIndexValue(Operation *op, Value value,
@@ -71,7 +95,7 @@ struct MulIOpInterface
AffineExpr lhs = cstr.getExpr(mulIOp.getLhs());
AffineExpr rhs = cstr.getExpr(mulIOp.getRhs());
- cstr.bound(value) == lhs *rhs;
+ cstr.bound(value) == (lhs * rhs);
}
};
@@ -162,11 +186,10 @@ struct SelectOpInterface
};
struct MinSIOpInterface
- : public ValueBoundsOpInterface::ExternalModel<MinSIOpInterface,
- arith::MinSIOp> {
+ : public ValueBoundsOpInterface::ExternalModel<MinSIOpInterface, MinSIOp> {
void populateBoundsForIndexValue(Operation *op, Value value,
ValueBoundsConstraintSet &cstr) const {
- auto minOp = cast<arith::MinSIOp>(op);
+ auto minOp = cast<MinSIOp>(op);
assert(value == minOp.getResult() && "invalid value");
AffineExpr lhs = cstr.getExpr(minOp.getLhs());
@@ -177,13 +200,12 @@ struct MinSIOpInterface
};
struct MaxSIOpInterface
- : public ValueBoundsOpInterface::ExternalModel<MaxSIOpInterface,
- arith::MaxSIOp> {
+ : public ValueBoundsOpInterface::ExternalModel<MaxSIOpInterface, MaxSIOp> {
void populateBoundsForIndexValue(Operation *op, Value value,
ValueBoundsConstraintSet &cstr) const {
- auto maxOp = cast<arith::MaxSIOp>(op);
+ auto maxOp = cast<MaxSIOp>(op);
assert(value == maxOp.getResult() && "invalid value");
-
+
AffineExpr lhs = cstr.getExpr(maxOp.getLhs());
AffineExpr rhs = cstr.getExpr(maxOp.getRhs());
cstr.bound(value) >= lhs;
@@ -197,8 +219,10 @@ struct MaxSIOpInterface
void mlir::arith::registerValueBoundsOpInterfaceExternalModels(
DialectRegistry ®istry) {
registry.addExtension(+[](MLIRContext *ctx, arith::ArithDialect *dialect) {
- arith::AddIOp::attachInterface<arith::AddIOpInterface>(*ctx);
arith::ConstantOp::attachInterface<arith::ConstantOpInterface>(*ctx);
+ arith::IndexCastUIOp::attachInterface<arith::IndexCastUIOpInterface>(*ctx);
+ arith::ExtSIOp::attachInterface<arith::ExtSIOpInterface>(*ctx);
+ arith::AddIOp::attachInterface<arith::AddIOpInterface>(*ctx);
arith::SubIOp::attachInterface<arith::SubIOpInterface>(*ctx);
arith::MulIOp::attachInterface<arith::MulIOpInterface>(*ctx);
arith::FloorDivSIOp::attachInterface<arith::FloorDivSIOpInterface>(*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 3f55037f46f09..dddbabe3a70b1 100644
--- a/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
+++ b/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
@@ -217,3 +217,51 @@ func.func @arith_maxsi_ub(%a: index) -> index {
%1 = "test.reify_bound"(%0) {type = "UB"} : (index) -> (index)
return %1 : index
}
+
+// -----
+
+// CHECK-LABEL: func @arith_index_castui(
+// CHECK-SAME: %[[a:.*]]: i32
+// CHECK: %[[lb:.*]] = arith.constant 0 : index
+// CHECK: return %[[lb]]
+func.func @arith_index_castui(%a: i32) -> index {
+ %0 = arith.index_castui %a : i32 to index
+ %1 = "test.reify_bound"(%0) {type = "LB"} : (index) -> (index)
+ return %1 : index
+}
+
+// -----
+
+func.func @arith_index_castui_ub(%a: i32) -> index {
+ %0 = arith.index_castui %a : i32 to index
+ // Unsigned cast is non-negative but has no upper bound.
+ // expected-error @below{{could not reify bound}}
+ %1 = "test.reify_bound"(%0) {type = "UB"} : (index) -> (index)
+ return %1 : index
+}
+
+// -----
+
+// CHECK-LABEL: func @arith_extsi(
+// CHECK: %[[c:.*]] = arith.constant -5 : index
+// CHECK: return %[[c]]
+func.func @arith_extsi() -> index {
+ %c_5 = arith.constant -5 : i32
+ %ext = arith.extsi %c_5 : i32 to i64
+ %0 = "test.reify_bound"(%ext) {constant, allow_integer_type} : (i64) -> (index)
+ return %0 : index
+}
+
+// -----
+
+// CHECK-LABEL: func @arith_extsi_propagates_bound(
+// CHECK-SAME: %[[a:.*]]: i32
+// CHECK: %[[ub:.*]] = arith.constant 5 : index
+// CHECK: return %[[ub]]
+func.func @arith_extsi_propagates_bound(%a: i32) -> index {
+ %c4 = arith.constant 4 : i32
+ %min = arith.minsi %a, %c4 : i32
+ %ext = arith.extsi %min : i32 to i64
+ %0 = "test.reify_bound"(%ext) {type = "UB", allow_integer_type} : (i64) -> (index)
+ return %0 : index
+}
>From 42b53912a5052945fe8c038a75977d43bfa2ad5f Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 18 Jun 2026 01:23:34 +0300
Subject: [PATCH 2/5] remove complex sample
---
.../Arith/value-bounds-op-interface-impl.mlir | 14 --------------
1 file changed, 14 deletions(-)
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 dddbabe3a70b1..d226abe29e565 100644
--- a/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
+++ b/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
@@ -251,17 +251,3 @@ func.func @arith_extsi() -> index {
%0 = "test.reify_bound"(%ext) {constant, allow_integer_type} : (i64) -> (index)
return %0 : index
}
-
-// -----
-
-// CHECK-LABEL: func @arith_extsi_propagates_bound(
-// CHECK-SAME: %[[a:.*]]: i32
-// CHECK: %[[ub:.*]] = arith.constant 5 : index
-// CHECK: return %[[ub]]
-func.func @arith_extsi_propagates_bound(%a: i32) -> index {
- %c4 = arith.constant 4 : i32
- %min = arith.minsi %a, %c4 : i32
- %ext = arith.extsi %min : i32 to i64
- %0 = "test.reify_bound"(%ext) {type = "UB", allow_integer_type} : (i64) -> (index)
- return %0 : index
-}
>From 10339faf2b44489824658c3de0eef2ad49cf47d6 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Thu, 18 Jun 2026 01:40:16 +0300
Subject: [PATCH 3/5] trigger infra again
---
mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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 d226abe29e565..b843159c01271 100644
--- a/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
+++ b/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
@@ -242,10 +242,10 @@ func.func @arith_index_castui_ub(%a: i32) -> index {
// -----
-// CHECK-LABEL: func @arith_extsi(
+// CHECK-LABEL: func @arith_extsi_const(
// CHECK: %[[c:.*]] = arith.constant -5 : index
// CHECK: return %[[c]]
-func.func @arith_extsi() -> index {
+func.func @arith_extsi_const() -> index {
%c_5 = arith.constant -5 : i32
%ext = arith.extsi %c_5 : i32 to i64
%0 = "test.reify_bound"(%ext) {constant, allow_integer_type} : (i64) -> (index)
>From 8491da3dbc707ce9acbaff8a3a32a7aa1ddb7431 Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Wed, 24 Jun 2026 15:14:11 +0300
Subject: [PATCH 4/5] remove model for IndexCastUIOp
---
.../Arith/IR/ValueBoundsOpInterfaceImpl.cpp | 12 ----------
.../Arith/value-bounds-op-interface-impl.mlir | 22 -------------------
2 files changed, 34 deletions(-)
diff --git a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
index 572e5a149112b..c260f0eabd0c2 100644
--- a/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -30,17 +30,6 @@ struct ConstantOpInterface
}
};
-struct IndexCastUIOpInterface
- : public ValueBoundsOpInterface::ExternalModel<IndexCastUIOpInterface,
- IndexCastUIOp> {
- void populateBoundsForIndexValue(Operation *op, Value value,
- ValueBoundsConstraintSet &cstr) const {
- auto indexCastOp = cast<IndexCastUIOp>(op);
- assert(value == indexCastOp.getOut() && "invalid value");
- cstr.bound(value) >= 0;
- }
-};
-
struct ExtSIOpInterface
: public ValueBoundsOpInterface::ExternalModel<ExtSIOpInterface, ExtSIOp> {
void populateBoundsForIndexValue(Operation *op, Value value,
@@ -220,7 +209,6 @@ void mlir::arith::registerValueBoundsOpInterfaceExternalModels(
DialectRegistry ®istry) {
registry.addExtension(+[](MLIRContext *ctx, arith::ArithDialect *dialect) {
arith::ConstantOp::attachInterface<arith::ConstantOpInterface>(*ctx);
- arith::IndexCastUIOp::attachInterface<arith::IndexCastUIOpInterface>(*ctx);
arith::ExtSIOp::attachInterface<arith::ExtSIOpInterface>(*ctx);
arith::AddIOp::attachInterface<arith::AddIOpInterface>(*ctx);
arith::SubIOp::attachInterface<arith::SubIOpInterface>(*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 b843159c01271..5b0105c8d6dcc 100644
--- a/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
+++ b/mlir/test/Dialect/Arith/value-bounds-op-interface-impl.mlir
@@ -220,28 +220,6 @@ func.func @arith_maxsi_ub(%a: index) -> index {
// -----
-// CHECK-LABEL: func @arith_index_castui(
-// CHECK-SAME: %[[a:.*]]: i32
-// CHECK: %[[lb:.*]] = arith.constant 0 : index
-// CHECK: return %[[lb]]
-func.func @arith_index_castui(%a: i32) -> index {
- %0 = arith.index_castui %a : i32 to index
- %1 = "test.reify_bound"(%0) {type = "LB"} : (index) -> (index)
- return %1 : index
-}
-
-// -----
-
-func.func @arith_index_castui_ub(%a: i32) -> index {
- %0 = arith.index_castui %a : i32 to index
- // Unsigned cast is non-negative but has no upper bound.
- // expected-error @below{{could not reify bound}}
- %1 = "test.reify_bound"(%0) {type = "UB"} : (index) -> (index)
- return %1 : index
-}
-
-// -----
-
// CHECK-LABEL: func @arith_extsi_const(
// CHECK: %[[c:.*]] = arith.constant -5 : index
// CHECK: return %[[c]]
>From be739e22f16ec567f1636ace1fccbae3783f5ecb Mon Sep 17 00:00:00 2001
From: MaxGraey <maxgraey at gmail.com>
Date: Wed, 24 Jun 2026 15:15:44 +0300
Subject: [PATCH 5/5] cleanup
---
mlir/lib/Dialect/Arith/IR/ArithDialect.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/mlir/lib/Dialect/Arith/IR/ArithDialect.cpp b/mlir/lib/Dialect/Arith/IR/ArithDialect.cpp
index ea16839d43a5e..bf005aea3bf5f 100644
--- a/mlir/lib/Dialect/Arith/IR/ArithDialect.cpp
+++ b/mlir/lib/Dialect/Arith/IR/ArithDialect.cpp
@@ -55,9 +55,9 @@ void arith::ArithDialect::initialize() {
SelectOp>();
declarePromisedInterfaces<bufferization::BufferizableOpInterface, ConstantOp,
IndexCastOp, SelectOp>();
- declarePromisedInterfaces<ValueBoundsOpInterface, ConstantOp, IndexCastUIOp,
- ExtSIOp, AddIOp, SubIOp, MulIOp, SelectOp,
- FloorDivSIOp, MinSIOp, MaxSIOp>();
+ declarePromisedInterfaces<ValueBoundsOpInterface, ConstantOp, ExtSIOp, AddIOp,
+ SubIOp, MulIOp, SelectOp, FloorDivSIOp, MinSIOp,
+ MaxSIOp>();
}
/// Materialize an integer or floating point constant.
More information about the Mlir-commits
mailing list