C言語を用いたマシン非依存な JITコンパイラ作成フレームワーク

Slides:



Advertisements
Similar presentations
プログラミング演習II 2004年11月 30日(第6回) 理学部数学科・木村巌.
Advertisements

実行時のメモリ構造(1) Jasminの基礎とフレーム内動作
コンパイラ 第9回 コード生成 ― スタックマシン ―
OSとコマンド OS:コンピュータを使うための基本プログラム コマンド:OS上で使用できる命令 OS本体であるカーネルの内部コマンド
第2回:Javaの変数と型の宣言 プログラミングII 2007年10月2日.
ファイルシステムキャッシュを 考慮した仮想マシン監視機構
侵入検知システム(IDS) 停止 IDS サーバへの不正アクセスが増加している
2006/10/19 山下 諒蔵 佐藤 春旗 前田 俊行 大山 恵弘 佐藤 秀明 住井英二郎
  【事例演習6】  数式インタプリタ      解 説     “インタプリタの基本的な仕組み”.
プログラムはなぜ動くのか.
の まとめ 2007/04/02 (Mon) / d;id:hzkr
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
ネストした仮想化を用いた VMの安全な帯域外リモート管理
同期的にアドバイスを活性化できる分散動的アスペクト指向システム
Javaプログラムの実行まで バイト Javaの コード 実行 ソースコード Java ファイル名 ファイル名 abc.java
動的スライスを用いたバグ修正前後の実行系列の差分検出手法の提案
変更文の移動を可能にした 静的単一代入形式上の部分冗長性除去
アスペクト指向プログラミングを用いたIDSオフロード
二分探索木によるサーチ.
進捗 Javaバイトコード変換による 細粒度CPU資源管理
型付きアセンブリ言語を用いた安全なカーネル拡張
細かい粒度でコードの再利用を可能とするメソッド内メソッドのJava言語への導入
プログラミング言語入門 手続き型言語としてのJava
言語プロセッサ2007 平成19年9月26日(水) (Ver.2 平成19年10月3日変更)
細かい粒度で コードの再利用を可能とする メソッド内メソッドと その効率の良い実装方法の提案
勉強会その3    2016/5/1 10 8分35秒 データの表現 演算.
暗黙的に型付けされる構造体の Java言語への導入
セキュリティ(3) 05A2013 大川内 斉.
最適化の方法 中田育男著 コンパイラの構成と最適化 朝倉書店, 1999年 第11章.
オブジェクト指向プログラムにおける エイリアス解析手法の提案と実現
コンピュータ系実験Ⅲ 「ワンチップマイコンの応用」 第1週目 アセンブリ言語講座
プログラミング言語入門.
動的スライスを用いたバグ修正前後の実行系列の差分検出手法
ユーザ毎にカスタマイズ可能な Webアプリケーションの 効率の良い実装方法
通信機構合わせた最適化をおこなう並列化ンパイラ
コンパイラ資料 実行時環境.
軽量な仮想マシンを用いたIoT機器の安全な監視
フロントエンドとバックエンドのインターフェース
Javaへの変換による 安全なC言語の実装
先週の復習: CPU が働く仕組み コンピュータの構造 pp 制御装置+演算装置+レジスタ 制御装置がなければ電卓と同様
B演習(言語処理系演習)第2回 田浦.
Javaバーチャルマシンを利用した 動的依存関係解析手法の提案
JAVAバイトコードにおける データ依存解析手法の提案と実装
参照されないリテラル 長谷川啓
コンピュータアーキテクチャ 第 2 回.
同期処理のモジュール化を 可能にする アスペクト指向言語
計算機プログラミングI 木曜日 1時限・5時限 担当: 増原英彦 第1回 2002年10月10日(木)
コンピュータアーキテクチャ 第 2 回.
プログラムの差分記述を 容易に行うための レイヤー機構付きIDEの提案
アルゴリズムとデータ構造1 2009年6月15日
ネットワーク・プログラミング デバイスドライバと環境変数.
ネットワーク・プログラミング Cプログラミングの基礎.
第5回 プログラミングⅡ 第5回
統合開発環境のための プログラミング言語拡張 フレームワーク
言語プロセッサ 第12日目 平成20年1月9日.
ドキュメントジェネレータ 詳細仕様 長谷川啓
エイリアス関係を考慮した Javaプログラム用静的スライシングツール
プログラムの一時停止時に 将来の実行情報を提供するデバッガ
動的スライスを用いたバグ修正前後の実行系列の差分検出手法の提案
動的スライスを用いたバグ修正前後の実行系列の差分検出手法の提案
Inline 展開のアルゴリズム 長谷川啓
アルゴリズムとデータ構造 2010年6月17日
プログラミング演習I 2003年6月11日(第9回) 木村巌.
情報処理Ⅱ 2005年11月25日(金).
情報処理Ⅱ 小テスト 2005年2月1日(火).
C#プログラミング実習 第1回.
TList リスト構造とは? 複数のデータを扱うために、 データの内容と、次のデータへのポインタを持つ構造体を使う。
1.2 言語処理の諸観点 (1)言語処理の利用分野
6.3 インタプリタ (1)インタプリタ(interpreter)とは
6.5 最終コード生成 (1)コードの形式 ①絶対2進コード(AB : absolute binary) 命令後のオペランドが絶対番地指定。
Presentation transcript:

