Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.

言語処理系入門€10

630 Aufrufe

Veröffentlicht am

会社でやっていたソフトウェア基礎講座での講義資料

Veröffentlicht in: Software
  • Als Erste(r) kommentieren

  • Gehören Sie zu den Ersten, denen das gefällt!

言語処理系入門€10

  1. 1. 言語処理系入門 第 10 回:コンパイラ III :コード生成,実 行時ライブラリ, GC 2010 年 1 月 8 日(金) 服部 健太
  2. 2. コンパイラのバックエンド ソース プログラム 字句・構文解析 型検査 CPS 変換 Cps.convTyping.checkparse クロージャ変換コード生成 目的 コード 実行時 ライブラリ 中間言語 抽象構文木 GC など実行時に必要な処理 実行時にロード・リンクされる C 言語で実装されることが多い C 言語,アセンブリコード, VM の中間言語(バイトコード) etc 今日のトピックス 2010/1/8 2言語処理系入門 10
  3. 3. クロージャ変換した後  K ::= def x1 = E1 and … and xn = En  | let x1 = E1 and … and xn = En in K  | let rec x1 = E1 and … and xn = En in K  | if V then K1 else K2  | case V of l1 -> K1 | … | ln -> Kn  | V (V1, … ,Vn)  E ::= V  |{V1;…; Vn }  | code f (x1,…,xn) = K in E  | V#l | V1#l <- V2  | op(V1, … ,Vn)  V ::= c | x∈Var  l ∈ Nat 大域変数の定義 2010/1/8 3言語処理系入門 10
  4. 4. コード生成の前処理  let rec の除去  let 式とフィールド更新の組み合わせに変換する [[let rec x = { …, li = x,… } in E]] ⇒ let x = { …, li = null,… } in let _ = x#i<-x in E  レコード生成,フィールド参照・更新をプリミティブ演算に変 換 [[{V1,…, Vn}]]  ⇒ let r = record(n) in let _ = setfld(r,1,V1) and … _ = setfld(r,n,Vn) in r  code のリフトアップ  式の中に散らばった code 定義を抜き出して集める  残りの式は,最初に実行される code の本体となる 2010/1/8 4言語処理系入門 10
  5. 5. コード生成の元言語  C ::= code f (x1,…,xn) = K  K ::= def x1 = E1 and … and xn = En  | let x1 = E1 and … and xn = En in K  | if V then K1 else K2  | case V of l1 -> K1 | … | ln -> Kn  | V (V1, … ,Vn)  E ::= V  | op(V1, … ,Vn)  V ::= c | x∈Var  l ∈ Nat 2010/1/8 5言語処理系入門 10
  6. 6. 末尾呼び出しの処理  CPS では,関数呼び出しはすべて末尾呼び出し  これをそのまま C 言語の関数呼び出しに変換すると, いずれスタックオーバーフローとなりうまくいかない  解決方法  ラベルと goto 文を使う  switch 文を使う  ディスパッチャから関数を呼び出す( Trampoline ス タイル) code f(x) = … g(x’) code g(y) = … h(y’) code h(z) = … j(z’) 2010/1/8 6言語処理系入門 10
  7. 7. Trampoline Style  ディスパッチャの構造 void dispatch(func_t f) { while ((f = f()) != NULL); }  f は関数ポインタ  f を呼び出すと次に実行すべき関数のポインタを 返す void *some_func(void) { … return next_func; } 2010/1/8 7言語処理系入門 10
  8. 8. コード生成(1)  変換関数  [[ ・ ]] は変換元の式 Kclosure と環境 ρ を受け取り, C 言語の コードを出力する  環境 ρ は,変数からその変数に割り当てられたインデック スを返す関数  変数,定数  [[x]]ρ⇒ locals[ρ(x)] x DOM(ρ)∈ x otherwise  [[n]]ρ⇒ CONST_INT(n) n∈Integer  [[b]]ρ⇒ CONST_BOOL(b) b∈{true,false}  プリミティブ演算子適用  [[op(V1,…, Vn)]]ρ ⇒ op([[V1]]ρ,…,[[Vn]]ρ) 2010/1/8 8言語処理系入門 10
  9. 9. コード生成(2)  def 式  大域変数としてそのまま使用する [[def x1 = E1 and … xn = En]]ρ ⇒ x1 = [[E1]]ρ; … xn = [[En]]ρ;  let 式  局所変数の空いてる場所に代入していく [[let x1 = E1 and … xn = En in K]]ρ ⇒ locals[l1] = [[E1]]ρ; … locals[ln] = [[En]]ρ; [[K]]ρ{x1→l1,x2→l2,…,xn→ln} where li = ρ.size + i - 1 for each 1≦i≦n 2010/1/8 9言語処理系入門 10
  10. 10. コード生成(3)  if 式 [[if V then K1 else K2]]ρ ⇒ if ([[V]]ρ == CONST_TRUE) { [[K1]]ρ } else { [[K2]]ρ }  case 式 [[case V of l1 -> K1 | … | ln -> Kn]]ρ ⇒ switch (GET_LABEL([[V]]ρ)) { case l1: { [[K1]]ρ } … case ln: { [[Kn]]ρ } } 2010/1/8 10言語処理系入門 10
  11. 11. コード生成(4)  関数定義 [[code f (x1,…,xn) = K]]φ ⇒   void* f(void) { locals[0] = args[0]; … locals[n-1] = args[n-1];   [[K]]{x1→0,…,xn→n-1} }  関数呼び出し [[V(V1,…,Vn)]]ρ ⇒ args[0] = [[V1]]ρ; … args[n-1] = [[Vn]]ρ; return [[V]]ρ; 関数に与える引数を args にセット 受け取った引数を locals にセット 2010/1/8 11言語処理系入門 10
  12. 12. 生成コードの例 #include <tfunlc.h> obj_t __g_fact__; static void *__t16__(void); static void *__t23__(void); static void *__t19__(void); static obj_t args[] __attribute__((unused)); static obj_t locals[] __attribute__((unused)); static obj_t *globals[] __attribute__((unused)); static void *__t16__(void) { locals[0] = args[0]; locals[1] = args[1]; locals[2] = args[2]; locals[3] = OP_GETFLD(locals[0],CONST_LBL(1)); locals[4] = OP_EQ(locals[2],CONST_INT(0)); locals[5] = OP_RECORD(CONST_LBL(2)); OP_SETFLD(locals[5],CONST_LBL(0),__t23__); OP_SETFLD(locals[5],CONST_LBL(1),locals[1]); if (locals[4] == CONST_TRUE) { locals[6] = OP_GETFLD(locals[5],CONST_LBL(0)); args[0] = locals[5]; args[1] = CONST_INT(1); return locals[6]; } else { locals[6] = OP_SUB(locals[2],CONST_INT(1)); locals[7] = OP_RECORD(CONST_LBL(3)); OP_SETFLD(locals[7],CONST_LBL(0),__t19__); OP_SETFLD(locals[7],CONST_LBL(1),locals[5]); OP_SETFLD(locals[7],CONST_LBL(2),locals[2]); locals[8] = OP_GETFLD(locals[3],CONST_LBL(0)); args[0] = locals[3]; args[1] = locals[7]; args[2] = locals[6]; return locals[8]; } return (void*)HALT; } static void *__t23__(void) { locals[0] = args[0]; locals[1] = args[1]; locals[2] = OP_GETFLD(locals[0],CONST_LBL(1)); locals[3] = OP_GETFLD(locals[2],CONST_LBL(0)); args[0] = locals[2]; args[1] = locals[1]; return locals[3]; } static void *__t19__(void) { locals[0] = args[0]; locals[1] = args[1]; locals[2] = OP_GETFLD(locals[0],CONST_LBL(2)); locals[3] = OP_GETFLD(locals[0],CONST_LBL(1)); locals[4] = OP_MUL(locals[2],locals[1]); locals[5] = OP_GETFLD(locals[3],CONST_LBL(0)); args[0] = locals[3]; args[1] = locals[4]; return locals[5]; } static void *__def_0__(void) { locals[0] = OP_RECORD(CONST_LBL(2)); OP_SETFLD(locals[0],CONST_LBL(0),__t16__); OP_SETFLD(locals[0],CONST_LBL(1),locals[0]); __g_fact__ = locals[0]; return (void*)HALT; } int main() { globals[0] = &__g_fact__; gc_init(globals, 1, locals, 9); exn_init(); if (tfl_run(__def_0__) < 0) return -1; return 0; } static obj_t args[3]; static obj_t locals[9]; static obj_t *globals[1]; 2010/1/8 12言語処理系入門 10
  13. 13. 実行時ライブラリ  以下のように,実行時に必要な定義をあらかじめ C 言語でライブラリとして実装しておく  strcat, print_int, など一部のプリミティブ演算子の実装  デフォルトの例外ハンドラ定義  ディスパッチャ定義  GC  etc…  生成したコードと実行時ライブラリをリンクするこ とで実行ファイルが出来上がる.  サンプルプログラムでは, rtlib/ の下に実行時ライ ブラリのソースがある 2010/1/8 13言語処理系入門 10
  14. 14. ゴミ集め( Garbage Collection )  使われないオブジェクトを回収する  「使われない」の定義  ルート集合から到達できないオブジェクト  ルート集合={レジスタ,大域変数,スタック上の局所変数, etc }  代表的な手法  参照カウント  各オブジェクトが被参照数を表すカウンタを保持  被参照数が 0 になったら解放される  マーク&スイープ  ゴミ集め時にルート集合からオブジェクトを辿って,印をつけておく  ヒープ領域をスキャンし,印のついていないオブジェクトを回収する  コピー GC  ヒープを 2 つの領域に分割し,片方だけ使う.  一方の領域が満杯になったら,ルート集合からオブジェクトを辿りなが ら,もう片方の領域にコピーしていく. 2010/1/8 14言語処理系入門 10
  15. 15. オブジェクト(ポインタ)の識別 方法  GC で回収されるもの  文字列バッファ,レコード  GC で回収されないもの  (ヒープに割り付けられないもの)  整数値,真偽値, Unit  文字列定数  ポインタと非ポインタをどうやって識別するか?  Exact 方式  下位 1 ビット分をタグとして使用する  Conservative GC  それっぽいものはすべてポインタとみなす ヒープ領域x: 123 truey: z: “hello” w: 5 true レジスタ等 25 “b” 2010/1/8 15言語処理系入門 10
  16. 16. データ表現  基本型  Unit,Bool,Int,etc.  下位 1 ビット分をタグとして使用  レコード型  複数のデータを含むオブジェクト  ヒープに確保される  バッファ型  可変文字列に使用  ヒープに確保される int sz sz “hello,worldn” bool 2010/1/8 16言語処理系入門 10
  17. 17. Cheney’s Algorithm  エレガントなコピー GC のアルゴリズム  余分な記憶領域が不要  free と scan の 2 つのポインタだけで OK  処理の概要  オブジェクトを移動先の空き領域 (free ポインタの指す先 ) にコピーし, free ポインタを先に進める  コピー先のオブジェクトの中をスキャンし,ポインタが含 まていたら,その先のオブジェクトをチェックし, scan ポインタを進める  もし,まだ移動してなかったら,そのオブジェクトを移動し ,新しいポインタをセット  すでに移動済みなら,移動先のポインタをセット  scan ポインタが free ポインタに追いついたら終了 2010/1/8 17言語処理系入門 10
  18. 18. Cheney’s Algorithm の動作 A B C D E F G Fromspace Tospace free scan 2010/1/8 18言語処理系入門 10
  19. 19. Cheney’s Algorithm の動作 A’A B C D E F G Fromspace Tospace free scan A’ 移動先のアド レスを入れて おく 2010/1/8 19言語処理系入門 10
  20. 20. Cheney’s Algorithm の動作 A’ B’ C’ A B C D E F G Fromspace Tospace free scan A’ B’ C’ 2010/1/8 20言語処理系入門 10
  21. 21. Cheney’s Algorithm の動作 A’ B’ D’ E’ C’ F’ G’ A B C D E F G Fromspace Tospace free scan A’ B’ C’ D’ E’ F’ G’ 2010/1/8 21言語処理系入門 10
  22. 22. Cheney’s Algorithm の動作 A’ B’ D’ E’ C’ F’ G’ A B C D E F G Fromspace Tospace free scan A’ B’ C’ D’ E’ F’ G’ 2010/1/8 22言語処理系入門 10
  23. 23. Cheney’s Algorithm の実装(1) flip() = to_space, from_space = from_space, to_space; heap_top = to_space + space_size; scan = free = to_space; foreach (r in route_set) r = copy(r); while (scan < free) { sz = size(scan); for (p = scan + 1; p < scan + sz; p++) *p = copy(*p); scan += sz; } 2010/1/8 23言語処理系入門 10
  24. 24. Cheney’s Algorithm の実装(2) copy(p) = if (is_noptr(p)) return p; if (is_forwarded(p)) return forward_address(p); else addr = free; sz = size(p); memcpy(heap_free,p,sizeof(obj_t)*sz); free += sz; forward_address(p) = addr; return addr; 2010/1/8 24言語処理系入門 10
  25. 25. 本講座で触れられなかった話題  モジュールシステムと分割コンパイル  トップレベルの(大域)環境を分割して扱えるように する  パターンマッチコンパイル  複雑なパターンマッチ式を,単純な case 式の組み合 わせに変換する  エスケープ解析  変数の指し示すオブジェクトがその変数の静的スコー プを抜けて,生存しうるかどうかを解析する  型付き中間言語  中間言語にも型情報を持たせることで,生成コードを 最適化しやすくなる  その他たくさん... 2010/1/8 25言語処理系入門 10
  26. 26. 演習課題  今週のサンプルプログラムを動かしてみよ  簡単なプログラムをコンパイルし,生成され たコードを確かめよ  以下のようなプリミティブ演算子を追加せよ  read_line  int_to_str, str_to_int, etc.  文字列が適切な形式でなかったら例外を投げる 2010/1/8 26言語処理系入門 10

×