目标代码生成
- 目标代码生成
- 11.1 基本问题
- 11.2 目标机器模型
- 11.3 一个简单的代码生成器
- 11.3.1 待用信息
- 11.3.2 寄存器描述何地址描述
- 寄存器信息
- 变量信息
- 11.3.3 代码生成算法
- 各中间代码对应的目标代码
- 11.4 寄存器分配
- 执行代价
- 11.5 DAG 的目标代码优化
- DAG节点计算顺序
- 11.6 窥孔优化
目标代码生成
以中间代码为输入,产生等价的目标程序为输出;目标代码三种形式:
- 能立即执行的机器语言代码,地址已定位
- 待装配的机器语言模块
- 汇编语言代码
着重考虑的问题:
- 如何使生成的目标代码较短
- 如何充分利用寄存器,减少目标代码访问存储单元的次数
11.1 基本问题
代码生成器的输入;
- 中间语言: 三地址码
- 符号表
目标程序: 汇编语言
指令选择
寄存器分配
计算顺序的选择
11.2 目标机器模型
11.3 一个简单的代码生成器
11.3.1 待用信息
一个基本块范围内考虑如何充分利用寄存器
- 当生成计算某变量值的目标代码时,尽可能的将该变量的值保存在寄存器中,直到该寄存器必须用来存放别的变量值,或达到基本块的出口;
- 后续的目标代码尽可能的引用保存在寄存器的值,而不访问主存。
具体做法
- 把还要引用的变量值金科嗯那个保存在寄存器中
- 把基本块内不再被引用的变量所占用的寄存器尽早释放
活跃信息: 变量是否还会在基本块内被引用
待用信息: 在那些中代码中被引用
当变量作为左值的时候,由于会对变量进行赋值,不用向内存中再去取值,变量可以使不活跃的,在寄存器中被任意丢弃,不会对性能产生影响。
11.3.2 寄存器描述何地址描述
寄存器信息
寄存器描述数组 Rvalue 记录了寄存器分配给某几个变量,几个变量。 执行A=B,寄存器R可能分配给A、B等多个变量。
变量信息
变量地址描述数组: Avalue ,记录当前变量的值存放的位置,寄存器中,主存中,还是既在寄存器中又在主存中。
A = A+ B,执行完后,变量值只在寄存器中,还没来的即写回主存。
11.3.3 代码生成算法
getR(i) 过程不考
各中间代码对应的目标代码
在基本块内,活跃变量在最后一次赋值后才存到内存中。
使用简化的方法: 算一次结果就存到内存中
11.4 寄存器分配
在循环中,寄存器不是平均分配,而是从可用寄存器中分出几个,固定分配给几个变量单独使用
执行代价
每条指令的执行代价 = 每条指令访问主存单元的次数 + 1
计算如果将某个固定的寄存器分配给该变量,执行代价能节省多少。
根据计算结果,把可用的几个寄存器,固定分配给节省执行代价多的几个变量
- 当变量被定值时(赋值),值才被放到寄存器中。定值前,引用一次,就要访问主存。因此,固定分配寄存器后,该变量被定值前,每引用一次,就减少一次主存访问,执行代价减少1
- 在基本块中,被定值,且基本块出口之后是活跃的,那么出基本块九阳储存到主存中。 固定分配后,出基本块时,无需转存,代价节省2
11.5 DAG 的目标代码优化
计算A = B op C,如果计算完B紧接着计算A,就可以及时利用寄存器中的信息
- 考虑先算DAG的右子树,再算左子树,最后算父节点
DAG节点计算顺序
设DAG有N个内部节点,线性表T[N] 记录计算顺序,初始为空值。
- 最后T[1],T[2], …,T[N] 即为节点计算顺序
11.6 窥孔优化
通过考察一小段目标指令(称为窥孔),把这些指令替换为更短和更快的一段指令,从而提高目标代码质量