-
-
[原创] llvm NewPassManager API分析及适配方案
-
发表于:
2022-5-15 22:15
19612
-
[原创] llvm NewPassManager API分析及适配方案
本文原发表于个人博客,分享到看雪,请多多指点。
本文从 llvmGetPassPluginInfo 出发,深入解读 PassBuilder 的各个 API,记录从 LegacyPassManager 迁移到 NewPassManager 的过程。本文使用llvm13,低版本相关API会不一致。
根据背景知识,llvm13以上通过 -fpass-plugin=libPass.so
指定被加载的 library,加载后调用该共享库的导出符号 llvmGetPassPluginInfo
获取相关信息,该函数需要返回结构体 llvm::PassPluginLibraryInfo
,位于 llvm/include/llvm/Passes/PassPlugin.h
。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
struct PassPluginLibraryInfo {
/ / / The API version understood by this plugin, usually \c
/ / / LLVM_PLUGIN_API_VERSION
uint32_t APIVersion;
/ / / A meaningful name of the plugin.
const char * PluginName;
/ / / The version of the plugin.
const char * PluginVersion;
/ / / The callback for registering plugin passes with a \c PassBuilder
/ / / instance
void ( * RegisterPassBuilderCallbacks)(PassBuilder &);
};
|
直接看范例吧,很好理解。
1
2
3
4
5
6
7
8
9
|
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
return {LLVM_PLUGIN_API_VERSION,
"Skeleton" ,
"1.0.0" ,
[](PassBuilder &PB) {
/ / xxxxxxx
}};
}
|
第一个参数传递版本号,和编译依赖的llvm环境有关,从环境中取值即可。加载时会检查clang版本号和plugin版本号是否一致,不一致会加载失败。
第二个参数是插件名字,随便传。
第三个参数是插件版本号,随便传。
第四个参数非常重要,是一个回调函数,clang加载该插件后会调用它,传递 PassBuilder &PB
这个关键对象。
问题转化为了:PassBuilder &PB
如何使用。
PassBuilder 声明位于 llvm/include/llvm/Passes/PassBuilder.h
,有大量函数供调用,总结如下:
- (xxx)Analyses,与加载pass不相关
- build(xxx)Pipeline,与加载pass不相关
- parsePassPipiline,尝试处理给定的处理串,可能用于测试
- printPassNames,打印所有的Pass
- registerAnalysisRegistrationCallback,与加载pass不相关
- register(xxx)EPCallback,核心API,与Legacy比较像
- registerPipelineParsingCallback,给opt用的,
opt --load-pass-plugin=libPass.so -passes=passName1,passName2
可以触发到
阅读 register(xxx)EPCallback 系列的声明,EP表示ExtensionPoint,与 llvm/include/llvm/Transforms/IPO/PassManagerBuilder.h
的 ExtensionPointTy 对应,ExtensionPointTy
这个枚举类,那味道可太熟悉了,LegacyPassManager的注册时刻。
随便选取一个,观察其实现,传入一个回调函数,将该函数添加到列表里。每个API传入的回调函数返回值都是 void,第一个参数是一个 PassManager,第二个参数是 OptimizationLevel,很容易理解,pass开发者可以根据不同的优化等级走不同的逻辑,并使用 PassManager 完成 pass 的注册。
1
2
3
4
|
void registerPeepholeEPCallback(
const std::function &C) {
PeepholeEPCallbacks.push_back(C);
}
|
对我们有用的部分,总结如下表,这里有一个大变更,最早的回调 registerPipelineStartEPCallback
只能注册 ModulePass
。
回调函数 |
回调时提供的对象 |
对应 ExtensionPointTy |
registerPeepholeEPCallback |
FunctionPassManager |
对应EP_Peephole |
registerLateLoopOptimizationsEPCallback |
LoopPassManager |
对应EP_LoopOptimizerEnd |
registerLoopOptimizerEndEPCallback |
LoopPassManager |
对应EP_LateLoopOptimizations |
registerScalarOptimizerLateEPCallback |
FunctionPassManager |
对应 EP_ScalarOptimizerLate |
registerCGSCCOptimizerLateEPCallback |
CGSCCPassManager |
对应EP_CGSCCOptimizerLate |
registerVectorizerStartEPCallback |
FunctionPassManager |
对应EP_VectorizerStart |
registerPipelineStartEPCallback |
ModulePassManager |
对应EP_EarlyAsPossible |
registerPipelineEarlySimplificationEPCallback |
ModulePassManager |
对应 EP_ModuleOptimizerEarly |
registerOptimizerLastEPCallback |
ModulePassManager |
对应EP_OptimizerLast |
ModulePassManager
是llvm传给plugin的,只提供一个API,addPass
,传递一个对象,但这里很复杂。
经过一些测试,总结了一个最小的样例,先看结构,再分析原理。
1
2
3
4
|
➜ / tmp clang - fpass - plugin = libSkeletonPass.so test.c
llvmGetPassPluginInfo
isRequired invoked
Module name is test.c!
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
PB.registerPipelineStartEPCallback(myCallback);
void myCallback(llvm::ModulePassManager &PM, llvm::PassBuilder::OptimizationLevel Level) {
PM.addPass(TestPass());
}
class TestPass {
public:
static StringRef name() {
errs() << "name invoked\n" ;
return "TestPass" ;
}
static bool isRequired() {
errs() << "isRequired invoked\n" ;
return true;
}
PreservedAnalyses run(Module &M, ModuleAnalysisManager &) {
errs() << "Module name is " << M.getName() << "!\n" ;
return PreservedAnalyses:: all ();
}
};
|
现在不需要继承任何类了,编译时llvm会通过模板来检查 TestPass 的方法,共有 3 个。
-
static StringRef name
必须,表示 pass 的名字。可以使用 PassInfoMixin
模板快速实现该API。
-
static bool isRequired
可选,如果返回true,则该pass不会被跳过。
-
PreservedAnalyses run(Module &M, ModuleAnalysisManager &)
必选,ModulePass 需要这里是 llvm::Module
和 ModuleAnalysisManager
,FunctionPass 需要这里是 Function
和 FunctionAnalysisManager
。
有了整体认知后,介绍相关的模板,llvm/include/llvm/IR/PassManager.h
。
[招生]科锐逆向工程师培训(2025年3月11日实地,远程教学同时开班, 第52期)!
最后于 2022-6-3 09:43
被LeadroyaL编辑
,原因: