Files
mysysy/README.md
2025-02-27 23:14:53 +08:00

402 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CCRG SysY Compiler
用于实现SysY编译器的代码框架。
## Getting Started
建议使用Ubuntu 22.04系统原生版本与WSL版本均可。
[Ubuntu下载与安装说明](https://ubuntu.com/download/desktop)
[WSL Ubuntu安装说明](https://learn.microsoft.com/en-us/windows/wsl/install)
SysY编译器前端基于[ANTLR](https://www.antlr.org/index.html)工具实现本仓库已经包含ANTLR 4.12.0版本的可执行程序与C++运行时库但编译ANTLR运行时库存在一些依赖需要提前安装。
```bash
sudo apt update
sudo apt install -y uuid-dev libutfcpp-dev pkg-config make git cmake openjdk-11-jre
```
依赖安装完成后可以开始构建SysY编译器构建过程包含了ANTLR运行时库的构建
```bash
git clone https://gitee.com/xsu1989/sysy.git
cd sysy
cmake -S . -B build
cmake --build build
```
构建完成后,可以运行一个小的测试用例。该测试将逗号分隔的整数或字符串列表进行格式化后重新输出,即将相邻参数之间的分隔统一调整为逗号外加一个空格。
```bash
cat /test/funcrparams.sysy
# -> 1,0xa , 011, "hellow"
./build/bin/sysyc -f test/funcrparams.sy
# -> 1, 0xa, 011, "hellow"
```
## 参考资料
所有资料均存放于`doc`目录
[ANTLR手册](doc/The%20Definitive%20ANTLR%204%20Reference.pdf)
[SysY语言规范](doc/sysy-2022-spec.pdf)
[SysY运行时库](doc/sysy-2022-runtime.pdf)
[ARM相关资料](doc/arm/)
[RISC-V相关资料](doc/riscv/)
## 实验1用ANTLR实现SysY词法/语法分析器
当前的代码框架已经部署好了编译环境,同学们可专注于程序开发。
在实验1中同学们需要完成的任务包括
- 参照SysY语言规范修改`src/SysY.g4`文件实现SysY词法/语法的完整定义
- 进阶内容SysY格式化器。修改`src/ASTPrinter.h``src/ASTPrinter.cpp`实现从AST输出源程序但输出的源程序是经过格式化的测试用例为`test/format-test.sy`,格式化后的参考结果为`test/format-ref.sy`
修改代码后只需要执行`cmake --build build`命令重新构建项目ANTLR工具会从`SysY.g4`生成词法/语法分析器,生成的文件位于`./build/src`目录。
```bash
$ ls build/src/
CMakeFiles SysYBaseVisitor.cpp SysY.interp SysYLexer.h SysYLexer.tokens SysYParser.h SysYVisitor.cpp
cmake_install.cmake SysYBaseVisitor.h SysYLexer.cpp SysYLexer.interp SysYParser.cpp SysY.tokens SysYVisitor.h
```
完成实验1后可以通过执行`sysyc`观察生成的IR。
```bash
./build/sysyc -s ast test/funcrparams.sy
```
完成格式化器进阶内容后,可以通过执行`sysyc`观察格式化器的执行效果。
```bash
./build/sysyc -f test/format-test.sy
```
## 实验2从AST生成中间表示
代码框架提供的基础设施包括
- IR相关数据结构的定义`src/IR.h`
- 创建IR对象的工具类`src/IRBuilder.h`
- IR生成器的示例代码`src/SysYIRGenerator.h`
在实验2中同学们需要完成的任务包括
- 熟悉掌握IR定义与相关数据结构
- 向`src/SysYIRGenerator.h`、`src/SysYIRGenerator.cpp`中增加相关代码从AST生成IR基于visitor机制
完成实验2后可以通过执行`sysyc`观察生成的IR。
```bash
./build/sysyc -s ir 01_add.sy
```
请同学们仔细阅读代码学习IR的定义。可以使用doxygen工具自动生成HTML文档
```bash
sudo apt install doxygen graphviz
doxygen doc/Doxyfile
```
上述命令执行完毕后将在doxygen/html下找到生成的代码文档。
## 实验3从SysY IR 生成ARMv7汇编代码
### 后端相关源码
当前ref2分支为ARMv7后端代码实验,已经包含了后端代码生成的代码框架,包含
- 后端生成代码头文件`src/Backend.h`
- 后端生成代码源文件`src/Backend.cpp`
本实验需要基于以上两个源文件添加ARMv7后端生成代码,完成这两个源文件中所有空函数的实现.
也可以按照自己的设计重头编写整个后端生成代码,不局限于本实验提供的后端代码框架,自由设计自己的ARMv7后端实现.
### 后端代码的编译与运行
在`src/sysyc.cpp`中调用了后端生成的最顶层函数接口`code_gen()`该函数会逐层调用各层级的代码生成函数并最终生成ARMv7汇编代码并打印至屏幕。
通过下列命令运行编译产生的sysyc
```bash
./build/sysyc 01_add.sy
```
或者通过下列命令只生成SysY IR代码
```bash
./build/sysyc 01_add.sy ir
```
### 测试
本实验提供了2个sysy源文件用于调测试,分别是 test/01_add.sy (基础) 和 test/11_add2.sy (带函数调用); 当完成sysyc的编译器后端后,可以通过test下的Makefile文件编译生成测试程序的可
执行二进制.
#### 在x86平台编译运行测试代码
##### 下载并配置ARMv7交叉编译器工具链
在[Arm GNU Toolchain](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads)下载安装交叉编译工具链,并设置环境变量PATH来使用交叉编译工具链
```bash
export PATH=${your_arm-gnu-toolchain_path}/bin:$PATH;
```
##### 安装qemu模拟器
```bash
sudo apt update
sudo apt install qemu-system-arm qemu-user
```
##### 编译测试程序
使用如下命令调用完成后端实现的sysyc生成ARMv7汇编代码,并调用ARMv7交叉编译工具链汇编并链接生成可执行文件.
```bash
cd test
../build/sysyc 01_add.sy > 01_add.s
arm-none-linux-gnueabihf-gcc 01_add.s -o 01_add.out -static #注意使用-static选项来静态链接
```
##### 使用qemu-arm模拟运行测试程序
```bash
cd test
qemu-arm ./01_add.out
echo $? #查看测试程序返回值
```
##### 使用makefile编译与运行
```bash
cd test
make all -r #利用sysyc编译两个测试程序
make run -r #使用qemu-arm模拟运行两个测试程序
```
#### 在树莓派上编译运行测试代码
参见'Raspberry.pdf'设置树莓派的编译运行环境,也可以在自己的x86电脑上利用ARMv7交叉编译工具链来编译测试程序, 将编译产生的可执行文件上传到树莓派上运行.
### 交叉编译器生成汇编代码
可以通过如下的命令让交叉编译器生成汇编代码,以参考gcc后端的编译行为.
```bash
cd test
cp 01_add.sy 01_add.c # 修改代码后缀名
arm-none-linux-gnueabihf-gcc 01_add.c -O0 -S -o 01_add.S #O0编译优化
```
请自己构造一些简单的c程序,阅读交叉编译器编译产生的汇编代码来理解ARMv7汇编代码与编译器后端行为.
### 实验考核
实验考核使用的sysy程序是 test/10_test.sy考核时需要展示完整测试过程包括(1)调用实现了后端代码生成功能的sysyc生成汇编代码(2)经过汇编链接生成二进制可执行文件,(3)在Qemu或
树莓派平台上运行结果正确
### 参考文档
参见doc/backend/下的ARMv7相关文档
## 实验4标量优化
### 实验任务
在前三次实验打通编译器前端-中端-后端基本功能的基础上,在编译中端增加标量优化支持
- 优化一: 循环优化,在控制流图中找出循环,完成循环不变量外提和强度削弱 (选作)
- 优化二:冗余删除,在公用子表达式删除、死代码删除、值编号、常数折叠和常数传播四种冗余优化中,选择完成至少一种优化。
- 进阶内容:在中端增加遍管理器支持
### 实验目标
通过实验,掌握到达定值分析、活跃变量分析、可用表达式分析等数据流分析方法,能够利用数据流分析和公用子表达式删除、死代码删除、值编号、常数折叠和常数传播等冗余优化算法实现相应的代码优化。
### 实验方法与主要步骤
1. 在编译中端增加到达定值分析、活跃变量分析、可用表达式分析等数据流分析遍
2. 实现所选循环优化和冗余删除任务种对应的优化算法,实现为对应的优化遍
3. 进阶内容:在中端增加遍管理器,管理实现的多个分析遍和优化遍,以更灵活的方式配置编译遍顺序
### 测试与验证
测试源程序为test/20_test_licm_sr.sytest/21_test_cse.sytest/22_test_dce.sytest/23_test_vn.sytest/24_test_cp_cf.sy经过实验四生成的sysyc编译器能够
1. 生成经过循环优化或冗余删除优化后的汇编代码交叉编译后的二进制文件可以在Qemu模拟器/树莓派上正确运行;
2. 对比查看优化前和优化后的IR是否达到了要实现的优化目标。
## 实验5寄存器分配
### 实验任务
在编译后端增加寄存器分配支持,选择一种分配方法实现寄存器分配
- 使用计数寄存器分配方法
- 进阶内容:图着色寄存器分配方法或线性扫描寄存器分配方法
### 实验目标
通过实验理解和掌握编译后端的寄存器分配方法,理解不同寄存器分配方法的优缺点
### 实验方法与主要步骤
1. 将ARMv7的R0-R10, R12作为可用寄存器资源
2. 遍历所有Function, 在每个Function内部做寄存器分配
3. 维护RegTable符号表记录哪些LocalValue分配到寄存器以及具体寄存器编号
4. 维护StackTable符号表记录哪些LocalValue分配到栈上以及栈上的位置
### 测试与验证
测试源程序为test/30_test_reg_alloc.sy验证经过实验五生成的sysyc编译器能够生成完成了寄存器分配的的汇编代码交叉编译后的二进制文件可以在Qemu模拟器/树莓派上正确运行。
1. 功能验证能够生成完成了寄存器分配的的汇编代码交叉编译后的二进制文件可以在Qemu模拟器/树莓派上运行且结果正确;
2. 性能验证:经寄存器分配后的代码在树莓派上的运行时间有效缩短。
## 实验6循环优化
### 实验任务
在之前实现编译器的基础上,在编译中端增加循环优化支持。
+ 任务一
针对特定的测试用例,手动应用循环变换技术,如循环展开、循环融合、循环拆分等循环优化手段。对比变换前后的程序性能,例如程序执行时间、资源消耗等指标的测量和评估,分析优化效果。
+ 任务二
SROA标量替换在编译中端增加标量替换的优化遍能够对循环进行一致性依赖分析识别循环中存在的可替换内存访问模式并将其替换为标量变量以减少内存访问开销并可能揭示更多的优化机会。
+ 任务三
循环展开,在编译中端增加循环展开的优化遍,增加指令级并行性,减少循环控制开销。同时分析循环展开对程序性能的影响,包括执行速度提升和潜在的代码膨胀问题。
+ 进阶内容在编译中端增加循环分块Loop Tiling的优化遍。将循环嵌套划分为更小、更规则的块以提高数据局部性和缓存利用率。探索循环分块对程序性能的具体影响以及如何根据目标硬件特性调整分块策略。
### 实验目标
通过实验,加深学生对编译器中循环优化算法的理解,提升他们在实际编译器开发中应用这些技术的能力。
### 实验方法与主要步骤
#### 实验方法
+ 可以从现有的成熟编译器入手例如LLVM分析LLVM中关于循环优化遍的实现。通过LLVM中的工具对循环变化前后的控制流图进行输出方便直观感受循环变换对循环结构带来的影响。
+ 之后根据课堂上的理论知识,设计所需的循环优化算法,并将其实现为编译器中端的新优化遍。
+ 最后,对优化结果进行评估,根据性能分析结果调整优化策略。
#### 主要步骤
1. 在编译器中端实现循环分析遍,该遍能够识别程序中的循环结构,并收集循环相关的信息,如循环边界、循环内的变量及其数据依赖关系等。
2. 根据实验任务要求,依次实现标量替换优化遍、循环展开优化遍和循环分块优化遍。
3. 如果在实验四中实现了遍管理器,将本实验实现的循环优化遍进行管理。
4. 将测试程序依次经过三个优化遍处理,对每次优化前后的程序进行基准测试,收集性能数据。分析每种优化方案对程序带来的性能提升,考虑不同优化顺序对性能优化带来的影响。
### 实验测试
测试程序为test/40_mm1.sy, test/41_mm2.sy, test/42_mm3.sy。经过实验六生成的sysyc编译器
1. 能够生成经过标量替换、循环展开或循环分块优化后的汇编代码交叉编译后的二进制文件可以在Qemu模拟器/树莓派上运行且结果正确
2. 对比优化前和优化后的IR查看是否达到了要实现的优化目标。
3. 对程序的性能指标进行分析,如程序执行时间、程序大小、访存次数等,计算优化带来的性能收益,并分析其中的原因。
# 实验7自动向量化
### 实验任务
在已有编译器框架的基础上,实验任务分为三个部分,旨在逐步引导学生掌握向量化技术的应用与开发。
+ 任务一测试现有编译器的向量化支持通过给定的测试用例对Clang编译器的自动向量化功能进行测试。熟悉Clang编译器在自动向量化和编译指导向量化方面的使用方法。分析给定循环的向量化可能性判断循环是否适合向量化并用Clang编译器进行测试对比循环向量化前后的中间表示IR理解向量化对IR的影响。
+ 任务二:从理论出发,设计并实现编译器中端的向量化优化遍。通过循环分析,识别适合向量化的循环,并进行必要的变换以满足向量化条件。
+ 任务三探索NEON指令集扩展在向量化中的应用并通过Intrinsic函数实现向量化。
+ 进阶任务:实现循环的多版本控制,从硬件角度看,由于向量运算的启动开销比变量运算大,所以当向量长度较小时,向量运算速度可能会低于对应的标量运算速度。所以进阶任务是对循环进行版本控制,为循环生成一个标量执行版本和向量执行版本。当迭代次数小时,用标量计算该循环,反之用向量计算。
### 实验目标
通过实验理解向量化的基本概念包括SIMD架构、自动向量化和编译指导向量化的原理。熟悉Clang编译器的自动向量化功能理解编译指导向量化的使用方法。最终设计并实现编译器中端的向量化优化遍通过循环分析识别并转换适合向量化的循环。
### 实验方法与主要步骤
#### 实验方法
可以先从简单的向量化场景出发,针对特定的指令进行向量化操作,然后在此基础上进行补充,考虑更全面的向量化场景。
例如,可以先对循环中的以下两种指令进行循环向量化:
1. 循环体中的```dst[i] = c``` (c是常数, i是迭代变量)
2. 循环体中的```dst[i]=src[i]```
在确定要并行化的循环形式之后,对原始循环按照如下流程实现向量化:
1. 检查循环体中的指令,确保符合向量化的基本条件。
例如循环迭代步为1迭代变量递增比较指令为小于等。
2. 识别循环体中可以向量化的语句: ```dst[i] = c``` 或 ```dst[i]=src[i]```
3. 更新循环的迭代步: ```i=i+1``` => ```i=i+4```
4. 尾循环处理。
上述就是针对简单场景进行循环向量化的实现流程,进而可以扩展到更复杂的应用场景中。
#### 主要步骤
1. 实现循环向量化所需要的分析遍,例如数据依赖分析。同时,为了对不能进行向量化的循环进行转化以实现向量化,还需要实现基本的循环优化遍,例如循环分裂、循环剥除等。
2. 编码实现循环向量化优化遍,将设计的算法集成到编译器中端。
3. 如果在实验四中实现了遍管理器,将本实验实现的循环向量化优化遍进行管理。
4. 分析性能数据,评估向量化对性能的具体影响,并根据测试结果调整优化策略。
### 实验测试
测试程序包括test/40_mm1.sy, test/41_mm2.sy, test/42_mm3.sy。通过本实验七生成的 sysyc 编译器需要满足以下要求:
1. 能够生成经过循环向量化优化后的汇编代码交叉编译后的二进制文件可以在Qemu模拟器/树莓派上运行且结果正确。
2. 对比优化前和优化后的IR查看是否达到了要实现的优化目标。分析 IR 的变化,理解向量化对代码结构的影响。
3. 对程序的性能指标进行分析,如程序执行时间、程序大小、访存次数等,计算优化带来的性能收益,并分析其中的原因。
# 实验8自动并行化
### 实验任务
在已有编译器框架的基础上,实现多线程并行技术。
+ 任务一对给定的测试程序进行手工并行化使用OpenMP的编译指导命令来指导编译器生成并行代码。对比并行化前后的性能包括但不限于执行时间、资源消耗等分析并行化带来的性能提升原因。
+ 任务二:在编译器中端开发一个并行化优化遍,自动将给定的矩阵相乘的串行程序进行并行化处理,以提高计算效率。对于不满足并行化的循环程序,能够进行适当的循环变换,暴露循环中的并行特征。
+ 任务三:通过交叉编译,将并行化后的程序部署到开发板上,检查程序输出的正确性,确保并行化没有改变程序的逻辑结果。记录并行化前后的程序性能数据,如执行时间、内存使用情况等,并计算加速比。
+ 进阶任务选择一个复杂算法如快速傅里叶变换、LU分解等分析其并行化潜力。在编译器中实现对该算法的并行化优化遍对比并行化前后的性能数据分析并行化在不同算法中的应用效果。
### 实验目标
深入理解并行化的判定条件,能够对不满足并行化的循环进行简单的循环变换从而实现并行化,实现基本的并行化优化遍。通过矩阵乘法等计算密集型程序的优化,实际演示并行化技术对提升程序性能的效果,并与现有的成熟自动并行化编译器进行效果比较,从而分析并行化性能提升的原因。
### 实验方法与主要步骤
#### 实验方法
+ 以矩阵乘测试程序为例,首先需要对程序中的循环进行依赖性检查,识别出其中不存在跨迭代依赖的循环。对于不满足并行化条件的循环进行必要的循环优化,暴露出其中的并行化机会。
+ 识别出可并行化的循环后,需要对线程任务进行划分。
+ 由于线程创建、销毁和同步等操作具有性能损耗,以矩阵乘为例,在并行化时要使线程处理的任务粒度足够大,尽可能并行化最外层循环。
+ 计算新的循环结构,确定每个线程要处理循环的上下界。
最后修改循环结构,并在循环前后分别插入线程创建(```_thread_create```)和线程等待(```_thread_join```)函数。
#### 主要步骤
1. 实现循环并行所需要的分析遍,例如数据依赖分析。同时,为了对不能进行并行化的循环进行转化以实现并行化,还需要实现基本的循环优化遍,例如循环分裂、循环剥除等。
2. 实现循环并行化对应的优化算法,实现对应的循环并行化优化遍
3. 如果在实验四中实现了遍管理器,将本实验实现的循环并行化优化遍进行管理。
4. 在开发板上运行并行化后的程序,验证其输出的正确性。并收集并行化前后的性能数据,计算并行化前后的加速比,量化并行化带来的性能提升。
### 实验测试
测试程序包括 test/40_mm1.sy, test/41_mm2.sy, test/42_mm3.sy。通过本实验八生成的 sysyc 编译器需要满足以下要求:
1. 能够生成经过循环并行化优化后的汇编代码交叉编译后的二进制文件可以在Qemu模拟器/树莓派上运行且结果正确。
2. 对比优化前和优化后的IR查看是否达到了要实现的优化目标。
3. 记录并行化前后的程序性能数据,包括但不限于执行时间、内存使用、处理器负载等,计算加速比,评估并行化对程序性能的提升效果。
## 致谢
[全国大学生计算机系统能力大赛](https://compiler.educg.net): 本实验使用全国大学生计算机系统能力大赛-编译系统实现赛SysY语言规范测试用例部分参考大赛设计。