优化后反而更慢的主因是pass未实际生效或后端不支持ir变换。需验证pass触发、检查spill count、统计汇编指令增长、确认循环canonical form、测试机器码生成及isel日志,并查阅目标架构限制文档。

为什么优化后反而更慢?先确认是不是 Pass 真的在生效
LLVM Pass 编译通过、opt -load 不报错,不等于它真正在改 IR。很多“变慢”其实是 Pass 没触发,或者只在极少数函数里跑了一次,而你误以为全局生效了。
最简单的验证方式:在 runOnFunction 开头加一行 llvm::errs() ,然后用 <code>opt -load ./MyPass.so -mypass -S input.ll 2>&1 | head -20 看输出。如果没打印、或只打了一两个函数名,说明 Pass 没覆盖到热点路径——后面所有性能分析都白忙。
- 检查
getAnalysisUsage是否误加了setPreservesAll(),这会让 LLVM 跳过后续所有依赖此 Pass 的优化 - 确认 Pass 注册时用了正确的类型:FunctionPass 对应单个函数,ModulePass 才能跨函数做全局替换;用错类型会导致 runOnFunction 根本不被调用
- 如果用了
opt -O2 -load ./MyPass.so,注意 Pass 默认插在-O2管线末尾,可能被后续的InstructionCombining或GVN把你的优化结果又抹掉了
IR 修改是否引入了额外开销
自定义 Pass 常见的“变慢”不是因为没优化,而是加了不该加的东西。比如手动 hoist 一个计算,却忘了它原本是常量折叠的;或者替换了 call 指令,但新 call 的函数没有 nounwind 或 readonly 属性,导致 LLVM 不敢做 LICM 或 DCE。
关键检查点:
- 用
opt -S -print-after-all input.ll 2>&1 | grep -A5 -B5 "YourPassName"抽出 Pass 前后的 IR 片段,对比是否多出了load、store、call或未被消除的phi节点 - 特别留意你插入的
getelementptr:如果 base 是 stack allocation(%alloca),而你又没加inbounds,LLVM 会插入运行时边界检查(即使目标平台不支持) - 避免在循环内生成新的
GlobalVariable或ConstantArray——它们不会被自动 deduplicate,每次实例化都增加指令数和缓存压力
寄存器压力与指令调度被破坏
LLVM 中端优化(如 LoopVectorize、LICM)高度依赖寄存器可用性。你手写的 Pass 如果大量引入新虚拟寄存器(比如把多个 scalar 计算展开成独立 add、mul),可能让后端寄存器分配器被迫溢出(spill)到栈,实际性能反降。
验证方法:
- 用
llc -march=x86-64 -debug-pass=Structure input.ll 2>&1 | grep "Register Allocator"看 spill count 是否激增 - 对比前后汇编:用
llc -march=x86-64 -o - input.ll | wc -l统计指令行数;增长超过 15% 就值得警惕 - 如果你的 Pass 插入了大量控制流(如
br+phi),检查是否无意中打破了循环的 canonical form,导致后续LoopRotate或IndVarSimplify失效
别忽略后端阶段的隐式惩罚
有些优化在 IR 层看着很美,一到机器码就翻车。典型例子:你在 IR 里把 int32 乘法拆成四个 int8 DP4A 调用,但目标后端(比如 llc -march=amdgcn)根本不认识这个 intrinsic,最终 fallback 成 scalar 指令+循环,比原来还慢三倍。
必须做的交叉验证:
- 用
llc -march=your-target -mcpu=your-cpu -o /dev/null input.ll测试能否成功生成机器码;失败说明后端不支持你引入的 IR 模式 - 启用后端调试:
llc -march=x86-64 -debug-only=isel input.ll 2>&1查看指令选择(ISel)阶段是否卡在 pattern match 上 - 如果用了自定义 intrinsic,确认
llvm/lib/Target/YourTarget/YourTargetIntrinsics.td已正确定义,且 TableGen 生成了对应 lowering 逻辑
真正难排查的慢,往往卡在 Pass 和后端之间那层薄薄的语义鸿沟里:IR 合法,编译通过,但后端看不懂你的意图,只能保守处理。这时候与其硬调 Pass,不如先查 llvm/docs/CodeGenerator.html 里目标架构的限制文档。











