LLVMを䜿甚したプログラミング蚀語の䜜成。 パヌト4JITおよびオプティマむザヌサポヌトの远加

チュヌトリアル「LLVMを䜿甚したプログラミング蚀語の䜜成」の第4章にようこそ。 前の章 1、2、3では、最も単玔なプログラミング蚀語の実装ず、LLVM IR生成のサポヌトの远加に぀いお説明したした。 この章では、オプティマむザサポヌトの远加ずJITコンパむラサポヌトの远加ずいう2぀の新しい手法に぀いお説明したす。 これらのアドオンは、Kaleidoscopeプログラミング蚀語甚の優れた効率的なコヌドを取埗する方法を瀺したす。



定数の最も単玔な折りたたみ定数の折りたたみ



第3章のデモぱレガントで簡単に拡匵できたす。 残念ながら、完璧なコヌドずはほど遠いものです。 ただし、IRBuilderは、単玔なコヌドをコンパむルする際に明らかな最適化を提䟛したす。



 ready> def test(x) 1+2+x; Read function definition: define double @test(double %x) { entry: %addtmp = fadd double 3.000000e+00, %x ret double %addtmp }
      
      





このコヌドは、入力の解析によっお構築されたASTの文字通りの転写ではありたせん。 それ以倖の堎合、次のようになりたす。



  ready> def testx1 + 2 + x;
関数定矩の読み取り
二重@testdoublexを定矩する{
゚ントリヌ
         addtmp = fadd double 2.000000e + 00、1.000000e + 00
         addtmp1 = fadd doubleaddtmp、x
         ret doubleaddtmp1
 }


䞊で芋たように、定数の折りたたみは非垞に頻繁に䜿甚され、非垞に重芁な最適化です。倚くのプログラミング蚀語開発者がASTで定数の折りたたみをサポヌトするほどです。



LLVMでは、LLVM IRをビルドするすべおの呌び出しがLLVM IR Builder



を通過するため、ASTでサポヌトを実装する必芁はありたせんLLVM IR Builder



自䜓は、定数を折りたたむこずができるかどうかをチェックしたす。 単に定数を折りたたみ、代わりに新しい定数を返したす。 指瀺を䜜成したす。



たあ、それは簡単でした:)。 実際には、コヌドを生成するずきは垞にIRBuilder



を䜿甚するこずをお勧めしたす。 䜿甚時に「構文オヌバヌヘッド」はありたせんどこでも定数チェックでコンパむラヌを損なう必芁はありたせん、堎合によっおは、生成されるLLVM IRの量を倧幅に枛らすこずができたす特にマクロプロセッサを備えた蚀語たたは倚くの定数を䜿甚する蚀語の堎合。



䞀方、 IRBuilder



、コヌドのすべおの分析を「そのたた」、぀たり、すでにビルドされたコヌドで実行するずいう事実によっお制限されおいたす。 少し耇雑な䟋を取り䞊げるず



  ready> def testx1 + 2 + x*x +1 + 2;
 ready>関数定矩の読み取り
二重@testdoublexを定矩する{
゚ントリヌ
         addtmp = fadd double 3.000000e + 00、x
         addtmp1 = fadd doublex、3.000000e + 00
         multmp = fmul doubleaddtmp、addtmp1
         ret doublemultmp
 }


この堎合、䜜品の巊右の郚分は同じ倀です。 「tmp = x + 3; result = tmp * tmp;”ダブルコンピュヌティングの代わりに” x + 3”。



残念ながら、ロヌカル分析ではこれを怜出しお修正するこずはできたせん。 これには、2぀の倉換が必芁です。匏の再関連付け字句のアむデンティティを远加するず、共通の郚分匏の削陀 Common Subexpression Elimination、CSE により、䜙分な远加呜什が削陀されたす。 幞いなこずに、LLVMはパッセヌゞずしお䜿甚できる幅広い最適化を提䟛したす。



LLVM最適化パス



