LLVM Module Pass 与 Function Pass 详解
一、核心区别与作用范围
| 特性 | Module Pass | Function Pass |
|---|---|---|
| 作用粒度 | 作用于整个编译单元(Module),可访问模块内所有函数、全局变量、元数据等 [[4]] | 作用于单个函数(Function),仅能访问当前函数的IR结构 [[2]] |
| 执行顺序 | 在整个Module级别执行一次 | 对Module中的每个函数独立执行一次 [[2]] |
| 依赖关系 | 可请求并使用Function Pass的分析结果(通过getAnalysis接口) [[35]] |
不能依赖Module Pass(Legacy PM中存在此限制) [[6]] |
| 行为特性 | 具有全局视角,可进行跨函数优化、内联决策、全局符号分析等 | 具有局部可预测性,系统可预期其行为仅影响当前函数 [[2]] |
二、典型应用场景
Module Pass 适用场景:
- 全局优化:函数内联(需分析调用图)、死函数消除
- 链接时优化(LTO):跨编译单元的符号合并与优化
- 全局变量初始化分析、模块级元数据处理
- 构建和维护全局数据结构(如调用图CallGraph)
Function Pass 适用场景:
- 函数内优化:常量传播、死代码消除、指令组合
- 基本块级分析:支配树(DominatorTree)、循环识别
- 局部寄存器分配前的指令调度
- 函数内特定模式匹配与转换
三、Pass 实现与注册(New Pass Manager)
LLVM 11+ 推荐使用 New Pass Manager(优化管线默认采用),其核心改进是分离Pass与Analysis,支持更灵活的pipeline组合 [[18]]。
1. Function Pass 实现示例
cpp
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
namespace {
struct HelloFunctionPass : public llvm::PassInfoMixin<HelloFunctionPass> {
llvm::PreservedAnalyses run(llvm::Function &F,
llvm::FunctionAnalysisManager &AM) {
llvm::errs() << "Hello from function: " << F.getName() << "\n";
return llvm::PreservedAnalyses::none(); // 表示未保留任何分析结果
}
};
// 静态注册(用于opt工具)
llvm::PassPluginLibraryInfo getHelloFunctionPassPluginInfo() {
return {
LLVM_PLUGIN_API_VERSION, "HelloFunctionPass", LLVM_VERSION_STRING,
[](llvm::PassBuilder &PB) {
// 注册到默认优化管线的EarlyCSE阶段后
PB.registerPipelineParsingCallback(
[](llvm::StringRef Name, llvm::FunctionPassManager &FPM,
llvm::ArrayRef<llvm::PassBuilder::PipelineElement>) {
if (Name == "hello-function") {
FPM.addPass(HelloFunctionPass());
return true;
}
return false;
});
}
};
}
} // namespace
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
return getHelloFunctionPassPluginInfo();
}
2. Module Pass 实现示例
cpp
struct HelloModulePass : public llvm::PassInfoMixin<HelloModulePass> {
llvm::PreservedAnalyses run(llvm::Module &M,
llvm::ModuleAnalysisManager &AM) {
llvm::errs() << "Module: " << M.getName() << " has "
<< M.size() << " functions\n";
// 可在Module Pass中请求Function级分析
auto &FAM = AM.getResult<llvm::FunctionAnalysisManagerModuleProxy>(M).getManager();
for (auto &F : M) {
if (!F.isDeclaration()) {
auto &DT = FAM.getResult<llvm::DominatorTreeAnalysis>(F);
// 使用支配树分析...
}
}
return llvm::PreservedAnalyses::none();
}
};
四、Pipeline 注册机制
1. 命令行使用(opt工具)
bash
# 运行Function Pass(自动包装在ModulePassManager中)
opt -passes='hello-function' input.ll -o output.ll
# 混合Module Pass与Function Pass
# 注意:Function Pass需用module作用域包装
opt -passes='no-op-module,function(hello-function),no-op-module' input.ll
# 显式使用module adaptor包装Function Pass
opt -passes='module(function(hello-function))' input.ll
2. Pipeline 构建层次结构
New Pass Manager 的IR层次为:Module → (CGSCC) → Function → Loop [[31]]
cpp
// C++代码中构建pipeline
llvm::PassBuilder PB;
llvm::ModulePassManager MPM;
// 添加Module Pass
MPM.addPass(HelloModulePass());
// 添加Function Pass序列(自动创建FunctionPassManager)
MPM.addPass(llvm::createModuleToFunctionPassAdaptor(
llvm::FunctionPassManager(
HelloFunctionPass(),
AnotherFunctionPass()
)
));
// 运行pipeline
llvm::ModuleAnalysisManager MAM;
MPM.run(*M, MAM);
3. 注册到Clang默认优化管线
cpp
PB.registerOptimizerLastEPCallback(
[](llvm::ModulePassManager &MPM, llvm::OptimizationLevel Level) {
if (Level.getSpeedupLevel() >= 2) {
MPM.addPass(HelloModulePass());
}
});
PB.registerPeepholeEPCallback(
[](llvm::FunctionPassManager &FPM, llvm::OptimizationLevel Level) {
FPM.addPass(HelloFunctionPass());
});
五、Legacy Pass Manager 对比(已逐步淘汰)
Legacy PM 使用虚函数接口,注册方式不同:
cpp
// Legacy Function Pass
struct LegacyHelloPass : public llvm::FunctionPass {
static char ID;
LegacyHelloPass() : FunctionPass(ID) {}
bool runOnFunction(llvm::Function &F) override {
llvm::errs() << "Hello: " << F.getName() << "\n";
return false; // 未修改IR
}
};
char LegacyHelloPass::ID = 0;
static llvm::RegisterPass<LegacyHelloPass> X("hello", "Hello Pass");
// 使用:opt -load libHello.so -hello input.bc
重要提示:LLVM 16+ 已全面转向New Pass Manager,建议新开发直接采用New PM。代码生成后端(CodeGen)目前仍部分使用Legacy PM [[17]]。
六、关键设计原则
- 作用域隔离:Function Pass不应产生跨函数副作用,保证可并行执行
- 分析复用:通过AnalysisManager避免重复计算(如多次请求DominatorTree)
- Pipeline组合:Module Pass可包含嵌套的FunctionPassManager,实现层次化优化
- PreservedAnalyses:精确声明保留的分析结果,避免不必要的重新计算
此设计使LLVM能够灵活组合不同粒度的优化,在保证正确性的同时最大化编译性能。
在使用FUN PASS的时候,如果涉及到整个module 的更改,应将其转换为module PASS