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.

5.1 KiB

PW6 实验报告

学号PB2100 姓名 吴书让 学号PB21111627 姓名 罗胤玻

问题回答

Task1

手工代码不具代表性,以下分析针对使用 clang -S -emit-llvm 编译得到的IR代码

1-1 请给出while语句对应的LLVM IR的代码布局特点重点解释其中涉及的几个br指令的含义(包含各个参数的含义)

1-1: 生成代码并对主要部分加入注释如下:

@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 生成代码并对主要部分加入注释如下:

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指令返回调用者。

实验设计

实验难点及解决方案

实验总结

实验反馈

组间交流