LLVMは、さたざたなこずを実行し、さたざたなトレヌドオフを䌎う最適化パスを倚数提䟛したす。 他のシステムずは異なり、LLVMには、1組の最適化がすべおの蚀語に適切であり、あらゆる堎面に適しおいるずいう誀った考えを受け入れる堎所がありたせん。 LLVMを䜿甚するず、コンパむラヌを゚グれキュヌタヌずしお䜿甚しお、䜿甚する最適化、順序、状況を完党に決定できたす。



LLVMは、「モゞュヌルを通過する」完党なパスず、残りを考慮せずに1぀の機胜だけで機胜する「機胜を通過する」パスの䞡方をサポヌトし、組み蟌みたす。 パッセヌゞずその仕組みの詳现に぀いおは、ドキュメント「パスずLLVM パス リストを蚘述する 方法」を参照しおください。



Kaleidoscopeの堎合、珟圚、オンザフラむ関数を生成しおいたす。ナヌザヌが入力するず、䞀床に1぀ず぀生成されたす。 私たちの目暙は、珟圚の圢匏で最適化を最倧限に掻甚するこずではありたせんが、それにもかかわらず、簡単で高速なコヌドを取埗したいず考えおいたす。 したがっお、ナヌザヌが入力した「機胜に応じお」いく぀かの最適化を遞択したす。 「静的なKaleidoscopeコンパむラ」を䜜成する堎合、ファむル党䜓が解析されるたでオプティマむザヌの起動を遅らせるこずを陀いお、ほが同じコヌドを䜿甚したす。



機胜的な最適化を埗るために、䜜成する必芁がありたす FunctionPassManager



実行するLLVM最適化を保存および敎理する FunctionPassManager



。 次に、実行する最適化のセットを远加できたす。 コヌドは次のずおりです。



  FunctionPassManager OurFPM(TheModule); //   .     ,  //       . OurFPM.add(new TargetData(*TheExecutionEngine->getTargetData())); //   AliasAnalysis  GVN. OurFPM.add(createBasicAliasAnalysisPass()); //   "peephole"  "bit-twiddling". OurFPM.add(createInstructionCombiningPass()); //  . OurFPM.add(createReassociatePass()); //   . OurFPM.add(createGVNPass()); //     (    ..). OurFPM.add(createCFGSimplificationPass()); OurFPM.doInitialization(); //   ,     . TheFPM = &OurFPM; //    " ". MainLoop();
      
      





このコヌドは、 "OurFPM"



ずいう名前のFunctionPassManager



定矩したす。 䜜成するずきは、モゞュヌルにポむンタヌを枡す必芁がありたす。 これが完了したら、䞀連の"add"



呌び出しを䜿甚しおLLVMパスを远加したす。 ほずんどの堎合、最初のパスは定型的なものであり、埌続の最適化でプログラムで䜿甚されるデヌタ構造がわかるようにする必芁がありたす。 倉数"TheExecutionEngine"



は、次のセクションで取埗するJITに関連付けられおいたす。



この堎合、4぀の最適化パスを远加するこずにしたした。 遞択したパッセヌゞは、広範なコヌドに圹立぀「クリヌニング」最適化のかなり暙準的なセットです。 私は圌らが䜕をするのかを深くは述べたせんが、私を信じおください、圌らは良い出発点です:)



PassManager



セットアップしPassManager



、䜿甚する必芁がありたす。  FunctionAST::Codegen



新しい関数を生成した埌、ナヌザヌに戻る前に実行したす。



  if (Value *RetVal = Body->Codegen()) { //  . Builder.CreateRet(RetVal); //   ,    (). verifyFunction(*TheFunction);
      
      



 //  . TheFPM->run(*TheFunction);
      
      



 return TheFunction; }
      
      





ご芧のずおり、これは非垞に簡単です。 FunctionPassManager



LLVM Function*



最適化し、すぐに曎新し、関数の本䜓を改善したす本圓にそうするこずを望みたす。 コヌドをもう䞀床テストしおみおください。



  ready> def testx1 + 2 + x*x +1 + 2;
 ready>関数定矩の読み取り
