7/11 全体ミーティング 発表 低級 言語

7/11 全体ミーティング 発表
低級言語のプログラム変換
D1 吉野 寿宏
<[email protected]>
Agenda




背景
プログラム変換器の数学的性質
テンプレートによる変換と型システム
変換の正しさの議論に向けて
Agenda




背景
プログラム変換器の数学的性質
テンプレートによる変換と型システム
変換の正しさの議論に向けて
修士論文の概要

低級言語のプログラム検証器を作るための
支援をするフレームワークを作成した

共通言語を固定し、その上で検証ロジックを構築
共通言語に対してプログラム変換してから検証

変換器と検証ロジックとは独立



新しいアーキテクチャに対応する際に検証ロジックは
弄らない
検証ロジックを取り替えるときに変換器を弄らない
研究背景
低級言語向けのプログラム検証器は…
 アーキテクチャへの依存性が強い


種々のアーキテクチャそれぞれに派生言語
検証器を作るたびにモデル化の必要


実装において、大きな負担
実装の移植性が悪いことがしばしば

検証の種類×対応アーキテクチャの種類数の
組み合わせが必要に
アイデア (図)
変換器
共通言語に
変換された
プログラム
低級言語の
プログラム
検証結果
Success
/Fail
検証
ロジック
共通言語の意味論
検証器
問題はどのように解決されるか

言語の意味論の複雑さを削減

低級言語の記述は変換器の中に収める



言語の記述は一度やってしまえば OK
検証ロジックの実装者は変換器を気にする必要
はない
システムの移植性の改善
一度検証ロジックを実装すれば、変換器を取り
替えて他のアーキテクチャにも対応可
⇒ 組み合わせの数が減らせる

共通言語 ADL

ADL: Architecture Description Language

低級言語の意味論を記述するための言語


特定のアーキテクチャに依存しない
低級言語に近いレベルの記述



λ計算のように抽象度の高い記述は変換が手間
レジスタ・メモリに対する操作を明示的に記述
なるべくシンプルな言語構造

基本となる命令数が多いと意味論が複雑化
共通言語
値の定義
Fat Pointer
3 種類の
コンストラクタが
それぞれ対応
Value
a は 1 バイト幅
のデータ
Data
共通言語
言語構文
簡単な例

整数を含む連結リストの要素の総和を求める

ハコは 4 バイト幅のデータと考えてください
start:
%r2 = 0;
goto &loop;
r2
0
6
3
1
1
loop:
r1
if %r1 = &null then
goto &end
else
nop;
%r2 = %r2 + *[4](%r1);
%r1 = *[4](%r1 + 4);
goto &loop;
end:
2
3
null
Agenda


背景
プログラム変換器の数学的性質



正しさとは何か
テンプレートによる変換と型システム
変換の正しさの議論に向けて
プログラム変換と正しさ

別の言語から共通言語に変換


検証したいプログラムは、アセンブリ言語や
機械語で書かれている
検証ロジックは ADL の意味論を用いて定義
⇒ プログラム変換が正しく行われなければ
検証内容が信用できない

変換の正しさってどういうこと?
プログラム変換の正しさ


プログラム = 機械の状態集合上の関数
変換の正しさ =
任意のプログラムに関して、2 つの言語の間での
インタプリタの状態の「対応」が整合的に構成できる
状態
State
変換元の
プログラム
変換後の
プログラム
状態’
State’
手続き型言語の抽象モデル

手続き型言語は、状態(≒変数)を変更
することで実行を進める計算モデル

状態を「インタプリタ」に持たせる


C 言語などコンパイラ型の言語でも、変数全体の
集合を考えることで同じように捉えられる
手続き型言語のプログラムとは状態集合上の
(部分)関数

しかし特に低級言語の場合は、1 命令の実行を
単位として、エラーも状態に含めて考えれば
全関数になることが多い
手続き型言語の抽象モデル

ある言語のインタプリタは
という組
: 状態集合
: 遷移関数 (



)
p はプログラム(パラメータ)であり、p が与えられる
と遷移関数が決まる
S
T
V
U
error
Conservative Simulation

