LLVM for Grad Students 大学院生のためのLLVM を訳す8

Linking With a Runtime Library

ランタイムライブラリへのリンク

あなたが明確でないなにかをする命令コードを必要とするとき, IRBuilderをつかってLLVM命令を使うことができる.

その代わり, 実行時のC言語動作とプログラムをリンクする必要がある.
この章では, どのように静的変換の代わりに2項演算子の結果ログの実行時ライブラリへ書くのかについて言及します.

つぎにLLVMのPassコードを示す. これは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::hetOrInsertFunctionとIRBuilder::CreateCallを必要とする. 前者はあなたの実行時の関数logop宣言を追加し, それはvoid logop(int i);と関数のプロトタイプ宣言と似ています.
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

もしあなたが好むのであるならば, 機械コードにコンパイルする前に実行時ライブラリと合わせることも可能です.
The llvm-linkユーティリテにおいてあなたはIdと同じような大雑把なIR-level考えることができる. 使い方はhelpをみてください.

Annotations 注釈

ほとんどプロジェクトはプログラマと対話することとなる. あなたは注釈が必要となる.LLVM Passにプログラムから追加情報を与えるいくつかの方法がある.
いくつかのアノテーションシステムを構築する方法がある.

・ 実用的で開発的な方法としてfunctionを利用することです いくつかの固有の空のfunctionで宣言し, ヘッダーファイル内で固有名を宣言する ソースのファイルをインクルードし, 何もしない関数を呼び出す.
あなたのPassで魔法を使うこととあなたの関数を呼ぶと, CallInst命令を探す.
例えば, あなたが__enable_instrumentation()とdisable_instrumentation()を呼び出したい時,プログラムがあなたの特定の領域にコードいじるを閉じ込めるようにする.


注釈は日本語めちゃくちゃになりそうだし, 違うこともやりたくなったので, ここで訳は終了.

小話ですが, ブログ毎日やろうとして書いてたら, 0時超えてて途切れてしまった.
あと, 困っていたLLVM Passを動作することできたので, 再現性を確認できたら書いていきたいと思う.

LLVM for Grad Students 大学院生のためのLLVM を訳す7

Now Make the Pass Do Something Mildly Interesting

魔法は見つけるときに必要に応じて、コードを変更することとあなたがプログラムのパターンを探したときにあるでしょう。
簡単な例を示します. すべてのfunctionのは最初の二項演算子(+, -等)を乗算する関数全てで交換したいとしましょう。

ここにコードを示す, 現在のバージョンでgitのllvm-pass-skeletonのレポジトリの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;
   }
 }
}

details:

dyn_cast(p)構造体はLLVMの固有のイントロスペクションユーティリティである.
コンパイラは、それらのすべての時間を使用する必要があるため,それはLLVMの効率的動的テストによってつくられたコードベースからいくつかの利用が可能である.
IがBinaryOperatorでない場合、この構造体は、NULLポインタを返すため、特別なケースに最適である.

IR Builderはコードを構築する. それはおそらくあなたが可能となる命令の種類を百万ほど持っています.

コードに新しい命令をつなげるために、引数として、新しい命令内で使われているすべての場所やスワップを見つける必要があります。
命令は値であることを思い出してください. ここで, 乗算命令は他の命令のオペランドで使われる, つまり, 引数としてわたされるということである.

古い命令は削除すべきかもしれないが, 簡潔にするため左ビットシフトを使用します.

