[Mlir-commits] [mlir] [MLIR] ValueBoundsOpInterface: slice size from source size (PR #221756)
Matthias Springer
llvmlistbot at llvm.org
Sun Oct 4 16:05:06 PDT 2026
https://github.com/matthias-springer updated https://github.com/llvm/llvm-project/pull/221756
>From 8dbb8c30e96d3905feaec0ca0af2c96849f21c0d Mon Sep 17 00:00:00 2001
From: Alexey Kreshchuk <alexey.kreshchuk at mobileye.com>
Date: Mon, 31 Aug 2026 12:35:18 +0300
Subject: [PATCH 1/5] [MLIR] ValueBoundsOpInterface: slice size from source
size
Improve ValueBoundsOpInterface for tensor.extract_slice
by adding an upper bound of `ceil((sourceSize - offset) / stride)`
---
.../Tensor/IR/ValueBoundsOpInterfaceImpl.cpp | 15 ++++
.../value-bounds-op-interface-impl.mlir | 79 +++++++++++++++++++
2 files changed, 94 insertions(+)
diff --git a/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp b/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
index 684cec2d6748a..3296b1bc16018 100644
--- a/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -132,6 +132,21 @@ struct ExtractSliceOpInterface
++ctr;
if (ctr == dim) {
cstr.bound(value)[dim] == extractSliceOp.getMixedSizes()[i];
+ int64_t staticStride = extractSliceOp.getStaticStrides()[i];
+ if (staticStride > 0) {
+ // Encode in-bounds slice constraint:
+ OpFoldResult offset = extractSliceOp.getMixedOffsets()[i];
+ AffineExpr offsetExpr = cstr.getExpr(offset);
+ AffineExpr sourceDimExpr =
+ cstr.getExpr(extractSliceOp.getSource(), i);
+ cstr.bound(value)[dim] <=
+ (sourceDimExpr - offsetExpr).ceilDiv(staticStride);
+ // To derive value[dim] <= source[i] - offset:
+ cstr.bound(extractSliceOp.getSource())[i] >= offsetExpr;
+ // To derive value[dim] <= source[i]:
+ if (auto offsetValue = llvm::dyn_cast_if_present<Value>(offset))
+ cstr.bound(offsetValue) >= 0;
+ }
return;
}
}
diff --git a/mlir/test/Dialect/Tensor/value-bounds-op-interface-impl.mlir b/mlir/test/Dialect/Tensor/value-bounds-op-interface-impl.mlir
index e957cc39831a7..2af2d917ca341 100644
--- a/mlir/test/Dialect/Tensor/value-bounds-op-interface-impl.mlir
+++ b/mlir/test/Dialect/Tensor/value-bounds-op-interface-impl.mlir
@@ -176,6 +176,85 @@ func.func @extract_slice_rank_reduce(%t: tensor<?x?xf32>, %sz: index) -> index {
// -----
+// An in-bounds extract_slice cannot exceed ceil((src - offset) / stride).
+// Default constant upper bounds are open, so size <= 8 reifies as 9.
+
+// CHECK-LABEL: func @extract_slice_in_bounds_constant_ub(
+// CHECK: %[[c9:.*]] = arith.constant 9 : index
+// CHECK: return %[[c9]]
+func.func @extract_slice_in_bounds_constant_ub(%t: tensor<10xf32>, %sz: index) -> index {
+ %0 = tensor.extract_slice %t[2][%sz][1] : tensor<10xf32> to tensor<?xf32>
+ %1 = "test.reify_bound"(%0) {dim = 0, type = "UB", constant} : (tensor<?xf32>) -> (index)
+ return %1 : index
+}
+
+// -----
+
+func.func @extract_slice_in_bounds_compare(%t: tensor<10xf32>, %sz: index) {
+ %c0 = arith.constant 0 : index
+ %c8 = arith.constant 8 : index
+ %0 = tensor.extract_slice %t[2][%sz][1] : tensor<10xf32> to tensor<?xf32>
+ %d = tensor.dim %0, %c0 : tensor<?xf32>
+ // expected-remark @below{{true}}
+ "test.compare"(%d, %c8) {cmp = "LE"} : (index, index) -> ()
+ return
+}
+
+// -----
+
+// src=10, stride=3 allows indices {0,3,6,9}, so size <= 4 = ceil(10/3).
+// floor(10/3) = 3 would be too tight.
+
+func.func @extract_slice_strided_in_bounds(%t: tensor<10xf32>, %sz: index) {
+ %c0 = arith.constant 0 : index
+ %c3 = arith.constant 3 : index
+ %c4 = arith.constant 4 : index
+ %0 = tensor.extract_slice %t[0][%sz][3] : tensor<10xf32> to tensor<?xf32>
+ %d = tensor.dim %0, %c0 : tensor<?xf32>
+ // expected-remark @below{{true}}
+ "test.compare"(%d, %c4) {cmp = "LE"} : (index, index) -> ()
+ // expected-error @below{{unknown}}
+ "test.compare"(%d, %c3) {cmp = "LE"} : (index, index) -> ()
+ return
+}
+
+// -----
+
+// A dynamic offset is non-negative, so size <= src - 0.
+
+func.func @extract_slice_dynamic_offset_in_bounds(%t: tensor<10xf32>, %off: index, %sz: index) {
+ %c0 = arith.constant 0 : index
+ %c10 = arith.constant 10 : index
+ %0 = tensor.extract_slice %t[%off][%sz][1] : tensor<10xf32> to tensor<?xf32>
+ %d = tensor.dim %0, %c0 : tensor<?xf32>
+ // expected-remark @below{{true}}
+ "test.compare"(%d, %c10) {cmp = "LE"} : (index, index) -> ()
+ return
+}
+
+// -----
+
+func.func @extract_slice_rank_reduce_in_bounds(%t: tensor<8x16xf32>, %sz: index) {
+ %c0 = arith.constant 0 : index
+ %c14 = arith.constant 14 : index
+ %0 = tensor.extract_slice %t[0, 2][1, %sz][1, 1] : tensor<8x16xf32> to tensor<?xf32>
+ %d = tensor.dim %0, %c0 : tensor<?xf32>
+ // expected-remark @below{{true}}
+ "test.compare"(%d, %c14) {cmp = "LE"} : (index, index) -> ()
+ return
+}
+
+// -----
+
+func.func @extract_slice_dynamic_stride_no_constant_ub(%t: tensor<10xf32>, %sz: index, %st: index) -> index {
+ %0 = tensor.extract_slice %t[0][%sz][%st] : tensor<10xf32> to tensor<?xf32>
+ // expected-error @below{{could not reify bound}}
+ %1 = "test.reify_bound"(%0) {dim = 0, type = "UB", constant} : (tensor<?xf32>) -> (index)
+ return %1 : index
+}
+
+// -----
+
// CHECK-LABEL: func @insert(
// CHECK-SAME: %[[t:.*]]: tensor<?xf32>
// CHECK: %[[c0:.*]] = arith.constant 0 : index
>From e12ca8c78f6fce8e01fab9385e2454ab8161379c Mon Sep 17 00:00:00 2001
From: Alexey Kreshchuk <alexey.kreshchuk at mobileye.com>
Date: Tue, 8 Sep 2026 15:12:32 +0300
Subject: [PATCH 2/5] Bounds only on value
---
mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp b/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
index 3296b1bc16018..001e1be76e2b6 100644
--- a/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -141,11 +141,8 @@ struct ExtractSliceOpInterface
cstr.getExpr(extractSliceOp.getSource(), i);
cstr.bound(value)[dim] <=
(sourceDimExpr - offsetExpr).ceilDiv(staticStride);
- // To derive value[dim] <= source[i] - offset:
- cstr.bound(extractSliceOp.getSource())[i] >= offsetExpr;
- // To derive value[dim] <= source[i]:
- if (auto offsetValue = llvm::dyn_cast_if_present<Value>(offset))
- cstr.bound(offsetValue) >= 0;
+ cstr.bound(value)[dim] <= sourceDimExpr - offsetExpr;
+ cstr.bound(value)[dim] <= sourceDimExpr;
}
return;
}
>From 7a18f005163afdd55355e128c4053e79abb04905 Mon Sep 17 00:00:00 2001
From: Alexey Kreshchuk <alexey.kreshchuk at mobileye.com>
Date: Mon, 14 Sep 2026 17:18:55 +0300
Subject: [PATCH 3/5] Use populateConstaints to fix regression
---
mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp b/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
index 001e1be76e2b6..639c5e333960c 100644
--- a/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -134,6 +134,8 @@ struct ExtractSliceOpInterface
cstr.bound(value)[dim] == extractSliceOp.getMixedSizes()[i];
int64_t staticStride = extractSliceOp.getStaticStrides()[i];
if (staticStride > 0) {
+ if (auto sizeValue = llvm::dyn_cast<Value>(extractSliceOp.getMixedSizes()[i]))
+ cstr.populateConstraints(sizeValue, std::nullopt);
// Encode in-bounds slice constraint:
OpFoldResult offset = extractSliceOp.getMixedOffsets()[i];
AffineExpr offsetExpr = cstr.getExpr(offset);
>From dd07a8aab9f8fe3d7f011b2c32b18e11da5f7e6b Mon Sep 17 00:00:00 2001
From: Alexey Kreshchuk <alexey.kreshchuk at mobileye.com>
Date: Mon, 14 Sep 2026 17:23:45 +0300
Subject: [PATCH 4/5] format
---
mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp b/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
index 639c5e333960c..271715aa51bec 100644
--- a/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -134,7 +134,8 @@ struct ExtractSliceOpInterface
cstr.bound(value)[dim] == extractSliceOp.getMixedSizes()[i];
int64_t staticStride = extractSliceOp.getStaticStrides()[i];
if (staticStride > 0) {
- if (auto sizeValue = llvm::dyn_cast<Value>(extractSliceOp.getMixedSizes()[i]))
+ if (auto sizeValue =
+ llvm::dyn_cast<Value>(extractSliceOp.getMixedSizes()[i]))
cstr.populateConstraints(sizeValue, std::nullopt);
// Encode in-bounds slice constraint:
OpFoldResult offset = extractSliceOp.getMixedOffsets()[i];
>From f503f2b49319e7ece790771941df794436ceb579 Mon Sep 17 00:00:00 2001
From: Alexey Kreshchuk <alexey.kreshchuk at mobileye.com>
Date: Tue, 29 Sep 2026 17:03:37 +0300
Subject: [PATCH 5/5] Add comment about populateConstraints
---
mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp b/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
index 271715aa51bec..56ecccd681aaf 100644
--- a/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.cpp
@@ -134,6 +134,12 @@ struct ExtractSliceOpInterface
cstr.bound(value)[dim] == extractSliceOp.getMixedSizes()[i];
int64_t staticStride = extractSliceOp.getStaticStrides()[i];
if (staticStride > 0) {
+ // Force analysis of the size value to get a better bound. Example:
+ // %size = affine.min affine_map<(d0) -> (5, -d0 + 24)>(%iv)
+ // %slice = tensor.extract_slice %source[%iv][%size][1]
+ // : tensor<24xf32> to tensor<?xf32>
+ // Here we get a bound of 24 immediately, so %size is skipped under
+ // the default stop condition unless we force its analysis.
if (auto sizeValue =
llvm::dyn_cast<Value>(extractSliceOp.getMixedSizes()[i]))
cstr.populateConstraints(sizeValue, std::nullopt);
More information about the Mlir-commits
mailing list