11 KiB
11 KiB
SysY 编译器目录结构设计
1. 编译流程图
[ 前端 Frontend ]
C 源代码
↓
Lexer / Parser(ANTLR)
↓
AST 构建 + 语义分析(Sema)
↓
AST(带类型 / 符号 / 常量)
[ 中端 Middle-end ]
AST
↓
IR 构建(平台无关 IR,LLVM 风格)
↓
IR 优化(ConstFold / DCE / mem2reg 等,可选)
↓
优化后的 IR
[ 后端 Backend(ARMv8-A / AArch64)]
IR
↓
IR → MIR(AArch64 指令 + 虚拟寄存器) ← 指令选择
↓
MIR → MIR(AArch64 指令 + 物理寄存器) ← 寄存器分配(含 spill/reload)
↓
栈帧构建 / Prologue / Epilogue 插入 ← FrameLowering
↓
MIR(最终机器指令)
↓
汇编打印
↓
ARMv8-A 汇编(.s)
2. 目录结构
.
├── CMakeLists.txt
├── README.md
├── .gitignore
├── src/
│ ├── CMakeLists.txt
│ ├── main.cpp
│ ├── antlr4/
│ │ └── SysY.g4
│ ├── frontend/
│ │ ├── AntlrDriver.cpp
│ │ └── AstBuilder.cpp
│ ├── ast/
│ │ ├── AstNodes.cpp
│ │ └── AstPrinter.cpp
│ ├── sem/
│ │ ├── Sema.cpp
│ │ ├── SymbolTable.cpp
│ │ └── ConstEval.cpp
│ ├── irgen/
│ │ ├── CMakeLists.txt
│ │ ├── IRGenDriver.cpp
│ │ ├── IRGenFunc.cpp
│ │ ├── IRGenStmt.cpp
│ │ ├── IRGenExp.cpp
│ │ └── IRGenDecl.cpp
│ ├── ir/
│ │ ├── Context.cpp
│ │ ├── Module.cpp
│ │ ├── Function.cpp
│ │ ├── BasicBlock.cpp
│ │ ├── Type.cpp
│ │ ├── Value.cpp
│ │ ├── Instruction.cpp
│ │ ├── IRBuilder.cpp
│ │ ├── IRPrinter.cpp
│ │ ├── analysis/
│ │ │ ├── DominatorTree.cpp
│ │ │ └── LoopInfo.cpp
│ │ └── passes/
│ │ ├── PassManager.cpp
│ │ ├── Mem2Reg.cpp
│ │ ├── ConstFold.cpp
│ │ ├── DCE.cpp
│ │ └── CFGSimplify.cpp
│ ├── mir/
│ │ ├── MIRContext.cpp
│ │ ├── MIRFunction.cpp
│ │ ├── MIRBasicBlock.cpp
│ │ ├── MIRInstr.cpp
│ │ ├── Register.cpp
│ │ ├── Lowering.cpp
│ │ ├── RegAlloc.cpp
│ │ ├── FrameLowering.cpp
│ │ ├── AsmPrinter.cpp
│ │ └── passes/
│ │ ├── PassManager.cpp
│ │ └── Peephole.cpp
│ └── utils/
│ ├── Log.cpp
│ └── CLI.cpp
├── include/
│ ├── frontend/
│ ├── ast/
│ ├── sem/
│ ├── irgen/
│ ├── ir/
│ ├── mir/
│ └── utils/
├── sylib/
│ ├── sylib.c
│ └── sylib.h
└── test/
├── test_case/
├── test_result/
└── run_tests.sh
3. 文件功能说明
3.1 顶层文件
CMakeLists.txt- 工程构建入口:配置编译器、添加子目录、链接生成最终可执行文件。
- 负责集成 ANTLR runtime(通过系统依赖或第三方源码),并将“ANTLR 生成代码所在的构建目录”加入编译。
README.md- 实验/工程说明:依赖环境、构建方式、测试脚本用法、ANTLR 生成命令(写在文档中,不提供脚本)。
.gitignore- 忽略构建产物(如
build/)、ANTLR 自动生成文件、test/test_result/等输出目录。
- 忽略构建产物(如
3.2 src/:实现代码
3.2.1 入口与构建
src/CMakeLists.txtsrc/子目录构建脚本:编译各子模块源文件,生成静态库/目标并链接到最终可执行文件。
src/main.cpp- 编译器入口:解析命令行参数、驱动完整流水线(Frontend → Middle-end → Backend)。
- 负责把不同阶段产物输出为文件(如输出
.ll/.s的开关可在此集中实现)。
3.2.2 src/antlr4/:SysY 文法(只提交 .g4)
-
src/antlr4/SysY.g4- SysY 语法定义(lexer + parser rules),为 ANTLR 生成 C++ lexer/parser 提供输入。
-
ANTLR 自动生成文件位置约定:该工程采用工程化方式:ANTLR 生成文件不进入仓库,统一放到构建目录,例如:
build/generated/antlr4/
-
典型生成物包括(随生成选项略有差异):
SysYLexer.cpp/.hSysYParser.cpp/.hSysYBaseVisitor.cpp/.h、SysYVisitor.cpp/.h*.tokens、*.interp
3.2.3 src/frontend/:ANTLR 调用与 AST 构建
src/frontend/AntlrDriver.cpp- 读取源代码并调用 ANTLR 生成的 lexer/parser,得到 parse tree。
- 由于语法正确性由测试保证,该模块只需提供“可用的解析入口”,不需要实现复杂的语法错误报告体系。
src/frontend/AstBuilder.cpp- 将 parse tree 转换为 AST(
src/ast/*),并在 AST 节点上保留必要的定位信息(可选,用于调试/日志)。
- 将 parse tree 转换为 AST(
3.2.4 src/ast/:抽象语法树(AST)
src/ast/AstNodes.cpp- AST 节点定义与实现:表达式、语句、声明、函数、类型等。
- AST 节点应当能够承载后续阶段附加信息(类型、符号绑定、常量值等)。
src/ast/AstPrinter.cpp- AST 调试打印:用于验证 AST 构建是否符合预期,便于定位前端/语义阶段问题。
3.2.5 src/sem/:语义分析(Sema)
src/sem/Sema.cpp- 语义分析主流程:符号解析、类型检查、控制流规则检查、必要的隐式转换插入/记录等。
- 输出为“带类型 / 符号 / 常量信息”的 AST(可通过在 AST 节点上附加属性实现)。
src/sem/SymbolTable.cpp- 符号表与作用域管理:支持嵌套作用域、变量/函数/参数/常量的注册与查找。
src/sem/ConstEval.cpp- 常量求值:用于数组维度、全局初始化、
const表达式等需要编译期计算的场景。
- 常量求值:用于数组维度、全局初始化、
3.2.6 src/irgen/:AST → IR(平台无关,LLVM 风格)
src/irgen/IRGenDriver.cpp- 驱动 Visitor 遍历 AST,调度各子模块完成翻译。
src/irgen/IRGenFunc.cpp- 函数翻译:函数定义、参数列表与返回值翻译;创建对应 IR 函数对象。
src/irgen/IRGenStmt.cpp- 语句翻译:if/while/return 等控制流构造,生成基本块与分支。
src/irgen/IRGenExp.cpp- 表达式翻译:算术运算、比较、逻辑运算、函数调用等ir指令生成。
src/irgen/IRGenDecl.cpp- 声明翻译:处理全局变量、局部变量、数组初始化与空间分配等。
3.2.7 src/ir/:LLVM 风格 IR 核心
src/ir/Context.cpp- IR 上下文:管理类型/常量创建与复用、字符串/符号等公共资源。
src/ir/Module.cpp- Module 容器:保存全局变量、函数列表、目标信息(如需要)、符号表等。
src/ir/Function.cpp- Function 容器:参数、基本块列表、属性/元信息等。
src/ir/BasicBlock.cpp- 基本块表示:指令序列、前驱/后继关系(显式或可计算)。
src/ir/Type.cpp- IR 类型系统:
i32/f32/void、指针、数组、函数类型等(按 SysY 支持范围裁剪)。
- IR 类型系统:
src/ir/Value.cpp- SSA 值体系:常量、参数、指令结果等统一抽象。
src/ir/Instruction.cpp- 指令体系:二元运算、比较、load/store、call、br/condbr、ret、phi、alloca 等(按需求逐步补齐)。
src/ir/IRBuilder.cpp- IR 构建工具:集中管理插入点与指令创建,降低 IRGen 复杂度。
src/ir/IRPrinter.cpp- IR 文本输出:打印为
.ll风格文本,支撑调试与测试对比。
- IR 文本输出:打印为
src/ir/analysis/DominatorTree.cpp- 支配树分析:为
mem2reg、部分 CFG 优化与循环分析提供基础能力。
- 支配树分析:为
src/ir/analysis/LoopInfo.cpp- 循环分析:识别循环结构,为后续优化(可选)提供信息。
src/ir/passes/PassManager.cpp- IR pass 管理:按优化级别组织 pipeline,并统一运行与统计。
src/ir/passes/Mem2Reg.cpp- SSA 构造关键 pass:将局部变量的
alloca/load/store提升为 SSA(插入 PHI)。
- SSA 构造关键 pass:将局部变量的
src/ir/passes/ConstFold.cpp- IR 常量折叠:简化可判定的常量表达与常量控制流分支(按实现范围裁剪)。
src/ir/passes/DCE.cpp- 死代码删除:删除无用指令与无用块(与 CFG 简化配合)。
src/ir/passes/CFGSimplify.cpp- CFG 简化:删除不可达块、合并空块、简化分支等。
3.2.8 src/mir/:Machine IR
src/mir/MIRContext.cpp- MIR 上下文:保存目标约束、指令集信息等。
src/mir/MIRFunction.cpp- 机器函数:包含机器基本块、栈帧信息、虚拟/物理寄存器管理等。
src/mir/MIRBasicBlock.cpp- 机器基本块:机器指令列表与 CFG 信息。
src/mir/MIRInstr.cpp- 机器指令:opcode + operands(寄存器/立即数/栈槽/符号/标签等)。
src/mir/Register.cpp- 寄存器表示:区分虚拟寄存器与物理寄存器,提供编号/属性等。
- 寄存器类(RegClass):GPR/FPR 等分类与可分配集合描述(供 RA 使用)。
src/mir/Lowering.cpp- IR → MIR:从平台无关 IR 生成 AArch64 指令的 MIR(在此完成指令选择),初始使用虚拟寄存器。
src/mir/RegAlloc.cpp- 寄存器分配主入口:将虚拟寄存器分配到物理寄存器,并驱动 spill/reload 等后续处理。
src/mir/FrameLowering.cpp- 栈帧布局与序言尾声:根据寄存器分配阶段记录的栈槽需求计算栈大小与对齐,插入 prologue/epilogue,保存/恢复 callee-saved 等。
src/mir/AsmPrinter.cpp- 汇编打印:将最终机器指令 MIR 打印为 ARMv8-A 汇编
.s。
- 汇编打印:将最终机器指令 MIR 打印为 ARMv8-A 汇编
src/mir/passes/PassManager.cpp- MIR pass 管理:组织后端 pass 的运行顺序(PreRA/PostRA/PEI 等阶段)。
src/mir/passes/Peephole.cpp- 窥孔优化:删除冗余 move、合并常见指令模式,提升最终汇编质量。
3.2.9 src/utils/:日志与命令行
src/utils/Log.cpp- 日志输出:统一调试信息、阶段信息与错误信息输出。
src/utils/CLI.cpp- 命令行参数解析:输入/输出路径、输出类型(IR/MIR/ASM)、优化级别等。
3.3 include/:头文件目录
include/ 目录与 src/ 分层保持一致,用于放置对外可复用的头文件(具体头文件列表按实现新增)。
3.4 sylib/:SysY 运行库
sylib/sylib.c- SysY 运行库实现(按实验/评测规范提供 I/O 等函数)。
sylib/sylib.h- 运行库头文件声明(供编译器生成
call时引用,或用于链接阶段)。
- 运行库头文件声明(供编译器生成
3.5 test/:测试
test/test_case/- 测试输入目录:存放
*.sy测试用例(可选搭配*.in作为 stdin 输入)。
- 测试输入目录:存放
test/test_result/- 测试输出目录:由测试脚本生成
.ll/.s、运行输出、diff 结果与日志等。 - 建议通过
.gitignore忽略,不纳入版本管理。
- 测试输出目录:由测试脚本生成
test/run_tests.sh- Bash 测试脚本:批量编译
test_case/,将产物写入test_result/,并汇总通过/失败信息。
- Bash 测试脚本:批量编译