本文原发表于个人博客,分享到看雪,请多多指点。
本文从 llvmGetPassPluginInfo 出发,深入解读 PassBuilder 的各个 API,记录从 LegacyPassManager 迁移到 NewPassManager 的过程。本文使用llvm13,低版本相关API会不一致。
根据背景知识,llvm13以上通过 -fpass-plugin=libPass.so
指定被加载的 library,加载后调用该共享库的导出符号 llvmGetPassPluginInfo
获取相关信息,该函数需要返回结构体 llvm::PassPluginLibraryInfo
,位于 llvm/include/llvm/Passes/PassPlugin.h
。
直接看范例吧,很好理解。
第一个参数传递版本号,和编译依赖的llvm环境有关,从环境中取值即可。加载时会检查clang版本号和plugin版本号是否一致,不一致会加载失败。
第二个参数是插件名字,随便传。
第三个参数是插件版本号,随便传。
第四个参数非常重要,是一个回调函数,clang加载该插件后会调用它,传递 PassBuilder &PB
这个关键对象。
问题转化为了:PassBuilder &PB
如何使用。
PassBuilder 声明位于 llvm/include/llvm/Passes/PassBuilder.h
,有大量函数供调用,总结如下:
阅读 register(xxx)EPCallback 系列的声明,EP表示ExtensionPoint,与 llvm/include/llvm/Transforms/IPO/PassManagerBuilder.h
的 ExtensionPointTy 对应,ExtensionPointTy
这个枚举类,那味道可太熟悉了,LegacyPassManager的注册时刻。
随便选取一个,观察其实现,传入一个回调函数,将该函数添加到列表里。每个API传入的回调函数返回值都是 void,第一个参数是一个 PassManager,第二个参数是 OptimizationLevel,很容易理解,pass开发者可以根据不同的优化等级走不同的逻辑,并使用 PassManager 完成 pass 的注册。
对我们有用的部分,总结如下表,这里有一个大变更,最早的回调 registerPipelineStartEPCallback
只能注册 ModulePass
。
ModulePassManager
是llvm传给plugin的,只提供一个API,addPass
,传递一个对象,但这里很复杂。
经过一些测试,总结了一个最小的样例,先看结构,再分析原理。
现在不需要继承任何类了,编译时llvm会通过模板来检查 TestPass 的方法,共有 3 个。
有了整体认知后,介绍相关的模板,llvm/include/llvm/IR/PassManager.h
。
这个对象由llvm传递给plugin,查看发现它是一个模板 PassManager
。
而 PassManager
也是一个模板,由 PassInfoMixin
提供一个 name
。
IRUnitT
就是传入的 llvm::Module
,然后推断出 AnalysisManagerT
的类型为 AnalysisManager<IRUnitT>
,也就是AnalysisManager<Module>
,恰好是 ModuleAnalysisManager
。
AnalysisManager
也是一个模板类,就不展开了。
继续看 addPass
的实现,有两个重载,第一个是传入 pass 时,将其加入到列表中;第二个是传入另一个 PassManager
时,将其内部的 passes
全部加入到列表中。
重点关注第一个,detail::PassModel
位于 llvm/include/llvm/IR/PassManagerInternal.h
,是一个模板类。IRUnitT
是传入的 llvm::Module
,PassT
是传入的用户自定义的结构体,PreservedAnalysesT
是传入的 PreservedAnalyses
,AnalysisManagerT
是传入的ModuleAnalysisManager
。
它的构造方法拿到用户自定义的结构体, run 方法调用传入的pass的run方法,需要满足 IRUnitT
和 AnalysisManagerT
的类型约束,这和上文提到的
ModulePass 需要这里是 llvm::Module
和 ModuleAnalysisManager
,FunctionPass 需要这里是 Function
和 FunctionAnalysisManager
。
相互印证。它的 name
方法调用传入的结构体的 static name
方法。它的 passIsRequiredImpl
会根据传入的结构体的 isRequired
是否声明而定,未声明时默认为 false,已声明则调用传入的结构体的 static isRequired
而 PassModel
继承 PassConcept
,PassConcept
也是模板,整体没什么东西,全部都是关键方法。
经过多层的模板套娃,我们已经完完全全理清楚了 ModulePassManager.addPass
的原理,开发起来更有底气!
通过 PassInfoMixin
可以省掉 static StringRef name()
。
此时还差一个细节,返回值 PreservedAnalyses
是什么?阅读文档后认为是这样,不确定对不对:
这时,相当于 ModulePass
的注册方式已搞定,接下来是 FunctionPass
。
直接贴方案吧,不可挑剔的方案,使用 createModuleToFunctionPassAdaptor
,来自 https://groups.google.com/g/llvm-dev/c/e_4WobR9WP0 。只要保证下文的 HelloWorld
有 PreservedAnalyses run(Function &F, FunctionAnalysisManager &)
方法就行。
registerPipelineParsingCallback
有很多个重载,其有 FunctionPassManager
和 ModulePassManager
,区别不大。回调函数的第一个参数是传入的 Pass 名字,只要有 Pass 需要被执行,就会传递进来,因此需要判断是不是在调用自己,有兴趣的自己试试吧。
搞不动了,Skeleton的迁移放在github了, https://github.com/LeadroyaL/llvm-pass-tutorial ,写得很烂。
其他项目懒得搞了,等什么时候不忙了或者有好心人帮忙适配一下。
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 &);
};
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 &);
};
extern
"C"
LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
return
{LLVM_PLUGIN_API_VERSION,
"Skeleton"
,
"1.0.0"
,
[](PassBuilder &PB) {
/
/
xxxxxxx
}};
}
extern
"C"
LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
return
{LLVM_PLUGIN_API_VERSION,
"Skeleton"
,
"1.0.0"
,
[](PassBuilder &PB) {
/
/
xxxxxxx
}};
}
void registerPeepholeEPCallback(
const std::function<void(FunctionPassManager &, OptimizationLevel)> &C) {
PeepholeEPCallbacks.push_back(C);
}
void registerPeepholeEPCallback(
const std::function<void(FunctionPassManager &, OptimizationLevel)> &C) {
PeepholeEPCallbacks.push_back(C);
}
回调函数 |
回调时提供的对象 |
对应 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 |
➜
/
tmp clang
-
fpass
-
plugin
=
libSkeletonPass.so test.c
llvmGetPassPluginInfo
isRequired invoked
Module name
is
test.c!
➜
/
tmp clang
-
fpass
-
plugin
=
libSkeletonPass.so test.c
llvmGetPassPluginInfo
isRequired invoked
Module name
is
test.c!
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
();
}
};
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
();
}
};
extern template
class
PassManager<Module>;
/
/
/
Convenience typedef
for
a
pass
manager over modules.
using ModulePassManager
=
PassManager<Module>;
extern template
class
PassManager<Module>;
/
/
/
Convenience typedef
for
a
pass
manager over modules.
using ModulePassManager
=
PassManager<Module>;
template <typename IRUnitT,
typename AnalysisManagerT
=
AnalysisManager<IRUnitT>,
typename... ExtraArgTs>
class
PassManager : public PassInfoMixin<
PassManager<IRUnitT, AnalysisManagerT, ExtraArgTs...>> {
/
/
xxxxxxxx
}
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
template <typename DerivedT> struct PassInfoMixin {
/
/
/
Gets the name of the
pass
we are mixed into.
static StringRef name() {
static_assert(std::is_base_of<PassInfoMixin, DerivedT>::value,
"Must pass the derived type as the template argument!"
);
StringRef Name
=
getTypeName<DerivedT>();
if
(Name.startswith(
"llvm::"
))
Name
=
Name.drop_front(strlen(
"llvm::"
));
return
Name;
}
};
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
using ModuleAnalysisManager
=
AnalysisManager<Module>;
template <typename IRUnitT,
typename AnalysisManagerT
=
AnalysisManager<IRUnitT>,
typename... ExtraArgTs>
class
PassManager : public PassInfoMixin<
PassManager<IRUnitT, AnalysisManagerT, ExtraArgTs...>> {
/
/
xxxxxxxx
}
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
template <typename DerivedT> struct PassInfoMixin {
/
/
/
Gets the name of the
pass
we are mixed into.
static StringRef name() {
static_assert(std::is_base_of<PassInfoMixin, DerivedT>::value,
"Must pass the derived type as the template argument!"
);
StringRef Name
=
getTypeName<DerivedT>();
if
(Name.startswith(
"llvm::"
))
Name
=
Name.drop_front(strlen(
"llvm::"
));
return
Name;
}
};
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
/
using ModuleAnalysisManager
=
AnalysisManager<Module>;
template <typename IRUnitT,
typename AnalysisManagerT
=
AnalysisManager<IRUnitT>,
typename... ExtraArgTs>
class
PassManager : public PassInfoMixin<
PassManager<IRUnitT, AnalysisManagerT, ExtraArgTs...>> {
template <typename PassT>
std::enable_if_t<!std::is_same<PassT, PassManager>::value>
addPass(PassT &&Pass) {
using PassModelT
=
detail::PassModel<IRUnitT, PassT, PreservedAnalyses, AnalysisManagerT,
ExtraArgTs...>;
Passes.emplace_back(new PassModelT(std::forward<PassT>(Pass)));
}
template <typename PassT>
std::enable_if_t<std::is_same<PassT, PassManager>::value>
addPass(PassT &&Pass) {
for
(auto &P : Pass.Passes)
Passes.emplace_back(std::move(P));
}
}
template <typename IRUnitT,
typename AnalysisManagerT
=
AnalysisManager<IRUnitT>,
typename... ExtraArgTs>
class
PassManager : public PassInfoMixin<
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2022-6-3 09:43
被LeadroyaL编辑
,原因: