U10 中间代码优化

一、概述

1、概念

指编译程序为了生成高质量的目标程序而做的各种加工和处理。

2、分类

1)分类1

  1. 与机器无关的优化技术:即与目标机无关的优化,通常是在中间代码上进行的优化。
    如:数据流分析 ,常量传播,公共子表达式删除,死代码删除,循环交换,代码内联等等
  2. 与机器相关的优化技术
    充分利用系统资源,(指令系统,寄存器资源)。
    特点:仅在特定体系结构下有效。

2)分类2

  1. 局部优化技术
    指在基本块内进行的优化
    例如,局部公共子表达式删除
  2. 全局优化技术
    函数/过程内进行的优化
    – 跨越基本块
    – 例如,全局数据流分析、循环优化
  3. 跨函数(过程间)优化技术
    – 整个程序范围内的优化
    – 例如,函数内联、函数间常量传播、跨函数别名分析,逃逸分析,死调用/死函数删除,全局变量优化等。

二、基本快、流图

1、基本块定义

  1. 基本块中的代码是连续的语句序列
  2. 程序的执行(控制流)只能从基本块的第一条语句进入
  3. 程序的执行只能从基本块的最后一条语句离开
  4. 符合上述条件的最大块

总结:有goto的和goto去的地方需要划分
在这里插入图片描述

2、流图

流图是一种有向图 G(V,E)
图的节点V: <基本块>组成的集合
图中B1-B2的边:B2的执行紧跟在B1之后

编译器按照“程序—流图—基本块—中间代码”选择合理的数据结构组织和管理中间代码。

三、局部优化(基本块内的优化)

1、利用代数性质(代数变换)

编译时完成常量表达式的计算,整数类型与实型的转换。5+6变成11

2、运算强度削弱

用一种需要较少执行时间的运算代替另一种运算,以减少运行时的运算强度时、空开销) ——比如乘法变移位。

3、常数合并和传播

如 x:=y 这样的赋值语句称为复写语句。由于 x 和 y 值相同,所以当满足一定条件时,在该赋值语句下面出现的 x 可用 y 来代替。

4、删除冗余代码

冗余代码就是毫无实际意义的代码,又称死代码(dead code)或无用代码(useless code)

5、窥孔优化(peep-hole)

窥孔优化关注在目标指令的一个较短的序列上,通常称其为“窥孔”。
通过删除其中的冗余代码,或者用更高效简洁的新代码来替代其中的部分代码,达到提升目标代码质量的目的。

6、消除公共子表达式

1)基本块DAG图的定义

  1. 图的叶节点由变量名或常量所标记。
    对于那些在基本块内先引用再赋值的变量,可以采用变量名加下标0的方式命名其初值。
  2. 图的中间节点由中间代码的操作符所标记
    代表着基本块中一条或多条中间代码。
  3. 基本块中变量的最终计算结果,都对应着图中的一个节点;
    具有初值的变量,其初值和最终值可以分别对应不同的节点。

通过DAG图可消除公共子表达式,得到更简洁的优化代码
此时需要两个算法:DAG图的生成算法,从DAG图导出代码的算法

2)构建DAG图的算法–消除公共子表达式

• 输入:基本块内的中间代码序列
• 输出:完成局部公共子表达式删除后的DAG图

  1. 图/节点表初始化:节点表,记录变量名和常量值,及当前对应的DAG图中的节点序号。初始状态为空。
  2. 顺序遍历每一条中间代码,按照以下规则建立DAG图
    形如z = x op y:在节点表中查找x (对y也做处理,对应j)如果找到,记录x 所对应的节点号 i。
    找到:记录x 所对应的节点号 i。
    未找到:在DAG图中新建叶节点(编号i),标记为x如果x为变量名,该标记更改为x0在节点表中增加新的表项(x, i)。
    z = x op y:寻找/创建左操作数为i、右操作数为j的DAG节点,其标记为op,假定其节点编号为k,将节点i和j分别与k相连,作为其左子节点和右子节点。
    z = x op y:在节点表中寻找z。
    如果找到,将z所对应的节点号更改为k;
    如果未找到,在节点表中新增表项(z, k)
    在这里插入图片描述

3)从DAG导出中间代码的启发式算法

  1. 初始化一个放置DAG图中间结点的队列。
  2. 如果DAG图中还有中间节点未进入队列,则执行步骤3,否则执行步骤5
  3. 选取一个尚未进入队列,但其所有父节点均已进入队列的中间节点n,将
    其加入队列;或选取没有父节点的中间节点,将其加入队列
  4. 如果n的最左子节点符合步骤3的条件,将其加入队列;并沿着当前节点
    的最左边,循环访问其最左子节点,最左子节点的最左子节点等,将符合
    步骤3条件的中间节点依次加入队列;如果出现不符合步骤3条件的最左子
    节点,执行步骤2
  5. 将中间节点队列逆序输出,便得到中间节点的计算顺序,将其整理成中间
    代码序。
    即按照前序遍历输出中间节点
    在这里插入图片描述
    在这里插入图片描述

四、局部优化详解

1、公共子表达式删除

利用数据流自上而下分析:等号右边在数据流中有等价的可替代
在这里插入图片描述
不断循环这个过程直到没有替代的为止。

2、死代码删除

变量的活性,等号右边表示需要用的,等号左边表示更新了,不能用了。
自下而上分析
在这里插入图片描述
不断循环这个过程直到没有删除的为止。

五、数据流分析

1、概念

用于获取数据在程序执行路径中如何流动的有关信息。
是局部优化、全局优化的基础
上述过程就是所谓的数据流分析

2、公式

