PreservedAnalyses 必须显式返回,New PM 默认全失效而非全保留;FunctionPass 可用 preserve<DominatorTreeAnalysis>() 安全保留未修改 CFG 的分析,漏写或误用类名将导致重复计算或错误优化。

PreservedAnalyses 必须显式返回,不能默认保留
LLVM New Pass Manager(从 14 开始默认启用)要求每个 Pass 显式声明哪些分析结果仍有效,PreservedAnalyses 不是“默认全保留”,而是“默认全失效”。如果你不返回 PreservedAnalyses::all() 或针对性保留,下游 Pass 就会重新运行对应分析,造成重复开销。
FunctionPass 中保留某个分析的典型写法
比如你写了一个 FunctionPass,只读取 DominatorTreeAnalysis,没修改 CFG,那就可以安全保留它。关键点在于:必须用 getPassID() 获取该分析的类型 ID,并调用 preserve<T>()。
示例代码片段:
PreservedAnalyses MyFunctionPass::run(Function &F, FunctionAnalysisManager &AM) {
// ... 执行逻辑,未改动 CFG 或函数签名
PreservedAnalyses PA;
PA.preserve<DominatorTreeAnalysis>(); // 显式保留
PA.preserveSet<CFGAnalyses>(); // 保留整个 CFG 相关分析集
return PA;
}
常见错误包括:
- 漏掉
PA.preserve<...>(),导致DominatorTree被丢弃,后续 Pass 被迫重建 - 误用
PA.preserve<DominatorTree>()(这是旧版类名,New PM 要求用DominatorTreeAnalysis) - 对已修改的函数仍调用
PreservedAnalyses::all(),掩盖了真实副作用,引发后续优化错误
ModulePass 和 LoopPass 的保留策略差异
ModulePass 影响范围更大,通常只能保留极少数全局不变的分析(如 ModuleAnalysisManager 自身),多数情况下建议只保留 AnalysisManagerBase::Invalidator 相关项或直接返回 none();而 LoopPass 若只修改 loop 内部指令,可考虑保留 LoopInfoAnalysis,但需确认未移动 loop header 或破坏 nesting 关系。
性能影响很实际:一次 LoopInfo 重建在大型函数中可能耗时毫秒级,而频繁触发会导致 -O3 编译时间明显上涨。
调试时怎么验证 PreservedAnalyses 是否生效
加 -debug-pass-manager 运行 opt,观察日志里某分析是否被标记为 “reused” 还是 “invalidated”。也可以在分析类的 run() 方法开头打日志——如果没被保留,它会被反复调用。
容易忽略的一点是:PreservedAnalyses 只影响 Analysis Manager 的缓存行为,不保证 IR 结构真的没变;若 Pass 实际修改了 CFG 却谎称保留 DominatorTree,后续基于该树的优化(如 LICM)可能生成错误代码。

















