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

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

Transcript of 「美しい日本のMLコンパイラ」 を読む~MinCaml -...

Page 1: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

1

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

福盛秀雄

http://fukumori.org

Ver.2005.08.11

Page 2: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

2

MinCamlとは

OCamlのサブセット

ないもの:•パターンマッチング•多相性(ポリモルフィズム)•ガーベッジコレクション•レコード型

あるもの:•型推論•基本型: int、float•派生型: tuple, array•高階関数

Page 3: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

3

コンパイラを構成するファイルとモジュール

<ファイル名>.mlで暗黙のモジュールが構成される

<ファイル名>.mliはインタフェース宣言同名のモジュールの外部仕様を定義する

syntax.ml → Syntaxモジュールtyping.ml → TypingモジュールkNormal.ml → KNormalモジュール

などなど

typing.mli → Typingモジュールの外部インタフェース

Page 4: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

4

.mli(インタフェース)を押さえる

<モジュール名>.fが各モジュールの主力関数

3: val f : Syntax.t -> Syntax.t

typing.mli

28: val f : Syntax.t -> t

kNormal.mli

1: val f : KNormal.t -> KNormal.t

alpha.mli

などなど

Page 5: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

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の内容は次のページで

Page 6: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

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

モジュール間でデータを変形させながら流していく姿が見える

流れているデータの内容はどうなっているのだろう?

Page 7: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

7

データ型定義を押さえる

Syntax.t 構文木を表現するデータ型

Type.t 型表現を表すデータ型

KNormal.t K正規系(中間表現)を表すデータ型

<モジュール名>.tがデータ型序盤~中盤の処理で重要なのは以下の3つ:

Page 8: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

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

再帰データ型でツリーを構成

Page 9: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

9

構文木の例

let rec sum x = if x <= 0 then 0 else sum (x - 1) + x inprint_int (sum 10000)

Page 10: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

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)

Page 11: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

11

K正規形の例

let rec sum x = if x <= 0 then 0 else sum (x - 1) + x inprint_int (sum 10000)

Page 12: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

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

Page 13: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

13

重要な変数とイディオム

“env”Id.t(変数名/関数名)をキーとするMap

値は「型」(Typing,KNormal)だったり「変数名/関数名の別名」(Alpha, Beta,他)だったり

environmentの略

M.emptyを初期値とし、再帰処理のときにこれを引数にして引き回す

Page 14: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

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

追加

Page 15: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

15

重要な変数とイディオム(3)

M.mem x envxに対応する型/別名がenvに存在するか

M.find x env

“member”

xに対応する型/別名を返す

“a” -> Type.Int“f” -> Type.Float

env M.find “a” env

Type.Int

例)

Page 16: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

16

型推論

Typingモジュールで実行Syntax.t->Syntax.tの変換

20: (KNormal.f 21: (Typing.f 22: (Parser.exp Lexer.token l)))

Syntax.tSyntax.t

main.ml

Page 17: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.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モジュールで定義される関数:

(基本的に) 参照関係は下から上へ= 下の関数が上の関数を呼び出す

Page 18: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

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型データ

型推論の結果を整理する

Page 19: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

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

Page 20: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.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

Page 21: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.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二つの変数に対する

パターンマッチ

Page 22: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.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に確定

Page 23: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

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")))

Page 24: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

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”まで実行した構文木

Page 25: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

25

K正規化KNormalモジュールで実行Syntax.t->KNormal.tの変換

19: (Alpha.f20: (KNormal.f21: (Typing.f

KNormal.t

Syntax.t

main.ml

Page 26: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

26

Syntax.tからKNormal.tへ実行前(Syntax.t) 実行後(KNormal.t)

“a+b+c-d”を例に:

「演算のネスト」を「Letのネスト」へ

KNormal.f

Page 27: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

27

実はこのままでは読みにくいので…後ほどAssocモジュールにてletの簡約を行う

Assoc.f

(図は説明のためのイメージ)実際は変数名などの付け替えがあるため、少し異なる

Page 28: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

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

Page 29: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.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に廻す

Page 30: 「美しい日本のMLコンパイラ」 を読む~MinCaml - Fukumorifukumori.org/MinCaml/MinCaml.pdf3 コンパイラを構成する ファイルとモジュール <ファイル名>.mlで暗黙のモジュールが構成される

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