编译要做什么

在预处理阶段生成了一个“干净”的源码文件,接下来的任务是把这个源码转换成汇编代码或者机器码。这一阶段主要包括:词法分析 → 语法分析 → 语义分析 → 中间表示及优化 → 汇编生成。

词法分析

目标:把字符流(源码)切分成有意义的最小单元:token

输入:预处理后的源码

输出:token 流,每个 token 有类型和值,例如:

  • 关键字:int、return
  • 标识符:main、x
  • 字面量:42、3.14
  • 操作符:+、*
  • 分隔符:;、{、}

编译器通过正则式或 DFA(有限状态机)匹配字符,跳过空格、注释。生成 token 流供下一阶段使用。

1
2
3
4
5
int x = 42;

// 上述代码会被解析为如下 token

[int][identifier:x][=][integer:42][;]

语法分析 / 解析(Parsing)

目标:把 token 按照语言语法结构组织成 AST(Abstract Syntax Tree, 抽象语法树)。

输入:token 流

输出:AST,反映程序结构(声明、表达式、控制流)

编译器基于 上下文无关文法(CFG) 对 token 流做递归下降或 LR/LL 解析。

AST 的节点表示语法元素,例如:

  • 函数定义节点
  • 变量声明节点
  • 表达式节点
1
2
3
4
5
6
7
8
9
10
int x = 42 + 3;

// 上述代码的 AST 可能表示为

Assignment
├─ Variable: x
└─ BinaryOp: +
├─ Literal: 42
└─ Literal: 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
2
3
4
5
6
7
8
int a = 2 + 3;
int b = 2 + 3;

// 上述代码可优化为如下形式

int tmp = 5;
int a = tmp;
int b = tmp;

生成目标汇编代码

目标:把 IR 转成特定 CPU 的汇编语言(x86, ARM 等)。

输入:优化后的 IR

输出:汇编文件 .s 或 .S

过程

  • 寄存器分配
  • 指令选择(选择与 IR 对应的 CPU 指令)
  • 栈帧分配(函数调用处理)
  • 生成汇编指令与数据段布局
1
2
3
4
5
6
7
int main() { return 42; }

// 可生成如下 x86汇编代码

_main:
mov eax, 42
ret

总结

阶段 输入 输出 关键作用
词法分析 字符流 token 流 分割最小语法单位
语法分析 token 流 AST 构建程序结构
语义分析 AST 语义正确的 AST / IR 类型检查、作用域、模板处理
IR & 优化 AST 优化后的 IR 提高性能、消除冗余
汇编生成 IR 汇编代码 .s 转为机器可执行指令

本网站由 Nooobad 使用 Stellar 1.33.1 主题创建。
除非另有说明,本博客中的所有文章均采用 CC BY-NC-SA 4.0 许可协议。转载时请注明文章来源。