Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic infra required to move Numba to NewPassManager #1046

Merged
merged 28 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
55ecc96
Add basic infra required to move Numba to NewPassManager
yashssh Apr 25, 2024
de1832a
formatting
yashssh May 3, 2024
4ea3d86
Use DEFINE_SIMPLE_CONVERSION_FUNCTIONS macro
yashssh May 8, 2024
445ebb4
Map speed level, size level to appropriate OptimizationLevel
yashssh May 9, 2024
5e9c0b9
Prefix old pass manager code with "legacy" prefix
yashssh May 13, 2024
d07559b
Revert "Prefix old pass manager code with "legacy" prefix"
yashssh May 14, 2024
5c90df3
fix makefiles
yashssh May 14, 2024
dfc2f34
Fix some function names
yashssh May 14, 2024
2477218
More formatting
yashssh May 16, 2024
63a4237
Add new pass manager examples, split npm-usage.py
yashssh May 16, 2024
592c7b9
Re-add -flto
yashssh May 16, 2024
20aeaa4
New pass manager documentation
yashssh May 16, 2024
e2da92f
fix example
yashssh May 16, 2024
ebb5c1e
Tidy up new pass manager bindings
gmarkall May 17, 2024
0833c1d
Address review comments
yashssh May 20, 2024
9fa47bd
Make opt_level and size_level public attributes of PTO set in __init__()
yashssh May 20, 2024
687a2b8
Remove 'New' from class names, remove ptr initialization of classes w…
yashssh May 21, 2024
af7a79c
Add opt_level asserts
yashssh May 23, 2024
e33bdca
Add pto tests
yashssh May 28, 2024
1babc4e
Update NPM docs
gmarkall May 23, 2024
1b36ab1
Add notes on how to correctly create new pass manager instances
gmarkall May 30, 2024
a90a3ac
Tidy up the examples
gmarkall May 30, 2024
a5b5467
Rename opt_level to speed_level and typos
yashssh May 31, 2024
b49dabd
fix tests
yashssh May 31, 2024
707a038
Typo
yashssh May 31, 2024
76171d0
Edit error message
yashssh May 31, 2024
4194dbb
Fix typo in file name
yashssh May 31, 2024
69c867c
Merge remote-tracking branch 'numba/main' into npm-numba
gmarkall Jun 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
1 change: 0 additions & 1 deletion docs/source/user-guide/examples/sum.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,3 @@
res = cfunc(A.ctypes.data_as(POINTER(c_int)), A.size)

print(res, A.sum())

55 changes: 55 additions & 0 deletions examples/optimization-passes-npm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
try:
import faulthandler; faulthandler.enable()
except ImportError:
pass

import llvmlite.ir as ll
import llvmlite.binding as llvm


llvm.initialize()
llvm.initialize_native_target()
llvm.initialize_native_asmprinter()

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
}
"""

# Run loop-unroll + simplifycfg on module

llmod = llvm.parse_assembly(strmod)
print(llmod)

target_machine = llvm.Target.from_default_triple().create_target_machine()
pto = llvm.create_pipeline_tuning_options(opt_level=0)
pb = llvm.create_pass_builder(target_machine, pto)

pm = llvm.create_new_module_pass_manager()
pm.add_loop_unroll_pass()
pm.add_simplify_cfg_pass()
pm.run(llmod, pb)

print(llmod)
Loading
Loading