3. 使用New Pass Manager
任务目标
将基于Legacy Pass Manager编写的LLVM FunctionPass迁移到New Pass Manager(NPM)框架,并注册到opt工具链中,使其可通过-passes=选项被调用。
环境依赖
需要LLVM 14或更高版本(NPM自LLVM 14起为默认Pass管理器)。确保llvm-config可用,并已安装开发头文件。
迁移前:Legacy Pass实现
以下是一个典型的Legacy FunctionPass,功能为统计函数基本块数量并打印结果:
// LegacyPass.cpp
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
namespace {
struct LegacyBBCounter : public FunctionPass {
static char ID;
LegacyBBCounter() : FunctionPass(ID) {}
bool runOnFunction(Function &F) override {
unsigned count = 0;
for (auto &BB : F)
(void)BB, ++count;
errs() << "Function " << F.getName() << " has " << count << " basic blocks\n";
return false; // 不修改函数
}
};
} // namespace
char LegacyBBCounter::ID = 0;
static RegisterPass<LegacyBBCounter> X("legacy-bbcounter",
"Count basic blocks (Legacy PM)");
迁移到New Pass Manager
NPM要求Pass继承PassInfoMixin,实现run方法,并通过llvm::RegisterPass的NPM版本注册。核心改动包括:移除ID静态成员、改用PreservedAnalyses声明对函数分析的保留情况、使用llvm::PassPluginLibraryInfo实现动态注册。
// NewPMBBCounter.cpp
#include "llvm/IR/Function.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
namespace {
struct NewPMBBCounter : public PassInfoMixin<NewPMBBCounter> {
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM) {
unsigned count = 0;
for (auto &BB : F)
(void)BB, ++count;
errs() << "Function " << F.getName() << " has " << count
<< " basic blocks (NPM)\n";
return PreservedAnalyses::all(); // 未修改任何内容
}
};
} // namespace
// 插件注册入口
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() {
return {LLVM_PLUGIN_API_VERSION, "NewPMBBCounter", LLVM_VERSION_STRING,
[](PassBuilder &PB) {
PB.registerPipelineParsingCallback(
[](StringRef Name, FunctionPassManager &FPM,
ArrayRef<PassBuilder::PipelineElement>) {
if (Name == "newpm-bbcounter") {
FPM.addPass(NewPMBBCounter());
return true;
}
return false;
});
}};
}
编译与运行
将上述代码保存为NewPMBBCounter.cpp,使用llvm-config获取编译参数并生成动态库:
g++ -shared -fPIC -std=c++17 NewPMBBCounter.cpp \
$(llvm-config --cxxflags --ldflags --libs) \
-o NewPMBBCounter.so
准备一个测试IR文件test.ll(内容为任意合法LLVM IR,例如包含两个基本块的函数),然后通过opt加载插件并运行Pass:
opt -load-pass-plugin=./NewPMBBCounter.so \
-passes=newpm-bbcounter test.ll -S -o /dev/null
验证方法
观察终端输出。若迁移成功,应看到类似以下预期输出(具体函数名与块数取决于test.ll内容):
Function main has 2 basic blocks (NPM)
同时,opt退出码为0且无报错信息。若输出中缺少(NPM)后缀或提示unknown pass name 'newpm-bbcounter',则说明注册失败,需检查插件加载路径或Pass名称拼写。
常见问题
- 链接错误:确保编译命令包含
$(llvm-config --cxxflags --ldflags --libs),且LLVM版本一致。 - Pass名称冲突:
registerPipelineParsingCallback中的字符串"newpm-bbcounter"必须与-passes=参数完全一致。 - 分析保留声明:若Pass修改了函数,应返回
PreservedAnalyses::none()或精确指定保留的分析;本例未修改故返回all()。