# 实验进度与测试方法

## 1. 当前实验进度

本文档用于记录当前仓库在各个 Lab 上的实现状态，以及对应的测试与验证方式。  
需要注意：本仓库当前仍处于“课程示例框架 + 逐步补全”的阶段，并不是一个已经完整实现全部 SysY 语义的编译器。

### 1.1 Lab1 当前进度

Lab1 对应前端语法分析与语法树构建。

当前状态：

- 已提供 `SysY.g4`、ANTLR 驱动与语法树打印能力。
- 已支持通过 `--emit-parse-tree` 输出语法树。
- 可使用 `parse-only` 模式单独构建前端，不依赖 `sem` / `irgen` / `mir`。

### 1.2 Lab2 当前进度

Lab2 对应“语法树 -> 语义检查 -> IR”。

当前状态可以拆成两部分来看：

1. `Sema`
   - 已完成一版基于当前 SysY grammar 的语义检查基础实现。
   - 已支持多层作用域、变量/常量重定义检查、先声明后使用。
   - 已支持函数符号收集、函数调用检查、`main` 入口检查。
   - 已支持 `break` / `continue` 使用位置检查。
   - 已支持 `return` 与函数返回类型匹配检查。
   - 已支持 `const` 常量表达式求值、数组维度检查、全局初始化常量性检查。
   - 已支持 `int/float` 标量表达式、比较、逻辑表达式的基础类型检查。
   - 已内建 `getint`、`putch`、`getfloat`、`getarray`、`putarray` 等常见运行库函数声明。

2. `IRGen`
   - 当前仓库原有 `IRGen` 仍是最小示例版本。
   - 当前只适合支持“局部 `int` 变量 + 常量 + 简单表达式 + `return`”这类极小子集。
   - 由于 grammar 已扩展，而 `IRGen` 尚未完全同步，所以 Lab2 目前**只完成了前半部分：Sema 基础扩展**。
   - Lab2 的 IR 生成部分仍需继续补全。

### 1.3 Lab3 当前进度

Lab3 对应“IR -> MIR -> 汇编”。

当前状态：

- 仓库中保留了最小后端链路。
- 仅适合消费当前最小 IR 子集。
- 尚不具备对完整 SysY 程序稳定生成汇编的能力。

### 1.4 Lab4-Lab6 当前进度

当前仓库已经预留：

- IR 分析与 Pass 目录结构
- `Mem2Reg`、`ConstFold`、`ConstProp`、`DCE`、`CSE`、`CFGSimplify` 等文件框架
- 循环分析、支配树、后端优化等实验入口

但这些阶段是否“完成”，取决于你们后续自行补全，不应默认认为仓库当前已经完全实现。

## 2. 推荐测试思路

建议把测试分成三层：

1. `单阶段验证`
   - 只验证某个阶段是否工作，例如只看 parse、只看 sema、只看 IR 输出。

2. `链路验证`
   - 从源码一路走到 IR 或汇编，再运行程序，比对 `.out`。

3. `批量回归`
   - 对 `test/test_case` 下多个测试统一执行，避免只靠 `simple_add.sy` 判断功能是否完成。

## 3. 别人拉取当前实现后的推荐编译方式

如果其他同学拉取了当前仓库，建议按下面顺序准备环境并编译。

### 3.1 先生成 ANTLR 输出

当前仓库的 CMake 会收集构建目录中的 ANTLR 生成文件，但不会自动调用 ANTLR，所以第一次构建前应先执行：

```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
```

### 3.2 如果只想验证 Lab1

只构建 parse-only 前端：

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

构建后可直接运行：

```bash
./scripts/test_lab1.sh test/test_case/functional
```

### 3.3 如果想验证当前 Lab2 的 Sema 部分

由于当前仓库中的 `IRGen` 还没有完全跟上新 grammar，而我们这次主要完成的是 `Sema`，所以推荐单独准备一个 `build-sema/` 目录来验证语义检查。

推荐命令如下：

