待续~
8.2优化背景
8.2.1例子
Operator Strength Reduction:OSR 运算符强度削减。如Sj = j + h*w,0<j<10,当通过代码上下文分析知道h*w是常量,那么原表达式可退化为只有加号运算的Sj = S(j-1)+1.
循环展开:for(i =1...10)sum+=i;展开为sum=1+2+3...+10。优点:避免来回执行的开销,减小Load/Store指令数。缺点:代码太长耗内存,同时有可能涉及到寄存器不够用。
8.2.2 优化的考虑因素:安全性(优化对程序结果没有影响),可获利性(执行时间,代码长度,CPU利用率),风险(优化的最坏结果)
8.2.3优化的时机
1)减少抽象的开销:通过分析和变换来减小冗余表达式、常量传播等开销。
2)利用特例:利用上下文优化,如C++编译器有时可以确定虚函数的调用总是调用同一个函数,此时可以减小每次都调用的代价。
3)将代码匹配到系统资源:结合目标机的特性来做特定优化。
8.3 优化的范围:优化器的代码区域。四种不同的范围:局部的、区域性的、全局的、整个程序。
8.4 局部优化:范围限于单个基本程序块。局部值编号要求程序块中的所有操作是从头到尾的直线式的执行顺序,树高平衡算法要求基本程序块只有一个出口。
1)LVN:local value numbering 局部值优化【1】,也有带有下标的扩展LVN,用于查找程序中的冗余表达式,通过重用计算过的表达式值来避免多次重复计算操作。
2)THB:tree height balancing 树高平衡,通过重新组织表达式树来发现更多指令层次的并行性。
8.5区域优化:一个基本程序块的代码区域可能为改进另一个程序块中的代码提供了上下文环境。从局部优化到区域优化,复杂之处在于需要处理控制流的多样性。常用技能:分析区域内的扩展基本程序块(Extended Basic Block:EBB)、控制流图CFG中的支配关系或强联通分量。
EBB:集合{A1,A2,...,Aj}(j>=1)是EBB,当它满足:A1具有多个前驱,其余Aj只有一个前驱。
支配者:在CFG中,如从根节点到节点Y的所有路径中都含有节点X,则成X支配Y。
1)SVN:Superlocal Value Numbering 超局部值编号。优化范围从基本程序块到扩展基本程序块(EBB)。算法涉及了二叉树、深度遍历。
SVN通过多个基本程序块的上下文环境能够发现单个基本程序中LVN发现的冗余之外的冗余信息。
问题1:令有两个EBB,EBB1: {B0,B1},EBB2:{B0,B2},其中B0,B1,B2都含有一个公共的表达式X,由LVN特性,知道表达式X的值编号是存在B0的。那么,当B1修改B0值编号表中的表达式X的值编号并退出程序作用域,程序进入EBB2作用域后就发现表达式X的值编号被B1污染了,静态单赋值形式SSA解决了该问题。
问题2:SVN可以发现LVN难以返现的冗余表达式,但{B1,B2}合并后才存在冗余,而单个B1,B2都可以分别构成EBB,那么B1,B2中的冗余就不能通过SVN发现,待解决。
2)循环展开。优点:减少内存访问。缺点:代码长度增加,寄存器不够用,转而访问内存Load指令,坐等爆栈。
8.6全局优化
1)全局数据流分析:利用活动信息查找未初始化变量,是一种编译时技术。涉及概念:Live Variable活动变量,它在树高平衡、静态单赋值、寄存器分配得到应用。
2)全局代码置放,根据运行编译后代码收集到的剖析信息重新安排可执行代码的布局。
相比之下,数据流分析较为保守,运行时剖析较为激进。前者考虑了所有可能性,后者假定代码未来的运行将与所剖析的运行共享某些运行时特征。
8.6.1利用活动信息查找未初始化变量。
相关概念:
LiveOut(b):从程序块b退出时所有活动(available)的变量,即b后继程序块入口处的活动(available)的变量集合。
VarKill(b):b程序块块中赋值表达式的等号左边变量,即改变量被重新定义了一次,所有不属于LiveOut(b)。
UEVar(b):b中向上展现的变量,在b中重新定义之前就开始使用的变量,即b程序块中赋值表达式的等号右边变量,属于b的前驱程序块的LiveOut集合。
8.6.2全局代码置放
代码:https://github.com/storyeah/Engineer-a-Compiler-2th
参考:
[1] LVN:http://wanghuanming.com/2015/05/implementation%20of%20lvn/