Upgrade to Pro — share decks privately, control downloads, hide ads and more …

形式手法特論:コンパイラの「正しさ」は証明できるか? #burikaigi / BuriKai...

Avatar for y_taka_23 y_taka_23
January 10, 2026

形式手法特論:コンパイラの「正しさ」は証明できるか? #burikaigi / BuriKaigi 2026

BuriKaigi 2026 で使用したスライドです。

本セッションでは、定理証明支援系 Lean を用いたコンパイラの実装技法を解説します。ただしこれは本質的にはコンパイラのトークではありません。頭の痛い複雑なロジックや、うんざりするほど多様な入力データと戦っている、すべてのソフトウェアエンジニアに贈る新しい世界への招待状です。

今日、プログラムを書く際に一緒に単体テストを書くことは、一種のマナーとして広く普及しています。しかし、かつて Dijkstra はこう言いました。「テストではバグの存在を示すことはできても、不在を示すことはできない」つまりテストが成功していたとしても、それはたまたまテストケースが不足していてバグを踏まなかった可能性が否定できない、というのです。一方で、仮に全通りのテストケースを生成してバグの不在を示そうとした場合、組み合わせの爆発により膨大な数のテストが必要になってしまいます。例えば「長さ 3 以下の char の配列を受け取る関数」をテストするだけでも入力パターンは 16,843,009 通り。通常の任意長の配列を受け取る関数ならば文字通り「無限個のテストケース」が必要です。

本セッションで紹介する定理証明は、文字通り、この「無限個のテストケース」を扱うための手法であるといえるでしょう。テストしたい関数の性質を型レベルの制約として表現することで、単体テストのような実行時ではなく静的な型検査時に、かつ「任意の char 配列」のような事実上無限個のテストケースに対して関数の性質を保証できます。

いくつかある定理証明支援系の中でも、Lean は単に証明を記述するだけでなく、実際に動くプログラミング言語であるという面で近年注目を浴びています。一例として、Amazon Web Service では、認可ポリシー記述言語である Cedar の開発と最適化のために、この Lean を採用しています。認可ポリシーエンジンの実装は「ロジックが複雑」「あらゆるパターンに対応する必要がある」「最終結果がぱっと見で分からない」「ミスがあると被害が甚大」という点で、まさに定理証明向きの事例と言えます。また、国内においてもちょうど日本語書籍『ゼロから始める Lean 言語入門』が出版されたばかりで、今 Lean が盛り上がりつつあるのは間違いないでしょう。

本セッションでは、Lean を利用して、自作言語をコンパイルしてシンプルな CPU 上で動かすための「証明付きコンパイラ」を実装します。コンパイラもまた、複雑なロジックと多様な入力が求められるソフトウェアの典型です。ところで、引数と戻り値を持つ個別の関数のテストならともかく、ここで言う「コンパイラの正しさ」とは何でしょう? コンパイルしたプログラムの挙動が正しいこと? ではその「正しい」とはどういう状況か、定義できるでしょうか?

この問いへの答えとして、今回の解説では、コンパイラの性質を「ソース言語の意味論」と「ターゲット言語の意味論」の間をつなぐものとして定式化し、実装したコンパイラが意味論を保存することを証明します。また、コンパイラの挙動を保証するための理論的な解説に加え、実際に動くプログラムを書けるという Lean の特性を活かして、「インタプリタ」「VM」そしてその間をつなぐ「コンパイラ」をそれぞれ実装し、簡単なプログラムをコンパイルして動かす様子もお見せします。

受講にあたって必要なものは、プログラミング経験者であれば普通に知っている程度の知識と、ほんの少しの知的好奇心だけです。定理証明や特定の CPU 命令セットに関する前提知識は要求しませんし、それどころかコンパイラとしては、最適化も行わない、本当に素朴な実装しかしません。むしろ「コンパイラの正しさとは何か?」を題材として、複雑なプログラムの挙動も数学的にきちんと定式化できるのだ、そしてそのための理論や考え方は、他ならぬあなた自身とも無関係の世界ではないのだ、という感動を味わって頂ければと思います。

イベントページ:https://fortee.jp/burikaigi-2026/proposal/e5d846d4-645d-4329-b0af-a02fc9a3ccfc

Avatar for y_taka_23

y_taka_23

January 10, 2026
Tweet

More Decks by y_taka_23

Other Decks in Technology

