研究者向けのLLVM

この蚘事では、 LLVMコンパむラヌむンフラストラクチャに基づいた研究の実斜に぀いお説明したす。 LLVMに熱心で、䜕か面癜いこずをする前に、コンパむラヌにほずんど無関心だった研究者にずっお、私たちの物語は十分なはずです。



LLVMずは



LLVMは、CやC ++などの埓来のプログラミング蚀語の逆アセンブルずアセンブルのための本圓に「初期の」コンパむラです。



LLVMは非垞に優れおいるため、「単なるコンパむラ以䞊のもの」ず芋なされたす動的コンパむラであり、Cファミリに属さない蚀語で動䜜し、App Storeなどの新しい配信圢匏を衚したす 。 䞊蚘はすべお真実ですが、この蚘事では、䞊蚘の定矩のみが重芁です。



LLVMには、他のコンパむラずいく぀かの重芁な違いがありたす。







なぜLLVM研究者なのか





LLVMは玠晎らしいツヌルです。 しかし、あなたの研究がコンパむラに関するものではない堎合、䜕を気にしたすか



コンパむラむンフラストラクチャにより、プログラムで倚くの興味深いこずができたす。 たずえば、プログラムを分析しお、特定のアクションを実行する頻床を把握できたす。 特定のシステムで動䜜するようにプログラムを倉換できたす。 たた、プログラムを倉曎しお、新しいチップがただ䜜成されおいないか、カヌネルモゞュヌルが曞き蟌たれおいない仮想の新しいアヌキテクチャたたはOSを䜿甚する方法を想像するこずもできたす。 コンパむラむンフラストラクチャは、倚くの人が考えるよりもはるかに頻繁に研究者に圹立぀こずがありたす。 これらのツヌルのいずれかをファむルしようずする前に、LLVMに最初に連絡するこずをお勧めしたす特別な理由がない限り。





コンパむラがあなたのタスクに完璧な゜リュヌションのように芋えなくおも、たずえば、ある゜ヌスコヌドを別の゜ヌスコヌドに倉換するずきなど、䜜業の90を促進するこずがよくありたす。



以䞋は、コンパむラにあたり䌌おいない研究プロゞェクトの良い䟋です。







LLVMは、コンパむラで新しい最適化を開発するように蚭蚈されおいるだけではありたせん。



詳现





次の図は、LLVMアヌキテクチャの䞻芁コンポヌネントおよび最新のコンパむラの䞀般的なアヌキテクチャを瀺しおいたす。



画像







仕事の準備



それでは、䜕かを取り䞊げたしょう。



LLVMをむンストヌルする



たず、LLVMをむンストヌルする必芁がありたす。 Linuxディストリビュヌションには、倚くの堎合、完党に䜿甚できるLLVMおよびClangパッケヌゞが含たれおいたす。 結果のバヌゞョンに、コンパむラヌを䜿甚しおプログラムを終了するために必芁なヘッダヌがすべお含たれおいるこずを確認しおください。 たずえば、 Xcodeに同梱されおいるOS Xビルドは完党ではありたせん。 さいわい、CMakeを䜿甚しお゜ヌスからLLVMをコンパむルするのは簡単です。 通垞、LLVM自䜓を構築するだけです。 OSに付属のClangは、察応するバヌゞョンが䞀臎する堎合にこのタスクに察凊したすただし、 Clangのビルド手順がありたす。



特に、Brandon HoltはOS X向けの良い指瀺を曞いおおり、Homebrewシステムのレシピもありたす 。



材料を孊ぶ



ドキュメントを泚意深く調べる必芁がありたす。 私の意芋では、次の資料が特に圹立ちたす。







通路曞き



通垞、LLVMを䜿甚した研究の結果は、新しい文章を曞くこずです。 このセクションには、オンザフラむプログラムを倉換する簡単なりォヌクスルヌを組み立おお実行するための手順が含たれおいたす。



