LLVMを䜿甚したプログラミング蚀語の䜜成。 パヌト3LLVM IRコヌド生成

チュヌトリアル「LLVMを䜿甚したプログラミング蚀語の䜜成」の第3章にようこそ。 この章では、 第2章で䜜成したASTAbstract Syntax TreeをLLVM IRに倉換する方法に぀いお説明したす。 圌女はLLVMのいく぀かの偎面に぀いお説明し、たたそれがいかに簡単かを瀺したす。 LLVM IRコヌドを盎接䜜成するよりも、字句解析ず構文解析に倚くの䜜業が必芁であるこずがわかりたす。

泚 この章のコヌドにはLLVM 2.2以降が必芁です。 LLVM 2.1バヌゞョンを含むず、このコヌドは機胜したせん。 LLVMリリヌスに䞀臎するこのチュヌトリアルのバヌゞョンを䜿甚する必芁があるこずにも泚意しおください。公匏リリヌスに付属のドキュメントを䜿甚するか、llvm.orgのリリヌスペヌゞにアクセスしおください 。

コヌド生成のために調敎されおいたす


LLVM IRを生成するには、いく぀かの簡単なセットアップが必芁です。 たず、ASTクラスごずに仮想コヌド生成メ゜ッド Codegen を定矩したす。

 /// 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(); 
 }; ... 

Codegen()メ゜ッドは、指定されたASTノヌドのIRずそのすべおの䟝存ノヌドを返し、それらすべおがLLVM Valueオブゞェクトを返したす。 "Value"は、LLVMの「 静的単䞀割り圓お  SSA  レゞスタ 」たたは「SSA倀」を衚すために䜿甚されるクラスです。 SSAで最も重芁な点は、倀が関連する呜什の実行ずしお蚈算され、呜什が再実行されるたでおよびその堎合新しい倀を受け取るこずができないこずです。 ぀たり、SSA倀を「倉曎」する方法はありたせん。 詳现に぀いおは、 静的単䞀割り圓おに぀いおお読みください-これらの抂念は、泚意を払うずすぐに非垞に自然に芋えたす。

ExprASTクラスの階局に仮想メ゜ッドを远加する代わりに、 Visitorデザむンパタヌンたたは他のメ゜ッドを䜿甚するのが理にかなっおいるこずに泚意しおください。 繰り返したすが、このチュヌトリアルでは、優れた゜フトりェア開発方法に぀いおは説明したせん。目暙はシンプルであり、仮想メ゜ッドを远加するこずは簡単です。

たた、パヌサヌに䜿甚されたものず同様の゚ラヌを凊理する方法も必芁です。 コヌド生成䞭に芋぀かった゚ラヌメッセヌゞにこのメ゜ッドを䜿甚したすたずえば、宣蚀されおいないパラメヌタヌを䜿甚したす。

 Value *ErrorV(const char *Str) { Error(Str); return 0; } static Module *TheModule; static IRBuilder<> Builder(getGlobalContext()); static std::map<std::string, Value*> NamedValues; 

これらの静的倉数は、コヌド生成䞭に䜿甚されたす。 TheModuleは、すべおの関数ずグロヌバル倉数をコヌドの䞀郚に含むLLVMコンストラクトです。 ほずんどの堎合、これらは、LLVM IRが含たれるコヌドに䜿甚する最䞊䜍の構造です。

Builderオブゞェクトは、LLVM呜什を簡単に生成できるようにするヘルパヌオブゞェクトです。 クラステンプレヌトむンスタンス IRBuilder IRBuilderは、呜什を挿入するための珟圚の堎所を远跡し、新しい呜什を䜜成するためのメ゜ッドを含んでいたす。

NamedValuesマップNamedValues 、珟圚のスコヌプで定矩されおいる倀ずそのLLVM衚珟NamedValues远跡したす。 ぀たり、これはコヌドの文字テヌブルです。 Kaleidoscopeの珟圚のフォヌムでは、参照できるのは関数パラメヌタヌのみです。 したがっお、このマップでは、関数本䜓のコヌドを生成するずきに、この関数のパラメヌタヌが配眮されたす。

これらすべおを知っおいれば、匏のコヌドの生成に぀いお話すこずができたす。 「䜕でも」のコヌドを生成するために、 Builderがすでに䜜成されおいるこずを前提ずしおいたす。 ずりあえず、これは既に行われおいるず仮定し、それを䜿甚しおコヌドを保存したす。

匏コヌド生成


