




版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領
文檔簡介
LLVMCookbook中文版LLVMCookbook交叉編譯將LLVMIR轉(zhuǎn)換為將LLVMbitcode將LLVMbitcode轉(zhuǎn)回為LLVM轉(zhuǎn)換LLVM鏈接LLVM執(zhí)行LLVM使用第2第4自定義LLVM實現(xiàn)一個分析實現(xiàn)一個別名分析使用其他分析第5編寫無用代碼消除編寫內(nèi)聯(lián)轉(zhuǎn)換編寫內(nèi)存優(yōu)化合并LLVM其他優(yōu)化第6LLVMIR使用GraphViz可視化LLVMIR合法化優(yōu)化第7實現(xiàn)棧幀多指令使用將LLVMIR轉(zhuǎn)換為使用使用第1LLVM交叉編譯將LLVMIR轉(zhuǎn)換為將LLVMbitcode將LLVMbitcode轉(zhuǎn)回為LLVM轉(zhuǎn)換LLVM鏈接LLVM執(zhí)行LLVM使用言代碼編譯為LLVMIR(IntermediateRepresentation——中間碼)以及如何把它轉(zhuǎn)為其他與其他編譯器(例如GNUCompilerCollection——GCC)不同,LLVM的設計目標是成為在我們開始本節(jié)之前,我們需要知道一點關于匯編碼的知識。的代碼有3種表示形式:內(nèi)存編譯器中的、存于磁盤的bod,以及用戶可讀的匯編碼。MR是基于靜態(tài)單賦值1(cngegnn——)的,并且提供了類型安全性、底層操作性、靈活性,因此能夠清楚表達絕大多數(shù)高級語言。這種表示形式貫穿的各個階段。事實上,M致力于成為一種足夠底層的通用,只有這樣,高級語言的諸多特性才能夠得以實現(xiàn)。同樣,M組織良好,也具備不錯的可讀性。如果你對理解本節(jié)提到的匯編碼有任何疑問,請參考本節(jié)結(jié)尾的另請參閱一節(jié)。$catdefinei32@test1(i32%A)%B=addi32%A,0reti32%Bdefineinternali32@test(i32%X,i32%dead){reti32%Xdefinei32@caller()%A=calli32@test(i32123,i32456)reti32%A$opt–S–instcombinetestfile.ll–o$cat;ModuleID=definei32@test1(i32%A){reti32%Adefineinternali32@test(i32%X,i32%dead){reti32%Xdefinei32@caller()%A=calli32@test(i32123,i32456)reti32%A$opt–S–deadargelimtestfile.ll–o$cat;ModuleID=definei32@test1(i32%A)%B=addi32%A,0reti32%Bdefineinternali32@test(i32%X){reti32%Xdefinei32@caller()%A=calli32@test(i32123)reti32%A在前面的代碼中,我們可以看到,第1個命令運行instcombinePass,會將指令合并,因此%Baddi32%A0;reti32%B被優(yōu)化為reti32%A,并且沒有改變原來的代碼,而是產(chǎn)在第2個樣例中,運行deadargelimpass,對第一個函數(shù)沒有任何影響,但優(yōu)化對第2個函數(shù)小,而Pass之間的依賴信息由LLVMPass管理器(PassManager)來統(tǒng)一管理,在Pass運行件。圖中,PassA中PassA.o引用了LLVMPasses.a,而自定義的Pass中MyPass.oObject文件與優(yōu)化器相似,LLVM代碼生成器(codegenerator)也采用了模塊的設計理念,它將代碼交叉編譯所謂交叉編譯,指的是我們能夠在一個平臺(例如x86)編譯并構(gòu)建二進制文件,而在另一個平臺(例如)運行。編譯二進制文件的機器稱為主機(ho),進制文件的平臺我們稱為目標平臺(g)。為相同平臺(主機與目標機器相同)編譯代碼我們稱為本機編譯(nveb),而當主機與目標機器為不同平臺時編譯代碼則稱為交叉編譯(oop)。在此之前你需要為系統(tǒng)(主機平臺)安裝以下包(程序installllvmonyourhost--DCMAKE_INSTALL_PREFIX=<工具鏈安裝目錄(可選-DLLVM_TABLEGEN=<已安裝的LLVM工具鏈目錄>/llvm--DCLANG_TABLEGEN=<已安裝的LLVM工具鏈目錄>/clang--DLLVM_DEFAULT_TARGET_TRIPLE=arm-linux----DCMAKE_CXX_FLAGS='-targetarmv7a-linux-gnueabihf-mcpu=cortex-a9-I/usr/arm-$cmake-GNinja<LLVM源碼目錄><上面的選項$CC='clang'CXX='clang++'cmake-GNinja<源碼目錄><上面的選項$$ninjasysroot[1]關代碼(position-independentcode——PIC)生成過程中的絕對地址重定向,這時候可以[1]sysroot:通常指的是系統(tǒng)的根目錄,例如Linux系統(tǒng)的根目錄為。有時我們可以通過將C源碼轉(zhuǎn)換為LLVM本節(jié)將使用C語言前端——Clang,把C語言源碼轉(zhuǎn)換為LLVMIR$catmultiply.cintmult(){inta=5;intb=3;intc=a*b;returnc;使用以下命令來將C語言代碼轉(zhuǎn)換成LLVM$clang-emit-llvm-Smultiply.c-o生成如下的LLVM$cat;ModuleID=targetdatalayout="e-m:e-i64:64-f80:128-n8:16:32:64-S128"targettriple="x86_64-unknown-linux-gnu";FunctionAttrs:nounwinduwtabledefinei32@mult()#0{%a=allocai32,align%b=allocai32,align%c=allocai32,align4storei325,i32*%a,align4storei323,i32*%b,align%1=loadi32*%a,align%2=loadi32*%b,align%3=mulnswi32%1,storei32%3,i32*%c,align%4=loadi32*%c,align4reti32%4$clang-cc1-emit-llvmtestfile.c-o將C語言代碼編譯為LLVMIR的過程從詞法分析開始——將C語言源碼分解成token流,每在語言的CFG(ContextFreeGrammar,上下文無關文法)的指導下將token流組織成在第2章中,我們將會看到詞法分析、語法分析和代碼生成的工作原理。關于LLVM將LLVMIR轉(zhuǎn)換為本節(jié)將介紹如何從LLVMIR來生成bitcode。LLVMbitcode(也稱為字節(jié)碼——bytecode)由兩部分組成:位流(bitstream,可類比字節(jié)流),以及將LLVMIR編碼成位流的編碼格首先創(chuàng)建LLVMIR代碼作為llvm-as$catdefinei32@mult(i32%a,i32%b)#0%1=mulnswi32%a,%breti32%1llvm-astest.ll–ollvm-as即是LLVM的匯編器。它會將LLVMIR轉(zhuǎn)為bitcode(就像把普通的匯編碼轉(zhuǎn)成可執(zhí)為了把M轉(zhuǎn)為bod,我們引入了區(qū)塊(bok)和記錄(od)的概念。區(qū)塊表示位流的區(qū)域,例如一個函數(shù)體、符號表等。每個區(qū)塊的內(nèi)容都對應一個特定的,例如M中函數(shù)體的是12。記錄由一個記錄碼和一個整數(shù)值組成,它們描述了在指令、全局變量描述符、類型描述中的實體。LLVMIR的bitcode文件由一個簡單的封裝結(jié)構(gòu)封裝。結(jié)構(gòu)包括一個描述文件段落偏移量將LLVMbitcode本節(jié)介紹如何將LLVMbitcode前一節(jié)創(chuàng)建的test.bcbitcode文件可作為llc的輸入,通過以下命令可把LLVMbitcode轉(zhuǎn)換$llctest.bc–o$cat.file.globl.align16,.type ##BB#0:Pushq.cfi_def_cfa_offset.cfi_offset%rbp,-16movq%rsp,%rbp.cfi_def_cfa_register%rbpimull%esi,%edimovl%edi,%eaxpopq%rbp.sizemult,.Ltmp3-$clang-Stest.bc-otest.s–fomit-frame- #使用Clangmcpu=cpu參數(shù)則可以指定其CPU,而-regallocbasicgreedy/fast/pbqp則可以指定寄存器將LLVMbitcode轉(zhuǎn)回為LLVM本節(jié)介紹如何通過反匯編工具llvm-dis把LLVMbitcode轉(zhuǎn)回為LLVMIR$llvm-distest.bc–o查看其生成的LLVM|$cat;ModuleID=definei32@mult(i32%a,i32%b)#0%1=mulnswi32%a,%breti32%1llvm-dis命令即是LLVM反匯編器,它使用LLVMbitcode文件作為輸入,輸出LLVMIR。轉(zhuǎn)換LLVM$opt–passnameinput.ll–o為輸入,創(chuàng)建等價的LLVMIR:$catmultiply.cintmult(){inta=5;intb=3;intc=a*b;returnc;$clang-emit-llvm-Smultiply.c-o$cat;ModuleID=targetdatalayout="e-m:e-i64:64-f80:128-n8:16:32:64-S128"targettriple="x86_64-unknown-linux-gnu";FunctionAttrs:nounwinduwtabledefinei32@mult()#0{%a=allocai32,align%b=allocai32,align%c=allocai32,align4storei325,i32*%a,align4storei323,i32*%b,align%1=loadi32*%a,align%2=loadi32*%b,align%3=mulnswi32%1,storei32%3,i32*%c,align%4=loadi32*%c,align4reti32%4$opt-mem2reg-Smultiply.ll-o$cat;ModuleID=targetdatalayout="e-m:e-i64:64-f80:128-n8:16:32:64-S128"targettriple="x86_64-unknown-linux-gnu";FunctionAttrs:nounwinduwtabledefinei32@mult(i32%a,i32%b)#0{%1=mulnswi32%a,%breti32%1gvn為了弄明白C語言代碼是如何映射到LLVMIR的,你可以在將C代碼轉(zhuǎn)換為IR之后(在“將C源碼轉(zhuǎn)換為LLVM匯編碼”一節(jié)提到),運行mem2regPass,它會幫助你明白C指令是如鏈接LLVM$cattest1.cintfunc(inta){a=a*2;return$cattest2.cexternintfunc(inta);intmain(){intnum=5;num=func(num);printf("numberis%d\n",num);returnnum;$clang-emit-llvm-Stest1.c-o$clang-emit-llvm-Stest2.c-o$llvm-astest1.ll-o$llvm-astest2.ll-o通過如下方式使用llvm-link命令鏈接兩個LLVMbitcode$llvm-linktest1.bctest2.bc–o執(zhí)行LLVM本節(jié)介紹如何執(zhí)行之前得到的LLVMbitcode你需要先安裝lli工具,用它來執(zhí)行LLVMbitcode|$llioutput.bcnumberis10這個樣例中輸出結(jié)果是“numberis10”,這就是由之前章節(jié)中的test1.c和test2.c鏈接得到的lli工具命令執(zhí)行LLVMbitcode格式程序,它使用LLVMbitcode格式作為輸入并且使用即時使用C語言前端——創(chuàng)建一個C語言的helloworld,文件名是$cattest.cintmain(){printf("helloworld\n");return0;}$clang$./a.outhelloworld$cattest.c#defineMAX100voidfunc(){inta[MAX];$clangtest.c-E#1"test.c"#1"<built-in>"#1"<built-in>"#308"<built-in>"#1"<commandline>"#1"<built-in>"#1"test.c"2voidfunc(){inta[100];|$clang-cc1test.c-ast-TranslationUnitDecl0x3f72c50<<invalidsloc>><invalid|-TypedefDecl0x3f73148<<invalidsloc>><invalidsloc>int128_t'|-TypedefDecl0x3f731a8<<invalidsloc>><invalidsloc>uint128_t'unsigned|-TypedefDecl0x3f73518<<invalidsloc>><invalidsloc>builtin_va_list'va_list_tag`-FunctionDecl0x3f735b8<test.c:3:1,line:5:1>line:3:6func'void()'`-CompoundStmt0x3f73790<col:13,`-DeclStmt0x3f73778<line:4:1,`-VarDecl0x3f73718<col:1,col:10>col:5a'int|$clangtest.c-S-emit-llvm-o|;ModuleID=|targetdatalayout="e-m:e-i64:64-f80:128-n8:16:32:64-|targettriple="x86_64-unknown-linux-|;FunctionAttrs:nounwind|definevoid@func()#0|%a=alloca[100xi32],align|ret為了得到用于相同test.c測試碼的機器碼,需要給Clang傳遞-S參數(shù)。如果你使用了-o參|$clang-Stest.c-o .file .globl .align16, .type # |# pushq .cfi_def_cfa_offset .cfi_offset%rbp,- %rsp, .cfi_def_cfa_register func,.Ltmp3- 在只使用-S參數(shù)的情況下,編譯器會在代碼生成的過程中產(chǎn)生機器碼。這里使用了-o參使用GO|$cat|package|import|funcmain()|fmt.Println("Test$llgo-dump;ModuleID=targetdatalayout="e-p:64:64:64..."targettriple="x86_64-unknown-linux"%0=type{i8*,i8*llgo編譯器是Go語言的前端,它用test.go程序作為輸入,輸出LLVMIR關于llgo的源碼下載及安裝步驟,請參見/go-llvmllgo使用你需要GCC4.5及以上版本,目標機器為x86-32/x86-64以及ARM處理器。當然,也需要下創(chuàng)建一個簡單的helloworld$cattestprog.cintmain(){printf("hello$gcctestprog.c-S-O1-o.file".string"Hello.globl.typemain,@functionsubq$8,%rspmovl$.LC0,%edicallputsmovl$0,%eaxaddq$8,%rsp.sizemain,.-$gcctestprog.c-S-O1-o--.file"#Startoffilescopeinline.ident"GCC:(GNU)4.5.020090928(experimental)LLVM:#Endoffilescopeinline.align.globl.typemain,@functionsubq$8,%rspmovl$.L.str,%edicallputsxorl%eax,%eaxaddq$8,%rsp.sizemain,.-.type.asciz“Hello.size.L.str,.section.note.GNU-第2詞法分析器和語法分析器,以及使用前端從AST(AbstractSyntaxTree)生成IR代碼。定義TOYHandSide——LHS),終結(jié)符號[2](terminal-symbol)和非終結(jié)符的組合在右邊(RightHandSide——RHS);當遇到一個LHS,就會根據(jù)推導規(guī)則生成與之對應的RHS。numeric_expr:=paran_expr:='('expression:=:=identifier'('expr_list:=:=expression(','primary:=expression:=primarybinoprhs:=(binoperatorprimary)*binoperators:='+'/'-'/'*'/'/'func_decl:=identifier'('identifier_list')'identifier_list:=(empty):=function_defn:='def'func_decltoplevel_expr:=deffoo(x,y)x+y*16詞法分析往往是編譯程序的第一步。詞法分析器把程序代碼的輸入流切分成okn,而語法分析器則接受這些okn并把okn流構(gòu)建成(抽象語法樹)。通常來說,被解析成okn的語言是基于上下文無關語法1的。一個okn可以是一個字符串,由一個或多個同一范疇的字符組成。對輸入字符流構(gòu)建成okn的過程稱為符號化(oknon)。為了把輸入的字符分組成okn,還需要有特定的定界符。對于詞法分析來說,有自動化的詞法分析工具來完成這個工作,比如。而我們接下來展示的語言來手動實現(xiàn)的。$vim$vimenumToken_Type{EOF_TOKEN=0,staticintstaticstd::stringstaticintget_token(){staticintLastChar='';LastChar=fgetc(file);if(isalpha(LastChar)){Identifier_string=LastChar;while(isalnum((LastChar=fgetc(file))))Identifier_string+=LastChar;if(Identifier_string=="def")returnDEF_TOKEN;returnif(isdigit(LastChar)){std::stringNumStr;do{NumStr+=LastChar;LastChar=}Numeric_Val=strtod(NumStr.c_str(),0);returnNUMERIC_TOKEN;if(LastChar=='#')doLastChar=fgetc(file);while(LastChar!=EOF&&LastChar!='\n'&&LastChar!='\r');if(LastChar!=EOF)returnif(LastChar==EOF)returnintThisChar=LastChar;LastChar=fgetc(file);returnThisChar;deffoo(x,y)x+y*16上下文無關語法:Context-FreeGrammar(CFG),X>=Y,X可以被Y替換,而無須考慮X的上下文,在這里X是一個非終結(jié)符。與之對應的是上下文相關語法,aXYbXZ,在不同的推導規(guī)則中X具有不同的語義,因此稱之為上下文相關語法。——在生成之時,我們需要運行詞法分析器來得到okn。我們即將要解析的語言由表達達式等。$vimclassBaseAST{public:virtualclassVariableAST:publicBaseAST{std::stringVar_Name;//定義stringVariableAST(std::string&name):Var_Name(name)//變量ASTclassNumericAST:publicBaseAST{intnumeric_val;publicNumericAST(intval):numeric_val(val)ClassBinaryAST:publicBaseASTstd::stringBin_Operator;//用于存儲二元運算符的stringBaseAST*LHS,*RHS;//用于存儲一個二元表達式的LHS和RHS//由于LHS和RHS二元操作可以是任何類型,因此用BaseASTBinaryAST(std::stringop,BaseAST*lhs,BaseAST*rhs):Bin_Operator(op),LHS(lhs),RHS(rhs){}//初始化二元運算符、二元表達式的LHS和classFunctionDeclAST{std::stringFunc_Name;std::vector<std::string>Arguments;FunctionDeclAST(conststd::string&name,conststd::vector<std::string>&args):Func_Name(name),Arguments(args)classFunctionDefnAST{FunctionDeclAST*Func_Decl;BaseAST*Body;FunctionDefnAST(FunctionDeclAST*proto,BaseAST*body):Func_Decl(proto),Body(body){}classFunctionCallAST:publicBaseAST{std::stringFunction_Callee;std::vector<BaseAST*>Function_Arguments;FunctionCallAST(conststd::string&callee,std::vector<BaseAST*>Function_Callee(callee),Function_Arguments(args)分析器的樣例。關于Clang使用的C++AST結(jié)構(gòu)的詳細信息,請參見語法分析器(p)根據(jù)語言的語法規(guī)則來解析代碼,解析階段決定了輸入的代碼是否能夠根據(jù)既定的語法組成okn流1。在此階段會構(gòu)造出一棵解析樹,而語法分析器則會定義一些函數(shù)來把代碼組織成一種被稱為下降的解析技術自頂向下解析,并用相互遞歸的函數(shù)構(gòu)建。$vimstaticintstaticvoidnext_token(){Current_token=get_token();staticBaseAST*Base_Parser(){switch(Current_token){default:returncaseIDENTIFIER_TOKEN:returnidentifier_parser();caseNUMERIC_TOKEN:returnnumeric_parser();case'(':return[1]原文為astringoftokens,譯者認為原文可能存在錯誤,應為astreamoftokens,故根據(jù)$vistaticBaseAST*numeric_parser()BaseAST*Result=newNumericAST(Numeric_Val);returnstaticBaseAST*identifier_parser(){std::stringIdName=Identifier_string;if(Current_token!='(')returnnewVariableAST(IdName);std::vector<BaseAST*>Args;if(Current_token!=')'){while(1)BaseAST*Arg=expression_parser();if(!Arg)return0;if(Current_token==')')if(Current_token!=',')return0;returnnewFunctionCallAST(IdName,staticFunctionDeclAST*func_decl_parser(){if(Current_token!=IDENTIFIER_TOKEN)return0;std::stringFnName=Identifier_string;if(Current_token!='(')return0;std::vector<std::string>Function_Argument_Names;while(next_token()==IDENTIFIER_TOKEN)if(Current_token!=')')return0;returnnewFunctionDeclAST(FnName,staticFunctionDefnAST*func_defn_parser(){FunctionDeclAST*Decl=func_decl_parser();if(Decl==0)return0;if(BaseAST*Body=expression_parser())returnnewFunctionDefnAST(Decl,Body);return0;staticBaseAST*expression_parser(){BaseAST*LHS=Base_Parser();if(!LHS)return0;returnbinary_op_parser(0,$vistaticstd::map<char,-<+</<staticvoidinit_precedence(){Operator_Precedence['-']=Operator_Precedence['+']=Operator_Precedence['/']=Operator_Precedence['*']=staticintgetBinOpPrecedence(){return-intTokPrec=Operator_Precedence[Current_token];if(TokPrec<=0)return-1;returnstaticBaseAST*binary_op_parser(intOld_Prec,BaseAST*LHS)while(1)intOperator_Prec=getBinOpPrecedence();if(Operator_Prec<Old_Prec)returnintBinOp=Current_token;BaseAST*RHS=Base_Parser();if(!RHS)return0;intNext_Prec=getBinOpPrecedence();if(Operator_Prec<Next_Prec){RHS=binary_op_parser(Operator_Prec+1,RHS);if(RHS==0)return0;LHS=newBinaryAST(std::to_string(BinOp),LHS,staticBaseAST*paran_parser(){BaseAST*V=expression_parser();if(!V)return0;if(Current_token!=')')return0;returnstaticvoidHandleDefn()if(FunctionDefnAST*F=func_defn_parser()){if(Function*LF=F->Codegen()){elsestaticvoidHandleTopExpression(){if(FunctionDefnAST*F=top_level_parser()){if(Function*LF=F->Codegen())else見/doxygen/classclang_1_1Parser.html。$vistaticvoidDriver(){while(1){switch(Current_token){caseEOF_TOKEN:return;case';':next_token();caseDEF_TOKEN:HandleDefn();break;default:HandleTopExpression();break;intmain(intargc,char*argv[]){LLVMContext&Context=getGlobalContext();file=fopen(argv[1],"r");if(file==0){printf("CouldnotopenModule_Ob=newModule("mycompiler",Context);return0;對TOY$clang++toy.cpp-O3-o$catexampledeffoo(x,y)x+y*16$./toy編譯器會以讀模式打開xp文件,并且將單詞組織成okn流。如果遇到d關鍵字即返回_,然后調(diào)用ndn函數(shù),它會存儲函數(shù)名和參數(shù)。程序會遞歸地檢查okn的類型,然后調(diào)用特定的okn處理函數(shù),把信息存儲在各自的中。理,請參見/docs/tutorial/LangImpl2.html#parser-basics。為每個AST類定義IR現(xiàn)在所有必要信息都存儲于AST這一數(shù)據(jù)結(jié)構(gòu)中,下一階段即是從AST生成LLVMIR。在的LLVMIR。為了生成LLVMIR,我們需要在每個AST類定義一個CodeGen虛函數(shù)(AST類在前面的$viclassBaseASTvirtualValue*Codegen()=classNumericAST:publicBaseASTvirtualValue*classVariableAST:publicBaseASTvirtualValue*這一函數(shù)返回值是LLVMValue對象,它表示了靜態(tài)單賦值(SSA)對象。在Codegen過程staticModulestaticIRBuilder<>Builder(getGlobalContext());staticstd::map<std::string,Value*>Named_Values;Builder對象幫助生成LLVMIR并且記錄程序的當前點,以插入LLVMNamed_Valuesmap對象記錄當前作用域中的所有已定義值,充當符號表的功能。對我們?yōu)楸磉_式生成IR為了實現(xiàn)TOY語言的LLVMIR$viValue*NumericAST::Codegen()returnConstantInt::get(Type::getInt32Ty(getGlobalContext()),Value*VariableAST::Codegen(){Value*V=Named_Values[Var_Name];returnV?V:0;Value*BinaryAST::Codegen(){Value*L=LHS->Codegen();Value*R=RHS->Codegen();if(L==0||R==0)return0;switch(atoi(Bin_Operator.c_str()))case'+':returnBuilder.CreateAdd(L,R,"addtmp");case'-':returnBuilder.CreateSub(L,R,"subtmp");case'*':returnBuilder.CreateMul(L,R,"multmp");case'/':returnBuilder.CreateUDiv(L,R,"divtmp");default:return0;為函數(shù)生成IRValue*FunctionCallAST::Codegen(){Function*CalleeF=for(unsignedi=0,e=Function_Arguments.size();i!=e;++i){if(ArgsV.back()==0)return0;returnBuilder.CreateCall(CalleeF,ArgsV,Function*FunctionDeclAST::Codegen(){std::vector<Type*>Integers(Arguments.size(),Type::getInt32Ty(geFunctionType*FT=FunctionType::get(Type::getInt32Ty(getGlobalContext()),Integers,false);Function*F=Function::Create(FT,Function::ExternalLinkage,Func_Name,Module_Ob);if(F->getName()!=Func_Name){F=Module_Ob-if(!F->empty())returnif(F->arg_size()!=Arguments.size())returnunsignedIdx=for(Function::arg_iteratorArg_It=F->arg_begin();Idx!=Arguments.size();++Arg_It,++Idx){Named_Values[Arguments[Idx]]=Arg_It;returnFunction*FunctionDefnAST::Codegen(){Function*TheFunction=Func_Decl->Codegen();if(TheFunction==0)return0;BasicBlock*BB=BasicBlock::Create(getGlobalContext(),"entry",if(Value*RetVal=Body->Codegen()){returnTheFunction;return0;好了,LLVMIR已經(jīng)準備好了!這些Codegen()函數(shù)會被解析頂層表達式的包裝函數(shù)調(diào)staticvoidHandleDefn()if(FunctionDefnAST*F=func_defn_parser()){if(Function*LF=F->Codegen()){elsestaticvoidHandleTopExpression(){if(FunctionDefnAST*F=top_level_parser()){if(Function*LF=F->Codegen())else所以,在成功解析之后,相應的Codegen()函數(shù)會被調(diào)用以生成LLVMIR。而dump()函數(shù)llvm-config--cxxflags--ldflags--system-libs--libs$clang++-O3toy.cpp`llvm-config--cxxflags--ldflags--system-libs--libs當toy編譯器在example程序上運行的時候,它會生成如下的LLVM$./toydefinei32@foo(i32%x,i32%y){%multmp=muli32%y,%addtmp=addi32%x,%multmpreti32%addtmp$catexample2foo(5,6);會得到如下的LLVM$./toyexample2definei32@1(){%calltmp=calli32@foo(i325,i326)reti32%calltmp增加IRstaticFunctionPassManagerFunctionPassManagerGlobal_FP=&My_FP;Function*FunctionDefnAST::Codegen(){Function*TheFunction=Func_Decl->Codegen();if(!TheFunction)return0;BasicBlock*BB=BasicBlock::Create(getGlobalContext(),"entry",if(Value*Return_Value=Body->Codegen()){return0;第3擴展前端并增加JITAST,實現(xiàn)了語法分析器,并且為語言生成了LLVMIR代碼。另外,我們也展示了如何在處理條件控制結(jié)構(gòu)——if/then/elseifx<2thenx+yx-ystaticvoidinit_precedence(){Operator_Precedence['<']=Value*BinaryAST::Codegen()case'<'L=Builder.CreateICmpULT(L,R,returnBuilder.CreateZExt(L,現(xiàn)在,LLVMIR將生成一個比較指令及布爾指令作為比較結(jié)果,而比較結(jié)果則會決定程enumstaticintget_token()if(Identifier_string=="def")returnDEF_TOKEN;if(Identifier_string=="if")returnIF_TOKEN;if(Identifier_string=="then")returnTHEN_TOKEN;if(Identifier_string=="else")returnclassExprIfAST:publicBaseAST{BaseAST*Cond,*Then,*Else;ExprIfAST(BaseAST*cond,BaseAST*then,BaseAST*:Cond(cond),Then(then),Else(else_st){}Value*Codegen()override;staticBaseAST*If_parser(){BaseAST*Cond=expression_parser();if(!Cond)returnif(Current_token!=THEN_TOKEN)return0;BaseAST*Then=expression_parser();if(Then==0)returnIf(Current_token!=ELSE_TOKEN)return0;BaseAST*Else=expression_parser();if(!Else)returnreturnnewExprIfAST(Cond,Then,解析邏輯倒是很簡單:首先查找iftoken,以及解析緊隨其后的條件表達式。之后是標識thentoken,以及解析true條件表達式;最后是查找elsetoken和解析false條件表達式。staticBaseAST*Base_Parser(){switch(Current_token){caseIF_TOKEN:returnLLVMIR了,讓我們來定義Codegen()Value*ExprIfAST::Codegen(){Value*Condtn=Cond->Codegen();if(Condtn==0)returnCondtn=Condtn,Builder.getInt32(0),Function*TheFunc=Builder.GetInsertBlock()-BasicBlock*ThenBBBasicBlock::Create(getGlobalContext(),"then",TheFunc);BasicBlock*ElseBB=BasicBlock::Create(getGlobalContext(),BasicBlock*MergeBB=BasicBlock::Create(getGlobalContext(),Builder.CreateCondBr(Condtn,ThenBB,ElseBB);Value*ThenVal=Then->Codegen();if(ThenVal==0)returnThenBB=Value*ElseVal=Else->Codegen();if(ElseVal==0)returnElseBB=TheFunc-PHINode*Phi=Builder.CreatePHI(Type::getInt32Ty(getGlobalContext()),2,"iftmp");Phi->addIncoming(ThenVal,ThenBB);Phi->addIncoming(ElseVal,ElseBB);returnPhi;$g++-gtoy.cpp`llvm-config--cxxflags--ldflags--system-libs--libscore`-O3-otoy$videfifx<3thenfib(x-1)+fib(x-$./toy;ModuleID='mytargetdatalayout="e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-definei32@fib(i32%x){%cmptmp=icmpulti32%x,bri1%cmptmp,label%ifcont,label ;preds=%subtmp=addi32%x,-%calltmp=calli32@fib(i32%subtmp1=addi32%x,-%calltmp2=calli32@fib(i32%addtmp=addi32%calltmp2,%calltmpbrlabel%ifcont ;preds=%iftmp=phii32[%addtmp,%else],[1,%entry]reti32%iftmp解析器會識別hn結(jié)構(gòu)以及根據(jù)條件真假執(zhí)行的相應語句,將數(shù)據(jù)存儲于中,以構(gòu)建。之后代碼生成器會把轉(zhuǎn)成M,條件語句隨之生成。無論條件為真還是假,都會生成關于Clang如何處理C++語言的ifelse語句的一些具體樣例,請參見fori=1,i<n,1inx+y;初始化表達式是i1,終止條件是in,第1行代碼表示i以1的東西叫PHI節(jié)點,它會選擇來自不同分支的i,因為我們的IR是SSA(singlestatic基本塊(兩條不同的路徑),為了在SSA形式的LLVMIR中表達這種分支情況,需要用%i=phii32[1,%entry],[%nextvar,%loop有循環(huán)變量的AST數(shù)據(jù)結(jié)構(gòu),以及定義語法分析器和Codegen()函數(shù)來生成LLVMIR:enumToken_Typestaticintget_token()if(Identifier_string==returnif(Identifier_string=="for")returnFOR_TOKEN;if(Identifier_string=="in")returnIN_TOKEN;classExprForAST:publicBaseAST{std::stringVar_Name;BaseAST*Start,*End,*Step,ExprForAST(conststd::string&varname,BaseAST*start,BaseAST*step,BaseAST:Var_Name(varname),Start(start),End(end),Step(step),Body(body){}Value*Codegen()staticBaseAST*For_parser(){if(Current_token!=IDENTIFIER_TOKEN)return0;std::stringIdName=Identifier_string;if(Current_token!='=')return0;BaseAST*Start=expression_parser();if(Start==0)returnif(Current_token!=',')return0;BaseAST*End=expression_parser();if(End==0)returnBaseAST*Step=if(Current_token==','){Step=expression_parser();if(Step==0)returnif(Current_token!=IN_TOKEN)return0;BaseAST*Body=expression_parser();if(Body==0)returnreturnnewExprForAST(IdName,Start,End,Step,再定義Codegen()函數(shù)生成LLVMValue*ExprForAST::Codegen()Value*StartVal=Start->Codegen();if(StartVal==0)returnFunction*TheFunction=Builder.GetInsertBlock()->getParent();BasicBlock*PreheaderBB=Builder.GetInsertBlock();BasicBlock*LoopBB=BasicBlock::Create(getGlobalContext(),"loop",TheFunction);PHINode*Variable=Builder.CreatePHI(Type::getInt32Ty(getGlobalContext()),2,Var_Name.c_str());Variable->addIncoming(StartVal,Value*OldVal=Named_Values[Var_Name];Named_Values[Var_Name]=Variable;if(Body->Codegen()==0)return0;Value*StepVal;if(Step){StepVal=Step->Codegen();if(StepVal==0)return}elseStepVal=ConstantInt::get(Type::getInt32Ty(getGlobalContext()),1);Value*NextVar=Builder.CreateAdd(Variable,StepVal,Value*EndCond=End->Codegen();if(EndCond==0)returnEndCond=EndCond,ConstantInt::get(Type::getInt32Ty(getGlobalContext()),0),"loopcond");BasicBlock*LoopEndBB=Builder.GetInsertBlock();BasicBlock*AfterBB=BasicBlock::Create(getGlobalContext(),Builder.CreateCondBr(EndCond,LoopBB,AfterBB);Variable->addIncoming(NextVar,if(OldVal)Named_Values[Var_Name]=OldVal;returnConstant::getNullValue(Type::getInt32Ty(getGlobalConte$g++-gtoy.cpp`llvm-config--cxxflags--ldflags--system-libs--libscore`-O3-otoy$videfprintstar(nfori=1,i<n,1.0inx+1$./toy前面的for循環(huán)代碼會生成以下的LLVM;ModuleID=‘mytargetdatalayout=“e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-definei32@printstar(i32%n,i32%x){brlabel%i=phii32[1,%entry],[%nextvar,%nextvar=addi32%i,%cmptmp=icmpulti32%i,bri1%cmptmp,label%loop,labelReti320體。然后它會像之前做的那樣,把塊轉(zhuǎn)成LLVMIR。|(邏輯或運算符)TOY|defbinary|(LHSRHS)ifLHSthenelseifRHSthenenumToken_Typestaticintget_token()if(Identifier_string=="in")returnif(Identifier_string=="binary")returnclassFunctionDeclAST{std::stringFunc_Name;std::vector<std::string>Arguments;boolisOperator;unsignedPrecedence;FunctionDeclAST(conststd::string&name,conststd::vector<std::string>&args,boolisoperator=false,unsignedprec=:Func_Name(name),Arguments(args),isOperator(isoperator),Precedence(prec){}boolisUnaryOp()const{returnisOperator&&==1;boolisBinaryOp()const{returnisOperator&&==2;chargetOperatorName()const{assert(isUnaryOp()||isBinaryOp());returnFunc_Name[Func_Name.size()-1];unsignedgetBinaryPrecedence()const{returnFunctionstaticFunctionDeclAST*func_decl_parser(){std::stringFnName;unsignedKind=unsignedBinaryPrecedence=switch(Current_token){returncaseFnName=Identifier_string;Kind=0;caseUNARY_TOKEN:if(!isascii(Current_token))return0;FnName=FnName+=(char)Current_token;Kind=1;caseif(!isascii(Current_token))return0;FnName=FnName+=(char)Current_token;Kind=2;if(Current_token==NUMERIC_TOKEN)if(Numeric_Val<1||Numeric_Val>100)return0;BinaryPrecedence=(unsigned)Numeric_Val;if(Current_token!='(')return0;std::vector<std::string>Function_Argument_Names;while(next_token()==IDENTIFIER_TOKEN)if(Current_token!=')')return0;if(Kind&&Function_Argument_Names.size()!=Kind)return0;returnnewFunctionDeclAST(FnName,Function_Argument_Names,Kind!=0,BinaryPrecedence);Value*BinaryAST::Codegen(){Value*L=LHS->Codegen();Value*R=RHS->Codegen();switch(Bin_Operator){case'+':returnBuilder.CreateAdd(L,R,"addtmp");case'-':returnBuilder.CreateSub(L,R,"subtmp");case'*':returnBuilder.CreateMul(L,R,"multmp");case'/':returnBuilder.CreateUDiv(L,R,"divtmp");case'<':L=Builder.CreateICmpULT(L,R,returnBuilder.CreateUIToFP(L,Type::getIntTy(getGlobalContext()),defaultFunction*F=TheModule->getFunction(std::string("binary")+Op);Value*Ops[2]={L,R};returnBuilder.CreateCall(F,Ops,Function*FunctionDefnAST::Codegen(){Function*TheFunction=Func_Decl->Codegen();if(!TheFunction)return0;if(Func_Decl-Operator_Precedence[Func_Decl->getOperatorName()]=Func_BasicBlock*BB=BasicBlock::Create(getGlobalContext(),"entry",if(Value*Return_Value=Body->Codegen()){$g++-gtoy.cpp`llvm-config--cxxflags--ldflags--system-libs--libscore`-O3-otoy$videfbinary|5(LHSRHS)ifLHSthenelseifRHSthen$./toyexampleoutput:;ModuleID='mytargetdatalayout="e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128"definei32@"binary|"(i32%LHS,i32%RHS){%ifcond=icmpeqi32%LHS,%ifcond1=icmpeqi32%RHS,%.=selecti1%ifcond1,i320,i32%iftmp5=selecti1%ifcond,i32%.,i321reti32%iftmp5defunary!(v)ifvthen如果變量v的值是真,則返回0,否則返回1首先在toy.cpp文件中為一元運算符定義enumenumToken_Type然后識別一元運算符字符串,返回staticintget_token()if(Identifier_string=="in")returnif(Identifier_string=="binary")returnBINARY_TOKEN;if(Identifier_string=="unary")returnUNARY_TOKEN;接著為一元運算符定義classExprUnaryAST:publicBaseAST{charOpcode;BaseAST*Operand;ExprUnaryAST(charopcode,BaseAST:Opcode(opcode),Operand(operand)virtualValuestaticBaseAST*unary_parser()if(!isascii(Current_token)||Current_token=='('||Current_token==',')returnBase_Parser();intOp=Current_token;if(ExprAST*Operand=unary_parser())returnnewExprUnaryAST(Opc,Operand);returnstaticBaseAST*binary_op_parser(intOld_Prec,BaseAST*LHS)while(1)intOperator_Prec=if(Operator_Prec<Old_Prec)returnLHS;intBinOp=Current_token;BaseAST*RHS=unary_parser();if(!RHS)returnintNext_Prec=getBinOpPrecedence();if(Operator_Prec<Next_Prec){RHS=binary_op_parser(Operator_Prec+1,RHS);if(RHS==0)returnLHS=newBinaryAST(std::to_string(BinOp),LHS,staticBaseAST*expression_parser(){BaseAST*LHS=unary_parser();ifreturnreturnbinary_op_parser(0,staticFunctionDeclAST*func_decl_parser(){std::stringFunction_Name=Identifier_string;unsignedKind=0;unsignedBinaryPrecedence=30;switch(Current_token){return0;caseFunction_Name=Identifier_string;Kind=0;caseUNARY_TOKEN:if(!isascii(Current_token))Function_Name="unary";Function_Name+=(char)Current_token;Kind=1;caseif(!isascii(Current_token))return0;Function_Name="binary";Function_Name+=(char)Current_token;Kind=2;if(Current_token==NUMERIC_TOKEN)if(Numeric_Val<1||Numeric_Val>100)return0;BinaryPrecedence=(unsigned)Numeric_Val;if(Current_token!='('){printf("errorinfunctiondeclaration");return0;std::vector<std::string>Function_Argument_Names;while(next_token()==IDENTIFIE
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025姐弟車輛財產(chǎn)贈與合同
- 2025租賃承包合同范本
- 2025短期勞動合同范本【標準】
- 2025年門面租賃合同書范本
- 2025解除合同的勞動合同法規(guī)定
- 2025電梯租賃合同
- 《銀屑病樣皮炎》課件
- 《直腸癌護理》課件
- 《中國心理咨詢發(fā)展史》課件
- 嬰兒及兒童期癲癇及癲癇綜合征的臨床護理
- 甲亢病人護理講課
- 2025年中國銅鋁復合母線行業(yè)市場運行現(xiàn)狀及投資戰(zhàn)略研究報告
- (高清版)DB1331∕T 072-2024 《雄安新區(qū)高品質(zhì)飲用水工程技術規(guī)程》
- 2025年金麗衢十二校高三語文第二次模擬聯(lián)考試卷附答案解析
- 廣東省深圳市福田區(qū)2023-2024學年六年級下學期英語期中試卷(含答案)
- 2023-2024學年廣東省廣州七中七年級(下)期中數(shù)學試卷(含答案)
- 2025年北京城市排水集團有限責任公司招聘筆試參考題庫含答案解析
- 課件-2025年春季學期 形勢與政策 第一講-加快建設社會主義文化強國
- 2025年山東惠民縣農(nóng)業(yè)投資發(fā)展限公司招聘10人歷年高頻重點提升(共500題)附帶答案詳解
- 大學美育知到智慧樹章節(jié)測試課后答案2024年秋長春工業(yè)大學
- 《基于嵌入式Linux的農(nóng)業(yè)信息采集系統(tǒng)設計與研究》
評論
0/150
提交評論