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
.下面是主干代码
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<...>
接口类似,如下:
#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
实例
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中移除它们
#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)和循环信息。如下面的代码
#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是否被保留
或许你可以从源码中找到答案 :-)
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2020-2-14 20:08
被微笑明天编辑
,原因: