Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

言語処理系入門€10

597 views

Published on

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

Published in: Software
  • Be the first to comment

  • Be the first to like this

言語処理系入門€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

×