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. Rust 框架分阶段指导
  2. PA3:中间代码生成

中间代码中的类型信息

从原始的tac本身来看似乎是不带有任何类型信息的,最终生成的汇编也不会带有类型信息,那么为什么还要向tac传递类型信息呢?这是为了在pa4中能获得更好的代码优化效果。这也是类型系统的作用之一,它可以指导代码优化,帮助生成更加高效的机器码。

常见的中间代码中一般都包含了类型信息。以jvm的byte code为例,byte code中保留了非常丰富的类型信息,例如一句

  System.out.println("hello world");

javac生成的byte code是:

  getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
  ldc           #3                  // String hello world
  invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V

这完全不像我们的tac中,可能会算出out的位置,println在虚表中的偏移量,然后用访存+call来实现这次调用。表面看起来javac似乎是很"不负责任"的,因为这样的中间代码如果直接按照字面意思来执行,显然是非常低效的;然而实际上正是因为这些信息的保留,让jvm可以充分利用它来优化代码,也很自然地就支持了反射等动态特性。

那么为什么类型信息可以帮助优化代码呢,这里可以举一个非常简单的例子:

  static int foo(A a, B b) {
    b.a = 1;
    a.a = 2;
    return b.a;
  }

假设A和B是两个java类,二者之间没有继承关系,那么可以直接断定这个函数的返回值是1,jvm做起这种优化来非常轻松。而如果中间代码只提供了这些信息:

  *(_T1 + xxx) = 1
  *(_T0 + yyy) = 2
  return *(_T1 + xxx)

那么只通过这个函数本身来确定它的返回值是不可能的。

当然,虽然上面说了这么多,我们能够做的优化其实是非常初级的,这个大家到了pa4就会看到。我们传递的类型信息也是很简单的,包括:

  1. 访存指令的性质,它是load/store了数组,对象,还是一块"不可变"的内存区域?

    • "不可变"的内存区域包括访问虚表指针,访问虚表,访问数组长度等,对它们的store相当于赋初值

  2. 函数参数表中是否有对象/数组为参数?

在pa4中的公共表达式提取这种优化手段中我们会描述怎么样使用这些信息。再次声明我们的实现非常简单,甚至显得有些幼稚,主要目的还是展示类型信息的确可以帮助中间代码优化,并不期望真的能够达到多么优秀的优化效果。

Previous中间代码Next运行时存储布局

Last updated 5 years ago

Was this helpful?