編譯原理lab3-cminus_compiler-LLVM簡要熟悉

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.0Optimized build.Default target: x86_64-pc-linux-gnuHost 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 i32store i32 0, i32* %op0%op1 = alloca [10 x i32]%op2 = getelementptr [10 x i32], [10 x i32]* %op1, i32 0, i32 0store 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 1store i32 %op4, i32* %op5%op6 = load i32, i32* %op5ret i32 %op6
}

對應的cpp

代碼auto bb = BasicBlock::create(module, “entry”, mainFun);創建名為entry的基本塊,并將其存到變量bb中(在打印輸出時,名字實際為laebl_entry)
代碼builder->set_insert_point(bb);用于進行SetInsertPoint設置,即將bb加入builder中。
而之后的代碼,則是具體的基本塊中的所需執行的語句。

// main函數->對應label_entryauto 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		;跳轉到77:
%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]=10a0GEP = 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]*2auto 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); // 創建IRBuilderType *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);      // 創建IRBuilderType* 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);      // 創建IRBuilderType* 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.pswp.cn/news/210994.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/210994.shtml
英文地址,請注明出處:http://en.pswp.cn/news/210994.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

TREK610C高壓放大器

181/2461/8938技術規格 輸出電壓&#xff1a;0到10 kV直流電壓 輸出電流&#xff1a;0到2 mA 轉換率&#xff1a;大于500 V/μs 信號帶寬&#xff1a;直流到1.0 kHz &#xff08;-3dB&#xff09; 放大倍數&#xff1a;1000 V/V 閉環系統以保持低噪音、高精確度電壓輸出 短…

最簡單的基于 FFmpeg 的音頻解碼器

最簡單的基于 FFmpeg 的音頻解碼器 最簡單的基于 FFmpeg 的音頻解碼器正文參考工程文件下載 參考雷霄驊博士的文章&#xff0c;鏈接&#xff1a;最簡單的基于FFMPEGSDL的音頻播放器&#xff1a;拆分-解碼器和播放器 最簡單的基于 FFmpeg 的音頻解碼器 正文 FFmpeg 音頻解碼器…

【ArcGIS微課1000例】0080:ArcGIS將shp轉json(geojson)案例教程

本文以案例的形式,講述在ArcGIS軟件中,將矢量數據轉為GeoJSON的方法。 擴展閱讀:【GIS風暴】GeoJSON數據格式案例全解 文章目錄 一、GeoJson簡介二、ArcGIS將矢量數據轉為GeoJSON一、GeoJson簡介 GeoJSON是一種基于JSON的地理空間數據交換格式,它定義了幾種類型JSON對象以…

Spring Cloud Gateway 網關的基礎使用

1. 什么是網關&#xff1f;網關有什么用&#xff1f; 在微服務架構中&#xff0c;網關就是一個提供統一訪問地址的組件&#xff0c;它解決了內部微服務與外部的交互問題。網關主要負責流量的路由和轉發&#xff0c;將外部請求引到對應的微服務實例上。同時提供身份認證、授權、…

Spring-Boot---配置文件

文章目錄 配置文件的作用配置文件的格式PropertiesProperties基本語法讀取Properties配置文件 ymlyml基本語法讀取yml配置文件 Properties VS Yml 配置文件的作用 整個項目中所有重要的數據都是在配置文件中配置的&#xff0c;具有非常重要的作用。比如&#xff1a; 數據庫的…

230. 二叉搜索樹中第K小的元素 --力扣 --JAVA

題目 給定一個二叉搜索樹的根節點 root &#xff0c;和一個整數 k &#xff0c;請你設計一個算法查找其中第 k 個最小元素&#xff08;從 1 開始計數&#xff09;。 解題思路 利用List存儲數據&#xff1b;遍歷整個樹&#xff0c;讀取數各個節點的value&#xff1b;對value進行…

Python繪制多分類ROC曲線

目錄 1 數據集介紹 1.1 數據集簡介 1.2 數據預處理 2隨機森林分類 2.1 數據加載 2.2 參數尋優 2.3 模型訓練與評估 3 繪制十分類ROC曲線 第一步&#xff0c;計算每個分類的預測結果概率 第二步&#xff0c;畫圖數據準備 第三步&#xff0c;繪制十分類ROC曲線 1 數據集…

【數據結構】——排序篇(上)

前言&#xff1a;前面我們已經學過了許許多多的排序方法&#xff0c;如冒泡排序&#xff0c;選擇排序&#xff0c;堆排序等等&#xff0c;那么我們就來將排序的方法總結一下。 我們的排序方法包括以下幾種&#xff0c;而快速排序和歸并排序我們后面進行詳細的講解。 直接插入…

Qt實現二維碼生成和識別

