Skip to content

Commit 7106db4

Browse files
authored
document scf dialect (#222)
fix #221
1 parent 9f76eb7 commit 7106db4

File tree

1 file changed

+114
-1
lines changed

1 file changed

+114
-1
lines changed

docs/dialects/scf.md

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,120 @@
55

66
# SCF Dialects
77

8-
# Reference
8+
The structured control flow (SCF) dialect is a dialect we adopt from the MLIR project with modifications to better fit the semantics of Python. This page will explain the SCF dialects semantics and how they are used.
9+
10+
## `scf.Yield`
11+
12+
The `scf.Yield` statement is used to mark the end of a block and yield to the region parent. It is used in the following way, for example with `scf.if` statement:
13+
14+
```mlir
15+
%value_1 = scf.if %cond {
16+
// body
17+
scf.yield %value
18+
} else {
19+
// body
20+
scf.yield %value
21+
}
22+
```
23+
24+
`scf.Yield` marks that the `%value` will be returned to the parent statement as its result. Unlike MLIR,
25+
most of the Kirin scf dialect can also terminate with `func.Return` statement to make things easier to lower from Python.
26+
27+
## `scf.If`
28+
29+
The `scf.If` statement is used to conditionally execute a block of code. It is used in the following way:
30+
31+
```mlir
32+
scf.if %cond {
33+
// body
34+
} else {
35+
// body
36+
}
37+
```
38+
39+
**Definition** The `scf.If` statement can have a `cond` argument, a `then_body` region with single block, and optionally a `else_body` with single block. The `then_body` block is executed if the condition is true, and the `else_body` block is executed if the condition is false.
40+
41+
**Termination** `then_body` must terminate with `scf.Yield` or `func.Return` statement. `else_body` is optional and can be omitted. If one of the body terminates with `scf.Yield` the other body must terminate explicitly with `scf.Yield` or `func.Return`.
42+
43+
## `scf.For`
44+
45+
The `scf.For` statement is used to iterate over a range of values. It is used in the following way:
46+
47+
```python
48+
def simple_loop():
49+
j = 0.0
50+
for i in range(10):
51+
j = j + i
52+
return j
53+
```
54+
55+
lowered to the following IR:
56+
57+
```llvm
58+
func.func simple_loop() -> !Any {
59+
^0(%simple_loop_self):
60+
│ %j = py.constant.constant 0.0
61+
│ %0 = py.constant.constant IList(range(0, 10))
62+
│ %j_1 = py.constant.constant 45.0
63+
│ %j_2 = scf.for %i in %0
64+
│ │ iter_args(%j_3 = %j) {
65+
│ │ %j_4 = py.binop.add(%j_3, %i)
66+
│ │ scf.yield %j_4
67+
│ }
68+
│ func.return %j_1
69+
} // func.func simple_loop
70+
```
71+
72+
**Definition** The [`scf.For`][kirin.dialects.scf.For] statement takes an `iterable` as an argument.
73+
74+
!!! note
75+
Unlike MLIR where the loop iterable is restricted to a step range, Kirin allows any Python iterable object to be used as a loop iterable by marking this iterable argument as `ir.types.Any`. While it can be any Python iterable object, the actual loop compilation can only happen if the iterable type is known and supported by the compiler implementation.
76+
77+
[`scf.For`][kirin.dialects.scf.For] can also take an optional `initializers` tuple of values that are used to initialize the loop variables (printed as right-hand side of the `iter_args` field).
78+
79+
**Termination** The loop body must terminate with `scf.Yield` or `func.Return` statement.
80+
81+
**Scoping** The loop body creates a new scope. As a result of this, any variables defined inside the loop body are not accessible outside the loop body unless they are explicitly yielded.
82+
83+
!!! warning "Known difference with Python `for` loop"
84+
The [`scf.For`][kirin.dialects.scf.For] statement does not follow exactly the same semantics as Python `for` loop. This difference is due to the context difference of compilation vs. interpretation. Like many other compiled languages, the loop body introduces a new scope and the loop variable is not accessible outside the loop body, e.g the following code will error in Julia:
85+
86+
```julia
87+
function simple_loop()
88+
for i in 1:10
89+
j = j + i
90+
if j > 5
91+
return j
92+
end
93+
end
94+
return j
95+
end
96+
```
97+
will error with `UndefVarError`:
98+
99+
```julia
100+
julia> simple_loop()
101+
ERROR: UndefVarError: `j` not defined in local scope
102+
Suggestion: check for an assignment to a local variable that shadows a global of the same name.
103+
Stacktrace:
104+
[1] simple_loop()
105+
@ Main ./REPL[1]:3
106+
[2] top-level scope
107+
@ REPL[2]:1
108+
```
109+
110+
However, in Python this code will work due to the fact that interpreter will not actually create a new scope for the loop body:
111+
112+
```python
113+
def simple_loop():
114+
for i in range(10):
115+
j = j + i
116+
if j == 5:
117+
return j
118+
return j # will refer to the j defined in the loop body
119+
```
120+
121+
## Reference
9122

10123
::: kirin.dialects.scf.stmts
11124
options:

0 commit comments

Comments
 (0)