Skip to content

Commit

Permalink
Merge pull request numba#1046 from yashssh/npm-numba
Browse files Browse the repository at this point in the history
Add basic infra required to move Numba to NewPassManager
  • Loading branch information
sklam authored Jul 8, 2024
2 parents cb8baa4 + 69c867c commit 9721cbf
Show file tree
Hide file tree
Showing 14 changed files with 1,191 additions and 33 deletions.
214 changes: 189 additions & 25 deletions docs/source/user-guide/binding/optimization-passes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,176 @@ Optimization passes

.. currentmodule:: llvmlite.binding

LLVM gives you the opportunity to fine-tune optimization passes.
Optimization passes are managed by a pass manager. There are 2
kinds of pass managers:
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:

* :class:`FunctionPassManager`, for optimizations that work on
single functions.

* :class:`ModulePassManager`, for optimizations that work on
whole modules.

llvmlite provides bindings for LLVM's *New* and *Legacy* pass managers, which
have slightly different APIs and behaviour. The differences between them and the
motivations for the New Pass Manager are outlined in the `LLVM Blog post on the
New Pass Manager
<https://blog.llvm.org/posts/2021-03-26-the-new-pass-manager/>`_.

In a future version of llvmlite, likely coinciding with a minimum LLVM version
requirement of 17, support for the Legacy Pass Manager will be removed. It is
recommended that new code using llvmlite uses the New Pass Manager, and existing
code using the Legacy Pass Manager be updated to use the New Pass Manager.


New Pass Manager APIs
=====================

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

.. class:: 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:

* .. attribute:: loop_interleaving

Enable loop interleaving.

* .. attribute:: loop_vectorization

Enable loop vectorization.

* .. attribute:: slp_vectorization

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

* .. attribute:: loop_unrolling

Enable loop unrolling.

* .. attribute:: speed_level

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

* .. attribute:: size_level

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

.. FIXME: Available from llvm16
.. * .. attribute:: 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 :class:`PassBuilder` object to manage the respective function
and module pass managers:

.. class:: PassBuilder(target_machine, pipeline_tuning_options)

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

.. method:: getModulePassManager()

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

.. method:: getFunctionPassManager()

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


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

.. class:: ModulePassManager()

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

.. method:: add_verifier()

Add the `Module Verifier
<https://llvm.org/docs/Passes.html#verify-module-verifier>`_ pass.

.. method:: run(module, passbuilder)

Run optimization passes on *module*, a :class:`ModuleRef` instance.


.. class:: FunctionPassManager()

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

.. method:: run(function, passbuilder)

Run optimization passes on *function*, a :class:`ValueRef` instance.


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

To instantiate the unpopulated instances, use:

.. function:: create_new_module_pass_manager()

Create an unpopulated :class:`ModulePassManager` instance.

and

.. function:: create_new_function_pass_manager()

Create an unpopulated :class:`FunctionPassManager` instance.


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

.. currentmodule:: None

.. method:: add_aa_eval_pass()

Add the `Exhaustive Alias Analysis Precision Evaluator
<https://llvm.org/docs/Passes.html#aa-eval-exhaustive-alias-analysis-precision-evaluator>`_
pass.

.. method:: add_loop_unroll_pass()

Add the `Loop Unroll
<https://llvm.org/docs/Passes.html#loop-unroll-unroll-loops>`_ pass.

.. method:: add_loop_rotate_pass()

Add the `Loop Rotate
<https://llvm.org/docs/Passes.html#loop-rotate-rotate-loops>`_ pass.

.. method:: add_instruction_combine_pass()

Add the `Combine Redundant Instructions
<https://llvm.org/docs/Passes.html#instcombine-combine-redundant-instructions>`_
pass.

.. method:: add_jump_threading_pass()

Add the `Jump Threading
<https://llvm.org/docs/Passes.html#jump-threading-jump-threading>`_ pass.

.. method:: add_simplify_cfg_pass()

Add the `Simplify CFG
<https://llvm.org/docs/Passes.html#simplifycfg-simplify-the-cfg>`_ pass.

.. currentmodule:: llvmlite.binding

Legacy Pass Manager APIs
========================

To instantiate either of these pass managers, you first need to
create and configure a :class:`PassManagerBuilder`.

Expand All @@ -24,42 +184,42 @@ create and configure a :class:`PassManagerBuilder`.

The ``populate`` method is available:

.. method:: populate(pm)
.. method:: populate(pm)

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

The following writable attributes are available:
The following writable attributes are available:

* .. attribute:: disable_unroll_loops
* .. attribute:: disable_unroll_loops

If ``True``, disable loop unrolling.
If ``True``, disable loop unrolling.

* .. attribute:: inlining_threshold
* .. attribute:: 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.
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.

* .. attribute:: loop_vectorize
* .. attribute:: loop_vectorize

If ``True``, allow vectorizing loops.
If ``True``, allow vectorizing loops.

* .. attribute:: opt_level
* .. attribute:: opt_level

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

* .. attribute:: size_level
* .. attribute:: size_level

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

* .. attribute:: slp_vectorize
* .. attribute:: 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.
If ``True``, enable the SLP vectorizer, which uses a
different algorithm than the loop vectorizer. Both may
be enabled at the same time.


.. class:: PassManager
Expand Down Expand Up @@ -142,13 +302,15 @@ create and configure a :class:`PassManagerBuilder`.
See `instnamer pass documentation <http://llvm.org/docs/Passes.html#instnamer-assign-names-to-anonymous-instructions>`_.

.. class:: ModulePassManager()
:no-index:

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

The ``run`` method is available:

.. method:: run(module)
:no-index:

Run optimization passes on the
*module*, a :class:`ModuleRef` instance.
Expand All @@ -157,6 +319,7 @@ create and configure a :class:`PassManagerBuilder`.
to the module. Otherwise returns ``False``.

.. class:: FunctionPassManager(module)
:no-index:

Create a new pass manager to run optimization passes on a
function of the given *module*, a :class:`ModuleRef` instance.
Expand All @@ -172,6 +335,7 @@ create and configure a :class:`PassManagerBuilder`.
Run all the initializers of the optimization passes.

* .. method:: run(function)
:no-index:

Run optimization passes on *function*, a
:class:`ValueRef` instance.
Expand Down
73 changes: 73 additions & 0 deletions examples/npm_passes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""
This example demonstrates how to use the new module pass manager to optimize a
module using the loop unrolling and CFG simplification passes.
"""

import faulthandler
import llvmlite.binding as llvm

# Dump Python traceback in the event of a segfault
faulthandler.enable()

# All are required to initialize LLVM
llvm.initialize()
llvm.initialize_native_target()
llvm.initialize_native_asmprinter()

# Module to optimize
strmod = """
define i32 @foo3(i32* noalias nocapture readonly %src) {
entry:
br label %loop.header
loop.header:
%iv = phi i64 [ 0, %entry ], [ %inc, %loop.latch ]
%r1 = phi i32 [ 0, %entry ], [ %r3, %loop.latch ]
%arrayidx = getelementptr inbounds i32, i32* %src, i64 %iv
%src_element = load i32, i32* %arrayidx, align 4
%cmp = icmp eq i32 0, %src_element
br i1 %cmp, label %loop.if, label %loop.latch
loop.if:
%r2 = add i32 %r1, 1
br label %loop.latch
loop.latch:
%r3 = phi i32 [%r1, %loop.header], [%r2, %loop.if]
%inc = add nuw nsw i64 %iv, 1
%exitcond = icmp eq i64 %inc, 9
br i1 %exitcond, label %loop.end, label %loop.header
loop.end:
%r.lcssa = phi i32 [ %r3, %loop.latch ]
ret i32 %r.lcssa
}
"""


module = llvm.parse_assembly(strmod)

print("Module before optimization:\n")
print(module)

# Set up the module pass manager used to run our optimization pipeline.
# We create it unpopulated, and then add the loop unroll and simplify CFG
# passes.
pm = llvm.create_new_module_pass_manager()
pm.add_loop_unroll_pass()
pm.add_simplify_cfg_pass()


# To run the pass manager, we need a pass builder object - we create pipeline
# tuning options with no optimization, then use that to create a pass builder.
target_machine = llvm.Target.from_default_triple().create_target_machine()
pto = llvm.create_pipeline_tuning_options(speed_level=0)
pb = llvm.create_pass_builder(target_machine, pto)

# Now we can run the pass manager on our module
pm.run(module, pb)


# We should observer a fully unrolled loop, and the function now consists of a
# single basic block executing all the iterations of the loop in a straight
# line.
print("\nModule after optimization:\n")
print(module)
Loading

0 comments on commit 9721cbf

Please sign in to comment.