3.7 KiB
3.7 KiB
要打通从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. 后端核心流程(必须实现)
graph LR
A[IR指令选择] --> B[寄存器分配]
B --> C[指令调度]
C --> D[汇编生成]
-
指令选择(关键步骤):
DAGBuilder:将IR转换为有向无环图(DAG)DAGCoverage:DAG到目标指令的映射Mid2End:IR到机器指令的转换接口
-
寄存器分配:
RegisterAlloc:基础寄存器分配器(可先实现简单算法如线性扫描)
-
汇编生成:
RiscvPrinter:将机器指令输出为RISC-V汇编- 实现基础指令集:
add/sub/lw/sw/beq/jal等
4. 最小可工作流程
// 精简版编译流程(跳过复杂优化)
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. 当前开发优先级建议
-
完成IR生成:
- 确保能构建基本块、函数、算术/内存/控制流指令
- 实现
createCall/createLoad/createStore等核心方法
-
实现Mem2Reg:
- 插入Phi节点
- 变量重命名(关键算法)
-
构建基础后端:
- 指令选择:实现IR到RISC-V的简单映射(例如:
IRAdd→add) - 寄存器分配:使用无限寄存器方案(后期替换为真实分配)
- 汇编打印:支持基础指令输出
- 指令选择:实现IR到RISC-V的简单映射(例如:
注意:循环优化、函数内联、高级寄存器分配等可在基础流程打通后逐步添加。初期可跳过复杂优化。
6. 调试建议
- 添加IR打印模块(
SysYPrinter)验证前端输出 - 使用简化测试用例:
int main() { int a = 1; int b = a + 2; return b; } - 逐步扩展支持:
- 算术运算 → 2. 条件分支 → 3. 函数调用 → 4. 数组访问
通过聚焦这些核心模块,你们可以快速打通从SysY到RISC-V的基础编译流程,后续再逐步添加优化传递提升代码质量。