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