【软件分析/静态程序分析学习笔记】5.数据流分析基础(Data Flow Analysis-Foundations)

写在前面的话

本渣有幸成为南京大学软件学院研究生,在前往仙林校区蹭课的时候偶然发现了这门宝藏课程,听了以后感觉深有收获,但又因为课程难度较大,国庆假期归来发现遗忘较多,因此开了一坑来记录自己对每节课知识点的理解。也由于这是本人第一次开坑写博客,结构内容自有诸多不合理之处,希望有问题的地方大家可以指出。

前两篇文章中比较直观的介绍了数据流分析是什么并举了几个算法的例子,本章将对数据流分析的基础部分进行详细讲解,这部分虽然名为基础,但是那种偏向底层的基础,非常晦涩难懂,没看过前两篇的请先看一下前面的内容,不过就算看不懂也没关系,反正我第一遍上完课也听不懂,老师说有些博士对这块内容可能都不是特别懂,所以平常心看看就行了。


系列文章目录

1.静态程序分析(Static Program Analysis)介绍
2.中间表示(Intermediate Representation)
3.数据流分析(Data Flow Analysis) (上):可达性分析(Reaching Definitions)
4.数据流分析Data Flow Analysis) (下):存活变量分析(Live Variables Analysis)及可用表达式分析(Available Expressions Analysis)
5.数据流分析基础(Data Flow Analysis-Foundations)
6.过程间分析(Interprocedural Analysis)
7.指针分析(Pointer Analysis)入门
8.指针分析基础知识(Pointer Analysis Foundations)


一、从另一个角度看待迭代算法

1.1 前向传播May算法回顾前向传播may算法

这是一个类似前文提到的Reaching Definitions的算法,是一个May分析,顺序是前向传播,相信大家并不陌生。

1.2 从另一个角度看待迭代算法

接下来要用数学的方式看待之前的迭代算法,首先先对其进行定义:

  1. 给定一个有着k个节点的CFG,迭代算法会在每次迭代过程中为每个节点n更新对应的OUT[n];
  2. 假设数据流分析中的数值域为V,那么我们可以定义一个k元组来保存每次迭代后的分析值。这个k元组内容为(OUT[n1],OUT[n2],…,OUT[nk]),表示为(V1 x V2 x … x Vk),记作Vk
  3. 每一次的迭代过程可以视作采取一个动作,通过使用转换函数和控制流函数,将一个元素Vk映射到新的元素Vk上。这个过程可以表示为函数F:Vk→Vk
  4. 然后算法迭代输出k元组,直到两次输出的k元组完全一致算法停止。

以上便是迭代算法数学层面的定义,将其用符号表示得到下图:
符号表示
当迭代算法没有停止的时候会有Xi=F(xi-1),但是当迭代停止的时候会有Xi+1=F(Xi)=Xi=F(xi-1),因此得到一个结论:
当X是不动点时,X=F(x)
当我们得到了这个数学表达形式以后,提出三个问题:
在这里插入图片描述
翻译一下,三个问题分别是:

  1. 算法是否一定会终止或到达不动点,或是一定会给数据流分析一个解决方案吗?
  2. 如果可以,那么是否只有一个解决方案或不动点?如果不止一个,我们的方案是否是最好最精确的?
  3. 算法何时到达不动点,或者说我们合适得到解决方案?

接下来将围绕这三个问题,讲解内容。


二、偏序

本章开始是大段的离散数学方面的概念知识点,懂的同学可以扫一眼过去,不懂得要自己理解一下,否则后面会直接一脸懵逼。
定义:
我们定义一个偏序集为一个对(P, ⊑),其中二元关系符在数据集P上定义了一种偏序关系,并且拥有以下性质:

  1. ∀x ∈ P, x ⊑ x(自反性)
  2. ∀x, y ∈ P, x ⊑ y ∧ y ⊑ x ⟹ x = y (反对称性)
  3. ∀x, y, z ∈ P, x ⊑ y ∧ y ⊑ z ⟹ x ⊑ z(传递性)
    举个例子
    举个例子,如图所示结构,定义的偏序符号是子串关系,根据定义可以发现满足三条性质,因此这是一个偏序关系。其中pin和sin并不满足⊑,但是并不妨碍定义的正确,并不是所有元素之间都要满足 ⊑。
    另一个例子
    再举个例子,偏序符号代表子集关系,同样满足三个性质,这张图可以记牢,后续经常用来讲解。
    提示:再思考一下,如果偏序符号代表<小于关系,性质1就不成立,构成的也就不是偏序集了,因此在后续讲解和理解过程中,可以将偏序关系粗略地理解为小于等于关系,这样方便理解,而且基本上不影响证明含义。

