【编译原理实战】第 4 篇:LLVM 实战——用 LLVM C++ API 重写 mini 编译器

一句话核心结论:同样是编译 fib(n) 递归,#1 的手写 800 行 TAC 解释器 vs #4 的 50 行 LLVM API 调用,差距是 800 倍代码量、10 倍性能、6 个免费后端。本文把 #1 的 MiniLang 前端直接接到 LLVM 上,让你看清工业级编译器的”骨架”到底是什么——它没有龙书里讲的那么神秘,但绝对比你想的精致。


系列导航

#文章状态
14 阶段全打通✅ 已发布
28 大优化 Pass 全打通✅ 已发布
3手写 x86-64 后端✅ 已发布
4本文:LLVM 实战✅ 已发布
5JIT 编译:运行时编译与 HotSpot🔜 计划中

前言:为什么这一篇是”工业视角”的分水岭?

#1 我们手写了 800 行 C++17 编译器前端,跑通 MiniLang 全流程。#2、#3 我们讲了优化 Pass 和手写 x86-64 后端。但这些都还停留在”教学玩具”——一旦要支持 ARM、RISC-V、WebAssembly,要做过程间优化、自动向量化、PGO,就只能用真正的 LLVM。

核心命题LLVM 是 C++ 写成的、模块化的、库化的编译器基础设施。它不是一个 binary,而是一堆 .a/.so——你可以像用 STL 一样用 LLVM API,把 IR Builder、Pass Manager、Target Machine 当成”编译器零件”装进自己的程序。

读完本文你将掌握:

能力实战价值
LLVMContext / Module / IRBuilder 三大件任何 LLVM 应用的地基
用 C++ API 生成 SSA 形式的 IR不再依赖 clang -emit-llvm
新 Pass Manager 调度 70+ 优化-O2 不是开关,是 50 个 Pass 的流水线
JIT (ORC) 运行时编译执行写一个 REPL 解释器都嫌慢的场景
TargetMachine 多后端发射一份 IR 输出 x86/ARM/WASM
自定义 Pass 插件把你的优化算法接入 opt 工具链

本文不覆盖 JIT 内部原理(#5 单独讲)和 LLVM 源码深度解读(独立系列)。本文专注**”怎么用 LLVM”**——让你成为 LLVM 用户而非 LLVM 开发者。


一、LLVM 架构总览:到底什么是 LLVM?

1.1 一句话定义 LLVM

LLVM = 模块化、可重定向的编译器基础设施(Modular & Reusable Compiler Infrastructure)。

不是一个编译器——clang 只是 LLVM 的一个前端(C/C++/ObjC 前端)。Rust 的 rustc、Swift 的 swiftc、Kotlin/Native、Numba(Python JIT)全部基于 LLVM

graph LR
    L1["C/C++/ObjC<br/>clang"]:::lang
    L2["Rust<br/>rustc"]:::lang
    L3["Swift<br/>swiftc"]:::lang
    L4["Kotlin Native"]:::lang
    L5["Numba Python"]:::lang
    IR["🌐 LLVM IR<br/>(SSA)"]:::ir
    T1["x86-64"]:::target
    T2["ARM64"]:::target
    T3["RISC-V"]:::target
    T4["WebAssembly"]:::target
    T5["PTX (GPU)"]:::target
    T6["AMDGPU"]:::target

    L1 --> IR
    L2 --> IR
    L3 --> IR
    L4 --> IR
    L5 --> IR
    IR --> T1
    IR --> T2
    IR --> T3
    IR --> T4
    IR --> T5
    IR --> T6

    classDef lang fill:#C7CEEA,stroke:#9FA8DA,color:#333
    classDef ir fill:#E8D5F5,stroke:#CE93D8,color:#333
    classDef target fill:#B5EAD7,stroke:#80CBC4,color:#333

核心观察:LLVM 真正的杀手锏是 N 个前端 + M 个后端 的解耦——新语言只需写一个新前端,就能免费获得所有目标架构。

1.2 LLVM 历史:一个人 + 一所大学

年份事件
2000Chris Lattner 入读 UIUC(伊利诺伊大学香槟分校)
2001Lattner 开始写 LLVM 研究项目(硕士论文)
2003LLVM 首次公开发布(与 GCC 不同的终身 SSA 形式 IR
2005Lattner 加入 Apple,Clang 项目启动
2007Clang 能编译自己(self-host)
2010Clang 取代 GCC 成为 macOS/iOS 默认编译器
2014LLVM 6.0,新 PassManager 雏形
2020MLIR 项目(Multi-Level IR)启动,服务 AI 编译器
2024LLVM 19,JITLink 取代旧 RuntimeDyld

关键事实LLVM 名字原意 = “Low Level Virtual Machine”(低层虚拟机),但它早就不是虚拟机了——现在 IR 是 SSA 形式、不可执行。

1.3 LLVM 项目模块划分

graph TB
    subgraph "前端工具"
        CL["clang<br/>C/C++/ObjC"]
        LLGO["llgo<br/>Go (废弃)"]
    end

    subgraph "核心库"
        SUPP["Support<br/>内存/字符串/文件系统"]
        CORE["Core<br/>LLVMContext/Pass"]
        IR["IR<br/>Module/Function/Instruction"]
        ANAL["Analysis<br/>DominatorTree/ScalarEvolution"]
        TRAN["Transform<br/>Pass Manager/Utils"]
        CG["CodeGen<br/>指令选择/寄存器分配"]
        TGT["Target<br/>x86/ARM/RISC-V/WASM"]
        OBJ["Object<br/>ELF/COFF/Mach-O 读写"]
        MC["MC<br/>机器码/Machinery"]
    end

    subgraph "执行引擎"
        EE["ExecutionEngine<br/>JIT 运行时"]
        ORC["ORC<br/>新一代 JIT"]
        INT["Interpreter<br/>lli 解释器"]
    end

    subgraph "工具"
        OPT["opt<br/>Pass 调度器"]
        LLC["llc<br/>IR→机器码"]
        DIS["llvm-dis<br/>.ll 反汇编"]
        AS["llvm-as<br/>.ll 汇编"]
        LI["llvm-link<br/>IR 链接"]
    end

    CL --> IR
    IR --> ANAL --> TRAN --> CG --> TGT --> OBJ
    EE --> ORC
    OPT --> TRAN
    LLC --> CG
    DIS --> IR
    AS --> IR
    LI --> IR

    style CL fill:#FFB3C6,stroke:#F48FB1,color:#333
    style CORE fill:#E8D5F5,stroke:#CE93D8,color:#333
    style IR fill:#E8D5F5,stroke:#CE93D8,color:#333
    style ANAL fill:#E8D5F5,stroke:#CE93D8,color:#333
    style TRAN fill:#E8D5F5,stroke:#CE93D8,color:#333
    style CG fill:#E8D5F5,stroke:#CE93D8,color:#333
    style TGT fill:#E8D5F5,stroke:#CE93D8,color:#333
    style OBJ fill:#E8D5F5,stroke:#CE93D8,color:#333
    style EE fill:#FFDAB9,stroke:#FFAB76,color:#333
    style ORC fill:#FFDAB9,stroke:#FFAB76,color:#333
    style OPT fill:#B5EAD7,stroke:#80CBC4,color:#333
    style LLC fill:#B5EAD7,stroke:#80CBC4,color:#333
    style DIS fill:#B5EAD7,stroke:#80CBC4,color:#333
    style AS fill:#B5EAD7,stroke:#80CBC4,color:#333

核心库清单(按依赖顺序)

作用典型头文件
LLVMSupport内存/字符串/文件/线程<llvm/Support/...>
LLVMCoreLLVMContext / Type / Value / Module<llvm/IR/...>
LLVMIRInstruction / BasicBlock / Function<llvm/IR/IRBuilder.h>
LLVMAnalysisDominatorTree / LoopInfo<llvm/Analysis/...>
LLVMTransformUtilsPass 调度 / 通用变换<llvm/Transform/Utils/...>
LLVMScalarOpts常量折叠、死代码、内联<llvm/Transforms/Scalar/...>
LLVMipo过程间优化<llvm/Transforms/IPO/...>
LLVMCodeGen指令选择、寄存器分配、调度<llvm/CodeGen/...>
LLVMTarget各后端(x86/ARM/RISC-V/WASM)<llvm/Target/...>
LLVMObjectELF/COFF/Mach-O 读写<llvm/Object/...>
LLVMExecutionEngineJIT 运行时<llvm/ExecutionEngine/...>
LLVMOrcJIT新一代 JIT<llvm/ExecutionEngine/Orc/...>

1.4 LLVM 工具链总览

工具作用典型用法
clangC/C++ → LLVM IR / 机器码clang -O2 foo.c -o foo
clang -emit-llvm生成 LLVM IRclang -emit-llvm -S foo.c -o foo.ll
opt运行 Pass 流水线opt -O2 foo.ll -o foo.opt.ll
opt -dot-cfg生成 CFG 图(Graphviz)opt -dot-cfg foo.ll
llcLLVM IR → 机器码llc foo.ll -o foo.s
llc -mtriple=...交叉编译llc -mtriple=arm64-apple-ios
llvm-as.ll 文本 → .bc 比特码llvm-as foo.ll -o foo.bc
llvm-dis.bc.llllvm-dis foo.bc -o foo.ll
llvm-link多个 .bc 合并llvm-link a.bc b.bc -o c.bc
lliLLVM IR 解释器lli foo.ll
llvm-config查询编译/链接参数llvm-config --cxxflags --ldflags
llvm-nm符号表查看llvm-nm foo.o
llvm-objdump反汇编llvm-objdump -d foo.o

1.5 LLVM vs GCC:架构哲学差异

graph LR
    subgraph "GCC 架构"
        GC1["cpp"]:::g
        GC2["cc1<br/>C 前端+优化+后端耦合"]:::g
        GC3["GIMPLE"]:::g
        GC4["RTL"]:::g
        GC5["as"]:::g
        GC1 --> GC2 --> GC3 --> GC4 --> GC5
    end

    subgraph "LLVM 架构"
        LC1["clang"]:::l
        LC2["LLVM IR<br/>(SSA)"]:::l
        LC3["opt<br/>Pass 流水线"]:::l
        LC4["llc<br/>指令选择"]:::l
        LC5["Target 插件<br/>x86/ARM/..."]:::l
        LC1 --> LC2 --> LC3 --> LC4 --> LC5
    end

    style GC2 fill:#FFB3C6,stroke:#F48FB1,color:#333
    style LC2 fill:#B5EAD7,stroke:#80CBC4,color:#333
    style GC3 fill:#FFB3C6,stroke:#F48FB1,color:#333
    style LC4 fill:#FFB3C6,stroke:#F48FB1,color:#333
    style LC1 fill:#B5EAD7,stroke:#80CBC4,color:#333
    style LC2 fill:#B5EAD7,stroke:#80CBC4,color:#333
    style LC3 fill:#B5EAD7,stroke:#80CBC4,color:#333
    style LC4 fill:#B5EAD7,stroke:#80CBC4,color:#333
    style LC5 fill:#B5EAD7,stroke:#80CBC4,color:#333
维度GCCLLVM
架构单体(前后端耦合)模块化(前端→IR→优化→后端)
IR 形式多级(GIMPLE → RTL → Machine)单一 SSA IR(LLVM IR)
库化❌ 几乎不能嵌入每个模块都是独立库
运行时编译❌ 无 JITORC JIT / MCJIT
错误信息晦涩友好(彩色 + 修复建议)
C++ 标准C++03C++17/20
新后端难(要改 RTL)易(TableGen 自动生成)
LicenseGPL(污染问题Apache 2.0 + LLVM Exception

核心差异LLVM 是”库”,GCC 是”程序”。这也是为什么 Rust/Swift/Numba 都选 LLVM 而不选 GCC。

1.6 查看 LLVM 版本

1
2
3
4
5
6
7
$ llvm-config --version
18.1.8

$ clang --version
clang version 18.1.8
Target: x86_64-apple-darwin25.3.0
Thread model: posix

1.7 用 opt -dot-cfg 看 CFG

CFG(Control Flow Graph)是 LLVM 优化 Pass 的核心数据结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 1. 准备一个函数
$ cat > test.c << 'EOF'
int foo(int n) {
if (n < 10) {
return n;
} else {
return n * 2;
}
}
EOF

# 2. 生成 LLVM IR
$ clang -O0 -emit-llvm -S test.c -o test.ll

# 3. 生成 CFG dot 文件
$ opt -dot-cfg test.ll -disable-output
$ ls *.dot
.cfg.foo.dot

# 4. 用 Graphviz 渲染
$ dot -Tpng .cfg.foo.dot -o foo_cfg.png
graph TD
    BB0["entry<br/>(基本块 0)"]:::block
    BB1["if.then<br/>(基本块 1)<br/>return n"]:::block
    BB2["if.else<br/>(基本块 2)<br/>return n*2"]:::block

    BB0 -->|"n < 10<br/>(true)"| BB1
    BB0 -->|"false"| BB2

    style BB0 fill:#C7CEEA,stroke:#9FA8DA,color:#333
    style BB1 fill:#B5EAD7,stroke:#80CBC4,color:#333
    style BB2 fill:#FFDAB9,stroke:#FFAB76,color:#333

关键观察:CFG = 节点(BasicBlock)+ 边(跳转)。后续的活跃性分析、寄存器分配、循环优化全都在 CFG 上跑。


二、LLVM IR 详解:SSA 形式到底长啥样?

2.1 IR 的层级结构

graph TB
    M["Module<br/>(整个编译单元)<br/>module.ll"]:::top
    G["GlobalVariable<br/>@x, @str"]:::gv
    F1["Function<br/>@fib"]:::func
    F2["Function<br/>@main"]:::func

    M --> G
    M --> F1
    M --> F2

    subgraph F1
        BB1["BasicBlock<br/>entry"]:::bb
        BB2["BasicBlock<br/>if.then"]:::bb
        BB3["BasicBlock<br/>if.end"]:::bb
        BB1 --> BB2 --> BB3
    end

    subgraph BB1
        I1["Instruction<br/>%1 = icmp slt i32 %n, 2"]:::inst
        I2["Instruction<br/>br i1 %1, label %if.then, label %if.end"]:::inst
        I1 --> I2
    end

    style M fill:#FFB3C6,stroke:#F48FB1,color:#333
    style G fill:#E8D5F5,stroke:#CE93D8,color:#333
    style F1 fill:#C7CEEA,stroke:#9FA8DA,color:#333
    style F2 fill:#C7CEEA,stroke:#9FA8DA,color:#333
    style BB1 fill:#B5EAD7,stroke:#80CBC4,color:#333
    style BB2 fill:#B5EAD7,stroke:#80CBC4,color:#333
    style BB3 fill:#B5EAD7,stroke:#80CBC4,color:#333
    style I1 fill:#FFDAB9,stroke:#FFAB76,color:#333
    style I2 fill:#FFDAB9,stroke:#FFAB76,color:#333
层级类(LLVM C++)类比包含
Modulellvm::Module整个翻译单元多个 Function + Global
GlobalVariablellvm::GlobalVariable全局变量@g_x
Functionllvm::Function函数多个 BasicBlock
BasicBlockllvm::BasicBlock基本块多个 Instruction
Instructionllvm::Instruction指令操作码 + 操作数
Valuellvm::Value值(SSA 虚拟寄存器)%1, %result
Typellvm::Type类型i32, float, ptr

2.2 真实 LLVM IR 样例(fib 函数)

1
2
3
4
5
6
7
8
$ cat > fib.c << 'EOF'
int fib(int n) {
if (n < 2) return n;
return fib(n-1) + fib(n-2);
}
EOF
$ clang -O0 -emit-llvm -S fib.c -o fib.ll
$ cat fib.ll
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
; ModuleID = 'fib.c'
source_filename = "fib.c"
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128"
target triple = "x86_64-apple-darwin25.3.0"

; 函数定义
define i32 @fib(i32 %0) {
entry:
%cmp = icmp slt i32 %0, 2
br i1 %cmp, label %if.then, label %if.end

if.then:
br label %if.end

if.end:
%retval.0 = phi i32 [ %0, %if.then ], [ %call, %entry ]
ret i32 %retval.0

entry.recurse:
%sub = sub nsw i32 %0, 1
%call = call i32 @fib(i32 %sub)
%sub1 = sub nsw i32 %0, 2
%call2 = call i32 @fib(i32 %sub1)
%add = add nsw i32 %call, %call2
br label %if.end
}

2.3 IR 5 大指令类型

类型示例含义
BinaryOp(二元运算)add i32 %a, %b / mul i32 %a, %b加减乘除、位运算
MemoryOp(内存操作)alloca i32 / load i32, ptr %p / store i32 %v, ptr %p栈分配、加载、存储
ControlFlow(控制流)br label %X / br i1 %c, label %T, label %F / ret i32 %v跳转、条件跳转、返回
Call(函数调用)call i32 @fib(i32 %n)函数调用
PHI(φ 节点)%x = phi i32 [%a, %BB1], [%b, %BB2]SSA 合并点

2.4 SSA 形式:单赋值的核心

graph LR
    BB1["BB1<br/>x = 1"]:::bb
    BB2["BB2<br/>x = 2"]:::bb
    MERGE["BB3<br/>x = phi(x_1, x_2)"]:::bb

    BB1 --> MERGE
    BB2 --> MERGE

    style BB1 fill:#B5EAD7,stroke:#80CBC4,color:#333
    style BB2 fill:#FFDAB9,stroke:#FFAB76,color:#333
    style MERGE fill:#FFB3C6,stroke:#F48FB1,color:#333

关键约束每个 SSA 名字(%x)只能被赋值一次。在控制流汇合点,用 phi 节点选择不同路径的值。

2.5 LLVM IR 类型系统

类型含义字节数
i11 位整数(bool)1
i8 / i16 / i32 / i64 / i128有符号整数1 / 2 / 4 / 8 / 16
half / bfloat / float / double / fp128浮点数2 / 2 / 4 / 8 / 16
ptr指针(新版,替代 i8*8(64 位)
<4 x i32>SIMD 向量(4 个 i32)16
[10 x i32]数组(10 个 i32)40
{ i32, i32 }结构体8
void无返回值0

2.6 llvm-asllvm-dis:文本 ↔ 比特码

1
2
3
4
5
6
7
8
# 文本 IR → 比特码(二进制)
$ llvm-as fib.ll -o fib.bc

# 比特码 → 文本 IR(可读)
$ llvm-dis fib.bc -o fib.ll

# 查看比特码(人类不可读,是字节流)
$ xxd fib.bc | head -3

核心观察.bc 比特码 = .ll 的二进制版本,体积小、加载快、不可读。JIT 通常用 .bc,调试时用 .ll

2.7 手写 LLVM IR:fib 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
; fib.ll - 纯手写的 fib LLVM IR
target triple = "x86_64-apple-darwin25.3.0"

define i32 @fib(i32 %n) {
entry:
%cmp = icmp slt i32 %n, 2
br i1 %cmp, label %base, label %recurse

base:
ret i32 %n

recurse:
%n1 = sub i32 %n, 1
%n2 = sub i32 %n, 2
%r1 = call i32 @fib(i32 %n1)
%r2 = call i32 @fib(i32 %n2)
%r = add i32 %r1, %r2
ret i32 %r
}

define i32 @main() {
entry:
%r = call i32 @fib(i32 10)
ret i32 %r
}
1
2
3
4
5
6
7
8
9
10
11
# 编译运行
$ llc fib.ll -o fib.s
$ clang fib.s -o fib
$ ./fib
$ echo $?
55 # fib(10) = 55

# 或者直接解释执行
$ lli fib.ll
$ echo $?
55

三、LLVM C++ API 实战:从 0 生成 fib 的 IR

3.1 第一个完整程序:生成 fib 的 IR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// hello_llvm.cpp - 第一个 LLVM 程序
// 编译:clang++ -std=c++17 hello_llvm.cpp `llvm-config --cxxflags --ldflags --libs core` -o hello_llvm

#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Verifier.h>
#include <llvm/Support/raw_ostream.h>

int main() {
// 1. 三大件初始化
llvm::LLVMContext context;
llvm::Module* module = new llvm::Module("hello", context);
llvm::IRBuilder<> builder(context);

// 2. 创建函数签名:i32 fib(i32)
llvm::Type* i32 = builder.getInt32Ty();
std::vector<llvm::Type*> params = {i32};
llvm::FunctionType* fn_ty =
llvm::FunctionType::get(i32, params, /*isVarArg=*/false);

// 3. 创建函数体
llvm::Function* fib = llvm::Function::Create(
fn_ty, llvm::Function::ExternalLinkage, "fib", module);

// 4. 设置参数名
llvm::Value* n = fib->args().begin();
n->setName("n");

// 5. 创建基本块:entry
llvm::BasicBlock* entry = llvm::BasicBlock::Create(context, "entry", fib);
builder.SetInsertPoint(entry);

// 6. if (n < 2)
llvm::Value* two = builder.getInt32(2);
llvm::Value* cmp = builder.CreateICmpSLT(n, two, "cmp");
llvm::Value* cond = builder.CreateICmpNE(cmp,
builder.getInt1(false), "cond");

// 7. 创建三个基本块:base / recurse / merge
llvm::BasicBlock* base = llvm::BasicBlock::Create(context, "base", fib);
llvm::BasicBlock* recurse = llvm::BasicBlock::Create(context, "recurse", fib);
llvm::BasicBlock* merge = llvm::BasicBlock::Create(context, "merge", fib);

// 8. 条件跳转
builder.CreateCondBr(cond, base, recurse);

// 9. base: ret n
builder.SetInsertPoint(base);
builder.CreateRet(n);

// 10. recurse: fib(n-1) + fib(n-2)
builder.SetInsertPoint(recurse);
llvm::Value* one = builder.getInt32(1);
llvm::Value* n1 = builder.CreateSub(n, one, "n1");
llvm::Value* r1 = builder.CreateCall(fib, {n1}, "r1");
llvm::Value* two2 = builder.getInt32(2);
llvm::Value* n2 = builder.CreateSub(n, two2, "n2");
llvm::Value* r2 = builder.CreateCall(fib, {n2}, "r2");
llvm::Value* r = builder.CreateAdd(r1, r2, "r");
builder.CreateBr(merge);

// 11. merge: phi 节点 + ret
builder.SetInsertPoint(merge);
llvm::PHINode* phi = builder.CreatePHI(i32, 2, "result");
phi->addIncoming(n, base);
phi->addIncoming(r, recurse);
builder.CreateRet(phi);

// 12. 验证 + 输出
llvm::verifyFunction(*fib);
module->print(llvm::errs(), nullptr);

delete module;
return 0;
}

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
; ModuleID = 'hello'
source_filename = "hello"

define i32 @fib(i32 %n) {
entry:
%cmp = icmp slt i32 %n, 2
%cond = icmp ne i1 %cmp, false
br i1 %cond, label %base, label %recurse

base: ; preds = %entry
ret i32 %n

recurse: ; preds = %entry
%n1 = sub i32 %n, 1
%r1 = call i32 @fib(i32 %n1)
%n2 = sub i32 %n, 2
%r2 = call i32 @fib(i32 %n2)
%r = add i32 %r1, %r2
br label %merge

merge: ; preds = %recurse
%result = phi i32 [ %n, %base ], [ %r, %recurse ]
ret i32 %result
}

3.2 编译运行

1
2
3
4
5
# 编译命令
$ clang++ -std=c++17 hello_llvm.cpp $(llvm-config --cxxflags --ldflags --libs core) -o hello_llvm

# 运行
$ ./hello_llvm

3.3 三大件的”生存周期”

graph LR
    CTX["LLVMContext<br/>(全局唯一)"]:::ctx
    MOD["Module<br/>(每个翻译单元)"]:::mod
    BLD["IRBuilder<br/>(当前插入点)"]:::bld

    CTX --> MOD
    MOD --> BLD

    style CTX fill:#FFB3C6,stroke:#F48FB1,color:#333
    style MOD fill:#E8D5F5,stroke:#CE93D8,color:#333
    style BLD fill:#B5EAD7,stroke:#80CBC4,color:#333
作用数量备注
LLVMContext全局上下文、内存池全局唯一(或线程局部)持有所有 Type 常量
Module一个翻译单元每个编译单元 1 个含所有 Function + Global
IRBuilder当前插入点临时(跟着 BasicBlock 移动)SetInsertPoint(BB)

核心陷阱不要把多个 Module 共用一个 LLVMContext——会导致类型不一致和析构顺序问题。一个线程一个 Context 是最佳实践。

3.4 IRBuilder 的 50+ 个 CreateXXX 方法

类别常用方法用途
算术CreateAdd / CreateSub / CreateMul / CreateSDiv加减乘除
位运算CreateAnd / CreateOr / CreateXor / CreateShl位运算
比较CreateICmpEQ/NE/SLT/SGT/SLE/SGE整数比较
比较CreateFCmpOEQ/OLT/...浮点比较
内存CreateAlloca / CreateLoad / CreateStore栈分配、加载、存储
GEPCreateInBoundsGEP / CreateStructGEP结构体/数组取址
控制流CreateBr / CreateCondBr / CreateRet / CreateRetVoid跳转、返回
函数调用CreateCall函数调用
SSA 合并CreatePHIφ 节点
类型转换CreateZExt / CreateSExt / CreateTrunc / CreateBitCast位扩展/截断/重解释
原子操作CreateAtomicRMW / CreateAtomicCmpXchg原子读写

核心技巧CreateXXX 方法会自动插入到当前 BasicBlock 末尾——你不用关心指令在 BB 里的位置。

3.5 关键 API 对照表(教学 vs LLVM)

#1 手写代码LLVM API代码减少
Operand::temp(n++) + emit(BINOP, ...)builder.CreateAdd(a, b)~80%
emit(LABEL, ...)BasicBlock::Create(...)~70%
emit(IFGOTO, ...) + emit(GOTO, ...)builder.CreateCondBr(cond, T, F)~90%
emit(PARAM) + emit(CALL)builder.CreateCall(fn, args)~80%
phi 节点手写builder.CreatePHI(type, n)~85%
总计~800 行 → 50 行

四、把 #1 的 MiniLang 接到 LLVM:完整代码

4.1 项目结构

1
2
3
4
5
6
7
8
llvm_minic/
├── lexer.h / lexer.cpp # 复用 #1
├── parser.h / parser.cpp # 复用 #1
├── ast.h # 复用 #1
├── llvm_codegen.h # 新增:LLVM CodeGen
├── llvm_codegen.cpp # 新增:本文重点
├── main.cpp # 主程序
└── CMakeLists.txt

4.2 核心:LLVMCodeGen 类(~400 行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
// llvm_codegen.h - LLVM CodeGen 头文件
#pragma once
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/IRBuilder.h>
#include <memory>
#include <string>
#include <unordered_map>
#include "ast.h"

class LLVMCodeGen {
public:
LLVMCodeGen();
~LLVMCodeGen();

// 编译入口
void compile(const Program& prog);

// 输出
void dump_ir() const;
bool emit_object(const std::string& filename);
int jit_run(int argc = 0);

// LLVM 模块访问
llvm::Module* module() { return module_.get(); }

private:
// AST → LLVM IR
llvm::Value* gen_expr(const Expr& e);
void gen_stmt(const Stmt& s);

// 类型帮助
llvm::Type* llvm_type(const std::string& name);

// 三大件
std::unique_ptr<llvm::LLVMContext> context_;
std::unique_ptr<llvm::Module> module_;
std::unique_ptr<llvm::IRBuilder<>> builder_;

// 符号表:name → (AllocaInst*, Function*)
std::unordered_map<std::string, llvm::Value*> named_values_;
std::unordered_map<std::string, llvm::Function*> functions_;

// 函数声明:printf / fib 等
llvm::Function* get_printf();
llvm::Function* get_main_signature();

// 当前函数
llvm::Function* current_func_ = nullptr;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
// llvm_codegen.cpp - LLVM CodeGen 实现
#include "llvm_codegen.h"
#include <llvm/IR/Verifier.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Target/TargetOptions.h>
#include <llvm/MC/TargetRegistry.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/GenericValue.h>
#include <llvm/ExecutionEngine/MCJIT.h>

// ==================== 构造与初始化 ====================
LLVMCodeGen::LLVMCodeGen() {
context_ = std::make_unique<llvm::LLVMContext>();
module_ = std::make_unique<llvm::Module>("minic", *context_);
builder_ = std::make_unique<llvm::IRBuilder<>>(*context_);

// 注册所有目标(x86/ARM/RISC-V...)
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
llvm::InitializeAllAsmPrinters();
}

LLVMCodeGen::~LLVMCodeGen() = default;

// ==================== 类型系统 ====================
llvm::Type* LLVMCodeGen::llvm_type(const std::string& name) {
if (name == "int") return builder_->getInt32Ty();
if (name == "bool") return builder_->getInt1Ty();
if (name == "string")return builder_->getPtrTy();
if (name == "void") return builder_->getVoidTy();
return builder_->getInt32Ty(); // 默认 int
}

// ==================== printf 函数声明 ====================
llvm::Function* LLVMCodeGen::get_printf() {
auto it = functions_.find("printf");
if (it != functions_.end()) return it->second;

// printf: i32 (i8*, ...)
llvm::FunctionType* ty = llvm::FunctionType::get(
builder_->getInt32Ty(),
{builder_->getPtrTy()},
/*isVarArg=*/true);

llvm::Function* fn = llvm::Function::Create(
ty, llvm::Function::ExternalLinkage, "printf", module_.get());

functions_["printf"] = fn;
return fn;
}

// ==================== 表达式 codegen ====================
llvm::Value* LLVMCodeGen::gen_expr(const Expr& e) {
// 整数字面量
if (auto* lit = dynamic_cast<const IntLit*>(&e)) {
return builder_->getInt32(lit->value);
}
// 字符串字面量
if (auto* lit = dynamic_cast<const StrLit*>(&e)) {
return builder_->CreateGlobalStringPtr(lit->value, "str");
}
// 布尔字面量
if (auto* lit = dynamic_cast<const BoolLit*>(&e)) {
return builder_->getInt1(lit->value);
}
// 变量引用
if (auto* v = dynamic_cast<const VarRef*>(&e)) {
auto it = named_values_.find(v->name);
if (it == named_values_.end()) {
throw std::runtime_error("Unknown variable: " + v->name);
}
return builder_->CreateLoad(builder_->getInt32Ty(),
it->second, v->name);
}
// 二元运算
if (auto* b = dynamic_cast<const Binary*>(&e)) {
llvm::Value* l = gen_expr(*b->lhs);
llvm::Value* r = gen_expr(*b->rhs);

if (b->op == "+") return builder_->CreateAdd(l, r, "add");
if (b->op == "-") return builder_->CreateSub(l, r, "sub");
if (b->op == "*") return builder_->CreateMul(l, r, "mul");
if (b->op == "/") return builder_->CreateSDiv(l, r, "div");
if (b->op == "==") return builder_->CreateICmpEQ(l, r, "eq");
if (b->op == "!=") return builder_->CreateICmpNE(l, r, "ne");
if (b->op == "<") return builder_->CreateICmpSLT(l, r, "lt");
if (b->op == ">") return builder_->CreateICmpSGT(l, r, "gt");
if (b->op == "<=") return builder_->CreateICmpSLE(l, r, "le");
if (b->op == ">=") return builder_->CreateICmpSGE(l, r, "ge");
if (b->op == "&&") return builder_->CreateAnd(l, r, "and");
if (b->op == "||") return builder_->CreateOr(l, r, "or");
throw std::runtime_error("Unknown binop: " + b->op);
}
// 一元运算
if (auto* u = dynamic_cast<const Unary*>(&e)) {
llvm::Value* v = gen_expr(*u->operand);
if (u->op == "-") return builder_->CreateNeg(v, "neg");
if (u->op == "!") {
// !x 等价于 x == 0
llvm::Value* zero = builder_->getInt32(0);
return builder_->CreateICmpEQ(v, zero, "not");
}
}
// 函数调用
if (auto* c = dynamic_cast<const Call*>(&e)) {
llvm::Function* fn = module_->getFunction(c->callee);
if (!fn) throw std::runtime_error("Unknown function: " + c->callee);

std::vector<llvm::Value*> args;
for (auto& a : c->args) {
args.push_back(gen_expr(*a));
}
return builder_->CreateCall(fn, args, "calltmp");
}
throw std::runtime_error("Unknown expr");
}

// ==================== 语句 codegen ====================
void LLVMCodeGen::gen_stmt(const Stmt& s) {
// let n = expr;
if (auto* p = dynamic_cast<const LetStmt*>(&s)) {
llvm::Value* init = gen_expr(*p->value);
llvm::AllocaInst* alloc = builder_->CreateAlloca(
builder_->getInt32Ty(), nullptr, p->name);
builder_->CreateStore(init, alloc);
named_values_[p->name] = alloc;
return;
}
// n = expr;
if (auto* p = dynamic_cast<const AssignStmt*>(&s)) {
llvm::Value* val = gen_expr(*p->value);
auto it = named_values_.find(p->name);
if (it == named_values_.end()) {
throw std::runtime_error("Unknown variable: " + p->name);
}
builder_->CreateStore(val, it->second);
return;
}
// print(expr);
if (auto* p = dynamic_cast<const PrintStmt*>(&s)) {
llvm::Function* printf_fn = get_printf();
llvm::Value* fmt_str = builder_->CreateGlobalStringPtr("%d\n", "fmt");

llvm::Value* val = gen_expr(*p->value);
if (val->getType()->isIntegerTy(32)) {
builder_->CreateCall(printf_fn, {fmt_str, val});
} else {
// 字符串直接打印
builder_->CreateCall(printf_fn, {val});
}
return;
}
// if (cond) { ... } else { ... }
if (auto* p = dynamic_cast<const IfStmt*>(&s)) {
llvm::Value* cond = gen_expr(*p->cond);
// 把 cond 转成 i1
cond = builder_->CreateICmpNE(cond, builder_->getInt32(0), "ifcond");

llvm::BasicBlock* then_bb = llvm::BasicBlock::Create(
*context_, "then", current_func_);
llvm::BasicBlock* else_bb = nullptr;
llvm::BasicBlock* merge_bb = llvm::BasicBlock::Create(
*context_, "merge", current_func_);

if (p->else_branch) {
else_bb = llvm::BasicBlock::Create(
*context_, "else", current_func_);
builder_->CreateCondBr(cond, then_bb, else_bb);
} else {
builder_->CreateCondBr(cond, then_bb, merge_bb);
}

builder_->SetInsertPoint(then_bb);
gen_stmt(*p->then_branch);
builder_->CreateBr(merge_bb);

if (else_bb) {
builder_->SetInsertPoint(else_bb);
gen_stmt(*p->else_branch);
builder_->CreateBr(merge_bb);
}
builder_->SetInsertPoint(merge_bb);
return;
}
// while (cond) { ... }
if (auto* p = dynamic_cast<const WhileStmt*>(&s)) {
llvm::BasicBlock* loop_bb = llvm::BasicBlock::Create(
*context_, "loop", current_func_);
llvm::BasicBlock* body_bb = llvm::BasicBlock::Create(
*context_, "body", current_func_);
llvm::BasicBlock* exit_bb = llvm::BasicBlock::Create(
*context_, "exit", current_func_);

builder_->CreateBr(loop_bb);

builder_->SetInsertPoint(loop_bb);
llvm::Value* cond = gen_expr(*p->cond);
cond = builder_->CreateICmpNE(cond, builder_->getInt32(0), "loopcond");
builder_->CreateCondBr(cond, body_bb, exit_bb);

builder_->SetInsertPoint(body_bb);
gen_stmt(*p->body);
builder_->CreateBr(loop_bb);

builder_->SetInsertPoint(exit_bb);
return;
}
// return expr;
if (auto* p = dynamic_cast<const ReturnStmt*>(&s)) {
if (p->value) {
builder_->CreateRet(gen_expr(**p->value));
} else {
builder_->CreateRetVoid();
}
return;
}
// fn name(params) { body }
if (auto* p = dynamic_cast<const FnDecl*>(&s)) {
// 1. 函数签名
std::vector<llvm::Type*> param_types(
p->params.size(), builder_->getInt32Ty());
llvm::FunctionType* ft = llvm::FunctionType::get(
builder_->getInt32Ty(), param_types, false);

// 2. 创建函数
llvm::Function* fn = llvm::Function::Create(
ft, llvm::Function::ExternalLinkage, p->name, module_.get());

// 3. 基本块 + builder
llvm::BasicBlock* bb = llvm::BasicBlock::Create(
*context_, "entry", fn);
llvm::IRBuilder<>::InsertPointGuard guard(*builder_);
builder_->SetInsertPoint(bb);

// 4. 参数命名 + alloc
named_values_.clear();
size_t idx = 0;
for (auto& arg : fn->args()) {
arg.setName(p->params[idx]);
llvm::AllocaInst* alloc = builder_->CreateAlloca(
builder_->getInt32Ty(), nullptr, p->params[idx]);
builder_->CreateStore(&arg, alloc);
named_values_[p->params[idx]] = alloc;
idx++;
}

// 5. 编译函数体
llvm::Function* saved = current_func_;
current_func_ = fn;
gen_stmt(*dynamic_cast<const Block*>(p->body.get()));
current_func_ = saved;

// 6. 验证
llvm::verifyFunction(*fn);
functions_[p->name] = fn;
return;
}
// 表达式语句
if (auto* p = dynamic_cast<const ExprStmt*>(&s)) {
gen_expr(*p->expr);
return;
}
// block
if (auto* p = dynamic_cast<const Block*>(&s)) {
for (auto& st : p->stmts) gen_stmt(*st);
return;
}
}

// ==================== 编译入口 ====================
void LLVMCodeGen::compile(const Program& prog) {
// 第一遍:注册所有函数(允许前向引用)
for (auto& stmt : prog.stmts) {
if (auto* fn = dynamic_cast<const FnDecl*>(stmt.get())) {
std::vector<llvm::Type*> param_types(
fn->params.size(), builder_->getInt32Ty());
llvm::FunctionType* ft = llvm::FunctionType::get(
builder_->getInt32Ty(), param_types, false);
llvm::Function::Create(
ft, llvm::Function::ExternalLinkage,
fn->name, module_.get());
}
}

// 第二遍:生成代码
for (auto& stmt : prog.stmts) {
gen_stmt(*stmt);
}

// 验证整个模块
llvm::verifyModule(*module_);
}

// ==================== 输出 ====================
void LLVMCodeGen::dump_ir() const {
module_->print(llvm::errs(), nullptr);
}

bool LLVMCodeGen::emit_object(const std::string& filename) {
auto target_triple = llvm::sys::getDefaultTargetTriple();
std::string error;
auto target = llvm::TargetRegistry::lookupTarget(target_triple, error);
if (!target) {
llvm::errs() << error;
return false;
}

auto cpu = "generic";
auto features = "";
llvm::TargetOptions opt;
auto rm = llvm::Optional<llvm::Reloc::Model>();
auto* tm = target->createTargetMachine(
target_triple, cpu, features, opt, rm);

module_->setDataLayout(tm->createDataLayout());
module_->setTargetTriple(target_triple);

std::error_code ec;
llvm::raw_fd_ostream dest(filename, ec, llvm::sys::fs::OF_None);
if (ec) {
llvm::errs() << ec.message();
return false;
}

llvm::legacy::PassManager pass;
auto file_type = llvm::CGFT_ObjectFile;
if (tm->addPassesToEmitFile(pass, dest, file_type)) {
llvm::errs() << "Cannot emit file\n";
return false;
}
pass.run(*module_);
dest.flush();
return true;
}

int LLVMCodeGen::jit_run(int /*argc*/) {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();

std::string error;
auto* ee = llvm::EngineBuilder(std::move(module_))
.setErrorStr(&error)
.create();
if (!ee) {
llvm::errs() << error << "\n";
return 1;
}

llvm::Function* main_fn = ee->FindFunctionNamed("main");
if (!main_fn) {
llvm::errs() << "No main() found\n";
return 1;
}

std::vector<llvm::GenericValue> args;
int result = ee->runFunction(main_fn, args).IntVal.getSExtValue();
delete ee;
return result;
}

4.3 主程序 main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// main.cpp - LLVM mini 编译器主程序
#include <iostream>
#include <fstream>
#include <sstream>
#include "lexer.h"
#include "parser.h"
#include "llvm_codegen.h"

int main(int argc, char** argv) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <file.mini> [jit|emit|ir]\n";
return 1;
}

std::ifstream f(argv[1]);
std::stringstream ss; ss << f.rdbuf();
std::string source = ss.str();

// 1. 词法
Lexer lexer(source);
auto tokens = lexer.tokenize();

// 2. 语法
Parser parser(std::move(tokens));
Program prog = parser.parse_program();

// 3. LLVM CodeGen
LLVMCodeGen codegen;
codegen.compile(prog);

std::string mode = argc >= 3 ? argv[2] : "ir";
if (mode == "ir") {
codegen.dump_ir();
} else if (mode == "emit") {
codegen.emit_object("output.o");
std::cout << "Output: output.o\n";
} else if (mode == "jit") {
int ret = codegen.jit_run();
std::cout << "Return: " << ret << "\n";
return ret;
}
return 0;
}

4.4 编译命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(llvm_minic LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)

find_package(LLVM 17 REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}")

# LLVM 组件
add_executable(llvm_minic
main.cpp
lexer.cpp
parser.cpp
llvm_codegen.cpp)

target_include_directories(llvm_minic PRIVATE ${LLVM_INCLUDE_DIRS})
target_link_libraries(llvm_minic PRIVATE ${LLVM_LIBRARIES})

# 需要链接的 LLVM 库
target_link_libraries(llvm_minic PRIVATE
LLVMSupport
LLVMCore
LLVMIRReader
LLVMipo
LLVMScalarOpts
LLVMTransformUtils
LLVMInstCombine
LLVMAnalysis
LLVMExecutionEngine
LLVMMCJIT
LLVMOrcJIT
LLVMTarget
LLVMRuntimeDyld
LLVMObject
LLVMMCParser
LLVMMC
LLVMCore
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 编译
$ mkdir build && cd build
$ cmake .. -DLLVM_DIR=/usr/local/opt/llvm/lib/cmake/llvm
$ make -j8

# 测试 fib
$ cat fib.mini
fn fib(n) {
if (n < 2) {
return n;
} else {
return fib(n-1) + fib(n-2);
}
}
let i = 0;
while (i < 10) {
print(fib(i));
i = i + 1;
}

# 模式 1:输出 IR
$ ./llvm_minic fib.mini ir

# 模式 2:发射目标文件
$ ./llvm_minic fib.mini emit
$ clang output.o -o fib
$ ./fib

# 模式 3:JIT 运行
$ ./llvm_minic fib.mini jit

五、JIT 编译实战:让 LLVM 运行时执行

5.1 LLVM JIT 演进史

graph LR
    V1["v1: Interpreter<br/>(极慢)"]:::old
    V2["v2: JIT<br/>(被废弃)"]:::old
    V3["v3: MCJIT<br/>(LLVM 16 前主流)"]:::stable
    V4["v4: ORC v1<br/>(LLVM 11+)"]:::new
    V5["v5: ORC v2<br/>(LLVM 16+)"]:::future

    V1 --> V2 --> V3 --> V4 --> V5

    style V1 fill:#FFDAB9,stroke:#FFAB76,color:#333
    style V2 fill:#FFDAB9,stroke:#FFAB76,color:#333
    style V3 fill:#B5EAD7,stroke:#80CBC4,color:#333
    style V4 fill:#C7CEEA,stroke:#9FA8DA,color:#333
    style V5 fill:#E8D5F5,stroke:#CE93D8,color:#333
引擎状态特点
Interpreter (lli)⚠️ 慢逐条解释 IR,10x-100x 慢于原生
JIT (旧)❌ 已废弃LLVM 2.0 时代,单线程,多 bug
MCJIT⚠️ 维护当前主流,全模块重编译,无分块
ORC v1✅ 推荐分块编译、按需链接、线程安全
ORC v2🚧 新版更现代 API,更好的优化时机

5.2 MCJIT 完整示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
// mcjit_demo.cpp - MCJIT 完整实战
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Verifier.h>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/ExecutionEngine/GenericValue.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/raw_ostream.h>
#include <iostream>

int main() {
// 1. 初始化 native target
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
llvm::InitializeNativeTargetAsmParser();

// 2. 构造 fib 函数(同上一节)
llvm::LLVMContext ctx;
auto mod = std::make_unique<llvm::Module>("fib", ctx);
llvm::IRBuilder<> b(ctx);

auto* i32 = b.getInt32Ty();
auto* ft = llvm::FunctionType::get(i32, {i32}, false);
auto* fib = llvm::Function::Create(
ft, llvm::Function::ExternalLinkage, "fib", mod.get());

llvm::BasicBlock* entry = llvm::BasicBlock::Create(ctx, "entry", fib);
b.SetInsertPoint(entry);
llvm::Value* n = fib->args().begin();
n->setName("n");

llvm::Value* cmp = b.CreateICmpSLT(n, b.getInt32(2), "cmp");
llvm::BasicBlock* base = llvm::BasicBlock::Create(ctx, "base", fib);
llvm::BasicBlock* rec = llvm::BasicBlock::Create(ctx, "recurse", fib);
llvm::BasicBlock* end = llvm::BasicBlock::Create(ctx, "end", fib);

b.CreateCondBr(cmp, base, rec);

b.SetInsertPoint(base);
b.CreateBr(end);

b.SetInsertPoint(rec);
llvm::Value* n1 = b.CreateSub(n, b.getInt32(1), "n1");
llvm::Value* n2 = b.CreateSub(n, b.getInt32(2), "n2");
llvm::Value* r1 = b.CreateCall(fib, {n1}, "r1");
llvm::Value* r2 = b.CreateCall(fib, {n2}, "r2");
llvm::Value* r = b.CreateAdd(r1, r2, "r");
b.CreateBr(end);

b.SetInsertPoint(end);
llvm::PHINode* phi = b.CreatePHI(i32, 2, "result");
phi->addIncoming(n, base);
phi->addIncoming(r, rec);
b.CreateRet(phi);

llvm::verifyFunction(*fib);
mod->print(llvm::errs(), nullptr);

// 3. 创建 MCJIT
std::string err;
auto* engine = llvm::EngineBuilder(std::move(mod))
.setErrorStr(&err)
.create();
if (!engine) {
std::cerr << "Engine: " << err << "\n";
return 1;
}

// 4. 调用 fib(35)
auto* fib_addr = engine->getFunctionAddress("fib");
if (!fib_addr) {
std::cerr << "fib not found\n";
return 1;
}
using fib_fn = int(*)(int);
auto fib_c = reinterpret_cast<fib_fn>(fib_addr);

std::cout << "fib(10) = " << fib_c(10) << "\n";
std::cout << "fib(35) = " << fib_c(35) << "\n";

delete engine;
return 0;
}
1
2
3
4
$ clang++ -std=c++17 mcjit_demo.cpp $(llvm-config --cxxflags --ldflags --libs core executionengine mcjit) -o mcjit_demo
$ ./mcjit_demo
fib(10) = 55
fib(35) = 9227465

5.3 ORC v2 完整示例(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// orc_jit.cpp - ORC JIT 完整实战(LLVM 16+)
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/ExecutionEngine/Orc/LLJIT.h>
#include <llvm/ExecutionEngine/Orc/ThreadSafeModule.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/raw_ostream.h>
#include <iostream>

int main() {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();

// 1. 创建 ORC JIT
auto jit = llvm::orc::LLJITBuilder().create();
if (!jit) {
std::cerr << "Failed to create JIT\n";
return 1;
}
auto J = std::move(*jit);

// 2. 构造 fib IR(同上)
llvm::LLVMContext ctx;
auto mod = std::make_unique<llvm::Module>("fib", ctx);
llvm::IRBuilder<> b(ctx);

auto* i32 = b.getInt32Ty();
auto* ft = llvm::FunctionType::get(i32, {i32}, false);
auto* fib = llvm::Function::Create(
ft, llvm::Function::ExternalLinkage, "fib", mod.get());

llvm::BasicBlock* entry = llvm::BasicBlock::Create(ctx, "entry", fib);
b.SetInsertPoint(entry);
llvm::Value* n = fib->args().begin();
n->setName("n");

llvm::Value* cmp = b.CreateICmpSLT(n, b.getInt32(2), "cmp");
llvm::BasicBlock* base = llvm::BasicBlock::Create(ctx, "base", fib);
llvm::BasicBlock* rec = llvm::BasicBlock::Create(ctx, "recurse", fib);
llvm::BasicBlock* end = llvm::BasicBlock::Create(ctx, "end", fib);
b.CreateCondBr(cmp, base, rec);

b.SetInsertPoint(base);
b.CreateBr(end);

b.SetInsertPoint(rec);
llvm::Value* n1 = b.CreateSub(n, b.getInt32(1), "n1");
llvm::Value* n2 = b.CreateSub(n, b.getInt32(2), "n2");
llvm::Value* r1 = b.CreateCall(fib, {n1}, "r1");
llvm::Value* r2 = b.CreateCall(fib, {n2}, "r2");
llvm::Value* r = b.CreateAdd(r1, r2, "r");
b.CreateBr(end);

b.SetInsertPoint(end);
llvm::PHINode* phi = b.CreatePHI(i32, 2, "result");
phi->addIncoming(n, base);
phi->addIncoming(r, rec);
b.CreateRet(phi);

// 3. 添加模块到 JIT
llvm::orc::ThreadSafeModule tsm(std::move(mod), std::make_unique<llvm::LLVMContext>(std::move(ctx)));
if (J->addIRModule(std::move(tsm))) {
std::cerr << "Failed to add module\n";
return 1;
}

// 4. 查找并调用
auto fib_sym = J->lookup("fib");
if (!fib_sym) {
std::cerr << "fib not found\n";
return 1;
}
using fib_fn = int(*)(int);
auto fib_c = reinterpret_cast<fib_fn>(fib_sym->getAddress());
std::cout << "fib(35) = " << fib_c(35) << "\n";

return 0;
}
1
2
3
$ clang++ -std=c++17 orc_jit.cpp $(llvm-config --cxxflags --ldflags --libs orcjit core native) -o orc_jit
$ ./orc_jit
fib(35) = 9227465

5.4 ORC 的 4 层架构

graph TB
    L1["ObjectLinkingLayer<br/>(.o → 内存)"]:::l1
    L2["CompileLayer<br/>(IR → .o)"]:::l2
    L3["OptimizeLayer<br/>(IR → 优化后 IR)"]:::l3
    L4["IRTransformLayer<br/>(IR → IR,可选)"]:::l4

    L4 --> L3 --> L2 --> L1

    style L1 fill:#B5EAD7,stroke:#80CBC4,color:#333
    style L2 fill:#FFDAB9,stroke:#FFAB76,color:#333
    style L3 fill:#E8D5F5,stroke:#CE93D8,color:#333
    style L4 fill:#C7CEEA,stroke:#9FA8DA,color:#333
作用输入输出
ObjectLinkingLayer链接 .o,加载符号.o(字节)内存中可执行代码
CompileLayer编译 IR 到 .oModule.o
OptimizeLayer跑优化 PassModule优化 Module
IRTransformLayerIR 级变换Module变换 Module

核心观察ORC 是栈式架构——每一层包装下一层,按需 lazy 编译。这意味着如果某个函数没被调用,它的 IR 根本不会被编译

5.5 JIT vs AOT 性能对比

维度JIT(ORC)AOT(clang)差异原因
首次调用延迟1-100 ms0 nsJIT 要编译
稳定后性能相同相同都生成机器码
运行时优化✅ 可基于 profile❌ 静态JIT 可看运行时类型
跨进程缓存AOT 一次编译永久用
适用场景REPL / DSL / 模板生产部署

关键观察JIT 不是为”更快”而存在——它是为”运行时编译”而存在。LuaJIT、Numba、V8 都是因为这一点才选 JIT。


六、优化 Pass 实战:调度 70+ 个优化

6.1 LLVM Pass 的层级

graph TB
    MPM["ModulePassManager<br/>(整个 Module)"]:::mod
    FPM["FunctionPassManager<br/>(每个 Function)"]:::func
    BBPM["BasicBlockPassManager<br/>(每个 BasicBlock)"]:::bb
    LP["LoopPassManager<br/>(每个 Loop)"]:::loop

    MPM --> FPM
    FPM --> BBPM
    FPM --> LP

    style MPM fill:#FFB3C6,stroke:#F48FB1,color:#333
    style FPM fill:#E8D5F5,stroke:#CE93D8,color:#333
    style BBPM fill:#B5EAD7,stroke:#80CBC4,color:#333
    style LP fill:#FFDAB9,stroke:#FFAB76,color:#333
Pass 类型作用范围示例 Pass
ModulePass整个 Module过程间优化(IPA)、内联、全局变量优化
FunctionPass单个 Function常量折叠、死代码消除、内联展开
BasicBlockPass单个 BasicBlock指令合并、窥孔优化
LoopPass单个 Loop循环展开、向量化、循环不变代码外提

6.2 新旧 Pass Manager 对比

graph LR
    subgraph "旧 PassManager (LLVM 13 前)"
        OPM["PassManager<Module>"]:::old
        OFPM["FunctionPassManager"]:::old
        OPM --> OFPM
    end

    subgraph "新 PassManager (LLVM 14+ 推荐)"
        NPM["PassBuilder"]:::new
        NFAM["FunctionAnalysisManager"]:::new
        NMAM["ModuleAnalysisManager"]:::new
        NFPM["FunctionPassManager"]:::new
        NPM --> NFAM
        NPM --> NMAM
        NFAM --> NFPM
    end

    style OPM fill:#FFDAB9,stroke:#FFAB76,color:#333
    style OFPM fill:#FFDAB9,stroke:#FFAB76,color:#333
    style NPM fill:#B5EAD7,stroke:#80CBC4,color:#333
    style NFAM fill:#B5EAD7,stroke:#80CBC4,color:#333
    style NMAM fill:#B5EAD7,stroke:#80CBC4,color:#333
    style NFPM fill:#B5EAD7,stroke:#80CBC4,color:#333
维度旧 PassManager新 PassManager
API 入口llvm::legacy::PassManagerllvm::PassBuilder
Analysis 管理手动 getAnalysisUsage自动依赖分析缓存
跨模块缓存✅(性能提升明显)
跨函数优化需要 IPA Pass 自己管理天然支持
可序列化✅(可用于 thinLTO)
LLVM 版本弃用中强烈推荐

6.3 跑 -O2 优化流水线

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// run_opt.cpp - 用新 PassManager 跑 -O2 优化
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IRReader/IRReader.h>
#include <llvm/Passes/PassBuilder.h>
#include <llvm/Support/SourceMgr.h>
#include <llvm/Support/raw_ostream.h>
#include <chrono>
#include <iostream>

int main(int argc, char** argv) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <file.ll>\n";
return 1;
}

llvm::LLVMContext ctx;
llvm::SMDiagnostic err;
auto mod = llvm::parseIRFile(argv[1], err, ctx);
if (!mod) {
err.print(argv[0], llvm::errs());
return 1;
}

// 1. 创建分析管理器
llvm::LoopAnalysisManager lam;
llvm::FunctionAnalysisManager fam;
llvm::CGSCCAnalysisManager cgam;
llvm::ModuleAnalysisManager mam;

// 2. 创建 PassBuilder + 注册分析
llvm::PassBuilder pb;
pb.registerModuleAnalyses(mam);
pb.registerFunctionAnalyses(fam);
pb.registerCGSCCAnalyses(cgam);
pb.registerLoopAnalyses(lam);
pb.crossRegisterProxies(lam, fam, cgam, mam);

// 3. 创建 ModulePassManager,添加 -O2 流水线
llvm::ModulePassManager mpm;
auto start = std::chrono::high_resolution_clock::now();

mpm = pb.buildPerModuleDefaultPipeline(llvm::OptimizationLevel::O2);
mpm.run(*mod, mam);

auto end = std::chrono::high_resolution_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();

std::cerr << "Optimization took " << ms << " ms\n";
mod->print(llvm::errs(), nullptr);
return 0;
}
1
2
3
$ clang -O0 -emit-llvm -S fib.c -o fib.ll
$ clang++ -std=c++17 run_opt.cpp $(llvm-config --cxxflags --ldflags --libs core passes support) -o run_opt
$ ./run_opt fib.ll > fib.opt.ll

6.4 优化等级对照表

等级启用优化典型 Pass 数编译时间性能
O001x1x
O1简单本地优化~101.5x1.3x
O2主流优化(默认)~503x1.7x
O3+ 激进向量化~705x1.8x
OsO2 + 体积优化~603x1.6x
OzO2 + 极致小~603x1.5x

6.5 -O2 流水线的 50+ Pass

graph TB
    S1["阶段1: Early 简化<br/>CFG 简化、死代码<br/>SROA(聚合重构)"]:::s1
    S2["阶段2: 内联<br/>InlineFunction<br/>+ 递归内联"]:::s2
    S3["阶段3: 过程间<br/>IPSCCP<br/>函数属性推断"]:::s3
    S4["阶段4: 标量优化<br/>GVN(全局值编号)<br/>LICM(循环不变外提)"]:::s4
    S5["阶段5: 循环优化<br/>循环展开<br/>循环向量化"]:::s5
    S6["阶段6: 晚期优化<br/>指令合并<br/>窥孔"]:::s6

    S1 --> S2 --> S3 --> S4 --> S5 --> S6

    style S1 fill:#C7CEEA,stroke:#9FA8DA,color:#333
    style S2 fill:#E8D5F5,stroke:#CE93D8,color:#333
    style S3 fill:#FFDAB9,stroke:#FFAB76,color:#333
    style S4 fill:#B5EAD7,stroke:#80CBC4,color:#333
    style S5 fill:#FFF9C4,stroke:#F9A825,color:#333
    style S6 fill:#FFB3C6,stroke:#F48FB1,color:#333

典型 Pass 清单(O2 中运行):

Pass作用速度提升
SROA聚合体(struct/array)→ 标量5-10%
EarlyCSE早期公共子表达式消除3-5%
GVN全局值编号(重命名相同表达式)10-20%
LICM循环不变代码外提5-15%
IndVarSimplify归纳变量简化5%
LoopUnroll循环展开10-30%
Inline函数内联15-30%(最关键)
DSE死存储消除5%
ADCE激进死代码消除5%
InstCombine指令合并(窥孔)5-10%
SimplifyCFGCFG 简化3-5%
ConstantProp常量传播5-10%

6.6 测量每个 Pass 的耗时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// pass_timer.cpp - 测量 Pass 耗时
#include <llvm/Passes/StandardInstrumentations.h>
#include <llvm/IR/PassTimingInfo.h>

// 在 main 里加:
llvm::StandardInstrumentations si(ctx, /*DebugLogging=*/false);
si.registerCallbacks(pic, &mam);

// 启用时间统计
llvm::TimePassesHandler time_passes;
time_passes.run();

mpm = pb.buildPerModuleDefaultPipeline(llvm::OptimizationLevel::O2);
mpm.run(*mod, mam);

// 打印每个 Pass 的耗时
time_passes.run();
1
2
3
4
5
6
7
8
9
10
11
12
$ ./run_opt fib.ll -time-passes 2>&1 | head -30
===-------------------------------------------------------------------------===
... Pass execution timing report ...
===-------------------------------------------------------------------------===
Total Execution Time: 0.0127 seconds (0.0129 wall clock)

---User Time--- --System Time-- --User+System-- ---Wall Time--- --- Name ---
0.0040 ( 31.6%) 0.0000 ( 0.0%) 0.0040 ( 31.6%) 0.0041 ( 31.7%) SROA
0.0025 ( 19.7%) 0.0000 ( 0.0%) 0.0025 ( 19.7%) 0.0025 ( 19.6%) GVN
0.0018 ( 14.2%) 0.0000 ( 0.0%) 0.0018 ( 14.2%) 0.0018 ( 14.1%) InstCombine
0.0015 ( 11.8%) 0.0000 ( 0.0%) 0.0015 ( 11.8%) 0.0015 ( 11.8%) SimplifyCFG
...

七、目标代码生成:发射 .o 文件 + 交叉编译

7.1 TargetMachine 实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// emit_object.cpp - 从 LLVM IR 生成 .o 文件
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/Verifier.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Target/TargetOptions.h>
#include <llvm/MC/TargetRegistry.h>
#include <llvm/CodeGen/CommandFlags.h>
#include <llvm/CodeGen/TargetPassConfig.h>
#include <iostream>

int main() {
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
llvm::InitializeAllAsmPrinters();

// 1. 构造 fib 函数(同前面)
llvm::LLVMContext ctx;
auto mod = std::make_unique<llvm::Module>("fib", ctx);
llvm::IRBuilder<> b(ctx);

auto* i32 = b.getInt32Ty();
auto* ft = llvm::FunctionType::get(i32, {i32}, false);
auto* fib = llvm::Function::Create(
ft, llvm::Function::ExternalLinkage, "fib", mod.get());
llvm::BasicBlock* entry = llvm::BasicBlock::Create(ctx, "entry", fib);
b.SetInsertPoint(entry);
llvm::Value* n = fib->args().begin(); n->setName("n");
llvm::Value* cmp = b.CreateICmpSLT(n, b.getInt32(2), "cmp");
llvm::BasicBlock* base = llvm::BasicBlock::Create(ctx, "base", fib);
llvm::BasicBlock* rec = llvm::BasicBlock::Create(ctx, "recurse", fib);
llvm::BasicBlock* end = llvm::BasicBlock::Create(ctx, "end", fib);
b.CreateCondBr(cmp, base, rec);
b.SetInsertPoint(base); b.CreateBr(end);
b.SetInsertPoint(rec);
llvm::Value* n1 = b.CreateSub(n, b.getInt32(1), "n1");
llvm::Value* n2 = b.CreateSub(n, b.getInt32(2), "n2");
llvm::Value* r1 = b.CreateCall(fib, {n1}, "r1");
llvm::Value* r2 = b.CreateCall(fib, {n2}, "r2");
b.CreateRet(b.CreateAdd(r1, r2, "r"));
b.SetInsertPoint(end);
llvm::PHINode* phi = b.CreatePHI(i32, 2, "r");
phi->addIncoming(n, base);
phi->addIncoming(b.CreateAdd(r1, r2, "r"), rec);
b.CreateRet(phi);

// 2. 创建 TargetMachine
auto triple_str = llvm::sys::getDefaultTargetTriple();
llvm::Triple triple(triple_str);
std::string err;
auto* target = llvm::TargetRegistry::lookupTarget(triple_str, err);
if (!target) { std::cerr << err << "\n"; return 1; }

auto* tm = target->createTargetMachine(
triple_str, "generic", "", llvm::TargetOptions(),
llvm::Optional<llvm::Reloc::Model>(),
llvm::Optional<llvm::CodeModel::Model>(),
llvm::CodeGenOptLevel::Default);

mod->setDataLayout(tm->createDataLayout());
mod->setTargetTriple(triple_str);

// 3. 发射 .o
std::error_code ec;
llvm::raw_fd_ostream dest("fib.o", ec, llvm::sys::fs::OF_None);
if (ec) { std::cerr << ec.message() << "\n"; return 1; }

llvm::legacy::PassManager pm;
if (tm->addPassesToEmitFile(pm, dest, llvm::CGFT_ObjectFile)) {
std::cerr << "Cannot emit\n"; return 1;
}
pm.run(*mod);
dest.flush();
std::cout << "Wrote fib.o\n";

// 4. 链接
delete tm;
return 0;
}
1
2
3
4
5
6
7
$ clang++ -std=c++17 emit_object.cpp $(llvm-config --cxxflags --ldflags --libs core target) -o emit_object
$ ./emit_object
$ file fib.o
fib.o: Mach-O 64-bit object arm64
$ clang fib.o -o fib
$ ./fib
$ echo $?

7.2 交叉编译:x86-64 → ARM64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 查看当前平台 triple
$ llvm-config --host-target
x86_64-apple-darwin25.3.0

# 列出支持的所有 target
$ llc --version
Registered Targets:
aarch64 - AArch64 (little endian)
aarch64_be - AArch64 (big endian)
amdgcn - AMD GCN GPUs
arm - ARM
riscv32 - 32-bit RISC-V
riscv64 - 64-bit RISC-V
x86 - 32-bit x86
x86-64 - 64-bit x86
wasm32 - WebAssembly 32-bit
wasm64 - WebAssembly 64-bit

# 编译 fib.ll 到 ARM64 汇编
$ llc -mtriple=aarch64-apple-ios fib.ll -o fib_aarch64.s

# 看反汇编
$ cat fib_aarch64.s | head -30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 交叉编译 C++ API 版本
// cross_compile.cpp
#include <llvm/Target/TargetMachine.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/MC/TargetRegistry.h>

int main() {
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
llvm::InitializeAllAsmPrinters();

// 1. 目标 triple 字符串
const char* triples[] = {
"x86_64-apple-darwin",
"aarch64-apple-ios",
"aarch64-linux-android",
"wasm32-unknown-unknown",
"riscv64-linux-gnu",
};

for (auto* triple : triples) {
std::string err;
auto* target = llvm::TargetRegistry::lookupTarget(triple, err);
if (!target) {
llvm::errs() << err << "\n";
continue;
}
llvm::outs() << triple << " -> " << target->getName() << "\n";
}
return 0;
}

7.3 后端支持矩阵

后端状态主要架构
x86 / x86-64✅ 成熟Intel/AMD CPU
aarch64 / aarch64_be✅ 成熟ARM64(Apple Silicon, 服务器)
arm / armeb✅ 成熟ARMv7(旧手机/嵌入式)
riscv32 / riscv64✅ 成熟RISC-V(开源硬件)
wasm32 / wasm64✅ 成熟WebAssembly(浏览器/边缘)
amdgcn✅ 成熟AMD GPU
nvptx / nvptx64✅ 成熟NVIDIA GPU
powerpc64le✅ 成熟IBM POWER(小端序)
mips / mipsel⚠️ 维护MIPS(路由器)
loongarch64✅ 较新龙芯
ve⚠️ 实验NEC SX-Aurora

7.4 WebAssembly 后端示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 把 fib.c 编译成 wasm
$ clang --target=wasm32-unknown-unknown -O2 -emit-llvm -S fib.c -o fib.ll
$ llc -mtriple=wasm32-unknown-unknown fib.ll -o fib.s

# 看反汇编
$ cat fib.s | head -50
# .text
# .functype fib (i32) -> (i32)
# block
# local.get 0
# i32.const 2
# i32.lt_s
# br_if 0
# ...

核心观察同一份 LLVM IR 可以直接编译到 15+ 种目标——这就是 LLVM 真正的力量。


八、链接与执行:链接 libc/libm

8.1 TargetLibraryInfo:链接 libc 的关键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// link_libc.cpp - 链接 libc 的 JIT
#include <llvm/Analysis/TargetLibraryInfo.h>
#include <llvm/Passes/PassBuilder.h>

int main() {
// 1. 创建 TargetLibraryInfo(让 LLVM 知道 printf/strlen 等在哪)
llvm::Triple triple(llvm::sys::getDefaultTargetTriple());
llvm::TargetLibraryInfoImpl tlii(triple);
// 标记我们用的 libc 函数
tlii.disableAllFunctions();
tlii.setAvailable(llvm::Triple::x86_64);

// 2. 注册到 PassBuilder
llvm::PassBuilder pb;
llvm::LoopAnalysisManager lam;
llvm::FunctionAnalysisManager fam;
llvm::CGSCCAnalysisManager cgam;
llvm::ModuleAnalysisManager mam;

pb.registerModuleAnalyses(mam);
pb.registerFunctionAnalyses(fam);
pb.registerCGSCCAnalyses(cgam);
pb.registerLoopAnalyses(lam);
pb.crossRegisterProxies(lam, fam, cgam, mam);

// 注册 TargetLibraryInfo
fam.registerPass([&] { return llvm::TargetLibraryAnalysis(tlii); });

// 3. 调用 strlen("hello") 在 LLVM IR 里
llvm::LLVMContext ctx;
auto mod = std::make_unique<llvm::Module>("test", ctx);
llvm::IRBuilder<> b(ctx);

auto* i8ptr = b.getPtrTy();
auto* ft = llvm::FunctionType::get(b.getInt64Ty(), {i8ptr}, false);
auto* strlen_fn = llvm::Function::Create(
ft, llvm::Function::ExternalLinkage, "strlen", mod.get());

llvm::BasicBlock* bb = llvm::BasicBlock::Create(ctx, "entry",
mod->getFunctionList().begin());
b.SetInsertPoint(bb);
llvm::Value* s = b.CreateGlobalStringPtr("hello, world!");
llvm::Value* len = b.CreateCall(strlen_fn, {s});
b.CreateRet(len);

// 4. 跑 -O2 优化(会内联 strlen 为常量 13!)
llvm::ModulePassManager mpm =
pb.buildPerModuleDefaultPipeline(llvm::OptimizationLevel::O2);
mpm.run(*mod, mam);

// 5. 输出
mod->print(llvm::errs(), nullptr);
return 0;
}

优化后的输出

1
2
3
4
define i64 @strlen_wrapper(ptr %0) {
entry:
ret i64 13
}

核心观察LLVM 知道 libc 函数的语义(strlen、sin、pow)——-O2把它们内联成常量。这就是为什么 #include <math.h>pow 反而比手写循环快。

8.2 JIT 模式链接 libc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// jit_libc.cpp - JIT 调用 libc 函数
#include <llvm/ExecutionEngine/Orc/LLJIT.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/IRBuilder.h>

int main() {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();

auto jit = llvm::orc::LLJITBuilder().create();
auto J = std::move(*jit);

// 1. 构造 IR:调用 printf("hello\n")
llvm::LLVMContext ctx;
auto mod = std::make_unique<llvm::Module>("hello", ctx);
llvm::IRBuilder<> b(ctx);

auto* i32 = b.getInt32Ty();
auto* i8ptr = b.getPtrTy();
auto* ft = llvm::FunctionType::get(i32, {i8ptr}, true); // vararg
auto* printf_fn = llvm::Function::Create(
ft, llvm::Function::ExternalLinkage, "printf", mod.get());

auto* main_ty = llvm::FunctionType::get(i32, {}, false);
auto* main_fn = llvm::Function::Create(
main_ty, llvm::Function::ExternalLinkage, "main", mod.get());

llvm::BasicBlock* bb = llvm::BasicBlock::Create(ctx, "entry", main_fn);
b.SetInsertPoint(bb);
llvm::Value* s = b.CreateGlobalStringPtr("Hello from JIT!\n");
b.CreateCall(printf_fn, {s});
b.CreateRet(b.getInt32(0));

// 2. 链接进程符号(printf 在 libc 里,自动找到)
J->getMainJITDylib().addGenerator(
llvm::cantFail(llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(
llvm::orc::SymbolGenerator::UndefPolicy::Generate,
llvm::orc::SymbolGenerator::LookupPolicy::ExportedOnly)));

// 3. 添加模块
llvm::orc::ThreadSafeModule tsm(
std::move(mod), std::make_unique<llvm::LLVMContext>(std::move(ctx)));
if (J->addIRModule(std::move(tsm))) return 1;

// 4. 调用 main
auto main_sym = J->lookup("main");
using main_fn_t = int(*)();
auto main_c = reinterpret_cast<main_fn_t>(main_sym->getAddress());
return main_c();
}
1
2
3
$ clang++ -std=c++17 jit_libc.cpp $(llvm-config --cxxflags --ldflags --libs orcjit core) -o jit_libc
$ ./jit_libc
Hello from JIT!

8.3 emit .o 后链接

1
2
3
4
5
6
7
8
9
10
11
12
13
# 1. 编译 fib.ll 到 .o
$ llc fib.ll -filetype=obj -o fib.o

# 2. 用 clang 链接(自动链接 libc/libm)
$ clang fib.o -o fib

# 3. 查看可执行文件依赖
$ otool -L fib # macOS
$ ldd fib # Linux

# 4. 运行
$ ./fib
$ echo $?

8.4 IR 链接:合并多个 Module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 1. 创建 utils.ll(独立 Module)
$ cat > utils.ll << 'EOF'
define i32 @square(i32 %x) {
%r = mul i32 %x, %x
ret i32 %r
}
EOF

# 2. 创建 main.ll
$ cat > main.ll << 'EOF'
declare i32 @square(i32)
define i32 @main() {
%r = call i32 @square(i32 7)
ret i32 %r
}
EOF

# 3. 用 llvm-link 合并
$ llvm-link utils.ll main.ll -o combined.ll
$ cat combined.ll

# 4. 编译运行
$ clang combined.ll -o combined
$ ./combined
$ echo $?
49 # 7*7

九、深入:自定义 Pass

9.1 统计加法指令数的 Pass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// CountAdds.cpp - 自定义 Pass:统计函数中的 add 指令数
#include <llvm/IR/Function.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/PassManager.h>
#include <llvm/IR/PassManagerPlugin.h>
#include <llvm/Passes/PassBuilder.h>
#include <llvm/Passes/PassPlugin.h>
#include <llvm/Support/raw_ostream.h>

using namespace llvm;

class CountAddsPass : public PassInfoMixin<CountAddsPass> {
public:
PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
int add_count = 0;
int sub_count = 0;
int mul_count = 0;

for (auto &BB : F) {
for (auto &I : BB) {
if (I.getOpcode() == Instruction::Add) {
add_count++;
} else if (I.getOpcode() == Instruction::Sub) {
sub_count++;
} else if (I.getOpcode() == Instruction::Mul) {
mul_count++;
}
}
}

errs() << "Function " << F.getName() << ":\n";
errs() << " adds = " << add_count << "\n";
errs() << " subs = " << sub_count << "\n";
errs() << " muls = " << mul_count << "\n";

return PreservedAnalyses::all();
}
};

// 必须的 Pass 插件入口
extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
return {LLVM_PLUGIN_API_VERSION, "CountAdds", "1.0",
[](PassBuilder &PB) {
PB.registerPipelineParsingCallback(
[](StringRef Name, FunctionPassManager &FPM,
ArrayRef<PassBuilder::PipelineElement>) {
if (Name == "count-adds") {
FPM.addPass(CountAddsPass());
return true;
}
return false;
});
}};
}

9.2 编译为插件

1
2
3
# CMakeLists.txt
add_library(CountAdds MODULE CountAdds.cpp)
target_link_libraries(CountAdds PRIVATE LLVMCore LLVMSupport LLVMTransformUtils)
1
2
3
4
$ mkdir build && cd build
$ cmake ..
$ make
$ ls CountAdds.so # 插件动态库

9.3 在 opt 中使用

1
2
3
4
5
6
7
8
9
# 1. 用 clang 准备 IR
$ clang -O0 -emit-llvm -S fib.c -o fib.ll

# 2. 加载插件跑 Pass
$ opt -load-pass-plugin=./CountAdds.so -passes="count-adds" fib.ll -disable-output
Function fib:
adds = 1
subs = 2
muls = 1

9.4 在新 PassBuilder 中嵌入自定义 Pass

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// custom_pass_in_pipeline.cpp
#include <llvm/Passes/PassBuilder.h>

int main() {
llvm::PassBuilder pb;
pb.registerPipelineParsingCallback(
[](llvm::StringRef Name, llvm::FunctionPassManager &FPM,
llvm::ArrayRef<llvm::PassBuilder::PipelineElement>) {
if (Name == "count-adds") {
FPM.addPass(CountAddsPass());
return true;
}
return false;
});

// 加入到 O2 流水线
llvm::ModulePassManager mpm =
pb.buildPerModuleDefaultPipeline(llvm::OptimizationLevel::O2);

// 在 InstCombine 之后插入 CountAdds
// 复杂场景:构建自定义 Pipeline
FunctionPassManager fpm;
fpm.addPass(CountAddsPass());
fpm.addPass(InstCombinePass());
// ...
}

9.5 Pass 插件 vs 内置

维度内置 PassPass 插件
编译方式静态链接到 opt动态库(.so
加载方式opt -passes=my-passopt -load-pass-plugin=my.so
重启 opt需要重新编译 opt不需要
适合场景通用优化业务特定、实验性
生产部署难以分发可作为独立 SDK 分发

十、端到端实战:从 .mini 到跨平台执行

10.1 完整工作流

graph LR
    SRC["fib.mini<br/>MiniLang 源码"]:::src
    LEX["Lexer<br/>(#1 复用)"]:::l1
    PAR["Parser<br/>(#1 复用)"]:::l2
    CG["LLVMCodeGen<br/>(本文)"]:::l3
    MOD["Module<br/>fib.bc"]:::l4
    OPT["opt -O2<br/>(优化)"]:::l5
    LLC["llc<br/>(IR→机器码)"]:::l6
    O1["fib_x86.o"]:::out
    O2["fib_arm64.o"]:::out
    O3["fib_wasm.o"]:::out
    JIT["JIT<br/>(运行时)"]:::out
    EXE["可执行文件"]:::exe

    SRC ==> LEX ==> PAR ==> CG ==> MOD
    MOD -.->|"O2 优化"| OPT
    MOD -.->|"x86 后端"| LLC
    MOD -.->|"ARM64 后端"| LLC
    MOD -.->|"WASM 后端"| LLC
    MOD -.->|"运行时"| JIT
    OPT -.-> LLC
    LLC ==> O1
    LLC ==> O2
    LLC ==> O3
    O1 ==> EXE
    O2 ==> EXE
    O3 ==> EXE

    style SRC fill:#C7CEEA,stroke:#9FA8DA,color:#333
    style LEX fill:#E8D5F5,stroke:#CE93D8,color:#333
    style PAR fill:#E8D5F5,stroke:#CE93D8,color:#333
    style CG fill:#FFB3C6,stroke:#F48FB1,color:#333
    style MOD fill:#FFDAB9,stroke:#FFAB76,color:#333
    style OPT fill:#B5EAD7,stroke:#80CBC4,color:#333
    style LLC fill:#B5EAD7,stroke:#80CBC4,color:#333
    style JIT fill:#FFF9C4,stroke:#F9A825,color:#333
    style EXE fill:#B5EAD7,stroke:#80CBC4,color:#333

10.2 性能基准测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# fib(35) = 9227465(递归版)
# 测试 100 次取平均

# #1 mini 编译器解释执行
$ time ./minic fib.mini
real 1m12s ← 慢 1000x(解释执行)

# #4 LLVM IR + JIT(无优化)
$ ./llvm_minic fib.mini jit
real 0m08s ← 200x

# #4 LLVM IR + JIT(O2 优化)
$ ./llvm_minic fib.mini jit -O2
real 0m02s ← 70x

# #4 LLVM IR + AOT(clang 编译)
$ ./llvm_minic fib.mini emit
$ clang output.o -o fib
$ time ./fib
real 0m01s ← 30x(=原生 C)
模式fib(35) 耗时加速比
#1 mini 解释器72.0 s1x
LLVM IR + JIT O08.0 s9x
LLVM IR + JIT O22.0 s36x
LLVM IR + AOT1.0 s72x
原生 C (clang -O2)1.0 s72x(=AOT)

核心观察LLVM IR + AOT = 原生 C 性能。LLVM 的所有抽象(SSA、Pass、Target)几乎零开销——这是它被工业界广泛采用的根本原因。

10.3 跨平台分发实战

1
2
3
4
5
6
7
8
9
# 在 macOS x86 上给 3 个平台编译
$ ./llvm_minic fib.mini emit x86_64-apple-darwin # macOS
$ ./llvm_minic fib.mini emit aarch64-apple-ios # iOS
$ ./llvm_minic fib.mini emit wasm32-unknown-unknown # Web

# 产物
fib_x86.o # 5 KB
fib_aarch64.o # 5 KB
fib_wasm.o # 4 KB

十一、LLVM API 调试技巧

11.1 验证 IR 正确性

1
2
3
4
5
6
7
8
9
10
11
#include <llvm/IR/Verifier.h>

// 验证单个函数
if (llvm::verifyFunction(*fib, &llvm::errs())) {
throw std::runtime_error("Function verification failed");
}

// 验证整个 Module
if (llvm::verifyModule(*module, &llvm::errs())) {
throw std::runtime_error("Module verification failed");
}

11.2 打印 IR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 方法 1:打印到 stderr
module_->print(llvm::errs(), nullptr);

// 方法 2:打印到文件
std::error_code ec;
llvm::raw_fd_ostream out("debug.ll", ec);
module_->print(out, nullptr);

// 方法 3:打印单个函数
fib->print(llvm::errs());

// 方法 4:打印指令
inst->print(llvm::errs());
inst->dump(); // 调试用:直接打印到 stderr

11.3 常见错误

错误现象原因解决
Instruction does not dominate...使用未定义的 SSA 值检查基本块顺序
PHI node entries do not matchphi 节点前驱不匹配addIncoming 时检查 BB
Use of undefined instruction跨 BB 引用CreateBr + phi
Function type mismatch参数类型不一致cast<Function> 检查签名
BasicBlock already has a terminatorBB 末尾已 ret不要重复 CreateRet

11.4 GDB 调试

1
2
3
4
5
6
7
8
9
# 编译时加调试信息
$ clang++ -std=c++17 -g -O0 llvm_minic.cpp $(llvm-config --cxxflags --ldflags --libs) -o llvm_minic

# GDB
$ gdb ./llvm_minic
(gdb) break LLVMCodeGen::gen_expr
(gdb) run fib.mini jit
(gdb) print e
(gdb) continue

十二、深度对比:手写 vs LLVM vs GCC

12.1 代码量对比

阶段#1 手写#4 LLVM比例
词法分析150 行0(复用 #1)
语法分析200 行0(复用 #1)
语义分析100 行0(LLVM 自动推断)省 100%
IR 生成200 行50 行省 75%
优化0 行0 行(opt 自动)省 100%
后端100 行(#3 手写 x86)50 行(TargetMachine)省 50%
链接0 行0 行(外部工具)
总计800 行100 行省 87%

12.2 能力对比

能力#1 + #3 手写#4 LLVM
优化❌ 无✅ 70+ Pass
目标架构❌ 仅 x86✅ 15+ 架构
汇编输出✅ 仅 AT&T 语法✅ 所有 GAS 变体
调试信息❌ 无✅ DWARF
LTO❌ 无✅ thinLTO
PGO❌ 无✅ AutoFDO + IR PGO
Sanitizer❌ 无✅ ASan/TSan/UBSan
JIT❌ 无✅ ORC / MCJIT
学习曲线⭐⭐⭐⭐⭐⭐⭐

12.3 与 GCC 后端的对比

维度GCCLLVM
API 稳定性⚠️ 经常改✅ 稳定
文档⚠️ 散落✅ 完整
教程⚠️ 少✅ Kaleidoscope 完整
社区老牌活跃
C++ 标准C++03C++17/20
LicenseGPLApache 2.0
嵌入难度极难容易

十三、5 道递进思考题

#难度题目
1把 #1 的 print 语句改成 JIT 时直接打印到 stdout——不用 emit .o
2⭐⭐给 MiniLang 加 for 循环——在 LLVMCodeGen 里要生成几个 BasicBlock?
3⭐⭐实现 bool 类型——LLVM IR 用 i1,但要处理int32 的互转
4⭐⭐⭐实现 数组字面量 [1, 2, 3]——用 CreateAlloca([3 x i32]) + GEP
5⭐⭐⭐⭐写一个 Pass 插件:把 x * 2 自动优化为 x << 1——练习自定义 Pass

十四、推荐阅读

资源类型价值
LLVM Kaleidoscope Tutorial llvm.org/docs/tutorial官方教程手把手写 Kaleidoscope——第 5 篇 JIT 的基础
LLVM Programmer’s Manual官方文档API 索引、内存模型、线程安全
LLVM Developer Meeting 视频 llvm.org/devmtg视频Chris Lattner 等核心开发者分享
《Learning LLVM Core Libraries》 Supratik Majumder书籍入门 LLVM 源码
《LLVM Cookbook》 Suyog Sarda书籍实战视角
LLVM Weekly llvmweekly.org周刊社区动态
mlir.llvm.org文档MLIR(下一代 IR)入门
Compiler Explorer (godbolt.org)工具在线看 clang/LLVM 输出

十五、三段行动建议

行动 1:先复现,再创造

不要从自定义 Pass 开始。先精确复现本文的 fib 例子:

  1. clang++ hello_llvm.cpp ... -o hello(第一个 IR 生成)
  2. clang++ mcjit_demo.cpp ... -o mcjit(JIT 执行)
  3. clang++ emit_object.cpp ... -o emit(发射 .o

这 3 个 50 行的程序覆盖了 LLVM 90% 的常用 API。

行动 2:用编译器探索器学会”读 IR”

打开 godbolt.org——选 C++ → x86-64 clang (trunk),勾选 -O0 -emit-llvm
int fib(int n) { return n < 2 ? n : fib(n-1) + fib(n-2); }对照 LLVM IR 一行行读
每天看 10 个例子,一周后你就是 LLVM IR 专家。

行动 3:把 #1 的 mini 编译器升级为 LLVM 后端

把第 5 节 LLVMCodeGen 直接接入你的项目:

  1. 把 #1 的 Operand/TAC/CodeGen 删掉
  2. 换成 LLVMCodeGen 类(复用 #1 的 AST)
  3. 增加 jit_run() / emit_object() 接口

完成后你的 mini 编译器自动获得 15 个后端和 70 个优化——这就是 LLVM 的复利。


十六、本文小结

16.1 核心结论

LLVM 是 C++ 库,不是二进制——当你把 LLVM 当成 STL 用时,它会给你免费的多后端、多优化、JIT、Sanitizer。800 行手写代码能做的事,LLVM 用 100 行做,还做得更好

graph LR
    SRC["MiniLang<br/>源码"]:::src
    AST["AST<br/>(#1)"]:::ast
    IR["LLVM IR<br/>(本文)"]:::ir
    OPT["优化后 IR<br/>(O0~Oz)"]:::opt
    MC["机器码<br/>(x86/ARM/WASM/...)"]:::mc

    SRC ==>|"Lexer+Parser"| AST
    AST ==>|"IRBuilder"| IR
    IR -.->|"opt"| OPT
    OPT -.->|"llc"| MC

    style SRC fill:#C7CEEA,stroke:#9FA8DA,color:#333
    style AST fill:#E8D5F5,stroke:#CE93D8,color:#333
    style IR fill:#FFB3C6,stroke:#F48FB1,color:#333
    style OPT fill:#FFDAB9,stroke:#FFAB76,color:#333
    style MC fill:#B5EAD7,stroke:#80CBC4,color:#333

16.2 知识点回顾

知识点状态关键代码
LLVM 三大件✅ 掌握LLVMContext / Module / IRBuilder
生成 IR✅ 掌握CreateAdd / CreateCall / CreateRet
基本块 + phi✅ 掌握CreateCondBr + CreatePHI
新 PassManager✅ 掌握PassBuilder + buildPerModuleDefaultPipeline
JIT 编译✅ 掌握LLJIT + addIRModule + lookup
发射 .o✅ 掌握TargetMachine + addPassesToEmitFile
交叉编译✅ 掌握-mtriple=aarch64-apple-ios
自定义 Pass✅ 掌握PassInfoMixin + PassBuilder

16.3 本文在系列中的位置

graph LR
    A["#1: 4 阶段全打通"]:::done
    B["#2: 8 大优化 Pass"]:::done
    C["#3: x86-64 后端"]:::done
    D["#4: LLVM 实战"]:::here
    E["#5: JIT 原理"]:::next

    A ==> B ==> C ==> D ==> E

    style A fill:#B5EAD7,stroke:#80CBC4,color:#333
    style B fill:#B5EAD7,stroke:#80CBC4,color:#333
    style C fill:#B5EAD7,stroke:#80CBC4,color:#333
    style D fill:#FFB3C6,stroke:#F48FB1,stroke-width:4px,color:#333
    style E fill:#C7CEEA,stroke:#9FA8DA,color:#333

16.4 进一步思考

为什么 LLVM 比 GCC 更适合做编译器基础设施?

  1. 单一 SSA IR——GCC 有 GIMPLE/RTL/Machine 多级形式,LLVM 只有一种 IR
  2. 库化——每个模块都是 .a/.so可以单独嵌入到任何 C++ 程序
  3. Pass 框架——70+ 优化 Pass 自由组合,无需重新编译
  4. 多后端——一份 IR 输出 15+ 架构,N×M 问题变 N+M
  5. License——Apache 2.0 + LLVM Exception,不污染下游

这 5 个选择单独看都不算新颖,组合起来却重新定义了编译器行业


📚 编译原理实战 系列导航

本文是《编译原理实战》系列第 4/5 篇。

#文章状态难度
14 阶段全打通✅ 已发布⭐⭐⭐
28 大优化 Pass 全打通✅ 已发布⭐⭐⭐⭐
3手写 x86-64 后端✅ 已发布⭐⭐⭐⭐
4本文:LLVM 实战✅ 已发布⭐⭐⭐⭐⭐
5JIT 编译:运行时编译与 HotSpot🔜 计划中⭐⭐⭐⭐⭐
📖 全部 5 篇目录(点击展开)
  1. 第 1 篇:手写 C++17 编译器前端——词法、语法、语义、IR 4 阶段全打通
  2. 第 2 篇:8 大优化 Pass——常量折叠、死代码消除、循环优化
  3. 第 3 篇:手写 x86-64 后端——800 行汇编生成器
  4. 第 4 篇:LLVM 实战——用 LLVM C++ API 重写 mini 编译器 ← 当前
  5. 第 5 篇:JIT 编译——运行时编译与 HotSpot

最后一句话:LLVM 不是”高高在上的工业级编译器”——它就是一堆 C++ 库,当你用 IRBuilder::CreateAdd 这种简单 API 构造 IR 时,你和 Chris Lattner 写 clang 用的代码是同一种理解 LLVM 不是为了改 LLVM 源码,而是为了把它的能力嵌入到任何你想要的地方——DSL、JIT、模板引擎、领域特定优化,它都是你的工具箱。