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

相关文章:

  • 个人电脑部署私有化大语言模型LLM
  • Python爬虫实战:研究mahotas库,构建图像获取及处理系统
  • 重型机械作业误伤预警响应时间缩短80%!陌讯多模态识别算法在工程现场的应用优化
  • build文件夹下面的主要配置文件
  • Day 29: 复习
  • 设计模式篇:在前端,我们如何“重构”观察者、策略和装饰器模式
  • (LeetCode 面试经典 150 题) 138. 随机链表的复制 (哈希表)
  • PyTorch 中 Tensor 统计学函数及相关概念
  • linux编译基础知识-库文件标准路径
  • 3D,对比2D孰优孰劣?
  • SEA-RAFT:更简单、更高效、更准确的RAFT架构
  • 重生之我在暑假学习微服务第八天《OpenFeign篇》
  • 【C语言】内存函数与数据在内存中的存储
  • 推荐系统学习笔记(六)自监督学习
  • Kubernetes 构建高可用、高性能 Redis 集群实战指南
  • Ubuntu系统VScode实现opencv(c++)视频及摄像头使用
  • ffmpeg命令和ffplay命令详解
  • 垃圾收集器ParNewCMS与底层三色标记算法详解
  • 【云计算】云主机的亲和性策略(四):云主机组
  • VAST视频广告技术实现:从零开始搭建视频广告投放系统
  • 【20min 急速入门】使用Demucs进行音轨分离
  • 【云计算】云主机的亲和性策略(三):云主机 宿主机
  • 【Android】RecyclerView实现新闻列表布局(1)适配器使用相关问题
  • MySQL 运算符
  • 【Android】使用 Intent 传递对象的两种序列化方式
  • 【Android】进度条ProgressBar 可拖拽进度条Seekbar
  • Javaweb————Apache Tomcat服务器介绍及Windows,Linux,MAC三种系统搭建Apache Tomcat
  • Vue 详情模块 4
  • 分布式微服务--Nacos作为配置中心(二)
  • Text2SQL:如何通过自然语言直接获取数据,打破技术壁垒?