What are those magic variables in lowered code?

I’m trying to understand the result of @code_lowered macro. Consider this simple example:

julia> mutable struct Foo
           value
       end

julia> foo = Foo(1)
Foo(1)

julia> @code_lowered(foo.value = 2)
CodeInfo(
1 ─ %1 = (Base.typeof)(x)
│   %2 = (Base.fieldtype)(%1, f)
│   %3 = (Base.convert)(%2, v)
│   %4 = (Base.setfield!)(x, f, %3)
└──      return %4
)

I can largely follow the logic but got really confused with x, f, and v. If I match to the original code, I can derive that:

  • x = foo so %1 = Foo
  • f = :value so %2 = Any
  • v = 2 so %3 = 2
  • %4 = 2

Is there any reason why these values are not just displayed as-is? Ideally something like this:

CodeInfo(
1 ─ %1 = (Base.typeof)(foo)
│   %2 = (Base.fieldtype)(%1, :value)
│   %3 = (Base.convert)(%2, Int64(2))
│   %4 = (Base.setfield!)(foo, :value, %3)
└──      return %4
)

Also, is there a naming convention for these variables?

1 Like

The various @code_ macros operate on function calls NOT expressions. In particular, everything, but the outermost call is evaluated. Now, in your example, there’s an extra bit of confusion, because a.b = c is syntactic sugar for setproperty!(a, :b, c). As a result, the what you’re asking for is

julia> @code_lowered setproperty!(a, :b, c)

which then does faithfully show you the lowered code of the setproperty! function: https://github.com/JuliaLang/julia/blob/4f17558e4c71908e8ba40d24e37bdacd5e2f2e5e/base/Base.jl#L34. If you want to see the lowering of an expression, an easy way to do so is to put it in an anonymous function:

julia> @code_lowered((()->foo.value = 2)())
CodeInfo(
1 ─     Base.setproperty!(Main.foo, :value, 2)
└──     return 2
)

or by asking the lowering code directly (if you happen to care about scoping differences):

Meta.lower(Main, :(foo.value = 2))
:($(Expr(:thunk, CodeInfo(
    @ none within `top-level scope'
1 ─     Base.setproperty!(foo, :value, 2)
└──     return 2
))))
6 Likes