1*e07a7fd5SMatthias Springer //===- BufferizableOpInterfaceImpl.cpp - Impl. of BufferizableOpInterface -===//
2*e07a7fd5SMatthias Springer //
3*e07a7fd5SMatthias Springer // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*e07a7fd5SMatthias Springer // See https://llvm.org/LICENSE.txt for license information.
5*e07a7fd5SMatthias Springer // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*e07a7fd5SMatthias Springer //
7*e07a7fd5SMatthias Springer //===----------------------------------------------------------------------===//
8*e07a7fd5SMatthias Springer 
9*e07a7fd5SMatthias Springer #include "mlir/Dialect/Bufferization/Transforms/FuncBufferizableOpInterfaceImpl.h"
10*e07a7fd5SMatthias Springer #include "mlir/Dialect/Bufferization/IR/BufferizableOpInterface.h"
11*e07a7fd5SMatthias Springer #include "mlir/Dialect/Bufferization/IR/Bufferization.h"
12*e07a7fd5SMatthias Springer #include "mlir/Dialect/Bufferization/Transforms/OneShotAnalysis.h"
13*e07a7fd5SMatthias Springer #include "mlir/Dialect/Func/IR/FuncOps.h"
14*e07a7fd5SMatthias Springer #include "mlir/Dialect/MemRef/IR/MemRef.h"
15*e07a7fd5SMatthias Springer #include "mlir/IR/Dialect.h"
16*e07a7fd5SMatthias Springer #include "mlir/IR/Operation.h"
17*e07a7fd5SMatthias Springer 
18*e07a7fd5SMatthias Springer namespace mlir {
19*e07a7fd5SMatthias Springer namespace bufferization {
20*e07a7fd5SMatthias Springer namespace func_ext {
21*e07a7fd5SMatthias Springer 
22*e07a7fd5SMatthias Springer void FuncAnalysisState::startFunctionAnalysis(FuncOp funcOp) {
23*e07a7fd5SMatthias Springer   analyzedFuncOps[funcOp] = FuncOpAnalysisState::InProgress;
24*e07a7fd5SMatthias Springer   auto createdEquiv = equivalentFuncArgs.try_emplace(funcOp, IndexMapping());
25*e07a7fd5SMatthias Springer   auto createdAliasingOperands =
26*e07a7fd5SMatthias Springer       aliasingFuncArgs.try_emplace(funcOp, IndexToIndexListMapping());
27*e07a7fd5SMatthias Springer   auto createdAliasingResults =
28*e07a7fd5SMatthias Springer       aliasingReturnVals.try_emplace(funcOp, IndexToIndexListMapping());
29*e07a7fd5SMatthias Springer   auto createdRead = readBbArgs.try_emplace(funcOp, BbArgIndexSet());
30*e07a7fd5SMatthias Springer   auto createdWritten = writtenBbArgs.try_emplace(funcOp, BbArgIndexSet());
31*e07a7fd5SMatthias Springer   (void)createdEquiv;
32*e07a7fd5SMatthias Springer   (void)createdAliasingOperands;
33*e07a7fd5SMatthias Springer   (void)createdAliasingResults;
34*e07a7fd5SMatthias Springer   (void)createdRead;
35*e07a7fd5SMatthias Springer   (void)createdWritten;
36*e07a7fd5SMatthias Springer #ifndef NDEBUG
37*e07a7fd5SMatthias Springer   assert(createdEquiv.second && "equivalence info exists already");
38*e07a7fd5SMatthias Springer   assert(createdAliasingOperands.second && "aliasing info exists already");
39*e07a7fd5SMatthias Springer   assert(createdAliasingResults.second && "aliasing info exists already");
40*e07a7fd5SMatthias Springer   assert(createdRead.second && "bbarg access info exists already");
41*e07a7fd5SMatthias Springer   assert(createdWritten.second && "bbarg access info exists already");
42*e07a7fd5SMatthias Springer #endif // NDEBUG
43*e07a7fd5SMatthias Springer }
44*e07a7fd5SMatthias Springer 
45*e07a7fd5SMatthias Springer /// Return the unique ReturnOp that terminates `funcOp`.
46*e07a7fd5SMatthias Springer /// Return nullptr if there is no such unique ReturnOp.
47*e07a7fd5SMatthias Springer static func::ReturnOp getAssumedUniqueReturnOp(FuncOp funcOp) {
48*e07a7fd5SMatthias Springer   func::ReturnOp returnOp;
49*e07a7fd5SMatthias Springer   for (Block &b : funcOp.getBody()) {
50*e07a7fd5SMatthias Springer     if (auto candidateOp = dyn_cast<func::ReturnOp>(b.getTerminator())) {
51*e07a7fd5SMatthias Springer       if (returnOp)
52*e07a7fd5SMatthias Springer         return nullptr;
53*e07a7fd5SMatthias Springer       returnOp = candidateOp;
54*e07a7fd5SMatthias Springer     }
55*e07a7fd5SMatthias Springer   }
56*e07a7fd5SMatthias Springer   return returnOp;
57*e07a7fd5SMatthias Springer }
58*e07a7fd5SMatthias Springer 
59*e07a7fd5SMatthias Springer /// Return the index-th bufferized function argument type. This assumes that the
60*e07a7fd5SMatthias Springer /// specified argument is a tensor. If the tensor is ranked, a layout map may be
61*e07a7fd5SMatthias Springer /// specified by the user. If no layout map is specified, a fully dynamic map is
62*e07a7fd5SMatthias Springer /// used.
63*e07a7fd5SMatthias Springer static BaseMemRefType
64*e07a7fd5SMatthias Springer getBufferizedFunctionArgType(FuncOp funcOp, int64_t index,
65*e07a7fd5SMatthias Springer                              const BufferizationOptions &options) {
66*e07a7fd5SMatthias Springer   auto tensorType =
67*e07a7fd5SMatthias Springer       funcOp.getFunctionType().getInput(index).dyn_cast<TensorType>();
68*e07a7fd5SMatthias Springer   assert(tensorType && "expected TensorType");
69*e07a7fd5SMatthias Springer   BaseMemRefType memrefType = getMemRefType(tensorType, options);
70*e07a7fd5SMatthias Springer 
71*e07a7fd5SMatthias Springer   auto layoutAttr = funcOp.getArgAttrOfType<AffineMapAttr>(
72*e07a7fd5SMatthias Springer       index, BufferizationDialect::kBufferLayoutAttrName);
73*e07a7fd5SMatthias Springer   if (!layoutAttr)
74*e07a7fd5SMatthias Springer     return memrefType;
75*e07a7fd5SMatthias Springer 
76*e07a7fd5SMatthias Springer   auto rankedMemrefType = memrefType.dyn_cast<MemRefType>();
77*e07a7fd5SMatthias Springer   assert(rankedMemrefType && "buffer layout not supported on unranked tensors");
78*e07a7fd5SMatthias Springer   return MemRefType::get(
79*e07a7fd5SMatthias Springer       rankedMemrefType.getShape(), rankedMemrefType.getElementType(),
80*e07a7fd5SMatthias Springer       layoutAttr.getValue(), rankedMemrefType.getMemorySpaceAsInt());
81*e07a7fd5SMatthias Springer }
82*e07a7fd5SMatthias Springer 
83*e07a7fd5SMatthias Springer /// Return the FuncOp called by `callOp`.
84*e07a7fd5SMatthias Springer static FuncOp getCalledFunction(CallOpInterface callOp) {
85*e07a7fd5SMatthias Springer   SymbolRefAttr sym = callOp.getCallableForCallee().dyn_cast<SymbolRefAttr>();
86*e07a7fd5SMatthias Springer   if (!sym)
87*e07a7fd5SMatthias Springer     return nullptr;
88*e07a7fd5SMatthias Springer   return dyn_cast_or_null<FuncOp>(
89*e07a7fd5SMatthias Springer       SymbolTable::lookupNearestSymbolFrom(callOp, sym));
90*e07a7fd5SMatthias Springer }
91*e07a7fd5SMatthias Springer 
92*e07a7fd5SMatthias Springer /// Get FuncAnalysisState.
93*e07a7fd5SMatthias Springer static const FuncAnalysisState &
94*e07a7fd5SMatthias Springer getFuncAnalysisState(const AnalysisState &state) {
95*e07a7fd5SMatthias Springer   Optional<const FuncAnalysisState *> maybeState =
96*e07a7fd5SMatthias Springer       state.getDialectState<FuncAnalysisState>(
97*e07a7fd5SMatthias Springer           func::FuncDialect::getDialectNamespace());
98*e07a7fd5SMatthias Springer   assert(maybeState.hasValue() && "FuncAnalysisState does not exist");
99*e07a7fd5SMatthias Springer   return **maybeState;
100*e07a7fd5SMatthias Springer }
101*e07a7fd5SMatthias Springer 
102*e07a7fd5SMatthias Springer /// Return the state (phase) of analysis of the FuncOp.
103*e07a7fd5SMatthias Springer static FuncOpAnalysisState getFuncOpAnalysisState(const AnalysisState &state,
104*e07a7fd5SMatthias Springer                                                   FuncOp funcOp) {
105*e07a7fd5SMatthias Springer   const FuncAnalysisState &funcState = getFuncAnalysisState(state);
106*e07a7fd5SMatthias Springer   auto it = funcState.analyzedFuncOps.find(funcOp);
107*e07a7fd5SMatthias Springer   if (it == funcState.analyzedFuncOps.end())
108*e07a7fd5SMatthias Springer     return FuncOpAnalysisState::NotAnalyzed;
109*e07a7fd5SMatthias Springer   return it->second;
110*e07a7fd5SMatthias Springer }
111*e07a7fd5SMatthias Springer 
112*e07a7fd5SMatthias Springer /// Return the index of the bbArg in the given FuncOp that is equivalent to the
113*e07a7fd5SMatthias Springer /// specified return value (if any).
114*e07a7fd5SMatthias Springer static Optional<int64_t> getEquivalentFuncArgIdx(FuncOp funcOp,
115*e07a7fd5SMatthias Springer                                                  const FuncAnalysisState &state,
116*e07a7fd5SMatthias Springer                                                  int64_t returnValIdx) {
117*e07a7fd5SMatthias Springer   auto funcOpIt = state.equivalentFuncArgs.find(funcOp);
118*e07a7fd5SMatthias Springer   if (funcOpIt == state.equivalentFuncArgs.end())
119*e07a7fd5SMatthias Springer     // No equivalence info stores for funcOp.
120*e07a7fd5SMatthias Springer     return None;
121*e07a7fd5SMatthias Springer 
122*e07a7fd5SMatthias Springer   auto retValIt = funcOpIt->getSecond().find(returnValIdx);
123*e07a7fd5SMatthias Springer   if (retValIt == funcOpIt->getSecond().end())
124*e07a7fd5SMatthias Springer     // Return value has no equivalent bbArg.
125*e07a7fd5SMatthias Springer     return None;
126*e07a7fd5SMatthias Springer 
127*e07a7fd5SMatthias Springer   return retValIt->getSecond();
128*e07a7fd5SMatthias Springer }
129*e07a7fd5SMatthias Springer 
130*e07a7fd5SMatthias Springer struct CallOpInterface
131*e07a7fd5SMatthias Springer     : public BufferizableOpInterface::ExternalModel<CallOpInterface,
132*e07a7fd5SMatthias Springer                                                     func::CallOp> {
133*e07a7fd5SMatthias Springer   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
134*e07a7fd5SMatthias Springer                               const AnalysisState &state) const {
135*e07a7fd5SMatthias Springer     func::CallOp callOp = cast<func::CallOp>(op);
136*e07a7fd5SMatthias Springer     FuncOp funcOp = getCalledFunction(callOp);
137*e07a7fd5SMatthias Springer     assert(funcOp && "expected CallOp to a FuncOp");
138*e07a7fd5SMatthias Springer 
139*e07a7fd5SMatthias Springer     const FuncAnalysisState &funcState = getFuncAnalysisState(state);
140*e07a7fd5SMatthias Springer     if (getFuncOpAnalysisState(state, funcOp) != FuncOpAnalysisState::Analyzed)
141*e07a7fd5SMatthias Springer       // FuncOp not analyzed yet. Assume that OpOperand is read.
142*e07a7fd5SMatthias Springer       return true;
143*e07a7fd5SMatthias Springer 
144*e07a7fd5SMatthias Springer     return funcState.readBbArgs.lookup(funcOp).contains(
145*e07a7fd5SMatthias Springer         opOperand.getOperandNumber());
146*e07a7fd5SMatthias Springer   }
147*e07a7fd5SMatthias Springer 
148*e07a7fd5SMatthias Springer   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
149*e07a7fd5SMatthias Springer                                const AnalysisState &state) const {
150*e07a7fd5SMatthias Springer     func::CallOp callOp = cast<func::CallOp>(op);
151*e07a7fd5SMatthias Springer     FuncOp funcOp = getCalledFunction(callOp);
152*e07a7fd5SMatthias Springer     assert(funcOp && "expected CallOp to a FuncOp");
153*e07a7fd5SMatthias Springer 
154*e07a7fd5SMatthias Springer     const FuncAnalysisState &funcState = getFuncAnalysisState(state);
155*e07a7fd5SMatthias Springer     if (getFuncOpAnalysisState(state, funcOp) != FuncOpAnalysisState::Analyzed)
156*e07a7fd5SMatthias Springer       // FuncOp not analyzed yet. Assume that OpOperand is written.
157*e07a7fd5SMatthias Springer       return true;
158*e07a7fd5SMatthias Springer 
159*e07a7fd5SMatthias Springer     return funcState.writtenBbArgs.lookup(funcOp).contains(
160*e07a7fd5SMatthias Springer         opOperand.getOperandNumber());
161*e07a7fd5SMatthias Springer   }
162*e07a7fd5SMatthias Springer 
163*e07a7fd5SMatthias Springer   SmallVector<OpResult> getAliasingOpResult(Operation *op, OpOperand &opOperand,
164*e07a7fd5SMatthias Springer                                             const AnalysisState &state) const {
165*e07a7fd5SMatthias Springer     func::CallOp callOp = cast<func::CallOp>(op);
166*e07a7fd5SMatthias Springer     FuncOp funcOp = getCalledFunction(callOp);
167*e07a7fd5SMatthias Springer     assert(funcOp && "expected CallOp to a FuncOp");
168*e07a7fd5SMatthias Springer     const FuncAnalysisState &funcState = getFuncAnalysisState(state);
169*e07a7fd5SMatthias Springer     if (getFuncOpAnalysisState(state, funcOp) !=
170*e07a7fd5SMatthias Springer         FuncOpAnalysisState::Analyzed) {
171*e07a7fd5SMatthias Springer       // FuncOp not analyzed yet. Any OpResult may be aliasing.
172*e07a7fd5SMatthias Springer       SmallVector<OpResult> result;
173*e07a7fd5SMatthias Springer       for (OpResult opResult : op->getOpResults())
174*e07a7fd5SMatthias Springer         if (opResult.getType().isa<TensorType>())
175*e07a7fd5SMatthias Springer           result.push_back(opResult);
176*e07a7fd5SMatthias Springer       return result;
177*e07a7fd5SMatthias Springer     }
178*e07a7fd5SMatthias Springer 
179*e07a7fd5SMatthias Springer     // Get aliasing results from state.
180*e07a7fd5SMatthias Springer     auto aliasingReturnVals =
181*e07a7fd5SMatthias Springer         funcState.aliasingReturnVals.lookup(funcOp).lookup(
182*e07a7fd5SMatthias Springer             opOperand.getOperandNumber());
183*e07a7fd5SMatthias Springer     SmallVector<OpResult> result;
184*e07a7fd5SMatthias Springer     for (int64_t resultIdx : aliasingReturnVals)
185*e07a7fd5SMatthias Springer       result.push_back(callOp->getOpResult(resultIdx));
186*e07a7fd5SMatthias Springer     return result;
187*e07a7fd5SMatthias Springer   }
188*e07a7fd5SMatthias Springer 
189*e07a7fd5SMatthias Springer   SmallVector<OpOperand *>
190*e07a7fd5SMatthias Springer   getAliasingOpOperand(Operation *op, OpResult opResult,
191*e07a7fd5SMatthias Springer                        const AnalysisState &state) const {
192*e07a7fd5SMatthias Springer     func::CallOp callOp = cast<func::CallOp>(op);
193*e07a7fd5SMatthias Springer     FuncOp funcOp = getCalledFunction(callOp);
194*e07a7fd5SMatthias Springer     assert(funcOp && "expected CallOp to a FuncOp");
195*e07a7fd5SMatthias Springer     const FuncAnalysisState &funcState = getFuncAnalysisState(state);
196*e07a7fd5SMatthias Springer     if (getFuncOpAnalysisState(state, funcOp) !=
197*e07a7fd5SMatthias Springer         FuncOpAnalysisState::Analyzed) {
198*e07a7fd5SMatthias Springer       // FuncOp not analyzed yet. Any OpOperand may be aliasing.
199*e07a7fd5SMatthias Springer       SmallVector<OpOperand *> result;
200*e07a7fd5SMatthias Springer       for (OpOperand &opOperand : op->getOpOperands())
201*e07a7fd5SMatthias Springer         if (opOperand.get().getType().isa<TensorType>())
202*e07a7fd5SMatthias Springer           result.push_back(&opOperand);
203*e07a7fd5SMatthias Springer       return result;
204*e07a7fd5SMatthias Springer     }
205*e07a7fd5SMatthias Springer 
206*e07a7fd5SMatthias Springer     // Get aliasing bbArgs from state.
207*e07a7fd5SMatthias Springer     auto aliasingFuncArgs = funcState.aliasingFuncArgs.lookup(funcOp).lookup(
208*e07a7fd5SMatthias Springer         opResult.getResultNumber());
209*e07a7fd5SMatthias Springer     SmallVector<OpOperand *> result;
210*e07a7fd5SMatthias Springer     for (int64_t bbArgIdx : aliasingFuncArgs)
211*e07a7fd5SMatthias Springer       result.push_back(&callOp->getOpOperand(bbArgIdx));
212*e07a7fd5SMatthias Springer     return result;
213*e07a7fd5SMatthias Springer   }
214*e07a7fd5SMatthias Springer 
215*e07a7fd5SMatthias Springer   BufferRelation bufferRelation(Operation *op, OpResult opResult,
216*e07a7fd5SMatthias Springer                                 const AnalysisState &state) const {
217*e07a7fd5SMatthias Springer     return BufferRelation::Equivalent;
218*e07a7fd5SMatthias Springer   }
219*e07a7fd5SMatthias Springer 
220*e07a7fd5SMatthias Springer   /// All function arguments are writable. It is the responsibility of the
221*e07a7fd5SMatthias Springer   /// CallOp to insert buffer copies where necessary.
222*e07a7fd5SMatthias Springer   LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
223*e07a7fd5SMatthias Springer                           BufferizationState &state) const {
224*e07a7fd5SMatthias Springer     func::CallOp callOp = cast<func::CallOp>(op);
225*e07a7fd5SMatthias Springer     unsigned numResults = callOp.getNumResults();
226*e07a7fd5SMatthias Springer     unsigned numOperands = callOp->getNumOperands();
227*e07a7fd5SMatthias Springer     FuncOp funcOp = getCalledFunction(callOp);
228*e07a7fd5SMatthias Springer     assert(funcOp && "expected CallOp to a FuncOp");
229*e07a7fd5SMatthias Springer     FunctionType funcType = funcOp.getFunctionType();
230*e07a7fd5SMatthias Springer     const FuncAnalysisState &funcState =
231*e07a7fd5SMatthias Springer         getFuncAnalysisState(state.getAnalysisState());
232*e07a7fd5SMatthias Springer     const OneShotBufferizationOptions &options =
233*e07a7fd5SMatthias Springer         static_cast<const OneShotBufferizationOptions &>(state.getOptions());
234*e07a7fd5SMatthias Springer 
235*e07a7fd5SMatthias Springer     // Result types of the bufferized CallOp.
236*e07a7fd5SMatthias Springer     SmallVector<Type> resultTypes;
237*e07a7fd5SMatthias Springer     // Replacement values for the existing CallOp. These are usually the results
238*e07a7fd5SMatthias Springer     // of the bufferized CallOp, unless a tensor result folds onto an operand.
239*e07a7fd5SMatthias Springer     SmallVector<Value> replacementValues(numResults, Value());
240*e07a7fd5SMatthias Springer     // For non-tensor results: A mapping from return val indices of the old
241*e07a7fd5SMatthias Springer     // CallOp to return val indices of the bufferized CallOp.
242*e07a7fd5SMatthias Springer     SmallVector<Optional<unsigned>> retValMapping(numResults, None);
243*e07a7fd5SMatthias Springer     // Operands of the bufferized CallOp.
244*e07a7fd5SMatthias Springer     SmallVector<Value> newOperands(numOperands, Value());
245*e07a7fd5SMatthias Springer 
246*e07a7fd5SMatthias Springer     // Based on previously gathered equivalence information, we know if a
247*e07a7fd5SMatthias Springer     // tensor result folds onto an operand. These are the only tensor value
248*e07a7fd5SMatthias Springer     // results that are supported at the moment.
249*e07a7fd5SMatthias Springer     //
250*e07a7fd5SMatthias Springer     // For tensors return values that do not fold onto an operand, additional
251*e07a7fd5SMatthias Springer     // work is needed (TODO) to either:
252*e07a7fd5SMatthias Springer     // * hoist a result into an inplaceable operand or
253*e07a7fd5SMatthias Springer     // * devise a better representation to truly return a buffer.
254*e07a7fd5SMatthias Springer     //
255*e07a7fd5SMatthias Springer     // Note: If a function has no body, no equivalence information is
256*e07a7fd5SMatthias Springer     // available. Consequently, a tensor return value cannot be proven to fold
257*e07a7fd5SMatthias Springer     // onto a FuncOp bbArg, so calls to such functions are not bufferizable at
258*e07a7fd5SMatthias Springer     // the moment.
259*e07a7fd5SMatthias Springer 
260*e07a7fd5SMatthias Springer     // 1. Compute the result types of the new CallOp. Tensor results that are
261*e07a7fd5SMatthias Springer     // equivalent to a FuncOp bbArg are no longer returned.
262*e07a7fd5SMatthias Springer     for (const auto &it : llvm::enumerate(callOp.getResultTypes())) {
263*e07a7fd5SMatthias Springer       unsigned returnValIdx = it.index();
264*e07a7fd5SMatthias Springer       Type returnType = it.value();
265*e07a7fd5SMatthias Springer       if (!returnType.isa<TensorType>()) {
266*e07a7fd5SMatthias Springer         // Non-tensor values are returned.
267*e07a7fd5SMatthias Springer         retValMapping[returnValIdx] = resultTypes.size();
268*e07a7fd5SMatthias Springer         resultTypes.push_back(returnType);
269*e07a7fd5SMatthias Springer         continue;
270*e07a7fd5SMatthias Springer       }
271*e07a7fd5SMatthias Springer 
272*e07a7fd5SMatthias Springer       if (Optional<int64_t> bbArgIdx =
273*e07a7fd5SMatthias Springer               getEquivalentFuncArgIdx(funcOp, funcState, returnValIdx)) {
274*e07a7fd5SMatthias Springer         // Return operands that are equivalent to some bbArg, are not
275*e07a7fd5SMatthias Springer         // returned.
276*e07a7fd5SMatthias Springer         FailureOr<Value> bufferOrFailure =
277*e07a7fd5SMatthias Springer             state.getBuffer(rewriter, callOp->getOpOperand(*bbArgIdx));
278*e07a7fd5SMatthias Springer         if (failed(bufferOrFailure))
279*e07a7fd5SMatthias Springer           return failure();
280*e07a7fd5SMatthias Springer         replacementValues[returnValIdx] = *bufferOrFailure;
281*e07a7fd5SMatthias Springer         newOperands[*bbArgIdx] = *bufferOrFailure;
282*e07a7fd5SMatthias Springer         continue;
283*e07a7fd5SMatthias Springer       }
284*e07a7fd5SMatthias Springer 
285*e07a7fd5SMatthias Springer       if (!options.allowReturnAllocs)
286*e07a7fd5SMatthias Springer         return callOp->emitError(
287*e07a7fd5SMatthias Springer             "call to FuncOp that returns non-equivalent tensors not supported");
288*e07a7fd5SMatthias Springer 
289*e07a7fd5SMatthias Springer       // Returning a memref. This memref is not equivalent to any bbArg. It is
290*e07a7fd5SMatthias Springer       // likely a newly allocated buffer. We may want to hoist such allocations
291*e07a7fd5SMatthias Springer       // to the call site in the future.
292*e07a7fd5SMatthias Springer       retValMapping[returnValIdx] = resultTypes.size();
293*e07a7fd5SMatthias Springer       resultTypes.push_back(funcType.getResult(resultTypes.size()));
294*e07a7fd5SMatthias Springer     }
295*e07a7fd5SMatthias Springer 
296*e07a7fd5SMatthias Springer     // 2. Rewrite tensor operands as memrefs based on `bufferizedFuncType`.
297*e07a7fd5SMatthias Springer     for (OpOperand &opOperand : callOp->getOpOperands()) {
298*e07a7fd5SMatthias Springer       unsigned idx = opOperand.getOperandNumber();
299*e07a7fd5SMatthias Springer       Value tensorOperand = opOperand.get();
300*e07a7fd5SMatthias Springer 
301*e07a7fd5SMatthias Springer       // Non-tensor operands are just copied.
302*e07a7fd5SMatthias Springer       if (!tensorOperand.getType().isa<TensorType>()) {
303*e07a7fd5SMatthias Springer         newOperands[idx] = tensorOperand;
304*e07a7fd5SMatthias Springer         continue;
305*e07a7fd5SMatthias Springer       }
306*e07a7fd5SMatthias Springer 
307*e07a7fd5SMatthias Springer       // Retrieve buffers for tensor operands. Tensor operand buffers, who's
308*e07a7fd5SMatthias Springer       // corresponding FuncOp bbArgs are equivalent to a returned tensor, were
309*e07a7fd5SMatthias Springer       // already stored in `newOperands` during Step 1.
310*e07a7fd5SMatthias Springer       Value buffer = newOperands[idx];
311*e07a7fd5SMatthias Springer       if (!buffer) {
312*e07a7fd5SMatthias Springer         FailureOr<Value> bufferOrFailure = state.getBuffer(rewriter, opOperand);
313*e07a7fd5SMatthias Springer         if (failed(bufferOrFailure))
314*e07a7fd5SMatthias Springer           return failure();
315*e07a7fd5SMatthias Springer         buffer = *bufferOrFailure;
316*e07a7fd5SMatthias Springer       }
317*e07a7fd5SMatthias Springer 
318*e07a7fd5SMatthias Springer       // Caller / callee type mismatch is handled with a CastOp.
319*e07a7fd5SMatthias Springer       auto memRefType = funcType.getInput(idx);
320*e07a7fd5SMatthias Springer       // Since we don't yet have a clear layout story, to_memref may
321*e07a7fd5SMatthias Springer       // conservatively turn tensors into more dynamic memref than necessary.
322*e07a7fd5SMatthias Springer       // If the memref type of the callee fails, introduce an extra memref.cast
323*e07a7fd5SMatthias Springer       // that will either canonicalize away or fail compilation until we can do
324*e07a7fd5SMatthias Springer       // something better.
325*e07a7fd5SMatthias Springer       if (buffer.getType() != memRefType) {
326*e07a7fd5SMatthias Springer         assert(
327*e07a7fd5SMatthias Springer             memref::CastOp::areCastCompatible(buffer.getType(), memRefType) &&
328*e07a7fd5SMatthias Springer             "CallOp::bufferize: cast incompatible");
329*e07a7fd5SMatthias Springer         Value castBuffer = rewriter.create<memref::CastOp>(callOp.getLoc(),
330*e07a7fd5SMatthias Springer                                                            memRefType, buffer);
331*e07a7fd5SMatthias Springer         buffer = castBuffer;
332*e07a7fd5SMatthias Springer       }
333*e07a7fd5SMatthias Springer       newOperands[idx] = buffer;
334*e07a7fd5SMatthias Springer     }
335*e07a7fd5SMatthias Springer 
336*e07a7fd5SMatthias Springer     // 3. Create the new CallOp.
337*e07a7fd5SMatthias Springer     Operation *newCallOp = rewriter.create<func::CallOp>(
338*e07a7fd5SMatthias Springer         callOp.getLoc(), funcOp.getSymName(), resultTypes, newOperands);
339*e07a7fd5SMatthias Springer     newCallOp->setAttrs(callOp->getAttrs());
340*e07a7fd5SMatthias Springer     // Get replacement values for non-tensor / non-equivalent results.
341*e07a7fd5SMatthias Springer     for (unsigned i = 0; i < replacementValues.size(); ++i) {
342*e07a7fd5SMatthias Springer       if (replacementValues[i])
343*e07a7fd5SMatthias Springer         continue;
344*e07a7fd5SMatthias Springer       replacementValues[i] = newCallOp->getResult(*retValMapping[i]);
345*e07a7fd5SMatthias Springer     }
346*e07a7fd5SMatthias Springer 
347*e07a7fd5SMatthias Springer     // 4. Replace the old op with the new op.
348*e07a7fd5SMatthias Springer     replaceOpWithBufferizedValues(rewriter, callOp, replacementValues);
349*e07a7fd5SMatthias Springer 
350*e07a7fd5SMatthias Springer     return success();
351*e07a7fd5SMatthias Springer   }
352*e07a7fd5SMatthias Springer };
353*e07a7fd5SMatthias Springer 
354*e07a7fd5SMatthias Springer struct ReturnOpInterface
355*e07a7fd5SMatthias Springer     : public BufferizableOpInterface::ExternalModel<ReturnOpInterface,
356*e07a7fd5SMatthias Springer                                                     func::ReturnOp> {
357*e07a7fd5SMatthias Springer   bool bufferizesToMemoryRead(Operation *op, OpOperand &opOperand,
358*e07a7fd5SMatthias Springer                               const AnalysisState &state) const {
359*e07a7fd5SMatthias Springer     return true;
360*e07a7fd5SMatthias Springer   }
361*e07a7fd5SMatthias Springer 
362*e07a7fd5SMatthias Springer   bool bufferizesToMemoryWrite(Operation *op, OpOperand &opOperand,
363*e07a7fd5SMatthias Springer                                const AnalysisState &state) const {
364*e07a7fd5SMatthias Springer     return false;
365*e07a7fd5SMatthias Springer   }
366*e07a7fd5SMatthias Springer 
367*e07a7fd5SMatthias Springer   SmallVector<OpResult> getAliasingOpResult(Operation *op, OpOperand &opOperand,
368*e07a7fd5SMatthias Springer                                             const AnalysisState &state) const {
369*e07a7fd5SMatthias Springer     return {};
370*e07a7fd5SMatthias Springer   }
371*e07a7fd5SMatthias Springer 
372*e07a7fd5SMatthias Springer   LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
373*e07a7fd5SMatthias Springer                           BufferizationState &state) const {
374*e07a7fd5SMatthias Springer #ifndef NDEBUG
375*e07a7fd5SMatthias Springer     auto returnOp = cast<func::ReturnOp>(op);
376*e07a7fd5SMatthias Springer     assert(isa<FuncOp>(returnOp->getParentOp()) &&
377*e07a7fd5SMatthias Springer            "only support FuncOp parent for ReturnOp");
378*e07a7fd5SMatthias Springer #endif // NDEBUG
379*e07a7fd5SMatthias Springer 
380*e07a7fd5SMatthias Springer     // ReturnOps are bufferized as part of FuncOps.
381*e07a7fd5SMatthias Springer     return failure();
382*e07a7fd5SMatthias Springer   }
383*e07a7fd5SMatthias Springer };
384*e07a7fd5SMatthias Springer 
385*e07a7fd5SMatthias Springer struct FuncOpInterface
386*e07a7fd5SMatthias Springer     : public BufferizableOpInterface::ExternalModel<FuncOpInterface, FuncOp> {
387*e07a7fd5SMatthias Springer   /// Rewrite function bbArgs and return values into buffer form (using the
388*e07a7fd5SMatthias Springer   /// canonical memref layout for now). This function bufferizes the function
389*e07a7fd5SMatthias Springer   /// signature and the ReturnOp. When the entire function body has been
390*e07a7fd5SMatthias Springer   /// bufferized, function return types can be switched to more concise memref
391*e07a7fd5SMatthias Springer   /// types as part of `foldMemRefCasts`.
392*e07a7fd5SMatthias Springer   ///
393*e07a7fd5SMatthias Springer   /// When a tensor function argument is known to be equivalent to a tensor
394*e07a7fd5SMatthias Springer   /// result, it is dropped from the return values.
395*e07a7fd5SMatthias Springer   ///
396*e07a7fd5SMatthias Springer   /// All function bbArgs are writable unless they are explicitly marked as
397*e07a7fd5SMatthias Springer   /// read-only. Callers must insert copies when needed.
398*e07a7fd5SMatthias Springer   ///
399*e07a7fd5SMatthias Springer   /// Note: Returning a memref is possible, but corresponding CallOp
400*e07a7fd5SMatthias Springer   /// bufferizations fail unless `allowReturnAllocs`.
401*e07a7fd5SMatthias Springer   LogicalResult bufferize(Operation *op, RewriterBase &rewriter,
402*e07a7fd5SMatthias Springer                           BufferizationState &state) const {
403*e07a7fd5SMatthias Springer     auto funcOp = cast<FuncOp>(op);
404*e07a7fd5SMatthias Springer     FunctionType funcType = funcOp.getFunctionType();
405*e07a7fd5SMatthias Springer     const FuncAnalysisState &funcState =
406*e07a7fd5SMatthias Springer         getFuncAnalysisState(state.getAnalysisState());
407*e07a7fd5SMatthias Springer     const BufferizationOptions &options = state.getOptions();
408*e07a7fd5SMatthias Springer 
409*e07a7fd5SMatthias Springer     // Construct the bufferized function type.
410*e07a7fd5SMatthias Springer     SmallVector<Type> argTypes;
411*e07a7fd5SMatthias Springer     for (const auto &it : llvm::enumerate(funcType.getInputs())) {
412*e07a7fd5SMatthias Springer       Type argType = it.value();
413*e07a7fd5SMatthias Springer       if (auto tensorType = argType.dyn_cast<TensorType>()) {
414*e07a7fd5SMatthias Springer         argTypes.push_back(
415*e07a7fd5SMatthias Springer             getBufferizedFunctionArgType(funcOp, it.index(), options));
416*e07a7fd5SMatthias Springer         continue;
417*e07a7fd5SMatthias Springer       }
418*e07a7fd5SMatthias Springer       argTypes.push_back(argType);
419*e07a7fd5SMatthias Springer     }
420*e07a7fd5SMatthias Springer 
421*e07a7fd5SMatthias Springer     // Bodiless functions are assumed opaque and we cannot know the
422*e07a7fd5SMatthias Springer     // bufferization contract they want to enforce. As a consequence, only
423*e07a7fd5SMatthias Springer     // support functions that don't return any tensors atm.
424*e07a7fd5SMatthias Springer     if (funcOp.getBody().empty()) {
425*e07a7fd5SMatthias Springer       SmallVector<Type> retTypes;
426*e07a7fd5SMatthias Springer       for (Type resultType : funcType.getResults()) {
427*e07a7fd5SMatthias Springer         if (resultType.isa<TensorType>())
428*e07a7fd5SMatthias Springer           return funcOp->emitError() << "cannot bufferize bodiless function "
429*e07a7fd5SMatthias Springer                                      << "that returns a tensor";
430*e07a7fd5SMatthias Springer         retTypes.push_back(resultType);
431*e07a7fd5SMatthias Springer       }
432*e07a7fd5SMatthias Springer       funcOp.setType(FunctionType::get(op->getContext(), argTypes, retTypes));
433*e07a7fd5SMatthias Springer       return success();
434*e07a7fd5SMatthias Springer     }
435*e07a7fd5SMatthias Springer 
436*e07a7fd5SMatthias Springer     // TODO: Support functions with multiple returns.
437*e07a7fd5SMatthias Springer     func::ReturnOp returnOp = getAssumedUniqueReturnOp(funcOp);
438*e07a7fd5SMatthias Springer     assert(returnOp && "expected func with single return op");
439*e07a7fd5SMatthias Springer 
440*e07a7fd5SMatthias Springer     // 1. Rewrite the bbArgs. Turn every tensor bbArg into a memref bbArg.
441*e07a7fd5SMatthias Springer     Block &frontBlock = funcOp.getBody().front();
442*e07a7fd5SMatthias Springer     for (BlockArgument &bbArg : frontBlock.getArguments()) {
443*e07a7fd5SMatthias Springer       auto tensorType = bbArg.getType().dyn_cast<TensorType>();
444*e07a7fd5SMatthias Springer       // Non-tensor types stay the same.
445*e07a7fd5SMatthias Springer       if (!tensorType)
446*e07a7fd5SMatthias Springer         continue;
447*e07a7fd5SMatthias Springer 
448*e07a7fd5SMatthias Springer       // Collect all uses of the bbArg.
449*e07a7fd5SMatthias Springer       SmallVector<OpOperand *> bbArgUses;
450*e07a7fd5SMatthias Springer       for (OpOperand &use : bbArg.getUses())
451*e07a7fd5SMatthias Springer         bbArgUses.push_back(&use);
452*e07a7fd5SMatthias Springer 
453*e07a7fd5SMatthias Springer       // Change the bbArg type to memref.
454*e07a7fd5SMatthias Springer       Type memrefType =
455*e07a7fd5SMatthias Springer           getBufferizedFunctionArgType(funcOp, bbArg.getArgNumber(), options);
456*e07a7fd5SMatthias Springer       bbArg.setType(memrefType);
457*e07a7fd5SMatthias Springer 
458*e07a7fd5SMatthias Springer       // Replace all uses of the original tensor bbArg.
459*e07a7fd5SMatthias Springer       rewriter.setInsertionPointToStart(&frontBlock);
460*e07a7fd5SMatthias Springer       if (!bbArgUses.empty()) {
461*e07a7fd5SMatthias Springer         // Insert to_tensor because the remaining function body has not been
462*e07a7fd5SMatthias Springer         // bufferized yet.
463*e07a7fd5SMatthias Springer         Value toTensorOp =
464*e07a7fd5SMatthias Springer             rewriter.create<bufferization::ToTensorOp>(funcOp.getLoc(), bbArg);
465*e07a7fd5SMatthias Springer         for (OpOperand *use : bbArgUses)
466*e07a7fd5SMatthias Springer           use->set(toTensorOp);
467*e07a7fd5SMatthias Springer       }
468*e07a7fd5SMatthias Springer     }
469*e07a7fd5SMatthias Springer 
470*e07a7fd5SMatthias Springer     // 2. For each result, keep track of which inplace argument it reuses.
471*e07a7fd5SMatthias Springer     SmallVector<Value> returnValues;
472*e07a7fd5SMatthias Springer     for (OpOperand &returnOperand : returnOp->getOpOperands()) {
473*e07a7fd5SMatthias Springer       Value returnVal = returnOperand.get();
474*e07a7fd5SMatthias Springer 
475*e07a7fd5SMatthias Springer       // If not a tensor type just forward it.
476*e07a7fd5SMatthias Springer       if (!returnVal.getType().isa<RankedTensorType>()) {
477*e07a7fd5SMatthias Springer         returnValues.push_back(returnVal);
478*e07a7fd5SMatthias Springer         continue;
479*e07a7fd5SMatthias Springer       }
480*e07a7fd5SMatthias Springer 
481*e07a7fd5SMatthias Springer       // If return operand is equivalent to some bbArg, no need to return it.
482*e07a7fd5SMatthias Springer       if (Optional<int64_t> equivBbArgIdx = getEquivalentFuncArgIdx(
483*e07a7fd5SMatthias Springer               funcOp, funcState, returnOperand.getOperandNumber())) {
484*e07a7fd5SMatthias Springer         rewriter.setInsertionPoint(returnOp);
485*e07a7fd5SMatthias Springer         Location loc = returnOp.getLoc();
486*e07a7fd5SMatthias Springer         Value toMemrefOp = rewriter.create<bufferization::ToMemrefOp>(
487*e07a7fd5SMatthias Springer             loc, getMemRefType(returnVal.getType().cast<TensorType>(), options),
488*e07a7fd5SMatthias Springer             returnVal);
489*e07a7fd5SMatthias Springer         BlockArgument equivBbArg = funcOp.getArgument(*equivBbArgIdx);
490*e07a7fd5SMatthias Springer         // Note: This copy will fold away. It must be inserted here to ensure
491*e07a7fd5SMatthias Springer         // that `returnVal` still has at least one use and does not fold away.
492*e07a7fd5SMatthias Springer         if (failed(
493*e07a7fd5SMatthias Springer                 createMemCpy(rewriter, loc, toMemrefOp, equivBbArg, options)))
494*e07a7fd5SMatthias Springer           return funcOp->emitError("could not generate copy for bbArg");
495*e07a7fd5SMatthias Springer         continue;
496*e07a7fd5SMatthias Springer       }
497*e07a7fd5SMatthias Springer 
498*e07a7fd5SMatthias Springer       returnValues.push_back(*state.getBuffer(rewriter, returnOperand));
499*e07a7fd5SMatthias Springer     }
500*e07a7fd5SMatthias Springer 
501*e07a7fd5SMatthias Springer     // 3. Rewrite the terminator without the in-place bufferizable values.
502*e07a7fd5SMatthias Springer     returnOp.operandsMutable().assign(returnValues);
503*e07a7fd5SMatthias Springer 
504*e07a7fd5SMatthias Springer     // 4. Rewrite the FuncOp type to buffer form.
505*e07a7fd5SMatthias Springer     funcOp.setType(FunctionType::get(op->getContext(), argTypes,
506*e07a7fd5SMatthias Springer                                      ValueRange(returnValues).getTypes()));
507*e07a7fd5SMatthias Springer 
508*e07a7fd5SMatthias Springer     return success();
509*e07a7fd5SMatthias Springer   }
510*e07a7fd5SMatthias Springer 
511*e07a7fd5SMatthias Springer   /// Return `true` if the given function argument is writable.
512*e07a7fd5SMatthias Springer   bool isWritable(Operation *op, Value value,
513*e07a7fd5SMatthias Springer                   const AnalysisState &state) const {
514*e07a7fd5SMatthias Springer     auto funcOp = cast<FuncOp>(op);
515*e07a7fd5SMatthias Springer     BlockArgument bbArg = value.dyn_cast<BlockArgument>();
516*e07a7fd5SMatthias Springer     assert(bbArg && "expected BlockArgument");
517*e07a7fd5SMatthias Springer 
518*e07a7fd5SMatthias Springer     // "bufferization.writable" overrides other writability decisions. This is
519*e07a7fd5SMatthias Springer     // currently used for testing only.
520*e07a7fd5SMatthias Springer     if (BoolAttr writable = funcOp.getArgAttrOfType<BoolAttr>(
521*e07a7fd5SMatthias Springer             bbArg.getArgNumber(), BufferizationDialect::kWritableAttrName))
522*e07a7fd5SMatthias Springer       return writable.getValue();
523*e07a7fd5SMatthias Springer 
524*e07a7fd5SMatthias Springer     // All function arguments are writable by default.
525*e07a7fd5SMatthias Springer     return true;
526*e07a7fd5SMatthias Springer   }
527*e07a7fd5SMatthias Springer 
528*e07a7fd5SMatthias Springer   bool isAllocationHoistingBarrier(Operation *op) const { return true; }
529*e07a7fd5SMatthias Springer };
530*e07a7fd5SMatthias Springer 
531*e07a7fd5SMatthias Springer } // namespace func_ext
532*e07a7fd5SMatthias Springer } // namespace bufferization
533*e07a7fd5SMatthias Springer } // namespace mlir
534*e07a7fd5SMatthias Springer 
535*e07a7fd5SMatthias Springer void mlir::bufferization::func_ext::
536*e07a7fd5SMatthias Springer     registerBufferizableOpInterfaceExternalModels(DialectRegistry &registry) {
537*e07a7fd5SMatthias Springer   registry.addExtension(+[](MLIRContext *ctx, func::FuncDialect *dialect) {
538*e07a7fd5SMatthias Springer     func::CallOp::attachInterface<func_ext::CallOpInterface>(*ctx);
539*e07a7fd5SMatthias Springer     func::FuncOp::attachInterface<func_ext::FuncOpInterface>(*ctx);
540*e07a7fd5SMatthias Springer     func::ReturnOp::attachInterface<func_ext::ReturnOpInterface>(*ctx);
541*e07a7fd5SMatthias Springer   });
542*e07a7fd5SMatthias Springer }
543