当前位置: 首页 > news >正文

编译器工作原理的显微镜级拆解

面向读者

  • 写过 main(){printf("hello");},却没见过它如何变成 0x48 0x65 0x6c 0x6c 0x6f 的你
  • 想知道“语法糖”究竟被谁“脱糖”的同学
  • 想自己写 DSL、脚本引擎、甚至 toy compiler 的勇士

编译器是一群“翻译官”接力写剧本

人类翻译流程编译器对应阶段关键产出
1. 把中文稿拆成单词词法分析Token 流
2. 按语法拼成句子语法分析抽象语法树 AST
3. 理解句子含义语义分析带类型注释的 AST + 符号表
4. 润色、改病句中间代码优化更精简的中间代码 IR
5. 定稿为英文剧本目标代码生成汇编/机器码
6. 剧组合并台词链接可执行文件

带着这张“翻译官分工表”,我们一步步走进编译器的内心。


一、词法分析:把字符流切成 “Token 羊肉串”

1.1 工作细节

  • 输入:纯文本 int main() {return 0;}
  • 输出:Token 序列
    (INT, "int"), (ID, "main"), (LPAREN, "("), ... , (RETURN, "return"), (INT_LIT, "0"), ...
    

1.2 内部机制

  • 有限自动机 (DFA):像地铁闸机,每读一个字符就“咔哒”一次切换状态,直到认出完整单词。
  • 正则→NFA→DFA:先用正则表达式描述关键字、标识符、字面量,再经 Thompson 算法→子集构造算法得到 DFA,最后最小化状态数,减少闸机数量。

比喻:
词法分析器就是高速收费站,字符是车流,Token 是一张张刷过闸机的高速通行卡。


二、 语法分析:把 Token 拼成“语法乐高城堡”

2.1 产出:抽象语法树 AST

   FunctionDecl├── type: int├── name: main├── params: []└── body:ReturnStmt└── value: 0

2.2 常用算法

算法特点比喻
递归下降手写直观乐高说明书一页页照着拼
LR(1)自动生成,能处理左递归机械臂按状态表自动拼装

语法错误例子:
int 123abc; → 分析器发现 123abc 不是合法标识符,抛 syntax error


三、 语义分析:给树贴“标签”和“说明书”

3.1 三件事

  1. 符号表管理:登记每个标识符的类型、作用域、存储类别。
  2. 类型检查:确保 int x = "hello"; 不被放行。
  3. 作用域与生命周期:决定变量何时生、何时死。

比喻:语义分析是给乐高城堡贴门牌号、安全规范及消防通道图。

3.2 典型错误

  • 未声明变量
  • 函数参数不匹配
  • 违反 const/volatile 语义

四、 中间代码生成:AST 的“骨架替身”

4.1 为什么需要 IR?

  • 机器无关:前后端解耦,同一前端可配多种后端。
  • 便于优化:树结构不利于全局数据流分析,IR 通常是线性三地址码或 SSA 形式。

4.2 三地址码示例

t1 = 0
return t1

4.3 SSA(静态单赋值)

每个变量只赋值一次,便于做“死代码删除”“常量传播”等手术。

比喻:把乐高城堡拍扁成平面图,方便工程师画优化路线图。


五、 代码优化:把“病句”变“金句”

优化类别例子效果
局部常量折叠 2+3→5减少指令数
循环循环不变外提减少重复计算
全局公共子表达式删除减少冗余
机器相关指令调度、寄存器分配利用 CPU 并行

比喻:编辑把“我非常非常非常非常高兴”润色成“我欣喜若狂”,既省字数又增强可读性。


六、 目标代码生成:把 IR 翻译成“地道方言”

6.1 三大难题

  1. 指令选择:从 IR 节点到具体指令的模式匹配(动态规划、树覆盖)。
  2. 寄存器分配:图着色算法,把无限虚拟寄存器塞进有限物理寄存器。
  3. 指令调度:重排指令顺序,减少流水线气泡。

6.2 输出示例(x86-64)

mov    eax,0
ret

七、 链接:把“分镜剧本”合成“整部电影”

  • 重定位:把 .o 里的相对地址改成最终内存地址。
  • 符号决议:找到 printf 在 libc 中的真正位置。
  • 静态 vs 动态链接
    • 静态:把所有台词提前打包,体积大但无运行时依赖。
    • 动态:演出当天再去借道具(.so/.dll),体积小但需环境存在。

八、实战:一条语句的 0→1 全链路

源码

int square(int x){return x * x;
}

8.1 各阶段产物

阶段关键产物片段
词法(INT,"int") (ID,"square") (LPAREN,"(") ...
语法FuncDef(type=int,name=square,params=[x],body=Return(Mul(x,x)))
语义符号表:x:int,register:%v0
中间(IR)entry: %t1 = mul %x, %x; ret %t1
优化x 是常量 3,直接 ret 9
汇编mov eax, edi; imul eax, eax; ret

九、 拓展:JIT vs AOT,两条不同的“出版路线”

AOT(提前编译)JIT(即时编译)
何时翻译程序运行前程序运行时
优点启动快、无运行时开销可按实际热点再优化
缺点编译慢、需全平台重编启动慢、占用内存
代表GCC/Clang 生成 ELF/EXEJava HotSpot、V8、ART

思维导图

编译器全景图
├── 前端(分析)
│   ├── 词法:字符→Token
│   ├── 语法:Token→AST
│   └── 语义:AST + 符号表
├── 中端(优化)
│   ├── IR 生成:AST→三地址/SSA
│   └── 优化:常量折叠、循环、寄存器
├── 后端(生成)
│   ├── 指令选择、调度、分配
│   └── 汇编/机器码
└── 链接├── 重定位└── 静态/动态链接

http://www.dtcms.com/a/310892.html

相关文章:

  • 【读代码】 KAG项目:开源知识图谱自动构建与推理平台原理与实践
  • PYTHON从入门到实践-18Django从零开始构建Web应用
  • SpringBoot3.x入门到精通系列:2.1 自动配置原理
  • 【软考中级网络工程师】知识点之 VRRP
  • 关于Web前端安全防御CSRF攻防的几点考虑
  • 关于人工智能AI>ML>DL>transformer及NLP的关系
  • MySQL(173)MySQL中的存储过程和函数有什么区别?
  • 【DeepSeek-R1 】分词系统架构解析
  • 快速了解决策树
  • API征服者:Python抓取星链卫星实时轨迹
  • Docker 部署与配置 MySQL 5.7
  • 四、Portainer图形化管理实战与Docker镜像原理
  • 2024年网络安全案例
  • 从数据丢失到动画流畅:React状态同步与远程数据加载全解析
  • Jotai:React轻量级原子化状态管理,告别重渲染困扰
  • 《深潜React列表渲染:调和算法与虚拟DOM Diff的优化深解》
  • 《React+TypeScript实战:前端状态管理的安全架构与性能优化深解》
  • Oracle 11g RAC集群部署手册(三)
  • SQL 四大语言分类详解:DDL、DML、DCL、DQL
  • Oracle 11g RAC集群部署手册(一)
  • 探索:Uniapp 安卓热更新
  • flink写paimon表的过程解析
  • cmd怎么取消关机命令
  • 【DL学习笔记】yaml、json、随机种子、浮点精度、amp
  • hcip---ospf知识点总结及实验配置
  • 学习嵌入式第十八天
  • rag学习-以项目为基础快速启动掌握rag
  • 深入 Go 底层原理(十):defer 的实现与性能开销
  • Vue3+ts自定义指令
  • 深入 Go 底层原理(二):Channel 的实现剖析