文章目录

  • 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 概述

「特点」

  • 独立于机器
  • 复杂性界于源语言和目标语言之间

「优点」

  • 使编译程序的结构在逻辑上更为简单明确
  • 便于进行与机器无关的代码优化工作
  • 易于移植

编译原理教程_8 静态语义分析和中间代码生成_布尔表达式

「常用的中间语言」

  • 后缀式:逆波兰表示
  • 图表示:抽象语法树(AST)、有向无环图(DAG)
  • 三地址代码:四元式、三元式、间接三元式

8.1.2 逆波兰式(后缀式)

1) 定义

编译原理教程_8 静态语义分析和中间代码生成_编译原理_02

逆波兰表示法即为后缀表示法,而默认我们使用的表达式是中缀表示法

程序设计语言中的表示

-----逆波兰表示-----

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@∗:=

逆波兰式的使用:需使用额外的标识符栈。顺序扫描逆波兰表达式的时候,遇到标识符直接入栈。

遇到运算符时:

  1. 根据运算符目数,从栈顶取出相应数目的标识符做运算,并把运算结果压栈
  2. 运算结束时,标识符栈应该只剩下一个元素,且为运算结果

2) 计算方式

编译原理教程_8 静态语义分析和中间代码生成_赋值语句_03

3) 表达式 => 后缀式

  • 表达式 => 后缀式的属性文法
  • 中缀表达式 => 后缀式

8.1.3 图表示法

1) 有向无环图

  • 对表达式中的每个子表达式,DAG中都有一个结点
  • 一个内部结点代表一个操作符,它的孩子代表操作数
  • 在一个 DAG 中代表公共子表达式的结点具有多个父节点

树形表示和三元式表示非常相似,如a:=b∗c+b∗da:=b∗c+b∗d表示为:

编译原理教程_8 静态语义分析和中间代码生成_后缀_04


注意赋值表达式中被赋值对象在树的左孩子节点位置

单目运算−T1−T1直接表示成:

编译原理教程_8 静态语义分析和中间代码生成_赋值语句_05

2) 举例

编译原理教程_8 静态语义分析和中间代码生成_后缀_06

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

编译原理教程_8 静态语义分析和中间代码生成_赋值语句_07

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,/)

编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_08

编译原理教程_8 静态语义分析和中间代码生成_后缀_09

4) 间接三元式

在三元式的基础上再度优化,使得式子发生变化时,可变性更强。

  • 「定义」间接三元式 = 三元式表 + 间接码表
  • 间接码表:一张指示器表,按运算的先后次序列出有关三元式在三元式表中的位置
  • 「优点」方便优化,节省空间

编译原理教程_8 静态语义分析和中间代码生成_编译原理_10

8.2 赋值语句的翻译

8.2.1 赋值语句的属性文法

1) 概述

  • 「赋值语句形式」$i d : = E $
  • 「赋值语句意义」
  • 对表达式 E 求值并置于变量 T 中
  • 编译原理教程_8 静态语义分析和中间代码生成_赋值语句_11
  • 「赋值语句 => 三地址代码的 S-属性文法」

2) 语义规则

  • 赋值语句生成三地址代码的 S-属性文法
  • g e n ( ) 函数用于生成语句的三地址代码

编译原理教程_8 静态语义分析和中间代码生成_赋值语句_12

8.2.2 赋值语句的翻译模式

  • 产生赋值语句三地址代码的翻译模式

编译原理教程_8 静态语义分析和中间代码生成_后缀_13

编译原理教程_8 静态语义分析和中间代码生成_编译原理_14

编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_15

8.3 数组元素引用的翻译

8.3.1 数组元素地址计算

1) 计算公式

  • 设A为n维数组,按行存放,每个元素宽度为w
  • $l o w_i $为第 i 维的下界
  • 编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_16为第 i维的上界
  • 编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_17为第 i ii 维可取值的个数编译原理教程_8 静态语义分析和中间代码生成_后缀_18
  • base为 A 的第一个元素相对地址
  • 元素
    编译原理教程_8 静态语义分析和中间代码生成_赋值语句_19相对地址公式
  • 可变部分 编译原理教程_8 静态语义分析和中间代码生成_后缀_20
  • 不变部分编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_21
  • 相对地址 = V + C

2) 产生式 / 属性定义

  • 产生式
  • 属性定义

编译原理教程_8 静态语义分析和中间代码生成_编译原理_22

8.3.2 带数组元素引用的赋值语句翻译模式