インタプリタ


が
をシミュレート
下記の性質を満たすように関係
構成できること、と定義
であるときに、
が



そうでない場合
存在し、
ついて
このとき
としたとき列
とnが
かつ n 未満の整数 i に
と書く
Conservative Simulation

Conservative simulation は要するに、

A B のとき、A で実行エラーが起こる場合は B でも
必ずエラー



ただし逆は必ずしも真ならず
この意味において B のほうが「保守的である」という定義
実行エラーが起こらないときは状態の対応が保存
「変換の正しさ」の定義

正しさ =
変換が成功した場合に、変換された
プログラムが元のプログラムに対して
conservative simulation

完全に(エラーになるかどうか等含め)同じ
動作、というわけではない

ただし保守的な(変換後の)ルールでエラーに
ならなければ、元のプログラムもエラーにならない
Agenda



背景
プログラム変換器の数学的性質
テンプレートによる変換と型システム



テンプレートによる変換ルール記述
型の導入による不正テンプレートの検査
変換の正しさの議論に向けて
低級言語の変換アルゴリズム

低級言語のプログラムは基本的に命令列


算術演算・論理演算
制御構造としては分岐のみで十分


ループや関数呼び出しなどは全てこれらで書ける
命令単位で順次変換

各命令はコンテクストを持たない

持っているとすればプロセッサの状態(レジスタ・
メモリ)のはず
テンプレートによる
変換ルールの記述

アセンブリ言語の命令 = 種別+オペランド

オペランドによってパラメータ化されている
命令種別
オペランド
変換ルール!
 たとえば mov 命令(データのコピー)
mov %r1, %r2
mov
mov%r3,
D %r5
,S
mov %r2, %r4
⇔
%r1 = %r2;
⇔⇔ D%r3= =S%r5;
;
⇔
%r2 = %r4;
テンプレートによる
変換ルールの記述
mov D , S
⇔
D = S ;

すべての可能性を列挙する手間が省ける

オペランド ::= レジスタ | メモリ | 即値
ADL の構文の非終端記号で言うと、l か v
⇒ ev (式) にマップすればカバーできる

テンプレート言語

ADL の式(ev)を拡張



テンプレート引数を許容するように
sizeof 演算子(l のデータ幅を返す)の追加
引数で指定できる箇所を ev に変更

代入の左辺、左辺値のデータサイズ指定
テンプレートのインスタンス化


コンテクスト :: 変数 → ev (式)
構文木をほぼそのまま写せばよい


テンプレート変数の部分は「接木」
変数を許すために便宜的に ev を使っている箇
所でダウンキャストが必要



左辺値のデータ幅指定 → 整数へ
代入左辺 → 左辺値または一時変数へ
ダウンキャストできない場合は、変換が失敗する
(変換エラー)
アーキテクチャ記述例
(IA-32、抜粋)
#operand {
eax = %eax;
ax = %eax[0,2];
al = %eax[0,1];
ah = %eax[1,1];
add(D, S) {
// calculate result
D = D + S;
// ZF
if D : int then
if D == 0 then %_zf = 1
else %_zf = 0
else if (D - &null) : int then
if (D - &null) == 0
then %_zf = 1
else %_zf = 0
else
%_zf = junk;
...
ebx = %ebx;
...
}
mov(D, S) {
D = S;
}
...
}
テンプレート言語の問題点
しかしこの定義だと…

テンプレート変数に予期しない値が入ってくる
可能性がある
例: mov(D, S) { D = S; } において D = 1 とか

そもそも不正なテンプレートを書ける
例: 引数にない変数を中で参照する可能性
例: 1 = 2; のような式を素で書いてしまう
⇒いずれも変換エラーが発生

「変換器をコンパイルする際に」、ではない
型理論の手法による解決

型を用いることによりこれらの問題を解決

変数に予期しない値が入らないことを検査



これだけだと runtime check と殆どかわらないが
型推論をすることにより、引数の条件を自動的に
求められることには意味がある(?)
不正なテンプレートを静的にチェック

