基本块和流图
•
采用图的方式表示中间代码,
有助于生成更好的代码
ä
构造方法
1.
把中间代码划分成基本块(
basic
block
,
BB
),每个基本块满足如下条件:
①
控制流只能从基本块的第一个指令进入
②
除了基本块的最后一条指令,控制流在离开基本块前不会停机或者跳转
2.
基本块形成了流图(
flow
graph
)的结点,流图的边指明了哪些基本块可能紧随一个基本块之后执行
ä
中断等程序行为可能打破基本块的上述约定
1
•
构造基本块和流图的目的是对代码进行优化
ä
如果中断正常返回,则中断本身的保护机制可以使程序正常执行下去,不影响正确性,但可能形象优化效果
ä
如果中断导致异常退出,优化的结果也不会有错,程序本身非正常终止
基本块(1)
•
基本块:
ä
定义:基本块是程序中最大限度顺序执行的语句序列,其中只有一个入口和出口,入口是其第一个语句,出口是其最后一个语句
ä
基本块的入口语句可能是
•
程序的第一个语句
•
跳转的目标语句
•
条件跳转的下一条语句
ä
基本块的结束语句可能是
•
停机语句
•
跳转语句
•
跳转目标语句的前一个语句(词法序)
•
基本块(续)
ä
构造方法
•
输入:一个三地址指令序列
•
输出:与之对应的基本块列表,每个指令恰好被分到一个基本块中
•
方法:
ä
首先,取定中间代码序列中哪些指令是首指令(
leader
,入口指令,基本块的第一条指令)
1.
中间代码的第一个三地址指令是一个首指令
2.
任意一个条件或无条件转移指令的目标指令是一个首指令
3.
紧跟在一个条件或无条件转移指令之后的指令是一个首指令
ä
然后,每个首指令对应的基本块包括了从它自己开始,直到下一个首指令(不含)或者中间程序的结尾指令之间的所有指令
ä
一个特殊情况:过程调用语句作为一个新的基本块的开始,甚至独立成为一个基本块
后续使用信息
•
下次不再引用意味着优化的机会
ä
寄存器优化
ä
临时名字存储单元的指派
•
计算后续使用信息
ä
三地址语句中名字的
使用(
use
)
定义:
•
假定三地址语句
i
把
a
的值赋给
x,
如果语句
j
用
x
作为运算对象,并且控制从
i
流到
j,
这条路径中没有
x
的其它赋值,则称
j
引用
x
在
i
定的值
•
此时,称
x
在语句
i
处活跃(
live
)
ä
在基本块内:后续使用信息
•
对每个基本块反向扫描
•
为每个名字
x
在符号表中登记它是否在本块中有后续使用
•
如果在本块中没有后续使用,则登记它是否在本块的出口活跃。缺省认为所有的非临时变量在出口都是活跃的
ä
算法
8.7
:对一个基本块中的每一个语句确定活跃性与后续使用信息
•
输入:一个三地址语句的基本块
B
,假设在基本块
B
开始时,所有的非临时变量都是活跃的
•
输出:对于每一个语句
i
:
x
= y op z
,
将
x
、
y
及
z
的活跃性信息及后续使用信息关联到
i
•
方法:从基本块
B
的最后一个语句开始,反向扫描到
B
的开始处。对每一个三地址语句
i
:
x
:= y op z
,
依次执行下述步骤:
ä
把当前符号表中
x
、
y
和
z
的后续使用信息和活跃信息附加到语句
i
上;(若
x
不活跃,则这个语句可以删掉)
ä
在符号表中设置
x
为
“无后续使用”和“不活跃”;
ä
把符号表中
y
和
z
的后续使用信息均置为
i
,
活跃信息均置为“活跃”。
注意:上述次序不能颠倒,因为x可能是y或z
流图(1)
•
定义:控制流图或流图
ä
结点是基本块的有向图
G
= (
N
,
E
,
root
)
•
N
是结点的集合,每个结点表示一个基本块
•
E
是边的集合,如果结点
n
i
和
n
j
间存在前驱和后继的关系,则在存在一条从
n
i
到
n
j
的有向边(此时意味着,在
n
i
执行后,可能会执行
n
j
)
ä
n
i
的出口语句是
goto
(s)
或
if …
goto
(s),
且(
s)
是的
n
j
入口语句
ä
n
j
在程序中的位置紧跟在
n
i
后,且
n
i
的出口语句不是无条件转移语句和停语句
•
root
是流图的首结点(或称为根结点),是包含程序第一个语句的基本块
ä
每个流图都可以等价变换为单入口,且每个结点最多有两个后继的图
流图的表示方式
•
可以用任意的表示图的数据结构表示流图
•
结点(基本块)
ä
基本块是一个指令序列
ä
可能要频繁改变这个指令序列
•
数量或组成的指令
•
目的之一是优化
ä
采用指令链表的形式较为高效