refactor(irgen): 完善irgen代码和文档,提升扩展兼容性
This commit is contained in:
@@ -2,49 +2,83 @@
|
||||
|
||||
## 1. 本实验定位
|
||||
|
||||
Lab2 的目标是在该示例基础上扩展语义覆盖范围,逐步把更多 SysY 语法正确翻译为 IR。
|
||||
Lab2 的目标是在该示例基础上扩展语义覆盖范围,并逐步把更多 SysY 语法正确翻译为 IR。
|
||||
|
||||
## 2. Lab2 要求
|
||||
|
||||
需要同学完成:
|
||||
|
||||
1. 熟悉 IR 相关数据结构与构建接口。
|
||||
2. 理解当前语法树 -> IR 的最小实现流程。
|
||||
3. 在现有框架上扩展 IR 生成能力,使其覆盖课程要求的Sysy语法。
|
||||
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 仅覆盖最小子集:
|
||||
当前语法树 -> 语义检查 -> IR 仅覆盖最小子集:
|
||||
|
||||
1. 常量整数、变量引用、二元加法表达式。
|
||||
2. 局部变量声明(当前采用 LLVM 前端常见的 `alloca/load/store` 内存模型)。
|
||||
3. `return` 语句。
|
||||
4. 单函数 `main` 的最小流程。
|
||||
|
||||
其中,`sema` 负责最基本的名称绑定与合法性检查,`irgen` 在此基础上继续生成 IR。
|
||||
如果语义检查阶段没有补全,后续 IR 生成阶段通常也无法正确处理变量引用、声明绑定等逻辑。
|
||||
|
||||
|
||||
说明:当前阶段变量统一采用内存模型:先 `alloca` 分配栈槽,再通过 `store/load` 读写。即使变量由常量初始化(如 `int a = 1;`),也会先 `store` 到栈槽,而不是直接把变量替换成 SSA 值。后续实验中,同学可按需求再重构。
|
||||
|
||||
此外,当前 IR 还维护了最基本的 use-def 关系:每个 `Value` 会记录哪些 `Instruction` 使用了它。
|
||||
这对后续做数据流分析、死代码删除、常量传播等优化会很有帮助;但目前相关实现,接口仍不完整,后续实验中还需要同学继续补充和完善。
|
||||
|
||||
## 5. 构建与运行
|
||||
## 5. 语法树与 Sema / IRGen 的关系
|
||||
|
||||
当前项目中的 `sema` 与 `irgen` 都不是面向独立 AST 设计的,而是直接遍历 ANTLR 生成的语法树节点来完成语义检查与 IR 生成。
|
||||
因此,`src/antlr4/SysY.g4` 中 rule 的命名、层级结构以及 labeled alternative 的写法,会直接影响 `SysYParser::*Context` 的类型名和访问接口;一旦 grammar 发生变化,`sem` / `irgen` 中对应的遍历逻辑通常也需要同步修改。
|
||||
|
||||
这也是为什么在 Lab1 扩展 grammar 后,Lab2 常常还需要继续修改 `sem` / `irgen`:
|
||||
不是因为 IR 本身一定变了,而是因为“语法树长什么样”,直接决定了语义检查和 IR 生成代码该如何遍历和取信息。
|
||||
|
||||
如果 grammar 扩展后 `sem` / `irgen` 没有同步修改,常见现象包括:
|
||||
|
||||
1. 编译阶段报错,例如某个 `SysYParser::*Context` 类型不存在,或某个成员函数不存在。
|
||||
2. 运行阶段报错,例如进入 `暂不支持的表达式形式`、`暂不支持的语句类型`,或名称绑定失败等分支。
|
||||
|
||||
遇到这类问题时,需要同学自行对照 `SysY.g4`、ANTLR 生成的 `SysYParser.h`,以及 `src/sem` / `src/irgen` 中的遍历逻辑,完成对应的接口调整与功能补全。ANTLR 生成的结构可参考 `build/generated/antlr4/SysYParser.h` 与 `build/generated/antlr4/SysYParser.cpp`;其中前者更适合查看各类 `SysYParser::*Context` 的名字与成员函数,后者可辅助查看规则展开后的具体实现。
|
||||
|
||||
## 6. 构建与运行
|
||||
|
||||
```bash
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build -j "$(nproc)"
|
||||
```
|
||||
|
||||
## 6. Lab2 验证方式
|
||||
## 7. Lab2 验证方式
|
||||
|
||||
可先用单个样例检查 IR 输出是否基本正确:
|
||||
|
||||
@@ -52,11 +86,7 @@ cmake --build build -j "$(nproc)"
|
||||
./build/bin/compiler --emit-ir test/test_case/functional/simple_add.sy
|
||||
```
|
||||
|
||||
如需打印语法树:
|
||||
|
||||
```bash
|
||||
./build/bin/compiler --emit-parse-tree test/test_case/functional/simple_add.sy
|
||||
```
|
||||
|
||||
推荐使用统一脚本验证 “IR -> LLVM 后端 -> 可执行程序” 整体链路。`--run` 模式下会自动读取同名 `.in`,并将程序输出与退出码和同名 `.out` 比对,用于验证 IR 的正确性:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user