现代化地编写LLVM Pass -- part II
Writing LLVM Pass in 2018-part II第二部分原文
Analysis —Thing that deserves its own article
在LLVM PassManager中,收集程序的分析任务也被构建为了Passes,它们不会也不应该修改IR内容。而且,和旧版PM相比,在新PM中,分析数据的管理和开发有很大的改变,所以我单独用一篇文章来阐述。这篇文章将会谈论如何用新的 AnalysisManager 来接受分析数据。打开你上手的编辑器,开始吧~ analysis pass在旧版Pass中,你会发现一个重要的特性就是analysis manager和PassManager高度融合。你可以通过 getAnalysis<...> 方法来获取某个分析数据,这也是 Pass 类的成员之一。然而在新版的PassManager中,analysis manager是一个单独的实例,可以在任何地方独立使用。为了让你理解这个特点,让我们在 旧版Pass 中使用 新版 AnalysisManager .下面是主干代码 1
2
3
4
5
6
7
| bool MyFuncPass::runOnFunction(Function& F) override {
PassBuilder PB;
FunctionAnalysisManager FAM;
PB.registerFunctionAnalyses(FAM);
// ...
return false;
}
|
PassBuilder我们很熟悉,需要他来向PM注册所有可用的Pass。在这之后,这里的FunctionAnalysisManager可以独立使用。 AnalysisManager 负责管理所有已注册的analysis Pass和它们的分析结果。比如,缓存一个analysis pass的结果,直到它对应的IR单元被修改。
所以我们应该如何从manager中获取分析结果呢?和旧版Pass中的 getAnalysis<...> 接口类似,如下: 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| #include "llvm/Analysis/AliasAnalysis.h"
bool MyFuncPass::runOnFunction(Function& F) override {
PassBuilder PB;
FunctionAnalysisManager FAM;
PB.registerFunctionAnalyses(FAM);
// How we do in legacy Passes:
AAResultWrapperPass& WrapperPass = getAnalysis<AAResultsWrapperPass>();
AAresults& AAR1 = WrapperPass.getAAResults();
// How we do with new AnalysisManager
AAResults& AAR2 = FAM.getResult<AAManager>(F);
return false;
}
|
上述代码使用AliasAnalysis作为我们想获得的分析数据。如果用旧的语法, 你需要先获取Pass的一个实例,然后用其中的一个成员函数来获取分析结果。在新语法中,你只需要用analysis Pass的类型(此处为 AAManager ),连同你的目标IR单元实例(此处为 Function )来调用 getResult<...> 。
这里指出 getAnalysis 和 getResult 返回类型是不同的。一方面, getAnalysis<T> 的返回类型是 T ,它的模板类型T是你希望的analysis Pass的类型,而不是analysis的结果。另一方面, getResult<T> 中的模板类型T仍代表你希望的analysis pass的类型,但 getResult 的返回类型是 T::Result (T中的Result成员)。这个区别揭示了analyses management设计中的一个重要变化: analysis结果与analysis Pass解耦。这将使得某些管理、数据验证更简单且高效。 AnalysisManager再回头来看新的PassManager. run 方法的第二个参数是对应的IR单元的 AnalysisManager 实例 1
2
3
4
5
6
7
| struct MyNewPass : public PassInfoMixin<MyNewPass> {
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {
AAResults& AAR = FAM.getResult<AAManager>(F);
// ...
return PreservedAnalyses::all();
}
};
|
因此你可以直接使用而不用通过PassBuilder来构建。 analysis data invalidation最后让我们谈论下分析数据的invalidation. 我们打算仅讨论normal Pass中最常用的部分。
PreservedAanlyses 是run方法所需的返回类型,记录着在Pass后仍然有效的一组分析数据。如果你只是想查看IR而不是修改它们,那么所有的分析在这之后都有效,只需要返回 PreservedAnalyses::all() 即可。
但如果你使用一些数据修改了分支的可能性并因此改变了block的频率信息,你需要从被保留的set中移除它们 1
2
3
4
5
6
7
8
9
10
| #include "llvm/Analysis/BlockFrequencyInfo.h"
struct MyNewPass : public PassInfoMixin<MyNewPass> {
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {
// ...Use some profile data to change BasicBlock frequencies...
PreservedAnalyses PA = PreservedAnalyses::all();
PA.abandon<BlockFrequencyAnalysis>();
return PA;
}
};
|
通常我们不是从 PreservedSet 中移除分析结果,而是声明一些被保留的分析。例如,你知道你的pass在函数内不会修改控制流(control flow graph)和循环信息。如下面的代码 1
2
3
4
5
6
7
8
9
10
11
| #include "llvm/Analysis/LoopInfo.h"
struct MyNewPass : public PassInfoMixin<MyNewPass> {
PreservedAnalyses run(Function &F, FunctionAnalysisManager &FAM) {
// ...
PreservedAnalyses PA = PreservedAnalyses::none();
PA.preserve<LoopAnalysis>();
PA.preserveSet<CFGAnalyses>();
return PA;
}
};
|
preserve<...> 方法声明了单个analysis的保留集(通过向其模板类型中传入analysis 类型)。 而preserveSet<...> 有些区别,它会保留一组analyse,你需要传入一个analysis set的类型(不同于Pass的概念)。有很多可用的analysis set的类型,比如此处的 CFGAnalyses 表示所有的控制流相关的analyse.
PS:
还有一些重要话题没有讨论,例如: - 如何写一个analysis pass
- 如何查询一个analysis是否被保留
或许你可以从源码中找到答案 :-)
|