API Documentation

Below is the API documentation for CodeInfoTools.jl


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

walk(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.

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

code_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

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

struct Statement{T}

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.

struct Canvas
    defs::Vector{Tuple{Int, Int}}
Canvas() = Canvas(Tuple{Int, Int}[], [], Int32[])

A Vector-like abstraction for Core code nodes.

Properties to keep in mind:

  1. Insertion anywhere is slow.
  2. Pushing to beginning is slow.
  3. Pushing to end is fast.
  4. Deletion is fast.
  5. Accessing elements is fast.
  6. Setting elements is fast.

Thus, if you build up a Canvas instance incrementally, everything should be fast.

Builder(fn::Function, t::Type...)

A wrapper around a Canvas instance. Call finish when done to produce a new CodeInfo instance.

slot!(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.

return!(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.

iterate(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)

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.


Validate Core.CodeInfo instances using Core.Compiler.verify. Also explicitly checks that the linetable in src::Core.CodeInfo is not empty.


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



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