编译原理复习-6

纯纯傻逼课

从数学上讲, 为给定源程序生成一个最优的目标程序是不可判定问题. 但还好我们有一系列较为可靠的启发式算法.

代码生成器主要有三个任务: 指令选择, 寄存器分配, 指令排序.

Chapter-8: 代码生成和优化

8.1 基本块的划分

  • 基本块是程序中最大限度顺序执行的语句序列,其中只有一个入口和出口,入口是其第一个语句,出口是其最后一个语句
    • 基本块的入口语句可能是
      • 程序的第一个语句
      • 跳转的目标语句
      • 条件跳转的下一条语句
    • 基本块的结束语句可能是
      • 停机语句
      • 跳转语句
      • 跳转目标语句的前一个语句(词法序)

构造方法:

  1. 确定首指令:
    • 中间代码的第一个三地址指令是一个首指令
    • 任意一个条件或无条件转移指令的目标指令是一个首指令
    • 紧跟在一个条件或无条件转移指令之后的指令是一个首指令
  2. 每个首指令对应的基本块包括了从它自己开始,直到下一个首指令(不含)或者中间程序的结尾指令之间的所有指令

过程调用语句作为一个新的基本块的开始,甚至独立成为一个基本块

8.2 三地址语句中名字的使用(use)定义

  • 假定三地址语句i把a的值赋给x,如果语句j用x作为运算对象,并且控制从i流到j,这条路径中没有x的其它赋值,则称j引用x在i定的值
  • 此时,称x在语句i处活跃(live)

确定算法:

  • 输入:一个三地址语句的基本块B,假设在基本块B开始时,所有的非临时变量都是活跃的
  • 输出:对于每一个语句i:x = y op z,将x、y及z的活跃性信息及后续使用信息关联到i
  • 方法:从基本块B的最后一个语句开始,反向扫描到B的开始处。对每一个三地址语句 i:x := y op z,依次执行下述步骤:
    1. 把当前符号表中x、y和z的后续使用信息和活跃信息附加到语句i上;(若x不活跃,则这个语句可以删掉)
    2. 在符号表中设置x为“无后续使用”和“不活跃”;
    3. 把符号表中y和z的后续使用信息均置为i,活跃信息均置为“活跃”。 

目前还不知道有什么用???????

8.3 基本块内优化

采用DAG表示

8.3.1 消除局部公共子表达式

  • 方法:检测公共子表达式的“值编码”方法
  • 当一个新的结点M将被加入到DAG中时
    • 检查是否存在一个结点N,它和M具有同样的运算符和子结点,且子结点顺序相同
    • 若存在,则N计算的值和M计算的值是一样的,可以用N替换M

如果有不活跃的公共表达式, 则可以删除一个. 如果都活跃, 则必须要有赋值语句去复制.

这种对不活跃的考察, 就是删除死代码

8.3.2 代数恒等式的使用

  • 局部强度削弱: 如 x^2 -> x*x
  • 常量合并
  • 交换律结合律检查

8.3.3 数组引用的表示

对数组而言, 情况较为不同, 比如下列式子:

x = a[ i ];
a[ j ] = y;
z = a[ i ];

其中a[i]并不是CSE, 因为i=j有可能成立.

在DAG中, 表示数组访问如下:

  • 取数组元素的右值(如x = a[ i ]),用新创建的运算符为=[ ]的结点表示,其左右子结点分别代表数组初始值(本例中为a0)和下标i,变量x是该结点标号之一
  • 取数组元素的左值(如a[ j ] = y),用新创建的运算符为[ ]=的结点表示,这个结点的三个子结点分别表示a0 、j和y,没有变量用这个结点标号
    • 左值结点的创建杀死了所有当前已建立的,其值依赖于a0的结点
    • 被杀死的结点都不再有标号,且不能作为CSE

8.4 控制节点

控制结点(必经结点)描述了流图上如下关系

  • 控制节点
    • n是m的控制节点(n dominates m)
    • 当且仅当, 从root到m的所有路径都经过n(想到m必须先到n)
    • 控制关系构成偏序集
  • 后控制节点
    • n是m的后控制节点(n post-dominates m)
    • 当且仅当, 从m到exit的所有路径都经过n(m想离开必须经过n)
    • 控制关系构成偏序集

到达-定值的迭代算法

变量x的定值:是一个语句,它(可能)赋值给x

  • 无二义的定值:语句真正对x定一个值
  • 二义的定值:
    • x作为形式参数,或由于别名的关系
    • 通过引用x的指针对x赋值 变量a的定值d到达一点p:如果有路径从紧跟d的点到达p,并且在这条路径上d没有被注销 定值的注销:如果在这条路径上有对变量a的其它定值,则称前面的定值被注销
  • 只有控制结点的无二义的定值才能注销其它的定值
  • 到达-定值可能是不精确的

几个集合:

  • gen[B]:基本块B中的定值d到达B的结束点,则d∈gen[B]
  • kill[B]:程序中不能到达B的结束点的定值d,则d∈kill[B]
  • in[B]:到达B的开始点的定值集合
  • out[B]:到达B的结束点的定值集合

例子如下:

Flow InOut

可用表达式

  • 表达式x+y在点p可用:如果从初始结点到p的每条路径上(不必是无环)都计算x+y,并且在最后一个这样的计算和p之间没有对x或y的赋值
  • 基本块注销表达式x+y:如果它对x或y赋值(或可能赋值),并且随后没有重新计算x+y
  • 基本块产生表达式x+y:如果它计算x+y,并且随后没有重新对x或y定义

对每个基本块有:

  • in[B]:块B开始点的可用表达式集合
  • out[B]:块B结束点的可用表达式集合
  • e_gen[B]:块B生成的可用表达式集合
  • e_kill[B]:U中被块B注销的可用表达式集合

活跃变量分析

活跃变量:反向计算流信息

  • 如果对于变量x和点p,x的值在由p点开始的路径上是否引用,如果引用,则称x在p点活跃
  • 否则称x在p点死亡
  • 如果寄存器中的值在基本块结束死亡,则不用写回内存,这个寄存器也可以优先被分配它用 几个集合
  • in[B]:块B开始点的活跃变量集合
  • out[B]:块B结束点的活跃变量集合
  • def[B]:块B中无二义定值且在该定值前没有引用的变量集
  • use[B]:块B中可能引用且在该引用前没有定值的变量集

公共子表达式