编译原理lab3-cminus_compiler-LLVM简要熟悉

news2025/1/23 10:26:12

lab3实验报告,我的实验报告图例很少,这次只有两张图,其余的都以复制输出的形式展现出来了,最终提交的代码在最后

  • [[#你的提交|你的提交]]
  • [[#实验设计|实验设计]]
  • [[#提交一:手动编写.ll|提交一:手动编写.ll]]
    • [[#提交一:手动编写.ll#assing(20)|assing(20)]]
    • [[#提交一:手动编写.ll#fun(220)|fun(220)]]
    • [[#提交一:手动编写.ll#if(233)|if(233)]]
    • [[#提交一:手动编写.ll#while(65)|while(65)]]
  • [[#提交2:利用LightIR+cpp编写生成.ll程序|提交2:利用LightIR+cpp编写生成.ll程序]]
    • [[#提交2:利用LightIR+cpp编写生成.ll程序#结果验证|结果验证]]
  • [[#问题1:cpp与.ll的对应|问题1:cpp与.ll的对应]]
  • [[#问题2:Vistor Pattern|问题2:Vistor Pattern]]
  • [[#问题3: getelementptr|问题3: getelementptr]]
  • [[#实验难点|实验难点]]
  • [[#实验反馈|实验反馈]]
  • [[#附录:编写文件|附录:编写文件]]
    • [[#附录:编写文件#assign_hand.ll|assign_hand.ll]]
    • [[#附录:编写文件#full_hand.ll|full_hand.ll]]
    • [[#附录:编写文件#if_hand.ll|if_hand.ll]]
    • [[#附录:编写文件#while_hand.ll|while_hand.ll]]
    • [[#附录:编写文件#assign的cpp文件|assign的cpp文件]]
    • [[#附录:编写文件#full的cpp文件|full的cpp文件]]
    • [[#附录:编写文件#if的cpp文件|if的cpp文件]]
    • [[#附录:编写文件#while的cpp文件|while的cpp文件]]

你的提交

助教提供了四个简单的c程序,分别是tests/lab3/c_cases/目录下的assign.c、fun.c、if.c、while.c.
你需要在tests/lab3/stu_ll/目录中,手工完成自己的assign_hand.ll、fun_hand.ll、if_handf.ll和while_hand.ll,以实现与上述四个C程序相同的逻辑功能.你需要添加必要的注释.ll文件的注释是以";"开头的。  
必要的情况下,你可以参考clang -S -emit-llvm的输出,但是你提交的结果必须避免同此输出一字不差。  
助教会用lli检查你结果的正确性,并用肉眼检查你的注释。
  • 需要完成 ./tests/lab3/stu_ll目录下的4个文件
  • 需要完成 ./tests/lab3/stu_cpp目录下的4个文件
  • 需要在 ./Report/lab3/ 目录下撰写实验报告

实验设计

1、用lli --version检查LLVM版本

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall$ lli --version
LLVM (http://llvm.org/):
  LLVM version 10.0.0
  
  Optimized build.
  Default target: x86_64-pc-linux-gnu
  Host CPU: skylake

10.0.0与10.0.1差别比较小,问题不大
2.利用clang -S -emit-llvm gcd_array.clli gcd_array.ll; echo $?
gcc -o gcd_array gcd_array.c./gcd_array;echo $?
输出:

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/ta_gcd$ clang -S -emit-llvm gcd_array.c
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/ta_gcd$ lli gcd_array.ll; echo $?
18
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/ta_gcd$ gcc -o gcd_array gcd_array.c
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/ta_gcd$ ./gcd_array;echo $?
18

可以看到,输出结果均为18。因此,可以验证 gcd_array.ll 文件正确地对应了 gcd_array.c 文件。
3.回到主目录中的build文件夹,使用命令:

编译与运行在 `${WORKSPACE}/build/` 下执行:
# 如果存在 CMakeCache.txt 要先删除
# rm CMakeCache.txt
cmake ..
make
make install
你可以得到对应`gcd_array_generator.cpp`的可执行文件。

我们可以看到gcd_array_generator文件

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ ls
CMakeCache.txt       gcd_array_generator  libflex.a    Makefile  test_logging
CMakeFiles           lexer                libIR_lib.a  parser    tests
cmake_install.cmake  libcminus_io.a       libOP_lib.a  src
cminusfc             libcommon.a          libsyntax.a  test_ast

再使用./gcd_array_generator,输出的便是gcd_array_generator.ll

提交一:手动编写.ll

assing(20)

在tests/lab3/c_cases中查看assign.c文件

int main(){
  int a[10];
  a[0] = 10;
  a[1] = a[0] * 2;
  return a[1];
}

写出对应的assign_hand.ll文件,在附录中
验证输入

 lli assign_hand.ll; echo $?
 gcc -o assign assign.c
 ./assign; echo $?

输出

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/c_cases$ gcc -o assign assign.c
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/c_cases$ ./assign; echo $?
20

和:

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/stu_ll$ lli assign_hand.ll; echo $?
20

fun(220)

fun.c文件

int callee(int a){
  return 2 * a;
}
int main(){
  return callee(110);
}

写出对应的full_hand.h,在附录中
验证输入:

 lli fun_hand.ll; echo $?
 gcc -o fun fun.c
 ./fun; echo $?

输出:

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/c_cases$ gcc -o fun fun.c
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/c_cases$ ./fun; echo $?
220

和:

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/stu_ll$ lli fun_hand.ll; echo $?
220

if(233)

查看if.c文件

int main(){
  float a = 5.555;
  if(a > 1)
    return 233;
  return 0;
}

写出对应的if_hand.ll,在附录中
验证输入

 lli if_hand.ll; echo $?
 gcc -o if if.c
 ./if; echo $?

输出

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/c_cases$ gcc -o if if.c
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/c_cases$ ./if; echo $?
233

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/stu_ll$ lli if_hand.ll; echo $?
233

while(65)

查看while.c文件

int main(){
  int a;
  int i;
  a = 10;
  i = 0;
  while(i < 10){
    i = i + 1;
    a = a + i;
  }
  return a;
}

写出对应while_hand.ll文件,在附录中
验证输出

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/stu_ll$ lli while_hand.ll; echo $?
65

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/c_cases$ gcc -o while while.c
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/tests/lab3/c_cases$ ./while; echo $?
65

提交2:利用LightIR+cpp编写生成.ll程序

根据gcd_array的例子来写。注意后续测试的时候需要在${WORKSPACE}/tests/lab3/CMakeLists.txt中去掉对应的注释,再在${WORKSPACE}/build/下执行cmake ..make指令,即可得到对应的可执行文件。
编写四个.cpp文件,然后生成对应的可执行文件
![[Snipaste_2023-12-08_17-35-29.png]]

结果验证

assign

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ ./stu_assign_generator  > assign.ll
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ lli assign.ll echo $?
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ lli assign.ll; echo $?
20

fill

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ ./stu_fun_generator > fun.ll
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ lli fun.ll;echo $?
220

if

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ ./stu_if_generator  >if.ll
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ lli if.ll;echo $?
233

while

sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ ./stu_while_generator > while.ll
sunny2004@sunny2004-VirtualBox:~/lab1/cminus_compiler-2023-fall/build$ lli while.ll;echo $?
65

问题1:cpp与.ll的对应

概述:请描述你的cpp代码片段和.ll的每个BasicBlock的对应关系。描述中请附上两者代码。
用命令./stu_assign_generator > assign.ll生成assign.ll文件中只有一个BasicBlock,在main函数中,名为entry,代码如下:

define i32 @main() {
label_entry:
  %op0 = alloca i32
  store i32 0, i32* %op0
  %op1 = alloca [10 x i32]
  %op2 = getelementptr [10 x i32], [10 x i32]* %op1, i32 0, i32 0
  store i32 10, i32* %op2
  %op3 = load i32, i32* %op2
  %op4 = mul i32 %op3, 2
  %op5 = getelementptr [10 x i32], [10 x i32]* %op1, i32 0, i32 1
  store i32 %op4, i32* %op5
  %op6 = load i32, i32* %op5
  ret i32 %op6
}

对应的cpp

代码auto bb = BasicBlock::create(module, “entry”, mainFun);创建名为entry的基本块,并将其存到变量bb中(在打印输出时,名字实际为laebl_entry)
代码builder->set_insert_point(bb);用于进行SetInsertPoint设置,即将bb加入builder中。
而之后的代码,则是具体的基本块中的所需执行的语句。

// main函数->对应label_entry
    auto mainFun = Function::create(FunctionType::get(Int32Type, {}), /* 创建 main 函数 */
                                    "main", module);
    auto bb = BasicBlock::create(module, "entry", mainFun); /* 创建基本块 */
    // BasicBlock的名字在生成中无所谓,但是可以方便阅读
    builder->set_insert_point(bb); /* 将基本块加入到builder中 */

    auto retAlloca = builder->create_alloca(Int32Type); /* 创建返回默认量 */
    builder->create_store(CONST_INT(0), retAlloca);     /* 给默认量赋0,表示默认ret 0 */

    auto *arrayType = ArrayType::get(Int32Type, 10); /* 申请10个int的内存空间,将地址指针存入arrayType中 */

    auto aAlloca = builder->create_alloca(arrayType); /* 创建aAlloca数组,即a[10] */

    auto a0 = builder->create_gep(aAlloca, {CONST_INT(0), CONST_INT(0)}); /* 用变量a0存指向a[0]的指针 */
    builder->create_store(CONST_INT(10), a0);                             /* 将10存入a[0] */

    auto tmp = builder->create_load(a0);                                  /* 取出a[0]的值存入变量tmp */
    auto mul = builder->create_imul(tmp, CONST_INT(2));                   /* 将值乘以2存入变量mul中 */
    auto a1 = builder->create_gep(aAlloca, {CONST_INT(0), CONST_INT(1)}); /* 用变量a1存指向a[1]的指针 */
    builder->create_store(mul, a1);                                       /* 将结果mul存入a[1]中 */

    auto res = builder->create_load(a1); /* 取出a[1]中的值作为返回结果,存到变量res中 */
    builder->create_ret(res);            /* 创建返回,将res返回 */

    std::cout << module->print();
    delete module;

而在基本块之间的跳转时,用代码builder->create_br(whileBB);进行直接跳转到名为whileBB的基本块;

用代码builder->create_cond_br(icmp, trueBB, falseBB);进行按条件icmp跳转到trueBB基本块或是falseBB基本块。
在这里插入图片描述

问题2:Vistor Pattern

请指出visitor.cpp中,treeVisitor.visit(exprRoot)执行时,以下几个Node的遍历序列:numberA、numberB、exprC、exprD、exprE、numberF、exprRoot。
序列请按如下格式指明:
exprRoot->numberF->exprE->numberA->exprD
答:根据main函数,我们可以得出如下步骤:
1.创建了值为4的A节点和值为2的B节点

auto numberA = NumberNode(4); 
auto numberB = NumberNode(2);

2.A作为左节点,B作为右节点,相乘得到exprC;B作为左节点,A作为右节点相除得到exprD

auto exprC = MulDivNode(numberA, numberB, "mul"); 
auto exprD = MulDivNode(numberB, numberA, "div"); 

3.exprC作为左节点,exprD作为右节点,相减得到exprE

auto exprE = AddSubNode(exprC, exprD, "sub"); 

4.创建值为4的F节点

auto numberF = NumberNode(5);

5.exprE作为左节点,F作为右节点,相加得到exprRoot

auto exprRoot = AddSubNode(exprE, numberF, "add");

综上,可以构建出如下的一棵树:
在这里插入图片描述

节点访问顺序如图所示,文字描述如下:

  1. 访问exprRoot时,由于为AddSubNode,先访问右子节点numberF,返回一个值,再访问左子节 点exprE;
  2. 访问exprE时,由于为AddSubNode,先访问右子节点exprD,再访问左子节点exprC;
  3. 访问exprD时,由于为MulDivNode,先访问左节点numberB,返回一个值,再访问右节点 numberA,返回一个值;
  4. 访问exprC时,由于为MulDivNode,先访问左节点numberA,返回一个值,再访问右节点 numberB,返回一个值; 总结:访问顺序为exprRoot->numberF->exprE->exprD->numberB->numberA->exprC- >numberA->numberB

问题3: getelementptr

请给出IR.md中提到的两种getelementptr用法的区别,并稍加解释:
%2 = getelementptr [10 x i32], [10 x i32]* %1, i32 0, i32 %0
%2 = getelementptr i32, i32* %1 i32 %0

答:GetElementPtr指令用于获取聚合数据结构的子元素的地址。它仅执行地址计算,不访问内存。是一条指针计算语句,本身并不进行任何数据的访问或修改,只进行指针的计算。
从结果的角度讲,两种getelementptr的用法的结果都是将一个i32*类型的,即一个i32内存的地址存入%2中。
第一种方法:数组在C中会分割指针,但在LLVM中,他只能确定数据类型的大小然后强制转换为指针,但不会分割他们。%1是基址,有两个索引0和%0,数组通过指针访问,所以需要两个索引
第一个用于分割指针(因为用的指针是[10 x i32],但是我们返回的是一个i32的,所以需要分割),
第二个用于索引数组本身(即偏移量)。
该方法适用的数组为int nums[] = {1, 2, 3};或者int a[10].
第二种方法:%1做为我们的基址地址,%0做为我们的索引(偏移量),并把计算出来的地址给%2。该方法适用的数组为int *nums = {1, 2, 3};

实际的用法中有着如下的区别:

1. 两种用法对于偏移值参数数量要求不同 
对于 %2 = getelementptr [10 x i32], [10 x i32]* %1, i32 0, i32 %0 ,将内存按照10*i32 的大小,即40字节,进行划分,而每个10*i32大小的内存中又按照i32,即4字节,进行划分,分为10个i32大小。此时需要两个偏移类型及偏移值来获得所需地址。可以将整个内存看做一个二维数组,每一个 元素代表一个i32大小内存,每一行为10个i32。其中第一个偏移值代表行号,以题目中i320为例,首先地址应当偏移0*40=0个字节;第二个偏移值代表列号,以题目中i32 %0为例,地址应再偏移%0*4=0个字节。最终获得的地址偏移量为0*40+0%*40=0%*40。(偏移是针对所给索引开始的指针%1的) 对于 %2 = getelementptr i32, i32* %1, i32 %0 将内存按照i32的大小,即4字节进行划分。此时需要一个偏移类型及偏移值来获取所需地址。可以将整个内存看做一个一维数组,每一个元素代表一个i32大小内存。其中偏移值代表元素的序号,以题目中i32 %0为例,地址应当偏移%0*4字节,即取第 [%0]个i32所在的地址。 

2. 两种用法对于索引开始的指针类型和计算基础类型要求不同 
对于 %2 = getelementptr [10 x i32], [10 x i32]* %1, i32 0, i32 %0 ,要求传入的%1必须 为[10 x i32]* 类型;而对于 %2 = getelementptr i32, i32* %1, i32 %0 ,要求传入的%1必须为 i32*类型。 

3. 两种用法的使用场景不同
对于 %2 = getelementptr [10 x i32], [10 x i32]* %1, i32 0, i32 %0 ,由于要求传入的%1 必须为[10 x i32]* 类型,则对于一个使用了上述方法的函数,该方法所针对的数组必须为一个在该函数 中长度特定且已知的数组,例如在本函数中申请的a[10]或者全局数组b[5]等,而函数形参中的u[]所传递 的数组由于长度未知,不能使用该方法。该方法兼容性较差,只能针对特定数组。对于 %2 = getelementptr i32, i32* %1, i32 %0 ,由于要求传入的%1必须为i32*类型,则对于一个使用了该方法的函数,该数组为一个在该函数中长度不确定的数组,例如:该数组为在其他函数中 申请的数组,然后作为本函数的形参例如u[](不确定长数组)的实参传入本函数。一个函数为了尽可能处理不同长度的数组,故使用该方法,因为兼容性较好,能够针对不同长度的数组 二者的使用区别有点类似于C语言中对于静态数组和动态数组的处理,一个是长度固定,一个是长度可变。

实验难点

第一部分是根据C程序完成.ll文件,这一部分的难点主要在于对IR的理解,要读懂手册。因为有汇编的基础以及查找到的北航编译原理实验的资料、助教提供的详尽的代码注释,理解起来并不算 难,在getelementptr这块费了比较大劲,但靠不断查阅资料自我消化还是理解了用法。
最难的就是补充.cpp文件然后生成.ll再验证
对变量进行操作时,要先给变量分配位置,用store将值赋给这个位置,再把它load出来
写分支时要理清楚进入分支、分支、出分支几部分

实验反馈

通俗易懂,又不算特别懂,快速上手,又有一些难度,代码和注释都很详细,nice!

附录:编写文件

assign_hand.ll

在tests/lab3/c_cases中查看assign.c文件

int main(){
  int a[10];
  a[0] = 10;
  a[1] = a[0] * 2;
  return a[1];
}
;int main(){
define dso_local i32 @main() #0 {

;int a[10]
%1 = alloca [10 x i32]	;%1为a[10]的起始地址

;a[0] = 10;
%2 = getelementptr inbounds [10 x i32], [10 x i32]* %1, i64 0, i64 0	;%2为a[0]
store i32 10, i32* %2	;%2赋值为10,完成a[0]赋值

;a[1] = a[0] * 2;
%3 = load i32, i32* %2	;%3加载%2的数据
%4 = mul nsw i32 %3, 2	;%4赋值为%3的二倍
%5 = getelementptr inbounds [10 x i32], [10 x i32]* %1, i64 0, i64 1	;%5存储a[1]
store i32 %4, i32* %5	;把%4的值赋值给%5,完成a[1]赋值

;return a[1];
%6 = load i32, i32* %5	;%6存储%5
ret i32 %6		;返回%6

;}
}

full_hand.ll

fun.c文件

int callee(int a){
  return 2 * a;
}
int main(){
  return callee(110);
}

;int callee(int a){
define dso_local i32 @callee(i32 %0) #0 {
%2 = alloca i32		;开辟空间	为什么不能%1???
store i32 %0, i32* %2	;%2存储参数a
%3 = load i32, i32* %2	;%3保存%2

;return 2 * a;
%4 = mul nsw i32 2, %3	;%4存储二倍的%3
ret i32 %4		;返回%4

;}
}

;int main(){
define dso_local i32 @main() #0 {

;return callee(110);
%1 = call i32 @callee(i32 110)	;%1存储callee()返回值	为什么不能%0???
ret i32 %1			;返回%1

;}
}

if_hand.ll

查看if.c文件

int main(){
  float a = 5.555;
  if(a > 1)
    return 233;
  return 0;
}
;int main(){
define dso_local i32 @main() #0 {

;float a = 5.555;
%1 = alloca i32	;开辟空间
%2 = alloca float	;开辟float空间
store float 0x40163851E0000000, float* %2	;%2赋值为5.555

;if(a > 1)
%3 = load float, float* %2	;把%2的数据加载到%3
%4 = fcmp ogt float %3, 1.000000e+00	;%3和1比较
br i1 %4, label %5, label %6	;4是则跳转到5,4否则跳转到6

;return 233;
5:
store i32 233, i32* %1	;%1赋值为233
br label %7		;跳转到7

;return 0;
6:
store i32 0, i32* %1		;%1赋值为0
br label %7		;跳转到7

7:
%8 = load i32, i32* %1	;把%1的数据加载到%8
ret i32 %8		;返回%8

;}
}

while_hand.ll

查看while.c文件

int main(){
  int a;
  int i;
  a = 10;
  i = 0;
  while(i < 10){
    i = i + 1;
    a = a + i;
  }
  return a;
}

;int main(){
define dso_local i32 @main() #0 {

;int a;
%1 = alloca i32	;开辟空间

;int i;
%2 = alloca i32	;开辟空间

;a = 10;
store i32 10, i32* %1	;%1赋值为10

;i = 0;
store i32 0, i32* %2		;%2赋值为0
br label %3		;跳转到3

;while(i < 10){
3:
%4 = load i32, i32* %2	;把%2的值加载到%4
%5 = icmp slt i32 %4, 10	;比较%4和10的大小
br i1 %5, label %6, label %11	;5是则跳转到6,5否则跳转到11

;i = i + 1;
6:
%7 = add nsw i32 %4, 1	;%7保存加一后的值
store i32 %7, i32* %2	;存储回%2

;a = a + i;
%8 = load i32, i32* %1	;a的数据加载到%8
%9 = load i32, i32* %2	;i的数据加载到%9
%10 = add nsw i32 %8, %9	;%10保存相加的数据
store i32 %10, i32* %1	;存储回%1
br label %3		;跳转到3

;}

;return a;
11:
%12 = load i32, i32* %1	;a的数据加载到%12
ret i32 %12		;返回%12

;}
}

assign的cpp文件

#include "BasicBlock.h"
#include "Constant.h"
#include "Function.h"
#include "IRBuilder.h"
#include "Module.h"
#include "Type.h"

#include <iostream>
#include <memory>

#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);
	auto mainFunTy = FunctionType::get(Int32Type, {});                  	// 通过返回值类型与参数类型列表得到函数类型
	auto mainFun = Function::create(mainFunTy, "main", module);             // 通过函数类型得到函数
	auto bb = BasicBlock::create(module, "entry", mainFun);
	builder->set_insert_point(bb);                                      	// 将当前插入指令点的位置设在bb


	
	//int a[10];
	auto *arrayType = ArrayType::get(Int32Type, 10);                    	// 在内存中为数组分配空间,参数表示数组的元素类型和元素个数
	auto aAlloca = builder->create_alloca(arrayType);
	//a[0]=10;
	auto a0GEP = builder->create_gep(aAlloca, {CONST_INT(0), CONST_INT(0)});// 获取a[0]地址
	builder->create_store(CONST_INT(10), a0GEP);                        	// a[0]=10
	a0GEP = builder->create_gep(aAlloca, {CONST_INT(0), CONST_INT(0)});     // 更新a[0]
	//a[1] = a[0] * 2;
	auto a0Load = builder->create_load(a0GEP);                          	// 加载a[0]
	auto a0mul2 = builder->create_imul(a0Load, CONST_INT(2));           	// a[0]*2
	auto a1GEP = builder->create_gep(aAlloca, {CONST_INT(0), CONST_INT(1)});// 获取a[1]地址
	builder->create_store(a0mul2, a1GEP);                               	// 将a[0]*2存入a[1]
	a1GEP = builder->create_gep(aAlloca, {CONST_INT(0), CONST_INT(1)});     // 更新a[1]
	//return a[1];
	auto a1Load = builder->create_load(a1GEP);                          	// 加载a[1]
	builder->create_ret(a1Load);                                        	// 返回a[1]
	
	
	std::cout << module->print();
	delete module;
	return 0;
}


full的cpp文件

#include "BasicBlock.h"
#include "Constant.h"
#include "Function.h"
#include "IRBuilder.h"
#include "Module.h"
#include "Type.h"

#include <iostream>
#include <memory>

#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); // 创建IRBuilder
    Type *Int32Type = Type::get_int32_type(module);

    // callee函数,创建函数
    std::vector<Type *> Ints(1, Int32Type);                /* 函数参数类型的vector,内含1个int类型 */
    auto calleeFunTy = FunctionType::get(Int32Type, Ints); /* 通过返回值类型与参数类型列表得到函数类型 */
    auto calleeFun = Function::create(calleeFunTy,         /* 由函数类型得到函数 */
                                      "callee", module);
    auto bb = BasicBlock::create(module, "fun", calleeFun); /* 创建基本块,命名为fun */
    builder->set_insert_point(bb);                          /* 将基本块插入builder中 */
    // 传参
    auto aAlloca = builder->create_alloca(Int32Type); /* 在内存中分配参数a的位置 */
    std::vector<Value *> args;                        /* 获取callee函数的形参,通过Function中的iterator */
    for (auto arg = calleeFun->arg_begin(); arg != calleeFun->arg_end(); arg++)
    {
        args.push_back(*arg); // * 号运算符是从迭代器中取出迭代器当前指向的元素
    }
    builder->create_store(args[0], aAlloca); /* 存储参数a */
    // 具体执行
    auto aLoad = builder->create_load(aAlloca);           /* 将参数a存到变量aLoad中 */
    auto res = builder->create_imul(aLoad, CONST_INT(2)); /* 将值乘以2存入变量res中 */
    builder->create_ret(res);                             /* 创建返回,将res返回 */

    // main函数
    auto mainFun = Function::create(FunctionType::get(Int32Type, {}), /* 创建 main 函数 */
                                    "main", module);
    bb = BasicBlock::create(module, "main", mainFun); /* 创建基本块,命名为main */
    builder->set_insert_point(bb);                    /* 将基本块加入到builder中 */
    // 设置默认返回
    auto retAlloca = builder->create_alloca(Int32Type); /* 创建返回默认量 */
    builder->create_store(CONST_INT(0), retAlloca);     /* 给默认量赋0,表示默认ret 0 */
    // 具体执行
    auto call = builder->create_call(calleeFun, {CONST_INT(110)}); /* 调用函数calleeFun,将结果存到变量call中 */
    builder->create_ret(call);                                     /* 返回结果值 */

    std::cout << module->print();
    delete module;

    return 0;
}

if的cpp文件

#include "BasicBlock.h"
#include "Constant.h"
#include "Function.h"
#include "IRBuilder.h"
#include "Module.h"
#include "Type.h"

#include <iostream>
#include <memory>

#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);      // 创建IRBuilder
 Type* Int32Type = Type::get_int32_type(module);

 // main函数
 auto mainFun = Function::create(FunctionType::get(Int32Type, {}),   /* 创建 main 函数 */
     "main", module);
 auto bb = BasicBlock::create(module, "main", mainFun);              /* 创建基本块,命名为main */
 builder->set_insert_point(bb);                              /* 将基本块加入到builder中 */
 // 设置默认返回
 auto retAlloca = builder->create_alloca(Int32Type);         /* 创建返回默认量 */
 builder->create_store(CONST_INT(0), retAlloca);             /* 给默认量赋0,表示默认ret 0 */
 // 具体执行
 Type* FloatType = Type::get_float_type(module);             /* 获取单个float类型的指针 */
 auto aAlloca = builder->create_alloca(FloatType);           /* 根据float类型的指针,申请一个float变量空间 */
 builder->create_store(CONST_FP(5.555), aAlloca);            /* 将值5.555存入该变量空间 */

 auto a = builder->create_load(aAlloca);                     /* 取出该变量空间内的值,即a的值 */
 auto fcmp = builder->create_fcmp_gt(a, CONST_FP(1.00));     /* 将其和1.00进行比较,返回结果存到fcmp中 */

 auto trueBB = BasicBlock::create(module, "trueBB", mainFun);/* 符合if条件的分支 */
 auto falseBB = BasicBlock::create(module, "falseBB", mainFun);  /* 不符合if条件的分支 */
 builder->create_cond_br(fcmp, trueBB, falseBB);             /* 根据fcmp创建跳转语句 */

 builder->set_insert_point(trueBB);      // if true; 分支的开始需要SetInsertPoint设置
 builder->create_ret(CONST_INT(233));    /* 创建返回,将值233返回 */

 builder->set_insert_point(falseBB);     // if false; 分支的开始需要SetInsertPoint设置
 builder->create_ret(CONST_INT(0));      /* 创建返回,将值0返回 */

 std::cout << module->print();
 delete module;

 return 0;
}

while的cpp文件

#include "BasicBlock.h"
#include "Constant.h"
#include "Function.h"
#include "IRBuilder.h"
#include "Module.h"
#include "Type.h"

#include <iostream>
#include <memory>

#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);      // 创建IRBuilder
 Type* Int32Type = Type::get_int32_type(module);

 // main函数
 auto mainFun = Function::create(FunctionType::get(Int32Type, {}),   /* 创建 main 函数 */
     "main", module);
 auto bb = BasicBlock::create(module, "main", mainFun);      /* 创建基本块,命名为main */
 builder->set_insert_point(bb);                              /* 将基本块加入到builder中 */
 // 设置默认返回
 auto retAlloca = builder->create_alloca(Int32Type);         /* 创建返回默认量 */
 builder->create_store(CONST_INT(0), retAlloca);             /* 给默认量赋0,表示默认ret 0 */
 // 创建基本块
 auto whileBB = BasicBlock::create(module, "whileBB", mainFun);  /* 进行while判断的基本块 */
 auto trueBB = BasicBlock::create(module, "trueBB", mainFun);    /* 符合判断条件的基本块分支 */
 auto falseBB = BasicBlock::create(module, "falseBB", mainFun);  /* 不符合判断条件的基本块分支 */
 // 具体执行
 auto aAlloca = builder->create_alloca(Int32Type);       /* 申请存a的空间,将地址赋值给指针aAlloca */
 auto iAlloca = builder->create_alloca(Int32Type);       /* 申请存i的空间,将地址赋值给指针iAlloca */
 builder->create_store(CONST_INT(10), aAlloca);          /* 将值10存入a的空间 */
 builder->create_store(CONST_INT(0), iAlloca);           /* 将值0存入i的空间 */
 builder->create_br(whileBB);                            /* 跳转到while循环条件判断,判断是否进入循环 */

 builder->set_insert_point(whileBB);                     /* while条件判断,设置SetInsertPoint */
 auto i = builder->create_load(iAlloca);                 /* 取出i */
 auto icmp = builder->create_icmp_lt(i, CONST_INT(10));  /* 判断i是否小于10,并将判断结果存到icmp中 */
 builder->create_cond_br(icmp, trueBB, falseBB);         /* 根据icmp创建跳转语句 */

 builder->set_insert_point(trueBB);                  // if true; 分支的开始需要SetInsertPoint设置
 i = builder->create_load(iAlloca);                  /* 取出i */
 auto tmp = builder->create_iadd(i, CONST_INT(1));   /* 将i加1,存到暂存变量tmp中,tmp=i+1 */
 builder->create_store(tmp, iAlloca);                /* 将tmp的值存到i中,i=tmp*/
 auto a = builder->create_load(aAlloca);             /* 取出a */
 i = builder->create_load(iAlloca);                  /* 取出i */
 tmp = builder->create_iadd(a, i);                   /* 将a加i的值存到tmp中,tmp=i+a */
 builder->create_store(tmp, aAlloca);                /* 将tmp存到a中,a=tmp */
 builder->create_br(whileBB);                        /* 跳转到while循环条件判断,判断是否继续循环 */

 builder->set_insert_point(falseBB);                 // if false; 分支的开始需要SetInsertPoint设置
 auto res = builder->create_load(aAlloca);           /* 取出a的值,存到res中,res=a */
 builder->create_ret(res);                           /* 将res返回,即return res */

 std::cout << module->print();
 delete module;

 return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1307339.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis - 做缓存时高并发问题:缓存穿透、击穿、雪崩,数据库缓存双写不一致

缓存穿透 当用户访问的数据既不在缓存也不在数据库中时&#xff0c;就会导致每个用户查询都会“穿透” 缓存“直抵”数据库。这种情况就称为缓存穿透。当高度发的访问请求到达时&#xff0c;缓存穿透不 仅增加了响应时间&#xff0c;而且还会引发对 DBMS 的高并发查询&…

frp配置内网穿透步骤

frp配置内网穿透步骤 1.环境准备1.1 云服务器1.2 frp包 2. frp安装2.1 server服务端设置2.2 客户端配置 实现目标通过云服务器ip:8080访问内网电脑启动的web项目localhost:8080 1.环境准备 1.1 云服务器 服务器安装centos7.9, 安全组入口方向开通 7500 7000 8080 8060端口 …

CSDN云账号签约流程

在电脑端上进行提现操作&#xff0c;按提示进行即可。 问题 手机端无法签约。 电脑签约

【详解优先级队列(堆)】

目录 堆的概念 堆的性质 堆的存储方式 堆的创建 堆的向下调整 向下过程(以小堆为例) 向下过程(以大堆为例) 建堆的时间复杂度O(n) 堆的插入与删除 堆的插入 向上调整建堆的时间复杂度O(nlogn) 堆的删除 常见习题 常用接口介绍 PriorityQueue的特性 Pri…

HNU计算机视觉作业一

前言 选修的是蔡mj老师的计算机视觉&#xff0c;上课还是不错的&#xff0c;但是OpenCV可能需要自己学才能完整把作业写出来。由于没有认真学&#xff0c;这门课最后混了80多分&#xff0c;所以下面作业解题过程均为自己写的&#xff0c;并不是标准答案&#xff0c;仅供参考 …

lammps编译(2Aug2023、intel2020、rtx4070ti)

说明&#xff1a; [rootnode101 ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootnode101 ~]# gcc -v Using built-in specs. COLLECT_GCCgcc COLLECT_LTO_WRAPPER/usr/libexec/gcc/x86_64-redhat-linux/4.8.5/lto-wrapper Target: x86_64-redhat-lin…

双十二哪个牌子的电视盒子好用?测评员总结电视盒子品牌排行榜

每次电商大促我会分享好物推荐&#xff0c;本期我要盘点的数码产品是电视盒子&#xff0c;电视盒子的功能和重要性大家都懂&#xff0c;但电视盒子如何选就不太了解了&#xff0c;我根据今年20多次的测评结果整理了电视盒子品牌排行榜&#xff0c;看看哪个牌子的电视盒子好用吧…

beebox靶场A3 low级别 xss通关教程(二)

六&#xff1a;xss get型 eval 通过观察我们可以发现url地址中存在一个date函数 那我们可以试一下把后面的date()函数去掉&#xff0c;直接写入一个alert(555) 发现直接弹出一个框&#xff0c;证明有xss漏洞 七&#xff1a;xss href 直接进入页面会看到是get方法&#xff0c…

HarmonyOS4.0从零开始的开发教程12给您的应用添加弹窗

HarmonyOS&#xff08;十&#xff09;给您的应用添加弹窗 概述 在我们日常使用应用的时候&#xff0c;可能会进行一些敏感的操作&#xff0c;比如删除联系人&#xff0c;这时候我们给应用添加弹窗来提示用户是否需要执行该操作&#xff0c;如下图所示&#xff1a; 弹窗是一种…

【51单片机系列】直流电机使用

本文是关于直流电机使用的相关介绍。 文章目录 一、直流电机介绍二、ULN2003芯片介绍三、在proteus中仿真实现对电机的驱动 51单片机的应用中&#xff0c;电机控制方面的应用也很多。在学习直流电机(PWM)之前&#xff0c;先使用GPIO控制电机的正反转和停止。但不能直接使用GPIO…

飞天使-linux操作的一些技巧与知识点4-ansible常用的技巧,配置等

文章目录 ansible配置文件的优先级尝试开始进行操作ansible常用模块ansible 的playbook示例安装phpplaybook中变量的引用 ansible yum install -y ansible 测试是否可用 ansible localhost -m ping /etc/ansible/ansible.cfg &#xff1a;主配置文件&#xff0c;配置 ansible…

3个好用的桌面管理软件!点赞

大家是不是觉得自己的桌面有时候特别乱、上班的时候会影响自己的心情。尤其是一些大企业&#xff0c;干净整洁的桌面是领导必须的要求。 今天就为大家推荐3款好用的桌面管理软件&#xff0c;这其中有的是适合企业用的&#xff0c;有的是适合个人用的&#xff0c;大家可根据自己…

KaiwuDB × 国网山东综能 | 分布式储能云边端一体化项目建设

项目背景 济南韩家峪村首个高光伏渗透率台区示范项目因其所处地理位置拥有丰富的光照资源&#xff0c;该区域住户 80% 以上的屋顶都安装了光伏板。仅 2022 年全年&#xff0c;光伏发电总量达到了百万千瓦时。 大量分布式光伏并网&#xff0c;在输出清洁电力的同时&#xff0c…

全志XR806开发板RTOS环境搭建及问题

测评一 RTOS环境搭建及问题 按照官网文档https://xr806.docs.aw-ol.com/rtos/env/说明&#xff0c;RTOS先拉取SDK&#xff0c;由于XR806是Cortex-M33 Star内核然后指定gcc-arm-none-eabi工具链的位置&#xff0c;再编译再烧录&#xff0c;开发过程和ESP32有些许相似&#xff0c…

深入理解Dubbo-7.服务消费调用源码分析

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱吃芝士的土豆倪&#xff0c;24届校招生Java选手&#xff0c;很高兴认识大家&#x1f4d5;系列专栏&#xff1a;Spring源码、JUC源码、Kafka原理、分布式技术原理&#x1f525;如果感觉博主的文章还不错的话&#xff…

媒介盒子:软文推广让你的品牌宣传更高效

软文推广在当今企业的宣传方式中具有至关重要的作用。随着互联网技术的不断发展和社交媒体的广泛使用&#xff0c;软文推广已经成为品牌和广告主的首选。如何在海量信息中脱颖而出&#xff0c;如何让内容在众多信息中获得更高的点击率与转化率&#xff0c;还需要借助软文推广的…

算法通关村第十八关-黄金挑战回溯困难问题

大家好我是苏麟 , 今天带来几道回溯比较困难的题 . 回溯有很多比较难的问题&#xff0c;这里我们看两个&#xff0c;整体来说这两个只是处理略复杂&#xff0c;还不是最难的问题 . 大纲 IP问题 IP问题 描述 : 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 …

UE5 - ArchvizExplorer与Map Border Collection结合 - 实现电子围栏效果

插件地址&#xff1a; https://www.unrealengine.com/marketplace/zh-CN/product/archviz-explorer https://www.unrealengine.com/marketplace/zh-CN/product/map-border-collection ArchvizExplorer扩展&#xff1a; https://download.csdn.net/download/qq_17523181/8843305…

独热编码和词向量的简单理解

把单词用向量表示&#xff0c;是把深度神经网络语言模型引入自然语言处理领域的一个核心技术。想要让机器理解单词&#xff0c;就必须要把它变成一串数字&#xff08;向量&#xff09;。下面介绍的 One-Hot Encoding&#xff08;One-Hot 编码&#xff09;和 Word Embedding &am…

【LittleXi】2023 ICPC ECfinal 出线 官方数据 民间预测

【LittleXi】2023 ICPC ECfinal 出线 官方数据 民间预测 说明&#xff1a; 参考去年、前年上海大学&#xff0c;设置210出线队伍 对6场区域赛&#xff08;不含港澳&#xff09;走Z字,每个学校最多三支队伍出线 字符串问题&#xff0c;可能会有几个名额失真&#xff0c;比如南…