编译原理课程笔记 - 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) => ROP Ri, (Rj)-Ri OP (Rj) => RiOP 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  | 【解答】该基本块的目标代码如下(指令后面为相应的注释):  |