1*0e9a4a3bSRiver Riddle //===- BufferResultsToOutParams.cpp - Calling convention conversion -------===//
2*0e9a4a3bSRiver Riddle //
3*0e9a4a3bSRiver Riddle // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*0e9a4a3bSRiver Riddle // See https://llvm.org/LICENSE.txt for license information.
5*0e9a4a3bSRiver Riddle // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*0e9a4a3bSRiver Riddle //
7*0e9a4a3bSRiver Riddle //===----------------------------------------------------------------------===//
8*0e9a4a3bSRiver Riddle 
9*0e9a4a3bSRiver Riddle #include "PassDetail.h"
10*0e9a4a3bSRiver Riddle #include "mlir/Dialect/Bufferization/Transforms/Passes.h"
11*0e9a4a3bSRiver Riddle #include "mlir/Dialect/MemRef/IR/MemRef.h"
12*0e9a4a3bSRiver Riddle #include "mlir/Dialect/StandardOps/IR/Ops.h"
13*0e9a4a3bSRiver Riddle #include "mlir/IR/Operation.h"
14*0e9a4a3bSRiver Riddle #include "mlir/Pass/Pass.h"
15*0e9a4a3bSRiver Riddle 
16*0e9a4a3bSRiver Riddle using namespace mlir;
17*0e9a4a3bSRiver Riddle 
18*0e9a4a3bSRiver Riddle // Updates the func op and entry block.
19*0e9a4a3bSRiver Riddle //
20*0e9a4a3bSRiver Riddle // Any args appended to the entry block are added to `appendedEntryArgs`.
21*0e9a4a3bSRiver Riddle static void updateFuncOp(FuncOp func,
22*0e9a4a3bSRiver Riddle                          SmallVectorImpl<BlockArgument> &appendedEntryArgs) {
23*0e9a4a3bSRiver Riddle   auto functionType = func.getType();
24*0e9a4a3bSRiver Riddle 
25*0e9a4a3bSRiver Riddle   // Collect information about the results will become appended arguments.
26*0e9a4a3bSRiver Riddle   SmallVector<Type, 6> erasedResultTypes;
27*0e9a4a3bSRiver Riddle   SmallVector<unsigned, 6> erasedResultIndices;
28*0e9a4a3bSRiver Riddle   for (const auto &resultType : llvm::enumerate(functionType.getResults())) {
29*0e9a4a3bSRiver Riddle     if (resultType.value().isa<BaseMemRefType>()) {
30*0e9a4a3bSRiver Riddle       erasedResultIndices.push_back(resultType.index());
31*0e9a4a3bSRiver Riddle       erasedResultTypes.push_back(resultType.value());
32*0e9a4a3bSRiver Riddle     }
33*0e9a4a3bSRiver Riddle   }
34*0e9a4a3bSRiver Riddle 
35*0e9a4a3bSRiver Riddle   // Add the new arguments to the function type.
36*0e9a4a3bSRiver Riddle   auto newArgTypes = llvm::to_vector<6>(
37*0e9a4a3bSRiver Riddle       llvm::concat<const Type>(functionType.getInputs(), erasedResultTypes));
38*0e9a4a3bSRiver Riddle   auto newFunctionType = FunctionType::get(func.getContext(), newArgTypes,
39*0e9a4a3bSRiver Riddle                                            functionType.getResults());
40*0e9a4a3bSRiver Riddle   func.setType(newFunctionType);
41*0e9a4a3bSRiver Riddle 
42*0e9a4a3bSRiver Riddle   // Transfer the result attributes to arg attributes.
43*0e9a4a3bSRiver Riddle   for (int i = 0, e = erasedResultTypes.size(); i < e; i++)
44*0e9a4a3bSRiver Riddle     func.setArgAttrs(functionType.getNumInputs() + i,
45*0e9a4a3bSRiver Riddle                      func.getResultAttrs(erasedResultIndices[i]));
46*0e9a4a3bSRiver Riddle 
47*0e9a4a3bSRiver Riddle   // Erase the results.
48*0e9a4a3bSRiver Riddle   func.eraseResults(erasedResultIndices);
49*0e9a4a3bSRiver Riddle 
50*0e9a4a3bSRiver Riddle   // Add the new arguments to the entry block if the function is not external.
51*0e9a4a3bSRiver Riddle   if (func.isExternal())
52*0e9a4a3bSRiver Riddle     return;
53*0e9a4a3bSRiver Riddle   Location loc = func.getLoc();
54*0e9a4a3bSRiver Riddle   for (Type type : erasedResultTypes)
55*0e9a4a3bSRiver Riddle     appendedEntryArgs.push_back(func.front().addArgument(type, loc));
56*0e9a4a3bSRiver Riddle }
57*0e9a4a3bSRiver Riddle 
58*0e9a4a3bSRiver Riddle // Updates all ReturnOps in the scope of the given FuncOp by either keeping them
59*0e9a4a3bSRiver Riddle // as return values or copying the associated buffer contents into the given
60*0e9a4a3bSRiver Riddle // out-params.
61*0e9a4a3bSRiver Riddle static void updateReturnOps(FuncOp func,
62*0e9a4a3bSRiver Riddle                             ArrayRef<BlockArgument> appendedEntryArgs) {
63*0e9a4a3bSRiver Riddle   func.walk([&](ReturnOp op) {
64*0e9a4a3bSRiver Riddle     SmallVector<Value, 6> copyIntoOutParams;
65*0e9a4a3bSRiver Riddle     SmallVector<Value, 6> keepAsReturnOperands;
66*0e9a4a3bSRiver Riddle     for (Value operand : op.getOperands()) {
67*0e9a4a3bSRiver Riddle       if (operand.getType().isa<BaseMemRefType>())
68*0e9a4a3bSRiver Riddle         copyIntoOutParams.push_back(operand);
69*0e9a4a3bSRiver Riddle       else
70*0e9a4a3bSRiver Riddle         keepAsReturnOperands.push_back(operand);
71*0e9a4a3bSRiver Riddle     }
72*0e9a4a3bSRiver Riddle     OpBuilder builder(op);
73*0e9a4a3bSRiver Riddle     for (auto t : llvm::zip(copyIntoOutParams, appendedEntryArgs))
74*0e9a4a3bSRiver Riddle       builder.create<memref::CopyOp>(op.getLoc(), std::get<0>(t),
75*0e9a4a3bSRiver Riddle                                      std::get<1>(t));
76*0e9a4a3bSRiver Riddle     builder.create<ReturnOp>(op.getLoc(), keepAsReturnOperands);
77*0e9a4a3bSRiver Riddle     op.erase();
78*0e9a4a3bSRiver Riddle   });
79*0e9a4a3bSRiver Riddle }
80*0e9a4a3bSRiver Riddle 
81*0e9a4a3bSRiver Riddle // Updates all CallOps in the scope of the given ModuleOp by allocating
82*0e9a4a3bSRiver Riddle // temporary buffers for newly introduced out params.
83*0e9a4a3bSRiver Riddle static LogicalResult updateCalls(ModuleOp module) {
84*0e9a4a3bSRiver Riddle   bool didFail = false;
85*0e9a4a3bSRiver Riddle   module.walk([&](CallOp op) {
86*0e9a4a3bSRiver Riddle     SmallVector<Value, 6> replaceWithNewCallResults;
87*0e9a4a3bSRiver Riddle     SmallVector<Value, 6> replaceWithOutParams;
88*0e9a4a3bSRiver Riddle     for (OpResult result : op.getResults()) {
89*0e9a4a3bSRiver Riddle       if (result.getType().isa<BaseMemRefType>())
90*0e9a4a3bSRiver Riddle         replaceWithOutParams.push_back(result);
91*0e9a4a3bSRiver Riddle       else
92*0e9a4a3bSRiver Riddle         replaceWithNewCallResults.push_back(result);
93*0e9a4a3bSRiver Riddle     }
94*0e9a4a3bSRiver Riddle     SmallVector<Value, 6> outParams;
95*0e9a4a3bSRiver Riddle     OpBuilder builder(op);
96*0e9a4a3bSRiver Riddle     for (Value memref : replaceWithOutParams) {
97*0e9a4a3bSRiver Riddle       if (!memref.getType().cast<BaseMemRefType>().hasStaticShape()) {
98*0e9a4a3bSRiver Riddle         op.emitError()
99*0e9a4a3bSRiver Riddle             << "cannot create out param for dynamically shaped result";
100*0e9a4a3bSRiver Riddle         didFail = true;
101*0e9a4a3bSRiver Riddle         return;
102*0e9a4a3bSRiver Riddle       }
103*0e9a4a3bSRiver Riddle       Value outParam = builder.create<memref::AllocOp>(
104*0e9a4a3bSRiver Riddle           op.getLoc(), memref.getType().cast<MemRefType>());
105*0e9a4a3bSRiver Riddle       memref.replaceAllUsesWith(outParam);
106*0e9a4a3bSRiver Riddle       outParams.push_back(outParam);
107*0e9a4a3bSRiver Riddle     }
108*0e9a4a3bSRiver Riddle 
109*0e9a4a3bSRiver Riddle     auto newOperands = llvm::to_vector<6>(op.getOperands());
110*0e9a4a3bSRiver Riddle     newOperands.append(outParams.begin(), outParams.end());
111*0e9a4a3bSRiver Riddle     auto newResultTypes = llvm::to_vector<6>(llvm::map_range(
112*0e9a4a3bSRiver Riddle         replaceWithNewCallResults, [](Value v) { return v.getType(); }));
113*0e9a4a3bSRiver Riddle     auto newCall = builder.create<CallOp>(op.getLoc(), op.getCalleeAttr(),
114*0e9a4a3bSRiver Riddle                                           newResultTypes, newOperands);
115*0e9a4a3bSRiver Riddle     for (auto t : llvm::zip(replaceWithNewCallResults, newCall.getResults()))
116*0e9a4a3bSRiver Riddle       std::get<0>(t).replaceAllUsesWith(std::get<1>(t));
117*0e9a4a3bSRiver Riddle     op.erase();
118*0e9a4a3bSRiver Riddle   });
119*0e9a4a3bSRiver Riddle 
120*0e9a4a3bSRiver Riddle   return failure(didFail);
121*0e9a4a3bSRiver Riddle }
122*0e9a4a3bSRiver Riddle 
123*0e9a4a3bSRiver Riddle namespace {
124*0e9a4a3bSRiver Riddle struct BufferResultsToOutParamsPass
125*0e9a4a3bSRiver Riddle     : BufferResultsToOutParamsBase<BufferResultsToOutParamsPass> {
126*0e9a4a3bSRiver Riddle   void runOnOperation() override {
127*0e9a4a3bSRiver Riddle     ModuleOp module = getOperation();
128*0e9a4a3bSRiver Riddle 
129*0e9a4a3bSRiver Riddle     for (auto func : module.getOps<FuncOp>()) {
130*0e9a4a3bSRiver Riddle       SmallVector<BlockArgument, 6> appendedEntryArgs;
131*0e9a4a3bSRiver Riddle       updateFuncOp(func, appendedEntryArgs);
132*0e9a4a3bSRiver Riddle       if (func.isExternal())
133*0e9a4a3bSRiver Riddle         continue;
134*0e9a4a3bSRiver Riddle       updateReturnOps(func, appendedEntryArgs);
135*0e9a4a3bSRiver Riddle     }
136*0e9a4a3bSRiver Riddle     if (failed(updateCalls(module)))
137*0e9a4a3bSRiver Riddle       return signalPassFailure();
138*0e9a4a3bSRiver Riddle   }
139*0e9a4a3bSRiver Riddle };
140*0e9a4a3bSRiver Riddle } // namespace
141*0e9a4a3bSRiver Riddle 
142*0e9a4a3bSRiver Riddle std::unique_ptr<Pass>
143*0e9a4a3bSRiver Riddle mlir::bufferization::createBufferResultsToOutParamsPass() {
144*0e9a4a3bSRiver Riddle   return std::make_unique<BufferResultsToOutParamsPass>();
145*0e9a4a3bSRiver Riddle }
146