API Reference
This page provides a comprehensive reference for IRTools functionality.
Reflection
IRTools.@code_ir
— Macro.@code_ir f(args...)
Convenience macro similar to @code_lowered
or @code_typed
. Retrieves the IR for the given function call.
julia> @code_ir gcd(10, 5)
1: (%1, %2, %3)
%4 = %2 == 0
br 4 unless %4
2: ...
IRTools.meta
— Function.meta(Tuple{...})
Construct metadata for a given method signature. Metadata can then be used to construct IR
or used to perform other reflection on the method.
See also @meta
, typed_meta
.
julia> IRTools.meta(Tuple{typeof(gcd),Int,Int})
Metadata for gcd(a::T, b::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} in Base at intfuncs.jl:31
IRTools.typed_meta
— Function.typed_meta(Tuple{...})
Same as @meta
, but represents the method after type inference. IR constructed with typed metadata will have type annotations.
See also @typed_meta
.
julia> IRTools.typed_meta(Tuple{typeof(gcd),Int,Int})
Typed metadata for gcd(a::T, b::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} in Base at intfuncs.jl:31
IRTools.@meta
— Macro.@meta f(args...)
Convenience macro for retrieving metadata without writing a full type signature.
julia> IRTools.@meta gcd(10, 5)
Metadata for gcd(a::T, b::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} in Base at intfuncs.jl:31
IRTools.@typed_meta
— Macro.@typed_meta f(args...)
Convenience macro for retrieving typed metadata without writing a full type signature.
julia> IRTools.@typed_meta gcd(10, 5)
Typed metadata for gcd(a::T, b::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} in Base at intfuncs.jl:31
IR Manipulation
IRTools.IR
— Type.IR()
IR(metadata; slots = false)
Represents a fragment of SSA-form code.
IR can be constructed from scratch, but more usually an existing Julia method is used as a starting point (see meta
for how to get metadata for a method). The slots
argument determines whether the IR preserves mutable variable slots; by default, these are converted to SSA-style variables.
As a shortcut, IR can be constructed directly from a type signature, e.g.
julia> IR(typeof(gcd), Int, Int)
1: (%1, %2, %3)
%4 = %2 == 0
br 4 unless %4
2: ...
IRTools.Statement
— Type.Statement(expr; type, line)
Represents a single statement in the IR. The expr
is a non-nested Julia expression (Expr
). type
represents the return type of the statement; in most cases this can be ignored and defaults to Any
. line
represents the source location of the statement; it is an integer index into the IR's line table.
As a convenience, if expr
is already a statement, the new statement will inherit its type and line number.
IRTools.Variable
— Type.Variable(N)
var(N)
Represents an SSA variable. Primarily used as an index into IR
objects.
IRTools.argument!
— Function.argument!(block, [value, type])
Create a new argument for the given block / IR fragment, and return the variable representing the argument.
julia> ir = IR();
julia> argument!(ir)
%1
julia> ir
1: (%1)
The at
keyword argument can be used to specify where the new argument should go; by default it is appended to the end of the argument list.
If there are branches to this block, they will be updated to pass value
(nothing
by default) as an argument.
Base.push!
— Function.push!(ir, x)
Append the statement or expression x
to the IR or block ir
, returning the new variable. See also pushfirst!
, insert!
.
julia> ir = IR();
julia> x = argument!(ir)
%1
julia> push!(ir, xcall(:*, x, x))
%2
julia> ir
1: (%1)
%2 = %1 * %1
Base.pushfirst!
— Function.pushfirst!(ir, x)
Insert the expression or statement x
into the given IR or block at the beginning, returning the new variable. See also push!
, insert!
.
julia> f(x) = 3x + 2
f (generic function with 1 method)
julia> ir = @code_ir f(1)
1: (%1, %2)
%3 = 3 * %2
%4 = %3 + 2
return %4
julia> pushfirst!(ir, :(println("hello, world")))
%5
julia> ir
1: (%1, %2)
%5 = println("hello, world")
%3 = 3 * %2
%4 = %3 + 2
return %4
Base.insert!
— Function.insert!(ir, v, x)
Insert the expression or statement x
into the given IR, just before the variable v
is defined, returning the new variable for x
. See also insertafter!
.
julia> f(x) = 3x + 2
f (generic function with 1 method)
julia> ir = @code_ir f(1)
1: (%1, %2)
%3 = 3 * %2
%4 = %3 + 2
return %4
julia> insert!(ir, IRTools.var(4), :(println("hello, world")))
%5
julia> ir
1: (%1, %2)
%3 = 3 * %2
%5 = println("hello, world")
%4 = %3 + 2
return %4
IRTools.insertafter!
— Function.insertafter!(ir, v, x)
Insert the expression or statement x
into the given IR, just before the variable v
is defined, returning the new variable for x
. See also insert!
.
julia> f(x) = 3x + 2
f (generic function with 1 method)
julia> ir = @code_ir f(1)
1: (%1, %2)
%3 = 3 * %2
%4 = %3 + 2
return %4
julia> IRTools.insertafter!(ir, IRTools.var(4), :(println("hello, world")))
%5
julia> ir
1: (%1, %2)
%3 = 3 * %2
%4 = %3 + 2
%5 = println("hello, world")
return %4
Base.empty
— Function.empty(ir)
Create an empty IR fragment based on the given IR. The line number table and any metadata are preserved from the original IR.
julia> ir = empty(@code_ir gcd(10, 5))
1:
julia> ir.meta
Metadata for gcd(a::T, b::T) where T<:Union{Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8} in Base at intfuncs.jl:31
Base.keys
— Function.keys(ir)
Return the variable keys for all statements defined in ir
.
julia> f(x) = 3x + 2;
julia> ir = @code_ir f(1)
1: (%1, %2)
%3 = 3 * %2
%4 = %3 + 2
return %4
julia> keys(ir)
2-element Array{IRTools.Variable,1}:
%3
%4
Base.haskey
— Function.haskey(ir, var)
Check whether the variable var
was defined in ir
.
julia> f(x) = 3x + 2;
julia> ir = @code_ir f(1)
1: (%1, %2)
%3 = 3 * %2
%4 = %3 + 2
return %4
julia> haskey(ir, var(3))
true
julia> haskey(ir, var(7))
false
IRTools.returnvalue
— Function.returnvalue(block)
Retreive the return value of a block.
julia> f(x) = 3x + 2;
julia> IRTools.block(@code_ir(f(1)), 1)
1: (%1, %2)
%3 = 3 * %2
%4 = %3 + 2
return %4
julia> IRTools.returnvalue(ans)
%4
IRTools.Pipe
— Type.Pipe(ir)
In general, it is not efficient to insert statements into IR; only appending is fast, for the same reason as with Vector
s.
For this reason, the Pipe
construct makes it convenient to incrementally build an new IR fragment from an old one, making efficient modifications as you go.
The general pattern looks like:
pr = IRTools.Pipe(ir)
for (v, st) in pr
# do stuff
end
ir = IRTools.finish(pr)
Iterating over pr
is just like iterating over ir
, except that within the loop, inserting and deleting statements in pr
around v
is efficient. Later, finish(pr)
converts it back to a normal IR fragment (in this case just a plain copy of the original).