-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[mlir][Transforms] Improve replaceOpWithMultiple
API
#132608
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
[mlir][Transforms] Improve replaceOpWithMultiple
API
#132608
Conversation
@llvm/pr-subscribers-mlir-sme @llvm/pr-subscribers-mlir-sparse Author: Matthias Springer (matthias-springer) ChangesThis commit adds an additional overload to In particular, one missing container type was // Compute the replacement value ranges. Some replacements are single
// values, some are value ranges.
SmallVector<ValueRange> repl;
repl.push_back(someValueRange); // OK
for (...) {
// push_back(Value) triggers an implicit conversion to ValueRange,
// which does not own the Value.
repl.push_back(someValue); // triggers use-after-scope later
} In this example, users should use Full diff: https://github.com/llvm/llvm-project/pull/132608.diff 3 Files Affected:
diff --git a/mlir/include/mlir/Transforms/DialectConversion.h b/mlir/include/mlir/Transforms/DialectConversion.h
index 8a70883293d91..cbf60b784af94 100644
--- a/mlir/include/mlir/Transforms/DialectConversion.h
+++ b/mlir/include/mlir/Transforms/DialectConversion.h
@@ -898,6 +898,10 @@ class ConversionPatternRewriter final : public PatternRewriter {
/// Replace the given operation with the new value ranges. The number of op
/// results and value ranges must match. The given operation is erased.
void replaceOpWithMultiple(Operation *op, ArrayRef<ValueRange> newValues);
+ template <typename RangeT>
+ void replaceOpWithMultiple(Operation *op, RangeT newValues) {
+ replaceOpWithMultiple(op, llvm::to_vector_of<ValueRange>(newValues));
+ }
/// PatternRewriter hook for erasing a dead operation. The uses of this
/// operation *must* be made dead by the end of the conversion process,
diff --git a/mlir/lib/Dialect/SparseTensor/Transforms/SparseTensorCodegen.cpp b/mlir/lib/Dialect/SparseTensor/Transforms/SparseTensorCodegen.cpp
index 6a66ad24a87b4..6291f3ea37230 100644
--- a/mlir/lib/Dialect/SparseTensor/Transforms/SparseTensorCodegen.cpp
+++ b/mlir/lib/Dialect/SparseTensor/Transforms/SparseTensorCodegen.cpp
@@ -616,8 +616,7 @@ class SparseCallConverter : public OpConversionPattern<func::CallOp> {
}
assert(packedResultVals.size() == op.getNumResults());
- rewriter.replaceOpWithMultiple(
- op, llvm::to_vector_of<ValueRange>(packedResultVals));
+ rewriter.replaceOpWithMultiple(op, packedResultVals);
return success();
}
};
diff --git a/mlir/test/lib/Dialect/Test/TestPatterns.cpp b/mlir/test/lib/Dialect/Test/TestPatterns.cpp
index b868f1a3a08da..e325003f5384c 100644
--- a/mlir/test/lib/Dialect/Test/TestPatterns.cpp
+++ b/mlir/test/lib/Dialect/Test/TestPatterns.cpp
@@ -1278,6 +1278,28 @@ class TestMultiple1ToNReplacement : public ConversionPattern {
}
};
+/// Test unambiguous overload resolution of replaceOpWithMultiple. This
+/// function is just to trigger compiler errors. It is never executed.
+[[maybe_unused]] void testReplaceOpWithMultipleOverloads(
+ ConversionPatternRewriter &rewriter, Operation *op, ArrayRef<ValueRange> r1,
+ SmallVector<ValueRange> r2, ArrayRef<SmallVector<Value>> r3,
+ SmallVector<SmallVector<Value>> r4, ArrayRef<ArrayRef<Value>> r5,
+ SmallVector<ArrayRef<Value>> r6, Value v, ValueRange vr,
+ ArrayRef<Value> ar) {
+ rewriter.replaceOpWithMultiple(op, r1);
+ rewriter.replaceOpWithMultiple(op, r2);
+ rewriter.replaceOpWithMultiple(op, r3);
+ rewriter.replaceOpWithMultiple(op, r4);
+ rewriter.replaceOpWithMultiple(op, r5);
+ rewriter.replaceOpWithMultiple(op, r6);
+ rewriter.replaceOpWithMultiple(op, {vr});
+ rewriter.replaceOpWithMultiple(op, {ar});
+ rewriter.replaceOpWithMultiple(op, {{v}});
+ rewriter.replaceOpWithMultiple(op, {{v, v}});
+ rewriter.replaceOpWithMultiple(op, {{v, v}, vr});
+ rewriter.replaceOpWithMultiple(op, {{v, v}, ar});
+ rewriter.replaceOpWithMultiple(op, {ar, {v, v}, vr});
+}
} // namespace
namespace {
|
@MaheshRavishankar I couldn't find the PR anymore where we were discussing this. I ran into similar problems with the API recently. Does this PR help? |
c4e8397
to
8d9755a
Compare
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.
LGTM!
FWIW I tried to fix the root cause of the example you gave previously here #121996 but ran out of bits in Type
on 32-bit platforms :( Hoping I can come up with some way in the future to fix use-after-free
✅ With the latest revision this PR passed the C/C++ code formatter. |
287bcbd
to
b6a28eb
Compare
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.
Making things not copy in the ideal case is slightly more involved as it affects all functions that are sinks: functions that take data from parameters and store them directly into internal datastructures, including transitively and must be able to move them.
This sadly disqualifies using ArrayRef
as parameter as it always returns a const
reference, which will never call the move constructor. The good news is that SmallVector
has a move constructor from SmallVectorImpl
, i.e. we do not need SmallVector<Value, 1>
.
I prototyped a solution in zero9178@b30cdb1 but it requires a few more changes including in ADT.
It makes all the sinks either have two versions: SmallVector<SmallVector<Value>>&&
for when the user does a std::move
or construct a copy to be moved. This is not ideal compared to e.g. having two overloads of every funciton (one for moving, one without), but an improvement nevertheless.
@@ -1520,7 +1520,7 @@ void ConversionPatternRewriterImpl::notifyOperationInserted( | |||
} | |||
|
|||
void ConversionPatternRewriterImpl::notifyOpReplaced( |
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.
This function is also a sink function i.e. needs to at the very least take SmallVector<SmallVector<Value>>&&
such that we can move repl
below into map
What about |
That would work. I just personally think behaviour is a bit unituative as the mutation is rather unexpected. i.e: SmallVector replacement = {...};
replaceOpWithMutable(op, replacements);
// contents of replacement are suddenly all empty vectors compare to having |
Update mlir/include/mlir/Transforms/DialectConversion.h Co-authored-by: Markus Böck <[email protected]> improve api
1b6eddf
to
f3b2445
Compare
I took a look at all the places where we currently call
Internally, a replacement for a single value is always stored as a (2), (4), (5), (6) always require a copy because the container does not own the range of values. It is not possible to move into the A copy could be avoided for (3) if the user passes the replacements with I am not entirely sure about about (1). I think it is not possible to use move semantics here. Unless maybe the user writes
I implemented this to make it possible to use move semantics with (3). (The other overloads copy.) |
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.
Still LGTM :)))
This commit adds an additional overload to
replaceOpWithMultiple
that accepts additional container types. This has been brought up by users of the newreplaceOpWithMultiple
API.In particular, one missing container type was
SmallVector<SmallVector<Value>>
. The "default"ArrayRef<ValueRange>
container type can lead to use-after-scope errors in cases such as:In this example, users should use
SmallVector<SmallVector<Value>> repl;
.