[Mlir-commits] [mlir] [MLIR][Rewrite] Support unqualified pattern names in disable/enable-patterns (PR #188988)

Mehdi Amini llvmlistbot at llvm.org
Wed Apr 22 06:39:29 PDT 2026


https://github.com/joker-eph updated https://github.com/llvm/llvm-project/pull/188988

>From f1b75d1d3bcece49e6d4c7efd6b3f20fa0c3a06e Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Thu, 26 Mar 2026 16:03:22 -0700
Subject: [PATCH] [MLIR][Rewrite] Support unqualified pattern names in
 disable/enable-patterns

FrozenRewritePatternSet previously required exact string matching for
disable-patterns and enable-patterns filter options. Pattern debug names
are auto-generated as fully qualified typenames (e.g.,
"(anonymous namespace)::FooBar"), but users often want to filter by just
the class name (e.g., "FooBar").

This change adds support for unqualified name matching: if a user-supplied
filter label contains no "::", it is also matched against the unqualified
part of the pattern debug name (the suffix after the last "::"). Labels
containing "::" still require exact matching as before.

A new test exercises disable/enable filtering by unqualified name for a
pattern living in an anonymous namespace.

Fixes #120489

Assisted-by: Claude Code
---
 mlir/lib/Rewrite/FrozenRewritePatternSet.cpp  | 55 ++++++++++++++++---
 .../Transforms/test-canonicalize-filter.mlir  | 23 ++++++++
 2 files changed, 70 insertions(+), 8 deletions(-)

diff --git a/mlir/lib/Rewrite/FrozenRewritePatternSet.cpp b/mlir/lib/Rewrite/FrozenRewritePatternSet.cpp
index d4cd24372efbc..2c1d4635cb235 100644
--- a/mlir/lib/Rewrite/FrozenRewritePatternSet.cpp
+++ b/mlir/lib/Rewrite/FrozenRewritePatternSet.cpp
@@ -64,10 +64,6 @@ FrozenRewritePatternSet::FrozenRewritePatternSet(
     RewritePatternSet &&patterns, ArrayRef<std::string> disabledPatternLabels,
     ArrayRef<std::string> enabledPatternLabels)
     : impl(std::make_shared<Impl>()) {
-  DenseSet<StringRef> disabledPatterns, enabledPatterns;
-  disabledPatterns.insert_range(disabledPatternLabels);
-  enabledPatterns.insert_range(enabledPatternLabels);
-
   // Functor used to walk all of the operations registered in the context. This
   // is useful for patterns that get applied to multiple operations, such as
   // interface and trait based patterns.
@@ -83,20 +79,63 @@ FrozenRewritePatternSet::FrozenRewritePatternSet(
         impl->nativeOpSpecificPatternList.push_back(std::move(pattern));
       };
 
+  // Pre-compute for each user-supplied filter label whether it is qualified
+  // (contains "::"). Unqualified labels additionally match against the
+  // suffix of a pattern's debug name after its last "::", allowing users to
+  // write, e.g., `disable-patterns=FooBar` instead of
+  // `disable-patterns=(anonymous namespace)::FooBar`.
+  auto computeQualifiedFlags = [](ArrayRef<std::string> labels) {
+    SmallVector<bool> flags;
+    flags.reserve(labels.size());
+    for (StringRef ul : labels)
+      flags.push_back(ul.contains("::"));
+    return flags;
+  };
+  SmallVector<bool> enabledQualified =
+      computeQualifiedFlags(enabledPatternLabels);
+  SmallVector<bool> disabledQualified =
+      computeQualifiedFlags(disabledPatternLabels);
+
+  // Returns true if `label` (a pattern's debug name or label) matches
+  // `userLabel`. If `userLabelQualified` is false, also match `userLabel`
+  // against the unqualified part of `label` (after the last "::").
+  auto matchesLabel = [](StringRef label, StringRef userLabel,
+                         bool userLabelQualified) {
+    if (label == userLabel)
+      return true;
+    if (!userLabelQualified) {
+      size_t pos = label.rfind("::");
+      if (pos != StringRef::npos && label.substr(pos + 2) == userLabel)
+        return true;
+    }
+    return false;
+  };
+
+  auto anyUserLabelMatches = [&](StringRef label,
+                                 ArrayRef<std::string> userLabels,
+                                 ArrayRef<bool> qualifiedFlags) {
+    for (auto [ul, qualified] : llvm::zip_equal(userLabels, qualifiedFlags))
+      if (matchesLabel(label, ul, qualified))
+        return true;
+    return false;
+  };
+
   for (std::unique_ptr<RewritePattern> &pat : patterns.getNativePatterns()) {
     // Don't add patterns that haven't been enabled by the user.
-    if (!enabledPatterns.empty()) {
+    if (!enabledPatternLabels.empty()) {
       auto isEnabledFn = [&](StringRef label) {
-        return enabledPatterns.count(label);
+        return anyUserLabelMatches(label, enabledPatternLabels,
+                                   enabledQualified);
       };
       if (!isEnabledFn(pat->getDebugName()) &&
           llvm::none_of(pat->getDebugLabels(), isEnabledFn))
         continue;
     }
     // Don't add patterns that have been disabled by the user.
-    if (!disabledPatterns.empty()) {
+    if (!disabledPatternLabels.empty()) {
       auto isDisabledFn = [&](StringRef label) {
-        return disabledPatterns.count(label);
+        return anyUserLabelMatches(label, disabledPatternLabels,
+                                   disabledQualified);
       };
       if (isDisabledFn(pat->getDebugName()) ||
           llvm::any_of(pat->getDebugLabels(), isDisabledFn))
diff --git a/mlir/test/Transforms/test-canonicalize-filter.mlir b/mlir/test/Transforms/test-canonicalize-filter.mlir
index dba5f05e84345..ad1784e9e5cb4 100644
--- a/mlir/test/Transforms/test-canonicalize-filter.mlir
+++ b/mlir/test/Transforms/test-canonicalize-filter.mlir
@@ -1,6 +1,12 @@
 // RUN: mlir-opt %s -pass-pipeline='builtin.module(func.func(canonicalize))' | FileCheck %s --check-prefix=NO_FILTER
 // RUN: mlir-opt %s -pass-pipeline='builtin.module(func.func(canonicalize{enable-patterns=TestRemoveOpWithInnerOps}))' | FileCheck %s --check-prefix=FILTER_ENABLE
 // RUN: mlir-opt %s -pass-pipeline='builtin.module(func.func(canonicalize{disable-patterns=TestRemoveOpWithInnerOps}))' | FileCheck %s --check-prefix=FILTER_DISABLE
+// Test that unqualified names (without namespace prefix) also work for filtering.
+// FoldToCallOpPattern is in an anonymous namespace, so its debug name is
+// "(anonymous namespace)::FoldToCallOpPattern". Filtering by the unqualified
+// name "FoldToCallOpPattern" should still work.
+// RUN: mlir-opt %s -pass-pipeline='builtin.module(func.func(canonicalize{disable-patterns=FoldToCallOpPattern}))' | FileCheck %s --check-prefix=DISABLE_ANON
+// RUN: mlir-opt %s -pass-pipeline='builtin.module(func.func(canonicalize{enable-patterns=FoldToCallOpPattern}))' | FileCheck %s --check-prefix=ENABLE_ANON
 
 // NO_FILTER-LABEL: func @remove_op_with_inner_ops_pattern
 // NO_FILTER-NEXT: return
@@ -14,3 +20,20 @@ func.func @remove_op_with_inner_ops_pattern() {
   }) : () -> ()
   return
 }
+
+// Test filtering by unqualified pattern name (without namespace prefix).
+// FoldToCallOpPattern lives in an anonymous namespace; its debug name is
+// "(anonymous namespace)::FoldToCallOpPattern". Filters without "::" should
+// match against the unqualified part after the last "::".
+
+// NO_FILTER-LABEL: func @fold_to_call_unqualified_filter
+// NO_FILTER-NEXT: call @callee
+// DISABLE_ANON-LABEL: func @fold_to_call_unqualified_filter
+// DISABLE_ANON-NEXT: "test.fold_to_call_op"
+// ENABLE_ANON-LABEL: func @fold_to_call_unqualified_filter
+// ENABLE_ANON-NEXT: call @callee
+func.func private @callee()
+func.func @fold_to_call_unqualified_filter() {
+  "test.fold_to_call_op"() {callee = @callee} : () -> ()
+  return
+}



More information about the Mlir-commits mailing list