The Decaf Book
  • Introduction
  • 语言规范
  • Java 框架分阶段指导
    • 本章导引
    • PA1-A:语法分析器的自动构造
      • 阶段任务
      • 词法分析
      • 抽象语法树
      • 文法分析
    • PA1-B:基于 LL(1) 的语法分析器半自动构造
      • 阶段任务
      • LL(1) 文法分析
      • 错误恢复
    • PA2:语义分析
      • 阶段任务
      • 类型、符号和作用域
      • 访问者模式
      • 符号表构建
      • 类型检查
    • PA3:中间代码生成
      • 阶段任务
      • TAC 程序
      • 面向对象机制
      • 控制流的翻译
    • PA4:中间代码优化
      • 实验内容
      • 基本块
      • 数据流分析概述
      • 数据流优化概述
      • 公共表达式提取
      • 复写传播
      • 常量传播
      • 死代码消除
  • Scala 框架分阶段指导
    • 本章导引
    • PA1:语法分析器的自动构造
      • 阶段任务
      • 词法分析
      • 抽象语法树
      • 文法分析
      • 访问者模式
    • PA2:语义分析
      • 阶段任务
      • 类型、符号和作用域
      • 符号表构建
      • 类型检查
    • PA3:中间代码生成
      • 阶段任务
      • TAC 程序
      • 面向对象机制
      • 控制流的翻译
    • PA3-JVM:JVM 字节码生成
      • 阶段任务
      • JVM 字节码简介
      • 翻译过程
    • PA4:中间代码优化
      • 实验内容
      • 基本块
      • 数据流分析概述
      • 数据流优化概述
      • 公共表达式提取
      • 复写传播
      • 常量传播
      • 死代码消除
  • Rust 框架分阶段指导
    • 本章导引
    • PA1-A:语法分析器的自动构造
      • 实验内容
      • lalr1使用指导
        • 编写lexer
        • impl块的可选属性
        • 产生式和语法动作
        • 解决冲突
        • 一个完整的例子
      • 抽象语法树
      • 框架中部分实现的解释
      • 文件结构
    • PA1-B:基于 LL(1) 的语法分析器半自动构造
      • 实验内容
      • lalr1使用指导
      • 错误恢复
      • 文件结构
    • PA2:语义分析
      • 实验内容
      • 语义分析
      • 符号表
      • visitor模式
    • PA3:中间代码生成
      • 实验内容
      • 中间代码
      • 中间代码中的类型信息
      • 运行时存储布局
      • 面向对象机制
      • tacvm简述
    • PA4:中间代码优化
      • 基本块
      • 数据流分析概述
      • 数据流优化概述
      • 公共表达式提取
      • 复写传播
      • 常量传播
      • 死代码消除
    • PA5:寄存器分配
      • 实验内容
      • 图着色基本原理
      • 着色算法
      • 预着色节点
      • 干涉图节点合并
      • 调用约定
Powered by GitBook
On this page

Was this helpful?

  1. Scala 框架分阶段指导
  2. PA4:中间代码优化

数据流优化概述

具体的优化方法详见各自的文档,这里只讲一下把它们组合起来之后得到的结果。

这些优化方法间不是独立的,某种优化可能会产生其它的优化机会,比如说:

  a = 1
  b = a + c

经过常量传播后变成

  a = 1
  b = 1 + c

此时可能a不再活跃,a = 1一句可以被死代码消除的过程删除。比较实际的例子是:公共表达式提取往往会产生很多复写语句,复写传播将这些复写语句的源操作数尽量向复写的源头传播,这样就会产生很多无用的复写语句,从而在死代码消除中被删除。

某种优化也可能阻止其它的优化进行,例如:

  if (b) {
    x + y; -> tmp = x + y;
  } else {
    x + y; -> tmp = x + y;
  }
  z = x + y; -> z = tmp;

如果先进行公共表达式提取,会导致if-else中的两个x + y都在后面被用到(从而z就不用再重复计算),而这两个多余的式子本来是可以被死代码删除给删掉的,现在则不能了。当然,最后的这个结果不是最优的根本原因还是在于我们实现的这些优化都是很初级的,如果能够使用更高级的优化技术,肯定还算可以把它优化到最简洁的形式。

一趟各种优化进行完之后,可能会产生新的优化机会,例如对下面的代码进行死代码消除(基于活跃变量分析):

  c = a + b
  d = b + c
  e = c + d
  ...
  z = x + y

如果z最后没有被用到,那么最后一句将被标记成死代码而删除,如果a-y也都没有在其它地方被用到,那么一轮死代码消除就会从中删除一条语句,不断迭代直到删光。可以想象这样的过程其实不是很高效:每轮计算中有大量的计算被重复了。虎书中给出了一些方法,可以尽量保存计算结果或者是在一趟优化中"看的更远",执行更多的优化。

我们的简单的优化器没有做这种特殊处理,采用的就是暴力的重复的方式:

pub fn optimize(&mut self) {
  crate::common_expr::work(self);
  crate::copy_prop::work(self);
  crate::const_prop::work(self);
  crate::aliveness::work(self);
}

pub fn optimizen(&mut self, n: u32) {
  for _ in 0..n { self.optimize(); }
}

测试代码中设定优化10次即停止,保证不会消耗太多的时间。请不要修改这个数字和各个优化执行的顺序,这涉及到判断大家的实现结果是否正确。

Previous数据流分析概述Next公共表达式提取

Last updated 5 years ago

Was this helpful?

或者,如果不是采用活跃变量分析来进行死代码消除,而是采用Faint Variables Analysis(我不知道怎么翻译),则可以一次性识别出这些语句都是死代码。关于Faint Variables Analysis可参考资料(从第22页起),这不在课程要求范围内。

general-frameworks