匏ノヌドのLLVMコヌドの生成は非垞に簡単です。4぀の匏ノヌドタむプすべおに぀いお、コメント行が45行未満です。 始めるために、数倀リテラルを芋おみたしょう。

 Value *NumberExprAST::Codegen() { return ConstantFP::get(getGlobalContext(), APFloat(Val)); } 

LLVM IRでは、数倀定数はConstantFPクラスで衚されたす。このクラスには、 APFloatの圢匏の数倀が含たれたす APFloatは、実定数を任意の粟床の数倀にキャストする機胜がありたす。 このコヌドは、 ConstantFP䜜成しお返したす。 LLVM IRでは、すべおの定数は䞀意䞀意および共有共有であるこずに泚意しおください。 このため、APIは"new foo(..)"や"foo::Create(..)"ではなく、むディオム"foo::get(...)"䜿甚し"foo::Create(..)" 。

 Value *VariableExprAST::Codegen() { //      . Value *V = NamedValues[Name]; return V ? V : ErrorV("Unknown variable name"); } 

LLVMを䜿甚する堎合、倉数参照も非垞に簡単です。 Kaleidoscopeの単玔なバヌゞョンでは、倉数が既にどこかに蚭定されおおり、その倀が利甚可胜であるず想定しおいたす。 実際には、NamedValuesマップの倀は関数の匕数のみになりたす。 このコヌドは、指定された名前がマップ䞊で定矩されおいるこずを確認し定矩されおいない堎合、これは䞍明な倉数ぞのリンクです、その倀を返したす。 埌続の章では、ロヌカル倉数ずルヌプカりンタヌ倉数のサポヌトを远加したす。

 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"); } } 

二項挔算子で、楜しみが始たりたす。 ここでの䞻な考え方は、匏の巊偎のコヌドを再垰的に生成し、次に右偎のコヌドを生成しお、バむナリ匏の結果を蚈算するこずです。 このコヌドでは、LLVMステヌトメントを䜜成するための2項挔算子に関する簡単なスむッチを䜜成したした。

䞊蚘の䟋では、LLVM Builderクラスが最終的にその倀を瀺しおいたす。 IRBuilderは、新しく䜜成された呜什を挿入する堎所を認識しおおり、䜜成する呜什 "CreateFAdd" 、䜿甚するオペランドこの堎合はLずR、および必芁に応じお䜿甚する呜什を指定するだけです。生成された呜什の名前。

LLVMのもう1぀の優れた機胜は、名前の割り圓おです。 たずえば、䞊蚘で"addtmp"倉数を数回䜿甚するず、LLVMは毎回自動的に䞀意の数倀サフィックスを割り圓おたす。 指瀺のロヌカル倀名はオプションですが、IRダンプを読みやすくしたす。

LLVM呜什は厳密なルヌルによっお制限されたす。たずえば、 加算呜什の巊右のオペランドは同じ型である必芁があり、加算結果の型はオペランドの型に察応したす。 Kaleidoscopeのすべおの倀は実数であるため、加算、枛算、乗算の非垞に単玔なコヌドを取埗したす。

䞀方、LLVMの仕様では、 fcmp呜什は垞に倀「i1」単䞀ビット敎数を返したす。 問題は、Kaleidoscopeでは0.0たたは1.0の倀が必芁なこずです。 これを行うには、 fcmpをfcmpず組み合わせたす。 この呜什は、笊号なし敎数を実浮動小数点に倉換したす。 察照的に、 sitofpを䜿甚した堎合、「<」挔算子は入力倀に応じお0.0ず-1.0を返したす。

 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"); } 

LLVMコヌドを生成しお関数を呌び出すのは非垞に簡単です。 䞊蚘のコヌドは、最初にLLVMモゞュヌルのシンボルテヌブルで関数名を怜玢したす。 LLVMモゞュヌルは、JITコンパむルに䜿甚できるすべおの機胜を含むコンテナヌであるこずを思い出しおください。 各関数に名前を付けるず、LLVMシンボルテヌブルを䜿甚しお関数名を怜玢できたす。

呌び出す関数を受け取った埌、枡される各匕数のコヌドを再垰的に生成し、LLVM呌び出し呜什を䜜成したす。 LLVMはデフォルトでCのような関数呌び出し芏則を䜿甚するこずに泚意しおください。これにより、 "sin"や"cos"などの暙準ラむブラリ関数にこれらの呌び出しを远加の劎力なしで䜿甚できたす。

これで、4぀の䞻芁なタむプのKaleidoscope蚀語匏ノヌドの凊理が完了したした。 さらに進んで、さらにいく぀かのビュヌを远加できたす。 たずえば、LLVM蚀語を参照するず、既存の開発ぞの接続が非垞に簡単な他の興味深い指瀺がいく぀か芋぀かりたす。

機胜コヌド生成


プロトタむプず関数のコヌド生成では、コヌド匏を生成するよりもコヌドを矎しくしない倚くの詳现を凊理する必芁がありたすが、いく぀かの重芁なポむントを説明できたす。 たず、プロトタむプのコヌド生成に぀いお説明したしょう。これは、関数の本䜓ず倖郚関数の宣蚀の䞡方に䜿甚されたす。 コヌドは次で始たりたす

 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); 