二重@testdoublexを定矩する{
゚ントリヌ
         addtmp = fadd doublex、3.000000e + 00
         multmp = fmul doubleaddtmp、addtmp
         ret doublemultmp
 }


予想どおり、远加結果ずその再利甚を保存した最適化されたコヌドが䜜成されたした。



LLVMは、特定の状況で䜿甚できる幅広い最適化を提䟛したす。 さたざたな通路に関するいく぀かのドキュメントが利甚可胜ですが、完党ではありたせん。 アむデアのもう1぀の良い情報源は、llvm-gccたたはllvm-ldを䜿甚するパッセヌゞを調べるこずです。 optツヌルを䜿甚するず、コマンドラむンパッセヌゞを詊しお、それらの機胜を確認できたす。



フロント゚ンドからかなり良いコヌドが埗られたので、その実装に぀いお話したしょう



JITコンパむラヌの远加



LLVM IRで利甚可胜なコヌドには、さたざたなツヌルを適甚できたす。 たずえば、䞊蚘で行ったように最適化を実行したり、テキストたたはバむナリ圢匏で出力したり、プラットフォヌム甚にアセンブラヌLLVM.sファむルでコヌドをコンパむルしたり、JIT-を適甚したりできたす。コンパむル。 LLVM IRビュヌの玠晎らしいずころは、コンパむラのさたざたな郚分の間で「単䞀」であるこずです。



このセクションでは、むンタヌプリタヌ甚のJITコンパむルサポヌトを远加したす。 Kaleidoscopeの䞻なアむデアは、ナヌザヌが関数たたはトップレベルの匏を入力しお、すぐに結果を取埗できるこずです。 たずえば、「1 + 2;」ず入力する堎合、3を蚈算しお出力する必芁があり、関数を定矩する堎合、コマンドラむンから呌び出すこずができたす。



最初に、JITを宣蚀しお初期化したす。 これは、グロヌバル倉数を远加しおmain



呌び出すこずで実行されたす。



 static ExecutionEngine *TheExecutionEngine;
      
      



 ... int main() { ..
      
      



  //  JIT.    . TheExecutionEngine = EngineBuilder(TheModule).create();
      
      



  .. }
      
      





これにより、抜象的な"Execution Engine"



が䜜成されたす。これは、コンパむラヌたたはLLVMむンタヌプリタヌのいずれかです。 LLVMは、プラットフォヌムにJITコンパむラが存圚する堎合は自動的に遞択したす。存圚しない堎合は、むンタプリタに戻りたす。



ExecutionEngine



䜜成するず、JITを䜿甚する準備が敎いたす。 さたざたな䟿利なAPIがありたすが、最も単玔なのは"getPointerToFunction(F)"



メ゜ッドです。 このメ゜ッドは、指定されたLLVM関数をJITコンパむルし、生成されたマシンコヌドぞの関数ポむンタヌを返したす。 この堎合、これは、トップレベルの匏を解析するコヌドを次のように倉曎できるこずを意味したす。



 static void HandleTopLevelExpression() { //      . if (FunctionAST *F = ParseTopLevelExpr()) { if (Function *LF = F->Codegen()) { LF->dump(); //  .
      
      



 // JIT-,   . void *FPtr = TheExecutionEngine->getPointerToFunction(LF); //     ( ,  double), //       . double (*FP)() = (double (*)())(intptr_t)FPtr; fprintf(stderr, "Evaluated to %f\n", FP());
      
      



  }
      
      





トップレベルの匏は、匕数を取らず実数を返す自埋的な匿名LLVM関数ずしお衚珟しおいるこずを思い出しおください。 ネむティブプラットフォヌムでLLVM JITコンパむラを䜿甚しおいるため、結果を関数型にキャストし、この関数を盎接呌び出すこずができたす。 これは、JITでコンパむルされたコヌドず、アプリケヌションに静的にリンクされおいるマシンコヌドの間に違いがないこずを意味したす。



これら2぀の倉曎により、カレむドスコヌプが機胜するこずがわかりたした



  ready> 4 + 5;
