[Mlir-commits] [mlir] 97d6fec - [mlir][tensor] Verify that `tensor.pad` padding is non-negative (#231389)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Fri Oct 23 01:30:06 PDT 2026


Author: Federico Bruzzone
Date: 2026-10-23T10:30:01+02:00
New Revision: 97d6feca9de0c1fdbbb346aaa8d0ff5cdc4d6b06

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

LOG: [mlir][tensor] Verify that `tensor.pad` padding is non-negative (#231389)

`tensor.pad` did not reject negative static `low` or `high` padding, so
an op like `tensor.pad %t low[-2] high[0]` was verified as long as the
result type was consistent with it.

With this PR we reject negative constant padding in `PadOp::verify`, and
document in the op description that the padding amounts must be
non-negative. A negative dynamic value is UB, since the verifier only
looks at the op itself.

Also make `FoldStaticPadding` keep negative constants as SSA operands,
so that canonicalization does not fold them into the attribute and
create an op the verifier rejects.

---------

Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Co-authored-by: Mehdi Amini <joker.eph at gmail.com>

Added: 
    

Modified: 
    mlir/include/mlir/Dialect/Tensor/IR/TensorOps.td
    mlir/lib/Dialect/Tensor/IR/TensorOps.cpp
    mlir/test/Dialect/Tensor/canonicalize.mlir
    mlir/test/Dialect/Tensor/invalid.mlir
    mlir/test/Dialect/Tensor/ops.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/include/mlir/Dialect/Tensor/IR/TensorOps.td b/mlir/include/mlir/Dialect/Tensor/IR/TensorOps.td
index beac1e91ee6e3c..6a1a19306c14c1 100644
--- a/mlir/include/mlir/Dialect/Tensor/IR/TensorOps.td
+++ b/mlir/include/mlir/Dialect/Tensor/IR/TensorOps.td
@@ -1275,6 +1275,9 @@ def Tensor_PadOp : Tensor_Op<"pad", [
     The result tensor dimensions are `low[i]` + `dim[i]` + `high[i]` for each
     dimension `i`. The number of elements of `low` and `high` must match the
     rank of the input tensor. They can be either a constant or a dynamic value.
+    The padding amounts must be non-negative. A negative constant is rejected
+    by the verifier. A negative dynamic value is undefined behavior: the
+    verifier does not look through the defining op of an SSA value.
 
     The region of the `tensor.pad` operation returns the value to use
     for the padding. The arguments of the region represent the index

diff  --git a/mlir/lib/Dialect/Tensor/IR/TensorOps.cpp b/mlir/lib/Dialect/Tensor/IR/TensorOps.cpp
index 53ee57fa72a17a..2ee6c93b313e69 100644
--- a/mlir/lib/Dialect/Tensor/IR/TensorOps.cpp
+++ b/mlir/lib/Dialect/Tensor/IR/TensorOps.cpp
@@ -3252,6 +3252,11 @@ void PadOp::getAsmResultNames(function_ref<void(Value, StringRef)> setNameFn) {
 }
 
 LogicalResult PadOp::verify() {
+  for (ArrayRef<int64_t> padding : {getStaticLow(), getStaticHigh()})
+    for (int64_t size : padding)
+      if (!ShapedType::isDynamic(size) && size < 0)
+        return emitOpError("padding must be non-negative, got ") << size;
+
   auto sourceType = llvm::cast<RankedTensorType>(getSource().getType());
   auto resultType = llvm::cast<RankedTensorType>(getResult().getType());
   auto expectedType =
@@ -3673,12 +3678,15 @@ struct FoldStaticPadding : public OpRewritePattern<PadOp> {
 
     auto outputDims = oldResultType.getShape();
 
-    // Extract the static info from the high and low operands.
+    // Extract the static info from the high and low operands. A negative
+    // constant is not folded, since the attribute must be non-negative.
+    // TODO: we should consider folding to a poison value in this case.
     SmallVector<int64_t> constOperandsLow;
     SmallVector<Value> newLows;
     for (auto operand : padTensorOp.getLow()) {
       APSInt intOp;
-      if (!matchPattern(operand, m_ConstantInt(&intOp))) {
+      if (!matchPattern(operand, m_ConstantInt(&intOp)) ||
+          intOp.getExtValue() < 0) {
         constOperandsLow.push_back(ShapedType::kDynamic);
         newLows.push_back(operand);
         continue;
@@ -3689,7 +3697,8 @@ struct FoldStaticPadding : public OpRewritePattern<PadOp> {
     SmallVector<Value> newHighs;
     for (auto operand : padTensorOp.getHigh()) {
       APSInt intOp;
-      if (!matchPattern(operand, m_ConstantInt(&intOp))) {
+      if (!matchPattern(operand, m_ConstantInt(&intOp)) ||
+          intOp.getExtValue() < 0) {
         constOperandsHigh.push_back(ShapedType::kDynamic);
         newHighs.push_back(operand);
         continue;

diff  --git a/mlir/test/Dialect/Tensor/canonicalize.mlir b/mlir/test/Dialect/Tensor/canonicalize.mlir
index 217ce64cb98683..f8d333fc0a90ac 100644
--- a/mlir/test/Dialect/Tensor/canonicalize.mlir
+++ b/mlir/test/Dialect/Tensor/canonicalize.mlir
@@ -1932,6 +1932,26 @@ func.func @pad_fold_static(%arg0: tensor<?x64x?x?xf32>) -> tensor<?x?x?x?xf32> {
 
 // -----
 
+// A negative constant is not folded into the attribute, since the verifier
+// requires the attribute to be non-negative.
+
+// CHECK-LABEL: func @pad_nofold_negative_constant(
+//  CHECK-SAME:   %[[ARG0:.*]]: tensor<4xf32>
+//       CHECK:   %[[C:.*]] = arith.constant -2 : index
+//       CHECK:   tensor.pad %[[ARG0]] low[%[[C]]] high[0]
+//       CHECK:   } : tensor<4xf32> to tensor<?xf32>
+func.func @pad_nofold_negative_constant(%arg0: tensor<4xf32>, %pad_value: f32)
+    -> tensor<?xf32> {
+  %c = arith.constant -2 : index
+  %0 = tensor.pad %arg0 low[%c] high[0] {
+  ^bb0(%arg1: index):
+    tensor.yield %pad_value : f32
+  } : tensor<4xf32> to tensor<?xf32>
+  return %0 : tensor<?xf32>
+}
+
+// -----
+
 // CHECK-LABEL: func @pad_nofold_same_static_shape(
 //  CHECK-SAME:   %[[ARG0:.*]]: tensor<5x6xf32>
 //       CHECK:   %[[PAD:.*]] = tensor.pad

diff  --git a/mlir/test/Dialect/Tensor/invalid.mlir b/mlir/test/Dialect/Tensor/invalid.mlir
index 19db4680b6a638..f613d309f1db45 100644
--- a/mlir/test/Dialect/Tensor/invalid.mlir
+++ b/mlir/test/Dialect/Tensor/invalid.mlir
@@ -731,3 +731,35 @@ func.func @no_fold_invalid_collapse() -> tensor<i64> {
     return %0 : tensor<i64>
 }
 
+// -----
+
+func.func @pad_negative_low(%arg0: tensor<10xf32>, %arg1: f32) -> tensor<8xf32> {
+  // expected-error @+1 {{padding must be non-negative, got -2}}
+  %0 = tensor.pad %arg0 low[-2] high[0] {
+  ^bb0(%arg2: index):
+    tensor.yield %arg1 : f32
+  } : tensor<10xf32> to tensor<8xf32>
+  return %0 : tensor<8xf32>
+}
+
+// -----
+
+func.func @pad_negative_high(%arg0: tensor<?x4xf32>, %arg1: f32) -> tensor<?x3xf32> {
+  // expected-error @+1 {{padding must be non-negative, got -1}}
+  %0 = tensor.pad %arg0 low[0, 0] high[0, -1] {
+  ^bb0(%arg2: index, %arg3: index):
+    tensor.yield %arg1 : f32
+  } : tensor<?x4xf32> to tensor<?x3xf32>
+  return %0 : tensor<?x3xf32>
+}
+
+// -----
+
+func.func @pad_negative_dynamic_dim(%arg0: tensor<?xf32>, %arg1: f32) -> tensor<?xf32> {
+  // expected-error @+1 {{padding must be non-negative, got -1}}
+  %0 = tensor.pad %arg0 low[-1] high[0] {
+  ^bb0(%arg2: index):
+    tensor.yield %arg1 : f32
+  } : tensor<?xf32> to tensor<?xf32>
+  return %0 : tensor<?xf32>
+}

diff  --git a/mlir/test/Dialect/Tensor/ops.mlir b/mlir/test/Dialect/Tensor/ops.mlir
index 681a934ba0698e..91f01915fac38c 100644
--- a/mlir/test/Dialect/Tensor/ops.mlir
+++ b/mlir/test/Dialect/Tensor/ops.mlir
@@ -312,6 +312,21 @@ func.func @pad_to_static_size(%arg0: tensor<?x?xf32>, %ub0: index, %ub1: index,
 
 // -----
 
+func.func @pad_positive_constant(%arg0: tensor<4xf32>, %pad_value: f32) -> tensor<?xf32> {
+  %c2 = arith.constant 2 : index
+  %0 = tensor.pad %arg0 low[%c2] high[0] {
+    ^bb0(%arg1: index):
+      tensor.yield %pad_value : f32
+    } : tensor<4xf32> to tensor<?xf32>
+  return %0 : tensor<?xf32>
+}
+// CHECK-LABEL: func @pad_positive_constant
+//       CHECK:   tensor.pad
+//  CHECK-SAME:     low[%{{.*}}]
+//       CHECK:    : tensor<4xf32> to tensor<?xf32>
+
+// -----
+
 // CHECK-LABEL: func @test_splat_op
 // CHECK-SAME: %[[S:.*]]: f32
 // CHECK-SAME: %[[P:.*]]: !llvm.ptr


        


More information about the Mlir-commits mailing list