```bash
cmake -S . -B build-sema -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=OFF
mkdir -p build-sema/generated
cp -r build/generated/antlr4 build-sema/generated/
cmake --build build-sema --target frontend utils sem -j "$(nproc)"
```

然后编译 `sema_check`：

```bash
g++ -std=c++17 \
  -Iinclude \
  -Isrc \
  -Ibuild-sema/generated/antlr4 \
  -Ithird_party/antlr4-runtime-4.13.2/runtime/src \
  tools/sema_check.cpp \
  build-sema/src/sem/libsem.a \
  build-sema/src/frontend/libfrontend.a \
  build-sema/src/utils/libutils.a \
  build-sema/libantlr4_runtime.a \
  -pthread \
  -o build-sema/sema_check
```

完成后即可运行：

```bash
./scripts/test_lab2_sema.sh positive
./scripts/test_lab2_sema.sh negative
```

说明：

- `build/` 主要用于 Lab1 parse-only 或后续全量构建
- `build-sema/` 主要用于当前阶段单独验证 `Sema`
- `scripts/test_lab2_sema.sh` 依赖 `./build-sema/sema_check`

### 3.4 如果后续要做全量构建

等 `IRGen` 与 grammar 完全同步后，可直接做全量构建：

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

但在当前阶段，不建议把“全量 build 成功”作为验证 `Sema` 的唯一标准，因为 Lab2 目前完成的是语义分析前半部分，不是整套 IR 生成。

## 4. Lab1 测试方法

### 3.1 构建命令

先生成 ANTLR 输出：

```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
```

然后使用 `parse-only` 构建：

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

### 3.2 单个样例测试

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

### 3.3 批量测试

仓库已提供 parse 批量测试脚本。为避免终端直接打印大量语法树导致输出过长，脚本会把每个用例的语法树输出写入单独日志文件。

```bash
./scripts/test_lab1.sh test/test_case/functional
```

如果希望指定日志目录，可以使用：

```bash
./scripts/test_lab1.sh test/test_case/functional test/test_result/lab1_parse_logs
```

终端中会看到形如：

```text
TEST test/test_case/functional/simple_add.sy -> test/test_result/lab1_parse_logs/simple_add.parse.log
...
ALL_PARSE_OK (...) logs: test/test_result/lab1_parse_logs
```

说明当前测试目录中的 `.sy` 文件都能通过语法分析；具体语法树内容可直接查看对应 `.parse.log` 文件。

## 5. Lab2 测试方法

Lab2 建议分成两部分测试：`Sema` 和 `IRGen`。

### 4.1 Lab2 当前推荐先测 Sema

因为当前仓库中 `IRGen` 还未完全同步到新 grammar，所以当前阶段更适合先用“语义检查”来证明 Lab2 前半部分已经实现。

#### 4.1.1 当前已验证通过的正例

下面这些测试用例已经可以作为当前 `Sema` 的正向样例：

```bash
./scripts/test_lab2_sema.sh positive
```

如果希望指定日志目录，可以使用：

```bash
./scripts/test_lab2_sema.sh positive test/test_result/lab2_sema_positive_logs
```

预期现象：

- 终端按用例打印 `TEST ... -> ...`
- 全部通过后输出 `ALL_SEMA_POSITIVE_OK (...)`
- 详细输出写入 `*.sema.log`

#### 4.1.2 当前可用于演示的反例

当前已经准备好的反例位于：

- `test/test_case/sema_negative/undef.sy`
- `test/test_case/sema_negative/break.sy`
- `test/test_case/sema_negative/ret.sy`
- `test/test_case/sema_negative/call.sy`

执行命令：

```bash
./scripts/test_lab2_sema.sh negative
```

如果希望指定日志目录，可以使用：

```bash
./scripts/test_lab2_sema.sh negative test/test_result/lab2_sema_negative_logs
```

预期现象：

- 终端按用例打印 `TEST ... -> ...`
- 全部符合预期后输出 `ALL_SEMA_NEGATIVE_OK (...)`
- 每个反例的详细错误信息写入对应 `.sema.log`

