BOLT for AArch64:默认关闭的优化标志全解及其在源码中的实现依据
本文以 BOLT 官方文档 BOLTAArch64OptimizationStatus.md 为主体,系统梳理在 AArch64 架构上可用、受限与不可用的 BOLT 默认关闭(default-off)优化标志,并结合 AArch64MCPlusBuilder.cpp、BinaryPasses.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-scale、call-score-power、jump-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.s 与 tail-duplication-cache.s(尾部复制)、double_jump.cpp 与 ext-double-jump.s(双跳转窥孔)、inline-small-function-1.s、inline-small-function-2.s(小函数内联)、safe-icf.s(安全 ICF)。小函数内联的字节数上限由 --inline-small-functions-bytes 控制,测试用例覆盖了该阈值附近的边界行为。
受限制支持的标志:必须先满足条件
这部分是本文的重点。文档明确列出 5 个“已实现但需要特定运行时或选项条件”的标志;在未满足条件时启用,BOLT 可能报错或静默不做任何变换。逐条拆解如下:
--inline-memcpy:仅内联已知常量大小的 memcpy,且超过 64 字节会被跳过
文档说明:“仅当拷贝大小是已知常量时才适用;AArch64 会跳过超过 64 字节的大小。”这一限制可以直接在源码中得到证实。在 BinaryPasses.cpp 的 InlineMemcpy::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.cpp(createInlineMemcpy 等目标专用接口),该文件整体职责就是为 BOLT 提供 AArch64 的机器指令构造能力(构建跳转桩、内联序列、返回指令等),这也是 BOLT 能以目标架构插件方式扩展新后端的基础。
--plt=hot|all:需要即时符号绑定
--plt 用于优化经由 PLT 的调用。文档指出它要求即时绑定(immediate binding);如果 BOLT 运行时无法更新二进制的绑定属性,则需要重新链接并加 -znow。AArch64 的 PLT 优化有一组专门的测试:plt-gnu-ld.test、plt-got.test、plt-call.test、plt-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)重排数据段。文档提示:move、split、aggressive 这几个(函数/基本块移动类)选项会关闭数据重排——即两者互斥,启用前需检查命令行中是否同时出现这些选项。
--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.c、icp-inline.c 测试了其余允许路径下的行为)。--simplify-rodata-loads 未在该测试中出现,属于文档声明的未实现项。
面向 AArch64 的实操建议
结合上述状态,可以在 AArch64 二进制上采用如下稳妥路线(均以文档与仓库证据为准):
- 编译期:始终保留重定位——
clang -O2 -g --emit-relocs(或链接时-Wl,-q),这是所有 BOLT 布局优化的前提; - 第一轮:开启收益最大的三类代码布局优化——
--reorder-functions、--reorder-blocks、--split-functions(策略默认profile2); - 第二轮:叠加
--align-blocks、--peepholes、--inline-small-functions(注意--inline-small-functions-bytes上限)与--icf=safe; - 按条件启用:
--inline-memcpy(接受 >64 字节拷贝不被处理的现实)、--plt(确认二进制为即时绑定或可加-znow重链)、--hugify(确认入口点可识别且不与--instrument混用)、--reorder-data(确认未同时启用move/split/aggressive)、--split-strategy=cdsplit(必须同时加--compact-code-model); - 避免使用:不可用清单中的标志,它们会在 AArch64 上直接报
BOLT-ERROR,具体报错文本可对照 unsupported-passes.test 确认。
延伸阅读:仓库中可继续深入的路径
- BOLTAArch64OptimizationStatus.md:本文的主体文档,标志状态表的原始出处;
- AArch64MCPlusBuilder.cpp:AArch64 目标专用的指令构建器,内联 memcpy、跳转桩(veneer/long-jmp stub)等序列在此生成;
- BinaryPasses.cpp:
InlineMemcpy与SpecializeMemcpy1的实现,含 64 字节上限逻辑; - SplitFunctions.cpp:
--split-strategy各策略(含 CDSplit)的拆分实现; - bolt/test/AArch64/:AArch64 目标的功能与回归测试全集,包括 veneer.s、compact-code-model.s、plt-gnu-ld.test 等。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00