Slide 1

Slide 1 text

形式手法特論 コンパイラの「正しさ」は 証明できるか? #burikaigi チェシャ猫 (@y_taka_23) BuriKaigi 2026 (10th Jan. 2026)

Slide 2

Slide 2 text

#burikaigi #burikaigi_b で X(旧 Twitter)よろしくね!

Slide 3

Slide 3 text

本日のアジェンダ ● 定理証明はどのような点が嬉しいのか? ○ 単体テストとの考え方の違いを具体例で見る ● コンパイラはどのように実装されるのか? ○ 単純な言語でコンパイルの仕組みを学ぶ ● コンパイラの正当性はどう定式化されるのか? ○ Lean を用いて検証可能な仕様と証明を与える

Slide 4

Slide 4 text

仕様記述と定理証明 1. How to Define and Ensure the Specification?

Slide 5

Slide 5 text

定理証明を触ったことある人?

Slide 6

Slide 6 text

コンパイラ実装したことある人?

Slide 7

Slide 7 text

生成 AI 使ってる人?

Slide 8

Slide 8 text

生成 AI 時代の仕様記述 ● 仕様を「記述すること」の重要性 ○ AI はそれっぽい実装を高速・大量に生成できる ○ 明示したコンテキスト外の事情は汲んでくれない ● 今後は仕様の「検証可能性」がより問われる時代に ○ まずい実装を機械的に弾ける、白黒が決まる仕様 ○ リッチな型システムや静的解析の利用

Slide 9

Slide 9 text

理解は全てに先立つ そして、理解とは記述である

Slide 10

Slide 10 text

例:リストの反転 ● リストを反転させる関数を実装したとする ● 仕様「2 回反転させたら元に戻る」が成り立ちそう def rev {A : Type} (l : List A) : List A := match l with | [] => [] | x :: xs => rev xs ++ [x]

Slide 11

Slide 11 text

● 単体テスト ● プロパティベーステスト ● 定理証明

Slide 12

Slide 12 text

● 単体テスト ● プロパティベーステスト ● 定理証明

Slide 13

Slide 13 text

単体テスト ● 各テスト観点に対し、欲しい仕様を個別に記述 ○ 空:rev (rev []) == [] ○ 1 個:rev (rev [42]) == [42] ○ 文字列:rev (rev ["foo", "bar"]) == ["foo", "bar"] ● 結果が予測可能な処理に対しては最もコスパが良い ○ その代わり、不具合の検出はテスト設計に強く依存

Slide 14

Slide 14 text

● 単体テスト ● プロパティベーステスト ● 定理証明

Slide 15

Slide 15 text

プロパティベーステスト(PBT) ● 個別のテストケースではなく性質(プロパティ)を記述 ○ 2 回反転で元に戻る:rev (rev xs) == xs ○ この性質は「リスト xs が何であろうと」成り立つ ● この xs を多数生成し、反例がないか確認 ○ 単なるランダム以上に、コーナーケースは狙って生成 ○ 反例が見つかった場合、最小の反例も探索

Slide 16

Slide 16 text

● 単体テスト ● プロパティベーステスト ● 定理証明

Slide 17

Slide 17 text

Lean Theorem Prover https://github.com/leanprover

Slide 18

Slide 18 text

Lean による証明 ● 真に抽象的な「任意の入力」に対して仕様を保証 ○ その代わり、証明を書く労力は正直かなり重い ● ユーザは証明項を構築するのが目標 ○ 手持ちの仮定を使って欲しい結論を組み立てる ○ 対話的コマンド(タクティク)により項を構築 ○ VSCode プラグインを使う方法が一般的

Slide 19

Slide 19 text

VSCode での Lean 記述

Slide 20

Slide 20 text

システム開発への利用 ● Lean は「数学の形式化」の文脈で言及されがち ● しかし、あくまでもプログラミング言語の一種 ○ 実際に動く(main を持つ)アプリが書ける ● AWS の認可エンジン Cedar の事例 ○ Differential Random Testing (DRT) ○ Rust 実装と Lean 実装で出力の一致を確認

Slide 21

Slide 21 text

参考:AWS Cedar での Lean 利用 https://speakerdeck.com/ytaka23/fp-matsuri-2025

Slide 22

Slide 22 text

セクション 1 のまとめ ● AI 時代における「検証可能な仕様記述」の重要性 ● プロパティベーステスト ○ 個別ケースではなく、包括的な「性質」を記述する ● Lean による証明 ○ 真に「任意の入力」に対する動作を保証 ○ 数学専用ではなく、現に動くプログラムが書ける

Slide 23

Slide 23 text

プログラム実行の意味論 2. Operational Semantics of the Interpreter and the VM

Slide 24

Slide 24 text

今回 Lean で実装する 証明付きコンパイラ

Slide 25

Slide 25 text

そもそもコンパイルとは何か?

Slide 26

Slide 26 text

プログラムの意味論とコンパイラ ● 評価器は言語の操作的意味論(実行方法)を定義する ○ ソース言語の評価器:インタプリタ ○ ターゲット言語の評価器:VM や実際の CPU ● コンパイラはソース言語をターゲット言語に写す ○ この「写し方」が意味論と整合する必要がある ○ 「写す前の実行」と「写した後での実行」の一致

Slide 27

Slide 27 text

x = 1071; y = 462; while (!(x == y)) { if (y <= x) { print x; x = x - y; } else { print y; y = y - x; }; }; print x; ソースコード アセンブリ CONST 1071 SETVAR x CONST 462 SETVAR y VAR x VAR y BEQ 19 0 VAR y VAR x BLE 0 8 VAR x OUT ... コンパイル

Slide 28

Slide 28 text

x = 1071; y = 462; while (!(x == y)) { if (y <= x) { print x; x = x - y; } else { print y; y = y - x; }; }; print x; ソースコード アセンブリ CONST 1071 SETVAR x CONST 462 SETVAR y VAR x VAR y BEQ 19 0 VAR y VAR x BLE 0 8 VAR x OUT ... inductive Com where | skip | assign (x : Ident) (a : AExp) | seq (c1 : Com) (c2 : Com) | ifThenElse (b : BExp) (c1 : Com) (c2 : Com) | while_ (b : BExp) (c1 : Com) | print (a : AExp) ソース言語の構文木 inductive Instr where | const (n : Int) | var (x : Ident) | setVar (x : Ident) | add | opp | branch (d : Int) | beq (d1 : Int) (d0 : Int) | ble (d1 : Int) (d0 : Int) | halt | out ターゲット言語の構文木 コンパイル 書き出し パース

Slide 29

Slide 29 text

x = 1071; y = 462; while (!(x == y)) { if (y <= x) { print x; x = x - y; } else { print y; y = y - x; }; }; print x; ソースコード アセンブリ CONST 1071 SETVAR x CONST 462 SETVAR y VAR x VAR y BEQ 19 0 VAR y VAR x BLE 0 8 VAR x OUT ... inductive Com where | skip | assign (x : Ident) (a : AExp) | seq (c1 : Com) (c2 : Com) | ifThenElse (b : BExp) (c1 : Com) (c2 : Com) | while_ (b : BExp) (c1 : Com) | print (a : AExp) ソース言語の構文木 inductive Instr where | const (n : Int) | var (x : Ident) | setVar (x : Ident) | add | opp | branch (d : Int) | beq (d1 : Int) (d0 : Int) | ble (d1 : Int) (d0 : Int) | halt | out ターゲット言語の構文木 1071 609 462 315 168 147 126 105 84 63 42 21 インタプリタ実行結果 VM 実行結果 1071 609 462 315 168 147 126 105 84 63 42 21 コンパイル インタプリタの 意味論 VM の意味論 書き出し パース

Slide 30

Slide 30 text

