Pass Manager 的功能
- 收集一系列可以在不同的IR粒度上执行的 passes 。
- 规划 transformation passes 和 analysis passes 的执行。
- 管理 analysis passes 生成的结果。为了提高效率, 要在多个 pass 之间共享分析结果,并且在分析结果 invalidated 后,即时进行更新。
pass 可以在 module,CGSCC, function, loop 这些抽象的粒度上执行。
The legacy pass manager
经典的 pass manager 要求每个 pass 声明它需要和保留的 analyses
不足
缺少了一些提供更好的优化条件的能力, 最典型的是对内联函数使用函数分析结果的能力( 原文:However, there were some missing features required for better optimization opportunities, most notably the ability to use function analysis results for arbitrary functions from the inliner. )
The new pass manager
设计
不需要每个 pass 声明它所需的 analyses, pass manager (PM) 不再负责安排 analysis 的运行而是分离出了 analysis manager。
普通的 pass 可以自己请求 analyses manager 运行指定的 analysis。
当 pass 遇到不合法的 analysis cache 时,交给 pass manager, pass manager 再调用 analysis manager 进行处理
新 PM 还将区分 module,CGSCC, function, loop 这 4 个不同粒度的 Pass。这让 pass 之间的关系更清楚。并且分别提供了 module,CGSCC, function, loop 4个粒度上的 analysis manager。
在 新 PM 中的执行顺序:
module (-> CGSCC) -> function -> loop
新 PM 可以根据一个 pipeline 的文本描述来控制 pass 的添加。 这是由于保存了 pass ID 到 构造器的映射。
在新PM只允许下级的 pass 访问上级的 analysis 但不是允许进行调用。这加强了并发 pass 的能力。
使用
pass manager 的创建
// 创建对应不同粒度的 analysis manager
// 必须以同样的顺序声明这些 manager, 这是为了让他们以正确顺序被析构
LoopAnalysisManager LAM;
FunctionAnalysisManager FAM;
CGSCCAnalysisManager CGAM;
ModuleAnalysisManager MAM;
// 创建 new pass manager 的 builder
PassBuilder PB;
// 将 pass manager 和对应的各个 analysis manager 进行绑定
PB.registerModuleAnalyses(MAM);
PB.registerCGSCCAnalyses(CGAM);
PB.registerFunctionAnalyses(FAM);
PB.registerLoopAnalyses(LAM);
PB.crossRegisterProxies(LAM, FAM, CGAM, MAM);
// 创建 pass manager 实例
// 这里指定了 -O2 优化 pipeline.
ModulePassManager MPM = PB.buildPerModuleDefaultPipeline(OptimizationLevel::O2);
// 优化名为 MyModule 的 IR
MPM.run(MyModule, MAM);
添加 pass 到 pass manager
要注意的是添加 pass 时, pass 类型和 manger 类型一定要对应。
llvm 也提供了如 createFunctionToLoopPassAdaptor 的函数来对 pass 类型进行转换。 如将 loop pass 转为 function pass 那么对于 function 中的所有 loop 该 loop pass 都会执行一次。
FunctionPassManager FPM;
// InstSimplifyPass is a function pass
FPM.addPass(InstSimplifyPass());
// LoopRotatePass is a loop pass
FPM.addPass(createFunctionToLoopPassAdaptor(LoopRotatePass()));
添加 pass 到 pipeline
用 PassBuilder::buildPerModuleDefaultPipeline() 可以根据指定的优化等级来添加 pass 到 pipeline。
也可以将特定的 pass 插入 pipeline 的特定部分:
PassBuilder PB;
PB.registerPipelineStartEPCallback([&](ModulePassManager &MPM,
PassBuilder::OptimizationLevel Level) {
MPM.addPass(FooPass());
};
这段代码在 pipeline 的开始处添加了 FooPass。
前端添加 pass 的例子可以看 Clang 的BackendUtil.cpp
后端添加 pass 的例子可以看
AMDGPUTargetMachine::registerPassBuilderCallbacks()
pass plugin 也可以添加 pass 到 pipeline。 opt -load-pass-plugin=path/to/plugin.so 就可以加载 pass plugin。
使用 analysis
当一个 pass 运行时, 它会持有一个 analysis manager。对于一个 analysis 的请求, analysis 会检查是否已经计算过该 analysis 并且结果仍有效, 如有效则返回 cache, 否则会通过调用 analysis 的 run 方法来构造新的结果, 缓存并返回。
pass 只持有相同粒度的 analysis manager, 比如 function pass 只能查看 function analysis。 对于需要查看不同粒度 analysis 的情况, 可以使用 proxy 来获取不同粒度上的 analysis manager。
这是一个从 CGSCCAnalysisManager 获取 FunctionAnalysisManager 的例子。使用 getResult 和 getManager 方法
FunctionAnalysisManager &FAM =
AM.getResult<FunctionAnalysisManagerCGSCCProxy>(InitialC, CG)
.getManager();
这是一个从 CGSCCAnalysisManager 获取 ModuleAnalysisManager 和缓存的 analysis 结果的例子。使用getResult 和 getCachedResult 方法。
const auto &MAMProxy =
AM.getResult<ModuleAnalysisManagerCGSCCProxy>(InitialC, CG);
FooAnalysisResult *AR = MAMProxy.getCachedResult<FooAnalysis>(M);
注意要求外层 IR analysis manager 计算 analysis 是不允许的, 原因如下:
- 内层调用外层分析可能导致平方级的时间复杂度
- 避免多线程 pass 的不确定性
例外是 loop passes 可以调用作为LoopStandardAnalysisResults参数传入的 function analysis。
处理不合法的 analysis
许多 analysis manager 代码的复杂度都是为了高效地验证 analysis result 的合法性。
处理不合法的 analysis 的一种方式是清除, 一般在 analysis 对应的 IR 不存在时使用此方式。
典型的一个 pass 处理不合法 analysis 的方式是声明它要保留什么 analyses 类型以及不要保留的analyses 类型。为了标记那些不再合法的 analysis, pass 可以返回一个 PreservedAnalyses, 里面包含了不合法的 analyses。例子如下:
// 没有做任何会影响 analyses 的变换
return PreservedAnalyses::all();
// 做了变换并且没有更新 analyses, 所有 analysis 可能都非法了
return PreservedAnalyses::none();
// 在变换的同时更新了 dominator tree 的信息,DominatorAnalysis 合法, 但其它 analysis 可能是非法的
PreservedAnalyses PA;
PA.preserve<DominatorAnalysis>();
return PA;
// 没有改变控制流, 任何只关心控制流的analyses都是合法的
PreservedAnalyses PA;
PA.preserveSet<CFGAnalyses>();
return PA;
pass manager 会对返回的 PreservedAnalyses 调用对应粒度的 analysis manager 的 invalidate 方法。
在 pass 也可以手动调用 analysis manager 的 invalidate 方法
FooModulePass::run(Module& M, ModuleAnalysisManager& AM) {
auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
// Invalidate all analysis results for function F1.
FAM.invalidate(F1, PreservedAnalyses::none());
// Invalidate all analysis results across the entire module.
AM.invalidate(M, PreservedAnalyses::none());
// 如果在 M 中直接删去了 F2 , 那么可以直接删除 analysis manager 中 F2 的入口
FAM.clear(F2);
...
}
一些特定的 analysis 提供了部分更新的方法来避免重新计算的高成本。
实现 analysis invalidation
一个 analysis 可以实现 invalidate()方法, 这样可以在 invalidation时可以更保守。比如:
bool FooAnalysisResult::invalidate(Function &F, const PreservedAnalyses &PA,
FunctionAnalysisManager::Invalidator &) {
auto PAC = PA.getChecker<FooAnalysis>();
// the default would be:
// return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>());
return !(PAC.preserved() || PAC.preservedSet<AllAnalysesOn<Function>>()
|| PAC.preservedSet<CFGAnalyses>());
}
这段代码的含义是
在以下这些情况下:
PreservedAnalyses
中含有FooAnalysis ( PAC.preserved() )
PreservedAnalyses 含有所有 analyses ( PAC.preserved() )
PreservedAnalyses 含有所有 function analyses ( PAC.preservedSet<AllAnalysesOn<Function>>() )
PreservedAnalyses 含有所有只关心 CFG 的 analyses ( PAC.preservedSet<CFGAnalyses>() )
FooAnalysisResult 就不应该是 Invalidated。
有些 analysis 一般不会 invalid, 可使用以下代码:
bool FooAnalysisResult::invalidate(Function &F, const PreservedAnalyses &PA,
FunctionAnalysisManager::Invalidator &) {
// 检查是否被明确地被 invalidated ,否则保持原有结果
auto PAC = PA.getChecker<FooAnalysis>();
return !PAC.preservedWhenStateless();
}
如果有些 analysis 依赖其它的 analysis , 可使用以下代码:
bool FooAnalysisResult::invalidate(Function &F, const PreservedAnalyses &PA,
FunctionAnalysisManager::Invalidator &Inv) {
auto PAC = PA.getChecker<FooAnalysis>();
if (!PAC.preserved() && !PAC.preservedSet<AllAnalysesOn<Function>>())
return true;
// Check transitive dependencies.
return Inv.invalidate<BarAnalysis>(F, PA) ||
Inv.invalidate<BazAnalysis>(F, PA);
}
opt
在 opt 调用 pass 时, 要表达出 pass 之间的关系, 如下, 我们要将一个 funtion pass 通过 function() 打包成一个 module pass:
opt -passes='function(no-op-function),no-op-module' /tmp/a.ll -S
-debug-pass-manager 可以显示出执行顺序
opt --print-passes 可以查看所有可用的 pass 和 analyses
为了确保一个名为 foo 的 analysis 在一个 pass 之前执行, 可以在 pass pipeline 中添加 require<foo>
相关类
PassBuilder
用于生成 pass, pass manager, 管理 analysis manager 和 pass pipeline
属性
public
struct PipelineElement {
StringRef Name;
std::vector<PipelineElement> InnerPipeline;
};
private
TargetMachine *TM | 用于保存目标机器的信息 |
PipelineTuningOptions PTO | 流水线优化选项 |
std::optional<PGOOptions> PGOOpt | 表示基于性能概要文件的优化选项 |
PassInstrumentationCallbacks *PIC | 用于测量和监视编译器优化流程的回调函数集合 |
还有一系列用于保存回调函数的 SmallVector<function<foo>>
// Extension Point callbacks
SmallVector<std::function<void(FunctionPassManager &, OptimizationLevel)>, 2>
PeepholeEPCallbacks;
SmallVector<std::function<void(LoopPassManager &, OptimizationLevel)>, 2>
LateLoopOptimizationsEPCallbacks;
SmallVector<std::function<void(LoopPassManager &, OptimizationLevel)>, 2>
LoopOptimizerEndEPCallbacks;
SmallVector<std::function<void(FunctionPassManager &, OptimizationLevel)>, 2>
ScalarOptimizerLateEPCallbacks;
SmallVector<std::function<void(CGSCCPassManager &, OptimizationLevel)>, 2>
CGSCCOptimizerLateEPCallbacks;
SmallVector<std::function<void(FunctionPassManager &, OptimizationLevel)>, 2>
VectorizerStartEPCallbacks;
// Module callbacks
SmallVector<std::function<void(ModulePassManager &, OptimizationLevel)>, 2>
OptimizerEarlyEPCallbacks;
SmallVector<std::function<void(ModulePassManager &, OptimizationLevel)>, 2>
OptimizerLastEPCallbacks;
SmallVector<std::function<void(ModulePassManager &, OptimizationLevel)>, 2>
FullLinkTimeOptimizationEarlyEPCallbacks;
SmallVector<std::function<void(ModulePassManager &, OptimizationLevel)>, 2>
FullLinkTimeOptimizationLastEPCallbacks;
SmallVector<std::function<void(ModulePassManager &, OptimizationLevel)>, 2>
PipelineStartEPCallbacks;
SmallVector<std::function<void(ModulePassManager &, OptimizationLevel)>, 2>
PipelineEarlySimplificationEPCallbacks;
SmallVector<std::function<void(ModuleAnalysisManager &)>, 2>
ModuleAnalysisRegistrationCallbacks;
SmallVector<std::function<bool(StringRef, ModulePassManager &,
ArrayRef<PipelineElement>)>,
2>
ModulePipelineParsingCallbacks;
SmallVector<
std::function<bool(ModulePassManager &, ArrayRef<PipelineElement>)>, 2>
TopLevelPipelineParsingCallbacks;
// CGSCC callbacks
SmallVector<std::function<void(CGSCCAnalysisManager &)>, 2>
CGSCCAnalysisRegistrationCallbacks;
SmallVector<std::function<bool(StringRef, CGSCCPassManager &,
ArrayRef<PipelineElement>)>,
2>
CGSCCPipelineParsingCallbacks;
// Function callbacks
SmallVector<std::function<void(FunctionAnalysisManager &)>, 2>
FunctionAnalysisRegistrationCallbacks;
SmallVector<std::function<bool(StringRef, FunctionPassManager &,
ArrayRef<PipelineElement>)>,
2>
FunctionPipelineParsingCallbacks;
// Loop callbacks
SmallVector<std::function<void(LoopAnalysisManager &)>, 2>
LoopAnalysisRegistrationCallbacks;
SmallVector<std::function<bool(StringRef, LoopPassManager &,
ArrayRef<PipelineElement>)>,
2>
LoopPipelineParsingCallbacks;
// AA callbacks
SmallVector<std::function<bool(StringRef Name, AAManager &AA)>, 2>
AAParsingCallbacks;
方法
- 注册 analyses 相关: registerFooAnalyses 对 Foo analyses manager 注册所有标准的 analyses, 同时用户可以手动添加其它的 analyses
- 构建 pipeline 相关
- 注册回调函数相关, 这些相关的回调时机也有助于理解 llvm 的工作过程
- 调用回调函数相关
PassManger
模板类, 模版信息如下:
template <typename IRUnitT,
typename AnalysisManagerT = AnalysisManager<IRUnitT>,
typename... ExtraArgTs>
class PassManager : public PassInfoMixin<
PassManager<IRUnitT, AnalysisManagerT, ExtraArgTs...>> {
...}
run 方法
一个 PassManager 本身也是一个可以在特定类型的IR上运行的 pass。 它也实现了 run 方法。
/// 在特定类型的 IR 上运行所有 pass
/// ExtraArgs 会传递给每一个 pass.
PreservedAnalyses run(IRUnitT &IR, AnalysisManagerT &AM,
ExtraArgTs... ExtraArgs) {
PreservedAnalyses PA = PreservedAnalyses::all();
// 向 analysis manager 请求 PassInstrumentationAnalysis 信息
// 之后会将 PI 用于运行 instrumenting 的回调函数
PassInstrumentation PI =
detail::getAnalysisResult<PassInstrumentationAnalysis>(
AM, IR, std::tuple<ExtraArgTs...>(ExtraArgs...));
// 在需要时, 将 debug 信息转为 DPValue 形式
bool ShouldConvertDbgInfo = shouldConvertDbgInfo(IR);
if (ShouldConvertDbgInfo)
doConvertDbgInfoToNew(IR);
// 遍历所有 pass
for (auto &Pass : Passes) {
// 检查 PI 的 BeforePass 回调函数, 如回调返回 false 停止本次 pass 执行
if (!PI.runBeforePass<IRUnitT>(*Pass, IR))
continue;
// 调用 Pass 的 run 方法
PreservedAnalyses PassPA = Pass->run(IR, AM, ExtraArgs...);
//更新 analysis manager 的 invalidated 状态
AM.invalidate(IR, PassPA);
// 调用 PI 的 runAfterPass 回调函数
PI.runAfterPass<IRUnitT>(*Pass, IR, PassPA);
// 计算交集得出该 pass manager 的保留集合
PA.intersect(std::move(PassPA));
}
if (ShouldConvertDbgInfo)
doConvertDebugInfoToOld(IR);
// 在每次循环结尾, invalidation 都被处理了, 所以余下的 analysis 都是要保留的
PA.preserveSet<AllAnalysesOn<IRUnitT>>();
return PA;
}
属性
protected
Passes 保存了所有 pass
using PassConceptT =
detail::PassConcept<IRUnitT, AnalysisManagerT, ExtraArgTs...>;
std::vector<std::unique_ptr<PassConceptT>> Passes;
ModulePassManager 就是 PassManager<Module>
using ModulePassManager = PassManager<Module>;
AnalysisManager
PassManager.h
惰性运行 analysis 并缓存它们的结果。
方法
clear()
适用对应 IR 被删除的情况下 删除对应的 analysis 缓存
getResult()
获取 analysis 结果, 在缓存合法时会使用缓存。实际工作由getResultImpl 函数完成
getResultImpl()
getCacheResult()
获取 anlysis 结果的缓存或 null , 不会运行 analysis
registerPass()
invalidate()
lookUpPass()
属性
private
类型信息
using AnalysisPassMapT =
DenseMap<AnalysisKey *, std::unique_ptr<PassConceptT>>;
using AnalysisResultListT =
std::list<std::pair<AnalysisKey *, std::unique_ptr<ResultConceptT>>>;
using AnalysisResultListMapT = DenseMap<IRUnitT *, AnalysisResultListT>;
using AnalysisResultMapT =
DenseMap<std::pair<AnalysisKey *, IRUnitT *>,
typename AnalysisResultListT::iterator>;
属性 | 含义 |
AnalysisPassMapT AnalysisPasses; | ID 到analysis pass 映射 |
AnalysisResultListMapT AnalysisResultLists; | 从 IR 单元到 analysis result list 的映射 |
AnalysisResultMapT AnalysisResults; | 从 analysis ID 和 IR 单元 到特定缓存的 analysis result 的映射 |
ModuleAnalysisManager 就是 AnalysisManager<Module>
using ModuleAnalysisManager = AnalysisManager<Module>;
PreservedAnalyses
PassManager.h
可以表达:
- 具体的在 transformation pass 运行后可以保留的 analyses。( preserved 函数 )
- 抽象的可以保留的 analyses 集合。 ( preservedSet 函数 )
- 某些必须放弃的analysis
使用了 AnalysisSetKey 类型来记录 analysis 和 analysis set。对一个 analysis 可以调用它的静态 ID 方法来得到它的 AnalysisSetKey。
属性
私有
如果一个 analysis 的 set 既在 PreservedIDs 和 NotPreservedAnlysisIDs 时, 这个 anlysis会判定为 Not perserve。
static AnalysisSetKey AllAnalysesKey; | 用于索引所有analyses |
SmallPtrSet<void *, 2> PreservedIDs; | 保留的 analyses 和 anlyses 的 ID |
SmallPtrSet<AnalysisKey *, 2> NotPreservedAnalysisIDs; | 明确不保留的 analyses 和 anlyses set |
AnalysisResultModel
MISC
在 llvm 中, 不存在 pass 的接口, 任何实例只要有可以接收IR的 run 方法就可以作为一个 pass 进行使用。