[Mlir-commits] [mlir] [mlir] Avoid repeated moves in RewritePatternSet (PR #220871)
Mehdi Amini
llvmlistbot at llvm.org
Thu Sep 3 04:05:58 PDT 2026
https://github.com/joker-eph created https://github.com/llvm/llvm-project/pull/220871
Multi-pattern add APIs forwarded the same constructor arguments once for each pattern type. An rvalue was therefore moved from repeatedly, and every pattern after the first could receive moved-from state.
Pass constructor arguments as lvalues when constructing multiple patterns, while preserving perfect forwarding when constructing a single pattern. Cover add, addWithLabel, and insert in the existing PatternMatch unit test.
Found by Coverity.
Assisted-by: Codex
>From e8f0298487f185c364f1999f96210b9d6f3c4b6b Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Wed, 2 Sep 2026 15:35:58 -0700
Subject: [PATCH] [mlir] Avoid repeated moves in RewritePatternSet
Multi-pattern add APIs forwarded the same constructor arguments once for
each pattern type. An rvalue was therefore moved from repeatedly, and every
pattern after the first could receive moved-from state.
Pass constructor arguments as lvalues when constructing multiple patterns,
while preserving perfect forwarding when constructing a single pattern. Cover
add, addWithLabel, and insert in the existing PatternMatch unit test.
The pre-fix test failed under Valgrind with:
arguments Which is: { "add", "" }
expected: { "add", "add" }
==600917== ERROR SUMMARY: 0 errors from 0 contexts
Valgrind reports no invalid access because a moved-from std::string remains
valid; the test exposes the lost state directly.
Found by Coverity.
Assisted-by: Codex
---
mlir/include/mlir/IR/PatternMatch.h | 37 ++++++++++++---------
mlir/unittests/IR/PatternMatchTest.cpp | 46 ++++++++++++++++++++++++++
2 files changed, 68 insertions(+), 15 deletions(-)
diff --git a/mlir/include/mlir/IR/PatternMatch.h b/mlir/include/mlir/IR/PatternMatch.h
index 83477c79ff582..531051e42b824 100644
--- a/mlir/include/mlir/IR/PatternMatch.h
+++ b/mlir/include/mlir/IR/PatternMatch.h
@@ -859,11 +859,8 @@ class RewritePatternSet {
typename... ConstructorArgs,
typename = std::enable_if_t<sizeof...(Ts) != 0>>
RewritePatternSet &add(ConstructorArg &&arg, ConstructorArgs &&...args) {
- // The following expands a call to emplace_back for each of the pattern
- // types 'Ts'.
- (addImpl<Ts>(/*debugLabels=*/{}, std::forward<ConstructorArg>(arg),
- std::forward<ConstructorArgs>(args)...),
- ...);
+ addImplForEach<Ts...>(/*debugLabels=*/{}, std::forward<ConstructorArg>(arg),
+ std::forward<ConstructorArgs>(args)...);
return *this;
}
/// An overload of the above `add` method that allows for attaching a set
@@ -876,11 +873,8 @@ class RewritePatternSet {
RewritePatternSet &addWithLabel(ArrayRef<StringRef> debugLabels,
ConstructorArg &&arg,
ConstructorArgs &&...args) {
- // The following expands a call to emplace_back for each of the pattern
- // types 'Ts'.
- (addImpl<Ts>(debugLabels, std::forward<ConstructorArg>(arg),
- std::forward<ConstructorArgs>(args)...),
- ...);
+ addImplForEach<Ts...>(debugLabels, std::forward<ConstructorArg>(arg),
+ std::forward<ConstructorArgs>(args)...);
return *this;
}
@@ -944,11 +938,8 @@ class RewritePatternSet {
typename... ConstructorArgs,
typename = std::enable_if_t<sizeof...(Ts) != 0>>
RewritePatternSet &insert(ConstructorArg &&arg, ConstructorArgs &&...args) {
- // The following expands a call to emplace_back for each of the pattern
- // types 'Ts'.
- (addImpl<Ts>(/*debugLabels=*/{}, std::forward<ConstructorArg>(arg),
- std::forward<ConstructorArgs>(args)...),
- ...);
+ addImplForEach<Ts...>(/*debugLabels=*/{}, std::forward<ConstructorArg>(arg),
+ std::forward<ConstructorArgs>(args)...);
return *this;
}
@@ -998,6 +989,22 @@ class RewritePatternSet {
}
private:
+ /// Add an instance of each pattern type. A single pattern receives perfectly
+ /// forwarded arguments. Multiple patterns receive lvalues so that a shared
+ /// rvalue argument is copied instead of moved from repeatedly.
+ template <typename... Ts, typename ConstructorArg,
+ typename... ConstructorArgs>
+ void addImplForEach(ArrayRef<StringRef> debugLabels, ConstructorArg &&arg,
+ ConstructorArgs &&...args) {
+ if constexpr (sizeof...(Ts) == 1) {
+ (addImpl<Ts>(debugLabels, std::forward<ConstructorArg>(arg),
+ std::forward<ConstructorArgs>(args)...),
+ ...);
+ } else {
+ (addImpl<Ts>(debugLabels, arg, args...), ...);
+ }
+ }
+
/// Add an instance of the pattern type 'T'. Return a reference to `this` for
/// chaining insertions.
template <typename T, typename... Args>
diff --git a/mlir/unittests/IR/PatternMatchTest.cpp b/mlir/unittests/IR/PatternMatchTest.cpp
index 1c67bfc284d32..f4ca5c4c49454 100644
--- a/mlir/unittests/IR/PatternMatchTest.cpp
+++ b/mlir/unittests/IR/PatternMatchTest.cpp
@@ -9,6 +9,9 @@
#include "mlir/IR/PatternMatch.h"
#include "gtest/gtest.h"
+#include <string>
+#include <vector>
+
#include "../../test/lib/Dialect/Test/TestDialect.h"
#include "../../test/lib/Dialect/Test/TestOps.h"
@@ -35,6 +38,49 @@ TEST(OpRewritePatternTest, GetGeneratedNames) {
}
} // end anonymous namespace
+namespace {
+template <int ID>
+struct ConstructorArgumentPattern : RewritePattern {
+ ConstructorArgumentPattern(MLIRContext *context, std::string argument,
+ std::vector<std::string> *arguments)
+ : RewritePattern(MatchAnyOpTypeTag(), /*benefit=*/1, context) {
+ arguments->push_back(std::move(argument));
+ }
+
+ LogicalResult matchAndRewrite(Operation *, PatternRewriter &) const override {
+ return failure();
+ }
+};
+
+using ConstructorArgumentPattern0 = ConstructorArgumentPattern<0>;
+using ConstructorArgumentPattern1 = ConstructorArgumentPattern<1>;
+
+TEST(RewritePatternSetTest, CopyArgumentsWhenAddingMultiplePatterns) {
+ MLIRContext context;
+ std::vector<std::string> arguments;
+
+ RewritePatternSet addPatterns(&context);
+ addPatterns.add<ConstructorArgumentPattern0, ConstructorArgumentPattern1>(
+ &context, std::string("add"), &arguments);
+ EXPECT_EQ(arguments, (std::vector<std::string>{"add", "add"}));
+
+ arguments.clear();
+ RewritePatternSet labeledPatterns(&context);
+ labeledPatterns
+ .addWithLabel<ConstructorArgumentPattern0, ConstructorArgumentPattern1>(
+ {"test"}, &context, std::string("addWithLabel"), &arguments);
+ EXPECT_EQ(arguments,
+ (std::vector<std::string>{"addWithLabel", "addWithLabel"}));
+
+ arguments.clear();
+ RewritePatternSet insertPatterns(&context);
+ insertPatterns
+ .insert<ConstructorArgumentPattern0, ConstructorArgumentPattern1>(
+ &context, std::string("insert"), &arguments);
+ EXPECT_EQ(arguments, (std::vector<std::string>{"insert", "insert"}));
+}
+} // end anonymous namespace
+
namespace {
LogicalResult anOpRewritePatternFunc(test::OpA op, PatternRewriter &rewriter) {
return failure();
More information about the Mlir-commits
mailing list