x = 1071; y = 462; while (!(x == y)) { if (y <= x) { print x; x = x - y; } else { print y; y = y - x; }; }; print x; ソースコード アセンブリ CONST 1071 SETVAR x CONST 462 SETVAR y VAR x VAR y BEQ 19 0 VAR y VAR x BLE 0 8 VAR x OUT ... inductive Com where | skip | assign (x : Ident) (a : AExp) | seq (c1 : Com) (c2 : Com) | ifThenElse (b : BExp) (c1 : Com) (c2 : Com) | while_ (b : BExp) (c1 : Com) | print (a : AExp) ソース言語の構文木 inductive Instr where | const (n : Int) | var (x : Ident) | setVar (x : Ident) | add | opp | branch (d : Int) | beq (d1 : Int) (d0 : Int) | ble (d1 : Int) (d0 : Int) | halt | out ターゲット言語の構文木 1071 609 462 315 168 147 126 105 84 63 42 21 インタプリタ実行結果 VM 実行結果 1071 609 462 315 168 147 126 105 84 63 42 21 コンパイル 一致 インタプリタの 意味論 VM の意味論 書き出し パース

Slide 31

Slide 31 text

コンパイラと定理証明 ● CompCert(POPL 2006)[1] ○ Coq で証明された C 言語 - アセンブリコンパイラ ● EUTypes 2019 サマースクールの講義資料 [2] ○ CompCert の開発者自身が証明の骨子を解説 ○ ソース・ターゲットとも単純化された言語を利用 ● 講義資料も Coq なので、今回は Lean で再構成 [1] https://doi.org/10.1145/1111037.1111042 [2] https://xavierleroy.org/courses/EUTypes-2019/

Slide 32

Slide 32 text

inductive Com where | skip | assign (x : Ident) (a : AExp) | seq (c1 : Com) (c2 : Com) | ifThenElse (b : BExp) (c1 : Com) (c2 : Com) | while_ (b : BExp) (c1 : Com) | print (a : AExp) ソース言語の構文木 1071 609 462 315 168 147 126 105 84 63 42 21 インタプリタ実行結果 インタプリタの 意味論

Slide 33

Slide 33 text

今回考えるソース言語 ● とてもシンプルな命令型言語 ○ int 変数、+、-、==、<=、!、&&、if、while、print ● インタプリタは以下の内部状態を持つ ○ 処理中のコード ○ 処理中のコードが終わった後の継続 ○ 変数ストア(変数名から int 値へのマップ)

Slide 34

Slide 34 text

x = 1071; y = 462; while (!(x == y)) { if (y <= x) { print x; x = x - y; } else { print y; y = y - x; }; }; print x; 変数ストア 処理中コード x : 0 y : 0 継続

Slide 35

Slide 35 text

y = 462; while (!(x == y)) { if (y <= x) { print x; x = x - y; } else { print y; y = y - x; }; }; print x; 変数ストア 処理中コード x : 0 y : 0 継続 x = 1071; 先頭の文の取り出し

Slide 36

Slide 36 text

y = 462; while (!(x == y)) { if (y <= x) { print x; x = x - y; } else { print y; y = y - x; }; }; print x; 変数ストア 処理中コード x : 1071 y : 0 継続 変数 x の更新

Slide 37

Slide 37 text

while (!(x == y)) { if (y <= x) { print x; x = x - y; } else { print y; y = y - x; }; }; print x; 変数ストア 処理中コード x : 1071 y : 462 継続 変数 y の更新 先頭の文の取り出し

Slide 38

Slide 38 text

while (!(x == y)) { if (y <= x) { print x; x = x - y; } else { print y; y = y - x; }; }; print x; 変数ストア 処理中コード x : 1071 y : 462 継続 if (y <= x) { print x; x = x - y; } else { print y; y = y - x; }; ループの中身の 取り出し

Slide 39

Slide 39 text

while (!(x == y)) { if (y <= x) { print x; x = x - y; } else { print y; y = y - x; }; }; print x; 変数ストア 処理中コード x : 1071 y : 462 継続 print x; x = x - y; if 文の then 処理

Slide 40

Slide 40 text

while (!(x == y)) { if (y <= x) { print x; x = x - y; } else { print y; y = y - x; }; }; print x; 変数ストア 処理中コード x : 609 y : 462 継続 変数 x の更新

Slide 41

Slide 41 text

while (!(x == y)) { if (y <= x) { print x; x = x - y; } else { print y; y = y - x; }; }; print x; 変数ストア 処理中コード x : 609 y : 462 継続 if (y <= x) { print x; x = x - y; } else { print y; y = y - x; }; 再度ループの中身の 取り出し

