Essentials

Vocabularies

LLVM.IR — Module
LLVM.IR

The 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
end

The 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.

source
LLVM.Build — Module
LLVM.Build

Construction 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...))
end
source
LLVM.Passes — Module
LLVM.Passes

Optimization 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)
end
source
LLVM.Analysis — Module
LLVM.Analysis

LLVM'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)
source
LLVM.ORC — Module
LLVM.ORC

The ORC just-in-time compiler: LLJIT, execution sessions, JIT dylibs, thread-safe modules and contexts, symbols and their definitions, definition generators, resource trackers, materialization units, and the layers of the JIT.

source

Version

LLVM.version — Function
LLVM.version() -> VersionNumber

The version of the LLVM library that LLVM.jl uses, which is the one Julia uses.

source

Initialization

LLVM.backends — Function
backends()

Return a list of back-ends supported by the LLVM library.

source
LLVM.InitializeAllTargets — Function
LLVM.InitializeAllTargets()
LLVM.InitializeXXXTarget()
LLVM.InitializeNativeTarget()

Enables use of specific targets.

source
LLVM.InitializeAllAsmParsers — Function
LLVM.InitializeAllAsmParsers()
LLVM.InitializeXXXAsmParser()
LLVM.InitializeNativeAsmParser()

Enables use of assembly parsing functionality for specific targets.

source
LLVM.InitializeAllAsmPrinters — Function
LLVM.InitializeAllAsmPrinters()
LLVM.InitializeXXXAsmPrinter()
LLVM.InitializeNativeAsmPrinter()

Enables use of assembly output functionality for specific targets.

source
LLVM.InitializeAllDisassemblers — Function
LLVM.InitializeAllDisassemblers()
LLVM.InitializeXXXDisassembler()
LLVM.InitializeNativeDisassembler()

Enables use of disassembly functionality for specific targets.

source

Contexts

LLVM.Context — Type
LLVM.Context

Execution 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.types

The 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.

source
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).

source
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.

source

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.

source
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.

source
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.

source
LLVM.activate — Method
activate(ctx::LLVM.Context)

Pushes a new context onto the context stack.

source
LLVM.deactivate — Method
deactivate(ctx::LLVM.Context)

Pops the current context from the context stack.

source
LLVM.context! — Function
context!(ctx::LLVM.Context) do
    ...
end

Temporarily activates the given context for the duration of the block.

source
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.

source
LLVM.activate — Method
activate(ts_ctx::LLVM.ThreadSafeContext)

Pushes a new thread-safe context onto the context stack.

source
LLVM.deactivate — Method
deactivate(ts_ctx::LLVM.ThreadSafeContext)

Pops the current thread-safe context from the context stack.

source
LLVM.ts_context! — Function
ts_context!(ts_ctx::LLVM.ThreadSafeContext) do
    ...
end

Temporarily activates the given thread-safe context for the duration of the block.

source

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).

source
LLVM.@dispose — Macro
@dispose foo=Foo() bar=Bar() begin
    ...
end

Helper 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.

source
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))
end

This 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 here

Only 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.

source
LLVM.adopt — Function
LLVM.adopt(obj) -> obj

Register 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.

source

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) -> obj

Register 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.

source
LLVM.mark_use — Function
LLVM.mark_use(obj) -> obj

Check 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).ref

This only reports the problem: using the object can still crash the process afterwards. When memcheck is disabled, this only returns obj.

source
LLVM.mark_dispose — Function
LLVM.mark_dispose(f, obj) -> nothing

Dispose 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 obj was disposed of already, or its owner was, the problem is reported, and f is 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 obj isn't tracked (see LLVM.mark_alloc), its disposal is reported, and f is called nonetheless.
  • When f returns, obj is recorded as disposed of, which ends the lifetime of the objects that it owns. When f throws, the exception is rethrown without recording the disposal (even though f may have freed the object already).

When memcheck is disabled, this only calls f(obj).

source
LLVM.mark_untracked — Function
LLVM.mark_untracked(obj) -> obj

Stop 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
end

If 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.

source

Exceptions

Memory buffers

LLVM.MemoryBuffer — Type
MemoryBuffer

A 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).

source
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.

source
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.

source
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.

source

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.

source