いま, このようなプログラムをコンパイルするとします. (レポジトリの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

LLVM for Grad Students 大学院生のためのLLVM を訳す6

Understanding LLVM IR

LLVM IRへの理解を深める.
LLVMでプログラムを動作させるには, IRがどのような構成なのか知っておく必要があるでしょう.
http://adriansampson.net/media/llvm/llvm-containers.svg

Containers: 構成

ここではLLVMプログラムにおいて重要なコンポーネントの外観を示します.

  • モジュールはソースがいるもしくは変換ユニットを表現する. すべてはモジュールに含まれています.
  • 最もわかりやすいのはFuntionはモジュールに含まれています. 実行可能コードのまとめとして名付けられた( C++で, functionとmethodはLLVM functionに対応している.)
  • 名前と引数を宣言したfunctionは主にBasicBlocksコンテナを含みます.

BasicBlockはコンパイラでお馴染みですが, イメージとしては命令の塊のようなものです.

  • Instructionは, ひとつのコード命令である. 抽象化レベルはRISC機械語とほとんどおなじである. 例えば命令は, 整数加算, 浮動小数点除算, メモリへの格納などがあります.

LLVMのほとんど -- instruction, BasicBlock, Functionを含んだ -- それらは多くの基底クラスと呼ばれる値受け渡しのC++クラスがある. 値は計算に使用できる任意の値である. 例えば, 数値であれば, アドレスであったり, グローバル変数, 定数などが含まれる.


An Instruction:

ここでは人が読めるLLVM IRの命令を示します.

%5 = add i32 %4, 2

この命令は2つの32bitの整数値を加算するものです. つまりレジスタ4の数値と定数 2 を加算し, レジスタ5にその結果を格納します.
RISC機械語を理想とするとき, どんな意味があるでしょう. 私達はレジスタを同じようにつかうが, レジスタは無限のように存在する.

つまり同じ命令は, C++命令のクラスのインスタンスとして, コンパイラ内部を表現する.
オブジェクトは(型, 他のオブジェクトの値を指すポインタのオペランドのリストへ)追加命令するオペコードを持つ.
我々の場合には、 定数2と他の命令に相当するレジスタ5を示す定数オブジェクトを指します。(LLVM IRは性的単一代入形式(SSA形式)のため, レジスタと命令は1つと変わりない. レジスタ番号はtext表現のものである.)
ところで, あなたのプログラムのLLVM IRを見たい場合, clangに次のコマンドを与えます.

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

Inspecting IR in Our Pass PassによるIRの検査(解析)

私達の裏で働いていたLLVM Passを取得してみよう. 私達は便利な共通のdump()と呼ばれるメソッドを使用して,重要なIRすべてのオブジェクトを解析することができる. それはIRを人間が読める程度の表現にしてくれます.
PassがFunctionを渡して, 命令の各BasicBlockのセットの上に各FunctionのBasicBlocksを反復処理するために利用してみましょう。

ここでは次のコードを示す. llvm-passskeletonのgitレポジトリのcontainersをcheck outすることで手に入れられる.

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のautoタイプとforeach構文を使用すると、簡単にLLVM IRの階層を移動することができます。
もし, 再びビルドするなら, IRの様々な部分が分割して表示されるはずです.

LLVM for Grad Students 大学院生のためのLLVM を訳す5

Let’s Write a Pass
通常LLVMの研究は、パスを書くことを意味する.
この章では, ビルドを通して簡単なプログラムを変換するパスを動作させる.

A Skeleton

私は無駄なLLVMのパスが含まれているテンプレートリポジトリ

sampsyo/llvm-pass-skeleton · GitHub

フルスクラッチでやると, ビルド設定のとき痛い目をみるかもしれない.

$ 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パスにはいくつかの種類があり、我々はfunctionパスと呼ばれる1つのpassを使用しています(はじめはこれがいい).
期待した通りに, すべての"Function"をコンパイルしているプログラムの中で見つけ, 上記のメソッドを呼び出します.
それはすべてのメソッド名前を出力します. 逆に言えば変換するときはtrueを変えさせる必要がある.
Details:
errs() はコンソールに出力するために使用できるLLVMが提供するC++出力ストリームである.
functionはFが変更していない場合はFalseを返り値として返す.

Build It (構築)

CMakeでパスを構築する.

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

もしLLVMが環境にインストールされていない場合, どこにあるかCmakeに教える必要があります.

あなたはLLVM_DIR環境変数に格納された中のshare/llvm/cmake/ディレクトリへのパスを与えることによって, LLVMを構築することができる.4
ここではHomebrewのPathを説明します.

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

構築するあなたのパスは共有ライブラリを生成する.
/build/skeleton/libSkeltonPass.soか同じような名前をあなたの依存プラットフォームから見つけることができる.

つぎのステップでは, このライブラリをいくつかの実際のPassコードの実行中ロードします.

Run It (実行)

新しいPassを実行するとき, Cプログラム上のclangと実行し, さきほどコンパイルした共有ライブラリのフラグを使用する.

clang -Xclang -load -Xclang build/skeleton/libSkeletonPass.* something.c
  • Xclang -load -Xclang path/to/lib.so はClangのパスをアクティブにしてロードします(

Adrian Sampson: Run an LLVM Pass Automatically with Clang
).

もし大規模なプロジェクトを処理する必要があるならば, MakefileのCFLAGSにそれらの引数やビルド環境を追加することができる.
(独立したclangの実行から一度で一つのパスができる. これにはoptコマンドを利用する. この方法は公式でやられているので省略する.)

おめでとう. これであなたも開発者です. つぎの ステップでは, hello-world Passを改良し, 興味のあるプログラムへ変更してゆきます.

LLVM for Grad Students 大学院生のためのLLVM を訳す4

Getting Oriented

 改良してみよう!!

Get LLVM

まず, LLVMを入手する必要がある. LinuxディストリビューションはしばしばLLVMとClangのパッケージを提供する.
しかし, 改良のためのすべてのヘッダが含まれたベージョンを確実に手に入れる必要がある.
例えばOS XXcodeでビルドする. これは十分ではない. 幸いにもCmakeがあるため, ソースからLLVMをビルドすることは
難しいことではない. 通常, あなただけのLLVM環境を構築する必要がある.システムが提供するClangはバージョンが一致する限り動作する.
(だけど, Clangをビルドするための命令はたくさんある・・・)

個人のOS X上ではBrand holtはよい命令を提供する. またHomebrew形式である.

RTFM(ヘルプぐらい読めやっていうニュアンスらしい??) 

  あなたはわかりやすいドキュメントを取得する必要があります. 私はそれら価値がある個人リンク先を見つける.

  • 自動的に生成されたDoxygenページ(http://llvm.org/doxygen/)が超重要です!!.
    あなたはLLVMを改良しながら進捗を出すためには,  これらのAPIドキュメントの内部を知る必要があります.
    しかし、内部構造が面倒なためグーグルでDoxygenのページを検索することを進める.
     (LLVMを入力せずにLLVMの結果を入手することも可能だよ(?))
    馬鹿げて聞こえるかもしれませんが、本当にこのようなLLVMAPIドキュメントの周りにジャンプする必要があります.
    それは私はAPIのナビするための他の良い方法を見つけてないためでもあります.
  • もしあなたがLLVM IR dumpの構文によって混乱する場合は言語参照マニュアル(http://llvm.org/docs/LangRef.html)が便利です.
  • プログラマのマニュアル(http://llvm.org/docs/ProgrammersManual.html)はLLVMへの独特なデータ構造のtoolchestはについて説明し、効率的な文字列、マップのかわりとなるSTL, ベクトルなどを含む.
    また、どこでも実行できる高速型のイントロスペクション·ツール(isa、cast、dyn_cast)の概要を説明している.
  • パスに何ができるのかについて質問があるときは書き込みをWriting an LLVM Pass(http://llvm.org/docs/WritingAnLLVMPass.html) のtutorialを読んでください.
    記事の詳細なチュートリアルについて読むことは賛成しない.なぜならこのではコンパイラ開発者ではなく研究者だからである.
    まずはMakefileのビルドシステム命令は無視して, CMakeベースの“out-of-source” instructions(http://llvm.org/docs/CMake.html#cmake-out-of-source-pass)へ進んでください. やはり一般的なパスについて解説した標準的なソースである.
  • GitHubのミラーは、オンラインLLVMのソースを閲覧するため、時には便利です.

 

どうでもいいはなしだけどキーボードのVキーの聞きがわるい...LLVMってうつときいつもLLMってなる.

Raizerのキーボード買おうかなって思ったけど24kって;;;

お金溜まったら買うかもなぁ

LLVM for Grad Students 大学院生のためのLLVM を訳す3

The Pieces

 ここではLLVMアーキテクチャの主要なコンポーネント図を示します(現代の他のコンパイラの構造でもある).

Front End, Passes, Back End

 

フロントエンド: 

ソースコードを取得し、中間表現(IR)に変換する.
この変換は、C++ソースコードの複雑さに完全に対処する必要はなく, 残りの作業が対応します.
おそらくこの部分を改変する必要はありません. あなたはプレーンClangを使用できる。

 

パス:

IRをさらにほかのIRへ変換する.
通常では, パスはコードの最適化を行う. すなわち, 速さはおいといて,入力IRと同じ方法で、出力IRプログラムを生成する。
その部分があなたの改良を行う場所です.
あなたの研究ツールは調べることと, IRのコンパイルプロセスの流れを変更すること を行うことができる.

 

 

バックエンド:

実際のマシンコードを生成します。ほとんどの状況でこの部分に触れることはない.

 

  本章は今日のコンパイラのほとんどの部分について解説したが, LLVMの新規性はもっとある.
プログラムはプロセス全体を通して同じIRを使う. 他のコンパイラでは, 各パスは、独自のIRを生成することがある.
逆アプローチのためのLLVM optsは私たちにとってとても重要な機能である.
フロントエンドとバックエンドの間でコードが動作中,心配するする必要はない.

 

LLVM for Grad Students 大学院生のためのLLVM を訳す2

Why Would a Grad Student Care About LLVM

  なぜ大学院生はLLVMについて議論しないのか

LLVMはよいコンパイラであるが, もしあなたがコンパイラの研究をしなかったら誰がやってるのであろうか.

あなたがいろいろなことをするプログラムが必要なとき、コンパイラ基板は便利である. 経験上、このようなことはたくさんあります. 何をするかプログラムを分析することができたりとか. 

使うシステムに機能するために変換し, 実際に新しいチップまたはカーネルモジュールを記述することなく、あなたの新しい仮定のアーキテクチャやOSを使用するふりをして変更します。  
大学院生の場合、コンパイラ基板は、より人々の信用よりも、適切なツールです。
 私はLLVMのために あなたは理由がない限り、これらツールのいずれかを改良する前に、デフォルトをさわることを勧めする: 

  • アーキテクチャシミュレータ
  • ピン(intelの開発ソフト(?))のような動的バイナリツール
  • ソースレベルでの変換(ASTの解析と逐次化を含むsed(stream editor)のような完全なツールシンプルなものから)
  • システムコールを受け取るカーネルの改良
  • ハイパーバイザに似たもの
    コンパイラは、タスクに合致していてもしていない場合でも、多くの場合、はるかに簡単な方法がある. 90% ソース-ソース変換を得ることができる.
    ここでコンパイラではないことを行うためにLLVMを使用する研究プロジェクトのいくつかの例は次のとおりです。
  1. 仮想ゴースト(Virtual Ghost)は,UIUCから危険にさらされたOSカーネルからプロセスを保護するために、コンパイラパスを使用できることを示した.
  2. UWからのCoreDetは、マルチスレッドの決定的プログラムを作成する.
  3. 近似計算研究は、エラーが発生しやすいハードウェアをシミュレートする場合, エラーを挿入するためにLLVMのパスを使用します。
  4. LLVMは新しいコンパイラの最適化を実現するためのものではないことをさらに念を押しておく.