此翻译模式与自下而上的语法分析方法结合在一起,可以一遍扫描就完成语法分析和翻译。

1) 赋值语句类别

编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_23

2) 赋值语句翻译

编译原理教程_8 静态语义分析和中间代码生成_后缀_24

编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_25

编译原理教程_8 静态语义分析和中间代码生成_赋值语句_26

编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_27

编译原理教程_8 静态语义分析和中间代码生成_赋值语句_28

8.3.3 类型转换

「类型转换的三个功能」

  • 检查操作数类型
  • 确定结果的类型
  • 如果有必要,将整型转变为实型

「类型转换的三地址代码举例」

编译原理教程_8 静态语义分析和中间代码生成_赋值语句_29

「类型转换属性文法」

编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_30

「类型转换的语义动作举例」

编译原理教程_8 静态语义分析和中间代码生成_后缀_31

编译原理教程_8 静态语义分析和中间代码生成_编译原理_32

8.4 布尔表达式的翻译

8.4.1 布尔表达式概述

1) 文法 & 用途

「文法」

编译原理教程_8 静态语义分析和中间代码生成_后缀_33

  • rop 为关系运算符,包含 [>, >=, <, <=, <>, ==]
  • i 表示单个布尔变量
  • 运算优先级:括号 > 条件表达式 > not > and > or

「用途」

  • 用于逻辑演算,计算逻辑值
  • 用于控制语句的条件式

2) 布尔表达式两种计算方法

「数值表示法」

  • 如同计算算术表达式一样,一步步算
  • 举例

「带优化的翻译法」

  • 相比数值表示法,可以减少很多冗余计算,效率更高
  • 适合于作为条件表达式的布尔表达式使用
  • 举例

8.4.2 按数值表示法翻译

1) 数值表示法

「a or b and not c」

  • 编译原理教程_8 静态语义分析和中间代码生成_编译原理_34
  • $ T_2:=b \ and\ T_1$
  • 编译原理教程_8 静态语义分析和中间代码生成_后缀_35

「a < b」

  • 等价于「if a < b then 1 else 0」,进行如下翻译
  • 编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_36
  • 编译原理教程_8 静态语义分析和中间代码生成_编译原理_37
  • 编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_38
  • 编译原理教程_8 静态语义分析和中间代码生成_后缀_39
  • 104 :

2) 翻译模式

「基础说明」

  • 过程 emit 将三地址代码送到输出文件中
  • nextstat: 输出序列中下一条三地址语句的地址索引
  • 过程 emit 每产生一条指令,nextstat 加 1
  • 举例

「布尔表达式翻译模式」

编译原理教程_8 静态语义分析和中间代码生成_赋值语句_40

编译原理教程_8 静态语义分析和中间代码生成_编译原理_41

「布尔表达式 a<b or c<d and e<f 翻译举例」

编译原理教程_8 静态语义分析和中间代码生成_编译原理_42

8.4.3 带优化的翻译

「核心思想」从左到右判断,当结果已经得出时,退出。

编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_43

「举例」

编译原理教程_8 静态语义分析和中间代码生成_编译原理_44

8.4.4 布尔表达式的属性文法

1) 属性

  • 语义函数 newlabel,返回一个新的符号标号
  • 对于一个布尔表达式 E,设置两个继承属性
  • E.true 是 E 为 ‘真’ 时控制流转向的标号
  • E.false 是 E 为 ‘假’ 时控制流转向的标号
  • E.code 记录 E 生成的三地址代码序列

2) 文法

  • 产生布尔表达式三地址代码的属性文法

编译原理教程_8 静态语义分析和中间代码生成_编译原理_45

编译原理教程_8 静态语义分析和中间代码生成_后缀_46

$ E\rightarrow E_1 \ and\ E_2$

编译原理教程_8 静态语义分析和中间代码生成_编译原理_47

编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_48

编译原理教程_8 静态语义分析和中间代码生成_赋值语句_49

编译原理教程_8 静态语义分析和中间代码生成_赋值语句_50

编译原理教程_8 静态语义分析和中间代码生成_编译原理_51

  • 举例

「布尔表达式:a < b or c < d and e < f」

「翻译流程:第一遍扫描 — 构建语法树」

  • 整个表达式的真假出口分别置为 Ltrue、Lfalse

编译原理教程_8 静态语义分析和中间代码生成_赋值语句_52

「翻译流程:第二遍扫描 — 自上而下求出继承属性」

编译原理教程_8 静态语义分析和中间代码生成_后缀_53