C言語を用いたマシン非依存な JITコンパイラ作成フレームワーク 栗田洋輔 千葉滋 東京工業大学

従来のJITコンパイラの開発と問題点 本格的なJITコンパイラ プラットフォーム依存の中間表現を作成し、それを機械語に変換 移植性の問題 マシンごとにプラットフォーム依存の中間表現を作成 実装の容易性の問題 高い開発スキルが必要 バイトコードから中間表現への変換 中間表現から機械語への変換 バイトコード 機械語 プラットフォーム依存の中間表現

本研究の目的 JIT コンパイラ作成を容易にするフレームワーク 移植性が高い 短いコンパイル時間でそこそこの品質のコードを生成 実装の容易性 実行速度 コンパイル時間 ネイティブコンパイラ △ × ◎ インタープリタ 従来のJITコンパイラ ○ 本フレームワークを用いて開発するJITコンパイラ

そこそこの最適化 本質的なオーバーヘッドを除去することで高速化を行う 本質的なオーバーヘッド 広い範囲のインタープリターに適用できる 汎用的な手法 本質的なオーバーヘッド バイトコードインタープリター 次の命令をフェッチしてデコードするコスト バイトコードのオペランドをメモリからロードするコスト 抽象構文木のインタープリター 抽象構文木を巡回してノードをデコードするコスト

命令をフェッチ・デコードする オーバーヘッド (1/2) 命令ポインタ(ip)のインクリメントと メモリからのフェッチ インタープリター トレース char *ip = code – 1; for (;;) { char* bcode = *++ip; switch (bcode) case PUSH_CONST: *++sp = (int)*++ip; break; case ADD: --sp; *sp += sp[1]; /* other cases …*/ } フェッチ デコード 本体 bcode = *++ip; PUSH_CONST 2 3 ADD バイトコード switch (bcode) ip *++sp = (int)*++ip; ip ip フェッチ デコード 本体 bcode = *++ip; ip switch (bcode) ip *++sp = (int)*++ip; フェッチ デコード 本体 bcode = *++ip; switch (bcode) --sp; *sp += sp[1];

命令をフェッチ・デコードする オーバーヘッド (2/2) バイトコードのデコード switchによる条件分岐で実装 インタープリター トレース char *ip = code – 1; for (;;) { char* bcode = *++ip; switch (bcode) case PUSH_CONST: *++sp = (int)*++ip; break; case ADD: --sp; *sp += sp[1]; /* other cases …*/ } フェッチ デコード 本体 bcode = *++ip; PUSH_CONST 2 3 ADD バイトコード switch (bcode) *++sp = (int)*++ip; フェッチ デコード 本体 bcode = *++ip; switch (bcode) *++sp = (int)*++ip; フェッチ デコード 本体 bcode = *++ip; switch (bcode) --sp; *sp += sp[1];

命令をフェッチ・デコードする オーバーヘッドの除去 高速化を達成 フェッチ・デコード部分を削除 本体部分のみを並べる 命令ポインタの指すアドレスが不正 フェッチ デコード 本体 bcode = *++ip; *++sp = (int)*++ip; bcode = *++ip; *++sp = (int)*++ip; switch (bcode) *++sp = (int)*++ip; フェッチ デコード 本体 switch (bcode) *++sp = (int)*++ip; --sp; *sp += sp[1]; フェッチ デコード 本体 switch (bcode) --sp; *sp += sp[1];

