[Mlir-commits] [mlir] bc4ffe8 - [mlir][spirv] Improve verification for SPIR-V TOSA ops (#195624)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Wed May 6 06:31:28 PDT 2026


Author: Davide Grohmann
Date: 2026-05-06T15:31:23+02:00
New Revision: bc4ffe8ac06dc106d591e0ac2e0edea9d45f9dc1

URL: https://github.com/llvm/llvm-project/commit/bc4ffe8ac06dc106d591e0ac2e0edea9d45f9dc1
DIFF: https://github.com/llvm/llvm-project/commit/bc4ffe8ac06dc106d591e0ac2e0edea9d45f9dc1.diff

LOG: [mlir][spirv] Improve verification for SPIR-V TOSA ops (#195624)

Add shape and attribute verification for several SPIR-V TOSA ops:
reductions, FFT2D, RFFT2D, MatMul, Clamp, Concat, and Resize.

Add negative parser/verification tests for the new checks.

Signed-off-by: Davide Grohmann <davide.grohmann at arm.com>

Added: 
    

Modified: 
    mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaOps.td
    mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaTypes.td
    mlir/lib/Dialect/SPIRV/IR/SPIRVTosaOps.cpp
    mlir/test/Dialect/SPIRV/IR/tosa-ops-verification.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaOps.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaOps.td
index e6336bf011e9d..bc3e55f9dfb42 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaOps.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaOps.td
@@ -223,7 +223,8 @@ class SPIRV_TosaReductionOp<string mnemonic, int opcode, list<Trait> traits = []
   SPIRV_TosaOpWithResult<mnemonic, opcode, !listconcat(traits, [
   AllElementTypesMatch<["input", "output"]>,
   AllRanksMatch<["input", "output"]>,
-  AxisValueLessThanRankOf<"input">])> {
+  AxisValueLessThanRankOf<"input">,
+  ReductionOutputShapeMatchesInputAndAxis<"input", "output">])> {
 
   let extraClassDeclaration = extraBaseClassDeclaration#[{
     ::mlir::spirv::TensorArmType getInputType() {
@@ -522,7 +523,10 @@ def SPIRV_TosaDepthwiseConv2DOp : SPIRV_TosaConvolutionOp<"DepthwiseConv2D", 4,
 }
 
 
-def SPIRV_TosaFFT2DOp : SPIRV_TosaOpWithComplexResult<"FFT2D", 5, [Pure]> {
+def SPIRV_TosaFFT2DOp : SPIRV_TosaOpWithComplexResult<"FFT2D", 5, [Pure,
+  AllShapesMatch<["input_real", "input_imag"]>,
+  Struct2TensorArmShapesMatchInput<"input_real">,
+  TensorDimsArePowerOfTwo<"input_real", [1, 2]>]> {
   let summary = "Performs FFT2D operation on the input.";
 
   let description = [{
@@ -575,6 +579,7 @@ def SPIRV_TosaFFT2DOp : SPIRV_TosaOpWithComplexResult<"FFT2D", 5, [Pure]> {
 
 
 def SPIRV_TosaMatMulOp : SPIRV_TosaOpWithResult<"MatMul", 6, [NoMemoryEffect,
+  MatMulShapesMatch<"A", "B", "output">,
   TypeConstraintImplicationOn<"A", I8, "output", [I32]>,
   TypeConstraintImplicationOn<"A", I16, "output", [I64]>,
   TypeConstraintImplicationOn<"A", BF16, "output", [F32]>,
@@ -687,7 +692,9 @@ def SPIRV_TosaMaxPool2DOp : SPIRV_TosaOpWithResult<"MaxPool2D", 7, [Pure,
 }
 
 
-def SPIRV_TosaRFFT2DOp : SPIRV_TosaOpWithComplexResult<"RFFT2D", 8, [Pure]> {
+def SPIRV_TosaRFFT2DOp : SPIRV_TosaOpWithComplexResult<"RFFT2D", 8, [Pure,
+  Struct2TensorArmRFFT2DShapesMatchInput<"input_real">,
+  TensorDimsArePowerOfTwo<"input_real", [1, 2]>]> {
   let summary = "Performs RFFT2D operation on the input.";
 
   let description = [{
@@ -794,7 +801,9 @@ def SPIRV_TosaTransposeConv2DOp : SPIRV_TosaConvolutionOp<"TransposeConv2D", 9,
 
 def SPIRV_TosaClampOp : SPIRV_TosaOpWithResult<"Clamp", 10, [Pure,
   AllTypesMatch<["input", "output"]>,
-  AllElementTypesMatch<["input", "output", "min_val", "max_val"]>]> {
+  AllElementTypesMatch<["input", "output", "min_val", "max_val"]>,
+  ClampBoundsAreNotNaN<"min_val", "max_val">,
+  ClampMinValLessThanOrEqualToMaxVal<"min_val", "max_val">]> {
   let summary = "Computes Clamp(min, max).";
 
   let description = [{
@@ -2217,6 +2226,8 @@ def SPIRV_TosaConcatOp : SPIRV_TosaOpWithResult<"Concat", 54, [Pure,
       return getInput1().getTypes();
     }
   }];
+
+  let hasVerifier = 1;
 }
 
 
@@ -2633,7 +2644,8 @@ def SPIRV_TosaResizeOp : SPIRV_TosaOpWithResult<"Resize", 63, [Pure,
   TypeConstraintImplicationOn<"input", F32, "output", [F32]>,
   TypeConstraintImplicationOn<"input", BF16, "output", [BF16]>,
   TypeConstraintImplicationOn<"input", I8, "output", [I8, I32]>,
-  TypeConstraintImplicationOn<"input", I16, "output", [I16, I64]>]> {
+  TypeConstraintImplicationOn<"input", I16, "output", [I16, I64]>,
+  NHWCInputOutputShapeMatch<"input", "output">]> {
   let summary = "Resize operation, supports various resize/upsample modes.";
 
   let description = [{

diff  --git a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaTypes.td b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaTypes.td
index bc981d8bf95cf..8497f4f0c4b46 100644
--- a/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaTypes.td
+++ b/mlir/include/mlir/Dialect/SPIRV/IR/SPIRVTosaTypes.td
@@ -186,39 +186,33 @@ PredOpTrait<output # " rank must be equal to max(1, rank(" # input # "))",
       And<[CPred<HasRank<input>.result>, CPred<HasRank<input>.result>]>,
       [CPred<"std::max(int64_t(1), " # Rank<input>.result # " - int64_t(1)) == " # Rank<output>.result>]>>;
 
-class AccTypeIn<list<string> allowedValues> :
-  CPred<"llvm::is_contained({" # !interleave(!foreach(value, allowedValues, "::mlir::spirv::TosaExtAccType::" # value), ",") # "}, getAccType())">;
+class DimOfType<string type, int dim> :
+  StrFunc<type # ".getDimSize(" # dim # ")">;
 
-class TypeImpliesAccType<string input, Type type, list<string> allowedAccTypes>:
-  PredOpTrait<"acc_type must be one in [" # !interleave(allowedAccTypes, ",") # "] when type has value " # type.summary,
-  Implies<ElementTypeIsPred<input, type>, [AccTypeIn<allowedAccTypes>]>>;
-
-class MatchBroadcastableShapes<string input1, string input2, string output>:
-  PredOpTrait<"the shape of " # input1 # " and " # input2 # " are compatible for broadcasting and the broadcast shape is equal to the " # output # " shape",
-  Implies<And<[CPred<HasRank<input1>.result>, CPred<HasRank<input2>.result>, CPred<HasRank<output>.result>,
-               CPred<Rank<input1>.result # " == " # Rank<input2>.result # " && " # Rank<input1>.result # " == " # Rank<output>.result>]>,
-    [CPred<"llvm::all_of_zip(" # Shape<input1>.result # ", " # Shape<input2>.result # ", " # Shape<output>.result # ", " #
-    "[](int64_t input1Dim, int64_t input2Dim, int64_t outputDim) { " #
-    "   bool dynamic = ShapedType::isDynamic(input1Dim) || ShapedType::isDynamic(input2Dim) || ShapedType::isDynamic(outputDim);"
-    "   bool broadcastableInputs = input1Dim == input2Dim || input1Dim == 1 || input2Dim == 1;" #
-    "   bool broacastDimMatchesOutputDim = std::max(input1Dim, input2Dim) == outputDim;"
-    "   return dynamic || (broadcastableInputs && broacastDimMatchesOutputDim);" #
-    "})">]>
-  >;
+class ShapedTypeOf<string input> :
+  StrFunc<"::llvm::cast<::mlir::ShapedType>($" # input # ".getType())">;
 
 class DimOf<string input, int dim> :
-  StrFunc<"::llvm::cast<::mlir::ShapedType>($" # input #
-          ".getType()).getDimSize(" # dim # ")">;
+  DimOfType<ShapedTypeOf<input>.result, dim>;
 
 class DimIsDynamic<string input, int dim> :
   CPred<"::mlir::ShapedType::isDynamic(" # DimOf<input, dim>.result # ")">;
 
+class DimOfTypeIsDynamic<string type, int dim> :
+  CPred<"::mlir::ShapedType::isDynamic(" #
+        DimOfType<type, dim>.result # ")">;
+
 class DimIsOne<string input, int dim> :
   CPred<DimOf<input, dim>.result # " == 1">;
 
 class DimsMatch<string lhs, int lhsDim, string rhs, int rhsDim> :
   CPred<DimOf<lhs, lhsDim>.result # " == " # DimOf<rhs, rhsDim>.result>;
 
+class DimsOfTypeMatch<string lhsType, int lhsDim, string rhsType,
+                      int rhsDim> :
+  CPred<DimOfType<lhsType, lhsDim>.result # " == " #
+        DimOfType<rhsType, rhsDim>.result>;
+
 class ProductDimsMatch<string lhs, int lhsDim, string rhs, int rhsDim,
                        string output, int outputDim> :
   CPred<DimOf<lhs, lhsDim>.result # " * " # DimOf<rhs, rhsDim>.result #
@@ -247,6 +241,65 @@ class ProductDimOrOneOrDynamicPred<string lhs, int lhsDim, string rhs,
     DimIsOne<output, outputDim>
   ]>;
 
+class AttrIsFloat<string attr> :
+  CPred<"::llvm::isa<::mlir::FloatAttr>(get" #
+        snakeCaseToCamelCase<attr>.ret # "())">;
+
+class AttrIsInteger<string attr> :
+  CPred<"::llvm::isa<::mlir::IntegerAttr>(get" #
+        snakeCaseToCamelCase<attr>.ret # "())">;
+
+class IntegerAttrValue<string attr> :
+  StrFunc<"::llvm::cast<::mlir::IntegerAttr>(get" #
+          snakeCaseToCamelCase<attr>.ret # "()).getValue()">;
+
+class FloatAttrValue<string attr> :
+  StrFunc<"::llvm::cast<::mlir::FloatAttr>(get" #
+          snakeCaseToCamelCase<attr>.ret # "()).getValue()">;
+
+class FloatAttrIsNaN<string attr> :
+  And<[AttrIsFloat<attr>,
+       CPred<FloatAttrValue<attr>.result # ".isNaN()">]>;
+
+class ReductionDimMatchPred<string input, string output, int dim> :
+  Or<[
+    DimIsDynamic<input, dim>,
+    DimIsDynamic<output, dim>,
+    And<[CPred<"getAxis() == " # dim>, DimIsOne<output, dim>]>,
+    And<[CPred<"getAxis() != " # dim>, DimsMatch<input, dim, output, dim>]>
+  ]>;
+
+class ReductionOutputShapeMatchesInputAndAxis<string input, string output>:
+PredOpTrait<"shape of " # output # " must match the shape of " # input #
+                " with the dimension at axis replaced by 1",
+  And<!foreach(dim, [0, 1, 2, 3, 4, 5],
+               Implies<
+                 And<[CPred<HasRank<input>.result>,
+                      CPred<HasRank<output>.result>,
+                      CPred<Rank<input>.result # " == " # Rank<output>.result>,
+                      CPred<Rank<input>.result # " > " # dim>]>,
+                 [ReductionDimMatchPred<input, output, dim>]>)>>;
+
+class AccTypeIn<list<string> allowedValues> :
+  CPred<"llvm::is_contained({" # !interleave(!foreach(value, allowedValues, "::mlir::spirv::TosaExtAccType::" # value), ",") # "}, getAccType())">;
+
+class TypeImpliesAccType<string input, Type type, list<string> allowedAccTypes>:
+  PredOpTrait<"acc_type must be one in [" # !interleave(allowedAccTypes, ",") # "] when type has value " # type.summary,
+  Implies<ElementTypeIsPred<input, type>, [AccTypeIn<allowedAccTypes>]>>;
+
+class MatchBroadcastableShapes<string input1, string input2, string output>:
+  PredOpTrait<"the shape of " # input1 # " and " # input2 # " are compatible for broadcasting and the broadcast shape is equal to the " # output # " shape",
+  Implies<And<[CPred<HasRank<input1>.result>, CPred<HasRank<input2>.result>, CPred<HasRank<output>.result>,
+               CPred<Rank<input1>.result # " == " # Rank<input2>.result # " && " # Rank<input1>.result # " == " # Rank<output>.result>]>,
+    [CPred<"llvm::all_of_zip(" # Shape<input1>.result # ", " # Shape<input2>.result # ", " # Shape<output>.result # ", " #
+    "[](int64_t input1Dim, int64_t input2Dim, int64_t outputDim) { " #
+    "   bool dynamic = ShapedType::isDynamic(input1Dim) || ShapedType::isDynamic(input2Dim) || ShapedType::isDynamic(outputDim);"
+    "   bool broadcastableInputs = input1Dim == input2Dim || input1Dim == 1 || input2Dim == 1;" #
+    "   bool broacastDimMatchesOutputDim = std::max(input1Dim, input2Dim) == outputDim;"
+    "   return dynamic || (broadcastableInputs && broacastDimMatchesOutputDim);" #
+    "})">]>
+  >;
+
 class ValuesIndicesShapesMatch<string values, string indices, string tensor>:
   PredOpTrait<"shapes of " # values # ", " # indices # ", and " # tensor #
                   " must satisfy [N,K,C], [N,W], [N,W,C]",
@@ -316,6 +369,85 @@ class Pool2DPadValuesLessThanKernel<string padAttr, string kernelAttr> :
          ElementsAttrValueLessThan<padAttr, 3, kernelAttr, 1>]
     >>;
 
+class MatMulShapesMatch<string lhs, string rhs, string output> : PredOpTrait<
+  "shapes of " # lhs # ", " # rhs # ", and " # output #
+      " must satisfy [N,H,C], [N,C,W], [N,H,W]",
+  And<[
+    SameDimsOrDynamicPred<lhs, 0, rhs, 0>,
+    SameDimsOrDynamicPred<lhs, 0, output, 0>,
+    SameDimsOrDynamicPred<lhs, 1, output, 1>,
+    SameDimsOrDynamicPred<lhs, 2, rhs, 1>,
+    SameDimsOrDynamicPred<rhs, 2, output, 2>
+  ]>>;
+
+class DimsOfTypeAllMatchOrDynamic<string lhsType, string rhsType,
+                                  string otherType, int dim> :
+  Or<[
+    DimOfTypeIsDynamic<lhsType, dim>,
+    DimOfTypeIsDynamic<rhsType, dim>,
+    DimOfTypeIsDynamic<otherType, dim>,
+    And<[DimsOfTypeMatch<lhsType, dim, rhsType, dim>,
+         DimsOfTypeMatch<lhsType, dim, otherType, dim>]>
+  ]>;
+
+class DimOfTypeMatchesHalfPlusOne<string inputType, int inputDim,
+                                  string outputType, int outputDim> :
+  Or<[
+    DimOfTypeIsDynamic<inputType, inputDim>,
+    DimOfTypeIsDynamic<outputType, outputDim>,
+    CPred<DimOfType<outputType, outputDim>.result # " == " #
+          DimOfType<inputType, inputDim>.result # " / 2 + 1">
+  ]>;
+
+class Struct2TensorArmShapesMatchInput<string input> : PredOpTrait<
+  "shapes of output_real and output_imag must match the shape of " # input,
+  And<!foreach(dim, [0, 1, 2],
+               DimsOfTypeAllMatchOrDynamic<
+                 ShapedTypeOf<input>.result,
+                 "getResultRealType()", "getResultImagType()", dim>)>>;
+
+class Struct2TensorArmRFFT2DShapesMatchInput<string input> : PredOpTrait<
+  "shapes of output_real and output_imag must satisfy [N,H,W/2 + 1] when " #
+      input # " has shape [N,H,W]",
+  And<[
+    DimsOfTypeAllMatchOrDynamic<ShapedTypeOf<input>.result,
+      "getResultRealType()", "getResultImagType()", 0>,
+    DimsOfTypeAllMatchOrDynamic<ShapedTypeOf<input>.result,
+      "getResultRealType()", "getResultImagType()", 1>,
+    DimOfTypeMatchesHalfPlusOne<ShapedTypeOf<input>.result,
+      2, "getResultRealType()", 2>,
+    DimOfTypeMatchesHalfPlusOne<ShapedTypeOf<input>.result,
+      2, "getResultImagType()", 2>
+  ]>>;
+
+class DimIsPowerOfTwo<string input, int dim> :
+  Or<[DimIsDynamic<input, dim>,
+      CPred<"llvm::isPowerOf2_64(static_cast<uint64_t>(" # DimOf<input, dim>.result # "))">]>;
+
+class TensorDimsArePowerOfTwo<string input, list<int> dims> : PredOpTrait<
+  "dimensions " # !interleave(dims, ", ") # " of " # input # " must be powers of two",
+  And<!foreach(dim, dims, DimIsPowerOfTwo<input, dim>)>>;
+
+class ClampBoundsAreNotNaN<string minVal, string maxVal>:
+  PredOpTrait<minVal # " and " # maxVal # " must not be NaN",
+    Neg<Or<[FloatAttrIsNaN<minVal>, FloatAttrIsNaN<maxVal>]>>>;
+
+class IntegerAttrLessThanOrEqual<string minVal, string maxVal> :
+  Implies<AttrIsInteger<minVal>,
+          [CPred<IntegerAttrValue<minVal>.result # ".sle(" #
+                 IntegerAttrValue<maxVal>.result # ")">]>;
+
+class FloatAttrLessThanOrEqual<string minVal, string maxVal> :
+  Implies<AttrIsFloat<minVal>,
+          [CPred<FloatAttrValue<minVal>.result # ".compare(" #
+                 FloatAttrValue<maxVal>.result #
+                 ") != ::llvm::APFloat::cmpGreaterThan">]>;
+
+class ClampMinValLessThanOrEqualToMaxVal<string minVal, string maxVal>:
+  PredOpTrait<minVal # " must be <= " # maxVal,
+    And<[IntegerAttrLessThanOrEqual<minVal, maxVal>,
+         FloatAttrLessThanOrEqual<minVal, maxVal>]>>;
+
 class TableSizeConstraint<string input, Type type, int size>:
   PredOpTrait<"table must have size " # size # " if " # input # " has element type " # type.summary,
       Implies<ElementTypeIsPred<input, type>, [CPred<"::llvm::cast<::mlir::ShapedType>(getTable().getType()).getShape()[0] == " # size>]>

diff  --git a/mlir/lib/Dialect/SPIRV/IR/SPIRVTosaOps.cpp b/mlir/lib/Dialect/SPIRV/IR/SPIRVTosaOps.cpp
index 0a559202556d5..c927ef8c48d58 100644
--- a/mlir/lib/Dialect/SPIRV/IR/SPIRVTosaOps.cpp
+++ b/mlir/lib/Dialect/SPIRV/IR/SPIRVTosaOps.cpp
@@ -281,6 +281,50 @@ LogicalResult verifyTransposeConv2DOutputShape(Operation *op,
   return success();
 }
 
+LogicalResult verifyConcatOutputShape(Operation *op, TypeRange inputTypes,
+                                      TensorArmType outputType, int32_t axis) {
+  constexpr StringLiteral errorMessage =
+      "failed to verify that shape of output must match the concatenation of "
+      "input1 along axis";
+  if (!outputType.hasRank())
+    return success();
+
+  if (llvm::any_of(inputTypes, [](Type type) {
+        return !cast<TensorArmType>(type).hasRank();
+      }))
+    return success();
+
+  for (int64_t dim = 0, rank = outputType.getRank(); dim < rank; ++dim) {
+    int64_t outputDim = outputType.getDimSize(dim);
+    if (ShapedType::isDynamic(outputDim))
+      continue;
+
+    if (dim != axis) {
+      for (Type type : inputTypes) {
+        int64_t inputDim = cast<TensorArmType>(type).getDimSize(dim);
+        if (ShapedType::isStatic(inputDim) && inputDim != outputDim)
+          return op->emitOpError(errorMessage);
+      }
+      continue;
+    }
+
+    int64_t concatDim = 0;
+    for (Type type : inputTypes) {
+      int64_t inputDim = cast<TensorArmType>(type).getDimSize(dim);
+      if (ShapedType::isDynamic(inputDim)) {
+        concatDim = ShapedType::kDynamic;
+        break;
+      }
+      concatDim += inputDim;
+    }
+
+    if (ShapedType::isStatic(concatDim) && concatDim != outputDim)
+      return op->emitOpError(errorMessage);
+  }
+
+  return success();
+}
+
 } // namespace
 
 LogicalResult TosaAvgPool2DOp::verify() {
@@ -317,6 +361,11 @@ LogicalResult TosaTransposeConv2DOp::verify() {
                                           getWeightType(), getResultType());
 }
 
+LogicalResult TosaConcatOp::verify() {
+  return verifyConcatOutputShape(getOperation(), getInput1Types(),
+                                 getResultType(), getAxis());
+}
+
 LogicalResult TosaSelectOp::verify() {
   TensorArmType condType = getConditionType();
   TensorArmType trueValType = getTrueValueType();

diff  --git a/mlir/test/Dialect/SPIRV/IR/tosa-ops-verification.mlir b/mlir/test/Dialect/SPIRV/IR/tosa-ops-verification.mlir
index fa3edbb3760e6..ca15b69303361 100644
--- a/mlir/test/Dialect/SPIRV/IR/tosa-ops-verification.mlir
+++ b/mlir/test/Dialect/SPIRV/IR/tosa-ops-verification.mlir
@@ -615,6 +615,34 @@ spirv.ARM.Graph @depthwise_conv2d_input_output_height_width_mismatch(%arg0: !spi
   spirv.ARM.GraphOutputs %7 : !spirv.arm.tensor<1x4x32761x4xi32>
 }
 
+//===----------------------------------------------------------------------===//
+// spirv.TOSA.FFT2D
+//===----------------------------------------------------------------------===//
+
+spirv.ARM.Graph @fft2d_input_shapes_must_match(%arg0: !spirv.arm.tensor<1x32x32xf32>, %arg1: !spirv.arm.tensor<1x32x16xf32>) -> (!spirv.arm.tensor<1x32x32xf32>) {
+  // expected-error @+1 {{op failed to verify that all of {input_real, input_imag} have same shape}}
+  %0 = spirv.Tosa.FFT2D inverse = true, local_bound = false, %arg0, %arg1 : !spirv.arm.tensor<1x32x32xf32>, !spirv.arm.tensor<1x32x16xf32> -> !spirv.struct<(!spirv.arm.tensor<1x32x32xf32>, !spirv.arm.tensor<1x32x32xf32>)>
+  spirv.ARM.GraphOutputs %arg0 : !spirv.arm.tensor<1x32x32xf32>
+}
+
+spirv.ARM.Graph @fft2d_output_shapes_must_match_inputs(%arg0: !spirv.arm.tensor<1x32x32xf32>, %arg1: !spirv.arm.tensor<1x32x32xf32>) -> (!spirv.arm.tensor<1x32x32xf32>) {
+  // expected-error @+1 {{op failed to verify that shapes of output_real and output_imag must match the shape of input_real}}
+  %0 = spirv.Tosa.FFT2D inverse = true, local_bound = false, %arg0, %arg1 : !spirv.arm.tensor<1x32x32xf32>, !spirv.arm.tensor<1x32x32xf32> -> !spirv.struct<(!spirv.arm.tensor<1x16x32xf32>, !spirv.arm.tensor<1x32x32xf32>)>
+  spirv.ARM.GraphOutputs %arg0 : !spirv.arm.tensor<1x32x32xf32>
+}
+
+spirv.ARM.Graph @fft2d_height_and_width_must_be_powers_of_two(%arg0: !spirv.arm.tensor<1x30x32xf32>, %arg1: !spirv.arm.tensor<1x30x32xf32>) -> (!spirv.arm.tensor<1x30x32xf32>) {
+  // expected-error @+1 {{op failed to verify that dimensions 1, 2 of input_real must be powers of two}}
+  %0 = spirv.Tosa.FFT2D inverse = true, local_bound = false, %arg0, %arg1 : !spirv.arm.tensor<1x30x32xf32>, !spirv.arm.tensor<1x30x32xf32> -> !spirv.struct<(!spirv.arm.tensor<1x30x32xf32>, !spirv.arm.tensor<1x30x32xf32>)>
+  spirv.ARM.GraphOutputs %arg0 : !spirv.arm.tensor<1x30x32xf32>
+}
+
+spirv.ARM.Graph @fft2d_width_must_be_power_of_two(%arg0: !spirv.arm.tensor<1x32x30xf32>, %arg1: !spirv.arm.tensor<1x32x30xf32>) -> (!spirv.arm.tensor<1x32x30xf32>) {
+  // expected-error @+1 {{op failed to verify that dimensions 1, 2 of input_real must be powers of two}}
+  %0 = spirv.Tosa.FFT2D inverse = true, local_bound = false, %arg0, %arg1 : !spirv.arm.tensor<1x32x30xf32>, !spirv.arm.tensor<1x32x30xf32> -> !spirv.struct<(!spirv.arm.tensor<1x32x30xf32>, !spirv.arm.tensor<1x32x30xf32>)>
+  spirv.ARM.GraphOutputs %arg0 : !spirv.arm.tensor<1x32x30xf32>
+}
+
 //===----------------------------------------------------------------------===//
 // spirv.TOSA.MatMul
 //===----------------------------------------------------------------------===//
@@ -679,6 +707,46 @@ spirv.ARM.Graph @matmul_mismatch_result_element_type_f8e5m2_input(%arg0: !spirv.
   spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<1x4x4xf32>
 }
 
+spirv.ARM.Graph @matmul_batch_dimensions_must_match(%arg0: !spirv.arm.tensor<2x4x3xi8>, %arg1: !spirv.arm.tensor<1x3x8xi8>, %arg2: !spirv.arm.tensor<1xi8>, %arg3: !spirv.arm.tensor<1xi8>) -> (!spirv.arm.tensor<2x4x8xi32>) {
+  // expected-error @+1 {{op failed to verify that shapes of A, B, and output must satisfy [N,H,C], [N,C,W], [N,H,W]}}
+  %0 = spirv.Tosa.MatMul %arg0, %arg1, %arg2, %arg3 : !spirv.arm.tensor<2x4x3xi8>, !spirv.arm.tensor<1x3x8xi8>, !spirv.arm.tensor<1xi8>, !spirv.arm.tensor<1xi8> -> !spirv.arm.tensor<2x4x8xi32>
+  spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<2x4x8xi32>
+}
+
+spirv.ARM.Graph @matmul_contraction_dimension_must_match(%arg0: !spirv.arm.tensor<1x4x3xi8>, %arg1: !spirv.arm.tensor<1x2x8xi8>, %arg2: !spirv.arm.tensor<1xi8>, %arg3: !spirv.arm.tensor<1xi8>) -> (!spirv.arm.tensor<1x4x8xi32>) {
+  // expected-error @+1 {{op failed to verify that shapes of A, B, and output must satisfy [N,H,C], [N,C,W], [N,H,W]}}
+  %0 = spirv.Tosa.MatMul %arg0, %arg1, %arg2, %arg3 : !spirv.arm.tensor<1x4x3xi8>, !spirv.arm.tensor<1x2x8xi8>, !spirv.arm.tensor<1xi8>, !spirv.arm.tensor<1xi8> -> !spirv.arm.tensor<1x4x8xi32>
+  spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<1x4x8xi32>
+}
+
+spirv.ARM.Graph @matmul_output_shape_must_match_inputs(%arg0: !spirv.arm.tensor<1x4x3xi8>, %arg1: !spirv.arm.tensor<1x3x8xi8>, %arg2: !spirv.arm.tensor<1xi8>, %arg3: !spirv.arm.tensor<1xi8>) -> (!spirv.arm.tensor<1x5x8xi32>) {
+  // expected-error @+1 {{op failed to verify that shapes of A, B, and output must satisfy [N,H,C], [N,C,W], [N,H,W]}}
+  %0 = spirv.Tosa.MatMul %arg0, %arg1, %arg2, %arg3 : !spirv.arm.tensor<1x4x3xi8>, !spirv.arm.tensor<1x3x8xi8>, !spirv.arm.tensor<1xi8>, !spirv.arm.tensor<1xi8> -> !spirv.arm.tensor<1x5x8xi32>
+  spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<1x5x8xi32>
+}
+
+//===----------------------------------------------------------------------===//
+// spirv.TOSA.RFFT2D
+//===----------------------------------------------------------------------===//
+
+spirv.ARM.Graph @rfft2d_output_shapes_must_match_input(%arg0: !spirv.arm.tensor<1x32x32xf32>) -> (!spirv.arm.tensor<1x32x32xf32>) {
+  // expected-error @+1 {{op failed to verify that shapes of output_real and output_imag must satisfy [N,H,W/2 + 1] when input_real has shape [N,H,W]}}
+  %0 = spirv.Tosa.RFFT2D local_bound = false, %arg0 : !spirv.arm.tensor<1x32x32xf32> -> !spirv.struct<(!spirv.arm.tensor<1x16x17xf32>, !spirv.arm.tensor<1x32x17xf32>)>
+  spirv.ARM.GraphOutputs %arg0 : !spirv.arm.tensor<1x32x32xf32>
+}
+
+spirv.ARM.Graph @rfft2d_height_and_width_must_be_powers_of_two(%arg0: !spirv.arm.tensor<1x30x32xf32>) -> (!spirv.arm.tensor<1x30x32xf32>) {
+  // expected-error @+1 {{op failed to verify that dimensions 1, 2 of input_real must be powers of two}}
+  %0 = spirv.Tosa.RFFT2D local_bound = false, %arg0 : !spirv.arm.tensor<1x30x32xf32> -> !spirv.struct<(!spirv.arm.tensor<1x30x17xf32>, !spirv.arm.tensor<1x30x17xf32>)>
+  spirv.ARM.GraphOutputs %arg0 : !spirv.arm.tensor<1x30x32xf32>
+}
+
+spirv.ARM.Graph @rfft2d_width_must_be_power_of_two(%arg0: !spirv.arm.tensor<1x32x30xf32>) -> (!spirv.arm.tensor<1x32x30xf32>) {
+  // expected-error @+1 {{op failed to verify that dimensions 1, 2 of input_real must be powers of two}}
+  %0 = spirv.Tosa.RFFT2D local_bound = false, %arg0 : !spirv.arm.tensor<1x32x30xf32> -> !spirv.struct<(!spirv.arm.tensor<1x32x16xf32>, !spirv.arm.tensor<1x32x16xf32>)>
+  spirv.ARM.GraphOutputs %arg0 : !spirv.arm.tensor<1x32x30xf32>
+}
+
 //===----------------------------------------------------------------------===//
 // spirv.TOSA.MaxPool2D
 //===----------------------------------------------------------------------===//
@@ -901,6 +969,18 @@ spirv.ARM.Graph @clamp_max_val_
diff erent_element_type_wrt_input_output(%arg0: !s
   spirv.ARM.GraphOutputs %3 : !spirv.arm.tensor<27x44x55xi8>
 }
 
+spirv.ARM.Graph @clamp_min_val_must_be_less_than_or_equal_to_max_val(%arg0: !spirv.arm.tensor<27x44x55xi8>) -> (!spirv.arm.tensor<27x44x55xi8>) {
+  // expected-error @+1 {{op failed to verify that min_val must be <= max_val}}
+  %3 = spirv.Tosa.Clamp min_val = 7 : i8, max_val = 3 : i8, nan_mode = <Propagate>, %arg0 : !spirv.arm.tensor<27x44x55xi8> -> !spirv.arm.tensor<27x44x55xi8>
+  spirv.ARM.GraphOutputs %3 : !spirv.arm.tensor<27x44x55xi8>
+}
+
+spirv.ARM.Graph @clamp_min_val_and_max_val_must_not_be_nan(%arg0: !spirv.arm.tensor<18x5x17x6xf32>) -> (!spirv.arm.tensor<18x5x17x6xf32>) {
+  // expected-error @+1 {{op failed to verify that min_val and max_val must not be NaN}}
+  %3 = spirv.Tosa.Clamp min_val = 0x7FC00000 : f32, max_val = 2.38255944E+38 : f32, nan_mode = <Ignore>, %arg0 : !spirv.arm.tensor<18x5x17x6xf32> -> !spirv.arm.tensor<18x5x17x6xf32>
+  spirv.ARM.GraphOutputs %3 : !spirv.arm.tensor<18x5x17x6xf32>
+}
+
 //===----------------------------------------------------------------------===//
 // spirv.TOSA.Erf
 //===----------------------------------------------------------------------===//
@@ -1939,6 +2019,12 @@ spirv.ARM.Graph @reduceall_axis_value_not_in_input_rank_range(%arg0: !spirv.arm.
   spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<18x22x23x12xi1>
 }
 
+spirv.ARM.Graph @reduceall_output_shape_must_match_input_and_axis(%arg0: !spirv.arm.tensor<18x22x23x12xi1>) -> (!spirv.arm.tensor<18x22x23x13xi1>) {
+  // expected-error @+1 {{op failed to verify that shape of output must match the shape of input with the dimension at axis replaced by 1}}
+  %0 = spirv.Tosa.ReduceAll axis = 2, %arg0 : !spirv.arm.tensor<18x22x23x12xi1> -> !spirv.arm.tensor<18x22x23x13xi1>
+  spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<18x22x23x13xi1>
+}
+
 //===----------------------------------------------------------------------===//
 // spirv.TOSA.ReduceAny
 //===----------------------------------------------------------------------===//
@@ -1955,6 +2041,12 @@ spirv.ARM.Graph @reduceany_axis_value_not_in_input_rank_range(%arg0: !spirv.arm.
   spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<25x13x30x8xi1>
 }
 
+spirv.ARM.Graph @reduceany_output_shape_must_match_input_and_axis(%arg0: !spirv.arm.tensor<25x13x30x8xi1>) -> (!spirv.arm.tensor<25x13x2x8xi1>) {
+  // expected-error @+1 {{op failed to verify that shape of output must match the shape of input with the dimension at axis replaced by 1}}
+  %0 = spirv.Tosa.ReduceAny axis = 2, %arg0 : !spirv.arm.tensor<25x13x30x8xi1> -> !spirv.arm.tensor<25x13x2x8xi1>
+  spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<25x13x2x8xi1>
+}
+
 //===----------------------------------------------------------------------===//
 // spirv.TOSA.ReduceMax
 //===----------------------------------------------------------------------===//
@@ -1977,6 +2069,12 @@ spirv.ARM.Graph @reducemax_axis_value_not_in_input_rank_range(%arg0: !spirv.arm.
   spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<8x30x12x3xi8>
 }
 
+spirv.ARM.Graph @reducemax_output_shape_must_match_input_and_axis(%arg0: !spirv.arm.tensor<8x30x12x3xi8>) -> (!spirv.arm.tensor<8x30x2x3xi8>) {
+  // expected-error @+1 {{op failed to verify that shape of output must match the shape of input with the dimension at axis replaced by 1}}
+  %0 = spirv.Tosa.ReduceMax axis = 2, nan_mode = <Propagate>, %arg0 : !spirv.arm.tensor<8x30x12x3xi8> -> !spirv.arm.tensor<8x30x2x3xi8>
+  spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<8x30x2x3xi8>
+}
+
 //===----------------------------------------------------------------------===//
 // spirv.TOSA.ReduceMin
 //===----------------------------------------------------------------------===//
@@ -1999,6 +2097,12 @@ spirv.ARM.Graph @reducemin_axis_value_not_in_input_rank_range(%arg0: !spirv.arm.
   spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<27x10x25x9xf16>
 }
 
+spirv.ARM.Graph @reducemin_output_shape_must_match_input_and_axis(%arg0: !spirv.arm.tensor<27x10x25x9xf16>) -> (!spirv.arm.tensor<27x10x2x9xf16>) {
+  // expected-error @+1 {{op failed to verify that shape of output must match the shape of input with the dimension at axis replaced by 1}}
+  %0 = spirv.Tosa.ReduceMin axis = 2, nan_mode = <Propagate>, %arg0 : !spirv.arm.tensor<27x10x25x9xf16> -> !spirv.arm.tensor<27x10x2x9xf16>
+  spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<27x10x2x9xf16>
+}
+
 //===----------------------------------------------------------------------===//
 // spirv.TOSA.ReduceProduct
 //===----------------------------------------------------------------------===//
@@ -2021,6 +2125,12 @@ spirv.ARM.Graph @reduceproduct_axis_value_not_in_input_rank_range(%arg0: !spirv.
   spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<2x16x25xf16>
 }
 
+spirv.ARM.Graph @reduceproduct_output_shape_must_match_input_and_axis(%arg0: !spirv.arm.tensor<2x16x25xf16>) -> (!spirv.arm.tensor<2x17x25xf16>) {
+  // expected-error @+1 {{op failed to verify that shape of output must match the shape of input with the dimension at axis replaced by 1}}
+  %0 = spirv.Tosa.ReduceProduct axis = 1, %arg0 : !spirv.arm.tensor<2x16x25xf16> -> !spirv.arm.tensor<2x17x25xf16>
+  spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<2x17x25xf16>
+}
+
 //===----------------------------------------------------------------------===//
 // spirv.TOSA.ReduceSum
 //===----------------------------------------------------------------------===//
@@ -2043,6 +2153,12 @@ spirv.ARM.Graph @reducesum_axis_value_not_in_input_rank_range(%arg0: !spirv.arm.
   spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<20x24x22xi32>
 }
 
+spirv.ARM.Graph @reducesum_output_shape_must_match_input_and_axis(%arg0: !spirv.arm.tensor<20x24x22xi32>) -> (!spirv.arm.tensor<20x25x22xi32>) {
+  // expected-error @+1 {{op failed to verify that shape of output must match the shape of input with the dimension at axis replaced by 1}}
+  %0 = spirv.Tosa.ReduceSum axis = 1, %arg0 : !spirv.arm.tensor<20x24x22xi32> -> !spirv.arm.tensor<20x25x22xi32>
+  spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<20x25x22xi32>
+}
+
 //===----------------------------------------------------------------------===//
 // spirv.TOSA.Concat
 //===----------------------------------------------------------------------===//
@@ -2071,6 +2187,18 @@ spirv.ARM.Graph @concat_axis_value_not_in_output_rank_range(%arg0: !spirv.arm.te
   spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<4x12xi8>
 }
 
+spirv.ARM.Graph @concat_non_axis_dimensions_must_match_output(%arg0: !spirv.arm.tensor<4x5xi8>, %arg1: !spirv.arm.tensor<3x7xi8>) -> (!spirv.arm.tensor<4x12xi8>) {
+  // expected-error @+1 {{op failed to verify that shape of output must match the concatenation of input1 along axis}}
+  %0 = spirv.Tosa.Concat axis = 1, %arg0, %arg1 : !spirv.arm.tensor<4x5xi8>, !spirv.arm.tensor<3x7xi8> -> !spirv.arm.tensor<4x12xi8>
+  spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<4x12xi8>
+}
+
+spirv.ARM.Graph @concat_axis_dimension_must_match_sum_of_inputs(%arg0: !spirv.arm.tensor<4x5xi8>, %arg1: !spirv.arm.tensor<4x7xi8>) -> (!spirv.arm.tensor<4x11xi8>) {
+  // expected-error @+1 {{op failed to verify that shape of output must match the concatenation of input1 along axis}}
+  %0 = spirv.Tosa.Concat axis = 1, %arg0, %arg1 : !spirv.arm.tensor<4x5xi8>, !spirv.arm.tensor<4x7xi8> -> !spirv.arm.tensor<4x11xi8>
+  spirv.ARM.GraphOutputs %0 : !spirv.arm.tensor<4x11xi8>
+}
+
 //===----------------------------------------------------------------------===//
 // spirv.TOSA.Pad
 //===----------------------------------------------------------------------===//
@@ -2299,6 +2427,15 @@ spirv.ARM.Graph @resize_bf16_input_output_element_type_must_be_bf16(%arg0: !spir
   spirv.ARM.GraphOutputs %4 : !spirv.arm.tensor<1x753x297x63xf32>
 }
 
+spirv.ARM.Graph @resize_input_output_batch_or_channel_mismatch(%arg0: !spirv.arm.tensor<1x48x33x63xf32>) -> (!spirv.arm.tensor<2x753x297x62xf32>) {
+  %1 = spirv.Constant dense<[16, 1, 9, 1]> : !spirv.arm.tensor<4xi32>
+  %2 = spirv.Constant dense<0> : !spirv.arm.tensor<2xi32>
+  %3 = spirv.Constant dense<[0, 8]> : !spirv.arm.tensor<2xi32>
+  // expected-error @+1 {{op failed to verify that shapes of input and output must satisfy [N,*,*,C] and [N,*,*,C]}}
+  %4 = spirv.Tosa.Resize mode = <Bilinear>, %arg0, %1, %2, %3 : !spirv.arm.tensor<1x48x33x63xf32>, !spirv.arm.tensor<4xi32>, !spirv.arm.tensor<2xi32>, !spirv.arm.tensor<2xi32> -> !spirv.arm.tensor<2x753x297x62xf32>
+  spirv.ARM.GraphOutputs %4 : !spirv.arm.tensor<2x753x297x62xf32>
+}
+
 //===----------------------------------------------------------------------===//
 // spirv.TOSA.Cast
 //===----------------------------------------------------------------------===//


        


More information about the Mlir-commits mailing list