|
| 1 | +# Lowered representation |
| 2 | + |
| 3 | +Let's start with a demonstration on simple function: |
| 4 | + |
| 5 | +```julia |
| 6 | +function summer(A::AbstractArray{T}) where T |
| 7 | + s = zero(T) |
| 8 | + for a in A |
| 9 | + s += a |
| 10 | + end |
| 11 | + return s |
| 12 | +end |
| 13 | + |
| 14 | +A = [1, 2, 5] |
| 15 | +``` |
| 16 | + |
| 17 | +ASTIntepreter2 uses the lowered representation of code: |
| 18 | + |
| 19 | +```julia |
| 20 | +julia> code = @code_lowered summer(A) |
| 21 | +CodeInfo( |
| 22 | +1 ─ s = (Main.zero)($(Expr(:static_parameter, 1))) |
| 23 | +│ %2 = A |
| 24 | +│ #temp# = (Base.iterate)(%2) |
| 25 | +│ %4 = #temp# === nothing |
| 26 | +│ %5 = (Base.not_int)(%4) |
| 27 | +└── goto #4 if not %5 |
| 28 | +2 ┄ %7 = #temp# |
| 29 | +│ a = (Core.getfield)(%7, 1) |
| 30 | +│ %9 = (Core.getfield)(%7, 2) |
| 31 | +│ s = s + a |
| 32 | +│ #temp# = (Base.iterate)(%2, %9) |
| 33 | +│ %12 = #temp# === nothing |
| 34 | +│ %13 = (Base.not_int)(%12) |
| 35 | +└── goto #4 if not %13 |
| 36 | +3 ─ goto #2 |
| 37 | +4 ┄ return s |
| 38 | +) |
| 39 | +``` |
| 40 | + |
| 41 | +To understand this package's internals, you need to familiarize yourself with these |
| 42 | +`CodeInfo` objects. The numbers on the left correspond to [basic blocks](https://en.wikipedia.org/wiki/Basic_block); |
| 43 | +when used in statements these are printed with a hash, e.g., in `goto #4 if not %6`, the |
| 44 | +`#4` refers to basic block 4. |
| 45 | +The numbers in the next column--e.g., `%1`, refer to [single static assignment (SSA) values](https://en.wikipedia.org/wiki/Static_single_assignment_form). |
| 46 | +Each statement (each line of this printout) corresponds to a single SSA value, |
| 47 | +but only those used later in the code are printed using assignment syntax. |
| 48 | +Wherever a previous SSA value is used, it's referenced by an `SSAValue` and printed as `%6`; |
| 49 | +for example, in `goto #4 if not %6`, the `%6` is the result of evaluating the 6th statement, |
| 50 | +which is `(Base.not_int)(%5)`, which in turn refers to the result of statement 5. |
| 51 | +Together lines 5 and 6 correspond to `!(#temp# === nothing)`. |
| 52 | +(The `#temp#` means that this was a generated variable name not present explicitly in the original source code.) |
| 53 | + |
| 54 | +Before diving into the details, let's first look at the statements themselves: |
| 55 | + |
| 56 | +```julia |
| 57 | +julia> code.code |
| 58 | +16-element Array{Any,1}: |
| 59 | + :(_3 = (Main.zero)($(Expr(:static_parameter, 1)))) |
| 60 | + :(_2) |
| 61 | + :(_4 = (Base.iterate)(%2)) |
| 62 | + :(_4 === nothing) |
| 63 | + :((Base.not_int)(%4)) |
| 64 | + :(unless %5 goto %16) |
| 65 | + :(_4) |
| 66 | + :(_5 = (Core.getfield)(%7, 1)) |
| 67 | + :((Core.getfield)(%7, 2)) |
| 68 | + :(_3 = _3 + _5) |
| 69 | + :(_4 = (Base.iterate)(%2, %9)) |
| 70 | + :(_4 === nothing) |
| 71 | + :((Base.not_int)(%12)) |
| 72 | + :(unless %13 goto %16) |
| 73 | + :(goto %7) |
| 74 | + :(return _3) |
| 75 | +``` |
| 76 | + |
| 77 | +You can see directly that the SSA assignments are implicit; they are not directly |
| 78 | +present in the statement list. |
| 79 | +The most noteworthy change here is the appearance of objects like `_3`, which are |
| 80 | +references that index into local variable slots: |
| 81 | + |
| 82 | +```julia |
| 83 | +julia> code.slotnames |
| 84 | +5-element Array{Any,1}: |
| 85 | + Symbol("#self#") |
| 86 | + :A |
| 87 | + :s |
| 88 | + Symbol("#temp#") |
| 89 | + :a |
| 90 | +``` |
| 91 | + |
| 92 | +When printing the whole `CodeInfo` object, these `slotnames` are substituted in. |
| 93 | +The types of objects that can be in `code.code` is well-described in the [Julia AST](https://docs.julialang.org/en/latest/devdocs/ast/) documentation. |
0 commit comments