目录
(1)如何求解数据流方程?
不论是Reaching Definition Analysis中对可达定义集合的求解,还是Liveness Analysis中对活跃变量集合的求解,本质上都是在解方程。
以RDA举例,其数据流方程如下:
可认为每个指令都对应方程中的两个变量,即IN(p)和OUT(p)。为了减少变量个数,把(1)式带入(2)式,以去掉IN(p):
此时方程中的变量为,而和则属于常数,因为其值随着指令的确定而相应确定。
此外,方程还有一个初值,即,
转化为OUT,
为了说明求解思路,先用更简化的符号来表示上述方程:
通过此方程可以看出,数据流方程的一个重要特点就是,待求的每个未知数既是若干方程的自变量,也是某个方程的因变量。
从此特点出发,我们很快发现一种求解的思路,即先给定一组未知数的初始值如,接着将这组值带入上述方程,得到又一组未知数的值,如果与相等,则说明找到了一组方程的解,因为这组值可让上述方程成立。如果与不相等,则重复上述过程,一定能找出方程的一组解。原因如下:
对于RDA,自变量越大(即集合的基数越大),根据方程,其因变量也相应越大。
由此可知,上述方程中的函数都是单调不减的,即如果,则有
,即
要让解集合随着迭代的进行而越来越大(即集合的基数越大),还需要一个初始条件,即
如果令,则有
此时初始条件和函数单调性共同作用,有如下链式反应:
由于是离散值(集合)且有上界(集合元素有上限),因此上述链式反应一定会终止,终止时会有
,这即为方程的一组解。
(2)WorkList Algorithm
上述思路对应的算法为Round-Robin Iterations,即各未知数的迭代次数(or更新次数)保持同步,即算法每次循环结束后都有
,而WorkList Algorithm则不同,其未知数的迭代次数不一定同步,在算法每次循环结束后可能会出现
,这说明针对未知数的值的更新,WorkList Algorithm有一套优先级规则。(其实这两种更新思路与数值分析中求解线性方程组的Jacobi迭代和Gauss-Seidel迭代有共通之处)
先给出WorkList Algorithm的(不规范的)伪代码:
WorkList()
for i <- 1 to n do
y[i] <- { } //初始化随具体情况而定
w <- [ y[1] y[2] ... y[n] ] //将未知数放入worklist中
while(!is_empty(w)) do
y[i] <- extract(w) //从worklist以某种方式抽出一个未知数
old <- y[i] //记录此未知数的值
y[i] <- f_i(y[1], y[2], ..., y[n]) //挑出以此未知数作为因变量的方程,并进行运算,结果赋给此未知数
if old != y[i] then //如果未知数的值在运算后发生变化
for y[k] <- dep(y[i]) do
//将依赖它的那些未知数放入worklist中,a依赖b的含义是
//以a为因变量的方程使用b作为自变量,因此当b发生变化,a也就可能发生变化
//故把a放入worklist中
w <- insert(w, y[k])
未完待续