View
136
Download
5
Category
Preview:
DESCRIPTION
「美しい日本の ML コンパイラ」 を読む~ MinCaml 解説. 福盛秀雄 http://fukumori.org. Ver.2005.08.11. MinCaml とは. OCaml のサブセット. あるもの: 型推論 基本型 : int 、 float 派生型 : tuple, array 高階関数. ないもの: パターンマッチング 多相性(ポリモルフィズム) ガーベッジコレクション レコード型. コンパイラを構成する ファイルとモジュール. <ファイル名> .ml で暗黙のモジュールが構成される. - PowerPoint PPT Presentation
Citation preview
1
「美しい日本の ML コンパイラ」を読む~ MinCaml 解説福盛秀雄http://fukumori.org
Ver.2005.08.11
2
MinCaml とはOCaml のサブセット
ないもの:•パターンマッチング•多相性(ポリモルフィズム)•ガーベッジコレクション•レコード型
あるもの:•型推論•基本型 : int 、 float•派生型 : tuple, array•高階関数
3
コンパイラを構成するファイルとモジュール<ファイル名> .ml で暗黙のモジュールが構成される
<ファイル名> .mli はインタフェース宣言同名のモジュールの外部仕様を定義する
syntax.ml → Syntax モジュールtyping.ml → Typing モジュールkNormal.ml → KNormal モジュールなどなど
typing.mli → Typing モジュールの外部インタフェース
4
.mli( インタフェース ) を押さえる< モジュール名 >.f が各モジュールの主力関数
3: val f : Syntax.t -> Syntax.ttyping.mli
28: val f : Syntax.t -> tkNormal.mli
1: val f : KNormal.t -> KNormal.talpha.mli
などなど
5
モジュール間のデータの流れ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)))))))))
Syntax.t
KNormal.tKNormal.t
Closure.prog
SparcAsm.prog
SparcAsm.prog
Syntax.t
KNormal.t
.mli ファイルに記述されている内容とMain モジュールの処理をつき合わせてみると…
モジュール間でデータを変形させながら流していく姿が見える
iter の内容は次のページで
6
モジュール間のデータの流れ(2)
6: ... Elim.f (ConstFold.f (Inline.f (Assoc.f (Beta.f e))))
main.ml
KNormal.tKNormal.tKNormal.tKNormal.t
KNormal.t
関数“ iter” の内容はこんなカンジ:(Closure.f へ )
(Alpha.f から )KNormal.t
モジュール間でデータを変形させながら流していく姿が見える
流れているデータの内容はどうなっているのだろう ?
7
データ型定義を押さえるSyntax.t 構文木を表現するデータ型
Type.t 型表現を表すデータ型
KNormal.t K 正規系(中間表現)を表すデータ型
< モジュール名 >.t がデータ型序盤~中盤の処理で重要なのは以下の 3 つ:
8
構文木 (Syntax.t)
1: type t = (* MinCaml の構文を表現するデータ型 *) 2: | Unit 3: | Bool of bool 4: | Int of int 8: | Add of t * t 19: | If of t * t * t 20: | Let of (Id.t * Type.t) * t * t 21: | Var of Id.t 22: | LetRec of fundef list * t (* mutual recursion *) 29: and fundef = { name : Id.t * Type.t;
args : (Id.t * Type.t) list; body : t }
syntax.ml
再帰データ型でツリーを構成
9
構文木の例let rec sum x = if x <= 0 then 0 else sum (x - 1) + x inprint_int (sum 10000)
10
K 正規形 (KNormal.t)
1: type t = 2: | Unit 3: | Int of int 6: | Add of Id.t * Id.t 13: | IfEq of Id.t * Id.t * t * t 14: | IfLE of Id.t * Id.t * t * t 15: | Let of (Id.t * Type.t) * t * t 16: | Var of Id.t 17: | LetRec of fundef list * t 25: and fundef = { name : Id.t * Type.t;
args : (Id.t * Type.t) list; body : t }
Syntax.t と見た目はほとんど同じだが…Bool は消滅― Int に変換されている
t から Id.t へ―非再帰型データ化If は 2 種類の表現を使用条件判断部を非再帰化
他は(おおむね)変更なし
kNormal.ml(i)
11
K 正規形の例let rec sum x = if x <= 0 then 0 else sum (x - 1) + x inprint_int (sum 10000)
12
型表現 (Type.t)
1: type t = (* MinCaml の型を表現するデータ型 *) 2: | Unit 3: | Bool 4: | Int 5: | Float 6: | Fun of t list * t (* arguments are uncurried *) 7: | Tuple of t list 8: | Array of t 9: | Var of t option ref 10: 11: let gentyp () = Var(ref None) (* 新しい型変数を作る *)
「引数のリスト」と「返り値」の組
ある変数の「型」型の確定前は“ ref None”確定後は“ ref Some t” となる
Syntax.t, KNormal.t の中で使われるtype.ml
13
重要な変数とイディオム“env”
Id.t( 変数名 / 関数名 ) をキーとする Map値は「型」( Typing,KNormal) だったり「変数名 / 関数名の別名」( Alpha, Beta, 他 ) だったり
environment の略
M.empty を初期値とし、再帰処理のときにこれを引数にして引き回す
14
重要な変数とイディオム (2)M.add x t env
x という変数 / 関数名に対応する型(あるいは別名) t を env に追加した、新たな env を返す
旧 env 新 envM.add “a” Type.Int env
“a” -> Type.Int
例)
“f” -> Type.Float “f” -> Type.Float
追加
15
重要な変数とイディオム (3)
M.mem x envx に対応する型 / 別名が env に存在するか
M.find x env
“member”
x に対応する型 / 別名を返す“a” -> Type.Int“f” -> Type.Float
env M.find “a” envType.Int
例)
16
型推論Typing モジュールで実行Syntax.t->Syntax.t の変換
20: (KNormal.f 21: (Typing.f 22: (Parser.exp Lexer.token l)))
Syntax.tSyntax.t
main.ml
17
Typing モジュールval deref_typ : Type.t -> Type.tval deref_id_typ : 'a * Type.t -> 'a * Type.tval deref_term : Syntax.t -> Syntax.tval occur : Type.t option ref -> Type.t -> boolval unify : Type.t -> Type.t -> unitval g : Type.t M.t -> Syntax.t -> Type.tval f : Syntax.t -> Syntax.t
Typing モジュールで定義される関数:
( 基本的に ) 参照関係は下から上へ= 下の関数が上の関数を呼び出す
18
Typing.f
164: let f e = 165: extenv := M.empty; 171: (try unify Type.Unit (g M.empty e) 172: with Unify _ -> failwith ”..."); 173: extenv := M.map deref_typ !extenv; 174: deref_term e
typing.ml プログラム全体の型推論はここで実施
e はプログラム全体を指す Syntax.t 型データ
型推論の結果を整理する
19
Typing.g
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
typing.ml
20
Typing.g (2)
121: | Let((x, t), e1, e2) -> (* let の型推論 *) 122: unify t (g env e1); 123: g (M.add x t env) e2
変数 x を型 t として追加し、e2 の型推論に進む
“let x = e1 in e2” の型推論:
e1 の型推論結果とt を一致させる
typing.ml
21
Typing.unify
66: let rec 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 間に型の不整合ありとみなし、例外を投げる
typing.ml二つの変数に対するパターンマッチ
22
66: let rec 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)
Typing.unify
例 ) let a = 1左辺“ a” の型はType.Var(ref None) からType.Var(ref Some(Type.Int)) に変化
typing.ml片方が未確定の型を持つ変数であった場合
r1 と同じ型変数が t2 内に出現していないかチェック(無限長の型が発生することを防止)
t1 が型未確定のケース
t1 の型を t2 に確定
23
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")))
MinCaml プログラム“let a = 1 in let b = 2 in a + b” を例に
Parser.exp から Typing.f に入る構文木
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")))
Syntax.Let (("a", Type.Int), Syntax.Int 1, Syntax.Let (("b", Type.Int), Syntax.Int 2, Syntax.Add (Syntax.Var "a", Syntax.Var "b")))
Typing.f の“ deref_term”まで実行した構文木
25
K 正規化KNormal モジュールで実行Syntax.t->KNormal.t の変換19: (Alpha.f20: (KNormal.f21: (Typing.f
KNormal.t
Syntax.t
main.ml
26
Syntax.t から KNormal.tへ実行前 (Syntax.t) 実行後 (KNormal.t)
“a+b+c-d” を例に:
「演算のネスト」を「 Let のネスト」へ
KNormal.f
27
実はこのままでは読みにくいので…後ほど Assoc モジュールにて let の簡約を行う
Assoc.f
(図は説明のためのイメージ)実際は変数名などの付け替えがあるため、少し異なる
28
KNormal.f
195: let f e = fst (g M.empty e)
fst は 2 要素のタプルの 1番目の要素を返す関数例) fst (1,2) = 1
KNormal.g は K簡約形のツリーと型の組すなわち (KNormal.t, Type.t) を返す関数(型は KNormal.insert_let で使用するために必要)
kNormal.ml
29
KNormal.g
58: let rec 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)))
kNormal.ml
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
Recommended