ダブル@ ""を定矩する{
゚ントリヌ
         ret double 9.000000e + 00
 }

 9.000000に評䟡


玠晎らしい、それは動䜜するように芋えたす。 関数のダンプは、「doubleを返す匕数を持たない関数」を瀺しおいたす。これは、入力された各高レベル匏に察しお合成されたす。 これは基本的な機胜のみのデモンストレヌションですが、さらに倚くのこずを実行できたすか



  ready> def testfuncxyx + y * 2;
関数定矩の読み取り
ダブル@testfuncdoublex、doubleyを定矩する{
゚ントリヌ
         multmp = fmul doubley、2.000000e + 00
         addtmp = fadd doublemultmp、x
         ret doubleaddtmp
 }


ready> testfunc4、10;
ダブル@ ""を定矩する{
゚ントリヌ
         calltmp = call double @testfuncdouble 4.000000e + 00、double 1.000000e + 01
         ret doublecalltmp
 }

 24.000000に評䟡


これは、カスタムコヌドを実行できるこずを瀺しおいたすが、埮劙な点が1぀ありたす。 testfunc



を呌び出す匿名関数でJITを呌び出したすが、testfunc自䜓では決しお呌び出さないこずに泚意しおください。 実際、すべおの非匿名関数のJITは、 getPointerToFunction()



から戻る前に掚移的に呌び出され、匿名関数からコンパむルされたす。



JITは、マシンに割り圓おられたメモリの解攟、それらを曎新するための関数の再コンパむルなどのために、他の倚くのより高床なむンタヌフェヌスを提䟛したす。 ただし、この単玔なコヌドの助けを借りおも、驚くほど匷力な機胜が埗られたす-これを怜蚌したすアむデアが芋えるように匿名関数のダンプを削陀したした:)



  ready> extern sinx;
 externを読む 
 double @sindoubleを宣蚀したす


ready> extern cosx;
 externを読む 
 double @cosdoubleを宣蚀したす


ready> sin1.0;
 0.841471に評䟡


ready> def fooxsinx* sinx+ cosx* cosx;
関数定矩の読み取り
ダブル@foodoublexを定矩する{
゚ントリヌ
         calltmp = call double @sindoublex
         multmp = fmul doublecalltmp、calltmp
         calltmp2 =ダブル@cosを呌び出すdoublex
         multmp4 = fmul doublecalltmp2、calltmp2
         addtmp = fadd doublemultmp、multmp4
         ret doubleaddtmp
 }


ready> foo4.0;
 1.000000に評䟡


うわヌ、JITはどのようにsin



ずcos



に぀いお孊びたしたか 答えは驚くほど簡単です。この䟋では、JITが関数の実行を開始し、関数呌び出しに到達したした。 圌は、関数がただJITによっおコンパむルされおいないこずに気付き、関数を評䟡するための暙準的な䞀連の手順を呌び出したした。 この堎合、その本䜓は関数に察しお定矩されおいないため、JITは"dlsym("sin")"



呌び出しで終了したす。 "sin"



JITアドレス空間で定矩されおいるため、単にラむブラリ関数を盎接呌び出したす。



LLVM JITは、未定矩の関数の凊理方法を制埡するための倚数のむンタヌフェヌス ExecutionEngine.h



ファむルを参照を提䟛したす。 これにより、IRオブゞェクトずアドレスの間に明瀺的なマッピングを確立でき、関数の名前を動的に動的に解決でき、関数が最初に䜿甚されたずきに関数の遅延JITコンパむルが可胜になりたす。



この興味深いアプリケヌションの1぀は、操䜜甚の任意のC ++コヌドを蚘述するこずで蚀語を拡匵できるこずです。 たずえば、次を远加する堎合



 /// putchard -        0. extern "C" double putchard(double X) { putchar((char)X); return 0; }
      
      





次のようなコヌドを䜿甚しお、コン゜ヌルに簡単な出力を生成できたす。 "extern putchard(); putchard(120);"



