[Mlir-commits] [mlir] [mlir][tosa] Move SLICE constant-operand checks to tosa-validate (PR #234584)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Oct 22 04:21:37 PDT 2026


https://github.com/Wijkqwe created https://github.com/llvm/llvm-project/pull/234584

Part of #233457.

`SliceOp::verify()` reads start/size from their tosa.const_shape producers
and checks their values, which is not local to the operation. Move the
ERROR_IF conditions to checkErrorIfSlice():

- ERROR_IF(any(start < 0))
- ERROR_IF(any(size <= 0))
- ERROR_IF(is_block_scale && start[rank - 1] % innermost_block_size != 0)

Drop the two MLIR-specific consistency checks (output shape matching the
size values, start + size within input bounds), following #233064.

This also fixes a latent out-of-bounds access: the block-scaled alignment
check called startValues.back() without guarding against a non-constant
start, which left startValues empty. tosa.dim satisfies the
TosaResolvableShapeOperands trait but does not resolve to constants, so
this was reachable and aborted the verifier. A regression test covers it.

Assisted-by: AI.

>From 1613f369f99b074b93ff782c25c1d90b22756ce8 Mon Sep 17 00:00:00 2001
From: wzy <WZYqwe2004 at outlook.com>
Date: Thu, 22 Oct 2026 17:08:01 +0800
Subject: [PATCH 1/3] it just works

---
 mlir/lib/Dialect/Tosa/IR/TosaOps.cpp          | 62 -------------------
 .../Tosa/Transforms/TosaValidation.cpp        | 28 +++++++++
 2 files changed, 28 insertions(+), 62 deletions(-)

diff --git a/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp b/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp
index 99c352ab8ed3c3..7bb798c2397fb1 100644
--- a/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp
+++ b/mlir/lib/Dialect/Tosa/IR/TosaOps.cpp
@@ -2452,68 +2452,6 @@ LogicalResult tosa::SliceOp::verify() {
       return emitOpError("length of size is not equal to rank of input shape");
   }
 
-  SmallVector<int64_t> startValues;
-  tosa::getConstShapeValues(start.getDefiningOp(), startValues);
-  if (startValues.size()) {
-    if (llvm::any_of(startValues, [](const int64_t v) {
-          return v < 0 && v != kInferableDimSize;
-        }))
-      return emitOpError("start values must be non-negative, got [")
-             << startValues << "]";
-  }
-
-  // ERROR_IF(is_block_scale<in_out_t>() && start[rank(shape1) - 1] %
-  // get_innermost_block_size<in_out_t>() != 0);
-  const auto elemType = getElementTypeOrSelf(input.getType());
-  if (const auto blockScaledType = llvm::dyn_cast<BlockScaledType>(elemType)) {
-    const auto startBlock = startValues.back();
-    const auto scaleBlock =
-        BlockShapeAttr::getBlockShapeValue(blockScaledType.getBlockShape());
-    if (startBlock % scaleBlock != 0) {
-      return emitOpError(
-                 "expected start innermost block size to match data type "
-                 "for block scaled input, got start block=")
-             << startBlock << ", scale block=" << scaleBlock;
-    }
-  }
-
-  SmallVector<int64_t> sizeValues;
-  if (!tosa::getConstShapeValues(size.getDefiningOp(), sizeValues))
-    return success();
-
-  if (llvm::any_of(sizeValues, [](const int64_t v) {
-        return v <= 0 && v != kInferableDimSize;
-      }))
-    return emitOpError("size values must be > 0, got [") << sizeValues << "]";
-  if (outputShape.hasRank()) {
-    SmallVector<int64_t> outputDims;
-    outputShape.getDims(outputDims);
-    const bool hasNoInferableDims = llvm::all_of(
-        sizeValues, [](const int64_t v) { return v != kInferableDimSize; });
-    if (hasNoInferableDims &&
-        failed(verifyCompatibleShape(outputDims, sizeValues)))
-      return emitOpError("expected output shape to match size values, got ")
-             << output.getType() << " vs [" << sizeValues << "]";
-  }
-
-  if (inputShape.hasRank() && startValues.size()) {
-    SmallVector<int64_t> inputDims;
-    inputShape.getDims(inputDims);
-    for (const auto &[index, vals] :
-         llvm::enumerate(llvm::zip_equal(startValues, sizeValues, inputDims))) {
-      const auto &[start, size, inputDim] = vals;
-      if (start == kInferableDimSize || size == kInferableDimSize ||
-          ShapedType::isDynamic(inputDim))
-        continue;
-      if (start + size > inputDim)
-        return emitOpError("start + size must be less than or equal to input "
-                           "dimension size, got start=")
-               << start << ", size=" << size
-               << " vs input dim size=" << inputDim << " at dimension "
-               << index;
-    }
-  }
-
   return success();
 }
 
