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"
1123aa5a74SRiver Riddle #include "mlir/Dialect/Func/IR/FuncOps.h"
120e9a4a3bSRiver Riddle #include "mlir/Dialect/MemRef/IR/MemRef.h"
130e9a4a3bSRiver Riddle #include "mlir/IR/Operation.h"
140e9a4a3bSRiver Riddle #include "mlir/Pass/Pass.h"
150e9a4a3bSRiver Riddle
160e9a4a3bSRiver Riddle using namespace mlir;
170e9a4a3bSRiver Riddle
18598c5ddbSMatthias Springer /// Return `true` if the given MemRef type has a fully dynamic layout.
hasFullyDynamicLayoutMap(MemRefType type)19598c5ddbSMatthias Springer static bool hasFullyDynamicLayoutMap(MemRefType type) {
20598c5ddbSMatthias Springer int64_t offset;
21598c5ddbSMatthias Springer SmallVector<int64_t, 4> strides;
22598c5ddbSMatthias Springer if (failed(getStridesAndOffset(type, strides, offset)))
23598c5ddbSMatthias Springer return false;
24*380a1b20SKazu Hirata if (!llvm::all_of(strides, ShapedType::isDynamicStrideOrOffset))
25598c5ddbSMatthias Springer return false;
26598c5ddbSMatthias Springer if (!ShapedType::isDynamicStrideOrOffset(offset))
27598c5ddbSMatthias Springer return false;
28598c5ddbSMatthias Springer return true;
29598c5ddbSMatthias Springer }
30598c5ddbSMatthias Springer
31598c5ddbSMatthias Springer /// Return `true` if the given MemRef type has a static identity layout (i.e.,
32598c5ddbSMatthias Springer /// no layout).
hasStaticIdentityLayout(MemRefType type)33598c5ddbSMatthias Springer static bool hasStaticIdentityLayout(MemRefType type) {
34598c5ddbSMatthias Springer return type.getLayout().isIdentity();
35598c5ddbSMatthias Springer }
36598c5ddbSMatthias Springer
370e9a4a3bSRiver Riddle // Updates the func op and entry block.
380e9a4a3bSRiver Riddle //
390e9a4a3bSRiver Riddle // Any args appended to the entry block are added to `appendedEntryArgs`.
40598c5ddbSMatthias Springer static LogicalResult
updateFuncOp(func::FuncOp func,SmallVectorImpl<BlockArgument> & appendedEntryArgs)41598c5ddbSMatthias Springer updateFuncOp(func::FuncOp func,
420e9a4a3bSRiver Riddle SmallVectorImpl<BlockArgument> &appendedEntryArgs) {
434a3460a7SRiver Riddle auto functionType = func.getFunctionType();
440e9a4a3bSRiver Riddle
450e9a4a3bSRiver Riddle // Collect information about the results will become appended arguments.
460e9a4a3bSRiver Riddle SmallVector<Type, 6> erasedResultTypes;
47d10d49dcSRiver Riddle BitVector erasedResultIndices(functionType.getNumResults());
480e9a4a3bSRiver Riddle for (const auto &resultType : llvm::enumerate(functionType.getResults())) {
49598c5ddbSMatthias Springer if (auto memrefType = resultType.value().dyn_cast<MemRefType>()) {
50598c5ddbSMatthias Springer if (!hasStaticIdentityLayout(memrefType) &&
51598c5ddbSMatthias Springer !hasFullyDynamicLayoutMap(memrefType)) {
52598c5ddbSMatthias Springer // Only buffers with static identity layout can be allocated. These can
53598c5ddbSMatthias Springer // be casted to memrefs with fully dynamic layout map. Other layout maps
54598c5ddbSMatthias Springer // are not supported.
55598c5ddbSMatthias Springer return func->emitError()
56598c5ddbSMatthias Springer << "cannot create out param for result with unsupported layout";
57598c5ddbSMatthias Springer }
58e3cd80eaSRiver Riddle erasedResultIndices.set(resultType.index());
59598c5ddbSMatthias Springer erasedResultTypes.push_back(memrefType);
600e9a4a3bSRiver Riddle }
610e9a4a3bSRiver Riddle }
620e9a4a3bSRiver Riddle
630e9a4a3bSRiver Riddle // Add the new arguments to the function type.
640e9a4a3bSRiver Riddle auto newArgTypes = llvm::to_vector<6>(
650e9a4a3bSRiver Riddle llvm::concat<const Type>(functionType.getInputs(), erasedResultTypes));
660e9a4a3bSRiver Riddle auto newFunctionType = FunctionType::get(func.getContext(), newArgTypes,
670e9a4a3bSRiver Riddle functionType.getResults());
680e9a4a3bSRiver Riddle func.setType(newFunctionType);
690e9a4a3bSRiver Riddle
700e9a4a3bSRiver Riddle // Transfer the result attributes to arg attributes.
71e3cd80eaSRiver Riddle auto erasedIndicesIt = erasedResultIndices.set_bits_begin();
72e3cd80eaSRiver Riddle for (int i = 0, e = erasedResultTypes.size(); i < e; ++i, ++erasedIndicesIt) {
730e9a4a3bSRiver Riddle func.setArgAttrs(functionType.getNumInputs() + i,
74e3cd80eaSRiver Riddle func.getResultAttrs(*erasedIndicesIt));
75e3cd80eaSRiver Riddle }
760e9a4a3bSRiver Riddle
770e9a4a3bSRiver Riddle // Erase the results.
780e9a4a3bSRiver Riddle func.eraseResults(erasedResultIndices);
790e9a4a3bSRiver Riddle
800e9a4a3bSRiver Riddle // Add the new arguments to the entry block if the function is not external.
810e9a4a3bSRiver Riddle if (func.isExternal())
82598c5ddbSMatthias Springer return success();
830e9a4a3bSRiver Riddle Location loc = func.getLoc();
840e9a4a3bSRiver Riddle for (Type type : erasedResultTypes)
850e9a4a3bSRiver Riddle appendedEntryArgs.push_back(func.front().addArgument(type, loc));
86598c5ddbSMatthias Springer
87598c5ddbSMatthias Springer return success();
880e9a4a3bSRiver Riddle }
890e9a4a3bSRiver Riddle
9058ceae95SRiver Riddle // Updates all ReturnOps in the scope of the given func::FuncOp by either
9158ceae95SRiver Riddle // keeping them as return values or copying the associated buffer contents into
9258ceae95SRiver Riddle // the given out-params.
updateReturnOps(func::FuncOp func,ArrayRef<BlockArgument> appendedEntryArgs)9358ceae95SRiver Riddle static void updateReturnOps(func::FuncOp func,
940e9a4a3bSRiver Riddle ArrayRef<BlockArgument> appendedEntryArgs) {
9523aa5a74SRiver Riddle func.walk([&](func::ReturnOp op) {
960e9a4a3bSRiver Riddle SmallVector<Value, 6> copyIntoOutParams;
970e9a4a3bSRiver Riddle SmallVector<Value, 6> keepAsReturnOperands;
980e9a4a3bSRiver Riddle for (Value operand : op.getOperands()) {
99598c5ddbSMatthias Springer if (operand.getType().isa<MemRefType>())
1000e9a4a3bSRiver Riddle copyIntoOutParams.push_back(operand);
1010e9a4a3bSRiver Riddle else
1020e9a4a3bSRiver Riddle keepAsReturnOperands.push_back(operand);
1030e9a4a3bSRiver Riddle }
1040e9a4a3bSRiver Riddle OpBuilder builder(op);
1050e9a4a3bSRiver Riddle for (auto t : llvm::zip(copyIntoOutParams, appendedEntryArgs))
1060e9a4a3bSRiver Riddle builder.create<memref::CopyOp>(op.getLoc(), std::get<0>(t),
1070e9a4a3bSRiver Riddle std::get<1>(t));
10823aa5a74SRiver Riddle builder.create<func::ReturnOp>(op.getLoc(), keepAsReturnOperands);
1090e9a4a3bSRiver Riddle op.erase();
1100e9a4a3bSRiver Riddle });
1110e9a4a3bSRiver Riddle }
1120e9a4a3bSRiver Riddle
1130e9a4a3bSRiver Riddle // Updates all CallOps in the scope of the given ModuleOp by allocating
1140e9a4a3bSRiver Riddle // temporary buffers for newly introduced out params.
updateCalls(ModuleOp module)1150e9a4a3bSRiver Riddle static LogicalResult updateCalls(ModuleOp module) {
1160e9a4a3bSRiver Riddle bool didFail = false;
11723aa5a74SRiver Riddle module.walk([&](func::CallOp op) {
1180e9a4a3bSRiver Riddle SmallVector<Value, 6> replaceWithNewCallResults;
1190e9a4a3bSRiver Riddle SmallVector<Value, 6> replaceWithOutParams;
1200e9a4a3bSRiver Riddle for (OpResult result : op.getResults()) {
121598c5ddbSMatthias Springer if (result.getType().isa<MemRefType>())
1220e9a4a3bSRiver Riddle replaceWithOutParams.push_back(result);
1230e9a4a3bSRiver Riddle else
1240e9a4a3bSRiver Riddle replaceWithNewCallResults.push_back(result);
1250e9a4a3bSRiver Riddle }
1260e9a4a3bSRiver Riddle SmallVector<Value, 6> outParams;
1270e9a4a3bSRiver Riddle OpBuilder builder(op);
1280e9a4a3bSRiver Riddle for (Value memref : replaceWithOutParams) {
129598c5ddbSMatthias Springer if (!memref.getType().cast<MemRefType>().hasStaticShape()) {
1300e9a4a3bSRiver Riddle op.emitError()
1310e9a4a3bSRiver Riddle << "cannot create out param for dynamically shaped result";
1320e9a4a3bSRiver Riddle didFail = true;
1330e9a4a3bSRiver Riddle return;
1340e9a4a3bSRiver Riddle }
135598c5ddbSMatthias Springer auto memrefType = memref.getType().cast<MemRefType>();
136598c5ddbSMatthias Springer auto allocType =
137598c5ddbSMatthias Springer MemRefType::get(memrefType.getShape(), memrefType.getElementType(),
138598c5ddbSMatthias Springer AffineMap(), memrefType.getMemorySpaceAsInt());
139598c5ddbSMatthias Springer Value outParam = builder.create<memref::AllocOp>(op.getLoc(), allocType);
140598c5ddbSMatthias Springer if (!hasStaticIdentityLayout(memrefType)) {
141598c5ddbSMatthias Springer // Layout maps are already checked in `updateFuncOp`.
142598c5ddbSMatthias Springer assert(hasFullyDynamicLayoutMap(memrefType) &&
143598c5ddbSMatthias Springer "layout map not supported");
144598c5ddbSMatthias Springer outParam =
145598c5ddbSMatthias Springer builder.create<memref::CastOp>(op.getLoc(), memrefType, outParam);
146598c5ddbSMatthias Springer }
1470e9a4a3bSRiver Riddle memref.replaceAllUsesWith(outParam);
1480e9a4a3bSRiver Riddle outParams.push_back(outParam);
1490e9a4a3bSRiver Riddle }
1500e9a4a3bSRiver Riddle
1510e9a4a3bSRiver Riddle auto newOperands = llvm::to_vector<6>(op.getOperands());
1520e9a4a3bSRiver Riddle newOperands.append(outParams.begin(), outParams.end());
1530e9a4a3bSRiver Riddle auto newResultTypes = llvm::to_vector<6>(llvm::map_range(
1540e9a4a3bSRiver Riddle replaceWithNewCallResults, [](Value v) { return v.getType(); }));
15523aa5a74SRiver Riddle auto newCall = builder.create<func::CallOp>(op.getLoc(), op.getCalleeAttr(),
1560e9a4a3bSRiver Riddle newResultTypes, newOperands);
1570e9a4a3bSRiver Riddle for (auto t : llvm::zip(replaceWithNewCallResults, newCall.getResults()))
1580e9a4a3bSRiver Riddle std::get<0>(t).replaceAllUsesWith(std::get<1>(t));
1590e9a4a3bSRiver Riddle op.erase();
1600e9a4a3bSRiver Riddle });
1610e9a4a3bSRiver Riddle
1620e9a4a3bSRiver Riddle return failure(didFail);
1630e9a4a3bSRiver Riddle }
1640e9a4a3bSRiver Riddle
16582ea0d8bSMatthias Springer LogicalResult
promoteBufferResultsToOutParams(ModuleOp module)16682ea0d8bSMatthias Springer mlir::bufferization::promoteBufferResultsToOutParams(ModuleOp module) {
16758ceae95SRiver Riddle for (auto func : module.getOps<func::FuncOp>()) {
1680e9a4a3bSRiver Riddle SmallVector<BlockArgument, 6> appendedEntryArgs;
169598c5ddbSMatthias Springer if (failed(updateFuncOp(func, appendedEntryArgs)))
170598c5ddbSMatthias Springer return failure();
1710e9a4a3bSRiver Riddle if (func.isExternal())
1720e9a4a3bSRiver Riddle continue;
1730e9a4a3bSRiver Riddle updateReturnOps(func, appendedEntryArgs);
1740e9a4a3bSRiver Riddle }
1750e9a4a3bSRiver Riddle if (failed(updateCalls(module)))
17682ea0d8bSMatthias Springer return failure();
17782ea0d8bSMatthias Springer return success();
17882ea0d8bSMatthias Springer }
17982ea0d8bSMatthias Springer
18082ea0d8bSMatthias Springer namespace {
18182ea0d8bSMatthias Springer struct BufferResultsToOutParamsPass
18282ea0d8bSMatthias Springer : BufferResultsToOutParamsBase<BufferResultsToOutParamsPass> {
runOnOperation__anon6cfd8ace0411::BufferResultsToOutParamsPass18382ea0d8bSMatthias Springer void runOnOperation() override {
18482ea0d8bSMatthias Springer if (failed(bufferization::promoteBufferResultsToOutParams(getOperation())))
1850e9a4a3bSRiver Riddle return signalPassFailure();
1860e9a4a3bSRiver Riddle }
1870e9a4a3bSRiver Riddle };
1880e9a4a3bSRiver Riddle } // namespace
1890e9a4a3bSRiver Riddle
1900e9a4a3bSRiver Riddle std::unique_ptr<Pass>
createBufferResultsToOutParamsPass()1910e9a4a3bSRiver Riddle mlir::bufferization::createBufferResultsToOutParamsPass() {
1920e9a4a3bSRiver Riddle return std::make_unique<BufferResultsToOutParamsPass>();
1930e9a4a3bSRiver Riddle }
194