Skip to content

Commit

Permalink
Rework pipeline registration example
Browse files Browse the repository at this point in the history
The HelloWorld pass includes registration with the optimisation
pipelines (on top of regular registration implemented for all other
examples). That registration has been guarded with a CMake flag to
optionally disable it. This is required on MacOS as for `opt` installed
with HomeBrew, registering out-of-tree passes with optimisation
pipelines leads to a seg-fault. Currently, in order for that coded to be
disabled one has to specify:
  LT_LEGACY_SKIP_PIPELINE_REGISTRATION=On
when calling CMake. In other words, the problematic
code is _enabled_ by default. It would make more sense to have it
_disabled_ by default so that it never causes an accidental seg-fault.

With this patch, the problematic registration is disabled by default.
LT_LEGACY_SKIP_PIPELINE_REGISTRATION is replaced with
HELLOWORLD_OPT_PIPELINE_REG, which is to be set to ON when requesting
registration with the optimisation pipelines.

This was suggested by Amirahmad Khordadi in issue #8 - thank you!

README.md was also updated to reflect the changes and with a few general
edits.
  • Loading branch information
banach-space committed Jan 19, 2020
1 parent e463893 commit 0f74d2c
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 33 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,12 @@ script:
# Build HelloWorld as a standalone project
- cd HelloWorld
- mkdir build && cd build
- cmake -DLT_LEGACY_SKIP_PIPELINE_REGISTRATION=On -DLT_LLVM_INSTALL_DIR="$LLVM_DIR" ../
- cmake -DLT_LLVM_INSTALL_DIR="$LLVM_DIR" ../
- make -j4
# Build the top project and run tests
- cd ../../
- mkdir build
- cd build
- cmake -DLT_LEGACY_SKIP_PIPELINE_REGISTRATION=On -DLT_LLVM_INSTALL_DIR="$LLVM_DIR" ../
- cmake -DLT_LLVM_INSTALL_DIR="$LLVM_DIR" ../
- make -j4
- ${LIT_PATH}/lit test/
21 changes: 11 additions & 10 deletions HelloWorld/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,18 @@ if(NOT LLVM_ENABLE_RTTI)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
endif()

# For LLVM installed via HomeBrew (on Darwin), 'opt' seg-faults when an
# out-of-tree pass is registered with an existing optimisation pipeline. I have
# not experienced this when building from sources. Similar issue is discussed
# here:
# The following flag is used to enable the registration of HelloWorld with the
# optimisation pipelines.
# NOTE: Due to a known issue with LLVM installed with HomeBrew:
# https://github.com/sampsyo/llvm-pass-skeleton/issues/7
# The following flag is used as a workaround for that - it guards the
# problematic code (which otherwise works fine on Linux and on Darwin when
# building LLVM from sources).
OPTION(LT_LEGACY_SKIP_PIPELINE_REGISTRATION "Don't register with any of the existing pipelines" OFF)
if(LT_LEGACY_SKIP_PIPELINE_REGISTRATION)
add_definitions(-DLT_LEGACY_SKIP_PIPELINE_REGISTRATION)
# the following flag should be set to OFF when:
# * working on Mac OS _and_,
# * using LLVM installed via HomeBrew.
# TL;DR 'opt' seg-faults when an out-of-tree pass is registered with an
# existing optimisation pipeline.
OPTION(HELLOWORLD_OPT_PIPELINE_REG "Register HelloWorld with the optimisation pipelines" OFF)
if(HELLOWORLD_OPT_PIPELINE_REG)
add_definitions(-DHELLOWORLD_OPT_PIPELINE_REG)
endif()

#===============================================================================
Expand Down
9 changes: 5 additions & 4 deletions HelloWorld/HelloWorld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,16 @@ static RegisterPass<LegacyHelloWorld>
false // This pass is not a pure analysis pass => false
);

