[Mlir-commits] [mlir] [MLIR][PDL] Use scoped map for ConstraintQuestion mapping (PR #228081)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Thu Oct 1 06:47:31 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: jumerckx

<details>
<summary>Changes</summary>

Two parallel branches can depend on the same constraint, however, they can't refer to the same ApplyConstraintOp if it lives in one of the two branches.
The test fails on main with:
```
input.mlir:18:3: error: operand #<!-- -->1 does not dominate this use
  pdl.pattern @<!-- -->rewrite_b : benefit(1) {
  ^
input.mlir:1:1: note: operand defined here (op in the same region)
module @<!-- -->constraint_with_result_in_sibling_branches {
^
```
this happens because the else branch refers to the wrong value for the constraint:
```mlir
pdl_interp.func @<!-- -->matcher(%arg0: !pdl.operation) {
  pdl_interp.switch_operation_name of %arg0 to ["test.a", "test.b"](^bb2, ^bb6) -> ^bb1
^bb1:  // 9 preds: ^bb0, ^bb2, ^bb3, ^bb4, ^bb5, ^bb6, ^bb7, ^bb8, ^bb9
  pdl_interp.finalize
^bb2:  // pred: ^bb0
  pdl_interp.check_operand_count of %arg0 is 0 -> ^bb3, ^bb1
^bb3:  // pred: ^bb2
  pdl_interp.check_result_count of %arg0 is 0 -> ^bb4, ^bb1
^bb4:  // pred: ^bb3
  %0 = pdl_interp.apply_constraint "get_attr"(%arg0 : !pdl.operation) : !pdl.attribute -> ^bb5, ^bb1
^bb5:  // pred: ^bb4
  pdl_interp.record_match @<!-- -->rewriters::@<!-- -->rewrite_a(%arg0, %0 : !pdl.operation, !pdl.attribute) : benefit(1), loc([%arg0]), root("test.a") -> ^bb1
^bb6:  // pred: ^bb0
  pdl_interp.check_operand_count of %arg0 is 0 -> ^bb7, ^bb1
^bb7:  // pred: ^bb6
  pdl_interp.check_result_count of %arg0 is 0 -> ^bb8, ^bb1
^bb8:  // pred: ^bb7
  %1 = pdl_interp.apply_constraint "get_attr"(%arg0 : !pdl.operation) : !pdl.attribute -> ^bb9, ^bb1
^bb9:  // pred: ^bb8
  // PROBLEM HERE because record_match refers to %0 instead of %1:
  pdl_interp.record_match @<!-- -->rewriters::@<!-- -->rewrite_b(%arg0, %0 : !pdl.operation, !pdl.attribute) : benefit(1), loc([%arg0]), root("test.b") -> ^bb1
}
```
By making the map scoped (analogous to the ValueMap), the correct constraint value is looked up in that branch.


Assisted-by: Claude Code (Claude Opus 5.5).

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


2 Files Affected:

- (modified) mlir/lib/Conversion/PDLToPDLInterp/PDLToPDLInterp.cpp (+14-6) 
- (modified) mlir/test/Conversion/PDLToPDLInterp/pdl-to-pdl-interp-matcher.mlir (+27) 


``````````diff
diff --git a/mlir/lib/Conversion/PDLToPDLInterp/PDLToPDLInterp.cpp b/mlir/lib/Conversion/PDLToPDLInterp/PDLToPDLInterp.cpp
index 5efe604bf47f4..d14a9aec6d7a8 100644
--- a/mlir/lib/Conversion/PDLToPDLInterp/PDLToPDLInterp.cpp
+++ b/mlir/lib/Conversion/PDLToPDLInterp/PDLToPDLInterp.cpp
@@ -45,6 +45,11 @@ struct PatternLowering {
 private:
   using ValueMap = llvm::ScopedHashTable<Position *, Value>;
   using ValueMapScope = llvm::ScopedHashTableScope<Position *, Value>;
+  using ConstraintOpMap = llvm::ScopedHashTable<ConstraintQuestion *,
+                                                pdl_interp::ApplyConstraintOp>;
+  using ConstraintOpMapScope =
+      llvm::ScopedHashTableScope<ConstraintQuestion *,
+                                 pdl_interp::ApplyConstraintOp>;
 
   /// Generate interpreter operations for the tree rooted at the given matcher
   /// node, in the specified region.
@@ -148,9 +153,9 @@ struct PatternLowering {
   /// set.
   DenseMap<Operation *, PDLPatternConfigSet *> *configMap;
 
-  /// A mapping from a constraint question to the ApplyConstraintOp
+  /// A scoped mapping from a constraint question to the ApplyConstraintOp
   /// that implements it.
-  DenseMap<ConstraintQuestion *, pdl_interp::ApplyConstraintOp> constraintOpMap;
+  ConstraintOpMap constraintOpMap;
 };
 } // namespace
 
@@ -167,6 +172,7 @@ void PatternLowering::lower(ModuleOp module) {
 
   // Define top-level scope for the arguments to the matcher function.
   ValueMapScope topLevelValueScope(values);
+  ConstraintOpMapScope topLevelConstraintScope(constraintOpMap);
 
   // Insert the root operation, i.e. argument to the matcher, at the root
   // position.
@@ -191,6 +197,7 @@ Block *PatternLowering::generateMatcher(MatcherNode &node, Region &region,
   if (!block)
     block = &region.emplaceBlock();
   ValueMapScope scope(values);
+  ConstraintOpMapScope constraintScope(constraintOpMap);
 
   // If this is the return node, simply insert the corresponding interpreter
   // finalize.
@@ -375,9 +382,10 @@ Value PatternLowering::getValueAt(Block *&currentBlock, Position *pos) {
     // Due to the order of traversal, the ApplyConstraintOp has already been
     // created and we can find it in constraintOpMap.
     auto *constrResPos = cast<ConstraintPosition>(pos);
-    auto i = constraintOpMap.find(constrResPos->getQuestion());
-    assert(i != constraintOpMap.end());
-    value = i->second->getResult(constrResPos->getIndex());
+    pdl_interp::ApplyConstraintOp applyConstraintOp =
+        constraintOpMap.lookup(constrResPos->getQuestion());
+    assert(applyConstraintOp && "expected constraint to be generated");
+    value = applyConstraintOp->getResult(constrResPos->getIndex());
     break;
   }
   default:
@@ -469,7 +477,7 @@ void PatternLowering::generate(BoolNode *boolNode, Block *&currentBlock,
         builder, loc, cstQuestion->getResultTypes(), cstQuestion->getName(),
         args, cstQuestion->getIsNegated(), success, failure);
 
-    constraintOpMap.insert({cstQuestion, applyConstraintOp});
+    constraintOpMap.insert(cstQuestion, applyConstraintOp);
     break;
   }
   default:
diff --git a/mlir/test/Conversion/PDLToPDLInterp/pdl-to-pdl-interp-matcher.mlir b/mlir/test/Conversion/PDLToPDLInterp/pdl-to-pdl-interp-matcher.mlir
index 86c0a92f5c6eb..e6a02b9b5cd9d 100644
--- a/mlir/test/Conversion/PDLToPDLInterp/pdl-to-pdl-interp-matcher.mlir
+++ b/mlir/test/Conversion/PDLToPDLInterp/pdl-to-pdl-interp-matcher.mlir
@@ -130,6 +130,33 @@ module @constraint_with_result_multiple {
 
 // -----
 
+// CHECK-LABEL: module @constraint_with_result_in_sibling_branches
+module @constraint_with_result_in_sibling_branches {
+  // Check that when the same constraint is generated in sibling branches of the
+  // matcher tree, each branch uses the result of its own apply_constraint.
+
+  // CHECK: func @matcher(%[[ROOT:.*]]: !pdl.operation)
+  // CHECK: pdl_interp.switch_operation_name of %[[ROOT]] to ["test.a", "test.b"](^[[A:.*]], ^[[B:.*]]) -> ^{{.*}}
+  // CHECK: ^[[A]]:
+  // CHECK: %[[ATTR_A:.*]] = pdl_interp.apply_constraint "get_attr"(%[[ROOT]]
+  // CHECK: pdl_interp.record_match @rewriters::@rewrite_a(%[[ROOT]], %[[ATTR_A]] : !pdl.operation, !pdl.attribute)
+  // CHECK: ^[[B]]:
+  // CHECK: %[[ATTR_B:.*]] = pdl_interp.apply_constraint "get_attr"(%[[ROOT]]
+  // CHECK: pdl_interp.record_match @rewriters::@rewrite_b(%[[ROOT]], %[[ATTR_B]] : !pdl.operation, !pdl.attribute)
+  pdl.pattern @rewrite_a : benefit(1) {
+    %root = operation "test.a"
+    %attr = apply_native_constraint "get_attr"(%root : !pdl.operation) : !pdl.attribute
+    rewrite %root with "rewriter"(%attr : !pdl.attribute)
+  }
+  pdl.pattern @rewrite_b : benefit(1) {
+    %root = operation "test.b"
+    %attr = apply_native_constraint "get_attr"(%root : !pdl.operation) : !pdl.attribute
+    rewrite %root with "rewriter"(%attr : !pdl.attribute)
+  }
+}
+
+// -----
+
 // CHECK-LABEL: module @negated_constraint
 module @negated_constraint {
   // CHECK: func @matcher(%[[ROOT:.*]]: !pdl.operation)

``````````

</details>


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


More information about the Mlir-commits mailing list