« 上一篇下一篇 »

从DAG到基本块的重组

DAG的各种优化处理可以生成DAG图时进行,也可以在DAG构造完成后通过对DAG的运算完成。在完成这些优化处理之后,我们就可以根据优化得到的DAG重组生成相应基本块的三地址代码。对每个具有一个或多个附加变量的结点,我们构造一个三地址语句来计算其中某个变量的值。我们倾向于把计算得到的结果赋给一个在基本块出口处活跃的变量。但是,如果我们没有全局活跃变量的信息作为依据,就要假设程序的所有变量都在基本块出口处活跃(但是不包含编译器为了处理表达式而生成的临时变量)。

如果结点有多个附加的活跃变量,我们就必须引入复制语句,以便给每一个变量都赋予正确的值。有时我们可以通过全局优化技术,设法用其中的一两个变量来替代其他变量,从而消除这些复制语句。

我们确定如果b在基本块的出口处不活跃,那么下面的三个语句就足以重建那个基本块了。第三个指令c =d + c必须使用d而不是b作为运算分量,因为经过优化的基本块不会计算b的值。

如果b和d都在出口处活跃,或者我们不能够确定它们是否在出口处活跃,那么我们还是需要计算d和b的值。我们可以用下面的序列来完成这个计算:

这个基本块仍然比原来的基本块高效。虽然指令数目相同,但我们已经把一个减法替换为一个复制运算。在大多数机器上,复制运算要比减法更加高效。不仅如此,我们还有可能通过全局分析把此基本块外对b的使用全部替换为对d的使用,从而消除在基本块外对b的使用。在这种情况下,我们就可以再次回到这个基本块并消除b=d。直观地讲,如果在任何使用b的这个值的时刻,d中的值仍然和b—样,那么我们就可以消除这个复制运算。这种情况是否成立依赖于程序如何重新计算d的值。

当从DAG重构基本块时,我们不仅要关心用哪些变量来存放DAG中的结点的值,还要关心计算不同结点值的指令的顺序。应记住如下规则:

1)指令的顺序必须遵守DAG中的结点的顺序。也就是说,只有在计算出一个结点的各个子结点的值之后,才可以计算这个结点的值。

2)对数组的赋值必须跟在所有(按照原基本块中的指令顺序)在它之前的对同一数组的赋值或求值运算之后。

3)对数组元素的求值必须跟在所有(在原基本块中)在它之前的对同一数组的赋值指令之后。对同一数组的两个求值运算可以交换顺序,只要在交换时它们都没有越过某个对同一数组的赋值运算即可。

4)一个变量的使用必须跟在所有(在原基本块中)在它之前的过程调用和指针间接赋值运算之后。

5)任何过程调用或者指针间接赋值都必须跟在所有(在原基本块中)在它之前的对任何变量的求值运算之后。

也就是说,当重组代码的时候,没有一个语句可以跨越过程调用或指针间接赋值运算。只有在两个使用同一个数组的指令都是数组访问而不是对数组元素赋值时,它们才可以交换顺序。

« 上一篇下一篇 »