From 91e15b7b5e67900d4f6e90afe5bdab681dd82e89 Mon Sep 17 00:00:00 2001 From: atlantis Date: Sat, 8 Oct 2022 12:45:55 +0800 Subject: [PATCH] [Lab2] Publish --- CMakeLists.txt | 22 +- Documentations/2-ir-gen-warmup/README.md | 199 +++++ Documentations/common/LightIR.md | 818 ++++++++++++++++++ Documentations/common/cminusf.md | 189 ++++ .../common/figs/cpp_class_inherits.png | Bin 0 -> 91878 bytes .../common/figs/image-20201109145323504.png | Bin 0 -> 32921 bytes Documentations/common/logging.md | 45 + Documentations/common/simple_cpp.md | 108 +++ README.md | 3 + Reports/2-ir-gen-warmup/report.md | 24 + include/lightir/BasicBlock.h | 71 ++ include/lightir/Constant.h | 77 ++ include/lightir/Function.h | 89 ++ include/lightir/GlobalVariable.h | 26 + include/lightir/IRBuilder.h | 101 +++ include/lightir/IRprinter.h | 13 + include/lightir/Instruction.h | 435 ++++++++++ include/lightir/Module.h | 64 ++ include/lightir/Type.h | 148 ++++ include/lightir/User.h | 34 + include/lightir/Value.h | 48 + src/CMakeLists.txt | 4 +- src/io/CMakeLists.txt | 6 + src/io/io.c | 16 + src/lightir/BasicBlock.cpp | 68 ++ src/lightir/CMakeLists.txt | 18 + src/lightir/Constant.cpp | 104 +++ src/lightir/Function.cpp | 133 +++ src/lightir/GlobalVariable.cpp | 29 + src/lightir/IRprinter.cpp | 47 + src/lightir/Instruction.cpp | 521 +++++++++++ src/lightir/Module.cpp | 100 +++ src/lightir/Type.cpp | 171 ++++ src/lightir/User.cpp | 43 + src/lightir/Value.cpp | 25 + tests/2-ir-gen-warmup/CMakeLists.txt | 42 + tests/2-ir-gen-warmup/c_cases/assign.c | 6 + tests/2-ir-gen-warmup/c_cases/fun.c | 6 + tests/2-ir-gen-warmup/c_cases/if.c | 6 + tests/2-ir-gen-warmup/c_cases/while.c | 11 + .../2-ir-gen-warmup/calculator/CMakeLists.txt | 21 + tests/2-ir-gen-warmup/calculator/calc.cpp | 48 + tests/2-ir-gen-warmup/calculator/calc_ast.cpp | 125 +++ tests/2-ir-gen-warmup/calculator/calc_ast.hpp | 90 ++ .../calculator/calc_builder.cpp | 69 ++ .../calculator/calc_builder.hpp | 24 + tests/2-ir-gen-warmup/calculator/calculator.l | 36 + tests/2-ir-gen-warmup/calculator/calculator.y | 110 +++ .../stu_cpp/assign_generator.cpp | 0 .../2-ir-gen-warmup/stu_cpp/fun_generator.cpp | 0 .../2-ir-gen-warmup/stu_cpp/if_generator.cpp | 0 .../stu_cpp/while_generator.cpp | 0 tests/2-ir-gen-warmup/stu_ll/assign_hand.ll | 0 tests/2-ir-gen-warmup/stu_ll/fun_hand.ll | 0 tests/2-ir-gen-warmup/stu_ll/if_hand.ll | 0 tests/2-ir-gen-warmup/stu_ll/while_hand.ll | 0 tests/2-ir-gen-warmup/ta_gcd/gcd_array.c | 27 + .../ta_gcd/gcd_array_generator.cpp | 170 ++++ tests/CMakeLists.txt | 1 + 59 files changed, 4587 insertions(+), 4 deletions(-) create mode 100644 Documentations/2-ir-gen-warmup/README.md create mode 100644 Documentations/common/LightIR.md create mode 100644 Documentations/common/cminusf.md create mode 100644 Documentations/common/figs/cpp_class_inherits.png create mode 100644 Documentations/common/figs/image-20201109145323504.png create mode 100644 Documentations/common/logging.md create mode 100644 Documentations/common/simple_cpp.md create mode 100644 Reports/2-ir-gen-warmup/report.md create mode 100644 include/lightir/BasicBlock.h create mode 100644 include/lightir/Constant.h create mode 100644 include/lightir/Function.h create mode 100644 include/lightir/GlobalVariable.h create mode 100644 include/lightir/IRBuilder.h create mode 100644 include/lightir/IRprinter.h create mode 100644 include/lightir/Instruction.h create mode 100644 include/lightir/Module.h create mode 100644 include/lightir/Type.h create mode 100644 include/lightir/User.h create mode 100644 include/lightir/Value.h create mode 100644 src/io/CMakeLists.txt create mode 100644 src/io/io.c create mode 100644 src/lightir/BasicBlock.cpp create mode 100644 src/lightir/CMakeLists.txt create mode 100644 src/lightir/Constant.cpp create mode 100644 src/lightir/Function.cpp create mode 100644 src/lightir/GlobalVariable.cpp create mode 100644 src/lightir/IRprinter.cpp create mode 100644 src/lightir/Instruction.cpp create mode 100644 src/lightir/Module.cpp create mode 100644 src/lightir/Type.cpp create mode 100644 src/lightir/User.cpp create mode 100644 src/lightir/Value.cpp create mode 100644 tests/2-ir-gen-warmup/CMakeLists.txt create mode 100644 tests/2-ir-gen-warmup/c_cases/assign.c create mode 100644 tests/2-ir-gen-warmup/c_cases/fun.c create mode 100644 tests/2-ir-gen-warmup/c_cases/if.c create mode 100644 tests/2-ir-gen-warmup/c_cases/while.c create mode 100644 tests/2-ir-gen-warmup/calculator/CMakeLists.txt create mode 100644 tests/2-ir-gen-warmup/calculator/calc.cpp create mode 100644 tests/2-ir-gen-warmup/calculator/calc_ast.cpp create mode 100644 tests/2-ir-gen-warmup/calculator/calc_ast.hpp create mode 100644 tests/2-ir-gen-warmup/calculator/calc_builder.cpp create mode 100644 tests/2-ir-gen-warmup/calculator/calc_builder.hpp create mode 100644 tests/2-ir-gen-warmup/calculator/calculator.l create mode 100644 tests/2-ir-gen-warmup/calculator/calculator.y create mode 100644 tests/2-ir-gen-warmup/stu_cpp/assign_generator.cpp create mode 100644 tests/2-ir-gen-warmup/stu_cpp/fun_generator.cpp create mode 100644 tests/2-ir-gen-warmup/stu_cpp/if_generator.cpp create mode 100644 tests/2-ir-gen-warmup/stu_cpp/while_generator.cpp create mode 100644 tests/2-ir-gen-warmup/stu_ll/assign_hand.ll create mode 100644 tests/2-ir-gen-warmup/stu_ll/fun_hand.ll create mode 100644 tests/2-ir-gen-warmup/stu_ll/if_hand.ll create mode 100644 tests/2-ir-gen-warmup/stu_ll/while_hand.ll create mode 100644 tests/2-ir-gen-warmup/ta_gcd/gcd_array.c create mode 100644 tests/2-ir-gen-warmup/ta_gcd/gcd_array_generator.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a491702..991332d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,10 +3,25 @@ cmake_minimum_required( VERSION 3.4 ) set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -std=c99") -set(CMAKE_BUILD_TYPE "Debug") -set(CMAKE_C_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb") -set(CMAKE_C_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall") +SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb") +SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall") +SET(CMAKE_CXX_FLAGS_ASAN "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=undefined -fsanitize=address") + +set(default_build_type "Debug") + +if(NOT(CMAKE_BUILD_TYPE_SHADOW STREQUAL CMAKE_BUILD_TYPE)) + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to '${default_build_type}'") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Choose the type of build." FORCE) + else() + message(STATUS "Building in ${CMAKE_BUILD_TYPE} mode") + endif() + + set(CMAKE_BUILD_TYPE_SHADOW ${CMAKE_BUILD_TYPE} CACHE STRING "used to detect changes in build type" FORCE) +endif() + set(CMAKE_CXX_STANDARD 17) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) find_package(FLEX REQUIRED) find_package(BISON REQUIRED) @@ -32,6 +47,7 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) include_directories(${PROJECT_SOURCE_DIR}) include_directories(${PROJECT_BINARY_DIR}) +include_directories(include/lightir) add_subdirectory(src) add_subdirectory(tests) diff --git a/Documentations/2-ir-gen-warmup/README.md b/Documentations/2-ir-gen-warmup/README.md new file mode 100644 index 0000000..2900912 --- /dev/null +++ b/Documentations/2-ir-gen-warmup/README.md @@ -0,0 +1,199 @@ +# lab2 实验文档 +- [lab2 实验文档](#lab2-实验文档) + - [0. 前言](#0-前言) + - [主要工作](#主要工作) + - [1. LLVM IR 部分](#1-llvm-ir-部分) + - [1.1 LLVM IR 介绍](#11-llvm-ir-介绍) + - [1.2 gcd 例子: 利用 clang 生成的 .ll](#12-gcd-例子-利用-clang-生成的-ll) + - [1.3 你的提交1: 手动编写 .ll](#13-你的提交1-手动编写-ll) + - [2. LightIR 部分](#2-lightir-部分) + - [2.1 LightIR - LLVM IR 的 cpp 接口](#21-lightir---llvm-ir-的-cpp-接口) + - [2.2 gcd 例子: 利用 LightIR + cpp 生成 .ll](#22-gcd-例子-利用-lightir--cpp-生成-ll) + - [2.3 你的提交2: 利用 LightIR + cpp 编写生成 .ll 的程序](#23-你的提交2-利用-lightir--cpp-编写生成-ll-的程序) + - [3. Lab3 的准备](#3-lab3-的准备) + - [3.1 了解 Visitor Pattern](#31-了解-visitor-pattern) + - [4. 实验要求](#4-实验要求) + - [4.1 目录结构](#41-目录结构) + - [4.2 编译、运行和验证](#42-编译运行和验证) + - [4.3 提交要求和评分标准](#43-提交要求和评分标准) +## 0. 前言 + +本次实验作为 Lab3 的前驱实验,独立于 Lab1。 +本次实验的目的是让大家熟悉 Lab3 所需要的相关知识: LLVM IR、 LightIR(LLVM IR 的轻量级 C++ 接口)和 Visitor Pattern(访问者模式)。 +在开始实验之前,请根据之前的[编译实验环境搭建](http://202.38.79.174/compiler_staff/2022fall-environment/-/blob/master/README.md)确保LLVM的版本为10.0.1,且PATH环境变量配置正确。可以通过`lli --version`命令是否可以输出10.0.1的版本信息来验证(其它版本不一定兼容)。 +本次实验设置的目的是为 Lab3 进行知识的准备与热身, coding 的工程量不大,但是需要一定的阅读、学习、理解。因此本次的实验报告相比之下内容要求会稍微多一些,以避免大家在 Lab3 时手足无措。 +这里助教提供了简单的[C++简介](Documentations/common/simple_cpp.md),对C++基本特性不熟悉的同学可以先阅读该文档。 + +### 主要工作 + +1. 第一部分: 了解 LLVM IR。通过 clang 生成的 .ll ,了解 LLVM IR 与 c 代码的对应关系,**完成1.3**。 +2. 第二部分: 了解 LightIR。通过助教提供的 c++ 例子,了解 LightIR 的 c++ 接口及实现,**完成2.3**。 +3. 第三部分: 理解 Visitor Pattern 。**完成3.1** +4. 实验报告:在 [report.md](./Reports/2-ir-gen-warmup/report.md) 中**回答3个问题**。 + +## 1. LLVM IR 部分 +### 1.1 LLVM IR 介绍 +根据[维基百科](https://zh.wikipedia.org/zh-cn/LLVM)的介绍,LLVM是一个自由软件项目,它是一种编译器基础设施,以C++写成,包含一系列模块化的编译器组件和工具链,用来开发编译器前端和后端。IR的全称是Intermediate Representation,即中间表示。LLVM IR是一种类似于汇编的底层语言。 + +LLVM IR的具体指令可以参考 [Reference Manual](http://llvm.org/docs/LangRef.html)。由于其手册过于复杂,助教筛选了后续实验中将要用到的子集,总结为了 [Light IR 手册](../common/LightIR.md#ir-%E6%A0%BC%E5%BC%8F)。如果有感兴趣的同学可以阅读原手册作为拓展。 + +作为一开始的参考,你可以先阅读其中 `IR格式` 和 `IR指令` 两节,后续有需要再反复参考。实验的最后,你需要在 [report.md](./Reports/2-ir-gen-warmup/report.md) 中**回答问题1**。 + +### 1.2 gcd 例子: 利用 clang 生成的 .ll +阅读 [tests/2-ir-gen-warmup/ta_gcd/gcd_array.c](../../tests/2-ir-gen-warmup/ta_gcd/gcd_array.c)。 + +根据 `clang -S -emit-llvm gcd_array.c` 指令,你可以得到对应的 `gcd_array.ll` 文件.你需要结合 [gcd_array.c](../../tests/2-ir-gen-warmup/ta_gcd/gcd_array.c) 阅读 `gcd_array.ll` ,理解其中每条LLVM IR指令与c代码的对应情况。 + +通过 `lli gcd_array.ll; echo $?` 指令,你可以测试 `gcd_array.ll` 执行结果的正确性。其中, + +- `lli` 会运行 `*.ll` 文件 +- `$?` 的内容是上一条命令所返回的结果,而 `echo $?` 可以将其输出到终端中 + +后续你会经常用到这两条指令。 + +### 1.3 你的提交1: 手动编写 .ll +助教提供了四个简单的c程序,分别是 `tests/2-ir-gen-warmup/c_cases/` 目录下的 [assign.c](../../tests/2-ir-gen-warmup/c_cases/assign.c)、 [fun.c](../../tests/2-ir-gen-warmup/c_cases/fun.c)、 [if.c](../../tests/2-ir-gen-warmup/c_cases/if.c) 和 [while.c](../../tests/2-ir-gen-warmup/c_cases/while.c)。你需要在 `tests/2-ir-gen-warmup/stu_ll/` 目录中,手工完成自己的 [assign_hand.ll](../../tests/2-ir-gen-warmup/stu_ll/assign_hand.ll)、 [fun_hand.ll](../../tests/2-ir-gen-warmup/stu_ll/fun_hand.ll)、 [if_handf.ll](../../tests/2-ir-gen-warmup/stu_ll/if_hand.ll) 和 [while_hand.ll](../../tests/2-ir-gen-warmup/stu_ll/while_hand.ll),以实现与上述四个C程序相同的逻辑功能。我们鼓励添加必要的注释。`.ll` 文件的注释是以 ";" 开头的。 + +必要的情况下,你可以参考 `clang -S -emit-llvm` 的输出,但是你提交的结果必须避免同此输出一字不差。 + + +## 2. LightIR 部分 +### 2.1 LightIR - LLVM IR 的 cpp 接口 +由于 LLVM IR 官方的 cpp 接口的调用链同样过于冗长,助教提供了 `LightIR` 这一 cpp 接口库。你需要阅读 [LightIR cpp APIs](../common/LightIR.md#c-apis)。 +lab3 部分会要求大家通过 `LightIR` 根据 `AST` 构建生成 LLVM IR。所以你需要仔细阅读文档了解其接口的设计。 + +### 2.2 gcd 例子: 利用 LightIR + cpp 生成 .ll +为了让大家更直观地感受并学会 `LightIR` 接口的使用,助教提供了 [tests/2-ir-gen-warmup/ta_gcd/gcd_array_generator.cpp](../../tests/2-ir-gen-warmup/ta_gcd/gcd_array_generator.cpp)。该 cpp 程序会生成与 gcd_array.c 逻辑相同的 LLVM IR 文件。助教提供了非常详尽的注释,请认真阅读作为参考! +该程序的编译与运行请参考 4.2 节。 + +### 2.3 你的提交2: 利用 LightIR + cpp 编写生成 .ll 的程序 +你需要在 `tests/2-ir-gen-warmup/stu_cpp/` 目录中,编写 [assign_generator.cpp](../../tests/2-ir-gen-warmup/stu_cpp/assign_generator.cpp)、 [fun_generator.cpp](../../tests/2-ir-gen-warmup/stu_cpp/fun_generator.cpp)、 [if_generator.cpp](../../tests/2-ir-gen-warmup/stu_cpp/if_generator.cpp) 和 [while_generator.cpp](../../tests/2-ir-gen-warmup/stu_cpp/while_generator.cpp),以生成与 1.3 节的四个 c 程序相同逻辑功能的 `.ll` 文件。你需要完成 [report.md](./Reports/2-ir-gen-warmup/report.md) 中的**问题2**。 + +## 3. Lab3 的准备 +### 3.1 了解 Visitor Pattern +Visitor Pattern(访问者模式)是一种在 LLVM 项目源码中被广泛使用的设计模式。在遍历某个数据结构(比如树)时,如果我们需要对每个节点做一些额外的特定操作, Visitor Pattern 就是个不错的思路。 +Visitor Pattern 是为了解决**稳定的数据结构**和**易变的操作耦合问题**而产生的一种设计模式。解决方法就是在被访问的类里面加一个对外提供接待访问者的接口,其关键在于在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。这里举一个应用实例来帮助理解访问者模式: 您在朋友家做客,您是访问者;朋友接受您的访问,您通过朋友的描述,然后对朋友的描述做出一个判断,这就是访问者模式。 +有关 Visitor Pattern 的含义、模式和特点,有梯子的同学可参考 [维基百科](https://en.wikipedia.org/wiki/Visitor_pattern#C++_example)。其中较为重要的一点原则在于, C++ 中对函数重载特性的支持。 +在 `tests/2-ir-gen-warmup/calculator` 中,助教编写了一个利用访问者模式,产生计算算数表达式的中间代码的程序。该程序首先对算数表达式进行语法分析生成语法树,再使用访问者模式来遍历语法树,产生中间代码。在 [calc_ast.hpp](../../tests/2-ir-gen-warmup/calculator/calc_ast.hpp) 中,我们定义了语法树的不同节点类型。在 [calc_builder.cpp](../../tests/2-ir-gen-warmup/calculator/calc_builder.cpp) 中,我们使用 LightIR 编写了不同的 `visit` 函数。根据节点类型的不同,编译器会在多种 `visit` 函数中,选择对应的实现进行调用。请认真阅读这两个文件和其它相关代码,理解语法树是如何通过访问者模式被遍历的,并在 [report.md](./Reports/2-ir-gen-warmup/report.md)中**回答问题3**。 + + +该程序使用方法如下: +``` shell +# 在 build 目录下操作 +$ make +$ ./calc +Input an arithmatic expression (press Ctrl+D in a new line after you finish the expression): +4 * (8 + 4 - 1) / 2 +result and result.ll have been generated. +$ ./result +22 +``` +其中,`result.ll` 是程序产生的中间代码,`result` 是中间代码编译产生的二进制,运行它就可以输出算数表达的结果。注:单独运行 `lli result.ll` 是会报错的,那怎么才能解决报错的根源问题,通过 `lli` 得到正确的运行结果呢?感兴趣的同学可以思考调研一下。 + + +## 4. 实验要求 + +### 4.1 目录结构 +除了下面指明你所要修改或提交的文件,其他文件请勿修改。 +``` log +. +├── CMakeLists.txt +├── Documentations +│   ├── ... +| ├── common <- LightIR 相关文档 +│   └── 2-ir-gen-warmup +│      └── README.md <- lab2 实验文档说明(你在这里) +├── include <- 实验所需的头文件 +│   ├── ... +│   ├── lightir +├── README.md +├── Reports +│   ├── ... +│   └── 2-ir-gen-warmup +│   └── report.md <- lab2 所需提交的实验报告,含3个问题(你要交) +├── src +│   ├── ... +│   └── lightir +└── tests + ├── CMakeLists.txt + ├── ... + └── 2-ir-gen-warmup <- lab2 文件夹 +    ├── c_cases <- 4个 c 程序 +    │   ├── assign.c +    │   ├── fun.c +    │   ├── if.c +    │   └── while.c +    ├── CMakeLists.txt <- 你在2.3节需要去掉注释(我们不收,你要改) +    ├── stu_cpp <- lab2 所需提交的 cpp 目录(你要交) +    │   ├── assign_generator.cpp +    │   ├── fun_generator.cpp +    │   ├── if_generator.cpp +    │   └── while_generator.cpp +    ├── stu_ll <- lab2 所需提交的 .ll 目录(你要交) +    │   ├── assign_hand.ll +    │   ├── fun_hand.ll +    │   ├── if_hand.ll +    │   └── while_hand.ll +    └── ta_gcd +    ├── gcd_array.c +    └── gcd_array_generator.cpp <- 助教提供的生成 gcd_array.ll 的 cpp +``` + +### 4.2 编译、运行和验证 + +* 编译与运行 + 在 `${WORKSPACE}/build/` 下执行: + ``` shell + # 如果存在 CMakeCache.txt 要先删除 + # rm CMakeCache.txt + cmake .. + make + make install + ``` + 你可以得到对应 `gcd_array_generator.cpp` 的可执行文件。 + 在完成2.3时,在 `${WORKSPACE}/tests/2-ir-gen-warmup/CMakeLists.txt` 中去掉对应的注释,再在 `${WORKSPACE}/build/` 下执行 `cmake ..` 与 `make` 指令,即可得到对应的可执行文件。 +* 验证 + 本次试验测试案例只有`${WORKSPACE}/tests/2-ir-gen-warmup/c_cases`中的4个样例。请大家自行验证。 + 助教会执行你们的代码,并使用 `diff` 命令进行验证。 + +### 4.3 提交要求和评分标准 +* 提交要求 + 本实验的提交要求分为两部分: 实验部分的文件和报告,git提交的规范性。 + * 实验部分: + * 需要完成 `./tests/2-ir-gen-warmup/stu_ll` 目录下的4个文件 + * 需要完成 `./tests/2-ir-gen-warmup/stu_cpp` 目录下的4个文件 + * 需要在 `./Reports/2-ir-gen-warmup/` 目录下撰写实验报告 + * 实验报告内容包括: + * 实验要求、3个问题、实验难点、实验反馈(具体参考 [report.md](./Reports/2-ir-gen-warmup/report.md)) + * 本次实验报告**参与**评分标准. + * git 提交规范: + * 不破坏目录结构( `report.md` 如果需要放图片,请放在 `./Reports/2-ir-gen-warmup/figs/` 下) + * 不上传临时文件(凡是自动生成的文件和临时文件请不要上传) + * git log 言之有物(不强制,请不要 git commit -m 'commit 1',git commit -m 'sdfsdf',每次 commit 请提交有用的 comment 信息) +* 提交方式: + * 代码提交:本次实验需要在希冀课程平台上发布的作业Lab2-代码提交提交自己仓库的 gitlab 链接(注:由于平台限制,请提交http协议格式的仓库链接。例:学号为 PB011001 的同学,Lab2 的实验仓库地址为`http://202.38.79.174/PB011001/2022fall-compiler_cminus.git`),我们会收集最后一次提交的评测分数,作为最终代码得分。 + * 报告提交:将 Reports/2-ir-gen-warmup/README.md 导出成 pdf 文件单独提交到Lab2-报告提交。 + * 提交异常:如果遇到在平台上提交异常的问题,请通过邮件联系助教,助教将收取截止日期之前,学生在 gitlab 仓库最近一次 commit 内容进行评测。 +* 评分标准: 本次实验的测试样例较为简单,所以为了拿高分请注意 report.md。 + * 1.3节 `.ll` 运行结果正确(一个5分,共20分) + * 2.3节 `.cpp` 运行结果正确(一个10分,共40分) + * `report.md` (40分) + * 禁止执行恶意代码,违者本次实验0分处理 +* 迟交规定 + * `Soft Deadline` : 2021/10/23 23:59:59 (北京标准时间,UTC+8) + * `Hard Deadline` : 2021/10/30 23:59:59 (北京标准时间,UTC+8) + * 迟交需要邮件通知TA: + * 邮箱: wch0925@mail.ustc.edu.cn + * 邮件主题: lab2迟交-学号 + * 内容: 包括迟交原因、最后版本commitID、迟交时间等 + * 迟交分数 + * x为迟交天数(对于 `Soft Deadline` 而言), grade 满分10 + ``` bash + final_grade = grade, x = 0 + final_grade = grade * (0.9)^x, 0 < x <= 7 + final_grade = 0, x > 7 # 这一条严格执行,请对自己负责 + ``` +* 关于抄袭和雷同 + 经过助教和老师判定属于作业抄袭或雷同情况,所有参与方一律零分,不接受任何解释和反驳。 + +如有任何问题,欢迎在论坛提意见进行批判指正。 diff --git a/Documentations/common/LightIR.md b/Documentations/common/LightIR.md new file mode 100644 index 0000000..bfc0b67 --- /dev/null +++ b/Documentations/common/LightIR.md @@ -0,0 +1,818 @@ +# Light IR +- [Light IR](#light-ir) + - [Light IR 简介](#lightir-简介) + - [IR 格式](#ir-格式) + - [IR 结构图](#ir-结构图) + - [IR 指令](#ir-指令) + - [指令格式](#指令格式) + - [Terminator Instructions](#terminator-instructions) + - [Ret](#ret) + - [Br](#br) + - [Standard binary operators](#standard-binary-operators) + - [Add FAdd](#add-fadd) + - [Sub FSub](#sub-fsub) + - [Mul FMul](#mul-fmul) + - [SDiv FDiv](#sdiv-fdiv) + - [Memory operators](#memory-operators) + - [Alloca](#alloca) + - [Load](#load) + - [Store](#store) + - [CastInst](#castinst) + - [ZExt](#zext) + - [FpToSi](#fptosi) + - [SiToFp](#sitofp) + - [Other operators](#other-operators) + - [ICmp FCmp](#icmp-fcmp) + - [Call](#call) + - [GetElementPtr](#getelementptr) + - [C++ APIs](#c-apis) + - [C++类关系图](#c类关系图) + - [Module](#module) + - [BasicBlock](#basicblock) + - [GlobalVariable](#globalvariable) + - [Constant](#constant) + - [Argument](#argument) + - [Function](#function) + - [IRBuilder](#irbuilder) + - [Instruction](#instruction) + - [Type](#type) + - [User](#user) + - [Use](#use) + - [Value](#value) + - [注意](#注意) + +## LightIR 简介 +为了让同学们方便理解并掌握 IR 核心技术,我们从复杂繁琐的 LLVM IR 中裁剪出了本课程实验所需要的精简 IR 子集(我们将其命名为 LightIR),并且实现了轻量级的库来进行 IR 的控制和生成。本文档旨在介绍 LightIR 的结构和具体指令,同时可当作 LightIR [C++ 接口](./LightIR.md#c-apis)的参考手册。LightIR 的 [IR 格式](./LightIR.md#ir-格式)和 [IR 指令](./LightIR.md#ir-指令)部分也可以参考 LLVM 官方的 IR 文档 [LLVM Reference Manual](http://llvm.org/docs/LangRef.html)。 + +## IR 格式 + +### IR 结构图 +![image-20201109145323504](figs/image-20201109145323504.png) +我们实验中需要生成的IR代码有着相对固定的结构模式。 + +- 最上层的是模块,可以理解为一个完整编译单元,来源于一个`cminus-f`源文件。模块包含全局变量和函数定义。 +- 函数由头部和函数体组成。函数的头部包括返回值类型、函数名和参数表(每个参数包括类型和值)。函数体可以由多个基本块构成。 +- 基本块是指程序顺序执行的语句序列,只有一个入口和一个出口。基本块由若干指令构成。 +- 注意一个基本块中的**只能有一条终止指令**(Ret/Br)。 + +以下面的`easy.c`与`easy.ll`为例进行说明。 +通过命令`clang -S -emit-llvm easy.c`可以得到对应的`easy.ll`如下(助教增加了额外的注释)。`.ll`文件中注释以`;`开头。 + +- `easy.c`: + ``` c + int main(){ + int a; + int b; + a = 1; + b = 2; + if(a < b) + b = 3; + return a + b; + } + ``` + +- `easy.ll`: + ``` c + ; 注释: .ll文件中注释以';'开头 + ; ModuleID = 'easy.c' + source_filename = "easy.c" + ; 注释: target的开始 + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + ; 注释: target的结束 + + ; 注释: 全局main函数的定义 + ; Function Attrs: noinline nounwind optnone uwtable + define dso_local i32 @main() #0 { + ; 注释: 第一个基本块的开始 + %1 = alloca i32, align 4 + %2 = alloca i32, align 4 + %3 = alloca i32, align 4 + store i32 0, i32* %1, align 4 + store i32 1, i32* %2, align 4 + store i32 2, i32* %3, align 4 + %4 = load i32, i32* %2, align 4 + %5 = load i32, i32* %3, align 4 + %6 = icmp slt i32 %4, %5 + br i1 %6, label %7, label %8 + ; 注释: 第一个基本块的结束 + + ; 注释: 第二个基本块的开始 + 7: ; preds = %0 + store i32 3, i32* %3, align 4 + br label %8 + ; 注释: 第二个基本块的结束 + + ; 注释: 第三个基本块的开始 + 8: ; preds = %7, %0 + %9 = load i32, i32* %2, align 4 + %10 = load i32, i32* %3, align 4 + %11 = add nsw i32 %9, %10 + ret i32 %11 ; 注释: 返回语句 + ; 注释: 第三个基本块的结束 + } + + attributes #0 = { noinline nounwind optnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } + + !llvm.module.flags = !{!0} + !llvm.ident = !{!1} + + !0 = !{i32 1, !"wchar_size", i32 4} + !1 = !{!"clang version 10.0.1 "} + ``` + + 其中,每个程序由一个个或多个模块组成,模块之间由链接器合并(本实验中并不涉及)。 + 每个模块组成如下: +- 目标信息: + ``` c + target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + target triple = "x86_64-unknown-linux-gnu" + ``` +- 全局符号: 全局变量、函数定义 +- 其它信息 + +每个函数的组成如下: +- 头部:函数返回值类型,函数名,函数参数 +- 一个或多个基本块: + - 每个基本块又有标签和指令组成。 + ``` c + 8: ; preds = %7, %0 + %9 = load i32, i32* %2, align 4 + %10 = load i32, i32* %3, align 4 + %11 = add nsw i32 %9, %10 + ret i32 %11 + ``` + 这个例子中,`8`就是标签。 + `%9 = load i32, i32* %2, align 4`中的`%9`是目的操作数,`load`是指令助记符,`i32`是32位整型,`i32*`是指向`i32`的指针类型,`%2`是源操作数,`align 4`表示4字节对齐。 + +## IR 指令 +- 采用 3 地址的方式 + - 区别于 X86 汇编的目标和源寄存器共用的模式: ADD EAX, EBX + - %2 = add i32 %0, %1 +- SSA 形式 + 无限寄存器 + - 每个变量都只被赋值一次 + - 容易确定操作间的依赖关系,便于优化分析 +- 强类型系统 + - 每个 Value 都具备自身的类型, + - IR类型系统: + - `i1`:1位宽的整数类型 + - `i32`:32位宽的整数类型 + - `float`:单精度浮点数类型 + - `pointer`:指针类型 + - 例如:`i32*, [10 x i32*]` + - `label`: 基本快的标识符类型 + - `functiontype`: 函数类型,包括函数返回值类型与参数类型 + +### 指令格式 + +#### Terminator Instructions +**注**:ret与br都是Terminator Instructions也就是终止指令,在llvm基本块的定义里,基本块是单进单出的,因此只能有一条终止指令(ret或br)。当一个基本块有两条终止指令,clang 在做解析会认为第一个终结指令是此基本块的结束,并会开启一个新的匿名的基本块(并占用了下一个编号)。 +##### Ret +- 概念:返回指令。用于将控制流(以及可选的值)从函数返回给调用者。`ret`指令有两种形式:一种返回值,然后终结函数,另一种仅终结函数。 +- 格式 + - `ret ` + - `ret void` +- 例子: + - `ret i32 %0` + - `ret void` +##### Br +- 概念:跳转指令。用于使控制流转移到当前功能中的另一个基本块。该指令有两种形式,分别对应于条件分支和无条件分支。 +- 格式: + - `br i1 , label , label ` + - `br label ` +- 例子: + - `br i1 %cond label %truebb label %falsebb` + - `br label %bb` +#### Standard binary operators +##### Add FAdd +- 概念:`add`指令返回其两个`i32`类型的操作数之和,返回值为`i32`类型,`fadd`指令返回其两个`float`类型的操作数之和,返回值为`float`类型。 +- 格式: + - ` = add , ` + - ` = fadd , ` +- 例子: + - `%2 = add i32 %1, %0` + - `%2 = fadd float %1, %0` + +##### Sub FSub +- 概念:`sub`指令返回其两个`i32`类型的操作数之差,返回值为`i32`类型,`fsub`指令返回其两个`float`类型的操作数之差,返回值为`float`类型。 +- 格式与例子与`add`,`fadd`类似 + +##### Mul FMul +- 概念:`mul`指令返回其两个`i32`类型的操作数之积,返回值为`i32`类型,`fmul`指令返回其两个`float`类型的操作数之积,返回值为`float`类型。 +- 格式与例子与`add`,`fadd`类似 + +##### SDiv FDiv +- 概念:`sdiv`指令返回其两个`i32`类型的操作数之商,返回值为`i32`类型,`fdiv`指令返回其两个`float`类型的操作数之商,返回值为`float`类型。 +- 格式与例子与`add`,`fadd`类似 + +#### Memory operators +##### Alloca +- 概念: `alloca`指令在当前执行函数的堆栈帧上分配内存,当该函数返回其调用者时将自动释放该内存。 始终在地址空间中为数据布局中指示的分配资源分配对象。 +- 格式:` = alloca ` +- 例子: + - `%ptr = alloca i32` + - `%ptr = alloca [10 x i32]` + +##### Load +- 概念:`load`指令用于从内存中读取。 +- 格式:` = load , * ` +- 例子:`%val = load i32, i32* %ptr` + +##### Store +- 概念:`store`指令用于写入内存。 +- 格式:`store , * ` +- 例子:`store i32 3, i32* %ptr` + +#### CastInst +##### ZExt +- 概念:`zext`指令将其操作数**零**扩展为`type2`类型。 +- 格式:` = zext to ` +- 例子:`%1 = zext i1 %0 to i32` + +##### FpToSi +- 概念:`fptosi`指令将浮点值转换为`type2`(整数)类型。 +- 格式:` = fptosi to ` +- 例子:`%Y = fptosi float 1.0E-247 to i32` + +##### SiToFp +- 概念:`sitofp`指令将有符号整数转换为`type2`(浮点数)类型。 +- 格式:` = sitofp to ` +- 例子:`%X = sitofp i32 257 to float` + +#### Other operators +##### ICmp FCmp +- 概念:`icmp`指令根据两个整数的比较返回布尔值,`fcmp`指令根据两个浮点数的比较返回布尔值。 +- 格式: + - ` = icmp , ` + - ` = eq | ne | sgt | sge | slt | sle` + - ` = fcmp , ` + - ` = eq | ne | ugt | uge | ult | ule` +- 例子:`i1 %2 = icmp sge i32 %0, %1` + +##### Call +- 概念:`call`指令用于使控制流转移到指定的函数,其传入参数绑定到指定的值。 在被调用函数中执行`ret`指令后,控制流程将在函数调用后继续执行该指令,并且该函数的返回值绑定到`result`参数。 +- 格式: + - ` = call () ` +- 例子: + - `%0 = call i32 @func( i32 %1, i32* %0)` + - `call @func( i32 %arg)` + +##### GetElementPtr +- 概念:`getelementptr`指令用于获取数组结构的元素的地址。 它仅执行地址计算,并且不访问内存。 +- 格式:` = getelementptr , * [, ]` +- 参数解释:第一个参数是计算基础类型,第二第三个参数表示索引开始的指针类型及指针,`[]`表示可重复参数,里面表示的数组索引的偏移类型及偏移值。(Question:思考指针类型为`[10 x i32]`指针和`i32`指针`getelementptr`用法的不同,并给出解释,实验结束后回答两者使用情况的区别) +- 例子: + - `%2 = getelementptr [10 x i32], [10 x i32]* %1, i32 0, i32 %0` + - `%2 = getelementptr i32, i32* %1 i32 %0` +- **额外阅读(很重要)**:[The Often Misunderstood GEP Instruction](https://llvm.org/docs/GetElementPtr.html) + +## C++ APIs + +### C++类关系图 + +![类继承关系](figs/cpp_class_inherits.png) + +上面是c++代码的类关系图。 + +- `Module`类表示IR的模块,是IR最上层的结构。 +- `Type`类表示IR的类型,派生出`ArrayType`, `FloatType`, `FunctionType`, `IntegerType`和`PointerType`等分别表示的数组类型、浮点型、函数类型、整数型和指针类型。例如,函数头部的返回值类型用一个`Type`对象表示,函数的参数表中每个参数的类型也是用`Type`对象表示(通常是`Type`的某个子类),而参数的值则是用`Value`对象表示。返回值类型和参数类型一起构成了函数类型,用`FunctionType`对象表示。 +- `Use`类用于描述`Value`的使用情况,`Value`类成员有一个`Use`链表来表示使用其的操作数链表。 +- `Value`类表示值,具体可以是`Argument`, `BasicBlock`, `Function`或`User`每一个`Value`对象都会有一个自己的类型。 + - `Function`类表示函数的定义,其中,成员`std::list arguments_`表示函数的参数(形参)。 + - `BasicBlock`表示基本块。 + - `User`类描述了值的使用情况,例如某个`Instruction`使用了哪些操作数。 + - `Constant`类表示常量,派生出各种类型的常量。 + - `GlobalVariable`类表示全局变量,在IR中位于模块内,与函数并列。 + - `Instruction`类表示基本块中的IR指令,派生出具体的各种指令。 + +下面是对各个类进行具体的介绍。 +### Module + +- 概念:一个编译单元。在 `cminus-f` 意义下表示一个文件。 +
+Module 的定义 (点击此处展开) + +```cpp +class Module +{ +public: + // 将函数f添加到该模块的函数链表上 + // 在函数被创建的时候会自动调用此方法 + void add_function(Function *f); + // 将全局变量g添加到该模块的全局变量链表上 + // 在全局变量被创建的时候会自动调用此方法 + void add_global_variable(GlobalVariable* g); + // 获取全局变量列表 + std::list get_global_variable(); + // 获得(创建)自定义的Pointer类型 + PointerType *get_pointer_type(Type *contained); + // 获得(创建)自定义的Array类型 + ArrayType *get_array_type(Type *contained, unsigned num_elements); + // 获得基本类型int32 + IntegerType *get_int32_type(); + // 其他基本类型类似... +private: + // 存储全局变量的链表 + std::list global_list_; + // 存储函数的链表 + std::list function_list_; + + // 存储基本类型 + IntegerType *int1_ty_; + IntegerType *int32_ty_; + Type *label_ty_; + Type *void_ty_; + FloatType *float32_ty_; + + // 存储自定义类型 + std::map pointer_map_; + std::map, ArrayType *> array_map_; +}; +``` +
+ +### BasicBlock + +- 概念:基本块。是一个是单入口单出口的代码块,可以作为分支指令目标对象。 + +
+BasicBlock 的定义 (点击此处展开) + +```cpp +class BasicBlock : public Value +{ +public: + // 创建并返回基本块,参数分别是基本块所属的模块,基本块名字(默认为空),基本块所属的函数 + static BasicBlock *create(Module *m, const std::string &name, Function *parent); + // 返回该基本块所属的函数 + Function *get_parent(); + // 返回该基本块所属的模块 + Module *get_module(); + // 返回该基本块的终止指令,若基本块的最后一条指令不是终止指令返回则返回 nullptr + Instruction *get_terminator(); + // 将指令 instr 添加到该基本块的指令链表末端,使用 IRBuilder 来创建函数时会自动调用此方法 + void add_instruction(Instruction *instr); + // 将指令 instr 添加到该基本块的指令链表首部 + void add_instr_begin(Instruction *instr); + // 将指令 instr 从该基本块的指令链表中移除,该 API 会同时维护好 instr 的操作数的 use 链表。 + void delete_instr(Instruction *instr); + // 判断该基本块是否为空 + bool empty(); + // 返回该基本块中的指令数目 + int get_num_of_instr(); + // 返回该基本块的指令链表 + std::list &get_instructions(); + // 将该基本块从所属函数的基本块链表中移除 + void erase_from_parent(); + + /****************APIs about cfg****************/ + // 返回前驱基本块集合 + std::list &get_pre_basic_blocks(); + // 返回后继基本块集合 + std::list &get_succ_basic_blocks(); + // 添加前驱基本块 + void add_pre_basic_block(BasicBlock *bb); + // 添加后继基本块 + void add_succ_basic_block(BasicBlock *bb); + // 移除前驱基本块 + void remove_pre_basic_block(BasicBlock *bb); + // 移除后继基本块 + void remove_succ_basic_block(BasicBlock *bb); + /****************APIs about cfg****************/ +private: + // 存储前驱基本块的链表 + std::list pre_bbs_; + // 存储后继基本块的链表 + std::list succ_bbs_; + // 存储该基本块指令的链表 + std::list instr_list_; + // 指向该基本块所属函数的指针 + Function *parent_; + +}; +``` +
+ +### GlobalVariable +- 概念:全局变量。 +
+GlobalVariable 的定义 (点击此处展开) + +```cpp +class GlobalVariable : public User +{ +public: + // 创建一个全局变量 + static GlobalVariable *create(std::string name, Module *m, Type* ty, + bool is_const, Constant* init ); +}; +``` +
+ +### Constant + +- 概念:常量。不同类型的常量由不同类来表示。 + +
+Constant 的定义 (点击此处展开) + +```cpp +class Constant : public User +{ +public: + Constant(Type *ty, const std::string &name = "", unsigned num_ops = 0); +}; +``` +
+ +
+整型常量 ConstantInt 的定义 (点击此处展开) + +```cpp +class ConstantInt : public Constant +{ +private: + // 该常量表示的值 + int value_; +public: + // 返回该常量中存的数 + int get_value(); + // 返回常量 const_val 中存的数 + static int get_value(ConstantInt *const_val); + // 以值 val 来创建常量 + static ConstantInt *get(int val, Module *m); + // 以值 val 来创建 bool 常量 + static ConstantInt *get(bool val, Module *m); +}; +``` +
+ +
+浮点数常量 ConstantFP 的定义 (点击此处展开) + +```cpp +class ConstantFP : public Constant +{ +private: + // 该常量表示的值 + float val_; +public: + // 以值 val 创建并返回浮点数常量 + static ConstantFP *get(float val, Module *m); + // 返回该常量中存的值 + float get_value(); +}; + +``` +
+ +
+ConstantZero 的定义 (点击此处展开) + +```cpp +// 用于全局变量初始化的零常量 +class ConstantZero : public Constant +{ +public: + // 创建并返回零常量 + static ConstantZero *get(Type *ty, Module *m); +}; +``` +
+ +### Argument + +- 概念:函数的参数。 + +
+Argument 的定义 (点击此处展开) + +```cpp +class Argument : public Value +{ +public: + // 返回该参数的所属的函数 + Function *get_parent(); + // 返回该参数在所在函数的参数列表中的序数 + unsigned get_arg_no() const; +private: + // 指向该参数的所属的函数的指针 + Function *parent_; + // 该参数在所在函数的参数列表中的序数 + unsigned arg_no_; +}; +``` +
+ +### Function +- 概念:函数。该类描述了一个过程,包含多个基本块。 + +
+Funtion 的定义 (点击此处展开) + +```cpp +class Function : public Value +{ +public: + // 创建并返回函数,参数依次是待创建函数类型 ty,函数名字 name (不可为空),函数所属的模块 parent + static Function *create(FunctionType *ty, const std::string &name, Module *parent); + // 返回该函数的函数类型 + FunctionType *get_function_type() const; + // 返回该函数的返回值类型 + Type *get_return_type() const; + // 将基本块 bb 添加至该函数末端(调用基本块的创建函数时会自动调用此函数来) + void add_basic_block(BasicBlock *bb); + // 得到该函数参数数量 + unsigned get_num_of_args() const; + // 得到该函数基本块数量 + unsigned get_num_basic_blocks() const; + // 得到该函数所属的Module + Module *get_parent() const; + // 得到该函数参数列表的起始迭代器 + std::list::iterator arg_begin() + // 得到该函数参数列表的终止迭代器 + std::list::iterator arg_end() + // 从函数的基本块链表中删除基本块 bb + void remove(BasicBlock* bb) + // 返回函数基本块链表 + std::list &get_basic_blocks() + // 返回函数的参数链表 + std::list &get_args() + // 给函数中未命名的基本块和指令命名 + void set_instr_name(); +private: + // 储存基本块的链表 + std::list basic_blocks_; + // 储存参数的链表 + std::list arguments_; + // 指向该函数所属的模块的指针 + Module *parent_; +}; +``` +
+ +### IRBuilder +- 概念:生成IR的辅助类。该类提供了独立的接口创建各种 IR 指令,并将它们插入基本块中(注意:该辅助类不做任何类型检查)。 + +
+IRBuilder 的定义 (点击此处展开) + +```cpp +class IRBuilder { +private: + // 该辅助类正在插入的基本块 + BasicBlock *BB_; + // 该辅助类绑定的模块 + Module *m_; +public: + // 返回当前插入的基本块 + BasicBlock *get_insert_block() + // 设置当前插入的基本块 + void set_insert_point(BasicBlock *bb) + // 创建的指令并对应插入到基本块中,函数名字和参数名字和IR文档是一一对应的 + // 具体查看 IRBuilder.h + Instruction *create_[instr_type](); +}; +``` +
+ +### Instruction + +- 概念:指令。该类是所有 LLVM 指令的基类。子类包含IR部分中的所有指令。 + +
+Instruction 的定义 (点击此处展开) + +```c++ +class Instruction : public User +{ +private: + // 该指令所属的基本块 + BasicBlock *parent_; + // 该指令的类型id + OpID op_id_; + // 该指令的操作数个数 + unsigned num_ops_; +public: + // 所有指令的创建都要通过IRBuilder进行,暂不需要关注Instruction类的实现细节 + //(注:不通过IRBuilder来创建指令,而直接调用指令子类的创建方法未经助教完善的测试) +}; +``` + +
+ +### Type + +- 概念:IR的类型(包含VoidType、LabelType、FloatType、IntegerType、ArrayType、PointerType)。module中可以通过API获得基本类型,并创建自定义类型。 +- 子类介绍:其中ArrayType、PointerType可以嵌套得到自定义类型,而VoidType、 IntegerType,FloatType可看做IR的基本类型,LabelType是BasicBlcok的类型,可作为跳转指令的参数,FunctionType表示函数类型。其中VoidType与LabelType 没有对应的子类,通过Type中的tid_字段判别,而其他类型均有对应子类 + +
+Type 的定义 (点击此处展开) + +```c++ +class Type { +private: + // 类型种类 + TypeID tid_; + Module *m_; +public: + // 判断是否是ty类型 + bool is_[ty]_type(); + // 获得ty类型 + static Type *get_[ty]_type(Module *m) + // 若是PointerType则返回指向的类型,若不是则返回nullptr。 + Type *get_pointer_element_type(); + // 若是ArrayType则返回数组元素的类型,若不是则返回nullptr。 + Type *get_array_element_type(); +}; +``` + +
+
+IntegerType 的定义 (点击此处展开) + +```c++ +class IntegerType : public Type { +public: + explicit IntegerType(unsigned num_bits ,Module *m); + // 创建 IntegerType 类型,IntegerType 包含 int1 与 int32 + static IntegerType *get(unsigned num_bits, Module *m ); + // 获得 IntegerType 类型的长度 + unsigned get_num_bits(); +private: + // 表示 IntegerType 类型的长度 + unsigned num_bits_; +}; +``` + +
+ +
+FloatType 的定义 (点击此处展开) + +```c++ +class FloatType : public Type { +public: + FloatType (Module *m); + // 创建 FloatType 类型 + static FloatType *get(Module *m); +}; +``` + +
+ +
+数组类型 ArrayType 的定义 (点击此处展开) + +```c++ +class ArrayType : public Type { +public: + // 判断数组元素类型是否合法 + static bool is_valid_element_type(Type *ty); + // 通过数组元素类型contained以及数组长度num_elements创建数组类型 + static ArrayType *get(Type *contained, unsigned num_elements); + // 得到该数组类型的元素类型 + Type *get_element_type() const; + // 获得该数组类型的长度 + unsigned get_num_of_elements(); +private: + // 数组元素类型 + Type *contained_; + // 数组长度 + unsigned num_elements_; +}; +``` +
+ +
+指针类型 PointerType 的定义 (点击此处展开) + +```c++ +class PointerType : public Type { +public: + // 获取该指针类型指向的元素类型 + Type *get_element_type() const; + // 创建指向类型为contained的指针类型 + static PointerType *get(Type *contained); +private: + // 记录指针指向的类型 + Type *contained_; +}; +``` +
+ +
+函数类型 FunctionType 的定义 (点击此处展开) + +```c++ +class FunctionType : public Type { +public: + // 判断返回值类型是否合法 + static bool is_valid_return_type(Type *ty); + // 判断参数类型是否合法 + static bool is_valid_argument_type(Type *ty); + // 根据返回值类型result,参数类型列表params创建函数类型 + static FunctionType *get(Type *result, std::vector params); + // 返回该函数类型的参数个数 + unsigned get_num_of_args() const; + // 获得该函数类型第i个参数的类型 + Type *get_param_type(unsigned i) const; + // 获得该函数类型的参数类型链表的起始迭代器 + std::vector::iterator param_begin(); + // 获得该函数类型的参数类型链表的结束迭代器 + std::vector::iterator param_end(); + // 获得该函数类型的返回值类型 + Type *get_return_type() const; +private: + // 返回值的类型 + Type *result_; + // 存储该函数类型的参数类型的链表 + std::vector args_; +} +``` +
+ +### User +- 概念:使用者。维护了 use-def 信息,表示该使用者用了哪些值。 + +
+User 的定义 (点击此处展开) + +```cpp +class User : public Value +{ +public: + // 从该使用者的操作数链表中取出第i个操作数 + Value *get_operand(unsigned i) const; + // 将该使用者的第i个操作数设为值v + void set_operand(unsigned i, Value *v); + // 将值v添加到该使用者的操作数链表上 + void add_operand(Value *v); + // 得到操作数链表的大小 + unsigned get_num_operand() const; + // 从该使用者的操作数链表中的所有操作数的使用情况中移除该使用者 + void remove_use_of_ops(); + // 移除操作数链表中索引为index1到index2的操作数 + // 例如想删除第0个操作数:remove_operands(0,0) + void remove_operands(int index1,int index2); +private: + // 参数列表,表示该使用者用到的参数 + std::vector operands_; + // 该使用者使用的参数个数 + unsigned num_ops_; +}; + +``` +
+ +### Use +- 概念:代表了值的使用情况。 + +
+Use 的定义 (点击此处展开) + +```cpp +struct Use +{ + // 使用者 + Value *val_; + // 使用者中值的序数 + // 对于 func(a, b),a为0,b为1 + unsigned arg_no_; +}; +``` +
+ +### Value +- 概念:值。代表一个可能用于指令操作数的带类型数据,是最基础的类,维护了 def-use 信息,即该值被哪些使用者使用。 + +
+Value 的定义 (点击此处展开) + +```cpp +class Value +{ +public: + // 获取该值的类型 + Type *get_type() const; + + // 取得该值的使用情况 + std::list &get_use_list() { return use_list_; } + // 添加加该值的使用情况 + void add_use(Value *val, unsigned arg_no = 0); + // 在所有地方将该值用新的值 new_val 替换,并维护好use_def和def_use链表 + void replace_all_use_with(Value *new_val); + // 将值 val 从使用链表中移除 + void remove_use(Value *val); +private: + // 值的类型 + Type *type_; + // 储存了该值的使用情况的链表 + std::list use_list_; +}; +``` +
+ +### 注意 + +助教在接口文档里筛选了可能会需要用到的接口,如果对API有问题的请移步issue讨论,本次`lightir`接口由助教自行设计实现,并做了大量测试,如有对助教的实现方法有异议或者建议的也请移步issue讨论,**请不要直接修改助教的代码,若因修改助教代码造成后续实验仓库合并的冲突请自行解决**。 diff --git a/Documentations/common/cminusf.md b/Documentations/common/cminusf.md new file mode 100644 index 0000000..232f15b --- /dev/null +++ b/Documentations/common/cminusf.md @@ -0,0 +1,189 @@ +# cminus-f 的语法与语义 + +## cminus-f 的语法 + +1. $`\text{program} \rightarrow \text{declaration-list}`$ +2. $`\text{declaration-list} \rightarrow \text{declaration-list}\ \text{declaration}\ |\ \text{declaration}`$ +3. $`\text{declaration} \rightarrow \text{var-declaration}\ |\ \text{fun-declaration}`$ +4. $`\text{var-declaration}\ \rightarrow \text{type-specifier}\ \textbf{ID}\ \textbf{;}\ |\ \text{type-specifier}\ \textbf{ID}\ \textbf{[}\ \textbf{INTEGER}\ \textbf{]}\ \textbf{;}`$ +5. $`\text{type-specifier} \rightarrow \textbf{int}\ |\ \textbf{float}\ |\ \textbf{void}`$ +6. $`\text{fun-declaration} \rightarrow \text{type-specifier}\ \textbf{ID}\ \textbf{(}\ \text{params}\ \textbf{)}\ \text{compound-stmt}`$ +7. $`\text{params} \rightarrow \text{param-list}\ |\ \textbf{void}`$ +8. $`\text{param-list} \rightarrow \text{param-list}\ ,\ \text{param}\ |\ \text{param}`$ +9. $`\text{param} \rightarrow \text{type-specifier}\ \textbf{ID}\ |\ \text{type-specifier}\ \textbf{ID}\ \textbf{[]}`$ +10. $`\text{compound-stmt} \rightarrow \textbf{\{}\ \text{local-declarations}\ \text{statement-list} \textbf{\}}`$ +11. $`\text{local-declarations} \rightarrow \text{local-declarations var-declaration}\ |\ \text{empty}`$ +12. $`\text{statement-list} \rightarrow \text{statement-list}\ \text{statement}\ |\ \text{empty}`$ +13. $`\begin{aligned}\text{statement} \rightarrow\ &\text{expression-stmt}\\ &|\ \text{compound-stmt}\\ &|\ \text{selection-stmt}\\ &|\ \text{iteration-stmt}\\ &|\ \text{return-stmt}\end{aligned}`$ +14. $`\text{expression-stmt} \rightarrow \text{expression}\ \textbf{;}\ |\ \textbf{;}`$ +15. $`\begin{aligned}\text{selection-stmt} \rightarrow\ &\textbf{if}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement}\\ &|\ \textbf{if}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement}\ \textbf{else}\ \text{statement}\end{aligned}`$ +16. $`\text{iteration-stmt} \rightarrow \textbf{while}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement}`$ +17. $`\text{return-stmt} \rightarrow \textbf{return}\ \textbf{;}\ |\ \textbf{return}\ \text{expression}\ \textbf{;}`$ +18. $`\text{expression} \rightarrow \text{var}\ \textbf{=}\ \text{expression}\ |\ \text{simple-expression}`$ +19. $`\text{var} \rightarrow \textbf{ID}\ |\ \textbf{ID}\ \textbf{[}\ \text{expression} \textbf{]}`$ +20. $`\text{simple-expression} \rightarrow \text{additive-expression}\ \text{relop}\ \text{additive-expression}\ |\ \text{additive-expression}`$ +21. $`\text{relop}\ \rightarrow \textbf{<=}\ |\ \textbf{<}\ |\ \textbf{>}\ |\ \textbf{>=}\ |\ \textbf{==}\ |\ \textbf{!=}`$ +22. $`\text{additive-expression} \rightarrow \text{additive-expression}\ \text{addop}\ \text{term}\ |\ \text{term}`$ +23. $`\text{addop} \rightarrow \textbf{+}\ |\ \textbf{-}`$ +24. $`\text{term} \rightarrow \text{term}\ \text{mulop}\ \text{factor}\ |\ \text{factor}`$ +25. $`\text{mulop} \rightarrow \textbf{*}\ |\ \textbf{/}`$ +26. $`\text{factor} \rightarrow \textbf{(}\ \text{expression}\ \textbf{)}\ |\ \text{var}\ |\ \text{call}\ |\ \text{integer}\ |\ \text{float}`$ +27. $`\text{integer} \rightarrow \textbf{INTEGER}`$ +28. $`\text{float} \rightarrow \textbf{FLOATPOINT}`$ +29. $`\text{call} \rightarrow \textbf{ID}\ \textbf{(}\ \text{args} \textbf{)}`$ +30. $`\text{args} \rightarrow \text{arg-list}\ |\ \text{empty}`$ +31. $`\text{arg-list} \rightarrow \text{arg-list}\ \textbf{,}\ \text{expression}\ |\ \text{expression}`$ + + +## cminus-f 的语义 + +在上述语法规则中,我们定义了 `cminus-f` 语言的语法,接着,我们对照语法规则,给出相关的语义和解释。 +在阅读前,需要理解 `cminus-f` 主要源自于 C 语言,因此它的行为都会接近 C 语言。 + +1. $`\text{program} \rightarrow \text{declaration-list}`$ +2. $`\text{declaration-list} \rightarrow \text{declaration-list}\ \text{declaration}\ |\ \text{declaration}`$ +3. $`\text{declaration} \rightarrow \text{var-declaration}\ |\ \text{fun-declaration}`$ + + 一个`程序`由一系列`声明`组成,声明包括了`函数声明`与`变量声明`,它们可以以任意顺序排列。 + + 全局变量需要初始化为全 0 + + 所有的变量必须在使用前先进行声明,所有的函数必须在使用前先进行定义 + + 一个`程序`中至少要有一个`main`函数的`声明` + + 因为没有原型这个概念, `cminus-f` 不区分函数的声明和定义。 + +4. $`\text{var-declaration}\ \rightarrow \text{type-specifier}\ \textbf{ID}\ \textbf{;}\ |\ \text{type-specifier}\ \textbf{ID}\ \textbf{[}\ \textbf{INTEGER}\ \textbf{]}\ \textbf{;}`$ +5. $`\text{type-specifier} \rightarrow \textbf{int}\ |\ \textbf{float}\ |\ \textbf{void}`$ + + `cminus-f` 的基础类型只有整型(`int`)、浮点型(`float`)和 `void`。而在变量声明中,只有整型和浮点型可以使用,`void` 仅用于函数声明。 + + 一个`变量声明`定义一个整型或者浮点型的变量,或者一个整型或浮点型的数组变量(这里整型指的是32位有符号整型,浮点数是指32位浮点数)。 + + 数组变量在声明时,$`\textbf{INTEGER}`$ 应当大于0。 + + 一次只能声明一个变量。 + +6. $`\text{fun-declaration} \rightarrow \text{type-specifier}\ \textbf{ID}\ \textbf{(}\ \text{params}\ \textbf{)}\ \text{compound-stmt}`$ +7. $`\text{params} \rightarrow \text{param-list}\ |\ \textbf{void}`$ +8. $`\text{param-list} \rightarrow \text{param-list}\ ,\ \text{param}\ |\ \text{param}`$ +9. $`\text{param} \rightarrow \text{type-specifier}\ \textbf{ID}\ |\ \text{type-specifier}\ \textbf{ID}\ \textbf{[]}`$ + + `函数声明`包含了返回类型,标识符,由逗号分隔的`形参`列表,还有一个`复合语句`。 + + 当函数的返回类型是 `void` 时,函数不返回任何值。 + + 函数的参数可以是 `void` ,也可以是一个列表。当函数的`形参`是`void`时,调用该函数时不用传入任何参数。 + + `形参`中跟着中括号代表数组参数,它们可以有不同长度。 + + 整型参数通过值来传入函数(pass by value),而数组参数通过引用来传入函数(pass by reference,即指针)。 + + 函数的`形参`拥有和`函数声明`的`复合语句`相同的作用域,并且每次函数调用都会产生一组独立内存的参数。(和C语言一致) + + 函数可以递归调用。 + +10. $`\text{compound-stmt} \rightarrow \textbf{\{}\ \text{local-declarations}\ \text{statement-list} \textbf{\}}`$ + + 一个`复合语句`由一对大括号和其中的`局部声明`与`语句列表`组成 + + `复合语句`的执行时,对包含着的语句按照`语句列表`中的顺序执行 + + `局部声明`拥有和`复合语句`中的`语句列表`一样的作用域,且其优先级高于任何同名的全局声明(常见的静态作用域) + +11. $`\text{local-declarations} \rightarrow \text{local-declarations var-declaration}\ |\ \text{empty}`$ +12. $`\text{statement-list} \rightarrow \text{statement-list}\ \text{statement}\ |\ \text{empty}`$ + + `局部声明`和`语句列表`都可以为空(empty表示空字符串,即$`\varepsilon`$) + +13. $`\begin{aligned}\text{statement} \rightarrow\ &\text{expression-stmt}\\ &|\ \text{compound-stmt}\\ &|\ \text{selection-stmt}\\ &|\ \text{iteration-stmt}\\ &|\ \text{return-stmt}\end{aligned}`$ +14. $`\text{expression-stmt} \rightarrow \text{expression}\ \textbf{;}\ |\ \textbf{;}`$ + + `表达式语句`由一个可选的`表达式`(即可以没有`表达式`)和一个分号组成 + + 我们通常使用`表达式语句`中的`表达式`计算时产生的副作用,所以这种`语句`用于赋值和函数调用 + +15. $`\begin{aligned}\text{selection-stmt} \rightarrow\ &\textbf{if}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement}\\ &|\ \textbf{if}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement}\ \textbf{else}\ \text{statement}\end{aligned}`$ + + `if`语句中的`表达式`将被求值,若结果的值等于0,则第二个`语句`执行(如果存在的话),否则第一个`语句`会执行。 + + 为了避免歧义,$`\textbf{else}`$将会匹配最近的$`\textbf{if}`$ + +16. $`\text{iteration-stmt} \rightarrow \textbf{while}\ \textbf{(}\ \text{expression}\ \textbf{)}\ \text{statement}`$ + + `while`语句是 `cminus-f` 中唯一的`迭代语句`。它执行时,会不断对`表达式`进行求值,并且在对`表达式`的求值结果等于 0 前,循环执行执下面的`语句` + +17. $`\text{return-stmt} \rightarrow \textbf{return}\ \textbf{;}\ |\ \textbf{return}\ \text{expression}\ \textbf{;}`$ + + `return`语句可以返回值,也可以不返回值。 + + 未声明为$`\textbf{void}`$类型的函数必须返回和函数返回类型相同的值 + + `return`会将程序的控制转移给当前函数的调用者,而$`\textbf{main}`$函数的`return`会使得程序终止 + +18. $`\text{expression} \rightarrow \text{var}\ \textbf{=}\ \text{expression}\ |\ \text{simple-expression}`$ +19. $`\text{var} \rightarrow \textbf{ID}\ |\ \textbf{ID}\ \textbf{[}\ \text{expression} \textbf{]}`$ + + 一个`表达式`可以是一个变量引用(即`var`)接着一个赋值符号(=)以及一个表达式,也可以是一个`简单表达式`。 + + `var` 可以是一个整型变量、浮点变量,或者一个取了下标的数组变量。 + + 数组的下标值为整型,作为数组下标值的表达式计算结果可能需要类型转换变成整型值 + + 一个负的下标会导致程序终止,需要调用框架中的内置函数`neg_idx_except` (该内部函数会主动退出程序,只需要调用该函数即可),但是对于上界并不做检查。 + + 赋值语义为:先找到 `var` 代表的变量地址(如果是数组,需要先对下标表达式求值),然后对右侧的表达式进行求值,求值结果将在转换成变量类型后存储在先前找到的地址中。同时,存储在 `var` 中的值将作为赋值表达式的求值结果。 + + 在 `C` 中,赋值对象(即 `var` )必须是左值,而左值可以通过多种方式获得。`cminus-f`中,唯一的左值就是通过 `var` 的语法得到的,因此 `cminus-f` 通过语法限制了 `var` 为左值,而不是像 `C` 中一样通过类型检查,这也是为什么 `cminus-f` 中不允许进行指针算数。 + +20. $`\text{simple-expression} \rightarrow \text{additive-expression}\ \text{relop}\ \text{additive-expression}\ |\ \text{additive-expression}`$ +21. $`\text{relop}\ \rightarrow \textbf{<=}\ |\ \textbf{<}\ |\ \textbf{>}\ |\ \textbf{>=}\ |\ \textbf{==}\ |\ \textbf{!=}`$ +22. $`\text{additive-expression} \rightarrow \text{additive-expression}\ \text{addop}\ \text{term}\ |\ \text{term}`$ +23. $`\text{addop} \rightarrow \textbf{+}\ |\ \textbf{-}`$ +24. $`\text{term} \rightarrow \text{term}\ \text{mulop}\ \text{factor}\ |\ \text{factor}`$ +25. $`\text{mulop} \rightarrow \textbf{*}\ |\ \textbf{/}`$ + + 一个`简单表达式`是一个`加法表达式`或者两个`加法表达式`的关系运算。当它是`加法表达式`时,它的值就是`加法表达式`的值。而当它是关系运算时,如果关系运算结果为真则值为整型值 1,反之则值为整型值 0。 + + `加法表达式`表现出了四则运算的结合性质与优先级顺序,四则运算的含义和`C`中的整型运算一致。 + + 浮点数和整型一起运算时,整型值需要进行类型提升,转换成浮点数类型,且运算结果也是浮点数类型 + +26. $`\text{factor} \rightarrow \textbf{(}\ \text{expression}\ \textbf{)}\ |\ \text{var}\ |\ \text{call}\ |\ \text{integer}\ |\ \text{float}`$ + + `因数`可以是一个括号包围的`表达式`(此时它的值是`表达式`的值),或者是一个`变量`(此时它的值是`变量`的值),或者是一个`函数调用`(此时它的值是`函数调用`的返回值),或者是一个`数字字面量`(此时它的值为该字面量的值)。当`因数`是数组变量时,除非此时它被用作一个`函数调用`中的数组参数,否则它必须要带有下标。 + +27. $`\text{integer} \rightarrow \textbf{INTEGER}`$ +28. $`\text{float} \rightarrow \textbf{FLOATPOINT}`$ +29. $`\text{call} \rightarrow \textbf{ID}\ \textbf{(}\ \text{args} \textbf{)}`$ +30. $`\text{args} \rightarrow \text{arg-list}\ |\ \text{empty}`$ +31. $`\text{arg-list} \rightarrow \text{arg-list}\ \textbf{,}\ \text{expression}\ |\ \text{expression}`$ + +`函数调用`由一个函数的`标识符`与一组括号包围的`实参`组成。`实参`可以为空,也可以是由逗号分隔的的`表达式`组成的列表,这些表达式代表着函数调用时,传给`形参`的值。`函数调用时`的`实参`数量和类型必须与`函数声明`中的`形参`一致,必要时需要进行类型转换。 + +`cminus-f`中包含四个预定义的函数 `input` 、 `output`、 `outputFloat` 和 `neg_idx_except`,它们的声明为: + +```c +int input(void) {...} +void output(int x) {...} +void outputFloat(float x) {...} +void neg_idx_except(void) {...} +``` + +* `input` 函数没有形参,且返回一个从标准输入中读到的整型值。 +* `output` 函数接受一个整型参数,然后将它的值打印到标准输出,并输出换行符。 +* `outputFloat` 函数接受一个浮点参数,然后将它的值打印到标准输出,并输出换行符。 +* `neg_idx_except` 函数没有形参,执行后报错并退出 + + +除此之外,其它规则和 C 中类似,比如同一个作用域下不允许定义重名变量或函数(本次实验中不做要求) + +## 提醒与补充 +1. 本次实验存在五种情况下的类型转换 + * 赋值时 + * 返回值类型和函数签名中的返回类型不一致时 + * 函数调用时实参和函数签名中的形参类型不一致时 + * 二元运算的两个参数类型不一致时 + * 下标计算时 +2. 如果对上述的语义有疑问可以通过发issue的方式进行交流(当然,我们推荐组内先进行讨论)。 diff --git a/Documentations/common/figs/cpp_class_inherits.png b/Documentations/common/figs/cpp_class_inherits.png new file mode 100644 index 0000000000000000000000000000000000000000..5f0d20a60e9da3331c5fbc998486753285f2a94a GIT binary patch literal 91878 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 Lh3EOt^xyptpAL`m literal 0 HcmV?d00001 diff --git a/Documentations/common/logging.md b/Documentations/common/logging.md new file mode 100644 index 0000000..fbcb8ac --- /dev/null +++ b/Documentations/common/logging.md @@ -0,0 +1,45 @@ +# logging 工具使用 + +## 介绍 +为了方便同学们在之后的实验中 debug,为大家设计了一个C++简单实用的分级日志工具。该工具将日志输出信息从低到高分成四种等级:`DEBUG`,`INFO`,`WARNING`,`ERROR`。通过设定环境变量`LOGV`的值,来选择输出哪些等级的日志。`LOGV`的取值是**0~3**,分别对应到上述的4种级别(`0:DEBUG`,`1:INFO`,`2:WARNING`,`3:ERROR`)。此外输出中还会包含打印该日志的代码所在位置。 + +## 使用 +项目编译好之后,可以在`build`目录下运行`test_logging`,该文件的源代码在`tests/test_logging.cpp`。用法如下: +```cpp +#include "logging.hpp" +// 引入头文件 +int main(){ + LOG(DEBUG) << "This is DEBUG log item."; + // 使用关键字LOG,括号中填入要输出的日志等级 + // 紧接着就是<<以及日志的具体信息,就跟使用std::cout一样 + LOG(INFO) << "This is INFO log item"; + LOG(WARNING) << "This is WARNING log item"; + LOG(ERROR) << "This is ERROR log item"; + return 0; +} +``` + +接着在运行该程序的时候,设定环境变量`LOGV=0`,那么程序就会输出级别**大于等于0**日志信息: +```bash +user@user:${ProjectDir}/build$ LOGV=0 ./test_logging +[DEBUG] (test_logging.cpp:5L main)This is DEBUG log item. +[INFO] (test_logging.cpp:6L main)This is INFO log item +[WARNING] (test_logging.cpp:7L main)This is WARNING log item +[ERROR] (test_logging.cpp:8L main)This is ERROR log item +``` +输出中除了包含日志级别和用户想打印的信息,在圆括号中还包含了打印该信息代码的具体位置(包括文件名称、所在行、所在函数名称),可以很方便地定位到出问题的地方。 + +假如我们觉得程序已经没有问题了,不想看那么多的DEBUG信息,那么我们就可以设定环境变量`LOGV=1`,选择只看**级别大于等于1**的日志信息: +```bash +user@user:${ProjectDir}/build$ LOGV=0 ./test_logging +[INFO] (test_logging.cpp:6L main)This is INFO log item +[WARNING] (test_logging.cpp:7L main)This is WARNING log item +[ERROR] (test_logging.cpp:8L main)This is ERROR log item +``` +当然`LOGV`值越大,日志的信息将更加简略。如果没有设定`LOGV`的环境变量,将默认不输出任何信息。 + +这里再附带一个小技巧,如果日志内容多,在终端观看体验较差,可以输入以下命令将日志输出到文件中: +``` +user@user:${ProjectDir}/build$ LOGV=0 ./test_logging > log +``` +然后就可以输出到文件名为log的文件中啦~ diff --git a/Documentations/common/simple_cpp.md b/Documentations/common/simple_cpp.md new file mode 100644 index 0000000..60a07e2 --- /dev/null +++ b/Documentations/common/simple_cpp.md @@ -0,0 +1,108 @@ +# CPP 简介 + +C++是一门面向对象的语言,从名字可以看出,C++从C中获得了许多灵感。但是随着C++的发展,它和C的差异也越来越大,一个典型的例子是C是弱类型的语言,而C++越来越支持强类型。因此我们不能直接认为C++兼容C,而应该先了解二者的区别。好在本次实验并不需要你们使用高级的C++特性,所以在此简单介绍一下部分特性便于理解。如果对C++有更深的兴趣,可以从Milo Yip的[游戏程序员的学习之路](https://github.com/miloyip/game-programmer/blob/master/game-programmer-zh-cn.jpg?raw=true)的C++部分开始看。 + +注:本介绍假设你有基本的C语言认知(略高于程设课标准),如果有不懂的C语言术语建议去搜索一下 + +## class + +class是C++面向对象的基础,它相当于对C中的结构体的扩展。除了保留了原来的结构体成员(即成员对象),它增加了成员函数、访问控制、继承和多态等。 + +假设某类为`Animal`,一个它的实例为`animal`,我们可以在`Animal`的定义中增加函数声明`void eat();`,这样声明的函数即是成员函数。成员函数的作用域中自带一个`Animal*`类型的指针`this`,指向调用该成员函数的实例。我们可以通过`animal.eat()`一样,用类似访问成员的方法访问该函数。 + +```cpp +// 注:C++中struct也会定义结构体,只是访问控制的默认选项有所区别 +struct Animal { + void eat() +} +``` + +类的访问控制指的是在定义class时,可以用`public`与`private`标签,指定接下来的成员是私有或是公开成员。公开成员可以在外部函数使用该类的实例时访问,而内部成员只有该类的成员函数能访问。访问控制的作用是对使用者隐藏实现的细节,而关注于设计者想要公开的接口,从而让使用者能更容易理解如何使用该类。详细介绍在[access specifiers](https://en.cppreference.com/w/cpp/language/access)。 + +类的继承是一种面向对象语言常用的代码复用方法,也是一种非常直观的抽象方式。我们可以定义`struct Cat : Animal`来声明`Cat`类是`Animal`类的子类,也就是`Cat`继承了`Animal`类。此时,新的`Cat`类从`Animal`类中继承了`void eat();`成员函数,并且可以在此之上定义额外的成员函数`void nyan()`。同理,我们也可以定义`struct Dog : Animal`来定义`Dog`类。 +```cpp +struct Cat : Animal { + // 从Animal中继承了void eat(); + void nyan() +}; + +struct Dog : Animal { + // 从Animal中继承了void eat(); + void wang() +}; +``` +我们可以通过合理的继承结构来将函数定义在合适的位置,使得大部分通用函数可以共享。 + +同学们可能会想到同是`Animal`,`Cat`和`Dog`可能会有相同名称与参数的函数,但是却有着不同的实现,这时我们就要用到虚函数了。子类中可以定义虚函数的实现,从而使得不同子类对于同一个名字的成员函数有不同实现。虚函数在调用时会通过虚函数表查找到对应的函数实现,而不是和普通类一样查找对应类型的函数实现。 +```cpp +struct Animal { + // = 0 表示该虚函数在Animal类中没有实现 + virtual void say() = 0; +}; + +struct Cat : Animal { + // override表示覆盖父函数中的实现,下同 + void say() override { + std::cout << "I'm a cat" << std::endl; + } +}; + +struct Dog : Animal { + void say() override{ + std::cout << "I'm a dog" << std::endl; + } +}; + +// 试一试 +int main() { + Cat c; + Dog d; + Animal* a; + c.say(); + d.say(); + a = &c; + a->say(); + a = &d; + a->say(); + return 0; +} +``` + +## 函数 + +C++中的函数可以重载,即可以有同名函数,但是要求它们的形参必须不同。如果想进一步了解,可以阅读[详细规则](https://en.cppreference.com/w/cpp/language/overload_resolution)。下面是函数重载的示例: + +```cpp +struct Point { + int x; + int y; +}; + +struct Line { + Point first; + Point second; +}; + +void print(Point p) { + printf("(%d, %d)", p.x, p.y); +} + +void print(Line s) { + print(s.first) // s.first == Point { ... } + printf("->"); + print(s.second) // s.second == Point { ... } +} +``` +我们可以看到上面的示例定义了两个`print`函数,并且它们的参数列表的类型不同。它们实际上是两个不同的函数(并且拥有不同的内部名字),但是C++能够正确的识别函数调用时使用了哪一个定义(前提是你正确使用了这一特性),并且在编译时就会链接上正确的实现。我们可以看到,这种特性非常符合人的直觉,并且没有带来额外开销。 + +## 泛型 + +不同于C中使用void指针来实现泛型函数(如`qsort`),C++中使用模板来帮助定义泛型类型与泛型函数等。由于模板过于复杂,这里不做深入介绍。这里你们需要理解的是,C++中的模板定义正如其名,在实例化前只是一个模板而不是参与编译的代码。只有在你使用的过程中指定了参数,编译器才会自动根据模板产生相应的代码,也就是实例化该参数对应的代码。比如`std::vector`是C++中常用的数组容器,在使用时必须指定参数,如果要实例化`int`类型的数组容器,必须要使用`std::vector`。 + +## 内存分配 + +C中,只能使用标准库中的`malloc`与`free`来进行内存分配,并且需要手动在内存上初始化类型。C++中增加了`new`与`delete`关键字,你可以使用`new classname(params)`的完成申请一块内存,利用构造函数(`classname(params)`即代表调用`classname`类型的一个构造函数)来完成内存初始化。而`delete variable`可以调用变量对应类型函数的析构函数来完成数据结构的清理和回收内存。但是它存在着和C一样的二次回收导致报错或忘记回收导致内存泄漏的问题。于是C++11引入了许多智能指针类型,本实验中用到的有两种,分别是: + +1. `std::shared_ptr`: 引用计数智能指针,使用一个共享变量来记录指针管理的对象被引用了几次。当对象引用计数为0时,说明当前该对象不再有引用,并且进程也无法再通过其它方式来引用它,也就意味着可以回收内存,这相当于低级的垃圾回收策略。 +2. `std::unique_ptr`: 表示所有权的智能指针,该指针要求它所管理的对象智能有一次引用,主要用于语义上不允许共享的对象(比如`llvm::Module`)。当引用计数为0时,它也会回收内存。 + diff --git a/README.md b/README.md index 07bc432..0f968e3 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,9 @@ * [lab1](./Documentations/1-parser/) + DDL:2022-10-03 23:59:59 (UTC+8) + +* [lab2](./Documentations/2-ir-gen-warmup/) + + DDL:2022-10-23 23:59:59 (UTC+8) ## FAQ: How to merge upstream remote branches diff --git a/Reports/2-ir-gen-warmup/report.md b/Reports/2-ir-gen-warmup/report.md new file mode 100644 index 0000000..1bcaa63 --- /dev/null +++ b/Reports/2-ir-gen-warmup/report.md @@ -0,0 +1,24 @@ +# lab2 实验报告 +学号 姓名 + +## 问题1: getelementptr +请给出 `IR.md` 中提到的两种 getelementptr 用法的区别,并稍加解释: + - `%2 = getelementptr [10 x i32], [10 x i32]* %1, i32 0, i32 %0` + - `%2 = getelementptr i32, i32* %1 i32 %0` + +## 问题2: cpp 与 .ll 的对应 +请说明你的 cpp 代码片段和 .ll 的每个 BasicBlock 的对应关系。 + +## 问题3: Visitor Pattern +分析 `calc` 程序在输入为 `4 * (8 + 4 - 1) / 2` 时的行为: +1. 请画出该表达式对应的抽象语法树(使用 `calc_ast.hpp` 中的 `CalcAST*` 类型和在该类型中存储的值来表示),并给节点使用数字编号。 +2. 请指出示例代码在用访问者模式遍历该语法树时的遍历顺序。 + +序列请按如下格式指明(序号为问题 2.1 中的编号): +3->2->5->1 + +## 实验难点 +描述在实验中遇到的问题、分析和解决方案。 + +## 实验反馈 +吐槽?建议? diff --git a/include/lightir/BasicBlock.h b/include/lightir/BasicBlock.h new file mode 100644 index 0000000..b3355c9 --- /dev/null +++ b/include/lightir/BasicBlock.h @@ -0,0 +1,71 @@ +#ifndef SYSYC_BASICBLOCK_H +#define SYSYC_BASICBLOCK_H + +#include "Instruction.h" +#include "Value.h" + +#include +#include +#include +#include +#include + +class Function; +class Instruction; +class Module; + +class BasicBlock : public Value, public llvm::ilist_node { + public: + static BasicBlock *create(Module *m, const std::string &name, Function *parent) { + auto prefix = name.empty() ? "" : "label_"; + return new BasicBlock(m, prefix + name, parent); + } + + // return parent, or null if none. + Function *get_parent() { return parent_; } + + Module *get_module(); + + /****************api about cfg****************/ + + std::list &get_pre_basic_blocks() { return pre_bbs_; } + std::list &get_succ_basic_blocks() { return succ_bbs_; } + void add_pre_basic_block(BasicBlock *bb) { pre_bbs_.push_back(bb); } + void add_succ_basic_block(BasicBlock *bb) { succ_bbs_.push_back(bb); } + + void remove_pre_basic_block(BasicBlock *bb) { pre_bbs_.remove(bb); } + void remove_succ_basic_block(BasicBlock *bb) { succ_bbs_.remove(bb); } + + /****************api about cfg****************/ + + /// Returns the terminator instruction if the block is well formed or null + /// if the block is not well formed. + const Instruction *get_terminator() const; + Instruction *get_terminator() { + return const_cast(static_cast(this)->get_terminator()); + } + + void add_instruction(Instruction *instr); + void add_instr_begin(Instruction *instr); + + void delete_instr(Instruction *instr); + + bool empty() { return instr_list_.empty(); } + + int get_num_of_instr() { return instr_list_.size(); } + llvm::ilist &get_instructions() { return instr_list_; } + + void erase_from_parent(); + + virtual std::string print() override; + + private: + explicit BasicBlock(Module *m, const std::string &name, Function *parent); + std::list pre_bbs_; + std::list succ_bbs_; + // std::list instr_list_; + llvm::ilist instr_list_; + Function *parent_; +}; + +#endif // SYSYC_BASICBLOCK_H diff --git a/include/lightir/Constant.h b/include/lightir/Constant.h new file mode 100644 index 0000000..d33419f --- /dev/null +++ b/include/lightir/Constant.h @@ -0,0 +1,77 @@ +// +// Created by cqy on 2020/6/29. +// + +#ifndef SYSYC_CONSTANT_H +#define SYSYC_CONSTANT_H +#include "User.h" +#include "Value.h" +#include "Type.h" + +class Constant : public User +{ +private: + // int value; +public: + Constant(Type *ty, const std::string &name = "", unsigned num_ops = 0) + : User(ty, name, num_ops) {} + ~Constant() = default; +}; + +class ConstantInt : public Constant +{ +private: + int value_; + ConstantInt(Type* ty,int val) + : Constant(ty,"",0),value_(val) {} +public: + + static int get_value(ConstantInt *const_val) { return const_val->value_; } + int get_value() { return value_; } + static ConstantInt *get(int val, Module *m); + static ConstantInt *get(bool val, Module *m); + virtual std::string print() override; +}; + +class ConstantArray : public Constant +{ +private: + std::vector const_array; + + ConstantArray(ArrayType *ty, const std::vector &val); +public: + + ~ConstantArray()=default; + + Constant* get_element_value(int index); + + unsigned get_size_of_array() { return const_array.size(); } + + static ConstantArray *get(ArrayType *ty, const std::vector &val); + + virtual std::string print() override; +}; + +class ConstantZero : public Constant +{ +private: + ConstantZero(Type *ty) + : Constant(ty,"",0) {} +public: + static ConstantZero *get(Type *ty, Module *m); + virtual std::string print() override; +}; + +class ConstantFP : public Constant +{ +private: + float val_; + ConstantFP(Type *ty, float val) + : Constant(ty,"",0), val_(val) {} +public: + static ConstantFP *get(float val, Module *m); + float get_value() { return val_; } + virtual std::string print() override; +}; + +#endif //SYSYC_CONSTANT_H diff --git a/include/lightir/Function.h b/include/lightir/Function.h new file mode 100644 index 0000000..507c59f --- /dev/null +++ b/include/lightir/Function.h @@ -0,0 +1,89 @@ +#ifndef SYSYC_FUNCTION_H +#define SYSYC_FUNCTION_H + +#include "BasicBlock.h" +#include "Type.h" +#include "User.h" + +#include +#include +#include +#include +#include +#include +#include +#include +class Module; +class Argument; +class Type; +class FunctionType; + +class Function : public Value, public llvm::ilist_node { + public: + Function(FunctionType *ty, const std::string &name, Module *parent); + virtual ~Function(); + static Function *create(FunctionType *ty, const std::string &name, Module *parent); + + FunctionType *get_function_type() const; + + Type *get_return_type() const; + + void add_basic_block(BasicBlock *bb); + + unsigned get_num_of_args() const; + unsigned get_num_basic_blocks() const; + + Module *get_parent() const; + + std::list::iterator arg_begin() { return arguments_.begin(); } + std::list::iterator arg_end() { return arguments_.end(); } + + void remove(BasicBlock *bb); + BasicBlock *get_entry_block() { return &*basic_blocks_.begin(); } + + llvm::ilist &get_basic_blocks() { return basic_blocks_; } + std::list &get_args() { return arguments_; } + + bool is_declaration() { return basic_blocks_.empty(); } + + void set_instr_name(); + std::string print(); + + private: + void build_args(); + + private: + llvm::ilist basic_blocks_; // basic blocks + std::list arguments_; // arguments + Module *parent_; + unsigned seq_cnt_; + // unsigned num_args_; + // We don't need this, all value inside function should be unnamed + // std::map sym_table_; // Symbol table of args/instructions +}; + +// Argument of Function, does not contain actual value +class Argument : public Value { + public: + /// Argument constructor. + explicit Argument(Type *ty, const std::string &name = "", Function *f = nullptr, unsigned arg_no = 0) + : Value(ty, name), parent_(f), arg_no_(arg_no) {} + virtual ~Argument() {} + + inline const Function *get_parent() const { return parent_; } + inline Function *get_parent() { return parent_; } + + /// For example in "void foo(int a, float b)" a is 0 and b is 1. + unsigned get_arg_no() const { + assert(parent_ && "can't get number of unparented arg"); + return arg_no_; + } + + virtual std::string print() override; + + private: + Function *parent_; + unsigned arg_no_; // argument No. +}; + +#endif // SYSYC_FUNCTION_H diff --git a/include/lightir/GlobalVariable.h b/include/lightir/GlobalVariable.h new file mode 100644 index 0000000..1d2d888 --- /dev/null +++ b/include/lightir/GlobalVariable.h @@ -0,0 +1,26 @@ +// +// Created by cqy on 2020/6/29. +// + +#ifndef SYSYC_GLOBALVARIABLE_H +#define SYSYC_GLOBALVARIABLE_H + +#include "Constant.h" +#include "User.h" + +#include +class Module; +class GlobalVariable : public User, public llvm::ilist_node { + private: + bool is_const_; + Constant *init_val_; + GlobalVariable(std::string name, Module *m, Type *ty, bool is_const, Constant *init = nullptr); + + public: + static GlobalVariable *create(std::string name, Module *m, Type *ty, bool is_const, Constant *init); + virtual ~GlobalVariable() = default; + Constant *get_init() { return init_val_; } + bool is_const() { return is_const_; } + std::string print(); +}; +#endif // SYSYC_GLOBALVARIABLE_H diff --git a/include/lightir/IRBuilder.h b/include/lightir/IRBuilder.h new file mode 100644 index 0000000..a06b967 --- /dev/null +++ b/include/lightir/IRBuilder.h @@ -0,0 +1,101 @@ +#ifndef SYSYC_IRBUILDER_H +#define SYSYC_IRBUILDER_H + +#include "BasicBlock.h" +#include "Function.h" +#include "Instruction.h" +#include "Value.h" + +class IRBuilder { + private: + BasicBlock *BB_; + Module *m_; + + public: + IRBuilder(BasicBlock *bb, Module *m) : BB_(bb), m_(m){}; + ~IRBuilder() = default; + Module *get_module() { return m_; } + BasicBlock *get_insert_block() { return this->BB_; } + void set_insert_point(BasicBlock *bb) { this->BB_ = bb; } // 在某个基本块中插入指令 + BinaryInst *create_iadd(Value *lhs, Value *rhs) { + return BinaryInst::create_add(lhs, rhs, this->BB_, m_); + } // 创建加法指令(以及其他算术指令) + BinaryInst *create_isub(Value *lhs, Value *rhs) { return BinaryInst::create_sub(lhs, rhs, this->BB_, m_); } + BinaryInst *create_imul(Value *lhs, Value *rhs) { return BinaryInst::create_mul(lhs, rhs, this->BB_, m_); } + BinaryInst *create_isdiv(Value *lhs, Value *rhs) { return BinaryInst::create_sdiv(lhs, rhs, this->BB_, m_); } + + CmpInst *create_icmp_eq(Value *lhs, Value *rhs) { + return CmpInst::create_cmp(CmpInst::EQ, lhs, rhs, this->BB_, m_); + } + CmpInst *create_icmp_ne(Value *lhs, Value *rhs) { + return CmpInst::create_cmp(CmpInst::NE, lhs, rhs, this->BB_, m_); + } + CmpInst *create_icmp_gt(Value *lhs, Value *rhs) { + return CmpInst::create_cmp(CmpInst::GT, lhs, rhs, this->BB_, m_); + } + CmpInst *create_icmp_ge(Value *lhs, Value *rhs) { + return CmpInst::create_cmp(CmpInst::GE, lhs, rhs, this->BB_, m_); + } + CmpInst *create_icmp_lt(Value *lhs, Value *rhs) { + return CmpInst::create_cmp(CmpInst::LT, lhs, rhs, this->BB_, m_); + } + CmpInst *create_icmp_le(Value *lhs, Value *rhs) { + return CmpInst::create_cmp(CmpInst::LE, lhs, rhs, this->BB_, m_); + } + + CallInst *create_call(Value *func, std::vector args) { + assert(dynamic_cast(func) && "func must be Function * type"); + return CallInst::create(static_cast(func), args, this->BB_); + } + + BranchInst *create_br(BasicBlock *if_true) { return BranchInst::create_br(if_true, this->BB_); } + BranchInst *create_cond_br(Value *cond, BasicBlock *if_true, BasicBlock *if_false) { + return BranchInst::create_cond_br(cond, if_true, if_false, this->BB_); + } + + ReturnInst *create_ret(Value *val) { return ReturnInst::create_ret(val, this->BB_); } + ReturnInst *create_void_ret() { return ReturnInst::create_void_ret(this->BB_); } + + GetElementPtrInst *create_gep(Value *ptr, std::vector idxs) { + return GetElementPtrInst::create_gep(ptr, idxs, this->BB_); + } + + StoreInst *create_store(Value *val, Value *ptr) { return StoreInst::create_store(val, ptr, this->BB_); } + LoadInst *create_load(Type *ty, Value *ptr) { return LoadInst::create_load(ty, ptr, this->BB_); } + LoadInst *create_load(Value *ptr) { + assert(ptr->get_type()->is_pointer_type() && "ptr must be pointer type"); + return LoadInst::create_load(ptr->get_type()->get_pointer_element_type(), ptr, this->BB_); + } + + AllocaInst *create_alloca(Type *ty) { return AllocaInst::create_alloca(ty, this->BB_); } + ZextInst *create_zext(Value *val, Type *ty) { return ZextInst::create_zext(val, ty, this->BB_); } + + SiToFpInst *create_sitofp(Value *val, Type *ty) { return SiToFpInst::create_sitofp(val, ty, this->BB_); } + FpToSiInst *create_fptosi(Value *val, Type *ty) { return FpToSiInst::create_fptosi(val, ty, this->BB_); } + + FCmpInst *create_fcmp_ne(Value *lhs, Value *rhs) { + return FCmpInst::create_fcmp(FCmpInst::NE, lhs, rhs, this->BB_, m_); + } + FCmpInst *create_fcmp_lt(Value *lhs, Value *rhs) { + return FCmpInst::create_fcmp(FCmpInst::LT, lhs, rhs, this->BB_, m_); + } + FCmpInst *create_fcmp_le(Value *lhs, Value *rhs) { + return FCmpInst::create_fcmp(FCmpInst::LE, lhs, rhs, this->BB_, m_); + } + FCmpInst *create_fcmp_ge(Value *lhs, Value *rhs) { + return FCmpInst::create_fcmp(FCmpInst::GE, lhs, rhs, this->BB_, m_); + } + FCmpInst *create_fcmp_gt(Value *lhs, Value *rhs) { + return FCmpInst::create_fcmp(FCmpInst::GT, lhs, rhs, this->BB_, m_); + } + FCmpInst *create_fcmp_eq(Value *lhs, Value *rhs) { + return FCmpInst::create_fcmp(FCmpInst::EQ, lhs, rhs, this->BB_, m_); + } + + BinaryInst *create_fadd(Value *lhs, Value *rhs) { return BinaryInst::create_fadd(lhs, rhs, this->BB_, m_); } + BinaryInst *create_fsub(Value *lhs, Value *rhs) { return BinaryInst::create_fsub(lhs, rhs, this->BB_, m_); } + BinaryInst *create_fmul(Value *lhs, Value *rhs) { return BinaryInst::create_fmul(lhs, rhs, this->BB_, m_); } + BinaryInst *create_fdiv(Value *lhs, Value *rhs) { return BinaryInst::create_fdiv(lhs, rhs, this->BB_, m_); } +}; + +#endif // SYSYC_IRBUILDER_H diff --git a/include/lightir/IRprinter.h b/include/lightir/IRprinter.h new file mode 100644 index 0000000..2ac936d --- /dev/null +++ b/include/lightir/IRprinter.h @@ -0,0 +1,13 @@ +#include "BasicBlock.h" +#include "Constant.h" +#include "Function.h" +#include "GlobalVariable.h" +#include "Instruction.h" +#include "Module.h" +#include "Type.h" +#include "User.h" +#include "Value.h" + +std::string print_as_op(Value *v, bool print_ty); +std::string print_cmp_type(CmpInst::CmpOp op); +std::string print_fcmp_type(FCmpInst::CmpOp op); diff --git a/include/lightir/Instruction.h b/include/lightir/Instruction.h new file mode 100644 index 0000000..fe502e7 --- /dev/null +++ b/include/lightir/Instruction.h @@ -0,0 +1,435 @@ +#ifndef SYSYC_INSTRUCTION_H +#define SYSYC_INSTRUCTION_H + +#include "Type.h" +#include "User.h" + +#include + +class BasicBlock; +class Function; + +class Instruction : public User, public llvm::ilist_node { + public: + enum OpID { + // Terminator Instructions + ret, + br, + // Standard binary operators + add, + sub, + mul, + sdiv, + // float binary operators + fadd, + fsub, + fmul, + fdiv, + // Memory operators + alloca, + load, + store, + // Other operators + cmp, + fcmp, + phi, + call, + getelementptr, + zext, // zero extend + fptosi, + sitofp + // float binary operators Logical operators + + }; + // create instruction, auto insert to bb + // ty here is result type + Instruction(Type *ty, OpID id, unsigned num_ops, BasicBlock *parent); + Instruction(Type *ty, OpID id, unsigned num_ops); + virtual ~Instruction() = default; + inline const BasicBlock *get_parent() const { return parent_; } + inline BasicBlock *get_parent() { return parent_; } + void set_parent(BasicBlock *parent) { this->parent_ = parent; } + // Return the function this instruction belongs to. + Function *get_function(); + Module *get_module(); + + OpID get_instr_type() const { return op_id_; } + std::string get_instr_op_name() { + switch (op_id_) { + case ret: return "ret"; break; + case br: return "br"; break; + case add: return "add"; break; + case sub: return "sub"; break; + case mul: return "mul"; break; + case sdiv: return "sdiv"; break; + case fadd: return "fadd"; break; + case fsub: return "fsub"; break; + case fmul: return "fmul"; break; + case fdiv: return "fdiv"; break; + case alloca: return "alloca"; break; + case load: return "load"; break; + case store: return "store"; break; + case cmp: return "cmp"; break; + case fcmp: return "fcmp"; break; + case phi: return "phi"; break; + case call: return "call"; break; + case getelementptr: return "getelementptr"; break; + case zext: return "zext"; break; + case fptosi: return "fptosi"; break; + case sitofp: return "sitofp"; break; + + default: return ""; break; + } + } + + bool is_void() { + return ((op_id_ == ret) || (op_id_ == br) || (op_id_ == store) || + (op_id_ == call && this->get_type()->is_void_type())); + } + + bool is_phi() { return op_id_ == phi; } + bool is_store() { return op_id_ == store; } + bool is_alloca() { return op_id_ == alloca; } + bool is_ret() { return op_id_ == ret; } + bool is_load() { return op_id_ == load; } + bool is_br() { return op_id_ == br; } + + bool is_add() { return op_id_ == add; } + bool is_sub() { return op_id_ == sub; } + bool is_mul() { return op_id_ == mul; } + bool is_div() { return op_id_ == sdiv; } + + bool is_fadd() { return op_id_ == fadd; } + bool is_fsub() { return op_id_ == fsub; } + bool is_fmul() { return op_id_ == fmul; } + bool is_fdiv() { return op_id_ == fdiv; } + bool is_fp2si() { return op_id_ == fptosi; } + bool is_si2fp() { return op_id_ == sitofp; } + + bool is_cmp() { return op_id_ == cmp; } + bool is_fcmp() { return op_id_ == fcmp; } + + bool is_call() { return op_id_ == call; } + bool is_gep() { return op_id_ == getelementptr; } + bool is_zext() { return op_id_ == zext; } + + bool isBinary() { + return (is_add() || is_sub() || is_mul() || is_div() || is_fadd() || is_fsub() || is_fmul() || is_fdiv()) && + (get_num_operand() == 2); + } + + bool isTerminator() { return is_br() || is_ret(); } + + private: + OpID op_id_; + unsigned num_ops_; + BasicBlock *parent_; +}; + +namespace detail { +template +struct tag { + using type = T; +}; +template +struct select_last { + // Use a fold-expression to fold the comma operator over the parameter pack. + using type = typename decltype((tag{}, ...))::type; +}; +template +using select_last_t = typename select_last::type; +}; // namespace detail + +template +inline constexpr bool always_false_v = false; + +template +class BaseInst : public Instruction { + protected: + template + static Inst *create(Args &&...args) { + if constexpr (std::is_same_v>, BasicBlock *>) { + auto ptr = new Inst(std::forward(args)...); + return ptr; + } else + static_assert(always_false_v, "ERROR"); + } + + template + BaseInst(Args &&...args) : Instruction(std::forward(args)...) {} +}; + +class BinaryInst : public BaseInst { + friend BaseInst; + + private: + BinaryInst(Type *ty, OpID id, Value *v1, Value *v2, BasicBlock *bb); + + public: + // create add instruction, auto insert to bb + static BinaryInst *create_add(Value *v1, Value *v2, BasicBlock *bb, Module *m); + + // create sub instruction, auto insert to bb + static BinaryInst *create_sub(Value *v1, Value *v2, BasicBlock *bb, Module *m); + + // create mul instruction, auto insert to bb + static BinaryInst *create_mul(Value *v1, Value *v2, BasicBlock *bb, Module *m); + + // create Div instruction, auto insert to bb + static BinaryInst *create_sdiv(Value *v1, Value *v2, BasicBlock *bb, Module *m); + + // create fadd instruction, auto insert to bb + static BinaryInst *create_fadd(Value *v1, Value *v2, BasicBlock *bb, Module *m); + + // create fsub instruction, auto insert to bb + static BinaryInst *create_fsub(Value *v1, Value *v2, BasicBlock *bb, Module *m); + + // create fmul instruction, auto insert to bb + static BinaryInst *create_fmul(Value *v1, Value *v2, BasicBlock *bb, Module *m); + + // create fDiv instruction, auto insert to bb + static BinaryInst *create_fdiv(Value *v1, Value *v2, BasicBlock *bb, Module *m); + + virtual std::string print() override; + + private: + void assertValid(); +}; + +class CmpInst : public BaseInst { + friend BaseInst; + + public: + enum CmpOp { + EQ, // == + NE, // != + GT, // > + GE, // >= + LT, // < + LE // <= + }; + + private: + CmpInst(Type *ty, CmpOp op, Value *lhs, Value *rhs, BasicBlock *bb); + + public: + static CmpInst *create_cmp(CmpOp op, Value *lhs, Value *rhs, BasicBlock *bb, Module *m); + + CmpOp get_cmp_op() { return cmp_op_; } + + virtual std::string print() override; + + private: + CmpOp cmp_op_; + + void assertValid(); +}; + +class FCmpInst : public BaseInst { + friend BaseInst; + + public: + enum CmpOp { + EQ, // == + NE, // != + GT, // > + GE, // >= + LT, // < + LE // <= + }; + + private: + FCmpInst(Type *ty, CmpOp op, Value *lhs, Value *rhs, BasicBlock *bb); + + public: + static FCmpInst *create_fcmp(CmpOp op, Value *lhs, Value *rhs, BasicBlock *bb, Module *m); + + CmpOp get_cmp_op() { return cmp_op_; } + + virtual std::string print() override; + + private: + CmpOp cmp_op_; + + void assert_valid(); +}; + +class CallInst : public BaseInst { + friend BaseInst; + + protected: + CallInst(Function *func, std::vector args, BasicBlock *bb); + + public: + static CallInst *create(Function *func, std::vector args, BasicBlock *bb); + FunctionType *get_function_type() const; + + virtual std::string print() override; +}; + +class BranchInst : public BaseInst { + friend BaseInst; + + private: + BranchInst(Value *cond, BasicBlock *if_true, BasicBlock *if_false, BasicBlock *bb); + BranchInst(BasicBlock *if_true, BasicBlock *bb); + + public: + static BranchInst *create_cond_br(Value *cond, BasicBlock *if_true, BasicBlock *if_false, BasicBlock *bb); + static BranchInst *create_br(BasicBlock *if_true, BasicBlock *bb); + + bool is_cond_br() const; + + virtual std::string print() override; +}; + +class ReturnInst : public BaseInst { + friend BaseInst; + + private: + ReturnInst(Value *val, BasicBlock *bb); + ReturnInst(BasicBlock *bb); + + public: + static ReturnInst *create_ret(Value *val, BasicBlock *bb); + static ReturnInst *create_void_ret(BasicBlock *bb); + bool is_void_ret() const; + + virtual std::string print() override; +}; + +class GetElementPtrInst : public BaseInst { + friend BaseInst; + + private: + GetElementPtrInst(Value *ptr, std::vector idxs, BasicBlock *bb); + + public: + static Type *get_element_type(Value *ptr, std::vector idxs); + static GetElementPtrInst *create_gep(Value *ptr, std::vector idxs, BasicBlock *bb); + Type *get_element_type() const; + + virtual std::string print() override; + + private: + Type *element_ty_; +}; + +class StoreInst : public BaseInst { + friend BaseInst; + + private: + StoreInst(Value *val, Value *ptr, BasicBlock *bb); + + public: + static StoreInst *create_store(Value *val, Value *ptr, BasicBlock *bb); + + Value *get_rval() { return this->get_operand(0); } + Value *get_lval() { return this->get_operand(1); } + + virtual std::string print() override; +}; + +class LoadInst : public BaseInst { + friend BaseInst; + + private: + LoadInst(Type *ty, Value *ptr, BasicBlock *bb); + + public: + static LoadInst *create_load(Type *ty, Value *ptr, BasicBlock *bb); + Value *get_lval() { return this->get_operand(0); } + + Type *get_load_type() const; + + virtual std::string print() override; +}; + +class AllocaInst : public BaseInst { + friend BaseInst; + + private: + AllocaInst(Type *ty, BasicBlock *bb); + + public: + static AllocaInst *create_alloca(Type *ty, BasicBlock *bb); + + Type *get_alloca_type() const; + + virtual std::string print() override; + + private: + Type *alloca_ty_; +}; + +class ZextInst : public BaseInst { + friend BaseInst; + + private: + ZextInst(OpID op, Value *val, Type *ty, BasicBlock *bb); + + public: + static ZextInst *create_zext(Value *val, Type *ty, BasicBlock *bb); + + Type *get_dest_type() const; + + virtual std::string print() override; + + private: + Type *dest_ty_; +}; + +class FpToSiInst : public BaseInst { + friend BaseInst; + + private: + FpToSiInst(OpID op, Value *val, Type *ty, BasicBlock *bb); + + public: + static FpToSiInst *create_fptosi(Value *val, Type *ty, BasicBlock *bb); + + Type *get_dest_type() const; + + virtual std::string print() override; + + private: + Type *dest_ty_; +}; + +class SiToFpInst : public BaseInst { + friend BaseInst; + + private: + SiToFpInst(OpID op, Value *val, Type *ty, BasicBlock *bb); + + public: + static SiToFpInst *create_sitofp(Value *val, Type *ty, BasicBlock *bb); + + Type *get_dest_type() const; + + virtual std::string print() override; + + private: + Type *dest_ty_; +}; + +class PhiInst : public BaseInst { + friend BaseInst; + + private: + PhiInst(OpID op, std::vector vals, std::vector val_bbs, Type *ty, BasicBlock *bb); + PhiInst(Type *ty, OpID op, unsigned num_ops, BasicBlock *bb) : BaseInst(ty, op, num_ops, bb) {} + Value *l_val_; + + public: + static PhiInst *create_phi(Type *ty, BasicBlock *bb); + Value *get_lval() { return l_val_; } + void set_lval(Value *l_val) { l_val_ = l_val; } + void add_phi_pair_operand(Value *val, Value *pre_bb) { + this->add_operand(val); + this->add_operand(pre_bb); + } + virtual std::string print() override; +}; + +#endif // SYSYC_INSTRUCTION_H diff --git a/include/lightir/Module.h b/include/lightir/Module.h new file mode 100644 index 0000000..6928276 --- /dev/null +++ b/include/lightir/Module.h @@ -0,0 +1,64 @@ +#ifndef SYSYC_MODULE_H +#define SYSYC_MODULE_H + +#include "Function.h" +#include "GlobalVariable.h" +#include "Instruction.h" +#include "Type.h" +#include "Value.h" + +#include +#include +#include +#include +#include +#include + +class GlobalVariable; +class Function; +class Module { + public: + explicit Module(std::string name); + ~Module(); + + Type *get_void_type(); + Type *get_label_type(); + IntegerType *get_int1_type(); + IntegerType *get_int32_type(); + PointerType *get_int32_ptr_type(); + FloatType *get_float_type(); + PointerType *get_float_ptr_type(); + + PointerType *get_pointer_type(Type *contained); + ArrayType *get_array_type(Type *contained, unsigned num_elements); + FunctionType *get_function_type(Type *retty, std::vector &args); + + void add_function(Function *f); + llvm::ilist &get_functions(); + void add_global_variable(GlobalVariable *g); + llvm::ilist &get_global_variable(); + std::string get_instr_op_name(Instruction::OpID instr) { return instr_id2string_[instr]; } + void set_print_name(); + std::string print(); + + private: + llvm::ilist global_list_; // The Global Variables in the module + llvm::ilist function_list_; // The Functions in the module + std::map value_sym_; // Symbol table for values + std::map instr_id2string_; // Instruction from opid to string + + std::string module_name_; // Human readable identifier for the module + std::string source_file_name_; // Original source file name for module, for test and debug + + private: + std::unique_ptr int1_ty_; + std::unique_ptr int32_ty_; + std::unique_ptr label_ty_; + std::unique_ptr void_ty_; + std::unique_ptr float32_ty_; + std::map> pointer_map_; + std::map, std::unique_ptr> array_map_; + std::map>, std::unique_ptr> function_map_; +}; + +#endif // SYSYC_MODULE_H diff --git a/include/lightir/Type.h b/include/lightir/Type.h new file mode 100644 index 0000000..945e807 --- /dev/null +++ b/include/lightir/Type.h @@ -0,0 +1,148 @@ +#ifndef SYSYC_TYPE_H +#define SYSYC_TYPE_H + +#include +#include + +class Module; +class IntegerType; +class FunctionType; +class ArrayType; +class PointerType; +class FloatType; + +class Type { + public: + enum TypeID { + VoidTyID, // Void + LabelTyID, // Labels, e.g., BasicBlock + IntegerTyID, // Integers, include 32 bits and 1 bit + FunctionTyID, // Functions + ArrayTyID, // Arrays + PointerTyID, // Pointer + FloatTyID // float + }; + + explicit Type(TypeID tid, Module *m); + ~Type() = default; + + TypeID get_type_id() const { return tid_; } + + bool is_void_type() const { return get_type_id() == VoidTyID; } + + bool is_label_type() const { return get_type_id() == LabelTyID; } + + bool is_integer_type() const { return get_type_id() == IntegerTyID; } + + bool is_function_type() const { return get_type_id() == FunctionTyID; } + + bool is_array_type() const { return get_type_id() == ArrayTyID; } + + bool is_pointer_type() const { return get_type_id() == PointerTyID; } + + bool is_float_type() const { return get_type_id() == FloatTyID; } + + static bool is_eq_type(Type *ty1, Type *ty2); + + static Type *get_void_type(Module *m); + + static Type *get_label_type(Module *m); + + static IntegerType *get_int1_type(Module *m); + + static IntegerType *get_int32_type(Module *m); + + static PointerType *get_int32_ptr_type(Module *m); + + static FloatType *get_float_type(Module *m); + + static PointerType *get_float_ptr_type(Module *m); + + static PointerType *get_pointer_type(Type *contained); + + static ArrayType *get_array_type(Type *contained, unsigned num_elements); + + Type *get_pointer_element_type(); + + Type *get_array_element_type(); + + int get_size(); + + Module *get_module(); + + std::string print(); + + private: + TypeID tid_; + Module *m_; +}; + +class IntegerType : public Type { + public: + explicit IntegerType(unsigned num_bits, Module *m); + + static IntegerType *get(unsigned num_bits, Module *m); + + unsigned get_num_bits(); + + private: + unsigned num_bits_; +}; + +class FunctionType : public Type { + public: + FunctionType(Type *result, std::vector params); + + static bool is_valid_return_type(Type *ty); + static bool is_valid_argument_type(Type *ty); + + static FunctionType *get(Type *result, std::vector params); + + unsigned get_num_of_args() const; + + Type *get_param_type(unsigned i) const; + std::vector::iterator param_begin() { return args_.begin(); } + std::vector::iterator param_end() { return args_.end(); } + Type *get_return_type() const; + + private: + Type *result_; + std::vector args_; +}; + +class ArrayType : public Type { + public: + ArrayType(Type *contained, unsigned num_elements); + + static bool is_valid_element_type(Type *ty); + + static ArrayType *get(Type *contained, unsigned num_elements); + + Type *get_element_type() const { return contained_; } + unsigned get_num_of_elements() const { return num_elements_; } + + private: + Type *contained_; // The element type of the array. + unsigned num_elements_; // Number of elements in the array. +}; + +class PointerType : public Type { + public: + PointerType(Type *contained); + Type *get_element_type() const { return contained_; } + + static PointerType *get(Type *contained); + + private: + Type *contained_; // The element type of the ptr. +}; + +class FloatType : public Type { + public: + FloatType(Module *m); + static FloatType *get(Module *m); + + private: +}; + +#endif // SYSYC_TYPE_H \ No newline at end of file diff --git a/include/lightir/User.h b/include/lightir/User.h new file mode 100644 index 0000000..09fb804 --- /dev/null +++ b/include/lightir/User.h @@ -0,0 +1,34 @@ +#ifndef SYSYC_USER_H +#define SYSYC_USER_H + +#include "Value.h" + +#include +// #include + +class User : public Value { + public: + User(Type *ty, const std::string &name = "", unsigned num_ops = 0); + virtual ~User() = default; + + std::vector &get_operands(); + + // start from 0 + Value *get_operand(unsigned i) const; + + // start from 0 + void set_operand(unsigned i, Value *v); + void add_operand(Value *v); + + unsigned get_num_operand() const; + + void remove_use_of_ops(); + void remove_operands(int index1, int index2); + + private: + // std::unique_ptr< std::list > operands_; // operands of this value + std::vector operands_; // operands of this value + unsigned num_ops_; +}; + +#endif // SYSYC_USER_H diff --git a/include/lightir/Value.h b/include/lightir/Value.h new file mode 100644 index 0000000..4eeb018 --- /dev/null +++ b/include/lightir/Value.h @@ -0,0 +1,48 @@ +#ifndef SYSYC_VALUE_H +#define SYSYC_VALUE_H + +#include +#include +#include + +class Type; +class Value; + +struct Use { + Value *val_; + unsigned arg_no_; // the no. of operand, e.g., func(a, b), a is 0, b is 1 + Use(Value *val, unsigned no) : val_(val), arg_no_(no) {} +}; + +class Value { + public: + explicit Value(Type *ty, const std::string &name = ""); + virtual ~Value() = default; + + Type *get_type() const { return type_; } + + std::list &get_use_list() { return use_list_; } + + void add_use(Value *val, unsigned arg_no = 0); + + bool set_name(std::string name) { + if (name_ == "") { + name_ = name; + return true; + } + return false; + } + std::string get_name() const; + + void replace_all_use_with(Value *new_val); + void remove_use(Value *val); + + virtual std::string print() = 0; + + private: + Type *type_; + std::list use_list_; // who use this value + std::string name_; // should we put name field here ? +}; + +#endif // SYSYC_VALUE_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 379e17d..e595568 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,2 +1,4 @@ add_subdirectory(parser) -add_subdirectory(common) \ No newline at end of file +add_subdirectory(common) +add_subdirectory(io) +add_subdirectory(lightir) \ No newline at end of file diff --git a/src/io/CMakeLists.txt b/src/io/CMakeLists.txt new file mode 100644 index 0000000..3517289 --- /dev/null +++ b/src/io/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(cminus_io io.c) + +install( + TARGETS cminus_io + ARCHIVE DESTINATION lib +) diff --git a/src/io/io.c b/src/io/io.c new file mode 100644 index 0000000..11b25c0 --- /dev/null +++ b/src/io/io.c @@ -0,0 +1,16 @@ +#include +#include +int input() { + int a; + scanf("%d", &a); + return a; +} + +void output(int a) { printf("%d\n", a); } + +void outputFloat(float a) { printf("%f\n", a); } + +void neg_idx_except() { + printf("negative index exception\n"); + exit(0); +} diff --git a/src/lightir/BasicBlock.cpp b/src/lightir/BasicBlock.cpp new file mode 100644 index 0000000..b2788a2 --- /dev/null +++ b/src/lightir/BasicBlock.cpp @@ -0,0 +1,68 @@ +#include "BasicBlock.h" + +#include "Function.h" +#include "IRprinter.h" +#include "Module.h" + +#include + +BasicBlock::BasicBlock(Module *m, const std::string &name = "", Function *parent = nullptr) + : Value(Type::get_label_type(m), name), parent_(parent) { + assert(parent && "currently parent should not be nullptr"); + parent_->add_basic_block(this); +} + +Module *BasicBlock::get_module() { return get_parent()->get_parent(); } + +void BasicBlock::add_instruction(Instruction *instr) { instr_list_.push_back(instr); } + +void BasicBlock::add_instr_begin(Instruction *instr) { instr_list_.push_front(instr); } + +void BasicBlock::delete_instr(Instruction *instr) { + instr_list_.remove(instr); + instr->remove_use_of_ops(); +} + +const Instruction *BasicBlock::get_terminator() const { + if (instr_list_.empty()) { + return nullptr; + } + switch (instr_list_.back().get_instr_type()) { + case Instruction::ret: return &instr_list_.back(); + + case Instruction::br: return &instr_list_.back(); + + default: return nullptr; + } +} + +void BasicBlock::erase_from_parent() { this->get_parent()->remove(this); } + +std::string BasicBlock::print() { + std::string bb_ir; + bb_ir += this->get_name(); + bb_ir += ":"; + // print prebb + if (!this->get_pre_basic_blocks().empty()) { + bb_ir += " ; preds = "; + } + for (auto bb : this->get_pre_basic_blocks()) { + if (bb != *this->get_pre_basic_blocks().begin()) + bb_ir += ", "; + bb_ir += print_as_op(bb, false); + } + + // print prebb + if (!this->get_parent()) { + bb_ir += "\n"; + bb_ir += "; Error: Block without parent!"; + } + bb_ir += "\n"; + for (auto &instr : this->get_instructions()) { + bb_ir += " "; + bb_ir += instr.print(); + bb_ir += "\n"; + } + + return bb_ir; +} diff --git a/src/lightir/CMakeLists.txt b/src/lightir/CMakeLists.txt new file mode 100644 index 0000000..f19ab03 --- /dev/null +++ b/src/lightir/CMakeLists.txt @@ -0,0 +1,18 @@ +add_library( + IR_lib STATIC + Type.cpp + User.cpp + Value.cpp + BasicBlock.cpp + Constant.cpp + Function.cpp + GlobalVariable.cpp + Instruction.cpp + Module.cpp + IRprinter.cpp +) + +target_link_libraries( + IR_lib + LLVMSupport +) diff --git a/src/lightir/Constant.cpp b/src/lightir/Constant.cpp new file mode 100644 index 0000000..6f90346 --- /dev/null +++ b/src/lightir/Constant.cpp @@ -0,0 +1,104 @@ +#include "Constant.h" + +#include "Module.h" + +#include +#include +#include + +struct pair_hash { + template + std::size_t operator()(const std::pair val) const { + auto lhs = std::hash()(val.first); + auto rhs = std::hash()(reinterpret_cast(val.second)); + return lhs ^ rhs; + } +}; + +static std::unordered_map, std::unique_ptr, pair_hash> cached_int; +static std::unordered_map, std::unique_ptr, pair_hash> cached_bool; +static std::unordered_map, std::unique_ptr, pair_hash> cached_float; +static std::unordered_map> cached_zero; + +ConstantInt *ConstantInt::get(int val, Module *m) { + if (cached_int.find(std::make_pair(val, m)) != cached_int.end()) + return cached_int[std::make_pair(val, m)].get(); + return (cached_int[std::make_pair(val, m)] = + std::unique_ptr(new ConstantInt(Type::get_int32_type(m), val))) + .get(); +} +ConstantInt *ConstantInt::get(bool val, Module *m) { + if (cached_bool.find(std::make_pair(val, m)) != cached_bool.end()) + return cached_bool[std::make_pair(val, m)].get(); + return (cached_bool[std::make_pair(val, m)] = + std::unique_ptr(new ConstantInt(Type::get_int1_type(m), val ? 1 : 0))) + .get(); +} +std::string ConstantInt::print() { + std::string const_ir; + Type *ty = this->get_type(); + if (ty->is_integer_type() && static_cast(ty)->get_num_bits() == 1) { + // int1 + const_ir += (this->get_value() == 0) ? "false" : "true"; + } else { + // int32 + const_ir += std::to_string(this->get_value()); + } + return const_ir; +} + +ConstantArray::ConstantArray(ArrayType *ty, const std::vector &val) : Constant(ty, "", val.size()) { + for (int i = 0; i < val.size(); i++) + set_operand(i, val[i]); + this->const_array.assign(val.begin(), val.end()); +} + +Constant *ConstantArray::get_element_value(int index) { return this->const_array[index]; } + +ConstantArray *ConstantArray::get(ArrayType *ty, const std::vector &val) { + return new ConstantArray(ty, val); +} + +std::string ConstantArray::print() { + std::string const_ir; + const_ir += this->get_type()->print(); + const_ir += " "; + const_ir += "["; + for (int i = 0; i < this->get_size_of_array(); i++) { + Constant *element = get_element_value(i); + if (!dynamic_cast(get_element_value(i))) { + const_ir += element->get_type()->print(); + } + const_ir += element->print(); + if (i < this->get_size_of_array()) { + const_ir += ", "; + } + } + const_ir += "]"; + return const_ir; +} + +ConstantFP *ConstantFP::get(float val, Module *m) { + if (cached_float.find(std::make_pair(val, m)) != cached_float.end()) + return cached_float[std::make_pair(val, m)].get(); + return (cached_float[std::make_pair(val, m)] = + std::unique_ptr(new ConstantFP(Type::get_float_type(m), val))) + .get(); +} + +std::string ConstantFP::print() { + std::stringstream fp_ir_ss; + std::string fp_ir; + double val = this->get_value(); + fp_ir_ss << "0x" << std::hex << *(uint64_t *)&val << std::endl; + fp_ir_ss >> fp_ir; + return fp_ir; +} + +ConstantZero *ConstantZero::get(Type *ty, Module *m) { + if (not cached_zero[ty]) + cached_zero[ty] = std::unique_ptr(new ConstantZero(ty)); + return cached_zero[ty].get(); +} + +std::string ConstantZero::print() { return "zeroinitializer"; } diff --git a/src/lightir/Function.cpp b/src/lightir/Function.cpp new file mode 100644 index 0000000..ac3f828 --- /dev/null +++ b/src/lightir/Function.cpp @@ -0,0 +1,133 @@ +#include "Function.h" + +#include "IRprinter.h" +#include "Module.h" + +Function::Function(FunctionType *ty, const std::string &name, Module *parent) + : Value(ty, name), parent_(parent), seq_cnt_(0) { + // num_args_ = ty->getNumParams(); + parent->add_function(this); + build_args(); +} +Function::~Function() { + for (auto *arg : arguments_) + delete arg; +} +Function *Function::create(FunctionType *ty, const std::string &name, Module *parent) { + return new Function(ty, name, parent); +} + +FunctionType *Function::get_function_type() const { return static_cast(get_type()); } + +Type *Function::get_return_type() const { return get_function_type()->get_return_type(); } + +unsigned Function::get_num_of_args() const { return get_function_type()->get_num_of_args(); } + +unsigned Function::get_num_basic_blocks() const { return basic_blocks_.size(); } + +Module *Function::get_parent() const { return parent_; } + +void Function::remove(BasicBlock *bb) { + basic_blocks_.remove(bb); + for (auto pre : bb->get_pre_basic_blocks()) { + pre->remove_succ_basic_block(bb); + } + for (auto succ : bb->get_succ_basic_blocks()) { + succ->remove_pre_basic_block(bb); + } +} + +void Function::build_args() { + auto *func_ty = get_function_type(); + unsigned num_args = get_num_of_args(); + for (int i = 0; i < num_args; i++) { + arguments_.push_back(new Argument(func_ty->get_param_type(i), "", this, i)); + } +} + +void Function::add_basic_block(BasicBlock *bb) { basic_blocks_.push_back(bb); } + +void Function::set_instr_name() { + std::map seq; + for (const auto &arg : this->get_args()) { + if (seq.find(&*arg) == seq.end()) { + auto seq_num = seq.size() + seq_cnt_; + if (arg->set_name("arg" + std::to_string(seq_num))) { + seq.insert({&*arg, seq_num}); + } + } + } + for (auto &bb1 : basic_blocks_) { + auto bb = &bb1; + if (seq.find(bb) == seq.end()) { + auto seq_num = seq.size() + seq_cnt_; + if (bb->set_name("label" + std::to_string(seq_num))) { + seq.insert({bb, seq_num}); + } + } + for (auto &instr : bb->get_instructions()) { + if (!instr.is_void() && seq.find(&instr) == seq.end()) { + auto seq_num = seq.size() + seq_cnt_; + if (instr.set_name("op" + std::to_string(seq_num))) { + seq.insert({&instr, seq_num}); + } + } + } + } + seq_cnt_ += seq.size(); +} + +std::string Function::print() { + set_instr_name(); + std::string func_ir; + if (this->is_declaration()) { + func_ir += "declare "; + } else { + func_ir += "define "; + } + + func_ir += this->get_return_type()->print(); + func_ir += " "; + func_ir += print_as_op(this, false); + func_ir += "("; + + // print arg + if (this->is_declaration()) { + for (int i = 0; i < this->get_num_of_args(); i++) { + if (i) + func_ir += ", "; + func_ir += static_cast(this->get_type())->get_param_type(i)->print(); + } + } else { + for (auto arg = this->arg_begin(); arg != arg_end(); arg++) { + if (arg != this->arg_begin()) { + func_ir += ", "; + } + func_ir += (*arg)->print(); + } + } + func_ir += ")"; + + // print bb + if (this->is_declaration()) { + func_ir += "\n"; + } else { + func_ir += " {"; + func_ir += "\n"; + for (auto &bb1 : this->get_basic_blocks()) { + auto bb = &bb1; + func_ir += bb->print(); + } + func_ir += "}"; + } + + return func_ir; +} + +std::string Argument::print() { + std::string arg_ir; + arg_ir += this->get_type()->print(); + arg_ir += " %"; + arg_ir += this->get_name(); + return arg_ir; +} \ No newline at end of file diff --git a/src/lightir/GlobalVariable.cpp b/src/lightir/GlobalVariable.cpp new file mode 100644 index 0000000..27b4c13 --- /dev/null +++ b/src/lightir/GlobalVariable.cpp @@ -0,0 +1,29 @@ +// +// Created by cqy on 2020/6/29. +// +#include "GlobalVariable.h" + +#include "IRprinter.h" + +GlobalVariable::GlobalVariable(std::string name, Module *m, Type *ty, bool is_const, Constant *init) + : User(ty, name, init != nullptr), is_const_(is_const), init_val_(init) { + m->add_global_variable(this); + if (init) { + this->set_operand(0, init); + } +} // global操作数为initval + +GlobalVariable *GlobalVariable::create(std::string name, Module *m, Type *ty, bool is_const, Constant *init = nullptr) { + return new GlobalVariable(name, m, PointerType::get(ty), is_const, init); +} + +std::string GlobalVariable::print() { + std::string global_val_ir; + global_val_ir += print_as_op(this, false); + global_val_ir += " = "; + global_val_ir += (this->is_const() ? "constant " : "global "); + global_val_ir += this->get_type()->get_pointer_element_type()->print(); + global_val_ir += " "; + global_val_ir += this->get_init()->print(); + return global_val_ir; +} \ No newline at end of file diff --git a/src/lightir/IRprinter.cpp b/src/lightir/IRprinter.cpp new file mode 100644 index 0000000..da0a734 --- /dev/null +++ b/src/lightir/IRprinter.cpp @@ -0,0 +1,47 @@ +#include "IRprinter.h" + +std::string print_as_op(Value *v, bool print_ty) { + std::string op_ir; + if (print_ty) { + op_ir += v->get_type()->print(); + op_ir += " "; + } + + if (dynamic_cast(v)) { + op_ir += "@" + v->get_name(); + } else if (dynamic_cast(v)) { + op_ir += "@" + v->get_name(); + } else if (dynamic_cast(v)) { + op_ir += v->print(); + } else { + op_ir += "%" + v->get_name(); + } + + return op_ir; +} + +std::string print_cmp_type(CmpInst::CmpOp op) { + switch (op) { + case CmpInst::GE: return "sge"; break; + case CmpInst::GT: return "sgt"; break; + case CmpInst::LE: return "sle"; break; + case CmpInst::LT: return "slt"; break; + case CmpInst::EQ: return "eq"; break; + case CmpInst::NE: return "ne"; break; + default: break; + } + return "wrong cmpop"; +} + +std::string print_fcmp_type(FCmpInst::CmpOp op) { + switch (op) { + case FCmpInst::GE: return "uge"; break; + case FCmpInst::GT: return "ugt"; break; + case FCmpInst::LE: return "ule"; break; + case FCmpInst::LT: return "ult"; break; + case FCmpInst::EQ: return "ueq"; break; + case FCmpInst::NE: return "une"; break; + default: break; + } + return "wrong fcmpop"; +} \ No newline at end of file diff --git a/src/lightir/Instruction.cpp b/src/lightir/Instruction.cpp new file mode 100644 index 0000000..e4cd6cd --- /dev/null +++ b/src/lightir/Instruction.cpp @@ -0,0 +1,521 @@ +#include "Instruction.h" + +#include "BasicBlock.h" +#include "Function.h" +#include "IRprinter.h" +#include "Module.h" +#include "Type.h" + +#include +#include +#include + +Instruction::Instruction(Type *ty, OpID id, unsigned num_ops, BasicBlock *parent) + : User(ty, "", num_ops), op_id_(id), num_ops_(num_ops), parent_(parent) { + parent_->add_instruction(this); +} + +Instruction::Instruction(Type *ty, OpID id, unsigned num_ops) + : User(ty, "", num_ops), op_id_(id), num_ops_(num_ops), parent_(nullptr) {} + +Function *Instruction::get_function() { return parent_->get_parent(); } + +Module *Instruction::get_module() { return parent_->get_module(); } + +BinaryInst::BinaryInst(Type *ty, OpID id, Value *v1, Value *v2, BasicBlock *bb) : BaseInst(ty, id, 2, bb) { + set_operand(0, v1); + set_operand(1, v2); + // assertValid(); +} + +void BinaryInst::assertValid() { + assert(get_operand(0)->get_type()->is_integer_type()); + assert(get_operand(1)->get_type()->is_integer_type()); + assert(static_cast(get_operand(0)->get_type())->get_num_bits() == + static_cast(get_operand(1)->get_type())->get_num_bits()); +} + +BinaryInst *BinaryInst::create_add(Value *v1, Value *v2, BasicBlock *bb, Module *m) { + return create(Type::get_int32_type(m), Instruction::add, v1, v2, bb); +} + +BinaryInst *BinaryInst::create_sub(Value *v1, Value *v2, BasicBlock *bb, Module *m) { + return create(Type::get_int32_type(m), Instruction::sub, v1, v2, bb); +} + +BinaryInst *BinaryInst::create_mul(Value *v1, Value *v2, BasicBlock *bb, Module *m) { + return create(Type::get_int32_type(m), Instruction::mul, v1, v2, bb); +} + +BinaryInst *BinaryInst::create_sdiv(Value *v1, Value *v2, BasicBlock *bb, Module *m) { + return create(Type::get_int32_type(m), Instruction::sdiv, v1, v2, bb); +} + +BinaryInst *BinaryInst::create_fadd(Value *v1, Value *v2, BasicBlock *bb, Module *m) { + return create(Type::get_float_type(m), Instruction::fadd, v1, v2, bb); +} + +BinaryInst *BinaryInst::create_fsub(Value *v1, Value *v2, BasicBlock *bb, Module *m) { + return create(Type::get_float_type(m), Instruction::fsub, v1, v2, bb); +} + +BinaryInst *BinaryInst::create_fmul(Value *v1, Value *v2, BasicBlock *bb, Module *m) { + return create(Type::get_float_type(m), Instruction::fmul, v1, v2, bb); +} + +BinaryInst *BinaryInst::create_fdiv(Value *v1, Value *v2, BasicBlock *bb, Module *m) { + return create(Type::get_float_type(m), Instruction::fdiv, v1, v2, bb); +} + +std::string BinaryInst::print() { + std::string instr_ir; + instr_ir += "%"; + instr_ir += this->get_name(); + instr_ir += " = "; + instr_ir += this->get_module()->get_instr_op_name(this->get_instr_type()); + instr_ir += " "; + instr_ir += this->get_operand(0)->get_type()->print(); + instr_ir += " "; + instr_ir += print_as_op(this->get_operand(0), false); + instr_ir += ", "; + if (Type::is_eq_type(this->get_operand(0)->get_type(), this->get_operand(1)->get_type())) { + instr_ir += print_as_op(this->get_operand(1), false); + } else { + instr_ir += print_as_op(this->get_operand(1), true); + } + return instr_ir; +} + +CmpInst::CmpInst(Type *ty, CmpOp op, Value *lhs, Value *rhs, BasicBlock *bb) + : BaseInst(ty, Instruction::cmp, 2, bb), cmp_op_(op) { + set_operand(0, lhs); + set_operand(1, rhs); + // assertValid(); +} + +void CmpInst::assertValid() { + assert(get_operand(0)->get_type()->is_integer_type()); + assert(get_operand(1)->get_type()->is_integer_type()); + assert(static_cast(get_operand(0)->get_type())->get_num_bits() == + static_cast(get_operand(1)->get_type())->get_num_bits()); +} + +CmpInst *CmpInst::create_cmp(CmpOp op, Value *lhs, Value *rhs, BasicBlock *bb, Module *m) { + return create(m->get_int1_type(), op, lhs, rhs, bb); +} + +std::string CmpInst::print() { + std::string instr_ir; + instr_ir += "%"; + instr_ir += this->get_name(); + instr_ir += " = "; + instr_ir += this->get_module()->get_instr_op_name(this->get_instr_type()); + instr_ir += " "; + instr_ir += print_cmp_type(this->cmp_op_); + instr_ir += " "; + instr_ir += this->get_operand(0)->get_type()->print(); + instr_ir += " "; + instr_ir += print_as_op(this->get_operand(0), false); + instr_ir += ", "; + if (Type::is_eq_type(this->get_operand(0)->get_type(), this->get_operand(1)->get_type())) { + instr_ir += print_as_op(this->get_operand(1), false); + } else { + instr_ir += print_as_op(this->get_operand(1), true); + } + return instr_ir; +} + +FCmpInst::FCmpInst(Type *ty, CmpOp op, Value *lhs, Value *rhs, BasicBlock *bb) + : BaseInst(ty, Instruction::fcmp, 2, bb), cmp_op_(op) { + set_operand(0, lhs); + set_operand(1, rhs); + // assertValid(); +} + +void FCmpInst::assert_valid() { + assert(get_operand(0)->get_type()->is_float_type()); + assert(get_operand(1)->get_type()->is_float_type()); +} + +FCmpInst *FCmpInst::create_fcmp(CmpOp op, Value *lhs, Value *rhs, BasicBlock *bb, Module *m) { + return create(m->get_int1_type(), op, lhs, rhs, bb); +} + +std::string FCmpInst::print() { + std::string instr_ir; + instr_ir += "%"; + instr_ir += this->get_name(); + instr_ir += " = "; + instr_ir += this->get_module()->get_instr_op_name(this->get_instr_type()); + instr_ir += " "; + instr_ir += print_fcmp_type(this->cmp_op_); + instr_ir += " "; + instr_ir += this->get_operand(0)->get_type()->print(); + instr_ir += " "; + instr_ir += print_as_op(this->get_operand(0), false); + instr_ir += ","; + if (Type::is_eq_type(this->get_operand(0)->get_type(), this->get_operand(1)->get_type())) { + instr_ir += print_as_op(this->get_operand(1), false); + } else { + instr_ir += print_as_op(this->get_operand(1), true); + } + return instr_ir; +} + +CallInst::CallInst(Function *func, std::vector args, BasicBlock *bb) + : BaseInst(func->get_return_type(), Instruction::call, args.size() + 1, bb) { + assert(func->get_num_of_args() == args.size()); + int num_ops = args.size() + 1; + set_operand(0, func); + for (int i = 1; i < num_ops; i++) { + set_operand(i, args[i - 1]); + } +} + +CallInst *CallInst::create(Function *func, std::vector args, BasicBlock *bb) { + return BaseInst::create(func, args, bb); +} + +FunctionType *CallInst::get_function_type() const { return static_cast(get_operand(0)->get_type()); } + +std::string CallInst::print() { + std::string instr_ir; + if (!this->is_void()) { + instr_ir += "%"; + instr_ir += this->get_name(); + instr_ir += " = "; + } + instr_ir += this->get_module()->get_instr_op_name(this->get_instr_type()); + instr_ir += " "; + instr_ir += this->get_function_type()->get_return_type()->print(); + + instr_ir += " "; + assert(dynamic_cast(this->get_operand(0)) && "Wrong call operand function"); + instr_ir += print_as_op(this->get_operand(0), false); + instr_ir += "("; + for (int i = 1; i < this->get_num_operand(); i++) { + if (i > 1) + instr_ir += ", "; + instr_ir += this->get_operand(i)->get_type()->print(); + instr_ir += " "; + instr_ir += print_as_op(this->get_operand(i), false); + } + instr_ir += ")"; + return instr_ir; +} + +BranchInst::BranchInst(Value *cond, BasicBlock *if_true, BasicBlock *if_false, BasicBlock *bb) + : BaseInst(Type::get_void_type(if_true->get_module()), Instruction::br, 3, bb) { + set_operand(0, cond); + set_operand(1, if_true); + set_operand(2, if_false); +} + +BranchInst::BranchInst(BasicBlock *if_true, BasicBlock *bb) + : BaseInst(Type::get_void_type(if_true->get_module()), Instruction::br, 1, bb) { + set_operand(0, if_true); +} + +BranchInst *BranchInst::create_cond_br(Value *cond, BasicBlock *if_true, BasicBlock *if_false, BasicBlock *bb) { + if_true->add_pre_basic_block(bb); + if_false->add_pre_basic_block(bb); + bb->add_succ_basic_block(if_false); + bb->add_succ_basic_block(if_true); + + return create(cond, if_true, if_false, bb); +} + +BranchInst *BranchInst::create_br(BasicBlock *if_true, BasicBlock *bb) { + if_true->add_pre_basic_block(bb); + bb->add_succ_basic_block(if_true); + + return create(if_true, bb); +} + +bool BranchInst::is_cond_br() const { return get_num_operand() == 3; } + +std::string BranchInst::print() { + std::string instr_ir; + instr_ir += this->get_module()->get_instr_op_name(this->get_instr_type()); + instr_ir += " "; + // instr_ir += this->get_operand(0)->get_type()->print(); + instr_ir += print_as_op(this->get_operand(0), true); + if (is_cond_br()) { + instr_ir += ", "; + instr_ir += print_as_op(this->get_operand(1), true); + instr_ir += ", "; + instr_ir += print_as_op(this->get_operand(2), true); + } + return instr_ir; +} + +ReturnInst::ReturnInst(Value *val, BasicBlock *bb) + : BaseInst(Type::get_void_type(bb->get_module()), Instruction::ret, 1, bb) { + set_operand(0, val); +} + +ReturnInst::ReturnInst(BasicBlock *bb) + : BaseInst(Type::get_void_type(bb->get_module()), Instruction::ret, 0, bb) {} + +ReturnInst *ReturnInst::create_ret(Value *val, BasicBlock *bb) { return create(val, bb); } + +ReturnInst *ReturnInst::create_void_ret(BasicBlock *bb) { return create(bb); } + +bool ReturnInst::is_void_ret() const { return get_num_operand() == 0; } + +std::string ReturnInst::print() { + std::string instr_ir; + instr_ir += this->get_module()->get_instr_op_name(this->get_instr_type()); + instr_ir += " "; + if (!is_void_ret()) { + instr_ir += this->get_operand(0)->get_type()->print(); + instr_ir += " "; + instr_ir += print_as_op(this->get_operand(0), false); + } else { + instr_ir += "void"; + } + + return instr_ir; +} + +GetElementPtrInst::GetElementPtrInst(Value *ptr, std::vector idxs, BasicBlock *bb) + : BaseInst(PointerType::get(get_element_type(ptr, idxs)), + Instruction::getelementptr, + 1 + idxs.size(), + bb) { + set_operand(0, ptr); + for (int i = 0; i < idxs.size(); i++) { + set_operand(i + 1, idxs[i]); + } + element_ty_ = get_element_type(ptr, idxs); +} + +Type *GetElementPtrInst::get_element_type(Value *ptr, std::vector idxs) { + Type *ty = ptr->get_type()->get_pointer_element_type(); + assert("GetElementPtrInst ptr is wrong type" && + (ty->is_array_type() || ty->is_integer_type() || ty->is_float_type())); + if (ty->is_array_type()) { + ArrayType *arr_ty = static_cast(ty); + for (int i = 1; i < idxs.size(); i++) { + ty = arr_ty->get_element_type(); + if (i < idxs.size() - 1) { + assert(ty->is_array_type() && "Index error!"); + } + if (ty->is_array_type()) { + arr_ty = static_cast(ty); + } + } + } + return ty; +} + +Type *GetElementPtrInst::get_element_type() const { return element_ty_; } + +GetElementPtrInst *GetElementPtrInst::create_gep(Value *ptr, std::vector idxs, BasicBlock *bb) { + return create(ptr, idxs, bb); +} + +std::string GetElementPtrInst::print() { + std::string instr_ir; + instr_ir += "%"; + instr_ir += this->get_name(); + instr_ir += " = "; + instr_ir += this->get_module()->get_instr_op_name(this->get_instr_type()); + instr_ir += " "; + assert(this->get_operand(0)->get_type()->is_pointer_type()); + instr_ir += this->get_operand(0)->get_type()->get_pointer_element_type()->print(); + instr_ir += ", "; + for (int i = 0; i < this->get_num_operand(); i++) { + if (i > 0) + instr_ir += ", "; + instr_ir += this->get_operand(i)->get_type()->print(); + instr_ir += " "; + instr_ir += print_as_op(this->get_operand(i), false); + } + return instr_ir; +} + +StoreInst::StoreInst(Value *val, Value *ptr, BasicBlock *bb) + : BaseInst(Type::get_void_type(bb->get_module()), Instruction::store, 2, bb) { + set_operand(0, val); + set_operand(1, ptr); +} + +StoreInst *StoreInst::create_store(Value *val, Value *ptr, BasicBlock *bb) { return create(val, ptr, bb); } + +std::string StoreInst::print() { + std::string instr_ir; + instr_ir += this->get_module()->get_instr_op_name(this->get_instr_type()); + instr_ir += " "; + instr_ir += this->get_operand(0)->get_type()->print(); + instr_ir += " "; + instr_ir += print_as_op(this->get_operand(0), false); + instr_ir += ", "; + instr_ir += print_as_op(this->get_operand(1), true); + return instr_ir; +} + +LoadInst::LoadInst(Type *ty, Value *ptr, BasicBlock *bb) : BaseInst(ty, Instruction::load, 1, bb) { + assert(ptr->get_type()->is_pointer_type()); + assert(ty == static_cast(ptr->get_type())->get_element_type()); + set_operand(0, ptr); +} + +LoadInst *LoadInst::create_load(Type *ty, Value *ptr, BasicBlock *bb) { return create(ty, ptr, bb); } + +Type *LoadInst::get_load_type() const { + return static_cast(get_operand(0)->get_type())->get_element_type(); +} + +std::string LoadInst::print() { + std::string instr_ir; + instr_ir += "%"; + instr_ir += this->get_name(); + instr_ir += " = "; + instr_ir += this->get_module()->get_instr_op_name(this->get_instr_type()); + instr_ir += " "; + assert(this->get_operand(0)->get_type()->is_pointer_type()); + instr_ir += this->get_operand(0)->get_type()->get_pointer_element_type()->print(); + instr_ir += ","; + instr_ir += " "; + instr_ir += print_as_op(this->get_operand(0), true); + return instr_ir; +} + +AllocaInst::AllocaInst(Type *ty, BasicBlock *bb) + : BaseInst(PointerType::get(ty), Instruction::alloca, 0, bb), alloca_ty_(ty) {} + +AllocaInst *AllocaInst::create_alloca(Type *ty, BasicBlock *bb) { return create(ty, bb); } + +Type *AllocaInst::get_alloca_type() const { return alloca_ty_; } + +std::string AllocaInst::print() { + std::string instr_ir; + instr_ir += "%"; + instr_ir += this->get_name(); + instr_ir += " = "; + instr_ir += this->get_module()->get_instr_op_name(this->get_instr_type()); + instr_ir += " "; + instr_ir += get_alloca_type()->print(); + return instr_ir; +} + +ZextInst::ZextInst(OpID op, Value *val, Type *ty, BasicBlock *bb) : BaseInst(ty, op, 1, bb), dest_ty_(ty) { + set_operand(0, val); +} + +ZextInst *ZextInst::create_zext(Value *val, Type *ty, BasicBlock *bb) { return create(Instruction::zext, val, ty, bb); } + +Type *ZextInst::get_dest_type() const { return dest_ty_; } + +std::string ZextInst::print() { + std::string instr_ir; + instr_ir += "%"; + instr_ir += this->get_name(); + instr_ir += " = "; + instr_ir += this->get_module()->get_instr_op_name(this->get_instr_type()); + instr_ir += " "; + instr_ir += this->get_operand(0)->get_type()->print(); + instr_ir += " "; + instr_ir += print_as_op(this->get_operand(0), false); + instr_ir += " to "; + instr_ir += this->get_dest_type()->print(); + return instr_ir; +} + +FpToSiInst::FpToSiInst(OpID op, Value *val, Type *ty, BasicBlock *bb) + : BaseInst(ty, op, 1, bb), dest_ty_(ty) { + set_operand(0, val); +} + +FpToSiInst *FpToSiInst::create_fptosi(Value *val, Type *ty, BasicBlock *bb) { + return create(Instruction::fptosi, val, ty, bb); +} + +Type *FpToSiInst::get_dest_type() const { return dest_ty_; } + +std::string FpToSiInst::print() { + std::string instr_ir; + instr_ir += "%"; + instr_ir += this->get_name(); + instr_ir += " = "; + instr_ir += this->get_module()->get_instr_op_name(this->get_instr_type()); + instr_ir += " "; + instr_ir += this->get_operand(0)->get_type()->print(); + instr_ir += " "; + instr_ir += print_as_op(this->get_operand(0), false); + instr_ir += " to "; + instr_ir += this->get_dest_type()->print(); + return instr_ir; +} + +SiToFpInst::SiToFpInst(OpID op, Value *val, Type *ty, BasicBlock *bb) + : BaseInst(ty, op, 1, bb), dest_ty_(ty) { + set_operand(0, val); +} + +SiToFpInst *SiToFpInst::create_sitofp(Value *val, Type *ty, BasicBlock *bb) { + return create(Instruction::sitofp, val, ty, bb); +} + +Type *SiToFpInst::get_dest_type() const { return dest_ty_; } + +std::string SiToFpInst::print() { + std::string instr_ir; + instr_ir += "%"; + instr_ir += this->get_name(); + instr_ir += " = "; + instr_ir += this->get_module()->get_instr_op_name(this->get_instr_type()); + instr_ir += " "; + instr_ir += this->get_operand(0)->get_type()->print(); + instr_ir += " "; + instr_ir += print_as_op(this->get_operand(0), false); + instr_ir += " to "; + instr_ir += this->get_dest_type()->print(); + return instr_ir; +} + +PhiInst::PhiInst(OpID op, std::vector vals, std::vector val_bbs, Type *ty, BasicBlock *bb) + : BaseInst(ty, op, 2 * vals.size()) { + for (int i = 0; i < vals.size(); i++) { + set_operand(2 * i, vals[i]); + set_operand(2 * i + 1, val_bbs[i]); + } + this->set_parent(bb); +} + +PhiInst *PhiInst::create_phi(Type *ty, BasicBlock *bb) { + std::vector vals; + std::vector val_bbs; + return create(Instruction::phi, vals, val_bbs, ty, bb); +} + +std::string PhiInst::print() { + std::string instr_ir; + instr_ir += "%"; + instr_ir += this->get_name(); + instr_ir += " = "; + instr_ir += this->get_module()->get_instr_op_name(this->get_instr_type()); + instr_ir += " "; + instr_ir += this->get_operand(0)->get_type()->print(); + instr_ir += " "; + for (int i = 0; i < this->get_num_operand() / 2; i++) { + if (i > 0) + instr_ir += ", "; + instr_ir += "[ "; + instr_ir += print_as_op(this->get_operand(2 * i), false); + instr_ir += ", "; + instr_ir += print_as_op(this->get_operand(2 * i + 1), false); + instr_ir += " ]"; + } + if (this->get_num_operand() / 2 < this->get_parent()->get_pre_basic_blocks().size()) { + for (auto pre_bb : this->get_parent()->get_pre_basic_blocks()) { + if (std::find(this->get_operands().begin(), this->get_operands().end(), static_cast(pre_bb)) == + this->get_operands().end()) { + // find a pre_bb is not in phi + instr_ir += ", [ undef, " + print_as_op(pre_bb, false) + " ]"; + } + } + } + return instr_ir; +} diff --git a/src/lightir/Module.cpp b/src/lightir/Module.cpp new file mode 100644 index 0000000..88cd072 --- /dev/null +++ b/src/lightir/Module.cpp @@ -0,0 +1,100 @@ +#include "Module.h" + +#include "Function.h" +#include "GlobalVariable.h" + +#include + +Module::Module(std::string name) : module_name_(name) { + void_ty_ = std::make_unique(Type::VoidTyID, this); + label_ty_ = std::make_unique(Type::LabelTyID, this); + int1_ty_ = std::make_unique(1, this); + int32_ty_ = std::make_unique(32, this); + float32_ty_ = std::make_unique(this); + // init instr_id2string + instr_id2string_.insert({Instruction::ret, "ret"}); + instr_id2string_.insert({Instruction::br, "br"}); + + instr_id2string_.insert({Instruction::add, "add"}); + instr_id2string_.insert({Instruction::sub, "sub"}); + instr_id2string_.insert({Instruction::mul, "mul"}); + instr_id2string_.insert({Instruction::sdiv, "sdiv"}); + + instr_id2string_.insert({Instruction::fadd, "fadd"}); + instr_id2string_.insert({Instruction::fsub, "fsub"}); + instr_id2string_.insert({Instruction::fmul, "fmul"}); + instr_id2string_.insert({Instruction::fdiv, "fdiv"}); + + instr_id2string_.insert({Instruction::alloca, "alloca"}); + instr_id2string_.insert({Instruction::load, "load"}); + instr_id2string_.insert({Instruction::store, "store"}); + instr_id2string_.insert({Instruction::cmp, "icmp"}); + instr_id2string_.insert({Instruction::fcmp, "fcmp"}); + instr_id2string_.insert({Instruction::phi, "phi"}); + instr_id2string_.insert({Instruction::call, "call"}); + instr_id2string_.insert({Instruction::getelementptr, "getelementptr"}); + instr_id2string_.insert({Instruction::zext, "zext"}); + instr_id2string_.insert({Instruction::sitofp, "sitofp"}); + instr_id2string_.insert({Instruction::fptosi, "fptosi"}); +} + +Module::~Module() {} + +Type *Module::get_void_type() { return void_ty_.get(); } + +Type *Module::get_label_type() { return label_ty_.get(); } + +IntegerType *Module::get_int1_type() { return int1_ty_.get(); } + +IntegerType *Module::get_int32_type() { return int32_ty_.get(); } + +PointerType *Module::get_pointer_type(Type *contained) { + if (pointer_map_.find(contained) == pointer_map_.end()) { + pointer_map_[contained] = std::make_unique(contained); + } + return pointer_map_[contained].get(); +} + +ArrayType *Module::get_array_type(Type *contained, unsigned num_elements) { + if (array_map_.find({contained, num_elements}) == array_map_.end()) { + array_map_[{contained, num_elements}] = std::make_unique(contained, num_elements); + } + return array_map_[{contained, num_elements}].get(); +} +FunctionType *Module::get_function_type(Type *retty, std::vector &args) { + if (not function_map_.count({retty, args})) { + function_map_[{retty, args}] = std::make_unique(retty, args); + } + return function_map_[{retty, args}].get(); +} + +PointerType *Module::get_int32_ptr_type() { return get_pointer_type(int32_ty_.get()); } + +FloatType *Module::get_float_type() { return float32_ty_.get(); } + +PointerType *Module::get_float_ptr_type() { return get_pointer_type(float32_ty_.get()); } + +void Module::add_function(Function *f) { function_list_.push_back(f); } +llvm::ilist &Module::get_functions() { return function_list_; } +void Module::add_global_variable(GlobalVariable *g) { global_list_.push_back(g); } +llvm::ilist &Module::get_global_variable() { return global_list_; } + +void Module::set_print_name() { + for (auto &func : this->get_functions()) { + func.set_instr_name(); + } + return; +} + +std::string Module::print() { + std::string module_ir; + for (auto &global_val : this->global_list_) { + module_ir += global_val.print(); + module_ir += "\n"; + } + for (auto &func : this->function_list_) { + module_ir += func.print(); + module_ir += "\n"; + } + return module_ir; +} diff --git a/src/lightir/Type.cpp b/src/lightir/Type.cpp new file mode 100644 index 0000000..3cbc285 --- /dev/null +++ b/src/lightir/Type.cpp @@ -0,0 +1,171 @@ +#include "Type.h" + +#include "Module.h" + +#include + +Type::Type(TypeID tid, Module *m) { + tid_ = tid; + m_ = m; +} + +Module *Type::get_module() { return m_; } + +bool Type::is_eq_type(Type *ty1, Type *ty2) { return ty1 == ty2; } + +Type *Type::get_void_type(Module *m) { return m->get_void_type(); } + +Type *Type::get_label_type(Module *m) { return m->get_label_type(); } + +IntegerType *Type::get_int1_type(Module *m) { return m->get_int1_type(); } + +IntegerType *Type::get_int32_type(Module *m) { return m->get_int32_type(); } + +PointerType *Type::get_pointer_type(Type *contained) { return PointerType::get(contained); } + +ArrayType *Type::get_array_type(Type *contained, unsigned num_elements) { + return ArrayType::get(contained, num_elements); +} + +PointerType *Type::get_int32_ptr_type(Module *m) { return m->get_int32_ptr_type(); } + +FloatType *Type::get_float_type(Module *m) { return m->get_float_type(); } + +PointerType *Type::get_float_ptr_type(Module *m) { return m->get_float_ptr_type(); } + +Type *Type::get_pointer_element_type() { + if (this->is_pointer_type()) + return static_cast(this)->get_element_type(); + else + return nullptr; +} + +Type *Type::get_array_element_type() { + if (this->is_array_type()) + return static_cast(this)->get_element_type(); + else + return nullptr; +} + +int Type::get_size() { + if (this->is_integer_type()) { + auto bits = static_cast(this)->get_num_bits() / 8; + return bits > 0 ? bits : 1; + } + if (this->is_array_type()) { + auto element_size = static_cast(this)->get_element_type()->get_size(); + auto num_elements = static_cast(this)->get_num_of_elements(); + return element_size * num_elements; + } + if (this->is_pointer_type()) { + if (this->get_pointer_element_type()->is_array_type()) { + return this->get_pointer_element_type()->get_size(); + } else { + return 4; + } + } + if (this->is_float_type()) { + return 4; + } + return 0; +} + +std::string Type::print() { + std::string type_ir; + switch (this->get_type_id()) { + case VoidTyID: type_ir += "void"; break; + case LabelTyID: type_ir += "label"; break; + case IntegerTyID: + type_ir += "i"; + type_ir += std::to_string(static_cast(this)->get_num_bits()); + break; + case FunctionTyID: + type_ir += static_cast(this)->get_return_type()->print(); + type_ir += " ("; + for (int i = 0; i < static_cast(this)->get_num_of_args(); i++) { + if (i) + type_ir += ", "; + type_ir += static_cast(this)->get_param_type(i)->print(); + } + type_ir += ")"; + break; + case PointerTyID: + type_ir += this->get_pointer_element_type()->print(); + type_ir += "*"; + break; + case ArrayTyID: + type_ir += "["; + type_ir += std::to_string(static_cast(this)->get_num_of_elements()); + type_ir += " x "; + type_ir += static_cast(this)->get_element_type()->print(); + type_ir += "]"; + break; + case FloatTyID: type_ir += "float"; break; + default: break; + } + return type_ir; +} + +IntegerType::IntegerType(unsigned num_bits, Module *m) : Type(Type::IntegerTyID, m), num_bits_(num_bits) {} + +IntegerType *IntegerType::get(unsigned num_bits, Module *m) { + if (num_bits == 1) { + return m->get_int1_type(); + } else if (num_bits == 32) { + return m->get_int32_type(); + } else { + assert("IntegerType::get has error num_bits"); + } +} + +unsigned IntegerType::get_num_bits() { return num_bits_; } + +FunctionType::FunctionType(Type *result, std::vector params) : Type(Type::FunctionTyID, nullptr) { + assert(is_valid_return_type(result) && "Invalid return type for function!"); + result_ = result; + + for (auto p : params) { + assert(is_valid_argument_type(p) && "Not a valid type for function argument!"); + args_.push_back(p); + } +} + +bool FunctionType::is_valid_return_type(Type *ty) { + return ty->is_integer_type() || ty->is_void_type() || ty->is_float_type(); +} + +bool FunctionType::is_valid_argument_type(Type *ty) { + return ty->is_integer_type() || ty->is_pointer_type() || ty->is_float_type(); +} + +FunctionType *FunctionType::get(Type *result, std::vector params) { + return result->get_module()->get_function_type(result, params); +} + +unsigned FunctionType::get_num_of_args() const { return args_.size(); } + +Type *FunctionType::get_param_type(unsigned i) const { return args_[i]; } + +Type *FunctionType::get_return_type() const { return result_; } + +ArrayType::ArrayType(Type *contained, unsigned num_elements) + : Type(Type::ArrayTyID, contained->get_module()), num_elements_(num_elements) { + assert(is_valid_element_type(contained) && "Not a valid type for array element!"); + contained_ = contained; +} + +bool ArrayType::is_valid_element_type(Type *ty) { + return ty->is_integer_type() || ty->is_array_type() || ty->is_float_type(); +} + +ArrayType *ArrayType::get(Type *contained, unsigned num_elements) { + return contained->get_module()->get_array_type(contained, num_elements); +} + +PointerType::PointerType(Type *contained) : Type(Type::PointerTyID, contained->get_module()), contained_(contained) {} + +PointerType *PointerType::get(Type *contained) { return contained->get_module()->get_pointer_type(contained); } + +FloatType::FloatType(Module *m) : Type(Type::FloatTyID, m) {} + +FloatType *FloatType::get(Module *m) { return m->get_float_type(); } diff --git a/src/lightir/User.cpp b/src/lightir/User.cpp new file mode 100644 index 0000000..92bb95e --- /dev/null +++ b/src/lightir/User.cpp @@ -0,0 +1,43 @@ +#include "User.h" + +#include + +User::User(Type *ty, const std::string &name, unsigned num_ops) : Value(ty, name), num_ops_(num_ops) { + // if (num_ops_ > 0) + // operands_.reset(new std::list()); + operands_.resize(num_ops_, nullptr); +} + +std::vector &User::get_operands() { return operands_; } + +Value *User::get_operand(unsigned i) const { return operands_[i]; } + +void User::set_operand(unsigned i, Value *v) { + assert(i < num_ops_ && "set_operand out of index"); + // assert(operands_[i] == nullptr && "ith operand is not null"); + operands_[i] = v; + v->add_use(this, i); +} + +void User::add_operand(Value *v) { + operands_.push_back(v); + v->add_use(this, num_ops_); + num_ops_++; +} + +unsigned User::get_num_operand() const { return num_ops_; } + +void User::remove_use_of_ops() { + for (auto op : operands_) { + op->remove_use(this); + } +} + +void User::remove_operands(int index1, int index2) { + for (int i = index1; i <= index2; i++) { + operands_[i]->remove_use(this); + } + operands_.erase(operands_.begin() + index1, operands_.begin() + index2 + 1); + // std::cout< + +Value::Value(Type *ty, const std::string &name) : type_(ty), name_(name) {} + +void Value::add_use(Value *val, unsigned arg_no) { use_list_.push_back(Use(val, arg_no)); } + +std::string Value::get_name() const { return name_; } + +void Value::replace_all_use_with(Value *new_val) { + for (auto use : use_list_) { + auto val = dynamic_cast(use.val_); + assert(val && "new_val is not a user"); + val->set_operand(use.arg_no_, new_val); + } +} + +void Value::remove_use(Value *val) { + auto is_val = [val](const Use &use) { return use.val_ == val; }; + use_list_.remove_if(is_val); +} diff --git a/tests/2-ir-gen-warmup/CMakeLists.txt b/tests/2-ir-gen-warmup/CMakeLists.txt new file mode 100644 index 0000000..e70dd26 --- /dev/null +++ b/tests/2-ir-gen-warmup/CMakeLists.txt @@ -0,0 +1,42 @@ +add_subdirectory(calculator) +add_executable( + gcd_array_generator + ta_gcd/gcd_array_generator.cpp +) +target_link_libraries( + gcd_array_generator + IR_lib +) + +# add_executable( +# stu_assign_generator +# stu_cpp/assign_generator.cpp +# ) +# target_link_libraries( +# stu_assign_generator +# IR_lib +# ) +# add_executable( +# stu_fun_generator +# stu_cpp/fun_generator.cpp +# ) +# target_link_libraries( +# stu_fun_generator +# IR_lib +# ) +# add_executable( +# stu_if_generator +# stu_cpp/if_generator.cpp +# ) +# target_link_libraries( +# stu_if_generator +# IR_lib +# ) +# add_executable( +# stu_while_generator +# stu_cpp/while_generator.cpp +# ) +# target_link_libraries( +# stu_while_generator +# IR_lib +# ) diff --git a/tests/2-ir-gen-warmup/c_cases/assign.c b/tests/2-ir-gen-warmup/c_cases/assign.c new file mode 100644 index 0000000..ba300f8 --- /dev/null +++ b/tests/2-ir-gen-warmup/c_cases/assign.c @@ -0,0 +1,6 @@ +int main(){ + int a[10]; + a[0] = 10; + a[1] = a[0] * 2; + return a[1]; +} diff --git a/tests/2-ir-gen-warmup/c_cases/fun.c b/tests/2-ir-gen-warmup/c_cases/fun.c new file mode 100644 index 0000000..a0f082b --- /dev/null +++ b/tests/2-ir-gen-warmup/c_cases/fun.c @@ -0,0 +1,6 @@ +int callee(int a){ + return 2 * a; +} +int main(){ + return callee(110); +} diff --git a/tests/2-ir-gen-warmup/c_cases/if.c b/tests/2-ir-gen-warmup/c_cases/if.c new file mode 100644 index 0000000..317ac49 --- /dev/null +++ b/tests/2-ir-gen-warmup/c_cases/if.c @@ -0,0 +1,6 @@ +int main(){ + float a = 5.555; + if(a > 1) + return 233; + return 0; +} diff --git a/tests/2-ir-gen-warmup/c_cases/while.c b/tests/2-ir-gen-warmup/c_cases/while.c new file mode 100644 index 0000000..6ba84de --- /dev/null +++ b/tests/2-ir-gen-warmup/c_cases/while.c @@ -0,0 +1,11 @@ +int main(){ + int a; + int i; + a = 10; + i = 0; + while(i < 10){ + i = i + 1; + a = a + i; + } + return a; +} diff --git a/tests/2-ir-gen-warmup/calculator/CMakeLists.txt b/tests/2-ir-gen-warmup/calculator/CMakeLists.txt new file mode 100644 index 0000000..2dc3e44 --- /dev/null +++ b/tests/2-ir-gen-warmup/calculator/CMakeLists.txt @@ -0,0 +1,21 @@ +flex_target(calc_lex calculator.l ${CMAKE_CURRENT_BINARY_DIR}/calc_lex.c) +bison_target(calc_syntax calculator.y + ${CMAKE_CURRENT_BINARY_DIR}/calc_syntax.c + DEFINES_FILE ${PROJECT_BINARY_DIR}/calculator.h) +add_flex_bison_dependency(calc_lex calc_syntax) +add_library(calc_syntax STATIC + ${BISON_calc_syntax_OUTPUTS} + ${FLEX_calc_lex_OUTPUTS} +) +add_executable( + calc + calc.cpp + calc_ast.cpp + calc_builder.cpp +) +target_link_libraries( + calc + IR_lib + calc_syntax + common +) diff --git a/tests/2-ir-gen-warmup/calculator/calc.cpp b/tests/2-ir-gen-warmup/calculator/calc.cpp new file mode 100644 index 0000000..26d01a8 --- /dev/null +++ b/tests/2-ir-gen-warmup/calculator/calc.cpp @@ -0,0 +1,48 @@ +extern "C" { + #include "syntax_tree.h" + extern syntax_tree *parse(const char*); +} +#include +#include +#include "calc_ast.hpp" +#include "calc_builder.hpp" +using namespace std::literals::string_literals; + +int main(int argc, char *argv[]) +{ + syntax_tree *tree = NULL; + const char *input = NULL; + + if (argc >= 3) { + printf("usage: %s\n", argv[0]); + printf("usage: %s \n", argv[0]); + return 1; + } + + if (argc == 2) { + input = argv[1]; + } else { + printf("Input an arithmatic expression (press Ctrl+D in a new line after you finish the expression):\n"); + } + + tree = parse(input); + CalcAST ast(tree); + CalcBuilder builder; + auto module = builder.build(ast); + auto IR = module->print(); + + std::ofstream output_stream; + auto output_file = "result.ll"; + output_stream.open(output_file, std::ios::out); + output_stream << "; ModuleID = 'calculator'\n"; + output_stream << IR; + output_stream.close(); + auto command_string = "clang -O0 -w "s + "result.ll -o result -L. -lcminus_io"; + auto ret = std::system(command_string.c_str()); + if (ret) { + printf("something went wrong!\n"); + } else { + printf("result and result.ll have been generated.\n"); + } + return ret; +} diff --git a/tests/2-ir-gen-warmup/calculator/calc_ast.cpp b/tests/2-ir-gen-warmup/calculator/calc_ast.cpp new file mode 100644 index 0000000..3d73e4a --- /dev/null +++ b/tests/2-ir-gen-warmup/calculator/calc_ast.cpp @@ -0,0 +1,125 @@ +#include "calc_ast.hpp" +#include +#include +#include +#define _AST_NODE_ERROR_ \ + std::cerr << "Abort due to node cast error."\ + "Contact with TAs to solve your problem."\ + << std::endl;\ + std::abort(); +#define _STR_EQ(a, b) (strcmp((a), (b)) == 0) + +void CalcAST::run_visitor(CalcASTVisitor &visitor) { + root->accept(visitor); +} + +CalcAST::CalcAST(syntax_tree* s) { + if (s == nullptr) { + std::cerr << "empty input tree!" << std::endl; + std::abort(); + } + auto node = transform_node_iter(s->root); + del_syntax_tree(s); + root = std::shared_ptr( + static_cast(node)); +} + +CalcASTNode * +CalcAST::transform_node_iter(syntax_tree_node *n) { + if (_STR_EQ(n->name, "input")) { + auto node = new CalcASTInput(); + auto expr_node = + static_cast( + transform_node_iter(n->children[0])); + node->expression = std::shared_ptr(expr_node); + return node; + } else if (_STR_EQ(n->name, "expression")) { + auto node = new CalcASTExpression(); + if (n->children_num == 3) { + auto add_expr_node = + static_cast( + transform_node_iter(n->children[0])); + node->expression = + std::shared_ptr(add_expr_node); + + auto op_name = n->children[1]->children[0]->name; + if (_STR_EQ(op_name, "+")) + node->op = OP_PLUS; + else if (_STR_EQ(op_name, "-")) + node->op = OP_MINUS; + + auto term_node = + static_cast( + transform_node_iter(n->children[2])); + node->term = std::shared_ptr(term_node); + } else { + auto term_node = + static_cast( + transform_node_iter(n->children[0])); + node->term = std::shared_ptr(term_node); + } + return node; + } else if (_STR_EQ(n->name, "term")) { + auto node = new CalcASTTerm(); + if (n->children_num == 3) { + auto term_node = + static_cast( + transform_node_iter(n->children[0])); + node->term = + std::shared_ptr(term_node); + + auto op_name = n->children[1]->children[0]->name; + if (_STR_EQ(op_name, "*")) + node->op = OP_MUL; + else if (_STR_EQ(op_name, "/")) + node->op = OP_DIV; + + auto factor_node = + static_cast( + transform_node_iter(n->children[2])); + node->factor = std::shared_ptr(factor_node); + } else { + auto factor_node = + static_cast( + transform_node_iter(n->children[0])); + node->factor = std::shared_ptr(factor_node); + } + return node; + } else if (_STR_EQ(n->name, "factor")) { + if (n->children_num == 3) { + return transform_node_iter(n->children[1]); + } else { + auto num_node = new CalcASTNum(); + num_node->val = std::stoi(n->children[0]->children[0]->name); + return num_node; + } + } else { + std::cerr << "[calc_ast]: transform failure!" << std::endl; + std::abort(); + } +} + + +void CalcASTNum::accept(CalcASTVisitor &visitor) { visitor.visit(*this); } +void CalcASTTerm::accept(CalcASTVisitor &visitor) { visitor.visit(*this); } +void CalcASTExpression::accept(CalcASTVisitor &visitor) { visitor.visit(*this); } + +void CalcASTInput::accept(CalcASTVisitor &visitor) { expression->accept(visitor); } + +void CalcASTFactor::accept(CalcASTVisitor &visitor) { + auto expr = + dynamic_cast(this); + if (expr) { + expr->accept(visitor); + return; + } + + auto num = + dynamic_cast(this); + if (num) { + num->accept(visitor); + return; + } + + _AST_NODE_ERROR_ +} diff --git a/tests/2-ir-gen-warmup/calculator/calc_ast.hpp b/tests/2-ir-gen-warmup/calculator/calc_ast.hpp new file mode 100644 index 0000000..fd3d2f5 --- /dev/null +++ b/tests/2-ir-gen-warmup/calculator/calc_ast.hpp @@ -0,0 +1,90 @@ +#ifndef _CALC_AST_HPP_ +#define _CALC_AST_HPP_ +extern "C" { + #include "syntax_tree.h" + extern syntax_tree *parse(const char *input); +} +#include +#include + +enum AddOp { + // + + OP_PLUS, + // - + OP_MINUS +}; + +enum MulOp { + // * + OP_MUL, + // / + OP_DIV +}; + +class CalcAST; + +struct CalcASTNode; +struct CalcASTInput; +struct CalcASTExpression; +struct CalcASTNum; +struct CalcASTTerm; +struct CalcASTFactor; + +class CalcASTVisitor; + +class CalcAST { +public: + CalcAST() = delete; + CalcAST(syntax_tree *); + CalcAST(CalcAST &&tree) { + root = tree.root; + tree.root = nullptr; + }; + CalcASTInput* get_root() { return root.get(); } + void run_visitor(CalcASTVisitor& visitor); +private: + CalcASTNode* transform_node_iter(syntax_tree_node *); + std::shared_ptr root = nullptr; +}; + +struct CalcASTNode { + virtual void accept(CalcASTVisitor &) = 0; +}; + +struct CalcASTInput: CalcASTNode { + virtual void accept(CalcASTVisitor &) override final; + std::shared_ptr expression; +}; + +struct CalcASTFactor: CalcASTNode { + virtual void accept(CalcASTVisitor &) override; +}; + +struct CalcASTNum: CalcASTFactor { + virtual void accept(CalcASTVisitor &) override final; + int val; +}; + +struct CalcASTExpression: CalcASTFactor { + virtual void accept(CalcASTVisitor &) override final; + std::shared_ptr expression; + AddOp op; + std::shared_ptr term; +}; + +struct CalcASTTerm : CalcASTNode { + virtual void accept(CalcASTVisitor &) override final; + std::shared_ptr term; + MulOp op; + std::shared_ptr factor; +}; + + +class CalcASTVisitor { +public: + virtual void visit(CalcASTInput &) = 0; + virtual void visit(CalcASTNum &) = 0; + virtual void visit(CalcASTExpression &) = 0; + virtual void visit(CalcASTTerm &) = 0; +}; +#endif diff --git a/tests/2-ir-gen-warmup/calculator/calc_builder.cpp b/tests/2-ir-gen-warmup/calculator/calc_builder.cpp new file mode 100644 index 0000000..9b5c36b --- /dev/null +++ b/tests/2-ir-gen-warmup/calculator/calc_builder.cpp @@ -0,0 +1,69 @@ +#include "calc_builder.hpp" +std::unique_ptr +CalcBuilder::build(CalcAST &ast) { + module = std::unique_ptr(new Module("Cminus code")); + builder = new IRBuilder(nullptr, module.get()); + auto TyVoid = Type::get_void_type(module.get()); + TyInt32 = Type::get_int32_type(module.get()); + + std::vector output_params; + output_params.push_back(TyInt32); + auto output_type = FunctionType::get(TyVoid, output_params); + auto output_fun = + Function::create( + output_type, + "output", + module.get()); + auto main = Function::create(FunctionType::get(TyInt32, {}), + "main", module.get()); + auto bb = BasicBlock::create(module.get(), "entry", main); + builder->set_insert_point(bb); + ast.run_visitor(*this); + builder->create_call(output_fun, {val}); + builder->create_ret(ConstantInt::get(0, module.get())); + return std::move(module); +} +void CalcBuilder::visit(CalcASTInput &node) { + node.expression->accept(*this); +} +void CalcBuilder::visit(CalcASTExpression &node) { + if (node.expression == nullptr) { + node.term->accept(*this); + } else { + node.expression->accept(*this); + auto l_val = val; + node.term->accept(*this); + auto r_val = val; + switch (node.op) { + case OP_PLUS: + val = builder->create_iadd(l_val, r_val); + break; + case OP_MINUS: + val = builder->create_isub(l_val, r_val); + break; + } + } +} + +void CalcBuilder::visit(CalcASTTerm &node) { + if (node.term == nullptr) { + node.factor->accept(*this); + } else { + node.term->accept(*this); + auto l_val = val; + node.factor->accept(*this); + auto r_val = val; + switch (node.op) { + case OP_MUL: + val = builder->create_imul(l_val, r_val); + break; + case OP_DIV: + val = builder->create_isdiv(l_val, r_val); + break; + } + } +} + +void CalcBuilder::visit(CalcASTNum &node) { + val = ConstantInt::get(node.val, module.get()); +} diff --git a/tests/2-ir-gen-warmup/calculator/calc_builder.hpp b/tests/2-ir-gen-warmup/calculator/calc_builder.hpp new file mode 100644 index 0000000..b4e2f5e --- /dev/null +++ b/tests/2-ir-gen-warmup/calculator/calc_builder.hpp @@ -0,0 +1,24 @@ +#ifndef _CALC_VISITOR_HPP_ +#define _CALC_VISITOR_HPP_ +#include "BasicBlock.h" +#include "Constant.h" +#include "Function.h" +#include "IRBuilder.h" +#include "Module.h" +#include "Type.h" +#include "calc_ast.hpp" +class CalcBuilder: public CalcASTVisitor { +public: + std::unique_ptr build(CalcAST &ast); +private: + virtual void visit(CalcASTInput &) override final; + virtual void visit(CalcASTNum &) override final; + virtual void visit(CalcASTExpression &) override final; + virtual void visit(CalcASTTerm &) override final; + + IRBuilder *builder; + Value *val; + Type *TyInt32; + std::unique_ptr module; +}; +#endif diff --git a/tests/2-ir-gen-warmup/calculator/calculator.l b/tests/2-ir-gen-warmup/calculator/calculator.l new file mode 100644 index 0000000..f5bc734 --- /dev/null +++ b/tests/2-ir-gen-warmup/calculator/calculator.l @@ -0,0 +1,36 @@ +%option noyywrap +%{ +/*****************声明和选项设置 begin*****************/ +#include +#include + +#include "syntax_tree.h" +#include "calculator.h" + +int lines; +int pos_start; +int pos_end; + +void pass_node(char *text){ + yylval.node = new_syntax_tree_node(text); +} + +/*****************声明和选项设置 end*****************/ + +%} + +%x COMMENT + +%% + +\+ {pos_start = pos_end; pos_end += 1; pass_node(yytext); return ADD;} +\- {pos_start = pos_end; pos_end += 1; pass_node(yytext); return SUB;} +\* {pos_start = pos_end; pos_end += 1; pass_node(yytext); return MUL;} +\/ {pos_start = pos_end; pos_end += 1; pass_node(yytext); return DIV;} +\( {pos_start = pos_end; pos_end += 1; pass_node(yytext); return LPARENTHESE;} +\) {pos_start = pos_end; pos_end += 1; pass_node(yytext); return RPARENTHESE;} +[0-9]+ { pos_start = pos_end; pos_end += strlen(yytext); pass_node(yytext); return NUM; } + +\n {lines++; pos_start = 1; pos_end = 1;} +[ \t] {pos_start = pos_end; pos_end += 1;} +%% diff --git a/tests/2-ir-gen-warmup/calculator/calculator.y b/tests/2-ir-gen-warmup/calculator/calculator.y new file mode 100644 index 0000000..0590f06 --- /dev/null +++ b/tests/2-ir-gen-warmup/calculator/calculator.y @@ -0,0 +1,110 @@ +%{ +#include +#include +#include +#include + +#include "syntax_tree.h" + +// external functions from lex +extern int yylex(); +extern int yyparse(); +extern int yyrestart(); +extern FILE * yyin; + +// external variables from lexical_analyzer module +extern int lines; +extern char * yytext; +extern int pos_end; +extern int pos_start; + +// Global syntax tree +syntax_tree *gt; + +// Error reporting +void yyerror(const char *s); +syntax_tree_node *node(const char *node_name, int children_num, ...); +%} + +%union { + struct _syntax_tree_node * node; + char * name; +} + +%token ADD +%token SUB +%token MUL +%token DIV +%token NUM +%token LPARENTHESE +%token RPARENTHESE +%type input expression addop term mulop factor num + +%start input + +%% +input : expression {$$ = node( "input", 1, $1); gt->root = $$;} + ; +expression : expression addop term {$$ = node( "expression", 3, $1, $2, $3);} + | term {$$ = node( "expression", 1, $1);} + ; + +addop : ADD {$$ = node( "addop", 1, $1);} + | SUB {$$ = node( "addop", 1, $1);} + ; + +term : term mulop factor {$$ = node( "term", 3, $1, $2, $3);} + | factor {$$ = node( "term", 1, $1);} + ; + +mulop : MUL {$$ = node( "mulop", 1, $1);} + | DIV {$$ = node( "mulop", 1, $1);} + ; + +factor : LPARENTHESE expression RPARENTHESE {$$ = node( "factor", 3, $1, $2, $3);} + | num {$$ = node( "factor", 1, $1);} + ; + +num : NUM {$$ = node( "num", 1, $1);} +%% + +void yyerror(const char * s) { + fprintf(stderr, "error at line %d column %d: %s\n", lines, pos_start, s); +} + +syntax_tree *parse(const char *input_path) +{ + if (input_path != NULL) { + if (!(yyin = fopen(input_path, "r"))) { + fprintf(stderr, "[ERR] Open input file %s failed.\n", input_path); + exit(1); + } + } else { + yyin = stdin; + } + + lines = pos_start = pos_end = 1; + gt = new_syntax_tree(); + yyrestart(yyin); + yyparse(); + return gt; +} + +syntax_tree_node *node(const char *name, int children_num, ...) +{ + syntax_tree_node *p = new_syntax_tree_node(name); + syntax_tree_node *child; + if (children_num == 0) { + child = new_syntax_tree_node("epsilon"); + syntax_tree_add_child(p, child); + } else { + va_list ap; + va_start(ap, children_num); + for (int i = 0; i < children_num; ++i) { + child = va_arg(ap, syntax_tree_node *); + syntax_tree_add_child(p, child); + } + va_end(ap); + } + return p; +} diff --git a/tests/2-ir-gen-warmup/stu_cpp/assign_generator.cpp b/tests/2-ir-gen-warmup/stu_cpp/assign_generator.cpp new file mode 100644 index 0000000..e69de29 diff --git a/tests/2-ir-gen-warmup/stu_cpp/fun_generator.cpp b/tests/2-ir-gen-warmup/stu_cpp/fun_generator.cpp new file mode 100644 index 0000000..e69de29 diff --git a/tests/2-ir-gen-warmup/stu_cpp/if_generator.cpp b/tests/2-ir-gen-warmup/stu_cpp/if_generator.cpp new file mode 100644 index 0000000..e69de29 diff --git a/tests/2-ir-gen-warmup/stu_cpp/while_generator.cpp b/tests/2-ir-gen-warmup/stu_cpp/while_generator.cpp new file mode 100644 index 0000000..e69de29 diff --git a/tests/2-ir-gen-warmup/stu_ll/assign_hand.ll b/tests/2-ir-gen-warmup/stu_ll/assign_hand.ll new file mode 100644 index 0000000..e69de29 diff --git a/tests/2-ir-gen-warmup/stu_ll/fun_hand.ll b/tests/2-ir-gen-warmup/stu_ll/fun_hand.ll new file mode 100644 index 0000000..e69de29 diff --git a/tests/2-ir-gen-warmup/stu_ll/if_hand.ll b/tests/2-ir-gen-warmup/stu_ll/if_hand.ll new file mode 100644 index 0000000..e69de29 diff --git a/tests/2-ir-gen-warmup/stu_ll/while_hand.ll b/tests/2-ir-gen-warmup/stu_ll/while_hand.ll new file mode 100644 index 0000000..e69de29 diff --git a/tests/2-ir-gen-warmup/ta_gcd/gcd_array.c b/tests/2-ir-gen-warmup/ta_gcd/gcd_array.c new file mode 100644 index 0000000..f747e81 --- /dev/null +++ b/tests/2-ir-gen-warmup/ta_gcd/gcd_array.c @@ -0,0 +1,27 @@ +int x[1]; +int y[1]; + +int gcd (int u, int v) { + if (v == 0) return u; + else return gcd(v, u - u / v * v); +} + +int funArray (int u[], int v[]) { + int a; + int b; + int temp; + a = u[0]; + b = v[0]; + if (a < b) { + temp = a; + a = b; + b = temp; + } + return gcd(a, b); +} + +int main(void) { + x[0] = 90; + y[0] = 18; + return funArray(x, y); +} \ No newline at end of file diff --git a/tests/2-ir-gen-warmup/ta_gcd/gcd_array_generator.cpp b/tests/2-ir-gen-warmup/ta_gcd/gcd_array_generator.cpp new file mode 100644 index 0000000..f8f63a8 --- /dev/null +++ b/tests/2-ir-gen-warmup/ta_gcd/gcd_array_generator.cpp @@ -0,0 +1,170 @@ +#include "BasicBlock.h" +#include "Constant.h" +#include "Function.h" +#include "IRBuilder.h" +#include "Module.h" +#include "Type.h" + +#include +#include + +#ifdef DEBUG // 用于调试信息,大家可以在编译过程中通过" -DDEBUG"来开启这一选项 +#define DEBUG_OUTPUT std::cout << __LINE__ << std::endl; // 输出行号的简单示例 +#else +#define DEBUG_OUTPUT +#endif + +#define CONST_INT(num) ConstantInt::get(num, module) + +#define CONST_FP(num) ConstantFP::get(num, module) // 得到常数值的表示,方便后面多次用到 + +int main() { + auto module = new Module("Cminus code"); // module name是什么无关紧要 + auto builder = new IRBuilder(nullptr, module); + Type *Int32Type = Type::get_int32_type(module); + + // 全局数组,x,y + auto *arrayType = ArrayType::get(Int32Type, 1); + auto initializer = ConstantZero::get(Int32Type, module); + auto x = GlobalVariable::create("x", module, arrayType, false, initializer);// 参数解释: 名字name,所属module,全局变量类型type, + auto y = GlobalVariable::create("y", module, arrayType, false, initializer);// 是否是常量定义(cminus中没有常量概念,应全都是false),初始化常量(ConstantZero类) + + // gcd函数 + // 函数参数类型的vector + std::vector Ints(2, Int32Type); + + //通过返回值类型与参数类型列表得到函数类型 + auto gcdFunTy = FunctionType::get(Int32Type, Ints); + + // 由函数类型得到函数 + auto gcdFun = Function::create(gcdFunTy, + "gcd", module); + + // BB的名字在生成中无所谓,但是可以方便阅读 + auto bb = BasicBlock::create(module, "entry", gcdFun); + + builder->set_insert_point(bb); // 一个BB的开始,将当前插入指令点的位置设在bb + + auto retAlloca = builder->create_alloca(Int32Type); // 在内存中分配返回值的位置 + auto uAlloca = builder->create_alloca(Int32Type); // 在内存中分配参数u的位置 + auto vAlloca = builder->create_alloca(Int32Type); // 在内存中分配参数v的位置 + + std::vector args; // 获取gcd函数的形参,通过Function中的iterator + for (auto arg = gcdFun->arg_begin(); arg != gcdFun->arg_end(); arg++) { + args.push_back(*arg); // * 号运算符是从迭代器中取出迭代器当前指向的元素 + } + + builder->create_store(args[0], uAlloca); // 将参数u store下来 + builder->create_store(args[1], vAlloca); // 将参数v store下来 + + auto vLoad = builder->create_load(vAlloca); // 将参数v load上来 + auto icmp = builder->create_icmp_eq(vLoad, CONST_INT(0)); // v和0的比较,注意ICMPEQ + + auto trueBB = BasicBlock::create(module, "trueBB", gcdFun); // true分支 + auto falseBB = BasicBlock::create(module, "falseBB", gcdFun); // false分支 + auto retBB = BasicBlock::create( + module, "", gcdFun); // return分支,提前create,以便true分支可以br + + auto br = builder->create_cond_br(icmp, trueBB, falseBB); // 条件BR + DEBUG_OUTPUT // 调试的时候故意留下来的,以醒目地提醒你这个调试用的宏定义方法 + builder->set_insert_point(trueBB); // if true; 分支的开始需要SetInsertPoint设置 + auto uLoad = builder->create_load(uAlloca); + builder->create_store(uLoad, retAlloca); + builder->create_br(retBB); // br retBB + + builder->set_insert_point(falseBB); // if false + uLoad = builder->create_load(uAlloca); + vLoad = builder->create_load(vAlloca); + auto div = builder->create_isdiv(uLoad, vLoad); // SDIV - div with S flag + auto mul = builder->create_imul(div, vLoad); // MUL - mul + auto sub = builder->create_isub(uLoad, mul); // the same + auto call = builder->create_call(gcdFun, {vLoad, sub}); // 创建call指令 + // {vLoad, sub} - 参数array + builder->create_store(call, retAlloca); + builder->create_br(retBB); // br retBB + + builder->set_insert_point(retBB); // ret分支 + auto retLoad = builder->create_load(retAlloca); + builder->create_ret(retLoad); + + // funArray函数 + auto Int32PtrType = Type::get_int32_ptr_type(module); // 单个参数的类型,指针 + std::vector IntPtrs(2, Int32PtrType); // 参数列表类型 + auto funArrayFunType = FunctionType::get(Int32Type, IntPtrs); // 函数类型 + auto funArrayFun = Function::create(funArrayFunType, "funArray", module); + bb = BasicBlock::create(module, "entry", funArrayFun); + builder->set_insert_point(bb); + auto upAlloca = builder->create_alloca(Int32PtrType); // u的存放 + auto vpAlloca = builder->create_alloca(Int32PtrType); // v的存放 + auto aAlloca = builder->create_alloca(Int32Type); // a的存放 + auto bAlloca = builder->create_alloca(Int32Type); // b的存放 + auto tempAlloca = builder->create_alloca(Int32Type); // temp的存放 + + std::vector args1; //获取funArrayFun函数的形参,通过Function中的iterator + for (auto arg = funArrayFun->arg_begin(); arg != funArrayFun->arg_end(); arg++) { + args1.push_back(*arg); // * 号运算符是从迭代器中取出迭代器当前指向的元素 + } + builder->create_store(args1[0], upAlloca); // 将参数u store下来 + builder->create_store(args1[1], vpAlloca); // 将参数v store下来 + + auto u0pLoad = builder->create_load(upAlloca); // 读取u + auto u0GEP = builder->create_gep(u0pLoad, {CONST_INT(0)}); // GEP: 获取u[0]地址 + auto u0Load = builder->create_load(u0GEP); // 从u[0]地址 读取u[0] + builder->create_store(u0Load, aAlloca); // 将u[0] 写入 a + auto v0pLoad = builder->create_load(vpAlloca); // 同上 + auto v0GEP = builder->create_gep(v0pLoad, {CONST_INT(0)}); + auto v0Load = builder->create_load(v0GEP); + builder->create_store(v0Load, bAlloca); + + auto aLoad = builder->create_load(aAlloca); + auto bLoad = builder->create_load(bAlloca); + icmp = builder->create_icmp_lt(aLoad, bLoad); + trueBB = BasicBlock::create(module, "trueBB", funArrayFun); + falseBB = BasicBlock::create(module, "falseBB", funArrayFun); + builder->create_cond_br(icmp, trueBB, falseBB); + + builder->set_insert_point(trueBB); + builder->create_store(aLoad, tempAlloca); + builder->create_store(bLoad, aAlloca); + auto tempLoad = builder->create_load(tempAlloca); + builder->create_store(tempLoad, bAlloca); + builder->create_br(falseBB); // 注意在下一个BB之前要Br一下 + + builder->set_insert_point(falseBB); + aLoad = builder->create_load(aAlloca); + bLoad = builder->create_load(bAlloca); + call = builder->create_call(gcdFun, {aLoad, bLoad}); + builder->create_ret(call); + + + // main函数 + auto mainFun = Function::create(FunctionType::get(Int32Type, {}), + "main", module); + bb = BasicBlock::create(module, "entry", mainFun); + // BasicBlock的名字在生成中无所谓,但是可以方便阅读 + builder->set_insert_point(bb); + + retAlloca = builder->create_alloca(Int32Type); + builder->create_store(CONST_INT(0), retAlloca); // 默认 ret 0 + + auto x0GEP = builder->create_gep(x, {CONST_INT(0), CONST_INT(0)}); // GEP: 这里为什么是{0, 0}呢? (实验报告相关) + builder->create_store(CONST_INT(90), x0GEP); + auto y0GEP = builder->create_gep(y, {CONST_INT(0), CONST_INT(0)}); // GEP: 这里为什么是{0, 0}呢? (实验报告相关) + builder->create_store(CONST_INT(18), y0GEP); + + x0GEP = builder->create_gep(x, {CONST_INT(0), CONST_INT(0)}); + y0GEP = builder->create_gep(y, {CONST_INT(0), CONST_INT(0)}); + call = builder->create_call(funArrayFun, {x0GEP, y0GEP}); // 为什么这里传的是{x0GEP, y0GEP}呢? + + builder->create_ret(call); + // 尽管已经有很多注释,但可能还是会遇到很多bug + // 所以强烈建议配置AutoComplete,效率会大大提高! + // 如果猜不到某个IR指令对应的C++的函数,建议把指令翻译成英语然后在method列表中搜索一下。 + // 最后,这个例子只涉及到了一些基本的指令生成, + // 对于额外的指令,包括数组,在之后的实验中可能需要大家自己搜索一下思考一下, + // 还有涉及到的C++语法,可以及时提问或者向大家提供指导哦! + // 对于这个例子里的代码风格/用法,如果有好的建议也欢迎提出! + std::cout << module->print(); + delete module; + return 0; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f485ab2..f8a17ad 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(parser) +add_subdirectory(2-ir-gen-warmup) add_executable(test_ast test_ast.cpp) add_executable(test_logging test_logging.cpp) target_link_libraries(test_logging common) -- GitLab