Transforms

Pass builders

LLVM.PassBuilder — Type
PassBuilder(; verify_each=false, debug_logging=false, pipeline_tuning_kwargs...)
PassBuilder(f; kwargs...)

Create a new pass builder. The pass builder is the main object used to construct and run pass pipelines. The verify_each keyword argument enables module verification after each pass, while debug_logging can be used to enable more output. Pass builder objects need to be disposed of after use, e.g., using @dispose or the do-block form.

Several other keyword arguments can override LLVM's version- and configuration-dependent pipeline defaults. This only has an effect when using one of LLVM's default pipelines, like default<O3>:

  • loop_interleaving::Bool: Enable loop interleaving.
  • loop_vectorization::Bool: Enable loop vectorization.
  • slp_vectorization::Bool: Enable SLP vectorization.
  • loop_unrolling::Bool: Enable loop unrolling.
  • forget_all_scev_in_loop_unroll::Bool: Forget all SCEV information in loop unrolling.
  • licm_mssa_opt_cap::Int: LICM MSSA optimization cap.
  • licm_mssa_no_acc_for_promotion_cap::Int: LICM MSSA no access for promotion cap.
  • call_graph_profile::Bool: Enable call graph profiling.
  • merge_functions::Bool: Enable function merging.

After a pass builder is constructed, custom passes can be registered with register!, passes or nested pass managers can be added with add!, and finally the passes can be run with run!:

@dispose pb = PassBuilder(verify_each=true) begin
    register!(pb, SomeCustomPass())
    add!(pb, SomeModulePass())
    add!(pb, FunctionPassManager()) do fpm
        add!(fpm, SomeFunctionPass())
    end
    run!(pb, mod, tm)
end

For quickly running a simple pass or pipeline, a shorthand run! method is provided that obviates the construction of a PassBuilder:

run!("some-pass", mod, tm; verify_each=true)

See also: register!, add!, run!

source
LLVM.run! — Function
run!(pb::PassBuilder, mod::Module, [tm::TargetMachine])
run!(pipeline::AbstractString, mod::Module, [tm::TargetMachine])

Run passes on a module. The passes are specified by a pass builder or a string that represents a pass pipeline. The target machine is used to optimize the passes.

source
LLVM.PassException — Type
PassException

The exception thrown by run! when a custom pass (or a custom target transform info) threw an exception while LLVM ran it. The original exception is available as its ex field, and the backtrace of where it was thrown as processed_bt.

source

Pass managers

LLVM.PassManager — Type
ModulePassManager()
CGSCCPassManager()
FunctionPassManager()
LoopPassManager(; use_memory_ssa=false)
AAManager()

Create a new pass manager of the specified type. These objects can be used to construct pass pipelines, by add!ing passes to them, and finally add!ing them to a parent pass manager or pass builder.

Creating a pass manager and adding it to a parent manager or builder can be shortened using a single add!:

add!(parent, ModulePassManager()) do mpm
    add!(mpm, SomeModulePass())
end

See also: add!, PassBuilder

source
LLVM.add! — Method
add!(pm::AbstractPassManager, pass)

Adds a pass or pipeline to a pass builder or pass manager, and returns the pass builder or pass manager.

The pass or pipeline should be a string or string-convertible object known by LLVM. These can be constructed by using pass constructors, e.g., InternalizePass(), or by manually specifying names like default<O3>.

When using custom passes, remember that they need to be registered with the pass builder before they can be used.

See also: register!

source

Passes and pipelines

The functions that return the names of LLVM's passes are listed on the Passes page.

LLVM.DefaultPipeline — Function
DefaultPipeline(; opt_level=0, options...) -> String

LLVM's default optimization pipeline for the optimization level opt_level (0 to 3, or "s" and "z" to optimize for size), as a string for use with add! or run!, e.g., "default<O3>". Other keyword arguments become options of the pipeline, but LLVM's default pipeline takes few: it is tuned using the keyword arguments of PassBuilder instead.

source

Custom passes

LLVM.CustomPass — Type
ModulePass(name, callback; required=false)
FunctionPass(name, callback; required=false, analyses=false)

Create a new custom pass. The name is a string that will be used to identify the pass in the pass manager. The callback is a function that will be called when the pass is run. The function should take a single argument, the module or function to be processed, and return a boolean indicating whether the pass made any changes. Set required=true for a pass needed for correctness; LLVM then does not skip it on optnone functions or under -opt-bisect-limit.

With analyses=true, a function pass can use LLVM's analyses: the callback is then called with two arguments, the function and its FunctionAnalysisManager, and may also return a PreservedAnalyses value describing the analyses that remain valid after the pass, instead of a boolean:

function my_pass!(f::LLVM.Function, am::LLVM.FunctionAnalysisManager)
    domtree = am[DomTree]
    changed = ...
    # this pass does not modify the CFG
    return changed ? PreservedAnalyses(CFGAnalyses) : PreservedAnalyses(AllAnalyses)
end
FunctionPass("my-pass", my_pass!; analyses=true)

Before using a custom pass, it must be registered with a pass builder using register!. LLVM.jl catches exceptions from these callbacks and rethrows them as PassException after control has returned from LLVM. Callbacks registered directly through the LLVM.API.LLVMPassBuilderExtensionsRegister*Pass APIs must provide an equivalent exception barrier: Julia exceptions must not escape a callback, because they bypass C++ destructors in LLVM's pass runner.

See also: register!

source
LLVM.run! — Method
run!(pass::CustomPass, target::Union{Module,Function}, [tm::TargetMachine]; kwargs...)

Run a single custom pass on a module or a function, using a temporary pass builder that is created with the given keyword arguments (see PassBuilder). A function pass that runs on a module runs on each of its functions, while a module pass cannot run on a function. Like for passes that run in a pipeline, exceptions thrown by the pass are rethrown as a PassException.

source
LLVM.register_callbacks! — Function
register_callbacks!(pb::PassBuilder, callback::Ptr{Cvoid})

Register a native callback that is called with LLVM's C++ PassBuilder (as a void *) when the pass builder is used to run passes. This makes it possible to use passes that are implemented in C++, by calling the PassBuilder's register*Callback methods, like pass plugins do. For example, for a library that provides a registerCallbacks function:

register_callbacks!(pb, cglobal((:registerCallbacks, libfoo)))

The callback needs to have the signature void (*)(void *), and the library that provides it needs to be built against the same version of LLVM as the one that Julia uses, and remain loaded while the pass builder is used. Callbacks are called in the order they were registered, after LLVM.jl registers Julia's passes, and must not throw Julia exceptions. To implement a pass in Julia instead, use register!.

source

Analyses in custom passes

LLVM.FunctionAnalysisManager — Type
FunctionAnalysisManager

The analysis manager of a pass pipeline, for the function that a custom pass (created using FunctionPass(...; analyses=true)) runs on. It provides the results of LLVM's function analyses, which it owns and caches:

  • am[T]: get the result of the analysis T (e.g., a DomTree), computing it if needed;
  • get(am, T, nothing): get the result of T only if it has been computed already;
  • invalidate!(am, preserved): invalidate the results that are not preserved.

The supported analyses are DomTree, PostDomTree, AssumptionCache, LazyValueInfo, ScalarEvolution and LoopInfo.

Analysis results borrowed from the manager must not be disposed of, and must not be used after the pass returns. They also become stale when the pass changes the IR in a way that affects them, as LLVM does not update them automatically: a pass that changes the IR and then queries an analysis again needs to update that analysis itself, or invalidate it first. What the pass returns only determines which analyses remain valid after the pass.

See also: PreservedAnalyses

source
LLVM.PreservedAnalyses — Type
PreservedAnalyses(analyses...)

The set of analyses that remain valid after a custom pass, returned by a pass created using FunctionPass(...; analyses=true). Each argument is either an analysis (e.g. DomTree), or a marker for a set of analyses (AllAnalyses or CFGAnalyses):

  • PreservedAnalyses(): no analysis is preserved (like returning true);
  • PreservedAnalyses(AllAnalyses): every analysis is preserved (like returning false);
  • PreservedAnalyses(CFGAnalyses): the pass did not change the control-flow graph;
  • PreservedAnalyses(DomTree): the pass kept the dominator tree up to date.

See also: FunctionAnalysisManager, invalidate!

source
LLVM.AllAnalyses — Type
AllAnalyses
CFGAnalyses

