《Engineering a Complier》学习笔记(五)
Intermediate Representation (中间表达式)
一. 概述
IR是编译器从语义分析到生成机器代码的中间过程。编译器可能有一种IR,也可能有多种。
IR可以分为3类:
- 图IR
- 线性IR
- 混合型IR
二. 图IR
与语法相关的树
1. 语法分析树
第3章中已介绍,不赘述
2. 抽象语法树(AST)
在语法树的基础上,省略了表示非终结符的大部分节点
3. 有向无环图(DAG)
DAG是一种具有共享机制的AST,相同子树只实例化一次,可能有多个父节点。
a * 2 + a * 2 * b的DAG:
4. 抽象层次
AST或DAG简洁的表达了IR技术,但对于底层的实现来说,缺少细节,举例:w ← a - 2 * b, 在不同层次上的AST:
其中,◆是一个运算符,反引用一个值,即:将值作为地址处理,返回内存地址中的内容。
编译器将具有相同生命周期和可见性的值存储在一起,称为数据区。(Data Area)
图
1. 控制流图
基本程序块(Basic Block):最大长度的无分支代码序列,开始于一个有标号的操作,结束于一个分支、跳转或条件判断操作。
控制流图(Control-flow Graph,CFG):控制各程序块的模型,是一个有向图G=(N,E),每个节点是一个基本程序块,每个边是一个可能的控制转移。
While和If在CFG中的表示:
单语句块(Single-Statement Block):对应源代码层次上单一语句的代码块。
2. 依赖关系图
依赖关系图(Data Dependence Graph):表达一个值从创建之处(定义)到使用之处(使用)的流动
示例:
3. 调用图(Call Graph)
调用图的结点表示每个过程,每个边表示每个不同过程的调用位置。
三. 线性IR
包含一个指令序列,其中各个指令按照顺序执行。指令可能包含多个操作,类似汇编代码。
1. 堆栈机代码
一种单地址代码,大部分操作从栈获得一个操作数,并将其结果堆入栈
如 a - 2 * b:
2. 三地址代码
包括1个运算符,2个操作数,1个结果,最多需要3个地址:
如 a - 2 * b:
3. 线性代码的表示
三地址代码表示为一个四元组,1个运算符,2个操作数,1个结果。
在代码中可以用不同方法实现。
4. 从线性代码构建控制流图
四. 从值到名字的映射
1.临时值的命名
以下式为例,源代码只包含4个变量,用三地址法处理时,变量的扩展以不同方法数目也不同。
从ATS到底层的线性IR,会产生很多临时变量。
2.静态单赋值形式(Static Single-Assignment Form)
SSA,一种命名规范,基于值的命名系统,由一种称为ϕ函数的伪码操作方法构造。
SSA程序两条约束:(1)每一条定义都有一个独特的名字(2)每一处使用都引用一条定义。
一个ϕ函数会融合不同的命名,并创建一个新的名字。
ϕ函数不遵守三地址模型,其可以放入多个name作为输入,用来处理诸如Switch...case等情形。
3.内存模型
内存模型描述了上述步骤所记录出来的值如何储存。
一般有两种策略:
(1)寄存器到寄存器
(2)内存到内存
五. 符号表
参考:https://www.jianshu.com/p/bda60193808d, https://www.cnblogs.com/programnote/p/4729467.html
当编译器将IR重新写入磁盘时,重新计算内容的代价比将其写入磁盘再读取的代价要小。
书中用哈希表来建立符号表。。。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具