From a0fdafce7043116c6c9665bf322ef62aa322c958 Mon Sep 17 00:00:00 2001 From: Lane0218 Date: Tue, 10 Mar 2026 15:45:14 +0800 Subject: [PATCH] =?UTF-8?q?docs(doc):=20=E6=9B=B4=E6=96=B0=20Lab4=20?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E4=BC=98=E5=8C=96=E5=AE=9E=E9=AA=8C=E8=AF=B4?= =?UTF-8?q?=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +-- doc/Lab4-寄存器分配.md | 52 ++++++++++++++++++++++++++++++------- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index fd6d293..896f5a5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # SysY 编译器课程实验(C++) 本仓库为“并行编译课程实验”提供一个 SysY 编译器的最小可运行示例,实验按 Lab1–Lab6 逐步完成: -从前端(词法/语法分析与语法树处理)到中端(IR 生成与优化),再到后端(ARM64/AArch64 汇编生成与寄存器分配),最后进行循环/并行相关优化。 +从前端(词法/语法分析与语法树处理)到中端(IR 生成与优化),再到后端(ARM64/AArch64 汇编生成、寄存器分配与后端优化),最后进行循环/并行相关优化。 ## 1. 实验介绍 @@ -12,7 +12,7 @@ | Lab1 | 语法树构建 | 基于 SysY 源程序完成语法分析与语法树构建,并按约定输出语法树 | | Lab2 | 中间表示生成 | 将语法树翻译为 LLVM 风格的中间表示(IR),并输出 IR | | Lab3 | 指令选择与汇编生成 | 将 IR 翻译为目标平台汇编代码(本项目以 ARM64/AArch64 为主) | -| Lab4 | 寄存器分配 | 为后端生成的虚拟寄存器分配物理寄存器,完成 spill/reload 等必要处理 | +| Lab4 | 寄存器分配与后端优化 | 为后端生成的虚拟寄存器分配物理寄存器,并完成 spill/reload、冗余指令消除与局部后端优化 | | Lab5 | 基本标量优化 | 实现常见的标量优化(如常量传播、死代码删除、简化 CFG 等) | | Lab6 | 并行/循环优化 | 面向循环的优化(循环变换/并行化等),提升数值计算类程序性能 | diff --git a/doc/Lab4-寄存器分配.md b/doc/Lab4-寄存器分配.md index 20fc9d6..125c495 100644 --- a/doc/Lab4-寄存器分配.md +++ b/doc/Lab4-寄存器分配.md @@ -1,9 +1,9 @@ -# Lab4:寄存器分配 +# Lab4:寄存器分配与后端优化 ## 1. 本实验定位 本仓库当前提供了一个“最小可运行”的 IR -> AArch64 汇编示例链路。 -Lab4 的目标是在 Lab3 示例基础上,把“固定寄存器 + 栈槽”的最小后端实现推进为“虚拟寄存器 -> 物理寄存器”的真实后端阶段,为完整 SysY 后端打基础。 +Lab4 的目标是在 Lab3 示例基础上,把“固定寄存器 + 栈槽”的最小后端实现推进为“虚拟寄存器 -> 物理寄存器”的真实后端阶段,并在此基础上补充局部后端优化,为完整 SysY 后端打基础。 ## 2. Lab4 要求 @@ -13,8 +13,10 @@ Lab4 的目标是在 Lab3 示例基础上,把“固定寄存器 + 栈槽”的 2. 理解当前 IR -> MIR -> 汇编输出流程中寄存器相关部分的最小实现现状。 3. 扩展当前 MIR 表达,使指令选择阶段能够产出虚拟寄存器,而不是继续固定使用 `w0`、`w8`、`w9`。 4. 在现有框架上实现真实寄存器分配,并处理 spill/reload、栈槽管理、callee-saved 保存恢复等后续问题。 -5. 图着色寄存器分配与线性扫描寄存器分配均可作为实现路线,同学可自行选择其中一种完成。 -6. 在 `test/test_case` 提供的全部测试用例上验证正确性,并在保证功能正确的前提下尽量减少冗余 spill/reload 与访存,提升生成代码质量。 +5. 在寄存器分配结果基础上,补充后端局部优化流程,减少明显冗余机器指令与低效访存。 +6. 可实现的后端优化包括但不限于:窥孔优化、冗余 `move/copy` 消除、局部访存冗余消除,以及简单恒等指令消除(如 `add/sub ..., #0`)。 +7. 图着色寄存器分配与线性扫描寄存器分配均可作为实现路线,同学可自行选择其中一种完成;后端优化部分也不限定具体实现方式,只要求功能正确、收益明确。 +8. 在 `test/test_case` 提供的全部测试用例上验证正确性,并在保证功能正确的前提下尽量减少冗余 spill/reload、无效拷贝、冗余访存与低效机器指令,提升生成代码质量。 ## 3. 当前代码框架(与 Lab4 直接相关) @@ -32,7 +34,11 @@ Lab4 的目标是在 Lab3 示例基础上,把“固定寄存器 + 栈槽”的 - `src/mir/FrameLowering.cpp` - `src/mir/AsmPrinter.cpp` -3. 入口流程 +3. 后端优化 Pass + - `src/mir/passes/PassManager.cpp` + - `src/mir/passes/Peephole.cpp` + +4. 入口流程 - `src/main.cpp` - `src/utils/CLI.h` - `src/utils/CLI.cpp` @@ -48,6 +54,8 @@ Lab4 的目标是在 Lab3 示例基础上,把“固定寄存器 + 栈槽”的 - `src/mir/RegAlloc.cpp`(实现真实寄存器分配主逻辑,可选择图着色或线性扫描) - `src/mir/FrameLowering.cpp`(根据寄存器分配结果完成栈帧布局、spill 栈槽计算与保存恢复) - `src/mir/AsmPrinter.cpp`(保证寄存器分配后的 MIR 能正确打印为最终汇编) + - `src/mir/passes/PassManager.cpp`(组织寄存器分配后相关优化 pass 的运行顺序) + - `src/mir/passes/Peephole.cpp`(实现窥孔优化与其他局部后端优化规则) 2. 视实现需要可能修改 - `src/mir/MIRBasicBlock.cpp`(当需要扩展基本块级活跃性或辅助接口时) @@ -57,16 +65,36 @@ Lab4 的目标是在 Lab3 示例基础上,把“固定寄存器 + 栈槽”的 ## 5. 当前最小示例实现说明 -当前后端中的寄存器相关实现仍停留在最小示例阶段: +当前后端中的寄存器分配与后端优化相关实现仍停留在最小示例阶段: 1. `Lowering.cpp` 当前直接使用固定物理寄存器 `w0`、`w8`、`w9` 生成机器指令,而不是先生成虚拟寄存器。 2. `RegAlloc.cpp` 当前仅执行最小一致性检查,不实现真实寄存器分配。 3. 当前 MIR 主要围绕单函数 `main`、单基本块与最小指令子集工作,尚未形成完整课程版本所需的寄存器分配基础设施。 4. `FrameLowering.cpp` 与 `AsmPrinter.cpp` 当前默认前面阶段已经给出可直接落地的固定寄存器结果,并未围绕完整 RA 流程展开。 -5. 因此,当前代码实际上**没有实现寄存器分配**,这一部分需要同学自行完成。 +5. `src/mir/passes/Peephole.cpp` 与 `src/mir/passes/PassManager.cpp` 当前仅保留了最小注释框架,尚未形成真实可运行的后端优化流程。 +6. 因此,当前代码实际上**没有实现完整的寄存器分配与后端优化**,这一部分需要同学自行完成。 + + +说明:本阶段不应继续沿用 Lab3 的“所有中间值统一写回栈槽 + 固定寄存器临时搬运”的做法,而应先把指令选择结果改造成带虚拟寄存器的 MIR,再进入寄存器分配阶段;在寄存器分配与栈帧落地完成后,再针对最终机器指令序列做局部后端优化。无论选择哪一种寄存器分配算法,都需要先解决几个共同前提:为机器指令补充 `use/def` 信息、能够遍历机器基本块与控制流关系、为虚拟寄存器维护分配状态,并在 spill 后为新引入的访存指令重新参与后续流程。 + +后端优化部分建议保持“局部、可验证、与当前框架贴合”的范围,典型可以包括: +1. 窥孔优化 + - 删除冗余 `move` + - 合并常见短指令模式 + - 消除无效恒等操作 -说明:本阶段不应继续沿用 Lab3 的“所有中间值统一写回栈槽 + 固定寄存器临时搬运”的做法,而应先把指令选择结果改造成带虚拟寄存器的 MIR,再进入寄存器分配阶段。无论选择哪一种算法,都需要先解决几个共同前提:为机器指令补充 `use/def` 信息、能够遍历机器基本块与控制流关系、为虚拟寄存器维护分配状态,并在 spill 后为新引入的访存指令重新参与后续流程。 +2. 冗余 `move/copy` 消除 + - 删除 `mov r, r` + - 压缩连续拷贝链 + - 在安全前提下减少无意义寄存器搬运 + +3. 局部访存冗余消除 + - 删除明显冗余的 `load/store` + - 识别相邻、无干扰的重复访存 + - 减少由 spill/reload 或固定模板生成带来的低效访存 + +说明:本实验中的后端优化重点是“局部机器级优化”,并不要求实现全局代码布局优化、复杂指令调度或更高级的机器级分析框架。目标是在保证语义正确的前提下,让最终汇编更紧凑、更直接。 可选的两条常见实现路线如下: @@ -113,4 +141,10 @@ cmake --build build -j "$(nproc)" ./scripts/verify_asm_with_qemu.sh test/test_case/simple_add.sy out/asm --run ``` -完成 Lab4 后,最终不应只停留在 `simple_add` 这一最小示例,而应对 `test/test_case` 下全部测试用例逐个回归,确保生成代码功能正确;在此基础上,再尽量减少不必要的 spill/reload、冗余访存与低效寄存器使用,以提升最终性能表现。 +建议在功能回归之外,再观察优化前后汇编输出差异。可按自己的实现方式保留调试日志、优化开关,或直接对比生成的汇编文本,重点关注: + +1. 是否删除了明显冗余的 `move/copy` 指令。 +2. 是否减少了不必要的 `load/store` 与重复访存。 +3. 是否消除了无意义的恒等操作。 + +完成 Lab4 后,最终不应只停留在 `simple_add` 这一最小示例,而应对 `test/test_case` 下全部测试用例逐个回归,确保生成代码功能正确;在此基础上,再尽量减少不必要的 spill/reload、无效拷贝、冗余访存与低效机器指令,以提升最终性能表现。