Build llvm
这里假设读者对 cmake 有一定的了解,并且会下载 llvm 源码,官网或者github上都能下到。官网有各个 release 版本的发布。
打算自己构建的话推荐是 github。
首先是建一个 build 目录,在其下运行如下命令,通过 cmake 生成 makefile。
更多的配置项请参阅 Building LLVM with CMake 。
cmake -G Ninja -DLLVM_BUILD_TESTS=OFF \
-DLLVM_TARGETS_TO_BUILD="AArch64;ARM;X86" \
-DCMAKE_INSTALL_PREFIX=/mnt2/usr/local \
-DCMAKE_BUILD_TYPE="Release" \
-DLLVM_PARALLEL_LINK_JOBS=8 ../llvm
这里选择了 Ninja 作为构建工具,之后运行 ninja
即可开始构建。
最后 ninja install
即可完成安装。
很有意思的是在安装生成的程序前可以重新指定安装目录。
通过 cmake -DCMAKE_INSTALL_PREFIX=/tmp/llvm -P cmake_install.cmake
完成。
what is pass
Embedding LLVM in your project
可参阅 Embedding LLVM in your project
cmake_minimum_required(VERSION 3.4.3)
project(SimpleProject)
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
# Set your project compile flags.
# E.g. if using the C++ header files
# you will need to enable C++11 support
# for your compiler.
include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
# Now build our tools
add_executable(simple-tool tool.cpp)
# Find the libraries that correspond to the LLVM components
# that we wish to use
llvm_map_components_to_libnames(llvm_libs support core irreader)
# Link against LLVM libraries
target_link_libraries(simple-tool ${llvm_libs})
Developing LLVM passes out of source
Hello pass 的例子是在 llvm 源码树下进行构建的。有时候我们希望不在源码树下构建自己的pass,可以采用以下方式。
目录结构如下:
<project dir>/
|
CMakeLists.txt
<pass name>/
|
CMakeLists.txt
Pass.cpp
...
一级目录下的 CMakeLists.txt
find_package(LLVM REQUIRED CONFIG)
add_definitions(${LLVM_DEFINITIONS})
include_directories(${LLVM_INCLUDE_DIRS})
add_subdirectory(<pass name>)
二级目录下的 CMakeLists.txt
add_library(LLVMPassname MODULE Pass.cpp)
这和源码树下的有些差异,源码树下的可以拷贝 llvm/lib/Transforms/Hello/
下的个 CMakeLists.txt
。采用了 llvm 内部定义的函数 add_llvm_library()
的方式进行添加。
Run an LLVM Pass Automatically with Clang1
可参阅 Run an LLVM Pass Automatically with Clang
官方给的教程通常需要以下步骤去调用我们自己的 Pass
文件。
首先生成 .bc
文件,然后使用 opt
命令调用我们的 Pass
,如果继续优化的话走第 3 步,之后通过 llc
生成 汇编文件,最后用汇编器和链接器生成可执行文件。
1. clang -c -emit-llvm code.c -o code.bc / clang -O0 -S -emit-llvm code.c -o code.ll
2. opt -load mypass.so -mpass < code.bc > code_inst.bc / > /dev/null
3. opt -O3 < code_inst.bc > code_opt.bc
4. llc code_opt.bc -o code_opt.s
5. use your favorite assembler and linkers to get the rest of the way to an executable
通过 LLVM 的 pass registry 进行 Pass 注册来使得我们的 pass 在加载 Pass 的 .so 文件时生效。
static void registerMyPass(const PassManagerBuilder &, PassManagerBase &PM) {
PM.add(new MyPass());
}
static RegisterStandardPasses RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible, registerMyPass);
这样就能使用下面熟悉的命令行方式来运行我们的 Pass 了。
clang -Xclang -load -Xclang mypass.so ...
插入 Pass 运行的位置是可控的,参考 ExtensionPointTy。
较新 LLVM 使用了新的 PassManager infrastructure
, 旧框架的类被移到 llvm::legacy namespace
下。
Clang 我没去查证过,目前尚不清楚是否已经全部切过去,有一个兼容的写法如下。
#include "llvm/IR/LegacyPassManager.h"
using namespace llvm;
static void registerPass(const PassManagerBuilder &, legacy::PassManagerBase &PM) {
...
}
运行 Pass
- 各个 Pass 流程时间统计
opt -load mypass.so -mpass -time-passes < code.bc > code_inst.bc
- 查看自己 Pass 支持选项
opt -load mypass.so -mpass -help
Debug a llvm Pass
如何进行 Pass 的调试呢?
Debug 信息输出
#include"llvm/Support/Debug.h"
// for more fine-grained control '-debug-only' option
#define DEBUG_TYPE "afl"
...
LLVM_DEBUG(dbgs() << "debug message\n");
...
特别注意 :需要 opt
本身是 Debug
选项下编译出来的,因为上述的 LLVM_DEBUG
在 Release
版本下失效。
即 llvm
工程编译的时候选择 Debug (默认) 而不是 Release。
最后在运行 Pass 的时候加个 -debug
选项。
opt -load mypass.so -mpass < code.bc > code_inst.bc -debug
opt -load mypass.so -mpass < code.bc > code_inst.bc -debug-only=afl
gdb opt
b llvm::Pass::preparePassManager
run -load Mypass -mpass < code.bc > /dev/null