⭐⭐⭐⭐⭐⭐
Github主页https://github.com/A-BigTree
项目链接https://github.com/A-BigTree/college_assignment
⭐⭐⭐⭐⭐⭐
编译器的代码优化和代码生成步骤通常比称为编译器的后端(back end),代码优化器和代码生成器的位置如下图:
Places for Potential Improvements
Source code
Intermediate code
Target code
Organization of the code Optimizer
Basic blocks and Flow graph
Basic blocks
把中间代码划分为基本块(basic block)。每个基本块是满足下列条件的最大的连续三地址码指令序列:
Partition into Basic Blocks
示例:
源码:
begin
read X;
read Y;
while (X mod Y<>0) do
begin
T:=X mod Y;
X:=Y;
Y:=T
end;
write Y
end
三地址码:
基本块划分如下:
Next-Use Information
如果一个变量的值当前存放在一个寄存器中,且之后一直不会被使用,那么这个寄存器就可以被分派给另一个变量。
The Use of a Name
假设三地址语句i给x赋了一个值。如果语句j的一个运算分量为x,并且从语句i开始可以通过未对x进行的赋值的路径到达语句j,那么我们说语句j使用了在语句i处计算得到的x的值。
我们可以进一步说x在语句i处为活跃变量(Live variable)。
Flow graph
当将一个中间代码程序划分成为基本块后,我们用一个流图来表示它们之间的控制流。流图的结点(nodes)就是这些基本块。
从基本块B到基本块C之间有一条边(edge)当且仅当基本块C的第一个指令可能紧跟在B的最后一个指令之后执行。存在这样一条边的原因有两种:
我们通常说B是C的前驱(predecessor),而C是B的一个后继(successor);
示例:
源码:
for i from 1 to 10 do
for j from 1 to 10 do
a[i,j]=0.0;
for i from 1 to 10 do
a[i,i]=1.0;
中间代码如下:
流图如下:
Loops
因为事实上每个程序会花很多时间执行循环,所以对一个编译器来说,为循环生成优良的代码就变得非常重要。很多代码转换依赖于对流图中“循环”的识别。如果下列条件成立,我们就说流图中的一个结点集合L是一个循环。
上面示例的流图中有三个循环:
Optimization of Basic Blocks
DAG(有向无环图)
我们按照如下方式为一个基本块构造DAG:
对于基本块:
a = b + c;
b = a - d;
c = b + c;
d = a - d;
该基本块的DAG如下图:
Common subexpression elimination
如果表达式E在某次出现之前已经被计算过,并且E中的变量的值从那次计算之后就一直没被改变,那么E的该次计算就称为一个公共子表达式(common subexpression)。如果将E的上一次计算结果赋予变量x,且x的值在中间没有被改变,那么我们就可以使用前面计算得到的值,从而避免重新计算E。
快速排序的代码片断的流图:
局部公共子表达式消除前后:
经过**公共子表达式消除**之后的 B 5 B_5 B5和 B 6 B_6 B6:
Copy propagation
考虑形如u=v
的赋值表达式,这种表达式被称为复制语句(copy statement),或者简称复制。因为常用的公共子表达式消除算法会引入这些复制语句,其他一些优化算法也会引起这样的语句。
在公共子表达式消除过程中引入的复制语句:
进行复制传播转换后的基本块 B 5 B_5 B5:
Dead Code Elimination
如果一个变量在某一程序点上的值可能会在以后被使用,那么我们就说这个变量在该点上活跃(live)。否则,它在该点上就是死的(dead)。与此相关的一个想法就是死(或者说无用)代码。所谓死代码就是其计算结果永远不会被使用的语句。程序员不大可能有意引入死代码,死代码多半是因为前面执行过的某些转换而造成的。
先进行复制传播再进行死代码消除,基本块 B 5 B_5 B5的进一步改进:
Data-flow analysis
上面介绍的所有优化都依赖于数据流分析。“数据流分析”指的是一组用来获取有关数据如何沿着程序执行路径流动的相关信息的技术。比如,实现全局公共子表达式消除的方法之一要求我们确定在程序的任何可能执行的路径上,两个在文字上相同的表达式是否会给出相同的值。另一个例子是,如果某一个赋值语句的结果在任何后续的执行路径中都没有被使用,那么我们可以把这个赋值语句当作死代码消除。
The Data-Flow Abstraction
流图会给出可能执行路径的信息:
这样,我们可以把从点 p 1 p_1 p1到点 p n p_n pn的一个执行路径(excution path,简称路径)定义为满足下列条件的点的序列 p 1 , p 2 , … , p n p_1,p_2,\dots,p_n p1,p2,…,pn:对于每个 i = 1 , 2 , … , n − 1 i=1,2,\dots,n-1 i=1,2,…,n−1:
下图说明数据流抽象的例子程序:
为了帮助用户调试他们的程序,我们可能希望找到在某个程序点上一个变量可能有哪些值,以及这些值可能在哪里定值。比如,我们可能对程序点(5)上的所有程序状态进行如下总结:a的值总是{1, 243}中的一个,而它有 { d 1 , d 2 } \{d_1,d_2\} {d1,d2}中的一个定值。可能沿着某些路径到达某个程序点的定值称为到达定值(reaching definition)。
⭐⭐⭐⭐⭐⭐
Github主页https://github.com/A-BigTree
项目链接https://github.com/A-BigTree/college_assignment
⭐⭐⭐⭐⭐⭐