ここで言う「静的」とは、変換器のコンパイル時


プログラムを実際に与えて変換する時、ではない
何度もコンパイルしてトライ&エラーする手間が
省ける
型の構造

右図のような subtype
relation を満たす型を導入

lvalue
const
assn
ADLの構文定義の構造を反映


int
const




⇔
⇔
n (整数)
v (Value)
ただし junk を除く
lvalue
assn


int
⇔
⇔
l (左辺値)
l | x (変数)
assignable の意
* (any)
⇔
ev (式)
図では上の方が subtype
* (any)
型付け規則


型環境
式に対する型付け規則 Γ|- ev :τ
型の健全性・完全性

健全性: 型検査に通るテンプレートは(正しい
引数が与えられれば)エラーを起こさない



エラーにならない ⇔ ダウンキャストが成功
ダウンキャストが可能な条件を考えれば自明
完全性: 正しいテンプレートは型検査を通る

ダウンキャストが成功する場合は、型付け規則より
型が整合的につけられることは明らか

Case analysis をすればよい
型を推論する

型付け規則をそのまま使える

(テンプレート)変数の型を全て型変数とおいて
制約を集めて解く



関連付けられているパターンに含まれている変数のみ
注目
「(変数) (型)」という形の制約だけが出てくる
不整合な型が出てきたら推論失敗


X lvalue かつ X int、とか
reg[sizeof(X), X] のような不正な式を静的に除外
さらなる応用

型を細かく定義することにより、細かい制限を
記述することが可能


reg
lvalue などと拡張して、レジスタのみを
受け付けるテンプレートを記述することができる
型を用いた constant folding の補助

現在はデータ幅指定の部分に算術式が書けない



変換時に定数になる「式」を書けると便利?
X:int で reg[sizeof(reg) – X, X]、のような用途を想定
型付け規則を拡張してより細かい型付けを行う
Agenda




背景
プログラム変換器の数学的性質
テンプレートによる変換と型システム
変換の正しさの議論に向けて

中間言語の設計
プログラム変換器の正しさを
保証するには

Conservative Simulation を満たす状態の
対応関係を(機械的に)構築できればよい

しかしポインタと整数値の対応関係など、
考えなければならないことは多い



ADL ではメモリブロックの検証のために、ポインタ
を fat pointer 的な扱いにしている
Junk の扱い
手で全部やるのは結構大変そう

最悪実機を使って全数検査という手段はあるが…
Conservative Simulation
Relation の性質
は半順序になる(はず)



Reflexivity: ≡ = id とすれば自明
Transitivity: 直感的には下図のような感じ
S
≡
S’
~
~
S”
T
≡
T’
~
~
T”
現在考えていること

実際の低級言語と ADL の中間にあたる
言語を定義し、推移律を持ち出せる

そのためにはADL のプログラムを中間言語の
プログラムに正しく変換できる必要
⇒ それを実際に、定理証明系などを用いて
証明できないだろうか?

そのため現在勉強中です…
中間言語

ADL では値が 3 種類ある

整数, ポインタ(ラベル+オフセット), Junk



Junk は ADL の枠組みの中で値が定められない場合に登場
たとえば変なポインタ演算をやると登場
しかし実際の CPU ではポインタは単なる整数

ADL で fat pointer を使っているのは、メモリブロック同士を
切り離し、ポインタ演算を安全にするため
→ 整数だけを扱う言語を考えれば十分(?)
中間言語

値は整数だけ、とすると…


変換器の正しさを
ADL から構文を一部削除 証明するには中間
 Conditional by kind が不要になる
言語との間で議論
すれば十分
どうやったら「正しい変換」になるかはまだ
考え中
共通言語に
変換された
プログラム
低級言語の
プログラム
中間言語
プログラム
?
今日のまとめ

低級言語プログラムの意味論を記述する
ための共通言語 ADL を紹介



プログラム変換にまつわる数学的定義
テンプレートによる変換ルールの記述
現在考えていることについて説明


テンプレート言語に型を導入
中間言語の設計・定理証明系を用いた証明