Execution
Generic Value
LLVM.GenericValue — Type
GenericValueA generic value that can be passed to or returned from a function in the execution engine.
Note that only simple types are supported, and for most use cases it is recommended to look up the address of the compiled function and ccall it directly.
This object needs to be disposed of using dispose.
Properties
val.intwidthThe bit width of the integer value stored in the generic value.
LLVM.dispose — Method
dispose(val::GenericValue)Dispose of the given generic value.
Integer
LLVM.GenericValue — Method
GenericValue(typ::LLVM.IntegerType, N::Integer)Create a generic value from an integer of the given type.
Base.convert — Method
convert(::Type{<:Integer}, val::GenericValue)Convert a generic value to an integer of the given type.
Floating Point
LLVM.GenericValue — Method
GenericValue(typ::LLVM.FloatingPointType, N::AbstractFloat)Create a generic value from a floating point number of the given type, which needs to be LLVM.FloatType() or LLVM.DoubleType(): generic values only support single and double precision floating point numbers.
LLVM.to_float — Function
LLVM.to_float(val::GenericValue, typ::LLVM.FloatingPointType) -> Float64Get the floating point number stored in a generic value. Unlike integers, generic values don't know the type of the floating point number they store, so it needs to be passed explicitly: LLVM.FloatType() or LLVM.DoubleType(). Use convert(T, LLVM.to_float(val, typ)) to get another Julia type.
Pointer
LLVM.GenericValue — Method
GenericValue(ptr::Ptr)Create a generic value from a pointer.
Base.convert — Method
convert(::Type{Ptr{T}}, val::GenericValue)Convert a generic value to a pointer.
MCJIT
LLVM.ExecutionEngine — Type
LLVM.ExecutionEngineAn execution engine that can run functions in a module.
Properties
engine.functionsThe functions in the modules of the execution engine, as a view that supports looking up a function by name (get, haskey and indexing). The functions cannot be iterated, so the view is not a collection.
Ownership
An execution engine takes ownership of the modules it is created with or that are added to it using push!, and disposes of them together with itself, so these modules must not be disposed of separately. The constructors take ownership of the module even if they fail. Use delete!(engine, mod) to take back ownership of a module.
LLVM.ExecutionEngine — Method
LLVM.ExecutionEngine(mod::Module)Create an execution engine for the given module, taking ownership of it: a JIT compiler if possible, or an interpreter otherwise.
This object needs to be disposed of using dispose.
LLVM.Interpreter — Function
Interpreter(mod::Module)Create an interpreter for the given module, taking ownership of it.
This object needs to be disposed of using dispose.
LLVM.dispose — Method
dispose(engine::ExecutionEngine)Dispose of the given execution engine.
Base.push! — Method
push!(engine::LLVM.ExecutionEngine, mod::Module)Add another module to the execution engine.
This takes ownership of the module.
Base.delete! — Method
delete!(engine::ExecutionEngine, mod::Module)Remove a module from the execution engine.
Ownership of the module is transferred back to the user. Does nothing if the module isn't part of the engine.
LLVM.execute — Function
LLVM.execute(engine::ExecutionEngine, f::LLVM.Function,
[args::AbstractVector{GenericValue}]) -> GenericValueRun the function f with the given arguments in the execution engine, and return its result. The arguments are only borrowed, while the result needs to be disposed of using dispose.
LLVM.lookup — Method
lookup(engine::ExecutionEngine, fn::AbstractString)Look up the address of the given function in the execution engine.
LLVM.run_static_constructors! — Function
LLVM.run_static_constructors!(engine::ExecutionEngine)
LLVM.run_static_destructors!(engine::ExecutionEngine)Run the static constructors or destructors of the modules in the execution engine, i.e., the functions listed in their llvm.global_ctors or llvm.global_dtors arrays. Code that relies on global initialization, like C++ code that has global objects, needs its constructors to run before it is executed, and its destructors after it was last used.
The execution engine doesn't run these functions by itself, also not when it is disposed of, and doesn't keep track of whether they have run already: calling these functions again, e.g., after adding a module, runs the constructors or destructors of all its modules again. The priorities of the constructors and destructors are not taken into account.
On macOS with LLVM 17 or later, generating machine code for a module rewrites its destructors into registrations with __cxa_atexit that are performed by an additional constructor. With a JIT engine, that constructor cannot be found, and running the static constructors of a module that has llvm.global_dtors crashes. Its destructors also cannot be run explicitly anymore. The Interpreter is not affected.
LLVM.run_static_destructors! — Function
LLVM.run_static_constructors!(engine::ExecutionEngine)
LLVM.run_static_destructors!(engine::ExecutionEngine)Run the static constructors or destructors of the modules in the execution engine, i.e., the functions listed in their llvm.global_ctors or llvm.global_dtors arrays. Code that relies on global initialization, like C++ code that has global objects, needs its constructors to run before it is executed, and its destructors after it was last used.
The execution engine doesn't run these functions by itself, also not when it is disposed of, and doesn't keep track of whether they have run already: calling these functions again, e.g., after adding a module, runs the constructors or destructors of all its modules again. The priorities of the constructors and destructors are not taken into account.
On macOS with LLVM 17 or later, generating machine code for a module rewrites its destructors into registrations with __cxa_atexit that are performed by an additional constructor. With a JIT engine, that constructor cannot be found, and running the static constructors of a module that has llvm.global_dtors crashes. Its destructors also cannot be run explicitly anymore. The Interpreter is not affected.
Process-wide symbols
LLVM.load_library_permanently — Function
LLVM.load_library_permanently(path::AbstractString)Load the dynamic library at path, and make its symbols available to the legacy execution engines (e.g., LLVM.JIT) for resolving external symbols. This uses LLVM's process-wide symbol search (sys::DynamicLibrary): the library stays loaded for the remainder of the process, and it is safe to load the same library multiple times. Throws an LLVMException if the library cannot be loaded.
For ORC JITs, add a DynamicLibrarySearchGenerator(jit, path) to the JITDylib that should see the library instead.
See also: LLVM.add_symbol, LLVM.find_symbol.
LLVM.add_symbol — Function
LLVM.add_symbol(name::AbstractString, ptr::Ptr)Make the symbol name resolve to the address ptr for the legacy execution engines (e.g., LLVM.JIT). Like LLVM.load_library_permanently, this uses LLVM's process-wide symbol search, where symbols added this way take precedence over those of libraries. The symbol remains defined for the remainder of the process (adding it again replaces its address), and LLVM.jl does not keep what ptr points to alive, e.g., a Julia object or a closure passed to @cfunction.
This does not affect ORC JITs: to define a symbol in a JITDylib, use absolute_symbols.
LLVM.find_symbol — Function
LLVM.find_symbol(name::AbstractString) -> Ptr{Cvoid}Look up the address of the symbol name the way the legacy execution engines do: in the symbols added with LLVM.add_symbol, and then in the process and the libraries loaded with LLVM.load_library_permanently. Returns C_NULL if the symbol cannot be found.
ORC
Thread-safe contexts and modules
LLVM.ThreadSafeContext — Type
ThreadSafeContextA thread-safe version of Context.
The underlying context is reference counted: the thread-safe context and every ThreadSafeModule of it share it, so that it remains alive while one of them is. That doesn't extend to the regular modules in the context, e.g., ones created after activating context(ts_ctx), copied from the module of a thread-safe module, or taken out of one using LLVM.unsafe_take_module!: these can only be used while the thread-safe context they were created with is alive, as disposing of it may free them. To keep such a module, wrap it in a thread-safe module, or serialize it, before disposing of the thread-safe context.
LLVM.ThreadSafeContext — Method
ThreadSafeContext(; opaque_pointers=nothing, library=LLVM.library())Create a new thread-safe context. The behavior of opaque_pointers and library is the same as in Context.
This object needs to be disposed of using dispose(::ThreadSafeContext).
LLVM.context — Method
context(ts_ctx::ThreadSafeContext)Obtain the context associated with a thread-safe context.
LLVM.dispose — Method
dispose(ctx::ThreadSafeContext)Dispose of the thread-safe context, releasing all resources associated with it. This frees the underlying context, and the modules in it, unless a thread-safe module (or foreign code) keeps it alive. Regular modules in the context can't be used afterwards in either case (see ThreadSafeContext).
If an exception is in flight, the context is leaked instead of freed, so that values captured by the exception remain valid; see dispose(::Context).
LLVM.ThreadSafeModule — Type
ThreadSafeModuleA thread-safe version of LLVM.Module.
A thread-safe module is consumed by adding it to a JIT, e.g., with add! or emit!, after which it can't be used anymore, and disposing of it does nothing, so it can be disposed of unconditionally, e.g., using the do-block form of its constructors (ThreadSafeModule(name) do tsm ... end). That's different from calling the module (tsm() do mod ... end), which gives access to the module it contains. The modules that an IR transformation receives are borrowed: they can be used during the transformation, but not be consumed or disposed of.
To use a thread-safe module that foreign code created, e.g., a ccall that returns a LLVMOrcThreadSafeModuleRef, wrap its handle with ThreadSafeModule(ref), which takes over the responsibility to dispose of or consume it, or ThreadSafeModule(ref; borrowed=true) to only use it, in which case it can't be disposed of or consumed. Neither keeps the object alive, or affects its lifetime otherwise: a borrowed thread-safe module can only be used for as long as its owner keeps it alive. To hand an owned thread-safe module over to foreign code, use LLVM.consume!.
LLVM.ThreadSafeModule — Method
ThreadSafeModule(name::AbstractString)Create a thread-safe module with the given name.
This object needs to be disposed of using dispose(::ThreadSafeModule).
LLVM.ThreadSafeModule — Method
ThreadSafeModule(mod::Module)Create a thread-safe module from a regular module. This transfers ownership of the module to the thread-safe module, so the module must not be disposed of afterwards.
The context of the module must be the same as the active thread-safe context of the module's library.
This object needs to be disposed of using dispose(::ThreadSafeModule).
LLVM.dispose — Method
dispose(mod::ThreadSafeModule)Dispose of the thread-safe module, releasing all resources associated with it, unless it has been consumed. Borrowed modules can't be disposed of.
LLVM.unsafe_module — Function
LLVM.unsafe_module(tsm::ThreadSafeModule)Get the module contained in tsm, without locking or activating its context. This is an escape hatch for when calling the thread-safe module (tsm() do mod ... end), which locks its context while giving access to the module, isn't possible, e.g., when the module needs to be used after the thread-safe module was obtained from and returned to foreign code.
The module is borrowed: it can only be used for as long as the thread-safe module and its context are alive, and the thread-safe module isn't consumed. The caller is responsible for synchronizing all accesses to the context (which other threads may be using to compile code), for activating it if needed, and should never dispose of the module. The memcheck debugging mode doesn't track the module afterwards, also not when calling tsm. To take ownership of the module instead, see LLVM.unsafe_take_module!.
LLVM.unsafe_take_module! — Function
LLVM.unsafe_take_module!(tsm::ThreadSafeModule) -> LLVM.ModuleMove the module out of tsm, leaving the thread-safe module empty, and return it. The caller owns the module afterwards: it has to be disposed of, or handed over to an operation that consumes it (like link!). This requires LLVM 16 or later.
This is for taking ownership of the module of a thread-safe module that foreign code owns, e.g., the one that Julia's code generator returns. That's destructive access, also through a borrowed thread-safe module: the caller must know that nothing else uses the module of tsm afterwards, as its owner keeps the empty thread-safe module. Using an empty thread-safe module (calling it, adding it to a JIT, or taking its module again) throws an ArgumentError; disposing of it is fine.
The module still belongs to the context of the thread-safe module, which the caller has to synchronize accesses to. It can only be used while the thread-safe context of tsm is alive (see ThreadSafeContext), e.g., inside the do-block that created it.
JITs
LLVM.LLJIT — Type
LLJITLLVM's standard ORC-based JIT, which compiles and links code on demand, i.e., when it is looked up. It needs to be disposed of using dispose, or by using the do-block form of its constructors.
Properties
jit.tripleThe target triple that the JIT compiles code for. Modules added to the JIT should use it.
jit.datalayout_stringThe data layout that the JIT compiles code for, as a string. Modules added to the JIT should use it (mod.datalayout = jit.datalayout_string). Use DataLayout(jit) to query the layout.
jit.global_prefixThe character that the JIT's target prepends to global symbols when mangling them (e.g., '_' on macOS), or '\0' if there is none, as a Cchar.
jit.execution_sessionThe ExecutionSession of a JIT, which manages its JITDylibs and symbol string pool.
lljit.main_dylibThe main JITDylib of the JIT, which lookup(lljit, name) searches.
lljit.ir_transform_layerThe IRTransformLayer of the JIT, which transforms IR modules before they are compiled. Modules added with add! pass through this layer, as can modules emitted by a materialization unit with emit!. By default, it does not change modules; use transform! to install a transformation.
LLVM.LLJITBuilder — Type
LLJITBuilder()Create a builder to customize the construction of an LLJIT, e.g., using target_machine_builder! or linking_layer_creator!. The builder is consumed when constructing the JIT; otherwise, it needs to be disposed of using dispose or the do-block form, which do nothing once it has been consumed.
LLVM.target_machine_builder! — Function
target_machine_builder!(builder::LLJITBuilder, tmb::TargetMachineBuilder)Use tmb to create the JIT's target machines, consuming it.
LLVM.linking_layer_creator! — Function
linking_layer_creator!(builder::LLJITBuilder, callback, ctx)Install a raw LLVM object-layer-creator callback and context pointer.
linking_layer_creator!(builder::LLJITBuilder, creator)Install a Julia object-layer creator, called with the execution session and target triple. The builder keeps it rooted until it is consumed by LLJIT. If it throws, the exception is rethrown as a CallbackException after LLJIT construction returns through LLVM.
LLVM.TargetMachineBuilder — Type
TargetMachineBuilder()
TargetMachineBuilder(tm::TargetMachine)Create a builder of target machines, as used by an LLJITBuilder to create the target machines that compile code. The builder either targets the host, or is based on tm, taking ownership of it.
The builder is consumed by target_machine_builder!; otherwise, it needs to be disposed of using dispose or the do-block form, which do nothing once it has been consumed.
LLVM.ObjectLinkingLayer — Type
ObjectLinkingLayerAn object linking layer, based on RuntimeDyld, for use with linking_layer_creator!. Use register! to attach a JITEventListener to it.
The layer is consumed by returning it from a linking layer creator, which hands it over to the JIT; otherwise, it needs to be disposed of using dispose or the do-block form of the constructor, which do nothing once it has been consumed.
LLVM.ObjectLinkingLayer — Method
ObjectLinkingLayer(es::ExecutionSession,
triple::AbstractString=LLVM.default_triple();
override_object_flags=nothing, auto_claim_object_symbols=nothing)Create a RuntimeDyld-based object linking layer that allocates memory using a SectionMemoryManager.
The layer is configured the same way LLJIT configures its default object layer for triple, which should describe the object files the layer will link. Objects for COFF targets (e.g., Windows) do not carry reliable symbol visibility information, so on those targets the layer uses the symbol flags from the IR instead of from the object file (override_object_flags), and takes responsibility for additional symbols that code generation introduced (auto_claim_object_symbols). Pass true or false to either keyword argument to override the default.
The triple defaults to the host's. In a linking_layer_creator! callback, pass the triple the callback receives:
linking_layer_creator!(builder) do es, triple
ObjectLinkingLayer(es, triple)
endOn LLVM 21 and newer, that is the triple of the process executing the code rather than that of the target machine, so pass the target's triple explicitly when JIT-compiling for a different object format.
LLVM.GDBRegistrationListener — Function
GDBRegistrationListener()
IntelJITEventListener()
OProfileJITEventListener()
PerfJITEventListener()Create a listener for the events of a JIT, to register the code that it emits with GDB, Intel VTune, OProfile or Linux' perf, e.g., using register! on an ObjectLinkingLayer. Creating a listener for a profiler that LLVM wasn't built with support for throws an UndefRefError.
LLVM.IntelJITEventListener — Function
GDBRegistrationListener()
IntelJITEventListener()
OProfileJITEventListener()
PerfJITEventListener()Create a listener for the events of a JIT, to register the code that it emits with GDB, Intel VTune, OProfile or Linux' perf, e.g., using register! on an ObjectLinkingLayer. Creating a listener for a profiler that LLVM wasn't built with support for throws an UndefRefError.
LLVM.OProfileJITEventListener — Function
GDBRegistrationListener()
IntelJITEventListener()
OProfileJITEventListener()
PerfJITEventListener()Create a listener for the events of a JIT, to register the code that it emits with GDB, Intel VTune, OProfile or Linux' perf, e.g., using register! on an ObjectLinkingLayer. Creating a listener for a profiler that LLVM wasn't built with support for throws an UndefRefError.
LLVM.PerfJITEventListener — Function
GDBRegistrationListener()
IntelJITEventListener()
OProfileJITEventListener()
PerfJITEventListener()Create a listener for the events of a JIT, to register the code that it emits with GDB, Intel VTune, OProfile or Linux' perf, e.g., using register! on an ObjectLinkingLayer. Creating a listener for a profiler that LLVM wasn't built with support for throws an UndefRefError.
LLVM.JuliaOJIT — Type
JuliaOJIT()
JuliaOJIT(f)Get a handle to Julia's own JIT, e.g., to add code to it that can be called from Julia code. The JIT is not owned by LLVM.jl, so disposing of the handle is a no-op.
Code is added to JITDylibs, which depend on the Julia version: on Julia 1.14 and later, create one with JITDylib(jljit, name), which creates a new JITDylib on every call; on older versions, use the single JITDylib that is shared by all users of the JIT (jljit.external_dylib). Look up symbols in the JITDylib that the code was added to, e.g., by choosing one at initialization and keeping it:
jd = if LLVM.supports_jit_dylib_creation(jljit)
JITDylib(jljit, "MyPackage")
else
jljit.external_dylib
endCode that adds many independently generated modules, whose symbol names needn't be unique across them (e.g., modules generated by Julia), can instead create a JITDylib per module on Julia 1.14 and later, to avoid conflicting symbol definitions in a shared JITDylib. The symbols of a created JITDylib aren't automatically available for symbol resolution by code that Julia compiles (looking them up explicitly works): exported symbols that such code must resolve (e.g., with ccall("extern name", llvmcall, ...)) belong in Julia's JuliaGlobals JITDylib on Julia 1.14 and later (lookup_dylib(jljit.execution_session, "JuliaGlobals")), and in jljit.external_dylib before.
Properties
jljit.ir_compile_layerThe IRCompileLayer of Julia's JIT, which compiles IR modules.
jljit.external_dylibThe JITDylib that is shared by all users of Julia's JIT, whose symbols are visible to Julia code. Only available before Julia 1.14; see above for what to use on newer versions.
LLVM.ExecutionSession — Type
ExecutionSessionThe execution session of a JIT, which manages the JIT's JITDylibs and symbol string pool. It is available as the execution_session property of a JIT.
JITDylibs
LLVM.JITDylib — Type
LLVM.JITDylibA JIT dynamic library: a set of symbol definitions in an execution session, which can be looked up and linked against.
Properties
jd.default_resource_trackerThe resource tracker that tracks code added to the JITDylib without an explicit tracker. The tracker is owned by the JITDylib, so disposing of it is not required (and does nothing). Removing the tracker, or clearing the JITDylib, destroys it, so get it again afterwards.
LLVM.JITDylib — Method
JITDylib(es::ExecutionSession, name; bare=false)Adds a new JITDylib to the ExecutionSession. The name must be unique and the bare=true no standard platform symbols are made available.
LLVM.JITDylib — Method
JITDylib(jljit::JuliaOJIT, name::AbstractString)Create a new JITDylib in Julia's JIT. Its name starts with name (which can be empty), followed by a unique suffix, so every call creates a new JITDylib, even for the same name; the names of the symbols in it are not changed. Code in it can use Julia's symbols, but its own symbols are not visible to other code.
This requires Julia 1.14 or later; on older versions, Julia's JIT only supports a single JITDylib, which is shared by all users of the JIT: see the external_dylib property of JuliaOJIT.
LLVM.supports_jit_dylib_creation — Function
LLVM.supports_jit_dylib_creation(jljit::JuliaOJIT)Check whether Julia's JIT supports creating JITDylibs with JITDylib(jljit, name), which is the case since Julia 1.14. Otherwise, use the JITDylib that is shared by all users of the JIT, jljit.external_dylib.
LLVM.lookup_dylib — Function
lookup_dylib(es::ExecutionSession, name) -> Union{JITDylib,Nothing}Get the JITDylib called name in es, or nothing if there is none.
LLVM.add! — Method
add!(lljit::LLJIT, jd::JITDylib, obj::MemoryBuffer)
add!(lljit::LLJIT, jd::JITDylib, tsm::ThreadSafeModule)
add!(lljit::LLJIT, rt::ResourceTracker, obj_or_tsm)Add an object file or IR module to jd, or to the JITDylib of the resource tracker rt. The code is compiled and linked lazily, when one of its symbols is looked up. The object or module is consumed, even if adding it fails.
Base.empty! — Method
empty!(jd::JITDylib)Remove all code and data from jd, releasing the resources of all of its resource trackers.
LLVM.lookup — Method
lookup(lljit::LLJIT, [jd::JITDylib], name) -> OrcTargetAddressLook up the symbol with (unmangled) name name in jd, defaulting to the main JITDylib, materializing it if necessary. Throws an LLVMException if the symbol cannot be found or materialized. Use pointer to convert the resulting address to a pointer.
LLVM.lookup — Method
lookup(jljit::JuliaOJIT, jd::JITDylib, name, [external_jd_only=false])Look up the symbol with (unmangled) name name in jd, and the JITDylibs it links against, materializing it if necessary.
On Julia versions before 1.14, jd is ignored, and the lookup searches all of Julia's JITDylibs (or only jljit.external_dylib if external_jd_only is set).
LLVM.OrcTargetAddress — Type
OrcTargetAddressAn address in the process that executes JIT-compiled code, as returned by lookup. Use pointer to convert it to a pointer.
Resource trackers
LLVM.ResourceTracker — Type
ResourceTracker(jd::JITDylib)
ResourceTracker(f, jd::JITDylib)Create a resource tracker for jd. Code added using a tracker, with add!(jit, rt, tsm_or_object), can later be removed from the JIT using remove!, without affecting other code in jd.
Resource trackers are reference counted: the returned reference needs to be released using dispose, or by using the do-block form. Releasing a tracker does not remove the code it tracks; that code then remains in jd until jd is cleared.
See also: the default_resource_tracker property of a JITDylib.
LLVM.remove! — Method
remove!(rt::ResourceTracker)Remove all code and data tracked by rt from the JIT. The tracker becomes defunct, and cannot be used to add code anymore (but still needs to be disposed of). Removing the default tracker of a JITDylib destroys it, so it can't be used anymore; the JITDylib then creates a new default tracker when needed.
It is the caller's responsibility to ensure that the removed code is not executing, and that no pointers into it are used anymore.
LLVM.transfer! — Function
transfer!(dst::ResourceTracker, src::ResourceTracker)Transfer tracking of all resources from src to dst, which should belong to the same JITDylib.
LLVM.dispose — Method
dispose(rt::ResourceTracker)Release a reference to the resource tracker rt, after which it can't be used anymore. This does not remove the tracked code. Disposing of a tracker again, or of the default tracker of a JITDylib, does nothing.
Symbols
LLVM.LLVMSymbol — Type
LLVMSymbolAn interned symbol name: an entry in the symbol string pool of an ExecutionSession. ORC identifies symbols by their linker-mangled names, which mangle computes and interns. Symbols from the same session are equal if and only if their names are.
Symbols are reference counted. Functions that return a symbol, like mangle and intern, return a new reference, which should eventually be released with release, or be passed to an API that takes ownership of it (e.g., absolute_symbols). Use retain to create an additional reference, e.g., to pass a symbol to multiple such APIs, or when passing on a symbol that was only borrowed.
LLVM.mangle — Function
mangle(jit, name) -> LLVMSymbolApply the target's linker mangling to name (e.g., prefixing an underscore on macOS), and intern the result in the JIT's execution session. The caller owns the returned reference; see LLVMSymbol.
LLVM.intern — Function
intern(es::ExecutionSession, name) -> LLVMSymbolIntern name, as is, in the symbol string pool of es. The caller owns the returned reference; see LLVMSymbol.
LLVM.retain — Function
retain(sym::LLVMSymbol)Acquire an additional reference to the symbol sym.
LLVM.release — Function
release(sym::LLVMSymbol)Release a reference to the symbol sym.
LLVM.SymbolFlags — Type
SymbolFlags(; exported=true, callable=false, weak=false,
materialization_side_effects_only=false, target_flags=0)The flags of a JIT symbol definition, as used by absolute_symbols, CustomMaterializationUnit and lazy_reexports: whether the symbol is exported from its JITDylib, whether it is callable (a function), whether it is weak (and can be overridden by another definition), and whether it only stands for the side effects of materializing it, without an address. target_flags are specific to the target (e.g., whether an ARM function uses the Thumb instruction set), and must fit in a byte.
LLVM.define! — Function
define!(jd::JITDylib, mu)Add the materialization unit mu to jd, which takes ownership of it. The unit is consumed even if this throws: on failure (e.g., because one of its symbols is already defined in jd) it is disposed of before the error is rethrown as an LLVMException.
LLVM.absolute_symbols — Function
absolute_symbols(name => address, ...)
absolute_symbols(name => (address, flags), ...)
absolute_symbols(pairs)Create a materialization unit that defines each symbol name (a LLVMSymbol) at a fixed address (a pointer, integer, or OrcTargetAddress), e.g., to make host functions or data available to JIT-compiled code. Symbols default to being exported; pass SymbolFlags to change that (absolute symbols have an address, so they can't be materialization_side_effects_only). The pairs can also be passed as a collection, e.g., a vector or a dictionary.
The unit takes ownership of the symbol names, and should be added to a JITDylib using define!:
define!(jd, absolute_symbols(mangle(lljit, "counter") => pointer(counter)))Definition generators
LLVM.DefinitionGenerator — Type
DefinitionGeneratorA generator that ORC consults when a lookup fails to find a symbol in a JITDylib, giving it the opportunity to define that symbol.
Attach a generator to a JITDylib with add!, which transfers ownership to the JITDylib. A generator that is never added should be disposed of with dispose.
See also: DynamicLibrarySearchGenerator, CustomDefinitionGenerator.
LLVM.dispose — Method
dispose(dg::DefinitionGenerator)Dispose of a definition generator, unless it was added to a JITDylib (which owns it then).
LLVM.DynamicLibrarySearchGenerator — Function
DynamicLibrarySearchGenerator(jit)
DynamicLibrarySearchGenerator(jit, path::AbstractString)Create a DefinitionGenerator that resolves symbols by looking them up in the current process, or in the dynamic library at path. That library is loaded when creating the generator, and stays loaded for the remainder of the process.
The generator is specific to the target of jit, whose linker mangling it undoes before looking up symbols. The do-block form disposes of the generator afterwards, unless it was added to a JITDylib.
LLVM.CustomDefinitionGenerator — Type
CustomDefinitionGenerator(f)Create a definition generator that calls f(kind, jd, jd_flags, lookup_set) whenever a lookup fails to find symbols in the JITDylib the generator is attached to. The arguments mirror those of LLVM's DefinitionGenerator::tryToGenerate:
kind::LLVM.LookupKind.T: whether this is a static (linker) lookup, or adlsym-like one;jd::JITDylib: the JITDylib to define the symbols in;jd_flags::LLVM.JITDylibLookupFlags.T: whether the lookup matches only exported symbols, or all of them;lookup_set::Vector{Pair{LLVMSymbol,LLVM.SymbolLookupFlags.T}}: the linker-mangled names of the symbols that were not found, each paired with a flag indicating whether the symbol is required or only weakly referenced.
f should define the symbols it can provide in jd, e.g., using define!. Symbols it does not define are left to other generators and JITDylibs in the search order, and its return value is ignored. The names in lookup_set are only valid during the call; retain them with retain before handing them to functions that take ownership, like absolute_symbols.
If f throws, the lookup fails with an LLVM error that includes the exception message. The original exception can be retrieved by calling check_callback_error! on the generator, which rethrows it as a CallbackException.
f runs synchronously on the thread performing the lookup, while LLVM holds locks that serialize definition generation. It must not perform lookups that can reach the same JITDylib again, as that may deadlock. Asynchronous generation (suspending the lookup) is not supported.
The generator is used like a DefinitionGenerator: attach it to a JITDylib with add!, which keeps it alive for the lifetime of that JITDylib, or dispose it. Its callback stays rooted until LLVM destroys the generator.
Materialization
LLVM.MaterializationUnit — Type
MaterializationUnitA unit that promises to define a set of symbols, and that materializes their definitions when one of them is looked up, as created by absolute_symbols and lazy_reexports (see CustomMaterializationUnit for units implemented in Julia).
A unit is consumed by adding it to a JITDylib with define!; otherwise, it needs to be disposed of using dispose, which does nothing once it has been consumed.
LLVM.CustomMaterializationUnit — Type
CustomMaterializationUnit(name, symbols, materialize, discard; init=nothing)Create a materialization unit that promises to define symbols, a collection of name => flags pairs mapping each LLVMSymbol to its SymbolFlags. Add it to a JITDylib with define!.
When any of these symbols is looked up, materialize(mr) is called with a MaterializationResponsibility for the symbols, which it should fulfill, e.g., by generating IR and emitting it with emit!(layer, mr, tsm); its requested_symbols property tells which symbols were requested. If a symbol is overridden by another definition before it was materialized, discard(jd, name) is called instead.
Like other MaterializationUnits, the unit is consumed by define!, and needs to be disposed of otherwise. Its callbacks stay rooted until LLVM materializes or destroys the unit.
If materialize throws, materialization of the symbols fails, and lookups report an LLVM error. Retrieve the original exception by calling check_callback_error! on the unit. An exception in discard is only reported that way.
The unit takes ownership of the symbol names. init can be used to specify an initializer symbol (an LLVMSymbol), which needs to be one of the symbols, with flags that have materialization_side_effects_only set. The unit takes ownership of an additional reference to it.
LLVM.MaterializationResponsibility — Type
MaterializationResponsibilityThe responsibility for materializing a set of symbols, as passed to the callback of a CustomMaterializationUnit. It is fulfilled by emitting code that defines these symbols, e.g., using emit!.
Properties
mr.requested_symbolsThe names of the symbols that were requested from the materialization unit that mr is responsible for, as a read-only view. The names are only fetched while iterating the view, so use collect to get a vector.
These names are borrowed: retain them before handing them to APIs that take ownership, or using them after the responsibility has been fulfilled.
Ownership
A responsibility is consumed by emit!, after which it can't be used anymore. The responsibility that an IR transformation receives is borrowed: it can be used during the transformation, but not be consumed.
LLVM.emit! — Method
emit!(layer, mr::MaterializationResponsibility, tsm::ThreadSafeModule)Emit the IR module tsm through layer (an IRTransformLayer or IRCompileLayer) to fulfill the responsibility mr. Both mr and tsm are consumed; a responsibility that is borrowed, e.g., by an IR transformation, cannot be emitted.
LLVM.IRTransformLayer — Type
IRTransformLayerThe layer of an LLJIT that transforms IR modules before they are compiled, available as its ir_transform_layer property.
LLVM.transform! — Function
transform!(f, layer::IRTransformLayer)Install f(tsm::ThreadSafeModule, mr::MaterializationResponsibility) as the transformation that layer applies to IR modules before they are compiled, replacing any previous one. f should modify the module in place, e.g., by running an optimization pipeline on it:
transform!(lljit.ir_transform_layer) do tsm, mr
tsm() do mod
run!("default<O2>", mod)
end
endBoth arguments are borrowed: f should not dispose of them, or pass them to APIs that take ownership. The Julia LLJIT wrapper used to install the transformation roots it. Keep that exact wrapper alive until the native JIT is destroyed, especially when wrapping a foreign JIT. Install the transformation before adding code. It may be called on whichever thread materializes code.
If f throws, materialization of the module fails, and the original exception can be retrieved by calling check_callback_error! on the layer.
LLVM.IRCompileLayer — Type
IRCompileLayerThe layer of Julia's JIT that compiles IR modules, available as the ir_compile_layer property of a JuliaOJIT, for use with emit!.
LLVM.lazy_reexports — Function
lazy_reexports(lctm, ism, source_jd, aliases)Create a materialization unit that defines lazy reexports of symbols in source_jd. aliases is a collection of alias => target or alias => (target, flags) pairs of LLVMSymbols, with SymbolFlags that default to an exported and callable symbol. Lazy reexports need to be callable.
Looking up an alias does not materialize its target. Instead, the alias resolves to a stub (managed by the indirect stubs manager ism) that calls into the lazy call-through manager lctm the first time it is called, which then looks up the target and updates the stub. Both managers must stay alive for as long as the stubs can be called.
The unit takes ownership of one reference for each occurrence of a name: when multiple aliases share a target, retain the target an additional time for every extra alias.
LLVM.LocalLazyCallThroughManager — Function
LocalLazyCallThroughManager(triple, es::ExecutionSession)
LocalLazyCallThroughManager(f, triple, es::ExecutionSession)Create a manager of lazy call-throughs for the current process, as used by lazy_reexports. Needs to be disposed of using dispose, or by using the do-block form, after the last call of a stub that uses it.
LLVM.LocalIndirectStubsManager — Function
LocalIndirectStubsManager(triple)
LocalIndirectStubsManager(f, triple)Create a manager of indirect stubs for the current process, as used by lazy_reexports. Needs to be disposed of using dispose, or by using the do-block form, after the last call of a stub it manages.
Callback errors
LLVM.CallbackException — Type
CallbackExceptionException captured inside a Julia callback and rethrown after the surrounding foreign operation has returned normally. The original exception is available in the ex field.
LLVM.check_callback_error! — Function
check_callback_error!(obj)Rethrow the first exception that was captured from a Julia callback of obj (e.g., a CustomDefinitionGenerator or CustomMaterializationUnit) as a CallbackException, clearing it. Returns nothing if no exception was captured.
Exceptions cannot propagate through LLVM, so callbacks that throw are reported to LLVM as failures instead, typically resulting in a generic error from the operation that triggered the callback.