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

    • 您可能对 repo 中的用户分支(用于堆叠的拉取请求和回退)不感兴趣,您可以使用以下配置从 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-project 的标准检出的源代码(如上所述)来执行独立构建,但您也可以从稀疏检出或从 发布页面上提供的 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

AArch64

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

FreeBSD

AArch64

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

macOS

arm64

Clang

Cygwin/Win32

x861, 3

GCC

Windows

x861

Visual Studio

Windows x64

x86-64

Visual Studio, Clang4

Windows on Arm

ARM64

Visual Studio, Clang4

注意

  1. 代码生成支持 Pentium 及以上处理器

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

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

  4. Visual Studio 可以单独编译 LLVM。使用 Clang 时,您还必须安装 Visual Studio。

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

如果您的空间受限,则可以仅构建选定的工具或仅构建选定的目标。Release 构建需要的空间要少得多。

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

软件

编译 LLVM 需要您安装多个软件包。下表列出了这些必需的软件包。“软件包”列是 LLVM 依赖的软件包的常用名称。“版本”列提供了软件包的“已知有效”版本。“注释”列描述了 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. 仅在构建带有 New 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++ 语言和库的改进和发展。因此,我们需要现代主机 C++ 工具链,包括编译器和标准库,才能构建 LLVM。

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

  • Clang 5.0

  • Apple Clang 10.0

  • GCC 7.4

  • Visual Studio 2019 16.8

比这些工具链旧的任何东西可能都可以工作,但需要使用特殊选项强制构建系统,并且实际上不是受支持的主机平台。另请注意,这些编译器的旧版本经常崩溃或错误编译 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 stack exchange上对此进行了非常好的讨论,并且 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++ 标准库,以便在引导的一部分中使用它作为新的主机。有两种简单的方法可以做到这一点,要么与 Clang 一起构建(和安装)libc++,然后将它与 -stdlib=libc++ 编译和链接标志一起使用,要么将 Clang 安装到与 GCC 相同的前缀中(上面的 $HOME/toolchains)。Clang 将在其自己的前缀中查找 libstdc++,如果找到则使用它。您还可以为 Clang 添加显式前缀,以使用 --gcc-toolchain=/opt/my/gcc/prefix 标志查找 GCC 工具链,在使用您刚刚构建的 Clang 进行引导时将其传递给编译和链接命令。

LLVM 入门

本指南的其余部分旨在帮助您启动并运行 LLVM,并为您提供有关 LLVM 环境的一些基本信息。

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

术语和符号

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

SRC_ROOT

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

OBJ_ROOT

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

发送补丁

请参阅贡献

二分查找提交

有关如何在 LLVM 上使用 git bisect,请参阅二分查找 LLVM 代码

回退更改

当使用 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 定义了以下构建类型

Debug (调试)

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

Release (发布)

对于这些构建类型,构建系统将编译启用优化的工具和库,并且不生成调试信息。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 下的层级目录中,$PREFIXCMAKE_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 构建时,需要传递一些额外的标志。

查看 How To Cross-Compile Clang/LLVM using Clang/LLVMClang docs on how to cross-compile in general 以获取有关交叉编译的更多信息。

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 bitcode 文件。为此,请使用如下命令(如果你已经在使用该模块,则可能不需要第一个命令)

% 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 bitcode 文件。在 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 作为自定义语言的编译器 - 包括 lowering(降级)、优化和代码生成。

  • 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 生成的条件 #includes。

llvm/lib

大多数源文件都在这里。通过将代码放在库中,LLVM 可以轻松地在 工具 之间共享代码。

llvm/lib/IR/

核心 LLVM 源文件,实现了诸如 Instruction 和 BasicBlock 等核心类。

llvm/lib/AsmParser/

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

llvm/lib/Bitcode/

用于读取和写入 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 编译场景中直接在运行时执行 bitcode 的库。

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

archiver (归档器) 生成一个包含给定 LLVM bitcode 文件的归档文件,可以选择包含索引以加快查找速度。

llvm-as

assembler (汇编器) 将人类可读的 LLVM 汇编代码转换为 LLVM bitcode。

llvm-dis

disassembler (反汇编器) 将 LLVM bitcode 转换为人类可读的 LLVM 汇编代码。

llvm-link

llvm-link,顾名思义,将多个 LLVM 模块链接到一个程序中。

lli

lli 是 LLVM 解释器,它可以直接执行 LLVM bitcode(尽管速度很慢……)。对于支持它的架构(目前是 x86、Sparc 和 PowerPC),默认情况下,lli 将充当即时编译器(如果编译了该功能),并且执行代码的速度快于解释器。

llc

llc 是 LLVM 后端编译器,它将 LLVM bitcode 转换为本机代码汇编文件。

opt

opt 读取 LLVM bitcode,应用一系列 LLVM 到 LLVM 的转换(在命令行上指定),并输出结果 bitcode。‘opt -help’ 是获取 LLVM 中可用程序转换列表的好方法。

opt 还可以对输入的 LLVM bitcode 文件运行特定的分析并打印结果。主要用于调试分析,或使自己熟悉分析的作用。

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 bitcode 文件

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

    -emit-llvm 选项可以与 -S 或 -c 选项一起使用,为代码生成 LLVM .ll.bc 文件(分别)。这允许你在 bitcode 文件上使用 标准 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 将显著减少 LLVM 可执行文件的链接时间,尤其是在 Linux 和 Windows 上。如果你是第一次构建 LLVM 并且 lld 作为二进制包不可用,那么你可能希望使用 gold linker 作为 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

    将其设置为你希望构建的目标。你可能希望将其仅设置为你的主机架构。例如,如果你使用的是 Intel 或 AMD 机器,则设置为 X86。你将在 llvm-project/llvm/lib/Target 目录中找到目标的完整列表。

  • -DLLVM_OPTIMIZED_TABLEGEN

    将其设置为 ON 以在构建期间生成完全优化的 TableGen 编译器,即使该构建是 Debug 构建。这将显著缩短你的构建时间。如果你的目的是调试 TableGen 编译器,则不应启用此选项。

  • -DLLVM_ENABLE_PROJECTS

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

  • -DLLVM_ENABLE_RUNTIMES

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

  • -DCLANG_ENABLE_STATIC_ANALYZER

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

  • -DLLVM_USE_SPLIT_DWARF

    如果你需要调试构建,请考虑将其设置为 ON,因为这将减轻链接器的内存压力。这将使链接速度更快,因为二进制文件将不包含任何调试信息。相反,调试信息位于单独的 DWARF 目标文件中(扩展名为 .dwo)。这仅适用于使用 ELF 的主机平台,例如 Linux。

  • -DBUILD_SHARED_LIBS

    将其设置为 ON 将构建共享库而不是静态库。这将减轻链接器的内存压力。但是,这应该仅在开发 llvm 时使用。有关更多信息,请参阅 BUILD_SHARED_LIBS