Markers for sets of analyses, used with PreservedAnalyses: AllAnalyses for every analysis, and CFGAnalyses for the analyses that only depend on the control-flow graph of a function (like DomTree and PostDomTree), i.e., on its blocks and their terminators.

source
LLVM.invalidate! — Function
invalidate!(am::FunctionAnalysisManager, preserved=PreservedAnalyses())

Invalidate the analysis results of the function that are not in the set of preserved analyses (by default, all of them), so that they are recomputed when they are queried next. Any result that was obtained before must not be used anymore. This is useful for a pass that changes the IR and then queries analyses that depend on it again.

Some analyses, like the assumption cache, are designed to survive invalidation, and need to be updated by the pass instead.

source

Custom target info

LLVM.AbstractTargetTransformInfo — Type
abstract type AbstractTargetTransformInfo

Subtype this to supply a custom TargetTransformInfo to pass pipelines that would otherwise see only LLVM's conservative baseline.

Most LLVM back-ends (host CPU, NVPTX, AMDGPU) carry their own TTI through a TargetMachine; callers that already have one and pass it to run! don't need this. The feature is aimed at out-of-tree back-ends not linked into libLLVM (e.g. Metal) and at tooling that runs passes without a TargetMachine at all. In those pipelines, the baseline TTI reports no flat address space, no branch divergence, etc., and TTI-sensitive passes such as InferAddressSpacesPass or UniformityAnalysis silently no-op.

Subtypes override only the queries they care about; any query left un-overridden is handled by LLVM's TargetTransformInfoImplBase. For a query receiving Value, provide a method accepting Value whenever adding specialized methods: an installed callback can receive any value kind.

Attach an instance with target_transform_info!; attaching nothing reverts to LLVM's native TTI. When a TargetMachine is also supplied to run!, a custom TTI takes precedence.

Exceptions from overridden queries are captured, LLVM receives a conservative answer, and the exception is rethrown as a PassException after the pass pipeline returns.

The queries receive address spaces and intrinsic IDs as UInts, and return address spaces as integers, or nothing for none.

Overridable queries:

source
LLVM.target_transform_info! — Function
target_transform_info!(pb::PassBuilder, tti::AbstractTargetTransformInfo)
target_transform_info!(pb::PassBuilder, ::Nothing)

Attach an AbstractTargetTransformInfo subtype instance to the pass builder, replacing any previously-attached custom TTI. Pass nothing to revert to LLVM's native TTI (derived from the TargetMachine, if any; otherwise TargetTransformInfoImplBase with full DataLayout/Module-aware defaults).

source
LLVM.flat_address_space — Function
flat_address_space(tti::AbstractTargetTransformInfo) -> Union{Integer,Nothing}

Address space the target treats as "flat" / generic, or nothing if there is none. Required — alongside is_noop_addr_space_cast — for InferAddressSpacesPass to fold addrspacecasts. If not defined, LLVM reports no flat AS.

source
LLVM.has_branch_divergence — Function
has_branch_divergence(tti::AbstractTargetTransformInfo) -> Bool

Whether the target can produce divergent control flow. Enables divergence-aware passes (SimplifyCFG, loop analyses) when true. If not defined, falls back to LLVM's baseline.

source
LLVM.is_single_threaded — Function
is_single_threaded(tti::AbstractTargetTransformInfo) -> Bool

Whether the target is single-threaded. A few passes skip concurrency-related transformations when true. If not defined, falls back to LLVM's baseline (which consults the module's "single-thread" flag).

source
LLVM.is_noop_addr_space_cast — Function
is_noop_addr_space_cast(tti::AbstractTargetTransformInfo,
                        from::Unsigned, to::Unsigned) -> Bool

Whether an addrspacecast from from to to is a noop at runtime. If not defined, falls back to LLVM's baseline.

source
LLVM.is_valid_addr_space_cast — Function
is_valid_addr_space_cast(tti::AbstractTargetTransformInfo,
                         from::Unsigned, to::Unsigned) -> Bool

Whether an addrspacecast from from to to is permitted. If not defined, falls back to LLVM's baseline.

source
LLVM.addrspaces_may_alias — Function
addrspaces_may_alias(tti::AbstractTargetTransformInfo,
                     as0::Unsigned, as1::Unsigned) -> Bool

