You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
6.2 KiB

12 months ago
# PW6 实验报告
学号PB21000179 姓名 吴书让
12 months ago
学号PB21111627 姓名 罗胤玻
## 问题回答
### Task1
**手工代码不具代表性,以下分析针对使用** `clang -S -emit-llvm` **编译得到的IR代码**
> 1-1 请给出while语句对应的LLVM IR的代码布局特点重点解释其中涉及的几个`br`指令的含义(包含各个参数的含义)
**1-1:**
生成代码并对主要部分加入注释如下:
```llvm
@b = dso_local global i32 0, align 4 ; 定义一个全局变量b初始值为0对齐方式为4
@a = dso_local global i32 0, align 4 ; 定义一个全局变量a初始值为0对齐方式为4
; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 { ; 定义一个名为main的函数返回类型为i32
%1 = alloca i32, align 4 ; 在栈上分配一个i32类型的空间对齐方式为4
store i32 0, i32* %1, align 4 ; 将0存储到%1指向的地址对齐方式为4
store i32 0, i32* @b, align 4 ; 将0存储到全局变量b对齐方式为4
store i32 3, i32* @a, align 4 ; 将3存储到全局变量a对齐方式为4
br label %2 ; 无条件跳转到标签2
2: ; 标签2循环条件判断基本块
%3 = load i32, i32* @a, align 4 ; 从全局变量a中加载一个i32类型的值对齐方式为4
%4 = icmp sgt i32 %3, 0 ; 比较%3和0的大小关系结果存储在%4中
br i1 %4, label %5, label %11 ; 根据%4的值进行条件跳转如果为真跳转到标签5否则跳转到标签11
5: ; 标签5循环体基本块
%6 = load i32, i32* @b, align 4 ; 从全局变量b中加载一个i32类型的值对齐方式为4
%7 = load i32, i32* @a, align 4 ; 从全局变量a中加载一个i32类型的值对齐方式为4
%8 = add nsw i32 %6, %7 ; 将%6和%7相加结果存储在%8中
store i32 %8, i32* @b, align 4 ; 将%8存储到全局变量b对齐方式为4
%9 = load i32, i32* @a, align 4 ; 从全局变量a中加载一个i32类型的值对齐方式为4
%10 = sub nsw i32 %9, 1 ; 将%9减1结果存储在%10中
store i32 %10, i32* @a, align 4 ; 将%10存储到全局变量a对齐方式为4
br label %2 ; 无条件跳转到标签2
11: ; 标签11循环结束后基本块
%12 = load i32, i32* @b, align 4 ; 从全局变量b中加载一个i32类型的值对齐方式为4
ret i32 %12 ; 返回%12作为函数的返回值
}
```
注意到如下的while语句对应的LLVM IR的代码布局特点
1. while语句的条件判断部分和循环体部分分别对应一个基本块
2. 循环体部分的最后一条语句为无条件跳转到条件判断部分的基本块,
3. 条件判断部分的最后一条语句为条件跳转到循环体部分的基本块或者跳转到循环体部分之后的基本块。
> 1-2 请简述函数调用语句对应的LLVM IR的代码特点
**1-2**
生成代码并对主要部分加入注释如下:
```llvm
define dso_local i32 @add(i32 %0, i32 %1) #0 {
%3 = alloca i32, align 4 ; 在栈上分配一个i32类型的空间对齐方式为4
%4 = alloca i32, align 4 ; 在栈上分配另一个i32类型的空间对齐方式为4
store i32 %0, i32* %3, align 4 ; 将参数%0的值存储到%3指向的地址中对齐方式为4
store i32 %1, i32* %4, align 4 ; 将参数%1的值存储到%4指向的地址中对齐方式为4
%5 = load i32, i32* %3, align 4 ; 从%3指向的地址中加载一个i32类型的值对齐方式为4
%6 = load i32, i32* %4, align 4 ; 从%4指向的地址中加载一个i32类型的值对齐方式为4
%7 = add nsw i32 %5, %6 ; 将%5和%6相加结果存储在%7中
%8 = sub nsw i32 %7, 1 ; 将%7减1结果存储在%8中
ret i32 %8 ; 返回%8
}
define dso_local i32 @main() #0 { ; 定义一个名为main的函数返回类型为i32
%1 = alloca i32, align 4 ; 在栈上分配一个i32类型的空间对齐方式为4
store i32 3, i32* %1, align 4 ; 将值3存储到%1指向的地址中对齐方式为4
%2 = alloca i32, align 4 ; 在栈上分配另一个i32类型的空间对齐方式为4
store i32 2, i32* %2, align 4 ; 将值2存储到%2指向的地址中对齐方式为4
%3 = load i32, i32* %1, align 4 ; 从%1指向的地址中加载一个i32类型的值对齐方式为4
%4 = load i32, i32* %2, align 4 ; 从%2指向的地址中加载一个i32类型的值对齐方式为4
%5 = call i32 @add(i32 %3, i32 %4) ; 调用add函数传入参数%3和%4并将返回值存储到%5中
%6 = add nsw i32 %5, 1 ; 将%5加1结果存储在%6中
ret i32 %6 ; 从函数中返回%6的值
}
```
注意到如下的函数调用语句对应的LLVM IR的代码特点
1. 函数调用语句对应的LLVM IR的代码中会先将函数的参数存储到栈上分配的空间中然后再调用函数。
2. 函数调用语句对应的LLVM IR的代码中会将函数的返回值存储到一个临时变量中然后再对临时变量进行操作。
3. 调用者使用`call`指令调用被调用者,被调用者使用`ret`指令返回调用者。
------
### Task2
> 2-1 请给出`SysYFIR.md`中提到的两种getelementptr用法的区别, 并解释原因:
**2-1**
- `getelementptr`实际上是一条指针计算语句,不进行任何数据的访问或修改,作用是计算指针并修改计算后指针的类型。
- 第一个参数为要进行计算原始指针的类型;
- 第二个参数是原始指针,往往是一个结构体指针,或数组首地址指针。
- 第二个参数及以后的参数都称为index表示要进行计算的参数作用在第二个参数给出的初始指针如结构体的第几个元素数组的第几个元素。
- **第一种用法**获取数组元素所在地址一个偏移0得到%1指向数组的第一个用第二个偏移0得到首元素向后偏移量返回此地址
- **第二种用法**获取指针地址第一个偏移0计算得到%1所指的变量并返回该变量的地址
-----
### Task3
> 3-1. 在`scope`内单独处理`func`的好处有哪些。
**3-1** 在不同的scope中可以使用相同的变量名而不必担心命名冲突的问题同时也可以提高代码的可读性和可维护性。
12 months ago
## 实验设计
## 实验难点及解决方案
## 实验总结
## 实验反馈
## 组间交流