[Mlir-commits] [mlir] Revert "[mlir][linalg] Make conv dim inference return pairing (outputImage, filterLoop)" (PR #181272)

Andrew Lazarev llvmlistbot at llvm.org
Thu Feb 12 15:53:55 PST 2026


https://github.com/alazarev created https://github.com/llvm/llvm-project/pull/181272

Reverts llvm/llvm-project#180859

Brakes buildbots:
https://lab.llvm.org/buildbot/#/builders/24/builds/17467
https://lab.llvm.org/buildbot/#/builders/52/builds/14978

>From 540287aa8005a2f6071d3c8ed01782fb19afee5c Mon Sep 17 00:00:00 2001
From: Andrew Lazarev <alazarev at google.com>
Date: Thu, 12 Feb 2026 15:53:30 -0800
Subject: [PATCH] =?UTF-8?q?Revert=20"[mlir][linalg]=20Make=20conv=20dim=20?=
 =?UTF-8?q?inference=20return=20pairing=20(outputImage,=20f=E2=80=A6"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This reverts commit 524ae2f3477e5a2307abf404976630609f2ecd39.
---
 .../mlir/Dialect/Linalg/IR/LinalgInterfaces.h |   8 +-
 .../Dialect/Linalg/IR/LinalgInterfaces.cpp    |  36 +---
 mlir/unittests/Dialect/CMakeLists.txt         |   1 -
 mlir/unittests/Dialect/Linalg/CMakeLists.txt  |  11 --
 .../Linalg/InferConvolutionDimsTest.cpp       | 173 ------------------
 5 files changed, 12 insertions(+), 217 deletions(-)
 delete mode 100644 mlir/unittests/Dialect/Linalg/CMakeLists.txt
 delete mode 100644 mlir/unittests/Dialect/Linalg/InferConvolutionDimsTest.cpp

diff --git a/mlir/include/mlir/Dialect/Linalg/IR/LinalgInterfaces.h b/mlir/include/mlir/Dialect/Linalg/IR/LinalgInterfaces.h
index 27fcad203c7aa..0ebbeea937554 100644
--- a/mlir/include/mlir/Dialect/Linalg/IR/LinalgInterfaces.h
+++ b/mlir/include/mlir/Dialect/Linalg/IR/LinalgInterfaces.h
@@ -105,12 +105,8 @@ struct ConvolutionDimensions {
 ///   7. All dimensions appear only once in any given indexing map.
 /// This allows e.g. detecting that some convolution is embedded within
 /// `linalgOp` with some orthogonal heuristic.
-///
-/// The `outputImage` and `filterLoop` arrays are ordered such that
-/// `outputImage[i]` pairs with `filterLoop[i]` based on the convolution access
-/// pattern in the input indexing map (e.g., `d0 + d2` pairs dimension 0 with
-/// dimension 2). Other dimension sets are returned in sorted order.
-///
+/// When multiple dimension occurrences exist that match any classification
+/// indices are returned in sorted order.
 /// Returns a failure if `output_image` (and implicitly `filter_loop`) is empty.
 FailureOr<ConvolutionDimensions> inferConvolutionDims(LinalgOp linalgOp);
 
diff --git a/mlir/lib/Dialect/Linalg/IR/LinalgInterfaces.cpp b/mlir/lib/Dialect/Linalg/IR/LinalgInterfaces.cpp
index c1501ad2bdd6d..b4b1347493529 100644
--- a/mlir/lib/Dialect/Linalg/IR/LinalgInterfaces.cpp
+++ b/mlir/lib/Dialect/Linalg/IR/LinalgInterfaces.cpp
@@ -732,17 +732,11 @@ getConstantsFromExprList(const SmallVector<AffineExpr, 2> &exprs) {
 
 /// Classifies dimensions in the `linalgOp` used by a convolution
 /// subcomputation, as captured by `inputExprWalker`. If
-/// `allowEmptyConvolvedDims` is not set this will fail if there is not
-/// at least one convolved dimension pair (output image + filter loop).
-///
-/// The returned dimensions are ordered as follows:
-/// - `outputImage` is sorted by dimension index.
-/// - `filterLoop` is ordered to match the pairing with `outputImage`, i.e.,
-///   `outputImage[i]` and `filterLoop[i]` are paired dimensions from the
-///   convolution access pattern (e.g., `oh + kh` pairs `oh` with `kh`).
-/// - `strides[i]` corresponds to `outputImage[i]`.
-/// - `dilations[i]` corresponds to `filterLoop[i]`.
-/// - Other dimension sets (batch, outputChannel, etc.) are sorted by index.
+/// `allowEmptyConvolvedDims` is not set this this will fail if there is not
+/// at least convolved dimension pair (output image + filter loop). Convolution
+/// dimensions are specified in sorted order, and strides match the order of
+/// the filter loop dimensions, while the dilations match the order of the
+/// output image dimensions.
 static FailureOr<ConvolutionDimensions>
 inferConvolutionDimsImpl(LinalgOp linalgOp,
                          ConvAccessExprWalker &inputExprWalker,
@@ -790,13 +784,12 @@ inferConvolutionDimsImpl(LinalgOp linalgOp,
   if (oi.empty() && !allowEmptyConvolvedDims)
     return failure();
 
-  // Return each set in sorted order, with outputImage and filterLoop
-  // ordered so that outputImage[i] pairs with filterLoop[i].
+  // Return each set in sorted order.
   ConvolutionDimensions dimensions{
       SmallVector<unsigned, 2>(batch.begin(), batch.end()),
       SmallVector<unsigned, 2>(oi.begin(), oi.end()),
       SmallVector<unsigned, 2>(oc.begin(), oc.end()),
-      /*filterLoop=*/SmallVector<unsigned, 2>{},
+      SmallVector<unsigned, 2>(fl.begin(), fl.end()),
       SmallVector<unsigned, 2>(ic.begin(), ic.end()),
       SmallVector<unsigned, 2>(depth.begin(), depth.end()),
       /*strides=*/SmallVector<int64_t, 2>{},
@@ -804,14 +797,9 @@ inferConvolutionDimsImpl(LinalgOp linalgOp,
   llvm::sort(dimensions.batch);
   llvm::sort(dimensions.outputImage);
   llvm::sort(dimensions.outputChannel);
+  llvm::sort(dimensions.filterLoop);
   llvm::sort(dimensions.inputChannel);
   llvm::sort(dimensions.depth);
-  // Order filterLoop to match the pairing with outputImage. Each outputImage
-  // dimension has a corresponding filterLoop dimension from the convolution
-  // access pattern (e.g., oh + kh). This ensures outputImage[i] pairs with
-  // filterLoop[i].
-  for (unsigned oiDim : dimensions.outputImage)
-    dimensions.filterLoop.push_back(inputExprWalker.convolvedDimMapping[oiDim]);
 
   // Use the op carried strides/dilations attribute if present.
   auto nativeStrides = linalgOp->getAttrOfType<DenseIntElementsAttr>("strides");
@@ -858,12 +846,8 @@ inferConvolutionDimsImpl(LinalgOp linalgOp,
 ///   7. All dimensions appear only once in any given indexing map.
 /// This allows e.g. detecting that some convolution is embedded within
 /// `linalgOp` with some orthogonal heuristic.
-///
-/// The `outputImage` and `filterLoop` arrays are ordered such that
-/// `outputImage[i]` pairs with `filterLoop[i]` based on the convolution access
-/// pattern in the input indexing map (e.g., `d0 + d2` pairs dimension 0 with
-/// dimension 2). Other dimension sets are returned in sorted order.
-///
+/// When multiple dimension occurrences exist that match any classification
+/// indices are returned in sorted order.
 /// Returns a failure if `output_image` (and implicitly `filter_loop`) is empty.
 FailureOr<ConvolutionDimensions>
 mlir::linalg::inferConvolutionDims(LinalgOp linalgOp) {
diff --git a/mlir/unittests/Dialect/CMakeLists.txt b/mlir/unittests/Dialect/CMakeLists.txt
index 269eccb1f93c3..aea247547473d 100644
--- a/mlir/unittests/Dialect/CMakeLists.txt
+++ b/mlir/unittests/Dialect/CMakeLists.txt
@@ -9,7 +9,6 @@ mlir_target_link_libraries(MLIRDialectTests
 add_subdirectory(AMDGPU)
 add_subdirectory(ArmSME)
 add_subdirectory(Index)
-add_subdirectory(Linalg)
 add_subdirectory(LLVMIR)
 add_subdirectory(MemRef)
 add_subdirectory(OpenACC)
diff --git a/mlir/unittests/Dialect/Linalg/CMakeLists.txt b/mlir/unittests/Dialect/Linalg/CMakeLists.txt
deleted file mode 100644
index a7da4e07c2551..0000000000000
--- a/mlir/unittests/Dialect/Linalg/CMakeLists.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-add_mlir_unittest(MLIRLinalgTests
-  InferConvolutionDimsTest.cpp
-)
-mlir_target_link_libraries(MLIRLinalgTests
-  PRIVATE
-  MLIRIR
-  MLIRArithDialect
-  MLIRFuncDialect
-  MLIRLinalgDialect
-  MLIRTensorDialect
-)
diff --git a/mlir/unittests/Dialect/Linalg/InferConvolutionDimsTest.cpp b/mlir/unittests/Dialect/Linalg/InferConvolutionDimsTest.cpp
deleted file mode 100644
index cc9c77e0f8b5a..0000000000000
--- a/mlir/unittests/Dialect/Linalg/InferConvolutionDimsTest.cpp
+++ /dev/null
@@ -1,173 +0,0 @@
-//===- InferConvolutionDimsTest.cpp - inferConvolutionDims unit tests -----===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "mlir/Dialect/Arith/IR/Arith.h"
-#include "mlir/Dialect/Func/IR/FuncOps.h"
-#include "mlir/Dialect/Linalg/IR/Linalg.h"
-#include "mlir/Dialect/Linalg/IR/LinalgInterfaces.h"
-#include "mlir/Dialect/Tensor/IR/Tensor.h"
-#include "mlir/IR/Builders.h"
-#include "mlir/IR/BuiltinTypes.h"
-#include "mlir/IR/MLIRContext.h"
-#include "gtest/gtest.h"
-
-using namespace mlir;
-using namespace mlir::linalg;
-
-namespace {
-
-class InferConvolutionDimsTest : public ::testing::Test {
-protected:
-  void SetUp() override {
-    registry.insert<arith::ArithDialect, linalg::LinalgDialect,
-                    tensor::TensorDialect, func::FuncDialect>();
-    ctx = std::make_unique<MLIRContext>(registry);
-    ctx->loadAllAvailableDialects();
-  }
-
-  DialectRegistry registry;
-  std::unique_ptr<MLIRContext> ctx;
-};
-
-/// Creates a Conv2DOp with loop order (d0, d1, d2, d3) where:
-///   d0 = output height (oh), parallel
-///   d1 = output width (ow), parallel
-///   d2 = kernel height (kh), reduction
-///   d3 = kernel width (kw), reduction
-///
-/// Indexing maps:
-///   input:  (d0 + d2, d1 + d3)
-///   filter: (d2, d3)
-///   output: (d0, d1)
-///
-/// Semantic pairing: d0 <-> d2, d1 <-> d3
-static linalg::Conv2DOp createConv2DOp(OpBuilder &builder, int64_t oh,
-                                       int64_t ow, int64_t kh, int64_t kw) {
-  Location loc = builder.getUnknownLoc();
-  auto f32Type = builder.getF32Type();
-  int64_t ih = oh + kh - 1;
-  int64_t iw = ow + kw - 1;
-  auto inputType = RankedTensorType::get({ih, iw}, f32Type);
-  auto filterType = RankedTensorType::get({kh, kw}, f32Type);
-  auto outputType = RankedTensorType::get({oh, ow}, f32Type);
-  Value input = tensor::EmptyOp::create(builder, loc, inputType.getShape(),
-                                        inputType.getElementType());
-  Value filter = tensor::EmptyOp::create(builder, loc, filterType.getShape(),
-                                         filterType.getElementType());
-  Value output = tensor::EmptyOp::create(builder, loc, outputType.getShape(),
-                                         outputType.getElementType());
-  return linalg::Conv2DOp::create(
-      builder, loc, outputType, ValueRange{input, filter}, ValueRange{output});
-}
-
-/// Creates a linalg.generic equivalent to the given Conv2DOp but with filter
-/// loop dimensions swapped. The resulting op has loop order (d0, d1, d2, d3):
-///   d0 = output height (oh), parallel
-///   d1 = output width (ow), parallel
-///   d2 = kernel width (kw), reduction  <-- swapped!
-///   d3 = kernel height (kh), reduction <-- swapped!
-///
-/// Indexing maps:
-///   input:  (d0 + d3, d1 + d2)
-///   filter: (d2, d3)
-///   output: (d0, d1)
-///
-/// Semantic pairing: d0 <-> d3, d1 <-> d2
-static linalg::GenericOp
-createConv2DWithSwappedFilterLoops(OpBuilder &builder,
-                                   linalg::Conv2DOp conv2DOp) {
-  Location loc = conv2DOp.getLoc();
-  MLIRContext *ctx = builder.getContext();
-
-  // Extract dimensions from the Conv2DOp. Require static shapes for simplicity.
-  auto inputType = cast<RankedTensorType>(conv2DOp.getInputs()[0].getType());
-  auto filterType = cast<RankedTensorType>(conv2DOp.getInputs()[1].getType());
-  auto outputType = cast<RankedTensorType>(conv2DOp.getOutputs()[0].getType());
-  assert(inputType.hasStaticShape() && "expected static input shape");
-  assert(filterType.hasStaticShape() && "expected static filter shape");
-  assert(outputType.hasStaticShape() && "expected static output shape");
-  (void)outputType;
-  int64_t kh = filterType.getDimSize(0);
-  int64_t kw = filterType.getDimSize(1);
-
-  // Filter dimensions are swapped: (kw, kh) instead of (kh, kw).
-  auto f32Type = builder.getF32Type();
-  auto swappedFilterType = RankedTensorType::get({kw, kh}, f32Type);
-  Value input = tensor::EmptyOp::create(builder, loc, inputType.getShape(),
-                                        inputType.getElementType());
-  Value filter =
-      tensor::EmptyOp::create(builder, loc, swappedFilterType.getShape(),
-                              swappedFilterType.getElementType());
-  Value output = tensor::EmptyOp::create(builder, loc, outputType.getShape(),
-                                         outputType.getElementType());
-
-  // Build indexing maps for swapped filter loop dimensions.
-  // Original Conv2DOp: (d0=oh, d1=ow, d2=kh, d3=kw)
-  // Swapped:           (d0=oh, d1=ow, d2=kw, d3=kh)
-  AffineExpr d0, d1, d2, d3;
-  bindDims(ctx, d0, d1, d2, d3);
-  auto inputMap = AffineMap::get(4, 0, {d0 + d3, d1 + d2}, ctx);
-  auto filterMap = AffineMap::get(4, 0, {d2, d3}, ctx);
-  auto outputMap = AffineMap::get(4, 0, {d0, d1}, ctx);
-  SmallVector<AffineMap> indexingMaps = {inputMap, filterMap, outputMap};
-  SmallVector<utils::IteratorType> iterTypes = {
-      utils::IteratorType::parallel, utils::IteratorType::parallel,
-      utils::IteratorType::reduction, utils::IteratorType::reduction};
-  return linalg::GenericOp::create(
-      builder, loc, outputType, ValueRange{input, filter}, ValueRange{output},
-      indexingMaps, iterTypes,
-      [&](OpBuilder &b, Location loc, ValueRange args) {
-        Value mul = arith::MulFOp::create(b, loc, args[0], args[1]);
-        Value add = arith::AddFOp::create(b, loc, args[2], mul);
-        linalg::YieldOp::create(b, loc, add);
-      });
-}
-
-TEST_F(InferConvolutionDimsTest, Conv2DPairing) {
-  // Use non-square kernel to ensure dimension swapping is tested properly.
-  const int64_t oh = 6, ow = 12, kh = 3, kw = 5;
-
-  // Create Conv2DOp where the standard loop order is (oh, ow, kh, kw).
-  OpBuilder builder(ctx.get());
-  linalg::Conv2DOp conv2DOp = createConv2DOp(builder, oh, ow, kh, kw);
-  FailureOr<ConvolutionDimensions> origDims = inferConvolutionDims(conv2DOp);
-  ASSERT_TRUE(succeeded(origDims));
-  ASSERT_EQ(origDims->outputImage.size(), 2u);
-  ASSERT_EQ(origDims->filterLoop.size(), 2u);
-
-  // Standard pairing: outputImage=[0,1], filterLoop=[2,3]
-  // d0 <-> d2 (oh <-> kh), d1 <-> d3 (ow <-> kw)
-  EXPECT_EQ(origDims->outputImage[0], 0u);
-  EXPECT_EQ(origDims->outputImage[1], 1u);
-  EXPECT_EQ(origDims->filterLoop[0], 2u);
-  EXPECT_EQ(origDims->filterLoop[1], 3u);
-
-  // Create equivalent generic with swapped filter loop order: (oh, ow, kw, kh)
-  linalg::GenericOp swappedOp =
-      createConv2DWithSwappedFilterLoops(builder, conv2DOp);
-  FailureOr<ConvolutionDimensions> swappedDims =
-      inferConvolutionDims(swappedOp);
-  ASSERT_TRUE(succeeded(swappedDims));
-  ASSERT_EQ(swappedDims->outputImage.size(), 2u);
-  ASSERT_EQ(swappedDims->filterLoop.size(), 2u);
-
-  // outputImage should still be [0, 1] after sorting.
-  EXPECT_EQ(swappedDims->outputImage[0], 0u);
-  EXPECT_EQ(swappedDims->outputImage[1], 1u);
-
-  // In swapped version:
-  //   Input map: (d0 + d3, d1 + d2) -> d0 <-> d3, d1 <-> d2
-  // So filterLoop should be [3, 2] to maintain
-  // outputImage[i] <-> filterLoop[i].
-  EXPECT_EQ(swappedDims->filterLoop[0], 3u)
-      << "outputImage[0]=0 should pair with filterLoop[0]=3 (oh <-> kh)";
-  EXPECT_EQ(swappedDims->filterLoop[1], 2u)
-      << "outputImage[1]=1 should pair with filterLoop[1]=2 (ow <-> kw)";
-}
-
-} // namespace



More information about the Mlir-commits mailing list