⚙️ 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

🔀 为什么采用 SSA

  • 每个值只定义一次
  • 优化更容易
  • 汇合点用 PHI 表达

🧩 核心对象关系

LLVMContext → Module → Function → BasicBlock → Instruction → Value/Use

📈 架构演进

  • Builder API 越来越丰富
  • Verifier 越来越严格
  • 更多 metadata / 属性被标准化

⚡ 生成流程 6 步

  1. 建 Context/Module/Function
  2. 建 BasicBlock
  3. SetInsertPoint
  4. Create 指令
  5. 补属性与 metadata
  6. 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
SSAPHI

🏛️ 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
  • 调用语义远比表面复杂

🌿 BranchInst

  • 无条件分支一个后继
  • 条件分支两个后继

🔄 CastInst

  • zext / sext / trunc
  • bitcast / ptrcast

🔗 PHINode

  • 汇合不同前驱值
  • 必须放在块头

🪢 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 收口

🧪 案例 2:函数调用

  • 先声明 callee
  • 准备参数
  • CreateCall

🧪 案例 3:循环

preheader -> header -> body -> latch -> header

循环变量一般由 header 中的 PHI 表达。

🚀 性能优化 1

  • 复用 Context / 常量
  • 顺序生成,少重排

🚀 性能优化 2

  • 设置准确属性
  • 给优化器更多语义

🚀 性能优化 3

  • 开发期多 verify
  • 批量场景按边界 verify

✅ 最佳实践 1

  • 显式管理 InsertPoint
  • 生成 terminator 后不要继续插普通指令

✅ 最佳实践 2

  • 先想 CFG,再写 CreateBr / CreateCondBr
  • PHI incoming 要和真实前驱对齐

✅ 最佳实践 3

  • 类型一次做对
  • 别把 bitcast 当万能胶

❌ 反模式 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 协作保证图结构、类型系统和控制流都正确。