学习LLVM第0篇:LLVM项目介绍

更全面的内容请直接参考官方文档,这里仅列出我在学习LLVM时用到的一些编译命令及遇到的问题,还会有些文档中没有提到的内容。

编译项目

  1. 安装CMakeNinja构建工具。

    这里除了Ninja构建系统,其实也可以选择其他的,比如Unix Makefiles。其中CMake可以理解为给开发者使用的构建工具接口,至于实际的构建系统,开发者可以显式指定让CMake去使用,比如下面在生成实际的构建系统时,就用了Ninja(默认为Unix Makefiles)。

    Mac系统建议使用Brew来安装,brew install cmake ninja,如果还需要编译文档,则需要安装doxygen、sphinx-doc,安装命令:pip install sphinx=1.8.5(使用brew安装时找不到这个版本的sphinx)、brew install doxygen

  2. 克隆LLVM仓库:git clone https://github.com/llvm/llvm-project

    如果国内clone太慢,也可以使用Gitee提供的镜像仓库 https://gitee.com/mirrors/LLVM (注意:镜像仓库目前每天同步一次,所以内容会有延后)

  3. 创建构建目录,用于存放构建系统文件和构建出来的东西,比如在项目的根目录创建:mkdir build,然后进到构建目录: cd build

  4. 生成构建系统:cmake -G Ninja -DLLVM_ENABLE_PROJECTS="clang;libcxx" ../llvm

    常用参数:

    • LLVM_ENABLE_PROJECTS:比如"clang;libcxx"。打算编译的项目列表,默认会编译LLVM,这里可以指定除LLVM之外的项目,比如clangclang-tools-extralibcxxlibc等。

      小提示:所有支持的项目可以去看llvm/CMakeLists.txt文件中的LLVM_ALL_PROJECTS变量的内容。当前的内容为:clang;clang-tools-extra;compiler-rt;debuginfo-tests;libc;libclc;libcxx;libcxxabi;libunwind;lld;lldb;mlir;openmp;parallel-libs;polly;pstl

    • LLVM_TARGETS_TO_BUILD:比如"X86;RISCV"。指定LLVM后端支持的目标架构,比如X86ARMRISCVWebAssembly

      小提示:所有支持的目标架构可以去看llvm/CMakeLists.txt文件中的LLVM_ALL_TARGETS变量的内容。当前的内容为:AArch64;AMDGPU;ARM;AVR;BPF;Hexagon;Lanai;Mips;MSP430;NVPTX;PowerPC;RISCV;Sparc;SystemZ;WebAssembly;X86;XCore

    • LLVM_ENABLE_SPHINX:是否编译LLVM文档。ON表示启动。依赖Sphinx

    • LLVM_ENABLE_DOXYGEN:是否编译LLVM API文档。ON表示启动。依赖Doxygen

  5. 进行构建:

    • cmake --build . 表示编译默认目标包括LLVM和LLVM_ENABLE_PROJECTS指定的项目

    • cmake --build . --target docs-llvm-html 表示只编译LLVM文档

      小提示:一开始我用的最新的3.1.1的Sphinx,运行这条命令会报错,在master分支上编译会提示:llvm/docs/TableGen/LangRef.rst:270:duplicate token description of SimpleValue, other instance in TableGen/LangRef,这是一个警告,但是因为编译的时候指定了-W,会将警告当作错误来对待,所以会直接退出。如果使用官方文档所用的Sphinx版本1.8.5(从文档的右下角可以看到),则会报llvm/docs/CommandGuide/llvm-dwarfdump.rst:40:unknown option: --debug-info。目前我的解决办法是去掉-W之后单独执行:sphinx-build -b html -d docs/_doctrees-llvm-html -q ../llvm/docs docs/html。不过打开生成的文档发现样式有点问题,每个文档的标题下面是一大块空白的,是CSS写的有问题。

      更新(2020-06-26):后面又试了下,如果去当前最新的release/10.x分支,使用Sphinx 1.8.5编译,则可以编译通过。所以需要编译Sphinx文档,建议使用1.8.5版本的Sphinx,并且编译的是release/10.x分支。

    • cmake --build . --target doxygen-llvm 表示只编译LLVM API文档,试了下发现编译需要非常长的时间,做好心理准备。官方用的Doxygen版本是1.8.13,我用的1.8.18编译目前没有发现问题。

    小提示:1. 想要查看有哪些target可以去查看生成的build/CMakeFiles/TargetDirectories.txt文件。像这里的doxygen-llvm我就是通过这种方式找到的,文档上一直没有找到。2. 通过在本地编译文档,可以很方便后面学习LLVM时使用,在线的文档没有本地文档访问快。并且也可以添加一些中文注解,方便理解。

将前面的命令放在一起方便拷贝:

1
2
3
4
5
6
7
8
9
10
11
12
git clone https://github.com/llvm/llvm-project
// Gitee源:git clone https://gitee.com/mirrors/LLVM
cd llvm-project && mkdir build && cd build
cmake -G Ninja\
-DLLVM_ENABLE_PROJECTS="clang;libcxx"\
-DLLVM_TARGETS_TO_BUILD="X86"\
-DLLVM_ENABLE_SPHINX=ON\
-DLLVM_ENABLE_DOXYGEN=ON\
../llvm
cmake --build . // 编译llvm、clang、libcxx
cmake --build . --target docs-llvm-html // 编译LLVM文档
cmake --build . --target doxygen-llvm // 编译LLVM API文档

官方文档参考:

常用LLVM命令

  • clang
    • clang -S -emit-llvm input.c -o out.ll:生成人可读的LLVM IR
    • clang -c -emit-llvm input.c -o out.bc:生成二进制LLVM IR,也可以不加-c,默认编译为二进制IR
  • opt
    • opt -load-pass-plugin=libHelloWorld.dylib -passes="hello-world" a.ll:使用HelloWorld Pass对out.ll进行处理。注意这是新的单独调用Pass的方式,老的方式为:opt -load libHelloWorld.dylib -legacy-hello-world a.ll
    • opt -analyze -view-cfg a.ll 生成控制流程图(Control-flow graph),可以很方便的查看函数中的各种BB及BB直接的跳转。
  • llvm-asllvm-dis
    • llvm-as a.ll -o a.bc:将人可读的LLVM IR编译为二进制LLVM IR
    • llvm-dis a.bc -o a.ll:将二进制LLVM IR反编译为人可读的LLVM IR
  • llc
    • llc a.ll -o a.out:将LLVM IR编译为汇编代码,进而可以使用原生汇编器编译为可执行文件
  • lli
    • lli a.ll:直接解释执行LLVM IR
  • llvm-link
    • llvm-link -S add.ll main.ll -o all.ll:链接多个LLVM IR为一个LLVM IR文件,-S表示输出的为人可读的LLVM IR,默认为二进制LLVM IR
  • llvm-config:使用llvm-config --help查看帮助。给依赖LLVM的项目提供支持,输出LLVM库的头文件地址、库文件地址、库列表。
    • llvm-config --cxxflags:生成编译时的头文件库参数,比如在我电脑上会输出-I/usr/local/Cellar/llvm/10.0.0_3/include -std=c++14 -stdlib=libc++ -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
    • llvm-config --ldflags:生成链接时的LLVM库文件地址,比如在我电脑上会输出-L/usr/local/Cellar/llvm/10.0.0_3/lib -Wl,-search_paths_first -Wl,-headerpad_max_install_names
    • llvm-config --libs:生成LLVM提供的库名称,结合llvm-config --ldflags,就可以让链接器找到对应的库文件。在--libs后面还可以添加各种组件名称,不添加表示所有组件。比如--libs core native,则只会输出core和native组件相关的库。通过下面的llvm-config --components命令可以列举出所有的LLVM组件列表
    • llvm-config --components:输出所有的LLVM组件

官方文档参考: