首页
社区
课程
招聘
[原创] llvm NewPassManager API分析及适配方案
发表于: 2022-5-15 22:15 19402

[原创] llvm NewPassManager API分析及适配方案

2022-5-15 22:15
19402

本文原发表于个人博客,分享到看雪,请多多指点。

本文从 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::ModulePassT是传入的用户自定义的结构体,PreservedAnalysesT是传入的 PreservedAnalysesAnalysisManagerT是传入的ModuleAnalysisManager

它的构造方法拿到用户自定义的结构体, run 方法调用传入的pass的run方法,需要满足 IRUnitTAnalysisManagerT 的类型约束,这和上文提到的

ModulePass 需要这里是 llvm::ModuleModuleAnalysisManager,FunctionPass 需要这里是 FunctionFunctionAnalysisManager

相互印证。它的 name 方法调用传入的结构体的 static name 方法。它的 passIsRequiredImpl 会根据传入的结构体的 isRequired 是否声明而定,未声明时默认为 false,已声明则调用传入的结构体的 static isRequired

PassModel 继承 PassConceptPassConcept 也是模板,整体没什么东西,全部都是关键方法。

经过多层的模板套娃,我们已经完完全全理清楚了 ModulePassManager.addPass 的原理,开发起来更有底气!

通过 PassInfoMixin 可以省掉 static StringRef name()

此时还差一个细节,返回值 PreservedAnalyses 是什么?阅读文档后认为是这样,不确定对不对:

这时,相当于 ModulePass 的注册方式已搞定,接下来是 FunctionPass

直接贴方案吧,不可挑剔的方案,使用 createModuleToFunctionPassAdaptor,来自 https://groups.google.com/g/llvm-dev/c/e_4WobR9WP0 。只要保证下文的 HelloWorldPreservedAnalyses run(Function &F, FunctionAnalysisManager &) 方法就行。

registerPipelineParsingCallback 有很多个重载,其有 FunctionPassManagerModulePassManager,区别不大。回调函数的第一个参数是传入的 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编辑 ,原因:
收藏
免费 3
支持
分享
打赏 + 80.00雪花
打赏次数 1 雪花 + 80.00
 
赞赏  Editor   +80.00 2022/06/13 恭喜您获得“雪花”奖励,安全圈有你而精彩!
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//