[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