LLVM 系统入门

概述

欢迎来到 LLVM 项目!

LLVM 项目包含多个组件。项目的核心本身称为“LLVM”。它包含处理中间表示并将其转换为目标文件所需的所有工具、库和头文件。工具包括汇编器、反汇编器、比特码分析器和比特码优化器。它还包含基本的回归测试。

类似 C 的语言使用 Clang 前端。此组件将 C、C++、Objective C 和 Objective C++ 代码编译成 LLVM 比特码——然后使用 LLVM 转换为目标文件。

其他组件包括:libc++ C++ 标准库LLD 链接器 等。

获取源代码并构建 LLVM

  1. 检出 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 项目的最新版本,请使用

      git clone --depth 1 https://github.com/llvm/llvm-project.git

    • 您可能对存储库中的用户分支不感兴趣(用于堆叠拉取请求和回退),您可以使用此配置从您的 git fetch(或 git pull)中过滤掉它们

git config --add remote.origin.fetch '^refs/heads/users/*'
git config --add remote.origin.fetch '^refs/heads/revert-*'
  1. 配置并构建 LLVM 和 Clang

    • cd llvm-project

    • cmake -S llvm -B build -G <generator> [options]

      一些常见的构建系统生成器是

      • Ninja — 用于生成 Ninja 构建文件。大多数 LLVM 开发人员使用 Ninja。

      • Unix Makefiles — 用于生成与 make 兼容的并行 makefile。

      • Visual Studio — 用于生成 Visual Studio 项目和解决方案。

      • Xcode — 用于生成 Xcode 项目。

      • 请参阅 CMake 文档 以获取更全面的列表。

      一些常见选项

      • -DLLVM_ENABLE_PROJECTS='...' — 您希望额外构建的 LLVM 子项目的用分号分隔的列表。可以包含以下任何一个:clang、clang-tools-extra、lldb、lld、polly 或 cross-project-tests。

        例如,要构建 LLVM、Clang 和 LLD,请使用 -DLLVM_ENABLE_PROJECTS="clang;lld"

      • -DCMAKE_INSTALL_PREFIX=directory — 为 directory 指定您希望将 LLVM 工具和库安装到的完整路径名(默认 /usr/local)。

      • -DCMAKE_BUILD_TYPE=type — 控制构建的优化级别和调试信息。type 的有效选项为 DebugReleaseRelWithDebInfoMinSizeRel。有关更详细的信息,请参阅 CMAKE_BUILD_TYPE

      • -DLLVM_ENABLE_ASSERTIONS=ON — 启用断言检查进行编译(对于 Debug 构建,默认为 ON,对于所有其他构建类型,默认为 OFF)。

      • -DLLVM_USE_LINKER=lld — 使用 lld 链接器 进行链接,假设它已安装在您的系统上。如果默认链接器速度很慢,这可以大大加快链接时间。

      • -DLLVM_PARALLEL_{COMPILE,LINK,TABLEGEN}_JOBS=N — 限制同时运行的编译/链接/tablegen 作业的数量。这对于链接尤其重要,因为链接可能会使用大量内存。如果您在构建 LLVM 时遇到内存问题,请尝试设置此值以限制同时运行的编译/链接/tablegen 作业的最大数量。

    • cmake --build build [--target <target>] 或直接使用上面指定的构建系统。

      • 默认目标(即 cmake --build buildmake -C build)将构建所有 LLVM。

      • check-all 目标(即 ninja check-all)将运行回归测试以确保一切正常。

      • CMake 将为每个工具和库生成构建目标,并且大多数 LLVM 子项目都会生成自己的 check-<project> 目标。

      • 运行串行构建将很慢。要提高速度,请尝试运行并行构建。这在 Ninja 中默认启用;对于 make,请使用选项 -j NN,其中 NN 是并行作业的数量,例如可用 CPU 的数量。

    • 一个仅构建 LLVM 而不构建任何其他子项目的简单 CMake 和构建/测试调用

      cmake -S llvm -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug

      ninja -C build check-llvm

      这将设置一个带有调试信息的 LLVM 构建,然后编译 LLVM 并运行 LLVM 测试。

    • 有关 CMake 选项的更详细信息,请参阅 CMake

    • 如果您遇到构建或测试失败,请参阅下面