Slide 42

Slide 42 text

後で使うコードは継続として保持 処理したい部分を切り出して実行

Slide 43

Slide 43 text

という処理を Lean で書くと

Slide 44

Slide 44 text

インタプリタのステップ実行 def stepExec (st : ItpState) : Option (Label × ItpState) := match st with | (.assign x a, k, s) => .some (.tau, (.skip, k, update x (aEval s a) s)) | (.seq c1 c2, k, s) => .some (.tau, (c1, .kSeq c2 k, s)) | (.ifThenElse b c1 c2, k, s) => .some (.tau, if bEval s b then c1 else c2, k, s) | (.while_ b c, k, s) => if bEval s b then .some (.tau, (c, .kWhile b c k, s)) else .some (.tau, (.skip, k, s)) | (.skip, .kSeq c k, s) => .some (.tau, (c, k, s)) | (.skip, .kWhile b c k, s) => .some (.tau, (.while_ b c, k, s)) | (.print a, k, s) => .some (.out (aEval s a), (.skip, k, s)) | (.skip, .kStop, _) => .none

Slide 45

Slide 45 text

インタプリタのステップ実行 def stepExec (st : ItpState) : Option (Label × ItpState) := match st with | (.assign x a, k, s) => .some (.tau, (.skip, k, update x (aEval s a) s)) | (.seq c1 c2, k, s) => .some (.tau, (c1, .kSeq c2 k, s)) | (.ifThenElse b c1 c2, k, s) => .some (.tau, if bEval s b then c1 else c2, k, s) | (.while_ b c, k, s) => if bEval s b then .some (.tau, (c, .kWhile b c k, s)) else .some (.tau, (.skip, k, s)) | (.skip, .kSeq c k, s) => .some (.tau, (c, k, s)) | (.skip, .kWhile b c k, s) => .some (.tau, (.while_ b c, k, s)) | (.print a, k, s) => .some (.out (aEval s a), (.skip, k, s)) | (.skip, .kStop, _) => .none さっき登場した while 文の実行

Slide 46

Slide 46 text

inductive Instr where | const (n : Int) | var (x : Ident) | setVar (x : Ident) | add | opp | branch (d : Int) | beq (d1 : Int) (d0 : Int) | ble (d1 : Int) (d0 : Int) | halt | out ターゲット言語の構文木 VM 実行結果 1071 609 462 315 168 147 126 105 84 63 42 21 VM の意味論

Slide 47

Slide 47 text

今回考えるターゲット言語 ● とてもシンプルなスタックマシン VM ○ 10 個の命令からなるアーキテクチャ(次ページ表) ● VM は以下の内部状態を持つ ○ プログラムカウンタ(フェッチ中の命令アドレス) ○ int を要素とするスタック ○ 変数ストア(インタプリタと同様)

Slide 48

Slide 48 text

CONST n スタックに数値 n を push VAR x 変数 x の値をスタックに push SETVAR x スタックから数値を 1 個 pop し、得られた値を変数 x に代入 ADD スタックから数値を 2 個 pop し、合計値を push OPP スタックから数値を 1 個 pop し、符号を反転させて push BRANCH d 無条件で d 個先の命令アドレスにジャンプ BEQ d1 d0 スタックから数値を 2 個 pop し、それらが等しければ d1 個先へ、 等しくなければ d0 個先へジャンプ BLE d1 d0 BEQ の不等号バージョン HALT 実行を停止する(コンパイル結果の末尾に付与) OUT n 数値 n を標準出力

Slide 49

Slide 49 text

CONST 1071 SETVAR x CONST 462 SETVAR y VAR x VAR y BEQ 19 0 VAR y VAR x BLE 0 8 VAR x OUT VAR x VAR y OPP ADD ... プログラムカウンタ スタック 0 変数ストア x : 0 y : 0

Slide 50

Slide 50 text

CONST 1071 SETVAR x CONST 462 SETVAR y VAR x VAR y BEQ 19 0 VAR y VAR x BLE 0 8 VAR x OUT VAR x VAR y OPP ADD ... プログラムカウンタ スタック 0 変数ストア x : 0 y : 0 PC 1071

