# SysY 编译器课程实验（C++）

本仓库为“并行编译课程实验”提供一个 SysY 编译器的最小可运行示例，实验按 Lab1–Lab6 逐步完成：
从前端（词法/语法分析与语法树处理）到中端（IR 生成、基本标量优化），再到后端（ARM64/AArch64 汇编生成、寄存器分配与后端优化），最后进行循环/并行相关优化。

## 1. 实验介绍

| 实验 | 名称 | 任务/目标 |
| --- | --- | --- |
| Lab1 | 语法树构建 | 基于 SysY 源程序完成语法分析与语法树构建 |
| Lab2 | 中间表示生成 | 将语法树翻译为 LLVM 风格的中间表示（IR），并输出 IR |
| Lab3 | 指令选择与汇编生成 | 将 IR 翻译为目标平台汇编代码（本项目以 ARM64/AArch64 为主） |
| Lab4 | 基本标量优化 | 实现常见的标量优化（如常量传播、死代码删除、简化 CFG 等） |
| Lab5 | 寄存器分配与后端优化 | 为后端生成的虚拟寄存器分配物理寄存器，并完成 spill/reload、冗余指令消除与局部后端优化 |
| Lab6 | 并行与循环优化 | 面向循环的优化（循环变换/并行化等），进一步提升程序性能 |

## 2. 参考资料

本仓库提供的示例代码和实验文档只是参考。我们非常鼓励大家在阅读当前仓库实现的同时，也结合自己的理解重新设计框架并完成实现，而不是机械照搬。

如果希望进一步参考编译相关项目和往届优秀实现，可以查看编译比赛官网的技术支持栏目：<https://compiler.educg.net/#/index?TYPE=26COM>。其中的“备赛推荐”整理了一些编译相关项目，也能看到往届优秀作品的开源实现，这些内容都很值得参考。

## 3. 头歌平台协作流程

头歌平台的代码托管方式与 GitHub/Gitee 类似。如果你希望基于当前仓库快速开始协作，可以参考下面这套流程。

### 3.1 组长 fork 课程仓库

组长打开课程仓库页面，点击右上角的 `Fork`，创建你们小组自己的仓库副本。后续组内开发统一基于这个 fork 后的仓库进行。

![组长 fork 课程仓库](doc/images/01.png)

### 3.2 组长邀请组员加入仓库

fork 完成后，组长进入自己的仓库页面，在右侧可以看到邀请码。把邀请码发给组员即可，组员不需要再 fork 课程仓库。

![组长查看邀请码](doc/images/02.png)

### 3.3 组员申请加入，组长审批通过

组员拿到邀请码后，可以在页面右上角的 `+` 菜单里选择 `加入项目`，然后提交加入申请。

![组员加入项目入口](doc/images/03.png)

申请发出后，组长到个人主页的待办事项中审批成员申请，同意后组员就可以正常参与仓库协作。

![组长审批组员申请](doc/images/04.png)

### 3.4 在本地克隆小组仓库并配置远端

组长和组员在成功加入小组仓库后，就可以从仓库页面复制 HTTPS 地址，在本地克隆代码：

![复制仓库 HTTPS 地址](doc/images/05.png)

下面示例使用 HTTPS 方式：

```bash
git clone <仓库 HTTPS 地址>
cd nudt-compiler-cpp
```

如果希望后续同步课程仓库更新，可以额外把课程主仓库配置为 `upstream`：

```bash
git remote add upstream https://bdgit.educoder.net/NUDT-compiler/nudt-compiler-cpp.git
git remote -v
```

配置完成后，常见的远端分工如下：

- `origin`：你们小组 fork 后的仓库，日常提交代码、推送分支都使用这个远端。
- `upstream`：课程主仓库，通常用于查看或同步课程团队发布的更新。

如果后续需要同步主仓库更新，可以先抓取远端信息：

```bash
git fetch upstream
```

### 3.5 提交与协作建议

借助 Git 进行协作开发，是当前软件开发中非常常见的一种工作方式，也是这门课程里需要大家掌握的基本能力。如果你对 Git 还不太熟悉，可以先看一下网络上的 Git 教程，例如：<https://liaoxuefeng.com/books/git/introduction/index.html>。

当然也没有必要一开始学得特别深入，只需要记住常见操作即可，例如 `clone`、`status`、`add`、`commit`、`pull`、`push`、分支切换与合并。遇到具体报错或不会处理的冲突时，可以把现象和命令发给大模型帮你分析。

Git Commit 提交的信息建议尽量写清楚，推荐使用下面的格式：