一、簡介 QZxing開源庫: 生成和識別條碼和二維碼 下載地址&#xff1a;https://gitcode.com/mirrors/ftylitak/qzxing/tree/master 二、編譯與使用 1.下載并解壓&#xff0c;解壓之后如圖所示 2.編譯 打開src目錄下的QZXing.pro&#xff0c;選擇合適的編譯器進行編譯 最后生…

util.js

一、util.js是什么&#xff1f; 1、util.js是Node.js提供的一個工具庫&#xff0c;主要用于輔助實現JavaScript代碼的通用功能。 2、除了Node.js中內置的模塊外&#xff0c;util.js是Node.js中最核心的模塊之一。 3、通過util.js&#xff0c;開發者可以輕松實現JavaScript常…

Unity 資源管理之StreamingAssets

StreamingAssets也是Unity中特殊的文件夾&#xff0c;用于存放運行時可以直接訪問的資源。StreamingAssets一般存放數據或配置文件、圖片、視頻資源等。 StreamingAssets的文件路徑可以通過Application.streamingAssetsPath來獲取。 加載或訪問使用WWW類或UnityWebRequest類。…

MIT6S081-Lab2總結

大家好&#xff0c;我叫徐錦桐&#xff0c;個人博客地址為www.xujintong.com&#xff0c;github地址為https://github.com/xjintong。平時記錄一下學習計算機過程中獲取的知識&#xff0c;還有日常折騰的經驗&#xff0c;歡迎大家訪問。 Lab2就是了解一下xv6的系統調用流程&…

Java - Synchronized的鎖升級之路

Synchronized鎖 Synchronized在Java JVM里的實現是基于進入和退出Monitor對象來實現方法同步和代碼塊同步的 monitor enter指令是在編譯后插入到同步代碼塊的開始位置 而monitor exit是插入到方法結束處和異常處 JVM要保證每個monitor enter必須有對應的monitor exit與之配對。…

解決服務端渲染程序SSR運行時報錯: ReferenceError: document is not defined

現象&#xff1a; 原因&#xff1a; 該錯誤表明在服務端渲染 (SSR) 過程中&#xff0c;有一些代碼嘗試在沒有瀏覽器環境的情況下執行與瀏覽器相關的操作。這在服務端渲染期間是一個常見的問題&#xff0c;因為在服務端渲染期間是沒有瀏覽器 API。 解決辦法&#xff1a; 1. 修…

bat腳本之while

在批處理&#xff08;BAT&#xff09;腳本中&#xff0c;while循環是一種常用的控制流結構&#xff0c;用于在滿足特定條件的情況下重復執行一段代碼。 while循環的基本語法如下&#xff1a; while [ condition ] do command1 command2 ... commandN done這里的 cond…

【2023傳智杯-新增場次】第六屆傳智杯程序設計挑戰賽AB組-DEF題復盤解題分析詳解【JavaPythonC++解題筆記】

本文僅為【2023傳智杯-第二場】第六屆傳智杯程序設計挑戰賽-題目解題分析詳解的解題個人筆記,個人解題分析記錄。 本文包含:第六屆傳智杯程序設計挑戰賽題目、解題思路分析、解題代碼、解題代碼詳解 文章目錄 一.前言二.賽題題目D題題目-E題題目-F題題目-二.賽題題解D題題解-…

深入理解Sentinel系列-1.初識Sentinel

&#x1f44f;作者簡介&#xff1a;大家好&#xff0c;我是愛吃芝士的土豆倪&#xff0c;24屆校招生Java選手&#xff0c;很高興認識大家&#x1f4d5;系列專欄&#xff1a;Spring源碼、JUC源碼、Kafka原理、分布式技術原理&#x1f525;如果感覺博主的文章還不錯的話&#xff…

待做-待補充-每個節點做事,時間,以及與角度的關系

文章目錄 待定內容紅黑樹應用場景限制什么是二叉樹遍歷遞歸遍歷1.前序遍歷 進入節點時2.中序遍歷 遍歷完左子樹回到節點。此操作需要等到所有左樹節點做完后才會做3.后序遍歷 遍歷完左右子樹回到節點。左右子樹的所有節點都做完操作后&#xff0c;回到當前節點才會做此操作 …

如何搭建自己的直播電商系統?

當下&#xff0c;傳統的圖文電商模式已經走向沒落&#xff0c;視頻電商備受追捧。抖音、快手、小紅書、京東、淘寶、拼多多都在發力直播電商業務&#xff0c;尤其是以抖音為首的直播電商備受用戶歡迎&#xff0c;它具有實時直播和強互動的特點&#xff0c;是傳統電商所不具備的…

<HarmonyOS第一課>保存應用數據【課后考核】

【習題】保存應用數據 判斷題 首選項是關系型數據庫。 錯誤(False) 應用中涉及到Student信息&#xff0c;如包含姓名&#xff0c;性別&#xff0c;年齡&#xff0c;身高等信息可以用首選項來存儲。 錯誤(False) 同一應用或進程中每個文件僅存在一個Preferences實例。 正確(T…