Slide 51

Slide 51 text

CONST 1071 SETVAR x CONST 462 SETVAR y VAR x VAR y BEQ 19 0 VAR y VAR x BLE 0 8 VAR x OUT VAR x VAR y OPP ADD ... プログラムカウンタ スタック 1 変数ストア x : 1071 y : 0 PC

Slide 52

Slide 52 text

CONST 1071 SETVAR x CONST 462 SETVAR y VAR x VAR y BEQ 19 0 VAR y VAR x BLE 0 8 VAR x OUT VAR x VAR y OPP ADD ... プログラムカウンタ スタック 1 変数ストア x : 1071 y : 0 462 PC

Slide 53

Slide 53 text

CONST 1071 SETVAR x CONST 462 SETVAR y VAR x VAR y BEQ 19 0 VAR y VAR x BLE 0 8 VAR x OUT VAR x VAR y OPP ADD ... プログラムカウンタ スタック 3 変数ストア x : 1071 y : 462 PC

Slide 54

Slide 54 text

CONST 1071 SETVAR x CONST 462 SETVAR y VAR x VAR y BEQ 19 0 VAR y VAR x BLE 0 8 VAR x OUT VAR x VAR y OPP ADD ... プログラムカウンタ スタック 4 変数ストア x : 1071 y : 462 PC 1071

Slide 55

Slide 55 text

CONST 1071 SETVAR x CONST 462 SETVAR y VAR x VAR y BEQ 19 0 VAR y VAR x BLE 0 8 VAR x OUT VAR x VAR y OPP ADD ... プログラムカウンタ スタック 5 変数ストア x : 1071 y : 462 PC 462 1071

Slide 56

Slide 56 text

プログラムカウンタが命令をフェッチ 途中結果はスタックに置きつつ状態更新

Slide 57

Slide 57 text

という処理を Lean で書くと

Slide 58

Slide 58 text

VM のステップ実行 def stepExec (code : Code) (st : VMState) : ExecResult := match st with | (pc, stk, s) => match instrAt code pc with | .some (.const n) => .next .tau (pc + 1, n :: stk, s) | .some (.var x) => .next .tau (pc + 1, s x :: stk, s) | .some .add => match stk with | n2 :: n1 :: stk' => .next .tau (pc + 1, (n1 + n2) :: stk', s) | _ => .fail | .some (.branch d) => .next .tau (pc + 1 + d, stk, s) | .some (.beq d1 d0) => match stk with ...

Slide 59

Slide 59 text

VM のステップ実行 def stepExec (code : Code) (st : VMState) : ExecResult := match st with | (pc, stk, s) => match instrAt code pc with | .some (.const n) => .next .tau (pc + 1, n :: stk, s) | .some (.var x) => .next .tau (pc + 1, s x :: stk, s) | .some .add => match stk with | n2 :: n1 :: stk' => .next .tau (pc + 1, (n1 + n2) :: stk', s) | _ => .fail | .some (.branch d) => .next .tau (pc + 1 + d, stk, s) | .some (.beq d1 d0) => match stk with ... add 命令による加算

Slide 60

Slide 60 text

セクション 2 のまとめ ● コンパイラの「正しさ」 ○ インタプリタの実行を VM の実行に正しく写す ● インタプリタの状態とその遷移 ○ 残りのコード断片を継続として保持しつつ更新 ● VM の状態とその遷移 ○ PC が命令列上を動き、命令に応じて状態を更新

Slide 61

Slide 61 text

コンパイルと模倣関係 3. Semantic Correctness of the Compilation Algorithm

Slide 62

Slide 62 text

算術式とブール式のコンパイル ● 演算を逆ポーランド順で命令列に変換 ○ 左辺 → 右辺 → 演算の順につなぐ ● 複雑な式でも再帰的にコンパイル可能 ○ 部分式が中間結果をスタックに残す (x + 1) - (y + 2) <= 0 VAR x CONST 1 ADD VAR y CONST 2 ADD OPP ADD CONST 0 BLE

Slide 63

Slide 63 text

算術式とブール式のコンパイル ● 演算を逆ポーランド順で命令列に変換 ○ 左辺 → 右辺 → 演算の順につなぐ ● 複雑な式でも再帰的にコンパイル可能 ○ 部分式が中間結果をスタックに残す (x + 1) - (y + 2) <= 0 VAR x CONST 1 ADD VAR y CONST 2 ADD OPP ADD CONST 0 BLE

Slide 64

Slide 64 text

if 文のコンパイル ● 条件、then 処理、else 処理を変換 ● それぞれの命令列の長さを確認し、 不要部分を飛び越すジャンプ命令を足す if (x <= 0) { x = x + 1; } else { y = 42; }; VAR x CONST 0 BLE 0 6 VAR x CONST 1 ADD SETVAR x BRANCH 3 CONST 42 SETVAR y ...

Slide 65

Slide 65 text

if 文のコンパイル ● 条件、then 処理、else 処理を変換 ● それぞれの命令列の長さを確認し、 不要部分を飛び越すジャンプ命令を足す if (x <= 0) { x = x + 1; } else { y = 42; }; VAR x CONST 0 BLE 0 6 VAR x CONST 1 ADD SETVAR x BRANCH 3 CONST 42 SETVAR y ...

Slide 66

Slide 66 text

while 文のコンパイル ● 条件、ループ本体をそれぞれ変換 ● 命令列の長さに応じジャンプ命令を足す ○ 条件不成立ならループを飛び越える ○ ループ末尾から条件部分へジャンプ while (x <= 0) { x = x + 1; }; VAR x CONST 0 BLE 0 6 VAR x CONST 1 ADD SETVAR x BRANCH -7 ...

Slide 67

Slide 67 text

while 文のコンパイル ● 条件、ループ本体をそれぞれ変換 ● 命令列の長さに応じジャンプ命令を足す ○ 条件不成立ならループを飛び越える ○ ループ末尾から条件部分へジャンプ while (x <= 0) { x = x + 1; }; VAR x CONST 0 BLE 0 6 VAR x CONST 1 ADD SETVAR x BRANCH -7 ...

Slide 68

Slide 68 text

という処理を Lean で書くと

Slide 69

Slide 69 text

文のコンパイル処理 def compileCom (c : Com) : Code := match c with | .assign x a => compileAExp a ++ [.setVar x] | .ifThenElse b ifso ifnot => let codeIfso := compileCom ifso let codeIfnot := compileCom ifnot compileBExp b 0 (codeLen codeIfso + 1) ++ codeIfso ++ .branch (codeLen codeIfnot) :: codeIfnot | .while_ b body => let codeBody := compileCom body let codeTest := compileBExp b 0 (codeLen codeBody + 1) codeTest ++ codeBody ++ [.branch (-(codeLen codeTest + codeLen codeBody + 1))] ...

Slide 70

Slide 70 text

文のコンパイル処理 def compileCom (c : Com) : Code := match c with | .assign x a => compileAExp a ++ [.setVar x] | .ifThenElse b ifso ifnot => let codeIfso := compileCom ifso let codeIfnot := compileCom ifnot compileBExp b 0 (codeLen codeIfso + 1) ++ codeIfso ++ .branch (codeLen codeIfnot) :: codeIfnot | .while_ b body => let codeBody := compileCom body let codeTest := compileBExp b 0 (codeLen codeBody + 1) codeTest ++ codeBody ++ [.branch (-(codeLen codeTest + codeLen codeBody + 1))] ... if 文のコンパイル

Slide 71

Slide 71 text

実行!

Slide 72

Slide 72 text

1071 609 462 315 168 147 126 105 84 63 42 21 インタプリタ実行結果 VM 実行結果 1071 609 462 315 168 147 126 105 84 63 42 21 一致

Slide 73

Slide 73 text

再考:コンパイラの正しさ ● 実行ステップは直接対応しない ○ インタプリタの 1 ステップは VM だと複数命令 ○ 最終的に観測される出力が一致していれば良さそう ● 無限ループするプログラムもコンパイル可能 ○ 停止後に出力列を比較する方法は使えない ○ 無限に動く状態遷移系の「一致」とは何か?

