第 7 章:LLVM Pass 框架
第 7 章:LLVM Pass 框架
“Pass 是 LLVM 优化系统的基本单元,每个 Pass 做一件事,做好一件事。”
7.1 Pass 概述
Pass 是 LLVM 中处理 IR 的基本单元。每个 Pass 接收 IR 作为输入,要么分析它(分析 Pass),要么修改它(转换 Pass)。
7.1.1 Pass 的两种类型
| 类型 | 作用 | 是否修改 IR | 典型示例 |
|---|---|---|---|
| 分析 Pass (Analysis Pass) | 计算信息 | ❌ 不修改 | LoopInfo, DominatorTree, AliasAnalysis |
| 转换 Pass (Transform Pass) | 优化/变换 | ✅ 修改 | InstCombine, LoopUnroll, GVN |
7.1.2 Pass 的作用范围
| 范围 | 说明 | 基类 |
|---|---|---|
| Module Pass | 处理整个模块 | PassInfoMixin |
| Function Pass | 处理单个函数 | PassInfoMixin |
| Loop Pass | 处理单个循环 | PassInfoMixin |
注意: 在新 PassManager 中,所有 Pass 都继承自
PassInfoMixin,不再区分 Module/Function/Loop 级别。通过PreservedAnalyses run(Function&, FunctionAnalysisManager&)等签名自动确定作用范围。
7.2 新 PassManager (New PassManager)
从 LLVM 14 开始,新 PassManager 成为默认。推荐使用新 PM 编写 Pass。
7.2.1 Function Analysis Pass 示例
// MyAnalysis.h
#ifndef LLVM_TRANSFORMS_MYANALYSIS_H
#define LLVM_TRANSFORMS_MYANALYSIS_H
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
namespace llvm {
// 分析结果
struct MyAnalysisInfo {
unsigned InstCount;
unsigned BBCount;
unsigned ArgCount;
bool HasLoops;
};
// 分析 Pass
class MyAnalysis : public AnalysisInfoMixin<MyAnalysis> {
friend AnalysisInfoMixin<MyAnalysis>;
static AnalysisKey Key;
public:
using Result = MyAnalysisInfo;
Result run(Function &F, FunctionAnalysisManager &AM);
};
} // namespace llvm
#endif
// MyAnalysis.cpp
#include "MyAnalysis.h"
#include "llvm/IR/Instructions.h"
namespace llvm {
AnalysisKey MyAnalysis::Key;
MyAnalysis::Result MyAnalysis::run(Function &F, FunctionAnalysisManager &AM) {
MyAnalysisInfo Info;
Info.InstCount = 0;
Info.BBCount = 0;
Info.ArgCount = F.arg_size();
Info.HasLoops = false;
for (auto &BB : F) {
Info.BBCount++;
for (auto &I : BB) {
Info.InstCount++;
}
}
// 简单检测循环(有回边)
for (auto &BB : F) {
Instruction *Terminator = BB.getTerminator();
for (unsigned i = 0; i < Terminator->getNumSuccessors(); i++) {
BasicBlock *Succ = Terminator->getSuccessor(i);
// 如果后继支配当前块,说明有循环
// (简化实现,实际应使用 LoopInfo)
if (&BB >= Succ) {
Info.HasLoops = true;
}
}
}
outs() << "=== MyAnalysis: " << F.getName() << " ===\n"
<< " 指令数: " << Info.InstCount << "\n"
<< " 基本块: " << Info.BBCount << "\n"
<< " 参数数: " << Info.ArgCount << "\n"
<< " 有循环: " << (Info.HasLoops ? "是" : "否") << "\n";
return Info;
}
} // namespace llvm
7.2.2 Function Transform Pass 示例
// MyTransform.h
#ifndef LLVM_TRANSFORMS_MYTRANSFORM_H
#define LLVM_TRANSFORMS_MYTRANSFORM_H
#include "llvm/IR/PassManager.h"
namespace llvm {
class MyTransformPass : public PassInfoMixin<MyTransformPass> {
public:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
static bool isRequired() { return true; }
};
} // namespace llvm
#endif
// MyTransform.cpp
#include "MyTransform.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Constants.h"
#include "llvm/Transforms/Utils/Local.h"
namespace llvm {
PreservedAnalyses MyTransformPass::run(Function &F, FunctionAnalysisManager &AM) {
bool Changed = false;
for (auto &BB : F) {
// 示例:消除冗余的 add x, 0
for (auto II = BB.begin(); II != BB.end(); ) {
Instruction &I = *II++;
if (auto *BO = dyn_cast<BinaryOperator>(&I)) {
if (BO->getOpcode() == Instruction::Add) {
// 检查是否是 add x, 0
if (auto *C = dyn_cast<ConstantInt>(BO->getOperand(1))) {
if (C->isZero()) {
// 用 x 替换 add x, 0
BO->replaceAllUsesWith(BO->getOperand(0));
BO->eraseFromParent();
Changed = true;
}
}
}
}
}
}
if (!Changed)
return PreservedAnalyses::all();
// 告诉 PassManager 我们修改了 IR
PreservedAnalyses PA;
PA.preserve<DominatorTreeAnalysis>(); // 保留支配树
PA.preserve<LoopAnalysis>(); // 保留循环信息
return PA;
}
} // namespace llvm
7.2.3 注册 Pass
// 在 opt 工具中注册(通常不需要手动做)
// 方式一:通过 PassBuilder 插件
// my_plugin.cpp
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
using namespace llvm;
// 注册分析 Pass
PassPluginLibraryInfo getMyPluginInfo() {
return {
LLVM_PLUGIN_API_VERSION,
"MyPlugin",
LLVM_VERSION_STRING,
[](PassBuilder &PB) {
PB.registerAnalysisRegistrationCallback(
[](FunctionAnalysisManager &FAM) {
FAM.registerPass([&] { return MyAnalysis(); });
});
PB.registerPipelineParsingCallback(
[](StringRef Name, FunctionPassManager &FPM,
ArrayRef<PassBuilder::PipelineElement>) {
if (Name == "my-transform") {
FPM.addPass(MyTransformPass());
return true;
}
return false;
});
}
};
}
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() {
return getMyPluginInfo();
}
# 编译为共享库
clang -shared -fPIC -o libmyplugin.so my_plugin.cpp \
$(llvm-config --cxxflags --ldflags --libs)
# 使用插件
opt -load-pass-plugin=./libmyplugin.so \
-passes='my-transform' input.ll -o output.ll
7.3 Legacy PassManager(已废弃)
注意: Legacy PM 已废弃,仅在维护旧代码时可能遇到。新代码应使用新 PM。
// Legacy Function Pass 示例(仅供参考)
#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
namespace llvm {
class MyLegacyPass : public FunctionPass {
public:
static char ID;
MyLegacyPass() : FunctionPass(ID) {}
bool runOnFunction(Function &F) override {
bool Changed = false;
// ... 优化逻辑 ...
return Changed;
}
};
char MyLegacyPass::ID = 0;
static RegisterPass<MyLegacyPass> X("my-pass", "My Legacy Pass");
} // namespace llvm
# 使用旧 PM(需要显式禁用新 PM)
opt -enable-new-pm=0 -load ./libmy.so -my-pass input.ll -o output.ll
7.4 内置分析 Pass
7.4.1 核心分析 Pass
| Pass | 说明 | 用途 |
|---|---|---|
DominatorTreeAnalysis | 支配树 | 循环分析、SSA 构建 |
LoopAnalysis | 循环信息 | 循环优化 |
PostDominatorTreeAnalysis | 后支配树 | SSA 构建、死代码消除 |
AAManager | 别名分析 | 内存依赖分析 |
ScalarEvolutionAnalysis | 标量演化 | 循环索引分析 |
AssumptionCacheAnalysis | 假设缓存 | @llvm.assume 管理 |
TargetLibraryAnalysis | 目标库信息 | 库函数识别 |
TargetIRAnalysis | 目标 IR 信息 | 成本估算 |
7.4.2 使用分析结果
PreservedAnalyses MyPass::run(Function &F, FunctionAnalysisManager &AM) {
// 获取分析结果
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
auto &LI = AM.getResult<LoopAnalysis>(F);
auto &SE = AM.getResult<ScalarEvolutionAnalysis>(F);
// 使用支配树
BasicBlock *Entry = &F.getEntryBlock();
for (auto &BB : F) {
if (DT.dominates(Entry, &BB)) {
outs() << BB.getName() << " 被 entry 支配\n";
}
}
// 使用循环信息
for (Loop *L : LI) {
outs() << "循环: " << L->getHeader()->getName()
<< " 深度: " << L->getLoopDepth() << "\n";
// 获取循环边界
if (auto *BTC = SE.getBackedgeTakenCount(L)) {
outs() << " 回边执行次数: ";
BTC->print(outs());
outs() << "\n";
}
}
return PreservedAnalyses::all();
}
7.5 Pass PreservedAnalyses
当转换 Pass 修改 IR 后,需要告诉 PassManager 哪些分析结果仍然有效:
PreservedAnalyses MyPass::run(Function &F, FunctionAnalysisManager &AM) {
// 场景一:什么都没变
return PreservedAnalyses::all();
// 场景二:全都变了(需要重新计算所有分析)
return PreservedAnalyses::none();
// 场景三:保留部分分析
PreservedAnalyses PA;
PA.preserve<DominatorTreeAnalysis>(); // 保留支配树
PA.preserve<LoopAnalysis>(); // 保留循环信息
PA.preserveSet<CFGAnalyses>(); // 保留所有 CFG 相关分析
return PA;
}
PreservedAnalyses 最佳实践:
| 优化类型 | 应该保留 | 应该废弃 |
|---|---|---|
| 死代码消除 | DominatorTree, LoopInfo | AliasAnalysis |
| 常量折叠 | 一切 | 无 |
| 循环展开 | DominatorTree | LoopInfo, ScalarEvolution |
| 内联 | 无 | 几乎所有 |
| GVN | 无 | 几乎所有 |
| InstCombine | 大部分 | AliasAnalysis |
7.6 Pass 流水线
7.6.1 使用 opt 运行 Pass
# 运行单个 Pass
opt -passes='instcombine' input.ll -o output.ll
# 运行多个 Pass(按顺序)
opt -passes='simplifycfg,instcombine,gvn' input.ll -o output.ll
# 使用优化级别
opt -passes='default<O2>' input.ll -o output.ll
# 嵌套 Pass
opt -passes='function(instcombine)' input.ll -o output.ll
opt -passes='module(function(instcombine,simplifycfg))' input.ll -o output.ll
# 查看 Pass 流水线
opt -passes='default<O2>' -print-pipeline-passes input.ll -o /dev/null
# 列出所有可用 Pass
opt --list-passes
# 调试 Pass
opt -passes='instcombine' -debug input.ll -o /dev/null
opt -passes='instcombine' -print-after-all input.ll -o /dev/null
7.6.2 O2 优化流水线
典型的 -O2 包含以下主要阶段:
Module Pass Pipeline:
├── ForceFunctionAttrsPass
├── InferFunctionAttrsPass
├── CoroutinesPass
│
├── ModuleInlinerWrapperPass (Module 内联)
│ ├── InlineAdvisorAnalysis
│ └── Function Pipeline (对每个函数):
│ ├── SimplifyCFGPass
│ ├── InstCombinePass
│ ├── SROA (标量替换聚合)
│ ├── EarlyCSEPass (公共子表达式消除)
│ ├── CallSiteSplittingPass
│ └── ...
│
├── GlobalOptPass (全局优化)
├── GlobalDCEPass (全局死代码消除)
│
├── Function Pipeline (对每个函数):
│ ├── SimplifyCFGPass
│ ├── InstCombinePass
│ ├── JumpThreadingPass
│ ├── SROA
│ ├── TailCallElimPass
│ ├── ReassociatePass
│ ├── ConstraintEliminationPass
│ ├── LoopSimplifyPass
│ ├── LCSSAPass
│ ├── LICMPass (循环不变量外提)
│ ├── LoopRotatePass
│ ├── LICMPass
│ ├── SimpleLoopUnswitchPass
│ ├── SimplifyCFGPass
│ ├── InstCombinePass
│ ├── LoopFlattenPass
│ ├── LoopIdiomPass
│ ├── IndVarSimplifyPass (归纳变量简化)
│ ├── LoopDeletionPass
│ ├── LoopUnrollPass (循环展开)
│ ├── GVNPass (全局值编号)
│ ├── SCCPPass (稀疏条件常量传播)
│ ├── BDCEPass (位追踪死代码消除)
│ ├── ADCEPass (激进死代码消除)
│ ├── MemCpyOptPass
│ ├── DSEPass (死存储消除)
│ ├── MergedLoadStoreMotionPass
│ ├── LoopSimplifyPass
│ ├── LCSSAPass
│ ├── SimplifyCFGPass
│ ├── InstCombinePass
│ ├── LoopVectorizePass (循环向量化)
│ ├── SLPVectorizerPass (SLP 向量化)
│ ├── LoopUnrollPass
│ ├── WarnMissedTransformationsPass
│ ├── InstCombinePass
│ ├── SimplifyCFGPass
│ └── ...
│
├── EliminateAvailableExternallyPass
├── GlobalDCEPass
└── ConstantMergePass
7.7 常见分析 Pass 详解
7.7.1 支配树 (Dominator Tree)
// 获取支配树
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
// 查询支配关系
bool Dom = DT.dominates(BB1, BB2); // BB1 支配 BB2?
// 获取最近公共支配者
BasicBlock *NCD = DT.findNearestCommonDominator(BB1, BB2);
// 获取直接支配者
BasicBlock *IDom = DT.getNode(&BB)->getIDom()->getBlock();
支配树示例:
CFG: Dominator Tree:
entry entry
│ / | \
├─► bb1 bb1 bb2 bb3
│ | / \
├─► bb2 bb4 bb5
│ \
├─► bb3 bb6
│
└─► exit
entry 支配所有块
bb1 支配 bb4, bb6
bb2 支配 bb4, bb5
7.7.2 循环分析 (Loop Analysis)
auto &LI = AM.getResult<LoopAnalysis>(F);
// 遍历所有顶层循环
for (Loop *TopL : LI) {
outs() << "顶层循环: " << TopL->getHeader()->getName() << "\n";
// 遍历子循环
for (Loop *SubL : TopL->getSubLoops()) {
outs() << " 子循环: " << SubL->getHeader()->getName()
<< " 深度: " << SubL->getLoopDepth() << "\n";
}
// 获取循环的基本块
for (BasicBlock *BB : TopL->getBlocks()) {
outs() << " 块: " << BB->getName() << "\n";
}
// 获取循环入口(header)
BasicBlock *Header = TopL->getHeader();
// 获取预入口(preheader)
BasicBlock *Preheader = TopL->getLoopPreheader();
// 获取回边
SmallVector<BasicBlock*, 4> Latches;
TopL->getLoopLatches(Latches);
// 获取出口
SmallVector<BasicBlock*, 4> ExitBlocks;
TopL->getExitBlocks(ExitBlocks);
}
7.7.3 别名分析 (Alias Analysis)
auto &AA = AM.getResult<AAManager>(F);
// 查询两个指针是否可能别名
auto Result = AA.alias(
MemoryLocation::getBeforeOrAfter(Ptr1),
MemoryLocation::getBeforeOrAfter(Ptr2)
);
// 结果类型
switch (Result) {
case AliasResult::NoAlias:
outs() << "两个指针不别名\n";
break;
case AliasResult::MayAlias:
outs() << "两个指针可能别名\n";
break;
case AliasResult::MustAlias:
outs() << "两个指针一定别名\n";
break;
case AliasResult::PartialAlias:
outs() << "两个指针部分别名\n";
break;
}
7.8 Pass 调试
7.8.1 调试选项
# 查看 Pass 前后的 IR
opt -passes='instcombine' -print-before-all -print-after-all input.ll -o /dev/null 2>&1
# 只打印特定 Pass 前后的 IR
opt -passes='instcombine' -print-before=instcombine -print-after=instcombine input.ll -o /dev/null 2>&1
# 启用调试输出
opt -passes='instcombine' -debug input.ll -o /dev/null 2>&1
# 打印统计信息
opt -passes='instcombine' -stats input.ll -o /dev/null 2>&1
# 验证 IR
opt -passes='verify' input.ll -o /dev/null
opt -passes='instcombine' -verify-each input.ll -o output.ll
7.8.2 查看特定分析
# 打印循环信息
opt -passes='print<loops>' -disable-output input.ll 2>&1
# 打印支配树
opt -passes='print<domtree>' -disable-output input.ll 2>&1
# 打印 ScalarEvolution
opt -passes='print<scalar-evolution>' -disable-output input.ll 2>&1
# 打印别名分析
opt -passes='print<aa>' -disable-output input.ll 2>&1
7.9 本章小结
| 概念 | 要点 |
|---|---|
| Pass 类型 | 分析 Pass(不修改 IR)和转换 Pass(修改 IR) |
| 新 PassManager | LLVM 14+ 默认,使用 PassInfoMixin |
| Legacy PM | 已废弃,仅用于维护旧代码 |
| PreservedAnalyses | 告诉 PM 哪些分析仍然有效 |
| opt 工具 | -passes='...' 运行 Pass |
| 调试 | -print-before-all, -debug, -stats |
扩展阅读
- Writing an LLVM Pass (New PM) — 新 PM 编写指南
- Using the New Pass Manager — 新 PM 使用
- Writing an LLVM Pass (Legacy) — 旧 PM(仅供参考)
- LLVM Passes — 内置 Pass 列表
下一章: 第 8 章:优化管线 — 深入学习 LLVM 的优化技术,包括内联、向量化、循环优化等。