# Lab2：中间表示生成

## 1. 本实验定位
  
Lab2 的目标是在该示例基础上扩展语义覆盖范围，并逐步把更多 SysY 语法正确翻译为 IR。

## 2. Lab2 要求

需要同学完成：

1. 熟悉 IR 相关数据结构与构建接口。
2. 理解当前语法树 -> 语义检查 -> IR 的最小实现流程。
3. 在现有框架上补充语义检查与 IR 生成功能，使其覆盖课程要求的 SysY 语法。

## 3. 相关文件

以下文件与本实验内容相关，建议优先阅读。

- `include/sem/Sema.h`
- `include/sem/SymbolTable.h`
- `src/sem/Sema.cpp`
- `src/sem/SymbolTable.cpp`
- `include/ir/IR.h`
- `src/ir/Context.cpp`
- `src/ir/Value.cpp`
- `src/ir/Instruction.cpp`
- `src/ir/BasicBlock.cpp`
- `src/ir/Function.cpp`
- `src/ir/Module.cpp`
- `src/ir/IRBuilder.cpp`
- `src/ir/IRPrinter.cpp`


- `include/irgen/IRGen.h`
- `src/irgen/IRGenDecl.cpp`
- `src/irgen/IRGenStmt.cpp`
- `src/irgen/IRGenExp.cpp`
- `src/irgen/IRGenFunc.cpp`
- `src/irgen/IRGenDriver.cpp`

## 4. 当前最小示例实现说明

当前语法树 -> 语义检查 -> IR 仅覆盖最小子集：

1. 常量整数、变量引用、二元加法表达式。
2. 局部变量声明（当前采用 LLVM 前端常见的 `alloca/load/store` 内存模型）。
3. `return` 语句。
4. 单函数 `main` 的最小流程。

其中，`sema` 负责最基本的名称绑定与合法性检查，`irgen` 在此基础上继续生成 IR。  
如果语义检查阶段没有补全，后续 IR 生成阶段通常也无法正确处理变量引用、声明绑定等逻辑。

当前 `irgen` 的组织方式基于 ANTLR Visitor 的实现。

`IRGenImpl` 继承自 `SysYBaseVisitor`，按照语法树节点类型分发到不同的 `visit*` 函数中完成 IR 生成。整体流程大致是：

1. `GenerateIR(tree, sema)` 先创建 `Module`，再构造 `IRGenImpl`。
2. 从语法树根节点开始访问，进入 `visitCompUnit`。
3. `visitFuncDef` 在 `Module` 中创建 `Function`，并把 `IRBuilder` 的插入点设置到入口基本块。
4. `visitBlockStmt` / `visitBlockItem` 顺序遍历块内声明与语句。
5. `visitDecl` / `visitVarDef` 为局部变量生成 `alloca` 和初始化 `store`。
6. `visitExp` 相关函数递归生成常量、`load`、`add` 等表达式值。
7. `visitReturnStmt` 生成 `ret`，终结当前基本块。

需要强调的是：当前 `IRGen` 还只是一个教学用的最小实现。它只支持 `int main()`、局部 `int` 变量、整数字面量、变量读取、二元加法与 `return`；函数形参、全局变量、控制流、调用、数组等都还需要同学后续补充。


说明：当前阶段变量统一采用内存模型：先 `alloca` 分配栈槽，再通过 `store/load` 读写。即使变量由常量初始化（如 `int a = 1;`），也会先 `store` 到栈槽，而不是直接把变量替换成 SSA 值。后续实验中，同学可按需求再重构。

此外，当前 IR 还维护了最基本的 use-def 关系：每个 `Value` 会记录自己的 `Use`/`User` 信息，`Instruction` 通过 operand 列表与这些关系自动关联。  
这对后续做数据流分析、死代码删除、常量传播等优化会很有帮助；但目前相关实现，接口仍不完整，后续实验中还需要同学继续补充和完善。

## 5. 语法树与 Sema / IRGen 的关系

当前项目中的 `sema` 与 `irgen` 都不是面向独立 AST 设计的，而是直接基于 ANTLR 生成的语法树节点，并通过 Visitor 方式完成语义检查与 IR 生成。  
因此，`SysY.g4` 中 rule 的命名、层级结构以及 labeled alternative 的写法，会直接影响 `SysYParser::*Context` 的类型名和访问接口；一旦 grammar 发生变化，`sem` / `irgen` 中对应的 `visit*` 逻辑通常也需要同步修改。

如果 grammar 扩展后 `sem` / `irgen` 没有同步修改，常见现象包括：

1. 编译阶段报错，例如某个 `SysYParser::*Context` 类型不存在，或某个成员函数不存在。
2. 运行阶段报错，例如进入 `暂不支持的表达式形式`、`暂不支持的语句类型`，或名称绑定失败等分支。

遇到这类问题时，需要同学对照 `SysY.g4`、ANTLR 生成的 `SysYParser.h`，以及 `src/sem` / `src/irgen` 中的 Visitor 遍历逻辑，完成对应的接口调整与功能补全。

## 6. 构建与运行

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

## 7. Lab2 验证方式

可先用单个样例检查 IR 输出是否基本正确：

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



推荐使用统一脚本验证 “IR -> LLVM 后端 -> 可执行程序” 整体链路。`--run` 模式下会自动读取同名 `.in`，并将程序输出与退出码和同名 `.out` 比对，用于验证 IR 的正确性：


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

但最终不能只检查 `simple_add`。完成 Lab2 后，应对 `test/test_case` 下全部测试用例逐个回归，确认 IR 生成与 `--run` 链路都能通过；如有需要，也可以自行编写批量测试脚本统一执行。
