[Mlir-commits] [mlir] [mlir][tensor] Add runtime verification for `cast`/`dim`/`extract`/`insert`/`extract_slice` (PR #141332)

Matthias Springer llvmlistbot at llvm.org
Fri May 23 21:59:43 PDT 2025


https://github.com/matthias-springer created https://github.com/llvm/llvm-project/pull/141332

Add `RuntimeVerifiableOpInterface` implementations for the following ops. These were mostly copied from the respective memref implementations. Only the part that deals with offsets and strides was removed.
* `tensor.cast`: `memref.cast`
* `tensor.dim`: `memref.dim`
* `tensor.extract`: `memref.load`
* `tensor.insert`: `memref.store`
* `tensor.extract_slice`: `memref.subview`


>From 20452381597a943ad2a714f006984e12b5c8e543 Mon Sep 17 00:00:00 2001
From: Matthias Springer <mspringer at nvidia.com>
Date: Sat, 24 May 2025 06:51:09 +0200
Subject: [PATCH] [mlir][tensor] Add runtime verification for
 `cast`/`dim`/`extract`/`insert`/`extract_slice`

---
 .../Tensor/Transforms/RuntimeOpVerification.h |  21 ++
 mlir/include/mlir/InitAllDialects.h           |   2 +
 .../Dialect/Tensor/Transforms/CMakeLists.txt  |   1 +
 .../Transforms/RuntimeOpVerification.cpp      | 208 ++++++++++++++++++
 .../Tensor/cast-runtime-verification.mlir     |  50 +++++
 .../Tensor/dim-runtime-verification.mlir      |  21 ++
 .../Tensor/extract-runtime-verification.mlir  |  64 ++++++
 .../extract_slice-runtime-verification.mlir   |  95 ++++++++
 8 files changed, 462 insertions(+)
 create mode 100644 mlir/include/mlir/Dialect/Tensor/Transforms/RuntimeOpVerification.h
 create mode 100644 mlir/lib/Dialect/Tensor/Transforms/RuntimeOpVerification.cpp
 create mode 100644 mlir/test/Integration/Dialect/Tensor/cast-runtime-verification.mlir
 create mode 100644 mlir/test/Integration/Dialect/Tensor/dim-runtime-verification.mlir
 create mode 100644 mlir/test/Integration/Dialect/Tensor/extract-runtime-verification.mlir
 create mode 100644 mlir/test/Integration/Dialect/Tensor/extract_slice-runtime-verification.mlir

diff --git a/mlir/include/mlir/Dialect/Tensor/Transforms/RuntimeOpVerification.h b/mlir/include/mlir/Dialect/Tensor/Transforms/RuntimeOpVerification.h
new file mode 100644
index 0000000000000..812c4dd6d2cb7
--- /dev/null
+++ b/mlir/include/mlir/Dialect/Tensor/Transforms/RuntimeOpVerification.h
@@ -0,0 +1,21 @@
+//===- RuntimeOpVerification.h - Op Verification ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_DIALECT_TENSOR_RUNTIMEOPVERIFICATION_H
+#define MLIR_DIALECT_TENSOR_RUNTIMEOPVERIFICATION_H
+
+namespace mlir {
+class DialectRegistry;
+
+namespace tensor {
+void registerRuntimeVerifiableOpInterfaceExternalModels(
+    DialectRegistry &registry);
+} // namespace tensor
+} // namespace mlir
+
+#endif // MLIR_DIALECT_TENSOR_RUNTIMEOPVERIFICATION_H
diff --git a/mlir/include/mlir/InitAllDialects.h b/mlir/include/mlir/InitAllDialects.h
index ea285ac7f16e3..261b0e00bdf86 100644
--- a/mlir/include/mlir/InitAllDialects.h
+++ b/mlir/include/mlir/InitAllDialects.h
@@ -84,6 +84,7 @@
 #include "mlir/Dialect/Tensor/IR/ValueBoundsOpInterfaceImpl.h"
 #include "mlir/Dialect/Tensor/TransformOps/TensorTransformOps.h"
 #include "mlir/Dialect/Tensor/Transforms/BufferizableOpInterfaceImpl.h"
+#include "mlir/Dialect/Tensor/Transforms/RuntimeOpVerification.h"
 #include "mlir/Dialect/Tensor/Transforms/SubsetInsertionOpInterfaceImpl.h"
 #include "mlir/Dialect/Tosa/IR/ShardingInterfaceImpl.h"
 #include "mlir/Dialect/Tosa/IR/TosaOps.h"
@@ -186,6 +187,7 @@ inline void registerAllDialects(DialectRegistry &registry) {
   tensor::registerBufferizableOpInterfaceExternalModels(registry);
   tensor::registerFindPayloadReplacementOpInterfaceExternalModels(registry);
   tensor::registerInferTypeOpInterfaceExternalModels(registry);
+  tensor::registerRuntimeVerifiableOpInterfaceExternalModels(registry);
   tensor::registerSubsetOpInterfaceExternalModels(registry);
   tensor::registerTilingInterfaceExternalModels(registry);
   tensor::registerValueBoundsOpInterfaceExternalModels(registry);
diff --git a/mlir/lib/Dialect/Tensor/Transforms/CMakeLists.txt b/mlir/lib/Dialect/Tensor/Transforms/CMakeLists.txt
index 7880d1c5a0c5d..ff84463cc507f 100644
--- a/mlir/lib/Dialect/Tensor/Transforms/CMakeLists.txt
+++ b/mlir/lib/Dialect/Tensor/Transforms/CMakeLists.txt
@@ -8,6 +8,7 @@ add_mlir_dialect_library(MLIRTensorTransforms
   MergeConsecutiveInsertExtractSlicePatterns.cpp
   ReshapePatterns.cpp
   RewriteAsConstant.cpp
+  RuntimeOpVerification.cpp
   SwapExtractSliceWithProducerPatterns.cpp
   SubsetInsertionOpInterfaceImpl.cpp
 
diff --git a/mlir/lib/Dialect/Tensor/Transforms/RuntimeOpVerification.cpp b/mlir/lib/Dialect/Tensor/Transforms/RuntimeOpVerification.cpp
new file mode 100644
index 0000000000000..6138821ee8c61
--- /dev/null
+++ b/mlir/lib/Dialect/Tensor/Transforms/RuntimeOpVerification.cpp
@@ -0,0 +1,208 @@
+//===- RuntimeOpVerification.cpp - Op Verification ------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Dialect/Tensor/Transforms/RuntimeOpVerification.h"
+
+#include "mlir/Dialect/Arith/IR/Arith.h"
+#include "mlir/Dialect/Arith/Utils/Utils.h"
+#include "mlir/Dialect/ControlFlow/IR/ControlFlow.h"
+#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
+#include "mlir/Dialect/Tensor/IR/Tensor.h"
+#include "mlir/Dialect/Utils/IndexingUtils.h"
+#include "mlir/Interfaces/RuntimeVerifiableOpInterface.h"
+
+using namespace mlir;
+
+namespace mlir {
+namespace tensor {
+namespace {
+/// Generate a runtime check for lb <= value < ub.
+Value generateInBoundsCheck(OpBuilder &builder, Location loc, Value value,
+                            Value lb, Value ub) {
+  Value inBounds1 = builder.createOrFold<arith::CmpIOp>(
+      loc, arith::CmpIPredicate::sge, value, lb);
+  Value inBounds2 = builder.createOrFold<arith::CmpIOp>(
+      loc, arith::CmpIPredicate::slt, value, ub);
+  Value inBounds =
+      builder.createOrFold<arith::AndIOp>(loc, inBounds1, inBounds2);
+  return inBounds;
+}
+
+struct CastOpInterface
+    : public RuntimeVerifiableOpInterface::ExternalModel<CastOpInterface,
+                                                         CastOp> {
+  void generateRuntimeVerification(Operation *op, OpBuilder &builder,
+                                   Location loc) const {
+    auto castOp = cast<CastOp>(op);
+    auto srcType = cast<TensorType>(castOp.getSource().getType());
+
+    // Nothing to check if the result is an unranked tensor.
+    auto resultType = dyn_cast<RankedTensorType>(castOp.getType());
+    if (!resultType)
+      return;
+
+    if (isa<UnrankedTensorType>(srcType)) {
+      // Check rank.
+      Value srcRank = builder.create<RankOp>(loc, castOp.getSource());
+      Value resultRank =
+          builder.create<arith::ConstantIndexOp>(loc, resultType.getRank());
+      Value isSameRank = builder.create<arith::CmpIOp>(
+          loc, arith::CmpIPredicate::eq, srcRank, resultRank);
+      builder.create<cf::AssertOp>(
+          loc, isSameRank,
+          RuntimeVerifiableOpInterface::generateErrorMessage(op,
+                                                             "rank mismatch"));
+    }
+
+    // Check dimension sizes.
+    for (const auto &it : llvm::enumerate(resultType.getShape())) {
+      // Static dim size -> static/dynamic dim size does not need verification.
+      if (auto rankedSrcType = dyn_cast<RankedTensorType>(srcType))
+        if (!rankedSrcType.isDynamicDim(it.index()))
+          continue;
+
+      // Static/dynamic dim size -> dynamic dim size does not need verification.
+      if (resultType.isDynamicDim(it.index()))
+        continue;
+
+      Value srcDimSz =
+          builder.create<DimOp>(loc, castOp.getSource(), it.index());
+      Value resultDimSz =
+          builder.create<arith::ConstantIndexOp>(loc, it.value());
+      Value isSameSz = builder.create<arith::CmpIOp>(
+          loc, arith::CmpIPredicate::eq, srcDimSz, resultDimSz);
+      builder.create<cf::AssertOp>(
+          loc, isSameSz,
+          RuntimeVerifiableOpInterface::generateErrorMessage(
+              op, "size mismatch of dim " + std::to_string(it.index())));
+    }
+  }
+};
+
+struct DimOpInterface
+    : public RuntimeVerifiableOpInterface::ExternalModel<DimOpInterface,
+                                                         DimOp> {
+  void generateRuntimeVerification(Operation *op, OpBuilder &builder,
+                                   Location loc) const {
+    auto dimOp = cast<DimOp>(op);
+    Value rank = builder.create<RankOp>(loc, dimOp.getSource());
+    Value zero = builder.create<arith::ConstantIndexOp>(loc, 0);
+    builder.create<cf::AssertOp>(
+        loc, generateInBoundsCheck(builder, loc, dimOp.getIndex(), zero, rank),
+        RuntimeVerifiableOpInterface::generateErrorMessage(
+            op, "index is out of bounds"));
+  }
+};
+
+/// Verifies that the indices on extract/insert ops are in-bounds of the
+/// tensor's index space: 0 <= index#i < dim#i
+template <typename OpTy>
+struct ExtractInsertOpInterface
+    : public RuntimeVerifiableOpInterface::ExternalModel<
+          ExtractInsertOpInterface<OpTy>, OpTy> {
+  void generateRuntimeVerification(Operation *op, OpBuilder &builder,
+                                   Location loc) const {
+    auto extractInsertOp = cast<OpTy>(op);
+
+    Value tensor;
+    if constexpr (std::is_same_v<OpTy, ExtractOp>) {
+      tensor = extractInsertOp.getTensor();
+    } else if constexpr (std::is_same_v<OpTy, InsertOp>) {
+      tensor = extractInsertOp.getDest();
+    } else {
+      llvm_unreachable("invalid op");
+    }
+    auto tensorType = cast<RankedTensorType>(tensor.getType());
+    auto rank = tensorType.getRank();
+    if (rank == 0) {
+      // Nothing to check for 0-d tensors.
+      return;
+    }
+
+    auto indices = extractInsertOp.getIndices();
+    auto zero = builder.create<arith::ConstantIndexOp>(loc, 0);
+    Value assertCond;
+    for (auto i : llvm::seq<int64_t>(0, rank)) {
+      Value dimOp = builder.createOrFold<tensor::DimOp>(loc, tensor, i);
+      Value inBounds =
+          generateInBoundsCheck(builder, loc, indices[i], zero, dimOp);
+      assertCond =
+          i > 0 ? builder.createOrFold<arith::AndIOp>(loc, assertCond, inBounds)
+                : inBounds;
+    }
+    builder.create<cf::AssertOp>(
+        loc, assertCond,
+        RuntimeVerifiableOpInterface::generateErrorMessage(
+            op, "out-of-bounds access"));
+  }
+};
+
+struct ExtractSliceOpInterface
+    : public RuntimeVerifiableOpInterface::ExternalModel<
+          ExtractSliceOpInterface, ExtractSliceOp> {
+  void generateRuntimeVerification(Operation *op, OpBuilder &builder,
+                                   Location loc) const {
+    auto extractSliceOp = cast<ExtractSliceOp>(op);
+    RankedTensorType sourceType = extractSliceOp.getSource().getType();
+
+    // For each dimension, assert that:
+    // 0 <= offset < dim_size
+    // 0 <= offset + (size - 1) * stride < dim_size
+    Value zero = builder.create<arith::ConstantIndexOp>(loc, 0);
+    Value one = builder.create<arith::ConstantIndexOp>(loc, 1);
+    for (int64_t i = 0, e = sourceType.getRank(); i < e; ++i) {
+      Value offset = getValueOrCreateConstantIndexOp(
+          builder, loc, extractSliceOp.getMixedOffsets()[i]);
+      Value size = getValueOrCreateConstantIndexOp(
+          builder, loc, extractSliceOp.getMixedSizes()[i]);
+      Value stride = getValueOrCreateConstantIndexOp(
+          builder, loc, extractSliceOp.getMixedStrides()[i]);
+
+      // Verify that offset is in-bounds.
+      Value dimSize = builder.createOrFold<tensor::DimOp>(
+          loc, extractSliceOp.getSource(), i);
+      Value offsetInBounds =
+          generateInBoundsCheck(builder, loc, offset, zero, dimSize);
+      builder.create<cf::AssertOp>(
+          loc, offsetInBounds,
+          RuntimeVerifiableOpInterface::generateErrorMessage(
+              op, "offset " + std::to_string(i) + " is out-of-bounds"));
+
+      // Verify that slice does not run out-of-bounds.
+      Value sizeMinusOne = builder.create<arith::SubIOp>(loc, size, one);
+      Value sizeMinusOneTimesStride =
+          builder.create<arith::MulIOp>(loc, sizeMinusOne, stride);
+      Value lastPos =
+          builder.create<arith::AddIOp>(loc, offset, sizeMinusOneTimesStride);
+      Value lastPosInBounds =
+          generateInBoundsCheck(builder, loc, lastPos, zero, dimSize);
+      builder.create<cf::AssertOp>(
+          loc, lastPosInBounds,
+          RuntimeVerifiableOpInterface::generateErrorMessage(
+              op, "extract_slice runs out-of-bounds along dimension " +
+                      std::to_string(i)));
+    }
+  }
+};
+} // namespace
+} // namespace tensor
+} // namespace mlir
+
+void mlir::tensor::registerRuntimeVerifiableOpInterfaceExternalModels(
+    DialectRegistry &registry) {
+  registry.addExtension(+[](MLIRContext *ctx, tensor::TensorDialect *dialect) {
+    CastOp::attachInterface<CastOpInterface>(*ctx);
+    DimOp::attachInterface<DimOpInterface>(*ctx);
+    ExtractOp::attachInterface<ExtractInsertOpInterface<ExtractOp>>(*ctx);
+    ExtractSliceOp::attachInterface<ExtractSliceOpInterface>(*ctx);
+    InsertOp::attachInterface<ExtractInsertOpInterface<InsertOp>>(*ctx);
+
+    // Load additional dialects of which ops may get created.
+    ctx->loadDialect<arith::ArithDialect, cf::ControlFlowDialect>();
+  });
+}
diff --git a/mlir/test/Integration/Dialect/Tensor/cast-runtime-verification.mlir b/mlir/test/Integration/Dialect/Tensor/cast-runtime-verification.mlir
new file mode 100644
index 0000000000000..e4aab32d4a390
--- /dev/null
+++ b/mlir/test/Integration/Dialect/Tensor/cast-runtime-verification.mlir
@@ -0,0 +1,50 @@
+// RUN: mlir-opt %s -generate-runtime-verification \
+// RUN:     -one-shot-bufferize="bufferize-function-boundaries" \
+// RUN:     -buffer-deallocation-pipeline=private-function-dynamic-ownership \
+// RUN:     -test-cf-assert \
+// RUN:     -convert-scf-to-cf \
+// RUN:     -convert-to-llvm | \
+// RUN: mlir-runner -e main -entry-point-result=void \
+// RUN:     -shared-libs=%tlir_runner_utils 2>&1 | \
+// RUN: FileCheck %s
+
+func.func private @cast_to_static_dim(%t: tensor<?xf32>) -> tensor<10xf32> {
+  %0 = tensor.cast %t : tensor<?xf32> to tensor<10xf32>
+  return %0 : tensor<10xf32>
+}
+
+func.func private @cast_to_ranked(%t: tensor<*xf32>) -> tensor<f32> {
+  %0 = tensor.cast %t : tensor<*xf32> to tensor<f32>
+  return %0 : tensor<f32>
+}
+
+func.func private @valid_cast(%t: tensor<*xf32>) -> tensor<?xf32> {
+  %0 = tensor.cast %t : tensor<*xf32> to tensor<?xf32>
+  return %0 : tensor<?xf32>
+}
+
+func.func @main() {
+  // All casts inside the called functions are invalid at runtime, except for
+  // the last one.
+  %alloc = tensor.empty() : tensor<5xf32>
+
+  //      CHECK: ERROR: Runtime op verification failed
+  // CHECK-NEXT: "tensor.cast"(%{{.*}}) : (tensor<?xf32>) -> tensor<10xf32>
+  // CHECK-NEXT: ^ size mismatch of dim 0
+  // CHECK-NEXT: Location: loc({{.*}})
+  %1 = tensor.cast %alloc : tensor<5xf32> to tensor<?xf32>
+  func.call @cast_to_static_dim(%1) : (tensor<?xf32>) -> (tensor<10xf32>)
+
+  // CHECK-NEXT: ERROR: Runtime op verification failed
+  // CHECK-NEXT: "tensor.cast"(%{{.*}}) : (tensor<*xf32>) -> tensor<f32>
+  // CHECK-NEXT: ^ rank mismatch
+  // CHECK-NEXT: Location: loc({{.*}})
+  %3 = tensor.cast %alloc : tensor<5xf32> to tensor<*xf32>
+  func.call @cast_to_ranked(%3) : (tensor<*xf32>) -> (tensor<f32>)
+
+  // A last cast that actually succeeds.
+  // CHECK-NOT: ERROR: Runtime op verification failed
+  func.call @valid_cast(%3) : (tensor<*xf32>) -> (tensor<?xf32>)
+
+  return
+}
diff --git a/mlir/test/Integration/Dialect/Tensor/dim-runtime-verification.mlir b/mlir/test/Integration/Dialect/Tensor/dim-runtime-verification.mlir
new file mode 100644
index 0000000000000..c6d8f698b9433
--- /dev/null
+++ b/mlir/test/Integration/Dialect/Tensor/dim-runtime-verification.mlir
@@ -0,0 +1,21 @@
+// RUN: mlir-opt %s -generate-runtime-verification \
+// RUN:     -one-shot-bufferize \
+// RUN:     -buffer-deallocation-pipeline \
+// RUN:     -test-cf-assert \
+// RUN:     -convert-to-llvm | \
+// RUN: mlir-runner -e main -entry-point-result=void \
+// RUN:     -shared-libs=%mlir_runner_utils 2>&1 | \
+// RUN: FileCheck %s
+
+func.func @main() {
+  %c4 = arith.constant 4 : index
+  %tensor = tensor.empty() : tensor<1xf32>
+
+  //      CHECK: ERROR: Runtime op verification failed
+  // CHECK-NEXT: "tensor.dim"(%{{.*}}, %{{.*}}) : (tensor<1xf32>, index) -> index
+  // CHECK-NEXT: ^ index is out of bounds
+  // CHECK-NEXT: Location: loc({{.*}})
+  %dim = tensor.dim %tensor, %c4 : tensor<1xf32>
+
+  return
+}
diff --git a/mlir/test/Integration/Dialect/Tensor/extract-runtime-verification.mlir b/mlir/test/Integration/Dialect/Tensor/extract-runtime-verification.mlir
new file mode 100644
index 0000000000000..8e3cab7be704d
--- /dev/null
+++ b/mlir/test/Integration/Dialect/Tensor/extract-runtime-verification.mlir
@@ -0,0 +1,64 @@
+// RUN: mlir-opt %s -generate-runtime-verification \
+// RUN:     -one-shot-bufferize="bufferize-function-boundaries" \
+// RUN:     -buffer-deallocation-pipeline=private-function-dynamic-ownership \
+// RUN:     -test-cf-assert \
+// RUN:     -convert-scf-to-cf \
+// RUN:     -convert-to-llvm | \
+// RUN: mlir-runner -e main -entry-point-result=void \
+// RUN:     -shared-libs=%tlir_runner_utils 2>&1 | \
+// RUN: FileCheck %s
+
+func.func @extract(%tensor: tensor<1xf32>, %index: index) {
+    tensor.extract %tensor[%index] :  tensor<1xf32>
+    return
+}
+
+func.func @extract_dynamic(%tensor: tensor<?xf32>, %index: index) {
+    tensor.extract %tensor[%index] :  tensor<?xf32>
+    return
+}
+
+func.func @extract_nd_dynamic(%tensor: tensor<?x?x?xf32>, %index0: index, %index1: index, %index2: index) {
+    tensor.extract %tensor[%index0, %index1, %index2] :  tensor<?x?x?xf32>
+    return
+}
+
+func.func @main() {
+  %0 = arith.constant 0 : index
+  %1 = arith.constant 1 : index
+  %n1 = arith.constant -1 : index
+  %2 = arith.constant 2 : index
+  %alloca_1 = tensor.empty() : tensor<1xf32>
+  %alloc_1 = tensor.empty(%1) : tensor<?xf32>
+  %alloc_2x2x2 = tensor.empty(%2, %2, %2) : tensor<?x?x?xf32>
+
+  //      CHECK: ERROR: Runtime op verification failed
+  // CHECK-NEXT: "tensor.extract"(%{{.*}}, %{{.*}}) : (tensor<1xf32>, index) -> f32
+  // CHECK-NEXT: ^ out-of-bounds access
+  // CHECK-NEXT: Location: loc({{.*}})
+  func.call @extract(%alloca_1, %1) : (tensor<1xf32>, index) -> ()
+
+  //      CHECK: ERROR: Runtime op verification failed
+  // CHECK-NEXT: "tensor.extract"(%{{.*}}, %{{.*}}) : (tensor<?xf32>, index) -> f32
+  // CHECK-NEXT: ^ out-of-bounds access
+  // CHECK-NEXT: Location: loc({{.*}})
+  func.call @extract_dynamic(%alloc_1, %1) : (tensor<?xf32>, index) -> ()
+
+  //      CHECK: ERROR: Runtime op verification failed
+  // CHECK-NEXT: "tensor.extract"(%{{.*}}, %{{.*}}) : (tensor<?x?x?xf32>, index, index, index) -> f32
+  // CHECK-NEXT: ^ out-of-bounds access
+  // CHECK-NEXT: Location: loc({{.*}})
+  func.call @extract_nd_dynamic(%alloc_2x2x2, %1, %n1, %0) : (tensor<?x?x?xf32>, index, index, index) -> ()
+
+  // CHECK-NOT: ERROR: Runtime op verification failed
+  func.call @extract(%alloca_1, %0) : (tensor<1xf32>, index) -> ()
+
+  // CHECK-NOT: ERROR: Runtime op verification failed
+  func.call @extract_dynamic(%alloc_1, %0) : (tensor<?xf32>, index) -> ()
+
+  // CHECK-NOT: ERROR: Runtime op verification failed
+  func.call @extract_nd_dynamic(%alloc_2x2x2, %1, %1, %0) : (tensor<?x?x?xf32>, index, index, index) -> ()
+
+  return
+}
+
diff --git a/mlir/test/Integration/Dialect/Tensor/extract_slice-runtime-verification.mlir b/mlir/test/Integration/Dialect/Tensor/extract_slice-runtime-verification.mlir
new file mode 100644
index 0000000000000..28f9be0fffe64
--- /dev/null
+++ b/mlir/test/Integration/Dialect/Tensor/extract_slice-runtime-verification.mlir
@@ -0,0 +1,95 @@
+// RUN: mlir-opt %s -generate-runtime-verification \
+// RUN:     -one-shot-bufferize="bufferize-function-boundaries" \
+// RUN:     -buffer-deallocation-pipeline=private-function-dynamic-ownership \
+// RUN:     -test-cf-assert \
+// RUN:     -convert-scf-to-cf \
+// RUN:     -convert-to-llvm | \
+// RUN: mlir-runner -e main -entry-point-result=void \
+// RUN:     -shared-libs=%tlir_runner_utils 2>&1 | \
+// RUN: FileCheck %s
+
+func.func @extract_slice(%tensor: tensor<1xf32>, %offset: index) {
+    tensor.extract_slice %tensor[%offset] [1] [1] : tensor<1xf32> to tensor<1xf32>
+    return
+}
+
+func.func @extract_slice_dynamic(%tensor: tensor<?x4xf32>, %offset: index, %size: index, %stride: index) {
+    tensor.extract_slice %tensor[%offset, 0] [%size, 4] [%stride, 1] : tensor<?x4xf32> to tensor<?x4xf32>
+    return
+}
+
+func.func @extract_slice_dynamic_rank_reduce(%tensor: tensor<?x4xf32>, %offset: index, %size: index, %stride: index) {
+    tensor.extract_slice %tensor[%offset, 0] [%size, 1] [%stride, 1] : tensor<?x4xf32> to tensor<?xf32>
+    return
+}
+
+func.func @main() {
+  %0 = arith.constant 0 : index
+  %1 = arith.constant 1 : index
+  %n1 = arith.constant -1 : index
+  %4 = arith.constant 4 : index
+  %5 = arith.constant 5 : index
+
+  %alloca = tensor.empty() : tensor<1xf32>
+  %alloca_4 = tensor.empty() : tensor<4x4xf32>
+  %alloca_4_dyn = tensor.cast %alloca_4 : tensor<4x4xf32> to tensor<?x4xf32>
+
+  // Offset is out-of-bounds and slice runs out-of-bounds
+  //      CHECK: ERROR: Runtime op verification failed
+  // CHECK-NEXT: "tensor.extract_slice"(%arg0, %arg1, %arg2, %arg3) <{operandSegmentSizes = array<i32: 1, 1, 1, 1>, static_offsets = array<i64: -9223372036854775808, 0>, static_sizes = array<i64: -9223372036854775808, 1>, static_strides = array<i64: -9223372036854775808, 1>}> : (tensor<?x4xf32>, index, index, index) -> tensor<?xf32>
+  // CHECK-NEXT: ^ offset 0 is out-of-bounds
+  // CHECK-NEXT: Location: loc({{.*}})
+  //      CHECK: ERROR: Runtime op verification failed
+  // CHECK-NEXT: "tensor.extract_slice"(%arg0, %arg1, %arg2, %arg3) <{operandSegmentSizes = array<i32: 1, 1, 1, 1>, static_offsets = array<i64: -9223372036854775808, 0>, static_sizes = array<i64: -9223372036854775808, 1>, static_strides = array<i64: -9223372036854775808, 1>}> : (tensor<?x4xf32>, index, index, index) -> tensor<?xf32>
+  // CHECK-NEXT: ^ extract_slice runs out-of-bounds along dimension 0
+  // CHECK-NEXT: Location: loc({{.*}})
+  func.call @extract_slice_dynamic_rank_reduce(%alloca_4_dyn, %5, %5, %1) : (tensor<?x4xf32>, index, index, index) -> ()
+
+  // Offset is out-of-bounds and slice runs out-of-bounds
+  //      CHECK: ERROR: Runtime op verification failed
+  // CHECK-NEXT: "tensor.extract_slice"(%arg0, %arg1) <{operandSegmentSizes = array<i32: 1, 1, 0, 0>, static_offsets = array<i64: -9223372036854775808>, static_sizes = array<i64: 1>, static_strides = array<i64: 1>}> : (tensor<1xf32>, index) -> tensor<1xf32>
+  // CHECK-NEXT: ^ offset 0 is out-of-bounds
+  // CHECK-NEXT: Location: loc({{.*}})
+  //      CHECK: ERROR: Runtime op verification failed
+  // CHECK-NEXT: "tensor.extract_slice"(%arg0, %arg1) <{operandSegmentSizes = array<i32: 1, 1, 0, 0>, static_offsets = array<i64: -9223372036854775808>, static_sizes = array<i64: 1>, static_strides = array<i64: 1>}> : (tensor<1xf32>, index) -> tensor<1xf32>
+  // CHECK-NEXT: ^ extract_slice runs out-of-bounds along dimension 0
+  // CHECK-NEXT: Location: loc({{.*}})
+  func.call @extract_slice(%alloca, %1) : (tensor<1xf32>, index) -> ()
+
+  // Offset is out-of-bounds and slice runs out-of-bounds
+  //      CHECK: ERROR: Runtime op verification failed
+  // CHECK-NEXT: "tensor.extract_slice"(%arg0, %arg1) <{operandSegmentSizes = array<i32: 1, 1, 0, 0>, static_offsets = array<i64: -9223372036854775808>, static_sizes = array<i64: 1>, static_strides = array<i64: 1>}> : (tensor<1xf32>, index) -> tensor<1xf32>
+  // CHECK-NEXT: ^ offset 0 is out-of-bounds
+  // CHECK-NEXT: Location: loc({{.*}})
+  //      CHECK: ERROR: Runtime op verification failed
+  // CHECK-NEXT: "tensor.extract_slice"(%arg0, %arg1) <{operandSegmentSizes = array<i32: 1, 1, 0, 0>, static_offsets = array<i64: -9223372036854775808>, static_sizes = array<i64: 1>, static_strides = array<i64: 1>}> : (tensor<1xf32>, index) -> tensor<1xf32>
+  // CHECK-NEXT: ^ extract_slice runs out-of-bounds along dimension 0
+  // CHECK-NEXT: Location: loc({{.*}})
+  func.call @extract_slice(%alloca, %n1) : (tensor<1xf32>, index) -> ()
+
+  // Slice runs out-of-bounds due to size
+  //      CHECK: ERROR: Runtime op verification failed
+  // CHECK-NEXT: "tensor.extract_slice"(%arg0, %arg1, %arg2, %arg3) <{operandSegmentSizes = array<i32: 1, 1, 1, 1>, static_offsets = array<i64: -9223372036854775808, 0>, static_sizes = array<i64: -9223372036854775808, 4>, static_strides = array<i64: -9223372036854775808, 1>}> : (tensor<?x4xf32>, index, index, index) -> tensor<?x4xf32>
+  // CHECK-NEXT: ^ extract_slice runs out-of-bounds along dimension 0
+  // CHECK-NEXT: Location: loc({{.*}})
+  func.call @extract_slice_dynamic(%alloca_4_dyn, %0, %5, %1) : (tensor<?x4xf32>, index, index, index) -> ()
+
+  // Slice runs out-of-bounds due to stride
+  //      CHECK: ERROR: Runtime op verification failed
+  // CHECK-NEXT: "tensor.extract_slice"(%arg0, %arg1, %arg2, %arg3) <{operandSegmentSizes = array<i32: 1, 1, 1, 1>, static_offsets = array<i64: -9223372036854775808, 0>, static_sizes = array<i64: -9223372036854775808, 4>, static_strides = array<i64: -9223372036854775808, 1>}> : (tensor<?x4xf32>, index, index, index) -> tensor<?x4xf32>
+  // CHECK-NEXT: ^ extract_slice runs out-of-bounds along dimension 0
+  // CHECK-NEXT: Location: loc({{.*}})
+  func.call @extract_slice_dynamic(%alloca_4_dyn, %0, %4, %4) : (tensor<?x4xf32>, index, index, index) -> ()
+
+  // CHECK-NOT: ERROR: Runtime op verification failed
+  func.call @extract_slice(%alloca, %0) : (tensor<1xf32>, index) -> ()
+
+  // CHECK-NOT: ERROR: Runtime op verification failed
+  func.call @extract_slice_dynamic(%alloca_4_dyn, %0, %4, %1) : (tensor<?x4xf32>, index, index, index) -> ()
+
+  // CHECK-NOT: ERROR: Runtime op verification failed
+  func.call @extract_slice_dynamic_rank_reduce(%alloca_4_dyn, %0, %1, %0) : (tensor<?x4xf32>, index, index, index) -> ()
+
+
+  return
+}



More information about the Mlir-commits mailing list