diff --git a/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp b/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp
index 5d465065497abf..3615a6078790a8 100644
--- a/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp
+++ b/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp
@@ -1408,6 +1408,34 @@ LogicalResult checkErrorIfSlice(Operation *op) {
            << ") which "
               "does not conform to the TOSA specification";
 
+  // ERROR_IF(any(start < 0))
+  if (hasStartValues &&
+      llvm::any_of(startValues, [](int64_t v) { return v < 0; }))
+    return op->emitOpError("start values must be non-negative, got [")
+           << startValues << "]";
+
+  // ERROR_IF(any(size <= 0))
+  if (hasSizeValues &&
+      llvm::any_of(sizeValues, [](int64_t v) { return v <= 0; }))
+    return op->emitOpError("size values must be > 0, got [") << sizeValues
+                                                             << "]";
+
+  // ERROR_IF(is_block_scale<in_out_t>() && start[rank(shape1) - 1] %
+  // get_innermost_block_size<in_out_t>() != 0)
+  if (hasStartValues) {
+    auto elemType = getElementTypeOrSelf(sliceOp.getInput1().getType());
+    if (auto blockScaledType = dyn_cast<BlockScaledType>(elemType)) {
+      const int64_t startBlock = startValues.back();
+      const int64_t scaleBlock =
+          BlockShapeAttr::getBlockShapeValue(blockScaledType.getBlockShape());
+      if (startBlock % scaleBlock != 0)
+        return op->emitOpError(
+                   "expected start innermost block size to match data type "
+                   "for block scaled input, got start block=")
+               << startBlock << ", scale block=" << scaleBlock;
+    }
+  }
+
   return success();
 }
 

>From e186821de847cabcb9f2b4d078941fab2674d8ea Mon Sep 17 00:00:00 2001
From: wzy <WZYqwe2004 at outlook.com>
Date: Thu, 22 Oct 2026 17:08:13 +0800
Subject: [PATCH 2/3] tests

---
 mlir/test/Dialect/Tosa/error_if_check.mlir    | 20 +++++
 .../tosa-validation-version-1p1-invalid.mlir  | 13 +++-
 mlir/test/Dialect/Tosa/verifier.mlir          | 76 ++++---------------
 3 files changed, 48 insertions(+), 61 deletions(-)

diff --git a/mlir/test/Dialect/Tosa/error_if_check.mlir b/mlir/test/Dialect/Tosa/error_if_check.mlir
index b87f124f64ac27..2149e1d00ea29d 100644
--- a/mlir/test/Dialect/Tosa/error_if_check.mlir
+++ b/mlir/test/Dialect/Tosa/error_if_check.mlir
@@ -356,3 +356,23 @@ func.func @test_matmul_rank3_rhs_broadcast_dynamic_lhs(%arg0: tensor<?x4x7xf32>,
   %0 = tosa.matmul %arg0, %arg1, %azp0, %bzp0 : (tensor<?x4x7xf32>, tensor<1x7x5xf32>, tensor<1xf32>, tensor<1xf32>) -> tensor<2x4x5xf32>
   return %0 : tensor<2x4x5xf32>
 }