Slide 74

Slide 74 text

弱模倣を用いて「正しさ」を示す

Slide 75

Slide 75 text

弱模倣関係 (S, L ∪ {τ}, →) と (T, L ∪ {τ}, →) をラベル付き遷移系とする。 以下が成り立つとき、R ⊆ S × T は弱模倣関係であるという。 任意の (s, t) ∈ R、a ∈ L、s’ ∈ S に対して、 s → ... → ... → s’ ならば、ある t’ ∈ T が存在して (s’, t’) ∈ R かつ t → ... → ... → t’ a τ τ 定義(弱模倣、Weak Simulation) a τ τ

Slide 76

Slide 76 text

s s’ t a を出力 出力なし 出力なし R による対応

Slide 77

Slide 77 text

s s’ t t’ a を出力 a を出力 出力なし 出力なし R による対応 R による対応 t 側も同じ a を出力して s 側に追いつき 再度 R による対応が成立した状態になれる 出力なし 出力なし

Slide 78

Slide 78 text

コンパイラにおける弱模倣関係 ● インタプリタの状態と VM 状態の対応関係を定義 ○ インタプリタの実行中コード = VM の PC 位置 ○ インタプリタの継続 = この後で実行する VM 命令列 ○ VM スタック = 空、変数ストアが等しい ● 弱模倣:対応した状態からインタプリタが進んだとき、 VM も何ステップかで追いつき、再び対応した状態になる

Slide 79

Slide 79 text

print (x + 1) + 2; 実行中コード print 42; 継続 変数ストア 変数ストア スタック CONST 100 SETVAR x VAR x CONST 1 ADD CONST 2 ADD OUT CONST 42 OUT HALT x : 100 x : 100 対応 PC

Slide 80

Slide 80 text

インタプリタが動いたとする

Slide 81

Slide 81 text

print (x + 1) + 2; 実行中コード print 42; 継続 変数ストア 変数ストア スタック CONST 100 SETVAR x VAR x CONST 1 ADD CONST 2 ADD OUT CONST 42 OUT HALT x : 100 x : 100 対応 PC

Slide 82

Slide 82 text

実行中コード print 42; 継続 変数ストア 変数ストア スタック CONST 100 SETVAR x VAR x CONST 1 ADD CONST 2 ADD OUT CONST 42 OUT HALT x : 100 x : 100 PC 出力 103

Slide 83

Slide 83 text

print 42; 実行中コード 継続 変数ストア 変数ストア スタック CONST 100 SETVAR x VAR x CONST 1 ADD CONST 2 ADD OUT CONST 42 OUT HALT x : 100 x : 100 PC 出力 103

Slide 84

Slide 84 text

VM が追いつく

Slide 85

Slide 85 text

print 42; 実行中コード 継続 変数ストア 変数ストア スタック CONST 100 SETVAR x VAR x CONST 1 ADD CONST 2 ADD OUT CONST 42 OUT HALT x : 100 x : 100 PC 出力 103 100

Slide 86

Slide 86 text

print 42; 実行中コード 継続 変数ストア 変数ストア スタック CONST 100 SETVAR x VAR x CONST 1 ADD CONST 2 ADD OUT CONST 42 OUT HALT x : 100 x : 100 PC 出力 103 1 100

Slide 87

Slide 87 text

print 42; 実行中コード 継続 変数ストア 変数ストア スタック CONST 100 SETVAR x VAR x CONST 1 ADD CONST 2 ADD OUT CONST 42 OUT HALT x : 100 x : 100 PC 出力 103 101

Slide 88

Slide 88 text

print 42; 実行中コード 継続 変数ストア 変数ストア スタック CONST 100 SETVAR x VAR x CONST 1 ADD CONST 2 ADD OUT CONST 42 OUT HALT x : 100 x : 100 PC 出力 103 2 101

Slide 89

Slide 89 text

print 42; 実行中コード 継続 変数ストア 変数ストア スタック CONST 100 SETVAR x VAR x CONST 1 ADD CONST 2 ADD OUT CONST 42 OUT HALT x : 100 x : 100 PC 出力 103 103