三、上界与下界

接下来给出上界与下界的定义:
给定偏序集(P, ⊑)及其子集S,满足S ⊆ P,则有:

  1. 若∀x ∈ S, x ⊑ u,那么u ∈ P是子集S的上界
  2. 若∀x ∈ S, l ⊑ x,那么l ∈ P是子集S的下界

其中上下界都可以不止一个,因此需要定义一个最小上界⊔S(lub)最大下界⊓S(glb),定义如下图,就不翻译了
在这里插入图片描述
如果子集S仅包含两个元素a,b,那么:

  1. ⊔S能被写作 a ⊔ b
  2. ⊓S能被写作 a ⊓ b

还有一些性质
性质
一个是说并不是每个偏序集都存在lub和glb,另一个是说如果存在的话,二者由自反性可以推出都是唯一的。


四、各种Lattice

4.1 Lattice

lattice中文翻译为格,但是没什么意义,因此还是尽量直接用英文lattice来代指。
定义:
给定偏序集(P, ⊑),∀a, b ∈ P,如果 a ⊔ b a ⊓ b 都存在,那么这个偏序集(P, ⊑)被称作一个lattice。
也就是说一个lattice的任意两个元素组成的元素对都有lubglb
接下来继续举例说明:

  1. 当偏度符号代表小于等于时,满足lattice的定义,ok。
    在这里插入图片描述
  2. 如上图情况时,pin和sin并没有一个在P内的元素构成二者的上界,自然也没有最小上界,因此不符合。在这里插入图片描述
  3. 上图所示情况可以看出符合,这也是一个lattice,同时这也是之前我提醒要记住的图,比较典型。

4.2 Semilattice

Semilattice中文翻译为半格。
定义:
给定偏序集(P, ⊑),∀a, b ∈ P,
如果只有 a ⊔ b 存在,则称偏序集为一个join semilattice
如果只有 a ⊓ b 存在,则称偏序集为一个meet semilattice

4.3 Complete Lattice

Complete Lattice中文翻译为全格。
定义:
给定一个lattice(P, ⊑),对于P的任意子集S,如果S的最小上界⊔S(lub)最大下界⊓S(glb)都存在,这个lattice就是一个complete lattice。

还是举几个例子说明一下:

  1. 如果P是正整数集,偏序符号代表小于等于,由于任意两个数之间都有lub和glb,因此是lattice;但是由于存在无穷大,也就没有最小上界lub了,因此不是complete lattice。在这里插入图片描述
    还是这张经典图,全格基本就长这样,不赘述了。

接下来引入两个符号:
由于csdn不支持这个符号,因此只能用图片展示,后续用英文topbottom代指。
在这里插入图片描述
其中也提到了任意有限的lattice都是complete lattice。

4.4 Product Lattice

Product Lattice的中文翻译是乘积格。
定义:
给定n个lattice,L1 = (P1, ⊑1), L2 = (P2, ⊑2), …, Ln = (Pn, ⊑n),如果每个lattice都有对应的i(最小上界)i(最大下界),那么我们得到一个Product Lattice Ln = (P, ⊑)并有以下四个定义:

  1. P = P1 × … × Pn
  2. (x1, …, xn) ⊑ (y1, …, yn) ⟺ (x1 ⊑ y1) ∧ … ∧ (xn ⊑ yn)
  3. (x1, …, xn) ⊔ (y1, …, yn) = (x11 y1, …, xnn yn)
  4. (x1, …, xn) ⊓ (y1, …, yn) = (x11 y1, …, xnn yn)
    并且如果L是一系列complete lattice的乘积,那么L也是complete lattice。

五、利用Lattice的数据流分析框架

一个数据流分析框架(D, L, F)包含:

  1. D:数据流的方向,前向或者后向
  2. L:包含数值作用域V和操作符meet ⊓ 或 join ⊔ 的lattice
  3. F:一系列V to V的传递函数

