中间表示上的代码优化依赖于具体所使用的中间表示:控制流图(CFG)、控制依赖图(CDG)、静态单赋值形式(SSA)、后续传递风格(CPS)等
共同的特点是需要进行程序分析,优化是全局进行的,而不是局部,通用的模式是:程序分析->程序重写
在这部分中,我们将基于控制流图进行讨论,但类似的技术可以用于其他类型的中间表示。
优化的一般模式
(1)程序分析:控制流分析,数据流分析,依赖分析等,得到被优化程序的静态保守信息(是对动态运行行为的近似)
(2)程序重写:以上一步得到的信息制导对程序的重写
常量传播
先进行到达定义分析,如果这个定义"x = 3"是唯一能够到达使用"a = x"的定义,那么可以进行这个替换。
常量传播算法
const_prop(Prog_t p)
{
// 第一步:程序分析
reaching_definition(p);
// 第二步:程序改写
foreach(stm s in p: y = x1, ..., xn)
foreach(use of xi in s)
if (the reaching def of xi is unique: xi = n)
y = x1, ..., xi - 1, n, xi + 1, ..., xn
}
注:针对程序p做到达定义分析后,就会在每个语句前面都有一组能够到达这个语句的一组对变量的定义,这个信息会指导第二步的程序改写
(注:上图右侧的集合为每个语句前面能够到达这个语句的定义)
拷贝传播
先进行到达定义分析,如果这个定义"x = y"是唯一能够到达使用"a = x"的定义,那么可以进行这个替换。
拷贝传播算法
copy_prop(Prog_t p)
{
// 第一步:程序分析
reaching_definition(p);
// 第二步:程序改写
foreach(stm s in p: y = x1, ..., xn)
foreach(use of xi in s)
if (the reaching def of xi is unique: xi = z)
y = x1, ..., xi - 1, z, xi + 1, ..., xn
}
死代码删除
需要进行活性分析,如果x不是该语句的live_out(什么叫语句的live_out?就是x变量的值,在这个语句在后面会用到),则可以将该语句移除。
死代码删除算法
dead_code(Prog_t p)
// 第一步:程序分析
liveness_analysis(p);
// 第二步:程序改写
foreach (stm s in p: y = ...)
if (y is NOT in live_out[s])
remove(s);
经过活性分析后就会得到每个语句的out[s],是活着出去的那些变量,是在它后面活跃的变量的集合,根据这样的信息再对程序进行改写