当前位置: 首页 > 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.xdnf.cn/news/1227565.html

相关文章:

  • 开箱即用的Next.js SSR企业级开发模板
  • 什么是doris
  • Typora v1.10.8 好用的 Markdown 编辑器
  • DreamBoards 借助 DreamHAT+ 雷达插件为 Raspberry Pi 提供 60GHz 毫米波雷达
  • 思途JSP学习 0801
  • 《软件测试与质量控制》实验报告一 测试用例设计
  • 逻辑回归参数调优实战指南
  • JS核心语法与实战技巧
  • 【读文献】Capacitor-drop AC-DC
  • 计数组合学7.10(舒尔函数的组合定义)
  • ls hgfs提示ls: cannot access ‘hgfs‘: Permission denied
  • Python 项目路径配置完全指南
  • 如何优雅删除Docker镜像和容器(保姆级别)
  • 开源工具FossFLOW,绘制技术图表
  • linux中posix消息队列的使用记录
  • Cesium性能优化
  • windows系统安装文生图大模型Stable diffusion V3.5 large(完整详细可用教程)
  • 第15讲——微分方程
  • 分类预测 | Matlab实现CPO-PNN冠豪猪算法优化概率神经网络多特征分类预测
  • 操作系统-lecture4(进程的调度)
  • ubuntu22.04系统入门 linux入门(二) 简单命令 多实践以及相关文件管理命令
  • 分布在背侧海马体CA1区域的位置细胞(place cells)对NLP中的深层语义分析的积极影响和启示
  • 设计模式1:创建型模式
  • Java 学习笔记:常用类、String 与日期时间处理
  • 在纯servlet项目中,使用@WebFilter定义了多个filter,如何设置filter的优先级
  • Google机器学习基础(语言模型)
  • Rust在CentOS 6上的移植
  • 梯度下降的基本原理
  • 【Shell脚本自动化编写——报警邮件,检查磁盘,web服务检测】
  • 如何理解推理模型