[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