region-based memory management in cyclone について
DESCRIPTION
Region-based Memory Management in Cyclone について. 発表者 : 前田俊行. Cyclone とは ?. 安全な C の方言 型による静的安全性チェック 配列境界検査などの動的安全性チェック それでいてプログラマに低レベルなメモリ制御をさせたい. Cyclone プログラムの例. ほとんど C と変わらない 書換率 = 10% 以下 ( 著者らの経験による ). void factorial(int* result, int n) { int x = 1; if (n > 1) { - PowerPoint PPT PresentationTRANSCRIPT
Region-based Memory Managementin Cycloneについて
発表者 : 前田俊行
Cyclone とは ?
• 安全な C の方言– 型による静的安全性チェック– 配列境界検査などの動的安全性チェック
• それでいてプログラマに低レベルなメモリ制御をさせたい
Cyclone プログラムの例
• ほとんど C と変わらない– 書換率 = 10% 以下 ( 著者らの経験による )
void factorial(int* result, int n){
int x = 1;
if (n > 1) {factorial(&x, n – 1);
}*result = x * n;
}
Cyclone で region ベースメモリ管理が必要なわけ
• スタックにデータを置けるようにしたい– そしてそのデータへのポインタを使いた
いvoid factorial(int* result, int n){
int x = 1;
if (n > 1) {factorial(&x, n – 1);
}*result = x * n;
}
ポインタ操作
ポインタ操作
スタック上にデータを置く
Region ベースメモリ管理とは ?
• メモリの確保と解放を Region という単位で行う手法
• Region は LIFO に確保、解放される
LIFO な region の確保と解放
Region の確保 =スタックへ push
Region の解放 =スタックから pop
Region の種類 : 2 つ
• 静的 region– 実行時にサイズが変わらない region– 本当のメモリスタック上に確保できる
• 動的 region– 実行時にサイズが変わるかもしれない regi
on– メモリスタック上には確保できない
Cyclone における Region の種類
• 関数 region ( 静的 region)
• ローカルブロック region ( 静的 region)
• 動的 region
• ヒープ region ( 動的 region)
関数 Region ( 静的 region)
• 関数の引数を保存する region
int sys_mlock(int start, int length){
…}
関数 region
int start
int length
Region スタック
関数呼び出し
新たに region を生成
関数 Region ( 静的 region)
• 関数の引数を保存する region
int sys_mlock(int start, int length){
…}
Region スタック関数からのリターン
生成した region を解放
ローカルブロック Region( 静的 region)
• ローカル変数を保存する region
{int i;struct stat s;
…}
Region スタック
ローカルブロックregion
int i
struct stat s
動的 Region
• region プリミティブによって確保される region
Region スタック
動的 region
int i
struct stat s
region x {int i;struct stat s;
…}
動的 Region の特徴
• rnew プリミティブで動的にメモリを確保できる
region x {int? array =
rnew(x) { for i < n : i };…
}
Region スタック
動的 region
Int? array
Region ハンドル
Region ハンドルを指定して長さ n の配列をメモリに
確保
長さ n の配列
ヒープ region ( 動的 region)
• プログラム実行中ずっと存在する region• グローバル変数を保存する region• malloc などがメモリを確保する region
int global_counter;…void func(){
…malloc();…
}
int global_counter
ヒープ region
新たに確保
Cyclone におけるメモリ安全性の保証
• メモリ安全性を破るプログラムはコンパイル時の型チェックではじく
int* function(){
int x;
return &x;}
メモリ安全でないプログラムの例
型チェックではじく!
Cyclone におけるポインタ型
• 全てのポインタ型は、そのポインタが指している region の情報を保持する
• ( 例 ) int*ρ– region ρ 中の整数値を指す
• ρ は region を識別する名前
シンプルな型チェックの例その 1
int*ρ p;ρ {
int x = 0;
p = &x;}*p = 123;
型エラーp の宣言の時点では
まだ ρ はスコープにない
シンプルな型チェックの例その 2
int*σ p;ρ {
int x = 0;
p = &x;}*p = 123;
型エラーp と &x の型が違
うp : int*σ
&x : int*ρ
関数の Region 多相性
• 関数はアクセスする region が具体的に分からなくてもよい
char?ρ1 strcpy <ρ1, ρ2> (char?ρ1, const char?ρ2);char?ρH strdup <ρ> (const char?ρ);char?ρ1 rstrdup <ρ1, ρ2> (region_t<ρ1>, const char?ρ2);
例 : Cyclone の文字列ライブラリのプロトタイプ宣言
( 注 ) ρH はヒープ region を表す
関数の Region 多相性の例
• 文字列複製関数 strdup の定義
char?ρ1 rstrdup <ρ1, ρ2> (region_t<ρ1>, const char?ρ2);
char?ρH strdup <ρ> (const char?ρ str){
return rstrdup <ρH, ρ> (heap_regions, str);}
( 注 )ρH, heap_regions は ヒープ region を表す
構造体の Region 多相性
• 構造体はアクセスする region が具体的に分からなくてもよい
struct List <ρ1, ρ2> {int*ρ1 head;struct List <ρ1, ρ2>*ρ2 tail;
};
例 : リスト構造体の定義
Subtyping
• 型は、より短命な region を持つ型にキャストできる
void f<ρ1, ρ2> (int b, int*ρ1 p1, int*ρ2 p2)ρ3 {
int*ρ3 p;if (b)
p = p1;else
p = p2;…
}
ρ1 と ρ2 は ρ3 より長生きなのでキャストできる
うっとおしい region 注釈を無くす
• 巧みな“デフォルトルール”によってプログラマが明示しなければならない注釈の量を減らす– ルール 1: ローカル変数は、推論する– ルール 2: 関数の引数や返り値の region は
全て新しい region を割り当てる– ルール 3: それ以外は全部ヒープ region に
してしまう
list_t list_copy(list_t lst){
list_t res = NULL;
for (list_t t = lst; t != NULL; t = t->tail)res = new List(new *t->hd, res);
return res;}
list_t list_copy <ρ1, ρ2> (list_t <ρ1, ρ2> lst){
list_t res = NULL;
for (list_t t = lst; t != NULL; t = t->tail)res = new List(new *t->hd, res);
return res;}
ルール 2 より引数には新しい region 名を与え
る
list_t <ρH, ρH> list_copy <ρ1, ρ2> (list_t <ρ1, ρ2> lst){
list_t res = NULL;
for (list_t t = lst; t != NULL; t = t->tail)res = new List(new *t->hd, res);
return res;}
ルール 3 によりヒープ region となる
list_t <ρH, ρH> list_copy <ρ1, ρ2> (list_t <ρ1, ρ2> lst){
list_t <ρH, ρH> res = NULL;
for (list_t t = lst; t != NULL; t = t->tail)res = new List(new *t->hd, res);
return res;}
ルール 1 により推論
list_t <ρH, ρH> list_copy <ρ1, ρ2> (list_t <ρ1, ρ2> lst){
list_t <ρH, ρH> res = NULL;
for (list_t <ρ1, ρ2> t = lst; t != NULL; t = t->tail)res = new List(new *t->hd, res);
return res;}
ルール 1 により推論
list_t <ρH, ρH> list_copy <ρ1, ρ2> (list_t <ρ1, ρ2> lst){
list_t <ρH, ρH> res = NULL;
for (list_t <ρ1, ρ2> t = lst; t != NULL; t = t->tail)res = new List(new *t->hd, res);
return res;}
デフォルトルール適用の例struct List <ρ1, ρ2> {
int*ρ1 head;struct List <ρ1, ρ2>*ρ2 tail;
};typedef struct List <ρ1, ρ2>*ρ2 list_t <ρ1, ρ2>;
Existential
• Existential 型とは、型のある部分を抽象化することによって複数の型を統一的に扱うための型である
• Cyclone はこの existential 型をサポートしている
Existential 型の例
struct IntFn ∃α {int (*func)(α);α env;
}
Existential 型
struct IntFn_int {int (*func)(int*ρ);int*ρ env;
}
struct IntFn_List {int (*func)(struct List*ρ);struct List* env;
}
型変数 α を実際の型で置き換えた型の値
は全てこの existential型に変換 (pack とい
う ) できるPack 可能 Pack 可能
Existential の pack の例struct IntFn α {∃
int (*func)(α);α env;
}int read <ρ> (int*ρ x) { return *x; }
L {int x = 0;struct IntFn pkg =
<int*L>{ .func = read <L>, .env = &x };}
Existential の pack型 int*L を抽象化
Existential の unpack の例struct IntFn α {∃
int (*func)(α);α env;
}int read <ρ> (int*ρ x) { return *x; }
int apply_IntFn(struct IntFn<L1> pkg){
let IntFn<α> { .func = f, .env = x } = pkg;return f ( x );
}Existential の unpack関数 f の型 : int (*)
(α)引数 x の型 : α
Existential とメモリ安全性上の問題
• Existential は型を隠蔽してしまうので dangling ポインタが発生する可能性がある
Existential によるdangling ポインタの発生の例struct IntFn α {∃
int (*func)(α);α env;
}int read <ρ> (int*ρ x) { return *x; }
L1 {struct IntFn pkg;L2 {
int x = 0;pkg =<int*L2>{ .func = read <L2>, .env = &x };
}…
}
Dangling ポインタ発生!だが型エラーにはならな
い…
解決策第 1 段階
• 関数の effect を考慮する– Effect =
関数がアクセスする ( かもしれない )region の集合
• 生きている region の集合を追跡し、呼び出そうとしている関数の effect と比較する– Effect 中の region は全て生きていなければならな
い
関数の effect はどう求めるか ?
• 引数と返り値の型にあらわれる regionを全て effect に含める– 最も conservative な推論– 利点 : プロトタイプ宣言だけで effect を
求められる
関数の effect の例
普通の関数int*ρ1 f ( int*ρ2, int*ρ1*ρ3); Effect : {ρ1, ρ2, ρ3}
多相型関数int compare(α, α); Effect : { regions_of (α) }
ただし regions_of は以下のとおり : regions_of (int) = 0 ( 空集合 ) regions_of (τ*ρ) = { ρ } regions_of (τ)∪ regions_of ((τ1, … , τn) → τ) = regions_of (τ1) regions_of (τ2) regions_of (τ)∪ ∪
解決策第 2 段階
• Region 制約を考慮する– Region 制約 =
どの region がどの region より長生きしなければならないか
• Existential の定義に region 制約を指定する– Existential を pack するには指定された region 制約を
満たしていなければならない
• Existential の unpack 後は指定した region 制約が満たされているとみなして型チェックを行う
Region 制約を指定した existential の例
struct IntFn<ρ> α: regions_of (α) <: ρ {∃int (*func)(α);α env;
}
regions_of (α) の全ての region がρ より長生きであるという制約を表す
Region 制約を満たした existential pack の例
struct IntFn<ρ> α : regions_of (α) <: ρ {∃int (*func)(α);α env;
}int read <ρ> (int*ρ x) { return *x; }
L {int x = 0;struct IntFn<L> pkg =
<int*L>{ .func = read <L>, .env = &x };}
region 制約 L <: Lを満たす
Region 制約を満たさない existential pack の例
struct IntFn<ρ> α : regions_of (α) <: ρ {∃int (*func)(α);α env;
}int read <ρ> (int*ρ x) { return *x; }
L1 {struct IntFn<L1> pkg;L2 {
int x = 0;pkg =<int*L2>{ .func = read <L2>, .env = &x };
}}
型エラー region 制約 L1 <: L2 を満たさない
Existential の unpack の例
L {struct IntFn<L> pkg;…let IntFn<α> { .func = f, .env = x } = pkg;
f ( x );}
struct IntFn<ρ> α : regions_of (α) <: ρ {∃int (*func)(α);α env;
} 関数 f の effect = { regions_of (α) }生きている region = { L }Region 制約 = { regions_of (α) <: L }つまり、関数 f は呼んでも安全!
まとめ
• Cyclone は region-based メモリ管理を用いた安全な C の方言である– 安全性は型チェックにより保証される– スタックにデータを置くことが可能で、さ
らにポインタで指すことも可能• Existential によって生じるメモリ安全性
の問題は関数の effect と region 制約によって解決されている
References
• Cyclone homepage http://www.cs.cornell.edu/projects/cyclone
• Region-based Memory Management in Cyclone, Dan Grossman, Greg Morrisett, Trevor Jim, Michael Hicks, Yanling Wang, and James Cheney. PLDI2002. To appear.