1 / 30

「美しい日本の ML コンパイラ」 を読む~ MinCaml 解説

「美しい日本の ML コンパイラ」 を読む~ MinCaml 解説. 福盛秀雄 http://fukumori.org. Ver.2005.08.11. MinCaml とは. OCaml のサブセット. あるもの: 型推論 基本型 : int 、 float 派生型 : tuple, array 高階関数. ないもの: パターンマッチング 多相性(ポリモルフィズム) ガーベッジコレクション レコード型. コンパイラを構成する ファイルとモジュール. <ファイル名> .ml で暗黙のモジュールが構成される.

chenoa
Download Presentation

「美しい日本の ML コンパイラ」 を読む~ MinCaml 解説

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 「美しい日本のMLコンパイラ」を読む~MinCaml解説「美しい日本のMLコンパイラ」を読む~MinCaml解説 福盛秀雄 http://fukumori.org Ver.2005.08.11

  2. MinCamlとは OCamlのサブセット • あるもの: • 型推論 • 基本型: int、float • 派生型: tuple, array • 高階関数 • ないもの: • パターンマッチング • 多相性(ポリモルフィズム) • ガーベッジコレクション • レコード型

  3. コンパイラを構成するファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される syntax.ml → Syntaxモジュール typing.ml → Typingモジュール kNormal.ml → KNormalモジュール などなど <ファイル名>.mliはインタフェース宣言 同名のモジュールの外部仕様を定義する typing.mli → Typingモジュールの外部インタフェース

  4. .mli(インタフェース)を押さえる <モジュール名>.fが 各モジュールの主力関数 typing.mli 3: val f : Syntax.t -> Syntax.t kNormal.mli 28: val f : Syntax.t -> t alpha.mli 1: val f : KNormal.t -> KNormal.t などなど

  5. モジュール間のデータの流れ .mliファイルに記述されている内容と Mainモジュールの処理をつき合わせてみると… main.ml 13: Emit.f outchan 14: (RegAlloc.f 15: (Simm13.f 16: (Virtual.f 17: (Closure.f 18: (iter !limit 19: (Alpha.f 20: (KNormal.f 21: (Typing.f 22: (Parser.exp Lexer.token l))))))))) モジュール間で データを変形させながら 流していく姿が見える SparcAsm.prog SparcAsm.prog Closure.prog KNormal.t KNormal.t KNormal.t Syntax.t iterの内容は 次のページで Syntax.t

  6. モジュール間のデータの流れ(2) 関数“iter”の内容はこんなカンジ: main.ml (Closure.fへ) モジュール間で データを変形させながら 流していく姿が見える KNormal.t 6: ... Elim.f (ConstFold.f (Inline.f (Assoc.f (Beta.f e)))) KNormal.t KNormal.t KNormal.t KNormal.t KNormal.t (Alpha.fから) 流れているデータの内容はどうなっているのだろう?

  7. データ型定義を押さえる <モジュール名>.tがデータ型 序盤~中盤の処理で重要なのは以下の3つ: Syntax.t 構文木を表現するデータ型 KNormal.t K正規系(中間表現)を表すデータ型 Type.t 型表現を表すデータ型

  8. 構文木(Syntax.t) syntax.ml 1: type t = (* MinCamlの構文を表現するデータ型 *) 2: |Unit 3: |Boolof bool 4: |Intof int 8: |Addof t * t 19: |Ifof t * t * t 20: |Letof (Id.t * Type.t) * t * t 21: |VarofId.t 22: |LetRecof fundef list * t (* mutual recursion *) 29: and fundef = { name : Id.t * Type.t; args : (Id.t * Type.t) list; body : t } 再帰データ型でツリーを構成

  9. 構文木の例 letrec sum x = if x <= 0 then 0 else sum (x - 1) + x in print_int (sum 10000)

  10. K正規形(KNormal.t) Syntax.tと見た目はほとんど同じだが… kNormal.ml(i) Boolは消滅―Intに変換されている 1: type t = 2: |Unit 3: |Intof int 6: |AddofId.t * Id.t 13: |IfEqofId.t * Id.t * t * t 14: |IfLEofId.t * Id.t * t * t 15: |Letof (Id.t * Type.t) * t * t 16: |VarofId.t 17: |LetRec of fundef list * t 25: and fundef = { name : Id.t * Type.t; args : (Id.t * Type.t) list; body : t } tからId.tへ―非再帰型データ化 Ifは2種類の表現を使用 条件判断部を非再帰化 他は(おおむね)変更なし

  11. K正規形の例 letrec sum x = if x <= 0 then 0 else sum (x - 1) + x in print_int (sum 10000)

  12. 型表現(Type.t) Syntax.t, KNormal.tの中で使われる type.ml 1: type t = (* MinCamlの型を表現するデータ型 *) 2: |Unit 3: |Bool 4: |Int 5: |Float 6: |Funof t list * t (* arguments are uncurried *) 7: |Tupleof t list 8: |Arrayof t 9: |Varof t option ref 10: 11: let gentyp () = Var(ref None) (* 新しい型変数を作る *) 「引数のリスト」と 「返り値」の組 ある変数の「型」 型の確定前は“ref None” 確定後は“ref Some t”となる

  13. 重要な変数とイディオム environmentの略 “env” Id.t(変数名/関数名)をキーとするMap 値は「型」(Typing,KNormal)だったり 「変数名/関数名の別名」(Alpha, Beta,他)だったり M.emptyを初期値とし、再帰処理のときに これを引数にして引き回す

  14. 重要な変数とイディオム(2) M.add x t env xという変数/関数名に対応する型 (あるいは別名)tをenvに追加した、 新たなenvを返す 例) M.add “a” Type.Int env 追加 旧env 新env “a” -> Type.Int “f” -> Type.Float “f” -> Type.Float

  15. 重要な変数とイディオム(3) “member” M.mem x env xに対応する型/別名がenvに存在するか M.find x env xに対応する型/別名を返す 例) M.find “a” env env “a” -> Type.Int Type.Int “f” -> Type.Float

  16. 型推論 Typingモジュールで実行 Syntax.t->Syntax.tの変換 main.ml 20: (KNormal.f 21: (Typing.f 22: (Parser.exp Lexer.token l))) Syntax.t Syntax.t

  17. Typingモジュール Typingモジュールで定義される関数: val deref_typ : Type.t -> Type.t val deref_id_typ : 'a * Type.t -> 'a * Type.t val deref_term : Syntax.t -> Syntax.t val occur : Type.t option ref -> Type.t -> bool val unify : Type.t -> Type.t -> unit val g : Type.t M.t -> Syntax.t -> Type.t val f : Syntax.t -> Syntax.t (基本的に) 参照関係は下から上へ = 下の関数が上の関数を呼び出す

  18. Typing.f eはプログラム全体を指すSyntax.t型データ typing.ml プログラム全体の 型推論はここで実施 164: let f e = 165: extenv := M.empty; 171: (try unify Type.Unit (g M.empty e) 172: withUnify _ -> failwith ”..."); 173: extenv := M.map deref_typ !extenv; 174: deref_term e 型推論の結果を 整理する

  19. Typing.g typing.ml 88: let rec g env e = (* 型推論ルーチン *) 89: try 90: match e with 91: | Unit -> Type.Unit 92: | Bool(_) -> Type.Bool 101: | Add(e1, e2) | Sub(e1, e2) -> 102: unify Type.Int (g env e1); 103: unify Type.Int (g env e2); 104: Type.Int Syntax.tから Type.tへの変換 e1,e2の型推論結果が Intであることを期待 Add/Subの結果はInt

  20. Typing.g (2) “let x = e1 in e2”の型推論: typing.ml 121: | Let((x, t), e1, e2) -> (* letの型推論 *) 122: unify t (g env e1); 123: g (M.add x t env) e2 e1の型推論結果と tを一致させる 変数xを型tとして追加し、 e2の型推論に進む

  21. Typing.unify 型変数間のチェック、代入を行う関数 二つの変数に対する パターンマッチ typing.ml 66: letrec unify t1 t2 = 67: match t1, t2 with 68: |Type.Unit, Type.Unit|Type.Bool, Type.Bool |Type.Int, Type.Int|Type.Float, Type.Float -> () 86: | _, _ -> raise (Unify(t1, t2)) t1とt2の型が既に判明しており、 かつ両者が同一の場合は何もしない 他のパターンについてのチェック および(必要ならば)型の確定 match文内のパターン(68-85行)で拾えなかった場合の処理: t1とt2間に型の不整合ありとみなし、例外を投げる

  22. Typing.unify 片方が未確定の型を持つ変数であった場合 typing.ml 66: letrec unify t1 t2 = 67: match t1, t2 with 80: | Type.Var({ contents = None } as r1), _ -> 81: if occur r1 t2 then raise (Unify(t1, t2)); 82: r1 := Some(t2) t1が型未確定のケース r1と同じ型変数がt2内に 出現していないかチェック (無限長の型が発生することを防止) 例) let a = 1 左辺“a”の型は Type.Var(ref None)から Type.Var(ref Some(Type.Int))に変化 t1の型をt2に確定

  23. Typing.fの実行結果 MinCamlプログラム “let a = 1 in let b = 2 in a + b”を例に Parser.expからTyping.fに入る構文木 Syntax.Let (("a", Type.Var {contents = None}), Syntax.Int 1, Syntax.Let (("b", Type.Var {contents = None}), Syntax.Int 2, Syntax.Add (Syntax.Var "a", Syntax.Var "b"))) Typing.fの“unify”まで実行した構文木 Syntax.Let (("a", Type.Var {contents = Some Type.Int}), Syntax.Int 1, Syntax.Let (("b", Type.Var {contents = Some Type.Int}), Syntax.Int 2, Syntax.Add (Syntax.Var "a", Syntax.Var "b")))

  24. Typing.fの実行結果(2) Typing.fの“unify”まで実行した構文木 Syntax.Let (("a", Type.Var {contents = Some Type.Int}), Syntax.Int 1, Syntax.Let (("b", Type.Var {contents = Some Type.Int}), Syntax.Int 2, Syntax.Add (Syntax.Var "a", Syntax.Var "b"))) Typing.fの“deref_term”まで実行した構文木 Syntax.Let (("a", Type.Int), Syntax.Int 1, Syntax.Let (("b", Type.Int), Syntax.Int 2, Syntax.Add (Syntax.Var "a", Syntax.Var "b")))

  25. K正規化 KNormalモジュールで実行 Syntax.t->KNormal.tの変換 main.ml 19: (Alpha.f 20: (KNormal.f 21: (Typing.f KNormal.t Syntax.t

  26. Syntax.tからKNormal.tへ “a+b+c-d”を例に: 実行前(Syntax.t) 実行後(KNormal.t) KNormal.f 「演算のネスト」を「Letのネスト」へ

  27. 実はこのままでは読みにくいので… 後ほどAssocモジュールにてletの簡約を行う Assoc.f (図は説明のためのイメージ) 実際は変数名などの付け替えがあるため、少し異なる

  28. KNormal.f fstは2要素のタプルの1番目の要素を返す関数 例) fst (1,2) = 1 kNormal.ml 195: let f e = fst (g M.empty e) KNormal.gはK簡約形のツリーと型の組 すなわち(KNormal.t, Type.t)を返す関数 (型はKNormal.insert_letで使用するために必要)

  29. KNormal.g kNormal.ml 58: letrec g env = function (* K正規化ルーチン本体 *) 59: | Syntax.Unit -> Unit, Type.Unit 60: | Syntax.Bool(b) -> Int(if b then 1 else 0), Type.Int 63: | Syntax.Not(e) -> g env (Syntax.If(e, Syntax.Bool(false), Syntax.Bool(true))) BoolからInt (0 or 1) への変換 Syntax.NotからSyntax.Ifへ 変換して再びgに廻す

  30. KNormal.g(2) 67: | Syntax.Add(e1, e2) -> (* 足し算のK正規化 *) 68: insert_let (g env e1) 69: (fun x -> insert_let (g env e2) 70: (fun y -> Add(x, y), Type.Int)) 例) (a+b) + (c+d) KNormal.g

More Related