Transcript

  1. 生成 AI 時代の仕様記述 • 仕様を「記述すること」の重要性 ◦ AI はそれっぽい実装を高速・大量に生成できる ◦ 明示したコンテキスト外の事情は汲んでくれない

    • 今後は仕様の「検証可能性」がより問われる時代に ◦ まずい実装を機械的に弾ける、白黒が決まる仕様 ◦ リッチな型システムや静的解析の利用
  2. 単体テスト • 各テスト観点に対し、欲しい仕様を個別に記述 ◦ 空:rev (rev []) == [] ◦

    1 個:rev (rev [42]) == [42] ◦ 文字列:rev (rev ["foo", "bar"]) == ["foo", "bar"] • 結果が予測可能な処理に対しては最もコスパが良い ◦ その代わり、不具合の検出はテスト設計に強く依存
  3. プロパティベーステスト(PBT) • 個別のテストケースではなく性質(プロパティ)を記述 ◦ 2 回反転で元に戻る:rev (rev xs) == xs

    ◦ この性質は「リスト xs が何であろうと」成り立つ • この xs を多数生成し、反例がないか確認 ◦ 単なるランダム以上に、コーナーケースは狙って生成 ◦ 反例が見つかった場合、最小の反例も探索
  4. セクション 1 のまとめ • AI 時代における「検証可能な仕様記述」の重要性 • プロパティベーステスト ◦ 個別ケースではなく、包括的な「性質」を記述する

    • Lean による証明 ◦ 真に「任意の入力」に対する動作を保証 ◦ 数学専用ではなく、現に動くプログラムが書ける
  5. プログラムの意味論とコンパイラ • 評価器は言語の操作的意味論(実行方法)を定義する ◦ ソース言語の評価器:インタプリタ ◦ ターゲット言語の評価器:VM や実際の CPU •

    コンパイラはソース言語をターゲット言語に写す ◦ この「写し方」が意味論と整合する必要がある ◦ 「写す前の実行」と「写した後での実行」の一致
  6. 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 ... コンパイル
  7. 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 ターゲット言語の構文木 コンパイル 書き出し パース
  8. 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 の意味論 書き出し パース
  9. 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 の意味論 書き出し パース
  10. コンパイラと定理証明 • 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/
  11. 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 インタプリタ実行結果 インタプリタの 意味論
  12. 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 継続
  13. 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; 先頭の文の取り出し
  14. 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 の更新
  15. while (!(x == y)) { if (y <= x) {

    print x; x = x - y; } else { print y; y = y - x; }; }; print x; 変数ストア 処理中コード x : 1071 y : 462 継続 変数 y の更新 先頭の文の取り出し
  16. 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; }; ループの中身の 取り出し
  17. 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 処理
  18. while (!(x == y)) { if (y <= x) {

    print x; x = x - y; } else { print y; y = y - x; }; }; print x; 変数ストア 処理中コード x : 609 y : 462 継続 変数 x の更新
  19. 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; }; 再度ループの中身の 取り出し
  20. インタプリタのステップ実行 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
  21. インタプリタのステップ実行 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 文の実行
  22. 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 の意味論
  23. 今回考えるターゲット言語 • とてもシンプルなスタックマシン VM ◦ 10 個の命令からなるアーキテクチャ(次ページ表) • VM は以下の内部状態を持つ

    ◦ プログラムカウンタ(フェッチ中の命令アドレス) ◦ int を要素とするスタック ◦ 変数ストア(インタプリタと同様)
  24. 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 を標準出力
  25. 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
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. 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 ...
  33. 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 命令による加算
  34. セクション 2 のまとめ • コンパイラの「正しさ」 ◦ インタプリタの実行を VM の実行に正しく写す •

    インタプリタの状態とその遷移 ◦ 残りのコード断片を継続として保持しつつ更新 • VM の状態とその遷移 ◦ PC が命令列上を動き、命令に応じて状態を更新
  35. 算術式とブール式のコンパイル • 演算を逆ポーランド順で命令列に変換 ◦ 左辺 → 右辺 → 演算の順につなぐ •

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

    複雑な式でも再帰的にコンパイル可能 ◦ 部分式が中間結果をスタックに残す (x + 1) - (y + 2) <= 0 VAR x CONST 1 ADD VAR y CONST 2 ADD OPP ADD CONST 0 BLE
  37. 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 ...
  38. 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 ...
  39. 文のコンパイル処理 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))] ...
  40. 文のコンパイル処理 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 文のコンパイル
  41. 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 一致
  42. 再考:コンパイラの正しさ • 実行ステップは直接対応しない ◦ インタプリタの 1 ステップは VM だと複数命令 ◦

    最終的に観測される出力が一致していれば良さそう • 無限ループするプログラムもコンパイル可能 ◦ 停止後に出力列を比較する方法は使えない ◦ 無限に動く状態遷移系の「一致」とは何か?
  43. 弱模倣関係 (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 τ τ
  44. s s’ t t’ a を出力 a を出力 出力なし 出力なし

    R による対応 R による対応 t 側も同じ a を出力して s 側に追いつき 再度 R による対応が成立した状態になれる 出力なし 出力なし
  45. コンパイラにおける弱模倣関係 • インタプリタの状態と VM 状態の対応関係を定義 ◦ インタプリタの実行中コード = VM の

    PC 位置 ◦ インタプリタの継続 = この後で実行する VM 命令列 ◦ VM スタック = 空、変数ストアが等しい • 弱模倣:対応した状態からインタプリタが進んだとき、 VM も何ステップかで追いつき、再び対応した状態になる
  46. 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
  47. 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
  48. 実行中コード 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
  49. 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
  50. 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
  51. 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
  52. 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
  53. 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
  54. 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
  55. 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
  56. 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
  57. 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 対応
  58. 定理: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 ...
  59. 定理: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 の対応
  60. 定理: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’ の対応
  61. セクション 3 のまとめ • コンパイラの実装 ◦ ソース言語の断片を再帰的に命令列に変換 • 弱模倣によるコンパイル処理の正当性 ◦

    無限ループを考えると「最終結果の一致」は無意味 ◦ インタプリタと VM で「対応する状態」を定義し、 その対応関係が実行に伴って保存されることを証明