Tips: 数据流分析可以看啊做在一个lattice的数值域上,迭代地使用传递函数和操作符。


六、Monotonicity and Fixed-Point Theorem

6.1 Monotonicity

Monotonicity的中文翻译为单调性。
定义:
一个函数f: L → L (L为lattice)是单调的,当且仅当∀x, y ∈ L,x ⊑ y ⟹ f(x) ⊑ f(y)。

6.2 Fixed-Point Theorem

Fixed-Point Theorem的中文翻译为不动点定理。
给定一个complete lattice (L, ⊑),如果满足f: L → L 并且偏序数据集L是有限,那么有:
在这里插入图片描述
接下来证明不同点的存在,以及为何这种迭代获得的就是最小不动点。

6.2.1 证明不动点的存在

不动点的存在
由于函数f的映射是从L到L的,所以f(⊥)仍然是complete lattice中的元素,故得到第一句。
根据单调性可以得到第二句,迭代使用f得到第三句,由于L是有限的,因此最终一定会获得一个不动点

6.2.2 证明该点就是最小不动点

证明过程
图中红框部分是数学归纳法,为了证出 f i(⊥) ⊑ f i(x),因为x可以任意取,而fi(⊥) ⊑ fi(x) = x 恒成立,在k次后fk(⊥)达到不动点的时候自然也是最小的了。(或者说每次迭代的fi(⊥)和fi(x)比都是最小的,因此最终也是最小的)


七、将迭代算法和不动点定理关联

7.1 关联

之前我们证明了不动点定理,那是针对单个偏序集和单个传递函数的定理,对应到CFG上就是针对单个节点(单个B)的,而本章内容是为了将其关联到整个迭代过程中,或是说整个数据流分析中。在这里插入图片描述
关系如图,接下来主要分析product lattice L k和对应整体的函数F,其中F包含对应各个节点的f: L → L以及通过⊔/⊓将控制域 L×L → L 的控制流函数。由于已知每个L是有限的,所以所有L整合的L k也是有限的,因此只需要证明函数F是单调的,就可以回答之前的三个问题的成立了。

7.2 证明F是单调的:

在这里插入图片描述
在上一章证明单调性的时候,默认函数f是单调的,而在本章证明的时候,根据我们上一篇文章中写的传递函数公式可以知道,我们主要的函数f是Gen和Kill,这都是单调的(由设计者决定的,基本都是单调的,否则没法使用,具体来说就是之前那些0000中如果某个字节变成了1,就不会变回0了),那么传递函数部分就是单调的了,接下来只需要证明操作符操作也是单调的,F就是单调的了。
证明前三行都没问题,第四五行主要是说,由第三行得知y ⊔ z是x的上界,而由定义得知x ⊔ z是最小上界,因此得到x ⊔ z ⊑ y ⊔ z。
在这里插入图片描述

此时,三个问题的前两个解决了。

7.3 算法何时会到达不动点

在这里插入图片描述
如图所示,lattice的从top到bottom的路径长度为高度h,而假设由k个节点,则最多迭代i=h*k次后到达不动点。其中高度h可以理解为有多少个需要分析的变量/定义等,也就是多少位字节表示。

自此三个问题都解决了。


八、May and Must Analyses, a Lattice View

8.1 May and Must Analyses的图示

本节的图非常重要,建议牢记

8.1.1 映射

首先先将之前的图映射到右侧椭圆形中,右侧椭圆形其实是一个product lattice,但是product lattice本质上还是一个lattice,故可以将椭圆形看作单个lattice。
在这里插入图片描述
需要提及的一点是,may和must analyses都是一个从unsafe往safe的过程

8.1.2 May Analyses

接下来用reaching definitions举例
在这里插入图片描述
may分析的目的可以看作是报错,因此初始化在bottom,此时分析结果为无错误,因此肯定是一个unsafe的结果;而在top处,分析结果是所有点都出错的,这个肯定是safe的,但是就有些是做了无用功了;因此我们的目的是在二者之间找到一个平衡点,假设{a,c}是truth,那么以此为界可以划分为两块(之所以是区域是因为这其实是一个product lattice),交界点便是truth。
在这里插入图片描述
因此最后可以得到上图,假设有一系列不动点,那么越往精度越低越不准,对于may来说最接近truth的就是最不动点。
Tips: 图中所有不动点都是safe的,其实是看算法设计情况,如果由不动点在unsafe区域,那么这个算法就是不合格的。

