2010年03月29日

LLVM の JIT を使ってみる (2)

JIT の API を見ていきます。

ざっと考えてみると、最低限プログラムを実行するためには、以下のような項目が必要だと考えられます。

  • 初期化、終了処理
  • コンパイル単位 (モジュール)
    • 関数定義
        • 式(変数、定数、関数呼び出しなど)
  • プログラムの実行、結果の取得


まずは HowToUseJIJT.cpp で使われている API から広げていきます。

索引: http://llvm.org/docs/doxygen/html/namespacemembers.html

  • 初期化、終了処理
bool llvm::InitializeNativeTarget()

(llvm/Target/TargetSelect.h)

JIT なので、当然ネイティブ環境で実行されます。

void llvm_shutdown()

(llvm/Support/ManagedStatic.h)

終了処理です。ManagedStatic 変数というのが何なのかまだよくわかってないのですが、全ての ManagedStatic 変数の後始末を行ってくれるそうです。

  • コンパイル単位 (モジュール)
LLVMContext Context;

(llvm/LLVMContext.h)

LLVM が使用するグローバルデータが入っているクラスです。1 スレッド 1 LLVMContext を保証する必要があります。

Module* m = new Module("モジュール名", Context);

(llvm/Module.h)

これが中間表現のメインコンテナです。

モジュールのメンバ関数を呼び出して、関数などを追加していきます。

  • 関数定義
Constant *getOrInsertFunction(StringRef Name,
                  const Type *RetTy, ...)  END_WITH_NULL

(llvm/Module.h)

この API で関数(を表現するデータ構造)を作ります。

可変長引数になっていて、「関数名、関数(の戻り値)の型、引数1 の型、引数 2 の型、…、NULL」となります。

モジュール内のシンボルテーブルを引き、関数が存在しなければ新たに作成し、モジュールに追加した後、返します。

型はいろいろあります。(llvm/Type.h)

00399   // These are the builtin types that are always available...
00400   //
00401   static const Type *getVoidTy(LLVMContext &C);
00402   static const Type *getLabelTy(LLVMContext &C);
00403   static const Type *getFloatTy(LLVMContext &C);
00404   static const Type *getDoubleTy(LLVMContext &C);
00405   static const Type *getMetadataTy(LLVMContext &C);
00406   static const Type *getX86_FP80Ty(LLVMContext &C);
00407   static const Type *getFP128Ty(LLVMContext &C);
00408   static const Type *getPPC_FP128Ty(LLVMContext &C);
00409   static const IntegerType *getInt1Ty(LLVMContext &C);
00410   static const IntegerType *getInt8Ty(LLVMContext &C);
00411   static const IntegerType *getInt16Ty(LLVMContext &C);
00412   static const IntegerType *getInt32Ty(LLVMContext &C);
00413   static const IntegerType *getInt64Ty(LLVMContext &C);

特定のアーキテクチャ固有らしき型も primitive に入ってますね。

BasicBlock *BB = BasicBlock::Create(Context, "ブロック名", 関数);

(BasicBlock.h)

BasicBlock (BB) は、ブランチ(ジャンプ)命令 (TerminatorInst) を中間に含まない(最後には含んでも良い)命令列を表現するデータ構造です。ブランチ命令や switch case のテーブルは必ず BB を参照することになります。(BB の型は Type::LabelTy です。)

  • 式(変数、定数、関数呼び出しなど)
Value *One = ConstantInt::get(Type::getInt32Ty(Context), 1);

定数の 1 (を表すデータ構造) は、こんな感じで作られるようです。Value は全ての値(を表すデータ構造)の基底なので、何でも指せます。

(llvm/Value.h)

同じ要領で、関数の引数を取得したり、加算式や関数呼び出し式、return 文(命令) などを生成しています。

ちょっと面白いと思った API が、

setTailCall(true)

これはおそらく、末尾呼び出しの最適化のための API でしょう。

関数の最後で関数が呼び出された場合、わざわざ呼び出し元の関数に戻ってくる必要が無いので、x86 で言うところの ret ではなく、jmp に最適化することができます。

参考: なんでも継続

  • プログラムの実行、結果の取得
  ExecutionEngine* EE = EngineBuilder(M).create();

(llvm/ExecutionEngine/ExecutionEngine.h)

  GenericValue gv = EE->runFunction(エントリー関数, 引数);

(llvm/ExecutionEngine/GenericValue.h)

  EE->freeMachineCodeForFunction(関数);
  delete EE;

後半駆け足になってしまいました。

後は実際にサンプルを参考にしながら API リファレンスを引き引き作っていくのが良さそうです。



トラックバックURL

コメントする

名前
URL
 
  絵文字
 
 
記事検索
最新コメント
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード