LLVM编译器工具链的获取、从源码构建、快速构建
LLVM编译器工具链在使用前需要安装,读者可以从直接从网络安装,也可以由源码构建出来。这里以在Ubuntu–20.04系统上的安装构建来作介绍。
apt联网安装
LLVM官网给出了在Debian和Ubuntu等系统下联网安装LLVM特定版本或最新版本的工具链的参考命令行,参看以下链接:
https://apt.llvm.org/
以在Ubuntu系统下安装LLVM工具链为例,可以使用apt-get命令逐个安装:
$ apt-get update$ apt-get install clang-15 lldb-15 lld-15
也可以用LLVM官方的脚本安装所有工具。先将以下deb源追加到/etc/apt/sources.list文件中。不同版本的Ubuntu的源是不一样的,下面是Ubuntu20.04的:
deb http://apt.llvm.org/focal/ llvm-toolchain-focal maindeb-src http://apt.llvm.org/focal/ llvm-toolchain-focal maindeb http://apt.llvm.org/focal/ llvm-toolchain-focal-18 maindeb-src http://apt.llvm.org/focal/ llvm-toolchain-focal-18 maindeb http://apt.llvm.org/focal/ llvm-toolchain-focal-19 maindeb-src http://apt.llvm.org/focal/ llvm-toolchain-focal-19 main
Ubuntu 22.04则对应这些源:
deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy maindeb-src http://apt.llvm.org/jammy/ llvm-toolchain-jammy maindeb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 maindeb-src http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 maindeb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 maindeb-src http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main
然后获取llvm安装脚本并执行一键安装:
$ wget https://apt.llvm.org/llvm.sh$ chmod +x llvm.sh$ sudo ./llvm.sh 15 all
脚本llvm.sh的运行过程可能会报缺少一些工具,用apt-get相应安装即可:
$ apt-get install lsb-release wget software-properties-common gnupg
安装完成后,运行clang检验安装效果:
$ clang-15 --help | head -n 6OVERVIEW: clang LLVM compilerUSAGE: clang [options] file...OPTIONS:-### Print (but do not run) the commands to run for this compilation
从源码构建
前面介绍的apt联网安装二进制方法,适合于LLVM编译器的用户。而从源码构建LLVM工具链的方式,则适用于基于LLVM进行二次开发的场景,例如clang前端扩展、Pass编写、后端适配开发、各类runtime运行时库开发、测试用例开发等。
环境要求
LLVM官网文档给出了其工具链构建的软硬件要求,位于https://llvm.org/docs/GettingStarted.html。
官网给出的构建环境要求是比较低的,一般运行Windows或Linux的X86 PC机都能胜任。但是从笔者的构建经验看,建议配置CPU不要低于Intel i5 10代,DDR不要少于16GB,硬盘余量不低于200GB。之所以要这么大的内存和硬盘空间,是因为基于LLVM的二次开发大概率用到Debug构建模式,而Debug模式输出的工具链体积庞大,单个工具就能达到GB级别。
LLVM工具链基于源码的Debug模式构建需要很长时间。笔者用Intel i7-12700这个20线程处理器搭配64GB DDR内存,在Ubuntu-20.04系统下多进程编译,需要40+分钟。当然,如果是已经进行过一次完整编译后修改了某些源码文件,然后作增量编译,一般只需要几分钟。此时的构建时间瓶颈,是在链接这一步骤。
软件环境上,LLVM的构建过程要求CMake版本不低于3.20.0,python版本不低于3.8,zlib版本不低于1.2.3.4,GNU Make版本不低于3.79。
基本构建
接下来的构建过程,以及本书中对LLVM代码的介绍,除特别说明外均基于LLVM-15版本的llvmorg-15.0.0-rc1这个tag。
首先是代码获取,将目标tag检出并标记为本地分支:
$ git clone https://github.com/llvm/llvm-project.git$ cd llvm-project$ git checkout llvmorg-15.0.0-rc1 –b llvmorg-15.0.0-rc1
LLVM工具链的构建常用模式是Release和Debug模式。其中Debug模式构建时间长,构建出的工具链体积较大,clang等几个主要工具的体积会达到2GB以上,这些工具的加载运行时间自然也更长。但是Debug模式构建出的工具链支持前端AST可视化打印、后端SelectionDAG打印、gdb调试等多种调试方式,适合工具链开发过程使用;Release模式构建时间短,输出工具链体积小,适用于工具版本发布场景。
先以Release模式构建工具链为例,构建过程的命令行如下:
$ mkdir build; cd build$ cmake -DLLVM_ENABLE_PROJECTS="clang;lld" \-DCMAKE_BUILD_TYPE=Release \-G "Unix Makefiles" ../llvm$ make
如果要以Debug模式构建工具链,只要替换cmake命令中的CMAKE_BUILD_TYPE选项为Debug,其他部分不变:
$ mkdir build; cd build$ cmake -DLLVM_ENABLE_PROJECTS="clang;lld" \-DCMAKE_BUILD_TYPE=Debug \-G "Unix Makefiles" ../llvm$ make
上面命令中,我们构建的是clang和lld这两个项目,它们对应LLVM代码树顶层的clang和lld两个目录。之所以不用在向LLVM_ENABLE_PROJECTS变量显式加入llvm项目,是因为llvm在默认构建范畴内。归属在顶层llvm目录下的llc、llvm-objdump和llvm-readelf等工具也在默认构建范畴,无需在命令行指定。
能用LLVM_ENABLE_PROJECTS指定的完整项目列表在llvm/CMakeLists.txt中,它们大多对应llvm-project项目的一个顶层目录:
set(LLVM_ALL_PROJECTS "bolt;clang;clang-tools-extra;compiler-rt;cross-project-tests;libc;libclc;libcxx;libcxxabi;libunwind;lld;lldb;mlir;openmp;polly;pstl")
读者可按需添加。构建完成后,clang等工具位于build/bin目录下。
更快的构建
在多核服务器上,使用多线程编译能大幅加快构建进程:
$ make -j32
但要留意,如果用Debug模式编译,多线程构建LLVM工具链的瓶颈往往在最后链接阶段对系统内存的需求。20个左右的工具并行链接的构建过程,能吃掉200GB以上内存;如果内存和硬盘交换区在都被用光,构建过程会报错终止。此时,开发者恶意降低并行线程数量再次make是可以完成构建的。
对于当前正在调试的目标工具,可以只针对该目标进行编译,也可以避免无用消耗提升效率。例如当前只调试后端,就可以先只编译llc工具:
$ make llc –j32
cmake能够生成Makefile,也可以生成build.ninja构建脚本,这两种构建脚本都能构建出LLVM工具链。相比于Makefile,ninja的构建脚本更为低层,没有Makefile中那些需要推导的依赖关系和隐式表达的规则,因而运行速度更快。尤其是在实际构建脚本由cmake这种元构建系统生成而不需要人为书写的情况下,ninja的低层表达方式更适合机器执行。用ninja构建需调整cmake这一步的-G选项,然后用ninja all执行构建:
$ cmake -G "Ninja" ../llvm$ ninja all
在ninja构建方式下,调整构建目标或使用多线程构建:
$ ninja llc$ ninja llc -j32
另外,LLVM工具链构建的瓶颈在链接阶段,安装使用LLVM发布版本的lld链接器,或是GNU的gold链接器也能大幅提高构建速度:
$ cmake -DLLVM_USE_LINKER=lld -G "Ninja" ../llvm
夜雨聆风