编译和安装LLVM整个流程
编译和安装LLVM整个流程
1.1 LLVM系统入门
LLVM项目包括多个组件。该项目的核心本身被称为LLVM。其中包含所需的所有工具、库和头文件,以便处理中间表达式并将其转换为目标对象文件。LLVM工具包括汇编程序、反汇编程序、位代码分析器和位代码优化器。另外,LLVM还包含基本的回归测试。
类C语言使用Clang前端。该组件使用LLVM将C、C++、Objective C和Objective C++代码编译为LLVM位代码,并将它们编译为目标对象文件。其他组件包括:libc++C++标准库、LLD链接器等。
1.1.1查看LLVM(包括Clang等子项目)
通过以下方式可获取llvm的源码。
在linux系统上:
git
clone
https://github.com/llvm/llvm-project.git
或者,在windows上:
git
clone
--config
core.autocrlf=false
https://github.com/llvm/llvm-project.git
为了节省存储并加快checkout签出时间,可能需要进行浅层克隆clone。例如,要获取LLVM项目的最新版本,请使用
git
clone
--depth
1
https://github.com/llvm/llvm-project.git
1.1.2配置和构建LLVM和Clang:
1. 构建系统生成器
通过以下方式构建LLVM。
cd
llvm-project
cmake
-S
llvm
-B
build
-G
<generator>
[options]
一些常见的构建系统生成器包括:
1)Ninja — 用于生成Ninja构建文件。大多数llvm开发人员都使用Ninja。
2)Unix
Makefiles — 用于生成与make兼容的并行makefiles生成文件。
3)Visual
Studio — 用于生成Visual Studio项目和解决方案。
4)Xcode — 用于生成Xcode项目。
5)有关更全面的list列表,请参阅CMake文档。
2. 一些常见选项
1)-DLLVM_ENABLE_PROJECTS='...' — 要额外构建的LLVM子项目的semicolon-separated分号分隔列表。可以包括以下任何一项:clang, clang-tools-extra, lldb, lld, polly, 或跨项目测试cross-project-tests。
例如, 构建LLVM, Clang和LLD,请使用 -DLLVM_ENABLE_PROJECTS="clang;lld".
2)-DCMAKE_INSTALL_PREFIX=directory — 为directory 目录指定要安装的LLVM工具和库的完整路径名(default默认方式为:/usr/local)。
3)-DCMAKE_BUILD_TYPE=type — 控制生成的优化级别和调试信息。类型的有效选项包括 Debug, Release, RelWithDebInfo和MinSizeRel. 有关更多详细信息,请参阅 CMAKE_BUILD_TYPE。
4)-DLLVM_ENABLE_ASSERTIONS=ON —在启用断言检查的情况下编译(对于调试生成,默认为ON,对于所有其他生成类型,默认为OFF)。
5)-DLLVM_USE_LINKER=lld — 与lld链接器链接,假设已经安装在系统上。如果默认链接器速度较慢,这会大大加快链接时间。
6)-DLLVM_PARALLEL_{COMPILE,LINK}_JOBS=N — 限制同时并行运行的编译/链接工作的数量。这对于链接来说尤其重要,因为链接可能会占用大量内存。如果在构建LLVM时遇到内存问题,请尝试将其设置为限制同时运行的编译/链接作业的最大数量。
3. 直接指定的生成系统
cmake
--build
.
[--target
<target>] 或上面直接指定的生成系统。
1)默认目标(即cmake--build.或make)将构建所有LLVM。
2)检测所有目标(即ninja check-all)将运行回归测试,以确保一切正常。
3)CMake将为每个工具和库生成构建目标,大多数LLVM子项目都会生成自己的检测check-<project>目标。
4)运行串行生成会很慢。要提高速度,请尝试运行并行构建。这是在Ninja中默认完成的;对于make,使用选项-j NN,其中NN是并行作业的数量,例如,可用CPU的数量。
4. 基本的CMake和构建/测试调用
基本的CMake和构建/测试调用,只构建LLVM,不构建其他子项目:
cmake
-S
llvm
-B
build
-G
Ninja
-DCMAKE_BUILD_TYPE=Debug
ninja
-C
build
check-llvm
使用调试信息设置LLVM构建,然后编译LLVM并运行LLVM测试。
5. 配置参考信息
1)有关CMake选项的更多详细信息,请参见CMake。
2)如果生成或测试失败,请参阅以下内容。
有关配置和编译LLVM的详细信息,请参阅LLVM入门(Getting Started with LLVM)相关内容。转到目录布局Directory Layout,以了解源代码树的布局。
1.2 独立构建
Stand-alone 独立构建能根据系统上已经存在的clang或llvm库的预构建版本,来构建子项目。
可以使用llvm项目标准checkout签出的源代码(如上所述)来进行独立构建,但也可以从稀疏checkout签出或从release发布页面上可用的原始码tarball来进行构建。
对于独立构建,必须有一个llvm安装,该安装已正确配置为可供其他项目的独立构建使用。这可以是发行版提供的LLVM安装,也可以自己构建,如下所示:
cmake -G Ninja -S path/to/llvm-project/llvm -B $builddir \
-DLLVM_INSTALL_UTILS=ON \
-DCMAKE_INSTALL_PREFIX=/path/to/llvm/install/prefix \
< other options >
ninja -C $builddir install
一旦安装了llvm,为了通过独立构建配置项目,请如下调用CMake:
cmake -G Ninja -S path/to/llvm-project/$subproj \
-B $buildir_subproj \
-DLLVM_EXTERNAL_LIT=/path/to/lit \
-DLLVM_ROOT=/path/to/llvm/install/prefix
请注意
独立生成需要在一个文件夹中进行,该文件夹不是生成LLVMN的原始文件夹($builddir!=$builddir_subproj)。
LLVM_ROOT应该指向LLVM安装的前缀。例如,如果LLVM安装到/usr/bin和/usr/lib64中,则应该配置-DLLVM_ROOT=/usr/。
LLVM_ROOT和LLVM_EXTERNAL_LT选项都是为所有子项目执行独立生成所必需的。每个子项目所需的其他选项可在下表中找到。
表1.1中列出的子项目支持check-$subproj和安装构建目标。
Sub-Project |
需要Sub-Directories |
需要CMake选项 |
llvm |
llvm, cmake, third-party |
LLVM_INSTALL_UTILS=ON |
clang |
clang, cmake |
CLANG_INCLUDE_TESTS=ON (只需要check-clang) |
lld |
lld, cmake |
|
表1.1 check-$subproj与安装构建的子项目
构建独立clang的示例:
#!/bin/sh
build_llvm=`pwd`/build-llvm
build_clang=`pwd`/build-clang
installprefix=`pwd`/install
llvm=`pwd`/llvm-project
mkdir -p $build_llvm
mkdir -p $installprefix
cmake -G Ninja -S $llvm/llvm -B $build_llvm \
-DLLVM_INSTALL_UTILS=ON \
-DCMAKE_INSTALL_PREFIX=$installprefix \
-DCMAKE_BUILD_TYPE=Release
ninja -C $build_llvm install
cmake -G Ninja -S $llvm/clang -B $build_clang \
-DLLVM_EXTERNAL_LIT=$build_llvm/utils/lit \
-DLLVM_ROOT=$installprefix
ninja -C $build_clang
1.3 软硬件环境要求
在开始使用LLVM系统之前,请查看下面给出的要求。以便提前知道需要什么硬件和软件,从而省去一些麻烦。
1.3.1硬件环境
表1.2 表示支持LLVM运行的平台。
OS |
Arch |
Compilers |
Linux |
x861 |
GCC, Clang |
Linux |
amd64 |
GCC, Clang |
Linux |
ARM |
GCC, Clang |
Linux |
Mips |
GCC, Clang |
Linux |
PowerPC |
GCC, Clang |
Linux |
SystemZ |
GCC, Clang |
Solaris |
V9 (Ultrasparc) |
GCC |
DragonFlyBSD |
amd64 |
GCC, Clang |
FreeBSD |
x861 |
GCC, Clang |
FreeBSD |
amd64 |
GCC, Clang |
NetBSD |
x861 |
GCC, Clang |
NetBSD |
amd64 |
GCC, Clang |
OpenBSD |
x861 |
GCC, Clang |
OpenBSD |
amd64 |
GCC, Clang |
macOS2 |
PowerPC |
GCC |
macOS |
x86 |
GCC, Clang |
Cygwin/Win32 |
x861, 3 |
GCC |
Windows |
x861 |
Visual Studio |
Windows x64 |
x86-64 |
Visual Studio |
表1.2 支持LLVM运行的平台
标注:
1)奔腾及以上处理器支持代码生成。
2)仅支持32位ABI代码生成。
3)要在基于Win32的系统上使用LLVM模块,可以使用-DBUILD_SHARED_LIBS=on配置LLVM。
请注意,调试构建需要大量的时间和磁盘空间。仅LLVM的构建将需要大约1-3GB的空间。LLVM和Clang的完整构建将需要大约15-20GB的磁盘空间。确切的空间要求将因系统而异。(它之所以如此之大,是因为所有的调试信息以及库被静态链接到多个工具中的事实)。
如果受空间约束,则只能生成选定的工具或选定的目标。Release版本所需的空间要少得多。
1.3.2 软件环境
编译LLVM需要安装多个软件包。下表列出了所需的软件包。Package列是LLVM所依赖的软件包的常用名称。Version列提供软件包的“已知工作”版本。Notes列描述了LLVM如何使用该包,并提供了其他详细信息。表1.3 表示支持LLVM运行的软件包。
Package |
Version |
Notes |
>=3.13.4 |
Makefile/workspace生成器 |
|
>=7.1.0 |
C/C++ compiler1 |
|
>=3.6 |
自动化测试套件2 |
|
>=1.2.3.4 |
压缩库3 |
|
3.79, 3.79.1 |
Makefile/build 处理器4 |
表1.3 支持LLVM运行的软件包
数字标注解析:
1)只需要C和C++语言,因此不需要为LLVM的目的构建其他语言。请参阅下面的具体版本信息。
2)只有在llvm/test目录中运行自动化测试套件时才需要。
3)可选项,为选定的LLVM工具添加压缩/解压缩功能。
4)可选项,可以使用CMake支持的任何其他构建工具。
此外,编译主机通常会有过多的Unix实用程序。明确地:
1)ar-档案库生成器。
2)bzip2-bzip2命令用于生成分发。
3)bunzip2-用于分发检查的bunzip2命令。
4)chmod-更改文件的权限。
5)cat-输出串联实用程序。
6)cp-复制文件。
7)date-打印当前日期/时间。
8)echo-打印到标准输出。
9)egrp-扩展的正则表达式搜索实用程序。
10)find-在文件系统中查找文件/目录。
11)grep-正则表达式搜索实用程序。
12)gzip-用于生成分发的gzip命令。
13)gunzip-用于分发检查的gunzip命令。
14)install-安装目录/文件。
15)mkdir-创建一个目录。
16)mv-移动(重命名)文件。
17)ranlib-用于归档库的符号表生成器。
18)rm-删除文件和目录。
19)sed-用于转换输出的流编辑器。
20)sh-用于生成构建脚本的Bourne shell。
21)tar—用于分发生成的磁盘归档。
22)test-在文件系统中测试内容。
23)unzip-用于分发检查的unzip命令。
24)zip-用于生成分发的zip-zip命令。
1.3.3 主机C++工具链,包括编译器和标准库
LLVM对主机C++编译器要求很高,因此往往会暴露编译器中的错误。还需要试图合理地密切关注C++语言和库的改进和发展。因此,为了构建LLVM,需要一个现代的主机C++工具链,包括编译器和标准库。
LLVM是使用编码标准中记录的C++子集编写的。为了强制执行此语言版本,在构建系统中,检查最流行的主机工具链中的特定最低版本:
1)Clang 5.0
2)Apple Clang 10.0
3)GCC 7.1
4)Visual Studio 2019 16.7
任何比这些工具链更旧的内容都可以工作,但需要使用特殊选项强制构建系统,而且它并不是真正受支持的主机平台。还要注意,这些编译器的旧版本经常会导致LLVM崩溃或编译错误。
对于不太广泛使用的主机工具链,如ICC或xlC,请注意,可能需要非常新的版本来支持LLVM中使用的所有C++功能。
跟踪某些版本的软件,这些软件在作为主机工具链的一部分使用时会失败。有时甚至包括链接器。
GNU ld 2.16.X。ld链接器的某些2.16.X版本会产生很长的警告消息,警告某些“.GNU.linkonce.t.*”符号是在丢弃的部分中定义的。可以安全地忽略这些消息,因为它们是错误的,并且链接是正确的。若使用ld 2.17,这些警告消息将消失。
GNU binutils 2.17:Binutils 2.17包含一个错误,该错误在构建LLVM时会导致巨大的链接时间(分钟而不是秒)。建议升级到更新的版本(2.17.50.0.4或更高版本)。
GNU Binutils 2.19.1 Gold:此Gold版本包含一个错误,该错误会在使用位置无关代码构建LLVM时导致间歇性故障。症状是关于循环依赖关系的错误。建议升级到新的Gold版本。
1.3.4 获取现代主机C++工具链
主要介绍适用于Linux和较旧BSD环境的工具链。在macOS上,应该有一个足够现代的Xcode,否则,可能需要升级,直到升级完成为止。Windows没有“系统编译器”,所以必须安装Visual Studio 2019(或更高版本),或最新版本的mingw64。FreeBSD 10.0及更新版本有一个现代的Clang作为系统编译器。
然而,一些Linux发行版和一些其他或更旧的BSD有时会有非常旧的GCC版本。这些步骤试图帮助升级编译器,即使在这样的系统上也是如此。但是,如果可能的话,鼓励使用具有满足这些要求的现代系统编译器的最新的发行版本。请注意,安装Clang和libc++的早期版本作为主机编译器是很有吸引力的,但是libc++直到最近才在Linux上经过很好的测试或设置构建。因此,建议只使用libstdc++和现代GCC作为引导程序中的初始主机,然后使用Clang(可能还有libc++)。
第一步是安装最新的GCC工具链。用户在版本要求方面遇到困难的最常见的发行版是Ubuntu Precise,12.04 LTS。对于这个发行版,一个简单的选择是安装测试PPA的工具链,并使用它来安装现代GCC。在ask-ubuntu堆栈交换和带有更新命令的github gist上对此进行了非常好的讨论。然而,并不是所有用户都可以使用PPA,还有许多其他发行版,所以从源代码构建和安装GCC可能是必要的(或者只是有用的,毕竟是在做编译器开发)。现在做这件事也很容易。
安装GCC 7.1.0的简单步骤:
% gcc_version=7.1.0
% wget https://ftp.gnu.org/gnu/gcc/gcc-${gcc_version}/gcc-${gcc_version}.tar.bz2
% wget https://ftp.gnu.org/gnu/gcc/gcc-${gcc_version}/gcc-${gcc_version}.tar.bz2.sig
% wget https://ftp.gnu.org/gnu/gnu-keyring.gpg
% signature_invalid=`gpg --verify --no-default-keyring --keyring ./gnu-keyring.gpg gcc-${gcc_version}.tar.bz2.sig`
% if [ $signature_invalid ]; then echo "Invalid signature" ; exit 1 ; fi
% tar -xvjf gcc-${gcc_version}.tar.bz2
% cd gcc-${gcc_version}
% ./contrib/download_prerequisites
% cd ..
% mkdir gcc-${gcc_version}-build
% cd gcc-${gcc_version}-build
% $PWD/../gcc-${gcc_version}/configure --prefix=$HOME/toolchains --enable-languages=c,c++
% make -j$(nproc)
% make install
有关更多详细信息,请查看优秀的GCC wiki条目,从中可获得大部分信息。
一旦有了GCC工具链,就可以将LLVM的构建配置为使用主机编译器和C++标准库的新工具链。由于新版本的libstdc++不在系统库搜索路径上,因此需要传递额外的链接器标志,以便在链接时(-L)和运行时(-rpath)可以找到。如果使用的是CMake,那么此调用应该会生成可工作的二进制文件:
% mkdir build
% cd build
% CC=$HOME/toolchains/bin/gcc CXX=$HOME/toolchains/bin/g++ \
cmake .. -DCMAKE_CXX_LINK_FLAGS="-Wl,-rpath,$HOME/toolchains/lib64 -L$HOME/toolchains/lib64"
如果未能设置rpath,大多数LLVM二进制文件将在启动时失败,并从类似于libstdc++.so.6的加载程序中发出消息:找不到版本“GLIBCXX_3.4.20”。这意味着需要调整-rpath链接器标志。
此方法将为所有可执行文件的rpath添加一个绝对路径。这对本地开发很好。如果想分发构建的二进制文件,以便可以在较旧的系统上运行,请将libstdc++.so.6复制到lib/目录中。所有LLVM的装载shipping二进制文件都有一个指向$ORIGIN/../lib的路径,所以会在那里找到libstdc++.so.6。非分布式二进制文件没有rpath集,也找不到libstdc++.so.6。将-DLLVM_LOCAL_RPATH=“$HOME/toolchains/lib64”传递给cmake,以便可以添加到libstdc++.so.6的绝对路径,如上所述。由于这些二进制文件不是分布式的,所以如果有一个绝对的本地路径,这就是很好的。
当构建Clang时,需要允许它访问现代C++标准库,以便在引导过程中将其用作新主机。有两种简单的方法可以做到这一点,要么与Clang一起构建(并安装)libc++,然后将其与-stdlib=libc++编译和链接标志一起使用;要么将Clang安装到与GCC相同的前缀(上面的$HOME/toolchains)中。Clang将在自己的前缀中查找libstdc++,如果找到就使用它。还可以为Clang添加一个显式前缀,以便查找带有--GCC toolchain=/opt/my/GCC/prefix标志的GCC工具链,并在使用just-built-Clang构建的Clang进行引导时,将其传递给编译和链接命令。
1.4 LLVM基础入门
其余部分介绍如何使用LLVM,并提供有关LLVM环境的一些基本信息。
后面部分介绍了LLVM源代码树的总体布局,一个使用LLVM工具链的简单示例,以及查找有关LLVM的更多信息或获得帮助的链接。
1.4.1术语和符号
以下名称用于表示特定于本地系统和工作环境的路径。这些不是需要设置的环境变量,只是下面文档其余部分中使用的字符串。在下面的任何示例中,只需将这些名称中的每一个替换为本地系统上的适当路径名即可。所有这些路径都是绝对的。
SRC_ROOT
这是LLVM源代码树的顶层目录。
OBJ_ROOT
这是LLVM目标树的顶层目录(即放置目标文件和编译程序的树,可以与SRC_ROOT相同)。
1.4.2 打开LLVM档案文件
如果有LLVM分发版,则需要先对其进行解压缩,然后才能开始编译。LLVM是作为许多不同的子项目分发的。每一个子项目都有自己的下载,这是一个用gzip程序压缩的TAR档案文件。
文件如下,其中x.y标记了版本号:
llvm-x.y.tar.gz
这是LLVM库和工具的源代码发布版本。
cfe-x.y.tar.gz格式
这是Clang前端的源代码发布版本。
1.4.3 从Git签出LLVM
可以从Git中checkout签出LLVM的源代码。
标注:
在正确调整.gitattribute设置后,将来不需要配置--config core.autocrlf=false。但在写文档时,Windows用户需要配置。
在linux上只需运行:
% git clone https://github.com/llvm/llvm-project.git
或者在Windows上,
% git clone --config core.autocrlf=false https://github.com/llvm/llvm-project.git
这是在当前目录中创建一个“llvm项目”目录,并用llvm和所有相关子项目的所有源代码,测试目录和文档文件的本地副本完全填充该目录。注意,与tarball原始码不同的是,tarball原始码将每个子项目都包含在一个单独的文件中,git存储库将所有项目一起包含。
如果想要获得特定的版本(而不是最新的修订版),可以在克隆存储库后签出标记。例如,git checkout llvmorg-6.0.1位于由上述命令创建的llvm项目目录中。使用git tag -l列出所有这些。
1.4.4 本地LLVM配置
签出存储库后,必须在构建LLVM套件源代码之前对其进行配置。此过程使用CMake。取消常规配置脚本的链接,CMake将以请求的任何格式生成构建文件,以及各种*.inc文件和llvm/include/llvm/Config/Config.h.CMake文件。
在命令行上使用格式-D<variable name>=<value>,变量将其传递给cmake。表1.4表示LLVM开发人员使用的一些常见选项。
Variable |
Purpose |
CMAKE_C_COMPILER |
告诉CMAKE要使用哪个C编译器。默认情况下,使用/usr/bin/cc。 |
CMAKE_CXX_COMPILER |
告诉cmake要使用哪个C++编译器。默认情况下,使用/usr/bin/c++。 |
CMAKE_BUILD_TYPE |
告诉CMAKE要使用哪个C++编译器。默认情况下,这将是/usr/bin/c++。 |
CMAKE_INSTALL_PREFIX |
告诉CMAKE要为哪种类型的生成文件。有效的选项有Debug、Release、RelWithDebInfo和MinSizeRel。默认值为“调试”。 CMAKE_INSTALL_PREFIX指定运行生成文件的安装操作时,要作为目标的安装目录。 |
Python3_EXECUTABLE |
通过向Python解释器传递路径,强制CMake使用特定的Python版本。默认情况下,使用PATH中解释器的Python版本。 |
LLVM_TARGETS_TO_BUILD |
一个分号分隔的列表,用于控制将构建哪些目标,并将其链接到LLVM中。默认列表定义为LLVM_ALL_TARGETS,可以设置为包括out-of-tree targets。默认值包括:AArch64、AMDGPU、ARM、AVR、BPF、Hexagon、Lanai、Mips、MSP430、NVPTX、PowerPC、RISCV、Sparc、SystemZ、WebAssembly、X86、XCore。将其设置为“主机”只会编译主机体系结构(例如,相当于在X86主机上指定X86),可以显著加快编译和测试时间。 |
LLVM_ENABLE_DOXYGEN |
用源代码构建基于DOXYGEN的文档。默认情况下,这是禁用的,因为它速度慢,会生成大量输出。 |
LLVM_ENABLE_PROJECTS |
一个分号分隔的列表,用于选择要额外构建的其他LLVM子项目。(仅在使用并列项目布局时有效,例如通过git)。默认列表为空。可以包括:clang、clang tools extra、cross-project-tests、flang、libc、libclc、lld、lldb、mlir、openmp、polly或pstl。 |
LLVM_ENABLE_RUNTIMES |
一个分号分隔的列表,用于选择要构建的运行时。(仅在使用完整的单一布局时有效)。默认列表为空。可以包括:编译器rt、libc、libcxx、libcxxabi、libunfold或openmp。 |
LLVM_ENABLE_SPHINX |
用源代码构建基于sphinx-based的文档。这在默认情况下是禁用的,因为它速度慢并且会生成大量输出。建议使用Sphinx1.5版或更高版本。 |
LLVM_BUILD_LLVM_DYLIB |
生成libLLVM.so。此库包含一组默认的LLVM组件,这些组件可以用LLVM_DYLIB_components重写。默认值包含大部分LLVM,并在tools/LLVM-shlib/CMakelists.txt中定义。此选项在Windows上不可用。 |
LLVM_OPTIMIZED_TABLEGEN |
构建在LLVM构建过程中使用的发布表生成。这可以显著加快调试构建的速度。 |
表1.4 LLVM开发人员使用的一些常见选项
要配置LLVM,请执行以下步骤:
将目录更改为对象根目录:
% cd OBJ_ROOT
运行cmake:
% cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=<type> -DCMAKE_INSTALL_PREFIX=/install/path
[other options] SRC_ROOT
1.4.5 编译LLVM套件源代码
与自动工具不同,使用CMake,构建类型是在配置中定义的。如果想更改构建类型,可以通过以下调用重新运行cmake:
%cmake-G“Unix Makefiles”-DCMAKE_BILD_TYPE=<TYPE>SRC_ROOT
在运行之间,CMake会保留为所有选项设置的值。CMake定义了以下构建类型:
1. 调试信息
这些构建是默认的。构建系统将编译未优化的工具和库,并启用调试信息和断言。
2. 释放信息
对于这些构建,构建系统将编译启用了优化的工具和库,而不会生成调试信息。CMakes的默认优化级别为-O3。这可以通过在CMAKE命令行上设置CMAKE_CXX_FLAGS_RERELEASE变量来配置。
3. 关联有关信息
这些生成在调试时很有用。它们生成带有调试信息的优化二进制文件。CMakes的默认优化级别为-O2。这可以通过在CMAKE命令行上设置CMAKE_CXX_FLAGS_RELWITHDEBINFO变量来配置。
配置LLVM后,可以通过输入OBJ_ROOT目录并发出以下命令来构建LLVM:
如果构建失败,看看是否使用了已知不编译LLVM的GCC版本。如果机器中有多个处理器,可能希望使用GNUMake提供的一些并行构建选项。例如,可以使用以下命令:
% make -j2
使用LLVM源代码时,有几个特殊目标非常有用:
make clean
删除生成的所有文件。这包括目标文件、生成的C/C++文件、库和可执行文件:
make install
在$PREFIX下的层次结构中安装LLVM头文件、库、工具和文档,该层次结构由CMAKE_INSTALL_PREFIX指定,默认为/usr/local。
make docs-llvm-html
如果配置为-DLLVM_ENABLE_SPHINX=On,这将在OBJ_ROOT/docs/html处生成一个目录,其中包含html格式的文档。
1.4.6 交叉编译LLVM
可以交叉编译LLVM本身。也就是说,可以创建LLVM可执行文件和库,将其托管在不同于构建(加拿大交叉构建)的平台上。为了生成用于交叉编译的构建文件,CMake提供了一个变量CMake_TOOLCHAIN_FILE,该变量可以定义编译器标志和CMake测试操作期间使用的变量。
这种生成的结果是无法在生成主机上运行,但可以在目标上执行的可执行文件。例如,以下CMake调用可以生成针对iOS的构建文件。这将适用于带有最新Xcode的
macOS:
% cmake -G "Ninja" -DCMAKE_OSX_ARCHITECTURES="armv7;armv7s;arm64"
-DCMAKE_TOOLCHAIN_FILE=<PATH_TO_LLVM>/cmake/platforms/iOS.cmake
-DCMAKE_BUILD_TYPE=Release -DLLVM_BUILD_RUNTIME=Off -DLLVM_INCLUDE_TESTS=Off
-DLLVM_INCLUDE_EXAMPLES=Off -DLLVM_ENABLE_BACKTRACES=Off [options]
<PATH_TO_LLVM>
注意:由于iOS SDK的限制,在为iOS构建时需要传递一些额外的标志。
有关交叉编译的更多信息,请查看如何使用Clang/LLVM交叉编译Clang/LLFM,以及关于如何交叉编译的Clang文档。
1.4.7 LLVM目标对象文件的位置
LLVM构建系统能够在多个LLVM构建之间共享单个LLVM源树(source tree)。因此,可以使用相同的源树为几个不同的平台或配置构建LLVM。
1)将目录更改为LLVM目标对象文件的定位:
% cd OBJ_ROOT
2)运行cmake
% cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release SRC_ROOT
LLVM构建将在OBJ_ROOT下创建一个与LLVM源树匹配的结构。在源树中存在源文件的每个级别,OBJ_ROOT中将有一个相应的CMakeFiles目录。在该目录下还有另一个目录,其名称以.dir结尾,可以在其中找到每个源的目标文件。
例如:
% cd llvm_build_dir
% find lib/Support/ -name APFloat*
lib/Support/CMakeFiles/LLVMSupport.dir/APFloat.cpp.o
1.4.8 可选配置项目
如果在支持binfmt_misc模块的Linux系统上运行,并且对该系统具有root访问权限,则可以将系统设置为直接执行LLVM位代码文件。要执行此操作,请使用以下命令(如果已经在使用模块,则可能不需要第一个命令):
% mount -t binfmt_misc none /proc/sys/fs/binfmt_misc
% echo ':llvm:M::BC::/path/to/lli:' > /proc/sys/fs/binfmt_misc/register
% chmod u+x hello.bc (if needed)
% ./hello.bc
这允许直接执行LLVM位代码文件。在Debian上,也可以使用此命令而不是上面的“echo”命令:
% sudo update-binfmts --install llvm /path/to/lli --magic 'BC'
1.5 目录布局
关于LLVM源库的一个有用信息来源是LLVM doxygen文档,可在https://llvm.org/doxygen/得到。以下是对代码布局的简要介绍:
1. llvm/cmake
生成系统文件。
llvm/cmake/modules
为llvm用户定义的选项构建配置。检查编译器版本和链接器标志。
llvm/cmake/平台
针对MSVC的Android NDK、iOS系统和非Windows主机的工具链配置。
2. llvm/examples
一些简单的例子展示了如何使用LLVM作为自定义语言的编译器,包括降低、优化和代码生成。
Kaleidoscope教程:Kaleidoscope语言教程是为一种非平凡的语言运行了一个较好的小编译器的实现,包括手写的lexer、解析器、AST,以及使用LLVM的代码生成支持-包括静态(预先准备好的)和各种实时(JIT)编译方法。
构建AJIT:构建AJIT教程的示例,该教程展示了LLVM的ORC JIT API如何与LLVM的其他部分交互。它还帮助如何重新组合它们,以构建适合用例的自定义JIT。
3. llvm/include
从LLVM库导出的公共头文件。三个主要子目录:
llvm/include/llvm
所有LLVM特定的头文件,以及LLVM不同部分的子目录:Analysis、CodeGen、Target、Transforms等。
llvm/include/lilvm/support
LLVM提供的通用支持库,但不一定特定于LLVM。例如,一些C++STL实用程序和处理库的命令行选项在此处存储头文件。
llvm/include/llvm/config
cmake配置的头文件。它们包装“标准”UNIX和C头文件。源代码可以包括这些头文件,这些头文件自动处理cmake生成的#includes条件。
4. llvm/lib
大多数源文件都在这里。通过将代码放在库中,LLVM可以轻松地在工具之间共享代码。
构建AJIT:构建AJIT教程的示例,该教程展示了LLVM的ORC JIT API如何与LLVM的其他部分交互。它还教导如何重新组合它们,以构建适合用例的自定义JIT。
llvm/include
从LLVM库导出的公共头文件。三个主要子目录:
llvm/include/llvm
所有LLVM特定的头文件,以及LLVM不同部分的子目录:Analysis、CodeGen、Target、Transforms等。
llvm/include/lilvm/support
LLVM提供的通用支持库,但不一定特定于LLVM。例如,一些C++STL实用程序和处理库的命令行选项在此处存储头文件。
llvm/include/llvm/config
cmake配置的头文件。它们包装“标准”UNIX和C头文件。源代码可以包括这些头文件,这些头文件自动处理cmake生成的条件#includes。
llvm/lib
大多数源文件都在这里。通过将代码放在库中,LLVM可以轻松地在工具之间共享代码。
llvm/lib/IR/
实现Instruction和BasicBlock等核心类的核心LLVM源文件。
llvm/lib/AsmParser/
LLVM汇编语言分析器库的源代码。
llvm/lib/比特码/
用于读取和写入位代码的代码。
llvm/lib/分析/
各种程序分析,如调用图、归纳变量、自然循环识别等。
llvm/lib/转换/
IR到IR程序转换,如攻击性死码消除、稀疏条件常数传播、内联、循环不变码运动、全局死码消除等。
llvm/lib/target/
描述代码生成的目标体系结构的文件。例如,llvm/lib/Target/X86保存X86机器描述。
llvm/lib/CodeGen/
代码生成器的主要部分:指令选择器、指令调度和寄存器分配。
llvm/lib/MC/
库在机器代码级别表示和处理代码。处理程序集和目标文件的发布。
llvm/lib/ExecutionEngine/
用于在解释和JIT编译的场景中在运行时直接执行位代码的库。
llvm/lib/support/
与llvm/include/ADT/和llvm/ininclude/Support/中的头文件相对应的源代码。
5. Llvm/bindings
包含LLVM编译器基础结构的绑定,以允许使用C或C++以外的语言编写的程序使用LLVM基础结构。LLVM项目为OCaml和Python提供了语言绑定。
6. llvm/projects
项目不是LLVM的严格组成部分,但与LLVM一起提供。这也是创建自己的基于LLVM的项目的目录,这些项目使用LLVM构建系统。
7. llvm/test
LLVM基础设施上的特性和回归测试,以及其他健全性检查。这些架构旨在快速运行,覆盖大量区域,而不是详尽无遗。
8. test-suite
LLVM的全面正确性、性能和基准测试套件。这是在一个单独的git存储库<https://github.com/llvm/llvm-test-suite>中提供的,因为它在各种许可证下包含大量的第三方代码。有关详细信息,请参阅测试帮助文档。
9. llvm/tools
由上面的库构建的可执行文件,构成了用户界面的主要部分。总是可以通过键入tool_name-help来获得工具的帮助。以下是对最重要的工具的简要介绍。
bugpoint
bugpoint用于调试优化过程或代码生成后端,方法是将给定的测试用例缩小到仍然会导致问题的过程和/或指令的最小数量,而且不管是崩溃还是编译错误。
llvm ar
归档器生成一个包含给定LLVM位代码文件的归档文件,可以选择使用索引以加快查找速度。
Llvm-as
汇编程序将人类可读的LLVM程序集转换为LLVM位代码。
Llvm-dis
反汇编程序将LLVM位代码转换为人类可读的LLVM汇编代码。
Llvm-link
llvm链接将多个llvm模块链接到一个程序中,这并不奇怪。
lli
lli是LLVM解释器,它可以直接执行LLVM位代码(尽管速度很慢)。对于支持它的体系结构(目前是x86、Sparc和PowerPC),默认情况下,lli将充当实时编译器(如果已经编译了该功能),并且执行代码的速度将比解释器快得多。
llc
llc是LLVM后端编译器,将LLVM位代码转换为本机代码汇编文件。
opt
opt读取LLVM位代码,应用一系列LLVM的转换(在命令行中指定),并输出生成的位代码。“opt-help”是获取LLVM中可用的程序转换列表的好方法。
opt还可以对输入LLVM位代码文件运行特定分析并打印结果。主要用于调试分析,或熟悉分析的作用。
10. llvm/utils
使用LLVM源代码的实用程序;有些是构建过程的一部分,因为它们是基础设施部分的代码生成器。
codegen-diff
codegen diff发现LLC生成的代码和LLI生成的代码之间的差异。如果正在调试其中一个,假设另一个生成正确的输出,那么这将非常有用。有关完整的用户手册,请运行“perldoc-codegen-diff”。
emacs/
用于LLVM程序集文件和TableGen描述文件的Emacs和XEmacs语法高亮显示。
getsrcs.sh
查找并输出所有未生成的源文件,如果希望跨目录进行大量开发,并且不想查找每个文件,这将非常有用。使用它的一种方法是从LLVM源代码树的顶部运行,例如:xemacs `utils/getsources.sh`。
llvmgrep
对LLVM中的每个源文件执行egrep -H -n,并向其传递llvmgrep命令行上提供的正则表达式。这是一种在源库中搜索特定正则表达式的有效方法。
tablegen/
包含用于从通用TableGen描述文件生成寄存器描述、指令集描述,甚至汇编程序的工具。
vim/
LLVM程序集文件和TableGen描述文件的vim语法高亮显示。
1.6 使用LLVM工具链的示例
本节给出了一个将LLVM与Clang前端一起使用的示例。
Clang示例
首先,创建一个简单的C文件,将其命名为“hello.C”:
#include <stdio.h>
int main() {
printf("hello world\n");
return 0;
}
接下来,将C文件编译为本机可执行文件:
% clang hello.c -o hello
Note
默认情况下,Clang与GCC的工作方式相同。标准的-S和-c参数照常工作(分别生成本机.S或.o文件)。
接下来,将C文件编译为LLVM位代码文件:
% clang -O3 -emit-llvm hello.c -c -o hello.bc
-emit-llvm选项可以与-S或-c选项一起使用,为代码分别输出llvm.ll或.bc文件。这可以对位代码文件使用标准LLVM工具。
以两种形式运行程序。要运行程序,请使用
% ./hello
和
% lli hello.bc
第二个示例显示了如何调用LLVM JIT与LLI。
使用llvm-dis实用程序查看llvm程序集代码:
% llvm-dis < hello.bc | less
使用LLC代码生成器将程序编译为本机程序集:
% llc hello.bc -o hello.s
将本机汇编语言文件汇编到程序中:
% /opt/SUNWspro/bin/cc -xarch=v9 hello.s -o hello.native # On Solaris
% gcc hello.s -o hello.native # On others
执行本机代码程序:
% ./hello.native
请注意,使用clang直接编译为本机代码(即,当-eemit-llvm选项不存在时)可以执行步骤6/7/8。
1.7 LLVM常用问题
如果在构建或使用LLVM时遇到问题,或者对LLVM有任何其他一般性问题,请参阅常见问题页面。
如果遇到内存和构建时间有限的问题,请尝试使用ninja而不是make进行构建。请考虑使用cmake配置以下选项:
1)-G Ninja
设置此选项,将允许使用Ninja而不是cmake进行构建。使用ninja构建可以显著缩短构建时间,尤其是增量构建,并提高内存使用率。
2)-DLLVM_USE_LINKER
将此选项设置为lld,将显著减少基于ELF的平台(如Linux)上LLVM可执行文件的链接时间。如果是第一次构建LLVM,并且lld不能作为二进制包提供,那么可能希望使用gold链接器作为GNU ld的更快替代方案。
3)-DCMAKE_BILD_TYPE
控制生成的优化级别和调试信息。此设置可能会影响RAM和磁盘的使用,有关详细信息,请参阅CMAKE_BUILD_TYPE。
4)-DLLVM_ENABLE_ASSERTIONS
此选项对于调试版本默认为ON,对于发布版本默认为OFF。如前一个选项中所述,使用Release构建类型并启用断言,可能是使用Debug构建类型的一个很好的替代方案。
5)-DLLVM_PARALEL_LINK_JOBS
将其设置为希望同时运行的作业数。这与make中使用的-j选项类似,但仅适用于链接作业。此选项只能与Ninja一起使用。可能希望使用数量非常少的作业,因为这将大大减少构建过程中使用的内存量。如果内存有限,可能希望将其设置为1。
6)-DLLVM_TARGETS_TO_BUILD
将其设置为希望生成的目标。可能希望将其设置为X86;但是,将在llvm项目/llvm/lib/Target目录中找到完整的目标列表。
7)-DLLVM_OPTIMIZED_TABLEGEN
将此选项设置为ON,以在构建过程中生成完全优化的表生成。这将显著改善构建时间。只有在使用“调试”生成类型时,这才有用。
8)-DLLVM_ENABLE_PROJECTS
将其设置为与要编译的项目(例如clang、lld等)相等。如果编译多个项目,请用分号分隔项目。如果遇到分号的问题,请尝试用单引号将其括起来。
9)-DLLVM_ENABLE_RUNTIMES
将其设置为要编译的运行时(例如libcxx、libcxxabi等)。如果编译多个运行时,请用分号分隔这些项。如果遇到分号的问题,请尝试用单引号将其括起来。
-如果不需要clang静态分析器,请将此选项设置为OFF。这应该会稍微改善构建时间。
10)-DLLVM_USE_SPLIT_DWARF
如果需要调试构建,请考虑将其设置为ON,因为这将减轻链接器上的内存压力。这将使链接速度更快,因为二进制文件将不包含任何调试信息;然而,这将以DWARF目标文件(扩展名.dwo)的形式生成调试信息。这只适用于使用ELF的主机平台,如Linux。
1.8 LLVM链接
本章只是介绍如何使用LLVM做一些简单的事情,还有很多更有趣和复杂的事情可以做,但这里没有记录。有关LLVM的更多信息,请查看:
1)LLVM主页
2)LLVM Doxygen Tree
3)启动使用LLVM的项目