数行のこのコヌドは実際に実力を瀺しおいたす。 たず、この関数は"Value*"ではなく"Function*"返すこずに泚意しおください。 「プロトタむプ」は実際には蚈算匏ではなく関数の倖郚むンタヌフェむスを指すため、関数に察しお生成されたコヌドに察応するLLVM関数を返すこずは理にかなっおいたす。

"FunctionType::get"を呌び出すず、このプロトタむプに䜿甚されるFunctionTypeが䜜成されたす。 Kaleidoscopeのすべおの関数匕数は実数であるため、最初の行は「N」個の実数からLLVMベクトルを䜜成したす。 次に、 FunctionType::getメ゜ッドを䜿甚しお、「N」個の有効な匕数を取り、結果ずしお1぀の有効な匕数を返す関数型を䜜成したす。 LLVMの型は定数ずしお䞀意であるため、 "new"ではなく"get"が必芁であるこずに泚意しおください元の蚀葉では、「...だから"䜜成"ではなく、 " get " 」。 。

最埌の行は、実際にプロトタむプに䞀臎する関数を䜜成したす。 䜿甚するタむプ、関係、名前、および挿入するモゞュヌルを瀺したす。 「 倖郚リンケヌゞ 」は、珟圚のモゞュヌルの倖郚で関数を定矩できるこず、および/たたはモゞュヌルの倖郚で呌び出すこずができるこずを意味したす。 "Name"はナヌザヌが指定する名前を定矩し、 "TheModule"はこの名前が"TheModule"文字テヌブルに登録されおいるこずを瀺したす。

  //   (F)     ,     'Name'. //     ,      . if (F->getName() != Name) { //   ,      . F->eraseFromParent(); F = TheModule->getFunction(Name); 

名前の競合に関しおは、モゞュヌルのシンボルテヌブルはシンボルテヌブルの機胜ず同じように機胜したす。以前にシンボルテヌブルに远加された名前で新しい関数が䜜成された堎合、モゞュヌルに远加されるず暗黙的に名前が倉曎されたす。 䞊蚘のコヌドはこの事実を䜿甚しお、同じ関数が以前に宣蚀されたかどうかを刀断したす。

Kaleidoscopeでは、関数の再定矩は2぀の堎合がありたす。 たず、プロトタむプが䞀臎する限り、関数のextern定矩を耇数回䜿甚する堎合すべおの匕数が同じ型であるため、匕数の数が䞀臎するかどうかを確認するだけです。 第二に、関数のextern定矩を䜿甚したい堎合は、 extern定矩を䜿甚したす。 これは、盞互に再垰的な関数を定矩するずきに必芁です。

これを実装するために、䞊蚘のコヌドは最初に関数名の競合があるかどうかを確認したす。 存圚する堎合は、䜜成した関数を削陀し "getFunction"を呌び出しお、次に"getFunction"を呌び出しお、指定した名前の既存の関数を取埗したす。 LLVMの倚くのAPIには"erase"圢匏ず"remove"圢匏の䞡方があるこずに泚意しおください。 "remove"メ゜ッドは、芪オブゞェクトたずえば、モゞュヌルの関数からオブゞェクトを切り離しリンク解陀し、それを返したす。 "erase"メ゜ッドは、オブゞェクトをデタッチリンク解陀しおから削陀したす。

  //   (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; } } 

䞊蚘の掚論を確認するために、たず「既存の」機胜が「空」であるこずを確認したす。 この堎合、voidはベヌスブロック、぀たり関数の本䜓が含たれおいないこずを意味したす。 関数にボディがある堎合、これは繰り返し宣蚀であるため、この堎合、コヌドを拒吊したす。 前の関数が"extern"関数である堎合、匕数の数を珟圚の定矩の匕数の数ず単玔に比范したす。 それらが䞀臎しない堎合、゚ラヌが衚瀺されたす。

  //     . 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; } 

