Helpful guide towards learning to LLVM passes (legacy and new passes) for beginners
A step-by-step tutorial for building an out-of-source LLVM pass
LLVM is an umbrella project for building compilers and code transformation tools. It consists of several sub-projects like Clang, LLD and, confusingly enough, the LLVM sub-project. We consider in this tutorial:
- Building the LLVM sub-project from source
- Building a trivial out-of-source LLVM pass.
We will be building LLVM v12.0.1
which is the latest as of this writing.
We assume that you have a working compiler toolchain (GCC or LLVM) and that CMake is installed (minimum version 3.4).
llvm-tutorial
├── .gitignore
├── ELI5.md
├── LICENSE
├── README.md
├── samples
│ ├── cfile.c
│ └── cppfile.cpp
└── src
├── CMakeLists.txt
├── CustomLegacyPass
│ ├── CMakeLists.txt
│ ├── MyLegacyPass.cpp
│ └── MyLegacyPass.h
└── CustomNewPass
├── CMakeLists.txt
├── MyNewPass.cpp
└── MyNewPass.h
4 directories, 13 files
- ./src dir: Boilerplate files for legacy and new passes are provided, along with CMake files for easy compilation.
- ./samples dir: Sample [.c|.cpp] files are provided for testing passes.
Compiling LLVM from source is mandatory if you are developing an in-source pass (within LLVM source tree). It can also be convenient in the case of developing out-of-source passes as it gives you full control over the compilation options. For example, a debug build of LLVM is much more pleasant to work with compared to an optimized one. To compile LLVM, please follow the following steps:
-
Download LLVM source and unpack it in a directory of your choice which will refer to as
[LLVM_SRC]
or download from github repo The LLVM Compiler Infrastructure -
Create a separate build directory
mkdir llvm-build cd llvm-build
-
Instruct CMake to detect and configure your build environment:
cmake -DCMAKE_BUILD_TYPE=Debug -DLLVM_TARGETS_TO_BUILD=X86 [PATH_TO_LLVM_SRC] #or cmake -DLLVM_ENABLE_PROJECTS='clang;openmp' \ -DCMAKE_BUILD_TYPE=Release \ -DLLVM_ENABLE_ASSERTIONS=ON \ -DLLVM_CCACHE_BUILD={ON,OFF} \ -G Ninja [PATH_TO_LLVM_SRC]
Note that we instructed cmake to only build
X86
backend. You can choose a different backend if needed. If you do not specifyLLVM_TARGETS_TO_BUILD
, then all supported backends will be built by default which requires more time. -
Now start the actual compilation within your build directory
cmake --build .
The
--build
option is a portable why to tell cmake to invoke the underlying build tool (make, ninja, xcodebuild, msbuild, etc.) -
Building takes some time to finish. After that you can install LLVM in its default directory which is
/usr/local
cmake --build . --target install
Alternatively, it's possible to set a different install directory
[LLVM_HOME]
. Since we will need[LLVM_HOME]
in the next stage, we assume that you have defined it as an environment variable$LLVM_HOME
. Now you can issue the following commandcmake -DCMAKE_INSTALL_PREFIX=$LLVM_HOME -P cmake_install.cmake
Note that
$LLVM_HOME
must not contain~
(tilde) to refer to your home directory as it won't be expanded. Use$HOME
or an absolute path instead.
Download header and pre-compiled binary files using Homebrew or Linuxbrew
-
Download and install llvm
brew install llvm brew install llvm@12 # alternative brew upgrade llvm # for upgrade
-
Exporting lib and include flags
export LDFLAGS="-L/usr/local/opt/llvm/lib" export CPPFLAGS="-I/usr/local/opt/llvm/include"
[Optional] For those using VScode, include following path in c++ include path settings
# PATH TO LLVM_HOME DIR /usr/local/opt/llvm/**
To build the skeleton legacy LLVM pass found in CustomLegacyPass
folder or,
to build the skeleton new LLVM pass found in CustomNewPass
folder :
[Important] Enable or add respective pass folder in CMakeLists.txt
cd llvm-tutorial
mkdir build
cd build
cmake ../src/
make
Clean build
git clean -d -f -x # or
rm -rf build && mkdir build
cmake
needs to find its LLVM configurations in [LLVM_DIR]
. We automatically
setup [LLVM_DIR]
based on $LLVM_HOME
for you. Now the easiest way to run the skeleton pass is to use Clang:
[Legacy]
# C frontend
clang \
-Xclang \
-load -Xclang build/CustomLegacyPass/libCustomLegacyPass.so samples/{file}.c
# C++ frontend
clang++ \
-Xclang \
-load -Xclang build/CustomLegacyPass/libCustomLegacyPass.* samples/{file}.cpp
[New]
# C frontend
clang \
-fexperimental-new-pass-manager \
-fpass-plugin=build/CustomNewPass/libCustomNewPass.so samples/{file}.c
# C++ frontend
clang++ \
-fexperimental-new-pass-manager \
-fpass-plugin=build/CustomNewPass/libCustomNewPass.so samples/{file}.cpp
NOTE:
- Execution of any command is from root dir, unless specifically mentioned
- Clang is the compiler front-end of the LLVM project. It can be installed separately in binary form.
for plugging in the pass in optimization pipeline
[.ll|.bc]
# samples/{file}.ll
clang -emit-llvm -S samples/{file}.c*
# samples/{file}.bc
clang -O1 -emit-llvm samples/{file}.c* -c
clang -Xclang -disable-O0-optnone -emit-llvm samples/test.c -c
# disable optimisation (O0)
# -O* represent optimization level
# To get IR in human readable format
cat samples/{file}.ll
Executing [.ll|.bc] files
lli samples/{file}[.ll|.bc]
We can also use the cc1 for generating IR:
clang \
-cc1 \
-emit-llvm samples/{file}[.c|.cpp] \
-o samples/{file}.ll
[Legacy]
opt \
-load build/CustomLegacyPass/libCustomLegacyPass.so \
-"MyLegacyPass" -disable-output samples/{file}.[bc|ll]
# Alternative
opt \
-load build/CustomLegacyPass/libCustomLegacyPass.so \
-MyLegacyPass < samples/{file}.[bc|ll] > /dev/null
[New]
opt \
-load-pass-plugin=build/CustomNewPass/libCustomNewPass.so \
-passes="MyNewPass" -disable-output samples/{file}.[bc|ll]
To generate modified [.bc] file after optimization performed by plugged in pass, we add -S flag to get output and store it in new [.bc] file
opt \
-S \
-load-pass-plugin=build/CustomNewPass/libCustomNewPass.so \
-passes="MyNewPass" samples/{file_V1}.bc > samples/{file_V2}.bc
To generate CFG files use -dot-cfg flag
opt -S -dot-cfg samples/{file_V1}.bc > {file_V2}.bc # or
opt -S -dot-cfg samples/{file_V1}.bc -o {file_V2}.bc
- Adrian Sampson's "LLVM for Grad Students"
sampsyo/llvm-pass-skeleton (github repo) - banach-space/llvm-tutor (github repo)
- LLVM Documentation
- Writing an LLVM Pass: 101;
LLVM 2019 tutorial by Andrzej Warzyński
This tutorial is based on the following resources
- Adrian Sampson's blog entry "LLVM for Grad Students" (link)
- tomgu1991.github.io blog : Introduction to LLVM Pass (link)
- LLVM docs (llvm.org doc)
- My First Language Frontend with LLVM Tutorial (link)
- Writing an LLVM Pass (link)
- Writing an LLVM New Pass (link)
- Using New Pass Manager (link)
- LLVM’s New Pass Manager (link)
- LLVM documentation: Building LLVM with CMake (link)
- Getting Started with the LLVM System (link)
- Projects built with LLVM (link)
- llvm: clang installed from linuxbrew is not able to link binary executable #22034 (github issue) (link)
- Installed LLVM doesn't get added to $PATH #29733 (github issue) (link)
- How to automatically register and load modern Pass in Clang? (so) (link)
- How to write your own compiler (link)