#ifdef HELLOWORLD_OPT_PIPELINE_REG
// Register LegacyHelloWorld as a step of an existing pipeline. The insertion
// point is set to 'EP_EarlyAsPossible', which means that LegacyHelloWorld will
// be run automatically at '-O{0|1|2|3}'.
#ifndef LT_LEGACY_SKIP_PIPELINE_REGISTRATION
// This trips 'opt' installed via HomeBrew. It's a known issues:
//
// NOTE: this trips 'opt' installed via HomeBrew (Mac OS). It's a known issues:
// https://github.com/sampsyo/llvm-pass-skeleton/issues/7
// I've tried all of the suggestions, but no luck. Locally I recommend either
// building from sources or commenting this out.
// Note: AFAIK, this is Mac OS only problem.
// building from sources or commenting this out. On Linux this always works
// fine.
static llvm::RegisterStandardPasses RegisterHelloWorld(
llvm::PassManagerBuilder::EP_EarlyAsPossible,
[](const llvm::PassManagerBuilder &Builder,
Expand Down
67 changes: 50 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,41 @@ $LLVM_DIR/bin/opt -load-pass-plugin libHelloWorld.dylib -passes=hello-world -dis
The **HelloWorld** pass doesn't modify the input module. The `-disable-output`
flag is used to prevent **opt** from printing the output bitcode file.

## Run HelloWorld automatically at any optimisation level
**NOTE:** On MacOS this only works when building LLVM from sources. More
information is available
[here](https://github.com/banach-space/llvm-tutor/blob/master/HelloWorld/HelloWorld.cpp#L118).

In order to run **HelloWorld** automatically at `-O{0|1|2|3}`, you have to enable
registration with the optimisation pipelines. This is done via
`HELLOWORLD_OPT_PIPELINE_REG` CMake variable:
```bash
export LLVM_DIR=<installation/dir/of/llvm/9>
mkdir build
cd build
cmake -DLT_LLVM_INSTALL_DIR=$LLVM_DIR -DHELLOWORLD_OPT_PIPELINE_REG=On <source/dir/llvm/tutor>/HelloWorld/
make
```
**HelloWorld** will now be run whenever an optimisation level is specified:
```bash
$LLVM_DIR/bin/opt -load libHelloWorld.dylib -O1 -disable-output input_for_hello.ll
# Expected output
(llvm-tutor) Hello from: foo
(llvm-tutor) number of arguments: 1
(llvm-tutor) Hello from: bar
(llvm-tutor) number of arguments: 2
(llvm-tutor) Hello from: fez
(llvm-tutor) number of arguments: 3
(llvm-tutor) Hello from: main
(llvm-tutor) number of arguments: 2
```
This registration is implemented in
[HelloWorld.cpp](https://github.com/banach-space/llvm-tutor/blob/master/HelloWorld/HelloWorld.cpp#L123).
Note that for this to work I used the Legacy Pass Manager (the plugin was
specified with `-load` rather than `-load-pass-plugin`).
[Here](#about-pass-managers-in-llvm) you can read more about pass managers in
LLVM.

Development Environment
=======================
## Platform Support And Requirements
Expand Down Expand Up @@ -512,32 +547,30 @@ LLVM is a quite complex project (to put it mildly) and passes lay at its
center - this is true for any [multi-pass
compiler](https://en.wikipedia.org/wiki/Multi-pass_compiler<Paste>). In order
to manage the passes, a compiler needs a pass manager. LLVM currently enjoys
not one, but two pass manager. This is important, because depending on which
pass manager you decide to use, the implementation (and in particular pass
registration) will look slightly differently. I have tried my best to make the
distinction in the source code very clear.
not one, but two pass managers. This is important because depending on which
pass manager you decide to use, the implementation of your pass (and in
particular how you _register_ it) will look slightly differently.

## Overview of Pass Managers in LLVM
As mentioned earlier, there are two pass managers in LLVM:
As I mentioned earlier, there are two pass managers in LLVM:
* _Legacy Pass Manager_ which currently is the default pass manager
* It is implemented in the _legacy_ namespace
* It is very well [documented](http://llvm.org/docs/WritingAnLLVMPass.html)
(more specifically, writing and registering a pass withing the Legacy PM is
very well documented)
* _New Pass Manager_ aka [_Pass Manager_](https://github.com/llvm-mirror/llvm/blob/ff8c1be17aa3ba7bacb1ef7dcdbecf05d5ab4eb7/include/llvm/IR/PassManager.h#L458) (that's how it's referred to in the code base)
* I understand that it is [soon to become](http://lists.llvm.org/pipermail/llvm-dev/2019-August/134326.html) the default pass manager in LLVM
* The source code is very throughly commented, but otherwise I am only aware of
this great [blog series](https://medium.com/@mshockwave/writing-llvm-pass-in-2018-preface-6b90fa67ae82) by Min-Yih Hsu.
* The source code is very throughly commented, but there is no official documentation. Min-Yih Hsu kindly wrote
this great [blog series](https://medium.com/@mshockwave/writing-llvm-pass-in-2018-preface-6b90fa67ae82) that you can refer to instead.

The best approach is to implement your passes for both pass managers.
Fortunately, once you have an implementation that works for one of them, it's
relatively straightforward to extend it so that it works for the other one as
well. All passes in LLVM provide an interface for both and that's what I've
been trying to achieve here as well.
If you are not sure which pass manager to use, it is probably best to make sure
that your passes are compatible with both. Fortunately, once you have an
implementation that works with one of them, it's relatively straightforward to
extend it so that it works with the other one as well.

## New vs Legacy PM When Running Opt
**MBAAdd** implements interface for both pass managers. This is how you will
use it via the legacy pass manager:
use it with the legacy pass manager:
```bash
$LLVM_DIR/bin/opt -load <build_dir>/lib/libMBAAdd.so -legacy-mba-add input_for_mba.ll -o out.ll
```
Expand All @@ -547,13 +580,13 @@ And this is how you run it with the new pass manager:
$LLVM_DIR/bin/opt -load-pass-plugin <build_dir>/lib/libMBAAdd.so -passes=mba-add input_for_mba.ll -o out.ll
```
There are two differences:
* the way you load your plugins: `-load` vs `-load-pass-plugin`
* the way you load your plugin: `-load` vs `-load-pass-plugin`
* the way you specify which pass/plugin to run: `-legacy-mba-add` vs
`-passes=mba-add`

The command line option is different because with the legacy pass manager you
_register_ a new command line option with **opt** and with the new pass manager
you just define the pass pipeline (via `-passes=`).
These differences stem from the fact that in the case of Legacy Pass Manager you
register a new command line option for **opt**, whereas New Pass Manager
simply requires you to define a pass pipeline (with `-passes=`).

Credits
========
Expand Down

0 comments on commit 0f74d2c

Please sign in to comment.