编译 LLVM 项目
2019-08-17 20:53 compiler
最近开始研究 llvm 项目了. Rust 编译器也是基于它实现的.
LLVM 项目本身有多个子项目组成:
- llvm, 编译器
- lldb, 类似 gdb 的调试工具
- clang, c/c++ 编译器前端
- libcxx, c++ 标准库实现, 质量很棒, 可读性比较强
- polly, 用来做数据计算优化
- openmp, c/c++/fortran 并行计算库
首先是下载源代码, llvm 项目在 github 上面有完整的源码镜像:
$ git clone https://github.com/llvm/llvm-project
之后是安装系统依赖包, 编译 llvm 项目, 通常使用 clang 和 ninja, 速度要快些:
$ sudo apt update
$ sudo apt install ninja-build cmake clang
$ sudo apt build-dep cmake clang llvm
编译 llvm 项目
在 llvm-project
源码根目录, 创建 llvm-out
目录, 作为编译时的工作目录.
$ mkdir -v llvm-out
$ cd llvm-out
$ cmake -DCMAKE_BUILD_TYPE=Release -G Ninja -DCMAKE_INSTALL_PREFIX=/opt \
-DLLVM_ENABLE_BINDINGS=OFF -DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ ../llvm
$ cmake --build .
上面的选项:
-G Ninja
用于生成ninja.build
, 使用 ninja 编译系统-DCMAKE_INSTALL_PREFIX=/opt
, 定义库的安装目录是/opt
-DLLVM_ENABLE_BINDINGS=Off
, 禁用其它语言绑定, 比如 ocaml/python/go-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
, 使用 clang 编译器
编译完成后, 安装 llvm 库到系统中:
$ cmake --build . --target install
hello pass 来测试 llvm
接下来写一个 hello pass, 测试以上安装的 llvm 库是可用的.
$ tree
|-- hello
| |-- CMakeLists.txt
| `-- hello.cpp
|-- hello-out
|-- test.bc
`-- test.c
其中 hello/
目录里面是 hello pass 的源代码,
CMakeLists.txt
的内容是:
project(hello)
cmake_minimum_required(VERSION 3.2)
include_directories(/opt/include)
add_library(hello MODULE hello.cpp)
target_compile_features(hello PRIVATE cxx_range_for cxx_auto_type)
set_target_properties(hello PROPERTIES COMPILE_FLAGS "-fno-rtti")
hello.cpp
内容是:
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
#define DEBUG_TYPE "hello"
STATISTIC(HelloCounter, "Counts number of functions greeted");
namespace {
// Hello - The first implementation, without getAnalysisUsage.
struct Hello : public FunctionPass {
static char ID; // Pass identification, replacement for typeid
Hello() : FunctionPass(ID) {}
bool runOnFunction(Function &F) override {
++HelloCounter;
errs() << "Hello: ";
errs().write_escaped(F.getName()) << '\n';
return false;
}
};
}
char Hello::ID = 0;
static RegisterPass<Hello> X("hello", "Hello World Pass");
编译完之后, 会生成 libhello.so
动态库, 这个就是 hello pass
模块.
test.c
内容如下:
#include <stdio.h>
int main(void) {
printf("Hello, world!\n");
return 0;
}
使用 clang
编译生成 llvm 的字节码:
$ clang -c test.c -emit-llvm -o test.bc
之后使用 opt
命令验证上面生成的 libhello.so
:
$ opt -load libhello.so -hello test.bc
终端打印的是:
WARNING: You're attempting to print out a bitcode file.
This is inadvisable as it may cause display problems. If
you REALLY want to taste LLVM bitcode first-hand, you
can force output with the `-f' option.
Hello: main
Hello: main
是上面 hello.cpp
中打印的, 这个测试 pass 是正常的.