10e9a4a3bSRiver Riddle //===- BufferResultsToOutParams.cpp - Calling convention conversion -------===//
20e9a4a3bSRiver Riddle //
30e9a4a3bSRiver Riddle // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40e9a4a3bSRiver Riddle // See https://llvm.org/LICENSE.txt for license information.
50e9a4a3bSRiver Riddle // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60e9a4a3bSRiver Riddle //
70e9a4a3bSRiver Riddle //===----------------------------------------------------------------------===//
80e9a4a3bSRiver Riddle 
90e9a4a3bSRiver Riddle #include "PassDetail.h"
100e9a4a3bSRiver Riddle #include "mlir/Dialect/Bufferization/Transforms/Passes.h"
110e9a4a3bSRiver Riddle #include "mlir/Dialect/MemRef/IR/MemRef.h"
120e9a4a3bSRiver Riddle #include "mlir/Dialect/StandardOps/IR/Ops.h"
130e9a4a3bSRiver Riddle #include "mlir/IR/Operation.h"
140e9a4a3bSRiver Riddle #include "mlir/Pass/Pass.h"
150e9a4a3bSRiver Riddle 
160e9a4a3bSRiver Riddle using namespace mlir;
170e9a4a3bSRiver Riddle 
180e9a4a3bSRiver Riddle // Updates the func op and entry block.
190e9a4a3bSRiver Riddle //
200e9a4a3bSRiver Riddle // Any args appended to the entry block are added to `appendedEntryArgs`.
210e9a4a3bSRiver Riddle static void updateFuncOp(FuncOp func,
220e9a4a3bSRiver Riddle                          SmallVectorImpl<BlockArgument> &appendedEntryArgs) {
230e9a4a3bSRiver Riddle   auto functionType = func.getType();
240e9a4a3bSRiver Riddle 
250e9a4a3bSRiver Riddle   // Collect information about the results will become appended arguments.
260e9a4a3bSRiver Riddle   SmallVector<Type, 6> erasedResultTypes;
27*e3cd80eaSRiver Riddle   llvm::BitVector erasedResultIndices(functionType.getNumResults());
280e9a4a3bSRiver Riddle   for (const auto &resultType : llvm::enumerate(functionType.getResults())) {
290e9a4a3bSRiver Riddle     if (resultType.value().isa<BaseMemRefType>()) {
30*e3cd80eaSRiver Riddle       erasedResultIndices.set(resultType.index());
310e9a4a3bSRiver Riddle       erasedResultTypes.push_back(resultType.value());
320e9a4a3bSRiver Riddle     }
330e9a4a3bSRiver Riddle   }
340e9a4a3bSRiver Riddle 
350e9a4a3bSRiver Riddle   // Add the new arguments to the function type.
360e9a4a3bSRiver Riddle   auto newArgTypes = llvm::to_vector<6>(
370e9a4a3bSRiver Riddle       llvm::concat<const Type>(functionType.getInputs(), erasedResultTypes));
380e9a4a3bSRiver Riddle   auto newFunctionType = FunctionType::get(func.getContext(), newArgTypes,
390e9a4a3bSRiver Riddle                                            functionType.getResults());
400e9a4a3bSRiver Riddle   func.setType(newFunctionType);
410e9a4a3bSRiver Riddle 
420e9a4a3bSRiver Riddle   // Transfer the result attributes to arg attributes.
43*e3cd80eaSRiver Riddle   auto erasedIndicesIt = erasedResultIndices.set_bits_begin();
44*e3cd80eaSRiver Riddle   for (int i = 0, e = erasedResultTypes.size(); i < e; ++i, ++erasedIndicesIt) {
450e9a4a3bSRiver Riddle     func.setArgAttrs(functionType.getNumInputs() + i,
46*e3cd80eaSRiver Riddle                      func.getResultAttrs(*erasedIndicesIt));
47*e3cd80eaSRiver Riddle   }
480e9a4a3bSRiver Riddle 
490e9a4a3bSRiver Riddle   // Erase the results.
500e9a4a3bSRiver Riddle   func.eraseResults(erasedResultIndices);
510e9a4a3bSRiver Riddle 
520e9a4a3bSRiver Riddle   // Add the new arguments to the entry block if the function is not external.
530e9a4a3bSRiver Riddle   if (func.isExternal())
540e9a4a3bSRiver Riddle     return;
550e9a4a3bSRiver Riddle   Location loc = func.getLoc();
560e9a4a3bSRiver Riddle   for (Type type : erasedResultTypes)
570e9a4a3bSRiver Riddle     appendedEntryArgs.push_back(func.front().addArgument(type, loc));
580e9a4a3bSRiver Riddle }
590e9a4a3bSRiver Riddle 
600e9a4a3bSRiver Riddle // Updates all ReturnOps in the scope of the given FuncOp by either keeping them
610e9a4a3bSRiver Riddle // as return values or copying the associated buffer contents into the given
620e9a4a3bSRiver Riddle // out-params.
630e9a4a3bSRiver Riddle static void updateReturnOps(FuncOp func,
640e9a4a3bSRiver Riddle                             ArrayRef<BlockArgument> appendedEntryArgs) {
650e9a4a3bSRiver Riddle   func.walk([&](ReturnOp op) {
660e9a4a3bSRiver Riddle     SmallVector<Value, 6> copyIntoOutParams;
670e9a4a3bSRiver Riddle     SmallVector<Value, 6> keepAsReturnOperands;
680e9a4a3bSRiver Riddle     for (Value operand : op.getOperands()) {
690e9a4a3bSRiver Riddle       if (operand.getType().isa<BaseMemRefType>())
700e9a4a3bSRiver Riddle         copyIntoOutParams.push_back(operand);
710e9a4a3bSRiver Riddle       else
720e9a4a3bSRiver Riddle         keepAsReturnOperands.push_back(operand);
730e9a4a3bSRiver Riddle     }
740e9a4a3bSRiver Riddle     OpBuilder builder(op);
750e9a4a3bSRiver Riddle     for (auto t : llvm::zip(copyIntoOutParams, appendedEntryArgs))
760e9a4a3bSRiver Riddle       builder.create<memref::CopyOp>(op.getLoc(), std::get<0>(t),
770e9a4a3bSRiver Riddle                                      std::get<1>(t));
780e9a4a3bSRiver Riddle     builder.create<ReturnOp>(op.getLoc(), keepAsReturnOperands);
790e9a4a3bSRiver Riddle     op.erase();
800e9a4a3bSRiver Riddle   });
810e9a4a3bSRiver Riddle }
820e9a4a3bSRiver Riddle 
830e9a4a3bSRiver Riddle // Updates all CallOps in the scope of the given ModuleOp by allocating
840e9a4a3bSRiver Riddle // temporary buffers for newly introduced out params.
850e9a4a3bSRiver Riddle static LogicalResult updateCalls(ModuleOp module) {
860e9a4a3bSRiver Riddle   bool didFail = false;
870e9a4a3bSRiver Riddle   module.walk([&](CallOp op) {
880e9a4a3bSRiver Riddle     SmallVector<Value, 6> replaceWithNewCallResults;
890e9a4a3bSRiver Riddle     SmallVector<Value, 6> replaceWithOutParams;
900e9a4a3bSRiver Riddle     for (OpResult result : op.getResults()) {
910e9a4a3bSRiver Riddle       if (result.getType().isa<BaseMemRefType>())
920e9a4a3bSRiver Riddle         replaceWithOutParams.push_back(result);
930e9a4a3bSRiver Riddle       else
940e9a4a3bSRiver Riddle         replaceWithNewCallResults.push_back(result);
950e9a4a3bSRiver Riddle     }
960e9a4a3bSRiver Riddle     SmallVector<Value, 6> outParams;
970e9a4a3bSRiver Riddle     OpBuilder builder(op);
980e9a4a3bSRiver Riddle     for (Value memref : replaceWithOutParams) {
990e9a4a3bSRiver Riddle       if (!memref.getType().cast<BaseMemRefType>().hasStaticShape()) {
1000e9a4a3bSRiver Riddle         op.emitError()
1010e9a4a3bSRiver Riddle             << "cannot create out param for dynamically shaped result";
1020e9a4a3bSRiver Riddle         didFail = true;
1030e9a4a3bSRiver Riddle         return;
1040e9a4a3bSRiver Riddle       }
1050e9a4a3bSRiver Riddle       Value outParam = builder.create<memref::AllocOp>(
1060e9a4a3bSRiver Riddle           op.getLoc(), memref.getType().cast<MemRefType>());
1070e9a4a3bSRiver Riddle       memref.replaceAllUsesWith(outParam);
1080e9a4a3bSRiver Riddle       outParams.push_back(outParam);
1090e9a4a3bSRiver Riddle     }
1100e9a4a3bSRiver Riddle 
1110e9a4a3bSRiver Riddle     auto newOperands = llvm::to_vector<6>(op.getOperands());
1120e9a4a3bSRiver Riddle     newOperands.append(outParams.begin(), outParams.end());
1130e9a4a3bSRiver Riddle     auto newResultTypes = llvm::to_vector<6>(llvm::map_range(
1140e9a4a3bSRiver Riddle         replaceWithNewCallResults, [](Value v) { return v.getType(); }));
1150e9a4a3bSRiver Riddle     auto newCall = builder.create<CallOp>(op.getLoc(), op.getCalleeAttr(),
1160e9a4a3bSRiver Riddle                                           newResultTypes, newOperands);
1170e9a4a3bSRiver Riddle     for (auto t : llvm::zip(replaceWithNewCallResults, newCall.getResults()))
1180e9a4a3bSRiver Riddle       std::get<0>(t).replaceAllUsesWith(std::get<1>(t));
1190e9a4a3bSRiver Riddle     op.erase();
1200e9a4a3bSRiver Riddle   });
1210e9a4a3bSRiver Riddle 
1220e9a4a3bSRiver Riddle   return failure(didFail);
1230e9a4a3bSRiver Riddle }
1240e9a4a3bSRiver Riddle 
1250e9a4a3bSRiver Riddle namespace {
1260e9a4a3bSRiver Riddle struct BufferResultsToOutParamsPass
1270e9a4a3bSRiver Riddle     : BufferResultsToOutParamsBase<BufferResultsToOutParamsPass> {
1280e9a4a3bSRiver Riddle   void runOnOperation() override {
1290e9a4a3bSRiver Riddle     ModuleOp module = getOperation();
1300e9a4a3bSRiver Riddle 
1310e9a4a3bSRiver Riddle     for (auto func : module.getOps<FuncOp>()) {
1320e9a4a3bSRiver Riddle       SmallVector<BlockArgument, 6> appendedEntryArgs;
1330e9a4a3bSRiver Riddle       updateFuncOp(func, appendedEntryArgs);
1340e9a4a3bSRiver Riddle       if (func.isExternal())
1350e9a4a3bSRiver Riddle         continue;
1360e9a4a3bSRiver Riddle       updateReturnOps(func, appendedEntryArgs);
1370e9a4a3bSRiver Riddle     }
1380e9a4a3bSRiver Riddle     if (failed(updateCalls(module)))
1390e9a4a3bSRiver Riddle       return signalPassFailure();
1400e9a4a3bSRiver Riddle   }
1410e9a4a3bSRiver Riddle };
1420e9a4a3bSRiver Riddle } // namespace
1430e9a4a3bSRiver Riddle 
1440e9a4a3bSRiver Riddle std::unique_ptr<Pass>
1450e9a4a3bSRiver Riddle mlir::bufferization::createBufferResultsToOutParamsPass() {
1460e9a4a3bSRiver Riddle   return std::make_unique<BufferResultsToOutParamsPass>();
1470e9a4a3bSRiver Riddle }
148