Instructions
Instructions represent the operations that are executed by the program. They are grouped in basic blocks, and can be iterated using the instructions
function. To create instructions, an instruction builder is used.
The abstract LLVM.Instruction
type supports a few additional APIs on top of the functionality from User
and Value
:
parent
: get the parent basic block of the instruction.opcode
: get the opcode of the instruction.remove!
/erase!
: delete the instruction from its parent basic block, or additionally also delete the instruction itself.Instruction(::Instruction)
: clone an instruction
Creating instructions
Instructions are created using an IRBuilder
. This object is first positioned, and then used to create instructions by calling specific functions.
To position an IRBuilder
, several APIs are available:
position
: get the basic block where the builder is currently positioned.position!(builder, ::Instruction)
: position the builder before an instruction.position!(builder, ::BasicBlock)
: position the builder at the end of a basic block.position!(builder)
: clear the position of the builder.
Given a pre-created Instruction
, or more commonly an instruction that has been delete!
d from a basic block, it is possible to insert it back into a different basic block by calling the insert!
function.
The essential functionality of the IRBuilder
is the ability to create instructions. This is done by calling specific functions:
julia> builder = IRBuilder();
julia> position!(builder, bb)
julia> ret!(builder);
julia> bb
entry:
ret void
For a full list of functions that can be used to create instructions, consult the API reference.
Debug location
When creating instructions with an IRBuilder
, it is possible to set a debug location for the instruction. This is done by calling the debuglocation!
function on the builder:
debuglocation!(builder)
: clear the debug location.debuglocation!(builder, ::Metadata)
: set the debug location to a specific metadata.debuglocation!(builder, ::Instruction)
: set the debug location to the same as another instruction.
Atomic instructions
Atomic instructions support a few additional APIs:
is_atomic
: check if the instruction is atomic.ordering
/ordering!
: get or set the ordering of the instruction.syncscope
/syncscope!
: get or set the synchronization scope of the instruction to a specificSyncScope
Call sites instructions
Call site instructions include calls, invokes, and callbr
instructions. These instruction types support a few additional APIs:
callconv
/callconv!
: get or set the calling convention of the call site.istailcall
/istailcall!
: get or set whether the call site is a tail call.called_type
: get the function type of the called value of the call site.called_operand
: get the called value of the call site.arguments
: get the arguments of the call site.
Operand bundles
Calls can also be associated with operand bundles, which are tagged sets of SSA values that can be associated with certain LLVM instructions, but cannot be dropped like metadata can.
To inspect the operand bundle of a call site, use the iterator returned by the operand_bundles
function on a call site instruction. This iterator returns objects that support the following APIs:
tag
: get the tag of the operand bundle.inputs
: get the inputs of the operand bundle, which itself is an iterator that can be indexed.
Operand bundles can also be created directly, using the OperandBundle
constructor:
julia> OperandBundle("deopt")
"deopt"()
julia> OperandBundle("deopt", [LLVM.ConstantInt(1)])
"deopt"(i64 1)
Whether constructed directly or looked up from a call site, operand bundles can be attached to a call site when calling the call!
function on an IRBuilder
.
Terminator instructions
Terminator instructions are the last instructions in a basic block, and are used to control the flow of execution. They support a few additional APIs:
isterminator
: check if the instruction is a terminator.successors
: get the successors of the terminator.
If the terminator is a branch, it's possible to check if the branch is conditional using the isconditional
function, and get or set the condition using the condition
and condition!
functions.
If the terminator is a switch, it's possible to get the default destination using the default_dest
function.
Phi nodes
Phi nodes are used to select a value based on the predecessor of a basic block. It's possible to inspect, and mutate, the incoming values using the iterator returned by the incoming
function, which supports the following APIs:
getindex
: get the incoming value at a specific index.push!
: add an incoming value (a value, block tuple) to the phi node.append!
: append multiple incoming values (an array of value, block tuples).
Fast math flags
Arithmetic instructions can be configured with different fast math flags, affecting optimizations that can be performed on the instruction. These flags can be queried and set using respectively the fast_math
and fast_math!
functions:
julia> inst = fadd!(builder, parameters(fun)[1], ConstantFP(1f0))
%1 = fadd float %0, 1.000000e+00
julia> fast_math(inst)
(nnan = false, ninf = false, nsz = false, arcp = false, contract = false, afn = false, reassoc = false)
julia> fast_math!(inst; nnan=true)
julia> inst
%1 = fadd nnan float %0, 1.000000e+00