Mais conteúdo relacionado Semelhante a 言語処理系入門10 (20) Mais de Kenta Hattori (20) 言語処理系入門102. コンパイラのバックエンド
ソース
プログラム 字句・構文解析 型検査 CPS 変換
Cps.convTyping.checkparse
クロージャ変換コード生成
目的
コード
実行時
ライブラリ
中間言語
抽象構文木
GC など実行時に必要な処理
実行時にロード・リンクされる
C 言語で実装されることが多い
C 言語,アセンブリコード,
VM の中間言語(バイトコード) etc 今日のトピックス
2010/1/8 2言語処理系入門 10
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. コード生成の前処理
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. コード生成の元言語
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. 末尾呼び出しの処理
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. 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. コード生成(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. コード生成(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. コード生成(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. コード生成(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. 生成コードの例
#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
14. ゴミ集め( Garbage Collection )
使われないオブジェクトを回収する
「使われない」の定義
ルート集合から到達できないオブジェクト
ルート集合={レジスタ,大域変数,スタック上の局所変数, etc }
代表的な手法
参照カウント
各オブジェクトが被参照数を表すカウンタを保持
被参照数が 0 になったら解放される
マーク&スイープ
ゴミ集め時にルート集合からオブジェクトを辿って,印をつけておく
ヒープ領域をスキャンし,印のついていないオブジェクトを回収する
コピー GC
ヒープを 2 つの領域に分割し,片方だけ使う.
一方の領域が満杯になったら,ルート集合からオブジェクトを辿りなが
ら,もう片方の領域にコピーしていく.
2010/1/8 14言語処理系入門 10
15. オブジェクト(ポインタ)の識別
方法
GC で回収されるもの
文字列バッファ,レコード
GC で回収されないもの
(ヒープに割り付けられないもの)
整数値,真偽値, Unit
文字列定数
ポインタと非ポインタをどうやって識別するか?
Exact 方式
下位 1 ビット分をタグとして使用する
Conservative GC
それっぽいものはすべてポインタとみなす
ヒープ領域x: 123
truey:
z:
“hello”
w:
5 true
レジスタ等
25 “b”
2010/1/8 15言語処理系入門 10
16. データ表現
基本型
Unit,Bool,Int,etc.
下位 1 ビット分をタグとして使用
レコード型
複数のデータを含むオブジェクト
ヒープに確保される
バッファ型
可変文字列に使用
ヒープに確保される
int
sz
sz “hello,worldn”
bool
2010/1/8 16言語処理系入門 10
17. Cheney’s Algorithm
エレガントなコピー GC のアルゴリズム
余分な記憶領域が不要
free と scan の 2 つのポインタだけで OK
処理の概要
オブジェクトを移動先の空き領域 (free ポインタの指す先 )
にコピーし, free ポインタを先に進める
コピー先のオブジェクトの中をスキャンし,ポインタが含
まていたら,その先のオブジェクトをチェックし, scan
ポインタを進める
もし,まだ移動してなかったら,そのオブジェクトを移動し
,新しいポインタをセット
すでに移動済みなら,移動先のポインタをセット
scan ポインタが free ポインタに追いついたら終了
2010/1/8 17言語処理系入門 10
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. 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