460 likes | 586 Views
POPL ミーティング 6/7 Inside ADL. 吉野 寿宏 ( 米澤研 , D1) < tossy-2@yl.is.s.u-tokyo.ac.jp >. ADL って何?. ADL: Architecture Description Language 低級言語の意味論を記述するために設計 された言語 修論研究のときに設計しました レジスタ・メモリを扱うことができる C ライクな式の構文を持つ. 修士論文の概要. 低級言語のプログラム検証器を作るための支援をするフレームワークを作成した 共通言語を固定し、その上で検証ロジックを構築
E N D
POPL ミーティング 6/7Inside ADL 吉野 寿宏 (米澤研, D1) <tossy-2@yl.is.s.u-tokyo.ac.jp>
ADLって何? • ADL: Architecture Description Language • 低級言語の意味論を記述するために設計された言語 • 修論研究のときに設計しました • レジスタ・メモリを扱うことができる • Cライクな式の構文を持つ
修士論文の概要 • 低級言語のプログラム検証器を作るための支援をするフレームワークを作成した • 共通言語を固定し、その上で検証ロジックを構築 • 共通言語に対してプログラム変換してから検証 • 変換器と検証ロジックとは独立 • 新しいアーキテクチャに対応する際に検証器は弄らない • 検証器を取り替えるときに変換器を弄らない
共通言語に変換されたプログラム 低級言語のプログラム 本研究のアイデア (図) 検証器 変換器 検証結果 Success/Fail 検証ロジック 共通言語の意味論
問題はどのように解決されるか • 言語の意味論の複雑さを削減 • 低級言語の記述は変換器の中に収める • 言語の記述は一度やってしまえば OK • 検証ロジックの実装者は変換器を気にする必要はない • システムの移植性の改善 • 一度検証ロジックを実装すれば、変換器を取り替えて他のアーキテクチャにも対応可 ⇒ 組み合わせの数が減らせる
今日のメニュー • ADLの設計について • プログラム変換器について • 型理論・プログラム解析との組み合わせ • まとめ
今日のメニュー • ADLの設計について • デザインの着想 • 構文、意味論の定義 • プログラム変換器について • 型理論・プログラム解析との組み合わせ • まとめ
Design Goals • 特定のアーキテクチャに依存しない • 種々の低級言語を記述するための言語なので • データの幅が 32bit とか 64bit であるとかいう制限はできるだけ排除 • 低級言語に近いレベルの記述 • λ計算のように抽象度の高い記述は変換が手間 • レジスタ・メモリに対する操作を明示的に記述できるとよいのでは • なるべくシンプルな言語構造 • 基本となる命令数が多いと意味論が複雑化
プログラム 抽象機械の定義 • アーキテクチャの記憶域に対応 • レジスタ、メモリ • プログラムは抽象機械上の操作を記述 ADL抽象機械
抽象機械の定義 • レジスタ・メモリ • メモリはラベルのつけられたブロックの集合 • 便宜上の目的で一時変数を用意 • 実際のアーキテクチャには存在しない部分 • 計算の途中結果を格納し、プログラムを記述しやすくする r1 l1 → <…> l2 → <…> l3 → <…> … x = 1 y = 2 z = &l1 … r2 r3 ADL抽象機械 …
言語の扱う値 • 整数とポインタがあれば十分 • 浮動小数点数などは整数にエンコードできる • 実CPU上のデータには「幅」の概念がある • 8bitの数(byte)、32bitの数(dword) など • 幅によってデータの範囲が制限される • しかし幅は計算を記述する上で障害になる 例: 32bit の値と 8bit の値を足すとどうなる? • 普通は 8bit の値を 32bit に coerce するが… • MSB を符号として解釈? それともゼロ拡張?
「Value」と「Data」、2 つの世界 • 記憶域から取り出す・保存するときに「解釈」がはさまると考える • 整数全体の集合 との対応づけ • 対応づけはアーキテクチャのパラメータとして与える • Data … 記憶領域の上での表現 • 「幅」の概念を持つ • Value … 計算に用いられる値 • Dataが「解釈」された値 • 任意の整数(とかポインタ)を表現可能
値の定義 Fat Pointer 3 種類のコンストラクタがそれぞれ対応 a は 1 バイト幅のデータ Value Data
junk ─ 不定値 • ADL の枠組みで値が定まらないことを表す • 実際の低級言語では アドレス = 整数 だが、ADLではメモリレイアウトについて仮定しない • メモリのラベルと整数のマッピングが不明なので • アセンブリ言語のプログラムでラベル名を用いることに相当 • 一部のポインタ演算等で現れる • 詳細は割愛
言語構文 (1) 左辺値 • 左辺値 … レジスタ、メモリ • [, ] にはさまれている部分はデータ幅の指定 • レジスタは一部分のデータをとってくることができる • メモリに関しては、オフセットは式の部分で計算するのでデータ幅指定のみでよい 注: 一時変数は Value を格納するのでここには含まれない
言語構文 (2) 式 • ev: Value に評価される式、eb: Bool 式 • 中置演算子、括弧などによる記述 • 四則演算、ビット演算 • C, Java などと同等の記述力 • レジスタ、メモリを参照するとデコードが発生する
言語構文 (3) 文 • 5 種類のコマンドから構成 • nop, error, 代入, goto(無条件分岐), 条件実行 • goto + 条件実行 = 条件分岐 • レジスタ、メモリへの代入時にはエンコードされる • 変数は Value を保持するのでエンコードは必要ない
ADL のプログラム • ADL では、プログラムもメモリに格納 • メモリブロック ::= コード列 | データ • メモリ :: ラベル → メモリブロック • ただしプログラムをデータとしていじることはできません • 命令ポインタ = ラベル名+命令インデクス • 「この命令は何バイト?」と考えることはナンセンス • 分岐はブロックの先頭(つまりラベルのある箇所)にのみ可能 • この制限により、メモリブロックがCFGの基本ブロックになる • …ので、その後の解析が楽になるかと考えている
1 2 3 簡単な例 • 整数を含む連結リストの要素の総和を求める • ハコは 4 バイト幅のデータと考えてください start: %r2 = 0; goto &loop;loop: if %r1 = &null then goto &end else nop; %r2 = %r2 + *[4](%r1); %r1 = *[4](%r1 + 4); goto &loop;end: r2 0 1 3 6 r1 null
今日のメニュー • ADLの設計について • プログラム変換器について • 変換アルゴリズム、テンプレート言語の設計 • 変換の正しさに関する定義 • 型理論・プログラム解析との組み合わせ • まとめ
プログラム変換器 • 実際の低級言語から ADL に変換 • 検証したいプログラムは、実際の CPU 上のアセンブリ言語や機械語で書かれている • 検証ロジックは ADL の意味論を用いて定義する • 意味論を保つ変換であることが必要 • そうでなければ検証が意味を為さない • 「意味論を保つとは何か」については後ほど定義
アセンブリ言語・機械語の変換アルゴリズム • これらの言語のプログラムは命令の列 • 算術演算・論理演算 • 制御構造としては分岐(無条件・条件)があれば十分 • ループや関数呼び出しなどは全てこれらで書ける • 基本的には命令単位で順次変換すればよい • 各命令はコンテクストを持たない • 持っているとすればプロセッサの状態(レジスタ・メモリ)のはず
mov D , S ⇔ D = S ; テンプレートによる変換ルールの記述 • アセンブリ言語の命令 = 種別 + オペランド • オペランドによってパラメータ化されている • たとえば mov 命令(データのコピー) 命令種別 オペランド 変換ルール! mov %r1, %r2 ⇔%r1 = %r2; mov %r3, %r5 ⇔%r3 = %r5; mov %r2, %r4 ⇔%r2 = %r4;
mov D , S ⇔ D = S ; テンプレートによる変換ルールの記述 • すべての可能性を列挙する手間が省ける • オペランド ::= レジスタ | メモリ | 即値 • ADL の構文の非終端記号で言うと、l か v ⇒ ev(式) にマップすればカバーできる
テンプレート言語 • ADL の式(ev)を拡張 • テンプレート引数を許容するように • sizeof 演算子(lvalue のデータ幅を返す)の追加 • 引数で指定できる箇所を evに変更 • 代入の左辺、左辺値のデータサイズ指定
テンプレートのインスタンス化 • コンテクスト :: 変数 → ev(式) • 構文木をほぼそのまま写せばよい • テンプレート変数の部分は「接木」 • 変数を許すために便宜的に evを使っている箇所でダウンキャストが必要 • 左辺値のデータ幅指定 → 整数へ • 代入左辺 → 左辺値または一時変数へ • ダウンキャストできない場合は、変換が失敗する(エラー)
パターンマッチング • 使用するテンプレートをどうやって指定する? ⇒ パターンマッチング • Prolog (や O’Caml)にヒント • 小さな木のマッチング • パターン言語の文法を右に示した
アーキテクチャ記述と変換アルゴリズム • パターン言語と ADL プログラムを関連付け • 命令マッピング パターン { 命令列 } • オペランドマッピング パターン = 式 • 変換アルゴリズムの概略 • 与えられた項 t をパターンと順にマッチング • マッチした場合、変数から項 t への束縛が返るので、オペランドマッピングを用いて式に変換 • 変換されたコンテクストを用いてテンプレートコードをインスタンス化
#operand { eax = %eax; ax = %eax[0,2]; al = %eax[0,1]; ah = %eax[1,1]; ebx = %ebx; ... } mov(D, S) { D = S; } ... 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; ... } アーキテクチャ記述例 (IA-32、抜粋)
プログラム変換の正しさ • プログラム = 機械の状態集合上の関数 • 変換の正しさ =実機械状態と ADL 抽象機械の状態の「対応」 が任意のプログラムに関して保存 状態 State 変換元の プログラム 変換後の プログラム 状態’ State’
状態の対応関係 レジスタ、メモリ、一時変数 レジスタの状態,メモリの中身 • 変換器(ルール)の実装者が定義 • 実機械の状態集合が、抽象機械の状態集合に埋め込まれるのがポイント • 抽象機械の状態の集合から実機械の状態の集合へ total な surjection が定義できればよい • 実機械では値として整数値しか用いないことを考えれば直観的に明らか • 変数の生存期間が局所的(ある一命令に対応するコード内で完結している)ならば変数については忘れてよい
変換の正しさを検査する • 今のところ、変換器の実装者に委ねている • いくつかの入力例について出力が等しくなることを確かめればある程度の確証を得るのでは? • (半)自動的に保証することは future work • パターンが決まっているし、プリミティブなループを含まないので、なんとかならないか? • その前段として、ADL の意味論を定理証明器に載せるべく現在勉強中
今日のメニュー • ADLの設計について • プログラム変換器について • 型理論・プログラム解析との組み合わせ • テンプレート言語の問題点 • テンプレート言語に対する型判定、型推論 • プログラム解析の手法の適用 • さらなる応用 • まとめ
テンプレート言語の問題点 これまでの定義では… • テンプレート変数に予期しない値が入ってくる 例: mov(D, S) { D = S; } に対して D = 1 などを与えると… • 不正なテンプレートを書ける 例: 引数にない変数を中で参照する可能性 例: 1 = 2; のような式を許容してしまう ⇒いずれも「変換時に」エラーが発生 • 「変換器をコンパイルする際に」、ではない
型理論の手法による解決 • 型を用いることにより、これらの問題を解決 • 変数に予期しない値が入ってこないように検査 • これは runtime check とほぼ変わらないが • 逆に、型推論を行うことで引数にどういった条件が必要かがわかる • 不正なテンプレートを静的にチェックできる • ここで言う「静的」とは、変換器のコンパイル時のこと • プログラムを実際に与えて変換する時、ではない • 何度もコンパイルしてトライ&エラーする手間が省ける
int lvalue const assn * (any) 型の構造 • 右図のような subtype relation を満たす型を導入 • ADLの構文定義の構造を反映 • int ⇔ n (整数) • const ⇔ v (Value) • ただし junk を除く • lvalue ⇔ l (左辺値) • assn ⇔ l | x (変数) • assignable の意 • * (any) ⇔ ev (式) • 図では上の方が subtype
型付け規則 • 型環境 • 式に対する型付け規則 Γ|- ev :τ
型付け規則 • Bool 式に対する型付け規則 Γ|-eb : bool • ほぼ自明なので割愛します • 文に対する型付け規則 Γ|- c • コード列に対する型付け規則 Γ|- <c;…> • これもほぼ自明なので割愛します
型の健全性・完全性 • 健全性: 型検査に通るテンプレートは(正しい引数が与えられれば)エラーを起こさない • エラーにならない ⇔ ダウンキャストが成功 • ダウンキャストが可能な条件を考えれば自明 • 完全性: 正しいテンプレートは型検査を通る • ダウンキャストが成功する場合は、型付け規則より型が整合的につけられることは明らか • case analysis をすればよい
int lvalue const assn * 型検査の一例 • 型環境 Γ= D : lvalue, S : * のもとで、文 D = S + 1を型付け
型を推論する • 型付け規則をそのまま使える • (テンプレート)変数の型を全て型変数とおいて制約を集めて解く • 関連付けられているパターンに含まれている変数のみ注目 • 「(変数) (型)」という形の制約だけが出てくる • 不整合な型が出てきたら推論失敗 • Xlvalue かつ Xint、とか • reg[sizeof(X), X] のような不正な式を静的に除外
さらなる応用 • 型を細かく定義することにより、細かい言語の制限を記述することが可能 • reg lvalue などとして、レジスタのみを受け付けるテンプレートを記述することができる • 型を用いた constant folding の補助 • 現在はデータ幅指定の部分に算術式が書けない • 変換時に定数になる「式」を書けると便利? • X:int で reg[sizeof(reg) – X, X]、のような用途を想定 • 型付け規則を拡張してより細かい型付けを行う
ほかの問題点 • ありえない instantiation がなされる可能性 例: 「レジスタのみ受け付ける」などが表現できない ⇒ あり得る組み合わせが全て正しく変換されていれば問題はないはず • 変数使用の局所性を保証できない • 同一テンプレートの外で定義されている(一時)変数を参照していないか • 手で確かめるのは容易だが、変数の生存解析により自動で検査できる
しかし大きな障害が… • 実際のところ、純粋にテンプレート言語だけで書けるアーキテクチャは少ない(or, ない) • call は次命令のアドレスを知らなければできない • Delayed branch をする場合、変換途中で後続命令を変換することが必要 • 対応するために、式の部分を拡張して任意のコードを書けるようにする必要 ⇒ たとえば Java の意味論を知らないと型が求められない?
今日のメニュー • ADLの設計について • プログラム変換器について • 型理論・プログラム解析との組み合わせ • まとめ
まとめ • 低級言語の意味論を記述するための言語 ADL の設計について述べた • プログラム変換器の実装について述べた • ルールベースの記述 • 変換ルールに対して型理論やプログラム解析の手法を応用できることを説明した • おかしなテンプレート、instantiation をはじく • 細かく型をつけることでいろいろな応用ができそう