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)Libraries and versions
LLVM.version — Function
LLVM.version() -> VersionNumber
LLVM.version(lib::LLVM.Library) -> VersionNumber
LLVM.version(obj) -> VersionNumberThe version of LLVM of the current library (see LLVM.library), of lib, or of the library that obj belongs to: an object that remembers its library, like a context, module, builder or target machine.
Code that depends on the version of LLVM should check it at run time, for the library it works with: e.g., LLVM.version(mod) >= v"21" when adding passes that run on mod. Checking it at compile time (@static if LLVM.version() ...) uses the library that LLVM.jl uses by default, while other libraries can be used at the same time.
LLVM.Library — Type
LLVM.LibraryAn LLVM library that LLVM.jl can use, together with the LLVMExtra library built for it. Libraries are registered under a name, e.g., :julia for the library Julia uses, and looked up by calling LLVM.Library(name).
Objects that own LLVM resources, like contexts, modules and target machines, remember the library that created them in their library property, and are always used with it. Values, types and metadata do not: they need to be used with the current library (see LLVM.library!), typically by working with them inside a context! block. They can be kept after leaving the block while their LLVM objects are alive, but using them requires selecting that library again. Mixing such handles from different libraries is a caller error that LLVM.jl cannot always detect.
Properties
lib.name
lib.version
lib.pathThe name the library was registered under, its version of LLVM, and its path.
LLVM.Library — Method
LLVM.Library(name::Symbol)Look up the library registered under name, e.g., :julia for the one Julia uses.
LLVM.library — Function
LLVM.library() -> LLVM.LibraryThe current LLVM library: the one that is used by operations whose arguments do not determine it, like creating a context. This is the library Julia uses, unless the libLLVM preference selects another one, and can be changed for the duration of a block using LLVM.library! or context!.
LLVM.library! — Function
LLVM.library!(f, lib::Union{LLVM.Library,Symbol})Make lib (or the library registered under that name) the current library while calling f, and the tasks it starts.
LLVM.register_library! — Function
LLVM.register_library!(name::Symbol, path; extra=path) -> LLVM.LibraryRegister the LLVM library at path under name, with the LLVMExtra library at extra, and return it. The library is loaded and initialized like Julia initializes its own: the native target is initialized if the library supports it. Registering the same library under another name returns the same Library.
The library should link LLVM statically and only export the C API, like the libraries built by deps/build_standalone.jl; loading another shared libLLVM in the process clashes with Julia's.
Initialization
LLVM.backends — Function
backends()Return a list of back-ends supported by the current 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.libraryThe LLVM.Library that the context belongs to, as do the objects in it.
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, library=LLVM.library())Create a new LLVM context, and make it the active context (see 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.
The context belongs to library (an LLVM.Library or the name of one). The do-block form Context(...) do ctx ... end also makes that library the current one (see LLVM.library!), which is required for using a context that does not belong to the current library.
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 of the current library (see LLVM.library) for the current task: the one that was activated most recently. Throws an exception if no context of that library is active, unless throw_error=false.
LLVM.activate — Method
activate(ctx::LLVM.Context)Make ctx the active context of its library for the current task (see context), until it is deactivated.
LLVM.deactivate — Method
deactivate(ctx::LLVM.Context)Undo activate(ctx). Contexts of a library need to be deactivated in the reverse order of their activation, which throws an error otherwise.
LLVM.context! — Function
context!(ctx::LLVM.Context) do
...
endTemporarily activates the given context for the duration of the block, and makes its library the current one (see LLVM.library!).
LLVM.ts_context — Function
ts_context(; throw_error::Bool=true)Get the active LLVM thread-safe context of the current library for the current task. Throws an exception if no thread-safe context of that library is active, unless throw_error=false.
LLVM.activate — Method
activate(ts_ctx::LLVM.ThreadSafeContext)Make ts_ctx the active thread-safe context of its library for the current task (see ts_context), until it is deactivated.
LLVM.deactivate — Method
deactivate(ts_ctx::LLVM.ThreadSafeContext)Undo activate(ts_ctx), in the reverse order of activation like for contexts.
LLVM.ts_context! — Function
ts_context!(ts_ctx::LLVM.ThreadSafeContext) do
...
endTemporarily activates the given thread-safe context for the duration of the block, and makes its library the current one (see LLVM.library!).
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.