+
+// -----
+
+func.func @test_slice_invalid_start_values(%arg0: tensor<4x31x31xf32>) {
+  %start = tosa.const_shape values(dense<[-2, 0, 0]> : tensor<3xindex>) : () -> !tosa.shape<3>
+  %size = tosa.const_shape values(dense<[1, 1, 1]> : tensor<3xindex>) : () -> !tosa.shape<3>
+  // expected-error at +1 {{'tosa.slice' op start values must be non-negative, got [-2, 0, 0]}}
+  %1 = tosa.slice %arg0, %start, %size : (tensor<4x31x31xf32>, !tosa.shape<3>, !tosa.shape<3>) -> tensor<1x1x1xf32>
+  return
+}
+
+// -----
+
+func.func @test_slice_invalid_size_values(%arg0: tensor<4x31x31xf32>) {
+  %start = tosa.const_shape values(dense<[0, 0, 0]> : tensor<3xindex>) : () -> !tosa.shape<3>
+  %size = tosa.const_shape values(dense<[1, 0, 1]> : tensor<3xindex>) : () -> !tosa.shape<3>
+  // expected-error at +1 {{'tosa.slice' op size values must be > 0, got [1, 0, 1]}}
+  %1 = tosa.slice %arg0, %start, %size : (tensor<4x31x31xf32>, !tosa.shape<3>, !tosa.shape<3>) -> tensor<?x?x?xf32>
+  return
+}
diff --git a/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-invalid.mlir b/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-invalid.mlir
index a68807fa7bb2fb..ee5a1b2ee0132f 100644
--- a/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-invalid.mlir
+++ b/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-invalid.mlir
@@ -1,4 +1,4 @@
-// RUN: mlir-opt %s -split-input-file -verify-diagnostics -tosa-attach-target="specification_version=1.1.draft profiles=pro_fp" -tosa-validate="strict-op-spec-alignment"
+// RUN: mlir-opt %s -split-input-file -verify-diagnostics -tosa-attach-target="specification_version=1.1.draft profiles=pro_fp extensions=mx_common,mx_fp4e2m1" -tosa-validate="strict-op-spec-alignment"
 
 // -----
 
@@ -32,3 +32,14 @@ module {
   // expected-error at +1 {{'tosa.variable' op is not profile-aligned: element type 'f64' is not legal}}
   tosa.variable @dynamic_var : tensor<2x?xf64>
 }
