PassBuilder 不支持直接注册新流水线,必须通过 registerPipelineStartEPCallback、registerOptimizerLastEPCallback 或 registerPipelineParsingCallback 三类扩展点显式注入;需注意类型匹配、符号链接、PassRegistry 注册及幂等性。

PassBuilder 本身不支持直接注册新流水线
你不能像往常那样用 RegisterStandardPasses 或全局静态构造函数把自定义流水线“塞进” PassBuilder 的默认调度中。它的设计是显式、按需、模块化的:所有流水线(Pipeline)必须通过 PassBuilder::registerPipelineStartEPCallback、registerVectorizerStartEPCallback 等回调点,或在构建 PassBuilder 实例后手动调用 registerPipelineParsingCallback 来注入。硬编码进默认流程会破坏其可组合性和跨目标一致性。
注册自定义流水线的三个关键入口点
实际开发中,最常用且可控的方式是利用 PassBuilder 提供的三类 EP(Extension Point)回调:
-
registerPipelineStartEPCallback:在每个优化层级(如O2)的最开始插入 pass,适合做 IR 预处理或全局上下文初始化 -
registerOptimizerLastEPCallback:在标准优化链末尾插入,适合做最终校验、统计或 post-pass 清理 -
registerPipelineParsingCallback:用于支持命令行参数(如-passes=foo,bar,my-custom-pipeline),必须手动解析字符串并添加到ModulePassManager或FunctionPassManager
注意:registerVectorizerStartEPCallback 这类专用点只在启用向量化时触发,不要误用于通用优化逻辑。
注册时最容易踩的坑
很多人在 registerPipelineParsingCallback 里直接 new 一个 MyCustomPipeline 并 add 进去,结果运行时报 Pass not found: my-custom-pipeline。根本原因是:
- 没在
PassRegistry::getPassRegistry()中注册 pass 类型名,导致parsePassPipeline找不到对应构造器 - 忘记在 CMakeLists.txt 里把自定义 pass 所在的库链接进
LLVMHello或你的工具(比如opt),导致符号未定义 - 用了
FunctionPass却注册到ModulePassManager,或反过来,类型不匹配引发断言失败 - 在
registerPipelineStartEPCallback中试图访问尚未生成的TargetLibraryInfo或LoopInfo,导致空指针解引用
一个最小可行注册示例
假设你写了一个叫 MySimplifyPass 的 FunctionPass,想让它在 -O2 下自动启用:
// 在你的 Pass 实现文件末尾
static llvm::PassBuilder::OptimizationLevel getOptLevelForMyPass() {
return llvm::PassBuilder::OptimizationLevel::O2;
}
static void registerMyPass(llvm::PassBuilder &PB) {
PB.registerPipelineStartEPCallback(
[](llvm::ModulePassManager &MPM, llvm::PassBuilder::OptimizationLevel Level) {
if (Level == getOptLevelForMyPass()) {
MPM.addPass(llvm::createModuleToFunctionPassAdaptor(MySimplifyPass()));
}
});
}
// 必须导出这个函数,供 LLVM 初始化时调用
extern "C" LLVM_ATTRIBUTE_WEAK void
llvm_register_passes(llvm::PassBuilder &PB) {
registerMyPass(PB);
}
关键点:不是靠宏或静态变量自动注册;必须确保该函数被链接进 libLLVM.so 或你的 opt 可执行体;createModuleToFunctionPassAdaptor 是桥接 FunctionPass 到 ModulePassManager 的必要封装。
复杂点在于,不同优化级别(O0/O1/O2/O3)触发的 EP 不同,且 PassBuilder 实例可能被多次创建(比如每次 opt -passes=... 调用都新建一个),所以注册逻辑必须幂等、无状态、不依赖单例全局变量。

















