首页
/ BOLT for AArch64:默认关闭的优化标志全解及其在源码中的实现依据

BOLT for AArch64:默认关闭的优化标志全解及其在源码中的实现依据

2026-09-05 16:57:42作者:伍霜盼Ellen

本文以 BOLT 官方文档 BOLTAArch64OptimizationStatus.md 为主体,系统梳理在 AArch64 架构上可用、受限与不可用的 BOLT 默认关闭(default-off)优化标志,并结合 AArch64MCPlusBuilder.cppBinaryPasses.cpp 的源码实现与 unsupported-passes.test 等测试用例,为每个结论提供可验证的仓库证据。读完后你可以:明确知道哪些 BOLT 优化可直接用于 AArch64 二进制、哪些必须先满足特定条件、哪些会在 AArch64 上直接报错,以及如何在仓库中找到对应的实现与回归测试。

适用前提:带重定位的二进制 + 代表性 Profile

按照 BOLT 概览文档 的通用要求,该状态文档在开头即明确了 BOLT 优化 AArch64 二进制的前提:

  • 二进制在链接时必须保留重定位信息,即编译链接时加上 --emit-relocs(Clang)或 -Wl,-q(链接器选项);
  • 必须提供有代表性的 profile 数据(由 BOLT 插桩运行时或 perf 采集生成)。

缺少重定位会导致 BOLT 无法准确解析函数与基本块;缺少有代表性的 profile,则基于执行计数的重排、冷热分离等优化失去依据。仓库中的测试用例 unsupported-passes.test 就演示了标准编译方式:

# 该测试用 clang 编译 hello.c 时显式保留了重定位
%clang %cflags hello.c -o hello -Wl,-q,-z,undefs

主要代码布局优化(收益最大,优先开启)

文档将以下三项列为“通常最先考虑的选项”,因为它们是 BOLT 优化中通常带来最大性能收益的代码布局类优化:

标志 优化内容
--reorder-functions=exec-count|hfsort|cdsort|pettis-hansen|random|user--function-order=<file> 重排函数
--reorder-blocks=normal|ext-tsp|cache|branch-predictor|reverse|cluster-shuffle 重排基本块
--split-functions--split-strategy=profile2|random2|randomN|all--split-all-cold--split-eh 分离热点与冷点代码

这三类优化直接改变指令的内存布局,目标是提高指令缓存命中率与分支预测成功率。在 SplitFunctions.cpp 中可以看到各拆分策略的实现入口:SplitStrategy 枚举驱动不同拆分算法,其中 CDSplit 策略会连续执行两轮拆分(先冷热分离的 SplitProfile2,再做 cache-directed 拆分),相关调参选项(如 call-score-scalecall-score-powerjump-score-power)都标注了“仅当 --split-strategy=cdsplit 时生效”。AArch64 上对应功能有 split-funcs-lite.s 等测试覆盖。

其他已支持的 AArch64 优化

文档同时列出以下在 AArch64 上受支持的优化:

标志 优化内容
--align-blocks--block-alignment=<uint> 对齐基本块(减少跨缓存行/取指边界的分支)
--tail-duplication=aggressive|moderate|cache 复制分支尾部(tail duplication)
--peepholes=double-jumps|tailcall-traps|useless-branches|all 运行窥孔优化
--inline-all--inline-small-functions;相关选项:--inline-ap--inline-limit=<uint>--inline-small-functions-bytes=<uint> 函数内联
--icf=safe|all 合并(折叠)相同函数

AArch64 上有对应的测试文件可以印证这些路径的实际行为,例如 tail-duplication-pass.stail-duplication-cache.s(尾部复制)、double_jump.cppext-double-jump.s(双跳转窥孔)、inline-small-function-1.sinline-small-function-2.s(小函数内联)、safe-icf.s(安全 ICF)。小函数内联的字节数上限由 --inline-small-functions-bytes 控制,测试用例覆盖了该阈值附近的边界行为。

受限制支持的标志:必须先满足条件

这部分是本文的重点。文档明确列出 5 个“已实现但需要特定运行时或选项条件”的标志;在未满足条件时启用,BOLT 可能报错静默不做任何变换。逐条拆解如下:

