LLVM - DTLTO(分布式 ThinLTO)

LLVM - DTLTO(分布式 ThinLTO)

DTLTO(分布式 ThinLTO)¶

分布式 ThinLTO(DTLTO)¶

分布式 ThinLTO(DTLTO)允许在链接步骤中通过外部分发系统(例如 Incredibuild)来分发后端的 ThinLTO 编译任务。

DTLTO 扩展了现有的 ThinLTO 分发支持,该支持使用独立的 thin-link、后端编译和 link 步骤。有关“独立 thin-link”方法的文档请参见:

https://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html

使用“独立 thin-link”方法要求构建系统能够处理由各个 summary index 文件动态指定的依赖,例如 Bazel。DTLTO 消除了这一要求,使其可用于任何支持进程内(in-process)ThinLTO 的构建流程。

下列命令展示了基本示例中“独立 thin-link”方法的步骤:

  1. clang -flto=thin -O2 t1.c t2.c -c
  2. clang -flto=thin -O2 t1.o t2.o -fuse-ld=lld -Wl,–thinlto-index-only
  3. clang -O2 -o t1.native.o t1.o -c -fthinlto-index=t1.o.thinlto.bc
  4. clang -O2 -o t2.native.o t2.o -c -fthinlto-index=t2.o.thinlto.bc
  5. clang t1.native.o t2.native.o -o a.out -fuse-ld=lld

使用 DTLTO,步骤 2 到 5 会作为链接步骤的一部分被内部执行。上述等价的 DTLTO 命令为:

clang -flto=thin -O2 t1.o t2.o -fuse-ld=lld -fthinlto-distributor=<distributor_process>

对于 DTLTO,LLD 为每个 ThinLTO 后端编译任务准备如下内容:

  • 一个单独的索引文件和输入/输出文件列表(对应上面步骤 2)。
  • 一个用于执行 ThinLTO 后端编译的 Clang 命令行。

这些信息通过 JSON 文件传递给 distributor_process,由其使用分发系统执行后端编译(对应上面步骤 3 和 4)。一旦完成,LLD 将把编译生成的本机目标文件整合回链接流程并完成链接(对应步骤 5)。

这种设计将分发系统的细节隔离出 LLVM 源代码之外。

LLVM 源树中包含了一个示例分发器(distributor),该示例在本地系统上执行所有工作。使用这个分发器运行示例的命令,例如:

clang -flto=thin -fuse-ld=lld -O2 t1.o t2.o -fthinlto-distributor=$(which python3) \
  -Xthinlto-distributor=$LLVMSRC/llvm/utils/dtlto/local.py

分发器(Distributors)¶

分发器是负责以下工作的程序:

  1. 消费 JSON 后端编译任务描述文件。
  2. 把任务描述转换为分发系统所需的请求。
  3. 阻塞(等待)直到所有后端编译完成。

分发器在失败时必须返回非零退出码。它们可以实现为平台原生可执行程序或脚本语言(例如 Python)。

Clang 与 LLD 提供了选项来指定用于管理后端编译的分发器程序。分发器选项与后端编译选项也可以一并指定,这些选项会被透明地转发。

后端编译当前通过调用 Clang 来执行。详见:

  • Clang 文档: https://clang.llvm.org/docs/ThinLTO.html
  • LLD 文档: https://lld.llvm.org/DTLTO.html

当 LLD 与分发器一起被调用时,LLD 会生成一个描述后端编译任务的 JSON 文件并执行分发器,同时把该文件传递给分发器。

JSON 模式(JSON Schema)¶

下面的示例说明了 JSON 格式(描述对模块 t1.ot2.o 的后端编译任务):

{
    "***mon": {
        "linker_output": "dtlto.elf",
        "args": ["/usr/bin/clang", "-O2", "-c", "-fprofile-sample-use=my.prof"],
        "inputs": ["my.prof"]
    },
    "jobs": [
        {
            "args": ["t1.o", "-fthinlto-index=t1.o.thinlto.bc", "-o", "t1.native.o", "-fproc-stat-report=t1.stats.txt"],
            "inputs": ["t1.o", "t1.o.thinlto.bc"],
            "outputs": ["t1.native.o", "t1.stats.txt"]
        },
        {
            "args": ["t2.o", "-fthinlto-index=t2.o.thinlto.bc", "-o", "t2.native.o", "-fproc-stat-report=t2.stats.txt"],
            "inputs": ["t2.o", "t2.o.thinlto.bc"],
            "outputs": ["t2.native.o", "t2.stats.txt"]
        }
    ]
}

jobs 数组中的每一项都表示一个后端编译任务。每个任务对象记录自己的命令行参数与输入/输出文件。共享的参数与输入定义在 ***mon 对象中一次即可。

保留条目(Reserved Entries):

  • ***mon.args 数组中的第一个条目指定要调用的编译器可执行文件。
  • 每个任务 inputs 数组的第一个条目为正在被编译的模块的 bitcode 文件。
  • 每个任务 inputs 数组的第二个条目为对应的单独 summary 索引文件。
  • 每个任务 outputs 数组的第一个条目为主输出的目标文件。

对于 outputs 数组,只有第一个条目为保留项(主输出文件);其余条目的顺序不保证。有些分发系统依赖主输出路径(例如用它作为编译任务的用户可读标签),因此将主输出放在保留位置。当前 DTLTO 实现不会产生多个输出文件,但未来若加入会产出额外输出文件的 LTO 选项,新的输出也将被包含到该数组中。

将命令行参数与输入/输出文件分别存储,允许在不修改分发器的情况下更换远程编译器,因为分发器不需要理解编译器命令行的细节。

要生成后端编译命令,会把公共参数与任务特定参数连接起来。

基于上面示例 JSON,分发器应以最大并行度执行下列后端编译命令:

/usr/bin/clang -O2 -c -fprofile-sample-use=my.prof t1.o -fthinlto-index=t1.o.thinlto.bc -o t1.native.o \
  -fproc-stat-report=t1.stats.txt

/usr/bin/clang -O2 -c -fprofile-sample-use=my.prof t2.o  -fthinlto-index=t2.o.thinlto.bc -o t2.native.o \
  -fproc-stat-report=t2.stats.txt

待办事项(TODOs)¶

下列功能已计划加入 DTLTO,但尚未实现:

  • 支持 ThinLTO 的进程内缓存(in-process cache)。
  • 支持 ELF/COFF 之外的平台。
  • 支持包含 bitcode 成员的归档(archives)。
  • 支持更多的 LTO 配置;当前仅支持非常有限的一组 LTO 配置,例如目前尚不支持基本块段(basic block sections)。

约束(Constraints)¶

  • 应使用版本匹配的 Clang 与 LLD(相互兼容的版本)。
  • 使用的分发器必须支持与正在使用的 LLD 版本相对应的 JSON 模式。

原文地址:https://llvm.org/docs/DTLTO.html

转载请说明出处内容投诉
CSS教程网 » LLVM - DTLTO(分布式 ThinLTO)

发表评论

欢迎 访客 发表评论

一个令你着迷的主题!

查看演示 官网购买