Modules
LLVM modules are the main container of LLVM IR code. They are created using the Module
constructor (not exported because of the name conflict with Base.Module
):
julia> mod = LLVM.Module("SomeModule")
; ModuleID = 'SomeModule'
source_filename = "SomeModule"
The only argument to the constructor is the module's name. Along with some other properties, this can be read and modified using dedicated functions:
name
/name!
: module nametriple
/triple!
: target triple stringdatalayout
/datalayout!
: a data layout string orDataLayout
objectinline_asm
/inline_asm!
: module-level inline assemblysdk_version
/sdk_version!
: Apple SDK versionset_used!
andset_compiler_used!
: to set@llvm.used
and@llvm.compiler.used
Textual representation
In the REPL, LLVM modules are displayed verbosely, i.e., they print their IR code. Simply printing the module object will instead output a compact object representation, so if you want to debug your application by printing IR you need to invoke the display
function or explicitly string
ify the object:
julia> print(mod)
LLVM.Module("SomeModule")
julia> show(stdout, "text/plain", mod) # equivalent of `display`
; ModuleID = 'SomeModule'
source_filename = "SomeModule"
julia> @info "My module:\n" * string(mod)
┌ Info: My module:
│ ; ModuleID = 'SomeModule'
└ source_filename = "SomeModule"
To parse an LLVM module from a textual string, simply use the parse
function:
julia> ir = """
define i64 @"add"(i64 %0, i64 %1) {
top:
%2 = add i64 %1, %0
ret i64 %2
}""";
julia> parse(LLVM.Module, ir)
define i64 @add(i64 %0, i64 %1) {
top:
%2 = add i64 %1, %0
ret i64 %2
}
Binary representation ("bitcode")
If you need the binary bitcode, you can convert the module to a vector of bytes, or write it to an I/O stream:
julia> # only showing the first two bytes, for brevity
convert(Vector{UInt8}, mod)[1:2]
2-element Vector{UInt8}:
0x42
0x43
julia> sprint(write, mod)[1:2]
"BC"
Parsing bitcode is again done with the parse
function, dispatching on the fact that the bitcode is represented as a vector of bytes:
julia> bc = UInt8[0x42, 0x43, ...]
julia> parse(LLVM.Module, bc)
source_filename = "SomeModule"
Contents
To iterate the contents of a module, several iterators are provided (with different levels of functionality, based on what the LLVM C API provides).
Global objects
Globals, such as global variables, can be iterated with the globals
function:
julia> mod = LLVM.Module("SomeModule");
julia> gv = GlobalVariable(mod, LLVM.Int32Type(), "SomeGlobal");
julia> collect(globals(mod))
1-element Vector{GlobalVariable}:
@SomeGlobal = external global i32
In addition to the iteration interface, it is possible to move from one global to the previous or next one using respectively the prevglobal
and nextglobal
functions.
Functions
Functions can be iterated with the functions
function:
julia> fun = LLVM.Function(mod, "SomeFunction", LLVM.FunctionType(LLVM.VoidType()));
julia> collect(functions(mod))
1-element Vector{LLVM.Function}:
declare void @SomeFunction()
Again, it is possible to move from one function to the previous or next one using respectively the prevfun
and nextfun
functions.
Flags
Modules can also have flags associated with them, which can be set and retrieved using the associative iterator returned by the flags
function:
julia> mod = LLVM.Module("SomeModule");
julia> flags(mod)["SomeFlag", LLVM.API.LLVMModuleFlagBehaviorError] = Metadata(ConstantInt(42))
i64 42
julia> mod
; ModuleID = 'SomeModule'
source_filename = "SomeModule"
!llvm.module.flags = !{!0}
!0 = !{i32 1, !"SomeFlag", i64 42}
Note the additional argument to setindex!
, which indicates the flag behavior.
Linking
Modules can be linked together using the link!
function. This function takes two modules, destroying the source module in the process:
julia> src = parse(LLVM.Module, "define void @foo() { ret void }");
julia> dst = parse(LLVM.Module, "define void @bar() { ret void }");
julia> link!(dst, src)
julia> dst
define void @bar() {
ret void
}
define void @foo() {
ret void
}