[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 ®istry);
+} // 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 ®istry) {
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 ®istry) {
+ 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