Type inference on linspace fails when given Float32 arguments on 0.6.1?


#1
julia> function cwt(a, b) 
           for i ∈ linspace(a, b, 4)
               println(i, " ", typeof(i))
           end
       end
cwt (generic function with 1 method)

julia> cwt(0f0, 3f0)
0.0 Float32
1.0 Float32
2.0 Float32
3.0 Float32

julia> @code_warntype cwt(0f0, 3f0)
Variables:
  #self# <optimized out>
  a::Float32
  b::Float32
  i::Any
  #temp#@_5::Any
  #temp#@_6::Core.MethodInstance
  #temp#@_7::Union{Int64, Tuple{Any,Int64}}
  #temp#@_8 <optimized out>
  #temp#@_9 <optimized out>

Body:
  begin 
      SSAValue(0) = $(Expr(:invoke, MethodInstance for linspace(::Float32, ::Float32, ::Int64), :(Main.linspace), :(a), :(b), 4))
      unless (SSAValue(0) isa StepRangeLen{Float32,Base.TwicePrecision{Float32},Base.TwicePrecision{Float32}})::Bool goto 5
      #temp#@_6::Core.MethodInstance = MethodInstance for start(::StepRangeLen{Float32,Base.TwicePrecision{Float32},Base.TwicePrecision{Float32}})
      goto 10
      5: 
      goto 7
      7: 
      #temp#@_7::Union{Int64, Tuple{Any,Int64}} = (Base.start)(SSAValue(0))::Union{Int64, Tuple{Any,Int64}}
      goto 12
      10: 
      #temp#@_7::Union{Int64, Tuple{Any,Int64}} = $(Expr(:invoke, :(#temp#@_6), :(Base.start), SSAValue(0)))
      12: 
      #temp#@_5::Any = #temp#@_7::Union{Int64, Tuple{Any,Int64}}
      14: 
      unless !((Base.done)(SSAValue(0), #temp#@_5::Any)::Any)::Any goto 23
      SSAValue(1) = (Base.next)(SSAValue(0), #temp#@_5::Any)::Tuple{Any,Any}
      i::Any = (Core.getfield)(SSAValue(1), 1)::Any
      #temp#@_5::Any = (Core.getfield)(SSAValue(1), 2)::Any # line 3:
      (Main.println)(i::Any, " ", (Main.typeof)(i::Any)::DataType)::Void
      21: 
      goto 14
      23: 
      return
  end::Void

julia> @code_warntype cwt(0, 3)
Variables:
  #self# <optimized out>
  a::Int64
  b::Int64
  i::Float64
  #temp#@_5::Int64
  u@_6::Int64
  shift_hi <optimized out>
  shift_lo <optimized out>
  x_hi <optimized out>
  x_lo <optimized out>
  #temp#@_11 <optimized out>
  #temp#@_12 <optimized out>
  w::Float64
  u@_14::Float64
  v::Float64

Body:
  begin 
      SSAValue(0) = $(Expr(:invoke, MethodInstance for linspace(::Type{Float64}, ::Int64, ::Int64, ::Int64, ::Int64), :(Base.linspace), :(Base.Float64), :(a), :(b), 4, 1))
      #temp#@_5::Int64 = 1
      3: 
      unless (Base.not_int)((Base.slt_int)((Core.getfield)(SSAValue(0), :len)::Int64, #temp#@_5::Int64)::Bool)::Bool goto 46
      $(Expr(:inbounds, false))
      # meta: location twiceprecision.jl next 187
      # meta: location twiceprecision.jl unsafe_getindex 194
      u@_6::Int64 = (Base.sub_int)(#temp#@_5::Int64, (Core.getfield)(SSAValue(0), :offset)::Int64)::Int64 # line 195:
      SSAValue(2) = (Base.mul_float)((Base.sitofp)(Float64, u@_6::Int64)::Float64, (Core.getfield)((Core.getfield)(SSAValue(0), :step)::Base.TwicePrecision{Float64}, :hi)::Float64)::Float64
      SSAValue(3) = (Base.mul_float)((Base.sitofp)(Float64, u@_6::Int64)::Float64, (Core.getfield)((Core.getfield)(SSAValue(0), :step)::Base.TwicePrecision{Float64}, :lo)::Float64)::Float64 # line 196:
      # meta: location twiceprecision.jl add2 445
      u@_14::Float64 = (Core.getfield)((Core.getfield)(SSAValue(0), :ref)::Base.TwicePrecision{Float64}, :hi)::Float64
      v::Float64 = SSAValue(2)
      SSAValue(7) = (Base.select_value)((Base.lt_float)((Base.abs_float)(u@_14::Float64)::Float64, (Base.abs_float)(v::Float64)::Float64)::Bool, (Core.tuple)(v::Float64, u@_14::Float64)::Tuple{Float64,Float64}, (Core.tuple)(u@_14::Float64, v::Float64)::Tuple{Float64,Float64})::Tuple{Float64,Float64}
      SSAValue(10) = (Base.getfield)(SSAValue(7), 1)::Float64
      u@_14::Float64 = SSAValue(10)
      SSAValue(12) = (Base.getfield)(SSAValue(7), 2)::Float64
      v::Float64 = SSAValue(12) # line 446:
      w::Float64 = (Base.add_float)(u@_14::Float64, v::Float64)::Float64
      # meta: pop location
      SSAValue(15) = (Base.add_float)((Base.sub_float)(u@_14::Float64, w::Float64)::Float64, v::Float64)::Float64
      # meta: pop location
      # meta: pop location
      $(Expr(:inbounds, :pop))
      SSAValue(24) = (Base.add_float)(w::Float64, (Base.add_float)(SSAValue(15), (Base.add_float)(SSAValue(3), (Core.getfield)((Core.getfield)(SSAValue(0), :ref)::Base.TwicePrecision{Float64}, :lo)::Float64)::Float64)::Float64)::Float64
      SSAValue(25) = (Base.add_int)(#temp#@_5::Int64, 1)::Int64
      i::Float64 = SSAValue(24)
      #temp#@_5::Int64 = SSAValue(25) # line 3:
      SSAValue(26) = i::Float64
      SSAValue(27) = Float64
      $(Expr(:inbounds, false))
      # meta: location coreio.jl println 5
      SSAValue(19) = (Core.typeassert)(Base.STDOUT, Base.IO)::IO
      SSAValue(21) = SSAValue(26)
      SSAValue(22) = " "
      SSAValue(23) = SSAValue(27)
      # meta: pop location
      $(Expr(:inbounds, :pop))
      (Base.print)(SSAValue(19), SSAValue(21), SSAValue(22), SSAValue(23), $(QuoteNode('\n')))::Void
      44: 
      goto 3
      46: 
      return
  end::Void

julia> @code_warntype cwt(0.0, 3.0)
Variables:
  #self# <optimized out>
  a::Float64
  b::Float64
  i::Float64
  #temp#@_5::Int64
  u@_6::Int64
  shift_hi <optimized out>
  shift_lo <optimized out>
  x_hi <optimized out>
  x_lo <optimized out>
  #temp#@_11 <optimized out>
  #temp#@_12 <optimized out>
  w::Float64
  u@_14::Float64
  v::Float64

Body:
  begin 
      SSAValue(0) = $(Expr(:invoke, MethodInstance for linspace(::Float64, ::Float64, ::Int64), :(Main.linspace), :(a), :(b), 4))
      #temp#@_5::Int64 = 1
      3: 
      unless (Base.not_int)((Base.slt_int)((Core.getfield)(SSAValue(0), :len)::Int64, #temp#@_5::Int64)::Bool)::Bool goto 46
      $(Expr(:inbounds, false))
      # meta: location twiceprecision.jl next 187
      # meta: location twiceprecision.jl unsafe_getindex 194
      u@_6::Int64 = (Base.sub_int)(#temp#@_5::Int64, (Core.getfield)(SSAValue(0), :offset)::Int64)::Int64 # line 195:
      SSAValue(2) = (Base.mul_float)((Base.sitofp)(Float64, u@_6::Int64)::Float64, (Core.getfield)((Core.getfield)(SSAValue(0), :step)::Base.TwicePrecision{Float64}, :hi)::Float64)::Float64
      SSAValue(3) = (Base.mul_float)((Base.sitofp)(Float64, u@_6::Int64)::Float64, (Core.getfield)((Core.getfield)(SSAValue(0), :step)::Base.TwicePrecision{Float64}, :lo)::Float64)::Float64 # line 196:
      # meta: location twiceprecision.jl add2 445
      u@_14::Float64 = (Core.getfield)((Core.getfield)(SSAValue(0), :ref)::Base.TwicePrecision{Float64}, :hi)::Float64
      v::Float64 = SSAValue(2)
      SSAValue(7) = (Base.select_value)((Base.lt_float)((Base.abs_float)(u@_14::Float64)::Float64, (Base.abs_float)(v::Float64)::Float64)::Bool, (Core.tuple)(v::Float64, u@_14::Float64)::Tuple{Float64,Float64}, (Core.tuple)(u@_14::Float64, v::Float64)::Tuple{Float64,Float64})::Tuple{Float64,Float64}
      SSAValue(10) = (Base.getfield)(SSAValue(7), 1)::Float64
      u@_14::Float64 = SSAValue(10)
      SSAValue(12) = (Base.getfield)(SSAValue(7), 2)::Float64
      v::Float64 = SSAValue(12) # line 446:
      w::Float64 = (Base.add_float)(u@_14::Float64, v::Float64)::Float64
      # meta: pop location
      SSAValue(15) = (Base.add_float)((Base.sub_float)(u@_14::Float64, w::Float64)::Float64, v::Float64)::Float64
      # meta: pop location
      # meta: pop location
      $(Expr(:inbounds, :pop))
      SSAValue(24) = (Base.add_float)(w::Float64, (Base.add_float)(SSAValue(15), (Base.add_float)(SSAValue(3), (Core.getfield)((Core.getfield)(SSAValue(0), :ref)::Base.TwicePrecision{Float64}, :lo)::Float64)::Float64)::Float64)::Float64
      SSAValue(25) = (Base.add_int)(#temp#@_5::Int64, 1)::Int64
      i::Float64 = SSAValue(24)
      #temp#@_5::Int64 = SSAValue(25) # line 3:
      SSAValue(26) = i::Float64
      SSAValue(27) = Float64
      $(Expr(:inbounds, false))
      # meta: location coreio.jl println 5
      SSAValue(19) = (Core.typeassert)(Base.STDOUT, Base.IO)::IO
      SSAValue(21) = SSAValue(26)
      SSAValue(22) = " "
      SSAValue(23) = SSAValue(27)
      # meta: pop location
      $(Expr(:inbounds, :pop))
      (Base.print)(SSAValue(19), SSAValue(21), SSAValue(22), SSAValue(23), $(QuoteNode('\n')))::Void
      44: 
      goto 3
      46: 
      return
  end::Void

julia> @code_warntype cwt(Int32(0), Int32(3))
Variables:
  #self# <optimized out>
  a::Int32
  b::Int32
  i::Float64
  #temp#@_5::Int64
  u@_6::Int64
  shift_hi <optimized out>
  shift_lo <optimized out>
  x_hi <optimized out>
  x_lo <optimized out>
  #temp#@_11 <optimized out>
  #temp#@_12 <optimized out>
  w::Float64
  u@_14::Float64
  v::Float64

Body:
  begin 
      SSAValue(0) = $(Expr(:invoke, MethodInstance for linspace(::Type{Float64}, ::Int32, ::Int32, ::Int64, ::Int64), :(Base.linspace), :(Base.Float64), :(a), :(b), 4, 1))
      #temp#@_5::Int64 = 1
      3: 
      unless (Base.not_int)((Base.slt_int)((Core.getfield)(SSAValue(0), :len)::Int64, #temp#@_5::Int64)::Bool)::Bool goto 46
      $(Expr(:inbounds, false))
      # meta: location twiceprecision.jl next 187
      # meta: location twiceprecision.jl unsafe_getindex 194
      u@_6::Int64 = (Base.sub_int)(#temp#@_5::Int64, (Core.getfield)(SSAValue(0), :offset)::Int64)::Int64 # line 195:
      SSAValue(2) = (Base.mul_float)((Base.sitofp)(Float64, u@_6::Int64)::Float64, (Core.getfield)((Core.getfield)(SSAValue(0), :step)::Base.TwicePrecision{Float64}, :hi)::Float64)::Float64
      SSAValue(3) = (Base.mul_float)((Base.sitofp)(Float64, u@_6::Int64)::Float64, (Core.getfield)((Core.getfield)(SSAValue(0), :step)::Base.TwicePrecision{Float64}, :lo)::Float64)::Float64 # line 196:
      # meta: location twiceprecision.jl add2 445
      u@_14::Float64 = (Core.getfield)((Core.getfield)(SSAValue(0), :ref)::Base.TwicePrecision{Float64}, :hi)::Float64
      v::Float64 = SSAValue(2)
      SSAValue(7) = (Base.select_value)((Base.lt_float)((Base.abs_float)(u@_14::Float64)::Float64, (Base.abs_float)(v::Float64)::Float64)::Bool, (Core.tuple)(v::Float64, u@_14::Float64)::Tuple{Float64,Float64}, (Core.tuple)(u@_14::Float64, v::Float64)::Tuple{Float64,Float64})::Tuple{Float64,Float64}
      SSAValue(10) = (Base.getfield)(SSAValue(7), 1)::Float64
      u@_14::Float64 = SSAValue(10)
      SSAValue(12) = (Base.getfield)(SSAValue(7), 2)::Float64
      v::Float64 = SSAValue(12) # line 446:
      w::Float64 = (Base.add_float)(u@_14::Float64, v::Float64)::Float64
      # meta: pop location
      SSAValue(15) = (Base.add_float)((Base.sub_float)(u@_14::Float64, w::Float64)::Float64, v::Float64)::Float64
      # meta: pop location
      # meta: pop location
      $(Expr(:inbounds, :pop))
      SSAValue(24) = (Base.add_float)(w::Float64, (Base.add_float)(SSAValue(15), (Base.add_float)(SSAValue(3), (Core.getfield)((Core.getfield)(SSAValue(0), :ref)::Base.TwicePrecision{Float64}, :lo)::Float64)::Float64)::Float64)::Float64
      SSAValue(25) = (Base.add_int)(#temp#@_5::Int64, 1)::Int64
      i::Float64 = SSAValue(24)
      #temp#@_5::Int64 = SSAValue(25) # line 3:
      SSAValue(26) = i::Float64
      SSAValue(27) = Float64
      $(Expr(:inbounds, false))
      # meta: location coreio.jl println 5
      SSAValue(19) = (Core.typeassert)(Base.STDOUT, Base.IO)::IO
      SSAValue(21) = SSAValue(26)
      SSAValue(22) = " "
      SSAValue(23) = SSAValue(27)
      # meta: pop location
      $(Expr(:inbounds, :pop))
      (Base.print)(SSAValue(19), SSAValue(21), SSAValue(22), SSAValue(23), $(QuoteNode('\n')))::Void
      44: 
      goto 3
      46: 
      return
  end::Void

julia> versioninfo()
Julia Version 0.6.1
Commit 0d7248e* (2017-10-24 22:15 UTC)
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: AMD Ryzen Threadripper 1950X 16-Core Processor
  WORD_SIZE: 64
  BLAS: libopenblas (ZEN)
  LAPACK: liblapack
  LIBM: libopenlibm
  LLVM: libLLVM-3.9.1 (ORCJIT, generic)

Other tests confirmed that allocations sky rocketed and performance plummeted when passing Float32 arguments. In all my tests, the type seemed to be stable and:

julia> eltype(linspace(0f0, 3f0, 4))
Float32

works as expected.

I could not reproduce on a 1 day old master; inference seems to have been fixed:

julia> function cwt(a, b) 
           for i ∈ linspace(a, b, 4)
               println(i, " ", typeof(i))
           end
       end
cwt (generic function with 1 method)

julia> cwt(0f0, 3f0)
0.0 Float32
1.0 Float32
2.0 Float32
3.0 Float32

julia> @code_warntype cwt(0f0, 3f0)
Variables:
  a::Float32
  b::Float32
  #temp#@_4::Int64
  i::Float32
  #temp#@_6::Int64
  #temp#@_7::Bool
  #temp#@_8::Tuple{Float32,Int64}

Body:
  begin
      SSAValue(0) = $(Expr(:invoke, MethodInstance for linspace(::Float32, ::Float32, ::Int64), :(Main.linspace), :(a), :(b), 4))::Union{StepRangeLen{Float32,Base.TwicePrecision{Float32},Base.TwicePrecision{Float32}}, StepRangeLen{Float32,Float64,Float64}}
      unless (SSAValue(0) isa StepRangeLen{Float32,Base.TwicePrecision{Float32},Base.TwicePrecision{Float32}})::Bool goto 5
      #temp#@_6::Int64 = $(Expr(:invoke, MethodInstance for start(::StepRangeLen{Float32,Base.TwicePrecision{Float32},Base.TwicePrecision{Float32}}), :(Base.start), SSAValue(0)))::Int64
      goto 13
      5: 
      unless (SSAValue(0) isa StepRangeLen{Float32,Float64,Float64})::Bool goto 9
      #temp#@_6::Int64 = $(Expr(:invoke, MethodInstance for start(::StepRangeLen{Float32,Float64,Float64}), :(Base.start), SSAValue(0)))::Int64
      goto 13
      9: 
      goto 11
      11: 
      (Base.error)("fatal error in type inference (type bound)")::Any
      13: 
      #temp#@_4::Int64 = #temp#@_6::Int64
      15: 
      unless (SSAValue(0) isa StepRangeLen{Float32,Base.TwicePrecision{Float32},Base.TwicePrecision{Float32}})::Bool goto 19
      #temp#@_7::Bool = $(Expr(:invoke, MethodInstance for done(::StepRangeLen{Float32,Base.TwicePrecision{Float32},Base.TwicePrecision{Float32}}, ::Int64), :(Base.done), SSAValue(0), :(#temp#@_4)))::Bool
      goto 27
      19: 
      unless (SSAValue(0) isa StepRangeLen{Float32,Float64,Float64})::Bool goto 23
      #temp#@_7::Bool = $(Expr(:invoke, MethodInstance for done(::StepRangeLen{Float32,Float64,Float64}, ::Int64), :(Base.done), SSAValue(0), :(#temp#@_4)))::Bool
      goto 27
      23: 
      goto 25
      25: 
      (Base.error)("fatal error in type inference (type bound)")::Any
      27: 
      unless (Base.not_int)(#temp#@_7::Bool)::Bool goto 48
      unless (SSAValue(0) isa StepRangeLen{Float32,Base.TwicePrecision{Float32},Base.TwicePrecision{Float32}})::Bool goto 32
      #temp#@_8::Tuple{Float32,Int64} = $(Expr(:invoke, MethodInstance for next(::StepRangeLen{Float32,Base.TwicePrecision{Float32},Base.TwicePrecision{Float32}}, ::Int64), :(Base.next), SSAValue(0), :(#temp#@_4)))::Tuple{Float32,Int64}
      goto 40
      32: 
      unless (SSAValue(0) isa StepRangeLen{Float32,Float64,Float64})::Bool goto 36
      #temp#@_8::Tuple{Float32,Int64} = $(Expr(:invoke, MethodInstance for next(::StepRangeLen{Float32,Float64,Float64}, ::Int64), :(Base.next), SSAValue(0), :(#temp#@_4)))::Tuple{Float32,Int64}
      goto 40
      36: 
      goto 38
      38: 
      (Base.error)("fatal error in type inference (type bound)")::Any
      40: 
      SSAValue(1) = #temp#@_8::Tuple{Float32,Int64}
      i::Float32 = (Core.getfield)(SSAValue(1), 1)::Float32
      #temp#@_4::Int64 = (Core.getfield)(SSAValue(1), 2)::Int64
      #= line 3 =#
      $(Expr(:invoke, MethodInstance for println(::Float32, ::String, ::Vararg{Any,N} where N), :(Main.println), :(i), " ", Float32))::Void
      46: 
      goto 15
      48: 
      return
  end::Void

julia> versioninfo()
Julia Version 0.7.0-DEV.2583
Commit 80d8719 (2017-11-22 18:20 UTC)
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: AMD Ryzen Threadripper 1950X 16-Core Processor
  WORD_SIZE: 64
  BLAS: libopenblas (ZEN)
  LAPACK: liblapack
  LIBM: libopenlibm
  LLVM: libLLVM-3.9.1 (ORCJIT, znver1)
Environment:

#2

To make sure, this is not a regression from 0.6, right?


#3

Testing on JuliaBox shows the same problem for 0.6.0.
What’s the best course of action is when I discover something like this? I’m not in need of this, and because it is already fixed on master perhaps it’s a nonissue?

EDIT: Inference was correct for 0.5.2


#4

Looks like it is coming from linspace

@code_warntype linspace(0.0f0, 9.0f0, 4)

vs

@code_warntype linspace(0.0, 9.0, 4)

There was a change in linspace in 0.6 by @timholy. It might come from there.