[Mlir-commits] [mlir] [MLIR][Transform] Fix crash in CheckUses when op lacks MemoryEffectOpInterface (PR #188998)

Mehdi Amini llvmlistbot at llvm.org
Fri Mar 27 06:39:12 PDT 2026


https://github.com/joker-eph created https://github.com/llvm/llvm-project/pull/188998

collectFreedValues used cast<MemoryEffectOpInterface> unconditionally on every op encountered during the walk. Ops that do not implement the interface (e.g. pdl.pattern, pdl.operands, pdl.types inside a transform.with_pdl_patterns region) trigger the cast assertion, crashing mlir-opt when -transform-dialect-check-uses is requested.

Change the cast to dyn_cast and skip ops that don't implement the interface; they cannot free transform values and are safely ignored.

Fixes #120944

Assisted-by: Claude Code

>From e402b51711c8b48cb735b94c079d3ebcf4dc63d6 Mon Sep 17 00:00:00 2001
From: Mehdi Amini <joker.eph at gmail.com>
Date: Fri, 27 Mar 2026 03:56:52 -0700
Subject: [PATCH] [MLIR][Transform] Fix crash in CheckUses when op lacks
 MemoryEffectOpInterface

collectFreedValues used cast<MemoryEffectOpInterface> unconditionally on
every op encountered during the walk. Ops that do not implement the
interface (e.g. pdl.pattern, pdl.operands, pdl.types inside a
transform.with_pdl_patterns region) trigger the cast assertion, crashing
mlir-opt when -transform-dialect-check-uses is requested.

Change the cast to dyn_cast and skip ops that don't implement the
interface; they cannot free transform values and are safely ignored.

Fixes #120944

Assisted-by: Claude Code
---
 .../Transform/Transforms/CheckUses.cpp        |  5 ++-
 .../Transform/check-use-after-free.mlir       | 37 +++++++++++++++++++
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/mlir/lib/Dialect/Transform/Transforms/CheckUses.cpp b/mlir/lib/Dialect/Transform/Transforms/CheckUses.cpp
index 18dfd504203a9..6ef8ca6c4b3af 100644
--- a/mlir/lib/Dialect/Transform/Transforms/CheckUses.cpp
+++ b/mlir/lib/Dialect/Transform/Transforms/CheckUses.cpp
@@ -339,9 +339,12 @@ class TransformOpMemFreeAnalysis {
     root->walk([&](Operation *child) {
       if (isa<transform::PatternDescriptorOpInterface>(child))
         return;
+      // Ops without the interface are assumed not to free any transform values.
       // TODO: extend this to conservatively handle operations with undeclared
       // side effects as maybe freeing the operands.
-      auto iface = cast<MemoryEffectOpInterface>(child);
+      auto iface = dyn_cast<MemoryEffectOpInterface>(child);
+      if (!iface)
+        return;
       instances.clear();
       iface.getEffectsOnResource(transform::TransformMappingResource::get(),
                                  instances);
diff --git a/mlir/test/Dialect/Transform/check-use-after-free.mlir b/mlir/test/Dialect/Transform/check-use-after-free.mlir
index 0fe8b5da17355..76f4b4fb6bd69 100644
--- a/mlir/test/Dialect/Transform/check-use-after-free.mlir
+++ b/mlir/test/Dialect/Transform/check-use-after-free.mlir
@@ -191,3 +191,40 @@ module attributes {transform.with_named_sequence} {
     transform.yield
   }
 }
+
+// -----
+
+// collectFreedValues should not crash on ops that don't implement
+// MemoryEffectOpInterface (e.g. pdl ops inside with_pdl_patterns).
+// https://github.com/llvm/llvm-project/issues/120944
+
+// CHECK-LABEL: func @foo
+func.func @foo(%arg0: index, %arg1: index, %arg2: index) {
+  scf.for %i = %arg0 to %arg1 step %arg2 {
+    %0 = arith.constant 0 : i32
+  }
+  return
+}
+
+module attributes {transform.with_named_sequence} {
+  transform.named_sequence @__transform_main(%root: !transform.any_op) {
+    transform.with_pdl_patterns %root : !transform.any_op {
+    ^bb0(%arg0: !transform.any_op):
+      pdl.pattern @match_const : benefit(1) {
+        %0 = pdl.operands
+        %1 = pdl.types
+        %2 = pdl.operation "arith.constant"(%0 : !pdl.range<value>) -> (%1 : !pdl.range<type>)
+        pdl.rewrite %2 with "transform.dialect"
+      }
+      sequence %arg0 : !transform.any_op failures(propagate) {
+      ^bb1(%arg1: !transform.any_op):
+        %0 = transform.pdl_match @match_const in %arg1 : (!transform.any_op) -> !transform.any_op
+        %1 = transform.get_parent_op %0 {op_name = "scf.for"} : (!transform.any_op) -> !transform.any_op
+        alternatives %1 : !transform.any_op {
+        ^bb2(%arg2: !transform.any_op):
+        }
+      }
+    }
+    transform.yield
+  }
+}



More information about the Mlir-commits mailing list