From 67167e9e51db54427ab18d6229a4b456a38e905b Mon Sep 17 00:00:00 2001 From: Lane0218 Date: Sat, 7 Mar 2026 23:07:30 +0800 Subject: [PATCH 1/3] =?UTF-8?q?docs(doc):=20=E6=9B=B4=E6=96=B0=20README=20?= =?UTF-8?q?=E8=87=AA=E6=A3=80=E8=AF=B4=E6=98=8E=E4=B8=8E=20git-submit=20?= =?UTF-8?q?=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .codex/skills/git-submit/SKILL.md | 10 +++++++--- README.md | 6 +++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.codex/skills/git-submit/SKILL.md b/.codex/skills/git-submit/SKILL.md index 22b3fa6..555a93e 100644 --- a/.codex/skills/git-submit/SKILL.md +++ b/.codex/skills/git-submit/SKILL.md @@ -19,9 +19,13 @@ description: 执行 Git 提交和推送工作流 - 误把生成物/大文件加入:先移除或补 `.gitignore` - 变更跨度过大:提醒用户是否需要拆分提交 -### 2)起草提交信息(中文;这是唯一一次需要用户确认的环节) +### 2)起草提交信息(中文;默认不需要用户确认) -根据对话历史和修改的文件信息,按下方规范先给出“建议的提交信息”,然后询问用户:**“是否确认/需要修改?”** +根据对话历史和修改的文件信息,按下方规范直接生成提交信息并继续执行后续流程。 + +- 如果用户已经明确给出提交信息,直接使用用户提供的版本 +- 如果用户没有指定提交信息,按下方规范自动生成最合适的一条 +- **不要**为了 commit message 再额外等待用户确认,除非用户明确要求先看 message 再提交 #### Git Commit Message 规范 @@ -88,4 +92,4 @@ refactor(irgen): 简化 AST → IR 构建流程 - 暂存所有改动:`git add -A` - 单行摘要:`git commit -m "(): "`,需要补充说明时用多行:`git commit -m "" -m "" -m ""` -- 推送:`git push`,推送完成后立即停止,不要再运行其他命令 \ No newline at end of file +- 推送:`git push`,推送完成后立即停止,不要再运行其他命令 diff --git a/README.md b/README.md index 900bbea..f02c22a 100644 --- a/README.md +++ b/README.md @@ -88,10 +88,10 @@ cmake --build build -j "$(nproc)" ./build/bin/compiler --help ``` -(可选)生成 IR 并验证 LLVM 工具链是否可用: +跑完整编译流程自检:从 SysY 源码生成 AArch64 汇编,完成汇编、链接,并在 QEMU 下运行结果程序: ```bash -./scripts/verify_ir_with_llvm.sh test/test_case/simple_add.sy out/ir --run +./scripts/verify_asm_with_qemu.sh test/test_case/simple_add.sy out/asm --run ``` -兼容说明:`scripts/gen_ir.sh` 仍可使用,但作为兼容入口会转调 `verify_ir_with_llvm.sh`。 +如果最终看到 `退出码: 3`,说明当前最小子集示例 `return a + b` 的完整链路已经跑通。 \ No newline at end of file From 10c1d153acbd09c58a3e6af2abb7289b7874e57c Mon Sep 17 00:00:00 2001 From: Lane0218 Date: Sat, 7 Mar 2026 23:14:57 +0800 Subject: [PATCH 2/3] =?UTF-8?q?docs(doc):=20=E9=87=8D=E5=91=BD=E5=90=8D?= =?UTF-8?q?=E5=AE=9E=E9=AA=8C=E8=AF=B4=E6=98=8E=E5=B9=B6=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=20Lab3=20=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...验说明.md => Lab1-语法树构建.md} | 2 +- ...说明.md => Lab2-中间表示生成.md} | 16 ++-- doc/Lab3-指令选择与汇编生成.md | 88 +++++++++++++++++++ 3 files changed, 95 insertions(+), 11 deletions(-) rename doc/{Lab1-ANTLR词法语法分析实验说明.md => Lab1-语法树构建.md} (98%) rename doc/{Lab2-AST到IR实验说明.md => Lab2-中间表示生成.md} (87%) create mode 100644 doc/Lab3-指令选择与汇编生成.md diff --git a/doc/Lab1-ANTLR词法语法分析实验说明.md b/doc/Lab1-语法树构建.md similarity index 98% rename from doc/Lab1-ANTLR词法语法分析实验说明.md rename to doc/Lab1-语法树构建.md index 46c0df1..ed1cda1 100644 --- a/doc/Lab1-ANTLR词法语法分析实验说明.md +++ b/doc/Lab1-语法树构建.md @@ -1,4 +1,4 @@ -# Lab1:用 ANTLR 实现 SysY 词法/语法分析器 +# Lab1:语法树构建 ## 1. 本实验定位 diff --git a/doc/Lab2-AST到IR实验说明.md b/doc/Lab2-中间表示生成.md similarity index 87% rename from doc/Lab2-AST到IR实验说明.md rename to doc/Lab2-中间表示生成.md index aa3f14c..79c019c 100644 --- a/doc/Lab2-AST到IR实验说明.md +++ b/doc/Lab2-中间表示生成.md @@ -1,4 +1,4 @@ -# Lab2:从 AST 生成中间表示(IR) +# Lab2:中间表示生成 ## 1. 本实验定位 @@ -11,9 +11,7 @@ Lab2 的目标是在该示例基础上扩展语义覆盖范围,逐步把更多 1. 熟悉 IR 相关数据结构与构建接口。 2. 理解当前 AST -> IR 的最小实现流程。 -3. 在现有框架上扩展 IR 生成能力,使其覆盖课程要求的Sysy语法。 - - +3. 在现有框架上扩展 IR 生成能力,使其覆盖课程要求的 SysY 语法。 ## 3. 当前代码框架(与 Lab2 直接相关) @@ -42,11 +40,11 @@ Lab2 的目标是在该示例基础上扩展语义覆盖范围,逐步把更多 - `src/ir/IR.h`(当现有 IR 指令/类型不够用时) - `src/ir/IRBuilder.cpp`(当需要新增构建接口时) - `src/ir/IRPrinter.cpp`(新增 IR 指令后补齐打印) - - `src/irgen/IRGen.h`(当需要扩展状态或辅助接口) + - `src/irgen/IRGen.h`(当需要扩展状态或辅助接口时) 2. 视实现需要可能修改 - - `src/main.cpp`(当需要调整输出阶段行为) - - `src/sem/*`(当新增语法依赖更多语义信息) + - `src/main.cpp`(当需要调整输出阶段行为时) + - `src/sem/*`(当新增语法依赖更多语义信息时) ## 5. 当前最小示例实现说明 @@ -74,11 +72,9 @@ cmake --build build -j "$(nproc)" ```bash ./build/bin/compiler --emit-ir test/test_case/simple_add.sy ``` -` -推荐使用统一脚本验证 “IR -> LLVM 后端 -> 可执行程序” 整体链路,用于验证ir的正确性: +推荐使用统一脚本验证 “IR -> LLVM 后端 -> 可执行程序” 整体链路,用于验证 IR 的正确性: ```bash ./scripts/verify_ir_with_llvm.sh test/test_case/simple_add.sy out/ir --run ``` - diff --git a/doc/Lab3-指令选择与汇编生成.md b/doc/Lab3-指令选择与汇编生成.md new file mode 100644 index 0000000..f0ec7ac --- /dev/null +++ b/doc/Lab3-指令选择与汇编生成.md @@ -0,0 +1,88 @@ +# Lab3:指令选择与汇编生成 + +## 1. 本实验定位 + +本仓库当前提供了一个“最小可运行”的 IR -> AArch64 汇编示例链路。 +Lab3 的目标是在该示例基础上扩展后端语义覆盖范围,逐步把更多 SysY IR 正确翻译为目标平台汇编代码。 + +## 2. Lab3 要求 + +需要同学完成: + +1. 熟悉 MIR 相关数据结构与后端阶段接口。 +2. 理解当前 IR -> MIR -> 汇编输出的最小实现流程。 +3. 在现有框架上扩展后端代码生成能力,使其覆盖课程要求的 SysY 语义。 + +## 3. 当前代码框架(与 Lab3 直接相关) + +1. MIR 定义与目标相关抽象 + - `include/mir/MIR.h` + - `src/mir/MIRContext.cpp` + - `src/mir/MIRFunction.cpp` + - `src/mir/MIRBasicBlock.cpp` + - `src/mir/MIRInstr.cpp` + - `src/mir/Register.cpp` + +2. IR -> MIR 与汇编生成 + - `src/mir/Lowering.cpp` + - `src/mir/RegAlloc.cpp` + - `src/mir/FrameLowering.cpp` + - `src/mir/AsmPrinter.cpp` + +3. 入口流程 + - `src/main.cpp` + - `src/utils/CLI.h` + - `src/utils/CLI.cpp` + +## 4. Lab3 需要补充的内容 + +1. 必须修改的文件 + - `src/mir/Lowering.cpp` + - `src/mir/RegAlloc.cpp` + - `src/mir/FrameLowering.cpp` + - `src/mir/AsmPrinter.cpp` + - `include/mir/MIR.h`(当现有 MIR 数据结构或接口不够用时) + - `src/mir/MIRInstr.cpp`(当需要新增机器指令或操作数表达时) + - `src/mir/MIRFunction.cpp`(当需要扩展栈帧或机器函数状态时) + - `src/mir/Register.cpp`(当需要扩展物理/虚拟寄存器表示时) + +2. 视实现需要可能修改 + - `src/main.cpp`(当需要调整输出阶段行为时) + - `src/utils/CLI.cpp`(当需要扩展后端相关命令行选项时) + - `scripts/verify_asm_with_qemu.sh`(当需要扩展统一验证脚本时) + +## 5. 当前最小示例实现说明 + +当前 IR -> 汇编仅覆盖最小子集: + +1. 仅支持单函数 `main`、单基本块的最小流程。 +2. 仅支持由当前 Lab2 最小 IR 产生的 `alloca`、`load`、`store`、`add`、`ret`。 +3. 局部变量与中间结果当前统一采用栈槽模型:所有值先映射到栈槽,再通过固定寄存器 `w0`、`w8`、`w9` 配合 `ldur/stur/add` 生成汇编。 +4. `RegAlloc` 当前仅执行最小一致性检查,不实现真实寄存器分配。 +5. `FrameLowering` 当前会插入最小序言/尾声,并按 16 字节对齐栈帧。 + + +说明:当前阶段后端主要用于演示完整流程。即使中间值可以暂存在寄存器中,也会先写回栈槽,而不是直接构造更接近最终机器代码的寄存器流。后续实验中,同学可按需求继续扩展指令选择、寄存器分配、调用约定与控制流相关功能。 + +## 6. 构建与运行 + +```bash +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build -j "$(nproc)" +``` + +## 7. Lab3 验证方式 + +项目编译后按提供测试输入回归: + +```bash +./build/bin/compiler --emit-asm test/test_case/simple_add.sy +``` + +推荐使用统一脚本验证 “源码 -> 汇编 -> 可执行程序” 整体链路,用于验证后端代码生成的正确性: + +```bash +./scripts/verify_asm_with_qemu.sh test/test_case/simple_add.sy out/asm --run +``` + +若最终输出 `退出码: 3`,说明当前最小子集示例 `return a + b` 的完整后端链路已经跑通。 From 77110aa7cecf4fa2d1258609c40d82c5d9012e4a Mon Sep 17 00:00:00 2001 From: Lane0218 Date: Sun, 8 Mar 2026 11:13:15 +0800 Subject: [PATCH 3/3] =?UTF-8?q?docs(doc):=20=E8=A1=A5=E5=85=85=20Lab4=20?= =?UTF-8?q?=E5=AF=84=E5=AD=98=E5=99=A8=E5=88=86=E9=85=8D=E6=8C=87=E5=AF=BC?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/Lab4-寄存器分配.md | 116 ++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 doc/Lab4-寄存器分配.md diff --git a/doc/Lab4-寄存器分配.md b/doc/Lab4-寄存器分配.md new file mode 100644 index 0000000..20fc9d6 --- /dev/null +++ b/doc/Lab4-寄存器分配.md @@ -0,0 +1,116 @@ +# Lab4:寄存器分配 + +## 1. 本实验定位 + +本仓库当前提供了一个“最小可运行”的 IR -> AArch64 汇编示例链路。 +Lab4 的目标是在 Lab3 示例基础上,把“固定寄存器 + 栈槽”的最小后端实现推进为“虚拟寄存器 -> 物理寄存器”的真实后端阶段,为完整 SysY 后端打基础。 + +## 2. Lab4 要求 + +需要同学完成: + +1. 熟悉 MIR 中寄存器、操作数、栈槽与机器函数之间的关系。 +2. 理解当前 IR -> MIR -> 汇编输出流程中寄存器相关部分的最小实现现状。 +3. 扩展当前 MIR 表达,使指令选择阶段能够产出虚拟寄存器,而不是继续固定使用 `w0`、`w8`、`w9`。 +4. 在现有框架上实现真实寄存器分配,并处理 spill/reload、栈槽管理、callee-saved 保存恢复等后续问题。 +5. 图着色寄存器分配与线性扫描寄存器分配均可作为实现路线,同学可自行选择其中一种完成。 +6. 在 `test/test_case` 提供的全部测试用例上验证正确性,并在保证功能正确的前提下尽量减少冗余 spill/reload 与访存,提升生成代码质量。 + +## 3. 当前代码框架(与 Lab4 直接相关) + +1. MIR 定义与寄存器相关抽象 + - `include/mir/MIR.h` + - `src/mir/MIRContext.cpp` + - `src/mir/MIRFunction.cpp` + - `src/mir/MIRBasicBlock.cpp` + - `src/mir/MIRInstr.cpp` + - `src/mir/Register.cpp` + +2. IR -> MIR、寄存器分配与后续落地 + - `src/mir/Lowering.cpp` + - `src/mir/RegAlloc.cpp` + - `src/mir/FrameLowering.cpp` + - `src/mir/AsmPrinter.cpp` + +3. 入口流程 + - `src/main.cpp` + - `src/utils/CLI.h` + - `src/utils/CLI.cpp` + +## 4. Lab4 需要补充的内容 + +1. 必须修改的文件 + - `include/mir/MIR.h`(扩展寄存器、操作数、机器函数等数据结构,使其能够表示虚拟寄存器与分配结果) + - `src/mir/Register.cpp`(补充物理/虚拟寄存器表示、可分配寄存器集合等) + - `src/mir/MIRInstr.cpp`(当需要扩展机器指令或操作数表达时) + - `src/mir/MIRFunction.cpp`(当需要维护虚拟寄存器、spill 栈槽、callee-saved 等状态时) + - `src/mir/Lowering.cpp`(将当前固定寄存器写法改造为生成虚拟寄存器形式的 MIR) + - `src/mir/RegAlloc.cpp`(实现真实寄存器分配主逻辑,可选择图着色或线性扫描) + - `src/mir/FrameLowering.cpp`(根据寄存器分配结果完成栈帧布局、spill 栈槽计算与保存恢复) + - `src/mir/AsmPrinter.cpp`(保证寄存器分配后的 MIR 能正确打印为最终汇编) + +2. 视实现需要可能修改 + - `src/mir/MIRBasicBlock.cpp`(当需要扩展基本块级活跃性或辅助接口时) + - `src/main.cpp`(当需要调整后端阶段行为时) + - `src/utils/CLI.cpp`(当需要扩展后端调试相关命令行选项时) + - `scripts/verify_asm_with_qemu.sh`(当需要扩展统一验证脚本时) + +## 5. 当前最小示例实现说明 + +当前后端中的寄存器相关实现仍停留在最小示例阶段: + +1. `Lowering.cpp` 当前直接使用固定物理寄存器 `w0`、`w8`、`w9` 生成机器指令,而不是先生成虚拟寄存器。 +2. `RegAlloc.cpp` 当前仅执行最小一致性检查,不实现真实寄存器分配。 +3. 当前 MIR 主要围绕单函数 `main`、单基本块与最小指令子集工作,尚未形成完整课程版本所需的寄存器分配基础设施。 +4. `FrameLowering.cpp` 与 `AsmPrinter.cpp` 当前默认前面阶段已经给出可直接落地的固定寄存器结果,并未围绕完整 RA 流程展开。 +5. 因此,当前代码实际上**没有实现寄存器分配**,这一部分需要同学自行完成。 + + +说明:本阶段不应继续沿用 Lab3 的“所有中间值统一写回栈槽 + 固定寄存器临时搬运”的做法,而应先把指令选择结果改造成带虚拟寄存器的 MIR,再进入寄存器分配阶段。无论选择哪一种算法,都需要先解决几个共同前提:为机器指令补充 `use/def` 信息、能够遍历机器基本块与控制流关系、为虚拟寄存器维护分配状态,并在 spill 后为新引入的访存指令重新参与后续流程。 + +可选的两条常见实现路线如下: + +1. 图着色寄存器分配 + - 整体思路:把“两个虚拟寄存器若在某一程序点同时活跃,则不能分配到同一个物理寄存器”转化为图着色问题。图中的结点表示虚拟寄存器,边表示二者互相干涉;若有 `K` 个可分配物理寄存器,则目标是对干涉图进行 `K` 着色。 + - 典型步骤: + 1. 先对 MIR 做活跃性分析,计算各基本块或各指令位置的 live-in/live-out。 + 2. 根据活跃信息构建干涉图;若需要优化 move,也可以额外记录可合并关系。 + 3. 按照可分配寄存器数 `K` 对图执行 simplify/select,必要时结合启发式选择 spill 候选。 + 4. 若图可以着色,则回填每个虚拟寄存器对应的物理寄存器;若不能着色,则把选中的虚拟寄存器重写为 spill/reload 形式,并重新进行分析与分配。 + 5. 分配完成后,把使用到的 callee-saved 寄存器、额外 spill 栈槽等信息交给 `FrameLowering.cpp` 与 `AsmPrinter.cpp` 继续处理。 + - 说明:图着色方法更接近经典教材中的完整后端流程,适合面向完整 SysY 后端逐步扩展;但实现成本通常更高,需要同学自己补齐活跃性分析、干涉图维护与 spill 重试机制。 + +2. 线性扫描寄存器分配 + - 整体思路:先把每个虚拟寄存器的活跃范围抽象为一个区间,再按照区间起点顺序扫描程序,动态维护当前正在占用物理寄存器的活跃区间集合;若出现寄存器不够用,再选择某个区间 spill。 + - 典型步骤: + 1. 为机器指令建立稳定顺序,并结合活跃性信息计算每个虚拟寄存器的 live interval。 + 2. 按区间起点排序后顺序扫描,维护当前仍然活跃的区间集合 `active`。 + 3. 每处理到一个新区间时,先移除已经结束的区间并释放其占用的物理寄存器。 + 4. 若存在空闲物理寄存器,则直接分配;若没有空闲寄存器,则比较当前区间与 `active` 中已有区间的结束位置,选择 spill 当前区间或 spill 一个结束更晚的旧区间。 + 5. 对 spill 的虚拟寄存器插入 reload/store 后,需要重新计算受影响区间,再继续后续分配与汇编落地。 + - 说明:线性扫描通常更容易先做出一个可运行版本,在函数数量较多、实现周期较紧的课程环境中也较常见;但如果要把效果做得更好,仍然需要认真处理区间切分、调用点约束、callee/caller-saved 寄存器使用策略等问题。 + +无论采用图着色还是线性扫描,都不应把寄存器分配理解为“把虚拟寄存器简单替换成物理寄存器名字”。真正完整的实现还需要和 spill/reload、栈帧布局、callee-saved 保存恢复以及最终汇编输出联动,否则后端仍然无法支撑完整 SysY 程序。 + +## 6. 构建与运行 + +```bash +cmake -S . -B build -DCMAKE_BUILD_TYPE=Release +cmake --build build -j "$(nproc)" +``` + +## 7. Lab4 验证方式 + +项目编译后可先按当前最小样例检查后端链路是否仍能运行: + +```bash +./build/bin/compiler --emit-asm test/test_case/simple_add.sy +``` + +推荐继续使用统一脚本验证 “源码 -> 汇编 -> 可执行程序” 整体链路,用于做最小回归: + +```bash +./scripts/verify_asm_with_qemu.sh test/test_case/simple_add.sy out/asm --run +``` + +完成 Lab4 后,最终不应只停留在 `simple_add` 这一最小示例,而应对 `test/test_case` 下全部测试用例逐个回归,确保生成代码功能正确;在此基础上,再尽量减少不必要的 spill/reload、冗余访存与低效寄存器使用,以提升最终性能表现。