例如：

- 使用未声明变量
- 循环外 `break`
- `void` 函数返回值
- 函数参数个数不匹配

#### 4.1.3 语义错误定位信息说明

语义错误信息中的 `@行:列` 用于标明错误位置。

例如：

```text
[error] [sema] @1:19 - 使用了未声明的标识符: a
```

表示：

- `1` 是第 1 行
- `19` 是第 19 列

也就是提示错误出现在源代码第 1 行第 19 列附近，便于快速定位。

#### 4.1.4 当前 Sema 已覆盖的主要错误类型

当前已实现的典型错误检测包括：

- 未声明标识符使用
- 同作用域重定义
- 函数重定义
- 缺少合法 `main`
- 函数参数数量或类型不匹配
- `break/continue` 不在循环中
- `return` 与函数返回类型不匹配
- 给 `const` 对象赋值
- 数组维度非法
- 全局初始化不满足编译期常量要求

### 4.2 Lab2 后续 IR 测试方式

当 `IRGen` 与当前 grammar 对齐后，可使用如下命令输出 IR：

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

若需要进一步验证 “IR -> 可执行程序” 链路，可使用：

```bash
./scripts/verify_ir.sh test/test_case/functional/simple_add.sy test/test_result/ir --run
```

但需要强调：  
在当前仓库状态下，这条命令只适合用于未来 IRGen 完成后的测试；不能拿它来证明当前已完成的 `Sema` 部分。

## 6. Lab3 测试方法

Lab3 对应汇编输出与后端链路。

### 5.1 构建

需要全量构建：

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

### 5.2 单个样例输出汇编

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

### 5.3 汇编链路验证

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

`--run` 模式下会：

1. 生成汇编
2. 交叉编译为 AArch64 可执行文件
3. 用 `qemu-aarch64` 运行
4. 将输出与同名 `.out` 比对

## 7. Lab4 测试方法

Lab4 是优化实验，测试重点不只是“能不能运行”，还包括“优化前后语义一致”。

建议按下面顺序验证：

1. 先确保未优化版本功能正确
2. 接入优化后再次跑 `verify_ir.sh` 或 `verify_asm.sh`
3. 比较优化前后的 IR 或汇编输出
4. 在多个测试上回归，避免某个优化只在 `simple_add` 上看起来没问题

推荐命令：

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

如果你们为优化实现了单独开关，也应额外对比：

```bash
./build/bin/compiler --emit-ir test/test_case/functional/simple_add.sy
./build/bin/compiler --emit-asm test/test_case/functional/simple_add.sy
```

## 8. Lab5 测试方法

Lab5 的测试重点是：

- 寄存器分配后代码仍然正确
- spill/reload 逻辑没有破坏语义
- 汇编仍能完整运行

推荐直接走后端完整链路：

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

完成寄存器分配后，不应只测单个样例，建议至少覆盖：

- `functional/`
- `performance/` 中若干较大样例

## 9. Lab6 测试方法

Lab6 重点是循环和并行相关优化，测试要分成功能正确性和优化收益两部分。

### 8.1 功能正确性

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

### 8.2 优化效果观察

你们可以对比优化前后的：

- IR 输出
- 汇编输出
- 执行时间
- 代码规模

例如：

```bash
./build/bin/compiler --emit-ir test/test_case/functional/simple_add.sy
./build/bin/compiler --emit-asm test/test_case/functional/simple_add.sy
```

真正评估循环优化时，建议使用包含明显循环结构的功能或性能测试，而不是只看 `simple_add.sy`。

## 10. 当前阶段的建议结论

如果你要汇报当前仓库状态，可以概括为：

1. Lab1 的语法树构建链路已经具备独立测试方式。
2. Lab2 当前已经完成 `Sema` 基础扩展，并可通过正反例直接演示。
3. Lab2 的 `IRGen` 还需要继续补全，当前不能把整份 Lab2 视为全部完成。
4. Lab3 及后续实验目前主要还是框架和最小样例能力，完整覆盖仍需后续实现。