「翻译流程:第三遍扫描 — 自上而下求出综合属性 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)
  • 把以编译原理教程_8 静态语义分析和中间代码生成_赋值语句_54编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_55为链首的两条链合并为一
  • 函数值返回合并后的链首
  • 过程编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_56
  • 完成 “回填”
  • 把 p 所链接的每个四元式的第四区段都填为 t

「布尔表达式的文法」

  • 编译原理教程_8 静态语义分析和中间代码生成_赋值语句_57
  • 编译原理教程_8 静态语义分析和中间代码生成_赋值语句_58
  • 编译原理教程_8 静态语义分析和中间代码生成_赋值语句_59
  • 编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_60
  • 编译原理教程_8 静态语义分析和中间代码生成_赋值语句_61
  • 编译原理教程_8 静态语义分析和中间代码生成_后缀_62
  • 编译原理教程_8 静态语义分析和中间代码生成_后缀_63
  • 编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_64

2) 布尔表达式翻译模式

编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_65

编译原理教程_8 静态语义分析和中间代码生成_编译原理_66

编译原理教程_8 静态语义分析和中间代码生成_赋值语句_67

编译原理教程_8 静态语义分析和中间代码生成_赋值语句_68

「举例:一遍扫描」
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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 静态语义分析和中间代码生成_赋值语句_69
  • 双分支:编译原理教程_8 静态语义分析和中间代码生成_后缀_70
  • 循环: 编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_71

8.5.1 控制语句的属性文法

1) 基础定义

「属性定义」

  • E.true、E.false 表示 E 为真 / 假时的跳转语句标号
  • S.begin、S.next 表示语句 S 开始执行、执行结束后的地址

「if-then 语句的属性文法」

编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_72

「if-then-else 语句的属性文法」

编译原理教程_8 静态语义分析和中间代码生成_编译原理_73

「while-do 语句的属性文法」

编译原理教程_8 静态语义分析和中间代码生成_编译原理_74

2) 三遍扫描示例

「三遍扫描的属性计算示例」

while a<b do
    if c<d then x:=y+z
    else x:=y-z
123
  • 第一遍:构建语法树
  • 第二遍:自上而下计算继承属性
  • 第三遍:自下而上计算综合属性(code)

编译原理教程_8 静态语义分析和中间代码生成_后缀_75

编译原理教程_8 静态语义分析和中间代码生成_后缀_76

编译原理教程_8 静态语义分析和中间代码生成_后缀_77

即上述 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 控制语句的属性文法

「控制语句的文法」

  • 编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_78
  • 编译原理教程_8 静态语义分析和中间代码生成_编译原理_79
  • 编译原理教程_8 静态语义分析和中间代码生成_编译原理_80
  • 编译原理教程_8 静态语义分析和中间代码生成_赋值语句_81
  • 编译原理教程_8 静态语义分析和中间代码生成_赋值语句_82
  • 编译原理教程_8 静态语义分析和中间代码生成_后缀_83
  • 编译原理教程_8 静态语义分析和中间代码生成_编译原理_84

其中 S 表示语句,L 表示语句表,A 表示赋值语句,E 表示布尔表达式。

1) if 语句的翻译模式

「改写后的产生式」

  • 编译原理教程_8 静态语义分析和中间代码生成_编译原理_85
  • 编译原理教程_8 静态语义分析和中间代码生成_后缀_86
  • 编译原理教程_8 静态语义分析和中间代码生成_后缀_63
  • 编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_88

「翻译模式」

  • S.nextlist 属性:保存 S 翻译生成的代码中需要回填的,要跳转到 S 的后继语句的那些四元式。

编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_89

2) while-do 语句的翻译模式

「改写后的产生式」

  • 编译原理教程_8 静态语义分析和中间代码生成_编译原理_90
  • 编译原理教程_8 静态语义分析和中间代码生成_后缀_63

「翻译模式」

编译原理教程_8 静态语义分析和中间代码生成_编译原理_92

3) 复合语句的翻译模式

「改写后的产生式」

  • 编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_93
  • 编译原理教程_8 静态语义分析和中间代码生成_赋值语句_94
  • 编译原理教程_8 静态语义分析和中间代码生成_后缀_63

「翻译模式」

编译原理教程_8 静态语义分析和中间代码生成_布尔表达式_96

「其它语句的翻译」

编译原理教程_8 静态语义分析和中间代码生成_赋值语句_97

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]需要产生两组计算数组的四元式:

  1. 一组计算varPart,存放结果到临时单元T中
  2. 一组计算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