-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[mlir][bufferize] Add hoist-dynamic-allocs-option to buffer-results-to-out-params #160985
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[mlir][bufferize] Add hoist-dynamic-allocs-option to buffer-results-to-out-params #160985
Conversation
@llvm/pr-subscribers-mlir @llvm/pr-subscribers-mlir-bufferization Author: lonely eagle (linuxlonelyeagle) ChangesAdd hoist-dynamic-allocs-option to buffer-results-to-out-params. This PR supported that obtain the size of the dynamic shape memref through the caller-callee relationship. Full diff: https://github.com/llvm/llvm-project/pull/160985.diff 5 Files Affected:
diff --git a/mlir/include/mlir/Dialect/Bufferization/Transforms/Passes.h b/mlir/include/mlir/Dialect/Bufferization/Transforms/Passes.h
index a2409f2796b94..e413a5ede5d64 100644
--- a/mlir/include/mlir/Dialect/Bufferization/Transforms/Passes.h
+++ b/mlir/include/mlir/Dialect/Bufferization/Transforms/Passes.h
@@ -5,6 +5,7 @@
#include "mlir/Dialect/Bufferization/IR/BufferizableOpInterface.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/Pass/Pass.h"
+#include "llvm/ADT/MapVector.h"
namespace mlir {
class FunctionOpInterface;
@@ -131,8 +132,8 @@ struct BufferResultsToOutParamsOpts {
/// Allocator function: Generate a memref allocation with the given type.
/// Since `promoteBufferResultsToOutParams` doesn't allow dynamically shaped
/// results, we don't allow passing a range of values for dynamic dims.
- using AllocationFn =
- std::function<FailureOr<Value>(OpBuilder &, Location, MemRefType)>;
+ using AllocationFn = std::function<FailureOr<Value>(OpBuilder &, Location,
+ MemRefType, ValueRange)>;
/// Memcpy function: Generate a memcpy between two memrefs.
using MemCpyFn =
@@ -147,8 +148,9 @@ struct BufferResultsToOutParamsOpts {
/// Allocation function; used to allocate a memref.
/// Default memref.alloc is used
AllocationFn allocationFn = [](OpBuilder &builder, Location loc,
- MemRefType type) {
- return memref::AllocOp::create(builder, loc, type).getResult();
+ MemRefType type, ValueRange dynamicSizes) {
+ return memref::AllocOp::create(builder, loc, type, dynamicSizes)
+ .getResult();
};
/// Memcpy function; used to create a copy between two memrefs.
@@ -164,15 +166,23 @@ struct BufferResultsToOutParamsOpts {
bool addResultAttribute = false;
/// If true, the pass eliminates the memref.alloc and memcpy if the returned
- /// memref is allocated in the current function.
+ /// memref is static allocated in the current function.
bool hoistStaticAllocs = false;
+
+ /// If true, the pass eliminates the memref.alloc and memcpy if the returned
+ /// memref is dynamic allocated in the current function.
+ bool hoistDynamicAllocs = false;
+
+ /// It maps the shape source of the dynamic shape memref returned by each
+ /// function.
+ llvm::DenseMap<func::FuncOp, SmallVector<SmallVector<Value>>> dynamicSizesMap;
};
/// Replace buffers that are returned from a function with an out parameter.
/// Also update all call sites.
LogicalResult
promoteBufferResultsToOutParams(ModuleOp module,
- const BufferResultsToOutParamsOpts &options);
+ BufferResultsToOutParamsOpts &options);
/// Drop all memref function results that are equivalent to a function argument.
LogicalResult dropEquivalentBufferResults(ModuleOp module);
diff --git a/mlir/include/mlir/Dialect/Bufferization/Transforms/Passes.td b/mlir/include/mlir/Dialect/Bufferization/Transforms/Passes.td
index a0d113c150c5e..cad44cb15f479 100644
--- a/mlir/include/mlir/Dialect/Bufferization/Transforms/Passes.td
+++ b/mlir/include/mlir/Dialect/Bufferization/Transforms/Passes.td
@@ -256,6 +256,8 @@ def BufferResultsToOutParamsPass
"Add the attribute 'bufferize.result' to all output parameters.">,
Option<"hoistStaticAllocs", "hoist-static-allocs", "bool",
/*default=*/"false", "Hoist static allocations to call sites.">,
+ Option<"hoistDynamicAllocs", "hoist-dynamic-allocs", "bool",
+ /*default=*/"false", "Hoist dynamic allocations to call sites.">,
];
let dependentDialects = ["memref::MemRefDialect"];
}
diff --git a/mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp b/mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
index e30e094c28467..ae68477f57a0d 100644
--- a/mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
+++ b/mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
@@ -43,6 +43,52 @@ static bool hasStaticIdentityLayout(MemRefType type) {
return type.getLayout().isIdentity();
}
+/// Return the dynamic shapes of the `memref` based on the define op. If the
+/// complete dynamic shape fails to be captured, return an empty value.
+/// Currently, only function parameters are supported for capturing.
+static ValueRange getDynamicSize(Value memref, func::FuncOp funcOp) {
+ auto *defOp = memref.getDefiningOp();
+ if (!defOp)
+ return {};
+ auto operands = defOp->getOperands();
+ SmallVector<Value> dynamicSizes;
+ for (Value size : operands) {
+ BlockArgument sizeSrc = mlir::dyn_cast<BlockArgument>(size);
+ if (!sizeSrc)
+ return {};
+
+ bool finded = false;
+ for (BlockArgument argument : funcOp.getArguments()) {
+ if (argument == sizeSrc) {
+ dynamicSizes.push_back(argument);
+ finded = true;
+ break;
+ }
+ }
+ if (!finded)
+ return {};
+ }
+ return dynamicSizes;
+}
+
+/// Returns the dynamic sizes at the callee, through the call relationship
+/// between the caller and callee.
+static ValueRange mapDynamicSizeAtCaller(func::CallOp call, func::FuncOp callee,
+ ValueRange dynamicSizes) {
+ SmallVector<Value> mapedDynamicSizes;
+ for (Value size : dynamicSizes) {
+ auto callOperands = call.getOperands();
+ for (size_t i = 0, e = callOperands.size(); i < e; ++i) {
+ Value src = callOperands[i];
+ BlockArgument dst = callee.getArgument(i);
+ if (size != dst)
+ continue;
+ mapedDynamicSizes.push_back(src);
+ }
+ }
+ return mapedDynamicSizes;
+}
+
// Updates the func op and entry block.
//
// Any args appended to the entry block are added to `appendedEntryArgs`.
@@ -109,7 +155,7 @@ updateFuncOp(func::FuncOp func,
// the given out-params.
static LogicalResult
updateReturnOps(func::FuncOp func, ArrayRef<BlockArgument> appendedEntryArgs,
- const bufferization::BufferResultsToOutParamsOpts &options) {
+ bufferization::BufferResultsToOutParamsOpts &options) {
auto res = func.walk([&](func::ReturnOp op) {
SmallVector<Value, 6> copyIntoOutParams;
SmallVector<Value, 6> keepAsReturnOperands;
@@ -120,12 +166,22 @@ updateReturnOps(func::FuncOp func, ArrayRef<BlockArgument> appendedEntryArgs,
keepAsReturnOperands.push_back(operand);
}
OpBuilder builder(op);
+ SmallVector<SmallVector<Value>> dynamicSizes;
for (auto [orig, arg] : llvm::zip(copyIntoOutParams, appendedEntryArgs)) {
- if (options.hoistStaticAllocs &&
+ bool hoistStaticAllocs =
+ options.hoistStaticAllocs &&
+ mlir::cast<MemRefType>(orig.getType()).hasStaticShape();
+ bool hoistDynamicAllocs =
+ options.hoistDynamicAllocs &&
+ !mlir::cast<MemRefType>(orig.getType()).hasStaticShape();
+ if ((hoistStaticAllocs || hoistDynamicAllocs) &&
isa_and_nonnull<bufferization::AllocationOpInterface>(
- orig.getDefiningOp()) &&
- mlir::cast<MemRefType>(orig.getType()).hasStaticShape()) {
+ orig.getDefiningOp())) {
orig.replaceAllUsesWith(arg);
+ if (hoistDynamicAllocs) {
+ SmallVector<Value> dynamicSize = getDynamicSize(orig, func);
+ dynamicSizes.push_back(dynamicSize);
+ }
orig.getDefiningOp()->erase();
} else {
if (failed(options.memCpyFn(builder, op.getLoc(), orig, arg)))
@@ -134,6 +190,10 @@ updateReturnOps(func::FuncOp func, ArrayRef<BlockArgument> appendedEntryArgs,
}
func::ReturnOp::create(builder, op.getLoc(), keepAsReturnOperands);
op.erase();
+ auto dynamicSizePair =
+ std::pair<func::FuncOp, SmallVector<SmallVector<Value>>>(func,
+ dynamicSizes);
+ options.dynamicSizesMap.insert(dynamicSizePair);
return WalkResult::advance();
});
return failure(res.wasInterrupted());
@@ -166,8 +226,16 @@ updateCalls(ModuleOp module,
}
SmallVector<Value, 6> outParams;
OpBuilder builder(op);
+ SmallVector<SmallVector<Value>> dynamicSizes =
+ options.dynamicSizesMap.lookup(callee);
+ size_t dynamicSizesIndex = 0;
for (Value memref : replaceWithOutParams) {
- if (!cast<MemRefType>(memref.getType()).hasStaticShape()) {
+ ValueRange dynamicSize = dynamicSizes.size() > dynamicSizesIndex
+ ? dynamicSizes[dynamicSizesIndex]
+ : SmallVector<Value>();
+ bool memrefStaticShape =
+ cast<MemRefType>(memref.getType()).hasStaticShape();
+ if (!memrefStaticShape && dynamicSize.empty()) {
op.emitError()
<< "cannot create out param for dynamically shaped result";
didFail = true;
@@ -177,8 +245,15 @@ updateCalls(ModuleOp module,
auto allocType =
MemRefType::get(memrefType.getShape(), memrefType.getElementType(),
AffineMap(), memrefType.getMemorySpace());
+
+ if (memrefStaticShape) {
+ dynamicSize = {};
+ } else {
+ ++dynamicSizesIndex;
+ dynamicSize = mapDynamicSizeAtCaller(op, callee, dynamicSize);
+ }
auto maybeOutParam =
- options.allocationFn(builder, op.getLoc(), allocType);
+ options.allocationFn(builder, op.getLoc(), allocType, dynamicSize);
if (failed(maybeOutParam)) {
op.emitError() << "failed to create allocation op";
didFail = true;
@@ -211,8 +286,7 @@ updateCalls(ModuleOp module,
}
LogicalResult mlir::bufferization::promoteBufferResultsToOutParams(
- ModuleOp module,
- const bufferization::BufferResultsToOutParamsOpts &options) {
+ ModuleOp module, bufferization::BufferResultsToOutParamsOpts &options) {
for (auto func : module.getOps<func::FuncOp>()) {
if (!options.filterFn(&func))
continue;
@@ -243,6 +317,8 @@ struct BufferResultsToOutParamsPass
options.addResultAttribute = true;
if (hoistStaticAllocs)
options.hoistStaticAllocs = true;
+ if (hoistDynamicAllocs)
+ options.hoistDynamicAllocs = true;
if (failed(bufferization::promoteBufferResultsToOutParams(getOperation(),
options)))
diff --git a/mlir/test/Transforms/buffer-results-to-out-params-hosit-dynamic-allocs.mlir b/mlir/test/Transforms/buffer-results-to-out-params-hosit-dynamic-allocs.mlir
new file mode 100644
index 0000000000000..f33eb8e26fbce
--- /dev/null
+++ b/mlir/test/Transforms/buffer-results-to-out-params-hosit-dynamic-allocs.mlir
@@ -0,0 +1,79 @@
+// RUN: mlir-opt -allow-unregistered-dialect -p 'builtin.module(buffer-results-to-out-params{hoist-dynamic-allocs})' %s -split-input-file | FileCheck %s
+
+func.func private @single_alloc(%size : index) -> (memref<?xf32>) {
+ %alloc = memref.alloc(%size) : memref<?xf32>
+ return %alloc : memref<?xf32>
+}
+
+func.func @single_alloc_test(%size : index) {
+ %alloc = call @single_alloc(%size) : (index) -> (memref<?xf32>)
+ "test.sink"(%alloc) : (memref<?xf32>) -> ()
+}
+
+// CHECK-LABEL: func.func private @single_alloc(
+// CHECK-SAME: %{{.*}}: index,
+// CHECK-SAME: %{{.*}}: memref<?xf32>) {
+
+// CHECK-LABEL: func.func @single_alloc_test(
+// CHECK-SAME: %[[size:.*]]: index) {
+// CHECK: %[[alloc:.*]] = memref.alloc(%[[size]]) : memref<?xf32>
+// CHECK: call @single_alloc(%[[size]], %[[alloc]]) : (index, memref<?xf32>) -> ()
+// CHECK: "test.sink"(%[[alloc]]) : (memref<?xf32>) -> ()
+// CHECK: }
+
+// -----
+
+func.func private @mult_alloc(%size0 : index, %size1 : index) -> (memref<?x?xf32>, memref<?xf32>) {
+ %alloc0 = memref.alloc(%size0, %size1) : memref<?x?xf32>
+ %alloc1 = memref.alloc(%size1) : memref<?xf32>
+ return %alloc0, %alloc1 : memref<?x?xf32>, memref<?xf32>
+}
+
+func.func @mult_alloc_test(%size0 : index, %size1: index) {
+ %alloc0, %alloc1 = call @mult_alloc(%size0, %size1) : (index, index) -> (memref<?x?xf32>, memref<?xf32>)
+ "test.sink"(%alloc0, %alloc1) : (memref<?x?xf32>, memref<?xf32>) -> ()
+}
+
+// CHECK-LABEL: func private @mult_alloc(
+// CHECK-SAME: %{{.*}}: index, %{{.*}}: index,
+// CHECK-SAME: %{{.*}}: memref<?x?xf32>, %{{.*}}: memref<?xf32>) {
+
+// CHECK-LABEL: func @mult_alloc_test(
+// CHECK-SAME: %[[size0:.*]]: index,
+// CHECK-SAME: %[[size1:.*]]: index) {
+// CHECK: %[[alloc0:.*]] = memref.alloc(%[[size0]], %[[size1]]) : memref<?x?xf32>
+// CHECK: %[[alloc1:.*]] = memref.alloc(%[[size1]]) : memref<?xf32>
+// CHECK: call @mult_alloc(%[[size0]], %[[size1]], %[[alloc0]], %[[alloc1]]) : (index, index, memref<?x?xf32>, memref<?xf32>) -> ()
+// CHECK: "test.sink"(%[[alloc0]], %[[alloc1]]) : (memref<?x?xf32>, memref<?xf32>) -> ()
+// CHECK: }
+
+
+// -----
+
+func.func private @complex_alloc(%size0 : index, %size1 : index) -> (memref<?x?xf32>, memref<4xf32>, memref<?xf32>) {
+ %alloc0 = memref.alloc(%size0, %size1) : memref<?x?xf32>
+ %alloc1 = memref.alloc() : memref<4xf32>
+ %alloc2 = memref.alloc(%size1) : memref<?xf32>
+ return %alloc0, %alloc1, %alloc2 : memref<?x?xf32>, memref<4xf32>, memref<?xf32>
+}
+
+func.func @complex_alloc_test(%size0 : index, %size1: index) {
+ %alloc0, %alloc1, %alloc2 = call @complex_alloc(%size0, %size1) : (index, index) -> (memref<?x?xf32>, memref<4xf32>, memref<?xf32>)
+ "test.sink"(%alloc0, %alloc1, %alloc2) : (memref<?x?xf32>, memref<4xf32>, memref<?xf32>) -> ()
+}
+
+// CHECK-LABEL: func private @complex_alloc(
+// CHECK-SAME: %{{.*}}: index, %{{.*}}: index,
+// CHECK-SAME: %{{.*}}: memref<?x?xf32>,
+// CHECK-SAME: %{{.*}}: memref<4xf32>,
+// CHECK-SAME: %{{.*}}: memref<?xf32>) {
+
+// CHECK-LABEL: func @complex_alloc_test(
+// CHECK-SAME: %[[size0:.*]]: index,
+// CHECK-SAME: %[[size1:.*]]: index) {
+// CHECK: %[[alloc0:.*]] = memref.alloc(%[[size0]], %[[size1]]) : memref<?x?xf32>
+// CHECK: %[[alloc1:.*]] = memref.alloc() : memref<4xf32>
+// CHECK: %[[alloc2:.*]] = memref.alloc(%[[size1]]) : memref<?xf32>
+// CHECK: call @complex_alloc(%[[size0]], %[[size1]], %[[alloc0]], %[[alloc1]], %[[alloc2]]) : (index, index, memref<?x?xf32>, memref<4xf32>, memref<?xf32>) -> ()
+// CHECK: "test.sink"(%[[alloc0]], %[[alloc1]], %[[alloc2]]) : (memref<?x?xf32>, memref<4xf32>, memref<?xf32>) -> ()
+// CHECK: }
diff --git a/mlir/test/Transforms/buffer-results-to-out-params-elim.mlir b/mlir/test/Transforms/buffer-results-to-out-params-hosit-static-allocs.mlir
similarity index 100%
rename from mlir/test/Transforms/buffer-results-to-out-params-elim.mlir
rename to mlir/test/Transforms/buffer-results-to-out-params-hosit-static-allocs.mlir
|
mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
Outdated
Show resolved
Hide resolved
maybe a naive question, but is there a reason to have separate options for static and dynamic allocs instead of just one |
} | ||
|
||
// CHECK-LABEL: func.func private @single_alloc( | ||
// CHECK-SAME: %{{.*}}: index, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should size args be removed from callee when they aren't used after hoisting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have already considered this issue. This issue has been resolved. #160755
// CHECK: %[[alloc2:.*]] = memref.alloc(%[[size1]]) : memref<?xf32> | ||
// CHECK: call @complex_alloc(%[[size0]], %[[size1]], %[[alloc0]], %[[alloc1]], %[[alloc2]]) : (index, index, memref<?x?xf32>, memref<4xf32>, memref<?xf32>) -> () | ||
// CHECK: "test.sink"(%[[alloc0]], %[[alloc1]], %[[alloc2]]) : (memref<?x?xf32>, memref<4xf32>, memref<?xf32>) -> () | ||
// CHECK: } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe add a "no-op" case where it's impossible to hoist? like when a dynamic size is defined inside the callee func.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we could introduce such examples in subsequent PRs, such as support for conatsant Op.I believe it is also acceptable to introduce such an example at this point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
although i guess in some cases it would be possible, but non-trivial. but it doesn't look like this option handles that case so would be good to track that behavior in a test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we could introduce such examples in subsequent PRs, such as support for constant Op.I believe it is also acceptable to introduce such an example at this point.
sure. i'm not suggesting handling this case now. but more to show this pass won't break in that case in the meantime. maybe i'm just being too cautious though
Good question. Separating them can improve the coupling of the Pass, but we can also use static allocation and dynamic allocation together. Please tell us your thoughts. |
I dont have a strong opinion on it, and I admit that I'm not confident about the repercussions of combining them. I think having options is always good, so fine with me to keep separate. |
mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
Outdated
Show resolved
Hide resolved
mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
Outdated
Show resolved
Hide resolved
options.hoistDynamicAllocs && | ||
!cast<MemRefType>(orig.getType()).hasStaticShape(); | ||
if ((hoistStaticAllocs || hoistDynamicAllocs) && | ||
isa_and_nonnull<bufferization::AllocationOpInterface>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you check if your implementation also works with memref.realloc
, which implements this interface. The first operand is not a size, but I think it does not matter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
func.func private @realloc(%memref : memref<?xf32>, %d:index) -> memref<?xf32> {
%alloc = memref.realloc %memref(%d) : memref<?xf32> to memref<?xf32>
return %alloc : memref<?xf32>
}
func.func private @main(%d:index) {
%c1 = arith.constant 1 : index
%alloc = memref.alloc(%c1) : memref<?xf32>
func.call @realloc(%alloc, %d) : (memref<?xf32>, index) -> (memref<?xf32>)
return
}
run pass
a.mlir:9:3: error: 'memref.alloc' op operand #0 must be variadic of index, but got 'memref<?xf32>'
func.call @realloc(%alloc, %d) : (memref<?xf32>, index) -> (memref<?xf32>)
^
a.mlir:9:3: note: see current operation: %2 = "memref.alloc"(%1, %arg0) <{operandSegmentSizes = array<i32: 2, 0>}> : (memref<?xf32>, index) -> memref<?xf32>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
func.func private @realloc(%memref : memref<?xf32>, %d:index) -> memref<?xf32> {
%alloc = memref.realloc %memref(%d) : memref<?xf32> to memref<?xf32>
return %alloc : memref<?xf32>
}
func.func private @main(%d:index) {
%c1 = arith.constant 1 : index
%alloc = memref.alloc(%c1) : memref<?xf32>
func.call @realloc(%alloc, %d) : (memref<?xf32>, index) -> (memref<?xf32>)
return
}
run pass
module {
func.func private @realloc(%arg0: memref<?xf32>, %arg1: index, %arg2: memref<?xf32>) {
return
}
func.func private @main(%arg0: index) {
%c1 = arith.constant 1 : index
%alloc = memref.alloc(%c1) : memref<?xf32>
%alloc_0 = memref.alloc(%arg0) : memref<?xf32>
call @realloc(%alloc, %arg0, %alloc_0) : (memref<?xf32>, index, memref<?xf32>) -> ()
return
}
}
Do I need to add realloc test for realloc?
mlir/lib/Dialect/Bufferization/Transforms/BufferResultsToOutParams.cpp
Outdated
Show resolved
Hide resolved
orig.getDefiningOp())) { | ||
orig.replaceAllUsesWith(arg); | ||
if (hoistDynamicAllocs) { | ||
SmallVector<Value> dynamicSize = getDynamicSize(orig, func); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens when the sizes could not be captured? You already performed the replaceAllUsesWith
. Is that correct? Should the hoisting of the value be skipped instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
func.func @foo() -> memref<?xf32> {
%c1 = arith.constant 1 : index
%c0 = arith.constant 0 : index
%f1 = arith.constant 1.0 : f32
%alloc = memref.alloc(%c1) : memref<?xf32>
memref.store %f1, %alloc[%c0] : memref<?xf32>
return %alloc : memref<?xf32>
}
run the pass
module {
func.func private @foo(%arg0: memref<?xf32>) {
%c1 = arith.constant 1 : index
%c0 = arith.constant 0 : index
%cst = arith.constant 1.000000e+00 : f32
memref.store %cst, %arg0[%c0] : memref<?xf32>
return
}
}
can't find dynamic size case.
func.func private @foo() -> memref<?xf32> {
%c1 = arith.constant 1 : index
%c0 = arith.constant 0 : index
%f1 = arith.constant 1.0 : f32
%alloc = memref.alloc(%c1) : memref<?xf32>
memref.store %f1, %alloc[%c0] : memref<?xf32>
return %alloc : memref<?xf32>
}
func.func @main() {
func.call @foo() : () -> memref<?xf32>
return
}
run the pass.
a.mlir:11:3: error: cannot create out param for dynamically shaped result
func.call @foo() : () -> memref<?xf32>
^
a.mlir:11:3: note: see current operation: %0 = "func.call"() <{callee = @foo}> : () -> memref<?xf32>
d87d4af
to
6e4abeb
Compare
Add hoist-dynamic-allocs-option to buffer-results-to-out-params. This PR supported that obtain the size of the dynamic shape memref through the caller-callee relationship.