跳转至

IR 自动化生成

学生将在本阶段提供的实验框架下,利用访问者模式遍历抽象语法树,调用 Light IR C++ 库,实现 IR 自动化生成。

实验框架介绍

抽象语法树

分析树(Parse Tree)与抽象语法树(Abstract Syntax Tree)

分析树在语法分析的过程中被构造;抽象语法树则是分析树的浓缩表示,使用运算符作为根节点和内部节点,并使用操作数作为子节点。进一步了解可以阅读 分析树和抽象语法树的比较。

实验框架实现了 Lab1 生成的分析树到 C++ 上的抽象语法树的转换。可以使用访问者模式来实现对抽象语法树的遍历,ast.hpp 文件中包含抽象语法树节点定义。

符号表 Scope

在 include/cminusf_builder.hpp 中,我们定义了一个用于存储作用域的类 Scope。它的作用是辅助我们在遍历语法树时,管理不同作用域中的变量。它提供了以下接口:

// 进入一个新的作用域
void enter();
// 退出一个作用域
void exit();
// 往当前作用域插入新的名字->值映射
bool push(std::string name, Value *val);
// 根据名字,寻找到值
Value* find(std::string name);
// 判断当前作用域是否是全局作用域
bool in_global();

你需要根据语义合理调用 enter 与 exit,并且在变量声明和使用时正确调用 push 与 find。

CminusfBuilder 类

CminusfBuilder 类定义在 cminusf_builder.hpp文件中,CminusfBuilder 类中定义了对抽象语法树不同语法节点的 visit 函数,实验已给出了一些语法树节点的访问规则,其余的需要学生补充。

在 CminusfBuilder 构造函数函数中,下列代码片段是对 Cminusf 语义中的 4 个预定义函数进行声明并加入全局符号表中,在生成 IR 时可从符号表中查找。我们的测试样例会使用这些函数,从而实现 IO。

scope.enter();
scope.push("input", input_fun);
scope.push("output", output_fun);
scope.push("outputFloat", output_float_fun);
scope.push("neg_idx_except", neg_idx_except_fun);

CminusfBuilder 类使用成员 context 存储翻译时状态,下列代码片段是 context 的定义,学生需要为该结构体添加更多域来存储翻译时的状态。

struct {
    // function that is being built
    Function *func = nullptr;
} context;

实验内容

阅读 Cminusf 语义,并根据语义补全 include/cminusfc/cminusf_builder.hpp 与 src/cminusfc/cminusf_builder.cpp 文件,实现 IR 自动产生的算法,使得它能正确编译任何合法的 Cminusf 程序,生成符合 Cminusf 语义的 IR。

友情提示:

  1. 请比较通过 cminusfc 产生的 IR 和通过 clang 产生的 IR 来找出可能的问题或发现新的思路。
  2. 使用 GDB 进行调试来检查错误的原因。
  3. 我们为 Function、Type 等类都实现了 print 接口,可以使用我们提供的 logging 工具 进行打印调试。
  4. 对于 C++ 不熟悉的学生可以参考 C++ 简介。

实验要求

仓库目录结构

与该阶段有关的文件如下。

.
├── CMakeLists.txt
├── include                             <- 实验所需的头文件
│   ├── ...
|   ├── cminusfc
|   |    └── cminusf_builder.hpp        <- 该阶段需要修改的文件
│   ├── lightir/*
│   └── common
│        ├── ast.hpp
│        ├── logging.hpp
│        └── syntax_tree.h
├── src
│   ├── ...
│   └── cminusfc
│       ├── cminusfc.cpp                <- cminusfc 的主程序文件
│       └── cminusf_builder.cpp         <- 该阶段需要修改的文件
└── tests
    ├── ...
    └── 2-ir-gen
        └── autogen
            ├── testcases                <- 助教提供的测试样例
            ├── answers                  <- 助教提供的测试样例
            └── eval_lab2.py             <- 助教提供的测试脚本

编译、运行和评测

编译

$ cd 2023ustc-jianmu-compiler
$ mkdir build
$ cd build
# 使用 cmake 生成 makefile 等文件
$ cmake ..
# 使用 make 进行编译
$ make
# 安装以链接 libcminus_io.a
$ sudo make install

如果构建成功,你会在 build 文件夹下找到 cminusfc 可执行文件,它能将 cminus 文件输出为 IR 文件,编译成二进制可执行文件。

运行

我们在 tests/testcases_general 文件夹中准备了一些通用案例。当需要对 .cminus 单个文件测试时,可以这样使用:

情况一:生成 IR 文件

Note

为了让 cminusfc 在 $PATH 中,一定要 sudo make install。

# 假设 cminusfc 的路径在你的 $PATH 中,并且你现在在 test.cminus 文件所在目录中
$ cminusfc test.cminus -emit-llvm

此时会在同目录下生成同名的 .ll 文件,在这里即为 test.ll。

情况二:生成可执行文件

上面生成的 .ll 文件用于阅读,如果需要运行,需要调用 clang 编译链接生成二进制文件 test

$ clang -O0 -w -no-pie test.ll -o test -lcminus_io

Note

上面的命令编译了 test.ll,并链接了 cminus_io 库。

测试

自动测试脚本和所有测试样例都是公开的,它在 tests/2-ir-gen/autogen 目录下,使用方法如下:

# 在 tests/2-ir-gen/autogen 目录下运行:
$ python3 ./eval_lab2.py
$ cat eval_result

测试结果会输出到 tests/2-ir-gen/autogen/eval_result。