LLVM 前端与优化层:Clang 如何把 C 代码变成 IR
上一篇我们讲清了 LLVM 的整体架构:前端 → IR → 优化器 → 后端 。这一篇往下钻一层,专门看**前端(Clang)和优化层(Pass)**这两块。
全程用一段 8 行的
hello.c做实验,每个阶段跑一条命令,把编译器内部的中间产物直接打印出来看。
文章目录
- [LLVM 前端与优化层:Clang 如何把 C 代码变成 IR](#LLVM 前端与优化层:Clang 如何把 C 代码变成 IR)
-
- 一、先把位置摆清楚
- [二、Clang 前端的三段流水线](#二、Clang 前端的三段流水线)
- 三、第一步:词法分析
-
- [3.1 实验代码](#3.1 实验代码)
- [3.2 命令](#3.2 命令)
- [3.3 输出(节选)](#3.3 输出(节选))
- [3.4 怎么读这个输出](#3.4 怎么读这个输出)
- [3.5 SourceLocation:为什么位置信息这么重要](#3.5 SourceLocation:为什么位置信息这么重要)
- 四、第二步:语法分析
-
- [4.1 命令](#4.1 命令)
- [4.2 输出(只看我们自己代码的部分)](#4.2 输出(只看我们自己代码的部分))
- [4.3 常见 AST 节点](#4.3 常见 AST 节点)
- [4.4 一个重要认知](#4.4 一个重要认知)
- [4.5 AST 的图形视图](#4.5 AST 的图形视图)
- [4.6 AST 是怎么组织的](#4.6 AST 是怎么组织的)
- 五、第三步:语义分析
-
- [5.1 语法 vs 语义](#5.1 语法 vs 语义)
- [5.2 语义分析靠什么:符号表](#5.2 语义分析靠什么:符号表)
- [5.3 实战:让语义分析报一次错](#5.3 实战:让语义分析报一次错)
- [5.4 语义分析的输出](#5.4 语义分析的输出)
- [六、LLVM 优化层](#六、LLVM 优化层)
-
- [6.1 IR 为什么是中枢](#6.1 IR 为什么是中枢)
- [6.2 Pass 是什么](#6.2 Pass 是什么)
- [6.3 两类 Pass](#6.3 两类 Pass)
- [七、实战:用 `opt` 跑一遍优化](#七、实战:用
opt跑一遍优化) -
- [7.1 先生成 bitcode](#7.1 先生成 bitcode)
- [7.2 跑优化](#7.2 跑优化)
- [7.3 关于原文两个 Pass 的纠错](#7.3 关于原文两个 Pass 的纠错)
- [7.4 `-stats` 能看什么](#7.4
-stats能看什么)
- [八、Pass 的依赖关系](#八、Pass 的依赖关系)
-
- [8.1 显式依赖](#8.1 显式依赖)
- [8.2 隐式依赖](#8.2 隐式依赖)
- [九、Pass API:按什么粒度写 Pass](#九、Pass API:按什么粒度写 Pass)
- 十、小结
- 参考资料
一、先把位置摆清楚
LLVM 的 IR 贯穿整个编译生命周期,而前端负责"把源码翻译成 IR",优化层负责"把 IR 变快"。
#mermaid-svg-WM4kGGjkaTr2WLYl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WM4kGGjkaTr2WLYl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WM4kGGjkaTr2WLYl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WM4kGGjkaTr2WLYl .error-icon{fill:#552222;}#mermaid-svg-WM4kGGjkaTr2WLYl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WM4kGGjkaTr2WLYl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WM4kGGjkaTr2WLYl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WM4kGGjkaTr2WLYl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WM4kGGjkaTr2WLYl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WM4kGGjkaTr2WLYl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WM4kGGjkaTr2WLYl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WM4kGGjkaTr2WLYl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WM4kGGjkaTr2WLYl .marker.cross{stroke:#333333;}#mermaid-svg-WM4kGGjkaTr2WLYl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WM4kGGjkaTr2WLYl p{margin:0;}#mermaid-svg-WM4kGGjkaTr2WLYl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-WM4kGGjkaTr2WLYl .cluster-label text{fill:#333;}#mermaid-svg-WM4kGGjkaTr2WLYl .cluster-label span{color:#333;}#mermaid-svg-WM4kGGjkaTr2WLYl .cluster-label span p{background-color:transparent;}#mermaid-svg-WM4kGGjkaTr2WLYl .label text,#mermaid-svg-WM4kGGjkaTr2WLYl span{fill:#333;color:#333;}#mermaid-svg-WM4kGGjkaTr2WLYl .node rect,#mermaid-svg-WM4kGGjkaTr2WLYl .node circle,#mermaid-svg-WM4kGGjkaTr2WLYl .node ellipse,#mermaid-svg-WM4kGGjkaTr2WLYl .node polygon,#mermaid-svg-WM4kGGjkaTr2WLYl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WM4kGGjkaTr2WLYl .rough-node .label text,#mermaid-svg-WM4kGGjkaTr2WLYl .node .label text,#mermaid-svg-WM4kGGjkaTr2WLYl .image-shape .label,#mermaid-svg-WM4kGGjkaTr2WLYl .icon-shape .label{text-anchor:middle;}#mermaid-svg-WM4kGGjkaTr2WLYl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WM4kGGjkaTr2WLYl .rough-node .label,#mermaid-svg-WM4kGGjkaTr2WLYl .node .label,#mermaid-svg-WM4kGGjkaTr2WLYl .image-shape .label,#mermaid-svg-WM4kGGjkaTr2WLYl .icon-shape .label{text-align:center;}#mermaid-svg-WM4kGGjkaTr2WLYl .node.clickable{cursor:pointer;}#mermaid-svg-WM4kGGjkaTr2WLYl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-WM4kGGjkaTr2WLYl .arrowheadPath{fill:#333333;}#mermaid-svg-WM4kGGjkaTr2WLYl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WM4kGGjkaTr2WLYl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WM4kGGjkaTr2WLYl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WM4kGGjkaTr2WLYl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WM4kGGjkaTr2WLYl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WM4kGGjkaTr2WLYl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-WM4kGGjkaTr2WLYl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WM4kGGjkaTr2WLYl .cluster text{fill:#333;}#mermaid-svg-WM4kGGjkaTr2WLYl .cluster span{color:#333;}#mermaid-svg-WM4kGGjkaTr2WLYl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WM4kGGjkaTr2WLYl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WM4kGGjkaTr2WLYl rect.text{fill:none;stroke-width:0;}#mermaid-svg-WM4kGGjkaTr2WLYl .icon-shape,#mermaid-svg-WM4kGGjkaTr2WLYl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WM4kGGjkaTr2WLYl .icon-shape p,#mermaid-svg-WM4kGGjkaTr2WLYl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-WM4kGGjkaTr2WLYl .icon-shape .label rect,#mermaid-svg-WM4kGGjkaTr2WLYl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WM4kGGjkaTr2WLYl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WM4kGGjkaTr2WLYl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WM4kGGjkaTr2WLYl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} C / C++ / Obj-C 源码
Clang 前端
(与硬件无关)
LLVM IR
优化层
(与硬件无关)
优化后的 IR
后端
(与硬件相关)
目标机器码
| 层 | 输入 | 输出 | 是否与目标硬件相关 |
|---|---|---|---|
| 前端(Clang) | 高级语言源码 | LLVM IR | 无关 |
| 优化层 | LLVM IR | 优化后的 LLVM IR | 无关 |
| 后端 | 优化后的 IR | 目标机器码 | 相关 |
小白速查 :前端最后一个环节就是生成 IR 。所以在 Clang 里,只要看到
.ll或.bc文件,就说明"前端已经干完活了"。
一个容易误解的点 :LLVM 各阶段并非只用一个 IR 。编译过程中不同阶段会采用不同的数据结构(比如后端会用 DAG、机器指令等),但会维护一个比较标准的 IR 作为主干。原文说"LLVM 优化层输入是 AST、输出是 DAG"------这句不准确 :优化层的输入和输出都是 IR;IR → DAG 是后端指令选择阶段才发生的事。下面第五节会详细说。
二、Clang 前端的三段流水线
#mermaid-svg-p6rP18uNdttjJpbV{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-p6rP18uNdttjJpbV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-p6rP18uNdttjJpbV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-p6rP18uNdttjJpbV .error-icon{fill:#552222;}#mermaid-svg-p6rP18uNdttjJpbV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-p6rP18uNdttjJpbV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-p6rP18uNdttjJpbV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-p6rP18uNdttjJpbV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-p6rP18uNdttjJpbV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-p6rP18uNdttjJpbV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-p6rP18uNdttjJpbV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-p6rP18uNdttjJpbV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-p6rP18uNdttjJpbV .marker.cross{stroke:#333333;}#mermaid-svg-p6rP18uNdttjJpbV svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-p6rP18uNdttjJpbV p{margin:0;}#mermaid-svg-p6rP18uNdttjJpbV .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-p6rP18uNdttjJpbV .cluster-label text{fill:#333;}#mermaid-svg-p6rP18uNdttjJpbV .cluster-label span{color:#333;}#mermaid-svg-p6rP18uNdttjJpbV .cluster-label span p{background-color:transparent;}#mermaid-svg-p6rP18uNdttjJpbV .label text,#mermaid-svg-p6rP18uNdttjJpbV span{fill:#333;color:#333;}#mermaid-svg-p6rP18uNdttjJpbV .node rect,#mermaid-svg-p6rP18uNdttjJpbV .node circle,#mermaid-svg-p6rP18uNdttjJpbV .node ellipse,#mermaid-svg-p6rP18uNdttjJpbV .node polygon,#mermaid-svg-p6rP18uNdttjJpbV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-p6rP18uNdttjJpbV .rough-node .label text,#mermaid-svg-p6rP18uNdttjJpbV .node .label text,#mermaid-svg-p6rP18uNdttjJpbV .image-shape .label,#mermaid-svg-p6rP18uNdttjJpbV .icon-shape .label{text-anchor:middle;}#mermaid-svg-p6rP18uNdttjJpbV .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-p6rP18uNdttjJpbV .rough-node .label,#mermaid-svg-p6rP18uNdttjJpbV .node .label,#mermaid-svg-p6rP18uNdttjJpbV .image-shape .label,#mermaid-svg-p6rP18uNdttjJpbV .icon-shape .label{text-align:center;}#mermaid-svg-p6rP18uNdttjJpbV .node.clickable{cursor:pointer;}#mermaid-svg-p6rP18uNdttjJpbV .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-p6rP18uNdttjJpbV .arrowheadPath{fill:#333333;}#mermaid-svg-p6rP18uNdttjJpbV .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-p6rP18uNdttjJpbV .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-p6rP18uNdttjJpbV .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-p6rP18uNdttjJpbV .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-p6rP18uNdttjJpbV .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-p6rP18uNdttjJpbV .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-p6rP18uNdttjJpbV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-p6rP18uNdttjJpbV .cluster text{fill:#333;}#mermaid-svg-p6rP18uNdttjJpbV .cluster span{color:#333;}#mermaid-svg-p6rP18uNdttjJpbV div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-p6rP18uNdttjJpbV .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-p6rP18uNdttjJpbV rect.text{fill:none;stroke-width:0;}#mermaid-svg-p6rP18uNdttjJpbV .icon-shape,#mermaid-svg-p6rP18uNdttjJpbV .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-p6rP18uNdttjJpbV .icon-shape p,#mermaid-svg-p6rP18uNdttjJpbV .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-p6rP18uNdttjJpbV .icon-shape .label rect,#mermaid-svg-p6rP18uNdttjJpbV .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-p6rP18uNdttjJpbV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-p6rP18uNdttjJpbV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-p6rP18uNdttjJpbV :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ① 词法分析
Lexical Analysis
② 语法分析
Syntax Analysis
③ 语义分析
Semantic Analysis
IR 生成
hello.c
字符流
Token 流
AST
抽象语法树
类型无误的 AST
LLVM IR
.ll / .bc
| 步骤 | 输入 | 输出 | 核心问题 |
|---|---|---|---|
| ① 词法分析 | 字符流 | Token 流 | "这串字符由哪些词组成?" |
| ② 语法分析 | Token 流 | AST | "这些词怎么组成合法的句子?" |
| ③ 语义分析 | AST | 类型无误的 AST | "这句话的意思对吗?" |
大白话:拿读句子打比方------
- 词法分析 = 把"我爱吃苹果"切成"我 / 爱 / 吃 / 苹果"
- 语法分析 = 判断"主语 + 谓语 + 宾语"结构成不成立
- 语义分析 = 判断"苹果"到底能不能被"吃"(类型是否匹配)
每个语言的前端都自带 这三件套,但最终都会产出同一种中间结构(IR),这正是 LLVM 能接入多种语言的原因。
三、第一步:词法分析
3.1 实验代码
c
#include <stdio.h>
#define HELLOWORD ("Hello World\n")
int main() {
printf(HELLOWORD);
return 0;
}
3.2 命令
bash
clang -cc1 -dump-tokens hello.c
小白速查 :
-cc1表示"直接调用 Clang 的前端编译器核心",绕过驱动层。加上-dump-tokens就会把词法分析结果打印出来。
3.3 输出(节选)
int 'int' [StartOfLine] Loc=<hello.c:5:1>
identifier 'main' [LeadingSpace] Loc=<hello.c:5:5>
l_paren '(' Loc=<hello.c:5:9>
void 'void' Loc=<hello.c:5:10>
r_paren ')' Loc=<hello.c:5:14>
l_brace '{' Loc=<hello.c:5:15>
identifier 'printf' [StartOfLine] [LeadingSpace] Loc=<hello.c:6:5>
l_paren '(' Loc=<hello.c:6:11>
l_paren '(' Loc=<hello.c:6:12 <Spelling=hello.c:3:19>>
string_literal '"hello world\n"' Loc=<hello.c:6:12 <Spelling=hello.c:3:20>>
r_paren ')' Loc=<hello.c:6:12 <Spelling=hello.c:3:35>>
r_paren ')' Loc=<hello.c:6:21>
semi ';' Loc=<hello.c:6:22>
return 'return' [StartOfLine] [LeadingSpace] Loc=<hello.c:7:5>
numeric_constant '0' [LeadingSpace] Loc=<hello.c:7:12>
semi ';' Loc=<hello.c:7:13>
r_brace '}' [StartOfLine] Loc=<hello.c:8:1>
eof '' Loc=<hello.c:8:2>
3.4 怎么读这个输出
| 列 | 含义 | 例子 |
|---|---|---|
| Token 类别 | 编译器内部的记号类型 | identifier、l_paren、string_literal、semi |
| 原文内容 | 源码里的原始文本 | 'main'、'('、'"hello world\n"' |
| 前置标记 | 是否在行首 / 前面有空格 | [StartOfLine]、[LeadingSpace] |
| 位置信息 | Loc=<文件:行:列> |
Loc=<hello.c:5:5> |
重点看宏 :
HELLOWORD已经被替换成"hello world\n",而且位置信息里带着<Spelling=hello.c:3:20>------指向宏定义那一行。这就是编译器"展开宏但仍能追溯到源头"的实现方式。
几类常见 Token:
| Token 类别 | 对应源码 |
|---|---|
identifier |
标识符(变量名、函数名) |
l_paren / r_paren |
左/右圆括号 |
l_brace / r_brace |
左/右花括号 |
semi |
分号 |
numeric_constant |
数字常量 |
string_literal |
字符串字面量 |
eof |
文件结束 |
3.5 SourceLocation:为什么位置信息这么重要
每个 Token 都带一个 SourceLocation 实例(Loc=<hello.c:6:11> 表示第 6 行第 11 列)。
它有三个关键作用:
- 报错定位 ------你看到的
hello.c:6:5: error:就是从这里来的 - 调试信息------生成 DWARF 调试信息时需要它
- 宏展开追踪 ------区分"写在宏里的位置"和"宏展开后的位置"(
Spelling=就是干这个的)
四、第二步:语法分析
4.1 命令
bash
clang -fsyntax-only -Xclang -ast-dump hello.c
| 参数 | 作用 |
|---|---|
-fsyntax-only |
只做语法/语义检查,不生成目标文件 |
-Xclang -ast-dump |
把 AST 打印出来(-Xclang 表示"这个参数直接传给 cc1") |
4.2 输出(只看我们自己代码的部分)
完整的 AST dump 会包含所有头文件 里的声明(几百行),我们只摘出 main 这段:
`-FunctionDecl 0x1c08c259e20 <hello.c:5:1, line:8:1> line:5:5 main 'int (void)'
`-CompoundStmt 0x1c08c25a058 <col:15, line:8:1>
|-CallExpr 0x1c08c259fd0 <line:6:5, col:21> 'int'
| |-ImplicitCastExpr <col:5> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
| | `-DeclRefExpr <col:5> 'int (const char *, ...)' Function 'printf'
| `-ImplicitCastExpr <line:3:19, col:35> 'const char *' <NoOp>
| `-ImplicitCastExpr <col:19, col:35> 'char *' <ArrayToPointerDecay>
| `-ParenExpr <col:19, col:35> 'char[13]' lvalue
| `-StringLiteral <col:20> 'char[13]' lvalue "hello world\n"
`-ReturnStmt <line:7:5, col:12>
`-IntegerLiteral <col:12> 'int' 0
把它画成树更直观:
#mermaid-svg-iVIPyPfcamsv9Dli{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-iVIPyPfcamsv9Dli .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iVIPyPfcamsv9Dli .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iVIPyPfcamsv9Dli .error-icon{fill:#552222;}#mermaid-svg-iVIPyPfcamsv9Dli .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iVIPyPfcamsv9Dli .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iVIPyPfcamsv9Dli .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iVIPyPfcamsv9Dli .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iVIPyPfcamsv9Dli .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iVIPyPfcamsv9Dli .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iVIPyPfcamsv9Dli .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iVIPyPfcamsv9Dli .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iVIPyPfcamsv9Dli .marker.cross{stroke:#333333;}#mermaid-svg-iVIPyPfcamsv9Dli svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iVIPyPfcamsv9Dli p{margin:0;}#mermaid-svg-iVIPyPfcamsv9Dli .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-iVIPyPfcamsv9Dli .cluster-label text{fill:#333;}#mermaid-svg-iVIPyPfcamsv9Dli .cluster-label span{color:#333;}#mermaid-svg-iVIPyPfcamsv9Dli .cluster-label span p{background-color:transparent;}#mermaid-svg-iVIPyPfcamsv9Dli .label text,#mermaid-svg-iVIPyPfcamsv9Dli span{fill:#333;color:#333;}#mermaid-svg-iVIPyPfcamsv9Dli .node rect,#mermaid-svg-iVIPyPfcamsv9Dli .node circle,#mermaid-svg-iVIPyPfcamsv9Dli .node ellipse,#mermaid-svg-iVIPyPfcamsv9Dli .node polygon,#mermaid-svg-iVIPyPfcamsv9Dli .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iVIPyPfcamsv9Dli .rough-node .label text,#mermaid-svg-iVIPyPfcamsv9Dli .node .label text,#mermaid-svg-iVIPyPfcamsv9Dli .image-shape .label,#mermaid-svg-iVIPyPfcamsv9Dli .icon-shape .label{text-anchor:middle;}#mermaid-svg-iVIPyPfcamsv9Dli .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-iVIPyPfcamsv9Dli .rough-node .label,#mermaid-svg-iVIPyPfcamsv9Dli .node .label,#mermaid-svg-iVIPyPfcamsv9Dli .image-shape .label,#mermaid-svg-iVIPyPfcamsv9Dli .icon-shape .label{text-align:center;}#mermaid-svg-iVIPyPfcamsv9Dli .node.clickable{cursor:pointer;}#mermaid-svg-iVIPyPfcamsv9Dli .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-iVIPyPfcamsv9Dli .arrowheadPath{fill:#333333;}#mermaid-svg-iVIPyPfcamsv9Dli .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iVIPyPfcamsv9Dli .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iVIPyPfcamsv9Dli .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iVIPyPfcamsv9Dli .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-iVIPyPfcamsv9Dli .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iVIPyPfcamsv9Dli .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-iVIPyPfcamsv9Dli .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iVIPyPfcamsv9Dli .cluster text{fill:#333;}#mermaid-svg-iVIPyPfcamsv9Dli .cluster span{color:#333;}#mermaid-svg-iVIPyPfcamsv9Dli div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-iVIPyPfcamsv9Dli .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-iVIPyPfcamsv9Dli rect.text{fill:none;stroke-width:0;}#mermaid-svg-iVIPyPfcamsv9Dli .icon-shape,#mermaid-svg-iVIPyPfcamsv9Dli .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iVIPyPfcamsv9Dli .icon-shape p,#mermaid-svg-iVIPyPfcamsv9Dli .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-iVIPyPfcamsv9Dli .icon-shape .label rect,#mermaid-svg-iVIPyPfcamsv9Dli .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iVIPyPfcamsv9Dli .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-iVIPyPfcamsv9Dli .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-iVIPyPfcamsv9Dli :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} FunctionDecl
main : int(void)
CompoundStmt
函数体
CallExpr : int
调用 printf
ReturnStmt
return 0
ImplicitCastExpr
函数 → 函数指针
DeclRefExpr
引用 printf
ImplicitCastExpr
const char*
ImplicitCastExpr
数组 → 指针
ParenExpr
括号表达式
StringLiteral
\hello world\
\
IntegerLiteral
0
4.3 常见 AST 节点
| 节点 | 含义 |
|---|---|
TranslationUnitDecl |
整个翻译单元的根节点 |
TypedefDecl |
类型别名声明(如 __int128) |
RecordType / Record |
结构体/类等记录类型 |
FunctionDecl |
函数声明/定义 |
ParmVarDecl |
函数参数声明 |
CompoundStmt |
语句块(一对 {}) |
CallExpr |
函数调用表达式 |
DeclRefExpr |
引用一个声明(如引用 printf) |
ImplicitCastExpr |
隐式类型转换(源码里没写,编译器自动加的) |
ParenExpr |
括号表达式(保留源码里的括号) |
StringLiteral / IntegerLiteral |
字面量 |
ReturnStmt |
return 语句 |
小白速查 :为什么会有那么多
ImplicitCastExpr?因为 C 语言里的隐式转换(数组退化成指针、函数退化成函数指针、整型提升)在 AST 里都会显式地建一个节点,这样后端就不用再猜了。
4.4 一个重要认知
语法分析器不关心代码"是什么意思",只验证"结构是否合法"。
比如 int x = "hello"; 在语法上是完全合法的(类型 标识符 = 表达式;),语法分析器会愉快地建出 AST------是语义分析才报错。
类比:语法分析检查"这句话符合中文语法吗",语义分析检查"这句话在现实里说得通吗"。"绿色的想法在睡觉"语法正确,但语义荒谬。
4.5 AST 的图形视图
Clang 还能直接弹出一个图形化的 AST 树:
bash
clang -fsyntax-only -Xclang -ast-view hello.c
它会生成 .dot 文件并调用 Graphviz 打开(需要系统装了 Graphviz)。
4.6 AST 是怎么组织的
ASTContext 类持有整个翻译单元的 AST。通过:
cpp
ASTContext::getTranslationUnitDecl()
拿到顶层的 TranslationUnitDecl,就能遍历访问任意一个 AST 节点。这也是写 Clang 工具(Clang-Tidy、clang-query、自定义静态分析器)的入口。
五、第三步:语义分析
5.1 语法 vs 语义
| 语法分析 | 语义分析 | |
|---|---|---|
| 关心什么 | 结构是否符合语法规则 | 含义和逻辑是否正确 |
| 检查项 | 括号配不配对、语句结构对不对 | 类型是否匹配、函数调用参数对不对、变量是否重复定义 |
| 报错例子 | int main( {(少了 )) |
int a[4]; int a[5];(重复定义) |
5.2 语义分析靠什么:符号表
符号表存储"标识符 → 类型 + 其他必要信息"的映射。
一种直观的实现方式:
- 解析完得到 AST
- 遍历 AST
- 每遇到一个标识符,就去符号表里查它的类型
- 检查当前上下文要求是否匹配
#mermaid-svg-ZIFNSfs8U26quZWF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZIFNSfs8U26quZWF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZIFNSfs8U26quZWF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZIFNSfs8U26quZWF .error-icon{fill:#552222;}#mermaid-svg-ZIFNSfs8U26quZWF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZIFNSfs8U26quZWF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZIFNSfs8U26quZWF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZIFNSfs8U26quZWF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZIFNSfs8U26quZWF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZIFNSfs8U26quZWF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZIFNSfs8U26quZWF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZIFNSfs8U26quZWF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZIFNSfs8U26quZWF .marker.cross{stroke:#333333;}#mermaid-svg-ZIFNSfs8U26quZWF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZIFNSfs8U26quZWF p{margin:0;}#mermaid-svg-ZIFNSfs8U26quZWF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZIFNSfs8U26quZWF .cluster-label text{fill:#333;}#mermaid-svg-ZIFNSfs8U26quZWF .cluster-label span{color:#333;}#mermaid-svg-ZIFNSfs8U26quZWF .cluster-label span p{background-color:transparent;}#mermaid-svg-ZIFNSfs8U26quZWF .label text,#mermaid-svg-ZIFNSfs8U26quZWF span{fill:#333;color:#333;}#mermaid-svg-ZIFNSfs8U26quZWF .node rect,#mermaid-svg-ZIFNSfs8U26quZWF .node circle,#mermaid-svg-ZIFNSfs8U26quZWF .node ellipse,#mermaid-svg-ZIFNSfs8U26quZWF .node polygon,#mermaid-svg-ZIFNSfs8U26quZWF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZIFNSfs8U26quZWF .rough-node .label text,#mermaid-svg-ZIFNSfs8U26quZWF .node .label text,#mermaid-svg-ZIFNSfs8U26quZWF .image-shape .label,#mermaid-svg-ZIFNSfs8U26quZWF .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZIFNSfs8U26quZWF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZIFNSfs8U26quZWF .rough-node .label,#mermaid-svg-ZIFNSfs8U26quZWF .node .label,#mermaid-svg-ZIFNSfs8U26quZWF .image-shape .label,#mermaid-svg-ZIFNSfs8U26quZWF .icon-shape .label{text-align:center;}#mermaid-svg-ZIFNSfs8U26quZWF .node.clickable{cursor:pointer;}#mermaid-svg-ZIFNSfs8U26quZWF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZIFNSfs8U26quZWF .arrowheadPath{fill:#333333;}#mermaid-svg-ZIFNSfs8U26quZWF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZIFNSfs8U26quZWF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZIFNSfs8U26quZWF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZIFNSfs8U26quZWF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZIFNSfs8U26quZWF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZIFNSfs8U26quZWF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZIFNSfs8U26quZWF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZIFNSfs8U26quZWF .cluster text{fill:#333;}#mermaid-svg-ZIFNSfs8U26quZWF .cluster span{color:#333;}#mermaid-svg-ZIFNSfs8U26quZWF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZIFNSfs8U26quZWF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZIFNSfs8U26quZWF rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZIFNSfs8U26quZWF .icon-shape,#mermaid-svg-ZIFNSfs8U26quZWF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZIFNSfs8U26quZWF .icon-shape p,#mermaid-svg-ZIFNSfs8U26quZWF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZIFNSfs8U26quZWF .icon-shape .label rect,#mermaid-svg-ZIFNSfs8U26quZWF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZIFNSfs8U26quZWF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZIFNSfs8U26quZWF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZIFNSfs8U26quZWF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
AST
遍历节点
遇到标识符?
查符号表
取类型
类型匹配?
报错
并给出来源位置
5.3 实战:让语义分析报一次错
c
#include <stdio.h>
#define HELLOWORD ("hello world\n")
int a[4];
int a[5]; // ← 同一个名字,不同类型
int main(void){
printf(HELLOWORD);
return 0;
}
bash
clang -c hello.c
输出:
hello.c:6:5: error: redefinition of 'a' with a different type: 'int[5]' vs 'int[4]'
6 | int a[5];
| ^
hello.c:5:5: note: previous definition is here
5 | int a[4];
| ^
1 error generated.
注意这条错误的性质 :
int a[4]; int a[5];在语法上毫无问题 ------两行都符合"类型 + 标识符 + 数组维度 + 分号"的规则。是语义分析发现"同名不同类型"才报的错。而且报错信息里同时给出了定义位置 和前一次定义的位置 ------这正是符号表 +
SourceLocation配合的结果。
5.4 语义分析的输出
语义分析的输出是"类型无误的 AST"。这个 AST 就是后续生成 IR 的直接输入。
一句话总结前端三阶段 :
词法 = 切词 → 语法 = 搭结构 → 语义 = 查含义 → IR = 翻译成通用语言。
六、LLVM 优化层
6.1 IR 为什么是中枢
#mermaid-svg-6OKA2hq8k0Hx7LrT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6OKA2hq8k0Hx7LrT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6OKA2hq8k0Hx7LrT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6OKA2hq8k0Hx7LrT .error-icon{fill:#552222;}#mermaid-svg-6OKA2hq8k0Hx7LrT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6OKA2hq8k0Hx7LrT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6OKA2hq8k0Hx7LrT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6OKA2hq8k0Hx7LrT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6OKA2hq8k0Hx7LrT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6OKA2hq8k0Hx7LrT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6OKA2hq8k0Hx7LrT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6OKA2hq8k0Hx7LrT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6OKA2hq8k0Hx7LrT .marker.cross{stroke:#333333;}#mermaid-svg-6OKA2hq8k0Hx7LrT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6OKA2hq8k0Hx7LrT p{margin:0;}#mermaid-svg-6OKA2hq8k0Hx7LrT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6OKA2hq8k0Hx7LrT .cluster-label text{fill:#333;}#mermaid-svg-6OKA2hq8k0Hx7LrT .cluster-label span{color:#333;}#mermaid-svg-6OKA2hq8k0Hx7LrT .cluster-label span p{background-color:transparent;}#mermaid-svg-6OKA2hq8k0Hx7LrT .label text,#mermaid-svg-6OKA2hq8k0Hx7LrT span{fill:#333;color:#333;}#mermaid-svg-6OKA2hq8k0Hx7LrT .node rect,#mermaid-svg-6OKA2hq8k0Hx7LrT .node circle,#mermaid-svg-6OKA2hq8k0Hx7LrT .node ellipse,#mermaid-svg-6OKA2hq8k0Hx7LrT .node polygon,#mermaid-svg-6OKA2hq8k0Hx7LrT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6OKA2hq8k0Hx7LrT .rough-node .label text,#mermaid-svg-6OKA2hq8k0Hx7LrT .node .label text,#mermaid-svg-6OKA2hq8k0Hx7LrT .image-shape .label,#mermaid-svg-6OKA2hq8k0Hx7LrT .icon-shape .label{text-anchor:middle;}#mermaid-svg-6OKA2hq8k0Hx7LrT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6OKA2hq8k0Hx7LrT .rough-node .label,#mermaid-svg-6OKA2hq8k0Hx7LrT .node .label,#mermaid-svg-6OKA2hq8k0Hx7LrT .image-shape .label,#mermaid-svg-6OKA2hq8k0Hx7LrT .icon-shape .label{text-align:center;}#mermaid-svg-6OKA2hq8k0Hx7LrT .node.clickable{cursor:pointer;}#mermaid-svg-6OKA2hq8k0Hx7LrT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6OKA2hq8k0Hx7LrT .arrowheadPath{fill:#333333;}#mermaid-svg-6OKA2hq8k0Hx7LrT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6OKA2hq8k0Hx7LrT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6OKA2hq8k0Hx7LrT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6OKA2hq8k0Hx7LrT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6OKA2hq8k0Hx7LrT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6OKA2hq8k0Hx7LrT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6OKA2hq8k0Hx7LrT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6OKA2hq8k0Hx7LrT .cluster text{fill:#333;}#mermaid-svg-6OKA2hq8k0Hx7LrT .cluster span{color:#333;}#mermaid-svg-6OKA2hq8k0Hx7LrT div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6OKA2hq8k0Hx7LrT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6OKA2hq8k0Hx7LrT rect.text{fill:none;stroke-width:0;}#mermaid-svg-6OKA2hq8k0Hx7LrT .icon-shape,#mermaid-svg-6OKA2hq8k0Hx7LrT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6OKA2hq8k0Hx7LrT .icon-shape p,#mermaid-svg-6OKA2hq8k0Hx7LrT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6OKA2hq8k0Hx7LrT .icon-shape .label rect,#mermaid-svg-6OKA2hq8k0Hx7LrT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6OKA2hq8k0Hx7LrT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6OKA2hq8k0Hx7LrT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6OKA2hq8k0Hx7LrT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} C
LLVM IR
(统一中枢)
C++
Rust
Swift
x86
ARM
RISC-V
GPU
优化 Pass
(都在 IR 上跑)
前端产生 IR,后端接收 IR,中间绝大部分目标无关的优化都发生在 IR 上。 这就是"一套优化服务所有语言和所有目标"的物理基础。
6.2 Pass 是什么
Pass = 对程序做一次遍历。
优化层的每一种优化方式,都封装成一个 Pass。多个 Pass 串起来跑,就构成了优化流水线。
6.3 两类 Pass
| 类型 | 干什么 | 会修改代码吗 | 例子 |
|---|---|---|---|
| 分析 Pass Analysis Pass | 收集程序信息、做静态分析 | 不会(只读) | domtree(支配树)、scalar-evolution、instcount |
| 转换 Pass Transformation Pass | 修改/重写程序以优化 | 会 | adce、instcombine、inline、constmerge |
大白话:
- 分析 Pass 像体检------只出报告,不动手术
- 转换 Pass 像手术------真的改代码
它们通常配合使用:先分析(拿到信息),再转换(根据信息做优化)。
#mermaid-svg-Z8WiuzOfhjs4vR9a{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Z8WiuzOfhjs4vR9a .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Z8WiuzOfhjs4vR9a .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Z8WiuzOfhjs4vR9a .error-icon{fill:#552222;}#mermaid-svg-Z8WiuzOfhjs4vR9a .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Z8WiuzOfhjs4vR9a .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Z8WiuzOfhjs4vR9a .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Z8WiuzOfhjs4vR9a .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Z8WiuzOfhjs4vR9a .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Z8WiuzOfhjs4vR9a .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Z8WiuzOfhjs4vR9a .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Z8WiuzOfhjs4vR9a .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Z8WiuzOfhjs4vR9a .marker.cross{stroke:#333333;}#mermaid-svg-Z8WiuzOfhjs4vR9a svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Z8WiuzOfhjs4vR9a p{margin:0;}#mermaid-svg-Z8WiuzOfhjs4vR9a .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Z8WiuzOfhjs4vR9a .cluster-label text{fill:#333;}#mermaid-svg-Z8WiuzOfhjs4vR9a .cluster-label span{color:#333;}#mermaid-svg-Z8WiuzOfhjs4vR9a .cluster-label span p{background-color:transparent;}#mermaid-svg-Z8WiuzOfhjs4vR9a .label text,#mermaid-svg-Z8WiuzOfhjs4vR9a span{fill:#333;color:#333;}#mermaid-svg-Z8WiuzOfhjs4vR9a .node rect,#mermaid-svg-Z8WiuzOfhjs4vR9a .node circle,#mermaid-svg-Z8WiuzOfhjs4vR9a .node ellipse,#mermaid-svg-Z8WiuzOfhjs4vR9a .node polygon,#mermaid-svg-Z8WiuzOfhjs4vR9a .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Z8WiuzOfhjs4vR9a .rough-node .label text,#mermaid-svg-Z8WiuzOfhjs4vR9a .node .label text,#mermaid-svg-Z8WiuzOfhjs4vR9a .image-shape .label,#mermaid-svg-Z8WiuzOfhjs4vR9a .icon-shape .label{text-anchor:middle;}#mermaid-svg-Z8WiuzOfhjs4vR9a .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Z8WiuzOfhjs4vR9a .rough-node .label,#mermaid-svg-Z8WiuzOfhjs4vR9a .node .label,#mermaid-svg-Z8WiuzOfhjs4vR9a .image-shape .label,#mermaid-svg-Z8WiuzOfhjs4vR9a .icon-shape .label{text-align:center;}#mermaid-svg-Z8WiuzOfhjs4vR9a .node.clickable{cursor:pointer;}#mermaid-svg-Z8WiuzOfhjs4vR9a .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Z8WiuzOfhjs4vR9a .arrowheadPath{fill:#333333;}#mermaid-svg-Z8WiuzOfhjs4vR9a .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Z8WiuzOfhjs4vR9a .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Z8WiuzOfhjs4vR9a .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Z8WiuzOfhjs4vR9a .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Z8WiuzOfhjs4vR9a .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Z8WiuzOfhjs4vR9a .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Z8WiuzOfhjs4vR9a .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Z8WiuzOfhjs4vR9a .cluster text{fill:#333;}#mermaid-svg-Z8WiuzOfhjs4vR9a .cluster span{color:#333;}#mermaid-svg-Z8WiuzOfhjs4vR9a div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Z8WiuzOfhjs4vR9a .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Z8WiuzOfhjs4vR9a rect.text{fill:none;stroke-width:0;}#mermaid-svg-Z8WiuzOfhjs4vR9a .icon-shape,#mermaid-svg-Z8WiuzOfhjs4vR9a .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Z8WiuzOfhjs4vR9a .icon-shape p,#mermaid-svg-Z8WiuzOfhjs4vR9a .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Z8WiuzOfhjs4vR9a .icon-shape .label rect,#mermaid-svg-Z8WiuzOfhjs4vR9a .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Z8WiuzOfhjs4vR9a .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Z8WiuzOfhjs4vR9a .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Z8WiuzOfhjs4vR9a :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} IR
分析 Pass
(收集信息)
分析结果
如:支配树 / 常量表
转换 Pass
(改 IR)
优化后的 IR
七、实战:用 opt 跑一遍优化
7.1 先生成 bitcode
bash
clang -emit-llvm -c hello.c -o hello.bc
7.2 跑优化
bash
opt -passes='instcount,adce' -o hello-tmp.bc hello.bc -stats
| 参数 | 作用 |
|---|---|
-passes='...' |
指定要跑的 Pass 列表(新 Pass Manager 的写法) |
-o hello-tmp.bc |
输出优化后的 bitcode |
-stats |
打印各 Pass 的统计信息(改了多少、删了多少) |
7.3 关于原文两个 Pass 的纠错
原文这里有两处需要修正:
| 原文说法 | 实际情况 |
|---|---|
"adce 是 Analysis Pass" |
❌ ADCE(Aggressive Dead Code Elimination)是转换 Pass。原文后半句"如果存在,则将冗余的计算消除掉"本身就在描述转换行为,前后矛盾 |
"mdgc(Merged Duplicates Global Constant)合并多个全局常量" |
⚠️ 这个 Pass 名在较新版本 LLVM 中已不是标准新 PM 的 Pass 名 。等价功能现在由 constmerge (合并重复全局常量)、globaldce(删除无用全局)承担 |
推荐的、在较新 LLVM 上确实能跑的写法:
bash
opt -passes='instcount,adce,constmerge,globaldce' -o hello-tmp.bc hello.bc -stats
实用建议 :想知道你手上的 LLVM 版本支持哪些 Pass,直接跑
opt --print-passes或opt -passes='default<O2>' --help-list。别照抄网上过时的 Pass 名------LLVM 在 14 版本前后从 legacy PM 切到 new PM,名字变了不少。
7.4 -stats 能看什么
... Statistics ...
4 instcount - Number of instructions
3 adce - Number of instructions removed
1 constmerge - Number of global constants merged
(具体条目随版本和代码而变)它告诉你每个 Pass 到底干了多少活,是调优时的第一手信息。
八、Pass 的依赖关系
8.1 显式依赖
转换 Pass 需要某个分析结果时,Pass 管理器会自动把被依赖的分析 Pass 排到前面。
比如某个转换 Pass 需要支配树:
cpp
// legacy Pass Manager 的写法(旧版本)
DominatorTree &DT = getAnalysis<DominatorTree>(Func);
你只管声明"我要 DOM tree",不用手动安排它先跑------管理器会自动处理。
原文这里少了个等号 :应该是
DominatorTree &DT = getAnalysis<DominatorTree>(Func);。另外这是 legacy Pass Manager 的 API;新版(new PM)里要改用AnalysisManager<Function> &AM+AM.getResult<DominatorTreeAnalysis>(F)。
8.2 隐式依赖
有些 Pass 要求IR 已经处于某种特定形态(比如必须已转成 SSA、必须已内联过)。
这种依赖没法被自动推导 ,必须由你手动按正确顺序把 Pass 加进队列:
- 通过命令行:
-passes='a,b,c'的顺序就是你指定的顺序 - 通过 Pass 管理器 API:按顺序
addPass()
小白速查 :显式依赖 = "我要用你的结果,你自动排前面";隐式依赖 = "我假设你已经干完了,你得自己保证顺序"。前者编译器管,后者你管。
九、Pass API:按什么粒度写 Pass
写 Pass 时要先选粒度------你一次处理多大范围的代码。
| Pass 子类 | 粒度 | 重载方法 | 限制 |
|---|---|---|---|
ModulePass |
整个模块 (一个 .bc/.ll) |
runOnModule() |
最自由:可删函数、改全局变量,函数处理顺序不确定 |
FunctionPass |
一个函数 | runOnFunction() |
禁止修改/删除外部函数、禁止删全局变量;函数顺序不确定 |
BasicBlockPass |
一个基本块 | runOnBasicBlock() |
除继承 FunctionPass 的限制外,还禁止修改/删除外部基本块 |
#mermaid-svg-Q2qVlg3Yn3lZS2Eu{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .error-icon{fill:#552222;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .marker.cross{stroke:#333333;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu p{margin:0;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .cluster-label text{fill:#333;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .cluster-label span{color:#333;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .cluster-label span p{background-color:transparent;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .label text,#mermaid-svg-Q2qVlg3Yn3lZS2Eu span{fill:#333;color:#333;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .node rect,#mermaid-svg-Q2qVlg3Yn3lZS2Eu .node circle,#mermaid-svg-Q2qVlg3Yn3lZS2Eu .node ellipse,#mermaid-svg-Q2qVlg3Yn3lZS2Eu .node polygon,#mermaid-svg-Q2qVlg3Yn3lZS2Eu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .rough-node .label text,#mermaid-svg-Q2qVlg3Yn3lZS2Eu .node .label text,#mermaid-svg-Q2qVlg3Yn3lZS2Eu .image-shape .label,#mermaid-svg-Q2qVlg3Yn3lZS2Eu .icon-shape .label{text-anchor:middle;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .rough-node .label,#mermaid-svg-Q2qVlg3Yn3lZS2Eu .node .label,#mermaid-svg-Q2qVlg3Yn3lZS2Eu .image-shape .label,#mermaid-svg-Q2qVlg3Yn3lZS2Eu .icon-shape .label{text-align:center;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .node.clickable{cursor:pointer;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .arrowheadPath{fill:#333333;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .cluster text{fill:#333;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .cluster span{color:#333;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu rect.text{fill:none;stroke-width:0;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .icon-shape,#mermaid-svg-Q2qVlg3Yn3lZS2Eu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .icon-shape p,#mermaid-svg-Q2qVlg3Yn3lZS2Eu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .icon-shape .label rect,#mermaid-svg-Q2qVlg3Yn3lZS2Eu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Q2qVlg3Yn3lZS2Eu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ModulePass
整个模块
最自由
FunctionPass
单个函数
限制更多
BasicBlockPass
单个基本块
限制最多
怎么选? 原则是"能小不大":能用 FunctionPass 就别用 ModulePass------粒度越小,越好组合、越好并行、越不容易破坏其他 Pass 的假设。
FunctionPass是实际用得最多的类型。
版本提醒 :BasicBlockPass在 new Pass Manager(LLVM 14+)里已被移除 。新 PM 里只有ModulePass、FunctionPass、CGSCCPass(调用图 SCC)、LoopPass等。写新 Pass 时请以llvm/include/llvm/Passes/下的实际头文件为准。
十、小结
| # | 结论 |
|---|---|
| 1 | Clang 前端 = 词法 → 语法 → 语义 三步,最终产出 LLVM IR |
| 2 | 词法分析把字符流切成 Token 流 ,每个 Token 带 SourceLocation,宏展开还能用 Spelling= 追溯到定义处 |
| 3 | 语法分析把 Token 流组织成 AST,只验证结构、不关心含义 |
| 4 | 语义分析靠符号表 做类型检查,输出类型无误的 AST |
| 5 | 优化层在 IR 上工作,输入输出都是 IR(不是 AST → DAG) |
| 6 | Pass = 一次遍历 ,分分析 Pass(只读)和转换 Pass(修改) |
| 7 | opt -passes='...' 是新 PM 的写法,Pass 名随版本变化,别照抄过时名字 |
| 8 | Pass 依赖分显式 (管理器自动排序)和隐式(你必须手动保证顺序) |
| 9 | 写 Pass 按粒度选 ModulePass / FunctionPass(BasicBlockPass 已在新 PM 移除) |
附:本文对原文的修正汇总
| # | 原文 | 修正 |
|---|---|---|
| 1 | "优化层输入是 AST,输出是 DAG" | 优化层输入输出都是 IR;IR→DAG 属于后端指令选择 |
| 2 | "adce 是 Analysis Pass" |
ADCE 是转换 Pass(原文后半句自己也在说"消除掉") |
| 3 | "mdgc 合并全局常量" |
新版 LLVM 用 constmerge / globaldce ;mdgc 非新 PM 标准名 |
| 4 | DominatorTree &DT getAnalysis<...> |
少了等号,应为 DominatorTree &DT = getAnalysis<...>,且属 legacy PM API |
| 5 | BasicBlockPass |
在 new PM(LLVM 14+)已移除 |
参考资料
- 原文:AISystem / 03Compiler / 01Tradition / 07LLVMFrontend.md(作者 ZOMI)
- LLVM 官方文档:https://llvm.org/docs/
- Pass 基础设施:https://llvm.org/docs/WritingAnLLVMPass.html
- 配套视频:BV1vd4y1t7vS