Building an out of Tree LLVM Pass using CMake
Compiling LLVM vs. using precompiled LLVM Libraries
There are two options for creating your own LLVM pass: Your pass can either live directly inside the LLVM source tree or it can live outside in its own tree. In this blog post, I will go into detail about setting up an out of source LLVM pass.
The LLVM documentation suggests using a precompiled version of the LLVM libraries. This does come with some advantages and disadvantages.
To make your LLVM pass more flexible your build system should allow building either from source or from the precompiled LLVM libraries.
Setting up the Build System
Building a LLVM pass using the LLVM sources is not as simple as using precompiled LLVM libraries. LLVMs build system makes use of lots of custom CMake macros which have to be included in your build system for it to work right.
Building the Pass
Detecting the configuration
First we need to determine if we are building against the LLVM sources or a precompiled version of LLVM.
if (NOT PATH_TO_LLVM)
message(FATAL_ERROR "
The cmake is supposed to be called with PATH_TO_LLVM pointing to
a precompiled version of LLVM or to to the source code of LLVM
Examples:
cmake -G \${CMAKE_GENERATOR}\ -DPATH_TO_LLVM=/opt/llvm-9.0.1 ${CMAKE_SOURCE_DIR}
cmake -G \${CMAKE_GENERATOR}\ -DPATH_TO_LLVM=/llvm-project/llvm ${CMAKE_SOURCE_DIR}
")
endif()
The argument -DPATH_TO_LLVM
is used to pass the path for the LLVM sources or precompiled libraries to the build system.
set (BUILD_AGAINST_PRECOMPILED_LLVM TRUE)
if (EXISTS ${PATH_TO_LLVM}/CMakeLists.txt)
set (BUILD_AGAINST_PRECOMPILED_LLVM FALSE)
endif()
Check if we are using a precompiled LLVM libraries and set BUILD_AGAINST_PRECOMPILED_LLVM
accordingly.
Including LLVM CMake Macros
Next we need to include the LLVM CMake macros which are used to add libraries (add_llvm_library
) and executables (add_llvm_executable
) to LLVM.
if (${BUILD_AGAINST_PRECOMPILED_LLVM})
set (search_paths
${PATH_TO_LLVM}
${PATH_TO_LLVM}/lib/cmake
${PATH_TO_LLVM}/lib/cmake/llvm
${PATH_TO_LLVM}/share/llvm/cmake/
)
find_package(LLVM REQUIRED CONFIG PATHS ${search_paths} NO_DEFAULT_PATH)
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
else()
set (LLVM_TARGETS_TO_BUILD "host" CACHE STRING "Only build targets for host architecture" FORCE)
add_subdirectory(${PATH_TO_LLVM} llvm-build)
get_target_property(LLVM_INCLUDE_DIRS LLVMSupport INCLUDE_DIRECTORIES)
list(APPEND CMAKE_MODULE_PATH
"${PATH_TO_LLVM}/cmake/modules"
)
set(LLVM_MAIN_SRC_DIR ${PATH_TO_LLVM})
endif()
If we are using the precompiled LLVM libraries we use PATH_TO_LLVM
to derive search paths CMake will look for the external project.
find_package
loads the CMake settings from the precompiled LLVM and allows us to manually add LLVM_CMAKE_DIR
to the CMAKE_MODULE_PATH
. Now CMake will find all the macros defined by the precompiled LLVM libraries.
In case we are building from LLVM sources things get a little bit simpler. We just have to create a subdirectory where we want to put the files that are created when building LLVM. We will save the directories containing all LLVM headers in LLVM_INCLUDE_DIRS
for later use. At the end we again manually add the path to the CMake macros we want to use to CMAKE_MODULE_PATH
.
include(LLVM-Config)
include(HandleLLVMOptions)
include(AddLLVM)
Now we can include the set of CMake modules we want to use in our build system. Those modules will allow us to use LLVM specific CMake functions and macros like add_llvm_library
and add_llvm_executable
.
Including LLVM Headers
The directories containing the LLVM headers we saved to LLVM_INCLUDE_DIRS
before can now be included.
include_directories(${LLVM_INCLUDE_DIRS})
Creating our Pass
We are using the CMake function add_llvm_library
to add our pass.
add_llvm_library(OutOfTreeLLVMPass MODULE
Hello.cpp
DEPENDS
intrinsics_gen
PLUGIN_TOOL
opt
)
Build the Pass
After setting up the build system our pass can be built by creating a build directory and running CMake inside the build directory.
Using sources
Assuming the LLVM sources are located at ../llvm-project/llvm
and our pass is located at ../OutOfTreeLLVMPass
cmake -DPATH_TO_LLVM=../llvm-project/llvm ../OutOfTreeLLVMPass
Using precompiled LLVM Libraries
After downloading the precompiled LLVM libraries we can run:
cmake -DPATH_TO_LLVM=../clang+llvm-9.0.0-x86_64-darwin-apple ../OutOfTreeLLVMPass
Building our pass is now as simple as running:
cmake --build .
Further Reading
Building an LLVM based tool. Lessosn learned
Feedback
If you would like to discuss this topic further or ask questions feel free to comment on the GitHub discussion issue for this post. Did I make any mistakes? If so feel free to create a pull request on GitHub Discussions.