强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

LLVM 开发指南 / 第 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, LoopInfoAliasAnalysis
常量折叠一切
循环展开DominatorTreeLoopInfo, 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)
新 PassManagerLLVM 14+ 默认,使用 PassInfoMixin
Legacy PM已废弃,仅用于维护旧代码
PreservedAnalyses告诉 PM 哪些分析仍然有效
opt 工具-passes='...' 运行 Pass
调试-print-before-all, -debug, -stats

扩展阅读

  1. Writing an LLVM Pass (New PM) — 新 PM 编写指南
  2. Using the New Pass Manager — 新 PM 使用
  3. Writing an LLVM Pass (Legacy) — 旧 PM(仅供参考)
  4. LLVM Passes — 内置 Pass 列表

下一章: 第 8 章:优化管线 — 深入学习 LLVM 的优化技术,包括内联、向量化、循环优化等。