1. 数据流分析

  • 我们目前已经学习了如何编译生成可执行代码,但是此时生成的代码的质量并不能够让人满意。因此,我们希望消除目标代码中的冗余指令,并且将低效的指令序列替换为具有相同功能的更加高效的序列,这便是“代码优化”。

  • 编译期优化需要在不运行程序的条件下获知程序的行为信息,因此会用到不同的静态分析(Static Analysis)技术:

    • 数据流分析:基于格(Lattice)理论研究数据流值(Dataflow Facts)在程序中的传播
    • 符号执行:使用符号值代替具体值执行程序,探索路径约束
    • 抽象解释:将程序的状态映射到抽象域
    • 模型检验:穷举程序状态空间检验程序性质

1.1 基本概念

1.1.1 控制流图(Control Fow Graph,CFG)

控制流图 表示了程序的控制流结构: - 节点 :基本块(Basic Block) - :控制流转移

1.1.2 数据流值(Dataflow Facts)

对于每个程序点,定义两个集合: - :基本块 入口处的数据流值 - :基本块 出口处的数据流值

1.1.3 转移函数(Transfer Function)

每个基本块 的语义通过转移函数 描述: 其中 的具体形式取决于具体的分析类型。

1.1.4 数据流值初始化

根据分析的方向有不同的初始化要求。

  • 前向分析(Forward Analysis):

    • 开始沿着CFG边方向分析
    • 需初始化

  • 后向分析(Backward Analysis):

    • 开始沿着CFG边反方向进行分析

    • 需初始化

1.1.5 数据流合并操作

在交汇点(如分支合并)执行格(Lattice)上的合并操作: 其中 meet操作,具体操作取决于分析类型,常见的操作如集合的并或交。

1.2 Worklist算法

Worklist算法是广度优先遍历(BFS)算法的变体,通过跳过不发生变化的结点的方式进行剪枝,使得冗余计算次数减少。

1.3 数据流分析理论基础

1.4 数据流分析的例子

1.4.1 到达定义分析(Reaching Definitions)

  • 目标:计算每个程序中点能到达的变量定义

  • 格结构,其中 为所有定义的集合

  • 转移函数

  • 合并:取并集

1.4.2 活跃变量分析(Live Variables)

  • 目标:确定变量在程序点是否会被后续使用

  • 格结构 为变量集合

  • 转移函数

  • 合并:取并集

1.4.3 可用表达式分析(Available Expressions)

  • 目标:判断表达式在程序点是否已被计算且未修改

  • 格结构 为表达式集合

  • 转移函数

  • 合并:取交集

1.4.4 小结

对于已学的三种数据流分析对比如下:

1.5 常量传播

1.5.1 常量传播分析的特点

  1. 有无穷多的数据流值(本质原因是常量取值的值域是无限的)
  2. 不是分配的(Non-Distributive)

1.5.2 格设计

  • 值域
    • : Undef,未初始化
    • : 已知常量值
    • : NAC,Not A Constant,非常量

1.5.3 转移函数规则

对于语句,定义(n为变量数):

  1. 赋值语句 x = expr 其中的递归定义:

  2. 其它语句,不会传播常量,保持状态不变。(忽略load、store等内存操作)

1.5.4 合并规则

常量传播数据流值的合并规则如下:

1.5.5 常量传播的改进(选学)

在传统的常量传播基础上,可以进行以下优化:

  1. 稀疏常量传播

  2. 条件常量传播

  3. 稀疏条件常量传播(Sparse Conditional Constant Propagation)

    • 将稀疏常量传播与条件常量传播相结合,就是当前主流的常量传播算法,稀疏条件常量传播算法,通常简写为 SCCP
    • LLVM 集成了 SCCP 为一个内置Pass,你可以通过 opt -sccp 开启SCCP优化。

1.6 稀疏数据流分析


2. 符号执行

3. 过程间分析

4. 指针分析

5. Datalog程序分析