From cd235e60ccd78b2b87a87ce705d6fcf359e3ff6d Mon Sep 17 00:00:00 2001 From: Lane0218 Date: Tue, 30 Dec 2025 12:34:21 +0800 Subject: [PATCH] =?UTF-8?q?docs(doc):=20=E6=B7=BB=E5=8A=A0=E5=AE=9E?= =?UTF-8?q?=E9=AA=8C=E8=B5=84=E6=96=99=E4=B8=8E=E5=8F=82=E8=80=83=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 doc/并行编译课程实验.xlsx 与 doc/reference-README.md - 移除旧的 doc/lab1-handout.md --- doc/lab1-handout.md | 86 ----------------- doc/reference-README.md | 150 ++++++++++++++++++++++++++++++ doc/并行编译课程实验.xlsx | Bin 0 -> 10294 bytes 3 files changed, 150 insertions(+), 86 deletions(-) delete mode 100644 doc/lab1-handout.md create mode 100644 doc/reference-README.md create mode 100644 doc/并行编译课程实验.xlsx diff --git a/doc/lab1-handout.md b/doc/lab1-handout.md deleted file mode 100644 index 69a1531..0000000 --- a/doc/lab1-handout.md +++ /dev/null @@ -1,86 +0,0 @@ -# Lab1 Handout:ANTLR 接入与构建指令 - -本文档说明如何在本工程中使用 ANTLR(C++ 目标)生成解析器代码,并完成编译构建。 - -## 1. 目录约定(与 doc/目录结构设计.md 一致) - -- 文法文件(提交到仓库): - - `src/antlr4/SysY.g4` -- ANTLR 自动生成文件(不提交到仓库): - - 统一输出到构建目录:`/generated/antlr4/` - - 典型文件:`SysYLexer.*`、`SysYParser.*`、`SysYBaseVisitor.*`、`SysYVisitor.*`、`*.tokens`、`*.interp` -- ANTLR4 C++ runtime(提交到仓库): - - `third_party/antlr4-runtime-4.13.2/` - - 工程通过 CMake 直接构建并链接该 runtime(无需系统安装 antlr4-runtime) - -## 2. 生成代码(启用 -visitor) - -说明: -- 本实验不提供脚本,请手动执行以下命令。 -- 生成时启用 `-visitor`,并关闭 listener(`-no-listener`)。 - -### 2.1 前置依赖 - -- Java(用于运行 `antlr-*.jar`) -- ANTLR4 完整 jar(本仓库已内置 4.13.2) - -本仓库内置 jar 路径: -- `third_party/antlr-4.13.2-complete.jar` - -### 2.2 生成命令 - -假设构建目录为 `build/`(可自行替换为 `build-debug/`、`out/` 等): - -```bash -mkdir -p build/generated/antlr4 - -java -jar third_party/antlr-4.13.2-complete.jar \ - -Dlanguage=Cpp \ - -visitor -no-listener \ - -Xexact-output-dir \ - -o build/generated/antlr4 \ - src/antlr4/SysY.g4 -``` - -生成完成后,请确认目录 `build/generated/antlr4/` 中出现: -- `SysYLexer.cpp/.h` -- `SysYParser.cpp/.h` -- `SysYBaseVisitor.cpp/.h`、`SysYVisitor.cpp/.h` - -## 3. 编译构建(CMake) - -本工程默认使用: -- `third_party/antlr4-runtime-4.13.2` 提供的 C++ runtime(无需额外安装) -- `build/generated/antlr4/*.cpp` 作为前端解析器实现的编译输入(需要先完成第 2 节的生成) - -### 3.1 配置 - -```bash -cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -``` - -### 3.2 构建 - -```bash -cmake --build build -j "$(nproc)" -``` - -成功后应当生成: -- 可执行文件目标:`compiler` - -## 4. 常见问题 - -### 4.1 找不到 SysYParser.h / SysYLexer.h - -原因:没有先生成,或生成目录不匹配。 - -排查: -- 确认 `build/generated/antlr4/` 下存在生成的 `.h/.cpp` -- 如果你使用的是非 `build/` 的构建目录(例如 `out/`),则生成输出也要改为 `out/generated/antlr4/` - -### 4.2 修改 SysY.g4 后编译行为未变化 - -原因:生成文件未更新。 - -解决: -- 每次修改 `src/antlr4/SysY.g4` 后都要重新执行第 2 节生成命令。 diff --git a/doc/reference-README.md b/doc/reference-README.md new file mode 100644 index 0000000..c3ce3e6 --- /dev/null +++ b/doc/reference-README.md @@ -0,0 +1,150 @@ +# 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.9.3版本的可执行程序与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 test/funcrparams.sy +# -> 1, 0xa, 011, "hellow" +``` + +## Documentation + +[ANTLR手册](doc/The%20Definitive%20ANTLR%204%20Reference.pdf) + +[SysY语言规范](doc/sysy-2022-spec.pdf) + +[SysY运行时库](doc/sysy-2022-runtime.pdf) + +## 实验1:用ANTLR实现SysY词法/语法分析器 + +当前的代码框架已经部署好了编译环境,同学们可专注于程序开发。 + +在实验1中,同学们需要完成的任务包括 + +- 参照SysY语言规范,修改`src/SysY.g4`文件,实现SysY词法/语法的完整定义 +- 修改任意代码后需要重新执行`cmake --build build`命令重新构建项目,ANTLR工具会从`SysY.g4`生成词法/语法分析器,生成的文件位于`./build/src`目录 +- (进阶内容)修改`src/ASTPrinter.h`与`src/ASTPrinter.cpp`,实现从AST输出源程序,但输出的源程序是经过格式化的,测试用例为`test/format-test.sy`,格式化后的参考结果为`test/format-ref.sy` + +## 实验2:从AST生成中间表示 + +exp2分支为大家准备好了进行实验2的基本代码框架,包括 + +- IR相关数据结构的定义:`src/IR.h` +- 创建IR对象的工具类`src/IRBuilder.h` +- IR生成器的示例代码`src/SysYIRGenerator.h` + +在实验2中,同学们需要完成的任务包括 + +- 熟悉掌握IR定义与相关数据结构 +- 从AST生成IR(基于visitor机制) + +请同学们仔细阅读代码学习IR的定义。可以使用doxygen工具自动生成HTML文档 + +```bash +sudo apt install doxygen graphviz +doxygen doc/Doxyfile +``` + +上述命令执行完毕后,将在doxygen/html下找到生成的代码文档。 + +## 实验3:从SysY IR 生成ARMv7汇编代码 +### 后端相关源码 +当前ref2分支为ARMv7后端代码实验,已经包含了后端代码生成的代码框架,包含 + +- 后端生成代码源文件`src/backend/codegen.cpp` +- 后端生成代码头文件`src/backend/codegen.hpp` + +本实验需要基于以上两个源文件添加ARMv7后端生成代码(也可以按照自己的设计重头编写整个后端生成代码),完成这两个源文件中所有空函数的实现.不局限于本实验提供的后端代码框架,自由设计自己的ARMv7后端实现. + +### 后端代码的编译与运行 +本实验也修改了驱动代码sysyc.cpp, sysyc.cpp可以调用后端生成的最顶层函数接口code_gen().该函数会逐层调用各层级的代码生产函数并最终生成ARMv7汇编代码并打印至屏幕.通过下列命令编译 +```bash +cmake -S . -B build +cmake --build build +``` +通过下列命令运行编译产生的sysyc +```bash +./sysyc 01_add.sy +``` +或者通过下列命令只生成SysY IR代码 +```bash +./sysyc 01_add.sy ir +``` +### 测试 +本实验提供了两个sysy源文件用于测试,分别是位于sysy-backend/test的01_add.sy 11_add2.sy; 当完成sysyc的编译器后端后,可以通过sysy-backend/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/bin/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.pptx'设置树莓派的编译运行环境,也可以在自己的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汇编代码与编译器后端行为. + +### 参考文档 +参见doc/backend/下的ARMv7相关文档 \ No newline at end of file diff --git a/doc/并行编译课程实验.xlsx b/doc/并行编译课程实验.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..b6bd13337bfc0ae276d597747d78c07eb2bcbf52 GIT binary patch literal 10294 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