Code associated with `invoke`

Is it possible to obtain the code associated with an invoke call?

Specifically, if I were to write:

sin(5.0)

I know that I can get access to one of various representations of the code via code_lowered, code_typed, etc. What is not clear to me is how I can do the analogous thing for

invoke(sin, Float64, 5.0)

Something like

@code_lowered invoke(sin, Float64, 5.0)

will fail because invoke is a built-in, so doesn’t have Julia code associated with it directly.
Is there a separate piece of functionality that I could use to return the code that invoke looks up?

I guess you want which:

https://docs.julialang.org/en/v1/base/base/#Base.which-Tuple{Any,%20Any}

Note what the macro form of code_lowered does:

julia> @macroexpand @code_lowered sin(5.0)
quote
    #= /cache/build/default-amdci4-0/julialang/julia-release-1-dot-9/usr/share/julia/stdlib/v1.9/InteractiveUtils/src/macros.jl:234 =#
    local var"#2#results" = InteractiveUtils.code_lowered(sin, (Base.typesof)(5.0))
    #= /cache/build/default-amdci4-0/julialang/julia-release-1-dot-9/usr/share/julia/stdlib/v1.9/InteractiveUtils/src/macros.jl:235 =#
    if InteractiveUtils.length(var"#2#results") == 1
        var"#2#results"[1]
    else
        var"#2#results"
    end
end

Thus, we can get the same information by just passing the types.

julia> code_lowered(sin, Tuple{Float64})[1]
CodeInfo(
1 ──       Core.NewvarNode(:(@_3))
β”‚          Core.NewvarNode(:(y))
β”‚          Core.NewvarNode(:(n))
β”‚          absx = Base.Math.abs(x)
β”‚    %5  = absx
β”‚    %6  = ($(Expr(:static_parameter, 1)))(Base.Math.pi)
β”‚    %7  = %6 / 4
β”‚    %8  = %5 < %7
└───       goto #5 if not %8
2 ── %10 = absx
β”‚    %11 = Base.Math.eps($(Expr(:static_parameter, 1)))
β”‚    %12 = Base.Math.sqrt(%11)
β”‚    %13 = %10 < %12
└───       goto #4 if not %13
3 ──       return x
4 ── %16 = Base.Math.sin_kernel(x)
└───       return %16
5 ── %18 = Base.Math.isnan(x)
└───       goto #7 if not %18
6 ── %20 = ($(Expr(:static_parameter, 1)))(Base.Math.NaN)
└───       return %20
7 ── %22 = Base.Math.isinf(x)
└───       goto #9 if not %22
8 ──       Base.Math.sin_domain_error(x)
9 ┄─ %25 = Base.Math.rem_pio2_kernel(x)
β”‚    %26 = Base.indexed_iterate(%25, 1)
β”‚          n = Core.getfield(%26, 1)
β”‚          @_3 = Core.getfield(%26, 2)
β”‚    %29 = Base.indexed_iterate(%25, 2, @_3)
β”‚          y = Core.getfield(%29, 1)
β”‚          n = n & 3
β”‚    %32 = n == 0
└───       goto #11 if not %32
10 ─ %34 = Base.Math.sin_kernel(y)
└───       return %34
11 ─ %36 = n == 1
└───       goto #13 if not %36
12 ─ %38 = Base.Math.cos_kernel(y)
└───       return %38
13 ─ %40 = n == 2
└───       goto #15 if not %40
14 ─ %42 = Base.Math.sin_kernel(y)
β”‚    %43 = -%42
└───       return %43
15 ─ %45 = Base.Math.cos_kernel(y)
β”‚    %46 = -%45
└───       return %46
)

Note that the procedural form does not need the value of the arguments at all.

I notice that your invoke call does not work. Called correctly, the arguments to pass to code_lowered should be exactly the same as the first two arguments passed to invoke.

julia> invoke(sin, Float64, 5.0)
ERROR: TypeError: in invoke, expected Type{T} where T<:Tuple, got Type{Float64}
Stacktrace:
 [1] top-level scope
   @ REPL[40]:1

julia> invoke(sin, Tuple{Float64}, 5.0)
-0.9589242746631385

julia> code_lowered(sin, Tuple{Float64});
1 Like

I notice that your invoke call does not work.

Sorry, yes, you are correct – I messed up the signature of invoke slightly. Your version of the call is correct.

I guess you want which:

@nsajko Thanks for this – I feel like it’s nearly what I need, but not quite. Specifically, while it gives me the Method, it doesn’t give me the typed-code (optimised and un-optimised) i.e. a CodeInfo / IRCode object. Do you know if there’s a way to get at that, given the method?

It looks (I think) like I can use a mix of Base.code_typed_by_type and which to figure it out though (I’ve broadened the signature to make it a little bit more interesting):

sig = Tuple{typeof(sin), Real}
m = which(sig)
typed_codes = Base.code_typed_by_type(sig);
typed_code = only(filter(x -> x[1].parent.def === m, typed_codes))

I think that the filtering step should only ever return a single bit of lowered code, but I’m not entirely sure.