バイトコードのオペランドをメモリからロードするコストの削減 オペランドの値が既知の場合には不必要 フェッチ デコード 本体 bcode = *++ip; *++sp = (int)*++ip; bcode = *++ip; *++sp = (int)*++ip; switch (bcode) *++sp = (int)*++ip; *++sp = 2; フェッチ デコード 本体 *++sp = 3; switch (bcode) *++sp = (int)*++ip; --sp; *sp += sp[1]; --sp; *sp += sp[1]; フェッチ デコード 本体 switch (bcode) --sp; *sp += sp[1];

JIT コンパイラ・フレームワーク の提案 プラットフォーム独立な中間表現 テンプレート 中間表現から機械語への変換はフレームワークが担当 高い移植性を実現(フレームワーク自体の移植は必要) テンプレート 中間表現の各ノードに対応する機械語のひな形 中間表現のセマンティクスを与える JITコンパイラの 作成者が実装 テンプレート バイトコード 機械語 プラットフォーム独立な中間表現

テンプレートの書き方 インタープリターの主ループそのもの 記述は容易 一部の命令は、テンプレート用に書き換えが必要 命令ポインタ (IP) を使っている場合 生成される機械語に IP は含められない IP でオペランド(即値)を取得する HOLEマクロを利用 gccインラインアセンブリに展開 機械語生成時に即値を挿入 IP で分岐先を決定する 分岐命令にはテンプレート不要 テンプレート char *ip = code – 1; for (;;) { char* bcode = *++ip; switch (bcode) case ADD: --sp; *sp += sp[1]; break; case PUSH_CONST: *++sp = (int)*++ip; case TMPL_PUSH_CONST: HOLE(hole1, int_val); *++sp = int_val; } case TMPL_PUSH_CONST: HOLE(hole1, int_val); *++sp = int_val; break;

プラットフォーム独立な中間表現 各ノード バイトコード命令(あるいは式)に対応 属性 中間表現への変換は容易 テンプレートのどの部分を使って機械語を生成するか HOLE に挿入する即値の値 中間表現 抽象構文木 バイトコード PLUS label: TMPL_PUSH holes: {hole1:=2} second third PUSH 2 NUMBER: 2 NUMBER: 3 PUSH 3 label: TMPL_PUSH holes: {hole1:=3} ADD 中間表現 label: PLUS label: TMPL_ADD holes: NULL label: NUMBER holes: {hole1:=2} label: NUMBER holes: {hole1:=3}

中間表現への変換は容易 インタープリタの制御構造を流用可能 appendCmpnt addHoleInfo を作るコード インタープリタの制御構造を流用可能 appendCmpnt BlockCmpntをリストの末尾に追加 addHoleInfo Holeに埋め込む値を設定 char *ip = code – 1; for (;;) { char* bcode = *++ip; switch (bcode) case ADD: cmpnt = appendCmpnt(cmpnt, ip v_ADD); break; case PUSH_CONST: appendCmpnt(cmpnt, ip, v_TMPL_PUSH_CONST); addHoleInfo(cmpnt, ip, v_push_const_hole, *((int *)(++ip)) ); } 中間表現 label: TMPL_PUSH_CONST holes: {push_const_hole := 2} holes: {push_const_hole := 3} label: ADD holes: NULL

フレームワークによるコード生成 入力:中間表現、出力:機械語 機械語生成の流れ コンパイル済みテンプレートの逆アセンブル情報を利用する 1.中間表現で指定されたコンパイル済みテンプレートを、最後のjmp命令やret命令を除きコピー 2. 相対アドレスを用いた部分をコードが移動した分だけずらす 3. 中間表現で指定された即値命令に指定された即値を埋め込む 4. バイトコードの分岐命令は機械語の分岐命令に変換 中間表現 コンパイル済みテンプレート 機械語 label: TMPL_PUSH holes: {hole1 := 2} 参照 コピー TMPL_PUSH movl $1234, %eax … movl $2, %eax … hole1: movl $1234, %eax … label: TMPL_PUSH holes: {hole1 := 3} movl $1234, %eax … movl $3, %eax … … TMPL_ADD label: TMPL_ADD holes: NULL …

バイトコードの分岐命令の処理 分岐先バイトコードのアドレスが入った中間表現を作る 機械語の分岐命令に変換 バイトコードのアドレスを機械語のアドレスへフレームワークが変換 バイトコード unsigned char code[] = { …, GOTO, -0x100, …}; 中間表現 label: … バイトコード番地: 0x0200 機械語 … 0x10000000 label: … バイトコード番地: 0x0204 … 0x10000020 中略 中略 label: NULL バイトコード番地: 0x0300 type: jump ジャンプオフセット: -0x0100 jmp 0x10000000 0x10001000