o u t [ S ] = g e n [ S ]   U   ( i n [ S ] – k i l l [ S ] ) out[S]=gen[S]\ U\ (in[S]–kill[S]) out[S]=gen[S] U (in[S]kill[S])
– S 代表某条语句(基本块,基本块集合,或语句集合) – S 代表某条语句(基本块,基本块集合,或语句集合) S代表某条语句(基本块,基本块集合,或语句集合)
– o u t [ S ] 代表在该语句末尾得到的数据流信息 – out[S]代表在该语句末尾得到的数据流信息 out[S]代表在该语句末尾得到的数据流信息
– g e n [ S ] 代表该语句本身产生的数据流信息 – gen[S]代表该语句本身产生的数据流信息 gen[S]代表该语句本身产生的数据流信息
– i n [ S ] 代表进入该语句时的数据流信息 – in[S]代表进入该语句时的数据流信息 in[S]代表进入该语句时的数据流信息
– k i l l [ S ] 代表该语句注销的数据流信息 – kill[S]代表该语句注销的数据流信息 kill[S]代表该语句注销的数据流信息
根据所需要的目的不同,这些特征的值都是不同的
比如对于公共子表达式删除
在这里插入图片描述

六、全局优化

全局优化处理的是基本块之间的优化受到控制流的影响

1、作用

活跃变量信息对于寄存器分配,不论是全局寄存器分配还是临时寄存器分配都有重要意义

  1. 如果拥有寄存器的变量x在p点开始的任何路径上不再活跃,可以释放寄存器。
  2. 如果两个变量的活跃范围不重合,则可以共享同一个寄存器

2、活跃变量定义-以消除死代码为例

在这里插入图片描述
达到定义分析是沿着流图路径的,有的数据流分析是 方向计算的
因此公式是 i n [ S ] = g e n [ S ]   U   ( o u t [ S ] – k i l l [ S ] ) in[S]=gen[S]\ U\ (out[S]–kill[S]) in[S]=gen[S] U (out[S]kill[S]),根据消除死代码的特殊性,可以写成 i n [ S ] = u s e [ S ]   U   ( o u t [ S ] – d e f [ S ] ) in[S]=use[S]\ U\ (out[S]–def[S]) in[S]=use[S] U (out[S]def[S])
• 可达定义分析的数据流:沿流图中的控制流方向计算
• 活跃变量分析的数据流:沿流图中控制流的反方向计算

PS:若是一个变量即被定义又被使用,应该以被使用为主,因此放在use中

3、分析

输入:程序流图,且基本块的use集和def集已计算完毕
输出:每个基本块入口和出口处的 i n [ B ] in[B] in[B] o u t [ B ] out[B] out[B]

如果计算得到in[B]与此前计算得出的in[B]不同,则循环执行,直到所有基本块的in[B]集合不再产生变化为止。
在这里插入图片描述
根据in和out的活跃变量即可删除死代码

4、可达定义分析

1)任务

– 在程序的某个静态点p(例如某个代码、基本块)执行前后
– 某个变量可能出现的值都是在哪里被定义的
在这里插入图片描述

2)分析

在这里插入图片描述
比如同一个语句块中一个变量定义多次,则后面会将前面覆盖掉
在这里插入图片描述

其中:
在这里插入图片描述

举例:
在这里插入图片描述

5、define-use链

1)概念

变量的定义-使用链(Define-Use链),变量的某一定义点,以及所有可能使用该定义点所定义变量值的使用点所组成的一个链。
在这里插入图片描述
因此可以赋值成:
在这里插入图片描述

七、静态单赋值SSA

1、前置知识-支配属性

1)概念

节点D支配节点N:如果从开始到N的每条路径都通过D
在这里插入图片描述
在这里插入图片描述

2)计算

利用数据流计算
在这里插入图片描述

3)支配边界

X的支配边界 D F ( X ) DF(X) DF(X) ={Y | 存在一个从X->Y的路径,且Y是这个路径上第一个不被X严格支配的节点。
在这里插入图片描述

2、概念

SSA:Static Single Assignment 是一种基于三元式或四元式的中间语言的表示形式,其目的是支撑静态优化。

每个变量只有一个定义,每一次使用变量的时候清楚的知道变量是在哪里定义的。

不相关的变量被改成了不同的名字

Φ函数

(phi)通常放置在基本块的开始,根据控制流选择变量的值
a 1 : = Φ ( a 1 , a 2 , … , a k ) a1:= Φ(a1, a2, …, ak) a1:=Φ(a1,a2,,ak):表示当前基本块有 k 个前驱基本
在这里插入图片描述
通常在优化处理的开始,将IR转化为SSA形式

3、SSA的支配属性

在这里插入图片描述

4、消除SSA

在这里插入图片描述

八、循环优化

1、发现循环

找回边, n − > d n->d n>d,且不经过d能够到达n的所有节点(含n)
在这里插入图片描述

2、优化点

1)循环不变式(Loop invariant)代码外提(code motion)

在这里插入图片描述

2)循环展开

将构成循环体的代码(不包括控制循环的测试和转移部分),重复产生许多次(这可在编译时确定),而不仅仅是一次,以空间换时间。
在这里插入图片描述

3)其他方法

  1. 把多重嵌套的循环变成单层循环
  2. 循环拆分(Loop Distribution)
  3. 循环融合(Loop Fusion )
  4. 循环交换(Loop Interchange)
  5. 循环并行(Loop Parallelization)

九、过程间分析与跨函数优化

1、内联:in_line 展开

把过程(或函数)调用改为in_line展开可节省许多处理过程(函数)调用所花费的开销。
在这里插入图片描述

十、总结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值