@code_warntype fails when function has a keyword argument


#1

In the following two functions are identical except that fun2() has a keyword argument. However, @code_warntype fails to “go into” the invoke Main... part that should give a warntype!

function fun1(v)
    kw = 1
    println(v[1])
end

function fun2(v; kw=1)
    println(v[1])
end

v = Vector{Real}(undef, 2)
v[1] = 1
v[2] = 2.2

julia> @code_warntype fun1(v)
Body::Nothing
1 ─ %1 = (Base.arrayref)(true, v, 1)::Real
│   %2 = (Main.println)(%1)::Core.Compiler.Const(nothing, false)
└──      return %2

julia> @code_warntype fun2(v)
Body::Nothing
1 ─ %1 = invoke Main.:(#fun2#3)(1::Int64, _1::Function, _2::Array{Real,1})::Core.Compiler.Const(nothing, false)
└──      return %1

it’s worrisome…


#2

This is not a failure — @code_warntype does not expand the callees recursively (unless they were inlined).

Just use @code_warntype fun2(v; kw = 1).

Also, note that if you are debugging type stablity, you should use arguments with concrete types. Abstract type parameters like in Vector{Real} preclude the optimizations that are allowed by knowing types at compile time (“type stability”), so are not really relevant.


#3

You can use Cthulhu to descend into madness functions with keyword arguments (among other things):

julia> @descend_code_typed fun2(v)

│ ─ %-1  = invoke fun2(::Array{Real,1})::Core.Compiler.Const(nothing, false)
CodeInfo(
    @ REPL[2]:2 within `fun2'
1 ─ %1 = invoke Main.:(#fun2#3)(1::Int64, _1::Function, _2::Array{Real,1})::Core.Compiler.Const(nothing, false)
└──      return %1
)
Select a call to descend into or ↩ to ascend. [q]uit.
Toggles: [o]ptimize, [w]arn, [d]ebuginfo.
Show: [L]LVM IR, [N]ative code
Advanced: dump [P]arams cache.

 • %1  = invoke #fun2#3(::Int64,::Function,::Array{Real,1})
   ↩

│ ─ %-1  = invoke #fun2#3(::Int64,::Function,::Array{Real,1})::Core.Compiler.Const(nothing, false)
CodeInfo(
    @ REPL[2]:2 within `#fun2#3'
   ┌ @ array.jl:729 within `getindex'
1 ─│ %1 = (Base.arrayref)(true, v, 1)::Real
│  └
│   %2 = (Main.println)(%1)::Core.Compiler.Const(nothing, false)
└──      return %2
)
Select a call to descend into or ↩ to ascend. [q]uit.
Toggles: [o]ptimize, [w]arn, [d]ebuginfo.
Show: [L]LVM IR, [N]ative code
Advanced: dump [P]arams cache.

 • ↩

#4

thanks. could you show how to install this package?


#5

It’s not registered, so you have to do add https://github.com/JuliaDebug/Cthulhu.jl in Pkg mode.


#6

that Vector{Real} is intentional (or else no warntype to show at all).

with @code_warntype not recursively gives warning, we would need to carefully watch out any invoke. Or, use Cthulhu as @mateuszbaran suggested.


#7

Perhaps you misunderstand the role of @code_warntype: the idea is that it helps you detect type instabilities when you call with concrete types. This will be marked even if we don’t descend into the callees. Eg

julia> @noinline f(x) = x > 0 ? 1 : 1.0
f (generic function with 1 method)

julia> g(x) = f(x^2)
g (generic function with 1 method)

julia> @code_warntype g(1)
Variables
  #self#::Core.Compiler.Const(g, false)
  x::Int64

Body::Union{Float64, Int64}
1 ─ %1 = Core.apply_type(Base.Val, 2)::Core.Compiler.Const(Val{2}, false)
│   %2 = (%1)()::Core.Compiler.Const(Val{2}(), false)
│   %3 = Base.literal_pow(Main.:^, x, %2)::Int64
│   %4 = Main.f(%3)::Union{Float64, Int64}
└──      return %4

where the ::Union{Float64, Int64} is highlighted if the terminal supports it. So you are not going to miss anything.