适用于电子科技大学编译原理期末考试复习。
1. 基本块
基本块(Basic Block)是程序中一段连续的指令序列,它具有以下特点:
只有一个入口:程序只能从基本块的第一条指令进入。
只有一个出口:程序只能从基本块的最后一条指令离开。
顺序执行:基本块内的指令按照顺序依次执行,不会出现分支或跳转。
基本块在编译过程中是一个重要的概念,它是编译器进行代码优化和分析的基本单位。通过将程序划分为基本块,编译器可以更好地理解程序的结构和逻辑,从而进行各种优化操作,例如循环优化、代码重排和冗余代码消除等。
基本块的划分方法
基本块的划分通常基于程序的控制流图(Control Flow Graph, CFG)。以下是划分基本块的一般步骤:
-
确定入口语句:
-
程序的第一条语句。
-
转移语句(如条件跳转或无条件跳转)的目标语句。
-
紧跟在条件转移语句后面的语句。
扫描二维码关注公众号,回复: 17570238 查看本文章
-
-
划分基本块:
-
每个入口语句对应一个基本块。
-
基本块由该入口语句到下一入口语句(不含下一入口语句),或到一转移语句(包括该转移语句),或到一停语句(包括该停语句)之间的代码序列组成。
-
-
删除不可达语句:凡未被纳入某一基本块的语句,都是程序中控制流程无法到达的语句,可以删除。
2. 流图
流图的结点是基本块。从基本块B到基本块C之间有一条边当且仅当基本块C的第一个指令可能紧跟在B的最后一条指令之后执行。
有两种方式可以确认这样的边:
有一个从B的结尾跳转到C的开头的条件或无条件跳转语句。
按照原来的三地址语句序列中的顺序,C紧跟在之B后,且B的结尾不存在无条件跳转语句。
3. 局部优化
局部优化(Local Optimization)是指在编译过程中,对程序的基本块进行优化的过程。
局部优化的方法:
常量传播:如果一个变量在基本块内被赋值为常量,那么在后续使用该变量的地方可以直接使用该常量。
删除公共子表达式:如果基本块内有多个相同的表达式,可以只计算一次,并在后续使用该表达式的地方重用结果。
删除死代码:如果基本块内有一些代码在任何情况下都不会被执行,可以将其删除。
删除无用赋值:无用赋值是指在程序中,某个变量被赋值后,其值在后续的代码中从未被使用过。这种赋值操作是冗余的,因为它不会影响程序的输出或行为。
复写传播:变量的值在被复制后没有被修改。在这种情况下,编译器可以将使用被复制变量的地方替换为使用原始变量,从而消除不必要的变量使用。
3.1 常量传播
A = op B 或者 A = B op C
若B和C都是常数,则在编译时可将A的值计算出来。
例如:x = 5; y = x * 2; z = a[y]; ,可以替换为 z = a[10]。(至于x和y能不能删除的问题,需要涉及到活跃变量分析)
3.2 删除公共子表达式
U = A - C * D
V = B + C * D
如果两个语句之间C和D的值未改变 则将公共表达式结果记为T:
T = C * D
U = A - T
V = B + T
3.3 删除死代码
if B then S1 else S2
如果B的值固定为 “真” 或 “假” ,则其中一个分支永远不会执行,这些分支的代码可以删除。
3.4 删除无用赋值
A = B + C
...
A = M + N
假如在两次赋值之间,A没有被使用过,那么第一个赋值语句就可以被删除。
3.5 复写传播
B = A
...
C = B * 2
假如两句代码之间,B的值没有发生变化,则可以用A来代替B。
B = A
...
C = A * 2
复写传播本身对效率没有任何提升,但是却为无用赋值的出现创造了条件。例如上面的例子中,我们减少了对B的使用,使得 B = A 这一语句更有可能成为无用赋值而被删除。
4. 全局优化
这里,我们只讨论对循环的优化。
循环:程序流图中有唯一入口结点的强连通子图。
强连通子图:任意两个结点均可互相连通的子图。
入口结点n:从循环外到循环内任何结点的所有通路都必通过此入口结点。
子图的首结点:子图中最早被执行的结点。
4.1 流图中的循环
必经结点:从程序流图的首结点出发,到达结点n的任一通路都必须经过的结点d,称d为n的必经结点,记为d DOM n。任何结点都是自己的必经结点。
必经结点集:程序流图中结点n的所有必经结点的集合称为n的必经结点集,记为D(n)。
回边:对于程序流图G = (N, E, n0)中的有向边n→d,如果d是n的必经结点,即d∈D(n) ,则称n→d为流图的一条回边。
程序流图G = (N, E, n0)中,n → d是回边,M是G中到达n而不经过d的结点集合,则{n, d} ∪ M 构成一个循环。
4.2 循环的优化方法
代码移动(代码外提):将循环不变量(即在循环中不改变的表达式)移到循环外面,减少计算量。
强度削弱:将一些复杂的运算替换为更简单的运算,例如将乘法替换为加法。
删除归纳变量:如果基本块内有一些代码在任何情况下都不会被执行,可以将其删除。