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?
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.