Skip to content

Commit deb8435

Browse files
[Preprocessing] Apply a pass-pipeline based on attributes on func-like ops. (iree-org#20287)
This PR adds a new mechanism for running some preprocessing passes on func-like ops. This is done by use of a new attribute `preprocessing-passpipeline` which has the textual representation of the pass-pipeline to run. It is expected that the textual representation is for a pass-manager already nested on a func-like op. --------- Signed-off-by: MaheshRavishankar <[email protected]>
1 parent ce883e7 commit deb8435

File tree

10 files changed

+228
-1
lines changed

10 files changed

+228
-1
lines changed

compiler/src/iree/compiler/Dialect/Util/IR/UtilAttrs.td

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,33 @@ def Util_NullAttr : AttrDef<Util_Dialect, "Null", [
207207
// #util.uninitialized
208208
//===----------------------------------------------------------------------===//
209209

210+
def Util_PreprocessingPipelineAttr : AttrDef<Util_Dialect,
211+
"PreprocessingPassPipeline"> {
212+
let mnemonic = "preprocessing_pipeline";
213+
let summary = "[{preprocessing pass-pipeline to run on the function}]";
214+
let description = [{
215+
Textual description of the preprocessing pass-pipeline to run on the
216+
function-like object that the attribute is specified on. Note that the
217+
string pipeline is implicitly nested to `builtin.module(func-lik(..))`
218+
operation. For example to run the transpose convolution pipeline, the
219+
attribute is specified as
220+
221+
```preprocess_pipeline = #util.preprocess_pipeline<
222+
"iree-preprocessing-transpose-convolution-pipeline">```
223+
}];
224+
let parameters = (ins
225+
AttrParameter<"std::string", "">:$pipelineString
226+
);
227+
228+
let assemblyFormat = [{
229+
`<` $pipelineString `>`
230+
}];
231+
}
232+
233+
//===----------------------------------------------------------------------===//
234+
// #util.uninitialized
235+
//===----------------------------------------------------------------------===//
236+
210237
def Util_UninitializedAttr : AttrDef<Util_Dialect, "Uninitialized", [
211238
TypedAttrInterface,
212239
DeclareAttrInterfaceMethods<Util_SizedStorageAttr, [

compiler/src/iree/compiler/Dialect/Util/IR/UtilTypes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "llvm/Support/Endian.h"
1313
#include "llvm/Support/MathExtras.h"
1414
#include "mlir/IR/Attributes.h"
15+
#include "mlir/IR/BuiltinAttributes.h"
1516
#include "mlir/IR/BuiltinTypes.h"
1617
#include "mlir/IR/Diagnostics.h"
1718
#include "mlir/IR/Location.h"
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright 2025 The IREE Authors
2+
//
3+
// Licensed under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
7+
#include "iree/compiler/Preprocessing/Common/Passes.h"
8+
9+
#include "iree/compiler/Dialect/Flow/IR/FlowDialect.h"
10+
#include "iree/compiler/Dialect/Util/IR/UtilDialect.h"
11+
#include "iree/compiler/Dialect/Util/IR/UtilOps.h"
12+
#include "iree/compiler/Dialect/Util/IR/UtilTypes.h"
13+
#include "llvm/Support/Debug.h"
14+
#include "mlir/Dialect/Func/IR/FuncOps.h"
15+
#include "mlir/Interfaces/FunctionInterfaces.h"
16+
17+
#define DEBUG_TYPE "iree-attr-based-pipeline"
18+
19+
namespace mlir::iree_compiler::Preprocessing {
20+
21+
#define GEN_PASS_DEF_ATTRBASEDPIPELINEPASS
22+
#include "iree/compiler/Preprocessing/Common/Passes.h.inc" // IWYU pragma: export
23+
24+
namespace {
25+
26+
struct AttrBasedPipelinePass
27+
: public iree_compiler::Preprocessing::impl::AttrBasedPipelinePassBase<
28+
AttrBasedPipelinePass> {
29+
void runOnOperation() override;
30+
};
31+
} // namespace
32+
33+
static const char kPreprocessingPipelineAttrName[] = "preprocessing_pipeline";
34+
35+
// Method to get the pass manager nested on a particular operation. There does
36+
// not seem to be a way to do this without specializing on the op itself.
37+
// When possible to do so, this method could be deleted.
38+
static std::optional<OpPassManager>
39+
getFunctionOpInterfacePassManager(FunctionOpInterface interfaceOp) {
40+
return TypeSwitch<Operation *, std::optional<OpPassManager>>(
41+
interfaceOp.getOperation())
42+
.Case<func::FuncOp, IREE::Util::FuncOp>(
43+
[&](auto funcOp) { return OpPassManager(funcOp.getOperationName()); })
44+
.Default([&](Operation *op) { return std::nullopt; });
45+
}
46+
47+
void AttrBasedPipelinePass::runOnOperation() {
48+
auto op = getOperation();
49+
SmallVector<FunctionOpInterface> funcLikeOps;
50+
op->walk([&](FunctionOpInterface funcLikeOp) {
51+
funcLikeOps.push_back(funcLikeOp);
52+
});
53+
54+
for (auto funcLikeOp : funcLikeOps) {
55+
auto attr = funcLikeOp->getAttr(kPreprocessingPipelineAttrName);
56+
if (!attr) {
57+
continue;
58+
}
59+
auto passPipelineAttr =
60+
dyn_cast<IREE::Util::PreprocessingPassPipelineAttr>(attr);
61+
if (!passPipelineAttr) {
62+
funcLikeOp.emitRemark(
63+
"expected preprocessing_pipeline attribute to be a `StringAttr` that "
64+
"specifies the pass pipeline to apply");
65+
continue;
66+
}
67+
LLVM_DEBUG({ llvm::dbgs() << "Parsed Attribute : " << passPipelineAttr; });
68+
69+
std::optional<OpPassManager> passManager =
70+
getFunctionOpInterfacePassManager(funcLikeOp);
71+
if (!passManager) {
72+
continue;
73+
}
74+
75+
std::string pipelineStr = passPipelineAttr.getPipelineString();
76+
if (failed(parsePassPipeline(pipelineStr, *passManager))) {
77+
funcLikeOp->emitOpError("failed to populate pass manager specified by : ")
78+
<< pipelineStr;
79+
return signalPassFailure();
80+
}
81+
82+
if (failed(runPipeline(*passManager, funcLikeOp))) {
83+
funcLikeOp->emitOpError("failed to run pass specified as attribute : ")
84+
<< pipelineStr;
85+
return signalPassFailure();
86+
}
87+
}
88+
}
89+
90+
} // namespace mlir::iree_compiler::Preprocessing

compiler/src/iree/compiler/Preprocessing/Common/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ iree_compiler_cc_library(
3131
name = "Transforms",
3232
srcs = [
3333
"ApplyPDLPatterns.cpp",
34+
"AttrBasedPipelinePass.cpp",
3435
"ConvertConv2DToImg2Col.cpp",
3536
"ConvertConvFilterToChannelsLast.cpp",
3637
"ConvertConvToChannelsLast.cpp",

compiler/src/iree/compiler/Preprocessing/Common/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ iree_cc_library(
2727
"Passes.h.inc"
2828
SRCS
2929
"ApplyPDLPatterns.cpp"
30+
"AttrBasedPipelinePass.cpp"
3031
"ConvertConv2DToImg2Col.cpp"
3132
"ConvertConvFilterToChannelsLast.cpp"
3233
"ConvertConvToChannelsLast.cpp"

compiler/src/iree/compiler/Preprocessing/Common/Passes.td

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,19 @@ def ApplyPDLPatternsPass : Pass<"iree-preprocessing-apply-pdl-patterns", "Module
2727
];
2828
}
2929

30+
def AttrBasedPipelinePass : Pass<"iree-preprocessing-attr-based-pipeline", ""> {
31+
let summary = "Run a pass pipeline specified as an attribute on any function-like operation";
32+
let description = [{
33+
The textual representation of the pass pipeline specified in the attribute
34+
`preprocessing_pipeline=#util.preprocessing_pipeline<...>` is to be run on the
35+
function-like operation.
36+
}];
37+
let dependentDialects = [
38+
"iree_compiler::IREE::Flow::FlowDialect",
39+
"iree_compiler::IREE::Util::UtilDialect",
40+
];
41+
}
42+
3043
def ConvertConv2DToImg2ColPass :
3144
Pass<"iree-preprocessing-convert-conv2d-to-img2col", ""> {
3245
let summary = "Convert linalg convolution ops to matmul img2col based implementation";

compiler/src/iree/compiler/Preprocessing/Common/test/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ iree_lit_test_suite(
1616
name = "lit",
1717
srcs = enforce_glob(
1818
[
19+
"attr_based_pipeline.mlir",
1920
"conv2d_to_img2col.mlir",
2021
"conv_filter_to_channels_last.mlir",
2122
"conv_to_channels_last.mlir",

compiler/src/iree/compiler/Preprocessing/Common/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ iree_lit_test_suite(
1414
NAME
1515
lit
1616
SRCS
17+
"attr_based_pipeline.mlir"
1718
"conv2d_to_img2col.mlir"
1819
"conv_filter_to_channels_last.mlir"
1920
"conv_to_channels_last.mlir"
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// RUN: iree-opt --iree-preprocessing-attr-based-pipeline --mlir-print-local-scope --split-input-file --verify-diagnostics %s | FileCheck %s
2+
3+
func.func @test(%lhs : tensor<10x20xf16>, %rhs : tensor<20x40xf16>,
4+
%outs : tensor<10x40xf16>) -> tensor<10x40xf16> attributes {
5+
preprocessing_pipeline = #util.preprocessing_pipeline<"iree-preprocessing-make-single-dispatch">} {
6+
%matmul = linalg.matmul ins(%lhs, %rhs : tensor<10x20xf16>, tensor<20x40xf16>)
7+
outs(%outs : tensor<10x40xf16>) -> tensor<10x40xf16>
8+
return %matmul : tensor<10x40xf16>
9+
}
10+
// CHECK-LABEL: test
11+
// CHECK: %[[DISPATCH:.+]] = flow.dispatch.region
12+
// CHECK: %[[MATMUL:.+]] = linalg.matmul
13+
// CHECK: flow.return %[[MATMUL]]
14+
// CHECK: return %[[DISPATCH]]
15+
16+
// -----
17+
18+
module {
19+
func.func @function1(%lhs : tensor<10x20xf16>, %rhs : tensor<20x40xf16>,
20+
%outs : tensor<10x40xf16>) -> tensor<10x40xf16> attributes {
21+
preprocessing_pipeline = #util.preprocessing_pipeline<"iree-preprocessing-generalize-linalg-matmul-experimental">} {
22+
%matmul = linalg.matmul ins(%lhs, %rhs : tensor<10x20xf16>, tensor<20x40xf16>)
23+
outs(%outs : tensor<10x40xf16>) -> tensor<10x40xf16>
24+
return %matmul : tensor<10x40xf16>
25+
}
26+
func.func @function2(%lhs : tensor<10x20xf16>, %rhs : tensor<20x40xf16>,
27+
%outs : tensor<10x40xf16>) -> tensor<10x40xf16> attributes {
28+
preprocessing_pipeline = #util.preprocessing_pipeline<"iree-preprocessing-pad-linalg-ops">} {
29+
%matmul = linalg.matmul ins(%lhs, %rhs : tensor<10x20xf16>, tensor<20x40xf16>)
30+
outs(%outs : tensor<10x40xf16>) -> tensor<10x40xf16>
31+
return %matmul : tensor<10x40xf16>
32+
}
33+
}
34+
// CHECK-LABEL: func @function1
35+
// CHECK: %[[GENERIC:.+]] = linalg.generic
36+
// CHECK: return %[[GENERIC]]
37+
// CHECK-LABEL: func @function2
38+
// CHECK-DAG: %[[PAD1:.+]] = tensor.pad
39+
// CHECK-DAG: %[[PAD2:.+]] = tensor.pad
40+
// CHECK: %[[MATMUL:.+]] = linalg.matmul
41+
// CHECK-SAME: ins(%[[PAD1]],
42+
// CHECK-SAME: outs(%[[PAD2]]
43+
// CHECK: %[[SLICE:.+]] = tensor.extract_slice %[[MATMUL]]
44+
// CHECK: return %[[SLICE]]
45+
46+
// -----
47+
48+
func.func @function(%lhs : tensor<10x20xf16>, %rhs : tensor<20x40xf16>,
49+
%outs : tensor<10x40xf16>) -> tensor<10x40xf16> attributes {
50+
preprocessing_pipeline = #util.preprocessing_pipeline<"iree-preprocessing-pad-linalg-ops,iree-preprocessing-generalize-linalg-matmul-experimental">} {
51+
%matmul = linalg.matmul ins(%lhs, %rhs : tensor<10x20xf16>, tensor<20x40xf16>)
52+
outs(%outs : tensor<10x40xf16>) -> tensor<10x40xf16>
53+
return %matmul : tensor<10x40xf16>
54+
}
55+
// CHECK-LABEL: func @function
56+
// CHECK-DAG: %[[PAD1:.+]] = tensor.pad
57+
// CHECK-DAG: %[[PAD2:.+]] = tensor.pad
58+
// CHECK: %[[MATMUL:.+]] = linalg.generic
59+
// CHECK-SAME: ins(%[[PAD1]],
60+
// CHECK-SAME: outs(%[[PAD2]]
61+
// CHECK: %[[SLICE:.+]] = tensor.extract_slice %[[MATMUL]]
62+
// CHECK: return %[[SLICE]]
63+
64+
// -----
65+
66+
// expected-remark@+1 {{expected preprocessing_pipeline attribute to be a `StringAttr` that specifies the pass pipeline to apply}}
67+
func.func @function() attributes {
68+
preprocessing_pipeline = "iree-preprocessing-pad-linalg-ops"} {
69+
return
70+
}

compiler/src/iree/compiler/Preprocessing/Passes.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ void buildPreprocessingPassPipeline(
108108
if (pipelineExtensions) {
109109
pipelineExtensions->extendPreprocessingPassPipeline(passManager);
110110
}
111+
112+
// 3. Run any pass pipelines specified through the use of
113+
// `preprocessing_pipeline` attribute.
114+
FunctionLikeNest(passManager).addPass(createAttrBasedPipelinePass);
111115
}
112116

113117
static void
@@ -124,11 +128,20 @@ buildTransposeConvolutionPassPipeline(OpPassManager &passManager,
124128
passManager.addPass(createCSEPass());
125129
}
126130

131+
/// Pass pipeline to make the computation within a function a single dispatch.
132+
/// Note that this expects a `OpPassManager` nested on `FunctionOpInterface`
133+
/// ops.
134+
static void
135+
buildMakeSingleDispatchPassPipeline(OpPassManager &passManager,
136+
const TransformOptions &options) {
137+
passManager.addPass(createMakeSingleDispatchForFunctionPass());
138+
}
139+
127140
void registerPreprocessingPasses() {
128141
registerCommonPreprocessingPasses();
129142

130143
PassPipelineRegistration<TransformOptions>
131-
globalOptimizationTransformPassPipeline(
144+
preprocessingTransposeConvolutionPassPipeline(
132145
"iree-preprocessing-transpose-convolution-pipeline",
133146
"Runs a pass pipeline for transposing and canonicalizing "
134147
"convolutions",
@@ -137,6 +150,15 @@ void registerPreprocessingPasses() {
137150
buildTransposeConvolutionPassPipeline(passManager,
138151
transformOptions);
139152
});
153+
154+
PassPipelineRegistration<TransformOptions>
155+
preprocessingMakeSingleDispatchPassPipeline(
156+
"iree-preprocessing-make-single-dispatch",
157+
"Runs passes to get a single dispatch for a function",
158+
[](OpPassManager &passManager,
159+
const TransformOptions &transformOptions) {
160+
buildMakeSingleDispatchPassPipeline(passManager, transformOptions);
161+
});
140162
}
141163

142164
} // namespace mlir::iree_compiler::Preprocessing

0 commit comments

Comments
 (0)