[Mlir-commits] [mlir] [mlir] Avoid repeated moves in RewritePatternSet (PR #220871)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Sep 3 04:06:42 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Mehdi Amini (joker-eph)

<details>
<summary>Changes</summary>

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

---
Full diff: https://github.com/llvm/llvm-project/pull/220871.diff


2 Files Affected:

- (modified) mlir/include/mlir/IR/PatternMatch.h (+22-15) 
- (modified) mlir/unittests/IR/PatternMatchTest.cpp (+46) 


``````````diff
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();

``````````

</details>


https://github.com/llvm/llvm-project/pull/220871


More information about the Mlir-commits mailing list