API Documentation
Below is the API documentation for CodeInfoTools.jl
Utilities
CodeInfoTools.code_info
— Functioncode_info(f::Function, tt::Type{T}; generated = true, debuginfo = :default) where T <: Tuple
code_info(f::Function, t::Type...; generated = true, debuginfo = :default)
Return lowered code for function f
with tuple type tt
. Equivalent to InteractiveUtils.@code_lowered
– but a function call and requires a tuple type tt
as input.
CodeInfoTools.walk
— Functionwalk(fn::Function, x)
A generic dispatch-based tree-walker which applies fn::Function
to x
, specialized to Code
node types (like Core.ReturnNode
, Core.GotoNode
, Core.GotoIfNot
, etc). Applies fn::Function
to sub-fields of nodes, and then zips the result back up into the node.
CodeInfoTools.get_slot
— Functionget_slot(ci::CodeInfo, s::Symbol)
Get the Core.Compiler.SlotNumber
associated with the s::Symbol
in ci::CodeInfo
. If there is no associated Core.Compiler.SlotNumber
, returns nothing
.
CodeInfoTools.code_inferred
— Functioncode_inferred(@nospecialize(f), t::Type...;
world = Base.get_world_counter(),
interp = Core.Compiler.NativeInterpreter(world))
Derives a Core.MethodInstance
specialization for signature Tuple{typeof(f), t::Type...}
and infers it with interp
. Can be used to derive inferred lowered code with custom interpreters, either as parts of custom compilation pipelines or for debugging purposes.
code_inferred
includes explicit checks which prevent the user from inadvertedly running inference multiple times on the same cached Core.CodeInfo
associated with the specialization.
Inference and optimization are stateful – if you try to do "dumb" things like grab the inferred Core.CodeInfo
, wipe it, and shove it through lambda
... it is highly unlikely to work and very likely to explode in your face.
Inference also caches the inferred Core.CodeInfo
associated with the Core.MethodInstance
specialization irrespective of the interpreter. That means (at least as far as I know at this time) you can't quickly infer with multiple interpreters without forcing a cache invalidation in between inference runs.
Working with Core.CodeInfo
CodeInfoTools.Variable
— Typeconst Variable = Core.SSAValue
var(id::Int) = Variable(id)
Alias for Core.SSAValue
– represents a primitive register in lowered code. See the section of Julia's documentation on lowered forms for more information.
CodeInfoTools.Statement
— Typestruct Statement{T}
node::T
type::Any
end
A wrapper around Core
nodes with an optional type
field to allow for user-based local propagation and other forms of analysis. Usage of Builder
or Canvas
will automatically wrap or unwrap nodes when inserting or calling finish
– so the user should never see Statement
instances directly unless they are working on type propagation.
For more information on Core
nodes, please see Julia's documentation on lowered forms.
CodeInfoTools.Canvas
— Typestruct Canvas
defs::Vector{Tuple{Int, Int}}
code::Vector{Any}
codelocs::Vector{Int32}
end
Canvas() = Canvas(Tuple{Int, Int}[], [], Int32[])
A Vector
-like abstraction for Core
code nodes.
Properties to keep in mind:
- Insertion anywhere is slow.
- Pushing to beginning is slow.
- Pushing to end is fast.
- Deletion is fast.
- Accessing elements is fast.
- Setting elements is fast.
Thus, if you build up a Canvas
instance incrementally, everything should be fast.
CodeInfoTools.Builder
— TypeBuilder(ci::Core.CodeInfo)
Builder(fn::Function, t::Type...)
Builder()
A wrapper around a Canvas
instance. Call finish
when done to produce a new CodeInfo
instance.
CodeInfoTools.slot!
— Functionslot!(b::Builder, name::Symbol; arg = false)::Core.SlotNumber
Add a new Core.SlotNumber
with associated name::Symbol
to the in-progress Core.CodeInfo
on the c::Canvas
inside b::Builder
. If arg == false
, also performs a pushfirst!
with a Core.NewvarNode
for consistency in the in-progress Core.CodeInfo
. (arg
controls whether or not we interpreter the new slot as an argument)
name::Symbol
must not already be associated with a Core.SlotNumber
.
CodeInfoTools.return!
— Functionreturn!(b::Builder, v::Variable)
return!(b::Builder, v::NewVariable)
Push a Core.ReturnNode
to the current end of b.to::Canvas
. Requires that the user pass in a v::Variable
or v::NewVariable
instance – so perform the correct unpack/tupling before creating a Core.ReturnNode
.
Base.iterate
— Functioniterate(b::Builder, (ks, i) = (pipestate(p.from), 1))
Iterate over the original CodeInfo
and add statements to a target Canvas
held by b::Builder
. iterate
builds the Canvas
in place – it also resolves local GlobalRef
instances to their global values in-place at the function argument (the 1st argument) of Expr(:call, ...)
instances. iterate
is the key to expressing idioms like:
for (v, st) in b
b[v] = swap(st)
end
At each step of the iteration, a new node is copied from the original CodeInfo
to the target Canvas
– and the user is allowed to setindex!
, push!
, or otherwise change the target Canvas
before the next iteration. The naming of Core.SSAValues
is taken care of to allow this.
CodeInfoTools.verify
— Functionverify(src::Core.CodeInfo)
Validate Core.CodeInfo
instances using Core.Compiler.verify
. Also explicitly checks that the linetable in src::Core.CodeInfo
is not empty.
CodeInfoTools.finish
— Functionfinish(b::Builder)
Create a new CodeInfo
instance from a Builder
. Renumbers the wrapped Canvas
in-place – then copies information from the original CodeInfo
instance and inserts modifications from the wrapped Canvas
Evaluation
CodeInfoTools.lambda
— FunctionIt is relatively difficult to prevent the user from shooting themselves in the foot with this sort of functionality. Please be aware of this. Segfaults should be cautiously expected.
lambda(m::Module, src::Core.CodeInfo)
lambda(m::Module, src::Core.CodeInfo, nargs::Int)
const λ = lambda
Create an anonymous @generated
function from a piece of src::Core.CodeInfo
. The src::Core.CodeInfo
is checked for consistency by verify
.
lambda
has a 2 different forms. The first form, given by signature:
lambda(m::Module, src::Core.CodeInfo)
tries to detect the correct number of arguments automatically. This may fail (for any number of internal reasons). Expecting this, the second form, given by signature:
lambda(m::Module, src::Core.CodeInfo, nargs::Int)
allows the user to specify the number of arguments via nargs
.
lambda
also has the shorthand λ
for those lovers of Unicode.