[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:40:44 PDT 2026
https://github.com/pechy updated https://github.com/llvm/llvm-project/pull/206310
>From 0032210bc80986c4fd3a61e2f193a1be24ef55c0 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 | 22 ++++++++++++++++---
.../ControlFlowToSCF/unreachable-blocks.mlir | 18 +++++++++++++++
3 files changed, 41 insertions(+), 6 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..28362abf3300d 100644
--- a/mlir/lib/Transforms/Utils/CFGToSCF.cpp
+++ b/mlir/lib/Transforms/Utils/CFGToSCF.cpp
@@ -950,8 +950,10 @@ static FailureOr<SmallVector<Block *>> transformToStructuredCFBranches(
return SmallVector<Block *>{};
if (regionEntry->getNumSuccessors() == 1) {
- // Single successor we can just splice together.
+ // Single successor we can splice together, if they are not self-loops and
+ // the successor's only predecessor is `regionEntry`.
Block *successor = regionEntry->getSuccessor(0);
+
for (auto &&[oldValue, newValue] : llvm::zip(
successor->getArguments(), getSuccessorOperands(regionEntry, 0)))
oldValue.replaceAllUsesWith(newValue);
@@ -1234,10 +1236,24 @@ static ReturnLikeExitCombiner createSingleExitBlocksForReturnLike(
/// Returns failure if any precondition is violated.
static LogicalResult
checkTransformationPreconditions(Region ®ion, CFGToSCFInterface &interface) {
- for (Block &block : region.getBlocks())
- if (block.hasNoPredecessors() && !block.isEntryBlock())
+ auto reachableBlocks = [®ion] {
+ SmallVector<Block *, 16> workList = {®ion.front()};
+ SmallPtrSet<Block *, 16> visited = {®ion.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