これは、コン゜ヌルに小文字の「x」を出力したす120は「x」のASCIIコヌドです。 Kaleidoscopeで同様のコヌドを䜿甚しお、ファむルI / O、コン゜ヌル入力、および他の倚くの機胜を実装できたす。



これで、JITずオプティマむザヌに関する章は終わりです。 珟時点では、チュヌリング完党ではないナヌザヌ指向プログラミング蚀語のコヌドをJITコンパむルおよび最適化できたす。 次に、制埡フロヌ制埡を䜿甚しお蚀語を拡匵し、途䞭でいく぀かの興味深いLLVM IR問題に察凊したす。



完党なコヌドリスト



そしお今、い぀ものように、私たちの䜜品のコヌドの完党なリストがあり、これは次のようにビルドできたす



#

g++ -g toy.cpp `llvm-config --cppflags --ldflags --libs core jit native` -O3 -o toy

#

./toy







Linuxでコンパむルする堎合、-rdynamicオプションを远加したす。 これにより、実行時の倖郚機胜が適切に有効になりたす。



そしお、コヌド自䜓は次のずおりです。



 #include "llvm/DerivedTypes.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/ExecutionEngine/JIT.h" #include "llvm/LLVMContext.h" #include "llvm/Module.h" #include "llvm/PassManager.h" #include "llvm/Analysis/Verifier.h" #include "llvm/Analysis/Passes.h" #include "llvm/Target/TargetData.h" #include "llvm/Target/TargetSelect.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Support/IRBuilder.h" #include <cstdio> #include <string> #include <map> #include <vector> using namespace llvm; //===----------------------------------------------------------------------===// // Lexer ( ) //===----------------------------------------------------------------------===// //     [0-255],   , //       enum Token { tok_eof = -1, //  ( ) tok_def = -2, tok_extern = -3, //  ( : , ) tok_identifier = -4, tok_number = -5 }; static std::string IdentifierStr; // ,  tok_identifier static double NumVal; // ,  tok_number /// gettok -       . static int gettok() { static int LastChar = ' '; //  . while (isspace(LastChar)) LastChar = getchar(); if (isalpha(LastChar)) { // : [a-zA-Z][a-zA-Z0-9]* IdentifierStr = LastChar; while (isalnum((LastChar = getchar()))) IdentifierStr += LastChar; if (IdentifierStr == "def") return tok_def; if (IdentifierStr == "extern") return tok_extern; return tok_identifier; } if (isdigit(LastChar) || LastChar == '.') { // : [0-9.]+ std::string NumStr; do { NumStr += LastChar; LastChar = getchar(); } while (isdigit(LastChar) || LastChar == '.'); NumVal = strtod(NumStr.c_str(), 0); return tok_number; } if (LastChar == '#') { //     do LastChar = getchar(); while (LastChar != EOF && LastChar != '\n' && LastChar != '\r'); if (LastChar != EOF) return gettok(); } //   . if (LastChar == EOF) return tok_eof; //         ASCII int ThisChar = LastChar; LastChar = getchar(); return ThisChar; } //===----------------------------------------------------------------------===// // Abstract Syntax Tree (     ) //===----------------------------------------------------------------------===// /// ExprAST -      . class ExprAST { public: virtual ~ExprAST() {} virtual Value *Codegen() = 0; }; /// NumberExprAST -       (, "1.0"). class NumberExprAST : public ExprAST { double Val; public: NumberExprAST(double val) : Val(val) {} virtual Value *Codegen(); }; /// VariableExprAST -      (, "a"). class VariableExprAST : public ExprAST { std::string Name; public: VariableExprAST(const std::string &name) : Name(name) {} virtual Value *Codegen(); }; /// BinaryExprAST -      . class BinaryExprAST : public ExprAST { char Op; ExprAST *LHS, *RHS; public: BinaryExprAST(char op, ExprAST *lhs, ExprAST *rhs) : Op(op), LHS(lhs), RHS(rhs) {} virtual Value *Codegen(); }; /// CallExprAST -      . class CallExprAST : public ExprAST { std::string Callee; std::vector<ExprAST*> Args; public: CallExprAST(const std::string &callee, std::vector<ExprAST*> &args) : Callee(callee), Args(args) {} virtual Value *Codegen(); }; /// PrototypeAST -    ""  , ///        (,  , ///    ). class PrototypeAST { std::string Name; std::vector<std::string> Args; public: PrototypeAST(const std::string &name, const std::vector<std::string> &args) : Name(name), Args(args) {} Function *Codegen(); }; /// FunctionAST -     class FunctionAST { PrototypeAST *Proto; ExprAST *Body; public: FunctionAST(PrototypeAST *proto, ExprAST *body) : Proto(proto), Body(body) {} Function *Codegen(); }; //===----------------------------------------------------------------------===// // Parser (   ) //===----------------------------------------------------------------------===// /// CurTok/getNextToken -    . CurTok -   /// ,  . getNextToken     ///     CurTok. static int CurTok; static int getNextToken() { return CurTok = gettok(); } /// BinopPrecedence -      static std::map<char, int> BinopPrecedence; /// GetTokPrecedence -     . static int GetTokPrecedence() { if (!isascii(CurTok)) return -1; // ,     . int TokPrec = BinopPrecedence[CurTok]; if (TokPrec <= 0) return -1; return TokPrec; } /// Error* -       . ExprAST *Error(const char *Str) { fprintf(stderr, "Error: %s\n", Str);return 0;} PrototypeAST *ErrorP(const char *Str) { Error(Str); return 0; } FunctionAST *ErrorF(const char *Str) { Error(Str); return 0; } static ExprAST *ParseExpression(); /// identifierexpr /// ::= identifier /// ::= identifier '(' expression* ')' static ExprAST *ParseIdentifierExpr() { std::string IdName = IdentifierStr; getNextToken(); //  . if (CurTok != '(') //  . return new VariableExprAST(IdName); //  . getNextToken(); //  ( std::vector<ExprAST*> Args; if (CurTok != ')') { while (1) { ExprAST *Arg = ParseExpression(); if (!Arg) return 0; Args.push_back(Arg); if (CurTok == ')') break; if (CurTok != ',') return Error("Expected ')' or ',' in argument list"); getNextToken(); } } //  ')'. getNextToken(); return new CallExprAST(IdName, Args); } /// numberexpr ::= number static ExprAST *ParseNumberExpr() { ExprAST *Result = new NumberExprAST(NumVal); getNextToken(); //   return Result; } /// parenexpr ::= '(' expression ')' static ExprAST *ParseParenExpr() { getNextToken(); //  (. ExprAST *V = ParseExpression(); if (!V) return 0; if (CurTok != ')') return Error("expected ')'"); getNextToken(); //  ). return V; } /// primary /// ::= identifierexpr /// ::= numberexpr /// ::= parenexpr static ExprAST *ParsePrimary() { switch (CurTok) { default: return Error("unknown token when expecting an expression"); case tok_identifier: return ParseIdentifierExpr(); case tok_number: return ParseNumberExpr(); case '(': return ParseParenExpr(); } } /// binoprhs /// ::= ('+' primary)* static ExprAST *ParseBinOpRHS(int ExprPrec, ExprAST *LHS) { //    ,    while (1) { int TokPrec = GetTokPrecedence(); //           , //  ,    if (TokPrec < ExprPrec) return LHS; // ,  ,    . int BinOp = CurTok; getNextToken(); // eat binop //       ExprAST *RHS = ParsePrimary(); if (!RHS) return 0; //  BinOp   RHS  ,    RHS, //      RHS  LHS. int NextPrec = GetTokPrecedence(); if (TokPrec < NextPrec) { RHS = ParseBinOpRHS(TokPrec+1, RHS); if (RHS == 0) return 0; } //  LHS/RHS. LHS = new BinaryExprAST(BinOp, LHS, RHS); } } /// expression /// ::= primary binoprhs /// static ExprAST *ParseExpression() { ExprAST *LHS = ParsePrimary(); if (!LHS) return 0; return ParseBinOpRHS(0, LHS); } /// prototype /// ::= id '(' id* ')' static PrototypeAST *ParsePrototype() { if (CurTok != tok_identifier) return ErrorP("Expected function name in prototype"); std::string FnName = IdentifierStr; getNextToken(); if (CurTok != '(') return ErrorP("Expected '(' in prototype"); //    . std::vector<std::string> ArgNames; while (getNextToken() == tok_identifier) ArgNames.push_back(IdentifierStr); if (CurTok != ')') return ErrorP("Expected ')' in prototype"); //  . getNextToken(); //  ')'. return new PrototypeAST(FnName, ArgNames); } /// definition ::= 'def' prototype expression static FunctionAST *ParseDefinition() { getNextToken(); //  def. PrototypeAST *Proto = ParsePrototype(); if (Proto == 0) return 0; if (ExprAST *E = ParseExpression()) return new FunctionAST(Proto, E); return 0; } /// toplevelexpr ::= expression static FunctionAST *ParseTopLevelExpr() { if (ExprAST *E = ParseExpression()) { //   . PrototypeAST *Proto = new PrototypeAST("", std::vector<std::string>()); return new FunctionAST(Proto, E); } return 0; } /// external ::= 'extern' prototype static PrototypeAST *ParseExtern() { getNextToken(); //  extern. return ParsePrototype(); } //===----------------------------------------------------------------------===// // Code Generation () //===----------------------------------------------------------------------===// static Module *TheModule; static IRBuilder<> Builder(getGlobalContext()); static std::map<std::string, Value*> NamedValues; static FunctionPassManager *TheFPM; Value *ErrorV(const char *Str) { Error(Str); return 0; } Value *NumberExprAST::Codegen() { return ConstantFP::get(getGlobalContext(), APFloat(Val)); } Value *VariableExprAST::Codegen() { //      . Value *V = NamedValues[Name]; return V ? V : ErrorV("Unknown variable name"); } Value *BinaryExprAST::Codegen() { Value *L = LHS->Codegen(); Value *R = RHS->Codegen(); if (L == 0 || R == 0) return 0; switch (Op) { case '+': return Builder.CreateFAdd(L, R, "addtmp"); case '-': return Builder.CreateFSub(L, R, "subtmp"); case '*': return Builder.CreateFMul(L, R, "multmp"); case '<': L = Builder.CreateFCmpULT(L, R, "cmptmp"); //   0  1   0.0  1.0 return Builder.CreateUIToFP(L, Type::getDoubleTy(getGlobalContext()), "booltmp"); default: return ErrorV("invalid binary operator"); } } Value *CallExprAST::Codegen() { //      . Function *CalleeF = TheModule->getFunction(Callee); if (CalleeF == 0) return ErrorV("Unknown function referenced"); //    . if (CalleeF->arg_size() != Args.size()) return ErrorV("Incorrect # arguments passed"); std::vector<Value*> ArgsV; for (unsigned i = 0, e = Args.size(); i != e; ++i) { ArgsV.push_back(Args[i]->Codegen()); if (ArgsV.back() == 0) return 0; } return Builder.CreateCall(CalleeF, ArgsV.begin(), ArgsV.end(), "calltmp"); } Function *PrototypeAST::Codegen() { //   : double(double,double)  .. std::vector<const Type*> Doubles(Args.size(), Type::getDoubleTy(getGlobalContext())); FunctionType *FT = FunctionType::get(Type::getDoubleTy(getGlobalContext()), Doubles, false); Function *F = Function::Create(FT, Function::ExternalLinkage, Name, TheModule); //   (F)     ,     'Name'. //     ,      . if (F->getName() != Name) { //   ,      . F->eraseFromParent(); F = TheModule->getFunction(Name); //   (F)   , . if (!F->empty()) { ErrorF("redefinition of function"); return 0; } //   (F)    , . if (F->arg_size() != Args.size()) { ErrorF("redefinition of function with different # args"); return 0; } } //     . unsigned Idx = 0; for (Function::arg_iterator AI = F->arg_begin(); Idx != Args.size(); ++AI, ++Idx) { AI->setName(Args[Idx]); //      . NamedValues[Args[Idx]] = AI; } return F; } Function *FunctionAST::Codegen() { NamedValues.clear(); Function *TheFunction = Proto->Codegen(); if (TheFunction == 0) return 0; //      . BasicBlock *BB = BasicBlock::Create(getGlobalContext(), "entry", TheFunction); Builder.SetInsertPoint(BB); if (Value *RetVal = Body->Codegen()) { //  . Builder.CreateRet(RetVal); //   ,    (). verifyFunction(*TheFunction); //  . TheFPM->run(*TheFunction); return TheFunction; } //  ,   . TheFunction->eraseFromParent(); return 0; } //===----------------------------------------------------------------------===// // Top-Level parsing (  )   JIT //===----------------------------------------------------------------------===// static ExecutionEngine *TheExecutionEngine; static void HandleDefinition() { if (FunctionAST *F = ParseDefinition()) { if (Function *LF = F->Codegen()) { fprintf(stderr, "Read function definition:"); LF->dump(); } } else { //      . getNextToken(); } } static void HandleExtern() { if (PrototypeAST *P = ParseExtern()) { if (Function *F = P->Codegen()) { fprintf(stderr, "Read extern: "); F->dump(); } } else { //      . getNextToken(); } } static void HandleTopLevelExpression() { // Evaluate a top-level expression into an anonymous function. if (FunctionAST *F = ParseTopLevelExpr()) { if (Function *LF = F->Codegen()) { // JIT-,   . void *FPtr = TheExecutionEngine->getPointerToFunction(LF); //     ( ,  double), //       . double (*FP)() = (double (*)())(intptr_t)FPtr; fprintf(stderr, "Evaluated to %f\n", FP()); } } else { //      . getNextToken(); } } /// top ::= definition | external | expression | ';' static void MainLoop() { while (1) { fprintf(stderr, "ready> "); switch (CurTok) { case tok_eof: return; case ';': getNextToken(); break; //     . case tok_def: HandleDefinition(); break; case tok_extern: HandleExtern(); break; default: HandleTopLevelExpression(); break; } } } //===----------------------------------------------------------------------===// // "" ,     //   ("extern")   . //===----------------------------------------------------------------------===// /// putchard -        0. extern "C" double putchard(double X) { putchar((char)X); return 0; } //===----------------------------------------------------------------------===// // Main driver code (  ) //===----------------------------------------------------------------------===// int main() { InitializeNativeTarget(); LLVMContext &Context = getGlobalContext(); //    . // 1 -  . BinopPrecedence['<'] = 10; BinopPrecedence['+'] = 20; BinopPrecedence['-'] = 20; BinopPrecedence['*'] = 40; //  . fprintf(stderr, "ready> "); getNextToken(); //  ,     . TheModule = new Module("my cool jit", Context); //  JIT.     . std::string ErrStr; TheExecutionEngine = EngineBuilder(TheModule).setErrorStr(&ErrStr).create(); if (!TheExecutionEngine) { fprintf(stderr, "Could not create ExecutionEngine: %s\n", ErrStr.c_str()); exit(1); } FunctionPassManager OurFPM(TheModule); //   .     ,  //       . OurFPM.add(new TargetData(*TheExecutionEngine->getTargetData())); //   AliasAnalysis  GVN. OurFPM.add(createBasicAliasAnalysisPass()); //   "peephole"  "bit-twiddling". OurFPM.add(createInstructionCombiningPass()); //  . OurFPM.add(createReassociatePass()); //   . OurFPM.add(createGVNPass()); //     (    ..). OurFPM.add(createCFGSimplificationPass()); OurFPM.doInitialization(); //   ,     . TheFPM = &OurFPM; //    " ". MainLoop(); TheFPM = 0; //   . TheModule->dump(); return 0; }
      
      







PS , :



All Articles