julia> function test()
for i=1:1 end
end
test (generic function with 1 method)
julia> @code_warntype test()
MethodInstance for test()
from test() in Main at REPL[1]:1
Arguments
#self#::Core.Const(test)
Locals
@_2::Union{Nothing, Tuple{Int64, Int64}}
i::Int64
Body::Nothing
1 ─ %1 = (1:1)::Core.Const(1:1)
│ (@_2 = Base.iterate(%1))
│ %3 = (@_2::Core.Const((1, 1)) === nothing)::Core.Const(false)
│ %4 = Base.not_int(%3)::Core.Const(true)
└── goto #4 if not %4
2 ─ %6 = @_2::Core.Const((1, 1))
│ (i = Core.getfield(%6, 1))
│ %8 = Core.getfield(%6, 2)::Core.Const(1)
│ (@_2 = Base.iterate(%1, %8))
│ %10 = (@_2::Core.Const(nothing) === nothing)::Core.Const(true)
│ %11 = Base.not_int(%10)::Core.Const(false)
└── goto #4 if not %11
3 ─ Core.Const(:(goto %6))
4 ┄ return nothing
julia>
The for loop turns into calls to iterate(...)
, which returns the next value/state or nothing if the iterator is exhausted. It’s not problematic because of small union optimization – that’s why it’s not printed in red but orange.
5 Likes
It’s worth observing that a non-trivial version of this test is completely type stable:
julia> function test2()
x = rand(1,10)
y = rand(5,10)
for i=1:y
x += y
end
x
end
test2 (generic function with 1 method)
julia> @code_warntype test2()
MethodInstance for test2()
from test2() @ Main REPL[14]:1
Arguments
#self#::Core.Const(test2)
Locals
@_2::Union{}
y::Matrix{Float64}
x::Matrix{Float64}
i::Union{}
Body::Union{}
1 ─ (x = Main.rand(1, 10))
│ (y = Main.rand(5, 10))
│ (1:y)
│ Core.Const(:(@_2 = Base.iterate(%3)))
│ Core.Const(:(@_2 === nothing))
│ Core.Const(:(Base.not_int(%5)))
│ Core.Const(:(goto %17 if not %6))
│ Core.Const(:(@_2))
│ Core.Const(:(i = Core.getfield(%8, 1)))
│ Core.Const(:(Core.getfield(%8, 2)))
│ Core.Const(:(x = x + y))
│ Core.Const(:(@_2 = Base.iterate(%3, %10)))
│ Core.Const(:(@_2 === nothing))
│ Core.Const(:(Base.not_int(%13)))
│ Core.Const(:(goto %17 if not %14))
│ Core.Const(:(goto %8))
└── Core.Const(:(return x))
The original is dead code:
julia> @code_llvm test()
; @ REPL[8]:1 within `test`
define void @julia_test_1195() #0 {
top:
; @ REPL[8]:2 within `test`
ret void
}
With live code, the small union optimization is applied, making the body of the loop type-stable.
1 Like