1. 概述
欢迎来到 LLVM 项目!在开始之前,你首先需要知道一些基本的信息。
首先,LLVM 项目有多个组件。该项目的核心本身称为“LLVM”
。它包含处理中间表示(intermediate representation,IR)并将其转换为目标文件所需的所有工具、库和头文件,例如:汇编器(assembler)、反汇编器(disassembler)、字节码分析器(bitcode analyzer)和字节码优化器(bitcode optimizer)。它还包含基本的回归测试。
另一个重要的部分是 Clang FrontEnd。该组件将 C、C++、Objective C 和 Objective C++ 代码编译成 LLVM 字节码,然后将字节码编译成目标文件。
还有一些其他的组件:libc++ C++标准库、LLD链接器等等。
2. 快速入门(总结)
LLVM 入门文档可能已经过时。因此,Clang 入门页面可能也是一个很好的开始。
下面是获取和运行 LLVM 的简要介绍:
-
阅读文档。
-
阅读文档。
-
阅读文档。重要的事情说三次~~~
-
签出 LLVM(包括相关子项目如 Clang):
git clone https://github.com/llvm/llvm-project.git
或者,在windows上:
git clone --config core.autocrlf=false https://github.com/llvm/llvm-project.git
-
配置、构建 LLVM 和 Clang:
cd llvm-project mkdir build cd build cmake -G <generator> [options] ../llvm
一些常见的
generator
有:Ninja
—— 用于生成 Ninja 构建文件。大多数 llvm 开发人员使用Ninja。Unix Makefiles
—— 用于生成与 make 兼容的并行 Makefiles。Visual Studio
—— 用于生成 Visual Studio 项目和解决方案。Xcode
—— 用于生成 Xcode 项目。
一些常见的
options
有:-
-DLLVM_ENABLE_PROJECTS='...'
—— 您想要额外构建的 LLVM 子项目列表,使用分号分隔。可以包括以下的任何子项目:clang
、clang-tools-extra
、libcxx
、libcxxabi
、libunwind
、lldb
、compiler-rt
、lld
、polly
、或debuginfo-test
。例如,要构建
LLVM
、Clang
、libcxx
和libcxxabi
,使用:-DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi"
-
-DCMAKE_INSTALL_PREFIX=directory
——directory
指定您要安装 LLVM 工具和库的完整路径名(默认/usr/local
)。 -
-DCMAKE_BUILD_TYPE=type
——type
的有效选项有Debug
、Release
、RelWithDebInfo
和MinSizeRel
。默认是Debug
。 -
-DLLVM_ENABLE_ASSERTIONS=On
—— 启用断言检查进行编译(对于Debug
构建,默认为Yes;对于所有其他构建类型,默认为No)。
-
运行您选择的构建工具:
- 默认目标(即
ninja
或make
)将构建 LLVM 的所有组件。 check-all
目标(即ninja check-all
)将运行回归测试,以确保一切正常工作。CMake
将为每个工具和库生成构建目标,而大多数 LLVM 子项目将生成它们自己的check-<project>
目标。- 运行一个串行构建将很慢。确保您运行一个并行构建。这在
Ninja
中已经默认完成了;对于make
,使用make -j NNN
(取适当的值给NNN
,例如 CPU 的数量)。
- 默认目标(即
-
有关更多信息,请参见 CMake
-
如果您得到一个“内部编译器错误(internal compiler error,ICE)”或测试失败,请参见
第3.3节 宿主 C++ 工具链,包括编译器和标准库
。
有关配置和编译 LLVM 的详细信息,请参阅 第4章 开始使用LLVM
。了解源代码树的布局,请参阅 第5章 目录布局
。
3. 需求
在开始使用 LLVM 系统之前,请检查下面给出的需求。这可能会为您节省一些麻烦,因为您可以提前知道需要什么硬件和软件。
3.1 硬件
现已知,LLVM 可以在以下主机平台上工作:
操作系统 | 架构 | 编译器 |
---|---|---|
Linux | x86(1) | GCC, Clang |
Linux | amd64 | GCC, Clang |
Linux | ARM | GCC, Clang |
Linux | PowerPC | GCC, Clang |
Solaris | V9 (Ultrasparc) | GCC |
FreeBSD | x86(1) | GCC, Clang |
FreeBSD | amd64 | GCC, Clang |
NetBSD | x861 | GCC, Clang |
NetBSD | amd64 | GCC, Clang |
macOS(2) | PowerPC | GCC |
macOS | x86 | GCC, Clang |
Cygwin/Win32 | x86(1,3) | GCC |
Windows | x861 | Visual Studio |
Windows | x64 x86-64 | Visual Studio |
注意
(1) 支持奔腾处理器及以上的代码生成
(2) 只支持32位 ABI 的代码生成
(3) 要在基于 Win32 的系统上使用 LLVM 模块,可以使用-DBUILD_SHARED_LIBS=On
来配置 LLVM。
注意,Debug
构建需要大量的时间和磁盘空间。一个仅有 LLVM 的构建将需要大约 1-3GB 的空间;而完整的 LLVM 和 Clang 构建需要大约 15-20GB 的磁盘空间。确切的空间需求会因系统而异。(它之所以这么大,是因为包含了所有的调试信息以及库被静态地链接到多个工具中)。
如果空间有限,则只能构建选定的工具或选定的目标。发行版构建所需的空间要小得多。
LLVM 套件可能可以在其他平台上编译,但不能保证这样做。如果编译成功,LLVM 实用程序应该能够汇编、反汇编、分析和优化 LLVM 字节码。代码生成也应该能够工作,尽管生成的本地代码可能无法在您的平台上工作。
3.2 软件
编译 LLVM 需要安装几个软件包。下表列出了这些所需的包。Package
列是 LLVM 所依赖的软件包的常用名称。Version
列提供包的“已知工作”版本。Notes列描述了LLVM如何使用这个包,并提供了其他细节。
Package | Version | Notes |
---|---|---|
GNU Make | 3.79, 3.79.1 | Makefile/build processor |
GCC | >=5.1.0 | C/C++ compiler(1) |
python | >=2.7 | Automated test suite(2) |
zlib | >=1.2.3.4 | Compression library(3) |
注意
(1) 只需要 C 和 C++ 语言,不需要为 LLVM 构建其他语言。有关特定版本信息,请参见下面。
(2) 只有希望在llvm/test
目录中运行自动化测试套件时才需要。
(3) 可选,向选定的 LLVM 工具添加压缩/解压
功能。
此外,您的编译主机应该具有通常过多的 Unix 实用程序。具体地说:
- ar —— 归档库构建器
- bzip2 —— 用于分发版生成的bzip2命令
- bunzip2 —— 用于分发版检查的bunzip2命令
- chmod —— 更改文件的权限
- cat —— 输出连接实用程序
- cp —— 复制文件
- date —— 打印当前日期/时间
- echo —— 打印到标准输出
- egrep —— 扩展正则表达式搜索工具
- find —— 在文件系统中查找文件/目录
- grep —— 正则表达式搜索工具
- gzip —— 用于分发版生成的gzip命令
- gunzip —— 用于分发版检查的gunzip命令
- install —— 安装目录/文件
- mkdir —— 创建一个目录
- mv —— 移动(重命名)文件
- ranlib —— 用于存档库的符号表生成器
- rm —— remove (delete)文件和目录
- sed —— 用于转换输出的sed流编辑器
- sh —— 用于生成构建脚本 - Bourne shell
- tar —— 磁带存档,用于分发版生成
- test —— 在文件系统中测试东西
- unzip —— 用于分发版检查的unzip命令
- zip —— 用于分发版生成的zip命令
3.3 宿主 C++ 工具链,包括编译器和标准库
LLVM 对宿主 C++ 编译器的要求非常高,因此很容易暴露编译器中的bug。我们还试图密切关注 C++ 语言和库的改进和开发。因此,为了构建 LLVM,我们需要一个现代的宿主 C++ 工具链,包括编译器和标准库。
LLVM 是使用编码标准中记录的 C++ 子集编写的。为了加强这一语言版本,我们检查了最流行的宿主工具链,以指定我们的构建系统支持的最低版本:
- Clang 3.5
- Apple Clang 6.0
- GCC 5.1
- Visual Studio 2017
当我们转换到上面列出的新编译器版本时,下面的版本当前是软错误。目前已知 LLVM 代码库可以使用以下编译器正确编译,不过这一点在不久的将来会发生变化:
- Clang 3.1
- Apple Clang 3.1
- GCC 4.8
- Visual Studio 2015 (Update 3)
任何比这些工具链更老的工具都可能可以工作,但是需要强制构建系统使用一个特殊的选项,而且实际上并不是一个支持的宿主平台。还要注意,这些编译器的旧版本经常崩溃或错误编译LLVM。
对于不太常用的宿主工具链,如 ICC
或 xlC
,请注意,可能需要一个非常新的版本来支持 LLVM 中使用的所有 C++ 特性。
我们跟踪某些已知的软件版本,这些版本在作为宿主工具链的一部分使用时可能会失败。这些甚至有时包括链接器。
- GNU ld 2.16.X
一些 2.16.X 版本的ld
链接器将生成非常长的警告消息,抱怨某些“.gnu.linkonce.t.*”
符号在被丢弃的部分中定义。您可以安全地忽略这些消息,因为它们是错误的,而链接是正确的。使用 ld 2.17 时这些消息会消失。 - GNU binutils 2.17
Binutils 2.17 包含一个在构建 LLVM 时导致大量链接时间(分钟而不是秒)的 bug。我们建议升级到更新的版本(2.17.50.0.4或更高版本)。 - GNU Binutils 2.19.1 Gold
这个版本的 Gold 包含一个 bug,它在使用位置无关代码构建 LLVM 时导致间歇性故障。症状是关于循环依赖关系的错误。我们建议升级到更新版本的 Gold。
3.3.1 获得一个现代的宿主 C++ 工具链
本节主要适用于 Linux 和较老的 BSDs。在 macOS 上,您应该有一个足够现代的 Xcode,否则您可能需要升级,直到您这样做。Windows没有“系统编译器”,因此必须安装 Visual Studio 2015 或 mingw64 的最新版本。FreeBSD 10.0 和更新的版本有一个现代化的 Clang 作为系统编译器。
然而,一些 Linux 发行版和一些其他的或更老的 BSDs 有时有非常老的 GCC 版本。这些步骤试图帮助您升级编译器,即使是在这样的系统上。但是,如果可能的话,我们鼓励您使用发行版的最新版本,并使用符合这些要求的现代系统编译器。注意,安装 Clang 和 libc++ 的早期版本作为宿主编译器是很有诱惑力的,但是 libc++ 直到最近才经过很好的测试或安装以在 Linux 上构建。因此,本指南建议只使用 libstdc++
和现代 GCC
作为引导程序中的初始主机,然后使用 Clang(可能还有 libc++)。
第一步是安装一个最新的 GCC
工具链。用户在版本要求上遇到困难的最常见的发行版是 Ubuntu Precise,12.04 LTS。对于这个发行版,一个简单的选择是安装工具链测试 PPA 并使用它安装现代 GCC。在 ask ubuntu stack exchange 和一个 github gist 上有一个关于这个的很好的讨论。然而,并不是所有用户都可以使用 PPAs,而且还有许多其他发行版,所以从源代码构建和安装 GCC 可能是必要的(或者仅仅是有用的,如果您在这里进行编译器开发的话)。现在做起来也很容易。
安装 GCC 5.1.0 的简单步骤:
% gcc_version=5.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++ 的新版本不在系统库搜索路径上,所以需要传递额外的链接器 flags,以便在链接时(-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: version ’GLIBCXX_3.4.20' not found
的消息。这意味着您需要调整 -rpath
链接器标志。
构建 Clang 时,需要让它访问现代 C++ 标准库,以便在引导程序中使用它作为新主机的一部分。有两种简单的方法可以做到这一点,要么与 Clang 一起构建(并安装) libc++,然后使用 -stdlib=libc++
编译和链接标志一起使用它,要么将 Clang 安装到与 GCC 相同的前缀中(上面的$HOME/toolchains
)。Clang 将在它自己的 libstdc++ prefix中查找,如果找到就使用它。您还可以为 Clang 添加一个显式前缀,以便使用 --gcc-toolchain=/opt/my/gcc/prefix
标志查找 GCC 工具链,在使用刚刚构建的 Clang 引导时,将其传递给编译命令和链接命令。
4. 开始使用 LLVM
本指南的其余部分旨在帮助您启动和运行 LLVM,并提供有关 LLVM 环境的一些基本信息。
本指南后面的部分描述了 LLVM 源代码树的总体布局,一个使用 LLVM 工具链的简单示例,并且第8章 Link
提供了有关 LLVM 的更多信息或通过电子邮件获得帮助。
4.1 术语和符号
在本手册中,以下名称用于表示特定于本地系统和工作环境的路径。这些不是您需要设置的环境变量,而是下面这个文档其余部分中使用的字符串。在下面的任何示例中,只需用本地系统上适当的路径名替换这些名称。所有这些路径都是绝对的:
SRC_ROOT
:这是 LLVM 源代码树的顶层目录。OBJ_ROOT
:这是 LLVM 目标文件树的顶层目录(即对象文件和编译程序将放在其中的树。它可能与 SRC_ROOT 相同)。
4.2 解压 LLVM 存档
如果您拥有 LLVM 发行版,则需要在开始编译它之前将其解压。LLVM 作为许多不同的子项目分发。每一个都有自己的下载,这是一个TAR存档文件,用 gzip 程序压缩。
文件如下,x.y
标记版本号:
llvm-x.y.tar.gz
:LLVM 库和工具的源代码发布。cfe-x.y.tar.gz
:Clang 前端的源代码发布。
4.3 从 Git 签出 LLVM
您还可以从 Git 签出 LLVM 的源代码。虽然 LLVM 项目的官方源代码存储库是 Subversion,但是我们正在迁移到 git 的过程中。我们目前建议所有开发人员在日常开发中使用 Git。
注意
在我们正确调整 .gitattribute 设置之后,将来不应该需要传递--config core.autocrlf=false
;但是在撰写本文时,Windows 用户需要传递--config core.autocrlf=false
。
简单地运行:
% git clone https://github.com/llvm/llvm-project.git
或在Windows上:
% git clone --config core.autocrlf=false https://github.com/llvm/llvm-project.git
这将在当前目录中创建一个 ‘llvm-project’
目录,并使用 LLVM 和所有相关子项目的所有源代码、测试目录和文档文件的本地副本完全填充该目录。注意,与 tarball
不同,tarball
将每个子项目包含在单独的文件中,git 存储库将所有项目都包含在一起。
如果您想获得一个特定的版本(与最近的版本相反),您可以在克隆存储库之后签出一个tag
。例如,在上面命令创建的 llvm-project
目录中,git checkout llvmorg-6.0.1
。使用 git tag -l
列出所有这些。
4.3.1 发送补丁
请阅读开发人员策略。
我们目前不接受 github pull 请求,所以您需要通过电子邮件向 llvm-commit 发送补丁,或者最好通过 Phabricator 发送补丁。
您通常希望确保您的分支有一个单独的提交,与您希望发送的评审相对应,与上游 origin/master 分支保持最新,并且不包含合并。有了这些之后,可以使用 git show
或 git format-patch
来输出差异,并将其附加到一个 Phabricator 评审(或电子邮件消息)中。
然而,使用 “Arcanist”
工具通常更容易。在安装 arcanist 后,你可以上传最新的提交使用:
% arc diff HEAD~1
另外,在发送补丁进行检查之前,请确保它的格式正确。为此,我们使用了 clang-format
,它通过 git-clang-format
脚本集成了 git。在某些系统上,它可能已经安装(或者可以通过包管理器安装)。如果是的话,你可以简单地运行它 —— 下面的命令将只格式化最近提交时更改的代码:
% git clang-format HEAD~1
注意,这将修改文件,但不会提交它们 —— 您可能想要运行:
% git commit --amend -a
以便让所有挂起的更改更新最后一次提交。
注意
如果您的系统上还没有安装clang-format
或git clang-format
,那么clang-format
二进制文件将与clang
一起构建,git
集成可以从clang/tools/clang-format/git-clang-format
运行。
4.3.2 让开发人员从 Git 提交更改
llvm/utils/git-svn/git-llvm
中提供了一个帮助脚本。将其添加到路径后,可以使用 git llvm push
向上推送提交的更改。虽然这创建了一个 Subversion
签出并将其打补丁,但它不需要您与它进行交互。
% export PATH=$PATH:$TOP_LEVEL_DIR/llvm-project/llvm/utils/git-svn/
% git llvm push
在 push subversion 几分钟后,svn 提交将被转换回 Git 提交,并进入正式的 Git 存储库。此时,git pull
应该在提交更改时返回更改。
您可能希望 git pull --rebase
将正式的git提交下载回存储库。每个提交的 SVN 修订号可以在提交消息的末尾找到,例如 llvm-svn: 350914
。
您还可能发现 -n
标志很有用,比如 git llvm push -n
。这将运行 committing _without_ actually
执行提交的所有步骤,并告诉您它将执行什么操作。如果你不确定是否会发生正确的事情,这是很有用的。
4.3.3 使用 Git 时还原更改
如果您正在使用 Git,并且需要恢复补丁,那么 Git 需要提供 commit hash,而不是 svn 修订。为了使事情变得更简单,您可以使用 `git llvm revert`` 来恢复 SVN 修订或 Git hash。
此外,您可以先使用 git llvm revert -n
运行来打印将要运行哪些 git 命令,而不需要做任何操作。
运行 git llvm revert
只会还原本地存储库中的内容。要将恢复推到上游,您仍然需要像前面描述的那样运行 git llvm push
。
% git llvm revert rNNNNNN # Revert by SVN id
% git llvm revert abcdef123456 # Revert by Git commit hash
% git llvm revert -n rNNNNNN # Print the commands without doing anything
4.3.4 通过 SVN 签出(不推荐)
在完全迁移到 Git 之前,您还可以从官方 Subversion 存储库中获得代码的新副本。
cd where-you-want-llvm-to-live
- 只读:
svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm
- 读写:
svn co https://user@llvm.org/svn/llvm-project/llvm/trunk llvm
这将在当前目录中创建一个 ‘llvm’
目录,并使用 LLVM 源代码、Makefiles、测试目录和文档文件的本地副本完全填充它。
如果您想获得一个特定的版本(与最近的版本相反),您可以从 “tags”
目录(而不是 “trunk”
目录)查看它。以下版本位于 “tags”
目录的以下子目录中:
- Release 3.5.0 及以后版本:RELEASE_350/final 等等
- Release 2.9 到 3.4:RELEASE_29/final 等等
- Release 1.1 到 2.8:RELEASE_11 等等
- Release 1.0: RELEASE_1
4.4 本地 LLVM 配置
签出存储库后,必须在构建前配置 LLVM 套件源代码。这个过程使用 CMake。取消到常规 configure
脚本的链接,CMake 以您请求的任何格式以及各种*.inc文件、和llvm/include/Config/config.h
生成构建文件。
变量在命令行上使用 -D<variable name>=<value>
传递给 cmake
。以下变量是开发 LLVM 的人员使用的一些常见选项。
变量 | 目的 |
---|---|
CMAKE_C_COMPILER | 告诉 cmake 使用哪个 C 编译器。默认情况下,这是 /usr/bin/cc 。 |
CMAKE_CXX_COMPILER | 告诉 cmake 使用哪个 C++ 编译器。默认情况下,这是 /usr/bin/c++ 。 |
CMAKE_BUILD_TYPE | 告诉 cmake 您要为哪种类型的构建生成文件。有效的选项有 Debug、Release、RelWithDebInfo 和 MinSizeRel。默认是 Debug。 |
CMAKE_INSTALL_PREFIX | 在运行构建文件的 install 操作时指定目标的 install 目录。 |
LLVM_TARGETS_TO_BUILD | 构建一个分号分隔的列表,控制将构建哪些目标并将其链接到 llvm。默认列表定义为LLVM_ALL_TARGETS,可以将其设置为包含树外目标。默认值包括:AArch64、AMDGPU、ARM、BPF、Hexagon、Mips、MSP430、NVPTX、PowerPC、Sparc、SystemZ、X86、XCore。 |
LLVM_ENABLE_DOXYGEN | 从源代码构建基于doxygen的文档,这在默认情况下是禁用的,因为它很慢,并且生成了很多输出。 |
LLVM_ENABLE_PROJECTS | 一个分号分隔的列表,选择要额外构建的其他LLVM子项目。(只有在使用并行项目布局时才有效,例如通过git)。默认列表为空。可以包括:clang、libcxx、libcxxabi、libunwind、lldb、编译器-rt、lld、polly或debugin -test。 |
LLVM_ENABLE_SPHINX | 从源代码构建基于sphinx的文档。这在默认情况下是禁用的,因为它很慢,而且会生成很多输出。建议使用Sphinx 1.5或更高版本。 |
LLVM_BUILD_LLVM_DYLIB | 生成libLLVM.so 。这个库包含一组可以用LLVM_DYLIB_COMPONENTS重写的默认LLVM组件。默认值包含大部分LLVM,在tools/ LLVM -shlib/CMakelists.txt中定义。 |
LLVM_OPTIMIZED_TABLEGEN | 构建一个在LLVM构建期间使用的发行版tablegen。这可以显著加快调试构建。 |
要配置LLVM,请遵循以下步骤:
- 将目录更改为目标文件根目录:
% cd OBJ_ROOT
- 运行cmake:
% cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=/install/path [other options] SRC_ROOT
4.5 编译 LLVM 套件源代码
与 autotools 不同,使用 CMake 时您的构建类型是在 configuration
中定义的。如果您想更改构建类型,可以通过以下调用重新运行 cmake:
% cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=type SRC_ROOT
在运行之间,CMake 保存所有选项的值集。CMake 定义了以下构建类型:
Debug
:
这个构建是默认的。构建系统将不经过优化地编译工具和库,并启用调试信息和断言。Release
:
对于这些构建,构建系统将编译启用了优化的工具和库,而不会生成调试信息。cmake默认优化级别为-O3。这可以通过在CMake命令行上设置CMAKE_CXX_FLAGS_RELEASE变量来配置。RelWithDebInfo
:
这些构建在调试时非常有用。它们生成带有调试信息的优化二进制文件。默认优化级别为-O2。这可以通过在CMake命令行上设置CMAKE_CXX_FLAGS_RELWITHDEBINFO变量来配置。
一旦配置好LLVM,就可以通过进入 OBJ_ROOT
目录并发出以下命令来构建它:
% make
如果构建失败,请检查这里,看看您是否使用了已知不编译 LLVM 的 GCC 版本。
如果您的计算机中有多个处理器,您可能希望使用 GNU Make 提供的一些并行构建选项。例如,您可以使用以下命令:
% make -j2
在使用 LLVM 源代码时,有几个特别的目标非常有用:
make clean
:
删除构建生成的所有文件。这包括目标文件、生成的 C/C++ 文件、库和可执行文件。make install
:
在$PREFIX
下的一个层次结构中安装 LLVM 头文件、库、工具和文档,$PREFIX
由CMAKE_INSTALL_PREFIX
指定,默认值为/usr/local
。make docs-llvm-html
:
如果配置为-DLLVM_ENABLE_SPHINX=On
,这将在OBJ_ROOT/docs/html
中生成一个目录,其中包含 html 格式的文档。
4.6 交叉编译 LLVM
可以交叉编译 LLVM 本身。也就是说,您可以创建 LLVM 可执行程序和库,并将其托管在不同于构建它们的平台(一个Canadian交叉构建)上。要生成用于交叉编译的构建文件,CMake
提供了一个变量 CMAKE_TOOLCHAIN_FILE
,它可以定义编译器标志和 CMake 测试操作期间使用的变量。
这样一个构建的结果是可执行文件,这些文件不能在构建主机上运行,但可以在目标上执行。例如,下面的 CMake 调用可以生成针对 iOS 的构建文件。这将工作在 macOS 与最新的 Xcode:
% 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 构建时需要传递一些额外的 flags。
查看如何使用 Clang/LLVM 交叉编译 Clang/LLVM 和关于如何交叉编译的Clang文档,了解有关交叉编译的更多信息。
4.7 LLVM 对象文件的位置
LLVM 构建系统能够在多个 LLVM 构建中共享一个 LLVM 源代码树。因此,可以使用相同的源代码树为几个不同的平台或配置构建 LLVM。
- 将目录更改为 LLVM 目标文件应该位于的位置:
% cd OBJ_ROOT
- 运行 cmake:
% cmake -G "Unix Makefiles" 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
4.8 可选配置项
如果您运行在支持 binfmt_misc 模块的 Linux 系统上,并且您在系统上具有根访问权限,那么您可以将您的系统设置为直接执行 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'
5. 目录布局
关于 LLVM 源代码库的一个有用信息来源是 LLVM doxygen 文档,可以从 http://llvm.org/doxygen/ 获得。下面是对代码布局的简要介绍:
5.1 llvm/examples
使用 LLVM IR 和 JIT 的简单示例。
5.2 llvm/include
从 LLVM 库导出的公共头文件。三个主要子目录:
llvm/include/llvm
针对 LLVM 不同部分的所有特定于 LLVM 的头文件和子目录:Analysis
、CodeGen
、Target
、Transforms
等等。llvm/include/llvm/Support
与 LLVM 一起提供的通用支持库,但不一定是特定于 LLVM 的。例如,一些 C++ STL 实用程序和一个处理库的命令行选项存储在这里的头文件。llvm/include/llvm/Config
由 cmake 配置的头文件。它们封装“标准” UNIX 和 C 头文件。源代码可以包含这些头文件,这些头文件自动处理 cmake 生成的条件#include
。
5.3 llvm/lib
大多数源文件都在这里。通过将代码放入库中,LLVM 使得在工具之间共享代码变得很容易。
llvm/lib/IR/
核心 LLVM 源文件,实现核心类,如Instruction
和BasicBlock
。llvm/lib/AsmParser/
LLVM 汇编语言解析器库的源代码。llvm/lib/Bitcode/
用于读取和写入字节码的代码。llvm/lib/Analysis/
各种程序分析,如调用图、归纳变量、自然循环识别等。llvm/lib/Transforms/
IR-to-IR 程序转换,如主动死代码消除、稀疏条件常数传播、内联、循环不变代码运动、死全局消除等。llvm/lib/Target/
描述用于代码生成的目标体系结构的文件。例如,llvm/lib/Target/X86
包含X86机器描述。llvm/lib/CodeGen/
代码生成器的主要部分:指令选择器、指令调度和寄存器分配。llvm/lib/MC/
(FIXME: T.B.D.) ….?llvm/lib/ExecutionEngine/
用于在解释和 JIT 编译的场景中在运行时直接执行字节码的库。llvm/lib/Support/
对应于llvm/include/ADT/
和llvm/include/Support/
中的头文件的源代码。
5.4 llvm/projects
Projects 不是严格意义上的 LLVM 的一部分,而是随 LLVM 一起发布的。这也是创建您自己的基于 LLVM 的项目的目录,这些项目利用 LLVM 构建系统。
5.5 llvm/test
特性和回归测试以及对 LLVM 基础设施的其他完整性检查。它们的目的是快速运行并覆盖很多领域,而不是面面俱到。
5.6 test-suite
为 LLVM 提供全面的正确性、性能和基准测试套件。这来自于一个单独的 git 存储库https://github.com/llvm/llvm-test-suite,因为它包含了大量第三方代码,使用各种许可证。有关详细信息,请参阅测试指南文档。
5.7 llvm/tools
由上述库构建的可执行程序,构成用户接口的主要部分。您总是可以通过键入 tool_name -help
来获得工具的帮助。下面是对最重要的工具的简要介绍。更详细的信息在命令指南中。
bugpoint
bugpoint 用于调试优化传递或代码生成后端,方法是将给定的测试用例缩小到仍然会导致问题(无论是崩溃还是错误编译)的最小传递和/或指令数量。有关使用 bugpoint 的更多信息,请参见 HowToSubmitABug.html。llvm-ar
归档器生成一个包含给定 LLVM 字节码文件的归档文件,可以选择使用索引来加快查找速度。llvm-as
汇编程序将人类可读的 LLVM assembly 转换为LLVM字节码。llvm-dis
反汇编程序将 LLVM 字节码转换为人类可读的 LLVM assembly。llvm-link
毫无疑问,llvm-link 将多个 LLVM 模块链接到一个程序中。lli
lli
是 LLVM 解释器,它可以直接执行LLVM字节码(虽然很慢……)。对于支持它的架构(目前是x86、Sparc和PowerPC),默认情况下,lli
将作为即时编译器(如果功能是在其中编译的)运行,并且执行代码的速度将比解释器快得多。llc
llc
是 LLVM 后端编译器,它将 LLVM 字节码转换为本机代码汇编文件。opt
opt
读取 LLVM 字节码,将一系列 LLVM 应用于 LLVM 转换(在命令行中指定),并输出结果字节码。“opt -help”
是获取 LLVM 中可用的程序转换列表的好方法。
opt 还可以对输入 LLVM 字节码文件运行特定的分析并打印结果。主要用于调试分析,或者熟悉分析的功能。
5.8 llvm/utils
使用 LLVM 源代码的实用程;有些是构建过程的一部分,因为它们是基础设施部分的代码生成器。
codegen-diff
codegen-diff 发现 LLC 生成的代码和 LLI 生成的代码之间的差异。如果您正在调试其中一个,假设另一个生成正确的输出,那么这将非常有用。要获得完整的用户手册,请运行 ``perldoc codegen-diff’`。emacs/
Emacs
和XEmacs
语法高亮显示 LLVM assembly 文件和 TableGen 描述文件。有关使用它们的信息,请参阅 README 文件。getsrcs.sh
查找和输出所有非生成的源文件,如果希望跨目录进行大量开发而不想查找每个文件,那么这非常有用。使用它的一种方法是运行,例如:xemacs 'utils/getsources.sh'
,从 LLVM 源代码树的顶部。llvmgrep
对 LLVM 中的每个源文件执行一个egrep -H -n
,并将 llvmgrep 命令行上提供的正则表达式传递给它。这是搜索特定正则表达式的源库的一种有效方法。- `TableGen
包含用于从公共 TableGen 描述文件生成寄存器描述、指令集描述甚至汇编器的工具。 vim/
vim 语法高亮显示 LLVM assembly 文件和 TableGen 描述文件。请参阅 README 文件了解如何使用它们。
6. 一个使用 LLVM 工具链的例子
本节给出一个使用带 Clang 前端的 LLVM 的示例。
6.1 使用 clang 的例子
-
首先,创建一个简单的 C 文件,命名为
'hello.c'
:#include <stdio.h> int main() { printf("hello world\n"); return 0; }
-
接下来,将 C 文件编译为原生可执行文件:
% clang hello.c -o hello
注意
默认情况下,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 assembly 代码:% 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 直接编译本机代码(即当-emit-llvm选项不存在时)将执行6/7/8步骤。
7. 常见问题
如果您在构建或使用 LLVM 时遇到问题,或者对 LLVM 有任何其他一般问题,请参阅常见问题页面。
8. 链接
本文档只是介绍如何使用 LLVM 来做一些简单的事情……还有许多有趣而复杂的事情您可以做,但这里没有文档说明(但是如果您想编写一些东西,我们很乐意接受一个补丁!)有关 LLVM 的更多信息,请查看: