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 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) { 23*4a3460a7SRiver Riddle auto functionType = func.getFunctionType(); 240e9a4a3bSRiver Riddle 250e9a4a3bSRiver Riddle // Collect information about the results will become appended arguments. 260e9a4a3bSRiver Riddle SmallVector<Type, 6> erasedResultTypes; 27d10d49dcSRiver Riddle BitVector erasedResultIndices(functionType.getNumResults()); 280e9a4a3bSRiver Riddle for (const auto &resultType : llvm::enumerate(functionType.getResults())) { 290e9a4a3bSRiver Riddle if (resultType.value().isa<BaseMemRefType>()) { 30e3cd80eaSRiver 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. 43e3cd80eaSRiver Riddle auto erasedIndicesIt = erasedResultIndices.set_bits_begin(); 44e3cd80eaSRiver Riddle for (int i = 0, e = erasedResultTypes.size(); i < e; ++i, ++erasedIndicesIt) { 450e9a4a3bSRiver Riddle func.setArgAttrs(functionType.getNumInputs() + i, 46e3cd80eaSRiver Riddle func.getResultAttrs(*erasedIndicesIt)); 47e3cd80eaSRiver 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) { 6523aa5a74SRiver Riddle func.walk([&](func::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)); 7823aa5a74SRiver Riddle builder.create<func::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; 8723aa5a74SRiver Riddle module.walk([&](func::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(); })); 11523aa5a74SRiver Riddle auto newCall = builder.create<func::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