【南大软件分析-学习笔记】Lecture 5-Data Flow Analysis-Foundation(上)
0. 前言
前面的两节课介绍了三种常见的数据流分析方法,接下来的两节课将介绍数据流分析的理论部分,废话不多说,直接开始了。(拔智齿肿成猪头了,太痛苦了)
1. Iterative Algorithm, Another View
首先我们先再来看一下迭代算法的通用伪代码模板,这里以 May & Forward Analysis 为例:
我们在上一节课的学习中已经知道,对于每个 BB,我们会使用一个 bit vectors 来表示它的输入 IN 和输出 OUT,那么,换一个角度,对于一个有 k 个节点的 CFG 来说,我们可以认为迭代算法在每轮迭代中实际更新的是每个节点 n 的 OUT[n],假设在该数据流分析中的值域为 V ,那么我可以定义一个 k-tuple 来表示每轮迭代更新后的结果:
它是集合 \((V_1 \times V_2 \times V_3 \times...\times V_k )\) 中的一个元素,这个集合记作 \(V_K\)。
与此同时,每一轮迭代可以看作从 \(V_k\) 中的一个元素到一个新元素的映射,映射过程就是 transfer functions 和 control-flow handling,可以将这个过程抽象为一个函数 \(F:V_k \rightarrow V_k\) 。
我们用一个简单的例子来简单的表示一下这个迭代过程:
算法会不断根据函数 F 输出一系列的 k-tuple,当某一轮的输出 k-tuple 与上一轮的 k-tuple 一致时,迭代停止,这与我们之前分析的实际情况也一致,这样一来,我们就可以得到关系:
也就是说:
我们将这种情况成为上述迭代算法达到了一个不动点(fixed point) X 。
我们也可以将找到的这个不动点 X 称作为数据流分析找到一个了一个可行解,那么接下来针对这个可行解,我们又引出以下三个问题:
- 这个算法一定能终止吗,也就是说它一定有不动点、有解吗?
- 如果一定有不动点的话,那么它会只有一个不动点还是有多个不动点,如果是有多个不动点的话,如何保证我们得到的是最好(最精确)的?
- 这个算法将会在什么时候达到一个不动点呢?
为了回答以上三个问题,我们需要学习一些离散数学中的知识。
2. Some math foundation
2.1 Partial Order
定义: 当一个集合满足偏序关系时,我们将此集合定义为偏序集(poset),用\((P,\preceq)\)表示,其中 \(\preceq\) 满足以下三个特性:
- Reflexivity(自反性):\(\ \forall x \in P,\, x \preceq x \\\)
- Antisymmetry(反对称性):$\ \forall x,y \in P,, x \preceq y \land y \preceq x \implies x = y \ $
- Transitivity(传递性):\(\ \forall x,y,z \in P,\, x \preceq y \land y \preceq z \implies x \preceq z\)
下面结合四个典型的例子来更好的理解相关概念:
-
\((S,\preceq)\) 是一个poset,其中 S 表示整数集,\(\preceq\) 表示小于等于。
-
\((S,\preceq)\) 是一个poset,其中 S 表示整数集,\(\preceq\) 表示小于。
-
\((S,\preceq)\) 是一个poset,其中 S 是一个字符串集合,\(\preceq\) 表示子串关系。
-
\((S,\preceq)\) 是一个poset,其中 S 是一个幂集,\(\preceq\) 表示子集关系。
结合偏序关系的三个特性,不难发现 example1,3,4 满足要求,是偏序集,而 example2不满足自反性,不是偏序集。
2.2 Upper and Lower Bounds
定义: 给定一个偏序集 \((P, \preceq)\),集合 \(S\) 是 \(P\) 的一个子集,我们称 \(u \in P\) 是 \(S\) 的一个上界(upper bound),当且仅当对于\(\forall x \in S\),\(x\preceq u\);同样的,\(l\in P\) 是 \(S\) 的一个下界(lower bound),当且仅当对于 \(\forall x \in S,l \preceq x\)。
以 example 4 的幂集为例,整个幂集表示集合 \(P\),绿色部分表示集合 \(S\),根据以上定义,我们可以得到 {a, b, c} 是集合 \(S\) 的一个上界,{ } 是集合 \(S\) 的一个下界。
进一步的,对于一个集合 \(S\) 来说,我们还可以定义最小上界(least upper bound,读作 lub or join),记作 \(\sqcup S\) ,对于 \(S\) 的每一个上界 \(u\) 来说,有 \(\sqcup S \preceq u\);同样的,还可以定义最大下界(greast lower bound,读作 glb or meet),记作 \(\sqcap S\),对于 S 的每一个下界 \(l\) ,有 \(l \preceq \sqcap S\)。通常的,如果集合 \(S\) 只有两个元素 a 和 b(S = {a, b}),难么还可以将 \(\sqcup S\) 写作 \(a \sqcup b\)(the join of a and b),同样的,也可以将 \(\sqcap S\) 写作 \(a \sqcap b\)(the meet of a and b)。
还是以上面的幂集为例,我们将集合 \(S\) 重新用绿色部分划分,如下所示:
重新划分集合 S 后根据以上定义不难发现,{a, b} 和 {a, b, c} 都是集合 S 的上界,而 {a, b, c} 是集合 S 的最小上界;而 { } 既是集合 S 的下界,又是最大下界。
关于上下界还有两个简单的特性:
-
不是所以的 poset 都有 lub 或 glb,比如下图中的集合 P 就没有 glb;
-
如果一个 poset 有 lub 或者 glb,那么它一定是唯一的;这个结合使用反证法和传递性不难得证。
2.3 Lattice
定义:对于一个偏序集 \((P, \preceq)\) 来说,如果 \(\forall a,b \in P\),\(a\sqcup b\) 和 \(a \sqcap b\) 都存在的话,那么可以将这个偏序集成为一个格,换句话说,偏序集 \(P\) 中的任意两个元素组成的子集都具有 glb 和 lub。
我们再看一下之前给出的四个例子,根据格的定义,不难得出,example 1 和 example 3 所表示的集合是一个格。
2.3.1 Semilattice
定义: 对于一个偏序集 \((P, \preceq)\) 来说,如果 \(\forall a,b \in P\),只存在 \(a\sqcup b\) 的话,就将这个偏序集称为 join semilattice;如果只存在 \(a\sqcap b\) 的话,就将这个偏序集称为 meet semilattice。
2.3.2 Complete Lattice
定义: 对于一个偏序集 \((P, \preceq)\) 来说,如果 \(\forall S \in P\),\(\sqcup S\) 和 \(\sqcap S\) 都存在的话,那么可以将这个偏序集成为一个完全格,换句话说,偏序集 \(P\) 中的任意子集 S 都具有 glb 和 lub。
example 4 中所展示的幂集就是一个典型的完全格。
对于每一个完全格 \((P,\preceq)\) 来说,它都有一个最大的元素 \(\top = \sqcup P\),称作 top;一个最小的元素 \(\bot = \sqcap P\),称作 bottom。
每一个有限格(finite lattice)都是完全格,但反过来,完全格不一定是有限的,比如[0 , 1]范围上的实数格。
2.3.3 Product Lattice
定义:给定一系列的格 \(L_1 = (P_1, \preceq),L_2=(P_2, \preceq),...,L_n=(P_n,\preceq)\),如果对于所有 \(i\),\((P_i,\preceq_i)\) 都有 \(\sqcup_i\) 和 \(\sqcap_i\),那么我们可以定义乘积格 \(L^n=(P,\preceq)\)如下:
不难得到:
- 乘积格也是格;
- 完全格构成的乘积格也是完全格。
3. Data Flow Analysis Framework via Lattice
在学习了格的相关知识后,我们再来看基于格的数据流分析,基于格的数据流分析基本框架为:(D, L, F),它由三部分组成:
- D:direction,数据集传输的方向,前向 or 反向;
- L:由值域 V 和一个 meet \(\sqcap\) 或 join \(\sqcup\) 操作符组成的格;
- F:从 V 到 V 的一系列传递函数。
数据流分析可以看做是在格的数值域上,迭代地使用传递函数和 meet / join 操作符。
4. Monotonicity & Fixed Point Theorem
4.1 Monotonicity
定义: 函数 \(f:L \rightarrow L(L 是格)\) 是单调的,当且仅当 \(\forall x,y \in L,x \preceq y \rightarrow f(x)\preceq f(y)\)。
4.2 Fixed Point Theorem
给定一个完全格 \((L,\preceq)\),如果 \(f:L \rightarrow L\) 是单调且有限的,那么:
- f 的最小不动点(least fixed point)可以通过以下方式得到:不断迭代计算 \(f(\bot),f(f(\bot)),...,f^k(\bot)\),直到到达一个不动点;
- f 的最大不动点(greast fixed point)可以通过类似的方式方式得到:不断迭代计算 \(f(\top),f(f(\top)),...,f^k(\top)\),直到到达一个不动点;
那么该如何证明不动点理论呢,我们分两步来证明,先证明不动点存在,再证明我们找到的不动点是最小的,以证明最小不动点为例,证明过程如下:
-
先证明不动点的存在
-
前面我们已经知道 \(\bot = \sqcap L\) 是格上的最小元素,所以 \(\bot \preceq f(\bot)\),并且我们 f 是单调的,不断应用 \(x \preceq y \rightarrow f(x)\preceq f(y)\) 的映射关系,我们可以得到一个上升链:
\[\bot \preceq f(\bot) \preceq f^2(\bot) \preceq ... \preceq f^H(\bot) \] -
又因为 L 是有限的,假设其高度为 H (H 是格上 bot 到 top 的最长路径),那么这个数列就应该是有限的,它的值域为:
\[\bot,f(\bot),f^2(\bot),....,f^H(\bot) \]并且这个数列的上限就应该为 \(f^H(\bot)\);
- 既然数列是有限的,那么根据鸽巢理论,必然存在 \(k < j \leq H + 1\) ,使得 \(f^k(\bot) = f^j(\bot)\),又因为 f 是单调的,那么\(f^k(\bot) \preceq...\preceq f^j(\bot)\),若令相同的值为a,中间的值为b,中间任意段应该满足\(a\preceq b \preceq a\),根据格的反对称性,首尾一致,那么中间所有的值也都一致,即 \(f^{fix}=f^k(\bot)=...=f^j(\bot)\),因此不动点存在。
-
-
再证明该不动点是最小不动点
采用数学归纳法来证明该不动点是最小不动点,即对于任意不动点x,都有 \(f^{fix} \preceq x\) 。
- 假设除了不动点 \(f^{fix}\) 外还有一个不动点 \(x\),使得 \(x = f(x)\),由于 \(\bot\) 是最小元素,根据函数的单调性,可以得到 \(f(\bot)\preceq f(x)\);
- 像上面一样,对两边使用多次函数的单调性,可以得到 \(f^i(\bot) \preceq f^i(x)\);
- 同样的,在上面我们已经证明,当迭代稳定后,\(f^{fix} = f^i(\bot) \preceq f^i(x) = x\),即 \(f^{fix} \preceq x\) ,最小不动点得证明。

浙公网安备 33010602011771号