要打通从SysY到RISC-V的完整编译流程,以下是必须实现的核心模块和关键步骤(按编译流程顺序)。在你们当前IR生成阶段,可以优先实现这些基础模块来快速获得可工作的RISC-V汇编输出: ### 1. **前端必须模块** - **词法/语法分析**(已完成): - `SysYLexer`/`SysYParser`:ANTLR生成的解析器 - **IR生成核心**:(已完成) - `SysYIRGenerator`:将AST转换为中间表示(IR) - `IRBuilder`:构建指令和基本块的工具类(你们正在实现的部分) - **IR打印器**:(基本完成) - `SysYIRPrinter`: 打印llvm ir格式的指令,优化遍后查看优化效果,la指令,subarray数组翻译范式需要改进 ### 2. **中端必要优化(最小集合)** - **CFG优化**:(待测试) - `SysYIROptPre`:CFG优化顺便解决IR生成的缺陷(void自动添加ret指令,合并嵌套if/while语句生成的多个exit(后续可以实现回填机制)) 常量传播 | 优化阶段 | 关键作用 | 是否必须 | |-------------------|----------------------------------|----------| | `Mem2Reg` | 消除冗余内存访问,转换为SSA形式 | ✅ 核心 |(必须) | `DCE` (死代码消除) | 移除无用指令 | ✅ 必要 |(必须) | `DFE` (死函数消除) | 移除未使用的函数 | ✅ 必要 |(必须) | `Global2Local` | 全局变量降级为局部变量 | ✅ 重要 | 还需要做 Reg2Mem ### 3. **后端核心流程(必须实现)** ```mermaid graph LR A[IR指令选择] --> B[寄存器分配] B --> C[指令调度] C --> D[汇编生成] ``` 1. **指令选择**(关键步骤): - `DAGBuilder`:将IR转换为有向无环图(DAG) - `DAGCoverage`:DAG到目标指令的映射 - `Mid2End`:IR到机器指令的转换接口 2. **寄存器分配**: - `RegisterAlloc`:基础寄存器分配器(可先实现简单算法如线性扫描) 3. **汇编生成**: - `RiscvPrinter`:将机器指令输出为RISC-V汇编 - 实现基础指令集:`add`/`sub`/`lw`/`sw`/`beq`/`jal`等 ### 4. **最小可工作流程** ```cpp // 精简版编译流程(跳过复杂优化) int main() { // 1. 前端解析 auto module = sysy::SysYIRGenerator().genIR(input); // 2. 关键中端优化 sysy::Mem2Reg(module).run(); // 必须 sysy::Global2Local(module).run(); // 必须 sysy::DCE(module).run(); // 推荐 // 3. 后端代码生成 auto backendModule = mid2end::CodeGenerater().run(module); riscv::RiscvPrinter().print("output.s", backendModule); } ``` ### 5. **当前开发优先级建议** 1. **完成IR生成**: - 确保能构建基本块、函数、算术/内存/控制流指令 - 实现`createCall`/`createLoad`/`createStore`等核心方法 2. **实现Mem2Reg**: - 插入Phi节点 - 变量重命名(关键算法) 3. **构建基础后端**: - 指令选择:实现IR到RISC-V的简单映射(例如:`IRAdd` → `add`) - 寄存器分配:使用无限寄存器方案(后期替换为真实分配) - 汇编打印:支持基础指令输出 > **注意**:循环优化、函数内联、高级寄存器分配等可在基础流程打通后逐步添加。初期可跳过复杂优化。 ### 6. 调试建议 - 添加IR打印模块(`SysYPrinter`)验证前端输出 - 使用简化测试用例: ```c int main() { int a = 1; int b = a + 2; return b; } ``` - 逐步扩展支持: 1. 算术运算 → 2. 条件分支 → 3. 函数调用 → 4. 数组访问 通过聚焦这些核心模块,你们可以快速打通从SysY到RISC-V的基础编译流程,后续再逐步添加优化传递提升代码质量。