1*0e9a4a3bSRiver Riddle //===- BufferResultsToOutParams.cpp - Calling convention conversion -------===// 2*0e9a4a3bSRiver Riddle // 3*0e9a4a3bSRiver Riddle // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0e9a4a3bSRiver Riddle // See https://llvm.org/LICENSE.txt for license information. 5*0e9a4a3bSRiver Riddle // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0e9a4a3bSRiver Riddle // 7*0e9a4a3bSRiver Riddle //===----------------------------------------------------------------------===// 8*0e9a4a3bSRiver Riddle 9*0e9a4a3bSRiver Riddle #include "PassDetail.h" 10*0e9a4a3bSRiver Riddle #include "mlir/Dialect/Bufferization/Transforms/Passes.h" 11*0e9a4a3bSRiver Riddle #include "mlir/Dialect/MemRef/IR/MemRef.h" 12*0e9a4a3bSRiver Riddle #include "mlir/Dialect/StandardOps/IR/Ops.h" 13*0e9a4a3bSRiver Riddle #include "mlir/IR/Operation.h" 14*0e9a4a3bSRiver Riddle #include "mlir/Pass/Pass.h" 15*0e9a4a3bSRiver Riddle 16*0e9a4a3bSRiver Riddle using namespace mlir; 17*0e9a4a3bSRiver Riddle 18*0e9a4a3bSRiver Riddle // Updates the func op and entry block. 19*0e9a4a3bSRiver Riddle // 20*0e9a4a3bSRiver Riddle // Any args appended to the entry block are added to `appendedEntryArgs`. 21*0e9a4a3bSRiver Riddle static void updateFuncOp(FuncOp func, 22*0e9a4a3bSRiver Riddle SmallVectorImpl<BlockArgument> &appendedEntryArgs) { 23*0e9a4a3bSRiver Riddle auto functionType = func.getType(); 24*0e9a4a3bSRiver Riddle 25*0e9a4a3bSRiver Riddle // Collect information about the results will become appended arguments. 26*0e9a4a3bSRiver Riddle SmallVector<Type, 6> erasedResultTypes; 27*0e9a4a3bSRiver Riddle SmallVector<unsigned, 6> erasedResultIndices; 28*0e9a4a3bSRiver Riddle for (const auto &resultType : llvm::enumerate(functionType.getResults())) { 29*0e9a4a3bSRiver Riddle if (resultType.value().isa<BaseMemRefType>()) { 30*0e9a4a3bSRiver Riddle erasedResultIndices.push_back(resultType.index()); 31*0e9a4a3bSRiver Riddle erasedResultTypes.push_back(resultType.value()); 32*0e9a4a3bSRiver Riddle } 33*0e9a4a3bSRiver Riddle } 34*0e9a4a3bSRiver Riddle 35*0e9a4a3bSRiver Riddle // Add the new arguments to the function type. 36*0e9a4a3bSRiver Riddle auto newArgTypes = llvm::to_vector<6>( 37*0e9a4a3bSRiver Riddle llvm::concat<const Type>(functionType.getInputs(), erasedResultTypes)); 38*0e9a4a3bSRiver Riddle auto newFunctionType = FunctionType::get(func.getContext(), newArgTypes, 39*0e9a4a3bSRiver Riddle functionType.getResults()); 40*0e9a4a3bSRiver Riddle func.setType(newFunctionType); 41*0e9a4a3bSRiver Riddle 42*0e9a4a3bSRiver Riddle // Transfer the result attributes to arg attributes. 43*0e9a4a3bSRiver Riddle for (int i = 0, e = erasedResultTypes.size(); i < e; i++) 44*0e9a4a3bSRiver Riddle func.setArgAttrs(functionType.getNumInputs() + i, 45*0e9a4a3bSRiver Riddle func.getResultAttrs(erasedResultIndices[i])); 46*0e9a4a3bSRiver Riddle 47*0e9a4a3bSRiver Riddle // Erase the results. 48*0e9a4a3bSRiver Riddle func.eraseResults(erasedResultIndices); 49*0e9a4a3bSRiver Riddle 50*0e9a4a3bSRiver Riddle // Add the new arguments to the entry block if the function is not external. 51*0e9a4a3bSRiver Riddle if (func.isExternal()) 52*0e9a4a3bSRiver Riddle return; 53*0e9a4a3bSRiver Riddle Location loc = func.getLoc(); 54*0e9a4a3bSRiver Riddle for (Type type : erasedResultTypes) 55*0e9a4a3bSRiver Riddle appendedEntryArgs.push_back(func.front().addArgument(type, loc)); 56*0e9a4a3bSRiver Riddle } 57*0e9a4a3bSRiver Riddle 58*0e9a4a3bSRiver Riddle // Updates all ReturnOps in the scope of the given FuncOp by either keeping them 59*0e9a4a3bSRiver Riddle // as return values or copying the associated buffer contents into the given 60*0e9a4a3bSRiver Riddle // out-params. 61*0e9a4a3bSRiver Riddle static void updateReturnOps(FuncOp func, 62*0e9a4a3bSRiver Riddle ArrayRef<BlockArgument> appendedEntryArgs) { 63*0e9a4a3bSRiver Riddle func.walk([&](ReturnOp op) { 64*0e9a4a3bSRiver Riddle SmallVector<Value, 6> copyIntoOutParams; 65*0e9a4a3bSRiver Riddle SmallVector<Value, 6> keepAsReturnOperands; 66*0e9a4a3bSRiver Riddle for (Value operand : op.getOperands()) { 67*0e9a4a3bSRiver Riddle if (operand.getType().isa<BaseMemRefType>()) 68*0e9a4a3bSRiver Riddle copyIntoOutParams.push_back(operand); 69*0e9a4a3bSRiver Riddle else 70*0e9a4a3bSRiver Riddle keepAsReturnOperands.push_back(operand); 71*0e9a4a3bSRiver Riddle } 72*0e9a4a3bSRiver Riddle OpBuilder builder(op); 73*0e9a4a3bSRiver Riddle for (auto t : llvm::zip(copyIntoOutParams, appendedEntryArgs)) 74*0e9a4a3bSRiver Riddle builder.create<memref::CopyOp>(op.getLoc(), std::get<0>(t), 75*0e9a4a3bSRiver Riddle std::get<1>(t)); 76*0e9a4a3bSRiver Riddle builder.create<ReturnOp>(op.getLoc(), keepAsReturnOperands); 77*0e9a4a3bSRiver Riddle op.erase(); 78*0e9a4a3bSRiver Riddle }); 79*0e9a4a3bSRiver Riddle } 80*0e9a4a3bSRiver Riddle 81*0e9a4a3bSRiver Riddle // Updates all CallOps in the scope of the given ModuleOp by allocating 82*0e9a4a3bSRiver Riddle // temporary buffers for newly introduced out params. 83*0e9a4a3bSRiver Riddle static LogicalResult updateCalls(ModuleOp module) { 84*0e9a4a3bSRiver Riddle bool didFail = false; 85*0e9a4a3bSRiver Riddle module.walk([&](CallOp op) { 86*0e9a4a3bSRiver Riddle SmallVector<Value, 6> replaceWithNewCallResults; 87*0e9a4a3bSRiver Riddle SmallVector<Value, 6> replaceWithOutParams; 88*0e9a4a3bSRiver Riddle for (OpResult result : op.getResults()) { 89*0e9a4a3bSRiver Riddle if (result.getType().isa<BaseMemRefType>()) 90*0e9a4a3bSRiver Riddle replaceWithOutParams.push_back(result); 91*0e9a4a3bSRiver Riddle else 92*0e9a4a3bSRiver Riddle replaceWithNewCallResults.push_back(result); 93*0e9a4a3bSRiver Riddle } 94*0e9a4a3bSRiver Riddle SmallVector<Value, 6> outParams; 95*0e9a4a3bSRiver Riddle OpBuilder builder(op); 96*0e9a4a3bSRiver Riddle for (Value memref : replaceWithOutParams) { 97*0e9a4a3bSRiver Riddle if (!memref.getType().cast<BaseMemRefType>().hasStaticShape()) { 98*0e9a4a3bSRiver Riddle op.emitError() 99*0e9a4a3bSRiver Riddle << "cannot create out param for dynamically shaped result"; 100*0e9a4a3bSRiver Riddle didFail = true; 101*0e9a4a3bSRiver Riddle return; 102*0e9a4a3bSRiver Riddle } 103*0e9a4a3bSRiver Riddle Value outParam = builder.create<memref::AllocOp>( 104*0e9a4a3bSRiver Riddle op.getLoc(), memref.getType().cast<MemRefType>()); 105*0e9a4a3bSRiver Riddle memref.replaceAllUsesWith(outParam); 106*0e9a4a3bSRiver Riddle outParams.push_back(outParam); 107*0e9a4a3bSRiver Riddle } 108*0e9a4a3bSRiver Riddle 109*0e9a4a3bSRiver Riddle auto newOperands = llvm::to_vector<6>(op.getOperands()); 110*0e9a4a3bSRiver Riddle newOperands.append(outParams.begin(), outParams.end()); 111*0e9a4a3bSRiver Riddle auto newResultTypes = llvm::to_vector<6>(llvm::map_range( 112*0e9a4a3bSRiver Riddle replaceWithNewCallResults, [](Value v) { return v.getType(); })); 113*0e9a4a3bSRiver Riddle auto newCall = builder.create<CallOp>(op.getLoc(), op.getCalleeAttr(), 114*0e9a4a3bSRiver Riddle newResultTypes, newOperands); 115*0e9a4a3bSRiver Riddle for (auto t : llvm::zip(replaceWithNewCallResults, newCall.getResults())) 116*0e9a4a3bSRiver Riddle std::get<0>(t).replaceAllUsesWith(std::get<1>(t)); 117*0e9a4a3bSRiver Riddle op.erase(); 118*0e9a4a3bSRiver Riddle }); 119*0e9a4a3bSRiver Riddle 120*0e9a4a3bSRiver Riddle return failure(didFail); 121*0e9a4a3bSRiver Riddle } 122*0e9a4a3bSRiver Riddle 123*0e9a4a3bSRiver Riddle namespace { 124*0e9a4a3bSRiver Riddle struct BufferResultsToOutParamsPass 125*0e9a4a3bSRiver Riddle : BufferResultsToOutParamsBase<BufferResultsToOutParamsPass> { 126*0e9a4a3bSRiver Riddle void runOnOperation() override { 127*0e9a4a3bSRiver Riddle ModuleOp module = getOperation(); 128*0e9a4a3bSRiver Riddle 129*0e9a4a3bSRiver Riddle for (auto func : module.getOps<FuncOp>()) { 130*0e9a4a3bSRiver Riddle SmallVector<BlockArgument, 6> appendedEntryArgs; 131*0e9a4a3bSRiver Riddle updateFuncOp(func, appendedEntryArgs); 132*0e9a4a3bSRiver Riddle if (func.isExternal()) 133*0e9a4a3bSRiver Riddle continue; 134*0e9a4a3bSRiver Riddle updateReturnOps(func, appendedEntryArgs); 135*0e9a4a3bSRiver Riddle } 136*0e9a4a3bSRiver Riddle if (failed(updateCalls(module))) 137*0e9a4a3bSRiver Riddle return signalPassFailure(); 138*0e9a4a3bSRiver Riddle } 139*0e9a4a3bSRiver Riddle }; 140*0e9a4a3bSRiver Riddle } // namespace 141*0e9a4a3bSRiver Riddle 142*0e9a4a3bSRiver Riddle std::unique_ptr<Pass> 143*0e9a4a3bSRiver Riddle mlir::bufferization::createBufferResultsToOutParamsPass() { 144*0e9a4a3bSRiver Riddle return std::make_unique<BufferResultsToOutParamsPass>(); 145*0e9a4a3bSRiver Riddle } 146