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

控制流的翻译

TAC 指令不支持 Decaf 语言中的条件、循环等高层次的控制流语句,而仅支持底层的跳转语句。 为此,我们需要将这些高层次的控制流语句翻译为语义等价的跳转语句。 这里,我们仅给出一个 while 循环的例子(TacEmitter.java):

@Override
default void visitWhile(Tree.While loop, FuncVisitor mv) {
    var exit = mv.freshLabel();
    Function<FuncVisitor, Temp> test = v -> {
        loop.cond.accept(this, v);
        return loop.cond.val;
    };
    Consumer<FuncVisitor> body = v -> {
        loopExits.push(exit);
        loop.body.accept(this, v);
        loopExits.pop();
    };
    emitWhile(test, body, exit, mv);
}

这里我们先调用 freshLabel 得到一个新的标签,它将在 TAC 指令中用来标识循环的出口。 而 test 是一个函数(lambda 表达式),它在定义的时候还未执行,在调用它的时候能够生成计算循环条件的那一段 TAC 指令。 之所以这样,是因为这一段 TAC 代码在辅助函数 emitWhile 执行的时候,才真正地生成并插入到 FuncVisitor 中。 同理,body 也是一个函数,在调用它的时候能够生成执行一次循环体的 TAC 指令。 最终,辅助函数 emitWhile 将实际上完成与循环语义等价的 TAC 指令生成。 其功能是,将如下形式的循环

while (cond) {
    block
}

转换为语义等价的 TAC 程序,形如

entry:
    cond = do test
    if (cond == 0) branch exit
    do block
    branch entry
exit:

即:进入 entry 标签后,先计算循环条件是否满足,把结果存在 cond 临时变量中。 若条件为真,则进入循环体;否则,退出循环。这一步采用条件跳转指令实现。 由于 exit 标签标记了整个循环的出口,那么在条件为假时,就跳转到 exit 标签;否则往下执行一次循环体。 循环体执行完毕后,我们还需要再次从循环头开始执行下一次,故直接跳转到 entry 标签即可。

Previous面向对象机制NextPA4:中间代码优化

Last updated 5 years ago

Was this helpful?