第 3 章:LLVM 整体架构
第 3 章:LLVM 整体架构
“好的架构不是设计出来的,是演化出来的。” — 但 LLVM 的架构确实是精心设计的。
3.1 LLVM 的层次结构
LLVM 采用多层表示的设计思想,从源代码到最终机器码,经过多个层次的逐步降低抽象级别。
3.1.1 四层 IR 模型
┌─────────────────────────────────────────────┐
│ Level 0: AST (抽象语法树) │
│ ───────────────────────────────────────── │
│ · 语言特定 (C/C++/ObjC) │
│ · 包含完整的类型信息和源码结构 │
│ · 由 Clang 前端生成 │
│ · C++ API: clang::ASTContext │
├─────────────────────────────────────────────┤
│ Level 1: LLVM IR │
│ ───────────────────────────────────────── │
│ · 语言无关的核心中间表示 │
│ · SSA (静态单赋值) 形式 │
│ · 类型安全、可验证 │
│ · .ll (文本) / .bc (二进制) │
│ · C++ API: llvm::Module, llvm::Function │
├─────────────────────────────────────────────┤
│ Level 2: SelectionDAG / MIR │
│ ───────────────────────────────────────── │
│ · 面向目标机器的中间表示 │
│ · SelectionDAG: 指令选择阶段使用 │
│ · MachineIR: 寄存器分配后使用 │
│ · 包含目标特定信息 │
│ · C++ API: llvm::SelectionDAG, MachineFunction │
├─────────────────────────────────────────────┤
│ Level 3: MCInst (机器指令) │
│ ───────────────────────────────────────── │
│ · 最底层的指令表示 │
│ · 直接映射到目标指令集 │
│ · 用于汇编输出和目标文件生成 │
│ · C++ API: llvm::MCInst │
└─────────────────────────────────────────────┘
3.1.2 各层之间的转换
源代码 AST LLVM IR SelectionDAG MachineIR MCInst
│ │ │ │ │ │
│ 词法/语法 │ IRGen │ 指令选择 │ 寄存器 │ MC层 │
│ 语义分析 │ (CodeGen) │ (ISel) │ 分配 │ 汇编输出 │
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
源码 ──────► Clang AST ────► LLVM IR ────► SelectionDAG ──► MIR ──────► MCInst
│ │ │ │ │
│ │ │ │ │
libclang opt Passes InstrEmitter RegAlloc MCCodeEmitter
Tooling 新PM/旧PM Lowering VirtRegMap AsmPrinter
3.2 LLVM IR — 核心中的核心
LLVM IR 是整个 LLVM 系统的基石,所有语言前端最终都要生成 LLVM IR,所有后端优化和代码生成都从 LLVM IR 开始。
3.2.1 IR 的三个等价形式
| 形式 | 文件后缀 | 说明 |
|---|---|---|
| 文本格式 (Textual IR) | .ll | 人类可读,便于调试 |
| 二进制格式 (Bitcode) | .bc | 紧凑高效,用于 LTO |
| 内存格式 (In-memory) | — | 程序操作的 C++ 对象 |
# 文本 → 二进制
llvm-as input.ll -o output.bc
# 二进制 → 文本
llvm-dis input.bc -o output.ll
# C/C++ → 文本 IR
clang -S -emit-llvm source.c -o source.ll
# C/C++ → 二进制 IR
clang -c -emit-llvm source.c -o source.bc
3.2.2 Module — IR 的顶层容器
┌───────────────────────────────────────┐
│ Module │
│ ─────────────────────────────────── │
│ · target triple │
│ · target datalayout │
│ · source_filename │
│ │
│ ┌─────────────────────────────────┐ │
│ │ GlobalVariable (全局变量) │ │
│ │ @global_var = global i32 42 │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Function (函数) │ │
│ │ ┌───────────────────────────┐ │ │
│ │ │ BasicBlock (基本块) │ │ │
│ │ │ ┌─────────────────────┐ │ │ │
│ │ │ │ Instruction (指令) │ │ │ │
│ │ │ │ %x = add i32 %a, 1│ │ │ │
│ │ │ └─────────────────────┘ │ │ │
│ │ └───────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ NamedMetadata (命名元数据) │ │
│ └─────────────────────────────────┘ │
└───────────────────────────────────────┘
3.3 Pass 框架
Pass 框架是 LLVM 优化系统的核心。每一个优化或分析都是一个独立的 Pass。
3.3.1 Pass 的分类
┌─────────────────────────────────────────────────────────┐
│ LLVM Pass │
│ │
│ ┌──────────────────┐ ┌────────────────────┐ │
│ │ 分析 Pass │ │ 转换 Pass │ │
│ │ (Analysis Pass) │ │ (Transform Pass) │ │
│ │ │ │ │ │
│ │ 不修改 IR │ │ 修改 IR │ │
│ │ 计算并缓存信息 │ │ 应用优化 │ │
│ │ │ │ │ │
│ │ 例如: │ │ 例如: │ │
│ │ · LoopInfo │ │ · InstCombine │ │
│ │ · DominatorTree │ │ · LoopUnroll │ │
│ │ · AliasAnalysis │ │ · GVN │ │
│ │ · ScalarEvolution│ │ · Inliner │ │
│ └──────────────────┘ └────────────────────┘ │
│ │
│ 按作用范围: │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Module Pass │ │Function Pass │ │ Loop Pass │ │
│ │ 整个模块 │ │ 单个函数 │ │ 单个循环 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────┘
3.3.2 新旧 PassManager
LLVM 有两套 Pass 管理系统:
| 特性 | Legacy PassManager | New PassManager |
|---|---|---|
| 状态 | 已废弃 | 当前默认(LLVM 14+) |
| 命名空间 | llvm:: (legacy) | llvm:: |
| Pass 注册 | RegisterPass | PassBuilder |
| Pass 流水线 | 硬编码 | 可编程 |
| 错误处理 | 断言 | Expected<>/Error |
| 推荐使用 | ❌ 不推荐 | ✅ 推荐 |
# 使用新 PassManager 的 opt
opt -passes='default<O2>' input.ll -o output.bc
# 使用旧 PassManager 的 opt(已废弃)
opt -O2 -enable-new-pm=0 input.ll -o output.bc
# 查看优化流水线
opt -passes='print<module>' -disable-output input.ll
3.4 后端架构
LLVM 后端负责将 LLVM IR 转换为目标机器码。
3.4.1 后端流水线
LLVM IR
│
▼
┌──────────────────────┐
│ 1. IR → DAG │ SelectionDAGISel
│ (构建 SelectionDAG)│
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ 2. 指令选择 │ TargetLowering
│ DAG → Machine DAG │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ 3. 指令合并/合法化 │ DAGCombine, Legalize
│ 优化 Machine DAG │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ 4. 指令调度 │ ScheduleDAG
│ (前寄存器分配) │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ 5. 寄存器分配 │ RegAllocGreedy
│ 虚拟寄存器→物理寄存│
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ 6. 指令调度 │ PostRAScheduler
│ (后寄存器分配) │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ 7. 指令发射 │ InstrEmitter
│ 生成 MachineInstr │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ 8. MC 层 │ MCCodeEmitter
│ MachineInstr → │
│ MCInst → 机器码 │
└──────────┬───────────┘
│
▼
目标文件 (.o)
3.4.2 后端模块化
每个目标后端是一个独立的库,只在需要时才链接:
lib/Target/
├── X86/ # x86/x86-64
│ ├── X86.td # TableGen 描述文件
│ ├── X86InstrInfo.cpp
│ ├── X86RegisterInfo.cpp
│ └── ...
├── AArch64/ # ARM 64-bit
├── ARM/ # ARM 32-bit
├── RISCV/ # RISC-V
├── MIPS/ # MIPS
├── PowerPC/ # PowerPC
├── SystemZ/ # IBM SystemZ
├── WebAssembly/ # WebAssembly
└── ...
3.5 模块化库设计
3.5.1 LLVM 库依赖关系
┌──────────────────────────────────────────────────┐
│ 工具层 │
│ clang | opt | llc | lld | lldb | llvm-as | ... │
└──────────────────┬───────────────────────────────┘
│
┌──────────────────▼───────────────────────────────┐
│ 组件层 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Frontend │ │Transform │ │ CodeGen │ │
│ │ Clang │ │ Utils │ │ │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ┌────▼────────────▼────────────▼─────┐ │
│ │ Core IR 层 │ │
│ │ Module → Function → BasicBlock │ │
│ │ → Instruction → Value → Type │ │
│ └─────────────────┬──────────────────┘ │
│ │ │
│ ┌─────────────────▼──────────────────┐ │
│ │ Support 层 │ │
│ │ MemoryBuffer, raw_ostream, │ │
│ │ CommandLine, Timer, Debug │ │
│ └────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────┐ │
│ │ MC 层 │ │
│ │ MCInst, MCStreamer, MCAsmParser │ │
│ └────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
3.5.2 核心库及其功能
| 库名 | 依赖 | 功能 | 典型用途 |
|---|---|---|---|
LLVMSupport | 无 | 基础工具类 | 命令行、内存管理、调试 |
LLVMCore | Support | IR 核心 | Module/Function/Instruction |
LLVMAnalysis | Core | 分析 Pass | 循环分析、别名分析 |
LLVMTransformUtils | Analysis | 转换工具 | 内联、循环变换 |
LLVMScalarOpts | TransformUtils | 标量优化 | GVN、InstCombine |
LLVMVectorize | TransformUtils | 向量化 | SLP、Loop Vectorizer |
LLVMipo | 多个 | 过程间优化 | LTO、全局优化 |
LLVMCodeGen | Core, MC | 代码生成 | 指令选择、寄存器分配 |
LLVMTarget | CodeGen | 目标框架 | TargetMachine、TargetLowering |
LLVMMC | Support | MC层 | MCInst、汇编器 |
LLVMObject | MC | 目标文件 | ELF、MachO、COFF |
LLVMBitReader | Core | Bitcode 读取 | .bc 文件解析 |
LLVMBitWriter | Core | Bitcode 写入 | .bc 文件生成 |
LLVMX86CodeGen | CodeGen | x86 后端 | x86 指令生成 |
LLVMX86AsmParser | MC | x86 汇编解析 | x86 汇编输入 |
LLVMX86Desc | MC | x86 描述 | x86 指令/寄存器描述 |
3.5.3 查看库信息
# 查看 LLVM 所有可用组件
llvm-config --components
# 查看某个组件的依赖
llvm-config --libs core analysis
# 查看所有库
llvm-config --libs all
# 查看系统链接标志
llvm-config --system-libs --ldflags
3.6 数据流与控制流
3.6.1 Value-Use 数据流
LLVM IR 使用**SSA(Static Single Assignment)**形式,每个变量只被赋值一次:
; SSA 形式示例
define i32 @example(i32 %x) {
entry:
%a = add i32 %x, 1 ; %a 只在此处定义
%b = mul i32 %a, 2 ; %b 只在此处定义
%c = add i32 %b, %a ; %c 使用了 %a 和 %b
ret i32 %c
}
Value-Use 链:
%a ──uses──► %b (via mul)
%a ──uses──► %c (via add)
%b ──uses──► %c (via add)
Use-Def 链:
%c ──defs──► %a (via add)
%c ──defs──► %b (via mul)
3.6.2 PHI 节点与控制流合并
当控制流汇合时,使用 PHI 节点选择正确的值:
define i32 @abs(i32 %x) {
entry:
%cmp = icmp sgt i32 %x, 0
br i1 %cmp, label %then, label %else
then:
br label %merge
else:
%neg = sub i32 0, %x
br label %merge
merge:
%result = phi i32 [ %x, %then ], [ %neg, %else ]
ret i32 %result
}
控制流图 (CFG):
entry
┌──────────┐
│ cmp = ...│
│ br cmp │
└──┬───┬───┘
│ │
then else
┌──┐ ┌────────┐
│ │ │ neg = -x│
└┬─┘ └───┬────┘
│ │
└───┬────┘
│
merge
┌─────────────────────┐
│ result = φ(x, neg) │
│ ret result │
└─────────────────────┘
3.7 类型系统
3.7.1 LLVM IR 类型层次
Type
├── VoidType (void)
├── HalfType (half — 16-bit float)
├── FloatType (float — 32-bit)
├── DoubleType (double — 64-bit)
├── X86_FP80Type (x86_fp80 — 80-bit)
├── FP128Type (fp128 — 128-bit)
├── IntegerType (i1, i8, i16, i32, i64, i128, ...)
├── FunctionType (i32 (i32, i32))
├── StructType ({i32, float, i8*})
├── ArrayType ([10 x i32])
├── PointerType (i32*, ptr)
├── VectorType (<4 x float>)
└── LabelType (label — basic block 标签)
3.7.2 地址空间
; 默认地址空间 (0)
%ptr = alloca i32
; 带显式地址空间
%ptr_global = addrspace(1) global i32 0
%ptr_shared = addrspace(3) global i32 0
; 常用地址空间
; 0: 通用地址空间 (CPU)
; 1: 全局内存 (GPU global memory)
; 2: 共享内存 (GPU shared/local memory)
; 3: 常量内存 (GPU constant memory)
3.8 Target Machine 抽象
每个目标后端需要实现 TargetMachine 接口:
// llvm/lib/Target/X86/X86TargetMachine.h (简化)
class X86TargetMachine : public LLVMTargetMachine {
public:
// 获取目标特定的指令信息
const X86InstrInfo *getInstrInfo() const override;
// 获取目标特定的寄存器信息
const X86RegisterInfo *getRegisterInfo() const override;
// 获取目标特定的帧低层信息
const X86FrameLowering *getFrameLowering() const override;
// 获取目标特定的指令低层信息
const X86TargetLowering *getTargetLowering() const override;
// 创建 MachineFunctionPass(代码生成入口)
MachineFunctionInfo *createMachineFunctionInfo(...) const;
// 添加代码生成 Pass 到流水线
void addPassesToEmitFile(
PassManagerBase &PM,
raw_postream &File,
CodeGenFileType FileType,
bool DisableVerify
) override;
};
3.8.1 Target 描述的三个层次
TableGen 描述 (.td 文件)
│
│ tblgen 工具
▼
┌──────────────────────────────┐
│ 自动生成的 C++ 头文件 │
│ X86GenInstrInfo.inc │
│ X86GenRegisterInfo.inc │
│ X86GenAsmWriter.inc │
└──────────────┬───────────────┘
│
│ 编译
▼
┌──────────────────────────────┐
│ 目标后端实现 │
│ X86InstrInfo.cpp │
│ X86RegisterInfo.cpp │
│ X86AsmPrinter.cpp │
└──────────────────────────────┘
3.9 编译流程全景
3.9.1 clang -O2 背后的完整流程
# 当你执行:
clang -O2 test.c -o test
# 实际经历了以下阶段:
test.c
│
├─ 预处理 (clang -E)
│ ↓
│ test.i
│
├─ 词法分析 + 语法分析 (clang Frontend)
│ ↓
│ Clang AST
│
├─ 语义分析 + IR 生成 (CodeGen)
│ ↓
│ LLVM IR (test.ll)
│
├─ 优化 Pass 流水线 (opt -O2)
│ ├─ SimplifyCFG
│ ├─ InstCombine
│ ├─ SROA
│ ├─ EarlyCSE
│ ├─ InlinerPass
│ ├─ GVN
│ ├─ LoopVectorize
│ ├─ SLPVectorize
│ └─ ... (约 100 个 Pass)
│ ↓
│ 优化后 LLVM IR
│
├─ 代码生成 (llc -O2)
│ ├─ SelectionDAG 构建
│ ├─ 指令选择
│ ├─ 指令调度 (pre-RA)
│ ├─ 寄存器分配
│ ├─ 指令调度 (post-RA)
│ ├─ Peephole 优化
│ └─ 汇编发射
│ ↓
│ test.s (汇编)
│
├─ 汇编器 (as / MC层)
│ ↓
│ test.o (目标文件)
│
└─ 链接器 (lld / ld)
↓
test (可执行文件)
3.9.2 优化级别
| 级别 | 说明 | Pass 数量 | 编译速度 | 运行速度 |
|---|---|---|---|---|
-O0 | 无优化 | ~10 | ⚡⚡⚡ | 🐢 |
-O1 | 基础优化 | ~40 | ⚡⚡ | 🏃 |
-O2 | 标准优化 | ~100 | ⚡ | 🚀 |
-O3 | 激进优化 | ~120 | 🐢 | 🚀🚀 |
-Os | 体积优化 | ~90 | ⚡ | 🏃 |
-Oz | 最小体积 | ~80 | ⚡ | 🚶 |
3.10 本章小结
| 概念 | 要点 |
|---|---|
| IR 层次 | AST → LLVM IR → SelectionDAG/MIR → MCInst |
| IR 形式 | .ll (文本)、.bc (二进制)、内存对象 |
| Pass 框架 | 分析 Pass + 转换 Pass,新 PassManager 为默认 |
| 后端流水线 | IR → DAG → 指令选择 → 调度 → 寄存器分配 → MC |
| 库设计 | 每个功能模块是独立的库,可选择性链接 |
| 类型系统 | 丰富的类型层次,支持地址空间 |
扩展阅读
- LLVM Programmer’s Manual — 编程指南
- LLVM Source Tree — 源码树结构
- Writing an LLVM Pass — 新 PassManager 编写指南
- LLVM Code Generation — 代码生成器文档
下一章: 第 4 章:LLVM IR 详解 — 深入学习 LLVM IR 的语法、类型系统和指令集。