[Mlir-commits] [mlir] [mlir][scf] Declare ControlFlow as a dependent dialect (PR #216852)
Alessandro Potenza
llvmlistbot at llvm.org
Mon Aug 17 14:50:53 PDT 2026
https://github.com/alepot55 created https://github.com/llvm/llvm-project/pull/216852
The canonicalization of a multi-block `scf.execute_region` creates `cf.br`
operations (`SCF.cpp`, `MultiBlockExecuteInliner`), but the SCF dialect does not
declare `cf::ControlFlowDialect` among its `dependentDialects`. On input where
nothing else has loaded the ControlFlow dialect, `mlir-opt --canonicalize` dies:
```mlir
llvm.func @f(%c: i1, %p: !llvm.ptr, %x: i64) {
scf.execute_region {
llvm.cond_br %c, ^bb1, ^bb2
^bb1:
llvm.store %x, %p : i64, !llvm.ptr
scf.yield
^bb2:
scf.yield
}
llvm.return
}
```
```
LLVM ERROR: cf.br created with unregistered dialect. If this is intended, please
call allowUnregisteredDialects() on the MLIRContext, or use
-allow-unregistered-dialect with the MLIR tool used.
```
**The decisive control:** prepend an unrelated function that merely mentions
`cf.br` and the exact same `@f` canonicalizes cleanly. Correctness here depends
on whether some other operation in the file happened to load the ControlFlow
dialect first, which is the symptom a `dependentDialects` entry exists to prevent.
The repro needs an `llvm.func` rather than a `func.func` parent only because
Func's inliner extension already loads ControlFlow; that is why no in-tree test
caught this. I checked: no existing test exercises the broken path (no test file
runs canonicalization over `scf.` IR without either `func.func` or a `cf` op).
Build modes, stated precisely: with assertions the abort comes from the
`Operation` constructor (`Operation.cpp`, guarded by `#ifndef NDEBUG`); in a pure
NDEBUG build the op is built as unregistered and the failure surfaces in the pass
verifier (`Verifier.cpp`, not NDEBUG-guarded). Either way `mlir-opt --canonicalize`
fails on valid input, so the test fails before and passes after in every configuration.
No CMake change is needed: `MLIRControlFlowDialect` is already in `LINK_LIBS PUBLIC`
for `MLIRSCFDialect`, and `SCF.cpp` already includes `ControlFlowOps.h`. There is no
dependency cycle: `cf` depends only on `arith`, which declares none.
### Verified by execution
- Crash reproduces on `d4e78d7f5`; new test fails on unpatched `mlir-opt`, passes with the patch.
- No regression: `Dialect/SCF` 46/46, plus MemRef, Affine, Vector, XeGPU, Transforms (368 total).
- Loading `cf` cannot perturb unrelated output: `ControlFlowDialect::initialize` registers no
dialect-level canonicalization patterns and no `OpAsmDialectInterface`, so no aliases change.
---
Assisted-by: Claude (Anthropic)
This patch was written with AI assistance, disclosed per the LLVM AI Tool Use Policy.
Everything reported above as verified was verified by building and running.
>From 97cf699537d869feca9c54991606173776724486 Mon Sep 17 00:00:00 2001
From: Alessandro Potenza <ap.alessandro.potenza at gmail.com>
Date: Mon, 17 Aug 2026 23:00:38 +0200
Subject: [PATCH] [mlir][scf] Declare ControlFlow as a dependent dialect
The canonicalization of a multi-block `scf.execute_region` inlines the
region into its parent and materializes `cf.br` operations for the region
entry edge and for every `scf.yield`. The SCF dialect never declared a
dependency on the ControlFlow dialect, so those `cf.br` ops are built even
when `cf` was never loaded into the MLIRContext.
The failure is masked in almost every test because something else in the
input happens to load `cf` first: any `cf` operation in the file loads it
at parse time, and loading the Func dialect loads it too, since the Func
inliner extension calls `getOrLoadDialect<cf::ControlFlowDialect>()`.
Outside of `func.func`, and with no `cf` op anywhere else in the file,
`mlir-opt --canonicalize` fails on valid input.
Add `cf::ControlFlowDialect` to the dialect's `dependentDialects` and add
a regression test that keeps the repro free of both `func.func` and `cf`.
`MLIRControlFlowDialect` is already a public link dependency of
`MLIRSCFDialect`, and `SCF.cpp` already includes `ControlFlowOps.h` before
the generated dialect definition, so no build changes are needed.
---
mlir/include/mlir/Dialect/SCF/IR/SCFOps.td | 4 ++-
.../SCF/canonicalize-dependent-dialects.mlir | 25 +++++++++++++++++++
2 files changed, 28 insertions(+), 1 deletion(-)
create mode 100644 mlir/test/Dialect/SCF/canonicalize-dependent-dialects.mlir
diff --git a/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td b/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td
index 1e7bba2cdad35..a0af0c59a37b5 100644
--- a/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td
+++ b/mlir/include/mlir/Dialect/SCF/IR/SCFOps.td
@@ -42,7 +42,9 @@ def SCF_Dialect : Dialect {
and then lowered to some final target like LLVM or SPIR-V.
}];
- let dependentDialects = ["arith::ArithDialect"];
+ // The canonicalization of a multi-block `scf.execute_region` materializes
+ // `cf.br` operations, so the ControlFlow dialect must always be loaded.
+ let dependentDialects = ["arith::ArithDialect", "cf::ControlFlowDialect"];
}
// Base class for SCF dialect ops.
diff --git a/mlir/test/Dialect/SCF/canonicalize-dependent-dialects.mlir b/mlir/test/Dialect/SCF/canonicalize-dependent-dialects.mlir
new file mode 100644
index 0000000000000..d5eb932eb59d9
--- /dev/null
+++ b/mlir/test/Dialect/SCF/canonicalize-dependent-dialects.mlir
@@ -0,0 +1,25 @@
+// RUN: mlir-opt %s --canonicalize | FileCheck %s
+
+// Canonicalizing a multi-block `scf.execute_region` materializes `cf.br` ops,
+// so the SCF dialect has to declare a dependency on the ControlFlow dialect.
+// This test deliberately contains no `cf` operation and does not use
+// `func.func`: parsing a `cf` operation, or loading the Func dialect (whose
+// inliner extension loads ControlFlow), would load the dialect for unrelated
+// reasons and mask the missing dependency.
+
+// CHECK-LABEL: llvm.func @multi_block_execute_region
+// CHECK-NOT: scf.execute_region
+// CHECK: llvm.cond_br
+// CHECK: llvm.store
+// CHECK: llvm.return
+llvm.func @multi_block_execute_region(%c: i1, %p: !llvm.ptr, %x: i64) {
+ scf.execute_region {
+ llvm.cond_br %c, ^bb1, ^bb2
+ ^bb1:
+ llvm.store %x, %p : i64, !llvm.ptr
+ scf.yield
+ ^bb2:
+ scf.yield
+ }
+ llvm.return
+}
More information about the Mlir-commits
mailing list