中間表現での最適化 中間表現のノードの並べ替え 複数ノードを最適化された1ノードへ置換 例 定数の畳み込み 中間表現 (最適化前) 例 定数の畳み込み 中間表現 (最適化前) label: TMPL_PUSH_CONST holes: {push_const_hole => 2} holes: {push_const_hole => 3} label: ADD holes: NULL 中間表現 (最適化後) label: TMPL_PUSH_CONST holes: {push_const_hole => 5} ・中間表現の範囲内で任意の最適化アルゴリズムを組み込み可能 ・ユーザーが望むバイトコードの最適化機能をJITコンパイラに追加できる  ・中間表現のオープンな仕様 ・やり方  ・  。。。 ・例:定数の畳み込み

抽象構文木インタープリターの場合 抽象構文木を直接解釈するインタープリター switch文がオーバーヘッド eval 関数が再帰呼び出し バイトコードインタープリターと同様 eval 関数が再帰呼び出し インタープリター int eval(List* expr) { switch (testElementType(expr)) { /* ... */ case PLUS : return eval(getSecond(expr)) + eval(getThird(expr)); case NUMBER : return getIntegerElement(expr); }} 抽象構文木 PLUS second third NUMBER: 2 NUMBER: 3

入れ子の関数呼び出しの中間表現 中間表現を木構造に 入れ子で呼ばれる関数を表すノードを 木構造の子ノードに 抽象構文木 PLUS second third NUMBER: 2 NUMBER: 3 中間表現 label: PLUS label: NUMBER holes: {hole1 := 2} label: NUMBER holes: {hole1 := 3}

入れ子の関数呼び出しのテンプレート 引数なしのvoid関数がテンプレート LABEL マクロ 値の受け渡しはグローバル変数で テンプレートの内部に他のテンプレートを挿入可能 中間表現 テンプレート PLUS PLUS_label1 PLUS_label1 void code_PLUS() { LABEL(PLUS_section); sp--; *sp = sp[1]; } LABEL(PLUS_label1); NUMBER 2 NUMBER 3 機械語 void code_PLUS() { void code_NUMBER() { int ret_val; HOLE(hole1, ret_val); *++sp = ret_val; } *++sp = 2; *++sp = 3; sp--; *sp = sp[1]; }

フレームワークのマシン非依存性 JITコンパイラの作成者 フレームワーク自体 マシンアーキテクチャを意識しない 中間表現はプラットフォーム独立 フレームワーク自体 マシンアーキテクチャ固有の実装 コンパイル済みテンプレートの機械語の解析 インラインアセンブリに展開されるマクロ 即値命令に定数を埋め込む方法 機械語の分岐命令 相対アドレスの調整 gccが必要 gccインラインアセンブリを使用

予備的な実験 ディスパッチにswitchを用いた抽象構文木 インタープリター Intel Xeon CPU 3.06GHz x 2 memory 2GB linux 2.6.17 gcc 4.1.1 glibc 2.4

関連研究 Cコンパイラが生成した機械語を利用する研究 ErtlらのJITコンパイラの移植に関する研究 [Ertl 04] 命令ポインタを除去して高速化するところが本研究に類似 インタープリタを書き換えないため、バイトコードの即値命令を機械語の即値命令に書き換えられない場合がある 本研究はフレームワークおよびそれが解釈する中間表現を提供している DyC [Grant 00] 伝統的なMultiflow Compilerをベースにしている 移植性に焦点が当てられていない Tempo [Noel 98] ランタイムコンパイラをフロントエンドからバックエンドまで提供 ネイティブコードの生成が自動化されているため、バイトコード命令レベルの最適化を行えない

まとめ JITコンパイラを作成するフレームワーク テンプレートが中間表現にセマンティクスを与える 高い移植性 そこそこの品質のコードを生成 プラットフォーム独立なコードを書くだけ そこそこの品質のコードを生成 オーバーヘッドを除去 命令のフェッチ・デコード オペランドのロード 実装が容易 インタープリタの実装を流用可能 テンプレートが中間表現にセマンティクスを与える 置き換えで独自の最適化が可能

終わり ご清聴ありがとうございました