refactor(irgen): 规范采用visitor生成

This commit is contained in:
jing
2026-03-18 02:07:34 +08:00
parent 7d4d60c546
commit dfa71bc0d7
8 changed files with 297 additions and 130 deletions

View File

@@ -50,26 +50,37 @@ Lab2 的目标是在该示例基础上扩展语义覆盖范围,并逐步把更
其中,`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` 会记录哪些 `Instruction` 使用了它
此外,当前 IR 还维护了最基本的 use-def 关系:每个 `Value` 会记录自己的 `Use`/`User` 信息,`Instruction` 通过 operand 列表与这些关系自动关联
这对后续做数据流分析、死代码删除、常量传播等优化会很有帮助;但目前相关实现,接口仍不完整,后续实验中还需要同学继续补充和完善。
## 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 生成代码该如何遍历和取信息。
当前项目中的 `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` 中的遍历逻辑,完成对应的接口调整与功能补全。ANTLR 生成的结构可参考 `build/generated/antlr4/SysYParser.h``build/generated/antlr4/SysYParser.cpp`;其中前者更适合查看各类 `SysYParser::*Context` 的名字与成员函数,后者可辅助查看规则展开后的具体实现。
遇到这类问题时,需要同学对照 `SysY.g4`、ANTLR 生成的 `SysYParser.h`,以及 `src/sem` / `src/irgen` 中的 Visitor 遍历逻辑,完成对应的接口调整与功能补全。
## 6. 构建与运行