文章目录
- 8. 静态语义分析和中间代码生成
- 8.1 中间语言
- 8.1.1 概述
- 8.1.2 逆波兰式(后缀式)
- 1) 定义
- 2) 计算方式
- 3) 表达式 => 后缀式
- 8.1.3 图表示法
- 1) 有向无环图
- 2) 举例
- 8.1.4 三地址代码
- 1) 概述
- 2) 四元式
- 3) 三元式
- 4) 间接三元式
- 8.2 赋值语句的翻译
- 8.2.1 赋值语句的属性文法
- 1) 概述
- 2) 语义规则
- 8.2.2 赋值语句的翻译模式
- 8.3 数组元素引用的翻译
- 8.3.1 数组元素地址计算
- 1) 计算公式
- 2) 产生式 / 属性定义
- 8.3.2 带数组元素引用的赋值语句翻译模式
- 1) 赋值语句类别
- 2) 赋值语句翻译
- 8.3.3 类型转换
- 8.4 布尔表达式的翻译
- 8.4.1 布尔表达式概述
- 1) 文法 & 用途
- 2) 布尔表达式两种计算方法
- 8.4.2 按数值表示法翻译
- 1) 数值表示法
- 2) 翻译模式
- 8.4.3 带优化的翻译
- 8.4.4 布尔表达式的属性文法
- 1) 属性
- 2) 文法
- 8.4.5 布尔表达式的一遍扫描翻译模式
- 1) 定义引入
- 2) 布尔表达式翻译模式
- 8.5 控制语句的翻译
- 8.5.1 控制语句的属性文法
- 1) 基础定义
- 2) 三遍扫描示例
- 8.5.2 控制语句的属性文法
- 1) if 语句的翻译模式
- 2) while-do 语句的翻译模式
- 3) 复合语句的翻译模式
- 4) 一遍扫描示例
- 5) for循环语句翻译
- 6) 数组的翻译
8. 静态语义分析和中间代码生成
8.1 中间语言
8.1.1 概述
「特点」
- 独立于机器
- 复杂性界于源语言和目标语言之间
「优点」
- 使编译程序的结构在逻辑上更为简单明确
- 便于进行与机器无关的代码优化工作
- 易于移植
「常用的中间语言」
- 后缀式:逆波兰表示
- 图表示:抽象语法树(AST)、有向无环图(DAG)
- 三地址代码:四元式、三元式、间接三元式
8.1.2 逆波兰式(后缀式)
1) 定义
逆波兰表示法即为后缀表示法,而默认我们使用的表达式是中缀表示法
程序设计语言中的表示 | -----逆波兰表示----- |
a+ba+b | ab+ab+ |
−a−a | a@a@ |
a+b∗ca+b∗c | abc∗+abc∗+ |
(a+b)∗c(a+b)∗c | ab+c∗ab+c∗ |
a:=b∗c+b∗da:=b∗c+b∗d | abc∗bd∗+:=abc∗bd∗+:= |
a:=b∗(c+b)∗(−d)a:=b∗(c+b)∗(−d) | bcb+∗d@∗:=bcb+∗d@∗:= |
逆波兰式的使用:需使用额外的标识符栈。顺序扫描逆波兰表达式的时候,遇到标识符直接入栈。
遇到运算符时:
- 根据运算符目数,从栈顶取出相应数目的标识符做运算,并把运算结果压栈
- 运算结束时,标识符栈应该只剩下一个元素,且为运算结果
2) 计算方式
3) 表达式 => 后缀式
- 表达式 => 后缀式的属性文法
- 中缀表达式 => 后缀式
8.1.3 图表示法
1) 有向无环图
- 对表达式中的每个子表达式,DAG中都有一个结点
- 一个内部结点代表一个操作符,它的孩子代表操作数
- 在一个 DAG 中代表公共子表达式的结点具有多个父节点
树形表示和三元式表示非常相似,如a:=b∗c+b∗da:=b∗c+b∗d表示为:
注意赋值表达式中被赋值对象在树的左孩子节点位置
单目运算−T1−T1直接表示成:
2) 举例
8.1.4 三地址代码
1) 概述
- 「形式」x : = y o p z x:=y\ op \ zx:=y o**p z
- 「理解」三地址代码可以看成是抽象语法树或有向无环图的一种线性表示
- 「抽象语法树 vs. 三地址代码」
- 「有向无环图 vs. 三地址代码」
- 「三地址语句的种类」
2) 四元式
四元式(op,A1,A2,R)(op,A1,A2,R)
opop为运算符
A1A1为第一运算对象
A2A2为第二运算对象
RR为运算结果
例如a:=b∗c+b∗da:=b∗c+b∗d的四元式表示:
(1)(∗,b,c,t1)(1)(∗,b,c,t1)
(2)(∗,b,d,t2)(2)(∗,b,d,t2)
(3)(+,t1,t2,t3)(3)(+,t1,t2,t3)
(4)(:=,t3,−,a)(4)(:=,t3,−,a)
和三元式的差别在于,四元式对中间结果的引用必须通过给定的名字(临时变量)
它的三地址码写法为:
t1:=b∗ct1:=b∗c
t2:=b∗dt2:=b∗d
t3:=t1∗t2t3:=t1∗t2
a:=t3a:=t3
3) 三元式
在四元式基础上优化,用编号代表结果。
三元式(op,A1,A2)(op,A1,A2)
opop为运算符
A1A1为第一运算对象
A2A2为第二运算对象
例如a:=b∗c+b∗da:=b∗c+b∗d表示为:
(1)(∗,b,c)(1)(∗,b,c)
(2)(∗,b,d)(2)(∗,b,d)
(3)(+,(1),(2))(3)(+,(1),(2)) 这里用(1)和(2)来表示中间计算结果的显式引用
(4)(:=,(3),a)(4)(:=,(3),a) 这里相当于a:=(3)a:=(3)
而单目运算的−b−b可以表示成(−,b,/)(−,b,/)
4) 间接三元式
在三元式的基础上再度优化,使得式子发生变化时,可变性更强。
- 「定义」间接三元式 = 三元式表 + 间接码表
- 间接码表:一张指示器表,按运算的先后次序列出有关三元式在三元式表中的位置
- 「优点」方便优化,节省空间
8.2 赋值语句的翻译
8.2.1 赋值语句的属性文法
1) 概述
- 「赋值语句形式」$i d : = E $
- 「赋值语句意义」
- 对表达式 E 求值并置于变量 T 中
- 「赋值语句 => 三地址代码的 S-属性文法」
2) 语义规则
- 赋值语句生成三地址代码的 S-属性文法
- g e n ( ) 函数用于生成语句的三地址代码
8.2.2 赋值语句的翻译模式
- 产生赋值语句三地址代码的翻译模式
8.3 数组元素引用的翻译
8.3.1 数组元素地址计算
1) 计算公式
- 设A为n维数组,按行存放,每个元素宽度为w
- $l o w_i $为第 i 维的下界
为第 i维的上界
为第 i ii 维可取值的个数
- base为 A 的第一个元素相对地址
- 元素
相对地址公式
- 可变部分
- 不变部分
- 相对地址 = V + C
2) 产生式 / 属性定义
- 产生式
- 属性定义
8.3.2 带数组元素引用的赋值语句翻译模式
此翻译模式与自下而上的语法分析方法结合在一起,可以一遍扫描就完成语法分析和翻译。
1) 赋值语句类别
2) 赋值语句翻译
8.3.3 类型转换
「类型转换的三个功能」
- 检查操作数类型
- 确定结果的类型
- 如果有必要,将整型转变为实型
「类型转换的三地址代码举例」
「类型转换属性文法」
「类型转换的语义动作举例」
8.4 布尔表达式的翻译
8.4.1 布尔表达式概述
1) 文法 & 用途
「文法」
- rop 为关系运算符,包含 [>, >=, <, <=, <>, ==]
- i 表示单个布尔变量
- 运算优先级:括号 > 条件表达式 > not > and > or
「用途」
- 用于逻辑演算,计算逻辑值
- 用于控制语句的条件式
2) 布尔表达式两种计算方法
「数值表示法」
- 如同计算算术表达式一样,一步步算
- 举例
「带优化的翻译法」
- 相比数值表示法,可以减少很多冗余计算,效率更高
- 适合于作为条件表达式的布尔表达式使用
- 举例
8.4.2 按数值表示法翻译
1) 数值表示法
「a or b and not c」
- $ T_2:=b \ and\ T_1$
「a < b」
- 等价于「if a < b then 1 else 0」,进行如下翻译
- 104 :
2) 翻译模式
「基础说明」
- 过程 emit 将三地址代码送到输出文件中
- nextstat: 输出序列中下一条三地址语句的地址索引
- 过程 emit 每产生一条指令,nextstat 加 1
- 举例
「布尔表达式翻译模式」
「布尔表达式 a<b or c<d and e<f 翻译举例」
8.4.3 带优化的翻译
「核心思想」从左到右判断,当结果已经得出时,退出。
「举例」
8.4.4 布尔表达式的属性文法
1) 属性
- 语义函数 newlabel,返回一个新的符号标号
- 对于一个布尔表达式 E,设置两个继承属性
- E.true 是 E 为 ‘真’ 时控制流转向的标号
- E.false 是 E 为 ‘假’ 时控制流转向的标号
- E.code 记录 E 生成的三地址代码序列
2) 文法
- 产生布尔表达式三地址代码的属性文法
$ E\rightarrow E_1 \ and\ E_2$
- 举例
「布尔表达式:a < b or c < d and e < f」
「翻译流程:第一遍扫描 — 构建语法树」
- 整个表达式的真假出口分别置为 Ltrue、Lfalse
「翻译流程:第二遍扫描 — 自上而下求出继承属性」
「翻译流程:第三遍扫描 — 自上而下求出综合属性 E.code」
E.code: (red)
if a < b goto Ltrue
goto L1
E.code: (blue)
if c < d goto L2
goto Lfalse
E.code: (green)
if e < f goto Ltrue
goto Lfalse
E.code: (purple)
if c < d goto L2
goto Lfalse
L2: if e < f goto Ltrue
goto Lfalse
E.code: (black)
if a < b goto Ltrue
goto L1
L1: if c < d goto L2
goto Lfalse
L2: if e < f goto Ltrue
goto Lfalse
8.4.5 布尔表达式的一遍扫描翻译模式
1) 定义引入
「翻译过程以四元式为中间语言」
- 四元式存入一个数组中,数组下标代表四元式的标号
- 约定
- 四元式 (jnz, a, -, p) 表示 if a goto p
- 四元式 (jrop, x, y, p) 表示 if x rop y goto p
- 四元式 (j, -, -, p) 表示 goto p
- 过程 emit 将四元式代码送到输出数组中
「E 综合属性 E.truelist 和 E.falselist」
- E.truelist 和 E.falselist 分别记录布尔表达式 E 所对应的四元式中需回填 “真”、“假” 出口的四元式的标号所构成的链表
- 举例
「语义变量和过程」
- 变量nextquad
- 指向下一条将要产生但尚未形成的四元式的地址(标号)
-
nextquad
初值为 1,每当执行一次 emit 之后,nextquad
自动增一
- 函数makelist(i)
- 创建一个仅含 i 的新链表,其中 i 式四元式数组的一个下标(标号)
- 函数返回指向这个链的指针
- 函数merge(p_1,p_2)
- 把以
和
为链首的两条链合并为一
- 函数值返回合并后的链首
- 过程
- 完成 “回填”
- 把 p 所链接的每个四元式的第四区段都填为 t
「布尔表达式的文法」
2) 布尔表达式翻译模式
「举例:一遍扫描」
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SJUY39Q1-1621079129249)(https://gitee.com/fakerlove/picture_1/raw/master/2020081909570077.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CDQJMlhn-1621079129251)(picture/20200819095711995.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oubr9RJg-1621079129251)(https://gitee.com/fakerlove/picture_1/raw/master/20200819095726580.png)]
8.5 控制语句的翻译
「常用的控制语句」
- 单分支:
- 双分支:
- 循环:
8.5.1 控制语句的属性文法
1) 基础定义
「属性定义」
- E.true、E.false 表示 E 为真 / 假时的跳转语句标号
- S.begin、S.next 表示语句 S 开始执行、执行结束后的地址
「if-then 语句的属性文法」
「if-then-else 语句的属性文法」
「while-do 语句的属性文法」
2) 三遍扫描示例
「三遍扫描的属性计算示例」
while a<b do
if c<d then x:=y+z
else x:=y-z
123
- 第一遍:构建语法树
- 第二遍:自上而下计算继承属性
- 第三遍:自下而上计算综合属性(code)
即上述 while 控制语句最终得到如下所示的翻译结果:
L1: if a<b goto L2
goto Lnext
L2: if c<d goto L3
goto L4
L3: T1 := y+z
x := T1
goto L1
L4: T2 := y-z
x := T2
goto L1
12345678910
8.5.2 控制语句的属性文法
「控制语句的文法」
其中 S 表示语句,L 表示语句表,A 表示赋值语句,E 表示布尔表达式。
1) if 语句的翻译模式
「改写后的产生式」
「翻译模式」
- S.nextlist 属性:保存 S 翻译生成的代码中需要回填的,要跳转到 S 的后继语句的那些四元式。
2) while-do 语句的翻译模式
「改写后的产生式」
「翻译模式」
3) 复合语句的翻译模式
「改写后的产生式」
「翻译模式」
「其它语句的翻译」
4) 一遍扫描示例
「控制语句示例」
while a<b do
if c<d then x:=y+z;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dgHJcnh5-1621079129264)(picture/20200819100011982.png)]
5) for循环语句翻译
现需要翻译语句:forI:=1step1untilYdoX:=X+1
等价于C语言的:
for(I=1;I<=Y;++I) X=X+1;
I:=1100
goto 103
I:=I+1
if I<=Y goto 105
goto 108
T:=X+1
X:=T
goto 102
6) 数组的翻译
对于一个静态的n维数组,要访问其中一个元素,可以使用下标法:A[i1,i2,…,in]
由于内存是一维连续空间,对于行主数组,比如一个2×32×3的二维数组,在内存中的布局A[1,1]A[1,2]A[1,3]A[2,1]A[2,2]A[2,3]
现知道数组AA的地址为aa,那A[i,j]A[i,j]的地址为:
a+(i−1)×3+(j−1)
设BB为n维数组B[l1:u1,l2:u2,…,ln:un]
显然di=ui−li+1。令b是数组首元素地址,那么行主数组下B[i1,i2,…,in]的地址D为:
D=b+(i1−l1)d2d3…dn+(i2−l2)d3…dn+(in−1−ln−1)dn+(in−ln)
对式子展开可以提取出式子中的固定部分和变化部分:
D=constPart+varPart
constPart=b−C
C=l1d2d3…dn+l2d3…dn+…+ln−1dn−1+lndn
varPart=i1d2d3…dn+i2d3…dn+…+in−1dn−1+indn
访问数组元素A[i1,i2,…,in]需要产生两组计算数组的四元式:
- 一组计算varPart,存放结果到临时单元T中
- 一组计算constPart,存放结果到另一个临时单元T1中
用T1[T]表示数组元素的地址
变址取数的四元式:(=[],T1[T],−,X),相当于X:=T1[T]
变址存数的四元式:([]=,X1,−,T1[T])相当于T1[T]:=X
现在有一个10*20的数组A,即d1=10,d2=20。则X:=A[I,J]的四元式序列为:
(∗,I,20,T1)
(+,T1,J,T1)
(−,A,21,T2)
(=[],T2[T1],−,T3)
(:=,T3,−,X)
对应:
varPart=20∗I+J
constPart=A−(1∗20+1)=A−21