18#define GEN_PASS_DEF_BUFFERRESULTSTOOUTPARAMSPASS
19#include "mlir/Dialect/Bufferization/Transforms/Passes.h.inc"
33 if (failed(type.getStridesAndOffset(strides, offset)))
35 if (!llvm::all_of(strides, ShapedType::isDynamic))
37 if (ShapedType::isStatic(offset))
45 return type.getLayout().isIdentity();
57 for (
Value size : operands) {
58 if (!isa<IndexType>(size.getType()))
64 auto arguments = funcOp.getArguments();
65 auto iter = llvm::find(arguments, sizeSrc);
66 if (iter == arguments.end())
68 dynamicSizes.push_back(*iter);
79 for (
Value size : dynamicSizes) {
80 for (
auto [src, dst] :
81 llvm::zip_first(call.getOperands(), callee.getArguments())) {
84 mappedDynamicSizes.push_back(src);
87 assert(mappedDynamicSizes.size() == dynamicSizes.size() &&
88 "could not find all dynamic sizes");
89 return mappedDynamicSizes;
100 bool addResultAttribute) {
101 auto functionType =
func.getFunctionType();
105 BitVector erasedResultIndices(functionType.getNumResults());
106 for (
const auto &resultType : llvm::enumerate(functionType.getResults())) {
107 if (
auto memrefType = dyn_cast<MemRefType>(resultType.value())) {
113 return func->emitError()
114 <<
"cannot create out param for result with unsupported layout";
116 erasedResultIndices.set(resultType.index());
117 erasedResultTypes.push_back(memrefType);
122 auto newArgTypes = llvm::to_vector<6>(
123 llvm::concat<const Type>(functionType.getInputs(), erasedResultTypes));
124 auto newFunctionType = FunctionType::get(
func.getContext(), newArgTypes,
125 functionType.getResults());
126 func.setType(newFunctionType);
129 auto erasedIndicesIt = erasedResultIndices.set_bits_begin();
130 for (
int i = 0, e = erasedResultTypes.size(); i < e; ++i, ++erasedIndicesIt) {
131 func.setArgAttrs(functionType.getNumInputs() + i,
132 func.getResultAttrs(*erasedIndicesIt));
133 if (addResultAttribute)
134 func.setArgAttr(functionType.getNumInputs() + i,
135 StringAttr::get(
func.getContext(),
"bufferize.result"),
136 UnitAttr::get(
func.getContext()));
140 if (failed(
func.eraseResults(erasedResultIndices)))
144 if (
func.isExternal())
147 for (
Type type : erasedResultTypes)
148 appendedEntryArgs.push_back(
func.front().addArgument(type, loc));
160 auto res =
func.walk([&](func::ReturnOp op) {
163 for (
Value operand : op.getOperands()) {
164 if (isa<MemRefType>(operand.getType()))
165 copyIntoOutParams.push_back(operand);
167 keepAsReturnOperands.push_back(operand);
176 for (
auto [orig, arg] : llvm::zip(copyIntoOutParams, appendedEntryArgs)) {
177 bool hoistStaticAllocs =
179 cast<MemRefType>(orig.getType()).hasStaticShape();
180 bool hoistDynamicAllocs =
182 !cast<MemRefType>(orig.getType()).hasStaticShape();
183 if ((hoistStaticAllocs || hoistDynamicAllocs) &&
184 isa_and_nonnull<bufferization::AllocationOpInterface>(
185 orig.getDefiningOp())) {
186 if (hoistDynamicAllocs) {
188 dynamicSizes.push_back(dynamicSize);
191 auto [it,
inserted] = primaryArgs.try_emplace(orig, arg);
196 if (failed(
options.memCpyFn(builder, op->getLoc(), it->second, arg)))
200 orig.replaceAllUsesWith(arg);
201 toErase.insert(orig.getDefiningOp());
202 }
else if (orig != arg) {
203 if (failed(
options.memCpyFn(builder, op.getLoc(), orig, arg)))
211 func::ReturnOp::create(builder, op.getLoc(), keepAsReturnOperands);
213 auto dynamicSizePair =
214 std::pair<func::FuncOp, SmallVector<SmallVector<Value>>>(
func,
216 map.insert(dynamicSizePair);
219 return failure(res.wasInterrupted());
227 bool didFail =
false;
229 module.walk([&](func::CallOp op) {
230 auto callee = symtab.lookup<func::FuncOp>(op.getCallee());
232 op.emitError() <<
"cannot find callee '" << op.getCallee() <<
"' in "
237 if (!
options.filterFn(&callee))
239 if (callee.isPublic() && !
options.modifyPublicFunctions)
241 if (callee.isExternal())
247 if (isa<MemRefType>(
result.getType()))
248 replaceWithOutParams.push_back(
result);
250 replaceWithNewCallResults.push_back(
result);
255 size_t dynamicSizesIndex = 0;
258 ? dynamicSizes[dynamicSizesIndex]
260 bool memrefStaticShape =
261 cast<MemRefType>(
memref.getType()).hasStaticShape();
262 if (!memrefStaticShape && dynamicSize.empty()) {
264 <<
"cannot create out param for dynamically shaped result";
268 auto memrefType = cast<MemRefType>(
memref.getType());
270 MemRefType::get(memrefType.getShape(), memrefType.getElementType(),
271 AffineMap(), memrefType.getMemorySpace());
273 if (memrefStaticShape) {
280 options.allocationFn(builder, op.getLoc(), allocType, dynamicSize);
281 if (failed(maybeOutParam)) {
282 op.emitError() <<
"failed to create allocation op";
286 Value outParam = maybeOutParam.value();
290 "layout map not supported");
292 memref::CastOp::create(builder, op.getLoc(), memrefType, outParam);
294 memref.replaceAllUsesWith(outParam);
295 outParams.push_back(outParam);
298 auto newOperands = llvm::to_vector<6>(op.getOperands());
299 newOperands.append(outParams.begin(), outParams.end());
300 auto newResultTypes = llvm::map_to_vector<6>(
301 replaceWithNewCallResults, [](
Value v) {
return v.
getType(); });
302 auto newCall = func::CallOp::create(
303 builder, op.getLoc(), op.getCalleeAttr(), newResultTypes, newOperands);
304 for (
auto t : llvm::zip(replaceWithNewCallResults, newCall.getResults()))
305 std::get<0>(t).replaceAllUsesWith(std::get<1>(t));
309 return failure(didFail);
318 for (
auto func : module.getOps<func::FuncOp>()) {
319 if (
func.isPublic() && !
options.modifyPublicFunctions)
321 if (
func.isExternal())
339struct BufferResultsToOutParamsPass
340 : bufferization::impl::BufferResultsToOutParamsPassBase<
341 BufferResultsToOutParamsPass> {
344 void runOnOperation()
override {
346 if (addResultAttribute)
347 options.addResultAttribute =
true;
348 if (hoistStaticAllocs)
349 options.hoistStaticAllocs =
true;
350 if (hoistDynamicAllocs)
351 options.hoistDynamicAllocs =
true;
352 if (modifyPublicFunctions)
353 options.modifyPublicFunctions =
true;
357 return signalPassFailure();
static SmallVector< Value > getDynamicSize(Value memref, func::FuncOp funcOp)
Return the dynamic shapes of the memref based on the defining op.
static LogicalResult updateCalls(ModuleOp module, const AllocDynamicSizesMap &map, const bufferization::BufferResultsToOutParamsOpts &options)
bufferization::BufferResultsToOutParamsOpts::AllocationFn AllocationFn
bufferization::BufferResultsToOutParamsOpts::MemCpyFn MemCpyFn
static LogicalResult updateFuncOp(func::FuncOp func, SmallVectorImpl< BlockArgument > &appendedEntryArgs, bool addResultAttribute)
static bool hasStaticIdentityLayout(MemRefType type)
Return true if the given MemRef type has a static identity layout (i.e., no layout).
static SmallVector< Value > mapDynamicSizeAtCaller(func::CallOp call, func::FuncOp callee, ValueRange dynamicSizes)
Returns the dynamic sizes at the callee, through the call relationship between the caller and callee.
llvm::DenseMap< func::FuncOp, SmallVector< SmallVector< Value > > > AllocDynamicSizesMap
static bool hasFullyDynamicLayoutMap(MemRefType type)
Return true if the given MemRef type has a fully dynamic layout.
static LogicalResult updateReturnOps(func::FuncOp func, ArrayRef< BlockArgument > appendedEntryArgs, AllocDynamicSizesMap &map, const bufferization::BufferResultsToOutParamsOpts &options)
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be inserted(the insertion happens right before the *insertion point). Since `begin` can itself be invalidated due to the memref *rewriting done from this method
static llvm::ManagedStatic< PassManagerOptions > options
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
This class represents an argument of a Block.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
This class helps build Operations.
This is a value defined by a result of an operation.
Operation is the basic unit of execution within MLIR.
operand_range getOperands()
Returns an iterator on the underlying Value's.
This class allows for representing and managing the symbol table used by operations with the 'SymbolT...
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
This class provides an abstraction over the different types of ranges over Values.
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Type getType() const
Return the type of this value.
static WalkResult advance()
static WalkResult interrupt()
LogicalResult promoteBufferResultsToOutParams(ModuleOp module, const BufferResultsToOutParamsOpts &options)
Replace buffers that are returned from a function with an out parameter.
Include the generated interface declarations.
llvm::SetVector< T, Vector, Set, N > SetVector
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
std::function< LogicalResult(OpBuilder &, Location, Value, Value)> MemCpyFn
Memcpy function: Generate a memcpy between two memrefs.
std::function< FailureOr< Value >(OpBuilder &, Location, MemRefType, ValueRange)> AllocationFn
Allocator function: Generate a memref allocation with the given type.