[Mlir-commits] [mlir] [mlir][tosa] Add option to validate function signature during validation (PR #214276)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Thu Aug 6 02:59:00 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir-linalg
Author: Luke Hutton (lhutton1)
<details>
<summary>Changes</summary>
This commit adds a new option to the TOSA validation pass, "validate-function-signature", which allows users to specify whether the function signature should be checked with respect to: use of tensor types (tosa.shape types are not supported at function boundaries), supported data types (currently there is no consideration for the loaded profiles/extensions) and the shape of tensor arguments/return types.
---
Full diff: https://github.com/llvm/llvm-project/pull/214276.diff
6 Files Affected:
- (modified) mlir/include/mlir/Dialect/Tosa/Transforms/Passes.td (+5-1)
- (modified) mlir/lib/Conversion/TosaToLinalg/TosaToLinalgPass.cpp (+2-1)
- (modified) mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp (+65-17)
- (modified) mlir/test/Dialect/Tosa/invalid.mlir (-15)
- (added) mlir/test/Dialect/Tosa/tosa-validation-validate-func-signature-invalid.mlir (+58)
- (added) mlir/test/Dialect/Tosa/tosa-validation-validate-func-signature-valid.mlir (+15)
``````````diff
diff --git a/mlir/include/mlir/Dialect/Tosa/Transforms/Passes.td b/mlir/include/mlir/Dialect/Tosa/Transforms/Passes.td
index 005cbfab782df..ac2b545ab481b 100644
--- a/mlir/include/mlir/Dialect/Tosa/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/Tosa/Transforms/Passes.td
@@ -90,7 +90,11 @@ def TosaValidation : Pass<"tosa-validate", "mlir::ModuleOp"> {
/*default=*/"false",
"Disable checks for operations that are determined to be invalid due to their "
"operand/result datatypes not aligning with the 'Supported Data Types' "
- "sections of the specifciation">
+ "sections of the specifciation">,
+ Option<"validateFunctionSignature", "validate-function-signature", "bool",
+ /*default=*/"false",
+ "Verify if the function signature is valid for TOSA dialect, i.e. all "
+ "function arguments and return types are tosa-conformant tensor types">,
];
}
diff --git a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgPass.cpp b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgPass.cpp
index 32f487d03858d..ca09299619bdb 100644
--- a/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgPass.cpp
+++ b/mlir/lib/Conversion/TosaToLinalg/TosaToLinalgPass.cpp
@@ -146,7 +146,8 @@ void mlir::tosa::registerTosaToLinalgPipelines() {
if (pipelineOpts.validation) {
validationOptions = TosaValidationOptions{
/*strictOpSpecAlignment=*/false,
- /*allowInvalidOpDatatypeCombinations=*/false};
+ /*allowInvalidOpDatatypeCombinations=*/false,
+ /*validateFunctionSignature=*/false};
}
tosa::addTosaToLinalgPasses(pm, tosaToLinalgOptions,
tosaToLinalgNamedOptions,
diff --git a/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp b/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp
index 83367fdf4e5f3..2008a4aa92c4a 100644
--- a/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp
+++ b/mlir/lib/Dialect/Tosa/Transforms/TosaValidation.cpp
@@ -186,6 +186,7 @@ struct TosaValidation : public tosa::impl::TosaValidationBase<TosaValidation> {
this->strictOpSpecAlignment = options.strictOpSpecAlignment;
this->allowInvalidOpDatatypeCombinations =
options.allowInvalidOpDatatypeCombinations;
+ this->validateFunctionSignature = options.validateFunctionSignature;
}
void runOnOperation() final;
@@ -671,7 +672,9 @@ struct TosaValidation : public tosa::impl::TosaValidationBase<TosaValidation> {
LogicalResult CheckVariableReadOrWrite(Operation *op);
LogicalResult validateValidElementType(Operation *op, Type type,
bool allowUnsigned = false);
- LogicalResult validateOperationElementTypes(Operation *op,
+ LogicalResult validateOperationElementTypes(TosaOp op,
+ bool allowUnsigned = false);
+ LogicalResult validateOperationElementTypes(func::FuncOp op,
bool allowUnsigned = false);
SmallVector<
@@ -912,18 +915,18 @@ LogicalResult TosaValidation::levelCheckSize(Operation *op,
<< " or below";
}
- int64_t element_bits = tosa::getBitWidth(getElementTypeOrSelf(type));
- int64_t element_bytes = std::max(INT64_C(1), element_bits / 8);
- int64_t size = element_bytes * type.getNumElements();
+ int64_t elementBits = tosa::getBitWidth(getElementTypeOrSelf(type));
+ int64_t elementBytes = std::max(INT64_C(1), elementBits / 8);
+ int64_t size = elementBytes * type.getNumElements();
// According to 1.11. Tensor Definitions of Tosa spec, the value of
// tensor_size_t is 1 << MAX_LOG2_SIZE) - 1 where MAX_LOG2_SIZE is
// defined in 1.7. Levels.
// For each tensor, the number of tensor elements multiplied by the
// element size in bytes must be representable as a tensor_size_t.
- const int64_t max_size =
+ const int64_t maxSize =
(INT64_C(1) << targetEnv.getLevel().MAX_LOG2_SIZE) - 1;
- if (size > max_size)
+ if (size > maxSize)
return op->emitOpError()
<< "failed level check: " << operandOrResult
<< " tensor size (in bytes) <= (1 << MAX_LOG2_SIZE - 1)";
@@ -1459,15 +1462,51 @@ LogicalResult TosaValidation::applyErrorIfCheck(Operation *op) {
}
LogicalResult TosaValidation::applyFunctionSignatureCheck(func::FuncOp op) {
- const auto isShapeType = [](Type type) { return isa<tosa::shapeType>(type); };
- if (llvm::any_of(op.getArgumentTypes(), isShapeType))
+ // Require tensor type parameters and results
+ const auto isTensorType = [](Type type) { return isa<TensorType>(type); };
+ if (!llvm::all_of(op.getArgumentTypes(), isTensorType))
return op.emitOpError()
<< "Function argument types must be a tensor type to be TOSA "
"compliant, got !tosa.shape type";
- if (llvm::any_of(op.getResultTypes(), isShapeType))
+ if (!llvm::all_of(op.getResultTypes(), isTensorType))
return op.emitOpError()
<< "Function return types must be a tensor type to be TOSA "
"compliant, got !tosa.shape type";
+
+ // Validate element types
+ if (failed(validateOperationElementTypes(op, !strictOpSpecAlignment)))
+ return failure();
+
+ // Level check
+ const TosaLevel tosaLevel = targetEnv.getLevel();
+ for (const auto &[idx, argType] : llvm::enumerate(op.getArgumentTypes())) {
+ const std::string inputDesc = llvm::formatv("input argument {0}", idx);
+ if (failed(levelCheckRank(op, argType, inputDesc, tosaLevel.MAX_RANK)))
+ return failure();
+ if (failed(levelCheckSize(op, argType, inputDesc)))
+ return failure();
+ }
+ for (const auto &[idx, resultType] : llvm::enumerate(op.getResultTypes())) {
+ const std::string resultDesc = llvm::formatv("return value {0}", idx);
+ if (failed(levelCheckRank(op, resultType, resultDesc, tosaLevel.MAX_RANK)))
+ return failure();
+ if (failed(levelCheckSize(op, resultType, resultDesc)))
+ return failure();
+ }
+
+ // Explicitly check for no zero dimensions
+ // Note: This check is not required for TOSA operations since it is mandated
+ // on construction
+ for (const Type &argType :
+ llvm::concat<const Type>(op.getArgumentTypes(), op.getResultTypes())) {
+ if (auto shapedType = dyn_cast<ShapedType>(argType)) {
+ if (llvm::any_of(shapedType.getShape(),
+ [](int64_t dim) { return dim == 0; }))
+ return op.emitOpError() << "Function argument or return types must not "
+ "have zero dimensions";
+ }
+ }
+
return success();
}
@@ -1508,8 +1547,7 @@ LogicalResult TosaValidation::validateValidElementType(Operation *op, Type type,
}
LogicalResult
-TosaValidation::validateOperationElementTypes(Operation *op,
- bool allowUnsigned) {
+TosaValidation::validateOperationElementTypes(TosaOp op, bool allowUnsigned) {
for (Value operand : op->getOperands()) {
Type elementTy = getElementTypeOrSelf(operand);
if (failed(validateValidElementType(op, elementTy, allowUnsigned)))
@@ -1522,11 +1560,23 @@ TosaValidation::validateOperationElementTypes(Operation *op,
return failure();
}
- if (auto variableOp = dyn_cast<tosa::VariableOp>(op)) {
+ if (auto variableOp = dyn_cast<tosa::VariableOp>(*op)) {
if (failed(
validateValidElementType(op, variableOp.getType(), allowUnsigned)))
return failure();
}
+ return success();
+}
+
+LogicalResult
+TosaValidation::validateOperationElementTypes(func::FuncOp op,
+ bool allowUnsigned) {
+ for (const Type &argType :
+ llvm::concat<const Type>(op.getArgumentTypes(), op.getResultTypes())) {
+ const Type elementTy = getElementTypeOrSelf(argType);
+ if (failed(validateValidElementType(op, elementTy, allowUnsigned)))
+ return failure();
+ }
return success();
}
@@ -1545,15 +1595,13 @@ void TosaValidation::runOnOperation() {
targetEnv = *maybeTargetEnv;
const auto functions = modOp.getOps<func::FuncOp>();
- if (llvm::any_of(functions, [&](func::FuncOp func) {
+ if (validateFunctionSignature &&
+ llvm::any_of(functions, [&](func::FuncOp func) {
return failed(applyFunctionSignatureCheck(func));
}))
return signalPassFailure();
- modOp.walk([&](Operation *op) {
- if (op->getDialect() != tosaDialect)
- return;
-
+ modOp.walk([&](TosaOp op) {
// validate operator element types:
// - rescale operator is allowed to have ui8/ui16/ui32
// operands/results when strictOpSpecAlignment is false
diff --git a/mlir/test/Dialect/Tosa/invalid.mlir b/mlir/test/Dialect/Tosa/invalid.mlir
index 40e7049bd6284..78fecd7bdc7d5 100644
--- a/mlir/test/Dialect/Tosa/invalid.mlir
+++ b/mlir/test/Dialect/Tosa/invalid.mlir
@@ -2168,21 +2168,6 @@ func.func @test_conv2d_block_scaled(%arg0: tensor<*xf4E2M1FN>, %arg1: tensor<*xf
// -----
-// expected-error at +1 {{func.func' op Function argument types must be a tensor type to be TOSA compliant, got !tosa.shape type}}
-func.func @test_shape_func_input(%arg0: !tosa.shape<1>) {
- return
-}
-
-// -----
-
-// expected-error at +1 {{'func.func' op Function return types must be a tensor type to be TOSA compliant, got !tosa.shape type}}
-func.func @test_shape_func_output() -> !tosa.shape<4> {
- %cst = tosa.const_shape {values = dense<[1, 2, 3, 4]> : tensor<4xindex>} : () -> !tosa.shape<4>
- return %cst : !tosa.shape<4>
-}
-
-// -----
-
func.func @test_cast_f32_plain_fp4(%arg0: tensor<4x32xf32>) -> tensor<4x32xf4E2M1FN> {
// expected-error at +1 {{'tosa.cast' op illegal: operation operand/result data types did not align with any profile or extension, got (f32,fp4e2m1)}}
%0 = tosa.cast %arg0 : (tensor<4x32xf32>) -> tensor<4x32xf4E2M1FN>
diff --git a/mlir/test/Dialect/Tosa/tosa-validation-validate-func-signature-invalid.mlir b/mlir/test/Dialect/Tosa/tosa-validation-validate-func-signature-invalid.mlir
new file mode 100644
index 0000000000000..ea16422b1ca73
--- /dev/null
+++ b/mlir/test/Dialect/Tosa/tosa-validation-validate-func-signature-invalid.mlir
@@ -0,0 +1,58 @@
+// RUN: mlir-opt %s -split-input-file -verify-diagnostics -tosa-attach-target="specification_version=1.1.draft profiles=pro_int,pro_fp extensions=shape level=8k" -tosa-validate="strict-op-spec-alignment validate-function-signature"
+
+// expected-error at +1 {{func.func' op Function argument types must be a tensor type to be TOSA compliant, got !tosa.shape type}}
+func.func @test_shape_func_input(%arg0: !tosa.shape<1>) {
+ return
+}
+
+// -----
+
+// expected-error at +1 {{'func.func' op Function return types must be a tensor type to be TOSA compliant, got !tosa.shape type}}
+func.func @test_shape_func_output() -> !tosa.shape<4> {
+ %cst = tosa.const_shape {values = dense<[1, 2, 3, 4]> : tensor<4xindex>} : () -> !tosa.shape<4>
+ return %cst : !tosa.shape<4>
+}
+
+// -----
+
+// expected-error at +1 {{'func.func' op failed level check: input argument 0 rank(shape) <= MAX_RANK}}
+func.func @test_argument_level_check(%arg0: tensor<1x2x3x4x5x6x7x8x9xi8>) {
+ return
+}
+
+// -----
+
+// expected-error at +1 {{'func.func' op failed level check: return value 0 rank(shape) <= MAX_RANK}}
+func.func @test_result_level_check() -> tensor<1x2x3x4x5x6x7x8xi8> {
+ %0 = arith.constant dense<0> : tensor<1x2x3x4x5x6x7x8xi8>
+ return %0 : tensor<1x2x3x4x5x6x7x8xi8>
+}
+
+// -----
+
+// expected-error at +1 {{'func.func' op Function argument or return types must not have zero dimensions}}
+func.func @test_argument_no_zero_dims(%arg0: tensor<1x0xi8>) {
+ return
+}
+
+// -----
+
+// expected-error at +1 {{'func.func' op Function argument or return types must not have zero dimensions}}
+func.func @test_result_level_check() -> tensor<1x0xi8> {
+ %0 = arith.constant dense<0> : tensor<1x0xi8>
+ return %0 : tensor<1x0xi8>
+}
+
+// -----
+
+// expected-error at +1 {{'func.func' op failed level check: unranked tensor}}
+func.func @test_unranked_identity(%arg0: tensor<*xi8>) -> tensor<*xi8> {
+ return %arg0 : tensor<*xi8>
+}
+
+// -----
+
+// expected-error at +1 {{'func.func' op is not profile-aligned: element type 'f64' is not legal}}
+func.func @test_unsupported_element_type(%arg0: tensor<1x2x1x4x5xf64>) -> tensor<1x2x1x4x5xf64> {
+ return %arg0 : tensor<1x2x1x4x5xf64>
+}
diff --git a/mlir/test/Dialect/Tosa/tosa-validation-validate-func-signature-valid.mlir b/mlir/test/Dialect/Tosa/tosa-validation-validate-func-signature-valid.mlir
new file mode 100644
index 0000000000000..e1b55cfe0663c
--- /dev/null
+++ b/mlir/test/Dialect/Tosa/tosa-validation-validate-func-signature-valid.mlir
@@ -0,0 +1,15 @@
+// RUN: mlir-opt %s -split-input-file -verify-diagnostics -tosa-attach-target="specification_version=1.1.draft profiles=pro_int,pro_fp extensions=shape level=8k" -tosa-validate="strict-op-spec-alignment validate-function-signature" | FileCheck %s
+
+// CHECK-LABEL: test_valid_arguments
+func.func @test_valid_arguments(%arg0: tensor<1x2x3x4xi8>, %arg1: tensor<1x2x3xf32>) {
+ return
+}
+
+// -----
+
+// CHECK-LABEL: test_valid_results
+func.func @test_valid_results() -> (tensor<1x2x3x4xi8>, tensor<1x2x3xf32>) {
+ %0 = arith.constant dense<0> : tensor<1x2x3x4xi8>
+ %1 = arith.constant dense<0.0> : tensor<1x2x3xf32>
+ return %0, %1 : tensor<1x2x3x4xi8>, tensor<1x2x3xf32>
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/214276
More information about the Mlir-commits
mailing list