编译原理课程笔记 - Chapter 4 目标代码生成
#目标代码生成
#任务
1 | 中间代码 --> 代码生成器 --> 目标程序 |
#目标代码的三种形式
- 能立即执行的机器码(所有地址已经定位)
- 待装配的机器语言模块(连接装入后即可执行)
- 汇编语言代码(经汇编程序汇编后即可执行)
此处选择汇编语言
#目标机器模型
此处假设目标计算机:
- 具有多个通用寄存器, 可以用来运算, 也可以用来取地址
- 支持四种指令形式
- 直接地址型
OP R, M
-R OP M => R
- 寄存器型
OP Ri, Rj
-Ri OP Rj => Ri
- 变址型
OP Ri, c(Rj)
-Ri OP (Rj+c) => Ri
- 间接型
OP Ri, (M)
-R OP (M) => R
OP Ri, (Rj)
-Ri OP (Rj) => Ri
OP Ri, (c(Rj))
-Ri OP ((Rj+c)) => Ri
- 直接地址型
OP可以是ADD,SUB,MUL,DIV等
#简单代码生成器
思路: 代码生成器在生成每一条指令时, 必须要知道每个寄存器中存储的是什么, 以及每个变量存储到什么位置
方法:
- 引入待用信息
- 寄存器描述数组
- 变量地址描述数组
#待用信息
待用信息
- 在基本块中, 四元式i对A定值, 四元式j对A取值, 且i,j之间无再对A定值, 则称j是i的变量A的待用信息
活跃信息
- 基本块中的一个名字在某个给定点之后仍被引用, 则称该名字在给定点是活跃的
修改符号表
- 记录待用信息和活跃信息
- 表示: (
待用信息[i/^]
,活跃信息[y/^]
)
变量名 | 待用/活跃信息栏 |
---|---|
A | (…, …) |
B | (…, …) |
… | (…, …) |
计算待用和活跃信息
- 开始时, 所有变量均为
非待用
, 根据基本块之后是否活跃填写活跃
或非活跃
- 逆序遍历每个四元式
i
- 把
A
的引用信息附加到四元式i
左值上 A
的引用信息附加(^,^)
- 把
B
,C
的引用信息附加到四元式i
上 B
,C
的引用信息附加(^,^)
- 把
例:
1 | T1:=B-C |
序号 | 四元式 | 左值 | 左操作数 | 右操作数 |
---|---|---|---|---|
(6) | W:=T2/T5 | (^,y) | (,) | (,) |
(5) | T5:=T3*T4 | (6,y) | (,) | (,) |
(4) | T4:=E-F | (3,y) | (,) | (,) |
(3) | T3:=D+1 | (3,y) | (,) | (,) |
(2) | T2:=A*T1 | (6,y) | (,) | (,) |
(1) | T1:=B-C | (2,y) | (,) | (,) |
变量名 | 待用/活跃信息栏 |
---|---|
T5 | (,)->(6,y)->(,) |
T4 | (,)->(3,y)->(,) |
T3 | (,)->(3,y)->(,) |
T2 | (,)->(6,y)->(,) |
T1 | (,)->(2,y)->(,) |
W | (,y)->(,^) |
F | (,)->(4,y) |
E | (,)->(4,y) |
D | (,)->(3,y) |
C | (,)->(1,y) |
B | (,)->(1,y) |
A | (,)->(2,y) |
#寄存器分配算法
寄存器描述和地址描述
- 寄存器描述数组
RVALUE
- 记录寄存器内存储的变量, 可以是多个
RVALUE[R1]={A,B}
- 变量地址描述数组
AVALUE
- 记录变量存储的位置(寄存器/内存)
AVALUE[A]={R1,R2,A}
代码生成算法
- 对于四元式
A:=B op C
, 依次执行
- 调用
GETREG(i: A:=B op C)
获取一个寄存器R
- 查询
AVALUE[B]
和AVALUE[C]
, 如果在寄存器中, 假设是B'
和C'
- 如果
B'≠R
, 先把B
加载到R
中, 生成代码:LD R,B'
- 进行运算, 生成代码:
op R,C'
- 更新描述信息
- 如果
B'
或C'
是R
, 就要把AVALUE
中的记录删除 - 设置
AVALUE[A]={R}, RVALUE[R]={A}
- 如果
- 如果
B
或C
仍存储在某个寄存器中, 但后续不再被引用且不再活跃, 就从AVALUE
和RVALUE
中删除B
和C
的记录
寄存器分配原则
- 尽可能用B独占的寄存器
- 尽可能用空闲的寄存器
- 抢占用非空闲寄存器
Ri
的值也存储在内存中Ri
最远才会用到
寄存器分配算法GETREG(i: A:=B op C)
如果B独占某个寄存器
- 如果
AB
是同一个标识符(即修改B的指令B:=B op C
)- 返回
B
所在的寄存器
- 返回
- 如果
B
在后面不会再引用(非待用, 非活跃)- 返回
B
所在的寄存器
- 返回
- 如果
如果有空闲寄存器, 返回空闲寄存器
抢占一个寄存器
Ri
,- 对于
Ri
中存储的每一个变量M
, 如果存储的不是A
, 或者存储的是A == C != B
且Ri
中不存储B
.(M==A && M==C && M!=B && B not in RVALUE[Ri]
)- 如果
M
仅存储在Ri
, 则要存到内存中:ST Ri, M
- 如果
M == B
, 或者M == C && B in RVALUE[Ri]
中, 则令AVALUE[M]={M,R}
, 否则令AVALUE[M]={M}
- 删除
M
在Ri
中的记录,RVALUE[Ri].reLD e(M)
- 返回
R
- 如果
- 对于
例:
寄存器: R0,R1;基本块出口活跃变量: W
1 | T1:=B-C |
答案:
1 | 【解答】该基本块的目标代码如下(指令后面为相应的注释): |