Slide 90

Slide 90 text

print 42; 実行中コード 継続 変数ストア 変数ストア スタック CONST 100 SETVAR x VAR x CONST 1 ADD CONST 2 ADD OUT CONST 42 OUT HALT x : 100 x : 100 PC 出力 103 出力 103

Slide 91

Slide 91 text

print 42; 実行中コード 継続 変数ストア 変数ストア スタック CONST 100 SETVAR x VAR x CONST 1 ADD CONST 2 ADD OUT CONST 42 OUT HALT x : 100 x : 100 PC 出力 103 出力 103

Slide 92

Slide 92 text

print 42; 実行中コード 継続 変数ストア 変数ストア スタック CONST 100 SETVAR x VAR x CONST 1 ADD CONST 2 ADD OUT CONST 42 OUT HALT x : 100 x : 100 PC 出力 103 出力 103 対応

Slide 93

Slide 93 text

という主張を Lean で書くと

Slide 94

Slide 94 text

定理:VM がインタプリタを弱模倣 inductive match_state (code : Code) : ItpState → VMState → Prop where | intro : ∀ (c : Com) (k : Cont) (s : Store) (pc : Int), code_at code pc (compileCom c) → compile_cont code k (pc + codeLen (compileCom c)) → match_state code (c, k, s) (pc, [], s) theorem simulation_step : ∀ (code : Code) (l : Label) (ist1 ist2 : ItpState) (vst1 : VMState), itpStep ist1 l ist2 → match_state code ist1 vst1 → ∃ (vst2 : VMState), vmWeakStep code l vst1 vst2 ∧ match_state code ist2 vst2 := by ...

Slide 95

Slide 95 text

定理:VM がインタプリタを弱模倣 inductive match_state (code : Code) : ItpState → VMState → Prop where | intro : ∀ (c : Com) (k : Cont) (s : Store) (pc : Int), code_at code pc (compileCom c) → compile_cont code k (pc + codeLen (compileCom c)) → match_state code (c, k, s) (pc, [], s) theorem simulation_step : ∀ (code : Code) (l : Label) (ist1 ist2 : ItpState) (vst1 : VMState), itpStep ist1 l ist2 → match_state code ist1 vst1 → ∃ (vst2 : VMState), vmWeakStep code l vst1 vst2 ∧ match_state code ist2 vst2 := by ... s と t の対応

Slide 96

Slide 96 text

定理:VM がインタプリタを弱模倣 inductive match_state (code : Code) : ItpState → VMState → Prop where | intro : ∀ (c : Com) (k : Cont) (s : Store) (pc : Int), code_at code pc (compileCom c) → compile_cont code k (pc + codeLen (compileCom c)) → match_state code (c, k, s) (pc, [], s) theorem simulation_step : ∀ (code : Code) (l : Label) (ist1 ist2 : ItpState) (vst1 : VMState), itpStep ist1 l ist2 → match_state code ist1 vst1 → ∃ (vst2 : VMState), vmWeakStep code l vst1 vst2 ∧ match_state code ist2 vst2 := by ... s’ と t’ の対応

Slide 97

Slide 97 text

セクション 3 のまとめ ● コンパイラの実装 ○ ソース言語の断片を再帰的に命令列に変換 ● 弱模倣によるコンパイル処理の正当性 ○ 無限ループを考えると「最終結果の一致」は無意味 ○ インタプリタと VM で「対応する状態」を定義し、 その対応関係が実行に伴って保存されることを証明

Slide 98

Slide 98 text

まとめ 4. Today’s Summary

Slide 99

Slide 99 text

本日のまとめ ● AI 台頭の時代、仕様の「検証可能性」が重要に ○ 合致しているかどうか明確に判定できる仕様記述 ● インタプリタ・VM・コンパイラ ○ インタプリタの意味論を VM の意味論に写す ● 弱模倣によるコンパイラの正当性 ○ 対応関係を定義、それが実行中保存されることを証明

Slide 100

Slide 100 text

Let’s Craft a Bullet-Proof Compiler with Lean! Presented By チェシャ猫 (@y_taka_23)