239 lines
7.1 KiB
ReStructuredText
239 lines
7.1 KiB
ReStructuredText
====================
|
|
Writing an LLVM Pass
|
|
====================
|
|
|
|
.. program:: opt
|
|
|
|
.. contents::
|
|
:local:
|
|
|
|
Introduction --- What is a pass?
|
|
================================
|
|
|
|
The LLVM pass framework is an important part of the LLVM system, because LLVM
|
|
passes are where most of the interesting parts of the compiler exist. Passes
|
|
perform the transformations and optimizations that make up the compiler, they
|
|
build the analysis results that are used by these transformations, and they
|
|
are, above all, a structuring technique for compiler code.
|
|
|
|
Unlike passes under the legacy pass manager where the pass interface is
|
|
defined via inheritance, passes under the new pass manager rely on
|
|
concept-based polymorphism, meaning there is no explicit interface (see
|
|
comments in ``PassManager.h`` for more details). All LLVM passes inherit from
|
|
the CRTP mix-in ``PassInfoMixin<PassT>``. The pass should have a ``run()``
|
|
method which returns a ``PreservedAnalyses`` and takes in some unit of IR
|
|
along with an analysis manager. For example, a function pass would have a
|
|
``PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);`` method.
|
|
|
|
We start by showing you how to construct a pass, from setting up the build,
|
|
creating the pass, to executing and testing it. Looking at existing passes is
|
|
always a great way to learn details.
|
|
|
|
Quick Start --- Writing hello world
|
|
===================================
|
|
|
|
Here we describe how to write the "hello world" of passes. The "HelloWorld"
|
|
pass is designed to simply print out the name of non-external functions that
|
|
exist in the program being compiled. It does not modify the program at all,
|
|
it just inspects it.
|
|
|
|
The code below already exists; feel free to create a pass with a different
|
|
name alongside the HelloWorld source files.
|
|
|
|
.. _writing-an-llvm-npm-pass-build:
|
|
|
|
Setting up the build
|
|
--------------------
|
|
|
|
First, configure and build LLVM as described in :doc:`GettingStarted`.
|
|
|
|
Next, we will reuse an existing directory (creating a new directory involves
|
|
modifying more ``CMakeLists.txt``s than we want). For
|
|
this example, we'll use ``llvm/lib/Transforms/HelloNew/HelloWorld.cpp``,
|
|
which has already been created. If you'd like to create your own pass, add a
|
|
new source file into ``llvm/lib/Transforms/HelloNew/CMakeLists.txt`` under
|
|
``HelloWorld.cpp``:
|
|
|
|
.. code-block:: cmake
|
|
|
|
add_llvm_component_library(LLVMHelloWorld
|
|
HelloWorld.cpp
|
|
|
|
DEPENDS
|
|
intrinsics_gen
|
|
)
|
|
|
|
Now that we have the build set up for a new pass, we need to write the code
|
|
for the pass itself.
|
|
|
|
.. _writing-an-llvm-npm-pass-basiccode:
|
|
|
|
Basic code required
|
|
-------------------
|
|
|
|
Now that the build is setup for a new pass, we just have to write it.
|
|
|
|
First we need to define the pass in a header file. We'll create
|
|
``llvm/include/llvm/Transforms/HelloNew/HelloWorld.h``. The file should
|
|
contain the following boilerplate:
|
|
|
|
.. code-block:: c++
|
|
|
|
#ifndef LLVM_TRANSFORMS_HELLONEW_HELLOWORLD_H
|
|
#define LLVM_TRANSFORMS_HELLONEW_HELLOWORLD_H
|
|
|
|
#include "llvm/IR/PassManager.h"
|
|
|
|
namespace llvm {
|
|
|
|
class HelloWorldPass : public PassInfoMixin<HelloWorldPass> {
|
|
public:
|
|
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
|
|
};
|
|
|
|
} // namespace llvm
|
|
|
|
#endif // LLVM_TRANSFORMS_HELLONEW_HELLOWORLD_H
|
|
|
|
This creates the class for the pass with a declaration of the ``run()``
|
|
method which actually runs the pass. Inheriting from ``PassInfoMixin<PassT>``
|
|
sets up some more boilerplate so that we don't have to write it ourselves.
|
|
|
|
Our class is in the ``llvm`` namespace so that we don't pollute the global
|
|
namespace.
|
|
|
|
Next we'll create ``llvm/lib/Transforms/HelloNew/HelloWorld.cpp``, starting
|
|
with
|
|
|
|
.. code-block:: c++
|
|
|
|
#include "llvm/Transforms/HelloNew/HelloWorld.h"
|
|
|
|
... to include the header file we just created.
|
|
|
|
.. code-block:: c++
|
|
|
|
using namespace llvm;
|
|
|
|
... is required because the functions from the include files live in the llvm
|
|
namespace. This should only be done in non-header files.
|
|
|
|
Next we have the pass's ``run()`` definition:
|
|
|
|
.. code-block:: c++
|
|
|
|
PreservedAnalyses HelloWorldPass::run(Function &F,
|
|
FunctionAnalysisManager &AM) {
|
|
errs() << F.getName() << "\n";
|
|
return PreservedAnalyses::all();
|
|
}
|
|
|
|
... which simply prints out the name of the function to stderr. The pass
|
|
manager will ensure that the pass will be run on every function in a module.
|
|
The ``PreservedAnalyses`` return value says that all analyses (e.g. dominator
|
|
tree) are still valid after this pass since we didn't modify any functions.
|
|
|
|
That's it for the pass itself. Now in order to "register" the pass, we need
|
|
to add it to a couple places. Add the following to
|
|
``llvm\lib\Passes\PassRegistry.def`` in the ``FUNCTION_PASS`` section
|
|
|
|
.. code-block:: c++
|
|
|
|
FUNCTION_PASS("helloworld", HelloWorldPass())
|
|
|
|
... which adds the pass under the name "helloworld".
|
|
|
|
``llvm\lib\Passes\PassRegistry.def`` is #include'd into
|
|
``llvm\lib\Passes\PassBuilder.cpp`` multiple times for various reasons. Since
|
|
it constructs our pass, we need to also add the proper #include in
|
|
``llvm\lib\Passes\PassBuilder.cpp``:
|
|
|
|
.. code-block:: c++
|
|
|
|
#include "llvm/Transforms/HelloNew/HelloWorld.h"
|
|
|
|
This should be all the code necessary for our pass, now it's time to compile
|
|
and run it.
|
|
|
|
Running a pass with ``opt``
|
|
---------------------------
|
|
|
|
Now that you have a brand new shiny pass, we can build :program:`opt` and use
|
|
it to run some LLVM IR through the pass.
|
|
|
|
.. code-block:: console
|
|
|
|
$ ninja -C build/ opt
|
|
# or whatever build system/build directory you are using
|
|
|
|
$ cat /tmp/a.ll
|
|
define i32 @foo() {
|
|
%a = add i32 2, 3
|
|
ret i32 %a
|
|
}
|
|
|
|
define void @bar() {
|
|
ret void
|
|
}
|
|
|
|
$ build/bin/opt -disable-output /tmp/a.ll -passes=helloworld
|
|
foo
|
|
bar
|
|
|
|
Our pass ran and printed the names of functions as expected!
|
|
|
|
Testing a pass
|
|
--------------
|
|
|
|
Testing our pass is important to prevent future regressions. We'll add a lit
|
|
test at ``llvm/test/Transforms/HelloNew/helloworld.ll``. See
|
|
:doc:`TestingGuide` for more information on testing.
|
|
|
|
.. code-block:: llvm
|
|
|
|
$ cat llvm/test/Transforms/HelloNew/helloworld.ll
|
|
; RUN: opt -disable-output -passes=helloworld %s 2>&1 | FileCheck %s
|
|
|
|
; CHECK: {{^}}foo{{$}}
|
|
define i32 @foo() {
|
|
%a = add i32 2, 3
|
|
ret i32 %a
|
|
}
|
|
|
|
; CHECK-NEXT: {{^}}bar{{$}}
|
|
define void @bar() {
|
|
ret void
|
|
}
|
|
|
|
$ ninja -C build check-llvm
|
|
# runs our new test alongside all other llvm lit tests
|
|
|
|
FAQs
|
|
====
|
|
|
|
Required passes
|
|
---------------
|
|
|
|
A pass that defines a static ``isRequired()`` method that returns true is a required pass. For example:
|
|
|
|
.. code-block:: c++
|
|
|
|
class HelloWorldPass : public PassInfoMixin<HelloWorldPass> {
|
|
public:
|
|
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
|
|
|
|
static bool isRequired() { return true; }
|
|
};
|
|
|
|
A required pass is a pass that may not be skipped. An example of a required
|
|
pass is ``AlwaysInlinerPass``, which must always be run to preserve
|
|
``alwaysinline`` semantics. Pass managers are required since they may contain
|
|
other required passes.
|
|
|
|
An example of how a pass can be skipped is the ``optnone`` function
|
|
attribute, which specifies that optimizations should not be run on the
|
|
function. Required passes will still be run on ``optnone`` functions.
|
|
|
|
For more implementation details, see
|
|
``PassInstrumentation::runBeforePass()``.
|