[Mlir-commits] [mlir] [mlir] Fix non-termination when a block becomes unreachable during rewriting (PR #221152)

Vijay Kandiah llvmlistbot at llvm.org
Fri Sep 4 00:07:39 PDT 2026


https://github.com/VijayKandiah created https://github.com/llvm/llvm-project/pull/221152

`GreedyPatternRewriteDriver` erases unreachable blocks at the top of each iteration here https://github.com/llvm/llvm-project/blob/85081add2d8b401e5f5ee67cf4af11531560f3bf/mlir/lib/Transforms/Utils/GreedyPatternRewriteDriver.cpp#L892-L899


This handles blocks that are unreachable in the input, but not a block that becomes unreachable *during* `processWorklist()`, which then never returns to give `eraseUnreachableBlocks` another turn. 

Here's a reproducer:

```mlir
// test.mlir
func.func @loop(%cond: i1, %init: i32) {
  %true = arith.constant true
  %c1 = arith.constant 1 : i32
  cf.cond_br %true, ^exit, ^header(%init : i32)
^header(%iv: i32):
  cf.cond_br %cond, ^exit, ^latch
^latch:
  %next = arith.addi %iv, %c1 : i32
  cf.br ^header(%next : i32)
^exit:
  return
}
```

`mlir-opt test.mlir -canonicalize` never terminates: folding `%true` removes the only entry edge into `^header`, so the loop becomes unreachable while the worklist is still running.

This PR skips operations that use their own result, leaving them to hit `eraseUnreachableBlocks` on the next iteration. Such operations are legal in unreachable code.

Assisted-by: Claude Opus 5 (Cursor)

>From b092285a03728a93630247e72e3b9579f8d8c73a Mon Sep 17 00:00:00 2001
From: Vijay Kandiah <vkandiah at nvidia.com>
Date: Fri, 4 Sep 2026 00:01:22 -0700
Subject: [PATCH] [mlir] Fix non-termination when a block becomes unreachable
 during rewriting

---
 .../Utils/GreedyPatternRewriteDriver.cpp      |  9 +++++++++
 mlir/test/Transforms/canonicalize-dce.mlir    | 20 +++++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/mlir/lib/Transforms/Utils/GreedyPatternRewriteDriver.cpp b/mlir/lib/Transforms/Utils/GreedyPatternRewriteDriver.cpp
index b71194e783ba0..8c5c39992d512 100644
--- a/mlir/lib/Transforms/Utils/GreedyPatternRewriteDriver.cpp
+++ b/mlir/lib/Transforms/Utils/GreedyPatternRewriteDriver.cpp
@@ -482,6 +482,15 @@ bool GreedyPatternRewriteDriver::processWorklist() {
       }
     });
 
+    // Folding an operation that uses its own result can loop forever, so
+    // skip it. Such operations are legal in unreachable code.
+    if (llvm::any_of(op->getOperands(), [op](Value operand) {
+          return operand.getDefiningOp() == op;
+        })) {
+      LLVM_DEBUG(logResultWithLine("failure", "operation uses its own result"));
+      continue;
+    }
+
     // If the operation is trivially dead - remove it.
     if (isOpTriviallyDead(op)) {
       rewriter.eraseOp(op);
diff --git a/mlir/test/Transforms/canonicalize-dce.mlir b/mlir/test/Transforms/canonicalize-dce.mlir
index 84631947970de..57b5832c71dbd 100644
--- a/mlir/test/Transforms/canonicalize-dce.mlir
+++ b/mlir/test/Transforms/canonicalize-dce.mlir
@@ -187,3 +187,23 @@ func.func @f() {
   %0 = "test.test_effects_result"() : () -> i32
   return
 }
+
+// -----
+
+// Test case: Terminate when a block becomes unreachable while patterns run.
+
+// CHECK:      func @f
+// CHECK-NEXT:   return
+
+func.func @f(%cond: i1, %init: i32) {
+  %true = arith.constant true
+  %c1 = arith.constant 1 : i32
+  cf.cond_br %true, ^exit, ^header(%init : i32)
+^header(%iv: i32):
+  cf.cond_br %cond, ^exit, ^latch
+^latch:
+  %next = arith.addi %iv, %c1 : i32
+  cf.br ^header(%next : i32)
+^exit:
+  return
+}



More information about the Mlir-commits mailing list