# Optimization passes

LLVM gives you the opportunity to fine-tune optimization passes. Optimization passes are managed by a pass manager. There are two kinds of pass managers:

- `FunctionPassManager`, for optimizations that work on single functions.

- `ModulePassManager`, for optimizations that work on whole modules.

llvmlite provides bindings for LLVM’s New Pass Manager. Prior to llvmlite 0.45 (LLVM 20), llvmlite also supported the Legacy Pass Manager, which had a different API and behavior. The differences between them and the motivations for the New Pass Manager are outlined in the LLVM Blog post on the New Pass Manager.

As of llvmlite 0.45 (LLVM 20), support for the Legacy Pass Manager has been removed. All code should now use the New Pass Manager APIs documented below. For users migrating from the legacy API, see the Legacy API Migration Guide later in this document.

## New Pass Manager APIs

To manage the optimization attributes we first need to instantiate a `PipelineTuningOptions` instance:

llvmlite.binding.create_pipeline_tuning_options(speed_level=2, size_level=0)

  Create a `PipelineTuningOptions` instance.

class llvmlite.binding.PipelineTuningOptions(speed_level=2, size_level=0)

  Creates a new PipelineTuningOptions object.

  The following writable attributes are available, whose default values depend on the initial setting of the speed and size optimization levels:

  - loop_interleaving

      Enable loop interleaving.

  - loop_vectorization

      Enable loop vectorization.

  - slp_vectorization

      Enable SLP vectorization, which uses a different algorithm to loop vectorization. Both may be enabled at the same time.

  - loop_unrolling

      Enable loop unrolling.

  - speed_level

      The level of optimization for speed, as an integer between 0 and 3.

  - size_level

      The level of optimization for size, as an integer between 0 and 2.

  - inlining_threshold

      The integer threshold for inlining one function into another. The higher the number, the more likely that inlining will occur. This attribute is write-only.

We also need a `PassBuilder` object to manage the respective function and module pass managers:

llvmlite.binding.create_pass_builder(tm, pto)

  Create a `PassBuilder` instance that uses the given `TargetMachine` (tm) and `PipelineTuningOptions` (pto) instances.

class llvmlite.binding.PassBuilder(target_machine, pipeline_tuning_options)

  A pass builder that uses the given `TargetMachine` and `PipelineTuningOptions` instances.

  getModulePassManager()

    Return a populated `ModulePassManager` object based on PTO settings.

  getFunctionPassManager()

    Return a populated `FunctionPassManager` object based on PTO settings.

  start_pass_timing()

    Enable the pass timers.

  finish_pass_timing()

    Returns a string containing the LLVM-generated timing report and disables the pass timers.

The `ModulePassManager` and `FunctionPassManager` classes implement the module and function pass managers:

class llvmlite.binding.ModulePassManager

  A pass manager for running optimization passes on an LLVM module.

  add_verifier()

    Add the Module Verifier pass.

  run(module, passbuilder)

    Run optimization passes on module, a `ModuleRef` instance.

class llvmlite.binding.FunctionPassManager

  A pass manager for running optimization passes on an LLVM function.

  run(function, passbuilder)

    Run optimization passes on function, a `ValueRef` instance.

These can be created with passes populated by using the `PassBuilder.getModulePassManager()` and `PassBuilder.getFunctionPassManager()` methods, or they can be instantiated unpopulated, then passes can be added using the `add_*` methods.

To instantiate the unpopulated instances, use:

llvmlite.binding.create_new_module_pass_manager()

  Create an unpopulated `ModulePassManager` instance.

and

llvmlite.binding.create_new_function_pass_manager()

  Create an unpopulated `FunctionPassManager` instance.

The `add_*` methods supported by both pass manager classes are:

add_aa_eval_pass()

  Add the Exhaustive Alias Analysis Precision Evaluator pass.

add_loop_unroll_pass()

  Add the Loop Unroll pass.

add_loop_rotate_pass()

  Add the Loop Rotate pass.

add_instruction_combine_pass()

  Add the Combine Redundant Instructions pass.

add_jump_threading_pass()

  Add the Jump Threading pass.

add_simplify_cfg_pass()

  Add the Simplify CFG pass.

add_refprune_pass()

  Add the Reference pruning pass.

### Example Usage of New API

Here’s a complete example showing how to use the New Pass Manager APIs:

examples/newpassmanagers.py

    """
    Demonstration of llvmlite's New Pass Manager API.

    This example shows how to use the new pass manager to optimize LLVM IR.
    Comments show the equivalent legacy pass manager approach for comparison.
    """

    import llvmlite.binding as llvm

    # Initialize LLVM
    llvm.initialize_native_target()
    llvm.initialize_native_asmprinter()

    # Create a module with some sample IR
    module = llvm.parse_assembly("""
    define i32 @test_function(i32 %x, i32 %y) {
    entry:
        %z = add i32 %x, %y
        %w = add i32 %z, 0    ; This can be optimized away
        %t = mul i32 %w, 1    ; This can also be optimized away
        ret i32 %t
    }

    define i32 @unused_function() {
    entry:
        ret i32 42
    }
    """)

    print("Original IR:")
    print(str(module))

    # Create target machine
    target = llvm.Target.from_default_triple()
    target_machine = target.create_target_machine()

    # NEW PASS MANAGER API:
    # Create pipeline tuning options with optimization settings
    pto = llvm.create_pipeline_tuning_options(speed_level=2, size_level=0)
    # LEGACY EQUIVALENT:
    # pmb = llvm.PassManagerBuilder()
    # pmb.opt_level = 2
    # pmb.size_level = 0

    # Optionally customize the tuning options
    pto.loop_vectorization = True
    pto.slp_vectorization = True
    pto.loop_unrolling = True
    # LEGACY EQUIVALENT:
    # pmb.loop_vectorize = True
    # pmb.slp_vectorize = True
    # pmb.disable_unroll_loops = False

    # Create the pass builder
    pass_builder = llvm.create_pass_builder(target_machine, pto)
    # LEGACY EQUIVALENT: PassManagerBuilder handles this internally

    # Get a populated module pass manager
    mpm = pass_builder.getModulePassManager()
    # LEGACY EQUIVALENT:
    # mpm = llvm.ModulePassManager()
    # pmb.populate(mpm)

    # Run the optimization passes
    mpm.run(module, pass_builder)
    # LEGACY EQUIVALENT:
    # changed = mpm.run(module)

    print("\nOptimized IR:")
    print(str(module))

    # For function-level optimizations, you can also use:
    fpm = pass_builder.getFunctionPassManager()
    # LEGACY EQUIVALENT:
    # fpm = llvm.FunctionPassManager(module)
    # pmb.populate(fpm)
    # fpm.initialize()

    for function in module.functions:
        fpm.run(function, pass_builder)
    # LEGACY EQUIVALENT:
    # for function in module.functions:
    #     fpm.run(function)
    # fpm.finalize()  # Call after all functions are processed

## Legacy API Migration Guide

As of llvmlite 0.45 (LLVM 20), the legacy pass manager API has been removed. This section provides a migration guide comparing the New Pass Manager API with the removed legacy API to help users understand the differences and migrate existing code:

Pass Manager API Comparison
Legacy Pass Manager | New Pass Manager
--- | ---

    # Setup
    pmb = PassManagerBuilder()
    pmb.opt_level = 2
    pmb.size_level = 0
    pmb.loop_vectorize = True

    # Setup
    pto = create_pipeline_tuning_options(
        speed_level=2, size_level=0)
    pto.loop_vectorization = True
    pass_builder = create_pass_builder(
        target_machine, pto)

    # Module optimization
    mpm = ModulePassManager()
    pmb.populate(mpm)
    mpm.run(module)

    # Module optimization
    mpm = pass_builder.getModulePassManager()
    mpm.run(module, pass_builder)

    # Function optimization
    fpm = FunctionPassManager(module)
    pmb.populate(fpm)
    fpm.initialize()
    fpm.run(function)
    fpm.finalize()

    # Function optimization
    fpm = pass_builder.getFunctionPassManager()
    fpm.run(function, pass_builder)