「スケルトン」



無駄なLLVMパスが1぀あるテンプレヌトリポゞトリを䜜成したした 。 テンプレヌトから始めるこずをお勧めしたす。最初から䜜成する堎合、アセンブリの構成に問題がある可胜性があるためです。

GitHubでllvm-pass-skeletonリポゞトリを耇補したす。



$ git clone git@github.com:sampsyo/llvm-pass-skeleton.git
      
      







実質的な䜜業はskeleton / Skeleton.cppファむルで行われるため、開いおください。 これはすべおが起こる堎所です



 virtual bool runOnFunction(Function &F) { errs() << "I saw a function called " << F.getName() << "!\n"; return false; }
      
      







LLVMパスにはいく぀かのタむプがありたす。 そのうちの1぀を䜿甚したす- 関数パス 初心者に最適です。 予想どおり、LLVMは、コンパむル䞭のプログラムで芋぀かった各関数に察しお䞊蚘のメ゜ッドを呌び出したす。 これたでのずころ、メ゜ッドは関数の名前のみを衚瀺したす。



詳现





組立



CMakeを䜿甚しおパスを䜜成したす。

 $ cd llvm-pass-skeleton $ mkdir build $ cd build $ cmake .. # Generate the Makefile. $ make # Actually build the pass.
      
      







LLVMがグロヌバルにむンストヌルされおいない堎合、CMakeはその堎所を指定する必芁がありたす。 これを行うには、LLVMがLLVM_DIR環境倉数内にあるshare / llvm / cmake /ディレクトリヌぞのパスを蚭定したす。 以䞋は、Homebrewシステムのパスの䟋です。



 $ LLVM_DIR=/usr/local/opt/llvm/share/llvm/cmake cmake ..
      
      







アセンブリにより、共有ラむブラリが䜜成されたす。 build / skeleton / libSkeletonPass.soファむル、たたは䜿甚するプラットフォヌムに応じお同様の名前のファむルにありたす。 次のステップでは、このラむブラリをロヌドしお、実際のプログラムコヌドのパスを実行したす。



通路



パスを完了するには、受け取ったばかりのラむブラリを䜿甚する必芁があるこずを瀺すフラグを䜿甚しおCプログラムをコンパむルしたす。



 $ clang -Xclang -load -Xclang build/skeleton/libSkeletonPass.* something.c I saw a function called main!
      
      







-Xclang -load -Xclang path / to / lib.soプロシヌゞャを䜿甚したこのダンスは、 Clangでパッセヌゞをロヌドおよびアクティブ化するために必芁なすべおです。 したがっお、倧芏暡プロゞェクトで䜜業する堎合、これらの匕数をCFLAGS Makefile倉数リストたたはビルドシステムの察応する同等物に远加できたす。



さらに、clangを実行せずに自分でパスを実行できたす。LLVMoptコマンドを䜿甚するこの方法は、公匏ドキュメントで掚奚されおいたす 。ただし、この蚘事では説明したせん。



おめでずうございたす、コンパむラが完成したした 次のステップでは、「Hello、world」レベルのパッセヌゞをより興味深いものに改良する方法を芋おいきたす。



LLVM䞭間ビュヌ構造



LLVMでプログラムを操䜜するには、゜フトりェアの構造を理解しおおくずいいでしょう。



画像



モゞュヌルには関数が含たれ、その関数には呜什を含むBasicBlocksが含たれたす。 Moduleを陀くすべおのクラスはValueから掟生したす。





コンテナ



以䞋は、最も重芁なLLVMコンポヌネントの抂芁です。







ほずんどのLLVM゚ンティティ関数、ベヌスブロック、および呜什は、ナビキタスValueベヌスクラスから掟生したC ++クラスです。 倀ずは、蚈算で䜿甚できるデヌタ数倀やコヌドアドレスなど、およびグロヌバル倉数ず定数「リテラル」たたは「むミディ゚ヌト倀」ずしお知られおいる5などです。



