Essentials
Vocabularies
LLVM.IR — Module
LLVM.IRThe LLVM IR object model: contexts, modules, values, types, metadata and debug info, along with predicates and operations to inspect and modify them (isdeclaration, erase!, replace_uses!, verify, ...).
using LLVM, LLVM.IR
for f in mod.functions
isdeclaration(f) && continue
for bb in f.blocks, inst in bb.instructions
# ...
end
endThe attributes, relationships and contents of these objects are accessed as properties, like fn.name, gv.linkage, inst.parent or f.blocks, rather than using functions.
LLVM.Build — Module
LLVM.BuildConstruction of IR: the IRBuilder and its instruction-building functions (add!, load!, call!, ret!, ...), constant expressions (const_add, ...), and the DIBuilder for debug info.
using LLVM, LLVM.IR, LLVM.Build
@dispose builder=IRBuilder() begin
position!(builder, LLVM.at_end(BasicBlock(f, "entry")))
ret!(builder, add!(builder, f.parameters...))
endLLVM.Passes — Module
LLVM.PassesOptimization passes and pipelines: the PassBuilder, pass managers, pass constructors like InstCombinePass, custom passes and pipeline callbacks.
using LLVM, LLVM.Passes
@dispose pb=PassBuilder() begin
add!(pb, InstCombinePass())
run!(pb, mod)
endLLVM.Analysis — Module
LLVM.AnalysisLLVM's analyses and the values they compute: dominator trees, and constant ranges and known bits of integers. Analyses can be used in custom passes through the analysis manager of a pass pipeline (see FunctionPass and FunctionAnalysisManager).
using LLVM, LLVM.IR, LLVM.Analysis
r = ConstantRange(64, 0, 100) + ConstantRange(64, 1) # [1, 101)Version
LLVM.version — Function
LLVM.version() -> VersionNumberThe version of the LLVM library that LLVM.jl uses, which is the one Julia uses.
Initialization
LLVM.backends — Function
backends()Return a list of back-ends supported by the LLVM library.
LLVM.InitializeAllTargetInfos — Function
LLVM.InitializeAllTargetInfos()
LLVM.InitializeXXXTargetInfo()Enables access to specific targets.
LLVM.InitializeAllTargets — Function
LLVM.InitializeAllTargets()
LLVM.InitializeXXXTarget()
LLVM.InitializeNativeTarget()Enables use of specific targets.
LLVM.InitializeAllTargetMCs — Function
LLVM.InitializeAllTargetMCs()
LLVM.InitializeXXXTargetMC()Enable use of machine code generation for specific targets.
LLVM.InitializeAllAsmParsers — Function
LLVM.InitializeAllAsmParsers()
LLVM.InitializeXXXAsmParser()
LLVM.InitializeNativeAsmParser()Enables use of assembly parsing functionality for specific targets.
LLVM.InitializeAllAsmPrinters — Function
LLVM.InitializeAllAsmPrinters()
LLVM.InitializeXXXAsmPrinter()
LLVM.InitializeNativeAsmPrinter()Enables use of assembly output functionality for specific targets.
LLVM.InitializeAllDisassemblers — Function
LLVM.InitializeAllDisassemblers()
LLVM.InitializeXXXDisassembler()
LLVM.InitializeNativeDisassembler()Enables use of disassembly functionality for specific targets.
Contexts
LLVM.Context — Type
LLVM.ContextExecution state for the core LLVM IR system. Created by calling the Context() constructor, and should be disposed of.
Most types are tied to a context instance. Multiple contexts can exist simultaneously. A single context is not thread safe. However, different contexts can execute on different threads simultaneously.
Properties
ctx.typesThe named types of the context, as a view that supports looking up a type by its name (haskey, get and indexing). LLVM does not support iterating these types, so the view is not a collection.
LLVM.Context — Method
LLVM.Context(; opaque_pointers=nothing)Create a new LLVM context. If opaque_pointers is true, the context will use opaque pointers instead of typed pointers (if suppoprted). Otherwise the behavior of the context depends on the LLVM version.
This object needs to be disposed of using dispose(::Context).
LLVM.dispose — Method
dispose(ctx::Context)Dispose of the context, releasing all resources associated with it, including the modules that are still part of it, which don't need to be disposed of separately. Neither the context nor its modules should be used after this operation.
If an exception is in flight (e.g., when the context is disposed of by a finally block during stack unwinding), the context is popped from the context stack but intentionally leaked instead of freed. This keeps any values captured by the exception valid, so that error reporting does not crash the process.
LLVM.supports_typed_pointers — Function
supports_typed_pointers()Check whether the current context supports typed pointers.
LLVM.jl also tracks the context in task-local scope:
LLVM.activate — Function
activate(obj)Make obj the active object of its kind for the current task, by pushing it onto a task-local stack, e.g., the stack of contexts whose top context() returns. Undo this using deactivate, in reverse order.
Packages can add methods for their own types, e.g., to maintain a task-local stack of the contexts of a related C API. Such a stack is separate from LLVM.jl's: activating one of these objects doesn't activate an LLVM context.
LLVM.deactivate — Function
deactivate(obj)Undo activate(obj), popping obj from the task-local stack that it was pushed onto, which throws an error if obj isn't the active object.
Packages can add methods for their own types, along with methods for activate.
LLVM.context — Method
context(; throw_error::Bool=true)Get the active LLVM context for the current tasks. Throws an exception if no context is active, unless throw_error=false.
LLVM.activate — Method
activate(ctx::LLVM.Context)Pushes a new context onto the context stack.
LLVM.deactivate — Method
deactivate(ctx::LLVM.Context)Pops the current context from the context stack.
LLVM.context! — Function
context!(ctx::LLVM.Context) do
...
endTemporarily activates the given context for the duration of the block.
LLVM.ts_context — Function
ts_context(; throw_error::Bool=true)Get the active LLVM thread-safe context for the current tasks. Throws an exception if no context is active, unless throw_error=false.
LLVM.activate — Method
activate(ts_ctx::LLVM.ThreadSafeContext)Pushes a new thread-safe context onto the context stack.
LLVM.deactivate — Method
deactivate(ts_ctx::LLVM.ThreadSafeContext)Pops the current thread-safe context from the context stack.
LLVM.ts_context! — Function
ts_context!(ts_ctx::LLVM.ThreadSafeContext) do
...
endTemporarily activates the given thread-safe context for the duration of the block.
Resources
LLVM.dispose — Function
dispose(obj)Release the resources of obj, e.g., the LLVM object that it wraps, after which it can't be used anymore. This is what @dispose and the do-block forms of constructors call. See the methods for LLVM.jl's types for what disposing of them entails, e.g., dispose(::Context) and dispose(::LLVM.Module). Whether an object can be disposed of more than once depends on its type.
Packages can add methods for their own types, e.g., wrappers of a related C API, so that they can be used with @dispose. Such a method releases the resource as its type's ownership semantics require. Adding it doesn't register a finalizer, or make the memcheck debugging mode check the type: use LLVM.mark_dispose for that (see Checking other wrapper types).
LLVM.@dispose — Macro
@dispose foo=Foo() bar=Bar() begin
...
endHelper macro for disposing resources (by calling the dispose function for every resource in reverse order) after executing a block of code. This is often equivalent to calling the resource constructor with do-block syntax, but without using (potentially costly) closures.
Resources are constructed in order, and each one is disposed of even if constructing a later resource fails. For example, if Bar() throws, foo is still disposed of.
LLVM.consume! — Function
LLVM.consume!(obj)
LLVM.consume!(buf::MemoryBuffer; borrow=false)Hand obj over to foreign code that takes ownership of it, returning its raw handle. This is for calling a C API that takes ownership of an object, e.g., using ccall, which LLVM.jl's own consuming operations (like adding a buffer or module to a JIT) do automatically. Afterwards, the wrapper can't be used anymore, and disposing of it does nothing, like after those operations:
@dispose tsm=ThreadSafeModule("jit") begin
...
ccall(:jl_consume_module, Cvoid, (LLVM.API.LLVMOrcThreadSafeModuleRef,),
LLVM.consume!(tsm))
endThis is an irreversible handoff, not a conversion: the object isn't disposed of if the foreign call fails, so validate the other arguments first, and call consume! right before the call. For a C API that only takes ownership when it succeeds, pass the object itself to the call (which converts it to its handle without consuming it), and call consume! after it succeeded.
Some foreign code takes ownership of an object, but keeps it alive and lets the caller keep using it, e.g., clang's SourceManager with a memory buffer that is then lexed. For a MemoryBuffer, LLVM.consume!(buf; borrow=true) expresses such a handover: the wrapper can still be used, but not consumed again, and disposing of it does nothing. This doesn't extend the lifetime of the buffer, so it can only be used for as long as its new owner keeps it alive:
@dispose buf=MemoryBuffer(data) begin
fid = ccall(:create_file_id, Cint, (Ptr{Cvoid}, LLVM.API.LLVMMemoryBufferRef),
source_manager, LLVM.consume!(buf; borrow=true))
lex(source_manager, fid, buf) # `buf` is still usable
end # and isn't freed hereOnly this wrapper changes state, not other wrappers of the same object, and the raw handle doesn't keep Julia objects alive that the wrapper references (e.g., the callbacks of an LLJITBuilder), so use GC.@preserve obj around the foreign call.
This is supported by the objects that track their ownership: MemoryBuffer, TargetMachine, ThreadSafeModule, MaterializationUnit, MaterializationResponsibility, DefinitionGenerator, ObjectLinkingLayer, TargetMachineBuilder and LLJITBuilder. Objects that are borrowed from LLVM (e.g., the thread-safe module and materialization responsibility of an IR transformation) can't be consumed, and neither can consumed or disposed objects.
LLVM.adopt — Function
LLVM.adopt(obj) -> objRegister obj as an object that foreign code handed over to the caller, e.g., one that a C API returned with ownership, which the caller is then responsible for disposing of (or for handing over again, e.g., to an operation that consumes it), like an object that LLVM.jl created. This is the opposite of LLVM.consume!:
ref = ccall(:create_module, LLVM.API.LLVMModuleRef, ())
mod = LLVM.adopt(LLVM.Module(ref))
...
dispose(mod)This is bookkeeping for the memcheck debugging mode, which otherwise reports disposing of the object as disposing of an unknown instance, and afterwards checks the object like one that LLVM.jl created. It does nothing else: it doesn't take ownership from foreign code that still owns the object, or make a borrowed, consumed or disposed object usable.
This is supported for LLVM.Module, Context, MemoryBuffer and LLVM.GenericValue. An adopted context owns the modules in it, like one that LLVM.jl created (see dispose(::Context)), so adopt it before adopting its modules. Unlike creating a context, adopting one doesn't activate it, while disposing of it pops it from the context stack, so activate it before disposing of it.
Memory checking
These functions make the memcheck debugging mode check the wrapper types of other packages (see Checking other wrapper types).
LLVM.mark_alloc — Function
LLVM.mark_alloc(obj; owner=nothing) -> objRegister obj as a newly allocated object with the memcheck debugging mode, which then reports using it after it was disposed of (see LLVM.mark_use), disposing of it twice (see LLVM.mark_dispose), and not disposing of it at all (when the process exits). This is meant for packages that wrap a C API in wrapper types of their own, to check these like LLVM.jl's objects (see Checking other wrapper types). Register the wrapper that owns a resource when creating it:
Thing() = LLVM.mark_alloc(Thing(API.thing_create()))Objects are identified by ===, not by == or hash: wrapping the same handle in an immutable wrapper type again gives the same object (if its other fields, if any, are === too), while a mutable wrapper is identified by the Julia object itself, and wrappers of different types are different objects. Registering an allocation of an object that is still alive is reported, as it wasn't disposed of; when the address of an object that was disposed of is reused, wrappers of the earlier object become indistinguishable from the new one.
owner is another tracked object whose disposal ends the lifetime of obj, like a context does with the modules in it: using or disposing of obj after its owner was disposed of is reported, and obj isn't reported as leaked once its owner was disposed of. This is only bookkeeping, which doesn't extend the lifetime of the owner's resource, or transfer ownership in the foreign library. The owner must be tracked and alive, and not be owned by obj; otherwise, the problem is reported, and obj is tracked without an owner.
When memcheck is disabled, this only returns obj (although the arguments are still evaluated). When it is enabled, it keeps the wrappers that it tracks reachable, also after they have been disposed of, so garbage collection doesn't finalize mutable wrappers.
LLVM.mark_use — Function
LLVM.mark_use(obj) -> objCheck that obj, as registered using LLVM.mark_alloc, hasn't been disposed of, and that its owner hasn't been disposed of either, reporting the problem otherwise. Objects that aren't tracked, e.g., wrappers of handles that the foreign library lends out, aren't checked. Typically, this is used when a wrapper is converted to its handle:
Base.unsafe_convert(::Type{API.ThingRef}, t::Thing) = LLVM.mark_use(t).refThis only reports the problem: using the object can still crash the process afterwards. When memcheck is disabled, this only returns obj.
LLVM.mark_dispose — Function
LLVM.mark_dispose(f, obj) -> nothingDispose of obj by calling f(obj) (discarding what it returns), and record its disposal with the memcheck debugging mode:
dispose(t::Thing) = LLVM.mark_dispose(API.thing_destroy, t)- When
objwas disposed of already, or its owner was, the problem is reported, andfis not called, as freeing its memory again would crash the process or corrupt the heap. This only applies to disposals that have been recorded: it doesn't synchronize concurrent (or recursive) disposal of the same object. - When
objisn't tracked (seeLLVM.mark_alloc), its disposal is reported, andfis called nonetheless. - When
freturns,objis recorded as disposed of, which ends the lifetime of the objects that it owns. Whenfthrows, the exception is rethrown without recording the disposal (even thoughfmay have freed the object already).
When memcheck is disabled, this only calls f(obj).
LLVM.mark_untracked — Function
LLVM.mark_untracked(obj) -> objStop tracking obj with the memcheck debugging mode, without disposing of it: it isn't checked anymore, or reported as leaked. Use this when a tracked object is handed over to the foreign library, which keeps it alive (and disposes of it), after the operation that takes ownership succeeded:
function Base.push!(c::Container, t::Thing)
API.container_append_owned(c, t)
LLVM.mark_untracked(t)
return c
endIf the object can't be used anymore after handing it over, e.g., because the operation destroys it, use LLVM.mark_dispose instead (LLVM.mark_dispose(t -> API.container_consume(c, t), t)), so that later uses are reported. This can also be used for wrappers of handles that the foreign library lends out, which might otherwise be mistaken for an object that was disposed of at the same address.
The objects that obj owned stay tracked without an owner, so they are reported as leaked unless they are disposed of (while the objects that they own keep their owner). Memcheck doesn't follow the object to its new owner, and doesn't relate other wrappers of the same resource to it: a wrapper of another type, e.g., one that views the resource differently, is a different object, that isn't tracked unless it is registered itself.
When memcheck is disabled, this only returns obj.
Exceptions
LLVM.LLVMException — Type
LLVMExceptionException type for errors reported by the LLVM API.
Memory buffers
LLVM.MemoryBuffer — Type
MemoryBufferA memory buffer representing a simple block of memory.
Some operations take ownership of a memory buffer, like adding an object file to a JIT or lazily parsing bitcode. These consume the buffer: it can't be used anymore afterwards, and disposing of it does nothing, so that it can be disposed of unconditionally, e.g., using the do-block form of its constructor.
Foreign code that takes ownership of a buffer can also keep it alive and let callers keep reading it, e.g., clang's SourceManager when creating a file from a buffer. To hand a buffer over to such code while keeping it usable, use LLVM.consume!(buf; borrow=true).
LLVM.MemoryBuffer — Method
MemoryBuffer(data::Vector{T}, name::AbstractString="", copy::Bool=true)Create a memory buffer from the given data. If copy is true, the data is copied into the buffer. Otherwise, the user is responsible for keeping the data alive across the lifetime of the buffer.
This object needs to be disposed of using dispose.
LLVM.MemoryBufferFile — Function
MemoryBufferFile(path::AbstractString)Create a memory buffer from the contents of a file.
This object needs to be disposed of using dispose.
LLVM.dispose — Method
dispose(membuf::MemoryBuffer)Dispose of the given memory buffer, unless it has been consumed. Disposing of a buffer that was consumed with borrow=true does nothing, and leaves it usable.
Other
LLVM.clopts — Function
clopts(opts...)Parse the given arguments using the LLVM command-line parser.
Note that this function modifies the global state of the LLVM library. It is also not safe to rely on the stability of the command-line options between different versions of LLVM.
LLVM.ismultithreaded — Function
ismultithreaded()Check whether LLVM is executing in thread-safe mode or not.