## Legacy Pass Manager APIs (Removed)

Warning

The Legacy Pass Manager API has been removed as of llvmlite 0.45 (LLVM 20). This documentation is kept for reference purposes only. New code should use the New Pass Manager API documented above.

The legacy API required creating and configuring a `PassManagerBuilder` to instantiate pass managers. This approach has been superseded by the factory functions and direct class instantiation provided by the New Pass Manager.

class llvmlite.binding.PassManagerBuilder

  Create a new pass manager builder. This object centralizes optimization settings.

  The `populate` method is available:

  populate(pm)

    Populate the pass manager pm with the optimization passes configured in this pass manager builder.

  The following writable attributes are available:

  - disable_unroll_loops

      If `True`, disable loop unrolling.

  - inlining_threshold

      The integer threshold for inlining one function into another. The higher the number, the more likely that inlining will occur. This attribute is write-only.

  - loop_vectorize

      If `True`, allow vectorizing loops.

  - opt_level

      The general optimization level, as an integer between 0 and 3.

  - size_level

      Whether and how much to optimize for size, as an integer between 0 and 2.

  - slp_vectorize

      If `True`, enable the SLP vectorizer, which uses a different algorithm than the loop vectorizer. Both may be enabled at the same time.

class llvmlite.binding.PassManager

  The base class for pass managers. Use individual `add_*` methods or `PassManagerBuilder.populate()` to add optimization passes.

  - add_constant_merge_pass()

      See constmerge pass documentation.

  - add_dead_arg_elimination_pass()

      See deadargelim pass documentation.

  - add_function_attrs_pass()

      See functionattrs pass documentation.

  - add_function_inlining_pass(self)

      See inline pass documentation.

  - add_global_dce_pass()

      See globaldce pass documentation.

  - add_global_optimizer_pass()

      See globalopt pass documentation.

  - add_ipsccp_pass()

      See ipsccp pass documentation.

  - add_dead_code_elimination_pass()

      See dce pass documentation.

  - add_cfg_simplification_pass()

      See simplifycfg pass documentation.

  - add_gvn_pass()

      See gvn pass documentation.

  - add_instruction_combining_pass()

      See instcombine pass documentation.

  - add_licm_pass()

      See licm pass documentation.

  - add_sccp_pass()

      See sccp pass documentation.

  - add_sroa_pass()

      See scalarrepl pass documentation.

      While the scalarrepl pass documentation describes the transformation performed by the pass added by this function, the pass corresponds to the `opt -sroa` command-line option and not to `opt -scalarrepl`.

  - add_type_based_alias_analysis_pass()

      See tbaa metadata documentation.

  - add_basic_alias_analysis_pass()

      See basicaa pass documentation.

  - add_instruction_namer_pass()

      See instnamer pass documentation.

  - add_refprune_pass()

    Add the Reference pruning pass.

class llvmlite.binding.ModulePassManager

  Create a new pass manager to run optimization passes on a module.

  The `run` method is available:

  run(module)

    Run optimization passes on the module, a `ModuleRef` instance.

    Returns `True` if the optimizations made any modification to the module. Otherwise returns `False`.

class llvmlite.binding.FunctionPassManager(module)

  Create a new pass manager to run optimization passes on a function of the given module, a `ModuleRef` instance.

  The following methods are available:

  - finalize()

      Run all the finalizers of the optimization passes.

  - initialize()

      Run all the initializers of the optimization passes.

  - run(function)

      Run optimization passes on function, a `ValueRef` instance.

      Returns `True` if the optimizations made any modification to the module. Otherwise returns `False`.