MLIR 24.0.0git
FoldIntoElementwise.cpp
Go to the documentation of this file.
1//===- FoldIntoElementwise.cpp - Fold Ops into elementwise if possible ---===//
2//
3// Part of the LLVM Project, 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//===----------------------------------------------------------------------===//
8//
9// This file implements folding ops such as transpose and broadcast into the
10// affine maps of elementwise consumers.
11//
12//===----------------------------------------------------------------------===//
13
20#include "llvm/ADT/STLExtras.h"
21#include "llvm/ADT/SmallVector.h"
22
23namespace mlir {
24#define GEN_PASS_DEF_LINALGFOLDINTOELEMENTWISEPASS
25#include "mlir/Dialect/Linalg/Passes.h.inc"
26} // namespace mlir
27
28using namespace mlir;
29using namespace mlir::linalg;
30
31#define DEBUG_TYPE "linalg-fold-into-elementwise"
32
33namespace {
34template <typename ProducerOpTy>
35struct ElementwiseOpFolder {
36 // Helper function to fold broadcast etc. into a consumer operand.
37 static bool fold(OpOperand *elwiseOperand, AffineMap elwiseMap,
38 SmallVector<Value> &newIns,
39 SmallVector<AffineMap> &newMaps) {
40 auto producerOp = elwiseOperand->get().getDefiningOp<ProducerOpTy>();
41 if (!producerOp || !elwiseMap.isProjectedPermutation())
42 return false;
43 newIns.push_back(producerOp.getInput());
44 // push in the new composed affine map
45 newMaps.push_back(
46 producerOp.getMatchingIndexingMap(producerOp.getDpsInputOperand(0))
47 .compose(elwiseMap));
48 return true;
49 }
50};
51
52template <typename... ProducerOps>
53struct FoldIntoElementwisePattern : public OpInterfaceRewritePattern<LinalgOp> {
55
56 LogicalResult matchAndRewrite(LinalgOp op,
57 PatternRewriter &rewriter) const override {
58 if (!isa<GenericOp, ElementwiseOp>(op.getOperation()) || !isElementwise(op))
59 return failure();
60
61 bool changed = false;
62 SmallVector<Value> newIns;
64 for (OpOperand *operand : op.getDpsInputOperands()) {
65 AffineMap consumerMap = op.getMatchingIndexingMap(operand);
66 const bool folded = (ElementwiseOpFolder<ProducerOps>::fold(
67 operand, consumerMap, newIns, newMaps) ||
68 ...);
69 if (folded) {
70 changed = true;
71 } else {
72 // push in original operand and its map.
73 newIns.push_back(operand->get());
74 newMaps.push_back(consumerMap);
75 }
76 }
77 if (!changed)
78 return failure();
79
80 // Keep all output operands and their maps unchanged.
81 SmallVector<AffineMap> originalMaps = op.getIndexingMapsArray();
82 newMaps.append(originalMaps.begin() + op.getNumDpsInputs(),
83 originalMaps.end());
84
85 // The maps of the rewritten op must still determine bounds for every loop
86 // dimension. Folding a broadcast can otherwise drop the only map result
87 // that covers a dimension.
88 // See `generic_broadcast_not_folded_non_invertible` in
89 // mlir/test/Dialect/Linalg/elementwise/fold.mlir for an example.
90 if (!inversePermutation(concatAffineMaps(newMaps, op.getContext())))
91 return failure();
92
93 rewriter.modifyOpInPlace(op, [&] {
94 for (auto [index, operand] : llvm::enumerate(op.getDpsInputOperands()))
95 op->setOperand(operand->getOperandNumber(), newIns[index]);
96 op->setAttr("indexing_maps", rewriter.getAffineMapArrayAttr(newMaps));
97 });
98 return success();
99 }
100};
101
102struct LinalgFoldIntoElementwisePass
104 LinalgFoldIntoElementwisePass> {
106 LinalgFoldIntoElementwisePass>::LinalgFoldIntoElementwisePassBase;
107
108 void runOnOperation() override {
109 Operation *op = getOperation();
110 RewritePatternSet patterns(op->getContext());
112
113 if (failed(applyPatternsGreedily(op, std::move(patterns))))
114 return signalPassFailure();
115 }
116};
117} // namespace
118
120 RewritePatternSet &patterns) {
121 patterns.add<FoldIntoElementwisePattern<TransposeOp, BroadcastOp>>(
122 patterns.getContext());
123}
return success()
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition AffineMap.h:46
bool isProjectedPermutation(bool allowZeroInResults=false) const
Returns true if the AffineMap represents a subset (i.e.
ArrayAttr getAffineMapArrayAttr(ArrayRef< AffineMap > values)
Definition Builders.cpp:327
IRValueT get() const
Return the current value being used by this operand.
This class represents an operand of an operation.
Definition Value.h:254
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
MLIRContext * getContext()
Return the context this operation is associated with.
Definition Operation.h:233
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
void modifyOpInPlace(Operation *root, CallableT &&callable)
This method is a utility wrapper around an in-place modification of an operation.
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
void populateLinalgFoldIntoElementwisePatterns(RewritePatternSet &patterns)
Populates patterns with patterns that fold operations like linalg.transform into elementwise op map.
bool isElementwise(LinalgOp op)
Check if a LinalgOp is an element-wise operation.
Definition Utils.cpp:217
Include the generated interface declarations.
AffineMap concatAffineMaps(ArrayRef< AffineMap > maps, MLIRContext *context)
Concatenates a list of maps into a single AffineMap, stepping over potentially empty maps.
LogicalResult applyPatternsGreedily(Region &region, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
AffineMap inversePermutation(AffineMap map)
Returns a map of codomain to domain dimensions such that the first codomain dimension for a particula...
OpInterfaceRewritePattern is a wrapper around RewritePattern that allows for matching and rewriting a...