Whether pointers in address spaces as0 and as1 may alias. If not defined, falls back to LLVM's (conservative) baseline.

source
LLVM.can_have_non_undef_global_initializer_in_address_space — Function
can_have_non_undef_global_initializer_in_address_space(
    tti::AbstractTargetTransformInfo, as::Unsigned) -> Bool

Whether globals in address space as may have non-undef initializers. If not defined, falls back to LLVM's baseline (which consults DataLayout::isNonIntegralAddressSpace).

source
LLVM.is_source_of_divergence — Function
is_source_of_divergence(tti::AbstractTargetTransformInfo, v::Value) -> Bool

Whether v is a source of divergence. Consulted by UniformityAnalysis. If not defined, falls back to LLVM's baseline.

source
LLVM.is_always_uniform — Function
is_always_uniform(tti::AbstractTargetTransformInfo, v::Value) -> Bool

Whether v is known to hold the same value across all threads. Consulted by UniformityAnalysis. If not defined, falls back to LLVM's baseline.

source
LLVM.get_assumed_addr_space — Function
get_assumed_addr_space(tti::AbstractTargetTransformInfo, v::Value)
    -> Union{Integer,Nothing}

Address space statically known to hold for v, or nothing for "no assumption". If not defined, falls back to LLVM's baseline.

source
LLVM.get_predicated_addr_space — Function
get_predicated_addr_space(tti::AbstractTargetTransformInfo, cond::Value)
    -> Union{Tuple{Value,Integer},Nothing}

Given the condition cond of an llvm.assume, return a (ptr, as) tuple if the condition implies that the pointer ptr is in address space as (e.g., when cond is a call to an intrinsic that checks the address space of ptr), or nothing otherwise. If not defined, falls back to LLVM's baseline.

source
LLVM.rewrite_intrinsic_with_address_space — Function
rewrite_intrinsic_with_address_space(tti::AbstractTargetTransformInfo,
    ii::Value, old::Value, new::Value) -> Union{Value,Nothing}

Rewrite an intrinsic call ii after its pointer operand old has been replaced by new (in a different address space). Return the rewritten value, or nothing to keep the existing call. If not defined, falls back to LLVM's baseline.

source
LLVM.collect_flat_address_operands — Function
collect_flat_address_operands(tti::AbstractTargetTransformInfo, iid::Unsigned)
    -> AbstractVector{<:Integer}

Positions of the arguments of calls to intrinsic iid that are flat-address-space pointers, numbered from 1 like call.arguments. The underlying C API supports at most 32 entries; returning more throws an error. If not defined, falls back to LLVM's baseline.

source

IR cloning

LLVM.clone_into! — Function
clone_into!(new::LLVM.Function, old::LLVM.Function; [suffix::AbstractString],
            [value_map::AbstractDict{<:Value,<:Value}],
            [changes::LLVM.LLVMCloneFunctionChangeType],
            [type_mapper::Function],
            [materializer::Function])

Clone the contents of a function old into a new function new. The value_map dictionary can be used to remap values from the old function to the new function, while suffix appends a suffix to all values cloned. The type_mapper and materializer functions can be used to respectively map types and materialize values on demand.

Exceptions thrown by either callback are captured and rethrown as a CallbackException after LLVM returns to Julia. The destination function may already be partially modified when this happens and should be discarded.

The changes argument determines how this function behaves; refer to the LLVM documentation of CloneFunctionInto for more details.

source
LLVM.clone — Function
clone(f::Function; [value_map::AbstractDict{Value,Value}])

Simpler version of clone_into! that clones a function f into a new function, optionally mapping values according to the value_map dictionary.

source
clone(bb::BasicBlock]; dest=parent(bb), [suffix::AbstractString],
      [value_map::AbstractDict{Value,Value}])

Clone a basic block bb by copying all instructions. The new block is inserted at the end of the parent function; this can be altered by setting dest to a different function, or to nothing to create a detached block. The suffix is appended to the name of the cloned basic block.

Warn

This function only remaps values that are defined in the cloned basic block. Values defined outside the basic block (e.g. function arguments) are not remapped by default. This means that the cloned basic block can generally only be used within the same function that it was cloned from, unless you manually remap other values. This can be done passing a value_map dictionary.

source