编译要做什么
在预处理阶段生成了一个“干净”的源码文件,接下来的任务是把这个源码转换成汇编代码或者机器码。这一阶段主要包括:词法分析 → 语法分析 → 语义分析 → 中间表示及优化 → 汇编生成。
词法分析
目标:把字符流(源码)切分成有意义的最小单元:token
输入:预处理后的源码
输出:token 流,每个 token 有类型和值,例如:
- 关键字:int、return
 - 标识符:main、x
 - 字面量:42、3.14
 - 操作符:+、*
 - 分隔符:;、{、}
 
编译器通过正则式或 DFA(有限状态机)匹配字符,跳过空格、注释。生成 token 流供下一阶段使用。
1  | int x = 42;  | 
语法分析 / 解析(Parsing)
目标:把 token 按照语言语法结构组织成 AST(Abstract Syntax Tree, 抽象语法树)。
输入:token 流
输出:AST,反映程序结构(声明、表达式、控制流)
编译器基于 上下文无关文法(CFG) 对 token 流做递归下降或 LR/LL 解析。
AST 的节点表示语法元素,例如:
- 函数定义节点
 - 变量声明节点
 - 表达式节点
 
1  | int x = 42 + 3;  | 
语义分析(Semantic Analysis)
目标:检查程序是否符合语言规则(类型、作用域、模板等)。任务包括:
- 类型检查:保证操作符两边类型合法,隐式类型转换可行。
 - 名称解析 / 作用域分析:变量、函数引用是否存在,是否符合作用域。
 - 重载解析:选择合适的函数重载。
 - 模板实例化(C++ 特有):生成实际代码。
 - 表达式合法性检查:数组下标、指针操作是否合法。
 
中间表示 / 优化(IR & Optimization)
目标:生成与具体硬件无关的中间代码,并进行优化。
输入:AST
输出:中间表示(IR,Intermediate Representation),通常是三地址码或 SSA(Static Single Assignment)形式。
优化策略:
- 常量折叠:int y = 2+3; → int y = 5;
 - 死代码消除:未使用的变量、函数删除
 - 循环优化:展开循环、循环不变代码外提
 - 公共子表达式消除
 
现代编译器(Clang/LLVM、GCC)在 IR 阶段能做很多高级优化,直接影响性能。
1  | int a = 2 + 3;  | 
生成目标汇编代码
目标:把 IR 转成特定 CPU 的汇编语言(x86, ARM 等)。
输入:优化后的 IR
输出:汇编文件 .s 或 .S
过程:
- 寄存器分配
 - 指令选择(选择与 IR 对应的 CPU 指令)
 - 栈帧分配(函数调用处理)
 - 生成汇编指令与数据段布局
 
1  | int main() { return 42; }  | 
总结
| 阶段 | 输入 | 输出 | 关键作用 | 
|---|---|---|---|
| 词法分析 | 字符流 | token 流 | 分割最小语法单位 | 
| 语法分析 | token 流 | AST | 构建程序结构 | 
| 语义分析 | AST | 语义正确的 AST / IR | 类型检查、作用域、模板处理 | 
| IR & 优化 | AST | 优化后的 IR | 提高性能、消除冗余 | 
| 汇编生成 | IR | 汇编代码 .s | 转为机器可执行指令 |