Strange @code_warntype

hello, I’ve tried to ensure type stability by using @code_warntype. However, something very strange happened: In the following, closureB() only differs from closureA() with a leading if-end block, which is irrelevant actually. But, a lot of types in gg() becomes ANY, is it a bug??? I’m using JuliaPro-0.6.3.1.

function closureA(x::Int64)::Function
    oneto3 = Base.OneTo(3)

    function f()
        for i in oneto3
            println(i)
        end
    end

    return f
end

function closureB(x::Int64)::Function
    ### the only difference with closureA()
    if x < 0
        error("abc")
    end
    ### the only difference with closureB()

    oneto3 = Base.OneTo(3)

    function f()
        for i in oneto3
            println(i)
        end
    end

    return f
end

ff = closureA(1)
gg = closureB(1)

julia> @code_warntype ff()
Variables:
  #self#::#f#5{Base.OneTo{Int64}}
  i::Int64
  #temp#::Int64

Body:
  begin
      SSAValue(0) = (Core.getfield)(#self#::#f#5{Base.OneTo{Int64}}, :oneto3)::Base.OneTo{Int64}
      #temp#::Int64 = 1
      3:
      unless (Base.not_int)((#temp#::Int64 === (Base.add_int)((Core.getfield)(SSAValue(0), :stop)::Int64, 1)::Int64)::Bool)::Bool goto 18
      SSAValue(3) = #temp#::Int64
      SSAValue(4) = (Base.add_int)(#temp#::Int64, 1)::Int64
      i::Int64 = SSAValue(3)
      #temp#::Int64 = SSAValue(4) # line 6:
      $(Expr(:inbounds, false))
      # meta: location coreio.jl println 5
      SSAValue(2) = (Core.typeassert)(Base.STDOUT, Base.IO)::IO
      # meta: pop location
      $(Expr(:inbounds, :pop))
      (Base.print)(SSAValue(2), i::Int64, $(QuoteNode('\n')))::Void
      16:
      goto 3
      18:
      return
  end::Void

julia> @code_warntype gg()
Variables:
  #self#::#f#8
  i::Any
  #temp#::Any

Body:
  begin
      SSAValue(0) = (Core.getfield)((Core.getfield)(#self#::#f#8, :oneto3)::Any, :contents)::Any
      #temp#::Any = (Base.start)(SSAValue(0))::Any
      3:
      unless !((Base.done)(SSAValue(0), #temp#::Any)::Any)::Any goto 12
      SSAValue(1) = (Base.next)(SSAValue(0), #temp#::Any)::Any
      i::Any = (Core.getfield)(SSAValue(1), 1)::Any
      #temp#::Any = (Core.getfield)(SSAValue(1), 2)::Any # line 12:
      (Main.println)(i::Any)::Void
      10:
      goto 3
      12:
      return
  end::Void


Tried your code on v1.0. It works fine:

julia> @code_warntype gg()
Body::Nothing
11 1 ── %1  = (Core.getfield)(#self#, :oneto3)::Base.OneTo{Int64}   
   │    %2  = (Base.getfield)(%1, :stop)::Int64                 │╻╷╷ iterate
   │    %3  = (Base.slt_int)(%2, 1)::Bool                       ││╻   isempty
   └───       goto #3 if not %3                                 ││  
   2 ──       goto #4                                           ││  
   3 ──       goto #4                                           ││  
   4 ┄─ %7  = φ (#2 => true, #3 => false)::Bool                 │   
   │    %8  = φ (#3 => 1)::Int64                                │   
   │    %9  = φ (#3 => 1)::Int64                                │   
   │    %10 = (Base.not_int)(%7)::Bool                          │   
   └───       goto #10 if not %10                               │   
   5 ┄─ %12 = φ (#4 => %8, #9 => %21)::Int64                    │   
   │    %13 = φ (#4 => %9, #9 => %22)::Int64                    │   
12 │          invoke Main.println(%12::Int64)                   │   
   │    %15 = (Base.getfield)(%1, :stop)::Int64                 ││╻╷  last
   │    %16 = (%13 === %15)::Bool                               ││╻   ==
   └───       goto #7 if not %16                                ││  
   6 ──       goto #8                                           ││  
   7 ── %19 = (Base.add_int)(%13, 1)::Int64                     ││╻   +
   └───       goto #8                                           │╻   iterate
   8 ┄─ %21 = φ (#7 => %19)::Int64                              │   
   │    %22 = φ (#7 => %19)::Int64                              │   
   │    %23 = φ (#6 => true, #7 => false)::Bool                 │   
   │    %24 = (Base.not_int)(%23)::Bool                         │   
   └───       goto #10 if not %24                               │   
   9 ──       goto #5                                           │   
   10 ─       return                                            │   

The issue to track for this is performance of captured variables in closures · Issue #15276 · JuliaLang/julia · GitHub.

thanks.

just a silly question: could I have more than one version of Julia installed? (wanna know before crashing everything)

could I have more than one version of Julia installed? (wanna know before crashing everything)

I am not the perfect person to answer this question. I only know, in Linux, you can download the binaries for various versions, but I’m not sure whether the packages of various versions are by default installed at the same place (hopefully not).

Yes, there is no problem with multiple installations.

performance-wise, should I treat those “captured variables in closure” like global variables and declare them with const ? for example:


function closure(x::Int64)
    const y = fill(x, 3)        # "const" needed here for performance ???

    function f(z::Int64)
        y .+= z
        println(y)
    end

    return f
end

const doesn’t do anything in local scope. There are some workaround in that issue including using let blocks and type annotations.

could you kindly give an example (when u’re free) ?

I just don’t know how to let the compiler knows the type of those “captured variables in closure” when the function is “generated”.

julia> function closureB(x::Int64)::Function
           ### the only difference with closureA()
           if x < 0
               error("abc")
           end
           ### the only difference with closureB()

           oneto3 = Base.OneTo(3)
           local f
           let oneto3 = oneto3
           function f()
               for i in oneto3
                   println(i)
               end
           end
           end

           return f
       end

closureB (generic function with 1 method)

julia> gg = closureB(1)
(::f) (generic function with 1 method)

julia> @code_warntype gg()
Variables:
  #self#::#f#6{Base.OneTo{Int64}}
  i::Int64
  #temp#::Int64

Body:
  begin
      SSAValue(0) = (Core.getfield)(#self#::#f#6{Base.OneTo{Int64}}, Symbol("#4#oneto3"))::Base.OneTo{Int64}
      #temp#::Int64 = 1
      3:
      unless (Base.not_int)((#temp#::Int64 === (Base.add_int)((Core.getfield)(SSAValue(0), :stop)::Int64, 1)::Int64)::Bool)::Bool goto 18
      SSAValue(3) = #temp#::Int64
      SSAValue(4) = (Base.add_int)(#temp#::Int64, 1)::Int64
      i::Int64 = SSAValue(3)
      #temp#::Int64 = SSAValue(4) # line 13:
      $(Expr(:inbounds, false))
      # meta: location coreio.jl println 5
      SSAValue(2) = (Core.typeassert)(Base.STDOUT, Base.IO)::IO
      # meta: pop location
      $(Expr(:inbounds, :pop))
      (Base.print)(SSAValue(2), i::Int64, $(QuoteNode('\n')))::Void
      16:
      goto 3
      18:
      return
  end::Void
1 Like