[Mlir-commits] [mlir] [mlir][linalg] Use the tile size for untransformed Winograd axes (PR #226119)
Samarth Narang
llvmlistbot at llvm.org
Thu Sep 24 05:02:57 PDT 2026
https://github.com/snarang181 updated https://github.com/llvm/llvm-project/pull/226119
>From 756c7e7cb25d54b2f6e23763257d2d245fe9f6e9 Mon Sep 17 00:00:00 2001
From: Samarth Narang <sn2910 at nyu.edu>
Date: Thu, 24 Sep 2026 11:00:12 +0000
Subject: [PATCH 1/2] [mlir][linalg] Use the tile size for untransformed
Winograd axes
When a Winograd input or output transform leaves one spatial axis
untransformed (alpha == 1), the tiled implementation already maps the offset
one to one, but hard-coded the corresponding slice size to 1. A tile of size 2
on that axis therefore paired a two-position result tile with a one-row
operand slice. Use the requested tile size instead, in both
WinogradInputTransformOp::getTiledImplementation and
WinogradOutputTransformOp::getResultTilePosition.
Doing so exposes the same gap in WinogradInputTransformOp::verify: for a
dynamic input extent it expected the transformed alpha unconditionally, so a
partial tile with a dynamic size on an untransformed axis was rejected. Derive
the expected alpha from leftTransform/rightTransform there as the static
branch already does.
Both are leftovers of #123675, which switched the offsets and the static
verifier branch to the identity mapping but not the sizes.
Fixes #225251
---
mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp | 13 +++--
.../Linalg/transform-tile-winograd.mlir | 51 +++++++++++++++++++
2 files changed, 57 insertions(+), 7 deletions(-)
diff --git a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
index 87b14439597b43..60164449917388 100644
--- a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
+++ b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
@@ -3550,7 +3550,7 @@ LogicalResult WinogradInputTransformOp::verify() {
SmallVector<int64_t> expectedOutputShape(6, inputH);
if (ShapedType::isDynamic(inputH)) {
- expectedOutputShape[getOutputAlphaHDim()] = tileSize;
+ expectedOutputShape[getOutputAlphaHDim()] = leftTransform ? tileSize : 1;
expectedOutputShape[getOutputTileHDim()] = ShapedType::kDynamic;
} else {
expectedOutputShape[getOutputAlphaHDim()] = leftTransform ? tileSize : 1;
@@ -3558,7 +3558,7 @@ LogicalResult WinogradInputTransformOp::verify() {
leftTransform ? (inputH - (r - 1)) / m : inputH;
}
if (ShapedType::isDynamic(inputW)) {
- expectedOutputShape[getOutputAlphaWDim()] = tileSize;
+ expectedOutputShape[getOutputAlphaWDim()] = rightTransform ? tileSize : 1;
expectedOutputShape[getOutputTileWDim()] = ShapedType::kDynamic;
} else {
expectedOutputShape[getOutputAlphaWDim()] = rightTransform ? tileSize : 1;
@@ -3685,9 +3685,9 @@ WinogradInputTransformOp::getTiledImplementation(OpBuilder &builder,
sliceOffsets.append(
{offsets[getOutputNDim()], offsetH, offsetW, offsets[getOutputCDim()]});
OpFoldResult sizeH =
- alphaH != 1 ? OpFoldResult(mappedSizeH) : OpFoldResult(oneAttr);
+ alphaH != 1 ? OpFoldResult(mappedSizeH) : sizes[getOutputTileHDim()];
OpFoldResult sizeW =
- alphaW != 1 ? OpFoldResult(mappedSizeW) : OpFoldResult(oneAttr);
+ alphaW != 1 ? OpFoldResult(mappedSizeW) : sizes[getOutputTileWDim()];
sliceSizes.append(
{sizes[getOutputNDim()], sizeH, sizeW, sizes[getOutputCDim()]});
int64_t inputRank = getInputOperandRank();
@@ -3818,13 +3818,12 @@ LogicalResult WinogradOutputTransformOp::getResultTilePosition(
Value mappedSizeW = affine::makeComposedAffineApply(
builder, loc, affineMap, sizes[getValueTileWDim()]);
- IntegerAttr oneAttr = builder.getI64IntegerAttr(1);
OpFoldResult offsetH = OpFoldResult(mappedOffsetH);
OpFoldResult offsetW = OpFoldResult(mappedOffsetW);
OpFoldResult sizeH =
- valueH != 1 ? OpFoldResult(mappedSizeH) : OpFoldResult(oneAttr);
+ valueH != 1 ? OpFoldResult(mappedSizeH) : sizes[getValueTileHDim()];
OpFoldResult sizeW =
- valueW != 1 ? OpFoldResult(mappedSizeW) : OpFoldResult(oneAttr);
+ valueW != 1 ? OpFoldResult(mappedSizeW) : sizes[getValueTileWDim()];
resultOffsets.append(
{offsets[getValueNDim()], offsetH, offsetW, offsets[getValueFDim()]});
diff --git a/mlir/test/Dialect/Linalg/transform-tile-winograd.mlir b/mlir/test/Dialect/Linalg/transform-tile-winograd.mlir
index beb8d0b1257384..03a42a4f74ce1b 100644
--- a/mlir/test/Dialect/Linalg/transform-tile-winograd.mlir
+++ b/mlir/test/Dialect/Linalg/transform-tile-winograd.mlir
@@ -386,3 +386,54 @@ module attributes {transform.with_named_sequence} {
// CHECK: %[[S8:.*]] = affine.apply #[[$MAP2]]()
// CHECK: %[[EXTRACTED_SLICE_9:.*]] = tensor.extract_slice %[[ARG12]][%[[ARG6]], %[[S5]], %[[S6]], %[[ARG8]]] [1, %[[S7]], 1, 1] [1, 1, 1, 1] : tensor<3x8x1x5xf32> to tensor<1x?x1x1xf32>
// CHECK: %[[S9:.*]] = linalg.winograd_output_transform fmr(F_4_3) ins(%[[EXTRACTED_SLICE]] : tensor<6x1x1x1x1x1xf32>) outs(%[[EXTRACTED_SLICE_9]] : tensor<1x?x1x1xf32>) -> tensor<1x?x1x1xf32>
+
+// -----
+
+func.func @tile_winograd_input_alpha_h_one(%arg0: tensor<1x3x10x2xf32>, %arg1: tensor<1x6x3x2x1x2xf32>) -> tensor<1x6x3x2x1x2xf32> {
+ %0 = linalg.winograd_input_transform fmr(F_4_3) ins(%arg0 : tensor<1x3x10x2xf32>) outs(%arg1 : tensor<1x6x3x2x1x2xf32>) -> tensor<1x6x3x2x1x2xf32>
+ return %0 : tensor<1x6x3x2x1x2xf32>
+}
+
+module attributes {transform.with_named_sequence} {
+ transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
+ %0 = transform.structured.match ops{["linalg.winograd_input_transform"]} in %arg1 : (!transform.any_op) -> !transform.any_op
+ %1, %loop:2 = transform.structured.tile_using_for %0 tile_sizes [0, 0, 2, 1, 0, 0] : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op)
+ transform.yield
+ }
+}
+
+// The untransformed H axis maps one to one, so the input slice must use the
+// tile size rather than a single row.
+// CHECK-LABEL: func.func @tile_winograd_input_alpha_h_one(
+// CHECK-SAME: %[[ARG0:.*]]: tensor<1x3x10x2xf32>, %[[ARG1:.*]]: tensor<1x6x3x2x1x2xf32>)
+// CHECK: scf.for %[[ARG2:.*]] = {{.*}} iter_args(%[[ARG3:.*]] = %[[ARG1]])
+// CHECK: scf.for %[[ARG4:.*]] = {{.*}} iter_args(%[[ARG5:.*]] = %[[ARG3]])
+// CHECK: %[[SIZE_H:.*]] = affine.min
+// CHECK: %[[IN:.*]] = tensor.extract_slice %[[ARG0]][0, %{{.*}}, %{{.*}}, 0] [1, %[[SIZE_H]], %{{.*}}, 2] [1, 1, 1, 1] : tensor<1x3x10x2xf32> to tensor<1x?x?x2xf32>
+// CHECK: %[[OUT:.*]] = tensor.extract_slice %[[ARG5]][0, 0, %[[ARG2]], %[[ARG4]], 0, 0] [1, 6, %[[SIZE_H]], 1, 1, 2] [1, 1, 1, 1, 1, 1] : tensor<1x6x3x2x1x2xf32> to tensor<1x6x?x1x1x2xf32>
+// CHECK: linalg.winograd_input_transform fmr(F_4_3) ins(%[[IN]] : tensor<1x?x?x2xf32>) outs(%[[OUT]] : tensor<1x6x?x1x1x2xf32>)
+
+// -----
+
+func.func @tile_winograd_output_alpha_h_one(%arg0: tensor<1x6x3x2x1x2xf32>, %arg1: tensor<1x3x8x2xf32>) -> tensor<1x3x8x2xf32> {
+ %0 = linalg.winograd_output_transform fmr(F_4_3) ins(%arg0 : tensor<1x6x3x2x1x2xf32>) outs(%arg1 : tensor<1x3x8x2xf32>) -> tensor<1x3x8x2xf32>
+ return %0 : tensor<1x3x8x2xf32>
+}
+
+module attributes {transform.with_named_sequence} {
+ transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
+ %0 = transform.structured.match ops{["linalg.winograd_output_transform"]} in %arg1 : (!transform.any_op) -> !transform.any_op
+ %1, %loop:2 = transform.structured.tile_using_for %0 tile_sizes [0, 0, 2, 1, 0, 0] : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op)
+ transform.yield
+ }
+}
+
+// CHECK-LABEL: func.func @tile_winograd_output_alpha_h_one(
+// CHECK-SAME: %[[ARG0:.*]]: tensor<1x6x3x2x1x2xf32>, %[[ARG1:.*]]: tensor<1x3x8x2xf32>)
+// CHECK: scf.for %[[ARG2:.*]] = {{.*}} iter_args(%[[ARG3:.*]] = %[[ARG1]])
+// CHECK: scf.for %[[ARG4:.*]] = {{.*}} iter_args(%[[ARG5:.*]] = %[[ARG3]])
+// CHECK: %[[SIZE_H:.*]] = affine.min
+// CHECK: %[[IN:.*]] = tensor.extract_slice %[[ARG0]][0, 0, %[[ARG2]], %[[ARG4]], 0, 0] [1, 6, %[[SIZE_H]], 1, 1, 2] [1, 1, 1, 1, 1, 1] : tensor<1x6x3x2x1x2xf32> to tensor<1x6x?x1x1x2xf32>
+// CHECK: %[[OUT:.*]] = tensor.extract_slice %[[ARG5]][0, %{{.*}}, %{{.*}}, 0] [1, %[[SIZE_H]], %{{.*}}, 2] [1, 1, 1, 1] : tensor<1x3x8x2xf32> to tensor<1x?x?x2xf32>
+// CHECK: %[[RES:.*]] = linalg.winograd_output_transform fmr(F_4_3) ins(%[[IN]] : tensor<1x6x?x1x1x2xf32>) outs(%[[OUT]] : tensor<1x?x?x2xf32>)
+// CHECK: tensor.insert_slice %[[RES]] into %[[ARG5]][0, %{{.*}}, %{{.*}}, 0] [1, %[[SIZE_H]], %{{.*}}, 2] [1, 1, 1, 1]
>From 69fe3635131c5b94d6b5d3ce401bfcac04ef9620 Mon Sep 17 00:00:00 2001
From: Samarth Narang <sn2910 at nyu.edu>
Date: Thu, 24 Sep 2026 12:02:45 +0000
Subject: [PATCH 2/2] Add the alpha_w == 1 mirror tests
---
.../Linalg/transform-tile-winograd.mlir | 49 +++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/mlir/test/Dialect/Linalg/transform-tile-winograd.mlir b/mlir/test/Dialect/Linalg/transform-tile-winograd.mlir
index 03a42a4f74ce1b..2cf3dad76987b1 100644
--- a/mlir/test/Dialect/Linalg/transform-tile-winograd.mlir
+++ b/mlir/test/Dialect/Linalg/transform-tile-winograd.mlir
@@ -437,3 +437,52 @@ module attributes {transform.with_named_sequence} {
// CHECK: %[[OUT:.*]] = tensor.extract_slice %[[ARG5]][0, %{{.*}}, %{{.*}}, 0] [1, %[[SIZE_H]], %{{.*}}, 2] [1, 1, 1, 1] : tensor<1x3x8x2xf32> to tensor<1x?x?x2xf32>
// CHECK: %[[RES:.*]] = linalg.winograd_output_transform fmr(F_4_3) ins(%[[IN]] : tensor<1x6x?x1x1x2xf32>) outs(%[[OUT]] : tensor<1x?x?x2xf32>)
// CHECK: tensor.insert_slice %[[RES]] into %[[ARG5]][0, %{{.*}}, %{{.*}}, 0] [1, %[[SIZE_H]], %{{.*}}, 2] [1, 1, 1, 1]
+
+// -----
+
+func.func @tile_winograd_input_alpha_w_one(%arg0: tensor<1x10x3x2xf32>, %arg1: tensor<6x1x2x3x1x2xf32>) -> tensor<6x1x2x3x1x2xf32> {
+ %0 = linalg.winograd_input_transform fmr(F_4_3) ins(%arg0 : tensor<1x10x3x2xf32>) outs(%arg1 : tensor<6x1x2x3x1x2xf32>) -> tensor<6x1x2x3x1x2xf32>
+ return %0 : tensor<6x1x2x3x1x2xf32>
+}
+
+module attributes {transform.with_named_sequence} {
+ transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
+ %0 = transform.structured.match ops{["linalg.winograd_input_transform"]} in %arg1 : (!transform.any_op) -> !transform.any_op
+ %1, %loop:2 = transform.structured.tile_using_for %0 tile_sizes [0, 0, 1, 2, 0, 0] : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op)
+ transform.yield
+ }
+}
+
+// CHECK-LABEL: func.func @tile_winograd_input_alpha_w_one(
+// CHECK-SAME: %[[ARG0:.*]]: tensor<1x10x3x2xf32>, %[[ARG1:.*]]: tensor<6x1x2x3x1x2xf32>)
+// CHECK: scf.for %[[ARG2:.*]] = {{.*}} iter_args(%[[ARG3:.*]] = %[[ARG1]])
+// CHECK: scf.for %[[ARG4:.*]] = {{.*}} iter_args(%[[ARG5:.*]] = %[[ARG3]])
+// CHECK: %[[SIZE_W:.*]] = affine.min
+// CHECK: %[[IN:.*]] = tensor.extract_slice %[[ARG0]][0, %{{.*}}, %{{.*}}, 0] [1, %{{.*}}, %[[SIZE_W]], 2] [1, 1, 1, 1] : tensor<1x10x3x2xf32> to tensor<1x?x?x2xf32>
+// CHECK: %[[OUT:.*]] = tensor.extract_slice %[[ARG5]][0, 0, %[[ARG2]], %[[ARG4]], 0, 0] [6, 1, 1, %[[SIZE_W]], 1, 2] [1, 1, 1, 1, 1, 1] : tensor<6x1x2x3x1x2xf32> to tensor<6x1x1x?x1x2xf32>
+// CHECK: linalg.winograd_input_transform fmr(F_4_3) ins(%[[IN]] : tensor<1x?x?x2xf32>) outs(%[[OUT]] : tensor<6x1x1x?x1x2xf32>)
+
+// -----
+
+func.func @tile_winograd_output_alpha_w_one(%arg0: tensor<6x1x2x3x1x2xf32>, %arg1: tensor<1x8x3x2xf32>) -> tensor<1x8x3x2xf32> {
+ %0 = linalg.winograd_output_transform fmr(F_4_3) ins(%arg0 : tensor<6x1x2x3x1x2xf32>) outs(%arg1 : tensor<1x8x3x2xf32>) -> tensor<1x8x3x2xf32>
+ return %0 : tensor<1x8x3x2xf32>
+}
+
+module attributes {transform.with_named_sequence} {
+ transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
+ %0 = transform.structured.match ops{["linalg.winograd_output_transform"]} in %arg1 : (!transform.any_op) -> !transform.any_op
+ %1, %loop:2 = transform.structured.tile_using_for %0 tile_sizes [0, 0, 1, 2, 0, 0] : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op)
+ transform.yield
+ }
+}
+
+// CHECK-LABEL: func.func @tile_winograd_output_alpha_w_one(
+// CHECK-SAME: %[[ARG0:.*]]: tensor<6x1x2x3x1x2xf32>, %[[ARG1:.*]]: tensor<1x8x3x2xf32>)
+// CHECK: scf.for %[[ARG2:.*]] = {{.*}} iter_args(%[[ARG3:.*]] = %[[ARG1]])
+// CHECK: scf.for %[[ARG4:.*]] = {{.*}} iter_args(%[[ARG5:.*]] = %[[ARG3]])
+// CHECK: %[[SIZE_W:.*]] = affine.min
+// CHECK: %[[IN:.*]] = tensor.extract_slice %[[ARG0]][0, 0, %[[ARG2]], %[[ARG4]], 0, 0] [6, 1, 1, %[[SIZE_W]], 1, 2] [1, 1, 1, 1, 1, 1] : tensor<6x1x2x3x1x2xf32> to tensor<6x1x1x?x1x2xf32>
+// CHECK: %[[OUT:.*]] = tensor.extract_slice %[[ARG5]][0, %{{.*}}, %{{.*}}, 0] [1, %{{.*}}, %[[SIZE_W]], 2] [1, 1, 1, 1] : tensor<1x8x3x2xf32> to tensor<1x?x?x2xf32>
+// CHECK: %[[RES:.*]] = linalg.winograd_output_transform fmr(F_4_3) ins(%[[IN]] : tensor<6x1x1x?x1x2xf32>) outs(%[[OUT]] : tensor<1x?x?x2xf32>)
+// CHECK: tensor.insert_slice %[[RES]] into %[[ARG5]][0, %{{.*}}, %{{.*}}, 0] [1, %{{.*}}, %[[SIZE_W]], 2] [1, 1, 1, 1]
More information about the Mlir-commits
mailing list