跳转至

Light IR 预热

本阶段实验需要完成以下两部分

  1. 阅读 Light IR 手册,了解 LLVM IR 与 Light IR 关系,认识 Light IR 的结构及指令语法。在 clang 翻译出的 IR 作为参考下,手动编写 IR 文件。

  2. 了解并掌握 Light IR C++ 库的使用方法,通过调用 Light IR C++ 库提供的接口生成 IR 文件。

与 Light IR 预热实验相关文件如下:

.
├── ...
├── include
│   ├── common
│   └── lightir/*
└── tests
    ├── ...
    └── 2-ir-gen
        └── warmup
            ├── CMakeLists.txt
            ├── c_cases           <- 需要翻译的 c 代码
            ├── calculator        <- 助教编写的计算器示例
            ├── stu_ll            <- 学生需要手动编写的 .ll 代码   (Task 1)
            ├── stu_cpp           <- 学生需要编写的 .ll 代码生成器 (Task 2)
            └── ta_gcd            <- 助教编写的 .ll 代码手动生成器示例

Warning

我们已经创建了 stu_llstu_cpp 目录下的相应空文件,请不要修改文件名!否则希冀平台的测试将会失败。

IR 介绍

LLVM IR

根据维基百科的介绍,LLVM 是一个自由软件项目,它是一种编译器基础设施,以 C++ 写成,包含一系列模块化的编译器组件和工具链,用来开发编译器前端和后端。IR 的全称是 Intermediate Representation,即中间表示。LLVM IR 是 LLVM 项目定义的编译器中间代码。

LLVM IR 指令参考手册:Reference Manual

使用 clang 生成 LLVM IR 并利用 lli 运行

LLVM 相关软件,我们已经在 lab0 阶段安装好。

LLVM IR 文件以 `.ll` 为文件后缀,clang 是 LLVM 工具链中的前端,可实现从 C 语言向 LLVM IR 的翻译,操作流程如下:

```shell
# 可用 clang 生成 C 代码对应的 .ll 文件
$ clang -S -emit-llvm gcd_array.c
# lli 可用来执行 .ll 文件
$ lli gcd_array.ll
# `$?` 的内容是上一条命令所返回的结果,而 `echo $?` 可以将其输出到终端中
$ echo $?
```

gcd_array.c 是实验提供的例子,学生可以通过使用 clang 翻译示例,并查阅 Light IR 手册来理解每条 LLVM IR 指令含义。

Light IR

LLVM IR 的目标是成为一种通用 IR(支持包括动态与静态语言),因此 IR 指令种类较为复杂繁多。本课程从 LLVM IR 中裁剪出了适用于教学的精简的 IR 子集,并将其命名为 Light IR。

Light IR 指令参考手册:Light IR 手册

Task 1:手工编写 IR 文件

实验内容

实验在 tests/2-ir-gen/warmup/c_cases/ 目录下提供了四个 C 程序:

  • assign.c
  • fun.c
  • if.c
  • while.c

学生需要在 test/2-ir-gen/warmup/stu_ll 目录中,手动将这四个 C 程序翻译成 IR 代码,得到 assign_hand.llfunc_hand.llif_handf.llwhile_hand.ll,可参考 clang -S -emit-llvm 的输出。

运行、测试

我们主要通过 lli 运行 tests/2-ir-gen/warmup/stu_ll 目录下四个.ll 文件,并通过检查返回值来判断 .ll 文件的正确性。

关于 .ll 文件的运行与测试,请参考这里

Task 2:使用 C++ 库生成 IR 文件

Light IR C++ 库介绍

LLVM 项目提供了辅助 IR 生成的 C++ 库,但其类继承关系过于复杂,并且存在很多为了编译性能的额外设计,不利于学生理解 IR 抽象。因此实验依据 LLVM 的设计,为 Light IR 提供了配套简化的 C++ 库。与 LLVM IR C++ 库相比,Light IR C++ 库仅保留必要的核心类,简化了核心类的继承关系与成员设计,且给学生提供与 LLVM 相同的生成 IR 的接口。

Light IR IR C++ 库参考手册:Light IR cpp APIs

使用 Light IR C++ 库生成 IR 示例

阅读样例 gcd_array.c, gcd_array_generator.cpp。结合该样例的注释与 Light IR C++ 库章节,掌握使用 Light IR C++ 库生成 IR 的方法

实验内容

实验在 tests/2-ir-gen/warmup/c_cases/ 目录下提供了四个 C 程序。学生需要在 tests/2-ir-gen/warmup/stu_cpp/ 目录中,参考上面提供的 gcd_array_generator.cpp 样例,使用 Light IR C++ 库,编写 assign_generator.cppfun_generator.cppif_generator.cppwhile_generator.cpp 四个 cpp 程序。这四个程序运行后应该能够生成 tests/2-ir-gen/warmup/c_cases/ 目录下四个 C 程序对应的 .ll 文件。

编译、运行、测试

ZLIB 相关报错

如果你在 cmake .. 一步遇到如下报错Target "IR_lib" links to target "ZLIB::ZLIB" but the target was not found.,请使用 sudo apt install zlib1g-dev 安装 zlib 库,然后重新 cmake .. && make

编译

$ cd 2024ustc-jianmu-compiler
$ mkdir build
$ cd build
# 使用 cmake 生成 makefile 等文件
$ cmake ..
# 使用 make 进行编译
$ make

如果构建成功,你会在 build 文件夹下找到 gcd_array_generator, stu_assign_generator 等可执行文件。

运行与测试

# 在 build 目录下操作
$ ./gcd_array_generator > gcd_array_generator.ll
$ lli gcd_array_generator.ll
$ echo $?

你可以通过观察原来的 C++ 代码来推断 echo $? 应该返回的正确结果,也可以通过 clang -S -emit-llvm 编译 tests/2-ir-gen/warmup/c_cases 目录下 4 个 CPP 文件获得 4 个相应的 .ll 文件,再用 lli 执行 .ll 文件来获取正确结果。

思考题

  1. Light IR 手册里,你已经了解了 IR 代码的基本结构,请尝试编写一个有全局变量的 cminus 程序,并用 clang 编译生成中间代码,解释全局变量在其中的位置。
  2. Light IR 中基本类型 label 在 Light IR C++ 库中是如何用类表示的?
  3. Light IR C++ 库中 Module 类中对基本类型与组合类型存储的方式是一样的吗?请尝试解释组合类型使用其存储方式的原因。