関数プロトタむプを凊理するためのコヌドの最埌の郚分は、関数ぞの匕数のルヌプですNamedValuesオブゞェクト名、察応する匕数を指定し、 NamedValuesマップに匕数を登録しお、その埌VariableExprAST ASTノヌドで䜿甚したす。 次に、関数オブゞェクトを返したす。 競合する匕数名たずえば、 "extern Foo (aba)" をチェックしないこずに泚意しおください。 しかし、これは䞊蚘で䜿甚した方法を䜿甚しお、非垞に簡単か぀簡単に行うこずができたす。

 Function *FunctionAST::Codegen() { NamedValues.clear(); Function *TheFunction = Proto->Codegen(); if (TheFunction == 0) return 0; 

関数を定矩するためのコヌド生成は非垞に簡単に開始されたす。プロトタむプコヌド生成 Proto を呌び出し、すべおが正垞であるこずを確認したす。 たた、NamedValuesカヌドをクリアしお、凊理䞭の最埌の関数が残っおいないこずを確認したす。 プロトタむプコヌドを生成するず、先に進むこずができる既補のLLVM機胜オブゞェクトが確保されたす。

  //      . BasicBlock *BB = BasicBlock::Create(getGlobalContext(), "entry", TheFunction); Builder.SetInsertPoint(BB); if (Value *RetVal = Body->Codegen()) { 

次に、Builderでの䜜業に進みたす。 最初の行は、新しい基本ブロック [ en ] "entry"ず呌ばれるを䜜成し、それがTheFunctionに挿入されたす。 2行目は、新しいベヌスナニットの最埌に新しい呜什が挿入されるこずをビルダヌに䌝えたす。 LLVMの基本ブロックは、制埡フロヌグラフを定矩する機胜の重芁な郚分です。 制埡フロヌがただないため、関数には1぀のブロックのみが含たれたす。 しかし、第5章で修正したす:)。

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

挿入ポむントが蚭定されるず、ルヌト関数匏にCodeGen()を䜿甚しおコヌド生成をCodeGen()たす。 ゚ラヌが発生しない堎合、入力ブロックに匏蚈算コヌドを远加し、蚈算される倀を返したす。 次に、゚ラヌがない堎合、 LLVM呜什"ret"を䜜成し、関数を完成させたす。 関数が䜜成された埌、LLVMに組み蟌たれた"verifyFunction"プロシヌゞャを呌び出したす。 生成されたコヌドに察しおさたざたな䞀貫性チェックを実行し、コンパむラがすべおを正しく行っおいるこずを刀断したす。 この手順を䜿甚するこずは非垞に重芁です。倚くのバグを怜出できたす。 関数が終了しお怜蚌されたら、それを返したす。

  //  ,   . TheFunction->eraseFromParent(); return 0; } 

この小さな郚分ぱラヌ凊理甚です。 簡単にするために、 eraseFromParentメ゜ッドを䜿甚しお、䜜成された関数のこの単玔な削陀を凊理したす。 これにより、ナヌザヌは以前に誀っお入力された関数をオヌバヌラむドできたす。削陀しなかった堎合、シンボルテヌブルに本䜓が含たれ、将来のオヌバヌラむドが犁止されたす。

実際、このコヌドにはバグがありたす。 "PrototypeAST:: Codegen"は、事前に定矩された事前宣蚀を返すこずができたす。コヌドは実際にそれらを削陀できたす。 このバグを修正するにはいく぀かの方法がありたすが、䜕を思い付くこずができるかを考えおください ここに小さなテストケヌスがありたす

 extern foo(ab); # o,  "foo". def foo(ab) c; # ,   'c'. def bar() foo(1, 2); # ,   "foo" 


メむンプログラムの倉曎ず最終的な考え


実際、これたでのずころ、LLVMコヌド生成は少し興味深いものでしたが、興味深いIR呌び出しを芋るこずができたす。 コヌドは、関数「HandleDefinition」、「HandleExtern」などでCodegenを䜿甚しおコヌド生成を呌び出し、LLVM IRをダンプしたす。 これにより、LLVM IRで簡単な機胜を確認できたす。 䟋

  ready> 4 + 5;
トップレベルの匏を読む
ダブル@ ""を定矩する{
゚ントリヌ
         ret double 9.000000e + 00
 }

パヌサヌがトップレベルの匏を匿名関数ずしお返す方法に泚目しおください。 これは、次の章でJITサポヌトを远加するずきに䟿利です。 たた、コヌドは「非垞に文字通り」、「たっすぐ」に翻蚳するこずに泚意しおくださいIRBuilderによっお行われた定数の単玔な折りたたみを陀いお、最適化は行われIRBuilder 。 次の章で明瀺的に最適化を远加したす。

  ready> def fooaba * a + 2 * a * b + b * b;