请参阅 LLVM 入门 部分,了解有关配置和编译 LLVM 的详细信息。转到 目录布局 以了解源代码树的布局。

独立构建

独立构建允许您针对系统上已存在的 clang 或 llvm 库的预构建版本构建子项目。

您可以使用 LLVM 项目标准签出的源代码(如上所述)进行独立构建,但您也可以从 稀疏签出 或从 发行版 页面上的 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_ROOTLLVM_EXTERNAL_LIT 选项都是必需的。每个子项目的其他必需选项可以在下表中找到。

对于下表中列出的子项目,支持 check-$subprojinstall 构建目标。

子项目

必需的子目录

必需的 CMake 选项

llvm

llvm, cmake, third-party

LLVM_INSTALL_UTILS=ON

clang

clang, cmake

CLANG_INCLUDE_TESTS=ON(仅在 check-clang 中需要)

lld

lld, cmake

构建独立 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

需求

在开始使用 LLVM 系统之前,请查看以下给出的需求。提前了解您需要的硬件和软件,这可以避免一些麻烦。

硬件

LLVM 已知可以在以下主机平台上工作

操作系统

架构

编译器

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. 支持 Pentium 及更高版本的处理器代码生成

  2. 仅支持 32 位 ABI 的代码生成

  3. 要在基于 Win32 的系统上使用 LLVM 模块,您可以使用 -DBUILD_SHARED_LIBS=On 配置 LLVM。

请注意,调试版本需要大量的时间和磁盘空间。仅构建 LLVM 需要大约 1-3 GB 的空间。完整构建 LLVM 和 Clang 需要大约 15-20 GB 的磁盘空间。确切的空间需求因系统而异。(空间如此之大是因为包含了所有调试信息,并且库被静态链接到多个工具中)。

如果您磁盘空间有限,可以只构建选定的工具或只构建选定的目标。发布版本需要的空间要少得多。

LLVM 套件可能在其他平台上编译,但不能保证。如果编译成功,LLVM 实用程序应该能够组装、反汇编、分析和优化 LLVM 位码。代码生成也应该可以工作,尽管生成的本地代码可能无法在您的平台上运行。

软件

编译 LLVM 需要安装几个软件包。下表列出了这些必需的软件包。Package 列是 LLVM 依赖的软件包的常用名称。Version 列提供了“已知可工作”的软件包版本。Notes 列描述了 LLVM 如何使用该软件包并提供了其他详细信息。

软件包

版本

注释

CMake

>=3.20.0

Makefile/工作区生成器

python

>=3.8

自动化测试套件1

zlib

>=1.2.3.4

压缩库2

GNU Make

3.79, 3.79.1

Makefile/构建处理器3

PyYAML

>=5.1

头文件生成器4

注意

  1. 仅当您希望在 llvm/test 目录中运行自动化测试套件,或计划利用任何 Python 库、实用程序或绑定时才需要。

  2. 可选,为选定的 LLVM 工具添加压缩/解压缩功能。

  3. 可选,您可以使用 CMake 支持的任何其他构建工具。

  4. 仅在使用新 Headergen 构建 libc 时需要。主要由 libc 使用。