+
+// -----
+
+func.func @test_slice_block_scaled_invalid_block_size(%arg0: tensor<4x4x64x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>) -> tensor<1x1x32x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>> {
+  %start = tosa.const_shape values(dense<[0, 0, 1]> : tensor<3xindex>) : () -> !tosa.shape<3>
+  %size = tosa.const_shape values(dense<[1, 1, 32]> : tensor<3xindex>) : () -> !tosa.shape<3>
+  // expected-error at +1 {{'tosa.slice' op expected start innermost block size to match data type for block scaled input, got start block=1, scale block=32}}
+  %0 = tosa.slice %arg0, %start, %size {input_unsigned = false} : (tensor<4x4x64x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>, !tosa.shape<3>, !tosa.shape<3>) -> tensor<1x1x32x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>
+  return %0 : tensor<1x1x32x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>
+}
+
diff --git a/mlir/test/Dialect/Tosa/verifier.mlir b/mlir/test/Dialect/Tosa/verifier.mlir
index 4eb15048e7d80f..8a907da90f1035 100644
--- a/mlir/test/Dialect/Tosa/verifier.mlir
+++ b/mlir/test/Dialect/Tosa/verifier.mlir
@@ -205,56 +205,6 @@ func.func @test_scalar_slice(%arg0: tensor<f32>) -> tensor<f32> {
 
 // -----
 
-func.func @test_slice_invalid_start_values(%arg0: tensor<4x31x31xf32>) {
-  %start = tosa.const_shape values(dense<[-2, 0, 0]> : tensor<3xindex>) : () -> !tosa.shape<3>
-  %size = tosa.const_shape values(dense<[1, 1, 1]> : tensor<3xindex>) : () -> !tosa.shape<3>
-  // expected-error at +1 {{'tosa.slice' op start values must be non-negative, got [-2, 0, 0]}}
-  %1 = tosa.slice %arg0, %start, %size : (tensor<4x31x31xf32>, !tosa.shape<3>, !tosa.shape<3>) -> tensor<1x1x1xf32>
-  return
-}
-
-// -----
-
-func.func @test_slice_invalid_size_values(%arg0: tensor<4x31x31xf32>) {
-  %start = tosa.const_shape values(dense<[0, 0, 0]> : tensor<3xindex>) : () -> !tosa.shape<3>
-  %size = tosa.const_shape values(dense<[1, 0, 1]> : tensor<3xindex>) : () -> !tosa.shape<3>
-  // expected-error at +1 {{'tosa.slice' op size values must be > 0, got [1, 0, 1]}}
-  %1 = tosa.slice %arg0, %start, %size : (tensor<4x31x31xf32>, !tosa.shape<3>, !tosa.shape<3>) -> tensor<?x?x?xf32>
-  return
-}
-
-// -----
-
-func.func @test_slice_output_shape_mismatch(%arg0: tensor<4x5x6xf32>) {
-  %start = tosa.const_shape values(dense<[0, 0, 0]> : tensor<3xindex>) : () -> !tosa.shape<3>
-  %size = tosa.const_shape values(dense<[1, 2, 3]> : tensor<3xindex>) : () -> !tosa.shape<3>
-  // expected-error at +1 {{'tosa.slice' op expected output shape to match size values, got 'tensor<1x2x4xf32>' vs [1, 2, 3]}}
-  %1 = tosa.slice %arg0, %start, %size : (tensor<4x5x6xf32>, !tosa.shape<3>, !tosa.shape<3>) -> tensor<1x2x4xf32>
-  return
-}
-
-// -----
-
-func.func @test_slice_invalid_start_plus_size(%arg0: tensor<?x2xf32>) {
-  %start = tosa.const_shape values(dense<[-1, 1]> : tensor<2xindex>) : () -> !tosa.shape<2>
-  %size = tosa.const_shape values(dense<[3, 2]> : tensor<2xindex>) : () -> !tosa.shape<2>
-  // expected-error at +1 {{'tosa.slice' op start + size must be less than or equal to input dimension size, got start=1, size=2 vs input dim size=2 at dimension 1}}
-  %1 = tosa.slice %arg0, %start, %size : (tensor<?x2xf32>, !tosa.shape<2>, !tosa.shape<2>) -> tensor<?x2xf32>
-  return
-}
-
-// -----
-
-func.func @test_slice_output_shape_mismatch_dynamic(%arg0: tensor<?x5x6xf32>) {
-  %start = tosa.const_shape values(dense<[0, 0, 0]> : tensor<3xindex>) : () -> !tosa.shape<3>
-  %size = tosa.const_shape values(dense<[1, 2, 3]> : tensor<3xindex>) : () -> !tosa.shape<3>
-  // expected-error at +1 {{'tosa.slice' op expected output shape to match size values, got 'tensor<?x2x4xf32>' vs [1, 2, 3]}}
-  %1 = tosa.slice %arg0, %start, %size : (tensor<?x5x6xf32>, !tosa.shape<3>, !tosa.shape<3>) -> tensor<?x2x4xf32>
-  return
-}
-
-// -----
-
 func.func @test_conv2d_mxfp_invalid_weight_zp(%arg0: tensor<1x4x4x32x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>, %arg1: tensor<8x1x1x32x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>, %arg2: tensor<8xf16>) -> tensor<1x4x4x8xf16> {
   %input_zp = "tosa.const"() <{values = dense<0.0> : tensor<1xf32>}> : () -> tensor<1xf32>
   %weight_zp = "tosa.const"() <{values = dense<0.0> : tensor<1xf16>}> : () -> tensor<1xf16>
@@ -2534,16 +2484,6 @@ func.func @test_transpose_block_scaled_illegal_perms(%input: tensor<29x12x32x96x
 
 // -----
 
-func.func @test_slice_block_scaled_invalid_block_size(%arg0: tensor<4x4x64x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>) -> tensor<1x1x32x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>> {
-  %start = tosa.const_shape values(dense<[0, 0, 1]> : tensor<3xindex>) : () -> !tosa.shape<3>
-  %size = tosa.const_shape values(dense<[1, 1, 32]> : tensor<3xindex>) : () -> !tosa.shape<3>
-  // expected-error at +1 {{'tosa.slice' op expected start innermost block size to match data type for block scaled input, got start block=1, scale block=32}}
-  %0 = tosa.slice %arg0, %start, %size {input_unsigned = false} : (tensor<4x4x64x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>, !tosa.shape<3>, !tosa.shape<3>) -> tensor<1x1x32x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>
-  return %0 : tensor<1x1x32x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>
-}
-
-// -----
-
 func.func @test_reverse_block_scaled_negative_axis(%arg0: tensor<2x64x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f8E4M3FN>>) -> tensor<2x64x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f8E4M3FN>> {
   // expected-error at +1 {{'tosa.reverse' op expected non-negative reverse axis}}
   %0 = tosa.reverse %arg0 axis(-1) : (tensor<2x64x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f8E4M3FN>>) -> tensor<2x64x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f8E4M3FN>>
@@ -2573,3 +2513,19 @@ func.func @test_reverse_block_scaled_rank_zero(%arg0: tensor<!tosa.block_scaled<
   %0 = tosa.reverse %arg0 axis(0) : (tensor<!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f8E4M3FN>>) -> tensor<!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f8E4M3FN>>
   return %0 : tensor<!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f8E4M3FN>>
 }
+
+// -----
+
+// Regression test: a block-scaled slice whose start comes from tosa.dim must
+// not crash the verifier. The start values are not compile-time constants, so
+// the block-alignment ERROR_IF can only be reported by tosa-validate when the
+// values are resolvable (see checkErrorIfSlice).
+func.func @test_slice_block_scaled_dynamic_start(%arg0: tensor<4x4x64x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>) -> tensor<1x1x32x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>> {
+  %s0 = tosa.dim %arg0 axis(0) : (tensor<4x4x64x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>) -> !tosa.shape<1>
+  %s1 = tosa.dim %arg0 axis(1) : (tensor<4x4x64x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>) -> !tosa.shape<1>
+  %s2 = tosa.dim %arg0 axis(2) : (tensor<4x4x64x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>) -> !tosa.shape<1>
+  %start = tosa.concat_shape %s0, %s1, %s2 : (!tosa.shape<1>, !tosa.shape<1>, !tosa.shape<1>) -> !tosa.shape<3>
+  %size = tosa.const_shape values(dense<[1, 1, 32]> : tensor<3xindex>) : () -> !tosa.shape<3>
+  %0 = tosa.slice %arg0, %start, %size : (tensor<4x4x64x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>, !tosa.shape<3>, !tosa.shape<3>) -> tensor<1x1x32x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>
+  return %0 : tensor<1x1x32x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>
+}

>From b926e6fe4f8e0f39835eee8561e40e110af93989 Mon Sep 17 00:00:00 2001
From: wzy <WZYqwe2004 at outlook.com>
Date: Thu, 22 Oct 2026 19:16:12 +0800
Subject: [PATCH 3/3] format

---
 mlir/test/Dialect/Tosa/tosa-validation-version-1p1-invalid.mlir | 1 -
 1 file changed, 1 deletion(-)

diff --git a/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-invalid.mlir b/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-invalid.mlir
index ee5a1b2ee0132f..b07eda0d1f3d4e 100644
--- a/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-invalid.mlir
+++ b/mlir/test/Dialect/Tosa/tosa-validation-version-1p1-invalid.mlir
@@ -42,4 +42,3 @@ func.func @test_slice_block_scaled_invalid_block_size(%arg0: tensor<4x4x64x!tosa
   %0 = tosa.slice %arg0, %start, %size {input_unsigned = false} : (tensor<4x4x64x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>, !tosa.shape<3>, !tosa.shape<3>) -> tensor<1x1x32x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>
   return %0 : tensor<1x1x32x!tosa.block_scaled<BLOCK_SHAPE_32:f8E8M0FNU:f4E2M1FN>>
 }
-



More information about the Mlir-commits mailing list