8.1 代码生成器和目标语言
8.1.1 代码生成器的设计
位置
设计
代码生成器的三个任务
- 指令选择:选择适当的指令实现IR语句
- 寄存器分配和指派:把哪个值放在哪个寄存器中
- 指令排序:按照什么顺序安排指令执行
输入中间表示及符号表信息:
- 四元式、三元式、间接三元式
- 字节代码、堆栈机代码、后缀表示
- 抽象语法树、DAG图、…
输出目标程序
- RISC、CISC
- 可重定向代码、汇编语言
8.1.2 目标语言
使用汇编代码作为目标语言
指令
- 加载:LD dst, addr
(把地址addr中的内容加载到dst所指的位置)
- 保存:ST x, r
(把寄存器r中的内容保存到x中)
- 计算:OP dst, src1, src2
(把src1和src2中的值运算后将结果存放到dst中)
- 无条件跳转:BR L
(控制流转向标号L的指令)
- 条件跳转:Bcond r, L
(对r中的值进行测试,如果为真则转向L。如BLTZ r L是当寄存器r中的值小于0时跳转到L)
寻址模式
- 变量x:指向分配x的内存位置
- a(r):地址是a的左值加上寄存器r中的值
(如LD R1,a(R2) 代表 R1=contents(a+contents(R2)))
- constant(r):寄存器r中内容加上前面的常数即其地址
(如LD R1,100(R2)代表R1=contents(100+contents(R2)))
- *r:寄存器r的内容所表示的位置上存放的内容位置
- *constant(r):寄存器r中内容加上常数所代表的位置上的内容所表示的位置
(如LD R1,*100(R2)代表R1=contents(contents(100+contents(R2))))
- 常数 #constant
程序及指令的代价
假设每个指令有固定的代价,设定为1加上运算分 量寻址模式的代价,例:
LD R0, R1:代价为1(将contents(R1)存入R0)
LD R0, M:代价是2(将内存中M的值存入R0)
LD R1, *100(R2):代价为2(将contents(contents(100+contents(R2)))存入R1)
直接将三地址代码翻译成机器语言容易导致冗余
8.2 一个简单的代码生成器
根据三地址指令序列生成机器指令
- 假设每个三地址指令只有一个对应的机器指令
- 有一组寄存器用于计算基本块内部的值
主要的目标是减少加载 (LD) 和保存 (ST) 指令,即最大限度地利用寄存器
寄存器的使用方法
- 执行运算时,运算分量必须放在寄存器中
- 存放临时变量
- 存放全局的值
- 进行运行时刻管理 (比如栈顶指针)
依次考虑各三地址指令,尽可能把值保留在寄存 器中,以减少寄存器/内存之间的数据交换
为一个三地址指令生成机器指令时
- 只有当运算分量不在寄存器中时,才从内存载入
- 尽量保证只有当寄存器中值不被使用时,才覆盖掉
数据结构
- 寄存器描述符:跟踪各个寄存器都存放了哪些变量的 当前值
- 地址描述符:各个变量的当前值存放在哪些位置(包括内存位置和寄存器) 上
8.2.1 代码生成算法
逐个处理三地址指令
运算语句:x = y + z
getReg(x = y + z)为x, y, z选择寄存器Rx, Ry, Rz
检查Ry的寄存器描述符,如果y不在Ry中则生成指令
- LD Ry, y' // y'表示存放y值的当前位置
- 类似地确定是否生成LD Rz, z'
生成指令ADD Rx, Ry, Rz
复制语句:x = y
getReg(x = y)为x和y选择相同的寄存器
如果y不在Ry中,则生成指令LD Ry, y(将y中的内容加载到寄存器Ry中)
基本块的收尾:
如果变量x活跃,且不在内存中,则生成指令ST x, Rx
8.2.2 函数getReg
根据寄存器描述符和地址描述符等数据流信息,为三地址指令/选择最佳的寄存器得到的机器指令的质量依赖于getReg函数选取寄存器的算法
//太抽象了没看懂