LLVM 前端与优化层:Clang 如何把 C 代码变成 IR

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 列)。

它有三个关键作用:

  1. 报错定位 ------你看到的 hello.c:6:5: error: 就是从这里来的
  2. 调试信息------生成 DWARF 调试信息时需要它
  3. 宏展开追踪 ------区分"写在宏里的位置"和"宏展开后的位置"(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 语义分析靠什么:符号表

符号表存储"标识符 → 类型 + 其他必要信息"的映射。

一种直观的实现方式:

  1. 解析完得到 AST
  2. 遍历 AST
  3. 每遇到一个标识符,就去符号表里查它的类型
  4. 检查当前上下文要求是否匹配

#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+)已移除

参考资料

相关推荐
大文说跨境1 小时前
亚马逊多店铺用过多款指纹浏览器,说点长期用的实际感受
前端·网络
中屹指纹浏览器svip1 小时前
中屹指纹浏览器和普通 Chrome 有什么区别?
前端·chrome·中屹指纹浏览器
feixing_fx2 小时前
CSS Grid 入门:二维布局时代的降维打击
前端·css·前端框架·css3
Lank_M2 小时前
JPG的8×8块里只要有条边,白底就保不住255
前端
coding漫漫长路2 小时前
主图纯白检测,阈值放宽到250会放过252的底
前端
计算机魔术师2 小时前
藏了八个月没被当漏洞的洞,AI红队一晚上翻出来了
前端
用户83134859306982 小时前
Vue3 v-for:常用用法与开发高频坑点
前端·javascript·vue.js
用户621007587022 小时前
Web 3D 开发实践:BIM+GIS 融合需要解决哪些技术问题?
前端·three.js
志尊宝2 小时前
AI原生前端开发(017):端侧AI与Vue 3结合实战
前端·人工智能·prompt·1024·ai-native