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 
18*598c5ddbSMatthias Springer /// Return `true` if the given MemRef type has a fully dynamic layout.
19*598c5ddbSMatthias Springer static bool hasFullyDynamicLayoutMap(MemRefType type) {
20*598c5ddbSMatthias Springer   int64_t offset;
21*598c5ddbSMatthias Springer   SmallVector<int64_t, 4> strides;
22*598c5ddbSMatthias Springer   if (failed(getStridesAndOffset(type, strides, offset)))
23*598c5ddbSMatthias Springer     return false;
24*598c5ddbSMatthias Springer   if (!llvm::all_of(strides, [](int64_t stride) {
25*598c5ddbSMatthias Springer         return ShapedType::isDynamicStrideOrOffset(stride);
26*598c5ddbSMatthias Springer       }))
27*598c5ddbSMatthias Springer     return false;
28*598c5ddbSMatthias Springer   if (!ShapedType::isDynamicStrideOrOffset(offset))
29*598c5ddbSMatthias Springer     return false;
30*598c5ddbSMatthias Springer   return true;
31*598c5ddbSMatthias Springer }
32*598c5ddbSMatthias Springer 
33*598c5ddbSMatthias Springer /// Return `true` if the given MemRef type has a static identity layout (i.e.,
34*598c5ddbSMatthias Springer /// no layout).
35*598c5ddbSMatthias Springer static bool hasStaticIdentityLayout(MemRefType type) {
36*598c5ddbSMatthias Springer   return type.getLayout().isIdentity();
37*598c5ddbSMatthias Springer }
38*598c5ddbSMatthias Springer 
390e9a4a3bSRiver Riddle // Updates the func op and entry block.
400e9a4a3bSRiver Riddle //
410e9a4a3bSRiver Riddle // Any args appended to the entry block are added to `appendedEntryArgs`.
42*598c5ddbSMatthias Springer static LogicalResult
43*598c5ddbSMatthias Springer updateFuncOp(func::FuncOp func,
440e9a4a3bSRiver Riddle              SmallVectorImpl<BlockArgument> &appendedEntryArgs) {
454a3460a7SRiver Riddle   auto functionType = func.getFunctionType();
460e9a4a3bSRiver Riddle 
470e9a4a3bSRiver Riddle   // Collect information about the results will become appended arguments.
480e9a4a3bSRiver Riddle   SmallVector<Type, 6> erasedResultTypes;
49d10d49dcSRiver Riddle   BitVector erasedResultIndices(functionType.getNumResults());
500e9a4a3bSRiver Riddle   for (const auto &resultType : llvm::enumerate(functionType.getResults())) {
51*598c5ddbSMatthias Springer     if (auto memrefType = resultType.value().dyn_cast<MemRefType>()) {
52*598c5ddbSMatthias Springer       if (!hasStaticIdentityLayout(memrefType) &&
53*598c5ddbSMatthias Springer           !hasFullyDynamicLayoutMap(memrefType)) {
54*598c5ddbSMatthias Springer         // Only buffers with static identity layout can be allocated. These can
55*598c5ddbSMatthias Springer         // be casted to memrefs with fully dynamic layout map. Other layout maps
56*598c5ddbSMatthias Springer         // are not supported.
57*598c5ddbSMatthias Springer         return func->emitError()
58*598c5ddbSMatthias Springer                << "cannot create out param for result with unsupported layout";
59*598c5ddbSMatthias Springer       }
60e3cd80eaSRiver Riddle       erasedResultIndices.set(resultType.index());
61*598c5ddbSMatthias Springer       erasedResultTypes.push_back(memrefType);
620e9a4a3bSRiver Riddle     }
630e9a4a3bSRiver Riddle   }
640e9a4a3bSRiver Riddle 
650e9a4a3bSRiver Riddle   // Add the new arguments to the function type.
660e9a4a3bSRiver Riddle   auto newArgTypes = llvm::to_vector<6>(
670e9a4a3bSRiver Riddle       llvm::concat<const Type>(functionType.getInputs(), erasedResultTypes));
680e9a4a3bSRiver Riddle   auto newFunctionType = FunctionType::get(func.getContext(), newArgTypes,
690e9a4a3bSRiver Riddle                                            functionType.getResults());
700e9a4a3bSRiver Riddle   func.setType(newFunctionType);
710e9a4a3bSRiver Riddle 
720e9a4a3bSRiver Riddle   // Transfer the result attributes to arg attributes.
73e3cd80eaSRiver Riddle   auto erasedIndicesIt = erasedResultIndices.set_bits_begin();
74e3cd80eaSRiver Riddle   for (int i = 0, e = erasedResultTypes.size(); i < e; ++i, ++erasedIndicesIt) {
750e9a4a3bSRiver Riddle     func.setArgAttrs(functionType.getNumInputs() + i,
76e3cd80eaSRiver Riddle                      func.getResultAttrs(*erasedIndicesIt));
77e3cd80eaSRiver Riddle   }
780e9a4a3bSRiver Riddle 
790e9a4a3bSRiver Riddle   // Erase the results.
800e9a4a3bSRiver Riddle   func.eraseResults(erasedResultIndices);
810e9a4a3bSRiver Riddle 
820e9a4a3bSRiver Riddle   // Add the new arguments to the entry block if the function is not external.
830e9a4a3bSRiver Riddle   if (func.isExternal())
84*598c5ddbSMatthias Springer     return success();
850e9a4a3bSRiver Riddle   Location loc = func.getLoc();
860e9a4a3bSRiver Riddle   for (Type type : erasedResultTypes)
870e9a4a3bSRiver Riddle     appendedEntryArgs.push_back(func.front().addArgument(type, loc));
88*598c5ddbSMatthias Springer 
89*598c5ddbSMatthias Springer   return success();
900e9a4a3bSRiver Riddle }
910e9a4a3bSRiver Riddle 
9258ceae95SRiver Riddle // Updates all ReturnOps in the scope of the given func::FuncOp by either
9358ceae95SRiver Riddle // keeping them as return values or copying the associated buffer contents into
9458ceae95SRiver Riddle // the given out-params.
9558ceae95SRiver Riddle static void updateReturnOps(func::FuncOp func,
960e9a4a3bSRiver Riddle                             ArrayRef<BlockArgument> appendedEntryArgs) {
9723aa5a74SRiver Riddle   func.walk([&](func::ReturnOp op) {
980e9a4a3bSRiver Riddle     SmallVector<Value, 6> copyIntoOutParams;
990e9a4a3bSRiver Riddle     SmallVector<Value, 6> keepAsReturnOperands;
1000e9a4a3bSRiver Riddle     for (Value operand : op.getOperands()) {
101*598c5ddbSMatthias Springer       if (operand.getType().isa<MemRefType>())
1020e9a4a3bSRiver Riddle         copyIntoOutParams.push_back(operand);
1030e9a4a3bSRiver Riddle       else
1040e9a4a3bSRiver Riddle         keepAsReturnOperands.push_back(operand);
1050e9a4a3bSRiver Riddle     }
1060e9a4a3bSRiver Riddle     OpBuilder builder(op);
1070e9a4a3bSRiver Riddle     for (auto t : llvm::zip(copyIntoOutParams, appendedEntryArgs))
1080e9a4a3bSRiver Riddle       builder.create<memref::CopyOp>(op.getLoc(), std::get<0>(t),
1090e9a4a3bSRiver Riddle                                      std::get<1>(t));
11023aa5a74SRiver Riddle     builder.create<func::ReturnOp>(op.getLoc(), keepAsReturnOperands);
1110e9a4a3bSRiver Riddle     op.erase();
1120e9a4a3bSRiver Riddle   });
1130e9a4a3bSRiver Riddle }
1140e9a4a3bSRiver Riddle 
1150e9a4a3bSRiver Riddle // Updates all CallOps in the scope of the given ModuleOp by allocating
1160e9a4a3bSRiver Riddle // temporary buffers for newly introduced out params.
1170e9a4a3bSRiver Riddle static LogicalResult updateCalls(ModuleOp module) {
1180e9a4a3bSRiver Riddle   bool didFail = false;
11923aa5a74SRiver Riddle   module.walk([&](func::CallOp op) {
1200e9a4a3bSRiver Riddle     SmallVector<Value, 6> replaceWithNewCallResults;
1210e9a4a3bSRiver Riddle     SmallVector<Value, 6> replaceWithOutParams;
1220e9a4a3bSRiver Riddle     for (OpResult result : op.getResults()) {
123*598c5ddbSMatthias Springer       if (result.getType().isa<MemRefType>())
1240e9a4a3bSRiver Riddle         replaceWithOutParams.push_back(result);
1250e9a4a3bSRiver Riddle       else
1260e9a4a3bSRiver Riddle         replaceWithNewCallResults.push_back(result);
1270e9a4a3bSRiver Riddle     }
1280e9a4a3bSRiver Riddle     SmallVector<Value, 6> outParams;
1290e9a4a3bSRiver Riddle     OpBuilder builder(op);
1300e9a4a3bSRiver Riddle     for (Value memref : replaceWithOutParams) {
131*598c5ddbSMatthias Springer       if (!memref.getType().cast<MemRefType>().hasStaticShape()) {
1320e9a4a3bSRiver Riddle         op.emitError()
1330e9a4a3bSRiver Riddle             << "cannot create out param for dynamically shaped result";
1340e9a4a3bSRiver Riddle         didFail = true;
1350e9a4a3bSRiver Riddle         return;
1360e9a4a3bSRiver Riddle       }
137*598c5ddbSMatthias Springer       auto memrefType = memref.getType().cast<MemRefType>();
138*598c5ddbSMatthias Springer       auto allocType =
139*598c5ddbSMatthias Springer           MemRefType::get(memrefType.getShape(), memrefType.getElementType(),
140*598c5ddbSMatthias Springer                           AffineMap(), memrefType.getMemorySpaceAsInt());
141*598c5ddbSMatthias Springer       Value outParam = builder.create<memref::AllocOp>(op.getLoc(), allocType);
142*598c5ddbSMatthias Springer       if (!hasStaticIdentityLayout(memrefType)) {
143*598c5ddbSMatthias Springer         // Layout maps are already checked in `updateFuncOp`.
144*598c5ddbSMatthias Springer         assert(hasFullyDynamicLayoutMap(memrefType) &&
145*598c5ddbSMatthias Springer                "layout map not supported");
146*598c5ddbSMatthias Springer         outParam =
147*598c5ddbSMatthias Springer             builder.create<memref::CastOp>(op.getLoc(), memrefType, outParam);
148*598c5ddbSMatthias Springer       }
1490e9a4a3bSRiver Riddle       memref.replaceAllUsesWith(outParam);
1500e9a4a3bSRiver Riddle       outParams.push_back(outParam);
1510e9a4a3bSRiver Riddle     }
1520e9a4a3bSRiver Riddle 
1530e9a4a3bSRiver Riddle     auto newOperands = llvm::to_vector<6>(op.getOperands());
1540e9a4a3bSRiver Riddle     newOperands.append(outParams.begin(), outParams.end());
1550e9a4a3bSRiver Riddle     auto newResultTypes = llvm::to_vector<6>(llvm::map_range(
1560e9a4a3bSRiver Riddle         replaceWithNewCallResults, [](Value v) { return v.getType(); }));
15723aa5a74SRiver Riddle     auto newCall = builder.create<func::CallOp>(op.getLoc(), op.getCalleeAttr(),
1580e9a4a3bSRiver Riddle                                                 newResultTypes, newOperands);
1590e9a4a3bSRiver Riddle     for (auto t : llvm::zip(replaceWithNewCallResults, newCall.getResults()))
1600e9a4a3bSRiver Riddle       std::get<0>(t).replaceAllUsesWith(std::get<1>(t));
1610e9a4a3bSRiver Riddle     op.erase();
1620e9a4a3bSRiver Riddle   });
1630e9a4a3bSRiver Riddle 
1640e9a4a3bSRiver Riddle   return failure(didFail);
1650e9a4a3bSRiver Riddle }
1660e9a4a3bSRiver Riddle 
16782ea0d8bSMatthias Springer LogicalResult
16882ea0d8bSMatthias Springer mlir::bufferization::promoteBufferResultsToOutParams(ModuleOp module) {
16958ceae95SRiver Riddle   for (auto func : module.getOps<func::FuncOp>()) {
1700e9a4a3bSRiver Riddle     SmallVector<BlockArgument, 6> appendedEntryArgs;
171*598c5ddbSMatthias Springer     if (failed(updateFuncOp(func, appendedEntryArgs)))
172*598c5ddbSMatthias Springer       return failure();
1730e9a4a3bSRiver Riddle     if (func.isExternal())
1740e9a4a3bSRiver Riddle       continue;
1750e9a4a3bSRiver Riddle     updateReturnOps(func, appendedEntryArgs);
1760e9a4a3bSRiver Riddle   }
1770e9a4a3bSRiver Riddle   if (failed(updateCalls(module)))
17882ea0d8bSMatthias Springer     return failure();
17982ea0d8bSMatthias Springer   return success();
18082ea0d8bSMatthias Springer }
18182ea0d8bSMatthias Springer 
18282ea0d8bSMatthias Springer namespace {
18382ea0d8bSMatthias Springer struct BufferResultsToOutParamsPass
18482ea0d8bSMatthias Springer     : BufferResultsToOutParamsBase<BufferResultsToOutParamsPass> {
18582ea0d8bSMatthias Springer   void runOnOperation() override {
18682ea0d8bSMatthias Springer     if (failed(bufferization::promoteBufferResultsToOutParams(getOperation())))
1870e9a4a3bSRiver Riddle       return signalPassFailure();
1880e9a4a3bSRiver Riddle   }
1890e9a4a3bSRiver Riddle };
1900e9a4a3bSRiver Riddle } // namespace
1910e9a4a3bSRiver Riddle 
1920e9a4a3bSRiver Riddle std::unique_ptr<Pass>
1930e9a4a3bSRiver Riddle mlir::bufferization::createBufferResultsToOutParamsPass() {
1940e9a4a3bSRiver Riddle   return std::make_unique<BufferResultsToOutParamsPass>();
1950e9a4a3bSRiver Riddle }
196