--inline-memcpy:仅内联已知常量大小的 memcpy,且超过 64 字节会被跳过

文档说明:“仅当拷贝大小是已知常量时才适用;AArch64 会跳过超过 64 字节的大小。”这一限制可以直接在源码中得到证实。在 BinaryPasses.cppInlineMemcpy::runOnFunctions 中:

// 从调用点前序指令中提取大小(仅 AArch64)。
// 模式:MOV X2, #nb-bytes; BL memcpy src, dest, X2.
std::optional<uint64_t> KnownSize =
    BC.MIB->findMemcpySizeInBytes(BB, II);

if (BC.isAArch64() && (!KnownSize.has_value() || *KnownSize > 64))
  continue;   // 大小未知或 > 64 字节:跳过

两个条件都体现为“跳过”而非报错:KnownSize 必须存在(即调用点前能匹配到 MOV X2, #size; BL memcpy 这种常量大小模式),且 *KnownSize <= 64。AArch64 上生成内联拷贝序列的指令构建逻辑位于 AArch64MCPlusBuilder.cppcreateInlineMemcpy 等目标专用接口),该文件整体职责就是为 BOLT 提供 AArch64 的机器指令构造能力(构建跳转桩、内联序列、返回指令等),这也是 BOLT 能以目标架构插件方式扩展新后端的基础。

--plt=hot|all:需要即时符号绑定

--plt 用于优化经由 PLT 的调用。文档指出它要求即时绑定(immediate binding);如果 BOLT 运行时无法更新二进制的绑定属性,则需要重新链接并加 -znow。AArch64 的 PLT 优化有一组专门的测试:plt-gnu-ld.testplt-got.testplt-call.testplt-mold-func-symbols.s,分别覆盖了 GNU ld、GOT 变体、mold 等不同链接器场景下的行为差异——不同链接器生成的 PLT 结构不同,是“需要特定条件”的具体来源之一。

--hugify:热代码放到大页(Huge Pages)

该标志将热代码放置在 2MB 大页以减少 TLB miss。文档给出的限制是:仅对具有可识别入口点(entry point)的二进制生效,且--instrument 互斥(插桩模式下跳过)。BOLT 的大页支持由 hugify 运行时组件 与驱动端逻辑共同实现,入口点识别依赖二进制的标准 ELF 入口符号,位置服务或无标准入口的可执行体可能无法享受该优化。

--reorder-data / --reorder-data-algo=count|funcs:数据段重排

按执行计数(count)或所属函数热度(funcs)重排数据段。文档提示:movesplitaggressive 这几个(函数/基本块移动类)选项会关闭数据重排——即两者互斥,启用前需检查命令行中是否同时出现这些选项。

--split-strategy=cdsplit:cache-directed 拆分在 AArch64 上要求 --compact-code-model

文档说明 CDSplit 在 AArch64 上需要同时启用 --compact-code-model。这一点在 unsupported-passes.test 中有直接的回归验证:

not llvm-bolt %t -o %t.bolt split-functions --split-strategy=cdsplit ...
CHECK-CDSPLIT: BOLT-ERROR: CDSplit is not supported with LongJmp. Try with '--compact-code-model'

从源码结构看,其背景是:AArch64 默认使用“长跳转(LongJmp)”跳转表机制,CDSplit 的紧凑布局与该机制不兼容,而 --compact-code-model 会切换为紧凑代码模型(对应测试 compact-code-model.s)。--split-strategy 的 CDSplit 分支实现在 SplitFunctions.cpp,其中 CDSplit 会先后执行 SplitPrfoile2(冷热分离)与 cache-directed 拆分两轮。

AArch64 上不可用的标志及确切报错信息

文档将“不可用标志”细分为两类语义:

  • Not applicable to AArch64(不适用于 AArch64):优化针对的架构特性在 AArch64 上不存在;
  • Not implemented for AArch64(尚未实现):优化本身对 AArch64 有意义,但当前版本没有为该目标实现。

完整清单如下(以文档原文为准):

标志 优化内容 类别(文档原注)
--jt-footprint-reduction 缩减跳转表占用空间 Not implemented for AArch64
--three-way-branch 重排三路分支 Not implemented for AArch64
--simplify-rodata-loads 用常量替换只读数据加载 Not implemented for AArch64
--frame-opt=hot|all 优化栈帧访问 Not implemented for AArch64
--indirect-call-promotion=calls|jump-tables|all 间接调用提升 Not implemented for AArch64
--memcpy1-spec=<func1,func2:cs1:cs2,...> 特化单字节 memcpy 调用 Not implemented for AArch64
--reg-reassign 重新分配寄存器以缩小编码体积 Not applicable to AArch64
--cmov-conversion 分支转条件移动指令 Not applicable to AArch64
--stoke / --stoke-out 输出 STOKE 优化数据 Not applicable to AArch64
--insert-retpolines 插入 retpoline Not applicable to AArch64

其中 --reg-reassign(寄存器重分配以压缩指令编码)是典型的 X86 特性:X86 通过 REX 前缀编码寄存器选择,改变寄存器编号可能改变指令长度;而 AArch64 定长 4 字节指令不存在这种编码尺寸差异,因此“不适用”而非“未实现”。

这些限制不是文档口头声明,而是由 unsupported-passes.test 用真实报错信息逐一锁定的。该测试在 system-linux + asserts + aarch64 目标下运行,预期每个标志都产生非零退出码与特定错误文本:

CHECK-FRAME-OPT:                BOLT-ERROR: frame-optimizer is supported only on X86
CHECK-THREE-WAY:                BOLT-ERROR: three way branch is supported only on X86
CHECK-JT-FOOTPRINT-REDUCTION:   BOLT-ERROR: jt-footprint-reduction is supported only on X86
CHECK-INDIRECT-CALL-PROMOTION:  BOLT-ERROR: ICP jump table promotion is disabled on AArch64
CHECK-MEMCPY1-SPEC:             BOLT-ERROR: specialize-memcpy is currently supported only on X86
CHECK-CMOV-CONV:                BOLT-ERROR: CMOV conversion is specific to X86
CHECK-REG-REASSIGN:             BOLT-ERROR: reg-reassign is specific to X86
CHECK-RETPOLINE:                BOLT-ERROR: retpoline-insertion is specific to X86
CHECK-STOKE:                    BOLT-ERROR: stoke-get-stat is specific to X86

值得注意的是错误措辞的差异:--frame-opt--three-way-branch--jt-footprint-reduction--memcpy1-spec 报的是 “supported only on X86”(X86 独有能力),而 --indirect-call-promotion 报的是 “disabled on AArch64”(在 AArch64 上被显式禁用,对应 icp.cicp-inline.c 测试了其余允许路径下的行为)。--simplify-rodata-loads 未在该测试中出现,属于文档声明的未实现项。

面向 AArch64 的实操建议

结合上述状态,可以在 AArch64 二进制上采用如下稳妥路线(均以文档与仓库证据为准):

  1. 编译期:始终保留重定位——clang -O2 -g --emit-relocs(或链接时 -Wl,-q),这是所有 BOLT 布局优化的前提;
  2. 第一轮:开启收益最大的三类代码布局优化——--reorder-functions--reorder-blocks--split-functions(策略默认 profile2);
  3. 第二轮:叠加 --align-blocks--peepholes--inline-small-functions(注意 --inline-small-functions-bytes 上限)与 --icf=safe
  4. 按条件启用--inline-memcpy(接受 >64 字节拷贝不被处理的现实)、--plt(确认二进制为即时绑定或可加 -znow 重链)、--hugify(确认入口点可识别且不与 --instrument 混用)、--reorder-data(确认未同时启用 move/split/aggressive)、--split-strategy=cdsplit(必须同时加 --compact-code-model);
  5. 避免使用:不可用清单中的标志,它们会在 AArch64 上直接报 BOLT-ERROR,具体报错文本可对照 unsupported-passes.test 确认。

延伸阅读:仓库中可继续深入的路径

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384