Skip to content

[mlir][Tensor] Check for out-of-bounds slice in insert/extract_slice verifier #130487

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

matthias-springer
Copy link
Member

@matthias-springer matthias-springer commented Mar 9, 2025

Also fix test cases that had invalid ops.

@llvmbot
Copy link
Member

llvmbot commented Mar 9, 2025

@llvm/pr-subscribers-mlir

@llvm/pr-subscribers-mlir-tensor

Author: Matthias Springer (matthias-springer)

Changes

Also fix a test case that had an invalid op.


Full diff: https://github.com/llvm/llvm-project/pull/130487.diff

3 Files Affected:

  • (modified) mlir/lib/Dialect/Tensor/IR/TensorOps.cpp (+35-2)
  • (modified) mlir/test/Dialect/Tensor/bubble-up-extract-slice-op.mlir (+1-1)
  • (modified) mlir/test/Dialect/Tensor/invalid.mlir (+16)
diff --git a/mlir/lib/Dialect/Tensor/IR/TensorOps.cpp b/mlir/lib/Dialect/Tensor/IR/TensorOps.cpp
index e3c8899cd44d9..6dfc05eab08e6 100644
--- a/mlir/lib/Dialect/Tensor/IR/TensorOps.cpp
+++ b/mlir/lib/Dialect/Tensor/IR/TensorOps.cpp
@@ -2354,11 +2354,44 @@ static LogicalResult produceSliceErrorMsg(SliceVerificationResult result,
 
 /// Verifier for ExtractSliceOp.
 LogicalResult ExtractSliceOp::verify() {
+  RankedTensorType sourceType = getSourceType();
+  SmallVector<OpFoldResult> mixedOffsets = getMixedOffsets();
+  SmallVector<OpFoldResult> mixedSizes = getMixedSizes();
+  SmallVector<OpFoldResult> mixedStrides = getMixedStrides();
+
   // Verify result type against inferred type.
   RankedTensorType expectedType = ExtractSliceOp::inferResultType(
-      getSourceType(), getMixedOffsets(), getMixedSizes(), getMixedStrides());
+      sourceType, mixedOffsets, mixedSizes, mixedStrides);
   SliceVerificationResult result = isRankReducedType(expectedType, getType());
-  return produceSliceErrorMsg(result, *this, expectedType);
+  if (result != SliceVerificationResult::Success)
+    return produceSliceErrorMsg(result, *this, expectedType);
+
+  // Verify that offsets, sizes, strides do not run out-of-bounds with respect
+  // to the source tensor.
+  for (int64_t i = 0, e = sourceType.getRank(); i < e; ++i) {
+    // Nothing to verify for dynamic source dims.
+    if (sourceType.isDynamicDim(i))
+      continue;
+    auto offsetOfr = dyn_cast<Attribute>(mixedOffsets[i]);
+    // Nothing to verify if the offset is dynamic.
+    if (!offsetOfr)
+      continue;
+    int64_t staticOffset = *getConstantIntValue(offsetOfr);
+    if (staticOffset >= sourceType.getDimSize(i))
+      return emitOpError("offset ") << i << " is out-of-bounds";
+    auto sizeOfr = dyn_cast<Attribute>(mixedSizes[i]);
+    auto strideOfr = dyn_cast<Attribute>(mixedStrides[i]);
+    if (!sizeOfr || !strideOfr)
+      continue;
+    int64_t staticSize = *getConstantIntValue(sizeOfr);
+    int64_t staticStride = *getConstantIntValue(strideOfr);
+    if (staticOffset + (staticSize - 1) * staticStride >=
+        sourceType.getDimSize(i))
+      return emitOpError("extraction along source dimension ")
+             << i << " runs out-of-bounds";
+  }
+
+  return success();
 }
 
 llvm::SmallBitVector ExtractSliceOp::getDroppedDims() {
diff --git a/mlir/test/Dialect/Tensor/bubble-up-extract-slice-op.mlir b/mlir/test/Dialect/Tensor/bubble-up-extract-slice-op.mlir
index 252e7494bff79..3900bc56f433d 100644
--- a/mlir/test/Dialect/Tensor/bubble-up-extract-slice-op.mlir
+++ b/mlir/test/Dialect/Tensor/bubble-up-extract-slice-op.mlir
@@ -31,7 +31,7 @@ func.func @no_bubble_up_extract_slice_on_non_contiguous(%src: tensor<60xf32>) ->
 
 func.func @no_bubble_up_extract_slice_on_stride(%src: tensor<60xf32>) -> tensor<1x1x5xf32> {
   %expand = tensor.expand_shape %src [[0, 1, 2]] output_shape [2, 3, 10] : tensor<60xf32> into tensor<2x3x10xf32>
-  %extract = tensor.extract_slice %expand[0, 0, 5][1, 1, 5][1, 1, 2] : tensor<2x3x10xf32> to tensor<1x1x5xf32>
+  %extract = tensor.extract_slice %expand[0, 0, 0][1, 1, 5][1, 1, 2] : tensor<2x3x10xf32> to tensor<1x1x5xf32>
   return %extract : tensor<1x1x5xf32>
 }
 
diff --git a/mlir/test/Dialect/Tensor/invalid.mlir b/mlir/test/Dialect/Tensor/invalid.mlir
index 654169841c1c1..64a8585738913 100644
--- a/mlir/test/Dialect/Tensor/invalid.mlir
+++ b/mlir/test/Dialect/Tensor/invalid.mlir
@@ -258,6 +258,22 @@ func.func @illegal_num_offsets(%arg0 : tensor<?x?x?xf32>, %arg1 : index, %arg2 :
 
 // -----
 
+func.func @extract_slice_offset_out_of_bounds(%arg0: tensor<10xf32>) {
+  // expected-error@+1 {{offset 0 is out-of-bounds}}
+  %0 = tensor.extract_slice %arg0 [10][1][1] : tensor<10xf32> to tensor<1xf32>
+  return
+}
+
+// -----
+
+func.func @extract_slice_runs_out_of_bounds(%arg0: tensor<9xf32>) {
+  // expected-error@+1 {{extraction along source dimension 0 runs out-of-bounds}}
+  %0 = tensor.extract_slice %arg0 [3][4][2] : tensor<9xf32> to tensor<4xf32>
+  return
+}
+
+// -----
+
 func.func @insert_slice_wrong_result_rank(%t1: tensor<?xf32>, %t2: tensor<?x?xf32>, %idx : index) {
   // expected-error @+1 {{expected rank to be smaller or equal to the other rank.}}
   %0 = tensor.insert_slice %t2 into %t1[0][4][1] : tensor<?x?xf32> into tensor<?xf32>

@matthias-springer
Copy link
Member Author

matthias-springer commented Mar 9, 2025

Note: This verification is not part of the SliceVerificationResult infrastructure because memref.subview is currently allowed to extract out-of-bounds (according to the op documentation).

@matthias-springer matthias-springer force-pushed the users/matthias-springer/tensor_extract_slice_verifier branch from 106cc32 to 654a40d Compare March 9, 2025 13:28
@matthias-springer matthias-springer changed the title [mlir][Tensor] Check for out-of-bounds extraction in extract_slice verifier [mlir][Tensor] Check for out-of-bounds slice in insert/extract_slice verifier Mar 9, 2025
func.func @insert_slice_of_insert_slice(%t: tensor<f32>, %r0: tensor<1x1xf32>, %r1: tensor<1x14xf32>, %pos: index)
-> tensor<1x14xf32>
{
%0 = tensor.insert_slice %t into %r0[1, 2] [1, 1] [1, 1]
%0 = tensor.insert_slice %t into %r0[0, 0] [1, 1] [1, 1]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making it all zero might be losing some test coverage. Maybe increase the size of the tensor instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This must be [0, 0], otherwise the two insert_slice cannot be merged.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont see why. The existing test is taking an offset of [1, 2] and composing it with [3, %pos] to get [4, %pos + 2] . That seems correct to me. This change is changing the test behavior and potentially dropping coverage.

Copy link
Member Author

@matthias-springer matthias-springer Mar 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous test was incorrect:

// This op is invalid because it inserts into %r0 at out-of-bounds offsets.
%0 = tensor.insert_slice %t into %r0[1, 2] [1, 1] [1, 1]
    : tensor<f32> into tensor<1x1xf32>

To fix it, let's try to make the tensor larger but keep the offsets, as you suggested (actually, we have to make both tensors larger now).

%0 = tensor.insert_slice %t into %r0[1, 2] [1, 1] [1, 1]
    : tensor<f32> into tensor<2x3xf32>
%1 = tensor.insert_slice %0 into %r1[0, %pos] [2, 3] [1, 1] 
    : tensor<2x3xf32> into tensor<2x14xf32>

These two tensor.insert_slice cannot be folded into a single tensor.insert_slice. The reason is because we need to insert both %t and some data from %r0 into %r1.

So the only way to fix the test (so that it still exercises the folding pattern) is to set the offsets to 0.

The existing test is taking an offset of [1, 2] and composing it with [3, %pos] to get [4, %pos + 2] . That seems correct to me. This change is changing the test behavior and potentially dropping coverage.

I think there's no way trigger this composition without the first insert_slice being out-of-bounds. The folding pattern code can probably be simplified.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, that makes sense. Thanks for the explanation.

func.func @insert_slice_of_insert_slice(%t: tensor<f32>, %r0: tensor<1x1xf32>, %r1: tensor<1x14xf32>, %pos: index)
-> tensor<1x14xf32>
{
%0 = tensor.insert_slice %t into %r0[1, 2] [1, 1] [1, 1]
%0 = tensor.insert_slice %t into %r0[0, 0] [1, 1] [1, 1]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont see why. The existing test is taking an offset of [1, 2] and composing it with [3, %pos] to get [4, %pos + 2] . That seems correct to me. This change is changing the test behavior and potentially dropping coverage.

func.func @insert_slice_of_insert_slice(%t: tensor<f32>, %r0: tensor<1x1xf32>, %r1: tensor<1x14xf32>, %pos: index)
-> tensor<1x14xf32>
{
%0 = tensor.insert_slice %t into %r0[1, 2] [1, 1] [1, 1]
%0 = tensor.insert_slice %t into %r0[0, 0] [1, 1] [1, 1]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, that makes sense. Thanks for the explanation.

@matthias-springer matthias-springer merged commit 418e07b into main Mar 12, 2025
11 checks passed
@matthias-springer matthias-springer deleted the users/matthias-springer/tensor_extract_slice_verifier branch March 12, 2025 07:34
bangtianliu added a commit to iree-org/llvm-project that referenced this pull request Mar 13, 2025
bangtianliu added a commit to iree-org/iree that referenced this pull request Mar 13, 2025
bump to
llvm/llvm-project@cdf10d7

cherry-pick revert
commit@[add3564](iree-org/llvm-project@add3564)
to this PR llvm/llvm-project#129850.
add revert commit
[f8fe920](llvm/llvm-project@f8fe920)
to this insert/extract_slice verifier PR:
llvm/llvm-project#130487.

---------

Signed-off-by: Bangtian Liu <[email protected]>
bangtianliu added a commit to iree-org/llvm-project that referenced this pull request Mar 14, 2025
kuhar pushed a commit to iree-org/iree that referenced this pull request Mar 15, 2025
qedawkins pushed a commit to iree-org/llvm-project that referenced this pull request Mar 17, 2025
pxanthopoulos pushed a commit to pxanthopoulos/iree that referenced this pull request Mar 17, 2025
matthias-springer added a commit that referenced this pull request Mar 24, 2025
Since #130487, `tensor.extract_slice` and `tensor.insert_slice` ops that
are statically detected to go out of bounds are rejected by the
verifier.

This commit fixes canonicalization patterns that currently fold
dynamically out-of-bounds ops (valid IR) to statically out-of-bounds ops
(invalid IR).
Eliasj42 pushed a commit to iree-org/iree that referenced this pull request Mar 24, 2025
bump to
llvm/llvm-project@e45090e

cherry-pick revert commit
iree-org/llvm-project@d5d87f9
to llvm/llvm-project#129850

cherry-pick revert commit
iree-org/llvm-project@3e15932
to llvm/llvm-project#130487

add commit
iree-org/llvm-project@a1fc066
to fix msvc debug build.

---------

Signed-off-by: Bangtian Liu <[email protected]>
Signed-off-by: Elias Joseph <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants