构建 LLVM 的发行版

简介

本文档面向希望构建和打包 LLVM 以及任何 LLVM 子项目工具组合以进行分发的人员。本文档涵盖了 LLVM 构建系统的有用功能,以及关于打包 LLVM 的最佳实践和一般信息。

如果您是 CMake 的新手,您可能会发现 使用 CMake 构建 LLVMCMake 入门 文档很有用。本文档中涵盖的一些内容是 高级构建配置 文档中描述的构建的内部工作原理。

通用发行版指南

在构建编译器发行版时,通常建议执行编译器的引导构建。这意味着使用您的主机工具链构建“阶段 1”编译器,然后使用“阶段 1”编译器构建“阶段 2”编译器。这样做是为了使您分发的编译器受益于新编译器提供的所有错误修复、性能优化和一般改进。

在决定如何构建您的发行版时,您需要评估一些权衡。最重要的两个是

  1. 发行版的编译时间与构建的编译器的性能

  2. 发行版的二进制大小与构建的编译器的性能

最大化生成的编译器性能的指南是使用 LTO、PGO 和静态链接所有内容。这将导致更大的整体发行版,并且生成时间更长,但它为编译器提供了最大的优化机会。

最小化发行版大小的指南是将 LLVM 和 Clang 库动态链接到工具中,以减少代码重复。这将对生成的二进制文件造成相当大的性能损失,因为它减少了优化机会,并且因为动态链接需要在进程启动时解析符号,这对于 C++ 代码来说可能非常慢。

警告

一个非常重要的注意事项:永远不应使用 BUILD_SHARED_LIBS CMake 选项构建发行版。该选项仅用于优化开发人员工作流程。由于设计和实现决策,LLVM 依赖于全局数据,这些数据最终可能会在共享库中重复,从而导致错误。因此,这不是分发 LLVM 或基于 LLVM 的工具的安全方法。

使用合理的性能构建发行版的最简单示例在 clang/cmake/caches/DistributionExample.cmake 中提供的 DistributionExample CMake 缓存文件中捕获。以下命令将执行并安装发行版构建

$ cmake -G Ninja -C <path to clang>/cmake/caches/DistributionExample.cmake <path to LLVM source>
$ ninja stage2-distribution
$ ninja stage2-install-distribution

installinstall-distribution 之间的区别

一个细微但重要的事情需要注意的是 installinstall-distribution 目标之间的区别。install 目标旨在安装您的构建配置为生成的所有 LLVM 部分,除了 LLVM 测试工具。或者,install-distribution 目标(推荐用于构建发行版)仅安装在配置时通过 LLVM_DISTRIBUTION_COMPONENTS 指定的 LLVM 的特定部分。

此外,默认情况下,install 目标会将 LLVM 测试工具安装为公共工具。可以通过将 LLVM_INSTALL_TOOLCHAIN_ONLY 设置为 On 来很好地更改这一点。LLVM 工具旨在用于 LLVM 的开发和测试,并且只应包含在支持 LLVM 开发的发行版中。

当使用 LLVM_DISTRIBUTION_COMPONENTS 构建时,构建系统还会生成一个 distribution 目标,该目标构建列表中指定的所有组件。这是一个方便的构建目标,允许仅构建分发的部分,而无需构建所有配置的目标。

多发行版配置

上面描述的 install-distribution 目标用于构建单个发行版。LLVM 的构建系统还支持构建多个发行版,例如,可以使用一个发行版仅包含工具,另一个发行版包含库(以启用开发)。这些通过将 LLVM_DISTRIBUTIONS 变量设置为包含所有发行版名称的列表(通常以大写字母开头,例如“Development”)来配置,然后将 LLVM_<distribution>_DISTRIBUTION_COMPONENTS 变量设置为该发行版的目标列表。对于每个发行版,构建系统都会生成一个 install-${distribution}-distribution 目标,其中 ${distribution} 是小写的发行版名称,用于安装该发行版。

每个发行版都会创建自己的一组 CMake 导出,并且为项目安装特定发行版的 CMake 导出的目标名为 ${project}-${distribution}-cmake-exports,其中 ${project} 是小写的项目名称,${distribution} 是小写的发行版名称,除非项目是 LLVM,在这种情况下,目标仅命名为 ${distribution}-cmake-exports。这些目标需要显式包含在 LLVM_<distribution>_DISTRIBUTION_COMPONENTS 变量中,以便作为发行版的一部分包含在内。

与单发行版设置不同,在构建多个发行版时,LLVM_RUNTIME_DISTRIBUTION_COMPONENTS 中指定的任何组件都不会自动添加到任何发行版。相反,您必须将目标显式包含在某些 LLVM_<distribution>_DISTRIBUTION_COMPONENTS 列表中。

默认情况下,每个目标可以出现在多个发行版中;目标将作为其出现的所有发行版的一部分安装,并且它将由它出现的最后一个发行版导出(发行版的顺序是它们在 LLVM_DISTRIBUTIONS 中出现的顺序)。我们还定义了一些伞状目标(例如,llvm-libraries 用于安装所有 LLVM 库);目标可以出现在与其伞状目标不同的发行版中,在这种情况下,目标将由它出现的发行版导出(而不是其伞状目标出现的发行版)。如果您希望强制目标仅出现在一个发行版中,并且伞状发行版与目标发行版一致,请将 LLVM_STRICT_DISTRIBUTIONS 设置为 On

我们强烈建议查看 clang/cmake/caches/MultiDistributionExample.cmake 作为配置多个发行版的示例。

仅库发行版的特别说明

LLVM 最强大的功能之一是其库优先的设计理念以及您可以使用 LLVM 的不同部分组合各种工具的方式。即使在这种情况下,也不支持使用 BUILD_SHARED_LIBS。如果您想将 LLVM 作为共享库分发以在工具中使用,建议的方法是使用 LLVM_BUILD_LLVM_DYLIB,您可以使用 LLVM_DYLIB_COMPONENTS 配置哪些 LLVM 组件是 libLLVM 的一部分。注意:LLVM_BUILD_LLVM_DYLIB 在 Windows 上不可用。

优化 LLVM 的选项

我们的 CMake 构建系统支持四种主要的构建优化。当执行引导构建时,除了将 CMAKE_BUILD_TYPE 设置为 Release 用于阶段 1 编译器之外,执行任何其他操作都是没有好处的。这是因为更密集的优化执行成本很高,并且阶段 1 编译器会被丢弃。所有进一步描述的选项都应在阶段 2 编译器上设置,可以使用 CMake 缓存文件,或通过在选项前加上 BOOTSTRAP_

第一个也是最容易使用的选项是通过设置 CMAKE_BUILD_TYPE 选项来设置编译器优化级别。主要的感兴趣的值是 ReleaseRelWithDebInfo。默认情况下,Release 选项使用 -O3 优化级别,而 RelWithDebInfo 使用 -O2。如果您想生成调试信息并使用 -O3,您可以覆盖 C 和 CXX 的 CMAKE_<LANG>_FLAGS_RELWITHDEBINFO 选项。DistributionExample.cmake 就是这样做的。

另一个易于使用的选项是链接时优化。您可以在阶段 2 构建中将 LLVM_ENABLE_LTO 选项设置为 ThinFull 以启用使用 LTO 构建 LLVM。这些选项将显着增加发行版中二进制文件的链接时间,但它将创建更快的二进制文件。如果您的发行版包含静态库,则不应使用此选项,因为库内部的对象将是 LLVM bitcode,这是不可移植的。

高级构建配置 文档描述了用于生成 LLVM 分析信息以驱动 Profile-Guided-Optimization 的内置工具。树内分析测试非常有限,生成分析信息需要大量时间,但它可以显着提高生成的二进制文件的性能。

除了 PGO 分析之外,我们还在树内对生成链接器顺序文件提供了有限的支持。这些文件为链接器提供了最终二进制布局中函数建议的排序。这可以通过物理分组在时间上彼此接近调用的函数来显着加快 clang 的速度。当前的工具仅在具有 dtrace(1) 的 Darwin 系统上可用。值得注意的是,dtrace 是非确定性的,因此使用 dtrace 生成的顺序文件也是非确定性的。

减小尺寸的选项

警告

为减小二进制大小而采取的任何步骤都将以生成的二进制文件的运行时性能为代价。

减小二进制大小的最简单且最不显着的方法是将 CMAKE_BUILD_TYPE 变量设置为 MinSizeRel,这将将编译器优化级别设置为 -Os,从而优化二进制大小。这将对大小的好处最小,对性能的影响也最小。

减小二进制大小的最有效方法是将 LLVM 动态链接到所有工具中。这通过减少基于 LLVM 的工具之间常见代码的重复来减小代码大小。这可以通过将以下两个 CMake 选项设置为 On 来完成:LLVM_BUILD_LLVM_DYLIBLLVM_LINK_LLVM_DYLIB

警告

永远不应使用 BUILD_SHARED_LIBS CMake 选项构建发行版。(有关更多说明,请参阅上面的警告。)。

相关的 CMake 选项

本节提供旨在帮助构建发行版的 CMake 选项的文档。这不是详尽的列表,并且在 使用 CMake 构建 LLVM 页面中记录了许多其他选项。已经记录的一些关键选项包括:LLVM_TARGETS_TO_BUILDLLVM_ENABLE_PROJECTSLLVM_ENABLE_RUNTIMESLLVM_BUILD_LLVM_DYLIBLLVM_LINK_LLVM_DYLIB

LLVM_ENABLE_RUNTIMES:STRING

当构建包含 LLVM 运行时项目(即 libcxx、compiler-rt、libcxxabi、libunwind...)的发行版时,重要的是使用刚刚构建的编译器构建这些项目。

LLVM_DISTRIBUTION_COMPONENTS:STRING

此变量可以设置为要安装的 LLVM 构建系统组件的分号分隔列表。所有基于 LLVM 的工具都是组件,以及大多数库和运行时。组件名称与构建系统目标的名称匹配。

LLVM_DISTRIBUTIONS:STRING

此变量可以设置为发行版的分号分隔列表。有关此变量和其他 CMake 变量的详细信息,请参阅上面的 多发行版配置 部分以配置多个发行版。

LLVM_RUNTIME_DISTRIBUTION_COMPONENTS:STRING

此变量可以设置为运行时库组件的分号分隔列表。这与 LLVM_ENABLE_RUNTIMES 结合使用,以指定要包含在发行版中的运行时库的组件。与 LLVM_DISTRIBUTION_COMPONENTS 一样,组件名称与构建系统目标的名称匹配。

LLVM_DYLIB_COMPONENTS:STRING

此变量可以设置为 LLVM 库组件的分号分隔名称。LLVM 库组件是删除 LLVM 前缀的库名称(即 Support、Demangle...)、LLVM 目标名称或特殊用途组件名称。特殊用途组件名称是

  1. all - 所有 LLVM 可用组件库

  2. Native - 本机系统的 LLVM 目标

  3. AllTargetsAsmParsers - 所有包含的目标 ASM 解析器库

  4. AllTargetsDescs - 所有包含的目标描述库

  5. AllTargetsDisassemblers - 所有包含的目标反汇编器库

  6. AllTargetsInfos - 所有包含的目标信息库

LLVM_INSTALL_TOOLCHAIN_ONLY:BOOL

此选项默认为 Off:设置为 On 时,它会从默认 install 目标中删除许多 LLVM 开发和测试工具以及组件库。不建议发行版包含开发工具,因为许多 LLVM 工具仅用于开发和测试用途。