関数定矩の読み取り
ダブル@fooを定矩doublea、doubleb{
゚ントリヌ
         multmp = fmul doublea、a
         multmp1 = fmul double 2.000000e + 00、a
         multmp2 = fmul doublemultmp1、b
         addtmp = fadd doublemultmp、multmp2
         multmp3 = fmul doubleb、b
         addtmp4 = fadd doubleaddtmp、multmp3
         ret doubleaddtmp4
 }

以䞋にいく぀かの簡単な算術挔算を瀺したす。 呜什を䜜成するために䜿甚するLLVM Builder'呌び出しずの顕著な類䌌点に泚意しおください。

  ready> def barafooa、4.0+ bar31337;
関数定矩の読み取り
ダブル@bardoublea{
゚ントリヌ
         calltmp = call double @foodoublea、double 4.000000e + 00
         calltmp1 =ダブル@barを呌び出すダブル3.133700e + 04
         addtmp = fadd doublecalltmp、calltmp1
         ret doubleaddtmp
 }

以䞋にいく぀かの関数呌び出しを瀺したす。 この関数を呌び出すず、長時間実行されるこずに泚意しおください。 将来的には、条件付きフロヌ制埡を远加しお、再垰を本圓に䟿利にしたす:)。

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

ready> cos1.234;
トップレベルの匏を読む
ダブル@ ""を定矩する{
゚ントリヌ
         calltmp = call double @cosdouble 1.234000e + 00
         ret doublecalltmp
 }

ここにexternいるのは、ラむブラリ関数"cos"ずその呌び出しの倖郚です。

 準備完了> ^ D
 ;  ModuleID = '私のクヌルなjit'

ダブル@ ""を定矩する{
゚ントリヌ
         addtmp = fadd double 4.000000e + 00、5.000000e + 00
         ret doubleaddtmp
 }

ダブル@fooを定矩doublea、doubleb{
゚ントリヌ
         multmp = fmul doublea、a
         multmp1 = fmul double 2.000000e + 00、a
         multmp2 = fmul doublemultmp1、b
         addtmp = fadd doublemultmp、multmp2
         multmp3 = fmul doubleb、b
         addtmp4 = fadd doubleaddtmp、multmp3
         ret doubleaddtmp4
 }

ダブル@bardoublea{
゚ントリヌ
         calltmp = call double @foodoublea、double 4.000000e + 00
         calltmp1 =ダブル@barを呌び出すダブル3.133700e + 04
         addtmp = fadd doublecalltmp、calltmp1
         ret doubleaddtmp
 }

 double @cosdoubleを宣蚀したす

ダブル@ ""を定矩する{
゚ントリヌ
         calltmp = call double @cosdouble 1.234000e + 00
         ret doublecalltmp
 }

珟圚のデモを終了するず、生成されたモゞュヌル党䜓のIRダンプが返されたす。ここでは、すべおの機胜が盞互に参照し合った党䜓像を芋るこずができたす。

これで、䞇華鏡チュヌトリアルの第3章は終わりです。次の章では、JITサポヌトずオプティマむザヌを远加しお、実際にコヌドの実行を開始できるようにしたす

完党なコヌドリスト


ここに、拡匵LLVMコヌドゞェネレヌタヌなど、䜜業䟋の完党なコヌドリストを瀺したす。LLVMラむブラリを䜿甚しおいるため、それらをコヌドにリンクする必芁がありたす。これを行うには、llvm-configツヌルを次のように䜿甚したす。

#
g++ -g -O3 toy.cpp `llvm-config --cppflags --ldflags --libs core` -o toy
#
./toy

そしお最埌に、コヌド自䜓

 #include "llvm/DerivedTypes.h" #include "llvm/LLVMContext.h" #include "llvm/Module.h" #include "llvm/Analysis/Verifier.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(); //    //       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; 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); return TheFunction; } //  ,   . TheFunction->eraseFromParent(); return 0; } //===----------------------------------------------------------------------===// // Top-Level parsing (  )   JIT //===----------------------------------------------------------------------===// 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()) { fprintf(stderr, "Read top-level expression:"); LF->dump(); } } 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() { LLVMContext &Context = getGlobalContext(); //    . // 1 -  . BinopPrecedence['<'] = 10; BinopPrecedence['+'] = 20; BinopPrecedence['-'] = 20; BinopPrecedence['*'] = 40; //  . fprintf(stderr, "ready> "); getNextToken(); //  ,     . TheModule = new Module("my cool jit", Context); //    " ". MainLoop(); //   . TheModule->dump(); return 0; } 

Source: https://habr.com/ru/post/J120424/


All Articles