[docs][NewPM] Add docs for writing NPM passes

As to not conflict with the legacy PM example passes under
llvm/lib/Transforms/Hello, this is under HelloNew. This makes the
CMakeLists.txt and general directory structure less confusing for people
following the example.

Much of the doc structure was taken from WritinAnLLVMPass.rst.

This adds a HelloWorld pass which simply prints out each function name.

More will follow after this, e.g. passes over different units of IR, analyses.
https://llvm.org/docs/WritingAnLLVMPass.html contains a lot more.

Reviewed By: ychen, asbirlea

Differential Revision: https://reviews.llvm.org/D86979
This commit is contained in:
Arthur Eubanks 2020-08-31 18:36:11 -07:00
parent 2ad38f7a46
commit c2590de30d
15 changed files with 313 additions and 2 deletions

View File

@ -54,6 +54,7 @@ intermediate LLVM representation.
TableGenFundamentals
Vectorizers
WritingAnLLVMPass
WritingAnLLVMNewPMPass
WritingAnLLVMBackend
yaml2obj
@ -107,6 +108,10 @@ Optimizations
:doc:`WritingAnLLVMPass`
Information on how to write LLVM transformations and analyses.
:doc:`WritingAnLLVMNewPMPass`
Information on how to write LLVM transformations under the new pass
manager.
:doc:`Passes`
A list of optimizations and analyses implemented in LLVM.

View File

@ -0,0 +1,209 @@
====================
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 and ``LLVMBuild.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

View File

@ -34,6 +34,10 @@ We start by showing you how to construct a pass, everything from setting up the
code, to compiling, loading, and executing it. After the basics are down, more
advanced features are discussed.
This document deals with the legacy pass manager. LLVM is transitioning to
the new pass manager, which has its own way of defining passes. For more
details, see :doc:`WritingAnLLVMNewPMPass`.
Quick Start --- Writing hello world
===================================

View File

@ -0,0 +1,23 @@
//===-- HelloWorld.h - Example Transformations ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#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

View File

@ -18,4 +18,4 @@
type = Library
name = Passes
parent = Libraries
required_libraries = AggressiveInstCombine Analysis Core Coroutines IPO InstCombine ObjCARC Scalar Support Target TransformUtils Vectorize Instrumentation
required_libraries = AggressiveInstCombine Analysis Core Coroutines HelloNew IPO InstCombine ObjCARC Scalar Support Target TransformUtils Vectorize Instrumentation

View File

@ -75,6 +75,7 @@
#include "llvm/Transforms/Coroutines/CoroEarly.h"
#include "llvm/Transforms/Coroutines/CoroElide.h"
#include "llvm/Transforms/Coroutines/CoroSplit.h"
#include "llvm/Transforms/HelloNew/HelloWorld.h"
#include "llvm/Transforms/IPO/AlwaysInliner.h"
#include "llvm/Transforms/IPO/ArgumentPromotion.h"
#include "llvm/Transforms/IPO/Attributor.h"

View File

@ -197,6 +197,7 @@ FUNCTION_PASS("ee-instrument", EntryExitInstrumenterPass(/*PostInlining=*/false)
FUNCTION_PASS("make-guards-explicit", MakeGuardsExplicitPass())
FUNCTION_PASS("post-inline-ee-instrument", EntryExitInstrumenterPass(/*PostInlining=*/true))
FUNCTION_PASS("gvn-hoist", GVNHoistPass())
FUNCTION_PASS("helloworld", HelloWorldPass())
FUNCTION_PASS("instcombine", InstCombinePass())
FUNCTION_PASS("instcount", InstCountPass())
FUNCTION_PASS("instsimplify", InstSimplifyPass())

View File

@ -6,6 +6,7 @@ add_subdirectory(Scalar)
add_subdirectory(IPO)
add_subdirectory(Vectorize)
add_subdirectory(Hello)
add_subdirectory(HelloNew)
add_subdirectory(ObjCARC)
add_subdirectory(Coroutines)
add_subdirectory(CFGuard)

View File

@ -0,0 +1,6 @@
add_llvm_component_library(LLVMHelloNew
HelloWorld.cpp
DEPENDS
intrinsics_gen
)

View File

@ -0,0 +1,17 @@
//===-- HelloWorld.cpp - Example Transformations --------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "llvm/Transforms/HelloNew/HelloWorld.h"
using namespace llvm;
PreservedAnalyses HelloWorldPass::run(Function &F,
FunctionAnalysisManager &AM) {
errs() << F.getName() << "\n";
return PreservedAnalyses::all();
}

View File

@ -0,0 +1,22 @@
;===- ./lib/Transforms/HelloNew/LLVMBuild.txt ------------------*- Conf -*--===;
;
; Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
; See https://llvm.org/LICENSE.txt for license information.
; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
;
;===------------------------------------------------------------------------===;
;
; This is an LLVMBuild description file for the components in this subdirectory.
;
; For more information on the LLVMBuild system, please see:
;
; http://llvm.org/docs/LLVMBuild.html
;
;===------------------------------------------------------------------------===;
[component_0]
type = Library
name = HelloNew
parent = Transforms
library_name = HelloNew
required_libraries = Core

View File

@ -15,7 +15,7 @@
;===------------------------------------------------------------------------===;
[common]
subdirectories = AggressiveInstCombine Coroutines IPO InstCombine Instrumentation Scalar Utils Vectorize ObjCARC CFGuard
subdirectories = AggressiveInstCombine Coroutines HelloNew IPO InstCombine Instrumentation Scalar Utils Vectorize ObjCARC CFGuard
[component_0]
type = Group

View File

@ -0,0 +1,12 @@
; 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
}

View File

@ -8,6 +8,7 @@ static_library("Passes") {
"//llvm/lib/Target",
"//llvm/lib/Transforms/AggressiveInstCombine",
"//llvm/lib/Transforms/Coroutines",
"//llvm/lib/Transforms/HelloNew",
"//llvm/lib/Transforms/IPO",
"//llvm/lib/Transforms/InstCombine",
"//llvm/lib/Transforms/Instrumentation",

View File

@ -0,0 +1,9 @@
static_library("HelloNew") {
output_name = "LLVMHelloNew"
deps = [
"//llvm/lib/Analysis",
"//llvm/lib/IR",
"//llvm/lib/Support",
]
sources = [ "HelloWorld.cpp" ]
}