[Mlir-commits] [mlir] 3d6ae4b - [MLIR] Fix infinite loop in tryFold in Graph Regions (#189232)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Fri Sep 11 01:24:07 PDT 2026


Author: Mehdi Amini
Date: 2026-09-11T08:24:02Z
New Revision: 3d6ae4bdf4c6d45d5e2f2c55c06f84568fde0fe0

URL: https://github.com/llvm/llvm-project/commit/3d6ae4bdf4c6d45d5e2f2c55c06f84568fde0fe0
DIFF: https://github.com/llvm/llvm-project/commit/3d6ae4bdf4c6d45d5e2f2c55c06f84568fde0fe0.diff

LOG: [MLIR] Fix infinite loop in tryFold in Graph Regions (#189232)

In a graph region (e.g. the module body), an op may use its own result
as an operand, creating a circular SSA dependency. When such an op was
passed to OpBuilder::tryFold, the do-while loop could spin forever:

1. foldCommutative moved the constant operand to the RHS (in-place fold,
foldResults still empty).
2. The next fold call returned the op's own result, signalling an
in-place fold, but foldSingleResultHook re-ran trait folds, found
nothing to swap, and returned success with empty foldResults.
  3. Step 2 repeated without end.

Fix: cap the number of in-place fold iterations at 64. Legitimate chains
are very short in practice (foldCommutative swaps once, then the op folds to
a value or constant), so the cap is never reached for correct IR. For the
circular-SSA case the loop now terminates and tryFold returns failure,
letting dialect conversion fall through to a conversion pattern instead of
looping.

An alternative could be for each iteration of the loop, snapshot the
operand list and attributes/properties. If a fold returns empty results
but the operands are unchanged, we have reached a fixpoint. Stop the
loop and return failure.
However this is more expensive for the common case, while fixing an edge
case.

Fixes #159675

Assisted-by: Claude Code

Added: 
    

Modified: 
    mlir/lib/IR/Builders.cpp
    mlir/test/Transforms/test-legalizer.mlir

Removed: 
    


################################################################################
diff  --git a/mlir/lib/IR/Builders.cpp b/mlir/lib/IR/Builders.cpp
index 98fdfb5dfd042..47e09faf0e04f 100644
--- a/mlir/lib/IR/Builders.cpp
+++ b/mlir/lib/IR/Builders.cpp
@@ -501,11 +501,21 @@ OpBuilder::tryFold(Operation *op, SmallVectorImpl<Value> &results,
   if (failed(op->fold(foldResults)))
     return cleanupFailure();
 
+  // Bound the number of in-place fold iterations. Legitimate chains are very
+  // short (e.g. foldCommutative swaps once, then the op folds to a value).
+  // Without a bound, circular SSA uses in graph regions can cause an infinite
+  // loop (e.g. addi(x, 0) where x is the op's own result).
+  constexpr int kMaxInPlaceFolds = 64;
   int count = 0;
   do {
     LDBG() << "Folded in place #" << count
            << " times: " << OpWithFlags(op, OpPrintingFlags().skipRegions());
-    count++;
+    if (++count >= kMaxInPlaceFolds) {
+      LDBG() << "Aborting after " << kMaxInPlaceFolds
+             << " in-place fold iterations: "
+             << OpWithFlags(op, OpPrintingFlags().skipRegions());
+      return cleanupFailure();
+    }
   } while (foldResults.empty() && succeeded(op->fold(foldResults)));
 
   // An in-place fold does not require generation of any constants.

diff  --git a/mlir/test/Transforms/test-legalizer.mlir b/mlir/test/Transforms/test-legalizer.mlir
index 96aeccfb7c161..1b20422298f74 100644
--- a/mlir/test/Transforms/test-legalizer.mlir
+++ b/mlir/test/Transforms/test-legalizer.mlir
@@ -496,3 +496,14 @@ func.func @test_preorder_legalization() {
   // expected-remark @+1 {{'func.return' is not legalizable}}
   return
 }
+
+// -----
+
+// Regression test for https://github.com/llvm/llvm-project/issues/159675.
+// In a graph region, SSA values may be used before their definitions. Folding
+// the addi below first swaps its operands, then repeatedly attempts an
+// in-place self-fold. The legalizer must stop rather than looping forever.
+// expected-remark at +1 {{op 'arith.addi' is not legalizable}}
+%0 = "arith.addi"(%1, %0) <{overflowFlags = #arith.overflow<none>}> : (index, index) -> index
+// expected-remark at +1 {{op 'arith.constant' is not legalizable}}
+%1 = "arith.constant"() <{value = 0 : index}> : () -> index


        


More information about the Mlir-commits mailing list