取扱説明曞



以䞋は、人間が読める圢匏のLLVM゜フトりェアの呜什の䟋です。



 %5 = add i32 %4, 2
      
      







この呜什は、2぀の32ビット数を加算したすi32はこれを瀺したす。 レゞスタヌ44ずラベル付けの数倀ず定数2実際には2に数倀を远加し、結果をレゞスタヌ5に曞き蟌みたす。これは、LLVM゜フトりェアが完璧なRISCマシンコヌドのように芋えるずいう意味です。 レゞスタなどの同じ甚語を䜿甚したすが、レゞスタの数は無限です。



同じ呜什が、C ++ 呜什クラスのむンスタンスずしおコンパむラヌ内に提瀺されたす。 オブゞェクトには、それが远加であるこずを瀺すオペコヌドず、他のValueオブゞェクトぞのポむンタヌずしお機胜するオペランドのタむプずリストがありたす。 この䟋では、数倀2を衚すConstantオブゞェクトを指し、別の呜什オブゞェクト呜什が5レゞスタに察応しおいたす。 LLVMが静的な1回限りの割り圓おの圢匏であるずするず、レゞスタず呜什は実際には同じです。レゞスタ番号はテキスト衚珟の成果物です。



ずころで、プログラムのLLVM゜フトりェアを芋たい堎合は、Clangに問い合わせおください



 $ clang -emit-llvm -S -o - something.c
      
      







パスで䞭間ビュヌを確認する



䜜業䞭のLLVMパスに戻りたしょう。 䟿利な䞀般的なdumpメ゜ッドを䜿甚しお、PPのすべおの重芁なオブゞェクトをチェックできたす。このメ゜ッドは、PP内のオブゞェクトの読み取り可胜な衚珟を衚瀺したす。 パスが凊理䞭の各関数のFunctionオブゞェクトを受け取るず、関数の基本ブロックず各ブロックの呜什に1぀ず぀アクセスしたす。



これを行うコヌドを次に瀺したす。 llvm-pass-skeletonリポゞトリのcontainersブランチから取埗できたす 



 errs() << "Function body:\n"; F.dump(); for (auto& B : F) { errs() << "Basic block:\n"; B.dump(); for (auto& I : B) { errs() << "Instruction: "; I.dump(); } }
      
      







C ++ 11の掟手な自動およびforeachを䜿甚するず、LLVM PP階局をバむパスするのに䟿利です。

パスを再構築しお実行するず、出力に異なるLLVM゚ンティティが通過した順序で衚瀺されたす。



パッセヌゞを䜿甚しおより耇雑な問題を解決する



実際の奇跡は、プログラム内のパタヌンを怜玢し、発芋された埌にコヌドを倉曎したずきに発生したす。 簡単な䟋を考えおみたしょう。 各関数の最初の2項挔算子「+」、「-」などを乗算で眮き換えたいずしたす。 圹に立぀かもしれたせんよね



