[Mlir-commits] [mlir] [MLIR] Introduce support for early exits (PR #166688)
Matthias Springer
llvmlistbot at llvm.org
Fri Jun 12 03:18:11 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]
+ scf.if %cond1 { // [P]
+ // Requests a break of the inner loop.
+ scf.break [%inner]
+ }
+ scf.if %cond2 { // [P]
+ // Requests a break of the outer loop. The scf.if and inner scf.loop
+ // both propagate the request upward.
+ scf.break [%outer]
+ }
+ scf.if %cond3 { // [P]
+ // Requests re-entry into the inner loop.
+ scf.continue [%inner]
+ }
+ }
+}
+return
+```
+
+```mlir
+// A loop that yields a result value on early exit.
+// scf.break carries operands that become the loop's results. (scf.continue
+// would instead carry operands that become the next iteration's iter_args,
+// but this loop has none.)
+%result = scf.loop token(%loop) -> f32 { // [H]
+ scf.if %found { // [P]
----------------
matthias-springer wrote:
nit: indentation of `[P]`
https://github.com/llvm/llvm-project/pull/166688
More information about the Mlir-commits
mailing list