[Mlir-commits] [mlir] [mlir][tosa] Emit a runtime assertion when lowering `tosa.pad` with negative padding (PR #232221)

Federico Bruzzone llvmlistbot at llvm.org
Thu Oct 15 23:28:20 PDT 2026


https://github.com/FedericoBruzzone updated https://github.com/llvm/llvm-project/pull/232221

>From 7b5ead7e072821fdae62881b1734898be6789bc3 Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Wed, 14 Oct 2026 17:45:50 +0200
Subject: [PATCH 1/2] [mlir][tosa] Fail lowering of `tosa.pad` -> `tensor.pad`
 with -1 padding

Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
---
 .../Conversion/TosaToTensor/TosaToTensor.cpp    |  7 +++++++
 .../TosaToTensor/tosa-to-tensor-invalid.mlir    | 10 ++++++++++
 .../Conversion/TosaToTensor/tosa-to-tensor.mlir | 17 -----------------
 3 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/mlir/lib/Conversion/TosaToTensor/TosaToTensor.cpp b/mlir/lib/Conversion/TosaToTensor/TosaToTensor.cpp
index 9bf9ca3ae7a89b..1c65abbb7af41a 100644
--- a/mlir/lib/Conversion/TosaToTensor/TosaToTensor.cpp
+++ b/mlir/lib/Conversion/TosaToTensor/TosaToTensor.cpp
@@ -354,6 +354,13 @@ class PadConverter : public OpConversionPattern<tosa::PadOp> {
       paddingVals.push_back(static_cast<int64_t>(idx.getInt()));
     }
 
+    // A padding of -1 means that the amount is not known, so it cannot be
+    // lowered to a `tensor.pad`, which requires non-negative padding.
+    if (llvm::is_contained(paddingVals, -1)) {
+      return rewriter.notifyMatchFailure(
+          padOp, "padding must not contain -1 (unknown amount)");
+    }
+
     ShapedType inputTy = cast<ShapedType>(input.getType());
     int64_t rank = inputTy.getRank();
 
diff --git a/mlir/test/Conversion/TosaToTensor/tosa-to-tensor-invalid.mlir b/mlir/test/Conversion/TosaToTensor/tosa-to-tensor-invalid.mlir
index 5e33ce2a2fcced..8741ea07431ec1 100644
--- a/mlir/test/Conversion/TosaToTensor/tosa-to-tensor-invalid.mlir
+++ b/mlir/test/Conversion/TosaToTensor/tosa-to-tensor-invalid.mlir
@@ -8,3 +8,13 @@ func.func @slice_resultType_unranked(%arg0: tensor<?xf32>) -> (tensor<*xf32>) {
   %2 = tosa.slice %arg0, %start, %size : (tensor<?xf32>, !tosa.shape<1>, !tosa.shape<1>) -> tensor<*xf32>
   return %2 : tensor<*xf32>
 }
+
+// -----
+
+func.func @pad_unknown_padding(%arg0: tensor<1x2xf32>) -> tensor<?x9xf32> {
+  %padding = tosa.const_shape values(dense<[-1, 2, 3, 4]> : tensor<4xindex>) : () -> !tosa.shape<4>
+  %pad_const = "tosa.const"() <{values = dense<3.14> : tensor<1xf32>}> : () -> tensor<1xf32>
+  // expected-error at +1 {{failed to legalize operation 'tosa.pad'}}
+  %0 = "tosa.pad"(%arg0, %padding, %pad_const) : (tensor<1x2xf32>, !tosa.shape<4>, tensor<1xf32>) -> tensor<?x9xf32>
+  return %0 : tensor<?x9xf32>
+}
diff --git a/mlir/test/Conversion/TosaToTensor/tosa-to-tensor.mlir b/mlir/test/Conversion/TosaToTensor/tosa-to-tensor.mlir
index c4e98bf7bece9d..414832d6b5d9fa 100644
--- a/mlir/test/Conversion/TosaToTensor/tosa-to-tensor.mlir
+++ b/mlir/test/Conversion/TosaToTensor/tosa-to-tensor.mlir
@@ -568,23 +568,6 @@ func.func @pad_dyn_input(%arg0 : tensor<?x2xf32>) -> (tensor<?x9xf32>) {
 }
 // -----
 
-func.func @pad_dyn_padding(%arg0 : tensor<1x2xf32>) -> (tensor<?x9xf32>) {
-  %0 = tosa.const_shape values(dense<[-1, 2, 3, 4]> : tensor<4xindex>) : () -> !tosa.shape<4>
-  %pad_const = "tosa.const"() <{values = dense<3.14> : tensor<1xf32>}> : () -> tensor<1xf32>
-  // CHECK-DAG: [[INDEX1:%.+]] = arith.constant -1 : index
-  // CHECK-DAG: [[INDEX2:%.+]] = arith.constant 2 : index
-  // CHECK-DAG: [[INDEX3:%.+]] = arith.constant 3 : index
-  // CHECK-DAG: [[INDEX4:%.+]] = arith.constant 4 : index
-  // CHECK-DAG: [[CST:%.+]] = arith.constant 3.140000e+00 : f32
-  // CHECK: tensor.pad %[[ARG0]] low{{\[}}[[INDEX1]], [[INDEX3]]] high{{\[}}[[INDEX2]], [[INDEX4]]]  {
-  // CHECK:   tensor.yield [[CST]]
-  // CHECK: } : tensor<1x2xf32> to tensor<?x9xf32>
-  %1 = "tosa.pad"(%arg0, %0, %pad_const)  : (tensor<1x2xf32>, !tosa.shape<4>, tensor<1xf32>)  -> (tensor<?x9xf32>)
-  return %1 : tensor<?x9xf32>
-}
-
-// -----
-
 // CHECK-LABEL: @concat
 // CHECK-SAME: %[[ARG0:.+]]: tensor<5x1xf32>
 // CHECK-SAME: %[[ARG1:.+]]: tensor<6x1xf32>

>From 1119e730eb2f2b4b01fbaf2ca2b87ed82a1eccc4 Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Thu, 15 Oct 2026 17:19:08 +0200
Subject: [PATCH 2/2] Implement the assert option

Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
---
 mlir/include/mlir/Conversion/Passes.td        |  1 +
 mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td  | 18 ++++++++++--------
 .../Conversion/TosaToTensor/CMakeLists.txt    |  1 +
 .../Conversion/TosaToTensor/TosaToTensor.cpp  | 15 ++++++++++-----
 .../TosaToTensor/TosaToTensorPass.cpp         |  2 ++
 .../TosaToTensor/tosa-to-tensor-invalid.mlir  | 10 ----------
 .../TosaToTensor/tosa-to-tensor.mlir          | 19 +++++++++++++++++++
 7 files changed, 43 insertions(+), 23 deletions(-)

diff --git a/mlir/include/mlir/Conversion/Passes.td b/mlir/include/mlir/Conversion/Passes.td
index f7a3954547cf75..34ec3258ef0fbe 100644
--- a/mlir/include/mlir/Conversion/Passes.td
+++ b/mlir/include/mlir/Conversion/Passes.td
@@ -1530,6 +1530,7 @@ def TosaToSPIRVTosa : Pass<"tosa-to-spirv-tosa"> {
 def TosaToTensorPass : Pass<"tosa-to-tensor"> {
   let summary = "Lower TOSA to the Tensor dialect";
   let dependentDialects = [
+    "cf::ControlFlowDialect",
     "tensor::TensorDialect",
   ];
   let description = [{
diff --git a/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td b/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td
index 339061b2aee837..9fccff3544f2a5 100644
--- a/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td
+++ b/mlir/include/mlir/Dialect/Tosa/IR/TosaOps.td
@@ -2108,6 +2108,16 @@ def Tosa_PadOp : Tosa_InferShapedTypeOp<"pad", [Pure]> {
     Returns a new tensor with the padding included. The pad_const value includes
     the zero point if the tensor uses a zero point.
 
+    The padding amounts must be non-negative. A negative padding is not valid
+    for execution. For a constant padding, the verifier accepts -1 as a
+    placeholder for an amount that is not known at compile time, in which case
+    the corresponding output dimension is dynamic.
+
+    The TOSA specification requires a negative padding to be diagnosed
+    (`ERROR_IF`). The `tosa-validate` pass reports it, and the lowering to the
+    Tensor dialect emits a runtime assertion. It is recommended to run
+    `tosa-validate` before lowering.
+
     Example:
 
     ```mlir
@@ -2115,14 +2125,6 @@ def Tosa_PadOp : Tosa_InferShapedTypeOp<"pad", [Pure]> {
     %padding = tosa.const_shape values(dense<[1, 2, 3, 4]> : tensor<4xindex>) : () -> !tosa.shape<4>
     tosa.pad %arg0, %padding, %pad_const : (tensor<1x2xf32>, !tosa.shape<4>, tensor<1xf32>) -> tensor<4x9xf32>
     ```
-
-    Example 2:
-
-    ```mlir
-    %pad_const = "tosa.const"() <{values = dense<3.14> : tensor<1xf32>}> : () -> tensor<1xf32>
-    %padding = tosa.const_shape values(dense<[-1, 2, 3, 4]> : tensor<4xindex>) : () -> !tosa.shape<4>
-    tosa.pad %arg0, %padding, %pad_const : (tensor<1x2xf32>, !tosa.shape<4>, tensor<1xf32>) -> tensor<?x9xf32>
-    ```
   }];
 
   let arguments = (ins
diff --git a/mlir/lib/Conversion/TosaToTensor/CMakeLists.txt b/mlir/lib/Conversion/TosaToTensor/CMakeLists.txt
index 2870baa20757b5..9220d76339b22f 100644
--- a/mlir/lib/Conversion/TosaToTensor/CMakeLists.txt
+++ b/mlir/lib/Conversion/TosaToTensor/CMakeLists.txt
@@ -10,6 +10,7 @@ add_mlir_conversion_library(MLIRTosaToTensor
   MLIRConversionPassIncGen
 
   LINK_LIBS PUBLIC
+  MLIRControlFlowDialect
   MLIRTensorDialect
   MLIRTensorUtils
   MLIRIR
diff --git a/mlir/lib/Conversion/TosaToTensor/TosaToTensor.cpp b/mlir/lib/Conversion/TosaToTensor/TosaToTensor.cpp
index 1c65abbb7af41a..f743a451c8b73c 100644
--- a/mlir/lib/Conversion/TosaToTensor/TosaToTensor.cpp
+++ b/mlir/lib/Conversion/TosaToTensor/TosaToTensor.cpp
@@ -13,6 +13,7 @@
 #include "mlir/Conversion/TosaToTensor/TosaToTensor.h"
 #include "mlir/Dialect/Arith/IR/Arith.h"
 #include "mlir/Dialect/Arith/Utils/Utils.h"
+#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
 #include "mlir/Dialect/Tensor/IR/Tensor.h"
 #include "mlir/Dialect/Tosa/IR/TosaOps.h"
 #include "mlir/Dialect/Tosa/Utils/ConversionUtils.h"
@@ -354,11 +355,15 @@ class PadConverter : public OpConversionPattern<tosa::PadOp> {
       paddingVals.push_back(static_cast<int64_t>(idx.getInt()));
     }
 
-    // A padding of -1 means that the amount is not known, so it cannot be
-    // lowered to a `tensor.pad`, which requires non-negative padding.
-    if (llvm::is_contained(paddingVals, -1)) {
-      return rewriter.notifyMatchFailure(
-          padOp, "padding must not contain -1 (unknown amount)");
+    // The TOSA specification requires a negative padding (-1 stands for an
+    // amount that is not known) to be diagnosed.
+    if (llvm::any_of(paddingVals, [](int64_t val) { return val < 0; })) {
+      Value fail = arith::ConstantOp::create(rewriter, loc,
+                                             rewriter.getBoolAttr(false));
+      cf::AssertOp::create(
+          rewriter, loc, fail,
+          rewriter.getStringAttr("tosa.pad: padding must be non-negative, run "
+                                 "tosa-validate to diagnose it statically"));
     }
 
     ShapedType inputTy = cast<ShapedType>(input.getType());
diff --git a/mlir/lib/Conversion/TosaToTensor/TosaToTensorPass.cpp b/mlir/lib/Conversion/TosaToTensor/TosaToTensorPass.cpp
index a7f415c574843e..5633b655d68065 100644
--- a/mlir/lib/Conversion/TosaToTensor/TosaToTensorPass.cpp
+++ b/mlir/lib/Conversion/TosaToTensor/TosaToTensorPass.cpp
@@ -13,6 +13,7 @@
 #include "mlir/Conversion/TosaToTensor/TosaToTensor.h"
 
 #include "mlir/Dialect/Arith/IR/Arith.h"
+#include "mlir/Dialect/ControlFlow/IR/ControlFlow.h"
 #include "mlir/Dialect/Tensor/IR/Tensor.h"
 #include "mlir/Dialect/Tosa/IR/TosaOps.h"
 #include "mlir/Dialect/Tosa/Transforms/Passes.h"
@@ -38,6 +39,7 @@ struct TosaToTensor : public impl::TosaToTensorPassBase<TosaToTensor> {
     target.addIllegalOp<tosa::SliceOp>();
     target.addIllegalOp<tosa::PadOp>();
     target.addLegalDialect<arith::ArithDialect>();
+    target.addLegalDialect<cf::ControlFlowDialect>();
     target.addLegalDialect<tensor::TensorDialect>();
 
     TypeConverter converter;
diff --git a/mlir/test/Conversion/TosaToTensor/tosa-to-tensor-invalid.mlir b/mlir/test/Conversion/TosaToTensor/tosa-to-tensor-invalid.mlir
index 8741ea07431ec1..5e33ce2a2fcced 100644
--- a/mlir/test/Conversion/TosaToTensor/tosa-to-tensor-invalid.mlir
+++ b/mlir/test/Conversion/TosaToTensor/tosa-to-tensor-invalid.mlir
@@ -8,13 +8,3 @@ func.func @slice_resultType_unranked(%arg0: tensor<?xf32>) -> (tensor<*xf32>) {
   %2 = tosa.slice %arg0, %start, %size : (tensor<?xf32>, !tosa.shape<1>, !tosa.shape<1>) -> tensor<*xf32>
   return %2 : tensor<*xf32>
 }
-
-// -----
-
-func.func @pad_unknown_padding(%arg0: tensor<1x2xf32>) -> tensor<?x9xf32> {
-  %padding = tosa.const_shape values(dense<[-1, 2, 3, 4]> : tensor<4xindex>) : () -> !tosa.shape<4>
-  %pad_const = "tosa.const"() <{values = dense<3.14> : tensor<1xf32>}> : () -> tensor<1xf32>
-  // expected-error at +1 {{failed to legalize operation 'tosa.pad'}}
-  %0 = "tosa.pad"(%arg0, %padding, %pad_const) : (tensor<1x2xf32>, !tosa.shape<4>, tensor<1xf32>) -> tensor<?x9xf32>
-  return %0 : tensor<?x9xf32>
-}
diff --git a/mlir/test/Conversion/TosaToTensor/tosa-to-tensor.mlir b/mlir/test/Conversion/TosaToTensor/tosa-to-tensor.mlir
index 414832d6b5d9fa..4ff26c3931c4d7 100644
--- a/mlir/test/Conversion/TosaToTensor/tosa-to-tensor.mlir
+++ b/mlir/test/Conversion/TosaToTensor/tosa-to-tensor.mlir
@@ -568,6 +568,25 @@ func.func @pad_dyn_input(%arg0 : tensor<?x2xf32>) -> (tensor<?x9xf32>) {
 }
 // -----
 
+func.func @pad_dyn_padding(%arg0 : tensor<1x2xf32>) -> (tensor<?x9xf32>) {
+  %0 = tosa.const_shape values(dense<[-1, 2, 3, 4]> : tensor<4xindex>) : () -> !tosa.shape<4>
+  %pad_const = "tosa.const"() <{values = dense<3.14> : tensor<1xf32>}> : () -> tensor<1xf32>
+  // CHECK: [[FALSE:%.+]] = arith.constant false
+  // CHECK: cf.assert [[FALSE]], "tosa.pad: padding must be non-negative, run tosa-validate to diagnose it statically"
+  // CHECK-DAG: [[INDEX1:%.+]] = arith.constant -1 : index
+  // CHECK-DAG: [[INDEX2:%.+]] = arith.constant 2 : index
+  // CHECK-DAG: [[INDEX3:%.+]] = arith.constant 3 : index
+  // CHECK-DAG: [[INDEX4:%.+]] = arith.constant 4 : index
+  // CHECK-DAG: [[CST:%.+]] = arith.constant 3.140000e+00 : f32
+  // CHECK: tensor.pad %[[ARG0]] low{{\[}}[[INDEX1]], [[INDEX3]]] high{{\[}}[[INDEX2]], [[INDEX4]]]  {
+  // CHECK:   tensor.yield [[CST]]
+  // CHECK: } : tensor<1x2xf32> to tensor<?x9xf32>
+  %1 = "tosa.pad"(%arg0, %0, %pad_const)  : (tensor<1x2xf32>, !tosa.shape<4>, tensor<1xf32>)  -> (tensor<?x9xf32>)
+  return %1 : tensor<?x9xf32>
+}
+
+// -----
+
 // CHECK-LABEL: @concat
 // CHECK-SAME: %[[ARG0:.+]]: tensor<5x1xf32>
 // CHECK-SAME: %[[ARG1:.+]]: tensor<6x1xf32>



More information about the Mlir-commits mailing list