Control Flow Operations¶
This document describes structured control flow operations from the MLIR scf (Structured Control Flow) dialect.
Total Operations: 9
Loop Operations¶
scf.for - For Loop¶
Description: For loop with lower bound, upper bound, and step. Supports loop-carried variables and signed/unsigned comparison.
Syntax:
scf.for %iv = %lb to %ub step %step {
// loop body
}
scf.for %iv = %lb to %ub step %step
iter_args(%arg = %init) -> (type) {
// loop body with loop-carried variable
scf.yield %new_value : type
}
scf.for unsigned %iv = %lb to %ub step %step : i32 {
// unsigned comparison
}
Operands:
- lb: Lower bound (index or integer)
- ub: Upper bound (exclusive)
- step: Step value (must be positive)
- iter_args: Initial values for loop-carried variables (optional)
Results: - Final values of loop-carried variables (if any)
Example:
// Simple loop
scf.for %i = %c0 to %c100 step %c1 {
// loop body
}
// Loop with accumulator
%sum = scf.for %i = %c0 to %c100 step %c1
iter_args(%acc = %c0_i32) -> (i32) {
%val = memref.load %array[%i] : memref<?xi32>
%new_acc = arith.addi %acc, %val : i32
scf.yield %new_acc : i32
}
// Unsigned comparison
scf.for unsigned %i = %lb to %ub step %step : i32 {
// loop body
}
scf.while - While Loop¶
Description: While loop with condition check in "before" region and loop body in "after" region.
Syntax:
%result = scf.while (%arg = %init) : (type) -> type {
// before region: condition check
%condition = ...
scf.condition(%condition) %arg : type
} do {
^bb0(%arg: type):
// after region: loop body
%next = ...
scf.yield %next : type
}
Regions:
- before: Condition check region (terminates with scf.condition)
- after: Loop body region (terminates with scf.yield)
Example:
%result = scf.while (%arg = %init) : (i32) -> i32 {
%condition = arith.cmpi slt, %arg, %limit : i32
scf.condition(%condition) %arg : i32
} do {
^bb0(%arg: i32):
%next = arith.addi %arg, %c1 : i32
scf.yield %next : i32
}
Conditional Operations¶
scf.if - If-Then-Else¶
Description: Conditional branch with optional else block and optional results.
Syntax:
scf.if %condition {
// then block
}
scf.if %condition {
// then block
} else {
// else block
}
%result = scf.if %condition -> type {
// then block
scf.yield %value : type
} else {
// else block
scf.yield %other : type
}
Operands:
- condition: Boolean condition (i1)
Results: - Values yielded from branches (if any)
Example:
// Simple if
scf.if %condition {
// then block
}
// If-else
scf.if %condition {
// then block
} else {
// else block
}
// If with results
%result = scf.if %condition -> i32 {
%value = arith.constant 1 : i32
scf.yield %value : i32
} else {
%value = arith.constant 0 : i32
scf.yield %value : i32
}
scf.index_switch - Index Switch¶
Description: Switch statement based on an index value with multiple cases and a default case.
Syntax:
%result = scf.index_switch %arg -> type
case 0 {
// case 0 block
scf.yield %value0 : type
}
case 1 {
// case 1 block
scf.yield %value1 : type
}
default {
// default block
scf.yield %default_value : type
}
Operands:
- arg: Index value to switch on
Attributes:
- cases: Array of case values
Example:
%result = scf.index_switch %idx -> i32
case 0 {
%c0 = arith.constant 10 : i32
scf.yield %c0 : i32
}
case 1 {
%c1 = arith.constant 20 : i32
scf.yield %c1 : i32
}
case 2 {
%c2 = arith.constant 30 : i32
scf.yield %c2 : i32
}
default {
%cd = arith.constant 0 : i32
scf.yield %cd : i32
}
Region Operations¶
scf.execute_region - Execute Region¶
Description: Execute a region exactly once. Allows multiple blocks within single-block contexts.
Syntax:
%result = scf.execute_region -> type {
// region body (can have multiple blocks)
scf.yield %value : type
}
%result = scf.execute_region -> type no_inline {
// region body with no_inline attribute
scf.yield %value : type
}
Attributes:
- no_inline: Optional flag to prevent inlining
Semantics: Executes the region exactly once. Useful for creating multi-block regions within operations that normally allow only single blocks.
Example:
// Simple execute region
%result = scf.execute_region -> i32 {
%x = arith.constant 42 : i32
scf.yield %x : i32
}
// With multiple blocks
%result = scf.execute_region -> i32 {
cf.cond_br %cond, ^bb1, ^bb2
^bb1:
%c1 = arith.constant 1 : i32
cf.br ^bb3(%c1 : i32)
^bb2:
%c2 = arith.constant 2 : i32
cf.br ^bb3(%c2 : i32)
^bb3(%arg: i32):
scf.yield %arg : i32
}
// With no_inline attribute
%result = scf.execute_region -> i32 no_inline {
%x = arith.constant 42 : i32
scf.yield %x : i32
}
Terminator Operations¶
scf.yield - Yield Values¶
Description: Terminates regions within SCF operations and yields values to parent operation.
Syntax:
scf.yield
scf.yield %value : type
scf.yield %value1, %value2 : type1, type2
Semantics: Used to terminate:
-
Loop bodies (
scf.for,scf.whileafter region) -
Conditional branches (
scf.if) -
Execute regions (
scf.execute_region) -
Switch cases (
scf.index_switch)
Example:
// Yield single value
scf.yield %value : i32
// Yield multiple values
scf.yield %a, %b : i32, f32
// Yield no values
scf.yield
scf.condition - Loop Continuation Condition¶
Description: Terminates the "before" region of scf.while. If condition is true, continues to "after" region; otherwise exits loop.
Syntax:
scf.condition(%condition) %args... : types...
Operands:
- condition: Boolean condition (i1)
- args: Values to pass to "after" region or return from loop
Semantics:
- If condition is true: execute "after" region with args
- If condition is false: exit loop and return args
Example:
// In scf.while before region
%keep_going = arith.cmpi slt, %i, %limit : i32
scf.condition(%keep_going) %i : i32
// With multiple values
%cond = arith.cmpi slt, %i, %limit : i32
scf.condition(%cond) %i, %sum : i32, i32
Common Patterns¶
Pattern 1: Simple Loop¶
scf.for %i = %c0 to %c100 step %c1 {
// loop body
}
Pattern 2: Loop with Accumulator¶
%sum = scf.for %i = %c0 to %c100 step %c1
iter_args(%acc = %c0) -> (i32) {
%val = memref.load %array[%i] : memref<?xi32>
%new_acc = arith.addi %acc, %val : i32
scf.yield %new_acc : i32
}
Pattern 3: Conditional with Results¶
%result = scf.if %cond -> i32 {
scf.yield %true_val : i32
} else {
scf.yield %false_val : i32
}
Pattern 4: While Loop¶
%final = scf.while (%arg = %init) : (i32) -> i32 {
%keep_going = arith.cmpi slt, %arg, %limit : i32
scf.condition(%keep_going) %arg : i32
} do {
^bb0(%arg: i32):
%next = arith.addi %arg, %c1 : i32
scf.yield %next : i32
}
Pattern 5: Nested Loops¶
scf.for %i = %c0 to %M step %c1 {
scf.for %j = %c0 to %N step %c1 {
// nested loop body
}
}
Pattern 6: Loop with Multiple Accumulators¶
%sum, %prod = scf.for %i = %c0 to %c100 step %c1
iter_args(%acc_sum = %c0, %acc_prod = %c1) -> (i32, i32) {
%val = memref.load %array[%i] : memref<?xi32>
%new_sum = arith.addi %acc_sum, %val : i32
%new_prod = arith.muli %acc_prod, %val : i32
scf.yield %new_sum, %new_prod : i32, i32
}