参考:
【课程笔记】南大软件分析课程3——数据流分析应用(课时3/4) - 简书
----------------------------------------------------------------------------
1. 数据流分析总览
may analysis: 输出可能正确的信息(需做 over-approximation 优化,才能成为 Safe-approximation 安全的近似,可以有误报),注意大多数静态分析都是 may analysis
must analysis: 输出必须正确的信息(需做 under-approximation 优化,才能成为 Safe-approximation 安全的近似,可以有漏报)
不同的数据分析应用会有不同的数据抽象方法和不同的流安全近似策略,即不同的转换函数和控制流处理。
Nodes (BBs/statements)、Edges (control flows)、CFG (a program)
例如:
application-specific Data <-
abstraction
(+/-/0)Nodes <-
Transfer function
Edges <-
Control-flow handling
不同的数据流分析 有 不同的数据抽象表达 和 不同的安全近似策略,如 不同的 转换规则 和 控制流处理。
2. 预备知识
输入/输出状态:程序执行前/执行后的状态(本质就是抽象表达的数据的状态,如变量的状态)。
- 每个 IR 块的执行将 input 转换为 output
- input 与之前的 output 关联,output 与之后的 input 关联
每个数据流分析的应用中,将每个程序点关联一个数据流值(data-flow value),这个值代表在那个点能观察到的所有可能的程序状态的抽象。
例如,我只关心x、y的值,我就用抽象来表示x、y所有可能的值的集合(输入/输出的值域/约束),就代表了该程序点的程序状态。
3. Reaching Definitions Analysis
Reaching Definitions Analysis 是一个 may analysis。
问题定义:给变量 v 一个定义 d(赋值),存在一条路径使得程序点 p 能够到达 q,且在这个过程中不能改变 v 的定义 d。
抽象表示:设程序有 n 条赋值语句,用 n 位向量来表示变量 v 的定义 d 能 reach 与不能 reach。
(1)公式分析
什么是definition?
D: v = x op y
,op为operation,如 x + y。
Transfer Function:
解释:基本块 B 的输出 = 块 B 内的所有变量 v 的定义(赋值/修改)语句 并上 (块 B 的输入 - 整个程序中其它所有变量 v 的定义)。
Control Flow:
解释:基本块 B 的输入 = 块 B 所有前驱块 P 的输出的并集。注意,所有前驱块意味着只要有一条路径能够到达块B,就是它的前驱,包括条件跳转与无条件跳转。
(2)算法
目的:输入 CFG,计算好每个基本块的 killB 和 genB,输出每个基本块最终的 IN[B] 和 OUT[B]。
方法:
(3)实例
抽象表示:设程序有 n 条赋值语句,用 n 位向量来表示变量 v 的定义 d 能 reach 与不能 reach。
说明:红色-第1次遍历;蓝色-第2次遍历;绿色-第3次遍历。
结果:3次遍历之后,每个基本块的 OUT[B] 都不再变化。
最后得到了,每个程序点关联的数据流值(该点所有可能的程序状态的一个抽象表示,也就是这个 n 位向量)。
(4)算法会停止吗?
会。
genS 和 killS 是不变的,只有 IN[S] 在变化,而 IN[S] 只会在原来的基础上越来越大(与more facts 并集),所以 OUT[S] 只会越来越大,直到达到一个上限。
所以最终 changes to any OUT 不会再发生变化,算法就停止了。
4. Live Variables Analysis
Live Variables Analysis 是一个 may analysis。
问题定义:某程序点 p 处的变量 v,从 p 开始到 exit 块的 CFG 中是否有某条路径用到了 v,如果用到了 v,则 v 在 p 点为 live,否则为 dead。其中有一个隐含条件,在点 p 和引用点之间不能重定义 v。
应用场景:可用于寄存器分配,如果寄存器满了,就需要替换掉不会被用到的变量。
抽象表示:程序中的 n 个变量用长度为 n bit 的向量来表示,对应 bit 为1,则该变量为 live,反之为 0 则为 dead。
(1)公式分析
Control Flow:
理解:我们是前向分析,只要有一个后一个节点的 IN 是 live,父节点就是 live。
Transfer Function:
理解:IN[B] = 本块中 use 出现在 define 之前的变量 U(OUT[B] 的 live 情况 - 本块中 define 的变量)。
特例分析:如以下图所示,第4种情况,v = v - 1,use 出现在 define 之前,v 为 live。
(2)算法
目的:输入 CFG,计算好每个基本块中的 defB 和 useB。输出每个基本块的 IN[B] 和 OUT[B]。
方法:首先初始化每个基本块的 IN[B] 为空集。遍历每一个基本块 B,按以上两个公式计算块 B 的OUT[B] 和 IN[B],只要这次遍历时有某个块的 IN[B] 发生变化,则重新遍历一次。
问题:遍历基本块的顺序有要求吗? 没有要求,但是会影响遍历的次数。
初始化规律:一般情况下,may analysis 全部初始化为空,must analysis 全部初始化为 all。
(3)实例
抽象表示:程序中的 n 个变量用长度为 n bit 的向量来表示,对应 bit 为1,则该变量为 live,反之为 0 则为 dead。
说明:从下往上遍历基本块,黑色-初始化;红色-第1次;蓝色-第2次;绿色-第3次。
结果:3次遍历后,IN[B] 不再变化,遍历结束。
5. Available Expressions Analysis
Available Expressions Analysis 是一个 must analysis。
问题定义:程序点 p 处的表达式 x op y
的值可用需满足2个条件,一是从 entry 到 p 点必须经过 x op y
,二是最后一次计算 x op y
之后,没有重定义操作数 x、y。(说明上一次计算的
x op y 的值还可用
)。
应用场景:用于优化,检测全局公共子表达式。
抽象表示:程序中的 n 个表达式,用长度为 n bit 的向量来表示,1表示可用,0表示不可用。
说明:属于forward分析。
(1)公式分析
Transfer Function:
理解:
genB — 基本块 B 中所有新的表达式(并且在这个表达式之后,不能对表达式中出现的变量进行重定义);
killB — 从 IN 中删除变量被重新定义的表达式。
Control Flow:
理解:从 entry 到 p 点的所有路径都必须保证 上一次最后一次计算该表达式的值 等于 现在计算该表达式的值(现在该表达式的值不用重新计算)。
问题:该分析为什么属于 must analysis 呢?
因为我们允许有漏报,不能有误报。虽然漏报了,但是不影响程序分析结果的正确性。
(2)算法
方法:首先将 OUT[entry] 初始化为空(因为目前没有一个表达式被计算),所有基本块的 OUT[B] 初始化为1...1。遍历每一个基本块 B,按以上两个公式计算块 B 的 IN[B] 和 OUT[B],只要这次遍历时有某个块的 OUT[B] 发生变化,则重新遍历一次。
(3)实例
抽象表示:程序中的 n 个表达式,用长度为 n bit 的向量来表示,1表示可用,0表示不可用。
说明:黑色-初始化;红色-第1次;蓝色-第2次。
结果:2次遍历后,OUT[B] 不再变化,遍历结束。
6. 三种分析技术对比
问题:怎样判断是 May 还是 Must?
Reaching Definitions 表示只要从赋值语句到点 p 存在1条路径,则为 reaching,结果不一定正确;
Live Variables 表示只要从点 p 到Exit 存在1条路径使用了变量 v,则为 live,结果不一定正确;
Available Expressions表示从 Entry 到点 p 的每一条路径都经过了该表达式,则为available,结果必须正确。