Functions
Functions are part of a module, and represent a callable piece of code. They can be looked up in a module using the functions
iterator, or created from scratch:
julia> mod = LLVM.Module("SomeModule");
julia> fty = LLVM.FunctionType(LLVM.VoidType(), [LLVM.Int32Type()])
void (i32)
julia> fun = LLVM.Function(mod, "SomeFunction", fty)
declare void @SomeFunction(i32)
Several APIs can be used to interact with functions:
function_type
: get the function type of the function (this differs fromvalue_type
, which will return a pointer to the function type).personality
/personality!
: get or set the personality function of the function (passnothing
to remove the personality function).callconv
/callconv!
: get or set the calling convention of the function.gc
/gc!
: get or set the garbage collector for the function.isintrinsic
: check if the function is an intrinsic.erase!
: delete the function from its parent module, and delete the object.
Intrinsics
Intrinsic functions are special function declarations that are recognized by the LLVM compiler and possibly treated specially by the code generator. It is normally not necessary to create Intrinsic
objects directly, as function declarations that match an intrinsic's name and signature will be treated as such:
julia> mod = LLVM.Module("SomeModule");
julia> f = LLVM.Function(mod, "llvm.trap", LLVM.FunctionType(LLVM.VoidType()))
; Function Attrs: cold noreturn nounwind
declare void @llvm.trap() #0
julia> isintrinsic(f)
true
However, the Intrinsic
type supports additional APIs:
name
: get the base name of the intrinsic, or a specific overloaded name by passing additional argument types.isoverloaded
: check if the intrinsic is overloaded.
It can also be useful to construct a function from a well-known intrinsic, to make sure the overloaded name is correct:
julia> mod = LLVM.Module("SomeModule");
julia> intr = LLVM.Intrinsic("llvm.abs")
Intrinsic(1): overloaded intrinsic
julia> isoverloaded(intr)
true
julia> LLVM.Function(mod, intr, [LLVM.Int32Type()])
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
declare i32 @llvm.abs.i32(i32, i1 immarg) #0
Attributes
Functions can have attributes associated with them, which can be set and retrieved using the iterators returned by the function_attributes
, parameter_attributes
and return_attributes
functions to respectively set attributes on the function, its parameters, and its return value:
julia> push!(function_attributes(fun), StringAttribute("nounwind"))
julia> push!(parameter_attributes(fun, 1), StringAttribute("nocapture"))
julia> push!(return_attributes(fun), StringAttribute("sret"))
julia> mod
; ModuleID = 'SomeModule'
source_filename = "SomeModule"
declare "sret" void @SomeFunction(i32 "nocapture") #0
attributes #0 = { "nounwind" }
Attributes can be removed from these iterators using the delete!
function.
Different kinds of attributes are supported:
EnumAttribute
: an attribute identified by its enum id, optionally associated with an integer value.StringAttribute
: an attribute identified by its string name, optionally associated with a string valueTypeAttribute
: an attribute identified by its enum id, associated with a type.
julia> EnumAttribute("nounwind")
EnumAttribute 39=0
julia> StringAttribute("frame-pointer", "none")
StringAttribute frame-pointer=none
julia> TypeAttribute("byval", LLVM.Int32Type())
TypeAttribute 72=LLVM.IntegerType(i32)
Parameters
Parameters are values that represent the arguments to a function, and can be used as operands to other values. They can be queried using the parameters
function:
julia> collect(parameters(fun))
1-element Vector{Argument}:
i32 %0
Basic blocks
Functions are composed of basic blocks, which are sequences of instructions that are executed in order. Basic blocks can be iterated using the blocks
function:
julia> fun
define i64 @add(i64 %0, i64 %1) {
top:
%2 = add i64 %1, %0
ret i64 %2
}
julia> collect(blocks(fun))
1-element Vector{BasicBlock}:
BasicBlock("top")
julia> # to simply get the first block
entry(fun)
top:
%2 = add i64 %1, %0
ret i64 %2
In addition to the iteration interface, it is possible to move from one basic block to the previous or next one using respectively the prevblock
and nextblock
functions.