⚙️ LLVM IR 生成
从 IRBuilder 到 Verifier 的源码级深度解析
基于 llvm/lib/IR 核心源码
2026-03-11 | 技术深度解读
📑 目录
- LLVM 与 IR 定位
- 核心数据模型
- LLVMContext / Function / BasicBlock
- IRBuilder 插入点与创建 API
- PHI / Call / Branch / Cast
- CFG 与 SSA
- Verifier 校验机制
- 设计模式
- 性能优化
- 最佳实践与反模式
🧠 LLVM 是什么
LLVM IR 是前端与后端之间的公共语言层。
📦 本次分析文件
| 文件 | 作用 |
|---|
| IRBuilder.h/.cpp | 造指令 |
| Function.cpp | 函数对象 |
| BasicBlock.cpp | 基本块管理 |
| Instructions.cpp | 具体指令实现 |
| Verifier.cpp | 合法性校验 |
| LLVMContext.cpp | 上下文与诊断 |
🏗️ IR 生成总览
AST → CodeGen → IRBuilder → Function/BasicBlock/Instruction → Verifier → Pass Manager → Backend
🧩 核心对象关系
LLVMContext → Module → Function → BasicBlock → Instruction → Value/Use
📈 架构演进
- Builder API 越来越丰富
- Verifier 越来越严格
- 更多 metadata / 属性被标准化
⚡ 生成流程 6 步
- 建 Context/Module/Function
- 建 BasicBlock
- SetInsertPoint
- Create 指令
- 补属性与 metadata
- verify
🌐 LLVMContext
- 全局语义根
- 管理诊断与 kind registry
- 很多 API 都依赖它
🔔 diagnose 机制
LLVMContext::emitError(...)
-> diagnose(DiagnosticInfo...)
-> handler/callback
统一错误与警告出口。
🏭 Function
- 函数签名、参数、属性、BasicBlock 容器
- 优化器强依赖其属性信息
🛠️ Function::Create
Function::Create(FunctionType *Ty, LinkageTypes Linkage, ...)
创建时就绑定类型、linkage、归属模块。
🏷️ Function 属性系统
- Linkage
- CallingConv
- Attributes
- Section / Comdat
🧹 dropAllReferences
销毁前先打断引用关系,避免循环依赖。
🧱 BasicBlock
- CFG 基本节点
- 顺序指令链
- 以 terminator 收尾
✂️ splitBasicBlock
把一个块切成两个,自动维护基本跳转结构。
🔁 removePredecessor
删除前驱时同步修 PHI incoming。
🚦 Terminator 规则
- 块尾必须是 terminator
- 后面不能再插普通指令
🧰 Instruction 家族
| 类 | 示例 |
|---|
| 算术 | Add/Mul/Sub |
| 控制流 | Br/Ret/Switch |
| 调用 | Call/Invoke |
| 转换 | ZExt/BitCast |
| SSA | PHI |
🏛️ IRBuilderBase
- 保存插入点
- 统一插入逻辑
- 对前端暴露 CreateXxx API
📍 InsertPoint / Insert
SetInsertPoint(BasicBlock*)
SetInsertPoint(Instruction*)
Insert(Instruction*)
➕ CreateAdd
auto *v = Builder.CreateAdd(lhs, rhs, "sum")
最终落到 BinaryOperator::Create。
↘️ CreateCondBr
Builder.CreateCondBr(cond, thenBB, elseBB)
CFG 分叉的核心 API。
🎯 插入点为何关键
- 插错块,语义就错
- 插在 terminator 后直接 invalid
- 会影响 dominance 与 PHI
🧮 BinaryOperator
算术类指令最终由 BinaryOperator 承载。
📞 CallInst
- callee + args + attrs + bundles
- 调用语义远比表面复杂
🔄 CastInst
- zext / sext / trunc
- bitcast / ptrcast
🪢 removeIncomingValue
边删除时同步移除 PHI incoming。
🧪 PHI 生成范式
entry:
br i1 %cond, label %then, label %else
then:
%a = add i32 %x, 1
br label %merge
else:
%b = sub i32 %x, 1
br label %merge
merge:
%v = phi i32 [ %a, %then ], [ %b, %else ]
🗺️ 控制流到 SSA
if/else AST → entry/then/else/merge → BranchInst + PHI
✅ Verifier 的意义
- Builder 不保证全局正确
- Verifier 是最后硬校验
🔍 visitInstruction
检查 operand 空值、父块归属、基础一致性。
📏 常见校验项
- PHI incoming 与前驱块匹配
- Branch 条件必须 i1
- Call 参数类型匹配
- BasicBlock 以 terminator 结束
🧭 Verifier 哲学
尽早失败,别把畸形 IR 放给后续优化器。
🧱 设计模式 1:Builder
把复杂构造过程封装为稳定 API。
🌲 设计模式 2:Composite
Function / BasicBlock / Instruction 构成组合结构。
👣 设计模式 3:Visitor
Verifier 与很多 Pass 都依赖 visitor 风格分发。
🛡️ 设计模式 4:RAII / Guard
InsertPointGuard 用来安全恢复插入点。
📐 UML 关系图
Value -> User -> Instruction -> {BinaryOperator, CallInst, BranchInst, CastInst, PHINode}
Function -> BasicBlock*
LLVMContext = semantic root
⏱️ 时序图
Create Function → Create Block → SetInsertPoint → Create IR → verifyFunction
📊 Value / Use 数据流
%1 = add i32 %a, %b
%2 = mul i32 %1, 4
%3 = call i32 @foo(i32 %2)
🧪 案例 1:if/else
- 先建块
- entry 条件跳转
- merge 建 PHI 收口
🧪 案例 3:循环
preheader -> header -> body -> latch -> header
循环变量一般由 header 中的 PHI 表达。
🚀 性能优化 3
- 开发期多 verify
- 批量场景按边界 verify
✅ 最佳实践 1
- 显式管理 InsertPoint
- 生成 terminator 后不要继续插普通指令
✅ 最佳实践 2
- 先想 CFG,再写 CreateBr / CreateCondBr
- PHI incoming 要和真实前驱对齐
❌ 反模式 1
在 terminator 后继续插指令。
❌ 反模式 2
PHI incoming 不完整或 block 指针错误。
❌ 反模式 3
过度依赖裸 new Instruction,而不是 Builder API。
🆚 LLVM IR vs AST / MLIR
| 层 | 优势 | 局限 |
|---|
| AST | 贴近语言 | 不适合底层优化 |
| MLIR | 多层方言 | 更复杂 |
| LLVM IR | 统一成熟 | 离高层语义更远 |
📝 一句话总结各文件
- IRBuilder:生成门面
- Function:语义容器
- BasicBlock:CFG 原子节点
- Instructions:具体语义
- Verifier:守门员
- LLVMContext:全局语义根
📚 扩展阅读
- LLVM Language Reference
- Clang CodeGen 源码
- InstCombine / GVN / SimplifyCFG
🎯 总结
LLVM IR 生成 本质上是在共享 Context 中,为 Function/BasicBlock 构造一张合法 SSA 图,并通过 Builder API 与 Verifier 协作保证图结构、类型系统和控制流都正确。