此外,您的编译主机预计拥有大量的 Unix 实用程序。具体来说

  • ar — 归档库构建器

  • bzip2 — 用于发行版生成的 bzip2 命令

  • bunzip2 — 用于发行版检查的 bunzip2 命令

  • chmod — 更改文件的权限

  • cat — 输出连接实用程序

  • cp — 复制文件

  • date — 打印当前日期/时间

  • echo — 打印到标准输出

  • egrep — 扩展正则表达式搜索实用程序

  • find — 在文件系统中查找文件/目录

  • grep — 正则表达式搜索实用程序

  • gzip — 用于发行版生成的 gzip 命令

  • gunzip — 用于发行版检查的 gunzip 命令

  • install — 安装目录/文件

  • mkdir — 创建目录

  • mv — 移动(重命名)文件

  • ranlib — 用于归档库的符号表构建器

  • rm — 删除(删除)文件和目录

  • sed — 用于转换输出的流编辑器

  • sh — 用于 make 构建脚本的 Bourne shell

  • tar — 用于发行版生成的磁带归档

  • test — 测试文件系统中的内容

  • unzip — 用于发行版检查的 unzip 命令

  • zip — 用于发行版生成的 zip 命令

主机 C++ 工具链(编译器和标准库)

LLVM 对主机 C++ 编译器要求很高,因此往往会暴露编译器中的错误。我们还尝试紧密跟踪 C++ 语言和库的改进和发展。因此,为了构建 LLVM,我们需要一个现代的主机 C++ 工具链,包括编译器和标准库。

LLVM 使用编码标准中记录的 C++ 子集编写。为了强制执行此语言版本,我们在构建系统中检查最流行的主机工具链的特定最低版本

  • Clang 5.0

  • Apple Clang 10.0

  • GCC 7.4

  • Visual Studio 2019 16.7

比这些工具链更旧的版本可能有效,但需要使用特殊选项强制构建系统,并且不是真正支持的主机平台。另请注意,这些编译器的旧版本经常崩溃或编译 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 时会导致链接时间过长(几分钟而不是几秒)。我们建议升级到较新的版本(2.17.50.0.4 或更高版本)。

GNU Binutils 2.19.1 Gold:此版本的 Gold 包含一个错误,在使用位置无关代码构建 LLVM 时会导致间歇性故障。症状是关于循环依赖关系的错误。我们建议升级到较新的 Gold 版本。

获取现代主机 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 的简单步骤

% gcc_version=7.4.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: version `GLIBCXX_3.4.20' not found。这意味着您需要调整 -rpath 链接器标志。

此方法会将绝对路径添加到所有可执行文件的 rpath 中。这对于本地开发来说很好。如果您想分发构建的二进制文件以便它们可以在旧系统上运行,请将 libstdc++.so.6 复制到 lib/ 目录中。所有 LLVM 的发布二进制文件都具有指向 $ORIGIN/../lib 的 rpath,因此它们将在那里找到 libstdc++.so.6。非分发二进制文件没有设置 rpath,将找不到 libstdc++.so.6。将 -DLLVM_LOCAL_RPATH="$HOME/toolchains/lib64" 传递给 cmake 以添加上述绝对路径到 libstdc++.so.6。由于这些二进制文件未分发,因此拥有绝对本地路径对它们来说很好。

构建 Clang 时,您需要授予它访问现代 C++ 标准库的权限,以便在引导程序的一部分中将其用作新主机。有两种简单的方法可以做到这一点,要么构建(并安装)libc++ 以及 Clang,然后将其与 -stdlib=libc++ 编译和链接标志一起使用,要么将 Clang 安装到与 GCC 相同的前缀 ($HOME/toolchains 上述) 中。Clang 将在其自己的前缀中查找 libstdc++ 并使用它(如果找到)。您还可以为 Clang 添加一个显式前缀以在其中查找 GCC 工具链,使用 --gcc-toolchain=/opt/my/gcc/prefix 标志,在使用您刚刚构建的 Clang 进行引导时将其传递给编译和链接命令。

LLVM 入门

本指南的其余部分旨在帮助您开始使用 LLVM 并提供有关 LLVM 环境的一些基本信息。

本指南的后面部分描述了 LLVM 源代码树的总体布局、使用 LLVM 工具链的简单示例以及用于查找有关 LLVM 的更多信息或通过电子邮件获取帮助的链接

术语和符号

在本手册中,以下名称用于表示特定于本地系统和工作环境的路径。这些不是您需要设置的环境变量,而只是在下文文档其余部分中使用的字符串。在以下任何示例中,只需将这些名称中的每一个替换为您本地系统上的相应路径名即可。所有这些路径都是绝对路径

SRC_ROOT

这是 LLVM 源代码树的顶层目录。

OBJ_ROOT

这是 LLVM 对象树的顶层目录(即对象文件和编译程序将放置的树。它可以与 SRC_ROOT 相同)。

发送补丁

请参阅贡献

二分提交

请参阅 二分 LLVM 代码,了解如何在 LLVM 上使用 git bisect

回退更改

使用 git 回退更改时,默认消息将显示“This reverts commit XYZ”。请保留此消息在提交消息的末尾,但在其前面添加一些关于为何回退此提交的详细信息。简要说明和/或演示问题的机器人的链接就足够了。

本地 LLVM 配置

签出存储库后,必须在构建 LLVM 套件源代码之前对其进行配置。此过程使用 CMake。与普通的 configure 脚本不同,CMake 会根据您的请求生成构建文件(以任何格式),以及各种 *.inc 文件和 llvm/include/llvm/Config/config.h.cmake

使用格式 -D<variable name>=<value> 在命令行上将变量传递给 cmake。以下变量是 LLVM 开发人员使用的一些常见选项。

  • CMAKE_C_COMPILER

  • CMAKE_CXX_COMPILER

  • CMAKE_BUILD_TYPE

  • CMAKE_INSTALL_PREFIX

  • Python3_EXECUTABLE

  • LLVM_TARGETS_TO_BUILD

  • LLVM_ENABLE_PROJECTS

  • LLVM_ENABLE_RUNTIMES

  • LLVM_ENABLE_DOXYGEN

  • LLVM_ENABLE_SPHINX

  • LLVM_BUILD_LLVM_DYLIB

  • LLVM_LINK_LLVM_DYLIB

  • LLVM_PARALLEL_LINK_JOBS

  • LLVM_OPTIMIZED_TABLEGEN

有关更多信息,请参阅 常用 CMake 变量列表

要配置 LLVM,请按照以下步骤操作

  1. 更改为对象根目录

    % cd OBJ_ROOT
    
  2. 运行 cmake

    % cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=<type> -DCMAKE_INSTALL_PREFIX=/install/path
      [other options] SRC_ROOT
    

编译 LLVM 套件源代码

与 autotools 不同,在 CMake 中,您的构建类型是在配置时定义的。如果要更改构建类型,可以使用以下调用重新运行 cmake

% cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=<type> SRC_ROOT

在运行之间,CMake 会保留为所有选项设置的值。CMake 定义了以下构建类型

调试

这些构建是默认的。构建系统将编译未经优化的工具和库,并启用调试信息和断言。

发布

对于这些构建,构建系统将编译启用了优化的工具和库,并且不会生成调试信息。CMake 的默认优化级别为 -O3。可以通过在 CMake 命令行上设置 CMAKE_CXX_FLAGS_RELEASE 变量来配置此变量。

RelWithDebInfo

这些构建在调试时很有用。它们生成带有调试信息的优化二进制文件。CMake 的默认优化级别为 -O2。可以通过在 CMake 命令行上设置 CMAKE_CXX_FLAGS_RELWITHDEBINFO 变量来配置此变量。

配置 LLVM 后,可以通过进入 OBJ_ROOT 目录并发出以下命令来构建它

% make

如果构建失败,请 在此处检查 以查看您是否正在使用已知无法编译 LLVM 的 GCC 版本。

如果您的机器有多个处理器,您可能希望使用 GNU Make 提供的一些并行构建选项。例如,您可以使用以下命令

% make -j2

在使用 LLVM 源代码时,有几个特殊的目标很有用

make clean

删除构建生成的所有文件。这包括目标文件、生成的 C/C++ 文件、库和可执行文件。

make install

将 LLVM 头文件、库、工具和文档安装在 $PREFIX 下的层次结构中,由 CMAKE_INSTALL_PREFIX 指定,默认为 /usr/local

make docs-llvm-html

如果使用 -DLLVM_ENABLE_SPHINX=On 进行配置,这将在 OBJ_ROOT/docs/html 中生成一个目录,其中包含 HTML 格式的文档。

交叉编译 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/LLVMClang 关于如何一般交叉编译的文档,以获取有关交叉编译的更多信息。

LLVM 对象文件的位置

LLVM 构建系统能够在多个 LLVM 构建之间共享单个 LLVM 源代码树。因此,可以使用相同的源代码树为多个不同的平台或配置构建 LLVM。

  • 更改为 LLVM 对象文件应所在的目录

    % cd OBJ_ROOT
    
  • 运行 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

可选配置项

如果您在支持 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'

目录布局

有关 LLVM 源代码库的有用信息来源之一是 LLVM doxygen 文档,可在 https://llvm.net.cn/doxygen/ 获取。以下是代码布局的简要介绍

llvm/cmake

生成系统构建文件。

llvm/cmake/modules

llvm 用户定义选项的构建配置。检查编译器版本和链接器标志。

llvm/cmake/platforms

Android NDK、iOS 系统和非 Windows 主机到目标 MSVC 的工具链配置。

llvm/examples

  • 一些简单的示例,展示了如何将 LLVM 作为自定义语言的编译器 - 包括降低、优化和代码生成。

  • Kaleidoscope 教程:Kaleidoscope 语言教程贯穿了一个不错的用于非平凡语言的小型编译器的实现,包括手写的词法分析器、解析器、AST,以及使用 LLVM 的代码生成支持 - 静态(提前)和各种方法的即时 (JIT) 编译。Kaleidoscope 教程适合初学者

  • BuildingAJIT:BuildingAJIT 教程的示例,展示了 LLVM 的 ORC JIT API 如何与 LLVM 的其他部分交互。它还教授如何将它们重新组合以构建适合您用例的自定义 JIT。

llvm/include

从 LLVM 库导出的公共头文件。三个主要子目录

llvm/include/llvm

所有 LLVM 特定的头文件,以及 LLVM 不同部分的子目录:AnalysisCodeGenTargetTransforms 等…

llvm/include/llvm/Support

LLVM 提供的通用支持库,但不一定特定于 LLVM。例如,一些 C++ STL 实用程序和命令行选项处理库将头文件存储在此处。

llvm/include/llvm/Config

cmake 配置的头文件。它们包装“标准”UNIX 和 C 头文件。源代码可以包含这些头文件,这些头文件会自动处理 cmake 生成的条件 #include。

llvm/lib

大多数源文件都在这里。通过将代码放入库中,LLVM 使得在 工具之间共享代码变得容易。

llvm/lib/IR/

实现核心类(如 Instruction 和 BasicBlock)的核心 LLVM 源文件。

llvm/lib/AsmParser/

LLVM 汇编语言解析器库的源代码。

llvm/lib/Bitcode/

读取和写入位代码的代码。

llvm/lib/Analysis/

各种程序分析,例如调用图、归纳变量、自然循环识别等。

llvm/lib/Transforms/

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/include/Support/ 中头文件的源代码。

llvm/bindings

包含 LLVM 编译器基础设施的绑定,允许用除 C 或 C++ 之外的其他语言编写的程序利用 LLVM 基础设施。LLVM 项目提供 OCaml 和 Python 的语言绑定。

llvm/projects

不严格属于 LLVM 但与 LLVM 一起发布的项目。这也是用于创建您自己的基于 LLVM 的项目的目录,这些项目利用 LLVM 构建系统。

llvm/test

LLVM 基础设施的功能和回归测试以及其他健全性检查。这些旨在快速运行并覆盖大量领域,而无需详尽无遗。

test-suite

LLVM 的全面正确性、性能和基准测试套件。它位于一个 单独的 git 存储库 <https://github.com/llvm/llvm-test-suite> 中,因为它包含大量在各种许可证下的第三方代码。有关详细信息,请参阅 测试指南 文档。

llvm/tools

从上述库构建的可执行文件,构成了用户界面的主要部分。您始终可以通过键入 tool_name -help 获取工具的帮助。以下是对最重要工具的简要介绍。更详细的信息请参见 命令指南

bugpoint

bugpoint 用于通过将给定的测试用例缩减到最少的传递和/或指令数量来调试优化传递或代码生成后端,这些传递和/或指令仍然会导致问题,无论是崩溃还是误编译。有关使用 bugpoint 的更多信息,请参见 HowToSubmitABug.html

llvm-ar

归档器生成一个包含给定 LLVM 位代码文件的存档,可以选择包含索引以加快查找速度。

llvm-as

汇编器将人类可读的 LLVM 汇编转换为 LLVM 位代码。

llvm-dis

反汇编器将 LLVM 位代码转换为人类可读的 LLVM 汇编。

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 位代码文件运行特定分析并打印结果。主要用于调试分析或熟悉分析的功能。

llvm/utils

用于处理 LLVM 源代码的实用程序;其中一些是构建过程的一部分,因为它们是基础设施部分的代码生成器。

codegen-diff

codegen-diff 查找 LLC 生成的代码和 LLI 生成的代码之间的差异。如果您正在调试其中一个,假设另一个生成正确的输出,这将很有用。有关完整用户手册,请运行 `perldoc codegen-diff'

emacs/

LLVM 汇编文件和 TableGen 描述文件的 Emacs 和 XEmacs 语法高亮显示。有关使用方法,请参见 README

getsrcs.sh

查找并输出所有未生成的源文件,如果希望跨目录进行大量开发并且不想查找每个文件,这将很有用。一种使用方法是从 LLVM 源代码树的顶部运行,例如:xemacs `utils/getsources.sh`

llvmgrep

对 LLVM 中的每个源文件执行 egrep -H -n 并将提供的正则表达式传递给它 llvmgrep 的命令行。这是一种有效地在源代码库中搜索特定正则表达式的方法。

TableGen/

包含用于从通用 TableGen 描述文件生成寄存器描述、指令集描述甚至汇编器的工具。

vim/

LLVM 汇编文件和 TableGen 描述文件的 vim 语法高亮显示。有关使用方法,请参见 README

使用 LLVM 工具链的示例

本节提供了一个使用 LLVM 和 Clang 前端的示例。

使用 clang 的示例

  1. 首先,创建一个简单的 C 文件,命名为“hello.c”。

    #include <stdio.h>
    
    int main() {
      printf("hello world\n");
      return 0;
    }
    
  2. 接下来,将 C 文件编译成本机可执行文件。

    % clang hello.c -o hello
    

    注意

    默认情况下,Clang 的工作方式与 GCC 相同。标准的 -S 和 -c 参数照常工作(分别生成本机 .s 或 .o 文件)。

  3. 接下来,将 C 文件编译成 LLVM 位代码文件。

    % clang -O3 -emit-llvm hello.c -c -o hello.bc
    

    -emit-llvm 选项可以与 -S 或 -c 选项一起使用,以分别为代码发出 LLVM .ll.bc 文件。这允许您在位代码文件上使用 标准 LLVM 工具

  4. 以两种形式运行程序。要运行程序,请使用

    % ./hello
    

    % lli hello.bc
    

    第二个示例演示了如何调用 LLVM JIT,lli

  5. 使用 llvm-dis 实用程序查看 LLVM 汇编代码。

    % llvm-dis < hello.bc | less
    
  6. 使用 LLC 代码生成器将程序编译成本地汇编。

    % llc hello.bc -o hello.s
    
  7. 将本地汇编语言文件汇编成程序。

    % /opt/SUNWspro/bin/cc -xarch=v9 hello.s -o hello.native   # On Solaris
    
    % gcc hello.s -o hello.native                              # On others
    
  8. 执行本地代码程序。

    % ./hello.native
    

    请注意,使用 clang 直接编译成本地代码(即,当 -emit-llvm 选项不存在时)会为您执行步骤 6/7/8。

常见问题

如果您在构建或使用 LLVM 时遇到问题,或者您有任何其他关于 LLVM 的一般问题,请参阅 常见问题解答 页面。

如果您遇到内存和构建时间有限的问题,请尝试使用 ninja 而不是 make 进行构建。请考虑使用 cmake 配置以下选项

  • -G Ninja 设置此选项将允许您使用 ninja 而不是 make 进行构建。使用 ninja 构建可以显着缩短构建时间,尤其是在增量构建时,并改善内存使用情况。

  • -DLLVM_USE_LINKER 将此选项设置为 lld 将显着减少基于 ELF 的平台(如 Linux)上 LLVM 可执行文件的链接时间。如果您是第一次构建 LLVM 并且 lld 无法作为二进制包提供给您,那么您可以使用 gold 链接器作为 GNU ld 的更快替代方案。

  • -DCMAKE_BUILD_TYPE 控制构建的优化级别和调试信息。此设置会影响 RAM 和磁盘使用情况,有关更多信息,请参见 CMAKE_BUILD_TYPE

  • -DLLVM_ENABLE_ASSERTIONS 此选项默认为 Debug 构建的 ON 以及 Release 构建的 OFF。如前一个选项所述,使用 Release 构建类型并启用断言可能是使用 Debug 构建类型的一个好替代方案。

  • -DLLVM_PARALLEL_LINK_JOBS 将其设置为希望同时运行的作业数。这类似于与 make 一起使用的 -j 选项,但仅适用于链接作业。此选项只能与 ninja 一起使用。您可能希望使用非常少的作业数,因为这将大大减少构建过程中使用的内存量。如果您内存有限,则可能希望将其设置为 1。

  • -DLLVM_TARGETS_TO_BUILD 将其设置为希望构建的目标。您可能希望将其设置为 X86;但是,您将在 llvm-project/llvm/lib/Target 目录中找到目标的完整列表。

  • -DLLVM_OPTIMIZED_TABLEGEN 将其设置为 ON 以在构建期间生成完全优化的 tablegen。这将显着缩短构建时间。这仅在您使用 Debug 构建类型时才有用。

  • -DLLVM_ENABLE_PROJECTS 将其设置为希望编译的项目(例如 clang、lld 等)。如果编译多个项目,请用分号分隔项目。如果您遇到分号问题,请尝试将其用单引号括起来。

  • -DLLVM_ENABLE_RUNTIMES 将其设置为希望编译的运行时(例如 libcxx、libcxxabi 等)。如果编译多个运行时,请用分号分隔项目。如果您遇到分号问题,请尝试将其用单引号括起来。

  • -DCLANG_ENABLE_STATIC_ANALYZER 如果不需要 clang 静态分析器,请将此选项设置为 OFF。这应该会稍微缩短构建时间。

  • -DLLVM_USE_SPLIT_DWARF 如果需要调试构建,请考虑将其设置为 ON,因为这将减轻链接器上的内存压力。这将使链接速度更快,因为二进制文件将不包含任何调试信息;但是,这将以 DWARF 对象文件(扩展名为 .dwo)的形式生成调试信息。这仅适用于使用 ELF 的主机平台,例如 Linux。