MLIR 24.0.0git
DropEquivalentBufferResults.cpp
Go to the documentation of this file.
1//===- DropEquivalentBufferResults.cpp - Calling convention conversion ----===//
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 pass drops return values from functions if they are equivalent to one of
10// their arguments. E.g.:
11//
12// ```
13// func.func @foo(%m : memref<?xf32>) -> (memref<?xf32>) {
14// return %m : memref<?xf32>
15// }
16// ```
17//
18// This functions is rewritten to:
19//
20// ```
21// func.func @foo(%m : memref<?xf32>) {
22// return
23// }
24// ```
25//
26// All call sites are updated accordingly. If a function returns a cast of a
27// function argument, it is also considered equivalent. A cast is inserted at
28// the call site in that case.
29
31
34
35#include "llvm/ADT/SetVector.h"
36
37namespace mlir {
38namespace bufferization {
39#define GEN_PASS_DEF_DROPEQUIVALENTBUFFERRESULTSPASS
40#include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc"
41} // namespace bufferization
42} // namespace mlir
43
44using namespace mlir;
45
46/// Get all the ReturnOp in the funcOp.
47static SmallVector<func::ReturnOp> getReturnOps(func::FuncOp funcOp) {
49 for (Block &b : funcOp.getBody()) {
50 if (auto candidateOp = dyn_cast<func::ReturnOp>(b.getTerminator())) {
51 returnOps.push_back(candidateOp);
52 }
53 }
54 return returnOps;
55}
56
57/// Get the operands at the specified position for all returnOps.
60 return llvm::map_to_vector(returnOps, [&](func::ReturnOp returnOp) {
61 return returnOp.getOperand(pos);
62 });
63}
64
65/// Check if all given values are the same buffer as the block argument (modulo
66/// cast ops).
68 BlockArgument argument) {
69 for (Value val : operands) {
70 while (auto castOp = val.getDefiningOp<memref::CastOp>())
71 val = castOp.getSource();
72
73 if (val != argument)
74 return false;
75 }
76 return true;
77}
78
80 ModuleOp module, DropBufferResultsOpts options) {
81 IRRewriter rewriter(module.getContext());
82
83 auto canModify = [&](func::FuncOp funcOp) {
84 return funcOp->getParentOp() == module.getOperation() &&
85 !funcOp.isExternal() &&
86 (!funcOp.isPublic() || options.modifyPublicFunctions);
87 };
88
90 // Collect the mapping of functions to their call sites.
91 module.walk([&](func::CallOp callOp) {
92 if (func::FuncOp calledFunc =
93 dyn_cast_or_null<func::FuncOp>(callOp.resolveCallable())) {
94 if (canModify(calledFunc))
95 callerMap[calledFunc].push_back(callOp);
96 }
97 return WalkResult::advance();
98 });
99
100 // Dropping a callee result can make a caller result equivalent to one of the
101 // caller's arguments. Revisit such callers until no more results can be
102 // dropped. A SetVector avoids adding the same function to the worklist more
103 // than once. Every revisit is triggered by deleting a result, so the
104 // algorithm also terminates for recursive call graphs.
106 for (auto funcOp : module.getOps<func::FuncOp>())
107 if (canModify(funcOp))
108 worklist.insert(funcOp);
109
110 while (!worklist.empty()) {
111 func::FuncOp funcOp = worklist.pop_back_val();
112 SmallVector<func::ReturnOp> returnOps = getReturnOps(funcOp);
113 if (returnOps.empty())
114 continue;
115
116 // Compute erased results.
117 size_t numReturnOps = returnOps.size();
118 size_t numReturnValues = funcOp.getFunctionType().getNumResults();
119 SmallVector<SmallVector<Value>> newReturnValues(numReturnOps);
120 BitVector erasedResultIndices(numReturnValues);
121 DenseMap<int64_t, int64_t> resultToArgs;
122 for (size_t i = 0; i < numReturnValues; ++i) {
123 bool erased = false;
124 SmallVector<Value> returnOperands =
125 getReturnOpsOperandInPos(returnOps, i);
126 for (BlockArgument bbArg : funcOp.getArguments()) {
127 if (operandsEqualFuncArgument(returnOperands, bbArg)) {
128 resultToArgs[i] = bbArg.getArgNumber();
129 erased = true;
130 break;
131 }
132 }
133
134 if (erased) {
135 erasedResultIndices.set(i);
136 } else {
137 for (auto [newReturnValue, operand] :
138 llvm::zip(newReturnValues, returnOperands)) {
139 newReturnValue.push_back(operand);
140 }
141 }
142 }
143
144 if (erasedResultIndices.none())
145 continue;
146
147 // Update function.
148 if (failed(funcOp.eraseResults(erasedResultIndices)))
149 return failure();
150
151 for (auto [returnOp, newReturnValue] :
152 llvm::zip(returnOps, newReturnValues))
153 returnOp.getOperandsMutable().assign(newReturnValue);
154
155 // Update function calls.
157 callOps.swap(callerMap[funcOp]);
158 for (func::CallOp callOp : callOps) {
159 func::FuncOp caller = callOp->getParentOfType<func::FuncOp>();
160 rewriter.setInsertionPoint(callOp);
161 auto newCallOp = func::CallOp::create(rewriter, callOp.getLoc(), funcOp,
162 callOp.getOperands());
163 SmallVector<Value> newResults;
164 int64_t nextResult = 0;
165 for (int64_t i = 0; i < callOp.getNumResults(); ++i) {
166 if (!resultToArgs.count(i)) {
167 // This result was not erased.
168 newResults.push_back(newCallOp.getResult(nextResult++));
169 continue;
170 }
171
172 // This result was erased.
173 Value replacement = callOp.getOperand(resultToArgs[i]);
174 Type expectedType = callOp.getResult(i).getType();
175 if (replacement.getType() != expectedType) {
176 // A cast must be inserted at the call site.
177 replacement = memref::CastOp::create(rewriter, callOp.getLoc(),
178 expectedType, replacement);
179 }
180 newResults.push_back(replacement);
181 }
182 rewriter.replaceOp(callOp, newResults);
183 callerMap[funcOp].push_back(newCallOp);
184 if (caller && canModify(caller))
185 worklist.insert(caller);
186 }
187 }
188
189 return success();
190}
191
192namespace {
193struct DropEquivalentBufferResultsPass
195 DropEquivalentBufferResultsPass> {
196 using Base::Base;
197
198 void runOnOperation() override {
199 // Convert pass options.
200 options.modifyPublicFunctions = modifyPublicFunctions;
201
203 options)))
204 return signalPassFailure();
205 }
206
207private:
208 bufferization::DropBufferResultsOpts options;
209};
210} // namespace
for(Operation *op :ops)
return success()
if(failed(verifyVectorMemoryOp(getOperation(), memrefType, getVectorType()))) return failure()
static SmallVector< func::ReturnOp > getReturnOps(func::FuncOp funcOp)
Get all the ReturnOp in the funcOp.
static bool operandsEqualFuncArgument(ArrayRef< Value > operands, BlockArgument argument)
Check if all given values are the same buffer as the block argument (modulo cast ops).
static SmallVector< Value > getReturnOpsOperandInPos(ArrayRef< func::ReturnOp > returnOps, size_t pos)
Get the operands at the specified position for all returnOps.
b
Return true if permutation is a valid permutation of the outer_dims_perm (case OuterOrInnerPerm::Oute...
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
static llvm::ManagedStatic< PassManagerOptions > options
This class represents an argument of a Block.
Definition Value.h:306
Block represents an ordered list of Operations.
Definition Block.h:34
This class coordinates rewriting a piece of IR outside of a pattern rewrite, providing a way to keep ...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
static WalkResult advance()
Definition WalkResult.h:47
LogicalResult dropEquivalentBufferResults(ModuleOp module, DropBufferResultsOpts options=DropBufferResultsOpts())
Drop all memref function results that are equivalent to a function argument.
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:734
Include the generated interface declarations.
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:123
Options for dropping equivalent memref buffer results.
Definition Passes.h:186