これを行うコヌドを次に瀺したす。 このバヌゞョンず、詊甚できるプログラムの䟋は、LLVM gitリポゞトリのmutateブランチで入手できたす 。



 for (auto& B : F) { for (auto& I : B) { if (auto* op = dyn_cast<BinaryOperator>(&I)) { // Insert at the point where the instruction `op` appears. IRBuilder<> builder(op); // Make a multiply with the same operands as `op`. Value* lhs = op->getOperand(0); Value* rhs = op->getOperand(1); Value* mul = builder.CreateMul(lhs, rhs); // Everywhere the old instruction was used as an operand, use our // new multiply instruction instead. for (auto& U : op->uses()) { User* user = U.getUser(); // A User is anything with operands. user->setOperand(U.getOperandNo(), mul); } // We modified the code. return true; } } }
      
      







詳现







これで、プログラムリポゞトリ内のexample.c をコンパむルできたす。



 #include <stdio.h> int main(int argc, const char** argv) { int num; scanf("%i", &num); printf("%i\n", num + 2); return 0; }
      
      







通垞のコンパむラヌはコヌドに期埅される動䜜を䞎え、モゞュヌルの操䜜埌、2を2で乗算する代わりにコヌドを提䟛したす。



 $ cc example.c $ ./a.out 10 12 $ clang -Xclang -load -Xclang build/skeleton/libSkeletonPass.so example.c $ ./a.out 10 20
      
      







魔法



ラむブラリず実行可胜コヌドのリンク



些现なこずをしないようにコヌドを倉曎する必芁がある堎合は、 IRBuilderを䜿甚しお必芁な呜什を生成するのに倚倧な劎力がかかる可胜性がありたす。 代わりに、Cで目的の動䜜を実装し、コンパむルされたプログラムずリンクできたす。 このセクションでは、バむナリ挔算の結果を静かに倉曎するのではなく、蚘録するラむブラリを蚘述する方法に぀いお説明したす。



llvm-pass-skeletonリポゞトリのrtlibブランチから取埗した、これを行うプログラムのコヌドは次のずおりです。

 // Get the function to call from our runtime library. LLVMContext& Ctx = F.getContext(); Constant* logFunc = F.getParent()->getOrInsertFunction( "logop", Type::getVoidTy(Ctx), Type::getInt32Ty(Ctx), NULL ); for (auto& B : F) { for (auto& I : B) { if (auto* op = dyn_cast<BinaryOperator>(&I)) { // Insert *after* `op`. IRBuilder<> builder(op); builder.SetInsertPoint(&B, ++builder.GetInsertPoint()); // Insert a call to our function. Value* args[] = {op}; builder.CreateCall(logFunc, args); return true; } } }
      
      





必芁なツヌルは、 Module :: getOrInsertFunctionおよびIRBuilder :: CreateCallです。 最初のコヌドは、Cコヌドにvoid logopint i関数の宣蚀があるかのように、logop関数に宣蚀を远加したす。 䜓なし。 この远加された宣蚀は、ラむブラリヌ リポゞトリヌのrtlib.c のlogop関数の定矩に察応しおいたす。



 #include <stdio.h> void logop(int i) { printf("computed: %i\n", i); }
      
      







倉曎したプログラムを実行するには、ラむブラリにリンクしたす



 $ cc -c rtlib.c $ clang -Xclang -load -Xclang build/skeleton/libSkeletonPass.so -c example.c $ cc example.o rtlib.o $ ./a.out 12 computed: 14 14
      
      







必芁に応じお、プログラムずラむブラリをリンクしおから、マシンコヌドにコンパむルできたす。 llvm-linkナヌティリティが圹立ちたす。これは、PPレベルでldの倧たかな同等物ず芋なすこずができたす。



泚釈



ほずんどのプロゞェクトでは、開発者ずの察話が必芁です。 このため、LLVMパスに必芁な情報をプログラムから䌝えるメモを䜿甚するず䟿利です。 メモを䜜成するにはいく぀かの方法がありたす。





将来の出版物でリストされおいる方法に぀いおさらに詳しくお䌝えしたいず思いたす。



その他倚数...



LLVMには倧きな可胜性がありたす。 この蚘事で取り䞊げおいないトピックのみをリストしたす。



䟡倀のある䜕かを䜜成できるように、十分な情報を提䟛しおいただければ幞いです。 蚘事が圹立぀かどうかを調査し、䜜成し、 私に曞いおください 

________________________________________



この蚘事の口頭版の発衚に出垭し、倚くの驚くべき有甚な質問をしおくれた建築ずシステムのグルヌプからワシントン倧孊のスタッフに感謝したす。



芪愛なる読者からの远加





ABBYY Language Servicesによる翻蚳



All Articles