Awesome
LLVM Fork for Pass Plugins
The official LLVM toolchains are not built with plugin support (on Windows). This repository distributes toolchains (starting from LLVM 10.x.x) for Linux, MacOS and Windows with plugin support enabled.
Toolchains are also distributed with Rust compatibility (the Rust compiler
internally uses a fork of LLVM). For example, this toolchain can be used for
building Rust code (with rustc
1.59) and also be able to apply custom LLVM pass
plugins on the LLVM IR.
Using another version of rustc
than the one specified by a distributed toolchain
should be fine, as long as your rustc
uses the same LLVM version. For instance,
the previous example could work with rustc
1.58 as well, since it also uses LLVM 13.
Installation
Just download one of the release archives, and extract it wherever you want. For the
remaining of this README, we assume a toolchain is installed into ~/llvm
.
LTO Pass Plugins for Optimizing C++
Let's say you have an LLVM plugin that implements a new pass hello
.
You can optimize C++ code at link time (LTO) with your custom pass, by using
the lld
linker and supplying it with your plugin as an LTO pass plugin:
$ clang++ main.cc \
-flto \
-B~/llvm/bin \
-fuse-ld=lld \
-Xlinker --load-pass-plugin=./libplugin.so \
-Xlinker --lto-newpm-passes='lto<O3>,hello'
</details>
<details>
<summary><em>On MacOS</em></summary>
$ clang++ main.cc \
-flto \
-B~/llvm/bin \
-fuse-ld=lld \
-Xlinker --load-pass-plugin=libplugin.dylib \
-Xlinker --lto-newpm-passes='lto<O3>,hello'
</details>
<details>
<summary><em>On Windows</em></summary>
$ clang++ main.cc \
-flto \
-B~/llvm/bin \
-fuse-ld=lld \
-Xlinker /load-pass-plugin:plugin.dll \
-Xlinker /lto-newpm-passes:'lto<O3>,hello'
</details>
Note: the lto<O3>
pass is called before the custom pass hello
, because
we might still want the default LTO pipeline to be run first.
Note: On Windows, the pass plugin must link lld.lib
.
LTO Pass Plugins for Optimizing Rust
Let's say you have an LLVM plugin that implements a new pass hello
.
You can optimize Rust code at link time (LTO) with your custom pass, by using
the lld
linker and supplying it with your plugin as an LTO pass plugin:
$ RUSTFLAGS=" \
-Clinker-plugin-lto \
-Clink-arg=-B~/llvm/bin \
-Clink-arg=-fuse-ld=lld \
-Clink-arg=-Xlinker \
-Clink-arg=--load-pass-plugin=/path/to/libplugin.so \
-Clink-arg=-Xlinker \
-Clink-arg=--lto-newpm-passes=hello \
" \
cargo b --release
</details>
<details>
<summary><em>On MacOS</em></summary>
$ RUSTFLAGS=" \
-Clinker-plugin-lto \
-Clinker=~/llvm/bin/lld \
-Clink-arg=--load-pass-plugin=/path/to/libplugin.dylib \
-Clink-arg=--lto-newpm-passes=hello \
" \
cargo b --release
</details>
<details>
<summary><em>On Windows</em></summary>
$ RUSTFLAGS=" \
-Clinker-plugin-lto \
-Clinker=~/llvm/bin/lld.exe \
-Clink-arg=/load-pass-plugin:/path/to/plugin.dll \
-Clink-arg=/lto-newpm-passes:hello \
" \
cargo b --release
</details>
Note: in a default cargo
build, a custom pass would be called in parallel on
each codegen unit being linked by lld
. If you want your pass to be called only
once, on the fully merged codegen units, you can add lto=true
in your
Cargo Profile.
Warning: cargo
also builds proc-macros and build-scripts, both requiring a linking
phase. As a result, your custom passes will be called on these targets as well.
Therefore, you should make sure to filter these out in your passes. For instance, you
could check for strings like "build_script_build"
in the LLVM IR module name. For
proc-macros, there is no clear hint in their module names, so you may need to resort
to some blacklisting scheme.
Note: On Windows, the pass plugin must link lld.lib
.