问题描述
|
达到定义的问题是数据流分析中最基本的问题之一。给定一个包含变量定义和用途的控制流程图,问题将导致计算哪些变量定义可以达到特定用途。
例如,考虑流程图:
____________
1:| x <- ... |
------------
| \\
| __________
| 2:| x <- ... |
| -----------
| /
____________
3:| ... <- x |
------------
可以通过块1或块2中的定义来达到在块3中使用变量x的目的。
用于计算哪些定义可以使用的算法是经典的数据流问题。使用Dragon编译器手册(新版)中的符号,定义数据流问题如下:
域:定义集(例如{x <-..,...})
方向:前进
传递函数:fb(x)= gen(B)U(x-kill(B))其中gen(B)是块B生成的定义集,而kill(B)是块B杀死的定义集
边界:OUT [ENTRY] = {},即没有定义流进入函数
Meet运算符:U(union),即流到一个块的定义是前一个块之外的定义的并集。
方程:OUT [B] = fb(IN [B]),IN [B] = U(P in pred)OUT [P]
初始化:OUT [B] = {}
但是,并非所有定义都相同。例如,块1中的定义可能永远不会在块3中使用,因为它可能会被块2中的定义杀死。另一方面,块2中的定义如果执行,将保留其值,直到在块中使用它为止。 3。
我想找到一种用法的到达定义,在从定义到用法的任何路径上都没有致命的定义。我的问题是是否存在类似的数据流问题(可能是传播等)。如何通过数据流分析解决它。
我确实有解决此问题的可能方法,但是如果已经存在解决方案,我就不想重新发明轮子。
解决方法
像这样更改问题定义:
Meet运算符:∩(相交),即流到一个块的定义是前一个块中定义的交集。
公式:OUT [B] = fb(IN [B]),
IN [B] =∩(P in pred)OUT [P]
,这是我解决问题的方法。这可能不是最有效的方法,但我相信它是有效的。我将把这个问题称为保留定义的问题。首先,我计算到达定义。我在以位集表示的定义集上使用迭代数据流算法。
为此,我首先需要计算每个块的gen(B)和kill(B)。这些是分别由每个块生成和终止的定义。请注意,kill(B)是实际kill(B)的超集,因为我不知道什么定义以及从哪个块中真正杀死了这些数据,因为此时我不考虑数据流。
应用到达定义后,我为控制流程图中的每个块设置了REACH_IN(B)和REACH_OUT(B)设置。我知道保留的定义是到达定义的子集。为了计算它们,我需要找出从程序进入每个块以来从未被杀死的定义。我将这些集称为非杀死集,并且将提供一种算法来为图中的每个块计算NO_KILL_IN(B)和NO_KILL_OUT(B)。这里是关于数据流分析的算法。
域:定义集(例如{x <-..,...})
方向:前进
传递函数:fb(x)= x-(kill(B)∩REACH_IN(B))其中kill(B)是阻止B杀死的定义集,而REACH_IN(B)是流入B的定义集。
边界:NO_KILL_OUT [ENTRY] = U(通用集),即所有定义都不会从函数输入中被杀死
Meet运算符:∩(交集),即,如果未在任何先前程序段中取消定义,则不取消定义。
公式:NO_KILL_OUT [B] = fb(IN [B]),NO_KILL_IN [B] =∩(P in pred)NO_KILL_OUT [P]
初始化:NO_KILL_OUT [B] = U
请注意,在tranfer函数中,我们计算kill(B)∩REACH_IN(B),这是在块B中被杀死的一组实际定义。如果不使用它,我们将过于悲观。算法计算每个块之前和之后哪些定义不能被杀死,而无需考虑是否已生成。为了计算保留的定义,我们简单地执行交集:
PRESERVE_IN(B)= REACH_IN(B)∩NO_KILL_IN(B)
PRESERVE_OUT(B)= REACH_OUT(B)∩NO_KILL_OUT(B)
,您可能想看一下时间逻辑,而计算树逻辑很适合在控制流图中定义路径上的属性。本文显示了CTL中数据流属性的一些示例:
通过时间逻辑证明编译器优化的正确性