[Mlir-commits] [mlir] [MLIR] Prevent CF to SCF transform from erasing blocks still in use (PR #206310)

Martin P llvmlistbot at llvm.org
Wed Jul 1 02:43:23 PDT 2026


https://github.com/pechy updated https://github.com/llvm/llvm-project/pull/206310

>From 5922b05e230282299355360cec8e704c244ef79d Mon Sep 17 00:00:00 2001
From: Martin Petracek <martin at petracek.net>
Date: Mon, 29 Jun 2026 23:27:05 +0700
Subject: [PATCH] [MLIR] Verify that all blocks are reachable before CFG to SCF
 transform

CFG to SCF transform cannot handle unreachable code. Properly verify
that all blocks are reachable and document this limitation in the pass
description.

Fixes #206086
---
 mlir/include/mlir/Transforms/CFGToSCF.h       |  7 ++++---
 mlir/lib/Transforms/Utils/CFGToSCF.cpp        | 19 +++++++++++++++++--
 .../ControlFlowToSCF/unreachable-blocks.mlir  | 18 ++++++++++++++++++
 3 files changed, 39 insertions(+), 5 deletions(-)
 create mode 100644 mlir/test/Conversion/ControlFlowToSCF/unreachable-blocks.mlir

diff --git a/mlir/include/mlir/Transforms/CFGToSCF.h b/mlir/include/mlir/Transforms/CFGToSCF.h
index 517e116f3c58e..2ca019ddfe23d 100644
--- a/mlir/include/mlir/Transforms/CFGToSCF.h
+++ b/mlir/include/mlir/Transforms/CFGToSCF.h
@@ -149,9 +149,10 @@ class CFGToSCFInterface {
 /// Otherwise a single control flow graph operation branching to one block
 /// per return-like operation kind remains.
 ///
-/// The transformation currently requires that all control flow graph operations
-/// have no side effects, implement the BranchOpInterface and does not have any
-/// operation produced successor operands.
+/// The transformation currently requires that the region has no unreachable
+/// blocks and that all control flow graph operations have no side effects,
+/// implement the BranchOpInterface and does not have any operation produced
+/// successor operands.
 /// Returns failure if any of the preconditions are violated or if any of the
 /// methods of `interface` failed. The IR is left in an unspecified state.
 ///
diff --git a/mlir/lib/Transforms/Utils/CFGToSCF.cpp b/mlir/lib/Transforms/Utils/CFGToSCF.cpp
index 0cc6bb43ddd38..e2ef9d5076d0d 100644
--- a/mlir/lib/Transforms/Utils/CFGToSCF.cpp
+++ b/mlir/lib/Transforms/Utils/CFGToSCF.cpp
@@ -952,6 +952,7 @@ static FailureOr<SmallVector<Block *>> transformToStructuredCFBranches(
   if (regionEntry->getNumSuccessors() == 1) {
     // Single successor we can just splice together.
     Block *successor = regionEntry->getSuccessor(0);
+
     for (auto &&[oldValue, newValue] : llvm::zip(
              successor->getArguments(), getSuccessorOperands(regionEntry, 0)))
       oldValue.replaceAllUsesWith(newValue);
@@ -1234,10 +1235,24 @@ static ReturnLikeExitCombiner createSingleExitBlocksForReturnLike(
 /// Returns failure if any precondition is violated.
 static LogicalResult
 checkTransformationPreconditions(Region &region, CFGToSCFInterface &interface) {
-  for (Block &block : region.getBlocks())
-    if (block.hasNoPredecessors() && !block.isEntryBlock())
+  const auto reachableBlocks = [&region] {
+    SmallVector<Block *, 16> workList = {&region.front()};
+    SmallPtrSet<Block *, 16> visited = {&region.front()};
+    while (!workList.empty()) {
+      Block *current = workList.pop_back_val();
+      for (Block *succ : current->getSuccessors()) {
+        if (visited.insert(succ).second) {
+          workList.push_back(succ);
+        }
+      }
+    }
+    return visited;
+  }();
+  for (Block &block : region.getBlocks()) {
+    if (!reachableBlocks.contains(&block))
       return block.front().emitOpError(
           "transformation does not support unreachable blocks");
+  }
 
   WalkResult result = region.walk([](Operation *operation) {
     if (operation->getNumSuccessors() == 0)
diff --git a/mlir/test/Conversion/ControlFlowToSCF/unreachable-blocks.mlir b/mlir/test/Conversion/ControlFlowToSCF/unreachable-blocks.mlir
new file mode 100644
index 0000000000000..4317e5a5a8102
--- /dev/null
+++ b/mlir/test/Conversion/ControlFlowToSCF/unreachable-blocks.mlir
@@ -0,0 +1,18 @@
+// RUN: mlir-opt --lift-cf-to-scf -verify-diagnostics -split-input-file %s
+
+// Verify that --lift-cf-to-scf does not crash when it encounters an
+// unreachable blocks (dead code).  Instead it should emit a clean error.
+// See: https://github.com/llvm/llvm-project/issues/206086
+
+module {
+  func.func @single_return_statement(%arg0: i32) -> i32 {
+    return %arg0 : i32
+  ^bb1:  // pred: ^bb1
+    // expected-error at below {{'arith.constant' op transformation does not support unreachable blocks}}
+    %true = arith.constant true
+    cf.cond_br %true, ^bb2, ^bb1
+  ^bb2:  // pred: ^bb1
+    return %arg0 : i32
+  }
+}
+



More information about the Mlir-commits mailing list