```text
<type>(<scope>): <subject>
```

常见的 `type` 有：

- `feat`：新增功能
- `fix`：修复 bug
- `refactor`：重构但不改变外部行为
- `docs`：文档修改
- `test`：测试相关
- `chore`：杂项维护

`scope` 用来说明改动的大致范围，例如 `frontend`、`irgen`、`backend`、`test`、`doc`。

`subject` 用一句简短的话说明“这次改了什么”。

例如：

```text
feat(irgen): 支持一元表达式生成
fix(frontend): 修复空语句解析错误
docs(doc): 补充实验环境配置说明
```

除了提交代码本身，也推荐大家把头歌平台上的协作功能真正用起来：

- `Issue` 适合用来拆分任务、记录 bug、整理讨论结果和跟踪待办。
- `PR` / `Merge Request` 适合用来做分支合并和代码评审。比较推荐的流程是：每个人在自己的分支上开发，完成一个相对独立的小功能后提交 PR，再由组内其他同学帮忙检查实现思路、代码质量和测试结果。

## 4. 实验环境配置

### 4.1 系统建议

建议使用 Ubuntu  22.04 或 WSL（Ubuntu  22.04 环境）。

### 4.2 安装基础依赖

本项目使用 CMake + C++17 构建；前端基于 ANTLR，运行 ANTLR 的 `antlr-*.jar` 需要 Java。

```bash
sudo apt update
sudo apt install -y build-essential cmake git openjdk-11-jre
```

### 4.3 安装 LLVM 工具链

`scripts/verify_ir.sh` 在 `--run` 模式下会调用 LLVM 工具链（`llc` 与 `clang`）将生成的 IR 编译、运行，并在存在同名 `.out` 时自动比对输出结果。

```bash
sudo apt update
sudo apt install -y llvm clang
```

### 4.4 安装 ARM64 交叉编译工具链与 QEMU

后续实验会生成 ARM64/AArch64 汇编代码，并使用 ARM64 交叉编译工具链完成汇编、链接；再用 QEMU 用户态模拟器运行生成的 ARM 可执行文件。

```bash
# 安装 ARM64 交叉编译工具链
sudo apt update
sudo apt install gcc-aarch64-linux-gnu

# 安装 QEMU 用户模式模拟器
sudo apt install qemu-user
```

## 5. 编译与运行

### 5.1 生成 Lexer/Parser

本仓库已内置 ANTLR jar：`third_party/antlr-4.13.2-complete.jar`。
当前 CMake 只会收集构建目录中的 Lexer/Parser 生成文件，不会自动调用 ANTLR；因此首次构建前，需要先生成 Lexer/Parser 及相关生成文件。

生成文件不提交到仓库，统一输出到 `build/generated/antlr4/`。

```bash
mkdir -p build/generated/antlr4

java -jar third_party/antlr-4.13.2-complete.jar \
  -Dlanguage=Cpp \
  -visitor -no-listener \
  -Xexact-output-dir \
  -o build/generated/antlr4 \
  src/antlr4/SysY.g4
```

### 5.2 Lab1 语法树构建

```bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=ON
cmake --build build -j "$(nproc)"
```

该模式只构建前端解析与语法树打印，不编译 `sem` / `irgen` / `mir`，适合 Lab1。

构建成功后，可执行文件位于：`./build/bin/compiler`。

运行语法树打印：

```bash
./build/bin/compiler --emit-parse-tree test/test_case/functional/simple_add.sy
```

### 5.3 全量构建

```bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=OFF
cmake --build build -j "$(nproc)"
```

该模式会继续编译 `sem` / `irgen` / `mir`，用于后续实验。

### 5.4 运行自检

运行帮助信息能正常输出，说明基本环境与可执行文件均正常：

```bash
./build/bin/compiler --help
```

若当前处于 Lab1，只需检查语法树输出是否符合预期。

若需要跑完整编译流程自检，则先使用全量构建模式，再执行下面的命令：从 SysY 源码生成 AArch64 汇编，完成汇编、链接，在 QEMU 下运行结果程序，并与 `test/test_case` 下同名 `.out` 自动比对：

```bash
./scripts/verify_asm.sh test/test_case/functional/simple_add.sy test/test_result/function/asm --run
```

如果最终看到 `输出匹配: test/test_case/simple_add.out`，说明当前示例用例 `return a + b` 的完整链路已经跑通。
但这条命令只适合做单个用例检查。完成对应实验后，不能只停留在 `simple_add`，还应覆盖 `test/test_case` 下全部测试用例；如有需要，也可以自行编写批量测试脚本统一执行。
