llvm 15+ 的 opt 默认不支持 legacypassmanager,必须用 -load 加载插件并采用新 passmanager 注册机制:pass 类需继承 passinfomixin,通过 llvmgetpassplugininfo 中 registerpipelineparsingcallback 注册,且必须使用 -passes="name" 语法调用,不再支持 -name 旧式写法。

LLVM 15+ 的 opt 默认不支持旧版 LegacyPassManager,必须用 -load 显式加载动态库,且 Pass 必须注册为新 PassManager 兼容形式,否则直接报错 “pass ‘xxx’ is not registered”。
opt -load 加载失败的常见原因
很多自定义 Pass 编译后,执行 opt -load ./MyPass.so -my-pass input.ll 却提示找不到 pass 或段错误。根本原因不是路径或符号导出问题,而是 LLVM 15+ 已彻底弃用 LegacyPassManager 的全局注册机制(RegisterPass),仅保留对新 PassManager 的支持。
- 旧写法(LLVM 12 及以前):
static RegisterPass<mypass> X("my-pass", "...")</mypass>—— 在 LLVM 15+ 下注册无效,opt查不到该 pass 名称 - 新写法必须显式注册到 PassBuilder:需在
PassPluginLibraryInfo中实现llvmGetPassPluginInfo,并调用builder.registerPipelineParsingCallback - Windows 下注意扩展名是
.dll,Linux 是.so,macOS 是.dylib;opt不会自动补全 -
-load路径必须是绝对路径或相对于当前工作目录的可访问路径,不能含空格或中文(尤其 Windows CMD 下易静默失败)
LLVM 15+ 自定义 Pass 的最小可运行结构
要让 opt 正确识别并运行你的 Pass,C++ 源码中至少要包含三部分:Pass 实现、Pipeline 解析注册、插件入口函数。缺一不可。
- Pass 类继承
llvm::PassInfoMixin<mypass></mypass>,而非FunctionPass等 legacy 类型 - 在
llvmGetPassPluginInfo函数里调用builder.registerPipelineParsingCallback([](StringRef Name, FunctionPassManager &FPM, ...) { if (Name == "my-pass") FPM.addPass(MyPass()); }) - CMakeLists.txt 中必须链接
LLVMCore和LLVMPasses,并启用-fPIC(add_library(... MODULE ...)) - 编译命令示例:
clang++ -std=c++17 -fPIC -shared -O2 FuncEncrypt.cpp -o FuncEncrypt.so $(llvm-config --ldflags --libs core passes support)
验证 opt 是否成功加载插件
不要一上来就跑 IR 文件,先用 --list-passes 确认插件是否被识别。这是最常被跳过的一步,也是定位问题最快的方式。
- 执行
opt -load ./FuncEncrypt.so --list-passes | grep my-pass,应输出类似my-pass - A Pass to Encrypt Function Name - 若无输出,说明插件未正确注册或
llvmGetPassPluginInfo未被调用(常见于符号未导出、链接遗漏LLVMPasses) - 若报错
undefined symbol: _ZN4llvm11PassBuilderC1...,说明链接时漏了LLVMPasses库 - 成功后才执行:
opt -load ./FuncEncrypt.so -passes="my-pass" input.ll -S -o output.ll(注意新版必须用-passes=,不再是-my-pass)
最容易被忽略的是:LLVM 15+ 的 opt 不再解析 legacy pass name(如 -my-pass),所有自定义 pass 必须走 -passes= 流水线语法,且名称必须与 registerPipelineParsingCallback 中匹配的字符串完全一致——大小写、连字符都不能错。