8.1.3 Must Analyses

同理可以获得must analyses的示意图。
must的目的可以看作是删除优化,因此初始化在top,此时分析结果为所有语句都可删除,那肯定是unsafe的,其余类比may可得。
在这里插入图片描述
因此最后可以得到上图,假设有一系列不动点,那么越往精度越低越不准,对于must来说最接近truth的就是最不动点。


九、MOP and Distributivity

9.1 Meet-Over-All-Paths Solution (MOP)

与我们之前使用的算法不同,我们以前使用的算法是在运行过程中沿着数据流反复迭代的,而MOP是找出从开头到结尾所有路径,分别进行传递函数计算,最后将算完的所有路径进行⊔/⊓,公式如下:
在这里插入图片描述
由于某些路径可能实际无法到达,因此MOP不是特别精确;而在正常的程序中路径过多,几乎无法全部枚举,因此MOP也不太实际。

9.2 迭代算法和MOP对比

在这里插入图片描述
可以发现MOP的join符号在F之外,而我们的迭代算法join在F之内。
在这里插入图片描述
经过证明MOP ⊑ Ours满足偏序关系,根据之前的图,对于may analyses,MOP更加接近truth因此更加精确。
同理,对于must analyses,有Ours ⊑ MOP,根据之前的图,MOP接近truth,更加精准。
只要满足了转换函数是distributive的条件,我们的方法就和MOP一样精准,上一篇文章中提到的三个算法都是distributive,下面将一个不是distributive的算法。


十、Constant Propagation

Constant Propagation的中文翻译是常量传播。

10.1 Constant Propagation定义:

程序点p处给定一个变量x,判断x在程序点p处是否一定指向一个常量。

这是一个类似must分析,因为必须所有到点p的path都是这个常量才行;但有和传统的不大一样。
在CFG中每个节点的输出包括一个对(x, v),其中x是变量而v是在该节点后x指向的具体值。

10.2 Constant Propagation具体分析:在这里插入图片描述

可以看作一个must分析,最下方NAC是not a constant,是safe but useless的。因为输出是一个对而不是一个值,因此初始化为UNDEF(undefined)。
UNDEF ⊓ v = v可能会导致未定义变量的漏报,但是由于这个算法只专注于常量传播分析,所以无所谓(我们就当作已经在跑这个算法之前跑过reaching definitions了)。
Tips:此处的meet和之前的不一样,这个是有一个自己的规则,记忆理解时可以当作并和与来记忆,但是实际使用时还是要按照规则来。

10.3 Constant Propagation传递函数

在这里插入图片描述
{x, _}中的 _是通配符,意思是无论x指向什么数值,都kill掉,然后赋予新的。val(x)指取出变量x中的具体数值。
二元操作比较特殊,都是常量仍是常量,其中一个是NAC得到NAC,否则得到UNDEF。

10.4 Constant Propagation为什么是Nondistributivity

在这里插入图片描述
如图所示,Ours与MOP并不相等。
Ours是左右a与a,b与b,最后c = a + b;
MOP是先左边c = 1 + 9 = 10,然后右边c = 9 + 1 = 10,再a与a,b与b,因此不一样。


十一、Worklist Algorithm

最后再提一下Worklist 算法,这是对我们之前的迭代算法一种完全的改进,不影响精度的情况下,有更高的效率。
传统迭代算法如下:
在这里插入图片描述
Worklist算法如下:
在这里插入图片描述
不同于普通迭代算法,一个OUT变了所有的都要跑一遍,worklist的效率高很多,因为gen和kill是不会变的,OUT变化只取决于IN的变化,因此先把所有节点放进去,然后每个变化的OUT都把后继放进去,来回的跑直到结束。


十二、总结

本篇文章比较长,辛苦各位看下来,看完本文需要掌握的知识点如下:

  • lattice和complete lattice的定义
  • 如何在lattice中总结may和must分析
  • 了解不动点定理
  • MOP与迭代算法产生的解之间的关系
  • Constant propagation analysis
  • worklist算法
  • 15
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值