静态单赋值
SSA即静态单赋值,Static Single-Assignment,这是一种中间表示形式。 之所以称之为单赋值,是因为每个名字在SSA中仅被赋值一次。
构造静态单赋值形式的过程会在CFG中的每个汇合点之后插入phi函数,汇合点即为CFG中多条代码路径汇合之处。在汇合点处,不同的静态单赋值形式名必须调和为一个名字。整个过程大致为两步:
(1) 插入PHI函数
在具有多个前趋的每个程序块起始处,插入相应的PHI函数。到底该插入哪些函数呢,这个是重点,这也决定了算法的效率,否则要是插入过多的无效的PHI函数,这会降低在静态单赋值形式上执行的某种种类分析的精确度,这也会占用空间,使得编译器浪费内存来表示冗余或不活动的PHI函数。他们同样会增加使用由此产生的静态单赋值形式的任何算法的代价,因为相关的算法必须遍历所有不必要的PHI函数。因此分为几种不同风格的静态单赋值的形式。这些风格的差别在于其插入PHI函数的条件。
1) 最小静态单赋值形式:在任何汇合点处插入一个PHI函数,只要对应于同一原始名字的两个不同定义汇合。这将插入符合静态单赋值形式定义、数目最少的PHI函数。但其中一些PHI函数可能是死亡的,定义并没有不一定在汇合时是活跃的。(我不知道一个PHI函数怎么能解决问题?)
2) 最大静态单赋值形式:在每个汇合点处为每个变量放置一个PHI函数。很显然,这种方法效率最差,但最简单;
3) 剪枝静态单赋值形式:向PHI函数插入算法中添加一个活跃性判断,以避免添加死亡的PHI函数。构造过程必须计算LiveOut集合,因此构建静态单赋值形式的代价高于构建最小静态单赋值形式。
4) 半剪枝静态单赋值形式:这是最小静态单赋值形式和剪枝静态单赋值形式之间的一种折中。在插入PHI函数之前,算法先删除那些只在def的基本块中活跃的变量,因为只在一个基本块中活跃的变量,是不用插入与之对应的PHI函数的。这个算法可以缩减名字空间并减少PHI函数的数目,而又没有计算LiveOut集合的开销。这个算法就是本书中介绍的方法。
(2) 重命名
在插入PHI函数之后,编译器可以计算可达定义。由于插入的PHI函数也是定义,它们确保了对任一使用处都只有一个定义能够到达。接下来,编译器可以重命名每个使用处的变量和临时值,以反映到达该处的定义。
构造SSA的过程如下
第一步 : 计算Domain Tree
在这一步也分为2个阶段去计算每一个Block的Imm Domain。然后通过Imm Domain结点就可以得到对应的Domain Tree。
1) 计算每一个Block的Domain 结点;
根据控制流图,自底向上遍历所有的结点,查看每一个结点的predecessor 结点,Domain(当前结点) =Domain(所有