[Mlir-commits] [mlir] [MLIR] Introduce support for early exits (PR #166688)
Matthias Springer
llvmlistbot at llvm.org
Fri Jun 12 03:18:10 PDT 2026
================
@@ -565,6 +580,87 @@ func.func @accelerator_compute(i64, i1) -> i64 { // An SSACFG region
}
```
+#### Region-Exit Terminators
+
+A terminator that implements `RegionExitTerminatorOpInterface` terminates the
+current region and identifies the operation that may receive the region exit. A
+normal region exit can target the immediately containing operation. For
+example, `scf.yield` exits only its own immediately enclosing region and
+returns control to the parent operation.
+
+Some terminators instead request a breaking-control-flow event addressed to a
+specific ancestor. The terminator transfers control back only to its immediate
+parent operation; reaching the addressed ancestor requires each intermediate
+parent operation to collaborate by propagating the request outward. The way a
+terminator designates the ancestor is dialect-defined. For example, the SCF
+dialect uses a builtin `token` value: `scf.loop` defines a control token as an
+entry block argument, and `scf.break` / `scf.continue` consume that token to
+identify the loop that should ultimately receive the early-exit event.
+
+Every intermediate operation between a breaking terminator and the addressed
+receiver must define `PropagateControlFlowBreak`. The receiver must implement
+`HasBreakingControlFlowOpInterface`. For example, a loop operation nested
+inside another loop operation body carries both traits simultaneously: it
+handles breaks whose token identifies itself, and propagates breaks whose token
+identifies an outer loop.
+
+Region-exit terminators may carry values, which are propagated to the
+target operation. For example, when breaking out of a loop that produces
+results, the terminator supplies those result values. The exact mapping between
+terminator operands and the receiving op's results is dialect-defined (the
+receiving op may also ignore operands entirely).
+
+Examples:
+
+```mlir
+// scf.yield is the standard immediate region-exit terminator.
+// It exits only its own immediately enclosing region.
+scf.if %cond {
+ scf.yield // returns control to the immediate parent of scf.if
+}
+```
+
+```mlir
+// Trait legend:
+// [H] = HasBreakingControlFlowOpInterface (receives/catches the break)
+// [P] = PropagateControlFlowBreak (passes the break request upward)
+// [H][P] = both: handles breaks whose token identifies it, and propagates
+// breaks whose token identifies an outer loop
+scf.loop token(%outer) { // [H]
+ scf.loop token(%inner) { // [H][P]
----------------
matthias-springer wrote:
nit: I would inverse the notation:
```
// Trait legend:
// [H] = receives/catches the break (via HasBreakingControlFlowOpInterface)
```
The current wording could be misinterpreted as "the second scf.loop has the [P] trait, but the first one does not":
```
scf.loop token(%outer) { // [H]
scf.loop token(%inner) { // [H][P]
```
https://github.com/llvm/llvm-project/pull/166688
More information about the Mlir-commits
mailing list