[Mlir-commits] [mlir] [mlir][linalg] Document and diagnose pack/unpack memref limits (PR #225773)

Federico Bruzzone llvmlistbot at llvm.org
Wed Sep 23 07:01:13 PDT 2026


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

>From f61c0ebce02c7db37ac4466e2291a4b6754f5458 Mon Sep 17 00:00:00 2001
From: Federico Bruzzone <federico.bruzzone.i at gmail.com>
Date: Wed, 23 Sep 2026 15:40:54 +0200
Subject: [PATCH] [mlir][linalg] Document and diagnose pack/unpack memref
 limits

Signed-off-by: Federico Bruzzone <federico.bruzzone.i at gmail.com>
---
 .../Dialect/Linalg/IR/LinalgRelayoutOps.td    | 10 +++++
 mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp      | 19 +++++---
 .../TransformOps/LinalgTransformOps.cpp       | 39 +++++++++++++++++
 .../Linalg/Transforms/BlockPackMatmul.cpp     |  3 +-
 .../Transforms/DataLayoutPropagation.cpp      | 21 +++++++++
 .../Transforms/PackAndUnpackPatterns.cpp      | 36 ++++++++++++----
 .../Linalg/Transforms/TilingInterfaceImpl.cpp | 16 +++++--
 .../Dialect/Linalg/Transforms/Transforms.cpp  | 12 ++++--
 .../Linalg/Transforms/Vectorization.cpp       |  6 ++-
 .../Dialect/Linalg/transform-lower-pack.mlir  | 43 +++++++++++++++++++
 .../Dialect/Linalg/transform-op-pack.mlir     | 40 ++++++++++++++++-
 11 files changed, 219 insertions(+), 26 deletions(-)

diff --git a/mlir/include/mlir/Dialect/Linalg/IR/LinalgRelayoutOps.td b/mlir/include/mlir/Dialect/Linalg/IR/LinalgRelayoutOps.td
index 95383e6262f713..b40549a278c697 100644
--- a/mlir/include/mlir/Dialect/Linalg/IR/LinalgRelayoutOps.td
+++ b/mlir/include/mlir/Dialect/Linalg/IR/LinalgRelayoutOps.td
@@ -193,6 +193,11 @@ def Linalg_PackOp : Linalg_RelayoutOp<"pack", [
     //                             \
     //            expect tensor<2x8xf32> because CeilDiv(9, 8) = 2
     ```
+
+    Memref semantics: `source` and `dest` may be memrefs, but only as the
+    result of bufferization or of subsequent scalar lowering. Transformations
+    on `linalg.pack` operate on the tensor form only. See
+    #225650 for the reasoning.
   }];
   let arguments = (ins TensorOrMemRef<[AnyType]>:$source,
       TensorOrMemRef<[AnyType]>:$dest, 
@@ -357,6 +362,11 @@ def Linalg_UnPackOp : Linalg_RelayoutOp<"unpack"> {
     //                       \  /   \ /
     //          Outer Dims: 9x3x8   Inner Dims: 4x2
     ```
+
+    Memref semantics: `source` and `dest` may be memrefs, but only as the
+    result of bufferization or of subsequent scalar lowering. Transformations
+    on `linalg.unpack` operate on the tensor form only. See
+    #225650 for the reasoning.
   }];
   let arguments = (ins TensorOrMemRef<[AnyType]>:$source,
       TensorOrMemRef<[AnyType]>:$dest,
diff --git a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
index 87b14439597b43..d13ad8ef1e3397 100644
--- a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
+++ b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp
@@ -6168,7 +6168,8 @@ static bool inferStaticShape(PackOp packOp, SmallVectorImpl<int64_t> &srcShape,
 }
 
 LogicalResult PackOp::canonicalize(PackOp packOp, PatternRewriter &rewriter) {
-  // TODO: Support Memref PackOp. Temporarily return failure.
+  // Pack/unpack transformations operate at tensor level. The memref form is
+  // only for inline bufferization and scalar lowering, see #225650 for details.
   if (!packOp.hasPureTensorSemantics())
     return failure();
 
@@ -6268,6 +6269,8 @@ bool PackOp::isLikePad() {
 ::mlir::LogicalResult
 PackOp::fold(FoldAdaptor adaptor,
              ::llvm::SmallVectorImpl<OpFoldResult> &results) {
+  // Pack/unpack transformations operate at tensor level. The memref form is
+  // only for inline bufferization and scalar lowering, see #225650 for details.
   if (!hasPureTensorSemantics())
     return failure();
   std::optional<Attribute> paddingValue;
@@ -6301,7 +6304,9 @@ struct FoldTensorCastPackOp : public OpRewritePattern<PackOp> {
 
   LogicalResult matchAndRewrite(PackOp op,
                                 PatternRewriter &rewriter) const override {
-    // TODO: Support Memref PackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!op.hasPureTensorSemantics())
       return failure();
 
@@ -6634,7 +6639,8 @@ static bool inferStaticShape(UnPackOp op, SmallVectorImpl<int64_t> &srcShape,
 
 LogicalResult UnPackOp::canonicalize(UnPackOp unPackOp,
                                      PatternRewriter &rewriter) {
-  // TODO: Support Memref UnPackOp. Temporarily return failure.
+  // Pack/unpack transformations operate at tensor level. The memref form is
+  // only for inline bufferization and scalar lowering, see #225650 for details.
   if (!unPackOp.hasPureTensorSemantics())
     return failure();
 
@@ -6751,7 +6757,8 @@ bool UnPackOp::isLikeUnPad() {
 ::mlir::LogicalResult
 UnPackOp::fold(FoldAdaptor adaptor,
                ::llvm::SmallVectorImpl<OpFoldResult> &results) {
-  // TODO: Support Memref UnPackOp. Temporarily return failure.
+  // Pack/unpack transformations operate at tensor level. The memref form is
+  // only for inline bufferization and scalar lowering, see #225650 for details.
   if (!hasPureTensorSemantics())
     return failure();
 
@@ -6783,7 +6790,9 @@ struct FoldTensorCastUnPackOp : public OpRewritePattern<UnPackOp> {
 
   LogicalResult matchAndRewrite(UnPackOp op,
                                 PatternRewriter &rewriter) const override {
-    // TODO: Support Memref UnPackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!op.hasPureTensorSemantics())
       return failure();
 
diff --git a/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp b/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp
index 16b03ab5426a0d..d2fae01b1e23e7 100644
--- a/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp
+++ b/mlir/lib/Dialect/Linalg/TransformOps/LinalgTransformOps.cpp
@@ -1549,6 +1549,11 @@ DiagnosedSilenceableFailure transform::LowerPackOp::applyToOne(
     transform::TransformRewriter &rewriter, linalg::PackOp target,
     transform::ApplyToEachResultList &transformResults,
     transform::TransformState &state) {
+  if (!target.hasPureTensorSemantics()) {
+    return mlir::emitSilenceableFailure(target->getLoc())
+           << "lower_pack only supports tensor semantics. The target has "
+              "memref operands, see #225650";
+  }
   rewriter.setInsertionPoint(target);
   bool lowerPadLikeWithInsertSlice = getLowerPadLikeWithInsertSlice();
   FailureOr<LowerPackResult> res =
@@ -1571,6 +1576,14 @@ DiagnosedSilenceableFailure transform::LowerUnPackOp::applyToOne(
     transform::TransformRewriter &rewriter, linalg::UnPackOp target,
     transform::ApplyToEachResultList &transformResults,
     transform::TransformState &state) {
+  if (!target.hasPureTensorSemantics()) {
+    DiagnosedSilenceableFailure diag =
+        emitSilenceableError()
+        << "lower_unpack only supports tensor semantics. The target has "
+           "memref operands, see #225650";
+    diag.attachNote(target->getLoc()) << "target payload op";
+    return diag;
+  }
   rewriter.setInsertionPoint(target);
   bool lowerUnpadLikeWithExtractSlice = getLowerUnpadLikeWithExtractSlice();
   FailureOr<LowerUnPackOpResult> res =
@@ -1863,6 +1876,12 @@ transform::PackOp::apply(transform::TransformRewriter &rewriter,
            << "requires target to map to exactly 1 LinalgOp (got "
            << llvm::range_size(targetOps) << ")";
   }
+  // Fail on memref operands: pack only supports tensor semantics.
+  if (!linalgOp.hasPureTensorSemantics()) {
+    return emitSilenceableError()
+           << "structured.pack only supports tensor semantics. The target "
+              "has memref operands, see #225650";
+  }
   // Fail on mismatched number of pack sizes.
   if (getMixedPackedSizes().size() != linalgOp.getNumLoops()) {
     return emitSilenceableError()
@@ -3727,6 +3746,16 @@ transform::TileUsingForOp::apply(transform::TransformRewriter &rewriter,
       diag.attachNote(op->getLoc()) << "target op";
       return diag;
     }
+    if (auto relayoutOp = dyn_cast<linalg::RelayoutOpInterface>(op);
+        relayoutOp &&
+        !cast<DestinationStyleOpInterface>(op).hasPureTensorSemantics()) {
+      DiagnosedSilenceableFailure diag =
+          emitSilenceableError()
+          << "tiling only supports tensor semantics for linalg.pack / "
+             "linalg.unpack. The target has memref operands, see #225650";
+      diag.attachNote(op->getLoc()) << "target op";
+      return diag;
+    }
 
     int64_t iterspaceRank = tilingInterface.getLoopIteratorTypes().size();
     if (tileInterchange.size() > static_cast<size_t>(iterspaceRank)) {
@@ -4046,6 +4075,16 @@ DiagnosedSilenceableFailure transform::tileToForallOpImpl(
     diag.attachNote(target->getLoc()) << "target op";
     return diag;
   }
+  if (auto relayoutOp = dyn_cast<linalg::RelayoutOpInterface>(target);
+      relayoutOp &&
+      !cast<DestinationStyleOpInterface>(target).hasPureTensorSemantics()) {
+    DiagnosedSilenceableFailure diag =
+        transformOp.emitSilenceableError()
+        << "tiling only supports tensor semantics for linalg.pack / "
+           "linalg.unpack. The target has memref operands, see #225650";
+    diag.attachNote(target->getLoc()) << "target op";
+    return diag;
+  }
   rewriter.setInsertionPoint(tileableOp);
   scf::SCFTilingOptions options;
   options.setLoopType(scf::SCFTilingOptions::LoopType::ForallOp);
diff --git a/mlir/lib/Dialect/Linalg/Transforms/BlockPackMatmul.cpp b/mlir/lib/Dialect/Linalg/Transforms/BlockPackMatmul.cpp
index 1f4cda459e738c..d5f44152af545c 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/BlockPackMatmul.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/BlockPackMatmul.cpp
@@ -92,7 +92,8 @@ transposePackedMatmul(RewriterBase &rewriter, linalg::LinalgOp linalgOp,
                       linalg::PackOp packOp, AffineMap operandMap,
                       ArrayRef<unsigned> blocksStartDimPos,
                       bool transposeOuterBlocks, bool transposeInnerBlocks) {
-  // TODO: Support Memref PackOp. Temporarily return failure.
+  // Pack/unpack transformations operate at tensor level. The memref form is
+  // only for inline bufferization and scalar lowering, see #225650 for details.
   if (!packOp.hasPureTensorSemantics())
     return failure();
 
diff --git a/mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp b/mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp
index d36ca43a6cbb36..6dbbb854176c4d 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/DataLayoutPropagation.cpp
@@ -572,6 +572,9 @@ struct BubbleUpPackOpThroughGenericOpPattern
 
   LogicalResult matchAndRewrite(linalg::PackOp packOp,
                                 PatternRewriter &rewriter) const override {
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!packOp.hasPureTensorSemantics())
       return failure();
 
@@ -598,6 +601,9 @@ class BubbleUpPackThroughPadOp final : public OpRewritePattern<linalg::PackOp> {
 
   LogicalResult matchAndRewrite(linalg::PackOp packOp,
                                 PatternRewriter &rewriter) const override {
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!packOp.hasPureTensorSemantics())
       return failure();
 
@@ -770,6 +776,8 @@ static LogicalResult
 bubbleUpPackOpThroughCollapseShape(tensor::CollapseShapeOp collapseOp,
                                    linalg::PackOp packOp,
                                    PatternRewriter &rewriter) {
+  // Pack/unpack transformations operate at tensor level. The memref form is
+  // only for inline bufferization and scalar lowering, see #225650 for details.
   if (!packOp.hasPureTensorSemantics())
     return failure();
 
@@ -878,6 +886,8 @@ static LogicalResult
 bubbleUpPackOpThroughExpandShape(tensor::ExpandShapeOp expandOp,
                                  linalg::PackOp packOp,
                                  PatternRewriter &rewriter) {
+  // Pack/unpack transformations operate at tensor level. The memref form is
+  // only for inline bufferization and scalar lowering, see #225650 for details.
   if (!packOp.hasPureTensorSemantics())
     return failure();
 
@@ -964,6 +974,9 @@ class BubbleUpPackOpThroughReshapeOp final
 
   LogicalResult matchAndRewrite(linalg::PackOp packOp,
                                 PatternRewriter &rewriter) const override {
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!packOp.hasPureTensorSemantics())
       return failure();
 
@@ -1017,6 +1030,8 @@ class BubbleUpPackOpThroughReshapeOp final
 static LogicalResult pushDownUnPackOpThroughExpandShape(
     linalg::UnPackOp unPackOp, tensor::ExpandShapeOp expandOp,
     PatternRewriter &rewriter, ControlPropagationFn controlFn) {
+  // Pack/unpack transformations operate at tensor level. The memref form is
+  // only for inline bufferization and scalar lowering, see #225650 for details.
   if (!unPackOp.hasPureTensorSemantics())
     return failure();
 
@@ -1094,6 +1109,9 @@ class PushDownUnPackOpThroughReshapeOp final
 
   LogicalResult matchAndRewrite(linalg::UnPackOp unPackOp,
                                 PatternRewriter &rewriter) const override {
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!unPackOp.hasPureTensorSemantics())
       return failure();
 
@@ -1296,6 +1314,9 @@ struct PushDownUnPackThroughPadOp : public OpRewritePattern<tensor::PadOp> {
     if (!unpackOp)
       return failure();
 
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!unpackOp.hasPureTensorSemantics())
       return failure();
 
diff --git a/mlir/lib/Dialect/Linalg/Transforms/PackAndUnpackPatterns.cpp b/mlir/lib/Dialect/Linalg/Transforms/PackAndUnpackPatterns.cpp
index c8e9f021938e56..7269197cf76b32 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/PackAndUnpackPatterns.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/PackAndUnpackPatterns.cpp
@@ -154,7 +154,9 @@ struct SimplifyPackToExpandShape : public OpRewritePattern<PackOp> {
                                 PatternRewriter &rewriter) const override {
     if (packOp.getPaddingValue())
       return rewriter.notifyMatchFailure(packOp, "expects no padding value");
-    // TODO: Support Memref PackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!packOp.hasPureTensorSemantics())
       return failure();
 
@@ -219,7 +221,9 @@ struct SimplifyUnPackToCollapseShape : public OpRewritePattern<UnPackOp> {
 
   LogicalResult matchAndRewrite(UnPackOp unpackOp,
                                 PatternRewriter &rewriter) const override {
-    // TODO: Support Memref UnPackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!unpackOp.hasPureTensorSemantics())
       return failure();
 
@@ -323,7 +327,9 @@ struct FoldUnpackWithExtractSliceOp
     if (!unpackOp)
       return failure();
 
-    // TODO: Support Memref UnPackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!unpackOp.hasPureTensorSemantics())
       return failure();
 
@@ -389,7 +395,9 @@ struct FoldProducerPackWithConsumerLinalgTransposeOp
     if (!packOp)
       return failure();
 
-    // TODO: Support Memref PackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!packOp.hasPureTensorSemantics())
       return failure();
 
@@ -452,7 +460,9 @@ struct FoldConsumerPackWithProducerLinalgTransposeOp
 
   LogicalResult matchAndRewrite(PackOp packOp,
                                 PatternRewriter &rewriter) const override {
-    // TODO: Support Memref PackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!packOp.hasPureTensorSemantics())
       return failure();
 
@@ -517,7 +527,9 @@ struct FoldProducerUnPackWithConsumerLinalgTransposeOp
     if (!unPackOp)
       return failure();
 
-    // TODO: Support Memref UnPackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!unPackOp.hasPureTensorSemantics())
       return failure();
 
@@ -569,7 +581,9 @@ struct FoldConsumerUnPackWithProducerLinalgTransposeOp
 
   LogicalResult matchAndRewrite(UnPackOp unPackOp,
                                 PatternRewriter &rewriter) const override {
-    // TODO: Support Memref UnPackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!unPackOp.hasPureTensorSemantics())
       return failure();
 
@@ -637,7 +651,9 @@ struct FoldEmptyTensorWithPackOp : public OpRewritePattern<PackOp> {
 
   LogicalResult matchAndRewrite(PackOp packOp,
                                 PatternRewriter &rewriter) const override {
-    // TODO: Support Memref PackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!packOp.hasPureTensorSemantics())
       return failure();
 
@@ -665,7 +681,9 @@ struct FoldEmptyTensorWithUnPackOp : public OpRewritePattern<UnPackOp> {
 
   LogicalResult matchAndRewrite(UnPackOp unPackOp,
                                 PatternRewriter &rewriter) const override {
-    // TODO: Support Memref UnPackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!unPackOp.hasPureTensorSemantics())
       return failure();
 
diff --git a/mlir/lib/Dialect/Linalg/Transforms/TilingInterfaceImpl.cpp b/mlir/lib/Dialect/Linalg/Transforms/TilingInterfaceImpl.cpp
index bbb23c00e5e193..f4688ba7fd219b 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/TilingInterfaceImpl.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/TilingInterfaceImpl.cpp
@@ -1130,7 +1130,9 @@ struct PackOpTiling
                          ArrayRef<OpFoldResult> offsets,
                          ArrayRef<OpFoldResult> sizes) const {
     auto packOp = cast<PackOp>(op);
-    // TODO: Support Memref PackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!packOp.hasPureTensorSemantics())
       return failure();
 
@@ -1505,7 +1507,9 @@ struct PackOpTiling
     ArrayRef<OpFoldResult> sizes(allSizes[0]);
 
     auto packOp = cast<PackOp>(op);
-    // TODO: Support Memref UnPackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!packOp.hasPureTensorSemantics())
       return failure();
 
@@ -1723,7 +1727,9 @@ struct UnPackOpTiling
       ArrayRef<OpFoldResult> sizes,
       ArrayRef<InnerTileAlignment> innerTileAlignments) const {
     auto unpackOp = cast<UnPackOp>(op);
-    // TODO: Support Memref UnPackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!unpackOp.hasPureTensorSemantics())
       return failure();
 
@@ -1976,7 +1982,9 @@ struct UnPackOpTiling
       return failure();
     }
     auto unPackOp = cast<UnPackOp>(op);
-    // TODO: Support Memref UnPackOp. Temporarily return failure.
+    // Pack/unpack transformations operate at tensor level. The memref form is
+    // only for inline bufferization and scalar lowering, see #225650 for
+    // details.
     if (!unPackOp.hasPureTensorSemantics())
       return failure();
 
diff --git a/mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp b/mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp
index a2d16c0a5b7d44..c3276f9aacdd48 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/Transforms.cpp
@@ -221,7 +221,8 @@ struct PackedOperandsDimList {
 FailureOr<LowerPackResult> linalg::lowerPack(RewriterBase &rewriter,
                                              linalg::PackOp packOp,
                                              bool lowerPadLikeWithInsertSlice) {
-  // TODO: Support Memref PackOp. Temporarily return failure.
+  // Pack/unpack transformations operate at tensor level. The memref form is
+  // only for inline bufferization and scalar lowering, see #225650 for details.
   if (!packOp.hasPureTensorSemantics())
     return failure();
 
@@ -355,7 +356,8 @@ FailureOr<LowerPackResult> linalg::lowerPack(RewriterBase &rewriter,
 FailureOr<LowerUnPackOpResult>
 linalg::lowerUnPack(RewriterBase &rewriter, linalg::UnPackOp unPackOp,
                     bool lowerUnpadLikeWithExtractSlice) {
-  // TODO: Support Memref UnPackOp. Temporarily return failure.
+  // Pack/unpack transformations operate at tensor level. The memref form is
+  // only for inline bufferization and scalar lowering, see #225650 for details.
   if (!unPackOp.hasPureTensorSemantics())
     return failure();
 
@@ -1044,7 +1046,8 @@ LogicalResult ExtractSliceOfPadTensorSwapPattern::matchAndRewrite(
 static Value getPackOpSourceOrPaddedSource(OpBuilder &builder,
                                            linalg::PackOp packOp) {
   Value input = packOp.getSource();
-  // TODO: Support Memref PackOp. Temporarily return just Op Source.
+  // Pack/unpack transformations operate at tensor level. The memref form is
+  // only for inline bufferization and scalar lowering, see #225650 for details.
   if (!packOp.hasPureTensorSemantics())
     return input;
 
@@ -1164,7 +1167,8 @@ getPackUnpackRankReducedPerm(ArrayRef<int64_t> shape,
 
 LogicalResult DecomposeOuterUnitDimsPackOpPattern::matchAndRewrite(
     linalg::PackOp packOp, PatternRewriter &rewriter) const {
-  // TODO: Support Memref PackOp. Temporarily return failure.
+  // Pack/unpack transformations operate at tensor level. The memref form is
+  // only for inline bufferization and scalar lowering, see #225650 for details.
   if (!packOp.hasPureTensorSemantics())
     return failure();
 
diff --git a/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp b/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp
index bb620641951dc6..ec59433182d26a 100644
--- a/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp
+++ b/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp
@@ -1937,7 +1937,8 @@ vectorizeDynamicLinalgOpPrecondition(linalg::LinalgOp op,
 static LogicalResult
 vectorizeUnPackOpPrecondition(linalg::UnPackOp unpackOp,
                               ArrayRef<int64_t> inputVectorSizes) {
-  // TODO: Support Memref UnPackOp. Temporarily return failure.
+  // Pack/unpack transformations operate at tensor level. The memref form is
+  // only for inline bufferization and scalar lowering, see #225650 for details.
   if (!unpackOp.hasPureTensorSemantics())
     return failure();
 
@@ -2315,7 +2316,8 @@ static LogicalResult vectorizeLinalgOpPrecondition(
 static LogicalResult
 vectorizePackOpPrecondition(linalg::PackOp packOp,
                             ArrayRef<int64_t> inputVectorSizes) {
-  // TODO: Support Memref PackOp. Temporarily return failure.
+  // Pack/unpack transformations operate at tensor level. The memref form is
+  // only for inline bufferization and scalar lowering, see #225650 for details.
   if (!packOp.hasPureTensorSemantics())
     return failure();
 
diff --git a/mlir/test/Dialect/Linalg/transform-lower-pack.mlir b/mlir/test/Dialect/Linalg/transform-lower-pack.mlir
index efe832233f1cc1..9d17d2fa1904b4 100644
--- a/mlir/test/Dialect/Linalg/transform-lower-pack.mlir
+++ b/mlir/test/Dialect/Linalg/transform-lower-pack.mlir
@@ -804,3 +804,46 @@ module attributes {transform.with_named_sequence} {
           transform.yield
   }
 }
+
+// -----
+
+func.func @pack_unsupported_memref(%arg0: memref<128x8xf32>, %arg1: memref<8x8x16x1xf32>) {
+  // expected-error @below {{lower_pack only supports tensor semantics. The target has memref operands}}
+  linalg.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [16, 1] into %arg1
+    : memref<128x8xf32> -> memref<8x8x16x1xf32>
+  return
+}
+
+module attributes {transform.with_named_sequence} {
+  transform.named_sequence @__transform_main(%module_op: !transform.any_op {transform.readonly}) {
+    %pack = transform.structured.match ops{["linalg.pack"]} in %module_op
+      : (!transform.any_op) -> !transform.op<"linalg.pack">
+    transform.structured.lower_pack %pack : (!transform.op<"linalg.pack">)
+      -> (!transform.op<"tensor.pad">, !transform.op<"tensor.expand_shape">, !transform.op<"linalg.transpose">)
+      transform.yield
+  }
+}
+
+// -----
+
+func.func @unpack_unsupported_memref(%arg0: memref<8x8x16x1xf32>, %arg1: memref<128x8xf32>) {
+  // expected-note @below {{target payload op}}
+  linalg.unpack %arg0 inner_dims_pos = [0, 1] inner_tiles = [16, 1] into %arg1
+    : memref<8x8x16x1xf32> -> memref<128x8xf32>
+  return
+}
+
+module attributes {transform.with_named_sequence} {
+  transform.named_sequence @__transform_main(%module_op: !transform.any_op {transform.readonly}) {
+    %unpack = transform.structured.match ops{["linalg.unpack"]} in %module_op
+      : (!transform.any_op) -> !transform.op<"linalg.unpack">
+    // expected-error @below {{lower_unpack only supports tensor semantics. The target has memref operands}}
+    transform.structured.lower_unpack %unpack : (!transform.op<"linalg.unpack">)
+      -> (!transform.op<"tensor.empty">,
+          !transform.op<"linalg.transpose">,
+          !transform.op<"tensor.collapse_shape">,
+          !transform.op<"tensor.extract_slice">,
+          !transform.op<"linalg.copy">)
+      transform.yield
+  }
+}
diff --git a/mlir/test/Dialect/Linalg/transform-op-pack.mlir b/mlir/test/Dialect/Linalg/transform-op-pack.mlir
index 13a07cc38e10bf..bf25198ddf6c5b 100644
--- a/mlir/test/Dialect/Linalg/transform-op-pack.mlir
+++ b/mlir/test/Dialect/Linalg/transform-op-pack.mlir
@@ -455,7 +455,7 @@ func.func @matmul_unsupported_memref_pack(%A: memref<?x?xf32>,
 module attributes {transform.with_named_sequence} {
   transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
       %0 = transform.structured.match ops{["linalg.matmul"]} in %arg1 : (!transform.any_op) -> !transform.any_op
-      // expected-error @below {{data tiling failed}}
+      // expected-error @below {{structured.pack only supports tensor semantics. The target has memref operands}}
       %1 = transform.structured.pack %0 packed_sizes = [2, 3, 4]
         : (!transform.any_op) -> (!transform.op<"linalg.generic">)
         transform.yield
@@ -464,6 +464,44 @@ module attributes {transform.with_named_sequence} {
 
 // -----
 
+func.func @pack_unsupported_memref_tile_using_for(%arg0: memref<128x256xf32>, %arg1: memref<4x8x32x32xf32>) {
+  // expected-note @below {{target op}}
+  linalg.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1
+    : memref<128x256xf32> -> memref<4x8x32x32xf32>
+  return
+}
+
+module attributes {transform.with_named_sequence} {
+  transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
+      %0 = transform.structured.match ops{["linalg.pack"]} in %arg1 : (!transform.any_op) -> !transform.any_op
+      // expected-error @below {{tiling only supports tensor semantics for linalg.pack / linalg.unpack. The target has memref operands}}
+      %1, %loops:2 = transform.structured.tile_using_for %0 tile_sizes [2, 4]
+        : (!transform.any_op) -> (!transform.any_op, !transform.any_op, !transform.any_op)
+        transform.yield
+  }
+}
+
+// -----
+
+func.func @pack_unsupported_memref_tile_using_forall(%arg0: memref<128x256xf32>, %arg1: memref<4x8x32x32xf32>) {
+  // expected-note @below {{target op}}
+  linalg.pack %arg0 inner_dims_pos = [0, 1] inner_tiles = [32, 32] into %arg1
+    : memref<128x256xf32> -> memref<4x8x32x32xf32>
+  return
+}
+
+module attributes {transform.with_named_sequence} {
+  transform.named_sequence @__transform_main(%arg1: !transform.any_op {transform.readonly}) {
+      %0 = transform.structured.match ops{["linalg.pack"]} in %arg1 : (!transform.any_op) -> !transform.any_op
+      // expected-error @below {{tiling only supports tensor semantics for linalg.pack / linalg.unpack. The target has memref operands}}
+      %1:2 = transform.structured.tile_using_forall %0 num_threads [2, 4]
+        : (!transform.any_op) -> (!transform.any_op, !transform.any_op)
+        transform.yield
+  }
+}
+
+// -----
+
 func.func @no_single_packing_op(%source: tensor<128x256xf32>, %dest: tensor<4x16x32x16xf32>) {
   %0 = linalg.pack %source inner_dims_pos = [0, 1] inner_tiles = [32, 16] into %dest : tensor<128x256xf32> -> tensor<4x16x32x16xf32>
   %1 = linalg.unpack %0 inner_dims_pos = [0, 1] inner_tiles = [32, 16] into %source : tensor<4x16x32x16xf32> -> tensor<128x256xf32>



More information about the Mlir-commits mailing list