SNAPKITTYWEST commited on
Commit
f5deffb
·
verified ·
1 Parent(s): bba512b

October 2026 main drop: mirror from GitHub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .github/workflows/lean.yml +32 -0
  2. .gitignore +1 -0
  3. License +110 -0
  4. NEXT_STEPS.md +36 -0
  5. README.md +492 -0
  6. backend/common/Harness.lean +307 -0
  7. backend/wasm/Wasm.lean +17 -0
  8. backend/wasm/Wasm/Correct.lean +292 -0
  9. backend/wasm/Wasm/Emit.lean +117 -0
  10. backend/wasm/Wasm/Frame.lean +355 -0
  11. backend/wasm/Wasm/Guard.lean +163 -0
  12. backend/wasm/Wasm/Init.lean +156 -0
  13. backend/wasm/Wasm/Isa.lean +256 -0
  14. backend/wasm/Wasm/Lower.lean +185 -0
  15. backend/wasm/Wasm/Mem.lean +89 -0
  16. backend/wasm/Wasm/Rel.lean +116 -0
  17. backend/wasm/Wasm/Seq.lean +65 -0
  18. backend/wasm/Wasm/SimAlu.lean +159 -0
  19. backend/wasm/Wasm/SimAlu2.lean +189 -0
  20. backend/wasm/Wasm/SimAux.lean +229 -0
  21. backend/wasm/Wasm/SimCtl.lean +146 -0
  22. backend/wasm/Wasm/SimExec.lean +329 -0
  23. backend/wasm/Wasm/SimMem.lean +188 -0
  24. backend/wasm/Wasm/SimStack.lean +398 -0
  25. backend/wasm/WasmMain.lean +135 -0
  26. backend/x86_64/Main.lean +149 -0
  27. backend/x86_64/X86.lean +20 -0
  28. backend/x86_64/X86/Correct.lean +181 -0
  29. backend/x86_64/X86/Emit.lean +143 -0
  30. backend/x86_64/X86/Flags.lean +128 -0
  31. backend/x86_64/X86/Guard.lean +230 -0
  32. backend/x86_64/X86/Init.lean +135 -0
  33. backend/x86_64/X86/Isa.lean +227 -0
  34. backend/x86_64/X86/Lower.lean +198 -0
  35. backend/x86_64/X86/Micro.lean +280 -0
  36. backend/x86_64/X86/Rel.lean +170 -0
  37. backend/x86_64/X86/Run.lean +143 -0
  38. backend/x86_64/X86/Seq.lean +77 -0
  39. backend/x86_64/X86/SimAux.lean +448 -0
  40. backend/x86_64/X86/SimBin.lean +100 -0
  41. backend/x86_64/X86/SimCtl.lean +282 -0
  42. backend/x86_64/X86/SimDiv.lean +368 -0
  43. backend/x86_64/X86/SimMem.lean +213 -0
  44. backend/x86_64/X86/SimMemOps.lean +281 -0
  45. backend/x86_64/X86/SimOps.lean +217 -0
  46. backend/x86_64/X86/SimStack.lean +292 -0
  47. backend/x86_64/X86/SimStep.lean +353 -0
  48. bcpl/BCPL.lean +5 -0
  49. bcpl/BCPL/Compile.lean +63 -0
  50. bcpl/BCPL/Correct.lean +298 -0
.github/workflows/lean.yml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: lean
2
+ on: [push, pull_request]
3
+ jobs:
4
+ build:
5
+ runs-on: ubuntu-latest
6
+ steps:
7
+ - uses: actions/checkout@v4
8
+ - name: Install elan
9
+ run: |
10
+ curl -sSfL https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh -o elan-init.sh
11
+ sh elan-init.sh -y --default-toolchain none
12
+ echo "$HOME/.elan/bin" >> "$GITHUB_PATH"
13
+ - name: Build
14
+ run: lake build
15
+ - name: Forbidden constructs
16
+ run: |
17
+ if grep -rnE '\bsorry\b|\badmit\b|native_decide|^\s*axiom ' formal word-ir forth bcpl wolfram backend --include='*.lean'; then exit 1; fi
18
+ - name: Axiom audit
19
+ run: lake env lean formal/Audit.lean
20
+ - name: Native x86-64 execution check
21
+ run: .lake/build/bin/wordc check 300
22
+ - name: Forth source files (x86-64)
23
+ run: for f in examples/forth/*.fs; do .lake/build/bin/wordc forth "$f" || exit 1; done
24
+ - name: Install wasmtime
25
+ run: |
26
+ curl -sSfL -o wasmtime.tar.xz https://github.com/bytecodealliance/wasmtime/releases/download/v30.0.2/wasmtime-v30.0.2-x86_64-linux.tar.xz
27
+ tar xf wasmtime.tar.xz
28
+ echo "$PWD/wasmtime-v30.0.2-x86_64-linux" >> "$GITHUB_PATH"
29
+ - name: WebAssembly execution check
30
+ run: .lake/build/bin/wasmw check 300
31
+ - name: Forth source files (WebAssembly)
32
+ run: for f in examples/forth/*.fs; do .lake/build/bin/wasmw forth "$f" || exit 1; done
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ .lake/
License ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Parr Source License — No AI Training, Version 1.0
2
+
3
+ Copyright (c) 2026 Ahmad Ali Parr
4
+
5
+ This is a custom source-available license inspired by the Functional Source
6
+ License. It is not the standard FSL and does not include an automatic grant of
7
+ an Apache, MIT, or other future license.
8
+
9
+ ## 1. Definitions
10
+
11
+ "Licensor" means Ahmad Ali Parr.
12
+
13
+ "Software" means the source code, documentation, examples, tests, and other
14
+ materials that the Licensor makes available under this license. Separately
15
+ licensed third-party materials remain governed by their respective licenses.
16
+
17
+ "Competing Use" means making the Software available to others in a commercial
18
+ product or service that substitutes for the Software, offers the same or
19
+ substantially similar functionality as the Software, or substitutes for a
20
+ product or service the Licensor offers using the Software as of the date the
21
+ applicable version is made available.
22
+
23
+ "AI Training" means using the Software, any portion of it, modifications or
24
+ derivatives of it, or data derived from it as training material for an artificial
25
+ intelligence or machine-learning model. This includes pretraining, fine-tuning,
26
+ continued training, distillation, reinforcement learning, and constructing or
27
+ supplying datasets for any of those purposes. Data derived from the Software
28
+ includes synthetic training examples generated from its code or documentation.
29
+
30
+ "Permitted Purpose" means any purpose that is neither a Competing Use nor AI
31
+ Training.
32
+
33
+ ## 2. License grant
34
+
35
+ Subject to compliance with this license, the Licensor grants you a worldwide,
36
+ nonexclusive, royalty-free license to use, copy, modify, create derivative works
37
+ of, publicly perform, publicly display, and redistribute the Software for
38
+ Permitted Purposes.
39
+
40
+ Permitted Purposes include internal business use, personal use, education,
41
+ research, and professional services provided to users who comply with this
42
+ license, provided those activities do not constitute Competing Use or AI
43
+ Training. Commercial use that is not a Competing Use or AI Training is permitted.
44
+
45
+ ## 3. No AI training
46
+
47
+ You may not use the Software for AI Training, or license, sell, distribute, or
48
+ otherwise supply it or data derived from it for AI Training, without separate
49
+ written permission from the Licensor. This restriction applies to commercial,
50
+ noncommercial, educational, research, and internal activities alike.
51
+
52
+ Using an existing AI model solely for inference, such as to review, explain, or
53
+ modify the Software, is permitted if that activity is otherwise a Permitted
54
+ Purpose and the Software and resulting derived data are not used for AI
55
+ Training. This license does not prohibit training on unrelated material or on
56
+ your own material that is independent of the Software.
57
+
58
+ ## 4. Redistribution and notices
59
+
60
+ If you redistribute the Software or any modification or derivative of it, you
61
+ must include a copy of this license and preserve the copyright notices provided
62
+ with the Software. You must identify your modifications and must not imply that
63
+ the Licensor endorses them.
64
+
65
+ The portions of a redistributed work that consist of the Software or its
66
+ modifications or derivatives remain subject to this license. You may not grant
67
+ recipients rights to those portions that conflict with this license. Independent
68
+ components may retain their own licenses.
69
+
70
+ ## 5. Patents
71
+
72
+ To the extent a Permitted Purpose necessarily infringes patent claims controlled
73
+ by the Licensor, the license grant includes a license under those claims. If you
74
+ bring a patent claim alleging that the Software infringes or contributes to
75
+ patent infringement, your patent license under this license terminates
76
+ immediately.
77
+
78
+ ## 6. Trademarks
79
+
80
+ This license grants no rights to the Licensor's trademarks, trade names, service
81
+ marks, or product names, except for reasonable attribution and identification of
82
+ the origin of the Software.
83
+
84
+ ## 7. Termination
85
+
86
+ Your rights under this license terminate automatically if you violate its
87
+ terms. Termination does not terminate the rights of recipients who obtained
88
+ copies from you and remain in compliance. Reinstatement of your rights requires
89
+ written permission from the Licensor.
90
+
91
+ ## 8. No automatic conversion
92
+
93
+ The Competing Use and AI Training restrictions do not expire automatically.
94
+ There is no conversion to another license after two years or any other period.
95
+ Any additional license grant requires a separate grant from the relevant rights
96
+ holder.
97
+
98
+ ## 9. Disclaimer of warranties
99
+
100
+ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED
101
+ "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
102
+ LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
103
+ TITLE, AND NONINFRINGEMENT.
104
+
105
+ ## 10. Limitation of liability
106
+
107
+ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL THE LICENSOR
108
+ OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER
109
+ IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN
110
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
NEXT_STEPS.md ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Next steps
2
+
3
+ Suggested development order. Each item names the gap it closes in the current code.
4
+
5
+ ## 1. Forth: parser fuel and a grammar specification
6
+
7
+ Colon definitions (lowered to IR `call`/`ret`), `RECURSE`, `EXIT`, `VARIABLE`,
8
+ `CONSTANT`, `0=`, `MOD`, the return-data stack words `>R R> R@` and
9
+ `DO … LOOP` with `I` and `J` are covered by `Forth.Program.compile_correct`.
10
+ The return-data stack is the IR's auxiliary stack (`tor`/`fromr`/`rfetch`),
11
+ which `call`/`ret` never touch, so loop parameters and `>R` values survive
12
+ calls and recursion, and unbalanced use across `EXIT` or a call is defined
13
+ behaviour. A text parser (`Forth.parse`) and a printer (`Forth.print`) exist,
14
+ with the round trip `parse (print P) = .ok P` proved for well-formed programs
15
+ (`Forth.parse_print`). The converse direction is proved in
16
+ `Forth/ParseProps.lean`: every successful parse is well formed (`parse_wf`), so
17
+ `print` is a canonical form (`parse_print_of_parse`), and the handling of case,
18
+ comments, `CONSTANT` and `RECURSE` is characterised by lemmas. `wordc forth` /
19
+ `wasmw forth` run a Forth source file on either backend. Remaining:
20
+
21
+ - The parser lemmas have side conditions where the text-level statement would
22
+ otherwise be false (a `( … )` comment body without `)`, `\` or newline, after
23
+ text not ending inside a `(` comment; case-insensitivity up to the spelling in
24
+ error messages). Not proved: that the fuel bounds of `parseSeq`/`parseTop`
25
+ are never exhausted (the "too deeply nested" / "too long" errors are
26
+ unreachable), and a full specification of `parse` as a grammar.
27
+ - Not implemented: `+LOOP`, `?DO`, `LEAVE` and `UNLOOP`. `LOOP` uses only the
28
+ equal case of ANS Forth's crossing rule (it ends when `index + 1 = limit`),
29
+ which is exact for a step of one; `+LOOP` would need the full rule.
30
+
31
+ ## Ongoing
32
+
33
+ The formal results cover the Lean machine models. Their agreement with real
34
+ assemblers and runtimes is checked by execution, not proved: keep running
35
+ `wordc check` and `wasmw check` (CI uses Wasmtime v30.0.2) after every change to
36
+ a model, lowering or emitter.
README.md ADDED
@@ -0,0 +1,492 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: other
3
+ license_name: parr-source-license-no-ai-training-1.0
4
+ license_link: https://huggingface.co/Snapkitty/ai-free/blob/main/License
5
+ tags:
6
+ - snapkitty
7
+ - october-2026-drop
8
+ - lean4
9
+ - forth
10
+ - bcpl
11
+ - wolfram
12
+ - compiler
13
+ - formal-verification
14
+ ---
15
+
16
+ > Mirrored from [https://github.com/SNAPKITTYAGENT9NOVA/ai-free](https://github.com/SNAPKITTYAGENT9NOVA/ai-free) at commit `1891a9e`. Part of the **SnapKitty October 2026 main drop**.
17
+
18
+ # UniversalWord: follow the word
19
+
20
+ **Three source-language perspectives. One word machine. Two executable destinations. A Lean file for every stage of the journey.**
21
+
22
+ UniversalWord, hosted here as `ai-free`, brings Forth, a BCPL subset, and Wolfram-style scalar and matrix computations into a shared intermediate language. Its central character is the machine word: a fixed-width value that can carry an integer, a truth flag, or a memory address. Around that value, the project builds reference semantics, translations, reusable proofs, target machine models, emitters, and executable comparison harnesses.
23
+
24
+ Think of this repository as a railway atlas. The source frontends are departure stations. Universal Word IR is the interchange where their different expressions become a common instruction stream. The x86-64 and WebAssembly backends are two routes onward. The Lean modules describe the track, establish how each connection behaves, and carry the argument from a source computation to a target outcome.
25
+
26
+ This README is your guided journey through that atlas. Every Lean file receives an individual stop, with a link, a description, and a reason to open it. You can follow the whole route or jump directly to the part that interests you: writing Forth, understanding compiler correctness, exploring matrix multiplication, inspecting native assembly, or studying WebAssembly dispatch.
27
+
28
+ **Inventory note:** the surveyed `master` snapshot, commit [`93d6d84`](https://github.com/SNAPKITTYWEST/ai-free/tree/93d6d84fe80d3b7f333e31324b35ba27a964b9cc), contains **81 `.lean` files**. The numbered atlas below covers all 81, including library entry files, executable entry points, and the audit. The count follows the actual source tree so every link leads to an existing file.
29
+
30
+ ## Choose your route
31
+
32
+ | Destination | Start here | What you will discover |
33
+ | --- | --- | --- |
34
+ | A first working example | [Forth.Example](forth/Forth/Example.lean) | How `5 DUP +` becomes a machine computation |
35
+ | Forth source text | [Forth.Parse](forth/Forth/Parse.lean) | Definitions, variables, comments, recursion, and loops |
36
+ | The common machine | [WordDialect.Machine](formal/WordDialect/Machine.lean) | Instructions, state, outcomes, and authoritative execution |
37
+ | Reusable compiler proofs | [WordIR.Frag](word-ir/WordIR/Frag.lean) | How verified control-flow fragments compose |
38
+ | Memory-oriented source programs | [BCPL.Semantics](bcpl/BCPL/Semantics.lean) | Word expressions, indirection, assignment, and statements |
39
+ | Mathematical computations | [Wolfram.MatrixExample](wolfram/Wolfram/MatrixExample.lean) | A concrete matrix product and its formal ingredients |
40
+ | Native execution | [X86.Lower](backend/x86_64/X86/Lower.lean) | Register conventions, assembly sequences, and guards |
41
+ | WebAssembly execution | [Wasm.Lower](backend/wasm/Wasm/Lower.lean) | Instruction functions and the dispatch loop |
42
+ | Proof dependency inspection | [Audit](formal/Audit.lean) | The project's namespace-wide axiom audit |
43
+
44
+ The numbered entries are arranged by the computation's journey rather than alphabetical order. Within each region, the order moves from vocabulary and execution toward translation, correctness, examples, and public imports. This makes the map useful both as a reference and as a reading course.
45
+
46
+ ## Before departure: the machine's vocabulary
47
+
48
+ A `Word n` is a Lean `BitVec n`. Addition, subtraction, and multiplication follow fixed-width modular arithmetic. A pointer interprets the same representation as a word address. Source-level arithmetic therefore has a precise destination: a bit-vector result, with signed and unsigned interpretations used where the operation calls for them.
49
+
50
+ The IR uses word-addressed memory. Address `a` selects one word; a 64-bit backend places that cell at a corresponding byte address separated by eight bytes. Its data stack is a list with the top at the head. For a binary expression written as `a b op`, `b` is on top and the result is `a op b`. Keeping that convention in mind makes the instruction definitions and compiler output much easier to follow.
51
+
52
+ Control addresses are instruction indices. The machine has virtual registers, a data stack, a return stack of code addresses, and a separate auxiliary stack of words. Calls and returns use the code-address stack. Forth's return-data operations use the auxiliary stack. That separation lets saved data and loop parameters survive calls and recursion, and it carries through both backends.
53
+
54
+ Execution can continue, halt, or trap. The executable targets report the shared traps with a common vocabulary:
55
+
56
+ | Exit code | Meaning |
57
+ | --- | --- |
58
+ | `0` | Successful halt |
59
+ | `1` | Data-stack underflow |
60
+ | `2` | Bad memory address |
61
+ | `3` | Division by zero |
62
+ | `4` | Bad program counter |
63
+ | `5` | Return-stack or auxiliary-stack underflow |
64
+ | `6` | Target stack capacity exceeded |
65
+
66
+ The IR describes unbounded stacks. The targets provide finite regions and check capacity before growing them. Their correctness results incorporate that relationship: a target reaches the IR outcome or reports overflow; when the execution stays within capacity, it reaches exactly the IR outcome. These are useful, explicit contracts to keep beside the rest of the tour.
67
+
68
+ ## Station I: the common word machine — files 1–10
69
+
70
+ Every route passes through this region. Its definitions give the shared instruction stream meaning, and its theorems provide the language used throughout the frontend and backend proofs.
71
+
72
+ ### 1. [Word.lean](formal/WordDialect/Word.lean) — the value carried everywhere
73
+
74
+ Defines `Word n`, pointer interpretation, comparison conditions, truth conversion, shifts, and rotations. Its lemmas establish pointer round trips and numeric behavior.
75
+
76
+ ### 2. [Memory.lean](formal/WordDialect/Memory.lean) — the address book
77
+
78
+ Defines word cells, address validity, and optional reads and writes. The accompanying theorems describe reading a written cell, preserving other cells, and commuting independent writes.
79
+
80
+ ### 3. [Machine.lean](formal/WordDialect/Machine.lean) — the interchange
81
+
82
+ Defines the IR instructions, traps, machine state, outcomes, `exec`, and `step`. Arithmetic, stack manipulation, register access, memory, and control flow meet here. It also defines the separate auxiliary stack.
83
+
84
+ ### 4. [Stack.lean](formal/WordDialect/Stack.lean) — count the passengers
85
+
86
+ Assigns each instruction its operand and result counts. The underflow theorems establish what happens when operands are missing and show that sufficient operands exclude a data-stack-underflow result.
87
+
88
+ ### 5. [Rules.lean](formal/WordDialect/Rules.lean) — the instruction handbook
89
+
90
+ Presents explicit execution rules for literals, pointers, arithmetic, memory, stack operations, and registers. Numeric lemmas connect word operations to modular arithmetic and exact results under stated bounds.
91
+
92
+ ### 6. [Invariants.lean](formal/WordDialect/Invariants.lean) — what stays in place
93
+
94
+ Proves frame conditions for a successful instruction: stack-depth change, stable address validity, memory and register effects, and return- and auxiliary-stack preservation. It also characterizes halting and trap causes.
95
+
96
+ ### 7. [Exec.lean](formal/WordDialect/Exec.lean) — from a step to a journey
97
+
98
+ Defines finite continuing paths with `Steps`, terminating executions with `Exec`, and the fuel-bounded interpreter `run`. Soundness, completeness, and determinism connect executable interpretation to the relations.
99
+
100
+ ### 8. [Init.lean](formal/WordDialect/Init.lean) — the first platform
101
+
102
+ Builds memory from an image, defines the initial state, and extracts a final data stack from an outcome. It includes the direct IR version of `5 DUP +` and proves its result.
103
+
104
+ ### 9. [WordDialect.lean](formal/WordDialect.lean) — the public doorway
105
+
106
+ Collects the word-machine modules into one import. Its role is architectural: users and downstream libraries can open the semantic foundation through `import WordDialect`.
107
+
108
+ ### 10. [Audit.lean](formal/Audit.lean) — the proof ledger
109
+
110
+ Imports the project libraries and implements `#audit_wd`. It traverses theorem declarations in the `WordDialect` namespace and collects their axiom dependencies. The command allows Lean's standard `propext`, `Quot.sound`, and `Classical.choice`, reporting other dependencies as errors.
111
+
112
+ ## Station II: the compiler's construction kit — files 11–17
113
+
114
+ The frontends share more than an instruction set. This region packages recurring compiler arguments so expressions, branches, and loops can reuse an established foundation.
115
+
116
+ ### 11. [Straight.lean](word-ir/WordIR/Straight.lean) — an uninterrupted stretch
117
+
118
+ Defines straight-line instructions, code placement with `At`, and sequence execution with `execSeq`. Its theorems turn sequence calculations into machine paths or trapping executions.
119
+
120
+ ### 12. [Frag.lean](word-ir/WordIR/Frag.lean) — track that fits its position
121
+
122
+ Defines position-dependent fragments whose emitted addresses adapt to their placement while their size stays fixed. Sequencing, conditionals, until loops, while loops, and repeat loops come with placement and execution rules.
123
+
124
+ ### 13. [Flag.lean](word-ir/WordIR/Flag.lean) — a shared signal convention
125
+
126
+ Defines source truth flags as all ones for true and zero for false. The IR comparison produces one or zero; `cmpFlagCode` converts that result to the Forth and BCPL convention.
127
+
128
+ ### 14. [RExpr.lean](word-ir/WordIR/RExpr.lean) — expressions with a destination
129
+
130
+ Defines register-and-memory expressions built from constants, registers, arithmetic, and loads. Each expression has an evaluator and an instruction sequence that leaves its result on the data stack.
131
+
132
+ ### 15. [Loop.lean](word-ir/WordIR/Loop.lean) — repeat with an invariant
133
+
134
+ Builds a counted loop using a virtual register, initialization, an unsigned bound test, and an increment. `countedLoop_run` carries a supplied invariant through every iteration to the endpoint.
135
+
136
+ ### 16. [Program.lean](word-ir/WordIR/Program.lean) — arrive at a final outcome
137
+
138
+ Lifts straight-line code into a complete program by appending `halt`. Its theorems connect a successful `execSeq` calculation to a halted execution and a trapping calculation to the same trap.
139
+
140
+ ### 17. [WordIR.lean](word-ir/WordIR.lean) — the workshop entrance
141
+
142
+ Exports the straight-line, fragment, flag, expression, loop, and whole-program tools through one import. This entry file shows the compiler layer's reusable vocabulary at a glance.
143
+
144
+ ## Station III: Forth, from text to execution — files 18–28
145
+
146
+ Forth makes the common machine tangible. Its data-stack operations have direct IR counterparts, while definitions, recursion, variables, return-data operations, and structured loops reveal how the translation grows into a complete source pipeline.
147
+
148
+ ### 18. [Semantics.lean](forth/Forth/Semantics.lean) — Forth's own account
149
+
150
+ Defines operations, blocks, programs, source state, and the `Run` relation. It specifies signed division, remainder, truth flags, memory access, calls, early exit, and `DO … LOOP`. The source return-data stack is separate from call frames.
151
+
152
+ ### 19. [Compile.lean](forth/Forth/Compile.lean) — words become instructions
153
+
154
+ Lowers primitives and blocks into IR fragments. It places the main block before `halt`, then lays out definition bodies followed by `ret`. Definition addresses come from fixed code sizes.
155
+
156
+ ### 20. [OpCorrect.lean](forth/Forth/OpCorrect.lean) — each primitive keeps its meaning
157
+
158
+ Proves that lowering a Forth operation produces straight-line code with the behavior specified by `Op.sem`. Success transfers the resulting source state to the machine; failure preserves the trap.
159
+
160
+ ### 21. [Correct.lean](forth/Forth/Correct.lean) — the complete translation argument
161
+
162
+ Connects source `Run` derivations to compiled machine paths, early returns, and matching traps. `DefsAt` describes dictionary-body placement, including recursive calls. `Program.compile_correct` lifts the argument to an initialized whole program.
163
+
164
+ ### 22. [Eval.lean](forth/Forth/Eval.lean) — a reference you can evaluate
165
+
166
+ Implements a fuel-bounded source interpreter and proves `eval_sound` against `Run`. Concrete evaluation can therefore supply a source-semantics derivation, including for recursive words.
167
+
168
+ ### 23. [Example.lean](forth/Forth/Example.lean) — five becomes ten
169
+
170
+ Takes `5 DUP +` through source semantics, compilation, and machine execution. The tiny example makes the entire frontend argument visible without a large program.
171
+
172
+ ### 24. [Parse.lean](forth/Forth/Parse.lean) — the text ticket office
173
+
174
+ Turns source text into a dictionary, main block, and variable count. It handles case-insensitive words, comments, literals, colon definitions, recursion, early exit, variables, constants, conditionals, and loops.
175
+
176
+ ### 25. [Print.lean](forth/Forth/Print.lean) — a return ticket to text
177
+
178
+ Prints programs using canonical variable and definition names, then proves `parse_print` for well-formed programs. The argument covers both token generation and parsing.
179
+
180
+ ### 26. [ParseProps.lean](forth/Forth/ParseProps.lean) — the parser's promises
181
+
182
+ Proves that successful parsing yields a well-formed program and that printing such a result gives canonical source. Further results cover case behavior, comments, constants, dictionary definitions, and `RECURSE`.
183
+
184
+ ### 27. [TextExample.lean](forth/Forth/TextExample.lean) — the journey in source spelling
185
+
186
+ Provides concrete examples spanning parsing, reference evaluation, compilation, and machine results. Its subjects include recursive sums, variables, constants, remainder, early exits, nested loops, and return-data operations. It also records parser outcomes for selected malformed inputs.
187
+
188
+ ### 28. [Forth.lean](forth/Forth.lean) — the frontend entrance
189
+
190
+ Exports the Forth library through one import, bringing its semantics, compiler, proofs, evaluator, text tools, and examples together. For an application, this is the convenient access point.
191
+
192
+ ## Station IV: BCPL and the memory-centered route — files 29–34
193
+
194
+ BCPL reaches the same machine from a different source perspective. Variables are memory cells, values are words, and expression and statement semantics supply the reference for compilation.
195
+
196
+ ### 29. [Semantics.lean](bcpl/BCPL/Semantics.lean) — expressions and statements in words
197
+
198
+ Defines the supported syntax, operator meanings, expression evaluation, assignment, and statement execution. It includes indirection, address-of, conditionals, and loops. Evaluation order and signed division are explicit, and comparisons use all-ones truth flags.
199
+
200
+ ### 30. [Compile.lean](bcpl/BCPL/Compile.lean) — memory programs enter the interchange
201
+
202
+ Lowers expressions to stack-producing instruction sequences and statements to fragments. Variables use a supplied address mapping, and statements restore the data stack on completion. Arithmetic and assignments use straight-line code; structured control uses the common fragment toolkit.
203
+
204
+ ### 31. [ExprCorrect.lean](bcpl/BCPL/ExprCorrect.lean) — expression results travel intact
205
+
206
+ Proves straight-line properties and correctness for operator and expression lowering, covering successful values and traps. These results establish the evaluator-to-instruction connection needed by statement compilation.
207
+
208
+ ### 32. [Correct.lean](bcpl/BCPL/Correct.lean) — statements reach their promised memory
209
+
210
+ Proves assignment and statement translation correctness. Successful compiled execution reaches the statement endpoint with the source result's memory and an unchanged data stack; trapping source execution leads to the matching machine trap.
211
+
212
+ ### 33. [Example.lean](bcpl/BCPL/Example.lean) — put five in a cell
213
+
214
+ Uses the assignment `x := 2 + 3` to demonstrate the complete BCPL route. Given an addressable variable cell, the reference statement produces the expected memory and the compiled machine program stores five there.
215
+
216
+ ### 34. [BCPL.lean](bcpl/BCPL.lean) — the BCPL doorway
217
+
218
+ Collects the BCPL semantics, translation, correctness results, and example under one import. The entry file marks a clean frontend boundary: the source language has its own account of execution, then connects through compiler proofs to the shared machine.
219
+
220
+ ## Station V: mathematical expressions and matrix cargo — files 35–39
221
+
222
+ The Wolfram-style route connects mathematical integer expressions to fixed-width computation. Matrix multiplication then turns that connection into nested loops over a concrete memory layout.
223
+
224
+ ### 35. [Scalar.lean](wolfram/Wolfram/Scalar.lean) — integers meet fixed-width words
225
+
226
+ Defines scalar expressions for integer literals, symbols, addition, multiplication, subtraction, and negation. Integer evaluation connects to `RExpr` lowering modulo the word width. Compilation and assignment theorems cover successful results and errors.
227
+
228
+ ### 36. [MatrixLayout.lean](wolfram/Wolfram/MatrixLayout.lean) — pack the rows
229
+
230
+ Defines partial dot sums, row-major matrix representation with `MatAt`, and destination-prefix updates with `CWritten`. Its lemmas describe extending the written output while preserving source cells outside that region.
231
+
232
+ ### 37. [MatrixDot.lean](wolfram/Wolfram/MatrixDot.lean) — three loops carry the product
233
+
234
+ Lowers matrix multiplication into row, column, and accumulation loops using four virtual registers. Geometry conditions specify address bounds and separation. Proofs progress from index expressions and accumulation to cells, rows, the full fragment, and `dotProgram_correct`.
235
+
236
+ ### 38. [MatrixExample.lean](wolfram/Wolfram/MatrixExample.lean) — a product you can recognize
237
+
238
+ Instantiates the matrix geometry and memory representation for `[[1,2],[3,4]]` multiplied by `[[5,6],[7,8]]`, whose product is `[[19,22],[43,50]]`. It stores the inputs at word addresses zero and four and the output at eight.
239
+
240
+ ### 39. [Wolfram.lean](wolfram/Wolfram.lean) — the mathematical entrance
241
+
242
+ Exports the scalar and matrix modules, connecting expression lowering, layout vocabulary, multiplication proofs, and the worked example. Start through this import when constructing a mathematical sample.
243
+
244
+ ## Station VI: the shared observation platform — file 40
245
+
246
+ ### 40. [Harness.lean](backend/common/Harness.lean) — compare the arrivals
247
+
248
+ Defines samples, expected Lean outcomes, target result decoding, the backend `Runner` interface, fixed examples, and generated programs. The harness compares trap codes or final data stack, memory, and virtual registers.
249
+
250
+ ## Station VII: the x86-64 express — files 41–62
251
+
252
+ The native route implements the IR with concrete registers, guarded stack regions, and GNU assembly. Its proof structure proceeds from machine semantics through local simulation to whole-program and entry-point results.
253
+
254
+ ### 41. [Isa.lean](backend/x86_64/X86/Isa.lean) — the native vehicle
255
+
256
+ Models the emitted x86-64 instruction subset, registers, condition flags, memory access, and target outcomes. Faults and runtime exits are explicit.
257
+
258
+ ### 42. [Lower.lean](backend/x86_64/X86/Lower.lean) — choose the native track
259
+
260
+ Defines runtime register roles, operand and capacity guards, instruction sizes, offsets, and lowering. The data stack uses `r15`, virtual registers use `r14`, IR memory uses `r13`, entry stack position uses `r12`, and auxiliary data uses `rbp`.
261
+
262
+ ### 43. [Emit.lean](backend/x86_64/X86/Emit.lean) — print the assembly journey
263
+
264
+ Prints lowered instructions in GNU assembler Intel syntax and supplies the runtime prologue and output/exit helpers. Runtime declarations establish memory and stack regions. Successful execution writes a binary state dump; traps and overflow use their exit codes.
265
+
266
+ ### 44. [Rel.lean](backend/x86_64/X86/Rel.lean) — match the two timetables
267
+
268
+ Defines runtime geometry and the relation between IR and native states. It maps stacks, virtual registers, memory, and return addresses into disjoint target regions. Frame lemmas support updates.
269
+
270
+ ### 45. [Run.lean](backend/x86_64/X86/Run.lean) — locate and follow native code
271
+
272
+ Defines native continuing and terminating execution relations, code placement, and theorems locating each lowered IR instruction. It connects offset calculations to the emitted instruction list, including the trailing bad-program-counter stub.
273
+
274
+ ### 46. [Seq.lean](backend/x86_64/X86/Seq.lean) — compose short native stretches
275
+
276
+ Provides straight-line native execution and its connection to native step paths. It also proves the conditional-jump-and-trap guard pattern.
277
+
278
+ ### 47. [Flags.lean](backend/x86_64/X86/Flags.lean) — read the signals correctly
279
+
280
+ Proves that native condition-code interpretation after subtraction matches all ten IR comparison predicates. The argument accounts for zero, sign, carry, and overflow flags.
281
+
282
+ ### 48. [Micro.lean](backend/x86_64/X86/Micro.lean) — the smallest useful moves
283
+
284
+ Establishes local effects of target loads, stores, scratch-register updates, stack-pointer adjustment, and pushing a word. Each lemma works with the state relation.
285
+
286
+ ### 49. [Guard.lean](backend/x86_64/X86/Guard.lean) — check room and operands
287
+
288
+ Proves operand-count guards and data- and return-stack capacity checks. With enough operands or room, the code continues; otherwise it produces the specified underflow or overflow outcome.
289
+
290
+ ### 50. [SimBin.lean](backend/x86_64/X86/SimBin.lean) — one pattern, many operators
291
+
292
+ Defines `MidSpec` and proves a generic binary-operation simulation. The pattern loads two operands, computes in scratch state, stores the result, and removes one stack slot.
293
+
294
+ ### 51. [SimOps.lean](backend/x86_64/X86/SimOps.lean) — instantiate the arithmetic route
295
+
296
+ Applies the generic binary pattern to arithmetic, bitwise operations, shifts, rotations, and comparisons. Its middle specifications show the target computation matches the IR operation.
297
+
298
+ ### 52. [SimStack.lean](backend/x86_64/X86/SimStack.lean) — rearrange the carried words
299
+
300
+ Proves simulations for literals, pointers, complement, duplication, dropping, swapping, copying a lower stack item, and rotation. Local target-step lemmas support the stack updates.
301
+
302
+ ### 53. [SimMem.lean](backend/x86_64/X86/SimMem.lean) — preserve neighboring regions
303
+
304
+ Develops address and frame lemmas for virtual-register and IR-memory access. It establishes the effects of reading and writing those cells while preserving stack and other-region relations.
305
+
306
+ ### 54. [SimMemOps.lean](backend/x86_64/X86/SimMemOps.lean) — perform the checked access
307
+
308
+ Proves the address bounds guard and complete simulations for virtual-register push/pop and memory load/store. Both successful access and bad-address traps are covered, with overflow behavior for a growing data stack.
309
+
310
+ ### 55. [SimDiv.lean](backend/x86_64/X86/SimDiv.lean) — handle division's special junctions
311
+
312
+ Covers unsigned division, signed division, and three-operand selection. The proofs include zero-divisor traps and the signed divisor-minus-one path that avoids native division overflow while preserving modular word semantics.
313
+
314
+ ### 56. [SimCtl.lean](backend/x86_64/X86/SimCtl.lean) — branch, call, and return
315
+
316
+ Proves simulations for jumps, conditional branches, calls, returns, and halt. The native return stack stores lowered return-code indices corresponding to IR addresses. Return underflow and call overflow receive explicit results.
317
+
318
+ ### 57. [SimAux.lean](backend/x86_64/X86/SimAux.lean) — a separate place for saved data
319
+
320
+ Proves auxiliary-stack access, push/pop effects, capacity and empty-stack guards, and the `tor`, `fromr`, and `rfetch` simulations. Its dedicated region and `rbp` pointer preserve saved words across native calls.
321
+
322
+ ### 58. [SimStep.lean](backend/x86_64/X86/SimStep.lean) — every instruction gets a connection
323
+
324
+ Combines the instruction-family results into `sim_exec`, covering each IR instruction and continuing, halted, or trapped outcomes. `Fits` describes required stack headroom, and simulation includes overflow where capacity runs out.
325
+
326
+ ### 59. [Correct.lean](backend/x86_64/X86/Correct.lean) — preserve the complete journey
327
+
328
+ Lifts instruction simulation to whole-program behavior. `lowerProg_correct_or_overflow` allows a matching IR outcome or overflow; `lowerProg_correct` gives the matching outcome when reachable states fit. Geometry, layout agreement, code-address bounds, and valid register indices make the program's target contract explicit.
329
+
330
+ ### 60. [Init.lean](backend/x86_64/X86/Init.lean) — begin at the binary entrance
331
+
332
+ Models the prologue and proves its effect, then relates the resulting native state to `State.init`. `Loader.Holds` gathers the loader's memory, placement, and entry-stack facts.
333
+
334
+ ### 61. [X86.lean](backend/x86_64/X86.lean) — the native library entrance
335
+
336
+ Exports the native model, lowering, emitter, relations, simulation modules, whole-program proofs, and initialization result. This import gives users the complete backend library.
337
+
338
+ ### 62. [Main.lean](backend/x86_64/Main.lean) — take the express for a run
339
+
340
+ Implements `wordc`: emit assembly, assemble and link with `as` and `ld`, execute, and compare against Lean. It supports fixed/generated checks and Forth files, plus auxiliary-stack and overflow samples.
341
+
342
+ ## Station VIII: the WebAssembly line — files 63–81
343
+
344
+ WebAssembly offers structured control, typed operands, and byte memory. This route represents each IR instruction as a function returning the next instruction index, then runs those functions through a dispatch loop.
345
+
346
+ ### 63. [Isa.lean](backend/wasm/Wasm/Isa.lean) — the WebAssembly vehicle
347
+
348
+ Models typed values, globals, byte-addressed linear memory, the emitted instruction subset, function tables, and structured execution. Reads and writes use little-endian bytes; loops and branches obey label discipline. Division and host-exit behavior are explicit.
349
+
350
+ ### 64. [Lower.lean](backend/wasm/Wasm/Lower.lean) — turn jumps into dispatch
351
+
352
+ Lowers each IR instruction to a function returning an `i32` next index. A dispatch loop calls through the function table. Globals track program counter and three stack pointers; memory regions hold registers, IR cells, and stacks.
353
+
354
+ ### 65. [Emit.lean](backend/wasm/Wasm/Emit.lean) — print the module
355
+
356
+ Produces WebAssembly text from lowered instructions and supplies memory declarations, data bytes, globals, imports, and the halt helper. Runtime layout constants determine region placement and capacity.
357
+
358
+ ### 66. [Mem.lean](backend/wasm/Wasm/Mem.lean) — eight bytes make a word
359
+
360
+ Proves that writing eight bytes reconstructs the same 64-bit value on a read at that address. Reads of disjoint cells stay unchanged, and writes preserve memory size.
361
+
362
+ ### 67. [Rel.lean](backend/wasm/Wasm/Rel.lean) — align the two state views
363
+
364
+ Defines runtime geometry and the relation between IR values and WebAssembly globals and memory. Separate views cover the data stack, registers, IR memory, return addresses, and auxiliary words.
365
+
366
+ ### 68. [Seq.lean](backend/wasm/Wasm/Seq.lean) — join target computations
367
+
368
+ Defines straight-line execution with `execL` and derives the target `Run` relation from successful calculations. Sequencing lemmas handle normal completion and abrupt outcomes, with useful conditional cases.
369
+
370
+ ### 69. [Frame.lean](backend/wasm/Wasm/Frame.lean) — update one region at a time
371
+
372
+ Proves how eight-byte writes affect the five state views. Updates to a stack, register, or IR-memory cell preserve the other regions. Further lemmas handle stack-pointer adjustment and pushing or popping return and auxiliary entries.
373
+
374
+ ### 70. [Guard.lean](backend/wasm/Wasm/Guard.lean) — the checked platform edge
375
+
376
+ Proves sequence append behavior, state-relation conveniences, operand guards, and capacity guards. Passing checks preserve the required relation; failing checks produce the appropriate exit.
377
+
378
+ ### 71. [SimAlu.lean](backend/wasm/Wasm/SimAlu.lean) — the arithmetic carriage
379
+
380
+ Provides generic binary-operation simulation and instantiates it for arithmetic, bitwise operations, rotations, and comparisons. It connects two stack-cell loads, a target operation, a result store, and stack adjustment.
381
+
382
+ ### 72. [SimAlu2.lean](backend/wasm/Wasm/SimAlu2.lean) — arithmetic's special connections
383
+
384
+ Handles shifts, division, signed division, and complement. Large shift counts yield zero as required by the IR, while division checks zero explicitly. The signed divisor-minus-one case preserves modular semantics while avoiding WebAssembly's signed-division overflow trap.
385
+
386
+ ### 73. [SimStack.lean](backend/wasm/Wasm/SimStack.lean) — move words through memory
387
+
388
+ Proves stack manipulation, selection, literals, pointers, and virtual-register transfers. General push and core-sequence lemmas support multiple operations, with guards for operand availability and capacity.
389
+
390
+ ### 74. [SimMem.lean](backend/wasm/Wasm/SimMem.lean) — find the right eight bytes
391
+
392
+ Proves IR-address checking, conversion to `mb + 8a`, and simulations of load and store. A valid word address reaches the corresponding bytes; an invalid address exits with bad-address code two.
393
+
394
+ ### 75. [SimCtl.lean](backend/wasm/Wasm/SimCtl.lean) — return the next station index
395
+
396
+ Covers jumps, branches, calls, returns, halt, and the bad-program-counter stub. Continuing instruction functions leave the next IR index for dispatch. Calls and returns update the dedicated return region; empty returns and full call stacks receive defined exits.
397
+
398
+ ### 76. [SimAux.lean](backend/wasm/Wasm/SimAux.lean) — preserve the saved-word connection
399
+
400
+ Proves `tor`, `fromr`, and `rfetch` over the auxiliary region tracked by `$ap`. It covers successful movement and copying, empty-stack code five, and capacity code six.
401
+
402
+ ### 77. [SimExec.lean](backend/wasm/Wasm/SimExec.lean) — one complete dispatch step
403
+
404
+ Combines all instruction-family results into `sim_step`. It defines stack headroom, trap-code mapping, and the relation between one emitted function's execution and one IR outcome.
405
+
406
+ ### 78. [Correct.lean](backend/wasm/Wasm/Correct.lean) — carry the result through the loop
407
+
408
+ Proves dispatch-loop preservation and lifts it to the module's start function. The two main results offer matching IR behavior with overflow allowed, or exact matching behavior under capacity conditions.
409
+
410
+ ### 79. [Init.lean](backend/wasm/Wasm/Init.lean) — start from an instantiated module
411
+
412
+ Constructs initial globals and zeroed memory with the emitted data image, proves runtime geometry and the initial state relation, and establishes `module_correct`. Program-layout fit and register and instruction bounds remain explicit.
413
+
414
+ ### 80. [Wasm.lean](backend/wasm/Wasm.lean) — the WebAssembly library entrance
415
+
416
+ Exports the target model, lowering, emitter, memory facts, relations, framing, guards, instruction simulations, whole-program correctness, and initialization. One import provides the full WebAssembly backend.
417
+
418
+ ### 81. [WasmMain.lean](backend/wasm/WasmMain.lean) — ride the line in Wasmtime
419
+
420
+ Implements `wasmw`: emit WAT, run it through Wasmtime, and compare target output with Lean. It supports sample checks, generated programs, source Forth execution, and source emission.
421
+
422
+ ## Make your first journey
423
+
424
+ Install Git and [elan](https://github.com/leanprover/elan), then make `lean` and `lake` available on your path. The repository pins `leanprover/lean4:v4.34.1`; elan selects that toolchain. The Lake package is named `UniversalWord`, and its manifest lists no external package dependencies.
425
+
426
+ ```sh
427
+ git clone https://github.com/SNAPKITTYWEST/ai-free.git
428
+ cd ai-free
429
+ lake build
430
+ ```
431
+
432
+ The default build includes the semantic and frontend libraries, shared harness, both backend libraries, and the `wordc` and `wasmw` executables. Native execution uses x86-64 Linux with GNU `as` and `ld`. WebAssembly execution uses [Wasmtime](https://github.com/bytecodealliance/wasmtime/releases); the workflow selects version `30.0.2`.
433
+
434
+ Try a short source file containing `5 DUP +`, or use the repository's [Forth examples](examples/forth). The commands below exercise real source parsing and compilation before comparing target behavior with Lean:
435
+
436
+ ```sh
437
+ .lake/build/bin/wordc forth examples/forth/fib.fs
438
+ .lake/build/bin/wasmw forth examples/forth/memory.fs 16
439
+ .lake/build/bin/wordc emit-forth examples/forth/sum.fs > /tmp/sum.s
440
+ .lake/build/bin/wasmw emit-forth examples/forth/sum.fs > /tmp/sum.wat
441
+ ```
442
+
443
+ The optional memory-word count defaults to sixteen. Variables occupy cells starting at zero; the executable frontend arranges memory for the parsed program. Forth definitions can call themselves, and `RECURSE` names the current definition. `DO … LOOP`, `I`, `J`, and return-data words provide especially interesting examples because their data survives calls through the auxiliary stack.
444
+
445
+ For a broader trip, run the fixed samples and three hundred generated programs on each destination:
446
+
447
+ ```sh
448
+ .lake/build/bin/wordc check 300
449
+ .lake/build/bin/wasmw check 300
450
+ ```
451
+
452
+ With no count, the generated-program count defaults to two hundred. `check 0` runs the fixed checks without generated programs. The shared reference interpreter uses bounded fuel; programs that exhaust it are reported as skipped. Generation uses a fixed seed, helping make comparisons reproducible. Backend checks additionally exercise auxiliary-stack behavior and intentional overflow.
453
+
454
+ You can also inspect a built-in sample's output without running the target:
455
+
456
+ ```sh
457
+ .lake/build/bin/wordc emit forth_five_dup_plus > /tmp/five.s
458
+ .lake/build/bin/wasmw emit forth_five_dup_plus > /tmp/five.wat
459
+ ```
460
+
461
+ Other named samples include `bcpl_sum_1_to_10`, `wolfram_dot_2x2`, and `raw_call_ret`. The shared harness contains the complete sample lists. Native execution artifacts are written under `/tmp/wordc`; WebAssembly artifacts use `/tmp/wasmw`. Successful target output is a binary state dump decoded by the harness, so these commands are most informative when used with the comparison tools or emitter output.
462
+
463
+ ## Read the proofs as a connected story
464
+
465
+ A source correctness theorem begins with a source execution result. The compiler translates that program into the shared IR, and its theorem establishes the corresponding machine execution. A backend theorem then connects that machine execution to a target model. Initialization results establish the relation at the target's starting state. The examples make these connections concrete with values and memory images you can recognize.
466
+
467
+ When exploring a theorem, start with its conclusion and then inspect its hypotheses. A matrix theorem's geometry explains where input and output cells live. A backend theorem's register condition explains which virtual-register indices the configured file supports. A capacity condition explains when a bounded target follows the IR outcome exactly. These hypotheses are part of the module's usable interface and often reveal the clearest path to constructing your own example.
468
+
469
+ For a small complete reading route, follow Forth's example into its compiler and correctness file, then open the shared harness and one executable entry point. For a deeper route, begin with the machine, read straight-line and fragment composition, and follow BCPL statement correctness. For nested-loop reasoning, take the counted-loop module into matrix layout, matrix multiplication, and its concrete example.
470
+
471
+ The two backend routes offer an illuminating comparison. Native lowering computes target offsets and uses native calls. WebAssembly lowering returns IR instruction indices through a table-driven dispatch loop. Both preserve the same source of meaning, use separate storage for call addresses and auxiliary words, and connect local instruction simulations to complete execution results. Their different implementations make the common semantic layer especially valuable to readers.
472
+
473
+ A useful exercise is to trace one value across the map. In `5 DUP +`, parsing produces a literal and two operations; reference evaluation duplicates five and adds the pair; compilation emits the corresponding IR sequence; either backend carries that sequence to its target representation. The final stack contains ten. For BCPL, trace the same arithmetic into a variable cell instead. For matrix multiplication, trace one output entry through its accumulator register, the inner loop, and the destination write. These three examples give you stack, memory, and loop perspectives on the same architecture. Once those routes feel familiar, the larger theorem files become easier to navigate: identify the local semantic result, find its compiled fragment, locate the appropriate simulation, and follow the composition to the final outcome. Each step has a named home in this atlas for your next exploration.
474
+
475
+ ## The project's verification practice
476
+
477
+ The repository includes a namespace-wide axiom audit and a [CI workflow](.github/workflows/lean.yml) that builds the libraries, checks prohibited proof constructs, runs the audit, exercises both executable backends, and runs the Forth example files. The audit command is:
478
+
479
+ ```sh
480
+ lake env lean formal/Audit.lean
481
+ ```
482
+
483
+ Formal results describe the machine models and their stated interfaces. Differential execution compares emitted artifacts through real assemblers and runtimes with the Lean reference. Together, these give readers a theorem route through the definitions and an execution route through concrete programs. The audit makes theorem dependencies inspectable across the imported libraries.
484
+
485
+ When adding an operation, a useful path is to define its semantic behavior, connect its translation with an appropriate correctness result, and add a shared sample. An existing example provides a manageable starting point. For source-interface work, parser and printer properties show how text behavior can receive the same attention as machine behavior. For backend work, the instruction-family files indicate where a new local simulation joins the complete step theorem.
486
+
487
+ After a code change, use the project's build, audit, and backend checks as the repository workflow prescribes. For navigation and documentation, this atlas lets you move directly to the relevant source file and follow its imports. Each numbered stop identifies a real module's contribution, from the first word representation to the two executable destinations.
488
+
489
+ ## License
490
+
491
+ The project uses the [Parr Source License — No AI Training, Version 1.0](License), copyright © 2026 Ahmad Ali Parr. The license text defines permitted purposes and its conditions, including restrictions concerning competing use and AI training. Refer to that file for the governing terms when using, modifying, or distributing the project.
492
+
backend/common/Harness.lean ADDED
@@ -0,0 +1,307 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import WordDialect
2
+ import WordIR
3
+ import Forth
4
+ import BCPL
5
+ import Wolfram
6
+
7
+ /-!
8
+ # Harness
9
+
10
+ Shared differential-testing harness for every backend. A backend supplies a `Runner`: it takes a
11
+ `Sample` (an IR program plus initial memory and register-file size), runs it on the real target,
12
+ and reports either the dumped final state or the exit code of a trap. The harness runs the same IR
13
+ program under the Lean semantics (`WordDialect.run`) and compares.
14
+
15
+ Trap exit codes are common to all backends: 1 stackUnderflow, 2 badAddress, 3 divideByZero,
16
+ 4 badPc, 5 returnUnderflow; 6 is a target stack overflow, which has no IR counterpart. On halt a backend writes, as raw little-endian 64-bit words, the data
17
+ stack depth, the data stack (top first), the IR memory and the virtual registers.
18
+ -/
19
+
20
+ open WordDialect
21
+
22
+ structure Sample where
23
+ name : String
24
+ prog : Prog 64
25
+ mem : List Nat
26
+ nregs : Nat := 4
27
+ /-- Set when the sample could not be built (e.g. Forth source failed to parse); `check`
28
+ then reports an error instead of running anything. -/
29
+ buildError : Option String := none
30
+
31
+ def mkMem (img : List Nat) : Memory 64 := Memory.ofImage img
32
+
33
+ inductive Result where
34
+ | halted (stack mem regs : List Nat)
35
+ | trap (code : Nat)
36
+ | timeout
37
+ deriving BEq, Repr
38
+
39
+ /-- Expected result under the Lean semantics. -/
40
+ def trapCode : Trap → Nat
41
+ | .stackUnderflow => 1
42
+ | .badAddress => 2
43
+ | .divideByZero => 3
44
+ | .badPc => 4
45
+ | .returnUnderflow => 5
46
+
47
+ def expected (s : Sample) : Result :=
48
+ match run s.prog 200000 (State.init (mkMem s.mem)) with
49
+ | none => .timeout
50
+ | some (.trapped t) => .trap (trapCode t)
51
+ | some (.next _) => .timeout
52
+ | some (.halted st) =>
53
+ .halted (st.dstack.map (·.toNat))
54
+ ((List.range s.mem.length).map fun a => (st.mem.cell (BitVec.ofNat 64 a)).toNat)
55
+ ((List.range s.nregs).map fun r => (st.regs r).toNat)
56
+
57
+ def u64 (b : ByteArray) (off : Nat) : Nat :=
58
+ (List.range 8).foldl (fun acc i => acc + (b.get! (off + i)).toNat * 2 ^ (8 * i)) 0
59
+
60
+ partial def readAll (h : IO.FS.Handle) (acc : ByteArray) : IO ByteArray := do
61
+ let chunk ← h.read 65536
62
+ if chunk.isEmpty then pure acc else readAll h (acc ++ chunk)
63
+
64
+ def parseDump (s : Sample) (b : ByteArray) : Option Result :=
65
+ if b.size < 8 then none else
66
+ let depth := u64 b 0
67
+ let m := s.mem.length
68
+ if b.size != 8 + 8 * depth + 8 * m + 8 * s.nregs then none else
69
+ some (.halted ((List.range depth).map fun i => u64 b (8 + 8 * i))
70
+ ((List.range m).map fun i => u64 b (8 + 8 * depth + 8 * i))
71
+ ((List.range s.nregs).map fun i => u64 b (8 + 8 * depth + 8 * m + 8 * i)))
72
+
73
+ /-- A backend: run a sample on the real target. -/
74
+ abbrev Runner := Sample → IO (Except String Result)
75
+
76
+ def check (run : Runner) (s : Sample) : IO Bool := do
77
+ if let some e := s.buildError then
78
+ IO.println s!"ERROR {s.name}: {e}"; return false
79
+ let exp := expected s
80
+ match exp with
81
+ | .timeout => IO.println s!"SKIP {s.name} (IR did not terminate within fuel)"; return true
82
+ | _ =>
83
+ match ← run s with
84
+ | .error e => IO.println s!"ERROR {s.name}: {e}"; return false
85
+ | .ok got =>
86
+ if got == exp then
87
+ IO.println s!"PASS {s.name} {repr exp}"; return true
88
+ else
89
+ IO.println s!"FAIL {s.name}\n lean: {repr exp}\n target: {repr got}"; return false
90
+
91
+ /-! ## Programs from the frontends -/
92
+
93
+ def forthSample (name : String) (b : Forth.Block) (mem : List Nat := List.replicate 16 0) : Sample :=
94
+ { name, prog := Forth.compileProgram b, mem }
95
+
96
+ /-- A sample written as Forth source text. A parse failure becomes a reported error. The memory
97
+ image is extended with zero cells if the program's `VARIABLE`s (cells `0 … vars - 1`) need more. -/
98
+ def forthTextSample (name : String) (src : String) (mem : List Nat := List.replicate 16 0) :
99
+ Sample :=
100
+ match Forth.parse src with
101
+ | .ok P => { name, prog := P.compile, mem := mem ++ List.replicate (P.vars - mem.length) 0 }
102
+ | .error e => { name, prog := [], mem, buildError := some s!"Forth parse error: {e}" }
103
+
104
+ open Forth in
105
+ def forthOps (ops : List Op) : Block := ops.foldr (fun o b => .op o b) .nil
106
+
107
+ def bcplAddr : BCPL.Var → Word 64 := fun x => BitVec.ofNat 64 x
108
+
109
+ def samplesFrontends : List Sample :=
110
+ open Forth in
111
+ [ forthSample "forth_five_dup_plus" Forth.fiveDupPlus
112
+ , forthSample "forth_countdown"
113
+ (.op (.lit 10) (.untilL (forthOps [.lit 1, .sub, .dup, .lit 0, .eq]) .nil))
114
+ , forthSample "forth_if_else" (.op (.lit 3) (.op (.lit 5) (.op .lt (.ite (.op (.lit 111) .nil) (.op (.lit 222) .nil) .nil))))
115
+ , forthSample "forth_store_fetch" (forthOps [.lit 42, .lit 7, .store, .lit 7, .fetch])
116
+ , forthSample "forth_sdiv_trunc" (forthOps [.lit (-7), .lit 2, .div])
117
+ , forthSample "forth_sdiv_intmin" (forthOps [.lit (-9223372036854775808), .lit (-1), .div])
118
+ , forthSample "forth_shifts" (forthOps [.lit 1, .lit 64, .lshift, .lit (-1), .lit 70, .rshift,
119
+ .lit 255, .lit 3, .rshift, .lit 1, .lit 63, .lshift])
120
+ , forthSample "forth_compare_signed" (forthOps [.lit (-1), .lit 1, .lt, .lit (-1), .lit 1, .ult,
121
+ .lit 5, .lit 5, .eq, .lit 5, .lit 6, .ne])
122
+ , forthSample "forth_stack_words" (forthOps [.lit 1, .lit 2, .lit 3, .rot, .over, .swap, .dup,
123
+ .drop])
124
+ , forthSample "forth_trap_div0" (forthOps [.lit 1, .lit 0, .div])
125
+ , forthSample "forth_trap_underflow" (forthOps [.add])
126
+ , forthSample "forth_trap_badaddr" (forthOps [.lit 1000, .fetch])
127
+ , forthTextSample "forth_text_sum_recursive" Forth.sumSrc
128
+ , forthTextSample "forth_text_fib_recursive"
129
+ ": FIB ( n -- fib[n] ) DUP 2 < IF ELSE DUP 1 - FIB SWAP 2 - FIB + THEN ;\n15 FIB"
130
+ , forthTextSample "forth_text_gcd_words"
131
+ ": MOD ( a b -- a mod b ) OVER OVER / * - ;\n\
132
+ : GCD ( a b -- g ) BEGIN SWAP OVER MOD DUP 0 = UNTIL DROP ;\n\
133
+ 1071 462 GCD 3 ! 3 @ \\ store the result at address 3, read it back"
134
+ , forthTextSample "forth_text_trap_in_word" ": INNER 1 0 / ; : OUTER 7 INNER 8 ; 5 OUTER"
135
+ , forthTextSample "forth_text_mod_zeq"
136
+ "-7 2 MOD 7 -2 MOD -7 -2 MOD 9223372036854775807 10 MOD\n\
137
+ -9223372036854775808 -1 MOD 0 0= 5 0= -1 0="
138
+ , forthTextSample "forth_text_trap_mod0" "5 1 0 MOD"
139
+ , forthTextSample "forth_text_variables"
140
+ "VARIABLE N VARIABLE ACC 100 CONSTANT LIMIT\n\
141
+ : STEP ( -- ) ACC @ N @ + ACC ! N @ 1 + N ! ;\n\
142
+ 0 N ! 0 ACC ! BEGIN STEP N @ LIMIT > UNTIL ACC @ N @"
143
+ , forthTextSample "forth_text_many_variables"
144
+ "VARIABLE A0 VARIABLE A1 VARIABLE A2 VARIABLE A3 VARIABLE A4 VARIABLE A5 VARIABLE A6\n\
145
+ VARIABLE A7 VARIABLE A8 VARIABLE A9 VARIABLE A10 VARIABLE A11 VARIABLE A12 VARIABLE A13\n\
146
+ VARIABLE A14 VARIABLE A15 VARIABLE A16 VARIABLE A17 VARIABLE A18 VARIABLE A19\n\
147
+ 77 A19 ! -5 A0 ! A19 @ A0 @ + A19"
148
+ , forthTextSample "forth_text_exit_recursive"
149
+ ": GCD ( a b -- g ) DUP 0= IF DROP EXIT THEN SWAP OVER MOD RECURSE ;\n\
150
+ : SUM ( n -- 0+...+n ) DUP 0= IF EXIT THEN DUP 1 - RECURSE + ;\n\
151
+ 1071 462 GCD 20 SUM"
152
+ , forthTextSample "forth_text_exit_dead_code" ": F 1 EXIT 2 3 ; : G F F + EXIT F ; G"
153
+ , forthTextSample "forth_text_do_loop_sum" "0 10 0 DO I + LOOP 0 1 0 DO I 7 + + LOOP"
154
+ , forthTextSample "forth_text_nested_loops" "0 3 0 DO 4 0 DO I J * + LOOP LOOP"
155
+ , forthTextSample "forth_text_rstack_across_call"
156
+ ": BUMP ( R: x -- x+1 ) R> 1 + >R ; : PEEK R@ ; 5 >R BUMP BUMP PEEK R>"
157
+ , forthTextSample "forth_text_rstack_recursive"
158
+ ": SUMR ( n -- 0+...+n ) DUP 0= IF EXIT THEN DUP >R 1 - RECURSE R> + ;\n10 SUMR"
159
+ , forthTextSample "forth_text_trap_fromr_empty" "1 2 R>"
160
+ , forthTextSample "forth_text_trap_rfetch_empty" "1 >R R> R@"
161
+ , forthTextSample "forth_text_loop_calls_loop"
162
+ ": TRI ( n -- 0+...+n-1 ) 0 SWAP 0 DO I + LOOP ;\n0 6 1 DO I TRI + LOOP 4 TRI"
163
+ , forthTextSample "forth_text_loop_exit"
164
+ ": FIND3 10 0 DO I 3 = IF I EXIT THEN LOOP 99 ; FIND3 R> R>"
165
+ , forthTextSample "forth_text_loop_recursive"
166
+ ": F ( acc n -- acc' ) DUP IF DUP 0 DO SWAP I + SWAP LOOP 1 - RECURSE ELSE DROP THEN ;\n\
167
+ 0 3 F"
168
+ , forthTextSample "forth_text_trap_do_underflow" "1 DO LOOP"
169
+ , forthTextSample "forth_text_trap_loop_rdrop" "3 0 DO R> DROP LOOP"
170
+ , forthTextSample "forth_text_trap_j_outside" "1 >R 2 >R J"
171
+ , { name := "bcpl_sum_1_to_10", mem := List.replicate 4 0,
172
+ prog := BCPL.compileProgram bcplAddr
173
+ (.seq (.assign (.var 1) (.num 10)) (.seq (.assign (.var 0) (.num 0))
174
+ (.while (.bin .ne (.var 1) (.num 0))
175
+ (.seq (.assign (.var 0) (.bin .add (.var 0) (.var 1)))
176
+ (.assign (.var 1) (.bin .sub (.var 1) (.num 1))))))) }
177
+ , { name := "bcpl_indirect", mem := List.replicate 8 0,
178
+ prog := BCPL.compileProgram bcplAddr
179
+ (.seq (.assign (.var 0) (.addrOf 5))
180
+ (.seq (.assign (.rv (.var 0)) (.num 77))
181
+ (.assign (.var 1) (.bin .add (.rv (.var 0)) (.num 1))))) }
182
+ , { name := "wolfram_dot_2x2", nregs := 4,
183
+ mem := [1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0],
184
+ prog := (Wolfram.dotFrag 2 2 2 0 4 8 : IR.Frag 64).emit 0 ++ [.halt] }
185
+ , { name := "wolfram_dot_3x2_2x4", nregs := 4,
186
+ mem := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
187
+ prog := (Wolfram.dotFrag 3 2 4 0 6 14 : IR.Frag 64).emit 0 ++ [.halt] }
188
+ ]
189
+
190
+ /-! ## Hand-written IR covering control flow and calls -/
191
+
192
+ def samplesRaw : List Sample :=
193
+ [ { name := "raw_call_ret", mem := [], nregs := 1,
194
+ prog := [.word 5, .call 4, .call 4, .halt, .dup, .add, .ret] }
195
+ , { name := "raw_trap_ret_empty", mem := [], nregs := 1, prog := [.ret] }
196
+ , { name := "raw_trap_badpc", mem := [], nregs := 1, prog := [.word 1, .jmp 100] }
197
+ , { name := "raw_rotates", mem := [], nregs := 1,
198
+ prog := [.word 0x8000000000000001, .word 1, .rotl, .word 0x8000000000000001, .word 1, .rotr,
199
+ .word 3, .word 130, .rotl, .halt] }
200
+ , { name := "raw_select_regs", mem := [], nregs := 3,
201
+ prog := [.word 9, .pop 2, .word 11, .word 22, .word 0, .select, .push 2, .word 11,
202
+ .word 22, .word 1, .select, .halt] }
203
+ ]
204
+
205
+ /-! ## Randomised differential testing -/
206
+
207
+ def edge : Array Nat :=
208
+ #[0, 1, 2, 3, 7, 63, 64, 65, 127, 128, 255, 2 ^ 63 - 1, 2 ^ 63, 2 ^ 63 + 1, 2 ^ 64 - 1,
209
+ 2 ^ 64 - 2, 0xdeadbeef, 0x123456789abcdef0]
210
+
211
+ def randWord : IO Nat := do
212
+ let k ← IO.rand 0 3
213
+ if k = 0 then
214
+ let hi ← IO.rand 0 (2 ^ 32 - 1)
215
+ let lo ← IO.rand 0 (2 ^ 32 - 1)
216
+ pure (hi * 2 ^ 32 + lo)
217
+ else
218
+ let i ← IO.rand 0 (edge.size - 1)
219
+ pure edge[i]!
220
+
221
+ def conds : Array Cond := #[.eq, .ne, .ult, .ule, .ugt, .uge, .slt, .sle, .sgt, .sge]
222
+
223
+ def genInstr (depth : Nat) : IO (List (Instr 64) × Int) := do
224
+ let r ← IO.rand 0 99
225
+ if depth < 2 && r >= 6 then
226
+ let k ← IO.rand 0 2
227
+ if k = 0 then do let w ← randWord; return ([.word (BitVec.ofNat 64 w)], 1)
228
+ else if k = 1 then do let q ← IO.rand 0 3; return ([.push q], 1)
229
+ else do let w ← randWord; return ([.word (BitVec.ofNat 64 w)], 1)
230
+ else
231
+ let k ← IO.rand 0 30
232
+ match k with
233
+ | 0 => do let w ← randWord; return ([.word (BitVec.ofNat 64 w)], 1)
234
+ | 1 => return ([.add], -1) | 2 => return ([.sub], -1) | 3 => return ([.mul], -1)
235
+ | 4 => return ([.and], -1) | 5 => return ([.or], -1) | 6 => return ([.xor], -1)
236
+ | 7 => return ([.not], 0)
237
+ | 8 => return ([.shl], -1) | 9 => return ([.shr], -1)
238
+ | 10 => return ([.rotl], -1) | 11 => return ([.rotr], -1)
239
+ | 12 => return ([.div], -1) | 13 => return ([.sdiv], -1)
240
+ | 14 => do let c ← IO.rand 0 9; return ([.cmp (conds[c]?.getD .eq)], -1)
241
+ | 15 => return ([.select], -2)
242
+ | 16 => return ([.dup], 1) | 17 => return ([.drop], -1) | 18 => return ([.swap], 0)
243
+ | 19 => return ([.over], 1) | 20 => return ([.rot], 0)
244
+ | 21 => do let q ← IO.rand 0 3; return ([.push q], 1)
245
+ | 22 => do let q ← IO.rand 0 3; return ([.pop q], -1)
246
+ | 23 => do let v ← randWord; let a ← IO.rand 0 17
247
+ return ([.word (BitVec.ofNat 64 v), .word (BitVec.ofNat 64 a), .store], 0)
248
+ | 24 => do let a ← IO.rand 0 17; return ([.word (BitVec.ofNat 64 a), .load], 1)
249
+ | 25 => do let v ← randWord; return ([.word (BitVec.ofNat 64 v), .word 0, .select], 0)
250
+ | 26 => return ([.tor], -1) | 27 => return ([.fromr], 1) | 28 => return ([.rfetch], 1)
251
+ | _ => do let w ← randWord; return ([.word (BitVec.ofNat 64 w)], 1)
252
+
253
+ def genProg (len : Nat) : IO (List (Instr 64)) := do
254
+ let mut code : List (Instr 64) := []
255
+ let mut depth : Int := 0
256
+ for _ in List.range len do
257
+ let (is, d) ← genInstr depth.toNat
258
+ code := code ++ is
259
+ depth := max 0 (depth + d)
260
+ -- a few forward jumps / conditional branches
261
+ let n := code.length
262
+ for _ in List.range 2 do
263
+ let k ← IO.rand 0 (n - 1)
264
+ let skip ← IO.rand 1 4
265
+ let kind ← IO.rand 0 1
266
+ if kind = 0 then
267
+ code := code.set k (.jmp (min n (k + 1 + skip)))
268
+ else
269
+ code := code.set k (.branch (min n (k + 1 + skip)))
270
+ return code ++ [.halt]
271
+
272
+ def fuzz (run : Runner) (count : Nat) : IO Bool := do
273
+ let mut ok := true
274
+ let mut skipped := 0
275
+ let mut halts := 0
276
+ let mut traps : Array Nat := #[0, 0, 0, 0, 0, 0]
277
+ let mut longest := 0
278
+ for i in List.range count do
279
+ let len ← IO.rand 4 40
280
+ let prog ← genProg len
281
+ let mem ← (List.range 16).mapM fun _ => randWord
282
+ let s : Sample := { name := s!"fuzz_{i}", prog, mem, nregs := 4 }
283
+ let exp := expected s
284
+ match exp with
285
+ | .halted st _ _ => halts := halts + 1; longest := max longest st.length
286
+ | .trap c => traps := traps.set! c (traps[c]! + 1)
287
+ | .timeout => pure ()
288
+ if exp == .timeout then skipped := skipped + 1 else
289
+ match ← run s with
290
+ | .error e => IO.println s!"ERROR fuzz_{i}: {e}"; ok := false
291
+ | .ok got =>
292
+ if got != exp then
293
+ IO.println s!"FAIL fuzz_{i}\n lean: {repr exp}\n target: {repr got}"
294
+ ok := false
295
+ IO.println s!"fuzz: {count} programs, {skipped} skipped; halted {halts}; traps by code (1 underflow, 2 badaddr, 3 div0, 4 badpc, 5 ret) {traps.toList.drop 1}; max final depth {longest}; {if ok then "all agree" else "MISMATCH"}"
296
+ return ok
297
+
298
+ def allSamples : List Sample := samplesFrontends ++ samplesRaw
299
+
300
+ /-- Run every sample and `count` random programs against a backend. -/
301
+ def checkAll (run : Runner) (count : Nat) : IO Bool := do
302
+ IO.setRandSeed 20260610
303
+ let mut ok := true
304
+ for s in allSamples do
305
+ if !(← check run s) then ok := false
306
+ if !(← fuzz run count) then ok := false
307
+ return ok
backend/wasm/Wasm.lean ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.Isa
2
+ import Wasm.Lower
3
+ import Wasm.Emit
4
+ import Wasm.Mem
5
+ import Wasm.Rel
6
+ import Wasm.Seq
7
+ import Wasm.Frame
8
+ import Wasm.Guard
9
+ import Wasm.SimAlu
10
+ import Wasm.SimAlu2
11
+ import Wasm.SimStack
12
+ import Wasm.SimMem
13
+ import Wasm.SimCtl
14
+ import Wasm.SimAux
15
+ import Wasm.SimExec
16
+ import Wasm.Correct
17
+ import Wasm.Init
backend/wasm/Wasm/Correct.lean ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.SimExec
2
+
3
+ /-!
4
+ # Wasm.Correct
5
+
6
+ Whole-program preservation for the WebAssembly backend. The module's start function is the
7
+ dispatch loop `loop { pc := call_indirect table[pc] ; br 0 }` over the function table
8
+ `funcsOf L p`. Running it from a state related to the IR state `s` reaches the outcome the IR
9
+ semantics reaches from `s`:
10
+
11
+ * IR `halted s'` ⇒ the loop halts (`exitHalt`) in a state `w'` with `Rel0 c s' w'`, so the data
12
+ stack, virtual registers, IR memory and return stack agree with `s'`;
13
+ * IR `trapped t` ⇒ the loop exits with code `trapCode t`.
14
+
15
+ Every instruction that grows a stack checks for room and exits with code 6 when the stack is
16
+ full, so there are two forms of the theorem:
17
+
18
+ * `lowerProg_correct_or_overflow` has no capacity hypothesis: the module reaches the IR outcome as
19
+ above, or it exits with code 6 (the run needed more stack than the target provides);
20
+ * `lowerProg_correct` additionally assumes the IR run stays within the capacities (`Fits` at every
21
+ reachable state); then no guard fires and the module reaches exactly the IR outcome.
22
+
23
+ Hypotheses of both (all explicit):
24
+ * `Geom c` the memory regions are disjoint and inside the 32-bit linear memory, and each
25
+ stack can hold at least one word;
26
+ * `hn` the program has fewer than `2^32` instructions (function indices are `i32`);
27
+ * `RegsOk` every `push r`/`pop r` names a register inside the register file.
28
+ -/
29
+
30
+ namespace WordDialect
31
+ namespace Wasm
32
+
33
+ /-- Every register index used by the program lies in the virtual register file. -/
34
+ def RegsOk (c : Cfg) (p : Prog 64) : Prop :=
35
+ ∀ (k : Nat) (i : WordDialect.Instr 64), p[k]? = some i →
36
+ ∀ r : Nat, (i = .push r ∨ i = .pop r) → r < c.nregs
37
+
38
+ def loopBody : List WI := [.globalGet pcG, .callIndirect, .globalSet pcG, .br 0]
39
+
40
+ theorem mainBody_eq : mainBody = [.loop loopBody] := rfl
41
+
42
+ /-- The relation at the top of the dispatch loop: operand stack empty, the `pc` global holding the
43
+ IR `pc` clamped to `n` (a jump past the end is clamped at lowering time), and every return index
44
+ within the program. -/
45
+ def RelD (c : Cfg) (n : Nat) (s : State 64) (w : WState) : Prop :=
46
+ Rel0 c s w ∧ w.stack = [] ∧ w.globals pcG = .i32 (ofN (min s.pc n)) ∧ ∀ a ∈ s.rstack, a ≤ n
47
+
48
+ /-- One IR step, seen from the top of the dispatch loop. -/
49
+ def LoopSim (c : Cfg) (fs : Funcs) (n : Nat) (w : WState) : Outcome 64 → Prop
50
+ | .next s' => ∃ w', RelD c n s' w' ∧
51
+ ∀ r, RunI fs (.loop loopBody) w' r → RunI fs (.loop loopBody) w r
52
+ | .halted s' => ∃ w', RunI fs (.loop loopBody) w (.halted w') ∧ Rel0 c s' w'
53
+ | .trapped t => RunI fs (.loop loopBody) w (.exit (trapCode t))
54
+
55
+ /-- The final-outcome form, for the loop. -/
56
+ def Final (c : Cfg) (fs : Funcs) (w : WState) : Outcome 64 → Prop
57
+ | .next _ => False
58
+ | .halted s' => ∃ w', RunI fs (.loop loopBody) w (.halted w') ∧ Rel0 c s' w'
59
+ | .trapped t => RunI fs (.loop loopBody) w (.exit (trapCode t))
60
+
61
+ /-- Exit code 6: a stack overflow guard fired. -/
62
+ def overflowCode : Nat := 6
63
+
64
+ /-- The final-outcome form, for the module's start function. -/
65
+ def FinalMain (c : Cfg) (fs : Funcs) (w : WState) : Outcome 64 → Prop
66
+ | .next _ => False
67
+ | .halted s' => ∃ w', Run fs mainBody w (.halted w') ∧ Rel0 c s' w'
68
+ | .trapped t => Run fs mainBody w (.exit (trapCode t))
69
+
70
+ section Loop
71
+
72
+ variable {c : Cfg} {fs : Funcs} {w : WState}
73
+
74
+ theorem funcs_at (L : Layout) (p : Prog 64) {j : Nat} (hj : j ≤ p.length) :
75
+ (funcsOf L p)[j]? = some (instrCode L p j) := by
76
+ simp [funcsOf, List.getElem?_range (show j < p.length + 1 by omega)]
77
+
78
+ theorem rgetPc {m : Nat} (hpc : w.globals pcG = .i32 (ofN m)) :
79
+ RunI fs (.globalGet pcG) w (.normal { w with stack := .i32 (ofN m) :: w.stack }) :=
80
+ .simple rfl (by simp [stepI, hpc])
81
+
82
+ /-- The dispatched function returned the next index `v`: one loop iteration, then the loop again
83
+ from the updated state. -/
84
+ theorem loop_next {m : Nat} {code : List WI} (hst : w.stack = [])
85
+ (hpc : w.globals pcG = .i32 (ofN m)) (hf : fs[(ofN m).toNat]? = some code) {w' : WState} {v : W32}
86
+ (hrun : Run fs code { w with stack := [] } (.normal w')) (hst' : w'.stack = [.i32 v]) {r : Res}
87
+ (hr : RunI fs (.loop loopBody)
88
+ { w' with stack := [], globals := setG w'.globals pcG (.i32 v) } r) :
89
+ RunI fs (.loop loopBody) w r := by
90
+ have h2 : RunI fs .callIndirect { w with stack := .i32 (ofN m) :: w.stack }
91
+ (.normal { w' with stack := [.i32 v] }) := by
92
+ have := RunI.callNormal (fs := fs) (s := { w with stack := .i32 (ofN m) :: w.stack }) (r := [])
93
+ (by simp [hst]) hf hrun hst'
94
+ exact this
95
+ have h3 : RunI fs (.globalSet pcG) { w' with stack := [.i32 v] }
96
+ (.normal { w' with stack := [], globals := setG w'.globals pcG (.i32 v) }) :=
97
+ .simple rfl rfl
98
+ have hb : Run fs loopBody w (.br 0 { w' with stack := [], globals := setG w'.globals pcG (.i32 v) }) :=
99
+ .consNormal (rgetPc hpc) (.consNormal h2 (.consNormal h3 (.consAbrupt .br trivial)))
100
+ refine .loopBr0 hb ?_
101
+ rw [hst]
102
+ exact hr
103
+
104
+ /-- The dispatched function exited or halted: so does the loop. -/
105
+ theorem loop_abrupt {m : Nat} {code : List WI} (hpc : w.globals pcG = .i32 (ofN m))
106
+ (hf : fs[(ofN m).toNat]? = some code) {res : Res}
107
+ (hrun : Run fs code { w with stack := [] } res) (hres : (∃ k, res = .exit k) ∨ (∃ w', res = .halted w')) :
108
+ RunI fs (.loop loopBody) w res := by
109
+ have h2 := RunI.callAbrupt (fs := fs) (s := { w with stack := .i32 (ofN m) :: w.stack })
110
+ (r := w.stack) rfl hf hrun (by rcases hres with ⟨k, rfl⟩ | ⟨w', rfl⟩ <;> trivial)
111
+ rcases hres with ⟨k, rfl⟩ | ⟨w', rfl⟩
112
+ · exact .loopExit (.consNormal (rgetPc hpc) (.consAbrupt h2 trivial))
113
+ · exact .loopHalted (.consNormal (rgetPc hpc) (.consAbrupt h2 trivial))
114
+
115
+ theorem loop_step (hg : Geom c) {p : Prog 64} (hn : p.length < 2 ^ 32) (hreg : RegsOk c p)
116
+ {s : State 64} (hs : RelD c p.length s w) :
117
+ LoopSim c (funcsOf c.layout p) p.length w (step p s) ∨
118
+ (¬ Fits c s ∧ RunI (funcsOf c.layout p) (.loop loopBody) w (.exit overflowCode)) := by
119
+ obtain ⟨h0, hst, hpc, hrs⟩ := hs
120
+ have hm : min s.pc p.length ≤ p.length := Nat.min_le_right _ _
121
+ have hf : (funcsOf c.layout p)[(ofN (min s.pc p.length)).toNat]? =
122
+ some (instrCode c.layout p (min s.pc p.length)) := by
123
+ rw [ofN_toNat (show min s.pc p.length < 2 ^ 32 by omega)]
124
+ exact funcs_at c.layout p hm
125
+ by_cases hlt : s.pc < p.length
126
+ · have hi : p[s.pc]? = some p[s.pc] := List.getElem?_eq_getElem hlt
127
+ have hcode : instrCode c.layout p (min s.pc p.length) = lowerInstr c.layout p.length s.pc p[s.pc] := by
128
+ rw [Nat.min_eq_left (Nat.le_of_lt hlt)]
129
+ simp [instrCode, hi]
130
+ rw [hcode] at hf
131
+ have hss := sim_step (fs := funcsOf c.layout p) hg (h0.setStack []) rfl hn hrs hi
132
+ (hreg s.pc _ hi)
133
+ rw [step_of_fetch hi]
134
+ generalize exec p[s.pc] s = o at hss ⊢
135
+ rcases hss with hss | ⟨hnf, h6⟩
136
+ rotate_left
137
+ · exact .inr ⟨hnf, loop_abrupt hpc hf h6 (.inl ⟨6, rfl⟩)⟩
138
+ left
139
+ cases o with
140
+ | next s' =>
141
+ obtain ⟨hrs', w', hr, h0', hst'⟩ := hss
142
+ refine ⟨{ w' with stack := [], globals := setG w'.globals pcG (.i32 (ofN (min s'.pc p.length))) },
143
+ ⟨?_, rfl, by simp [setG], hrs'⟩, fun r hr' => loop_next hst hpc hf hr hst' hr'⟩
144
+ exact Rel0.congr (h0'.setStack []) rfl (by simp [setG, pcG, spG]) (by simp [setG, pcG, rpG])
145
+ (by simp [setG, pcG, apG])
146
+ | halted s' =>
147
+ obtain ⟨w', hr, h0'⟩ := hss
148
+ exact ⟨w', loop_abrupt hpc hf hr (.inr ⟨w', rfl⟩), h0'⟩
149
+ | trapped t =>
150
+ exact loop_abrupt hpc hf hss (.inl ⟨_, rfl⟩)
151
+ · rw [step_pc_oob (by omega)]
152
+ have hcode : instrCode c.layout p (min s.pc p.length) = [.exitTrap 4] := by
153
+ rw [Nat.min_eq_right (by omega)]
154
+ simp [instrCode]
155
+ rw [hcode] at hf
156
+ exact .inl (loop_abrupt hpc hf sim_stub (.inl ⟨4, rfl⟩))
157
+
158
+ /-- Whole-program preservation, for the dispatch loop. -/
159
+ theorem loop_correct (hg : Geom c) {p : Prog 64} (hn : p.length < 2 ^ 32) (hreg : RegsOk c p)
160
+ {s : State 64} {o : Outcome 64} (h : Exec p s o) :
161
+ ∀ {w : WState}, RelD c p.length s w → (∀ s', Steps p s s' → Fits c s') →
162
+ Final c (funcsOf c.layout p) w o := by
163
+ induction h with
164
+ | @halt s s' hst =>
165
+ intro w hs hb
166
+ have := loop_step hg hn hreg hs
167
+ rw [hst] at this
168
+ rcases this with this | ⟨hnf, _⟩
169
+ · exact this
170
+ · exact absurd (hb s .refl) hnf
171
+ | @trap s t hst =>
172
+ intro w hs hb
173
+ have := loop_step hg hn hreg hs
174
+ rw [hst] at this
175
+ rcases this with this | ⟨hnf, _⟩
176
+ · exact this
177
+ · exact absurd (hb s .refl) hnf
178
+ | @next s s' o hst _ ih =>
179
+ intro w hs hb
180
+ have := loop_step hg hn hreg hs
181
+ rw [hst] at this
182
+ rcases this with this | ⟨hnf, _⟩
183
+ rotate_left
184
+ · exact absurd (hb s .refl) hnf
185
+ obtain ⟨w', hs', hlift⟩ := this
186
+ have hih := ih hs' (fun s'' hss => hb s'' (.cons hst hss))
187
+ cases o with
188
+ | next _ => exact hih.elim
189
+ | halted s2 =>
190
+ obtain ⟨w'', hr, h0⟩ := hih
191
+ exact ⟨w'', hlift _ hr, h0⟩
192
+ | trapped t => exact hlift _ hih
193
+
194
+ /-- Whole-program preservation for the dispatch loop, with no capacity hypothesis: the IR outcome,
195
+ or exit 6 when a stack overflow guard fires. -/
196
+ theorem loop_correct_or_overflow (hg : Geom c) {p : Prog 64} (hn : p.length < 2 ^ 32)
197
+ (hreg : RegsOk c p) {s : State 64} {o : Outcome 64} (h : Exec p s o) :
198
+ ∀ {w : WState}, RelD c p.length s w →
199
+ Final c (funcsOf c.layout p) w o ∨ RunI (funcsOf c.layout p) (.loop loopBody) w (.exit overflowCode) := by
200
+ induction h with
201
+ | @halt s s' hst =>
202
+ intro w hs
203
+ have := loop_step hg hn hreg hs
204
+ rw [hst] at this
205
+ rcases this with this | ⟨_, h6⟩
206
+ · exact .inl this
207
+ · exact .inr h6
208
+ | @trap s t hst =>
209
+ intro w hs
210
+ have := loop_step hg hn hreg hs
211
+ rw [hst] at this
212
+ rcases this with this | ⟨_, h6⟩
213
+ · exact .inl this
214
+ · exact .inr h6
215
+ | @next s s' o hst _ ih =>
216
+ intro w hs
217
+ have := loop_step hg hn hreg hs
218
+ rw [hst] at this
219
+ rcases this with ⟨w', hs', hlift⟩ | ⟨_, h6⟩
220
+ · rcases ih hs' with hih | h6
221
+ · left
222
+ cases o with
223
+ | next _ => exact hih.elim
224
+ | halted s2 =>
225
+ obtain ⟨w'', hr, h0⟩ := hih
226
+ exact ⟨w'', hlift _ hr, h0⟩
227
+ | trapped t => exact hlift _ hih
228
+ · exact .inr (hlift _ h6)
229
+ · exact .inr h6
230
+
231
+ theorem final_main {p : Prog 64} {o : Outcome 64} (hl : Final c fs w o) : FinalMain c fs w o := by
232
+ cases o with
233
+ | next _ => exact hl.elim
234
+ | halted s' =>
235
+ obtain ⟨w', hr, h0⟩ := hl
236
+ refine ⟨w', ?_, h0⟩
237
+ rw [mainBody_eq]
238
+ exact .consAbrupt hr trivial
239
+ | trapped t =>
240
+ show Run _ mainBody w _
241
+ rw [mainBody_eq]
242
+ exact .consAbrupt hl trivial
243
+
244
+ /-- Whole-program preservation for the module's start function, with no capacity hypothesis: the
245
+ module reaches the IR outcome, or exits with code 6 because a stack was full. -/
246
+ theorem lowerProg_correct_or_overflow (hg : Geom c) {p : Prog 64} (hn : p.length < 2 ^ 32)
247
+ (hreg : RegsOk c p) {s : State 64} {o : Outcome 64} (h : Exec p s o) (hs : RelD c p.length s w) :
248
+ FinalMain c (funcsOf c.layout p) w o ∨ Run (funcsOf c.layout p) mainBody w (.exit overflowCode) := by
249
+ rcases loop_correct_or_overflow hg hn hreg h hs with hl | h6
250
+ · exact .inl (final_main (p := p) hl)
251
+ · right
252
+ rw [mainBody_eq]
253
+ exact .consAbrupt h6 trivial
254
+
255
+ /-- Whole-program preservation, for the module's start function `mainBody`. When the IR run stays
256
+ within the stack capacities, no overflow guard fires and the outcome is exactly the IR's. -/
257
+ theorem lowerProg_correct (hg : Geom c) {p : Prog 64} (hn : p.length < 2 ^ 32) (hreg : RegsOk c p)
258
+ {s : State 64} {o : Outcome 64} (h : Exec p s o) (hs : RelD c p.length s w)
259
+ (hb : ∀ s', Steps p s s' → Fits c s') :
260
+ FinalMain c (funcsOf c.layout p) w o := by
261
+ exact final_main (p := p) (loop_correct hg hn hreg h hs hb)
262
+
263
+ end Loop
264
+
265
+ /-- The state the runtime establishes before the dispatch loop (`Wasm.Emit`): empty operand stack,
266
+ `pc = 0`, `sp = dEnd`, `rp = rEnd`, `ap = aEnd` (empty auxiliary stack), and the register file and IR memory regions holding the IR
267
+ state's initial contents. `RelD` then holds at entry, so `lowerProg_correct` applies. -/
268
+ theorem init_rel {c : Cfg} (n : Nat) {s0 : State 64} {w0 : WState} (hg : Geom c)
269
+ (hpc0 : s0.pc = 0) (hd : s0.dstack = []) (hr : s0.rstack = [])
270
+ (hst : w0.stack = []) (hpc : w0.globals pcG = .i32 (ofN 0))
271
+ (hsp : w0.globals spG = .i32 (ofN c.dEnd)) (hrp : w0.globals rpG = .i32 (ofN c.rEnd))
272
+ (ha : s0.astack = []) (hap : w0.globals apG = .i32 (ofN c.aEnd))
273
+ (hregs : RegRel c w0.mem s0.regs) (hmem : MemRel c w0.mem s0.mem)
274
+ (hiv : ∀ a : Word 64, s0.mem.valid a = decide (a.toNat < c.M)) (hsize : w0.mem.size = c.msize) :
275
+ RelD c n s0 w0 :=
276
+ ⟨{ sp := by simpa [hd] using hsp
277
+ rp := by simpa [hr] using hrp
278
+ stack := by intro i v h; simp [hd] at h
279
+ regs := hregs
280
+ mem := hmem
281
+ rs := by intro i a h; simp [hr] at h
282
+ irvalid := hiv
283
+ size := hsize
284
+ capD := by simp [hd]; exact hg.dBase_le
285
+ capR := by simp [hr]; exact hg.rBase_le
286
+ ap := by simpa [ha] using hap
287
+ aux := by intro i v h; simp [ha] at h
288
+ capA := by simp [ha]; have := hg.aBase_8; omega },
289
+ hst, by simpa [hpc0] using hpc, by simp [hr]⟩
290
+
291
+ end Wasm
292
+ end WordDialect
backend/wasm/Wasm/Emit.lean ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.Lower
2
+
3
+ /-!
4
+ # Wasm.Emit
5
+
6
+ WebAssembly text (WAT) for a lowered program, plus the small runtime. The instruction text is
7
+ printed from exactly the `WI` list `lowerProg` builds, so the text and the verified model cannot
8
+ diverge. Hand-written: the imports, the memory declaration with the IR memory image, and the `$halt`
9
+ helper that dumps the final state (data-stack depth, data stack, IR memory, virtual registers as raw
10
+ little-endian 64-bit words) through WASI `fd_write` and exits 0. Traps are `proc_exit(code)`.
11
+ -/
12
+
13
+ namespace WordDialect
14
+ namespace Wasm
15
+ namespace Emit
16
+
17
+ /-- Placement of the runtime regions in linear memory. -/
18
+ structure Runtime where
19
+ memImage : List Nat
20
+ nregs : Nat
21
+
22
+ def Runtime.rf (_ : Runtime) : Nat := 0x1000
23
+ def Runtime.mb (r : Runtime) : Nat := r.rf + 8 * r.nregs
24
+ def Runtime.dBase (_ : Runtime) : Nat := 0x10000
25
+ def Runtime.dEnd (r : Runtime) : Nat := r.dBase + 8 * 65536
26
+ def Runtime.rBase (r : Runtime) : Nat := r.dEnd
27
+ def Runtime.rEnd (r : Runtime) : Nat := r.rBase + 8 * 4096
28
+ def Runtime.aBase (r : Runtime) : Nat := r.rEnd
29
+ def Runtime.aEnd (r : Runtime) : Nat := r.aBase + 8 * 4096
30
+ def Runtime.pages (r : Runtime) : Nat := (r.aEnd + 65535) / 65536
31
+ def Runtime.msize (r : Runtime) : Nat := 65536 * r.pages
32
+
33
+ def Runtime.layout (r : Runtime) : Layout :=
34
+ { dEnd := r.dEnd, rEnd := r.rEnd, rf := r.rf, mb := r.mb, M := r.memImage.length,
35
+ dBase := r.dBase, rBase := r.rBase, aEnd := r.aEnd, aBase := r.aBase }
36
+
37
+ def ind (d : Nat) : String := String.ofList (List.replicate (2 * d) ' ')
38
+
39
+ def line (d : Nat) (s : String) : String := ind d ++ s ++ "\n"
40
+
41
+ partial def instr (d : Nat) : WI → String
42
+ | .i32const v => line d s!"i32.const {v.toNat}"
43
+ | .i64const v => line d s!"i64.const {v.toNat}"
44
+ | .globalGet i => line d s!"global.get {i}"
45
+ | .globalSet i => line d s!"global.set {i}"
46
+ | .i64load off => line d s!"i64.load offset={off}"
47
+ | .i64store off => line d s!"i64.store offset={off}"
48
+ | .i64add => line d "i64.add" | .i64sub => line d "i64.sub" | .i64mul => line d "i64.mul"
49
+ | .i64and => line d "i64.and" | .i64or => line d "i64.or" | .i64xor => line d "i64.xor"
50
+ | .i64shl => line d "i64.shl" | .i64shru => line d "i64.shr_u"
51
+ | .i64rotl => line d "i64.rotl" | .i64rotr => line d "i64.rotr"
52
+ | .i64divu => line d "i64.div_u" | .i64divs => line d "i64.div_s"
53
+ | .i64eq => line d "i64.eq" | .i64ne => line d "i64.ne"
54
+ | .i64ltu => line d "i64.lt_u" | .i64leu => line d "i64.le_u"
55
+ | .i64gtu => line d "i64.gt_u" | .i64geu => line d "i64.ge_u"
56
+ | .i64lts => line d "i64.lt_s" | .i64les => line d "i64.le_s"
57
+ | .i64gts => line d "i64.gt_s" | .i64ges => line d "i64.ge_s"
58
+ | .i64eqz => line d "i64.eqz"
59
+ | .i32add => line d "i32.add" | .i32sub => line d "i32.sub" | .i32shl => line d "i32.shl"
60
+ | .i32gtu => line d "i32.gt_u" | .i32eq => line d "i32.eq"
61
+ | .i32wrap => line d "i32.wrap_i64"
62
+ | .i64extu => line d "i64.extend_i32_u"
63
+ | .select => line d "select"
64
+ | .loop b => line d "loop" ++ String.join (b.map (instr (d + 1))) ++ line d "end"
65
+ | .ite t => line d "if" ++ String.join (t.map (instr (d + 1))) ++ line d "end"
66
+ | .br l => line d s!"br {l}"
67
+ | .exitTrap c => line d s!"i32.const {c}" ++ line d "call $proc_exit" ++ line d "unreachable"
68
+ | .exitHalt => line d "global.get 1" ++ line d "call $halt" ++ line d "unreachable"
69
+ | .callIndirect => line d "call_indirect (type $step)"
70
+
71
+ def hexByte (n : Nat) : String :=
72
+ let s := String.ofList (Nat.toDigits 16 n)
73
+ "\\" ++ (if s.length < 2 then "0" ++ s else s)
74
+
75
+ /-- Byte `i` (little-endian) of the word `w`, as placed in the data segment. -/
76
+ def byteOf (w i : Nat) : Nat := (w / 256 ^ i) % 256
77
+
78
+ def wordBytes (w : Nat) : String :=
79
+ String.join ((List.range 8).map fun i => hexByte (byteOf w i))
80
+
81
+ def program (r : Runtime) (p : Prog 64) : String :=
82
+ let m := r.memImage.length
83
+ let L := r.layout
84
+ let fs := funcsOf L p
85
+ "(module\n" ++
86
+ " (type $step (func (result i32)))\n" ++
87
+ " (import \"wasi_snapshot_preview1\" \"fd_write\" (func $fd_write (param i32 i32 i32 i32) (result i32)))\n" ++
88
+ " (import \"wasi_snapshot_preview1\" \"proc_exit\" (func $proc_exit (param i32)))\n" ++
89
+ s!" (memory (export \"memory\") {r.pages})\n" ++
90
+ " (global $pc (mut i32) (i32.const 0))\n" ++
91
+ s!" (global $sp (mut i32) (i32.const {r.dEnd}))\n" ++
92
+ s!" (global $rp (mut i32) (i32.const {r.rEnd}))\n" ++
93
+ s!" (global $ap (mut i32) (i32.const {r.aEnd}))\n" ++
94
+ (if m = 0 then "" else
95
+ s!" (data (i32.const {r.mb}) \"{String.join (r.memImage.map wordBytes)}\")\n") ++
96
+ " (func $write (param $p i32) (param $n i32)\n" ++
97
+ " (i32.store (i32.const 16) (local.get $p))\n" ++
98
+ " (i32.store (i32.const 20) (local.get $n))\n" ++
99
+ " (drop (call $fd_write (i32.const 1) (i32.const 16) (i32.const 1) (i32.const 24))))\n" ++
100
+ " (func $halt (param $sp i32)\n" ++
101
+ s!" (i64.store (i32.const 0) (i64.extend_i32_u (i32.shr_u (i32.sub (i32.const {r.dEnd}) (local.get $sp)) (i32.const 3))))\n" ++
102
+ " (call $write (i32.const 0) (i32.const 8))\n" ++
103
+ s!" (call $write (local.get $sp) (i32.sub (i32.const {r.dEnd}) (local.get $sp)))\n" ++
104
+ s!" (call $write (i32.const {r.mb}) (i32.const {8 * m}))\n" ++
105
+ s!" (call $write (i32.const {r.rf}) (i32.const {8 * r.nregs}))\n" ++
106
+ " (call $proc_exit (i32.const 0)))\n" ++
107
+ String.join ((fs.zipIdx).map fun (b, k) =>
108
+ s!" (func $f{k} (type $step)\n" ++ String.join (b.map (instr 2)) ++ " )\n") ++
109
+ s!" (table {fs.length} funcref)\n" ++
110
+ s!" (elem (i32.const 0) {String.intercalate " " ((List.range fs.length).map fun k => s!"$f{k}")})\n" ++
111
+ " (func $main (export \"_start\")\n" ++
112
+ String.join (mainBody.map (instr 2)) ++
113
+ " )\n)\n"
114
+
115
+ end Emit
116
+ end Wasm
117
+ end WordDialect
backend/wasm/Wasm/Frame.lean ADDED
@@ -0,0 +1,355 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.Seq
2
+
3
+ /-!
4
+ # Wasm.Frame
5
+
6
+ How a write of one 8-byte cell into one region affects the five views
7
+ `StackRel RegRel MemRel RsRel AuxRel`:
8
+ the views of the other regions are unchanged, and the view of the written region changes in the
9
+ expected way. All statements are about `Rel0` and the byte memory only.
10
+ -/
11
+
12
+ namespace WordDialect
13
+ namespace Wasm
14
+
15
+ section Keep
16
+
17
+ variable {c : Cfg} {s : State 64} {w : WState} {m' : WMem} {A : Nat} {v : W}
18
+
19
+ theorem keep_stack (hs : Rel0 c s w) (hw : w.mem.write64 A v = some m')
20
+ (hA : A + 8 ≤ c.dBase ∨ c.dEnd ≤ A) : StackRel c m' s.dstack := by
21
+ have hcap := hs.capD
22
+ intro i x hi
23
+ have hik : i < s.dstack.length := by
24
+ by_cases hh : i < s.dstack.length
25
+ · exact hh
26
+ · rw [List.getElem?_eq_none (by omega)] at hi; simp at hi
27
+ rw [read64_write64_other hw (by omega)]
28
+ exact hs.stack i x hi
29
+
30
+ theorem keep_regs (hs : Rel0 c s w) (hw : w.mem.write64 A v = some m')
31
+ (hA : A + 8 ≤ c.rf ∨ c.rf + 8 * c.nregs ≤ A) : RegRel c m' s.regs := by
32
+ intro r hr
33
+ rw [read64_write64_other hw (by omega)]
34
+ exact hs.regs r hr
35
+
36
+ theorem keep_mem (hs : Rel0 c s w) (hw : w.mem.write64 A v = some m')
37
+ (hA : A + 8 ≤ c.mb ∨ c.mb + 8 * c.M ≤ A) : MemRel c m' s.mem := by
38
+ intro a ha
39
+ rw [read64_write64_other hw (by omega)]
40
+ exact hs.mem a ha
41
+
42
+ theorem keep_rs (hs : Rel0 c s w) (hw : w.mem.write64 A v = some m')
43
+ (hA : A + 8 ≤ c.rBase ∨ c.rEnd ≤ A) : RsRel c m' s.rstack := by
44
+ have hcap := hs.capR
45
+ intro i a hi
46
+ have hik : i < s.rstack.length := by
47
+ by_cases hh : i < s.rstack.length
48
+ · exact hh
49
+ · rw [List.getElem?_eq_none (by omega)] at hi; simp at hi
50
+ rw [read64_write64_other hw (by omega)]
51
+ exact hs.rs i a hi
52
+
53
+ theorem keep_aux (hs : Rel0 c s w) (hw : w.mem.write64 A v = some m')
54
+ (hA : A + 8 ≤ c.aBase ∨ c.aEnd ≤ A) : AuxRel c m' s.astack := by
55
+ have hcap := hs.capA
56
+ intro i x hi
57
+ have hik : i < s.astack.length := by
58
+ by_cases hh : i < s.astack.length
59
+ · exact hh
60
+ · rw [List.getElem?_eq_none (by omega)] at hi; simp at hi
61
+ rw [read64_write64_other hw (by omega)]
62
+ exact hs.aux i x hi
63
+
64
+ end Keep
65
+
66
+ section Write
67
+
68
+ variable {c : Cfg} {s : State 64} {w : WState} {m' : WMem} {v : W}
69
+
70
+ /-- Write into data-stack slot `j`. -/
71
+ theorem wr_stack_at (hg : Geom c) (hs : Rel0 c s w) {j : Nat} (hj : j < s.dstack.length) {A : Nat}
72
+ (hA : A = c.dEnd - 8 * s.dstack.length + 8 * j)
73
+ (hw : w.mem.write64 A v = some m') :
74
+ Rel0 c { s with dstack := s.dstack.set j v } { w with mem := m' } := by
75
+ subst hA
76
+ have hcap := hs.capD
77
+ have hdG := hg.dEnd_ge
78
+ obtain ⟨hsz, _⟩ := write64_size hw
79
+ refine { sp := ?_, rp := hs.rp, stack := ?_, regs := ?_, mem := ?_, rs := ?_, irvalid := hs.irvalid,
80
+ size := ?_, capD := ?_, capR := hs.capR, ap := hs.ap, aux := ?_, capA := hs.capA }
81
+ · simpa using hs.sp
82
+ · intro i x hi
83
+ simp only [List.length_set] at hi ⊢
84
+ by_cases hij : i = j
85
+ · subst hij
86
+ have : x = v := by simpa [List.getElem?_set_self hj] using hi.symm
87
+ subst this
88
+ exact read64_write64_same hw
89
+ · rw [List.getElem?_set_ne (by omega)] at hi
90
+ rw [read64_write64_other hw (by omega)]
91
+ exact hs.stack i x hi
92
+ · exact keep_regs (s := s) hs hw (by rcases hg.dS_F with h | h <;> omega)
93
+ · exact keep_mem (s := s) hs hw (by rcases hg.dS_M with h | h <;> omega)
94
+ · exact keep_rs (s := s) hs hw (by rcases hg.dS_K with h | h <;> omega)
95
+ · show m'.size = c.msize
96
+ rw [hsz]; exact hs.size
97
+ · simpa using hs.capD
98
+ · exact keep_aux (s := s) hs hw (by rcases hg.dS_A with h | h <;> omega)
99
+
100
+ theorem wr_stack (hg : Geom c) (hs : Rel0 c s w) {j : Nat} (hj : j < s.dstack.length)
101
+ (hw : w.mem.write64 (c.dEnd - 8 * s.dstack.length + 8 * j) v = some m') :
102
+ Rel0 c { s with dstack := s.dstack.set j v } { w with mem := m' } :=
103
+ wr_stack_at hg hs hj rfl hw
104
+
105
+ /-- Update one global. -/
106
+ def setG (g : Nat → Val) (i : Nat) (v : Val) : Nat → Val := fun j => if j = i then v else g j
107
+
108
+ /-- Write virtual register `r`. -/
109
+ theorem wr_regs (hg : Geom c) (hs : Rel0 c s w) {r : Nat} (hr : r < c.nregs)
110
+ (hw : w.mem.write64 (c.rf + 8 * r) v = some m') :
111
+ Rel0 c { s with regs := State.setReg s.regs r v } { w with mem := m' } := by
112
+ obtain ⟨hsz, _⟩ := write64_size hw
113
+ refine { sp := hs.sp, rp := hs.rp, stack := ?_, regs := ?_, mem := ?_, rs := ?_,
114
+ irvalid := hs.irvalid, size := ?_, capD := hs.capD, capR := hs.capR, ap := hs.ap,
115
+ aux := ?_, capA := hs.capA }
116
+ · exact keep_stack (s := s) hs hw (by rcases hg.dS_F with h | h <;> omega)
117
+ · intro r' hr'
118
+ by_cases h : r' = r
119
+ · subst h
120
+ simpa [State.setReg] using read64_write64_same hw
121
+ · rw [read64_write64_other hw (by omega)]
122
+ simpa [State.setReg, h] using hs.regs r' hr'
123
+ · exact keep_mem (s := s) hs hw (by rcases hg.dF_M with h | h <;> omega)
124
+ · exact keep_rs (s := s) hs hw (by rcases hg.dF_K with h | h <;> omega)
125
+ · show m'.size = c.msize
126
+ rw [hsz]; exact hs.size
127
+ · exact keep_aux (s := s) hs hw (by rcases hg.dF_A with h | h <;> omega)
128
+
129
+ /-- Write IR memory word `a`, mirroring an IR store. -/
130
+ theorem wr_mem (hg : Geom c) (hs : Rel0 c s w) {a : W} {im : WordDialect.Memory 64}
131
+ (hv : s.mem.write? a v = some im)
132
+ (hw : w.mem.write64 (c.mb + 8 * a.toNat) v = some m') :
133
+ Rel0 c { s with mem := im } { w with mem := m' } := by
134
+ obtain ⟨hsz, _⟩ := write64_size hw
135
+ have hvalid : s.mem.valid a = true := by
136
+ by_cases hh : s.mem.valid a = true
137
+ · exact hh
138
+ · have := (Memory.write?_none_iff s.mem a v).mpr (by simpa using hh); rw [hv] at this; simp at this
139
+ have hlt : a.toNat < c.M := by
140
+ have := hs.irvalid a
141
+ rw [hvalid] at this
142
+ simpa using this.symm
143
+ refine { sp := hs.sp, rp := hs.rp, stack := ?_, regs := ?_, mem := ?_, rs := ?_, irvalid := ?_,
144
+ size := ?_, capD := hs.capD, capR := hs.capR, ap := hs.ap, aux := ?_, capA := hs.capA }
145
+ · exact keep_stack (s := s) hs hw (by rcases hg.dS_M with h | h <;> omega)
146
+ · exact keep_regs (s := s) hs hw (by rcases hg.dF_M with h | h <;> omega)
147
+ · intro a' ha'
148
+ by_cases h : a' = a.toNat
149
+ · subst h
150
+ rw [read64_write64_same hw, BitVec.ofNat_toNat, BitVec.setWidth_eq]
151
+ have := (Memory.read?_some (Memory.read_write_same hv)).2
152
+ rw [this]
153
+ · rw [read64_write64_other hw (by omega)]
154
+ have hne : BitVec.ofNat 64 a' ≠ a := by
155
+ intro e
156
+ apply h
157
+ have h2 := congrArg BitVec.toNat e
158
+ have hM : a' < 2 ^ 64 := by have := hg.mb_le; have := hg.msize_lt; omega
159
+ simp [BitVec.toNat_ofNat, Nat.mod_eq_of_lt hM] at h2
160
+ exact h2
161
+ rw [Memory.cell_write_other hv hne]
162
+ exact hs.mem a' ha'
163
+ · exact keep_rs (s := s) hs hw (by rcases hg.dM_K with h | h <;> omega)
164
+ · intro a'; rw [Memory.write?_valid hv]; exact hs.irvalid a'
165
+ · show m'.size = c.msize
166
+ rw [hsz]; exact hs.size
167
+ · exact keep_aux (s := s) hs hw (by rcases hg.dM_A with h | h <;> omega)
168
+
169
+ /-- Adjust the data-stack pointer upward by `n` words (popping `n` elements). -/
170
+ theorem adj_sp (hg : Geom c) (hs : Rel0 c s w) {n : Nat} (hn : n ≤ s.dstack.length) :
171
+ Rel0 c { s with dstack := s.dstack.drop n }
172
+ { w with globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * (s.dstack.length - n)))) } := by
173
+ have hcap := hs.capD
174
+ refine { sp := ?_, rp := ?_, stack := ?_, regs := hs.regs, mem := hs.mem, rs := hs.rs,
175
+ irvalid := hs.irvalid, size := hs.size, capD := ?_, capR := hs.capR, ap := ?_,
176
+ aux := hs.aux, capA := hs.capA }
177
+ · simp [setG, spG]
178
+ · have : rpG ≠ spG := by decide
179
+ simpa [setG, this] using hs.rp
180
+ · intro i x hi
181
+ simp only [List.length_drop]
182
+ rw [List.getElem?_drop] at hi
183
+ have := hs.stack (n + i) x hi
184
+ have e : c.dEnd - 8 * (s.dstack.length - n) + 8 * i = c.dEnd - 8 * s.dstack.length + 8 * (n + i) := by
185
+ omega
186
+ rw [e]; exact this
187
+ · simp only [List.length_drop]; omega
188
+ · have : apG ≠ spG := by decide
189
+ simpa [setG, this] using hs.ap
190
+
191
+ /-- Push: the stack pointer moved down one word and the new top written. -/
192
+ theorem push_rel (hg : Geom c) (hs : Rel0 c s w)
193
+ (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd)
194
+ (hw : w.mem.write64 (c.dEnd - 8 * (s.dstack.length + 1)) v = some m') :
195
+ Rel0 c { s with dstack := v :: s.dstack }
196
+ { w with mem := m', globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * (s.dstack.length + 1)))) } := by
197
+ have hcap := hs.capD
198
+ obtain ⟨hsz, _⟩ := write64_size hw
199
+ refine { sp := ?_, rp := ?_, stack := ?_, regs := ?_, mem := ?_, rs := ?_, irvalid := hs.irvalid,
200
+ size := ?_, capD := ?_, capR := hs.capR, ap := ?_, aux := ?_, capA := hs.capA }
201
+ · simp [setG, spG]
202
+ · have : rpG ≠ spG := by decide
203
+ simpa [setG, this] using hs.rp
204
+ · intro i x hi
205
+ simp only [List.length_cons] at hi ⊢
206
+ cases i with
207
+ | zero =>
208
+ simp at hi; subst hi
209
+ simpa using read64_write64_same hw
210
+ | succ i =>
211
+ simp only [List.getElem?_cons_succ] at hi
212
+ have hik : i < s.dstack.length := by
213
+ have := (List.getElem?_eq_some_iff.mp hi).1; simpa using this
214
+ have e : c.dEnd - 8 * (s.dstack.length + 1) + 8 * (i + 1) = c.dEnd - 8 * s.dstack.length + 8 * i := by
215
+ omega
216
+ rw [e, read64_write64_other hw (by omega)]
217
+ exact hs.stack i x hi
218
+ · exact keep_regs (s := s) hs hw (by rcases hg.dS_F with h | h <;> omega)
219
+ · exact keep_mem (s := s) hs hw (by rcases hg.dS_M with h | h <;> omega)
220
+ · exact keep_rs (s := s) hs hw (by rcases hg.dS_K with h | h <;> omega)
221
+ · show m'.size = c.msize
222
+ rw [hsz]; exact hs.size
223
+ · simp only [List.length_cons]; omega
224
+ · have : apG ≠ spG := by decide
225
+ simpa [setG, this] using hs.ap
226
+ · exact keep_aux (s := s) hs hw (by rcases hg.dS_A with h | h <;> omega)
227
+
228
+ /-- Call: the return-stack pointer moved down and the return index written. -/
229
+ theorem push_rs (hg : Geom c) (hs : Rel0 c s w) {a : Nat}
230
+ (hfit : c.rBase + 8 * (s.rstack.length + 1) ≤ c.rEnd)
231
+ (hw : w.mem.write64 (c.rEnd - 8 * (s.rstack.length + 1)) (BitVec.ofNat 64 a) = some m') :
232
+ Rel0 c { s with rstack := a :: s.rstack }
233
+ { w with mem := m', globals := setG w.globals rpG (.i32 (ofN (c.rEnd - 8 * (s.rstack.length + 1)))) } := by
234
+ have hcap := hs.capR
235
+ obtain ⟨hsz, _⟩ := write64_size hw
236
+ refine { sp := ?_, rp := ?_, stack := ?_, regs := ?_, mem := ?_, rs := ?_, irvalid := hs.irvalid,
237
+ size := ?_, capD := hs.capD, capR := ?_, ap := ?_, aux := ?_, capA := hs.capA }
238
+ · have : spG ≠ rpG := by decide
239
+ simpa [setG, this] using hs.sp
240
+ · simp [setG, rpG]
241
+ · exact keep_stack (s := s) hs hw (by rcases hg.dS_K with h | h <;> omega)
242
+ · exact keep_regs (s := s) hs hw (by rcases hg.dF_K with h | h <;> omega)
243
+ · exact keep_mem (s := s) hs hw (by rcases hg.dM_K with h | h <;> omega)
244
+ · intro i x hi
245
+ simp only [List.length_cons] at hi ⊢
246
+ cases i with
247
+ | zero =>
248
+ simp at hi; subst hi
249
+ simpa using read64_write64_same hw
250
+ | succ i =>
251
+ simp only [List.getElem?_cons_succ] at hi
252
+ have hik : i < s.rstack.length := by
253
+ have := (List.getElem?_eq_some_iff.mp hi).1; simpa using this
254
+ have e : c.rEnd - 8 * (s.rstack.length + 1) + 8 * (i + 1) = c.rEnd - 8 * s.rstack.length + 8 * i := by
255
+ omega
256
+ rw [e, read64_write64_other hw (by omega)]
257
+ exact hs.rs i x hi
258
+ · show m'.size = c.msize
259
+ rw [hsz]; exact hs.size
260
+ · simp only [List.length_cons]; omega
261
+ · have : apG ≠ rpG := by decide
262
+ simpa [setG, this] using hs.ap
263
+ · exact keep_aux (s := s) hs hw (by rcases hg.dK_A with h | h <;> omega)
264
+
265
+ /-- Return: the return-stack pointer moved up one entry. -/
266
+ theorem pop_rs (hs : Rel0 c s w) {a : Nat} {rs : List Nat} (hr : s.rstack = a :: rs) :
267
+ Rel0 c { s with rstack := rs }
268
+ { w with globals := setG w.globals rpG (.i32 (ofN (c.rEnd - 8 * rs.length))) } := by
269
+ have hcap := hs.capR
270
+ rw [hr] at hcap
271
+ simp only [List.length_cons] at hcap
272
+ refine { sp := ?_, rp := ?_, stack := hs.stack, regs := hs.regs, mem := hs.mem, rs := ?_,
273
+ irvalid := hs.irvalid, size := hs.size, capD := hs.capD, capR := ?_, ap := ?_,
274
+ aux := hs.aux, capA := hs.capA }
275
+ · have : spG ≠ rpG := by decide
276
+ simpa [setG, this] using hs.sp
277
+ · simp [setG, rpG]
278
+ · intro i x hi
279
+ have h := hs.rs (i + 1) x (by simpa [hr] using hi)
280
+ rw [hr] at h
281
+ simp only [List.length_cons] at h
282
+ have e : c.rEnd - 8 * (rs.length + 1) + 8 * (i + 1) = c.rEnd - 8 * rs.length + 8 * i := by omega
283
+ rw [e] at h
284
+ exact h
285
+ · show c.rBase + 8 * rs.length ≤ c.rEnd
286
+ omega
287
+ · have : apG ≠ rpG := by decide
288
+ simpa [setG, this] using hs.ap
289
+
290
+ /-- `tor`: the auxiliary-stack pointer moved down one word and the new top written. -/
291
+ theorem push_aux (hg : Geom c) (hs : Rel0 c s w) {a : W}
292
+ (hfit : c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd)
293
+ (hw : w.mem.write64 (c.aEnd - 8 * (s.astack.length + 1)) a = some m') :
294
+ Rel0 c { s with astack := a :: s.astack }
295
+ { w with mem := m', globals := setG w.globals apG (.i32 (ofN (c.aEnd - 8 * (s.astack.length + 1)))) } := by
296
+ have hcap := hs.capA
297
+ obtain ⟨hsz, _⟩ := write64_size hw
298
+ refine { sp := ?_, rp := ?_, stack := ?_, regs := ?_, mem := ?_, rs := ?_, irvalid := hs.irvalid,
299
+ size := ?_, capD := hs.capD, capR := hs.capR, ap := ?_, aux := ?_, capA := ?_ }
300
+ · have : spG ≠ apG := by decide
301
+ simpa [setG, this] using hs.sp
302
+ · have : rpG ≠ apG := by decide
303
+ simpa [setG, this] using hs.rp
304
+ · exact keep_stack (s := s) hs hw (by rcases hg.dS_A with h | h <;> omega)
305
+ · exact keep_regs (s := s) hs hw (by rcases hg.dF_A with h | h <;> omega)
306
+ · exact keep_mem (s := s) hs hw (by rcases hg.dM_A with h | h <;> omega)
307
+ · exact keep_rs (s := s) hs hw (by rcases hg.dK_A with h | h <;> omega)
308
+ · show m'.size = c.msize
309
+ rw [hsz]; exact hs.size
310
+ · simp [setG, apG]
311
+ · intro i x hi
312
+ simp only [List.length_cons] at hi ⊢
313
+ cases i with
314
+ | zero =>
315
+ simp at hi; subst hi
316
+ simpa using read64_write64_same hw
317
+ | succ i =>
318
+ simp only [List.getElem?_cons_succ] at hi
319
+ have hik : i < s.astack.length := by
320
+ have := (List.getElem?_eq_some_iff.mp hi).1; simpa using this
321
+ have e : c.aEnd - 8 * (s.astack.length + 1) + 8 * (i + 1) = c.aEnd - 8 * s.astack.length + 8 * i := by
322
+ omega
323
+ rw [e, read64_write64_other hw (by omega)]
324
+ exact hs.aux i x hi
325
+ · simp only [List.length_cons]; omega
326
+
327
+ /-- `fromr`: the auxiliary-stack pointer moved up one word. -/
328
+ theorem pop_aux (hs : Rel0 c s w) {a : W} {as : List W} (hr : s.astack = a :: as) :
329
+ Rel0 c { s with astack := as }
330
+ { w with globals := setG w.globals apG (.i32 (ofN (c.aEnd - 8 * as.length))) } := by
331
+ have hcap := hs.capA
332
+ rw [hr] at hcap
333
+ simp only [List.length_cons] at hcap
334
+ refine { sp := ?_, rp := ?_, stack := hs.stack, regs := hs.regs, mem := hs.mem, rs := hs.rs,
335
+ irvalid := hs.irvalid, size := hs.size, capD := hs.capD, capR := hs.capR, ap := ?_,
336
+ aux := ?_, capA := ?_ }
337
+ · have : spG ≠ apG := by decide
338
+ simpa [setG, this] using hs.sp
339
+ · have : rpG ≠ apG := by decide
340
+ simpa [setG, this] using hs.rp
341
+ · simp [setG, apG]
342
+ · intro i x hi
343
+ have h := hs.aux (i + 1) x (by simpa [hr] using hi)
344
+ rw [hr] at h
345
+ simp only [List.length_cons] at h
346
+ have e : c.aEnd - 8 * (as.length + 1) + 8 * (i + 1) = c.aEnd - 8 * as.length + 8 * i := by omega
347
+ rw [e] at h
348
+ exact h
349
+ · show c.aBase + 8 * as.length ≤ c.aEnd
350
+ omega
351
+
352
+ end Write
353
+
354
+ end Wasm
355
+ end WordDialect
backend/wasm/Wasm/Guard.lean ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.Frame
2
+
3
+ /-!
4
+ # Wasm.Guard
5
+
6
+ `execL` over appended lists, the operand-count guard `uf`, and small conveniences used by every
7
+ instruction proof.
8
+ -/
9
+
10
+ namespace WordDialect
11
+ namespace Wasm
12
+
13
+ theorem execL_append (a b : List WI) (s : WState) :
14
+ execL (a ++ b) s = (execL a s).bind (execL b) := by
15
+ induction a generalizing s with
16
+ | nil => simp [execL]
17
+ | cons i is ih =>
18
+ simp only [List.cons_append, execL]
19
+ cases stepI i s <;> simp [ih]
20
+
21
+ theorem Rel0.setStack {c : Cfg} {s : State 64} {w : WState} (h : Rel0 c s w) (x : List Val) :
22
+ Rel0 c s { w with stack := x } :=
23
+ { sp := h.sp, rp := h.rp, stack := h.stack, regs := h.regs, mem := h.mem, rs := h.rs,
24
+ irvalid := h.irvalid, size := h.size, capD := h.capD, capR := h.capR, ap := h.ap, aux := h.aux,
25
+ capA := h.capA }
26
+
27
+ /-- `Rel0` only looks at the globals `sp`, `rp`, `ap` and the memory. -/
28
+ theorem Rel0.congr {c : Cfg} {s : State 64} {w w' : WState} (h : Rel0 c s w)
29
+ (hm : w'.mem = w.mem) (hsp : w'.globals spG = w.globals spG)
30
+ (hrp : w'.globals rpG = w.globals rpG) (hap : w'.globals apG = w.globals apG) : Rel0 c s w' :=
31
+ { sp := by rw [hsp]; exact h.sp, rp := by rw [hrp]; exact h.rp,
32
+ stack := by rw [hm]; exact h.stack, regs := by rw [hm]; exact h.regs,
33
+ mem := by rw [hm]; exact h.mem, rs := by rw [hm]; exact h.rs, irvalid := h.irvalid,
34
+ size := by rw [hm]; exact h.size, capD := h.capD, capR := h.capR,
35
+ ap := by rw [hap]; exact h.ap, aux := by rw [hm]; exact h.aux, capA := h.capA }
36
+
37
+ section Uf
38
+
39
+ variable {c : Cfg} {s : State 64} {w : WState} {fs : Funcs}
40
+
41
+ theorem sp_lt (hg : Geom c) (hs : Rel0 c s w) : c.dEnd - 8 * s.dstack.length < 2 ^ 32 := by
42
+ have := hg.dEnd_le; have := hg.msize_lt; omega
43
+
44
+ theorem spToNat (hg : Geom c) (hs : Rel0 c s w) {u : WState} (hu : u.globals = w.globals) :
45
+ u.globals spG = .i32 (ofN (c.dEnd - 8 * s.dstack.length)) := by
46
+ rw [hu]; exact hs.sp
47
+
48
+ theorem ult_ofN {a b : Nat} (ha : a < 2 ^ 32) (hb : b < 2 ^ 32) :
49
+ (ofN a).ult (ofN b) = decide (a < b) := by
50
+ simp [BitVec.ult_eq_decide, ofN_toNat ha, ofN_toNat hb]
51
+
52
+ /-- Enough operands: the guard falls through, changing nothing. -/
53
+ theorem uf_pass (hg : Geom c) (hs : Rel0 c s w) {k : Nat} (hk0 : 0 < k) (hk : k ≤ s.dstack.length) :
54
+ Run fs (uf c.layout k) w (.normal w) := by
55
+ have hk0' : k ≠ 0 := by omega
56
+ have hX := sp_lt hg hs
57
+ have hlim : c.dEnd - 8 * k < 2 ^ 32 := by have := hg.dEnd_le; have := hg.msize_lt; omega
58
+ have hexec : execL [.globalGet spG, i32c (c.layout.dEnd - 8 * k), .i32gtu] w =
59
+ some { w with stack := .i32 (bool32 false) :: w.stack } := by
60
+ simp only [execL, stepI, hs.sp, i32c, Cfg.layout, Option.bind_some, bool32]
61
+ rw [show BitVec.ofNat 32 (c.dEnd - 8 * k) = ofN (c.dEnd - 8 * k) from rfl, ult_ofN hlim hX]
62
+ have hnum : ¬ (c.dEnd - 8 * k < c.dEnd - 8 * s.dstack.length) := by omega
63
+ simp [hnum]
64
+ have h3 : Run fs [.globalGet spG, i32c (c.layout.dEnd - 8 * k), .i32gtu] w
65
+ (.normal { w with stack := .i32 (bool32 false) :: w.stack }) :=
66
+ run_of_execL (by simp [WI.isCtl, i32c]) hexec
67
+ simp only [uf, hk0', if_false]
68
+ have h4 := run_ite_false (fs := fs) (thn := [.exitTrap 1])
69
+ (w := { w with stack := .i32 (bool32 false) :: w.stack }) (r := w.stack) (by simp [bool32])
70
+ exact run_append _ h3 h4
71
+
72
+ theorem uf_trap (hg : Geom c) (hs : Rel0 c s w) {k : Nat} (hk0 : 0 < k) (hk3 : k ≤ 3)
73
+ (hk : s.dstack.length < k) :
74
+ Run fs (uf c.layout k) w (.exit 1) := by
75
+ have hk0' : k ≠ 0 := by omega
76
+ have hX := sp_lt hg hs
77
+ have hcap := hs.capD
78
+ have hlim : c.dEnd - 8 * k < 2 ^ 32 := by have := hg.dEnd_le; have := hg.msize_lt; omega
79
+ have hdG := hg.dEnd_ge
80
+ have hexec : execL [.globalGet spG, i32c (c.layout.dEnd - 8 * k), .i32gtu] w =
81
+ some { w with stack := .i32 (bool32 true) :: w.stack } := by
82
+ simp only [execL, stepI, hs.sp, i32c, Cfg.layout, Option.bind_some, bool32]
83
+ rw [show BitVec.ofNat 32 (c.dEnd - 8 * k) = ofN (c.dEnd - 8 * k) from rfl, ult_ofN hlim hX]
84
+ have hnum : c.dEnd - 8 * k < c.dEnd - 8 * s.dstack.length := by omega
85
+ simp [hnum]
86
+ have h3 : Run fs [.globalGet spG, i32c (c.layout.dEnd - 8 * k), .i32gtu] w
87
+ (.normal { w with stack := .i32 (bool32 true) :: w.stack }) :=
88
+ run_of_execL (by simp [WI.isCtl, i32c]) hexec
89
+ simp only [uf, hk0', if_false]
90
+ have h4 := run_ite_exit (fs := fs) (w := { w with stack := .i32 (bool32 true) :: w.stack })
91
+ (r := w.stack) (c := bool32 true) (k := 1) rfl (by simp [bool32])
92
+ have := run_append _ h3 h4
93
+ simpa using this
94
+
95
+ end Uf
96
+
97
+ section Ovf
98
+
99
+ variable {c : Cfg} {s : State 64} {w : WState} {fs : Funcs}
100
+
101
+ theorem ovf_cmp {u : WState} {g base p : Nat} (hp : u.globals g = .i32 (ofN p)) (hP : p < 2 ^ 32)
102
+ (hb : base + 8 < 2 ^ 32) :
103
+ execL [i32c (base + 8), .globalGet g, .i32gtu] u =
104
+ some { u with stack := .i32 (bool32 (decide (p < base + 8))) :: u.stack } := by
105
+ have hu := ult_ofN hP hb
106
+ simp only [ofN] at hp hu
107
+ simp [execL, stepI, i32c, hp, hu]
108
+
109
+ theorem ovf_pass {u : WState} {g base p : Nat} (hp : u.globals g = .i32 (ofN p)) (hP : p < 2 ^ 32)
110
+ (hb : base + 8 < 2 ^ 32) (h : base + 8 ≤ p) : Run fs (ovf g base) u (.normal u) := by
111
+ have h1 := run_of_execL (fs := fs) (by simp [WI.isCtl, i32c]) (ovf_cmp hp hP hb)
112
+ have hf : decide (p < base + 8) = false := by simp; omega
113
+ rw [hf] at h1
114
+ have h2 := run_ite_false (fs := fs) (thn := [.exitTrap 6])
115
+ (w := { u with stack := .i32 (bool32 false) :: u.stack }) (r := u.stack) (by simp [bool32])
116
+ have := run_append _ h1 h2
117
+ simpa [ovf] using this
118
+
119
+ theorem ovf_trap {u : WState} {g base p : Nat} (hp : u.globals g = .i32 (ofN p)) (hP : p < 2 ^ 32)
120
+ (hb : base + 8 < 2 ^ 32) (h : p < base + 8) : Run fs (ovf g base) u (.exit 6) := by
121
+ have h1 := run_of_execL (fs := fs) (by simp [WI.isCtl, i32c]) (ovf_cmp hp hP hb)
122
+ have ht : decide (p < base + 8) = true := by simp; omega
123
+ rw [ht] at h1
124
+ have h2 := run_ite_exit (fs := fs) (w := { u with stack := .i32 (bool32 true) :: u.stack })
125
+ (r := u.stack) (c := bool32 true) (k := 6) rfl (by simp [bool32])
126
+ have := run_append _ h1 h2
127
+ simpa [ovf] using this
128
+
129
+ /-- Data stack has room for one more word: the guard falls through. -/
130
+ theorem ovfD_pass (hg : Geom c) (hs : Rel0 c s w)
131
+ (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
132
+ Run fs (ovf spG c.layout.dBase) w (.normal w) := by
133
+ have := hg.dEnd_le; have := hg.msize_lt; have := hg.dBase_8
134
+ exact ovf_pass hs.sp (sp_lt hg hs) (by simp [Cfg.layout]; omega) (by simp [Cfg.layout]; omega)
135
+
136
+ /-- Data stack full: the guard exits 6. -/
137
+ theorem ovfD_trap (hg : Geom c) (hs : Rel0 c s w)
138
+ (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
139
+ Run fs (ovf spG c.layout.dBase) w (.exit 6) := by
140
+ have := hg.dEnd_le; have := hg.msize_lt; have := hg.dBase_8; have := hs.capD
141
+ exact ovf_trap hs.sp (sp_lt hg hs) (by simp [Cfg.layout]; omega) (by simp [Cfg.layout]; omega)
142
+
143
+ theorem rp_lt (hg : Geom c) (hs : Rel0 c s w) : c.rEnd - 8 * s.rstack.length < 2 ^ 32 := by
144
+ have := hg.rEnd_le; have := hg.msize_lt; omega
145
+
146
+ /-- Return stack has room for one more entry: the guard falls through. -/
147
+ theorem ovfR_pass (hg : Geom c) (hs : Rel0 c s w)
148
+ (hfit : c.rBase + 8 * (s.rstack.length + 1) ≤ c.rEnd) :
149
+ Run fs (ovf rpG c.layout.rBase) w (.normal w) := by
150
+ have := hg.rEnd_le; have := hg.msize_lt; have := hg.rBase_8
151
+ exact ovf_pass hs.rp (rp_lt hg hs) (by simp [Cfg.layout]; omega) (by simp [Cfg.layout]; omega)
152
+
153
+ /-- Return stack full: the guard exits 6. -/
154
+ theorem ovfR_trap (hg : Geom c) (hs : Rel0 c s w)
155
+ (h : ¬ c.rBase + 8 * (s.rstack.length + 1) ≤ c.rEnd) :
156
+ Run fs (ovf rpG c.layout.rBase) w (.exit 6) := by
157
+ have := hg.rEnd_le; have := hg.msize_lt; have := hg.rBase_8; have := hs.capR
158
+ exact ovf_trap hs.rp (rp_lt hg hs) (by simp [Cfg.layout]; omega) (by simp [Cfg.layout]; omega)
159
+
160
+ end Ovf
161
+
162
+ end Wasm
163
+ end WordDialect
backend/wasm/Wasm/Init.lean ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.Correct
2
+ import Wasm.Emit
3
+
4
+ /-!
5
+ # Wasm.Init
6
+
7
+ The state a module emitted by `Wasm.Emit.program` starts in, and the end-to-end theorem from it.
8
+
9
+ `initState r` is what WebAssembly instantiation produces for the module's declarations, built
10
+ from the same constants and byte encoding the emitter prints:
11
+ * globals `$pc $sp $rp $ap` (indices 0 1 2 3) initialised to `0`, `dEnd`, `rEnd`, `aEnd`;
12
+ * linear memory of `pages` pages, all zero except the data segment at `mb`, which holds the IR
13
+ memory image word by word (`byteOf`, little-endian);
14
+ * an empty operand stack.
15
+ That an engine instantiates the module this way is the WebAssembly specification's instantiation
16
+ rule; like the rest of the model, it is trusted and checked by `wasmw`.
17
+
18
+ `module_correct`: for every program and memory image that fit the runtime's fixed layout, running
19
+ the module's start function from `initState` reaches the IR outcome from `State.init`, or exits 6.
20
+ The only remaining hypotheses are about the program itself.
21
+ -/
22
+
23
+ namespace WordDialect
24
+ namespace Wasm
25
+ namespace Emit
26
+
27
+ /-- Byte `p` of linear memory at instantiation: the data segment, else zero. -/
28
+ def imageByte (img : List Nat) (mb p : Nat) : Nat :=
29
+ if mb ≤ p ∧ p < mb + 8 * img.length then byteOf (img.getD ((p - mb) / 8) 0) ((p - mb) % 8) else 0
30
+
31
+ /-- Globals at instantiation: `$pc = 0`, `$sp = dEnd`, `$rp = rEnd`, `$ap = aEnd`. -/
32
+ def initGlobals (r : Runtime) : Nat → Val := fun j =>
33
+ if j = pcG then .i32 (ofN 0) else if j = spG then .i32 (ofN r.dEnd)
34
+ else if j = rpG then .i32 (ofN r.rEnd) else if j = apG then .i32 (ofN r.aEnd) else .i32 0#32
35
+
36
+ /-- The machine state of the instantiated module, before its start function runs. -/
37
+ def initState (r : Runtime) : WState :=
38
+ { stack := [], globals := initGlobals r, mem := { bytes := imageByte r.memImage r.mb, size := r.msize } }
39
+
40
+ /-- The runtime's fixed placement, as a `Cfg`. -/
41
+ def Runtime.cfg (r : Runtime) : Cfg :=
42
+ { dEnd := r.dEnd, dBase := r.dBase, rEnd := r.rEnd, rBase := r.rBase, rf := r.rf,
43
+ nregs := r.nregs, mb := r.mb, M := r.memImage.length, msize := r.msize, aEnd := r.aEnd,
44
+ aBase := r.aBase }
45
+
46
+ theorem cfg_layout (r : Runtime) : r.cfg.layout = r.layout := rfl
47
+
48
+ /-- The register file and IR memory fit below the data stack. -/
49
+ def Runtime.Fits (r : Runtime) : Prop := 8 * (r.nregs + r.memImage.length) ≤ 0x10000 - 0x1000
50
+
51
+ theorem msize_eq (r : Runtime) : r.msize = 655360 := by
52
+ simp [Runtime.msize, Runtime.pages, Runtime.aEnd, Runtime.aBase, Runtime.rEnd, Runtime.rBase,
53
+ Runtime.dEnd, Runtime.dBase]
54
+
55
+ /-- The runtime's layout satisfies `Geom` whenever the register file and memory image fit. -/
56
+ theorem geom (r : Runtime) (h : r.Fits) : Geom r.cfg := by
57
+ have hm := msize_eq r
58
+ simp only [Runtime.Fits] at h
59
+ constructor <;>
60
+ simp only [Runtime.cfg, hm, Runtime.dEnd, Runtime.dBase, Runtime.rEnd, Runtime.rBase, Runtime.rf,
61
+ Runtime.mb, Runtime.aEnd, Runtime.aBase] <;> omega
62
+
63
+ theorem rdBytes_shift (b : Nat → Nat) (a : Nat) :
64
+ ∀ n, rdBytes b a n = rdBytes (fun q => b (a + q)) 0 n := by
65
+ intro n
66
+ induction n with
67
+ | zero => rfl
68
+ | succ n ih => simp only [rdBytes, ih, Nat.zero_add]
69
+
70
+ theorem rdBytes_zero (a : Nat) : ∀ n, rdBytes (fun _ => 0) a n = 0 := by
71
+ intro n; induction n with
72
+ | zero => rfl
73
+ | succ n ih => simp [rdBytes, ih]
74
+
75
+ /-- Reading word `a` of the data segment gives the image's word `a`. -/
76
+ theorem read_image (r : Runtime) (h : r.Fits) {a : Nat} (ha : a < r.memImage.length) :
77
+ (initState r).mem.read64 (r.mb + 8 * a) = some (BitVec.ofNat 64 (r.memImage.getD a 0)) := by
78
+ have hm := msize_eq r
79
+ simp only [Runtime.Fits] at h
80
+ have hsz : r.mb + 8 * a + 8 ≤ (initState r).mem.size := by
81
+ simp only [initState, hm, Runtime.mb, Runtime.rf]; omega
82
+ simp only [WMem.read64, hsz, ↓reduceIte]
83
+ congr 1
84
+ rw [rdBytes_shift]
85
+ have hc : rdBytes (fun q => (initState r).mem.bytes (r.mb + 8 * a + q)) 0 8 =
86
+ rdBytes (fun q => (r.memImage.getD a 0 / 256 ^ q) % 256) 0 8 := by
87
+ apply rdBytes_congr
88
+ intro i hi
89
+ have h1 : r.mb ≤ r.mb + 8 * a + i ∧ r.mb + 8 * a + i < r.mb + 8 * r.memImage.length := by
90
+ omega
91
+ have h2 : (r.mb + 8 * a + i - r.mb) / 8 = a := by omega
92
+ have h3 : (r.mb + 8 * a + i - r.mb) % 8 = i := by omega
93
+ simp only [initState, imageByte, Nat.zero_add]
94
+ simp only [h1, and_self, ↓reduceIte, h2, h3]
95
+ rfl
96
+ rw [hc, rdBytes_pow]
97
+ apply BitVec.eq_of_toNat_eq
98
+ simp [BitVec.toNat_ofNat, Nat.mod_mod_of_dvd]
99
+
100
+ /-- Outside the data segment, linear memory reads as zero. -/
101
+ theorem read_zero (r : Runtime) (h : r.Fits) {A : Nat} (hA : A + 8 ≤ r.mb) :
102
+ (initState r).mem.read64 A = some 0#64 := by
103
+ have hm := msize_eq r
104
+ simp only [Runtime.Fits] at h
105
+ have hsz : A + 8 ≤ (initState r).mem.size := by
106
+ simp only [initState, hm, Runtime.mb, Runtime.rf] at hA ⊢; omega
107
+ simp only [WMem.read64, hsz, ↓reduceIte]
108
+ congr 1
109
+ have hc : rdBytes (initState r).mem.bytes A 8 = rdBytes (fun _ => 0) A 8 := by
110
+ apply rdBytes_congr
111
+ intro i hi
112
+ have : ¬ (r.mb ≤ A + i ∧ A + i < r.mb + 8 * r.memImage.length) := by omega
113
+ simp [initState, imageByte, this]
114
+ rw [hc, rdBytes_zero]
115
+
116
+ /-- The instantiated module is related to the IR's initial state: `RelD` holds at the top of the
117
+ dispatch loop, for any program length. -/
118
+ theorem init_relD (r : Runtime) (h : r.Fits) (n : Nat) :
119
+ RelD r.cfg n (State.init (Memory.ofImage r.memImage)) (initState r) := by
120
+ have hg := geom r h
121
+ have hm := msize_eq r
122
+ have hF := h
123
+ simp only [Runtime.Fits] at hF
124
+ refine init_rel n hg rfl rfl rfl rfl ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_
125
+ · simp [initState, initGlobals]
126
+ · simp [initState, initGlobals, Runtime.cfg, spG, pcG]
127
+ · simp [initState, initGlobals, Runtime.cfg, spG, pcG, rpG]
128
+ · rfl
129
+ · simp [initState, initGlobals, Runtime.cfg, spG, pcG, rpG, apG]
130
+ · intro reg hr
131
+ have hr' : reg < r.nregs := hr
132
+ have := read_zero r h (A := r.rf + 8 * reg) (by simp only [Runtime.mb]; omega)
133
+ simpa [State.init, Runtime.cfg] using this
134
+ · intro a ha
135
+ have ha' : a < r.memImage.length := ha
136
+ have hlt : a < 2 ^ 64 := by omega
137
+ have := read_image r h ha'
138
+ simpa [State.init, Memory.ofImage, Runtime.cfg, BitVec.toNat_ofNat, Nat.mod_eq_of_lt hlt] using this
139
+ · intro a
140
+ rfl
141
+ · simp [initState, Runtime.cfg]
142
+
143
+ /-- End to end: the module `Wasm.Emit.program r p` declares, instantiated, runs its start function
144
+ to the outcome the IR reaches from `State.init` on the memory image, or exits 6 (stack
145
+ overflow). Hypotheses are only about the program: it fits the layout, it has fewer than `2^32`
146
+ instructions, and its register indices are in range. -/
147
+ theorem module_correct (r : Runtime) (p : Prog 64) (h : r.Fits) (hn : p.length < 2 ^ 32)
148
+ (hreg : RegsOk r.cfg p) {o : Outcome 64} (hx : Exec p (State.init (Memory.ofImage r.memImage)) o) :
149
+ FinalMain r.cfg (funcsOf r.layout p) (initState r) o ∨
150
+ Run (funcsOf r.layout p) mainBody (initState r) (.exit overflowCode) := by
151
+ rw [← cfg_layout]
152
+ exact lowerProg_correct_or_overflow (geom r h) hn hreg hx (init_relD r h p.length)
153
+
154
+ end Emit
155
+ end Wasm
156
+ end WordDialect
backend/wasm/Wasm/Isa.lean ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /-!
2
+ # Wasm.Isa
3
+
4
+ A model of the WebAssembly subset the backend emits, following the WebAssembly core
5
+ specification:
6
+
7
+ * typed operand stack (`i32` / `i64`), mutable globals, linear memory, a function table;
8
+ * `i64.load`/`i64.store` on **bytes** (little-endian), taking an `i32` address plus a static offset
9
+ computed in unbounded arithmetic; an access with any byte outside the memory traps. There is no
10
+ aliasing assumption: overlapping accesses are modelled exactly;
11
+ * `i64.div_s` traps on division by zero and on `INT64_MIN / -1`; `i64.div_u` traps on division by
12
+ zero; shifts and rotates take the count modulo 64 (the spec's definition);
13
+ * `if` (without `else`) and `loop`/`br` with the spec's label discipline, including unwinding the
14
+ operand stack to the label height on a branch; `call_indirect` through the function table (a
15
+ callee runs on a fresh operand stack and returns the single value it leaves).
16
+
17
+ Two host calls are terminal pseudo-instructions: `exitTrap c` (`i32.const c; call $proc_exit`) and
18
+ `exitHalt` (`global.get sp; call $halt`, which dumps the machine state and exits 0).
19
+
20
+ Semantics is a big-step relation, so loops need no fuel. That the model matches a real
21
+ WebAssembly engine is an assumption, tested by differential execution (`wasmw`).
22
+ -/
23
+
24
+ namespace WordDialect
25
+ namespace Wasm
26
+
27
+ abbrev W := BitVec 64
28
+ abbrev W32 := BitVec 32
29
+
30
+ inductive Val where
31
+ | i32 (v : W32)
32
+ | i64 (v : W)
33
+ deriving DecidableEq
34
+
35
+ /-- Little-endian read of `n` bytes starting at `a` (each byte taken modulo 256). -/
36
+ def rdBytes (b : Nat → Nat) (a : Nat) : Nat → Nat
37
+ | 0 => 0
38
+ | n + 1 => rdBytes b a n + (b (a + n) % 256) * 256 ^ n
39
+
40
+ structure WMem where
41
+ bytes : Nat → Nat
42
+ size : Nat
43
+
44
+ def WMem.read64 (m : WMem) (a : Nat) : Option W :=
45
+ if a + 8 ≤ m.size then some (BitVec.ofNat 64 (rdBytes m.bytes a 8)) else none
46
+
47
+ def WMem.write64 (m : WMem) (a : Nat) (v : W) : Option WMem :=
48
+ if a + 8 ≤ m.size then
49
+ some { m with bytes := fun p => if a ≤ p ∧ p < a + 8 then (v.toNat / 256 ^ (p - a)) % 256 else m.bytes p }
50
+ else none
51
+
52
+ structure WState where
53
+ stack : List Val
54
+ globals : Nat → Val
55
+ mem : WMem
56
+
57
+ inductive WI where
58
+ | i32const (v : W32)
59
+ | i64const (v : W)
60
+ | globalGet (i : Nat)
61
+ | globalSet (i : Nat)
62
+ | i64load (off : Nat)
63
+ | i64store (off : Nat)
64
+ | i64add | i64sub | i64mul | i64and | i64or | i64xor
65
+ | i64shl | i64shru | i64rotl | i64rotr | i64divu | i64divs
66
+ | i64eq | i64ne | i64ltu | i64leu | i64gtu | i64geu | i64lts | i64les | i64gts | i64ges
67
+ | i64eqz
68
+ | i32add | i32sub | i32shl | i32gtu | i32eq
69
+ | i32wrap
70
+ | i64extu
71
+ | select
72
+ | loop (body : List WI)
73
+ | ite (thn : List WI)
74
+ | br (l : Nat)
75
+ | callIndirect
76
+ | exitTrap (code : Nat)
77
+ | exitHalt
78
+
79
+ /-- Control constructs, calls and terminal pseudo-instructions (handled by `RunI`, not `stepI`). -/
80
+ def WI.isCtl : WI → Bool
81
+ | .loop _ | .ite _ | .br _ | .callIndirect | .exitTrap _ | .exitHalt => true
82
+ | _ => false
83
+
84
+ def bool32 (b : Bool) : W32 := if b then 1#32 else 0#32
85
+
86
+ def bin64 (f : W → W → W) (s : WState) : Option WState :=
87
+ match s.stack with
88
+ | .i64 b :: .i64 a :: r => some { s with stack := .i64 (f a b) :: r }
89
+ | _ => none
90
+
91
+ def cmp64 (f : W → W → Bool) (s : WState) : Option WState :=
92
+ match s.stack with
93
+ | .i64 b :: .i64 a :: r => some { s with stack := .i32 (bool32 (f a b)) :: r }
94
+ | _ => none
95
+
96
+ def bin32 (f : W32 → W32 → W32) (s : WState) : Option WState :=
97
+ match s.stack with
98
+ | .i32 b :: .i32 a :: r => some { s with stack := .i32 (f a b) :: r }
99
+ | _ => none
100
+
101
+ /-- One non-control instruction; `none` is a trap. -/
102
+ def stepI : WI → WState → Option WState
103
+ | .i32const v, s => some { s with stack := .i32 v :: s.stack }
104
+ | .i64const v, s => some { s with stack := .i64 v :: s.stack }
105
+ | .globalGet i, s => some { s with stack := s.globals i :: s.stack }
106
+ | .globalSet i, s =>
107
+ match s.stack with
108
+ | v :: r => some { s with stack := r, globals := fun j => if j = i then v else s.globals j }
109
+ | _ => none
110
+ | .i64load off, s =>
111
+ match s.stack with
112
+ | .i32 a :: r =>
113
+ match s.mem.read64 (a.toNat + off) with
114
+ | some v => some { s with stack := .i64 v :: r }
115
+ | none => none
116
+ | _ => none
117
+ | .i64store off, s =>
118
+ match s.stack with
119
+ | .i64 v :: .i32 a :: r =>
120
+ match s.mem.write64 (a.toNat + off) v with
121
+ | some m => some { s with stack := r, mem := m }
122
+ | none => none
123
+ | _ => none
124
+ | .i64add, s => bin64 (· + ·) s
125
+ | .i64sub, s => bin64 (· - ·) s
126
+ | .i64mul, s => bin64 (· * ·) s
127
+ | .i64and, s => bin64 (· &&& ·) s
128
+ | .i64or, s => bin64 (· ||| ·) s
129
+ | .i64xor, s => bin64 (· ^^^ ·) s
130
+ | .i64shl, s => bin64 (fun a b => a <<< (b.toNat % 64)) s
131
+ | .i64shru, s => bin64 (fun a b => a >>> (b.toNat % 64)) s
132
+ | .i64rotl, s => bin64 (fun a b => a.rotateLeft (b.toNat % 64)) s
133
+ | .i64rotr, s => bin64 (fun a b => a.rotateRight (b.toNat % 64)) s
134
+ | .i64divu, s =>
135
+ match s.stack with
136
+ | .i64 b :: .i64 a :: r =>
137
+ if b = 0#64 then none
138
+ else some { s with stack := .i64 (BitVec.ofNat 64 (a.toNat / b.toNat)) :: r }
139
+ | _ => none
140
+ | .i64divs, s =>
141
+ match s.stack with
142
+ | .i64 b :: .i64 a :: r =>
143
+ if b = 0#64 ∨ (a = BitVec.intMin 64 ∧ b = BitVec.allOnes 64) then none
144
+ else some { s with stack := .i64 (BitVec.ofInt 64 (Int.tdiv a.toInt b.toInt)) :: r }
145
+ | _ => none
146
+ | .i64eq, s => cmp64 (fun a b => a == b) s
147
+ | .i64ne, s => cmp64 (fun a b => a != b) s
148
+ | .i64ltu, s => cmp64 (fun a b => a.ult b) s
149
+ | .i64leu, s => cmp64 (fun a b => a.ule b) s
150
+ | .i64gtu, s => cmp64 (fun a b => b.ult a) s
151
+ | .i64geu, s => cmp64 (fun a b => b.ule a) s
152
+ | .i64lts, s => cmp64 (fun a b => a.slt b) s
153
+ | .i64les, s => cmp64 (fun a b => a.sle b) s
154
+ | .i64gts, s => cmp64 (fun a b => b.slt a) s
155
+ | .i64ges, s => cmp64 (fun a b => b.sle a) s
156
+ | .i64eqz, s =>
157
+ match s.stack with
158
+ | .i64 a :: r => some { s with stack := .i32 (bool32 (a == 0#64)) :: r }
159
+ | _ => none
160
+ | .i32add, s => bin32 (· + ·) s
161
+ | .i32sub, s => bin32 (· - ·) s
162
+ | .i32shl, s => bin32 (fun a b => a <<< (b.toNat % 32)) s
163
+ | .i32gtu, s =>
164
+ match s.stack with
165
+ | .i32 b :: .i32 a :: r => some { s with stack := .i32 (bool32 (b.ult a)) :: r }
166
+ | _ => none
167
+ | .i32eq, s =>
168
+ match s.stack with
169
+ | .i32 b :: .i32 a :: r => some { s with stack := .i32 (bool32 (a == b)) :: r }
170
+ | _ => none
171
+ | .i32wrap, s =>
172
+ match s.stack with
173
+ | .i64 a :: r => some { s with stack := .i32 (BitVec.ofNat 32 a.toNat) :: r }
174
+ | _ => none
175
+ | .i64extu, s =>
176
+ match s.stack with
177
+ | .i32 a :: r => some { s with stack := .i64 (BitVec.ofNat 64 a.toNat) :: r }
178
+ | _ => none
179
+ | .select, s =>
180
+ match s.stack with
181
+ | .i32 c :: v2 :: v1 :: r => some { s with stack := (if c ≠ 0#32 then v1 else v2) :: r }
182
+ | _ => none
183
+ | _, _ => none
184
+
185
+ /-- Outcomes of running a list of instructions. -/
186
+ inductive Res where
187
+ | normal (s : WState)
188
+ | br (n : Nat) (s : WState)
189
+ | exit (code : Nat)
190
+ | halted (s : WState)
191
+ | fault
192
+
193
+ /-- Leaving a labelled construct entered with operand stack `entry`: a branch to label 0 is
194
+ consumed (for `if`; `loop` re-enters) and the operand stack is unwound to its height at entry;
195
+ other branches propagate outward. -/
196
+ inductive BlockOut (entry : List Val) : Res → Res → Prop where
197
+ | norm {s} : BlockOut entry (.normal s) (.normal s)
198
+ | br0 {s} : BlockOut entry (.br 0 s) (.normal { s with stack := entry })
199
+ | brS {n s} : BlockOut entry (.br (n + 1) s) (.br n s)
200
+ | exit {c} : BlockOut entry (.exit c) (.exit c)
201
+ | halted {s} : BlockOut entry (.halted s) (.halted s)
202
+ | fault : BlockOut entry .fault .fault
203
+
204
+ /-- Results that abandon the rest of an instruction sequence. -/
205
+ def Res.abrupt : Res → Prop
206
+ | .normal _ => False
207
+ | _ => True
208
+
209
+ /-- A function table: entry `i` is the body of function `i`. -/
210
+ abbrev Funcs := List (List WI)
211
+
212
+ mutual
213
+ inductive RunI (fs : Funcs) : WI → WState → Res → Prop where
214
+ | simple {i s s'} : i.isCtl = false → stepI i s = some s' → RunI fs i s (.normal s')
215
+ | simpleFault {i s} : i.isCtl = false → stepI i s = none → RunI fs i s .fault
216
+ | loopNormal {body s s'} : Run fs body s (.normal s') → RunI fs (.loop body) s (.normal s')
217
+ | loopBr0 {body s s' r} :
218
+ Run fs body s (.br 0 s') → RunI fs (.loop body) { s' with stack := s.stack } r →
219
+ RunI fs (.loop body) s r
220
+ | loopBrS {body s n s'} : Run fs body s (.br (n + 1) s') → RunI fs (.loop body) s (.br n s')
221
+ | loopExit {body s c} : Run fs body s (.exit c) → RunI fs (.loop body) s (.exit c)
222
+ | loopHalted {body s s'} : Run fs body s (.halted s') → RunI fs (.loop body) s (.halted s')
223
+ | loopFault {body s} : Run fs body s .fault → RunI fs (.loop body) s .fault
224
+ | iteThen {thn s r c res res'} :
225
+ s.stack = .i32 c :: r → c ≠ 0#32 → Run fs thn { s with stack := r } res →
226
+ BlockOut r res res' → RunI fs (.ite thn) s res'
227
+ | iteElse {thn s r c} :
228
+ s.stack = .i32 c :: r → c = 0#32 → RunI fs (.ite thn) s (.normal { s with stack := r })
229
+ | iteFault {thn s} : (∀ c r, s.stack ≠ .i32 c :: r) → RunI fs (.ite thn) s .fault
230
+ | br {l s} : RunI fs (.br l) s (.br l s)
231
+ | callNormal {s r i body s' v} :
232
+ s.stack = .i32 i :: r → fs[i.toNat]? = some body →
233
+ Run fs body { s with stack := [] } (.normal s') → s'.stack = [.i32 v] →
234
+ RunI fs .callIndirect s (.normal { s' with stack := .i32 v :: r })
235
+ | callBad {s r i body s'} :
236
+ s.stack = .i32 i :: r → fs[i.toNat]? = some body →
237
+ Run fs body { s with stack := [] } (.normal s') → (∀ v, s'.stack ≠ [.i32 v]) →
238
+ RunI fs .callIndirect s .fault
239
+ | callAbrupt {s r i body res} :
240
+ s.stack = .i32 i :: r → fs[i.toNat]? = some body →
241
+ Run fs body { s with stack := [] } res → res.abrupt →
242
+ RunI fs .callIndirect s (match res with | .br _ _ => .fault | other => other)
243
+ | callMissing {s r i} :
244
+ s.stack = .i32 i :: r → fs[i.toNat]? = none → RunI fs .callIndirect s .fault
245
+ | callBadStack {s} : (∀ i r, s.stack ≠ .i32 i :: r) → RunI fs .callIndirect s .fault
246
+ | exitTrap {c s} : RunI fs (.exitTrap c) s (.exit c)
247
+ | exitHalt {s} : RunI fs .exitHalt s (.halted s)
248
+
249
+ inductive Run (fs : Funcs) : List WI → WState → Res → Prop where
250
+ | nil {s} : Run fs [] s (.normal s)
251
+ | consNormal {i is s s' r} : RunI fs i s (.normal s') → Run fs is s' r → Run fs (i :: is) s r
252
+ | consAbrupt {i is s r} : RunI fs i s r → r.abrupt → Run fs (i :: is) s r
253
+ end
254
+
255
+ end Wasm
256
+ end WordDialect
backend/wasm/Wasm/Lower.lean ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import WordDialect
2
+ import Wasm.Isa
3
+
4
+ /-!
5
+ # Wasm.Lower
6
+
7
+ Lowering of Universal Word IR (64-bit words) to WebAssembly.
8
+
9
+ The IR has arbitrary jumps, calls and returns; WebAssembly has structured control. Solution: every
10
+ IR instruction `k` becomes its own function `f_k : () → i32` that performs the instruction and
11
+ returns the index of the next IR instruction; the module is a dispatch loop
12
+
13
+ loop { pc := call_indirect (table[pc]) ; br 0 }
14
+
15
+ Function `n` (past the last instruction) is the bad-pc stub. Every jump target is clamped to `n` at
16
+ lowering time, so a jump past the end traps `badPc` exactly as in the IR semantics.
17
+
18
+ Globals: `0 = pc` (i32), `1 = sp` data-stack pointer (i32, grows down, empty = `dEnd`), `2 = rp`
19
+ return-stack pointer (i32, grows down, empty = `rEnd`), `3 = ap` auxiliary-stack pointer (i32, grows
20
+ down, empty = `aEnd`). The register file, IR memory and the three stacks live in linear memory at the
21
+ static addresses of `Layout`; the operand stack is used only for temporaries. `call` pushes the IR
22
+ index of the return instruction onto the return stack; `ret` pops it. `tor`/`fromr`/`rfetch` move or
23
+ copy words between the data stack and the auxiliary stack; taking from an empty auxiliary stack
24
+ exits 5 (`returnUnderflow`), as `ret` does on an empty return stack.
25
+
26
+ Every `load`/`store` is bounds-checked against the IR memory size, and every popping instruction
27
+ checks the operand count against `dEnd`, so traps match the IR semantics. Every instruction that
28
+ grows the data stack checks for room above `dBase`, `call` checks for room above `rBase`, and `tor`
29
+ checks for room above `aBase`; on
30
+ overflow the module exits with code 6. The IR has unbounded stacks, so this exit has no IR
31
+ counterpart: it reports that the run exceeded the target's stack capacity.
32
+ -/
33
+
34
+ namespace WordDialect
35
+ namespace Wasm
36
+
37
+ structure Layout where
38
+ dEnd : Nat -- data stack: empty at `dEnd`
39
+ rEnd : Nat -- return stack: empty at `rEnd`
40
+ rf : Nat -- virtual register file, cell `r` at `rf + 8r`
41
+ mb : Nat -- IR memory, word `a` at `mb + 8a`
42
+ M : Nat -- IR memory size in words
43
+ dBase : Nat -- data stack: full at `dBase`
44
+ rBase : Nat -- return stack: full at `rBase`
45
+ aEnd : Nat -- auxiliary stack: empty at `aEnd`
46
+ aBase : Nat -- auxiliary stack: full at `aBase`
47
+
48
+ def pcG : Nat := 0
49
+ def spG : Nat := 1
50
+ def rpG : Nat := 2
51
+ def apG : Nat := 3
52
+
53
+ def i32c (n : Nat) : WI := .i32const (BitVec.ofNat 32 n)
54
+ def i64c (n : Nat) : WI := .i64const (BitVec.ofNat 64 n)
55
+
56
+ def spAdd (d : Nat) : List WI := [.globalGet spG, i32c d, .i32add, .globalSet spG]
57
+ def spSub (d : Nat) : List WI := [.globalGet spG, i32c d, .i32sub, .globalSet spG]
58
+
59
+ /-- Operand-count guard for `k` operands: trap `stackUnderflow` unless `sp ≤ dEnd - 8k`. -/
60
+ def uf (L : Layout) (k : Nat) : List WI :=
61
+ if k = 0 then [] else [.globalGet spG, i32c (L.dEnd - 8 * k), .i32gtu, .ite [.exitTrap 1]]
62
+
63
+ /-- Overflow guard on the stack whose pointer is global `g`: exit 6 unless one more word fits,
64
+ i.e. unless `base + 8 ≤ ptr`. -/
65
+ def ovf (g base : Nat) : List WI := [i32c (base + 8), .globalGet g, .i32gtu, .ite [.exitTrap 6]]
66
+
67
+ /-- Push the word computed by `val`. -/
68
+ def pushWith (val : List WI) : List WI :=
69
+ spSub 8 ++ [.globalGet spG] ++ val ++ [.i64store 0]
70
+
71
+ /-- `[sp + off]` as an `i64` on the operand stack. -/
72
+ def ldS (off : Nat) : List WI := [.globalGet spG, .i64load off]
73
+
74
+ /-- Address of IR memory word `[sp+0]`, bounds-checked first. -/
75
+ def memAddr (L : Layout) : List WI :=
76
+ ldS 0 ++ [.i64const (BitVec.ofNat 64 L.M), .i64geu, .ite [.exitTrap 2]] ++
77
+ ldS 0 ++ [.i32wrap, i32c 3, .i32shl, i32c L.mb, .i32add]
78
+
79
+ /-- Binary operation on `[sp+8] (op) [sp+0]`, result in `[sp+8]`, then pop one. -/
80
+ def binBody (op : List WI) : List WI :=
81
+ [.globalGet spG] ++ ldS 8 ++ ldS 0 ++ op ++ [.i64store 8] ++ spAdd 8
82
+
83
+ def binOp (L : Layout) (op : List WI) : List WI := uf L 2 ++ binBody op
84
+
85
+ /-- Shifts: a count of 64 or more yields 0. -/
86
+ def shiftBody (op : WI) : List WI :=
87
+ [.globalGet spG, .i64const 0#64] ++ ldS 8 ++ ldS 0 ++ [op] ++
88
+ ldS 0 ++ [.i64const 64#64, .i64geu, .select, .i64store 8] ++ spAdd 8
89
+
90
+ def shiftOp (L : Layout) (op : WI) : List WI := uf L 2 ++ shiftBody op
91
+
92
+ /-- Trap `divideByZero` when the divisor `[sp+0]` is zero. -/
93
+ def zeroGuard : List WI := ldS 0 ++ [.i64eqz, .ite [.exitTrap 3]]
94
+
95
+ /-- Signed division of `[sp+8]` by `[sp+0]` (non-zero), result in `[sp+8]`, pop one. A divisor of -1
96
+ yields `0 - a` and the divisor passed to `i64.div_s` is then 1, so the spec's `INT64_MIN / -1` trap
97
+ cannot occur. -/
98
+ def sdivBody : List WI :=
99
+ [.globalGet spG, .i64const 0#64] ++ ldS 8 ++ [.i64sub] ++
100
+ ldS 8 ++ [.i64const 1#64] ++ ldS 0 ++ ldS 0 ++ [.i64const (BitVec.allOnes 64), .i64eq, .select,
101
+ .i64divs] ++ ldS 0 ++ [.i64const (BitVec.allOnes 64), .i64eq, .select, .i64store 8] ++
102
+ spAdd 8
103
+
104
+ def cmpOp : WordDialect.Cond → WI
105
+ | .eq => .i64eq | .ne => .i64ne | .ult => .i64ltu | .ule => .i64leu | .ugt => .i64gtu
106
+ | .uge => .i64geu | .slt => .i64lts | .sle => .i64les | .sgt => .i64gts | .sge => .i64ges
107
+
108
+ /-- Code for IR instruction number `k` of a program of `n` instructions: performs it and leaves
109
+ the next instruction index on the operand stack. -/
110
+ def lowerInstr (L : Layout) (n k : Nat) : WordDialect.Instr 64 → List WI
111
+ | .word w => ovf spG L.dBase ++ pushWith [.i64const w] ++ [i32c (k + 1)]
112
+ | .ptr p => ovf spG L.dBase ++ pushWith [.i64const p.toWord] ++ [i32c (k + 1)]
113
+ | .load =>
114
+ uf L 1 ++ [.globalGet spG] ++ memAddr L ++ [.i64load 0, .i64store 0] ++ [i32c (k + 1)]
115
+ | .store =>
116
+ uf L 2 ++ memAddr L ++ ldS 8 ++ [.i64store 0] ++ spAdd 16 ++ [i32c (k + 1)]
117
+ | .add => binOp L [.i64add] ++ [i32c (k + 1)]
118
+ | .sub => binOp L [.i64sub] ++ [i32c (k + 1)]
119
+ | .mul => binOp L [.i64mul] ++ [i32c (k + 1)]
120
+ | .and => binOp L [.i64and] ++ [i32c (k + 1)]
121
+ | .or => binOp L [.i64or] ++ [i32c (k + 1)]
122
+ | .xor => binOp L [.i64xor] ++ [i32c (k + 1)]
123
+ | .div => uf L 2 ++ zeroGuard ++ binBody [.i64divu] ++ [i32c (k + 1)]
124
+ | .sdiv => uf L 2 ++ zeroGuard ++ sdivBody ++ [i32c (k + 1)]
125
+ | .not =>
126
+ uf L 1 ++ [.globalGet spG] ++ ldS 0 ++ [.i64const (BitVec.allOnes 64), .i64xor, .i64store 0] ++
127
+ [i32c (k + 1)]
128
+ | .shl => shiftOp L .i64shl ++ [i32c (k + 1)]
129
+ | .shr => shiftOp L .i64shru ++ [i32c (k + 1)]
130
+ | .rotl => binOp L [.i64rotl] ++ [i32c (k + 1)]
131
+ | .rotr => binOp L [.i64rotr] ++ [i32c (k + 1)]
132
+ | .cmp c => binOp L [cmpOp c, .i64extu] ++ [i32c (k + 1)]
133
+ | .select =>
134
+ uf L 3 ++ [.globalGet spG] ++ ldS 16 ++ ldS 8 ++ ldS 0 ++ [.i64const 0#64, .i64ne, .select,
135
+ .i64store 16] ++ spAdd 16 ++ [i32c (k + 1)]
136
+ | .jmp t => [i32c (min t n)]
137
+ | .branch t =>
138
+ uf L 1 ++ [i32c (min t n), i32c (k + 1)] ++ ldS 0 ++ [.i64const 0#64, .i64ne, .select] ++ spAdd 8
139
+ | .call t =>
140
+ ovf rpG L.rBase ++ [.globalGet rpG, i32c 8, .i32sub, .globalSet rpG, .globalGet rpG, i64c (k + 1), .i64store 0,
141
+ i32c (min t n)]
142
+ | .ret =>
143
+ [.globalGet rpG, i32c L.rEnd, .i32eq, .ite [.exitTrap 5], .globalGet rpG, .i64load 0, .i32wrap,
144
+ .globalGet rpG, i32c 8, .i32add, .globalSet rpG]
145
+ | .push r => ovf spG L.dBase ++ pushWith [i32c (L.rf + 8 * r), .i64load 0] ++ [i32c (k + 1)]
146
+ | .pop r =>
147
+ uf L 1 ++ [i32c (L.rf + 8 * r)] ++ ldS 0 ++ [.i64store 0] ++ spAdd 8 ++ [i32c (k + 1)]
148
+ | .dup => uf L 1 ++ ovf spG L.dBase ++ spSub 8 ++ [.globalGet spG] ++ ldS 8 ++ [.i64store 0] ++ [i32c (k + 1)]
149
+ | .drop => uf L 1 ++ spAdd 8 ++ [i32c (k + 1)]
150
+ | .swap =>
151
+ uf L 2 ++ [.globalGet spG] ++ ldS 8 ++ [.globalGet spG] ++ ldS 0 ++ [.i64store 8, .i64store 0] ++
152
+ [i32c (k + 1)]
153
+ | .over =>
154
+ uf L 2 ++ ovf spG L.dBase ++ spSub 8 ++ [.globalGet spG, .globalGet spG, .i64load 16, .i64store 0] ++ [i32c (k + 1)]
155
+ | .rot =>
156
+ uf L 3 ++ [.globalGet spG] ++ ldS 16 ++ [.globalGet spG] ++ ldS 0 ++ [.globalGet spG] ++ ldS 8 ++
157
+ [.i64store 16, .i64store 8, .i64store 0] ++ [i32c (k + 1)]
158
+ | .tor =>
159
+ uf L 1 ++ ovf apG L.aBase ++ [.globalGet apG, i32c 8, .i32sub, .globalSet apG, .globalGet apG] ++
160
+ ldS 0 ++ [.i64store 0] ++ spAdd 8 ++ [i32c (k + 1)]
161
+ | .fromr =>
162
+ [.globalGet apG, i32c L.aEnd, .i32eq, .ite [.exitTrap 5]] ++ ovf spG L.dBase ++
163
+ pushWith [.globalGet apG, .i64load 0] ++ [.globalGet apG, i32c 8, .i32add, .globalSet apG] ++
164
+ [i32c (k + 1)]
165
+ | .rfetch =>
166
+ [.globalGet apG, i32c L.aEnd, .i32eq, .ite [.exitTrap 5]] ++ ovf spG L.dBase ++
167
+ pushWith [.globalGet apG, .i64load 0] ++ [i32c (k + 1)]
168
+ | .halt => [.exitHalt]
169
+
170
+ /-- Function `k`: the code of IR instruction `k`, or the bad-pc stub past the end. -/
171
+ def instrCode (L : Layout) (p : Prog 64) (k : Nat) : List WI :=
172
+ match p[k]? with
173
+ | some i => lowerInstr L p.length k i
174
+ | none => [.exitTrap 4]
175
+
176
+ /-- The function table: one function per IR instruction, then the bad-pc stub. -/
177
+ def funcsOf (L : Layout) (p : Prog 64) : Funcs :=
178
+ (List.range (p.length + 1)).map (instrCode L p)
179
+
180
+ /-- The module's start function: the dispatch loop. -/
181
+ def mainBody : List WI :=
182
+ [.loop [.globalGet pcG, .callIndirect, .globalSet pcG, .br 0]]
183
+
184
+ end Wasm
185
+ end WordDialect
backend/wasm/Wasm/Mem.lean ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.Isa
2
+
3
+ /-!
4
+ # Wasm.Mem
5
+
6
+ Byte-level memory facts: a write of eight bytes is read back by a read at the same address, and
7
+ leaves every read at a disjoint address unchanged. These replace any "word cell" memory assumption.
8
+ -/
9
+
10
+ namespace WordDialect
11
+ namespace Wasm
12
+
13
+ theorem rdBytes_congr {b b' : Nat → Nat} {a : Nat} :
14
+ ∀ {n : Nat}, (∀ i, i < n → b (a + i) = b' (a + i)) → rdBytes b a n = rdBytes b' a n := by
15
+ intro n
16
+ induction n with
17
+ | zero => intro _; rfl
18
+ | succ n ih =>
19
+ intro h
20
+ simp only [rdBytes]
21
+ rw [ih (fun i hi => h i (by omega)), h n (by omega)]
22
+
23
+ theorem rdBytes_pow (v : Nat) : ∀ n : Nat,
24
+ rdBytes (fun p => (v / 256 ^ p) % 256) 0 n = v % 256 ^ n := by
25
+ intro n
26
+ induction n with
27
+ | zero => simp [rdBytes, Nat.mod_one]
28
+ | succ n ih =>
29
+ simp only [rdBytes, Nat.zero_add]
30
+ rw [ih, Nat.pow_succ, Nat.mod_mul]
31
+ simp [Nat.mod_mod, Nat.mul_comm]
32
+
33
+ theorem write64_size {m m' : WMem} {a : Nat} {v : W} (h : m.write64 a v = some m') :
34
+ m'.size = m.size ∧ a + 8 ≤ m.size := by
35
+ unfold WMem.write64 at h
36
+ split at h
37
+ · rename_i hlt; simp at h; subst h; exact ⟨rfl, hlt⟩
38
+ · simp at h
39
+
40
+ theorem write64_some {m : WMem} {a : Nat} {v : W} (h : a + 8 ≤ m.size) :
41
+ ∃ m', m.write64 a v = some m' := by
42
+ simp [WMem.write64, h]
43
+
44
+ theorem read64_write64_same {m m' : WMem} {a : Nat} {v : W} (h : m.write64 a v = some m') :
45
+ m'.read64 a = some v := by
46
+ obtain ⟨hs, hlt⟩ := write64_size h
47
+ unfold WMem.write64 at h
48
+ rw [if_pos hlt] at h
49
+ simp at h
50
+ subst h
51
+ unfold WMem.read64
52
+ rw [if_pos (by simpa using hlt)]
53
+ congr 1
54
+ have hc : rdBytes (fun p => if a ≤ p ∧ p < a + 8 then (v.toNat / 256 ^ (p - a)) % 256 else m.bytes p) a 8
55
+ = rdBytes (fun p => (v.toNat / 256 ^ p) % 256) 0 8 := by
56
+ rw [show rdBytes (fun p => (v.toNat / 256 ^ p) % 256) 0 8 =
57
+ rdBytes (fun p => (v.toNat / 256 ^ (p - a)) % 256) a 8 from ?_]
58
+ · apply rdBytes_congr
59
+ intro i hi
60
+ have h1 : a ≤ a + i := by omega
61
+ have h2 : a + i < a + 8 := by omega
62
+ simp [h1, h2]
63
+ · simp only [rdBytes]
64
+ simp [Nat.add_sub_cancel_left]
65
+ rw [hc, rdBytes_pow]
66
+ have : v.toNat < 256 ^ 8 := by have := v.isLt; simpa using this
67
+ rw [Nat.mod_eq_of_lt this]
68
+ simp
69
+
70
+ theorem read64_write64_other {m m' : WMem} {a b : Nat} {v : W} (h : m.write64 a v = some m')
71
+ (hd : b + 8 ≤ a ∨ a + 8 ≤ b) : m'.read64 b = m.read64 b := by
72
+ obtain ⟨hs, hlt⟩ := write64_size h
73
+ unfold WMem.write64 at h
74
+ rw [if_pos hlt] at h
75
+ simp at h
76
+ subst h
77
+ unfold WMem.read64
78
+ simp only []
79
+ by_cases hb : b + 8 ≤ m.size
80
+ · rw [if_pos hb, if_pos hb]
81
+ congr 2
82
+ apply rdBytes_congr
83
+ intro i hi
84
+ have : ¬ (a ≤ b + i ∧ b + i < a + 8) := by omega
85
+ simp [this]
86
+ · rw [if_neg hb, if_neg hb]
87
+
88
+ end Wasm
89
+ end WordDialect
backend/wasm/Wasm/Rel.lean ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import WordDialect
2
+ import Wasm.Lower
3
+ import Wasm.Mem
4
+
5
+ /-!
6
+ # Wasm.Rel
7
+
8
+ The relation between an IR state and a WebAssembly machine state.
9
+
10
+ Linear-memory regions (byte intervals, all within the memory and within the 32-bit address space,
11
+ pairwise disjoint):
12
+ * data stack `[dBase, dEnd)`, with `k` words on it the top is at `dEnd - 8k`;
13
+ * register file `[rf, rf + 8 nregs)`;
14
+ * IR memory `[mb, mb + 8 M)`, word `a` at `mb + 8a`;
15
+ * return stack `[rBase, rEnd)`, with `k` entries the top is at `rEnd - 8k`; an entry is the IR index
16
+ of the return instruction;
17
+ * auxiliary stack `[aBase, aEnd)`, with `k` words on it the top is at `aEnd - 8k`.
18
+ Everything is stated at natural-number byte addresses and `read64` on the byte memory.
19
+ -/
20
+
21
+ namespace WordDialect
22
+ namespace Wasm
23
+
24
+ structure Cfg where
25
+ dEnd : Nat
26
+ dBase : Nat
27
+ rEnd : Nat
28
+ rBase : Nat
29
+ rf : Nat
30
+ nregs : Nat
31
+ mb : Nat
32
+ M : Nat
33
+ msize : Nat
34
+ aEnd : Nat
35
+ aBase : Nat
36
+
37
+ def Cfg.layout (c : Cfg) : Layout :=
38
+ { dEnd := c.dEnd, rEnd := c.rEnd, rf := c.rf, mb := c.mb, M := c.M, dBase := c.dBase,
39
+ rBase := c.rBase, aEnd := c.aEnd, aBase := c.aBase }
40
+
41
+ structure Geom (c : Cfg) : Prop where
42
+ msize_lt : c.msize < 2 ^ 32
43
+ dEnd_le : c.dEnd ≤ c.msize
44
+ rEnd_le : c.rEnd ≤ c.msize
45
+ rf_le : c.rf + 8 * c.nregs ≤ c.msize
46
+ mb_le : c.mb + 8 * c.M ≤ c.msize
47
+ dBase_le : c.dBase ≤ c.dEnd
48
+ rBase_le : c.rBase ≤ c.rEnd
49
+ dBase_8 : c.dBase + 8 ≤ c.dEnd
50
+ rBase_8 : c.rBase + 8 ≤ c.rEnd
51
+ dEnd_ge : 24 ≤ c.dEnd
52
+ dS_F : c.dEnd ≤ c.rf ∨ c.rf + 8 * c.nregs ≤ c.dBase
53
+ dS_M : c.dEnd ≤ c.mb ∨ c.mb + 8 * c.M ≤ c.dBase
54
+ dS_K : c.dEnd ≤ c.rBase ∨ c.rEnd ≤ c.dBase
55
+ dF_M : c.rf + 8 * c.nregs ≤ c.mb ∨ c.mb + 8 * c.M ≤ c.rf
56
+ dF_K : c.rf + 8 * c.nregs ≤ c.rBase ∨ c.rEnd ≤ c.rf
57
+ dM_K : c.mb + 8 * c.M ≤ c.rBase ∨ c.rEnd ≤ c.mb
58
+ aEnd_le : c.aEnd ≤ c.msize
59
+ aBase_8 : c.aBase + 8 ≤ c.aEnd
60
+ dS_A : c.dEnd ≤ c.aBase ∨ c.aEnd ≤ c.dBase
61
+ dF_A : c.rf + 8 * c.nregs ≤ c.aBase ∨ c.aEnd ≤ c.rf
62
+ dM_A : c.mb + 8 * c.M ≤ c.aBase ∨ c.aEnd ≤ c.mb
63
+ dK_A : c.rEnd ≤ c.aBase ∨ c.aEnd ≤ c.rBase
64
+
65
+ /-- A natural number as an `i32`. -/
66
+ def ofN (a : Nat) : W32 := BitVec.ofNat 32 a
67
+
68
+ theorem ofN_toNat {a : Nat} (h : a < 2 ^ 32) : (ofN a).toNat = a := by
69
+ simp [ofN, BitVec.toNat_ofNat, Nat.mod_eq_of_lt h]
70
+
71
+ theorem ofN_inj {a b : Nat} (ha : a < 2 ^ 32) (hb : b < 2 ^ 32) (h : ofN a = ofN b) : a = b := by
72
+ have := congrArg BitVec.toNat h
73
+ simpa [ofN, BitVec.toNat_ofNat, Nat.mod_eq_of_lt ha, Nat.mod_eq_of_lt hb] using this
74
+
75
+ theorem ofN_add (a b : Nat) : ofN a + ofN b = ofN (a + b) := (BitVec.ofNat_add a b).symm
76
+
77
+ theorem ofN_sub {a b : Nat} (h : b ≤ a) : ofN a - ofN b = ofN (a - b) := by
78
+ apply BitVec.eq_of_toNat_eq
79
+ simp only [ofN, BitVec.toNat_sub, BitVec.toNat_ofNat]
80
+ have h1 : (a - b) % 2 ^ 32 = ((2 ^ 32 - b % 2 ^ 32) + a % 2 ^ 32) % 2 ^ 32 := by omega
81
+ exact h1.symm
82
+
83
+ def StackRel (c : Cfg) (m : WMem) (d : List W) : Prop :=
84
+ ∀ i v, d[i]? = some v → m.read64 (c.dEnd - 8 * d.length + 8 * i) = some v
85
+
86
+ def RegRel (c : Cfg) (m : WMem) (regs : Nat → W) : Prop :=
87
+ ∀ r, r < c.nregs → m.read64 (c.rf + 8 * r) = some (regs r)
88
+
89
+ def MemRel (c : Cfg) (m : WMem) (im : WordDialect.Memory 64) : Prop :=
90
+ ∀ a, a < c.M → m.read64 (c.mb + 8 * a) = some (im.cell (BitVec.ofNat 64 a))
91
+
92
+ def RsRel (c : Cfg) (m : WMem) (rs : List Nat) : Prop :=
93
+ ∀ i a, rs[i]? = some a → m.read64 (c.rEnd - 8 * rs.length + 8 * i) = some (BitVec.ofNat 64 a)
94
+
95
+ def AuxRel (c : Cfg) (m : WMem) (as : List W) : Prop :=
96
+ ∀ i v, as[i]? = some v → m.read64 (c.aEnd - 8 * as.length + 8 * i) = some v
97
+
98
+ /-- The relation, except for the operand stack and the `pc` global (those are added at the top of
99
+ the dispatch loop by `RelD` in `Wasm.Correct`). -/
100
+ structure Rel0 (c : Cfg) (s : State 64) (w : WState) : Prop where
101
+ sp : w.globals spG = .i32 (ofN (c.dEnd - 8 * s.dstack.length))
102
+ rp : w.globals rpG = .i32 (ofN (c.rEnd - 8 * s.rstack.length))
103
+ stack : StackRel c w.mem s.dstack
104
+ regs : RegRel c w.mem s.regs
105
+ mem : MemRel c w.mem s.mem
106
+ rs : RsRel c w.mem s.rstack
107
+ irvalid : ∀ a : Word 64, s.mem.valid a = decide (a.toNat < c.M)
108
+ size : w.mem.size = c.msize
109
+ capD : c.dBase + 8 * s.dstack.length ≤ c.dEnd
110
+ capR : c.rBase + 8 * s.rstack.length ≤ c.rEnd
111
+ ap : w.globals apG = .i32 (ofN (c.aEnd - 8 * s.astack.length))
112
+ aux : AuxRel c w.mem s.astack
113
+ capA : c.aBase + 8 * s.astack.length ≤ c.aEnd
114
+
115
+ end Wasm
116
+ end WordDialect
backend/wasm/Wasm/Seq.lean ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.Rel
2
+
3
+ /-!
4
+ # Wasm.Seq
5
+
6
+ Straight-line execution (`execL`) with its `Run` derivation, sequencing, and the operand-count
7
+ guard `uf`.
8
+ -/
9
+
10
+ namespace WordDialect
11
+ namespace Wasm
12
+
13
+ /-- Straight-line evaluation of non-control instructions. -/
14
+ def execL : List WI → WState → Option WState
15
+ | [], s => some s
16
+ | i :: is, s => (stepI i s).bind (execL is)
17
+
18
+ theorem run_of_execL {fs : Funcs} : ∀ {is : List WI} {s s' : WState},
19
+ (∀ i ∈ is, i.isCtl = false) → execL is s = some s' → Run fs is s (.normal s')
20
+ | [], s, s', _, h => by simp [execL] at h; subst h; exact .nil
21
+ | i :: is, s, s', hc, h => by
22
+ simp only [execL] at h
23
+ cases hs : stepI i s with
24
+ | none => simp [hs] at h
25
+ | some s1 =>
26
+ rw [hs] at h
27
+ exact .consNormal (.simple (hc i (by simp)) hs)
28
+ (run_of_execL (fun j hj => hc j (by simp [hj])) h)
29
+
30
+ theorem run_append {fs : Funcs} : ∀ (a : List WI) {b : List WI} {s s' : WState} {r : Res},
31
+ Run fs a s (.normal s') → Run fs b s' r → Run fs (a ++ b) s r
32
+ | [], b, s, s', r, h1, h2 => by cases h1; simpa using h2
33
+ | i :: a, b, s, s', r, h1, h2 => by
34
+ cases h1 with
35
+ | consNormal hi hs => exact .consNormal hi (run_append a hs h2)
36
+ | consAbrupt hi hab => exact absurd hab (by simp [Res.abrupt])
37
+
38
+ theorem run_append_abrupt {fs : Funcs} : ∀ (a : List WI) {b : List WI} {s : WState} {r : Res},
39
+ Run fs a s r → r.abrupt → Run fs (a ++ b) s r
40
+ | [], b, s, r, h1, hr => by cases h1; exact absurd hr (by simp [Res.abrupt])
41
+ | i :: a, b, s, r, h1, hr => by
42
+ cases h1 with
43
+ | consNormal hi hs => exact .consNormal hi (run_append_abrupt a hs hr)
44
+ | consAbrupt hi hab => exact .consAbrupt hi hab
45
+
46
+ /-- `if` whose condition is false: continue with the condition popped. -/
47
+ theorem run_ite_false {fs : Funcs} {thn : List WI} {w : WState} {r : List Val} (hs : w.stack = .i32 0#32 :: r) :
48
+ Run fs [.ite thn] w (.normal { w with stack := r }) :=
49
+ .consNormal (.iteElse hs rfl) .nil
50
+
51
+ /-- `if (cond ≠ 0) { exit code }`. -/
52
+ theorem run_ite_exit {fs : Funcs} {w : WState} {r : List Val} {c : W32} {k : Nat}
53
+ (hs : w.stack = .i32 c :: r) (hc : c ≠ 0#32) : Run fs [.ite [.exitTrap k]] w (.exit k) :=
54
+ .consAbrupt (.iteThen hs hc (.consAbrupt .exitTrap trivial) .exit) trivial
55
+
56
+ /-- All instructions are non-control. -/
57
+ def allSimple (l : List WI) : Bool := l.all fun i => !i.isCtl
58
+
59
+ theorem allSimple_spec {l : List WI} (h : allSimple l = true) : ∀ i ∈ l, i.isCtl = false := by
60
+ intro i hi
61
+ have := List.all_eq_true.mp h i hi
62
+ simpa using this
63
+
64
+ end Wasm
65
+ end WordDialect
backend/wasm/Wasm/SimAlu.lean ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.Guard
2
+
3
+ /-!
4
+ # Wasm.SimAlu
5
+
6
+ Simulation of the binary operations: the generic `sim_binop` (operand guard, two loads, the
7
+ operation, a store into the second slot, pop one) and its instantiations.
8
+ -/
9
+
10
+ namespace WordDialect
11
+ namespace Wasm
12
+
13
+ section Sim
14
+
15
+ variable {c : Cfg} {s : State 64} {w : WState} {fs : Funcs}
16
+
17
+ theorem run_finish {u : WState} (nxt : Nat) :
18
+ Run fs [i32c nxt] u (.normal { u with stack := .i32 (ofN nxt) :: u.stack }) :=
19
+ run_of_execL (by simp [WI.isCtl, i32c]) (by simp [execL, stepI, i32c, ofN])
20
+
21
+ theorem ctl_binBody {op : List WI} (hctl : ∀ i ∈ op, i.isCtl = false) :
22
+ ∀ i ∈ binBody op, i.isCtl = false := by
23
+ intro i hi
24
+ simp only [binBody, ldS, spAdd, List.mem_append, List.mem_cons, List.mem_nil_iff, or_false] at hi
25
+ rcases hi with ((((hi | hi | hi) | hi | hi) | hi) | hi) | hi | hi | hi | hi
26
+ all_goals first | exact hctl i ‹_› | (subst_vars; rfl)
27
+
28
+ /-- Core of every instruction shaped `uf 2; <body>; next`: the body (given as an execution fact)
29
+ overwrites the second slot with `y` and pops one. -/
30
+ theorem sim_bin_core (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W} {pre : List WI}
31
+ (hpre : Run fs pre w (.normal w))
32
+ (hd : s.dstack = b :: a :: d) {B : List WI} {y : W} (hctlB : ∀ i ∈ B, i.isCtl = false)
33
+ (hB : ∀ m', w.mem.write64 (c.dEnd - 8 * s.dstack.length + 8) y = some m' →
34
+ execL B w = some { w with mem := m', globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * s.dstack.length + 8))) })
35
+ (nxt : Nat) :
36
+ ∃ w', Run fs (pre ++ (B ++ [i32c nxt])) w (.normal w') ∧
37
+ Rel0 c { s with dstack := y :: d } w' ∧ w'.stack = [.i32 (ofN nxt)] := by
38
+ have hlen : s.dstack.length = d.length + 2 := by simp [hd]
39
+ have hk : 2 ≤ s.dstack.length := by omega
40
+ have hcap := hs.capD
41
+ obtain ⟨m', hm'⟩ : ∃ m', w.mem.write64 (c.dEnd - 8 * s.dstack.length + 8) y = some m' :=
42
+ write64_some (by rw [hs.size]; have := hg.dEnd_le; omega)
43
+ have hbR := run_of_execL (fs := fs) hctlB (hB m' hm')
44
+ have hrel1 := wr_stack hg hs (j := 1) (by omega) (v := y) (m' := m') (by simpa using hm')
45
+ have hrel2 := adj_sp hg hrel1 (n := 1) (by simp [hd])
46
+ have e3 : ({ { s with dstack := s.dstack.set 1 y } with dstack := ({ s with dstack := s.dstack.set 1 y } : State 64).dstack.drop 1 } : State 64) = { s with dstack := y :: d } := by
47
+ simp [hd]
48
+ rw [e3] at hrel2
49
+ have e2 : c.dEnd - 8 * s.dstack.length + 8 = c.dEnd - 8 * (s.dstack.length - 1) := by omega
50
+ rw [e2] at hbR
51
+ refine ⟨_, run_append _ hpre (run_append _ hbR (run_finish nxt)), ?_, ?_⟩
52
+ · refine Rel0.congr (hrel2.setStack []) rfl ?_ ?_ ?_
53
+ · simp [setG, spG, List.length_set]
54
+ · simp [setG, spG, rpG]
55
+ · simp [setG, spG, apG]
56
+ · simp [hst]
57
+
58
+ theorem sim_binop (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W} {pre : List WI}
59
+ (hpre : Run fs pre w (.normal w))
60
+ (hd : s.dstack = b :: a :: d) {op : List WI} (hctl : ∀ i ∈ op, i.isCtl = false) {y : W}
61
+ (hop : ∀ (u : WState) (r : List Val), u.stack = .i64 b :: .i64 a :: r →
62
+ execL op u = some { u with stack := .i64 y :: r }) (nxt : Nat) :
63
+ ∃ w', Run fs (pre ++ (binBody op ++ [i32c nxt])) w (.normal w') ∧
64
+ Rel0 c { s with dstack := y :: d } w' ∧ w'.stack = [.i32 (ofN nxt)] := by
65
+ have hlen : s.dstack.length = d.length + 2 := by simp [hd]
66
+ have hX := sp_lt hg hs
67
+ have hr1 : w.mem.read64 (c.dEnd - 8 * s.dstack.length + 8) = some a := by
68
+ have := hs.stack 1 a (by simp [hd]); simpa using this
69
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some b := by
70
+ have := hs.stack 0 b (by simp [hd]); simpa using this
71
+ refine sim_bin_core hg hs hst hpre hd (ctl_binBody hctl) ?_ nxt
72
+ intro m' hm'
73
+ have hsp8 : ofN (c.dEnd - 8 * s.dstack.length) + ofN 8 = ofN (c.dEnd - 8 * s.dstack.length + 8) :=
74
+ ofN_add _ _
75
+ have hpre : execL ([.globalGet spG] ++ ldS 8 ++ ldS 0) w = some { w with stack := .i64 b :: .i64 a :: .i32 (ofN (c.dEnd - 8 * s.dstack.length)) :: w.stack } := by
76
+ simp [ldS, execL, stepI, hs.sp, ofN_toNat hX, hr0, hr1]
77
+ have hop' := hop { w with stack := .i64 b :: .i64 a :: .i32 (ofN (c.dEnd - 8 * s.dstack.length)) :: w.stack } (.i32 (ofN (c.dEnd - 8 * s.dstack.length)) :: w.stack) rfl
78
+ have hstore : execL [.i64store 8] { w with stack := .i64 y :: .i32 (ofN (c.dEnd - 8 * s.dstack.length)) :: w.stack } = some { w with mem := m' } := by
79
+ simp [execL, stepI, ofN_toNat hX, hm']
80
+ have hadd : execL (spAdd 8) { w with mem := m' } = some { w with mem := m', globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * s.dstack.length + 8))) } := by
81
+ have hsp8' : ofN (c.dEnd - 8 * s.dstack.length) + 8#32 = ofN (c.dEnd - 8 * s.dstack.length + 8) := hsp8
82
+ simp [spAdd, execL, stepI, bin32, hs.sp, i32c]
83
+ unfold setG
84
+ rw [hsp8']
85
+ simp only [binBody, execL_append, hpre, Option.bind_some, hop', hstore, hadd]
86
+
87
+ theorem uf2_pass (hg : Geom c) (hs : Rel0 c s w) {b a : W} {d : List W} (hd : s.dstack = b :: a :: d) :
88
+ Run fs (uf c.layout 2) w (.normal w) :=
89
+ uf_pass hg hs (k := 2) (by omega) (by simp [hd])
90
+
91
+ /-- Conclusion shared by every instruction simulation. -/
92
+ abbrev Sim1 (c : Cfg) (fs : Funcs) (body : List WI) (s' : State 64) (w : WState) (nxt : Nat) : Prop :=
93
+ ∃ w', Run fs body w (.normal w') ∧ Rel0 c s' w' ∧ w'.stack = [.i32 (ofN nxt)]
94
+
95
+ macro "hop_tac" : tactic =>
96
+ `(tactic| (intro u r h; simp [execL, stepI, bin64, cmp64, bool32, Word.rotl, Word.rotr, Word.ofBool, WordDialect.Cond.eval, cmpOp, h]))
97
+
98
+ theorem sim_add (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
99
+ (hd : s.dstack = b :: a :: d) :
100
+ Sim1 c fs (lowerInstr c.layout n k .add) { s with dstack := (a + b) :: d } w (k + 1) := by
101
+ have := sim_binop (fs := fs) hg hs hst (uf2_pass hg hs hd) hd (op := [.i64add]) (by simp [WI.isCtl]) (y := a + b) (by hop_tac) (k + 1)
102
+ simpa [lowerInstr, binOp, List.append_assoc] using this
103
+
104
+ theorem sim_sub (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
105
+ (hd : s.dstack = b :: a :: d) :
106
+ Sim1 c fs (lowerInstr c.layout n k .sub) { s with dstack := (a - b) :: d } w (k + 1) := by
107
+ have := sim_binop (fs := fs) hg hs hst (uf2_pass hg hs hd) hd (op := [.i64sub]) (by simp [WI.isCtl]) (y := a - b) (by hop_tac) (k + 1)
108
+ simpa [lowerInstr, binOp, List.append_assoc] using this
109
+
110
+ theorem sim_mul (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
111
+ (hd : s.dstack = b :: a :: d) :
112
+ Sim1 c fs (lowerInstr c.layout n k .mul) { s with dstack := (a * b) :: d } w (k + 1) := by
113
+ have := sim_binop (fs := fs) hg hs hst (uf2_pass hg hs hd) hd (op := [.i64mul]) (by simp [WI.isCtl]) (y := a * b) (by hop_tac) (k + 1)
114
+ simpa [lowerInstr, binOp, List.append_assoc] using this
115
+
116
+ theorem sim_and (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
117
+ (hd : s.dstack = b :: a :: d) :
118
+ Sim1 c fs (lowerInstr c.layout n k .and) { s with dstack := (a &&& b) :: d } w (k + 1) := by
119
+ have := sim_binop (fs := fs) hg hs hst (uf2_pass hg hs hd) hd (op := [.i64and]) (by simp [WI.isCtl]) (y := a &&& b) (by hop_tac) (k + 1)
120
+ simpa [lowerInstr, binOp, List.append_assoc] using this
121
+
122
+ theorem sim_or (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
123
+ (hd : s.dstack = b :: a :: d) :
124
+ Sim1 c fs (lowerInstr c.layout n k .or) { s with dstack := (a ||| b) :: d } w (k + 1) := by
125
+ have := sim_binop (fs := fs) hg hs hst (uf2_pass hg hs hd) hd (op := [.i64or]) (by simp [WI.isCtl]) (y := a ||| b) (by hop_tac) (k + 1)
126
+ simpa [lowerInstr, binOp, List.append_assoc] using this
127
+
128
+ theorem sim_xor (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
129
+ (hd : s.dstack = b :: a :: d) :
130
+ Sim1 c fs (lowerInstr c.layout n k .xor) { s with dstack := (a ^^^ b) :: d } w (k + 1) := by
131
+ have := sim_binop (fs := fs) hg hs hst (uf2_pass hg hs hd) hd (op := [.i64xor]) (by simp [WI.isCtl]) (y := a ^^^ b) (by hop_tac) (k + 1)
132
+ simpa [lowerInstr, binOp, List.append_assoc] using this
133
+
134
+ theorem sim_rotl (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
135
+ (hd : s.dstack = b :: a :: d) :
136
+ Sim1 c fs (lowerInstr c.layout n k .rotl) { s with dstack := Word.rotl a b :: d } w (k + 1) := by
137
+ have := sim_binop (fs := fs) hg hs hst (uf2_pass hg hs hd) hd (op := [.i64rotl]) (by simp [WI.isCtl]) (y := Word.rotl a b) (by hop_tac) (k + 1)
138
+ simpa [lowerInstr, binOp, List.append_assoc] using this
139
+
140
+ theorem sim_rotr (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
141
+ (hd : s.dstack = b :: a :: d) :
142
+ Sim1 c fs (lowerInstr c.layout n k .rotr) { s with dstack := Word.rotr a b :: d } w (k + 1) := by
143
+ have := sim_binop (fs := fs) hg hs hst (uf2_pass hg hs hd) hd (op := [.i64rotr]) (by simp [WI.isCtl]) (y := Word.rotr a b) (by hop_tac) (k + 1)
144
+ simpa [lowerInstr, binOp, List.append_assoc] using this
145
+
146
+ theorem ext_bool (v : Bool) : BitVec.setWidth 64 (bool32 v) = Word.ofBool v := by
147
+ cases v <;> simp [bool32, Word.ofBool]
148
+
149
+ theorem sim_cmp (n k : Nat) (cd : WordDialect.Cond) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = [])
150
+ {a b : W} {d : List W} (hd : s.dstack = b :: a :: d) :
151
+ Sim1 c fs (lowerInstr c.layout n k (.cmp cd)) { s with dstack := Word.ofBool (cd.eval a b) :: d } w (k + 1) := by
152
+ have := sim_binop (fs := fs) hg hs hst (uf2_pass hg hs hd) hd (op := [cmpOp cd, .i64extu]) (by cases cd <;> simp [cmpOp, WI.isCtl])
153
+ (y := Word.ofBool (cd.eval a b)) (by intro u r h; cases cd <;> simp [execL, stepI, bin64, cmp64, ext_bool, WordDialect.Cond.eval, cmpOp, h]) (k + 1)
154
+ simpa [lowerInstr, binOp, List.append_assoc] using this
155
+
156
+ end Sim
157
+
158
+ end Wasm
159
+ end WordDialect
backend/wasm/Wasm/SimAlu2.lean ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.SimAlu
2
+
3
+ /-!
4
+ # Wasm.SimAlu2
5
+
6
+ Shifts (a count of 64 or more yields 0), `div` and `sdiv` (zero guard; `sdiv` by -1 avoids the
7
+ `INT64_MIN / -1` trap of `i64.div_s`), and `not`.
8
+ -/
9
+
10
+ namespace WordDialect
11
+ namespace Wasm
12
+
13
+ section Sim
14
+
15
+ variable {c : Cfg} {s : State 64} {w : WState} {fs : Funcs}
16
+
17
+ theorem ctl_shiftBody (op : WI) (hop : op.isCtl = false) : ∀ i ∈ shiftBody op, i.isCtl = false :=
18
+ allSimple_spec (by simp only [allSimple, shiftBody, ldS, spAdd, List.all_append, List.all_cons, List.all_nil, hop, Bool.not_false, Bool.and_true, Bool.true_and]; decide)
19
+
20
+ theorem shiftBody_split (op : WI) : shiftBody op =
21
+ ([.globalGet spG, .i64const 0#64] ++ ldS 8 ++ ldS 0) ++ [op] ++
22
+ (ldS 0 ++ [.i64const 64#64, .i64geu, .select, .i64store 8] ++ spAdd 8) := by
23
+ simp [shiftBody, List.append_assoc]
24
+
25
+ theorem sim_shift_gen (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
26
+ {pre : List WI} (hpre : Run fs pre w (.normal w)) (hd : s.dstack = b :: a :: d)
27
+ {op : WI} (hctl : op.isCtl = false) {z y : W}
28
+ (hop : ∀ (u : WState) (r : List Val), u.stack = .i64 b :: .i64 a :: r →
29
+ execL [op] u = some { u with stack := .i64 z :: r })
30
+ (hy : y = if (64#64).ule b then 0#64 else z) (nxt : Nat) :
31
+ ∃ w', Run fs (pre ++ (shiftBody op ++ [i32c nxt])) w (.normal w') ∧
32
+ Rel0 c { s with dstack := y :: d } w' ∧ w'.stack = [.i32 (ofN nxt)] := by
33
+ have hlen : s.dstack.length = d.length + 2 := by simp [hd]
34
+ have hX := sp_lt hg hs
35
+ have hr1 : w.mem.read64 (c.dEnd - 8 * s.dstack.length + 8) = some a := by
36
+ have := hs.stack 1 a (by simp [hd]); simpa using this
37
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some b := by
38
+ have := hs.stack 0 b (by simp [hd]); simpa using this
39
+ refine sim_bin_core hg hs hst hpre hd (ctl_shiftBody op hctl) ?_ nxt
40
+ intro m' hm'
41
+ have hsp8 : ofN (c.dEnd - 8 * s.dstack.length) + 8#32 = ofN (c.dEnd - 8 * s.dstack.length + 8) :=
42
+ ofN_add _ _
43
+ have h1 : execL ([.globalGet spG, .i64const 0#64] ++ ldS 8 ++ ldS 0) w = some { w with stack := .i64 b :: .i64 a :: .i64 0#64 :: .i32 (ofN (c.dEnd - 8 * s.dstack.length)) :: w.stack } := by
44
+ simp [ldS, execL, stepI, hs.sp, ofN_toNat hX, hr0, hr1]
45
+ have hop' := hop { w with stack := .i64 b :: .i64 a :: .i64 0#64 :: .i32 (ofN (c.dEnd - 8 * s.dstack.length)) :: w.stack } (.i64 0#64 :: .i32 (ofN (c.dEnd - 8 * s.dstack.length)) :: w.stack) rfl
46
+ have h3 : execL (ldS 0 ++ [.i64const 64#64, .i64geu, .select, .i64store 8] ++ spAdd 8) { w with stack := .i64 z :: .i64 0#64 :: .i32 (ofN (c.dEnd - 8 * s.dstack.length)) :: w.stack } = some { w with mem := m', globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * s.dstack.length + 8))) } := by
47
+ subst hy
48
+ by_cases hc : (64#64).ule b = true
49
+ · have hm2 : w.mem.write64 (c.dEnd - 8 * s.dstack.length + 8) 0#64 = some m' := by simpa [hc] using hm'
50
+ simp [ldS, spAdd, execL, stepI, bin32, cmp64, hs.sp, ofN_toNat hX, hr0, bool32, hc, hm2, i32c, hsp8]
51
+ rfl
52
+ · have hc' : (64#64).ule b = false := by simpa using hc
53
+ have hm2 : w.mem.write64 (c.dEnd - 8 * s.dstack.length + 8) z = some m' := by simpa [hc'] using hm'
54
+ simp [ldS, spAdd, execL, stepI, bin32, cmp64, hs.sp, ofN_toNat hX, hr0, bool32, hc', hm2, i32c, hsp8]
55
+ rfl
56
+ rw [shiftBody_split, execL_append, execL_append, h1]
57
+ simp only [Option.bind_some]
58
+ rw [hop']
59
+ simp only [Option.bind_some]
60
+ exact h3
61
+
62
+ theorem ule64_iff (b : W) : ((64#64).ule b = true) ↔ 64 ≤ b.toNat := by
63
+ simp [BitVec.ule_eq_decide]
64
+
65
+ theorem sim_shl (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
66
+ (hd : s.dstack = b :: a :: d) :
67
+ Sim1 c fs (lowerInstr c.layout n k .shl) { s with dstack := Word.shl a b :: d } w (k + 1) := by
68
+ have := sim_shift_gen (fs := fs) hg hs hst (uf2_pass hg hs hd) hd (op := .i64shl) rfl
69
+ (z := a <<< (b.toNat % 64)) (y := Word.shl a b)
70
+ (by intro u r h; simp [execL, stepI, bin64, h])
71
+ (by
72
+ by_cases hc : 64 ≤ b.toNat
73
+ · simp [(ule64_iff b).mpr hc, Word.shl, show ¬ b.toNat < 64 by omega]
74
+ · have : ¬ ((64#64).ule b = true) := fun h => hc ((ule64_iff b).mp h)
75
+ simp [this, Word.shl, show b.toNat < 64 by omega, Nat.mod_eq_of_lt (show b.toNat < 64 by omega)]) (k + 1)
76
+ simpa [lowerInstr, shiftOp, List.append_assoc] using this
77
+
78
+ theorem ushiftRight_ge {a : W} {m : Nat} (h : 64 ≤ m) : a >>> m = 0#64 := by
79
+ apply BitVec.eq_of_toNat_eq
80
+ simp only [BitVec.toNat_ushiftRight, Nat.shiftRight_eq_div_pow]
81
+ have : a.toNat < 2 ^ m := Nat.lt_of_lt_of_le a.isLt (Nat.pow_le_pow_right (by omega) h)
82
+ simp [Nat.div_eq_of_lt this]
83
+
84
+ theorem sim_shr (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
85
+ (hd : s.dstack = b :: a :: d) :
86
+ Sim1 c fs (lowerInstr c.layout n k .shr) { s with dstack := Word.shr a b :: d } w (k + 1) := by
87
+ have := sim_shift_gen (fs := fs) hg hs hst (uf2_pass hg hs hd) hd (op := .i64shru) rfl
88
+ (z := a >>> (b.toNat % 64)) (y := Word.shr a b)
89
+ (by intro u r h; simp [execL, stepI, bin64, h])
90
+ (by
91
+ by_cases hc : 64 ≤ b.toNat
92
+ · simp [(ule64_iff b).mpr hc, Word.shr, ushiftRight_ge hc]
93
+ · have : ¬ ((64#64).ule b = true) := fun h => hc ((ule64_iff b).mp h)
94
+ simp [this, Word.shr, Nat.mod_eq_of_lt (show b.toNat < 64 by omega)]) (k + 1)
95
+ simpa [lowerInstr, shiftOp, List.append_assoc] using this
96
+
97
+ /-- The zero guard, passing case. -/
98
+ theorem zg_pass (hg : Geom c) (hs : Rel0 c s w) {b : W} {d : List W} (hd : s.dstack = b :: d) (hne : b ≠ 0#64) :
99
+ Run fs zeroGuard w (.normal w) := by
100
+ have hX := sp_lt hg hs
101
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some b := by
102
+ have := hs.stack 0 b (by simp [hd]); simpa using this
103
+ have h1 : execL (ldS 0 ++ [.i64eqz]) w = some { w with stack := .i32 (bool32 false) :: w.stack } := by
104
+ simp [ldS, execL, stepI, hs.sp, ofN_toNat hX, hr0, bool32, hne]
105
+ have h2 : Run fs (ldS 0 ++ [.i64eqz]) w (.normal { w with stack := .i32 (bool32 false) :: w.stack }) :=
106
+ run_of_execL (by simp [ldS, WI.isCtl]) h1
107
+ have h3 := run_ite_false (fs := fs) (thn := [.exitTrap 3]) (w := { w with stack := .i32 (bool32 false) :: w.stack }) (r := w.stack) (by simp [bool32])
108
+ simpa [zeroGuard, List.append_assoc] using run_append _ h2 h3
109
+
110
+ /-- The zero guard, trapping case. -/
111
+ theorem zg_trap (hg : Geom c) (hs : Rel0 c s w) {d : List W} (hd : s.dstack = 0#64 :: d) :
112
+ Run fs zeroGuard w (.exit 3) := by
113
+ have hX := sp_lt hg hs
114
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some 0#64 := by
115
+ have := hs.stack 0 0#64 (by simp [hd]); simpa using this
116
+ have h1 : execL (ldS 0 ++ [.i64eqz]) w = some { w with stack := .i32 (bool32 true) :: w.stack } := by
117
+ simp [ldS, execL, stepI, hs.sp, ofN_toNat hX, hr0, bool32]
118
+ have h2 : Run fs (ldS 0 ++ [.i64eqz]) w (.normal { w with stack := .i32 (bool32 true) :: w.stack }) :=
119
+ run_of_execL (by simp [ldS, WI.isCtl]) h1
120
+ have h3 := run_ite_exit (fs := fs) (w := { w with stack := .i32 (bool32 true) :: w.stack }) (r := w.stack) (c := bool32 true) (k := 3) rfl (by simp [bool32])
121
+ simpa [zeroGuard, List.append_assoc] using run_append _ h2 h3
122
+
123
+ theorem sim_div (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
124
+ (hd : s.dstack = b :: a :: d) (hb : b ≠ 0#64) :
125
+ Sim1 c fs (lowerInstr c.layout n k .div) { s with dstack := a.udiv b :: d } w (k + 1) := by
126
+ have hpre : Run fs (uf c.layout 2 ++ zeroGuard) w (.normal w) :=
127
+ run_append _ (uf2_pass hg hs hd) (zg_pass hg hs (d := a :: d) hd hb)
128
+ have := sim_binop (fs := fs) hg hs hst hpre hd (op := [.i64divu]) (by simp [WI.isCtl])
129
+ (y := a.udiv b) (by intro u r h; simp [execL, stepI, h, hb, BitVec.udiv_def]) (k + 1)
130
+ simpa [lowerInstr, List.append_assoc] using this
131
+
132
+ theorem sim_div_trap (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) {a : W} {d : List W}
133
+ (hd : s.dstack = 0#64 :: a :: d) :
134
+ Run fs (lowerInstr c.layout n k .div) w (.exit 3) := by
135
+ have h := run_append (fs := fs) _ (uf2_pass hg hs hd)
136
+ (run_append_abrupt _ (zg_trap hg hs (d := a :: d) hd) (b := binBody [.i64divu] ++ [i32c (k + 1)]) trivial)
137
+ simpa [lowerInstr, List.append_assoc] using h
138
+
139
+ theorem ctl_sdivBody : ∀ i ∈ sdivBody, i.isCtl = false :=
140
+ allSimple_spec (by decide)
141
+
142
+ theorem sdiv_allOnes (a : W) : a.sdiv (BitVec.allOnes 64) = 0#64 - a := by
143
+ rw [← BitVec.neg_one_eq_allOnes, show (-1#64 : W) = -(1#64) from rfl, BitVec.sdiv_neg (by decide), BitVec.sdiv_one]
144
+ simp
145
+
146
+ theorem sdiv_ne (a b : W) (hm : b ≠ BitVec.allOnes 64) :
147
+ BitVec.ofInt 64 (Int.tdiv a.toInt b.toInt) = a.sdiv b := by
148
+ have hq : (a.sdiv b).toInt = Int.tdiv a.toInt b.toInt :=
149
+ BitVec.toInt_sdiv_of_ne_or_ne a b (Or.inr (by rwa [BitVec.neg_one_eq_allOnes]))
150
+ rw [← hq]; simp
151
+
152
+ theorem sim_sdiv (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
153
+ (hd : s.dstack = b :: a :: d) (hb : b ≠ 0#64) :
154
+ Sim1 c fs (lowerInstr c.layout n k .sdiv) { s with dstack := a.sdiv b :: d } w (k + 1) := by
155
+ have hpre : Run fs (uf c.layout 2 ++ zeroGuard) w (.normal w) :=
156
+ run_append _ (uf2_pass hg hs hd) (zg_pass hg hs (d := a :: d) hd hb)
157
+ have hlen : s.dstack.length = d.length + 2 := by simp [hd]
158
+ have hX := sp_lt hg hs
159
+ have hr1 : w.mem.read64 (c.dEnd - 8 * s.dstack.length + 8) = some a := by
160
+ have := hs.stack 1 a (by simp [hd]); simpa using this
161
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some b := by
162
+ have := hs.stack 0 b (by simp [hd]); simpa using this
163
+ have hsp8 : ofN (c.dEnd - 8 * s.dstack.length) + 8#32 = ofN (c.dEnd - 8 * s.dstack.length + 8) :=
164
+ ofN_add _ _
165
+ have := sim_bin_core (fs := fs) hg hs hst hpre hd ctl_sdivBody (y := a.sdiv b) ?_ (k + 1)
166
+ · simpa [lowerInstr, List.append_assoc] using this
167
+ intro m' hm'
168
+ by_cases hbm : b = BitVec.allOnes 64
169
+ · subst hbm
170
+ rw [sdiv_allOnes] at hm'
171
+ have hm2 : w.mem.write64 (c.dEnd - 8 * s.dstack.length + 8) (-a) = some m' := by simpa using hm'
172
+ simp [sdivBody, ldS, spAdd, execL_append, execL, stepI, bin64, cmp64, bin32, hs.sp, ofN_toNat hX, hr0, hr1, bool32, hm2, i32c, hsp8]
173
+ rfl
174
+ · have hbm' : ¬ b = 18446744073709551615#64 := by simpa using hbm
175
+ rw [← sdiv_ne a b hbm] at hm'
176
+ simp [sdivBody, ldS, spAdd, execL_append, execL, stepI, bin64, cmp64, bin32, hs.sp, ofN_toNat hX, hr0, hr1, bool32, hm', i32c, hsp8, hbm', hb]
177
+ rfl
178
+
179
+ theorem sim_sdiv_trap (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) {a : W} {d : List W}
180
+ (hd : s.dstack = 0#64 :: a :: d) :
181
+ Run fs (lowerInstr c.layout n k .sdiv) w (.exit 3) := by
182
+ have h := run_append (fs := fs) _ (uf2_pass hg hs hd)
183
+ (run_append_abrupt _ (zg_trap hg hs (d := a :: d) hd) (b := sdivBody ++ [i32c (k + 1)]) trivial)
184
+ simpa [lowerInstr, List.append_assoc] using h
185
+
186
+ end Sim
187
+
188
+ end Wasm
189
+ end WordDialect
backend/wasm/Wasm/SimAux.lean ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.SimCtl
2
+
3
+ /-!
4
+ # Wasm.SimAux
5
+
6
+ The auxiliary data stack: `tor` (operand guard, aux overflow guard, move `[sp]` to the new aux top),
7
+ `fromr` and `rfetch` (empty-aux check exiting 5, data overflow guard, push `[ap]`; `fromr` also pops
8
+ the aux stack). Every outcome is covered: next, `returnUnderflow` (exit 5), and overflow (exit 6).
9
+ `tor` on an empty data stack is the generic operand-guard case of `sim_step`.
10
+ -/
11
+
12
+ namespace WordDialect
13
+ namespace Wasm
14
+
15
+ section Sim
16
+
17
+ variable {c : Cfg} {s : State 64} {w : WState} {fs : Funcs}
18
+
19
+ theorem ap_lt (hg : Geom c) (hs : Rel0 c s w) : c.aEnd - 8 * s.astack.length < 2 ^ 32 := by
20
+ have := hg.aEnd_le; have := hg.msize_lt; omega
21
+
22
+ /-- Auxiliary stack has room for one more word: the guard falls through. -/
23
+ theorem ovfA_pass (hg : Geom c) (hs : Rel0 c s w)
24
+ (hfit : c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd) :
25
+ Run fs (ovf apG c.layout.aBase) w (.normal w) := by
26
+ have := hg.aEnd_le; have := hg.msize_lt; have := hg.aBase_8
27
+ exact ovf_pass hs.ap (ap_lt hg hs) (by simp [Cfg.layout]; omega) (by simp [Cfg.layout]; omega)
28
+
29
+ /-- Auxiliary stack full: the guard exits 6. -/
30
+ theorem ovfA_trap (hg : Geom c) (hs : Rel0 c s w)
31
+ (h : ¬ c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd) :
32
+ Run fs (ovf apG c.layout.aBase) w (.exit 6) := by
33
+ have := hg.aEnd_le; have := hg.msize_lt; have := hg.aBase_8; have := hs.capA
34
+ exact ovf_trap hs.ap (ap_lt hg hs) (by simp [Cfg.layout]; omega) (by simp [Cfg.layout]; omega)
35
+
36
+ /-- The empty-auxiliary-stack check of `fromr`/`rfetch`. -/
37
+ def auxE (L : Layout) : List WI := [.globalGet apG, i32c L.aEnd, .i32eq, .ite [.exitTrap 5]]
38
+
39
+ /-- Non-empty auxiliary stack: the check falls through. -/
40
+ theorem auxE_pass (hg : Geom c) (hs : Rel0 c s w) {a : W} {as : List W} (ha : s.astack = a :: as) :
41
+ Run fs (auxE c.layout) w (.normal w) := by
42
+ have hcap := hs.capA
43
+ rw [ha] at hcap
44
+ simp only [List.length_cons] at hcap
45
+ have hX : c.aEnd - 8 * (as.length + 1) < 2 ^ 32 := by have := ap_lt hg hs; rw [ha] at this; simpa using this
46
+ have hE : c.aEnd < 2 ^ 32 := by have := hg.aEnd_le; have := hg.msize_lt; omega
47
+ have hap : w.globals apG = .i32 (ofN (c.aEnd - 8 * (as.length + 1))) := by
48
+ have := hs.ap; rw [ha] at this; simpa using this
49
+ have hne : ofN (c.aEnd - 8 * (as.length + 1)) ≠ ofN c.aEnd := by
50
+ intro h; have := ofN_inj hX hE h; omega
51
+ have e1 : execL [.globalGet apG, i32c c.layout.aEnd, .i32eq] w =
52
+ some { w with stack := .i32 0#32 :: w.stack } := by
53
+ simp [execL, stepI, hap, i32c, Cfg.layout, bool32]
54
+ exact hne
55
+ have r1 := run_of_execL (fs := fs) (by simp [WI.isCtl, i32c]) e1
56
+ have r2 := run_ite_false (fs := fs) (thn := [.exitTrap 5])
57
+ (w := { w with stack := .i32 0#32 :: w.stack }) (r := w.stack) rfl
58
+ have := run_append _ r1 r2
59
+ simpa [auxE] using this
60
+
61
+ /-- Empty auxiliary stack: the check exits 5 (`returnUnderflow`). -/
62
+ theorem auxE_trap (hs : Rel0 c s w) (ha : s.astack = []) :
63
+ Run fs (auxE c.layout) w (.exit 5) := by
64
+ have hap : w.globals apG = .i32 (ofN c.aEnd) := by
65
+ have := hs.ap; rw [ha] at this; simpa using this
66
+ have e1 : execL [.globalGet apG, i32c c.layout.aEnd, .i32eq] w =
67
+ some { w with stack := .i32 1#32 :: w.stack } := by
68
+ simp [execL, stepI, hap, i32c, Cfg.layout, bool32, ofN]
69
+ have r1 := run_of_execL (fs := fs) (by simp [WI.isCtl, i32c]) e1
70
+ have r2 := run_ite_exit (fs := fs) (w := { w with stack := .i32 1#32 :: w.stack }) (r := w.stack)
71
+ (c := 1#32) (k := 5) rfl (by decide)
72
+ have := run_append _ r1 r2
73
+ simpa [auxE] using this
74
+
75
+ theorem tor_split (L : Layout) (n k : Nat) : lowerInstr L n k .tor =
76
+ uf L 1 ++ ovf apG L.aBase ++
77
+ (([.globalGet apG, i32c 8, .i32sub, .globalSet apG, .globalGet apG] ++ ldS 0 ++ [.i64store 0] ++
78
+ spAdd 8) ++ [i32c (k + 1)]) := by
79
+ simp [lowerInstr, List.append_assoc]
80
+
81
+ theorem sim_tor (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a : W} {d : List W}
82
+ (hd : s.dstack = a :: d) (hfit : c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd) :
83
+ Sim1 c fs (lowerInstr c.layout n k .tor) { s with dstack := d, astack := a :: s.astack } w (k + 1) := by
84
+ have hlen : s.dstack.length = d.length + 1 := by simp [hd]
85
+ have hX := sp_lt hg hs
86
+ have hA := ap_lt hg hs
87
+ have hcapA := hs.capA
88
+ have hcapD := hs.capD
89
+ have haE := hg.aEnd_le
90
+ have hsz := hg.msize_lt
91
+ have hlt : c.aEnd - 8 * (s.astack.length + 1) < 2 ^ 32 := by omega
92
+ have hsub : ofN (c.aEnd - 8 * s.astack.length) - 8#32 = ofN (c.aEnd - 8 * (s.astack.length + 1)) := by
93
+ have : (8#32 : W32) = ofN 8 := rfl
94
+ rw [this, ofN_sub (by omega)]
95
+ congr 1
96
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some a := by
97
+ have := hs.stack 0 a (by simp [hd]); simpa using this
98
+ obtain ⟨m', hm'⟩ : ∃ m', w.mem.write64 (c.aEnd - 8 * (s.astack.length + 1)) a = some m' :=
99
+ write64_some (by rw [hs.size]; omega)
100
+ have hsp8 : ofN (c.dEnd - 8 * s.dstack.length) + 8#32 = ofN (c.dEnd - 8 * s.dstack.length + 8) :=
101
+ ofN_add _ _
102
+ have hspa : w.globals 1 = .i32 (ofN (c.dEnd - 8 * s.dstack.length)) := hs.sp
103
+ have hapa : w.globals 3 = .i32 (ofN (c.aEnd - 8 * s.astack.length)) := hs.ap
104
+ have hexec : execL ([.globalGet apG, i32c 8, .i32sub, .globalSet apG, .globalGet apG] ++ ldS 0 ++
105
+ [.i64store 0] ++ spAdd 8) w =
106
+ some { w with mem := m', globals := setG (setG w.globals apG (.i32 (ofN (c.aEnd - 8 * (s.astack.length + 1))))) spG (.i32 (ofN (c.dEnd - 8 * s.dstack.length + 8))) } := by
107
+ simp [ldS, spAdd, execL, stepI, bin32, hapa, hspa, i32c, hsub, apG, spG, hst,
108
+ ofN_toNat hX, ofN_toNat hlt, hr0, hm', hsp8]
109
+ unfold setG; rfl
110
+ have hrel1 := push_aux hg hs hfit hm'
111
+ have hrel2 := adj_sp hg hrel1 (n := 1) (by simp [hd])
112
+ have e3 : ({ { s with astack := a :: s.astack } with
113
+ dstack := ({ s with astack := a :: s.astack } : State 64).dstack.drop 1 } : State 64) =
114
+ { s with dstack := d, astack := a :: s.astack } := by
115
+ simp [hd]
116
+ rw [e3] at hrel2
117
+ have hrel3 : Rel0 c { s with dstack := d, astack := a :: s.astack }
118
+ { w with mem := m', globals := setG (setG w.globals apG (.i32 (ofN (c.aEnd - 8 * (s.astack.length + 1))))) spG (.i32 (ofN (c.dEnd - 8 * s.dstack.length + 8))) } := by
119
+ refine Rel0.congr (hrel2.setStack []) rfl ?_ ?_ ?_
120
+ · have e2 : c.dEnd - 8 * s.dstack.length + 8 = c.dEnd - 8 * (s.dstack.length - 1) := by omega
121
+ simp [setG, spG, e2]
122
+ · simp [setG, spG, rpG]
123
+ · simp [setG, spG, apG]
124
+ have := core_gen (fs := fs) (pre := uf c.layout 1 ++ ovf apG c.layout.aBase)
125
+ (run_append _ (uf1_pass hg hs hd) (ovfA_pass hg hs hfit))
126
+ (B := [.globalGet apG, i32c 8, .i32sub, .globalSet apG, .globalGet apG] ++ ldS 0 ++ [.i64store 0] ++
127
+ spAdd 8)
128
+ (allSimple_spec (by simp [allSimple, ldS, spAdd, WI.isCtl, i32c])) ⟨_, hexec, hrel3, hst⟩ (k + 1)
129
+ rw [tor_split]
130
+ exact this
131
+
132
+ theorem sim_tor_ovf (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) {a : W} {d : List W}
133
+ (hd : s.dstack = a :: d) (h : ¬ c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd) :
134
+ Run fs (lowerInstr c.layout n k .tor) w (.exit 6) := by
135
+ rw [tor_split, List.append_assoc]
136
+ exact run_append _ (uf1_pass hg hs hd) (run_append_abrupt _ (ovfA_trap hg hs h) trivial)
137
+
138
+ theorem fromr_split (L : Layout) (n k : Nat) : lowerInstr L n k .fromr =
139
+ auxE L ++ ovf spG L.dBase ++
140
+ ((pushWith [.globalGet apG, .i64load 0] ++ [.globalGet apG, i32c 8, .i32add, .globalSet apG]) ++
141
+ [i32c (k + 1)]) := by
142
+ simp [lowerInstr, auxE, List.append_assoc]
143
+
144
+ theorem rfetch_split (L : Layout) (n k : Nat) : lowerInstr L n k .rfetch =
145
+ auxE L ++ ovf spG L.dBase ++ (pushWith [.globalGet apG, .i64load 0] ++ [i32c (k + 1)]) := by
146
+ simp [lowerInstr, auxE, List.append_assoc]
147
+
148
+ /-- Pushing the auxiliary top `[ap]` onto the data stack. -/
149
+ theorem push_atop (hg : Geom c) (hs : Rel0 c s w) {a : W} {as : List W} (ha : s.astack = a :: as)
150
+ (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
151
+ ∃ w1, execL (pushWith [.globalGet apG, .i64load 0]) w = some w1 ∧
152
+ Rel0 c { s with dstack := a :: s.dstack } w1 ∧ w1.stack = w.stack := by
153
+ have hA := ap_lt hg hs
154
+ have hr0 : w.mem.read64 (c.aEnd - 8 * s.astack.length) = some a := by
155
+ have := hs.aux 0 a (by simp [ha]); simpa using this
156
+ exact push_gen hg hs hfit (val := [.globalGet apG, .i64load 0]) (v := a) (by simp [WI.isCtl])
157
+ (by
158
+ intro u _ hm hap
159
+ simp [execL, stepI, hap, hs.ap, ofN_toNat hA, hm, hr0])
160
+
161
+ theorem sim_rfetch (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a : W}
162
+ {as : List W} (ha : s.astack = a :: as) (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
163
+ Sim1 c fs (lowerInstr c.layout n k .rfetch) { s with dstack := a :: s.dstack } w (k + 1) := by
164
+ obtain ⟨w1, he, hr, hst1⟩ := push_atop hg hs ha hfit
165
+ have := core_gen (fs := fs) (pre := auxE c.layout ++ ovf spG c.layout.dBase)
166
+ (run_append _ (auxE_pass hg hs ha) (ovfD_pass hg hs hfit))
167
+ (B := pushWith [.globalGet apG, .i64load 0])
168
+ (allSimple_spec (by simp [allSimple, pushWith, spSub, WI.isCtl, i32c])) ⟨w1, he, hr, by rw [hst1, hst]⟩
169
+ (k + 1)
170
+ rw [rfetch_split]
171
+ exact this
172
+
173
+ theorem sim_fromr (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a : W}
174
+ {as : List W} (ha : s.astack = a :: as) (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
175
+ Sim1 c fs (lowerInstr c.layout n k .fromr) { s with dstack := a :: s.dstack, astack := as } w
176
+ (k + 1) := by
177
+ obtain ⟨w1, he, hr, hst1⟩ := push_atop hg hs ha hfit
178
+ have hcap := hs.capA
179
+ rw [ha] at hcap
180
+ simp only [List.length_cons] at hcap
181
+ have hX : c.aEnd - 8 * (as.length + 1) < 2 ^ 32 := by have := ap_lt hg hs; rw [ha] at this; simpa using this
182
+ have hap1 : w1.globals apG = .i32 (ofN (c.aEnd - 8 * (as.length + 1))) := by
183
+ have := hr.ap; simp only [ha, List.length_cons] at this; exact this
184
+ have hsp8 : ofN (c.aEnd - 8 * (as.length + 1)) + 8#32 = ofN (c.aEnd - 8 * as.length) := by
185
+ rw [show (8#32 : W32) = ofN 8 from rfl, ofN_add]; congr 1; omega
186
+ have e2 : execL [.globalGet apG, i32c 8, .i32add, .globalSet apG] w1 =
187
+ some { w1 with globals := setG w1.globals apG (.i32 (ofN (c.aEnd - 8 * as.length))) } := by
188
+ simp [execL, stepI, bin32, hap1, i32c, hsp8]
189
+ unfold setG; rfl
190
+ have hra : ({ s with dstack := a :: s.dstack } : State 64).astack = a :: as := ha
191
+ have hrel := pop_aux hr hra
192
+ have hB : execL (pushWith [.globalGet apG, .i64load 0] ++ [.globalGet apG, i32c 8, .i32add, .globalSet apG]) w =
193
+ some { w1 with globals := setG w1.globals apG (.i32 (ofN (c.aEnd - 8 * as.length))) } := by
194
+ rw [execL_append, he]
195
+ exact e2
196
+ have := core_gen (fs := fs) (pre := auxE c.layout ++ ovf spG c.layout.dBase)
197
+ (run_append _ (auxE_pass hg hs ha) (ovfD_pass hg hs hfit))
198
+ (B := pushWith [.globalGet apG, .i64load 0] ++ [.globalGet apG, i32c 8, .i32add, .globalSet apG])
199
+ (allSimple_spec (by simp [allSimple, pushWith, spSub, WI.isCtl, i32c])) ⟨_, hB, hrel, by simp [hst1, hst]⟩
200
+ (k + 1)
201
+ rw [fromr_split]
202
+ exact this
203
+
204
+ theorem sim_fromr_trap (n k : Nat) (hs : Rel0 c s w) (ha : s.astack = []) :
205
+ Run fs (lowerInstr c.layout n k .fromr) w (.exit 5) := by
206
+ rw [fromr_split, List.append_assoc]
207
+ exact run_append_abrupt _ (auxE_trap hs ha) trivial
208
+
209
+ theorem sim_rfetch_trap (n k : Nat) (hs : Rel0 c s w) (ha : s.astack = []) :
210
+ Run fs (lowerInstr c.layout n k .rfetch) w (.exit 5) := by
211
+ rw [rfetch_split, List.append_assoc]
212
+ exact run_append_abrupt _ (auxE_trap hs ha) trivial
213
+
214
+ theorem sim_fromr_ovf (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) {a : W} {as : List W}
215
+ (ha : s.astack = a :: as) (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
216
+ Run fs (lowerInstr c.layout n k .fromr) w (.exit 6) := by
217
+ rw [fromr_split, List.append_assoc]
218
+ exact run_append _ (auxE_pass hg hs ha) (run_append_abrupt _ (ovfD_trap hg hs h) trivial)
219
+
220
+ theorem sim_rfetch_ovf (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) {a : W} {as : List W}
221
+ (ha : s.astack = a :: as) (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
222
+ Run fs (lowerInstr c.layout n k .rfetch) w (.exit 6) := by
223
+ rw [rfetch_split, List.append_assoc]
224
+ exact run_append _ (auxE_pass hg hs ha) (run_append_abrupt _ (ovfD_trap hg hs h) trivial)
225
+
226
+ end Sim
227
+
228
+ end Wasm
229
+ end WordDialect
backend/wasm/Wasm/SimCtl.lean ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.SimMem
2
+
3
+ /-!
4
+ # Wasm.SimCtl
5
+
6
+ Control flow: `jmp`, `branch`, `call`, `ret` (and its `returnUnderflow` exit 5), `halt`, and the
7
+ bad-pc stub (exit 4). Each leaves the next IR index on the operand stack, which the dispatch loop
8
+ stores into the `pc` global.
9
+ -/
10
+
11
+ namespace WordDialect
12
+ namespace Wasm
13
+
14
+ section Sim
15
+
16
+ variable {c : Cfg} {s : State 64} {w : WState} {fs : Funcs}
17
+
18
+ theorem sim_jmp (n k t : Nat) (hs : Rel0 c s w) (hst : w.stack = []) :
19
+ Sim1 c fs (lowerInstr c.layout n k (.jmp t)) s w (min t n) :=
20
+ ⟨_, run_finish (min t n), hs.setStack _, by simp [hst]⟩
21
+
22
+ theorem sim_branch (n k t : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {cv : W}
23
+ {d : List W} (hd : s.dstack = cv :: d) :
24
+ Sim1 c fs (lowerInstr c.layout n k (.branch t)) { s with dstack := d } w
25
+ (if Word.isTrue cv then min t n else k + 1) := by
26
+ have hlen : s.dstack.length = d.length + 1 := by simp [hd]
27
+ have hX := sp_lt hg hs
28
+ have hcap := hs.capD
29
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some cv := by
30
+ have := hs.stack 0 cv (by simp [hd]); simpa using this
31
+ have hsp8 : ofN (c.dEnd - 8 * s.dstack.length) + 8#32 = ofN (c.dEnd - 8 * s.dstack.length + 8) :=
32
+ ofN_add _ _
33
+ have e2 : c.dEnd - 8 * s.dstack.length + 8 = c.dEnd - 8 * (s.dstack.length - 1) := by omega
34
+ have el : execL ([i32c (min t n), i32c (k + 1)] ++ ldS 0 ++ [.i64const 0#64, .i64ne, .select] ++ spAdd 8) w =
35
+ some { w with stack := [.i32 (ofN (if Word.isTrue cv then min t n else k + 1))],
36
+ globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * (s.dstack.length - 1)))) } := by
37
+ rw [← e2]
38
+ by_cases hc : cv = 0#64
39
+ · simp [ldS, spAdd, execL, stepI, cmp64, bin32, hs.sp, ofN_toNat hX, hr0, bool32, hc, i32c, hsp8,
40
+ Word.isTrue, hst]
41
+ exact ⟨rfl, rfl⟩
42
+ · simp [ldS, spAdd, execL, stepI, cmp64, bin32, hs.sp, ofN_toNat hX, hr0, bool32, hc, i32c, hsp8,
43
+ Word.isTrue, hst]
44
+ exact ⟨rfl, rfl⟩
45
+ have rl := run_of_execL (fs := fs) (by simp [ldS, spAdd, WI.isCtl, i32c]) el
46
+ have hrel := adj_sp hg hs (n := 1) (by simp [hd])
47
+ have e3 : ({ s with dstack := s.dstack.drop 1 } : State 64) = { s with dstack := d } := by simp [hd]
48
+ rw [e3] at hrel
49
+ refine ⟨_, ?_, hrel.setStack _, rfl⟩
50
+ have := run_append _ (uf1_pass (fs := fs) hg hs hd) rl
51
+ simpa [lowerInstr, List.append_assoc] using this
52
+
53
+ theorem sim_call (n k t : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = [])
54
+ (hfit : c.rBase + 8 * (s.rstack.length + 1) ≤ c.rEnd) :
55
+ Sim1 c fs (lowerInstr c.layout n k (.call t)) { s with rstack := (k + 1) :: s.rstack } w (min t n) := by
56
+ have hcap := hs.capR
57
+ have hrE := hg.rEnd_le
58
+ have hsz := hg.msize_lt
59
+ have hlt : c.rEnd - 8 * (s.rstack.length + 1) < 2 ^ 32 := by omega
60
+ have hsub : ofN (c.rEnd - 8 * s.rstack.length) - 8#32 = ofN (c.rEnd - 8 * (s.rstack.length + 1)) := by
61
+ have : (8#32 : W32) = ofN 8 := rfl
62
+ rw [this, ofN_sub (by omega)]
63
+ congr 1
64
+ obtain ⟨m', hm'⟩ : ∃ m', w.mem.write64 (c.rEnd - 8 * (s.rstack.length + 1)) (BitVec.ofNat 64 (k + 1)) = some m' :=
65
+ write64_some (by rw [hs.size]; omega)
66
+ have el : execL [.globalGet rpG, i32c 8, .i32sub, .globalSet rpG, .globalGet rpG, i64c (k + 1), .i64store 0,
67
+ i32c (min t n)] w =
68
+ some { w with mem := m', stack := [.i32 (ofN (min t n))],
69
+ globals := setG w.globals rpG (.i32 (ofN (c.rEnd - 8 * (s.rstack.length + 1)))) } := by
70
+ simp [execL, stepI, bin32, hs.rp, i32c, i64c, hsub, setG, ofN_toNat hlt, hm', hst]
71
+ exact ⟨rfl, rfl⟩
72
+ have rl := run_of_execL (fs := fs) (by simp [WI.isCtl, i32c, i64c]) el
73
+ refine ⟨_, ?_, (push_rs hg hs hfit hm').setStack _, rfl⟩
74
+ simpa [lowerInstr] using run_append _ (ovfR_pass (fs := fs) hg hs hfit) rl
75
+
76
+ theorem sim_call_ovf (n k t : Nat) (hg : Geom c) (hs : Rel0 c s w)
77
+ (h : ¬ c.rBase + 8 * (s.rstack.length + 1) ≤ c.rEnd) :
78
+ Run fs (lowerInstr c.layout n k (.call t)) w (.exit 6) := by
79
+ have := run_append_abrupt (fs := fs) _ (ovfR_trap hg hs h)
80
+ (b := [.globalGet rpG, i32c 8, .i32sub, .globalSet rpG, .globalGet rpG, i64c (k + 1), .i64store 0,
81
+ i32c (min t n)]) trivial
82
+ simpa [lowerInstr] using this
83
+
84
+ theorem ret_split (L : Layout) : lowerInstr L n k .ret =
85
+ [.globalGet rpG, i32c L.rEnd, .i32eq] ++ [.ite [.exitTrap 5]] ++
86
+ [.globalGet rpG, .i64load 0, .i32wrap, .globalGet rpG, i32c 8, .i32add, .globalSet rpG] := rfl
87
+
88
+ theorem sim_ret (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a : Nat}
89
+ {rs : List Nat} (hr : s.rstack = a :: rs) (ha : a < 2 ^ 32) :
90
+ Sim1 c fs (lowerInstr c.layout n k .ret) { s with rstack := rs } w a := by
91
+ have hcap := hs.capR
92
+ rw [hr] at hcap
93
+ simp only [List.length_cons] at hcap
94
+ have hrE := hg.rEnd_le
95
+ have hsz := hg.msize_lt
96
+ have hX : c.rEnd - 8 * (rs.length + 1) < 2 ^ 32 := by omega
97
+ have hE : c.rEnd < 2 ^ 32 := by omega
98
+ have hrp : w.globals rpG = .i32 (ofN (c.rEnd - 8 * (rs.length + 1))) := by
99
+ have := hs.rp; rw [hr] at this; simpa using this
100
+ have hne : ofN (c.rEnd - 8 * (rs.length + 1)) ≠ ofN c.rEnd := by
101
+ intro h; have := ofN_inj hX hE h; omega
102
+ have hr0 : w.mem.read64 (c.rEnd - 8 * (rs.length + 1)) = some (BitVec.ofNat 64 a) := by
103
+ have := hs.rs 0 a (by simp [hr]); rw [hr] at this; simpa using this
104
+ have e1 : execL [.globalGet rpG, i32c c.layout.rEnd, .i32eq] w = some { w with stack := [.i32 0#32] } := by
105
+ simp [execL, stepI, hrp, i32c, Cfg.layout, bool32, hst]
106
+ exact hne
107
+ have r1 := run_of_execL (fs := fs) (by simp [WI.isCtl, i32c]) e1
108
+ have r2 := run_ite_false (fs := fs) (thn := [.exitTrap 5]) (w := { w with stack := [.i32 0#32] }) (r := []) rfl
109
+ have hsp8 : ofN (c.rEnd - 8 * (rs.length + 1)) + 8#32 = ofN (c.rEnd - 8 * rs.length) := by
110
+ rw [show (8#32 : W32) = ofN 8 from rfl, ofN_add]; congr 1; omega
111
+ have hwrap : BitVec.setWidth 32 (BitVec.ofNat 64 a) = ofN a := by
112
+ apply BitVec.eq_of_toNat_eq
113
+ have : a < 2 ^ 64 := by omega
114
+ simp [ofN, BitVec.toNat_setWidth, Nat.mod_eq_of_lt this]
115
+ have e3 : execL [.globalGet rpG, .i64load 0, .i32wrap, .globalGet rpG, i32c 8, .i32add, .globalSet rpG]
116
+ { { w with stack := [.i32 0#32] } with stack := [] } =
117
+ some { w with stack := [.i32 (ofN a)], globals := setG w.globals rpG (.i32 (ofN (c.rEnd - 8 * rs.length))) } := by
118
+ simp [execL, stepI, bin32, hrp, ofN_toNat hX, hr0, i32c, hsp8, hwrap]
119
+ unfold setG; rfl
120
+ have r3 := run_of_execL (fs := fs) (by simp [WI.isCtl, i32c]) e3
121
+ refine ⟨_, ?_, (pop_rs hs hr).setStack _, rfl⟩
122
+ rw [ret_split]
123
+ exact run_append _ (run_append _ r1 r2) r3
124
+
125
+ theorem sim_ret_trap (n k : Nat) (hs : Rel0 c s w) (hst : w.stack = []) (hr : s.rstack = []) :
126
+ Run fs (lowerInstr c.layout n k .ret) w (.exit 5) := by
127
+ have hrp : w.globals rpG = .i32 (ofN c.rEnd) := by
128
+ have := hs.rp; rw [hr] at this; simpa using this
129
+ have e1 : execL [.globalGet rpG, i32c c.layout.rEnd, .i32eq] w = some { w with stack := [.i32 1#32] } := by
130
+ simp [execL, stepI, hrp, i32c, Cfg.layout, bool32, hst, ofN]
131
+ have r1 := run_of_execL (fs := fs) (by simp [WI.isCtl, i32c]) e1
132
+ have r2 := run_ite_exit (fs := fs) (w := { w with stack := [.i32 1#32] }) (r := []) (c := 1#32) (k := 5)
133
+ rfl (by decide)
134
+ rw [ret_split]
135
+ exact run_append_abrupt _ (run_append _ r1 r2) trivial
136
+
137
+ theorem sim_halt (n k : Nat) : Run fs (lowerInstr c.layout n k .halt) w (.halted w) :=
138
+ .consAbrupt .exitHalt trivial
139
+
140
+ theorem sim_stub : Run fs [.exitTrap 4] w (.exit 4) :=
141
+ .consAbrupt .exitTrap trivial
142
+
143
+ end Sim
144
+
145
+ end Wasm
146
+ end WordDialect
backend/wasm/Wasm/SimExec.lean ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.SimAux
2
+
3
+ /-!
4
+ # Wasm.SimExec
5
+
6
+ One IR step is simulated by the function the lowering emits for the instruction, for every
7
+ instruction and every outcome (`next`, `halted`, `trapped`). This is the case analysis over the
8
+ whole instruction set that the dispatch-loop theorem (`Wasm.Correct`) iterates.
9
+
10
+ Trap codes (the argument of `proc_exit`): 1 `stackUnderflow`, 2 `badAddress`, 3 `divideByZero`,
11
+ 4 `badPc`, 5 `returnUnderflow` (an empty return stack on `ret`, or an empty auxiliary stack on
12
+ `fromr`/`rfetch`).
13
+
14
+ Every instruction that grows a stack first checks for room, and exits with code 6 when the stack
15
+ is full. So `sim_step` has no capacity hypothesis: the code either simulates the IR step, or the
16
+ stack lacks the headroom `Fits` describes and the code exits 6. Code 6 has no IR counterpart (IR
17
+ stacks are unbounded); it reports that the run exceeded the target's capacity.
18
+ -/
19
+
20
+ namespace WordDialect
21
+ namespace Wasm
22
+
23
+ def trapCode : Trap → Nat
24
+ | .stackUnderflow => 1
25
+ | .badAddress => 2
26
+ | .divideByZero => 3
27
+ | .badPc => 4
28
+ | .returnUnderflow => 5
29
+
30
+ /-- Headroom for one step: room to push one data word, one return index and one auxiliary word.
31
+ With this headroom no overflow guard fires. -/
32
+ def Fits (c : Cfg) (s : State 64) : Prop :=
33
+ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd ∧ c.rBase + 8 * (s.rstack.length + 1) ≤ c.rEnd ∧
34
+ c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd
35
+
36
+ /-- What simulating one IR outcome means for the body of one dispatched function. On `next`, the
37
+ function returns the (clamped) next IR index, and every return index stays within the program. -/
38
+ def StepSim (c : Cfg) (fs : Funcs) (n : Nat) (body : List WI) (w : WState) : Outcome 64 → Prop
39
+ | .next s' => (∀ a ∈ s'.rstack, a ≤ n) ∧
40
+ ∃ w', Run fs body w (.normal w') ∧ Rel0 c s' w' ∧ w'.stack = [.i32 (ofN (min s'.pc n))]
41
+ | .halted s' => ∃ w', Run fs body w (.halted w') ∧ Rel0 c s' w'
42
+ | .trapped t => Run fs body w (.exit (trapCode t))
43
+
44
+ section Step
45
+
46
+ variable {c : Cfg} {s : State 64} {w : WState} {fs : Funcs} {n : Nat}
47
+
48
+ theorem Rel0.setPc (h : Rel0 c s w) (q : Nat) : Rel0 c { s with pc := q } w :=
49
+ { sp := h.sp, rp := h.rp, stack := h.stack, regs := h.regs, mem := h.mem, rs := h.rs,
50
+ irvalid := h.irvalid, size := h.size, capD := h.capD, capR := h.capR, ap := h.ap, aux := h.aux,
51
+ capA := h.capA }
52
+
53
+ theorem next_sim {body : List WI} {s1 : State 64} {q : Nat} (hrs : ∀ a ∈ s1.rstack, a ≤ n)
54
+ (h : Sim1 c fs body s1 w (min q n)) : StepSim c fs n body w (.next { s1 with pc := q }) := by
55
+ obtain ⟨w', hr, h0, hst⟩ := h
56
+ exact ⟨hrs, w', hr, h0.setPc q, hst⟩
57
+
58
+ theorem fall_sim {body : List WI} {d : List W} (hlt : s.pc < n) (hrs : ∀ a ∈ s.rstack, a ≤ n)
59
+ (h : Sim1 c fs body { s with dstack := d } w (s.pc + 1)) : StepSim c fs n body w (s.fall d) := by
60
+ rw [← Nat.min_eq_left (show s.pc + 1 ≤ n by omega)] at h
61
+ exact next_sim (s1 := { s with dstack := d }) hrs h
62
+
63
+ theorem shape1 {l : List W} (h : 1 ≤ l.length) : ∃ a d, l = a :: d := by
64
+ cases l with
65
+ | nil => simp at h
66
+ | cons a d => exact ⟨a, d, rfl⟩
67
+
68
+ theorem shape2 {l : List W} (h : 2 ≤ l.length) : ∃ b a d, l = b :: a :: d := by
69
+ rcases l with _ | ⟨b, _ | ⟨a, d⟩⟩
70
+ · simp at h
71
+ · simp at h
72
+ · exact ⟨b, a, d, rfl⟩
73
+
74
+ theorem shape3 {l : List W} (h : 3 ≤ l.length) : ∃ x y z d, l = x :: y :: z :: d := by
75
+ rcases l with _ | ⟨x, _ | ⟨y, _ | ⟨z, d⟩⟩⟩
76
+ · simp at h
77
+ · simp at h
78
+ · simp at h
79
+ · exact ⟨x, y, z, d, rfl⟩
80
+
81
+ /-- Every instruction that pops operands starts with the operand-count guard. -/
82
+ theorem lowerInstr_uf (L : Layout) (n k : Nat) (i : WordDialect.Instr 64) (h : 0 < i.pops) :
83
+ ∃ rest, lowerInstr L n k i = uf L i.pops ++ rest ∧ i.pops ≤ 3 := by
84
+ cases i <;> simp only [Instr.pops] at h ⊢ <;>
85
+ first
86
+ | exact absurd h (by decide)
87
+ | exact ⟨_, by simp only [lowerInstr, binOp, shiftOp, List.append_assoc]; rfl, by decide⟩
88
+
89
+ theorem sim_step (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = [])
90
+ {p : Prog 64} (hn : p.length < 2 ^ 32) (hrs : ∀ a ∈ s.rstack, a ≤ p.length)
91
+ {i : WordDialect.Instr 64} (hi : p[s.pc]? = some i)
92
+ (hreg : ∀ r, (i = .push r ∨ i = .pop r) → r < c.nregs) :
93
+ StepSim c fs p.length (lowerInstr c.layout p.length s.pc i) w (exec i s) ∨
94
+ (¬ Fits c s ∧ Run fs (lowerInstr c.layout p.length s.pc i) w (.exit 6)) := by
95
+ have nfD : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd → ¬ Fits c s := fun h hf => h hf.1
96
+ have nfR : ¬ c.rBase + 8 * (s.rstack.length + 1) ≤ c.rEnd → ¬ Fits c s := fun h hf => h hf.2.1
97
+ have nfA : ¬ c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd → ¬ Fits c s := fun h hf => h hf.2.2
98
+ have hlt : s.pc < p.length := by
99
+ by_cases h : s.pc < p.length
100
+ · exact h
101
+ · rw [List.getElem?_eq_none (by omega)] at hi; simp at hi
102
+ by_cases hk : s.dstack.length < i.pops
103
+ · rw [exec_underflow i s hk]
104
+ obtain ⟨rest, hlow, h3⟩ := lowerInstr_uf c.layout p.length s.pc i (by omega)
105
+ rw [hlow]
106
+ exact .inl <| run_append_abrupt _ (uf_trap hg hs (by omega) h3 hk) trivial
107
+ have hk' : i.pops ≤ s.dstack.length := by omega
108
+ cases i with
109
+ | word v =>
110
+ by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
111
+ · exact .inl <| fall_sim hlt hrs (sim_word _ _ hg hs hst v hf)
112
+ · exact .inr ⟨nfD hf, sim_word_ovf _ _ hg hs v hf⟩
113
+ | ptr q =>
114
+ by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
115
+ · exact .inl <| fall_sim hlt hrs (sim_ptr _ _ hg hs hst q hf)
116
+ · exact .inr ⟨nfD hf, sim_ptr_ovf _ _ hg hs q hf⟩
117
+ | load =>
118
+ obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
119
+ by_cases ha : a.toNat < c.M
120
+ · have hv : s.mem.valid a = true := (valid_iff hs a).mpr ha
121
+ have hex : exec .load s = s.fall (s.mem.cell a :: d) := by
122
+ simp [exec, hd, Memory.read?, hv]
123
+ rw [hex]
124
+ exact .inl <| fall_sim hlt hrs (sim_load _ _ hg hs hst hd ha)
125
+ · have hv : s.mem.valid a = false := by
126
+ cases h : s.mem.valid a
127
+ · rfl
128
+ · exact absurd ((valid_iff hs a).mp h) ha
129
+ have hex : exec .load s = .trapped .badAddress := by
130
+ simp [exec, hd, Memory.read?, hv]
131
+ rw [hex]
132
+ exact .inl <| sim_load_trap _ _ hg hs hd (by omega)
133
+ | store =>
134
+ obtain ⟨a, v, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
135
+ cases hw : s.mem.write? a v with
136
+ | none =>
137
+ have hv : s.mem.valid a = false := (Memory.write?_none_iff s.mem a v).mp hw
138
+ have ha : ¬ a.toNat < c.M := fun h => by rw [(valid_iff hs a).mpr h] at hv; simp at hv
139
+ have hex : exec .store s = .trapped .badAddress := by simp [exec, hd, hw]
140
+ rw [hex]
141
+ exact .inl <| sim_store_trap _ _ hg hs hd (by omega)
142
+ | some im =>
143
+ have hex : exec .store s = .next { { s with dstack := d, mem := im } with pc := s.pc + 1 } := by
144
+ simp [exec, hd, hw]
145
+ rw [hex]
146
+ have h := sim_store (fs := fs) p.length s.pc hg hs hst hd hw
147
+ rw [← Nat.min_eq_left (show s.pc + 1 ≤ p.length by omega)] at h
148
+ exact .inl <| next_sim hrs h
149
+ | add =>
150
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
151
+ rw [show exec .add s = s.fall ((a + b) :: d) by simp [exec, hd]]
152
+ exact .inl <| fall_sim hlt hrs (sim_add _ _ hg hs hst hd)
153
+ | sub =>
154
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
155
+ rw [show exec .sub s = s.fall ((a - b) :: d) by simp [exec, hd]]
156
+ exact .inl <| fall_sim hlt hrs (sim_sub _ _ hg hs hst hd)
157
+ | mul =>
158
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
159
+ rw [show exec .mul s = s.fall ((a * b) :: d) by simp [exec, hd]]
160
+ exact .inl <| fall_sim hlt hrs (sim_mul _ _ hg hs hst hd)
161
+ | div =>
162
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
163
+ by_cases hb : b = 0#64
164
+ · subst hb
165
+ rw [show exec .div s = .trapped .divideByZero by simp [exec, hd]]
166
+ exact .inl <| sim_div_trap _ _ hg hs hd
167
+ · rw [show exec .div s = s.fall (a.udiv b :: d) by simp [exec, hd, hb]]
168
+ exact .inl <| fall_sim hlt hrs (sim_div _ _ hg hs hst hd hb)
169
+ | sdiv =>
170
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
171
+ by_cases hb : b = 0#64
172
+ · subst hb
173
+ rw [show exec .sdiv s = .trapped .divideByZero by simp [exec, hd]]
174
+ exact .inl <| sim_sdiv_trap _ _ hg hs hd
175
+ · rw [show exec .sdiv s = s.fall (a.sdiv b :: d) by simp [exec, hd, hb]]
176
+ exact .inl <| fall_sim hlt hrs (sim_sdiv _ _ hg hs hst hd hb)
177
+ | and =>
178
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
179
+ rw [show exec .and s = s.fall ((a &&& b) :: d) by simp [exec, hd]]
180
+ exact .inl <| fall_sim hlt hrs (sim_and _ _ hg hs hst hd)
181
+ | or =>
182
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
183
+ rw [show exec .or s = s.fall ((a ||| b) :: d) by simp [exec, hd]]
184
+ exact .inl <| fall_sim hlt hrs (sim_or _ _ hg hs hst hd)
185
+ | xor =>
186
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
187
+ rw [show exec .xor s = s.fall ((a ^^^ b) :: d) by simp [exec, hd]]
188
+ exact .inl <| fall_sim hlt hrs (sim_xor _ _ hg hs hst hd)
189
+ | not =>
190
+ obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
191
+ rw [show exec .not s = s.fall ((~~~a) :: d) by simp [exec, hd]]
192
+ exact .inl <| fall_sim hlt hrs (sim_not _ _ hg hs hst hd)
193
+ | shl =>
194
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
195
+ rw [show exec .shl s = s.fall (Word.shl a b :: d) by simp [exec, hd]]
196
+ exact .inl <| fall_sim hlt hrs (sim_shl _ _ hg hs hst hd)
197
+ | shr =>
198
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
199
+ rw [show exec .shr s = s.fall (Word.shr a b :: d) by simp [exec, hd]]
200
+ exact .inl <| fall_sim hlt hrs (sim_shr _ _ hg hs hst hd)
201
+ | rotl =>
202
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
203
+ rw [show exec .rotl s = s.fall (Word.rotl a b :: d) by simp [exec, hd]]
204
+ exact .inl <| fall_sim hlt hrs (sim_rotl _ _ hg hs hst hd)
205
+ | rotr =>
206
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
207
+ rw [show exec .rotr s = s.fall (Word.rotr a b :: d) by simp [exec, hd]]
208
+ exact .inl <| fall_sim hlt hrs (sim_rotr _ _ hg hs hst hd)
209
+ | cmp cd =>
210
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
211
+ rw [show exec (.cmp cd) s = s.fall (Word.ofBool (cd.eval a b) :: d) by simp [exec, hd]]
212
+ exact .inl <| fall_sim hlt hrs (sim_cmp _ _ cd hg hs hst hd)
213
+ | select =>
214
+ obtain ⟨cc, y, x, d, hd⟩ := shape3 (by simpa [Instr.pops] using hk')
215
+ rw [show exec .select s = s.fall ((if Word.isTrue cc then x else y) :: d) by simp [exec, hd]]
216
+ exact .inl <| fall_sim hlt hrs (sim_select _ _ hg hs hst hd)
217
+ | jmp t =>
218
+ rw [show exec (.jmp t) s = .next { s with pc := t } by simp [exec]]
219
+ exact .inl <| next_sim hrs (sim_jmp _ _ t hs hst)
220
+ | branch t =>
221
+ obtain ⟨cv, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
222
+ have h := sim_branch (fs := fs) p.length s.pc t hg hs hst hd
223
+ by_cases hc : Word.isTrue cv = true
224
+ · rw [show exec (.branch t) s = .next { { s with dstack := d } with pc := t } by simp [exec, hd, hc]]
225
+ simp only [hc, ↓reduceIte] at h
226
+ exact .inl <| next_sim hrs h
227
+ · rw [show exec (.branch t) s = s.fall d by simp [exec, hd, hc]]
228
+ have hc' : Word.isTrue cv = false := by simpa using hc
229
+ simp only [hc', Bool.false_eq_true, ↓reduceIte] at h
230
+ exact .inl <| fall_sim hlt hrs h
231
+ | call t =>
232
+ rw [show exec (.call t) s = .next { { s with rstack := (s.pc + 1) :: s.rstack } with pc := t } by simp [exec]]
233
+ by_cases hf : c.rBase + 8 * (s.rstack.length + 1) ≤ c.rEnd
234
+ · refine .inl (next_sim ?_ (sim_call _ _ t hg hs hst hf))
235
+ intro a ha
236
+ simp only [List.mem_cons] at ha
237
+ rcases ha with ha | ha
238
+ · omega
239
+ · exact hrs a ha
240
+ · exact .inr ⟨nfR hf, sim_call_ovf _ _ t hg hs hf⟩
241
+ | ret =>
242
+ cases hr : s.rstack with
243
+ | nil =>
244
+ rw [show exec .ret s = .trapped .returnUnderflow by simp [exec, hr]]
245
+ exact .inl <| sim_ret_trap _ _ hs hst hr
246
+ | cons a rs =>
247
+ rw [show exec .ret s = .next { { s with rstack := rs } with pc := a } by simp [exec, hr]]
248
+ have ha : a ≤ p.length := hrs a (by simp [hr])
249
+ have h := sim_ret (fs := fs) p.length s.pc hg hs hst hr (by omega)
250
+ rw [← Nat.min_eq_left ha] at h
251
+ exact .inl <| next_sim (fun b hb => hrs b (by simp [hr, hb])) h
252
+ | push r =>
253
+ rw [show exec (.push r) s = s.fall (s.regs r :: s.dstack) by simp [exec]]
254
+ by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
255
+ · exact .inl <| fall_sim hlt hrs (sim_push _ _ hg hs hst (hreg r (.inl rfl)) hf)
256
+ · exact .inr ⟨nfD hf, sim_push_ovf _ _ hg hs r hf⟩
257
+ | pop r =>
258
+ obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
259
+ rw [show exec (.pop r) s =
260
+ .next { { s with dstack := d, regs := State.setReg s.regs r a } with pc := s.pc + 1 } by
261
+ simp [exec, hd]]
262
+ have h := sim_pop (fs := fs) p.length s.pc hg hs hst (hreg r (.inr rfl)) hd
263
+ rw [← Nat.min_eq_left (show s.pc + 1 ≤ p.length by omega)] at h
264
+ exact .inl <| next_sim hrs h
265
+ | dup =>
266
+ obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
267
+ rw [show exec .dup s = s.fall (a :: a :: d) by simp [exec, hd]]
268
+ by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
269
+ · exact .inl <| fall_sim hlt hrs (sim_dup _ _ hg hs hst hd hf)
270
+ · exact .inr ⟨nfD hf, sim_dup_ovf _ _ hg hs hd hf⟩
271
+ | drop =>
272
+ obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
273
+ rw [show exec .drop s = s.fall d by simp [exec, hd]]
274
+ exact .inl <| fall_sim hlt hrs (sim_drop _ _ hg hs hst hd)
275
+ | swap =>
276
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
277
+ rw [show exec .swap s = s.fall (a :: b :: d) by simp [exec, hd]]
278
+ exact .inl <| fall_sim hlt hrs (sim_swap _ _ hg hs hst hd)
279
+ | over =>
280
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
281
+ rw [show exec .over s = s.fall (a :: b :: a :: d) by simp [exec, hd]]
282
+ by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
283
+ · exact .inl <| fall_sim hlt hrs (sim_over _ _ hg hs hst hd hf)
284
+ · exact .inr ⟨nfD hf, sim_over_ovf _ _ hg hs hd hf⟩
285
+ | rot =>
286
+ obtain ⟨x, y, z, d, hd⟩ := shape3 (by simpa [Instr.pops] using hk')
287
+ rw [show exec .rot s = s.fall (z :: x :: y :: d) by simp [exec, hd]]
288
+ exact .inl <| fall_sim hlt hrs (sim_rot _ _ hg hs hst hd)
289
+ | tor =>
290
+ obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
291
+ rw [show exec .tor s = .next { { s with dstack := d, astack := a :: s.astack } with pc := s.pc + 1 } by
292
+ simp [exec, hd]]
293
+ by_cases hf : c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd
294
+ · have h := sim_tor (fs := fs) p.length s.pc hg hs hst hd hf
295
+ rw [← Nat.min_eq_left (show s.pc + 1 ≤ p.length by omega)] at h
296
+ exact .inl <| next_sim hrs h
297
+ · exact .inr ⟨nfA hf, sim_tor_ovf _ _ hg hs hd hf⟩
298
+ | fromr =>
299
+ cases ha : s.astack with
300
+ | nil =>
301
+ rw [show exec .fromr s = .trapped .returnUnderflow by simp [exec, ha]]
302
+ exact .inl <| sim_fromr_trap _ _ hs ha
303
+ | cons a as =>
304
+ rw [show exec .fromr s =
305
+ .next { { s with dstack := a :: s.dstack, astack := as } with pc := s.pc + 1 } by
306
+ simp [exec, ha]]
307
+ by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
308
+ · have h := sim_fromr (fs := fs) p.length s.pc hg hs hst ha hf
309
+ rw [← Nat.min_eq_left (show s.pc + 1 ≤ p.length by omega)] at h
310
+ exact .inl <| next_sim hrs h
311
+ · exact .inr ⟨nfD hf, sim_fromr_ovf _ _ hg hs ha hf⟩
312
+ | rfetch =>
313
+ cases ha : s.astack with
314
+ | nil =>
315
+ rw [show exec .rfetch s = .trapped .returnUnderflow by simp [exec, ha]]
316
+ exact .inl <| sim_rfetch_trap _ _ hs ha
317
+ | cons a as =>
318
+ rw [show exec .rfetch s = s.fall (a :: s.dstack) by simp [exec, ha]]
319
+ by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
320
+ · exact .inl <| fall_sim hlt hrs (sim_rfetch _ _ hg hs hst ha hf)
321
+ · exact .inr ⟨nfD hf, sim_rfetch_ovf _ _ hg hs ha hf⟩
322
+ | halt =>
323
+ rw [show exec .halt s = .halted s by simp [exec]]
324
+ exact .inl <| ⟨w, sim_halt _ _, hs⟩
325
+
326
+ end Step
327
+
328
+ end Wasm
329
+ end WordDialect
backend/wasm/Wasm/SimMem.lean ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.SimStack
2
+
3
+ /-!
4
+ # Wasm.SimMem
5
+
6
+ `load` and `store`: the bounds check `memAddr` (address below `M`, else exit 2 = `badAddress`),
7
+ the byte address `mb + 8a`, and the simulation of both instructions and of their traps.
8
+ -/
9
+
10
+ namespace WordDialect
11
+ namespace Wasm
12
+
13
+ section Sim
14
+
15
+ variable {c : Cfg} {s : State 64} {w : WState} {fs : Funcs}
16
+
17
+ theorem memAddr_split (L : Layout) : memAddr L =
18
+ (ldS 0 ++ [.i64const (BitVec.ofNat 64 L.M), .i64geu]) ++ [.ite [.exitTrap 2]] ++
19
+ (ldS 0 ++ [.i32wrap, i32c 3, .i32shl, i32c L.mb, .i32add]) := by
20
+ simp [memAddr, List.append_assoc]
21
+
22
+ theorem M_lt (hg : Geom c) : c.M < 2 ^ 32 := by
23
+ have := hg.mb_le; have := hg.msize_lt; omega
24
+
25
+ theorem geu_M (hg : Geom c) (a : W) :
26
+ (BitVec.ofNat 64 c.M).ule a = decide (c.M ≤ a.toNat) := by
27
+ have hM := M_lt hg
28
+ have : c.M % 2 ^ 64 = c.M := Nat.mod_eq_of_lt (by omega)
29
+ simp [BitVec.ule_eq_decide, BitVec.toNat_ofNat, this]
30
+
31
+ theorem addr_eq (hg : Geom c) {a : W} (ha : a.toNat < c.M) :
32
+ BitVec.setWidth 32 a <<< 3 + BitVec.ofNat 32 c.mb = ofN (c.mb + 8 * a.toNat) := by
33
+ have hmb := hg.mb_le; have hsz := hg.msize_lt
34
+ apply BitVec.eq_of_toNat_eq
35
+ have h1 : a.toNat < 2 ^ 32 := by omega
36
+ have h2 : a.toNat * 8 < 2 ^ 32 := by omega
37
+ have h3 : c.mb < 2 ^ 32 := by omega
38
+ have h4 : c.mb + 8 * a.toNat < 2 ^ 32 := by omega
39
+ simp [ofN, BitVec.toNat_add, BitVec.toNat_shiftLeft, BitVec.toNat_setWidth, Nat.shiftLeft_eq,
40
+ Nat.mod_eq_of_lt h1, Nat.mod_eq_of_lt h2, Nat.mod_eq_of_lt h3, Nat.mod_eq_of_lt h4]
41
+ omega
42
+
43
+ /-- In-bounds address: `memAddr` leaves the byte address `mb + 8a` on the operand stack. -/
44
+ theorem memAddr_ok (hg : Geom c) {u : WState} {sp : Nat} (hsp : u.globals spG = .i32 (ofN sp))
45
+ (hX : sp < 2 ^ 32) {a : W} (hr : u.mem.read64 sp = some a) (ha : a.toNat < c.M) :
46
+ Run fs (memAddr c.layout) u (.normal { u with stack := .i32 (ofN (c.mb + 8 * a.toNat)) :: u.stack }) := by
47
+ have hlt : ¬ c.M ≤ a.toNat := by omega
48
+ have e1 : execL (ldS 0 ++ [.i64const (BitVec.ofNat 64 c.layout.M), .i64geu]) u =
49
+ some { u with stack := .i32 0#32 :: u.stack } := by
50
+ simp [ldS, execL, stepI, cmp64, hsp, ofN_toNat hX, hr, Cfg.layout, geu_M hg, hlt, bool32]
51
+ have r1 := run_of_execL (fs := fs) (by simp [ldS, WI.isCtl]) e1
52
+ have r2 := run_ite_false (fs := fs) (thn := [.exitTrap 2]) (w := { u with stack := .i32 0#32 :: u.stack })
53
+ (r := u.stack) rfl
54
+ have e3 : execL (ldS 0 ++ [.i32wrap, i32c 3, .i32shl, i32c c.layout.mb, .i32add]) { u with stack := u.stack } =
55
+ some { u with stack := .i32 (ofN (c.mb + 8 * a.toNat)) :: u.stack } := by
56
+ simp [ldS, execL, stepI, bin32, hsp, ofN_toNat hX, hr, i32c, Cfg.layout, addr_eq hg ha]
57
+ have r3 := run_of_execL (fs := fs) (by simp [ldS, WI.isCtl, i32c]) e3
58
+ rw [memAddr_split]
59
+ exact run_append _ (run_append _ r1 r2) r3
60
+
61
+ /-- Out-of-bounds address: `memAddr` exits with code 2 (`badAddress`). -/
62
+ theorem memAddr_trap (hg : Geom c) {u : WState} {sp : Nat} (hsp : u.globals spG = .i32 (ofN sp))
63
+ (hX : sp < 2 ^ 32) {a : W} (hr : u.mem.read64 sp = some a) (ha : c.M ≤ a.toNat) :
64
+ Run fs (memAddr c.layout) u (.exit 2) := by
65
+ have e1 : execL (ldS 0 ++ [.i64const (BitVec.ofNat 64 c.layout.M), .i64geu]) u =
66
+ some { u with stack := .i32 1#32 :: u.stack } := by
67
+ simp [ldS, execL, stepI, cmp64, hsp, ofN_toNat hX, hr, Cfg.layout, geu_M hg, ha, bool32]
68
+ have r1 := run_of_execL (fs := fs) (by simp [ldS, WI.isCtl]) e1
69
+ have r2 := run_ite_exit (fs := fs) (w := { u with stack := .i32 1#32 :: u.stack }) (r := u.stack)
70
+ (c := 1#32) (k := 2) rfl (by decide)
71
+ rw [memAddr_split]
72
+ exact run_append_abrupt _ (run_append _ r1 r2) trivial
73
+
74
+ /-- `valid a` is exactly `a < M`. -/
75
+ theorem valid_iff (hs : Rel0 c s w) (a : W) : s.mem.valid a = true ↔ a.toNat < c.M := by
76
+ rw [hs.irvalid a]; simp
77
+
78
+ theorem mem_cell (hs : Rel0 c s w) {a : W} (ha : a.toNat < c.M) :
79
+ w.mem.read64 (c.mb + 8 * a.toNat) = some (s.mem.cell a) := by
80
+ have := hs.mem a.toNat ha
81
+ rwa [BitVec.ofNat_toNat, BitVec.setWidth_eq] at this
82
+
83
+ theorem addr_lt (hg : Geom c) {a : W} (ha : a.toNat < c.M) : c.mb + 8 * a.toNat < 2 ^ 32 := by
84
+ have := hg.mb_le; have := hg.msize_lt; omega
85
+
86
+ theorem sim_load (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a : W} {d : List W}
87
+ (hd : s.dstack = a :: d) (ha : a.toNat < c.M) :
88
+ Sim1 c fs (lowerInstr c.layout n k .load) { s with dstack := s.mem.cell a :: d } w (k + 1) := by
89
+ have hlen : s.dstack.length = d.length + 1 := by simp [hd]
90
+ have hX := sp_lt hg hs
91
+ have hcap := hs.capD
92
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some a := by
93
+ have := hs.stack 0 a (by simp [hd]); simpa using this
94
+ obtain ⟨m', hm'⟩ : ∃ m', w.mem.write64 (c.dEnd - 8 * s.dstack.length) (s.mem.cell a) = some m' :=
95
+ write64_some (by rw [hs.size]; have := hg.dEnd_le; omega)
96
+ have hA := addr_lt hg ha
97
+ have rg : Run fs [.globalGet spG] w
98
+ (.normal { w with stack := .i32 (ofN (c.dEnd - 8 * s.dstack.length)) :: w.stack }) :=
99
+ run_of_execL (by simp [WI.isCtl]) (by simp [execL, stepI, hs.sp])
100
+ have rm := memAddr_ok (fs := fs) hg (u := { w with stack := .i32 (ofN (c.dEnd - 8 * s.dstack.length)) :: w.stack })
101
+ hs.sp hX hr0 ha
102
+ have el : execL [.i64load 0, .i64store 0]
103
+ { { w with stack := .i32 (ofN (c.dEnd - 8 * s.dstack.length)) :: w.stack } with
104
+ stack := .i32 (ofN (c.mb + 8 * a.toNat)) ::
105
+ ({ w with stack := .i32 (ofN (c.dEnd - 8 * s.dstack.length)) :: w.stack } : WState).stack } =
106
+ some { w with mem := m' } := by
107
+ simp [execL, stepI, ofN_toNat hA, ofN_toNat hX, mem_cell hs ha, hm']
108
+ have rl := run_of_execL (fs := fs) (by simp [WI.isCtl]) el
109
+ have hrel := wr_stack hg hs (j := 0) (by omega) (v := s.mem.cell a) (m' := m') (by simpa using hm')
110
+ have e : s.dstack.set 0 (s.mem.cell a) = s.mem.cell a :: d := by simp [hd]
111
+ rw [e] at hrel
112
+ refine ⟨{ w with mem := m', stack := [.i32 (ofN (k + 1))] }, ?_, hrel.setStack _, rfl⟩
113
+ have := run_append _ (uf1_pass (fs := fs) hg hs hd)
114
+ (run_append _ rg (run_append _ rm (run_append _ rl (run_finish (fs := fs) (k + 1)))))
115
+ simpa [lowerInstr, List.append_assoc, hst] using this
116
+
117
+ theorem sim_load_trap (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) {a : W} {d : List W}
118
+ (hd : s.dstack = a :: d) (ha : c.M ≤ a.toNat) :
119
+ Run fs (lowerInstr c.layout n k .load) w (.exit 2) := by
120
+ have hX := sp_lt hg hs
121
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some a := by
122
+ have := hs.stack 0 a (by simp [hd]); simpa using this
123
+ have rg : Run fs [.globalGet spG] w
124
+ (.normal { w with stack := .i32 (ofN (c.dEnd - 8 * s.dstack.length)) :: w.stack }) :=
125
+ run_of_execL (by simp [WI.isCtl]) (by simp [execL, stepI, hs.sp])
126
+ have rm := memAddr_trap (fs := fs) hg (u := { w with stack := .i32 (ofN (c.dEnd - 8 * s.dstack.length)) :: w.stack })
127
+ hs.sp hX hr0 ha
128
+ have := run_append _ (uf1_pass (fs := fs) hg hs hd)
129
+ (run_append _ rg (run_append_abrupt _ rm (b := [.i64load 0, .i64store 0] ++ [i32c (k + 1)]) trivial))
130
+ simpa [lowerInstr, List.append_assoc] using this
131
+
132
+ theorem sim_store (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a v : W}
133
+ {d : List W} (hd : s.dstack = a :: v :: d) {im : WordDialect.Memory 64}
134
+ (hv : s.mem.write? a v = some im) :
135
+ Sim1 c fs (lowerInstr c.layout n k .store) { s with dstack := d, mem := im } w (k + 1) := by
136
+ have hlen : s.dstack.length = d.length + 2 := by simp [hd]
137
+ have hX := sp_lt hg hs
138
+ have hcap := hs.capD
139
+ have hvalid : s.mem.valid a = true := by
140
+ by_cases hh : s.mem.valid a = true
141
+ · exact hh
142
+ · have := (Memory.write?_none_iff s.mem a v).mpr (by simpa using hh); rw [hv] at this; simp at this
143
+ have ha : a.toNat < c.M := (valid_iff hs a).mp hvalid
144
+ have hA := addr_lt hg ha
145
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some a := by
146
+ have := hs.stack 0 a (by simp [hd]); simpa using this
147
+ have hr1 : w.mem.read64 (c.dEnd - 8 * s.dstack.length + 8) = some v := by
148
+ have := hs.stack 1 v (by simp [hd]); simpa using this
149
+ obtain ⟨m', hm'⟩ : ∃ m', w.mem.write64 (c.mb + 8 * a.toNat) v = some m' :=
150
+ write64_some (by rw [hs.size]; have := hg.mb_le; omega)
151
+ have rm := memAddr_ok (fs := fs) hg (u := w) hs.sp hX hr0 ha
152
+ have hsp16 : ofN (c.dEnd - 8 * s.dstack.length) + 16#32 = ofN (c.dEnd - 8 * s.dstack.length + 16) :=
153
+ ofN_add _ _
154
+ have el : execL (ldS 8 ++ [.i64store 0] ++ spAdd 16)
155
+ { w with stack := .i32 (ofN (c.mb + 8 * a.toNat)) :: w.stack } =
156
+ some { w with mem := m', globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * s.dstack.length + 16))) } := by
157
+ simp [ldS, spAdd, execL, stepI, bin32, hs.sp, ofN_toNat hX, ofN_toNat hA, hr1, hm', i32c, hst, hsp16]
158
+ unfold setG
159
+ rfl
160
+ have rl := run_of_execL (fs := fs) (by simp [ldS, spAdd, WI.isCtl, i32c]) el
161
+ have hrel1 := wr_mem hg hs hv hm'
162
+ have hrel2 := adj_sp hg hrel1 (n := 2) (by simp [hd])
163
+ have e3 : ({ { s with mem := im } with dstack := ({ s with mem := im } : State 64).dstack.drop 2 } : State 64) =
164
+ { s with dstack := d, mem := im } := by
165
+ simp [hd]
166
+ rw [e3] at hrel2
167
+ have e2 : c.dEnd - 8 * s.dstack.length + 16 = c.dEnd - 8 * (s.dstack.length - 2) := by omega
168
+ rw [e2] at rl
169
+ refine ⟨_, ?_, hrel2.setStack [.i32 (ofN (k + 1))], rfl⟩
170
+ have := run_append _ (uf2_pass (fs := fs) hg hs hd)
171
+ (run_append _ rm (run_append _ rl (run_finish (fs := fs) (k + 1))))
172
+ simpa [lowerInstr, List.append_assoc, hst] using this
173
+
174
+ theorem sim_store_trap (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) {a v : W} {d : List W}
175
+ (hd : s.dstack = a :: v :: d) (ha : c.M ≤ a.toNat) :
176
+ Run fs (lowerInstr c.layout n k .store) w (.exit 2) := by
177
+ have hX := sp_lt hg hs
178
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some a := by
179
+ have := hs.stack 0 a (by simp [hd]); simpa using this
180
+ have rm := memAddr_trap (fs := fs) hg (u := w) hs.sp hX hr0 ha
181
+ have := run_append _ (uf2_pass (fs := fs) hg hs hd)
182
+ (run_append_abrupt _ rm (b := ldS 8 ++ [.i64store 0] ++ spAdd 16 ++ [i32c (k + 1)]) trivial)
183
+ simpa [lowerInstr, List.append_assoc] using this
184
+
185
+ end Sim
186
+
187
+ end Wasm
188
+ end WordDialect
backend/wasm/Wasm/SimStack.lean ADDED
@@ -0,0 +1,398 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Wasm.SimAlu2
2
+
3
+ /-!
4
+ # Wasm.SimStack
5
+
6
+ `not`, `drop`, `dup`, `over`, `swap`, `rot`, `select`, `word`, `ptr`, `push`, `pop`.
7
+ -/
8
+
9
+ namespace WordDialect
10
+ namespace Wasm
11
+
12
+ section Sim
13
+
14
+ variable {c : Cfg} {s : State 64} {w : WState} {fs : Funcs}
15
+
16
+ /-- Finish an instruction: given the execution of its body, append the next-pc push. -/
17
+ theorem core_gen {pre B : List WI} {s' : State 64} (hpre : Run fs pre w (.normal w))
18
+ (hctl : ∀ i ∈ B, i.isCtl = false)
19
+ (hB : ∃ w1, execL B w = some w1 ∧ Rel0 c s' w1 ∧ w1.stack = []) (nxt : Nat) :
20
+ ∃ w', Run fs (pre ++ (B ++ [i32c nxt])) w (.normal w') ∧ Rel0 c s' w' ∧
21
+ w'.stack = [.i32 (ofN nxt)] := by
22
+ obtain ⟨w1, he, hr, hs1⟩ := hB
23
+ exact ⟨{ w1 with stack := .i32 (ofN nxt) :: w1.stack },
24
+ run_append _ hpre (run_append _ (run_of_execL hctl he) (run_finish nxt)), hr.setStack _, by simp [hs1]⟩
25
+
26
+ theorem uf1_pass (hg : Geom c) (hs : Rel0 c s w) {a : W} {d : List W} (hd : s.dstack = a :: d) :
27
+ Run fs (uf c.layout 1) w (.normal w) :=
28
+ uf_pass hg hs (k := 1) (by omega) (by simp [hd])
29
+
30
+ theorem uf3_pass (hg : Geom c) (hs : Rel0 c s w) {x y z : W} {d : List W} (hd : s.dstack = x :: y :: z :: d) :
31
+ Run fs (uf c.layout 3) w (.normal w) :=
32
+ uf_pass hg hs (k := 3) (by omega) (by simp [hd])
33
+
34
+ theorem ld_slot (hs : Rel0 c s w) {j : Nat} {v : W} (hj : s.dstack[j]? = some v) :
35
+ w.mem.read64 (c.dEnd - 8 * s.dstack.length + 8 * j) = some v := hs.stack j v hj
36
+
37
+ theorem xor_lit (a : W) : a ^^^ 18446744073709551615#64 = ~~~a := by
38
+ have := BitVec.xor_allOnes (x := a)
39
+ simpa using this
40
+
41
+ theorem sim_not (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a : W} {d : List W}
42
+ (hd : s.dstack = a :: d) :
43
+ Sim1 c fs (lowerInstr c.layout n k .not) { s with dstack := (~~~a) :: d } w (k + 1) := by
44
+ have hlen : s.dstack.length = d.length + 1 := by simp [hd]
45
+ have hX := sp_lt hg hs
46
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some a := by
47
+ have := hs.stack 0 a (by simp [hd]); simpa using this
48
+ obtain ⟨m', hm'⟩ : ∃ m', w.mem.write64 (c.dEnd - 8 * s.dstack.length) (~~~a) = some m' :=
49
+ write64_some (by rw [hs.size]; have := hg.dEnd_le; have := hs.capD; omega)
50
+ have hexec : execL ([.globalGet spG] ++ ldS 0 ++ [.i64const (BitVec.allOnes 64), .i64xor, .i64store 0]) w = some { w with mem := m' } := by
51
+ simp [ldS, execL, stepI, bin64, hs.sp, ofN_toNat hX, hr0, hm', xor_lit]
52
+ have hrel := wr_stack hg hs (j := 0) (by omega) (v := ~~~a) (m' := m') (by simpa using hm')
53
+ have e : s.dstack.set 0 (~~~a) = (~~~a) :: d := by simp [hd]
54
+ rw [e] at hrel
55
+ have := core_gen (fs := fs) (pre := uf c.layout 1) (uf1_pass hg hs hd)
56
+ (B := [.globalGet spG] ++ ldS 0 ++ [.i64const (BitVec.allOnes 64), .i64xor, .i64store 0])
57
+ (by simp only [ldS, List.append_assoc]; exact allSimple_spec (by decide)) ⟨_, hexec, hrel, hst⟩ (k + 1)
58
+ simpa [lowerInstr, List.append_assoc] using this
59
+
60
+ theorem sim_drop (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a : W} {d : List W}
61
+ (hd : s.dstack = a :: d) :
62
+ Sim1 c fs (lowerInstr c.layout n k .drop) { s with dstack := d } w (k + 1) := by
63
+ have hX := sp_lt hg hs
64
+ have hsp8 : ofN (c.dEnd - 8 * s.dstack.length) + 8#32 = ofN (c.dEnd - 8 * s.dstack.length + 8) :=
65
+ ofN_add _ _
66
+ have hexec : execL (spAdd 8) w = some { w with globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * s.dstack.length + 8))) } := by
67
+ simp [spAdd, execL, stepI, bin32, hs.sp, i32c]
68
+ unfold setG
69
+ rw [hsp8]
70
+ have hrel := adj_sp hg hs (n := 1) (by simp [hd])
71
+ have e : s.dstack.drop 1 = d := by simp [hd]
72
+ have e2 : c.dEnd - 8 * s.dstack.length + 8 = c.dEnd - 8 * (s.dstack.length - 1) := by
73
+ have := hs.capD; have : s.dstack.length = d.length + 1 := by simp [hd]
74
+ omega
75
+ rw [e2] at hexec
76
+ have hrel' : Rel0 c { s with dstack := d } { w with globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * (s.dstack.length - 1)))) } := by
77
+ have e3 : ({ s with dstack := s.dstack.drop 1 } : State 64) = { s with dstack := d } := by rw [e]
78
+ rw [e3] at hrel; exact hrel
79
+ have := core_gen (fs := fs) (pre := uf c.layout 1) (uf1_pass hg hs hd) (B := spAdd 8)
80
+ (allSimple_spec (by decide)) ⟨_, hexec, hrel', hst⟩ (k + 1)
81
+ simpa [lowerInstr, List.append_assoc] using this
82
+
83
+ theorem push_gen (hg : Geom c) (hs : Rel0 c s w)
84
+ (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) {val : List WI} {v : W}
85
+ (hctl : ∀ i ∈ val, i.isCtl = false)
86
+ (hval : ∀ u : WState, u.globals spG = .i32 (ofN (c.dEnd - 8 * (s.dstack.length + 1))) →
87
+ u.mem = w.mem → u.globals apG = w.globals apG →
88
+ execL val u = some { u with stack := .i64 v :: u.stack }) :
89
+ ∃ w1, execL (pushWith val) w = some w1 ∧ Rel0 c { s with dstack := v :: s.dstack } w1 ∧
90
+ w1.stack = w.stack := by
91
+ have hX := sp_lt hg hs
92
+ have hcap := hs.capD
93
+ have hdE := hg.dEnd_le
94
+ have hsz := hg.msize_lt
95
+ have hlt : c.dEnd - 8 * (s.dstack.length + 1) < 2 ^ 32 := by omega
96
+ have hsub : ofN (c.dEnd - 8 * s.dstack.length) - 8#32 = ofN (c.dEnd - 8 * (s.dstack.length + 1)) := by
97
+ have : (8#32 : W32) = ofN 8 := rfl
98
+ rw [this, ofN_sub (by omega)]
99
+ congr 1
100
+ obtain ⟨m', hm'⟩ : ∃ m', w.mem.write64 (c.dEnd - 8 * (s.dstack.length + 1)) v = some m' :=
101
+ write64_some (by rw [hs.size]; omega)
102
+ have h1 : execL (spSub 8) w = some { w with globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * (s.dstack.length + 1)))) } := by
103
+ simp [spSub, execL, stepI, bin32, hs.sp, i32c, hsub]
104
+ rfl
105
+ have hg1 : execL [.globalGet spG] { w with globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * (s.dstack.length + 1)))) } = some { w with stack := .i32 (ofN (c.dEnd - 8 * (s.dstack.length + 1))) :: w.stack, globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * (s.dstack.length + 1)))) } := by
106
+ simp [execL, stepI, setG, spG]
107
+ have h2 := hval { w with stack := .i32 (ofN (c.dEnd - 8 * (s.dstack.length + 1))) :: w.stack, globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * (s.dstack.length + 1)))) } (by simp [setG, spG]) rfl
108
+ (by simp [setG, spG, apG])
109
+ have h3 : execL [.i64store 0] { w with stack := .i64 v :: .i32 (ofN (c.dEnd - 8 * (s.dstack.length + 1))) :: w.stack, globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * (s.dstack.length + 1)))) } = some { w with mem := m', globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * (s.dstack.length + 1)))) } := by
110
+ simp [execL, stepI, ofN_toNat hlt, hm']
111
+ refine ⟨{ w with mem := m', globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * (s.dstack.length + 1)))) }, ?_, push_rel hg hs hfit hm', rfl⟩
112
+ have e1 : pushWith val = spSub 8 ++ ([.globalGet spG] ++ (val ++ [.i64store 0])) := by
113
+ simp [pushWith, List.append_assoc]
114
+ rw [e1, execL_append, h1]
115
+ simp only [Option.bind_some]
116
+ rw [execL_append, hg1]
117
+ simp only [Option.bind_some]
118
+ rw [execL_append, h2]
119
+ simp only [Option.bind_some]
120
+ exact h3
121
+
122
+ theorem sim_word (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) (v : W)
123
+ (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
124
+ Sim1 c fs (lowerInstr c.layout n k (.word v)) { s with dstack := v :: s.dstack } w (k + 1) := by
125
+ have hB := push_gen hg hs hfit (val := [.i64const v]) (v := v) (by simp [WI.isCtl])
126
+ (by intro u _ _ _; simp [execL, stepI])
127
+ obtain ⟨w1, he, hr, hst1⟩ := hB
128
+ have := core_gen (fs := fs) (pre := ovf spG c.layout.dBase) (ovfD_pass hg hs hfit) (B := pushWith [.i64const v])
129
+ (allSimple_spec (by simp [allSimple, pushWith, spSub, WI.isCtl, i32c])) ⟨w1, he, hr, by rw [hst1, hst]⟩ (k + 1)
130
+ simpa [lowerInstr, pushWith, List.append_assoc] using this
131
+
132
+ theorem sim_ptr (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) (q : WordDialect.Ptr 64)
133
+ (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
134
+ Sim1 c fs (lowerInstr c.layout n k (.ptr q)) { s with dstack := q.toWord :: s.dstack } w (k + 1) :=
135
+ sim_word n k hg hs hst q.toWord hfit
136
+
137
+ theorem sim_push (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {r : Nat}
138
+ (hr : r < c.nregs) (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
139
+ Sim1 c fs (lowerInstr c.layout n k (.push r)) { s with dstack := s.regs r :: s.dstack } w (k + 1) := by
140
+ have hrf := hg.rf_le
141
+ have hsz := hg.msize_lt
142
+ have hB := push_gen hg hs hfit (val := [i32c (c.layout.rf + 8 * r), .i64load 0]) (v := s.regs r)
143
+ (by simp [WI.isCtl, i32c])
144
+ (by
145
+ intro u _ hm _
146
+ have h1 := hs.regs r hr
147
+ have h2 : c.rf + 8 * r < 2 ^ 32 := by omega
148
+ simp [execL, stepI, i32c, Cfg.layout, hm, BitVec.toNat_ofNat, Nat.mod_eq_of_lt h2, h1])
149
+ obtain ⟨w1, he, hr1, hst1⟩ := hB
150
+ have := core_gen (fs := fs) (pre := ovf spG c.layout.dBase) (ovfD_pass hg hs hfit)
151
+ (B := pushWith [i32c (c.layout.rf + 8 * r), .i64load 0])
152
+ (allSimple_spec (by simp [allSimple, pushWith, spSub, WI.isCtl, i32c])) ⟨w1, he, hr1, by rw [hst1, hst]⟩ (k + 1)
153
+ simpa [lowerInstr, pushWith, List.append_assoc] using this
154
+
155
+ theorem sim_dup (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a : W} {d : List W}
156
+ (hd : s.dstack = a :: d) (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
157
+ Sim1 c fs (lowerInstr c.layout n k .dup) { s with dstack := a :: a :: d } w (k + 1) := by
158
+ have hlen : s.dstack.length = d.length + 1 := by simp [hd]
159
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some a := by
160
+ have := hs.stack 0 a (by simp [hd]); simpa using this
161
+ have hsz := hg.msize_lt
162
+ have hdE := hg.dEnd_le
163
+ have hlt : c.dEnd - 8 * (s.dstack.length + 1) < 2 ^ 32 := by omega
164
+ have hB := push_gen hg hs hfit (val := ldS 8) (v := a)
165
+ (by simp [ldS, WI.isCtl])
166
+ (by
167
+ intro u hu hm _
168
+ have e : c.dEnd - 8 * (s.dstack.length + 1) + 8 = c.dEnd - 8 * s.dstack.length := by omega
169
+ simp [ldS, execL, stepI, hu, ofN_toNat hlt, e, hm, hr0])
170
+ obtain ⟨w1, he, hr, hst1⟩ := hB
171
+ have e : a :: s.dstack = a :: a :: d := by rw [hd]
172
+ rw [e] at hr
173
+ have := core_gen (fs := fs) (pre := uf c.layout 1 ++ ovf spG c.layout.dBase)
174
+ (run_append _ (uf1_pass hg hs hd) (ovfD_pass hg hs hfit)) (B := pushWith (ldS 8))
175
+ (allSimple_spec (by simp [allSimple, pushWith, spSub, ldS, WI.isCtl, i32c])) ⟨w1, he, hr, by rw [hst1, hst]⟩ (k + 1)
176
+ simpa [lowerInstr, pushWith, List.append_assoc] using this
177
+
178
+ theorem sim_over (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
179
+ (hd : s.dstack = b :: a :: d) (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
180
+ Sim1 c fs (lowerInstr c.layout n k .over) { s with dstack := a :: b :: a :: d } w (k + 1) := by
181
+ have hlen : s.dstack.length = d.length + 2 := by simp [hd]
182
+ have hr1 : w.mem.read64 (c.dEnd - 8 * s.dstack.length + 8) = some a := by
183
+ have := hs.stack 1 a (by simp [hd]); simpa using this
184
+ have hsz := hg.msize_lt
185
+ have hdE := hg.dEnd_le
186
+ have hlt : c.dEnd - 8 * (s.dstack.length + 1) < 2 ^ 32 := by omega
187
+ have hB := push_gen hg hs hfit (val := [.globalGet spG, .i64load 16]) (v := a)
188
+ (by simp [WI.isCtl])
189
+ (by
190
+ intro u hu hm _
191
+ have e : c.dEnd - 8 * (s.dstack.length + 1) + 16 = c.dEnd - 8 * s.dstack.length + 8 := by omega
192
+ simp [execL, stepI, hu, ofN_toNat hlt, e, hm, hr1])
193
+ obtain ⟨w1, he, hr, hst1⟩ := hB
194
+ have e : a :: s.dstack = a :: b :: a :: d := by rw [hd]
195
+ rw [e] at hr
196
+ have := core_gen (fs := fs) (pre := uf c.layout 2 ++ ovf spG c.layout.dBase)
197
+ (run_append _ (uf2_pass hg hs hd) (ovfD_pass hg hs hfit))
198
+ (B := pushWith [.globalGet spG, .i64load 16])
199
+ (allSimple_spec (by simp [allSimple, pushWith, spSub, WI.isCtl, i32c])) ⟨w1, he, hr, by rw [hst1, hst]⟩ (k + 1)
200
+ simpa [lowerInstr, pushWith, List.append_assoc] using this
201
+
202
+ theorem sim_swap (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {a b : W} {d : List W}
203
+ (hd : s.dstack = b :: a :: d) :
204
+ Sim1 c fs (lowerInstr c.layout n k .swap) { s with dstack := a :: b :: d } w (k + 1) := by
205
+ have hlen : s.dstack.length = d.length + 2 := by simp [hd]
206
+ have hX := sp_lt hg hs
207
+ have hcap := hs.capD
208
+ have hdE := hg.dEnd_le
209
+ have hr1 : w.mem.read64 (c.dEnd - 8 * s.dstack.length + 8) = some a := by
210
+ have := hs.stack 1 a (by simp [hd]); simpa using this
211
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some b := by
212
+ have := hs.stack 0 b (by simp [hd]); simpa using this
213
+ obtain ⟨m1, hm1⟩ : ∃ m1, w.mem.write64 (c.dEnd - 8 * s.dstack.length + 8) b = some m1 :=
214
+ write64_some (by rw [hs.size]; omega)
215
+ obtain ⟨hsz1, _⟩ := write64_size hm1
216
+ obtain ⟨m2, hm2⟩ : ∃ m2, m1.write64 (c.dEnd - 8 * s.dstack.length) a = some m2 :=
217
+ write64_some (by rw [hsz1, hs.size]; omega)
218
+ have hexec : execL ([.globalGet spG] ++ ldS 8 ++ [.globalGet spG] ++ ldS 0 ++ [.i64store 8, .i64store 0]) w = some { w with mem := m2 } := by
219
+ simp [ldS, execL, stepI, hs.sp, ofN_toNat hX, hr0, hr1, hm1, hm2]
220
+ have hrel1 := wr_stack hg hs (j := 1) (by omega) (v := b) (m' := m1) (by simpa using hm1)
221
+ have hrel2 := wr_stack_at hg hrel1 (j := 0) (by simp; omega) (v := a) (m' := m2) (A := c.dEnd - 8 * s.dstack.length)
222
+ (by simp) (by simpa using hm2)
223
+ have e : (({ s with dstack := s.dstack.set 1 b } : State 64).dstack.set 0 a) = a :: b :: d := by simp [hd]
224
+ have e3 : ({ { s with dstack := s.dstack.set 1 b } with dstack := (({ s with dstack := s.dstack.set 1 b } : State 64).dstack.set 0 a) } : State 64) = { s with dstack := a :: b :: d } := by
225
+ simp [hd]
226
+ rw [e3] at hrel2
227
+ have := core_gen (fs := fs) (pre := uf c.layout 2) (uf2_pass hg hs hd)
228
+ (B := [.globalGet spG] ++ ldS 8 ++ [.globalGet spG] ++ ldS 0 ++ [.i64store 8, .i64store 0])
229
+ (allSimple_spec (by simp [allSimple, ldS, WI.isCtl])) ⟨_, hexec, hrel2, hst⟩ (k + 1)
230
+ simpa [lowerInstr, List.append_assoc] using this
231
+
232
+ theorem sim_rot (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {cc b a : W} {d : List W}
233
+ (hd : s.dstack = cc :: b :: a :: d) :
234
+ Sim1 c fs (lowerInstr c.layout n k .rot) { s with dstack := a :: cc :: b :: d } w (k + 1) := by
235
+ have hlen : s.dstack.length = d.length + 3 := by simp [hd]
236
+ have hX := sp_lt hg hs
237
+ have hcap := hs.capD
238
+ have hdE := hg.dEnd_le
239
+ have hr2 : w.mem.read64 (c.dEnd - 8 * s.dstack.length + 16) = some a := by
240
+ have := hs.stack 2 a (by simp [hd]); simpa using this
241
+ have hr1 : w.mem.read64 (c.dEnd - 8 * s.dstack.length + 8) = some b := by
242
+ have := hs.stack 1 b (by simp [hd]); simpa using this
243
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some cc := by
244
+ have := hs.stack 0 cc (by simp [hd]); simpa using this
245
+ obtain ⟨m1, hm1⟩ : ∃ m1, w.mem.write64 (c.dEnd - 8 * s.dstack.length + 16) b = some m1 :=
246
+ write64_some (by rw [hs.size]; omega)
247
+ obtain ⟨hsz1, _⟩ := write64_size hm1
248
+ obtain ⟨m2, hm2⟩ : ∃ m2, m1.write64 (c.dEnd - 8 * s.dstack.length + 8) cc = some m2 :=
249
+ write64_some (by rw [hsz1, hs.size]; omega)
250
+ obtain ⟨hsz2, _⟩ := write64_size hm2
251
+ obtain ⟨m3, hm3⟩ : ∃ m3, m2.write64 (c.dEnd - 8 * s.dstack.length) a = some m3 :=
252
+ write64_some (by rw [hsz2, hsz1, hs.size]; omega)
253
+ have hexec : execL ([.globalGet spG] ++ ldS 16 ++ [.globalGet spG] ++ ldS 0 ++ [.globalGet spG] ++ ldS 8 ++ [.i64store 16, .i64store 8, .i64store 0]) w = some { w with mem := m3 } := by
254
+ simp [ldS, execL, stepI, hs.sp, ofN_toNat hX, hr0, hr1, hr2, hm1, hm2, hm3]
255
+ have hrel1 := wr_stack_at hg hs (j := 2) (by omega) (v := b) (m' := m1) (A := c.dEnd - 8 * s.dstack.length + 16) (by simp) hm1
256
+ have hrel2 := wr_stack_at hg hrel1 (j := 1) (by simp; omega) (v := cc) (m' := m2) (A := c.dEnd - 8 * s.dstack.length + 8)
257
+ (by simp) (by simpa using hm2)
258
+ have hrel3 := wr_stack_at hg hrel2 (j := 0) (by simp; omega) (v := a) (m' := m3) (A := c.dEnd - 8 * s.dstack.length)
259
+ (by simp) (by simpa using hm3)
260
+ have e3 : ({ { { s with dstack := s.dstack.set 2 b } with dstack := (({ s with dstack := s.dstack.set 2 b } : State 64).dstack.set 1 cc) } with dstack := ((({ s with dstack := s.dstack.set 2 b } : State 64).dstack.set 1 cc).set 0 a) } : State 64) = { s with dstack := a :: cc :: b :: d } := by
261
+ simp [hd]
262
+ rw [e3] at hrel3
263
+ have := core_gen (fs := fs) (pre := uf c.layout 3) (uf3_pass hg hs hd)
264
+ (B := [.globalGet spG] ++ ldS 16 ++ [.globalGet spG] ++ ldS 0 ++ [.globalGet spG] ++ ldS 8 ++ [.i64store 16, .i64store 8, .i64store 0])
265
+ (allSimple_spec (by simp [allSimple, ldS, WI.isCtl])) ⟨_, hexec, hrel3, hst⟩ (k + 1)
266
+ simpa [lowerInstr, List.append_assoc] using this
267
+
268
+ /-- Core of instructions that overwrite slot `j` with `y` and then pop `n` words. -/
269
+ theorem sim_wa_core (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {pre : List WI}
270
+ (hpre : Run fs pre w (.normal w)) {j n : Nat} {y : W} {L' : List W}
271
+ (hj : j < s.dstack.length) (hn : n ≤ s.dstack.length) (hL' : (s.dstack.set j y).drop n = L')
272
+ {B : List WI} (hctlB : ∀ i ∈ B, i.isCtl = false)
273
+ (hB : ∀ m', w.mem.write64 (c.dEnd - 8 * s.dstack.length + 8 * j) y = some m' →
274
+ execL B w = some { w with mem := m', globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * s.dstack.length + 8 * n))) })
275
+ (nxt : Nat) :
276
+ ∃ w', Run fs (pre ++ (B ++ [i32c nxt])) w (.normal w') ∧
277
+ Rel0 c { s with dstack := L' } w' ∧ w'.stack = [.i32 (ofN nxt)] := by
278
+ have hcap := hs.capD
279
+ obtain ⟨m', hm'⟩ : ∃ m', w.mem.write64 (c.dEnd - 8 * s.dstack.length + 8 * j) y = some m' :=
280
+ write64_some (by rw [hs.size]; have := hg.dEnd_le; omega)
281
+ have hbR := run_of_execL (fs := fs) hctlB (hB m' hm')
282
+ have hrel1 := wr_stack hg hs (j := j) hj (v := y) (m' := m') hm'
283
+ have hrel2 := adj_sp hg hrel1 (n := n) (by simpa using hn)
284
+ have e3 : ({ { s with dstack := s.dstack.set j y } with dstack := ({ s with dstack := s.dstack.set j y } : State 64).dstack.drop n } : State 64) = { s with dstack := L' } := by
285
+ rw [← hL']
286
+ rw [e3] at hrel2
287
+ have e2 : c.dEnd - 8 * s.dstack.length + 8 * n = c.dEnd - 8 * (s.dstack.length - n) := by omega
288
+ rw [e2] at hbR
289
+ refine ⟨_, run_append _ hpre (run_append _ hbR (run_finish nxt)), ?_, ?_⟩
290
+ · refine Rel0.congr (hrel2.setStack []) rfl ?_ ?_ ?_
291
+ · simp [setG, spG, List.length_set]
292
+ · simp [setG, spG, rpG]
293
+ · simp [setG, spG, apG]
294
+ · simp [hst]
295
+
296
+ theorem sim_select (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {cc y x : W} {d : List W}
297
+ (hd : s.dstack = cc :: y :: x :: d) :
298
+ Sim1 c fs (lowerInstr c.layout n k .select) { s with dstack := (if Word.isTrue cc then x else y) :: d } w (k + 1) := by
299
+ have hlen : s.dstack.length = d.length + 3 := by simp [hd]
300
+ have hX := sp_lt hg hs
301
+ have hr2 : w.mem.read64 (c.dEnd - 8 * s.dstack.length + 16) = some x := by
302
+ have := hs.stack 2 x (by simp [hd]); simpa using this
303
+ have hr1 : w.mem.read64 (c.dEnd - 8 * s.dstack.length + 8) = some y := by
304
+ have := hs.stack 1 y (by simp [hd]); simpa using this
305
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some cc := by
306
+ have := hs.stack 0 cc (by simp [hd]); simpa using this
307
+ have hsp16 : ofN (c.dEnd - 8 * s.dstack.length) + 16#32 = ofN (c.dEnd - 8 * s.dstack.length + 16) :=
308
+ ofN_add (c.dEnd - 8 * s.dstack.length) 16
309
+ have := sim_wa_core (fs := fs) hg hs hst (uf3_pass hg hs hd) (j := 2) (n := 2)
310
+ (y := if Word.isTrue cc then x else y) (L' := (if Word.isTrue cc then x else y) :: d)
311
+ (by omega) (by omega) (by simp [hd]) (B := [.globalGet spG] ++ ldS 16 ++ ldS 8 ++ ldS 0 ++ [.i64const 0#64, .i64ne, .select, .i64store 16] ++ spAdd 16)
312
+ (allSimple_spec (by simp [allSimple, ldS, spAdd, WI.isCtl, i32c])) ?_ (k + 1)
313
+ · simpa [lowerInstr, List.append_assoc] using this
314
+ intro m' hm'
315
+ by_cases hc : cc = 0#64
316
+ · have hm2 : w.mem.write64 (c.dEnd - 8 * s.dstack.length + 16) y = some m' := by simpa [hc, Word.isTrue] using hm'
317
+ simp [ldS, spAdd, execL_append, execL, stepI, cmp64, bin32, hs.sp, ofN_toNat hX, hr0, hr1, hr2, bool32, hc, hm2, i32c, hsp16]
318
+ rfl
319
+ · have hm2 : w.mem.write64 (c.dEnd - 8 * s.dstack.length + 16) x = some m' := by simpa [hc, Word.isTrue] using hm'
320
+ simp [ldS, spAdd, execL_append, execL, stepI, cmp64, bin32, hs.sp, ofN_toNat hX, hr0, hr1, hr2, bool32, hc, hm2, i32c, hsp16]
321
+ rfl
322
+
323
+ theorem sim_pop (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = []) {r : Nat}
324
+ (hr : r < c.nregs) {a : W} {d : List W} (hd : s.dstack = a :: d) :
325
+ Sim1 c fs (lowerInstr c.layout n k (.pop r)) { s with dstack := d, regs := State.setReg s.regs r a } w (k + 1) := by
326
+ have hlen : s.dstack.length = d.length + 1 := by simp [hd]
327
+ have hX := sp_lt hg hs
328
+ have hcap := hs.capD
329
+ have hrf := hg.rf_le
330
+ have hsz := hg.msize_lt
331
+ have hr0 : w.mem.read64 (c.dEnd - 8 * s.dstack.length) = some a := by
332
+ have := hs.stack 0 a (by simp [hd]); simpa using this
333
+ have hrfl : c.rf + 8 * r < 2 ^ 32 := by omega
334
+ obtain ⟨m', hm'⟩ : ∃ m', w.mem.write64 (c.rf + 8 * r) a = some m' :=
335
+ write64_some (by rw [hs.size]; omega)
336
+ have hsp8 : ofN (c.dEnd - 8 * s.dstack.length) + 8#32 = ofN (c.dEnd - 8 * s.dstack.length + 8) :=
337
+ ofN_add _ _
338
+ have hexec : execL ([i32c (c.layout.rf + 8 * r)] ++ ldS 0 ++ [.i64store 0] ++ spAdd 8) w = some { w with mem := m', globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * s.dstack.length + 8))) } := by
339
+ simp [ldS, spAdd, execL_append, execL, stepI, bin32, hs.sp, ofN_toNat hX, hr0, i32c, Cfg.layout, BitVec.toNat_ofNat, Nat.mod_eq_of_lt hrfl, hm', hsp8]
340
+ unfold setG
341
+ rfl
342
+ have hrel1 := wr_regs hg hs hr hm'
343
+ have hrel2 := adj_sp hg hrel1 (n := 1) (by simp [hd])
344
+ have e3 : ({ { s with regs := State.setReg s.regs r a } with dstack := ({ s with regs := State.setReg s.regs r a } : State 64).dstack.drop 1 } : State 64) = { s with dstack := d, regs := State.setReg s.regs r a } := by
345
+ simp [hd]
346
+ rw [e3] at hrel2
347
+ have hrel3 : Rel0 c { s with dstack := d, regs := State.setReg s.regs r a } { w with mem := m', globals := setG w.globals spG (.i32 (ofN (c.dEnd - 8 * s.dstack.length + 8))) } := by
348
+ refine Rel0.congr (hrel2.setStack []) rfl ?_ ?_ ?_
349
+ · have e2 : c.dEnd - 8 * s.dstack.length + 8 = c.dEnd - 8 * (s.dstack.length - 1) := by omega
350
+ simp [setG, spG, e2]
351
+ · simp [setG, spG, rpG]
352
+ · simp [setG, spG, apG]
353
+ have := core_gen (fs := fs) (pre := uf c.layout 1) (uf1_pass hg hs hd)
354
+ (B := [i32c (c.layout.rf + 8 * r)] ++ ldS 0 ++ [.i64store 0] ++ spAdd 8)
355
+ (allSimple_spec (by simp [allSimple, ldS, spAdd, WI.isCtl, i32c])) ⟨_, hexec, hrel3, hst⟩ (k + 1)
356
+ simpa [lowerInstr, List.append_assoc] using this
357
+
358
+ /-! Data-stack overflow: every instruction that grows the data stack exits 6 when it is full. -/
359
+
360
+ theorem sim_word_ovf (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (v : W)
361
+ (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
362
+ Run fs (lowerInstr c.layout n k (.word v)) w (.exit 6) := by
363
+ have := run_append_abrupt (fs := fs) _ (ovfD_trap hg hs h)
364
+ (b := pushWith [.i64const v] ++ [i32c (k + 1)]) trivial
365
+ simpa [lowerInstr, List.append_assoc] using this
366
+
367
+ theorem sim_ptr_ovf (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (q : WordDialect.Ptr 64)
368
+ (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
369
+ Run fs (lowerInstr c.layout n k (.ptr q)) w (.exit 6) := by
370
+ have := run_append_abrupt (fs := fs) _ (ovfD_trap hg hs h)
371
+ (b := pushWith [.i64const q.toWord] ++ [i32c (k + 1)]) trivial
372
+ simpa [lowerInstr, List.append_assoc] using this
373
+
374
+ theorem sim_push_ovf (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) (r : Nat)
375
+ (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
376
+ Run fs (lowerInstr c.layout n k (.push r)) w (.exit 6) := by
377
+ have := run_append_abrupt (fs := fs) _ (ovfD_trap hg hs h)
378
+ (b := pushWith [i32c (c.layout.rf + 8 * r), .i64load 0] ++ [i32c (k + 1)]) trivial
379
+ simpa [lowerInstr, List.append_assoc] using this
380
+
381
+ theorem sim_dup_ovf (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) {a : W} {d : List W}
382
+ (hd : s.dstack = a :: d) (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
383
+ Run fs (lowerInstr c.layout n k .dup) w (.exit 6) := by
384
+ have := run_append (fs := fs) _ (uf1_pass hg hs hd) (run_append_abrupt _ (ovfD_trap hg hs h)
385
+ (b := spSub 8 ++ [.globalGet spG] ++ ldS 8 ++ [.i64store 0] ++ [i32c (k + 1)]) trivial)
386
+ simpa [lowerInstr, List.append_assoc] using this
387
+
388
+ theorem sim_over_ovf (n k : Nat) (hg : Geom c) (hs : Rel0 c s w) {a b : W} {d : List W}
389
+ (hd : s.dstack = b :: a :: d) (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
390
+ Run fs (lowerInstr c.layout n k .over) w (.exit 6) := by
391
+ have := run_append (fs := fs) _ (uf2_pass hg hs hd) (run_append_abrupt _ (ovfD_trap hg hs h)
392
+ (b := spSub 8 ++ [.globalGet spG, .globalGet spG, .i64load 16, .i64store 0] ++ [i32c (k + 1)]) trivial)
393
+ simpa [lowerInstr, List.append_assoc] using this
394
+
395
+ end Sim
396
+
397
+ end Wasm
398
+ end WordDialect
backend/wasm/WasmMain.lean ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Harness
2
+ import Wasm
3
+
4
+ /-!
5
+ # wasmw
6
+
7
+ Execution harness for the WebAssembly backend. For each program it lowers the IR to WebAssembly,
8
+ emits WAT (`Wasm.Emit`), runs it under `wasmtime` (WASI), runs the same IR program under the Lean
9
+ semantics, and compares the exit status and the dumped data stack, IR memory and virtual registers
10
+ (the shared `Harness`).
11
+
12
+ `wasmtime` is found through the `WASMTIME` environment variable, else on `PATH`.
13
+
14
+ `check` also runs `auxSamples`, raw IR programs for the auxiliary stack (`tor`/`fromr`/`rfetch`),
15
+ against the Lean semantics, and the overflow programs, which must exit 6.
16
+
17
+ Usage: `wasmw check [fuzzCount]`, `wasmw emit <sample>`, `wasmw forth <file.fs> [memWords]` (run a
18
+ Forth source file under wasmtime and compare with the Lean semantics), `wasmw emit-forth <file.fs>`.
19
+ -/
20
+
21
+ open WordDialect
22
+
23
+ def runtimeFor (s : Sample) : Wasm.Emit.Runtime := { memImage := s.mem, nregs := s.nregs }
24
+
25
+ def watFor (s : Sample) : String :=
26
+ let r := runtimeFor s
27
+ Wasm.Emit.program r s.prog
28
+
29
+ def workDir : String := "/tmp/wasmw"
30
+
31
+ def runWasm (s : Sample) : IO (Except String Result) := do
32
+ IO.FS.createDirAll workDir
33
+ let path := s!"{workDir}/{s.name}.wat"
34
+ IO.FS.writeFile path (watFor s)
35
+ let cmd := (← IO.getEnv "WASMTIME").getD "wasmtime"
36
+ let child ← IO.Process.spawn { cmd, args := #["run", path], stdout := .piped, stderr := .piped, stdin := .null }
37
+ let bytes ← readAll child.stdout ByteArray.empty
38
+ let err ← child.stderr.readToEnd
39
+ let code ← child.wait
40
+ if code == 0 then
41
+ match parseDump s bytes with
42
+ | some r => return .ok r
43
+ | none => return .error s!"malformed dump ({bytes.size} bytes): {err}"
44
+ else if code.toNat ≥ 1 && code.toNat ≤ 6 then
45
+ return .ok (.trap code.toNat)
46
+ else
47
+ return .error s!"wasmtime exit {code}: {err}"
48
+
49
+ /-- Raw IR programs exercising the auxiliary stack: values parked across `call`/`ret` and
50
+ recursion, `R@` copies, a counted loop, and the traps (empty auxiliary stack → 5, `tor` on an empty
51
+ data stack → 1). Each is checked against the Lean semantics. -/
52
+ def auxSamples : List Sample :=
53
+ [ { name := "aux_basic",
54
+ prog := [.word 5#64, .tor, .word 7#64, .rfetch, .add, .fromr, .mul, .halt], mem := [] },
55
+ { name := "aux_across_call",
56
+ prog := [.word 3#64, .tor, .call 6, .fromr, .add, .halt,
57
+ .word 10#64, .rfetch, .add, .ret], mem := [] },
58
+ { name := "aux_recursion_fact",
59
+ prog := [.word 5#64, .call 3, .halt,
60
+ .dup, .word 0#64, .cmp .eq, .branch 15,
61
+ .dup, .tor, .word 1#64, .sub, .call 3, .fromr, .mul, .ret,
62
+ .drop, .word 1#64, .ret], mem := [] },
63
+ { name := "aux_counted_loop",
64
+ prog := [.word 0#64, .word 10#64, .tor,
65
+ .rfetch, .add, .fromr, .word 1#64, .sub, .dup, .tor, .word 0#64, .cmp .ne, .branch 3,
66
+ .fromr, .drop, .halt], mem := [] },
67
+ { name := "aux_fromr_empty", prog := [.word 1#64, .fromr, .halt], mem := [] },
68
+ { name := "aux_rfetch_empty", prog := [.rfetch, .halt], mem := [] },
69
+ { name := "aux_unbalanced", prog := [.word 1#64, .tor, .fromr, .fromr, .halt], mem := [] },
70
+ { name := "aux_tor_underflow", prog := [.tor, .halt], mem := [] },
71
+ { name := "aux_ret_keeps_aux",
72
+ prog := [.call 3, .fromr, .halt, .word 42#64, .tor, .ret], mem := [] } ]
73
+
74
+ def checkAux : IO Bool := do
75
+ let mut ok := true
76
+ for s in auxSamples do
77
+ if !(← check runWasm s) then ok := false
78
+ return ok
79
+
80
+ /-- Programs whose IR run never terminates and grows a stack without bound: the target must stop
81
+ them with the overflow exit (code 6) instead of corrupting memory. -/
82
+ def overflowSamples : List Sample :=
83
+ [ { name := "ovf_word_loop", prog := [.word 1#64, .jmp 0], mem := [] },
84
+ { name := "ovf_dup_loop", prog := [.word 1#64, .dup, .jmp 1], mem := [] },
85
+ { name := "ovf_call_loop", prog := [.call 0], mem := [] },
86
+ { name := "ovf_tor_loop", prog := [.word 1#64, .tor, .jmp 0], mem := [] } ]
87
+
88
+ def checkOverflow : IO Bool := do
89
+ let mut ok := true
90
+ for s in overflowSamples do
91
+ match ← runWasm s with
92
+ | .ok (.trap 6) => IO.println s!"PASS {s.name} overflow exit 6"
93
+ | .ok got => IO.println s!"FAIL {s.name} expected overflow exit 6, got {repr got}"; ok := false
94
+ | .error e => IO.println s!"ERROR {s.name}: {e}"; ok := false
95
+ return ok
96
+
97
+ /-- A Forth source file as a sample: named after the file, `memWords` zero words of IR memory. -/
98
+ def forthFileSample (path : String) (memWords : Nat) : IO Sample := do
99
+ let src ← IO.FS.readFile path
100
+ let name := (System.FilePath.mk path).fileStem.getD "forth"
101
+ return forthTextSample name src (List.replicate memWords 0)
102
+
103
+ def parseMem (rest : List String) : Option Nat :=
104
+ match rest with
105
+ | [] => some 16
106
+ | [m] => m.toNat?
107
+ | _ => none
108
+
109
+ def main (args : List String) : IO UInt32 := do
110
+ match args with
111
+ | ["emit", name] =>
112
+ match (allSamples ++ auxSamples).find? (·.name == name) with
113
+ | some s => IO.println (watFor s); return 0
114
+ | none => IO.eprintln "unknown sample"; return 1
115
+ | "forth" :: path :: rest =>
116
+ match parseMem rest with
117
+ | none => IO.eprintln "usage: wasmw forth <file.fs> [memWords]"; return 2
118
+ | some m =>
119
+ let s ← forthFileSample path m
120
+ return (if ← check runWasm s then 0 else 1)
121
+ | "emit-forth" :: path :: rest =>
122
+ match parseMem rest with
123
+ | none => IO.eprintln "usage: wasmw emit-forth <file.fs> [memWords]"; return 2
124
+ | some m =>
125
+ let s ← forthFileSample path m
126
+ match s.buildError with
127
+ | some e => IO.eprintln e; return 1
128
+ | none => IO.println (watFor s); return 0
129
+ | "check" :: rest =>
130
+ let count := match rest with | [n] => n.toNat! | _ => 200
131
+ let agree ← checkAll runWasm count
132
+ let aux ← checkAux
133
+ let ovf ← checkOverflow
134
+ return (if agree && aux && ovf then 0 else 1)
135
+ | _ => IO.eprintln "usage: wasmw check [fuzzCount] | wasmw emit <sample> | wasmw forth <file.fs> [memWords] | wasmw emit-forth <file.fs> [memWords]"; return 2
backend/x86_64/Main.lean ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Harness
2
+ import X86
3
+
4
+ /-!
5
+ # wordc
6
+
7
+ Native execution harness for the x86-64 backend. For each program it
8
+ 1. lowers the IR to x86-64 and emits GNU assembly (`X86.Emit`),
9
+ 2. assembles and links it with `as` / `ld`,
10
+ 3. runs the binary on this machine,
11
+ 4. runs the same IR program under the Lean semantics (`WordDialect.run`), and
12
+ 5. compares exit status and the dumped data stack, IR memory and virtual registers
13
+ (the shared `Harness`).
14
+
15
+ `check` also runs the auxiliary-stack programs (`tor`/`fromr`/`rfetch`) in `auxSamples`.
16
+
17
+ Usage: `wordc check [fuzzCount]`, `wordc emit <sample>`, `wordc forth <file.fs> [memWords]` (run a
18
+ Forth source file natively and compare with the Lean semantics), `wordc emit-forth <file.fs>`.
19
+ -/
20
+
21
+ open WordDialect
22
+
23
+ def dBase : Nat := 0x10000000
24
+ def capacity : Nat := 65536
25
+ def dEnd : Nat := dBase + 8 * capacity
26
+ /-- Return-stack capacity in entries: 512 KiB of the native stack below the entry `rsp`. -/
27
+ def rcap : Nat := 65536
28
+ /-- Auxiliary-stack placement: `acap` words in `.astack`, linked at `aBase` (disjoint from
29
+ `.dstack`, which ends at `dEnd = 0x10080000`). -/
30
+ def aBase : Nat := 0x10100000
31
+ def acap : Nat := 65536
32
+ def aEnd : Nat := aBase + 8 * acap
33
+
34
+ def runtimeFor (s : Sample) : X86.Emit.Runtime :=
35
+ { dEnd := dEnd, capacity := capacity, rcap := rcap, memImage := s.mem, nregs := s.nregs,
36
+ aEnd := aEnd, acap := acap }
37
+
38
+ def layoutFor (s : Sample) : X86.Layout := (runtimeFor s).layout
39
+
40
+ def asmFor (s : Sample) : String :=
41
+ X86.Emit.program (runtimeFor s) (X86.lowerProg (layoutFor s) s.prog)
42
+
43
+ def workDir : String := "/tmp/wordc"
44
+
45
+ def runNative (s : Sample) : IO (Except String Result) := do
46
+ IO.FS.createDirAll workDir
47
+ let base := s!"{workDir}/{s.name}"
48
+ IO.FS.writeFile (base ++ ".s") (asmFor s)
49
+ let a ← IO.Process.output { cmd := "as", args := #["-o", base ++ ".o", base ++ ".s"] }
50
+ if a.exitCode != 0 then return .error s!"as failed: {a.stderr}"
51
+ let l ← IO.Process.output { cmd := "ld", args := #[s!"--section-start=.dstack={X86.Emit.hex dBase}",
52
+ s!"--section-start=.astack={X86.Emit.hex aBase}", "-o", base, base ++ ".o"] }
53
+ if l.exitCode != 0 then return .error s!"ld failed: {l.stderr}"
54
+ let child ← IO.Process.spawn { cmd := base, stdout := .piped, stderr := .null, stdin := .null }
55
+ let bytes ← readAll child.stdout ByteArray.empty
56
+ let code ← child.wait
57
+ if code == 0 then
58
+ match parseDump s bytes with
59
+ | some r => return .ok r
60
+ | none => return .error s!"malformed dump ({bytes.size} bytes)"
61
+ else
62
+ return .ok (.trap code.toNat)
63
+
64
+
65
+ /-- Programs whose IR run never terminates and grows a stack without bound: the native code must
66
+ stop them with the overflow exit (code 6) instead of running off the end of a stack. -/
67
+ def overflowSamples : List Sample :=
68
+ [ { name := "ovf_word_loop", prog := [.word 1#64, .jmp 0], mem := [] },
69
+ { name := "ovf_dup_loop", prog := [.word 1#64, .dup, .jmp 1], mem := [] },
70
+ { name := "ovf_call_loop", prog := [.call 0], mem := [] },
71
+ { name := "ovf_tor_loop", prog := [.word 1#64, .tor, .jmp 0], mem := [] } ]
72
+
73
+ /-- Raw IR programs for the auxiliary stack, checked against the Lean semantics. -/
74
+ def auxSamples : List Sample :=
75
+ [ -- park 7, copy it (R@), add, take it back, multiply: (3 + 7) * 7 = 70
76
+ { name := "aux_basic", mem := [], nregs := 1,
77
+ prog := [.word 7, .tor, .word 3, .rfetch, .add, .fromr, .mul, .halt] }
78
+ -- a value parked across a `call` whose body uses the aux stack itself: 5*5 + 100 = 125
79
+ , { name := "aux_across_call", mem := [], nregs := 1,
80
+ prog := [.word 100, .tor, .word 5, .call 7, .fromr, .add, .halt,
81
+ .tor, .rfetch, .fromr, .mul, .ret] }
82
+ -- recursive factorial keeping `n` on the aux stack across the recursive call: 5! = 120
83
+ , { name := "aux_fact_rec", mem := [], nregs := 1,
84
+ prog := [.word 5, .call 3, .halt,
85
+ .dup, .branch 8, .drop, .word 1, .ret,
86
+ .dup, .tor, .word 1, .sub, .call 3, .fromr, .mul, .ret] }
87
+ -- `fromr` / `rfetch` on an empty aux stack trap returnUnderflow (5)
88
+ , { name := "aux_fromr_empty", mem := [], nregs := 1, prog := [.word 1, .fromr, .halt] }
89
+ , { name := "aux_rfetch_empty", mem := [], nregs := 1, prog := [.rfetch, .halt] }
90
+ -- `ret` does not see aux-stack values: still returnUnderflow (5)
91
+ , { name := "aux_ret_not_aux", mem := [], nregs := 1, prog := [.word 1, .tor, .ret] }
92
+ -- `tor` on an empty data stack traps stackUnderflow (1)
93
+ , { name := "aux_tor_underflow", mem := [], nregs := 1, prog := [.tor, .halt] }
94
+ ]
95
+
96
+ def checkAux : IO Bool := do
97
+ let mut ok := true
98
+ for s in auxSamples do
99
+ if !(← check runNative s) then ok := false
100
+ return ok
101
+
102
+ def checkOverflow : IO Bool := do
103
+ let mut ok := true
104
+ for s in overflowSamples do
105
+ match ← runNative s with
106
+ | .ok (.trap 6) => IO.println s!"PASS {s.name} overflow exit 6"
107
+ | .ok got => IO.println s!"FAIL {s.name} expected overflow exit 6, got {repr got}"; ok := false
108
+ | .error e => IO.println s!"ERROR {s.name}: {e}"; ok := false
109
+ return ok
110
+
111
+ /-- A Forth source file as a sample: named after the file, `memWords` zero words of IR memory. -/
112
+ def forthFileSample (path : String) (memWords : Nat) : IO Sample := do
113
+ let src ← IO.FS.readFile path
114
+ let name := (System.FilePath.mk path).fileStem.getD "forth"
115
+ return forthTextSample name src (List.replicate memWords 0)
116
+
117
+ def parseMem (rest : List String) : Option Nat :=
118
+ match rest with
119
+ | [] => some 16
120
+ | [m] => m.toNat?
121
+ | _ => none
122
+
123
+ def main (args : List String) : IO UInt32 := do
124
+ match args with
125
+ | ["emit", name] =>
126
+ match (allSamples ++ auxSamples).find? (·.name == name) with
127
+ | some s => IO.println (asmFor s); return 0
128
+ | none => IO.eprintln "unknown sample"; return 1
129
+ | "forth" :: path :: rest =>
130
+ match parseMem rest with
131
+ | none => IO.eprintln "usage: wordc forth <file.fs> [memWords]"; return 2
132
+ | some m =>
133
+ let s ← forthFileSample path m
134
+ return (if ← check runNative s then 0 else 1)
135
+ | "emit-forth" :: path :: rest =>
136
+ match parseMem rest with
137
+ | none => IO.eprintln "usage: wordc emit-forth <file.fs> [memWords]"; return 2
138
+ | some m =>
139
+ let s ← forthFileSample path m
140
+ match s.buildError with
141
+ | some e => IO.eprintln e; return 1
142
+ | none => IO.println (asmFor s); return 0
143
+ | "check" :: rest =>
144
+ let count := match rest with | [n] => n.toNat! | _ => 200
145
+ let agree ← checkAll runNative count
146
+ let aux ← checkAux
147
+ let ovf ← checkOverflow
148
+ return (if agree && aux && ovf then 0 else 1)
149
+ | _ => IO.eprintln "usage: wordc check [fuzzCount] | wordc emit <sample> | wordc forth <file.fs> [memWords] | wordc emit-forth <file.fs> [memWords]"; return 2
backend/x86_64/X86.lean ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.Isa
2
+ import X86.Lower
3
+ import X86.Emit
4
+ import X86.Flags
5
+ import X86.Run
6
+ import X86.Rel
7
+ import X86.Seq
8
+ import X86.Micro
9
+ import X86.Guard
10
+ import X86.SimBin
11
+ import X86.SimOps
12
+ import X86.SimStack
13
+ import X86.SimMem
14
+ import X86.SimMemOps
15
+ import X86.SimDiv
16
+ import X86.SimCtl
17
+ import X86.SimAux
18
+ import X86.SimStep
19
+ import X86.Correct
20
+ import X86.Init
backend/x86_64/X86/Correct.lean ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.SimStep
2
+
3
+ /-!
4
+ # X86.Correct
5
+
6
+ Whole-program preservation: running the lowered x86 code from a state related to the IR state
7
+ `s` reaches the same final outcome the IR semantics reaches from `s`.
8
+
9
+ * IR `halted s'` ⇒ the x86 code halts in a state `x'` with `Rel0 c p s' x'` (so the data
10
+ stack, virtual registers, IR memory and return stack agree with `s'`);
11
+ * IR `trapped t` ⇒ the x86 code exits with the same trap `t`.
12
+
13
+ Every instruction that grows the data stack or the auxiliary stack, and every `call`, checks for
14
+ room first and exits with `overflow` (exit code 6) when the stack is full, so there are two forms:
15
+
16
+ * `lowerProg_correct_or_overflow` has no capacity hypothesis: the x86 code reaches the IR outcome
17
+ as above, or exits with `overflow` (the run needed more stack than the target provides);
18
+ * `lowerProg_correct` additionally assumes the IR run stays within the capacities (`Fits` at every
19
+ reachable state); then no guard fires and the outcome is exactly the IR's.
20
+
21
+ Hypotheses of both (all explicit):
22
+ * `Geom c` the memory regions (data stack, register file, IR memory, native stack,
23
+ auxiliary stack) are disjoint and within the 64-bit address space, the
24
+ data and auxiliary stacks hold at least one word each and the return stack
25
+ at least two;
26
+ * `L` matches `c` data-stack end and limit, return-stack gap, auxiliary-stack end and limit,
27
+ and IR memory size;
28
+ * `hsz` code indices fit in 64 bits;
29
+ * `RegsOk` every `push r`/`pop r` names a register inside the register file.
30
+ -/
31
+
32
+ namespace WordDialect
33
+ namespace X86
34
+
35
+ open Reg
36
+
37
+ /-- Every register index used by the program lies in the virtual register file. -/
38
+ def RegsOk (c : Cfg) (p : Prog 64) : Prop :=
39
+ ∀ (k : Nat) (i : WordDialect.Instr 64), p[k]? = some i → ∀ r : Nat, (i = WordDialect.Instr.push r ∨ i = WordDialect.Instr.pop r) → r < c.nregs
40
+
41
+ /-- The final-outcome form of `Sim`. -/
42
+ def Final (c : Cfg) (p : Prog 64) (code : List (Instr Nat)) (x : M) : Outcome 64 → Prop
43
+ | .next _ => False
44
+ | .halted s' => ∃ x', XExec code x (.halted x') ∧ Rel0 c p s' x'
45
+ | .trapped t => XExec code x (.trapped t)
46
+
47
+ theorem offs_ge {p : Prog 64} {k : Nat} (h : p.length ≤ k) : offs p k = offs p p.length := by
48
+ simp [offs, List.take_of_length_le h, List.take_length]
49
+
50
+ /-- One IR step from a related state: the bad-pc stub, or the dispatched instruction. -/
51
+ theorem step_sim {c : Cfg} {p : Prog 64} {L : Layout} (hg : Geom c)
52
+ (hL : L.dEnd = ofN c.dEnd) (hMs : L.memSize = c.M) (hLd : L.dLim = ofN (c.dBase + 8))
53
+ (hLr : L.rGap = ofN (8 * c.rcap - 16)) (hLa : L.aEnd = ofN c.aEnd)
54
+ (hLal : L.aLim = ofN (c.aBase + 8)) (hsz : ∀ k, offs p k < 2 ^ 64) (hreg : RegsOk c p)
55
+ {s : State 64} {x : M} (hs : Rel c p s x) :
56
+ Sim c p (lowerProg L p) x (WordDialect.step p s) ∨
57
+ (¬ Fits c s ∧ XExec (lowerProg L p) x .overflow) := by
58
+ cases hi : p[s.pc]? with
59
+ | none =>
60
+ left
61
+ have hst : WordDialect.step p s = .trapped .badPc := by simp [WordDialect.step, hi]
62
+ rw [hst]
63
+ have hge : p.length ≤ s.pc := by
64
+ by_cases hh : s.pc < p.length
65
+ · rw [List.getElem?_eq_getElem hh] at hi; simp at hi
66
+ · omega
67
+ obtain ⟨f0, _⟩ := lowerProg_stub L p
68
+ have hpc : x.pc = offs p p.length := by rw [hs.2, offs_ge hge]
69
+ exact XExec.trap (by rw [xstep_of_fetch (by rw [hpc]; exact f0)]; rfl)
70
+ | some i =>
71
+ have hst : WordDialect.step p s = WordDialect.exec i s := by simp [WordDialect.step, hi]
72
+ rw [hst]
73
+ exact sim_exec hg hL hMs hLd hLr hLa hLal hsz hs hi (fun r hr => hreg s.pc i hi r hr)
74
+
75
+ /-- Whole-program preservation, with no capacity hypothesis: the x86 code reaches the IR
76
+ outcome, or exits with `overflow` because a stack was full. -/
77
+ theorem lowerProg_correct_or_overflow {c : Cfg} {p : Prog 64} {L : Layout} (hg : Geom c)
78
+ (hL : L.dEnd = ofN c.dEnd) (hMs : L.memSize = c.M) (hLd : L.dLim = ofN (c.dBase + 8))
79
+ (hLr : L.rGap = ofN (8 * c.rcap - 16)) (hLa : L.aEnd = ofN c.aEnd)
80
+ (hLal : L.aLim = ofN (c.aBase + 8)) (hsz : ∀ k, offs p k < 2 ^ 64) (hreg : RegsOk c p)
81
+ {s : State 64} {o : Outcome 64} (h : Exec p s o) :
82
+ ∀ {x : M}, Rel c p s x →
83
+ Final c p (lowerProg L p) x o ∨ XExec (lowerProg L p) x .overflow := by
84
+ induction h with
85
+ | @halt s s' hst =>
86
+ intro x hs
87
+ have := step_sim hg hL hMs hLd hLr hLa hLal hsz hreg hs
88
+ rw [hst] at this
89
+ rcases this with this | ⟨_, h6⟩
90
+ · exact .inl this
91
+ · exact .inr h6
92
+ | @trap s t hst =>
93
+ intro x hs
94
+ have := step_sim hg hL hMs hLd hLr hLa hLal hsz hreg hs
95
+ rw [hst] at this
96
+ rcases this with this | ⟨_, h6⟩
97
+ · exact .inl this
98
+ · exact .inr h6
99
+ | @next s s' o hst _ ih =>
100
+ intro x hs
101
+ have := step_sim hg hL hMs hLd hLr hLa hLal hsz hreg hs
102
+ rw [hst] at this
103
+ rcases this with ⟨x1, hsteps, hrel⟩ | ⟨_, h6⟩
104
+ · rcases ih hrel with hih | h6
105
+ · left
106
+ cases o with
107
+ | next _ => exact hih.elim
108
+ | halted s2 =>
109
+ obtain ⟨x', hx, hr⟩ := hih
110
+ exact ⟨x', XExec.of_steps hsteps hx, hr⟩
111
+ | trapped t => exact XExec.of_steps hsteps hih
112
+ · exact .inr (XExec.of_steps hsteps h6)
113
+ · exact .inr h6
114
+
115
+ /-- Whole-program preservation. When the IR run stays within the stack capacities (`Fits` at
116
+ every reachable state), no overflow guard fires and the outcome is exactly the IR's. -/
117
+ theorem lowerProg_correct {c : Cfg} {p : Prog 64} {L : Layout} (hg : Geom c)
118
+ (hL : L.dEnd = ofN c.dEnd) (hMs : L.memSize = c.M) (hLd : L.dLim = ofN (c.dBase + 8))
119
+ (hLr : L.rGap = ofN (8 * c.rcap - 16)) (hLa : L.aEnd = ofN c.aEnd)
120
+ (hLal : L.aLim = ofN (c.aBase + 8)) (hsz : ∀ k, offs p k < 2 ^ 64) (hreg : RegsOk c p)
121
+ {s : State 64} {o : Outcome 64} (h : Exec p s o) :
122
+ ∀ {x : M}, Rel c p s x → (∀ s', Steps p s s' → Fits c s') →
123
+ Final c p (lowerProg L p) x o := by
124
+ induction h with
125
+ | @halt s s' hst =>
126
+ intro x hs hb
127
+ have := step_sim hg hL hMs hLd hLr hLa hLal hsz hreg hs
128
+ rw [hst] at this
129
+ rcases this with this | ⟨hnf, _⟩
130
+ · exact this
131
+ · exact absurd (hb s .refl) hnf
132
+ | @trap s t hst =>
133
+ intro x hs hb
134
+ have := step_sim hg hL hMs hLd hLr hLa hLal hsz hreg hs
135
+ rw [hst] at this
136
+ rcases this with this | ⟨hnf, _⟩
137
+ · exact this
138
+ · exact absurd (hb s .refl) hnf
139
+ | @next s s' o hst _ ih =>
140
+ intro x hs hb
141
+ have := step_sim hg hL hMs hLd hLr hLa hLal hsz hreg hs
142
+ rw [hst] at this
143
+ rcases this with ⟨x1, hsteps, hrel⟩ | ⟨hnf, _⟩
144
+ · have hih := ih hrel (fun s'' hss => hb s'' (Steps.cons hst hss))
145
+ cases o with
146
+ | next _ => exact hih.elim
147
+ | halted s2 =>
148
+ obtain ⟨x', hx, hr⟩ := hih
149
+ exact ⟨x', XExec.of_steps hsteps hx, hr⟩
150
+ | trapped t => exact XExec.of_steps hsteps hih
151
+ · exact absurd (hb s .refl) hnf
152
+
153
+ /-- The state the runtime prologue establishes (`X86.Emit`): empty stacks, `r12 = rsp = sp0`,
154
+ `r13 r14 r15 rbp` at the conventions, and the register file and IR memory regions holding the IR
155
+ state's initial contents. `Rel` then holds at the entry point, so `lowerProg_correct` applies. -/
156
+ theorem init_rel {c : Cfg} {p : Prog 64} {s0 : State 64} {x0 : M} (hg : Geom c)
157
+ (hd : s0.dstack = []) (hr : s0.rstack = []) (ha : s0.astack = []) (hrc : 1 ≤ c.rcap)
158
+ (h15 : x0.regs Reg.r15 = ofN c.dEnd) (h14 : x0.regs Reg.r14 = ofN c.rf)
159
+ (h13 : x0.regs Reg.r13 = ofN c.mb) (h12 : x0.regs Reg.r12 = ofN c.sp0)
160
+ (hsp : x0.regs Reg.rsp = ofN c.sp0) (hbp : x0.regs Reg.rbp = ofN c.aEnd) (hregs : RegRel c x0.mem s0.regs)
161
+ (hmem : MemRel c x0.mem s0.mem) (hiv : ∀ a : Word 64, s0.mem.valid a = decide (a.toNat < c.M))
162
+ (hxv : RegionsValid c x0.mem.valid) (hpc : x0.pc = offs p s0.pc) : Rel c p s0 x0 :=
163
+ ⟨{ r15 := by simpa [hd] using h15
164
+ r14 := h14
165
+ r13 := h13
166
+ r12 := h12
167
+ rsp := by simpa [hr] using hsp
168
+ rbp := by simpa [ha] using hbp
169
+ aux := by intro i v h; simp [ha] at h
170
+ stack := by intro i v h; simp [hd] at h
171
+ regs := hregs
172
+ mem := hmem
173
+ rs := by intro i a h; simp [hr] at h
174
+ irvalid := hiv
175
+ xvalid := hxv
176
+ capD := by simp [hd]; exact hg.dBase_le
177
+ capR := by simp [hr]; exact hrc
178
+ capA := by have := hg.aBase_8; simp [ha]; omega }, hpc⟩
179
+
180
+ end X86
181
+ end WordDialect
backend/x86_64/X86/Emit.lean ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.Lower
2
+
3
+ /-!
4
+ # X86.Emit
5
+
6
+ GNU `as` (Intel syntax) text for a lowered program, plus the small runtime.
7
+
8
+ The program text is produced from exactly the instruction list `lowerProg` builds, one
9
+ `.L<index>:` label per x86 instruction, so the text and the verified model cannot diverge.
10
+
11
+ The runtime (`prologue`, `epilogue`) is the only hand-written assembly. It
12
+ * sets `r12 r13 r14 r15 rbp` to the conventions in `X86.Lower`,
13
+ * implements `exitHalt` as: write `depth`, the data stack, IR memory and the virtual register
14
+ file to stdout (raw little-endian 64-bit words), then `exit(0)`,
15
+ * implements `exitTrap t` as `exit(code t)` and `exitOvf` (stack overflow) as `exit(6)`.
16
+ -/
17
+
18
+ namespace WordDialect
19
+ namespace X86
20
+ namespace Emit
21
+
22
+ open Reg
23
+
24
+ def trapCode : Trap → Nat
25
+ | .stackUnderflow => 1
26
+ | .badAddress => 2
27
+ | .divideByZero => 3
28
+ | .badPc => 4
29
+ | .returnUnderflow => 5
30
+
31
+ def regName : Reg → String
32
+ | .rax => "rax" | .rcx => "rcx" | .rdx => "rdx" | .rbx => "rbx" | .rsp => "rsp" | .rbp => "rbp"
33
+ | .r12 => "r12" | .r13 => "r13" | .r14 => "r14" | .r15 => "r15"
34
+
35
+ def ccName : Cc → String
36
+ | .e => "e" | .ne => "ne" | .b => "b" | .be => "be" | .a => "a" | .ae => "ae"
37
+ | .l => "l" | .le => "le" | .g => "g" | .ge => "ge"
38
+
39
+ def hex (n : Nat) : String := "0x" ++ String.ofList (Nat.toDigits 16 n)
40
+
41
+ def lbl (t : Nat) : String := s!".L{t}"
42
+
43
+ def mem (b : Reg) (disp : Int) : String :=
44
+ if disp = 0 then s!"qword ptr [{regName b}]"
45
+ else if disp > 0 then s!"qword ptr [{regName b} + {disp}]"
46
+ else s!"qword ptr [{regName b} - {-disp}]"
47
+
48
+ def instr : Instr Nat → String
49
+ | .movImm d v => s!"movabs {regName d}, {hex v.toNat}"
50
+ | .movRR d s => s!"mov {regName d}, {regName s}"
51
+ | .load d b disp => s!"mov {regName d}, {mem b disp}"
52
+ | .store b disp s => s!"mov {mem b disp}, {regName s}"
53
+ | .loadIdx d b i => s!"mov {regName d}, qword ptr [{regName b} + {regName i}*8]"
54
+ | .storeIdx b i s => s!"mov qword ptr [{regName b} + {regName i}*8], {regName s}"
55
+ | .add d s => s!"add {regName d}, {regName s}"
56
+ | .sub d s => s!"sub {regName d}, {regName s}"
57
+ | .imul d s => s!"imul {regName d}, {regName s}"
58
+ | .and d s => s!"and {regName d}, {regName s}"
59
+ | .or d s => s!"or {regName d}, {regName s}"
60
+ | .xor d s => s!"xor {regName d}, {regName s}"
61
+ | .addImm d v => s!"add {regName d}, {v}"
62
+ | .not d => s!"not {regName d}"
63
+ | .neg d => s!"neg {regName d}"
64
+ | .cmp a b => s!"cmp {regName a}, {regName b}"
65
+ | .cmpImm a v => s!"cmp {regName a}, {v}"
66
+ | .test a b => s!"test {regName a}, {regName b}"
67
+ | .shlCl d => s!"shl {regName d}, cl"
68
+ | .shrCl d => s!"shr {regName d}, cl"
69
+ | .rolCl d => s!"rol {regName d}, cl"
70
+ | .rorCl d => s!"ror {regName d}, cl"
71
+ | .cmov c d s => s!"cmov{ccName c} {regName d}, {regName s}"
72
+ | .div s => s!"div {regName s}"
73
+ | .idiv s => s!"idiv {regName s}"
74
+ | .cqo => "cqo"
75
+ | .jmp t => s!"jmp {lbl t}"
76
+ | .jcc c t => s!"j{ccName c} {lbl t}"
77
+ | .call t => s!"call {lbl t}"
78
+ | .ret => "ret"
79
+ | .exitHalt => "jmp .Lhalt"
80
+ | .exitTrap t => s!"jmp .Ltrap{trapCode t}"
81
+ | .exitOvf => "jmp .Ltrap6"
82
+
83
+ def body (code : List (Instr Nat)) : String :=
84
+ String.intercalate "\n" (code.zipIdx.map fun (i, k) => s!"{lbl k}:\n\t{instr i}") ++ "\n"
85
+
86
+ /-- Data-stack placement (`capacity` words ending at `dEnd`), return-stack capacity in entries,
87
+ IR memory image, register-file size, and auxiliary-stack placement (`acap` words ending at
88
+ `aEnd`). -/
89
+ structure Runtime where
90
+ dEnd : Nat
91
+ capacity : Nat
92
+ rcap : Nat
93
+ memImage : List Nat
94
+ nregs : Nat
95
+ aEnd : Nat
96
+ acap : Nat
97
+
98
+ def Runtime.dBase (r : Runtime) : Nat := r.dEnd - 8 * r.capacity
99
+
100
+ def Runtime.aBase (r : Runtime) : Nat := r.aEnd - 8 * r.acap
101
+
102
+ /-- The lowering's view of the runtime: the constants the guards and bounds checks use. -/
103
+ def Runtime.layout (r : Runtime) : Layout :=
104
+ { dEnd := BitVec.ofNat 64 r.dEnd, memSize := r.memImage.length,
105
+ dLim := BitVec.ofNat 64 (r.dBase + 8), rGap := BitVec.ofNat 64 (8 * r.rcap - 16),
106
+ aEnd := BitVec.ofNat 64 r.aEnd, aLim := BitVec.ofNat 64 (r.aBase + 8) }
107
+
108
+ /-- The runtime prologue. Its model is `X86.Emit.prologueCode` (`X86/Init.lean`), where each `lea`
109
+ is the `movImm` of the address the linker resolves the symbol to; keep the two in step. -/
110
+ def prologue (r : Runtime) : String :=
111
+ "\t.intel_syntax noprefix\n\t.text\n\t.globl _start\n_start:\n" ++
112
+ "\tmov r12, rsp\n\tlea r13, [rip + irmem]\n\tlea r14, [rip + regfile]\n" ++
113
+ s!"\tmovabs r15, {hex r.dEnd}\n\tmovabs rbp, {hex r.aEnd}\n"
114
+
115
+ def sysWrite (bufSetup : String) (len : String) : String :=
116
+ s!"\t{bufSetup}\n\tmov rdx, {len}\n\tmov rax, 1\n\tmov rdi, 1\n\tsyscall\n"
117
+
118
+ def epilogue (r : Runtime) : String :=
119
+ let m := r.memImage.length
120
+ ".Lhalt:\n" ++
121
+ s!"\tmovabs rax, {hex r.dEnd}\n\tsub rax, r15\n\tshr rax, 3\n\tlea rbx, [rip + hdr]\n\tmov qword ptr [rbx], rax\n" ++
122
+ sysWrite "mov rsi, rbx" "8" ++
123
+ "\tlea rbx, [rip + hdr]\n\tmov rdx, qword ptr [rbx]\n\tshl rdx, 3\n\tmov rsi, r15\n\tmov rax, 1\n\tmov rdi, 1\n\tsyscall\n" ++
124
+ sysWrite "lea rsi, [rip + irmem]" (toString (8 * m)) ++
125
+ sysWrite "lea rsi, [rip + regfile]" (toString (8 * r.nregs)) ++
126
+ "\txor rdi, rdi\n\tjmp .Lexit\n" ++
127
+ String.join ((List.range 6).map fun k => s!".Ltrap{k + 1}:\n\tmov rdi, {k + 1}\n\tjmp .Lexit\n") ++
128
+ ".Lexit:\n\tmov rax, 60\n\tsyscall\n" ++
129
+ "\t.data\n\t.balign 8\nirmem:\n" ++
130
+ String.join (r.memImage.map fun v => s!"\t.quad {v}\n") ++
131
+ "\t.section .dstack, \"aw\", @nobits\n\t.balign 8\n" ++
132
+ s!"\t.skip {8 * r.capacity}\n" ++
133
+ "\t.section .astack, \"aw\", @nobits\n\t.balign 8\n" ++
134
+ s!"\t.skip {8 * r.acap}\n" ++
135
+ "\t.bss\n\t.balign 8\nhdr:\n\t.skip 8\nregfile:\n" ++ s!"\t.skip {8 * (r.nregs + 1)}\n"
136
+
137
+ /-- Full assembly text for a lowered program. -/
138
+ def program (r : Runtime) (code : List (Instr Nat)) : String :=
139
+ prologue r ++ body code ++ epilogue r
140
+
141
+ end Emit
142
+ end X86
143
+ end WordDialect
backend/x86_64/X86/Flags.lean ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.Lower
2
+
3
+ /-!
4
+ # X86.Flags
5
+
6
+ The condition-code semantics (`Cc.holds` on `subFlags`) coincides with the IR comparison
7
+ predicates (`Cond.eval`). This is the fact that makes `cmp; cmovcc` a correct lowering of IR
8
+ `CMP`, for all ten conditions, in all four of `ZF SF CF OF`.
9
+ -/
10
+
11
+ namespace WordDialect
12
+ namespace X86
13
+
14
+ theorem sub_zero_iff (a b : W) : (a - b == 0#64) = (a == b) := by
15
+ have h : (a - b = 0#64) ↔ (a = b) := by
16
+ constructor
17
+ · intro h
18
+ apply BitVec.eq_of_toNat_eq
19
+ have := congrArg BitVec.toNat h
20
+ simp [BitVec.toNat_sub] at this
21
+ have := a.isLt; have := b.isLt
22
+ omega
23
+ · intro h; subst h; simp
24
+ by_cases h' : a = b
25
+ · simp [h']
26
+ · have hne : ¬ (a - b = 0#64) := fun hh => h' (h.mp hh)
27
+ rw [beq_eq_false_iff_ne.mpr hne, beq_eq_false_iff_ne.mpr h']
28
+
29
+ theorem holds_e (a b : W) : Cc.e.holds (subFlags a b) = (a == b) := by
30
+ unfold Cc.holds subFlags; exact sub_zero_iff a b
31
+
32
+ theorem holds_ne (a b : W) : Cc.ne.holds (subFlags a b) = (a != b) := by
33
+ unfold Cc.holds subFlags
34
+ simp only [sub_zero_iff]
35
+ rfl
36
+
37
+ theorem holds_b (a b : W) : Cc.b.holds (subFlags a b) = a.ult b := by
38
+ unfold Cc.holds subFlags; rfl
39
+
40
+ theorem holds_ae (a b : W) : Cc.ae.holds (subFlags a b) = b.ule a := by
41
+ unfold Cc.holds subFlags
42
+ simp only [BitVec.ult_eq_decide, BitVec.ule_eq_decide]
43
+ by_cases h : a.toNat < b.toNat <;> simp [h] <;> omega
44
+
45
+ theorem holds_be (a b : W) : Cc.be.holds (subFlags a b) = a.ule b := by
46
+ unfold Cc.holds subFlags
47
+ simp only [sub_zero_iff, BitVec.ult_eq_decide, BitVec.ule_eq_decide]
48
+ by_cases h2 : a = b
49
+ · subst h2; simp
50
+ · have h3 : a.toNat ≠ b.toNat := fun h => h2 (BitVec.eq_of_toNat_eq h)
51
+ have h4 : (a == b) = false := beq_eq_false_iff_ne.mpr h2
52
+ by_cases h1 : a.toNat < b.toNat
53
+ · simp [h1, h4]; omega
54
+ · simp [h1, h4]
55
+ omega
56
+
57
+ theorem holds_a (a b : W) : Cc.a.holds (subFlags a b) = b.ult a := by
58
+ unfold Cc.holds subFlags
59
+ simp only [sub_zero_iff, BitVec.ult_eq_decide]
60
+ by_cases h2 : a = b
61
+ · subst h2; simp
62
+ · have h3 : a.toNat ≠ b.toNat := fun h => h2 (BitVec.eq_of_toNat_eq h)
63
+ have h4 : (a == b) = false := beq_eq_false_iff_ne.mpr h2
64
+ by_cases h1 : a.toNat < b.toNat
65
+ · simp [h1, h4]; omega
66
+ · simp [h1, h4]
67
+ omega
68
+
69
+ theorem holds_signed_core (a b : W) :
70
+ ((a - b).msb != (a.msb != b.msb && a.msb != (a - b).msb)) = a.slt b := by
71
+ have ha := a.isLt; have hb := b.isLt
72
+ simp only [BitVec.msb_eq_decide, BitVec.slt_eq_decide, BitVec.toInt_eq_toNat_cond,
73
+ BitVec.toNat_sub]
74
+ by_cases c1 : 2 ^ (64 - 1) ≤ a.toNat <;> by_cases c2 : 2 ^ (64 - 1) ≤ b.toNat <;>
75
+ by_cases c3 : 2 ^ (64 - 1) ≤ (2 ^ 64 - b.toNat + a.toNat) % 2 ^ 64 <;>
76
+ simp [c1, c2, c3] <;> omega
77
+
78
+ theorem holds_l (a b : W) : Cc.l.holds (subFlags a b) = a.slt b := by
79
+ unfold Cc.holds subFlags; simp only []; exact holds_signed_core a b
80
+ theorem beq_eq_not_bne (x y : Bool) : (x == y) = !(x != y) := by
81
+ cases x <;> cases y <;> rfl
82
+
83
+ theorem holds_ge (a b : W) : Cc.ge.holds (subFlags a b) = b.sle a := by
84
+ unfold Cc.holds subFlags
85
+ simp only []
86
+ have hc := holds_signed_core a b
87
+ rw [beq_eq_not_bne, hc, BitVec.slt_eq_decide, BitVec.sle_eq_decide]
88
+ by_cases h : a.toInt < b.toInt <;> simp [h] <;> omega
89
+
90
+
91
+ theorem holds_le (a b : W) : Cc.le.holds (subFlags a b) = a.sle b := by
92
+ unfold Cc.holds subFlags
93
+ simp only [sub_zero_iff]
94
+ rw [holds_signed_core a b, BitVec.slt_eq_decide, BitVec.sle_eq_decide]
95
+ by_cases h2 : a = b
96
+ · subst h2; simp
97
+ · have h3 : a.toInt ≠ b.toInt := fun h => h2 (BitVec.toInt_inj.mp h)
98
+ have h4 : (a == b) = false := beq_eq_false_iff_ne.mpr h2
99
+ by_cases h1 : a.toInt < b.toInt <;> simp [h1, h4] <;> omega
100
+
101
+ theorem holds_g (a b : W) : Cc.g.holds (subFlags a b) = b.slt a := by
102
+ unfold Cc.holds subFlags
103
+ simp only [sub_zero_iff]
104
+ have hc := holds_signed_core a b
105
+ rw [beq_eq_not_bne, hc, BitVec.slt_eq_decide, BitVec.slt_eq_decide]
106
+ by_cases h2 : a = b
107
+ · subst h2; simp
108
+ · have h3 : a.toInt ≠ b.toInt := fun h => h2 (BitVec.toInt_inj.mp h)
109
+ have h4 : (a == b) = false := beq_eq_false_iff_ne.mpr h2
110
+ by_cases h1 : a.toInt < b.toInt <;> simp [h1, h4] <;> omega
111
+
112
+ /-- `cmp a, b; jcc/cmovcc` evaluates exactly the IR comparison predicate. -/
113
+ theorem holds_ccOf (c : WordDialect.Cond) (a b : W) :
114
+ (ccOf c).holds (subFlags a b) = c.eval a b := by
115
+ cases c <;> simp only [ccOf, WordDialect.Cond.eval]
116
+ · exact holds_e a b
117
+ · exact holds_ne a b
118
+ · exact holds_b a b
119
+ · exact holds_be a b
120
+ · exact holds_a a b
121
+ · exact holds_ae a b
122
+ · exact holds_l a b
123
+ · exact holds_le a b
124
+ · exact holds_g a b
125
+ · exact holds_ge a b
126
+
127
+ end X86
128
+ end WordDialect
backend/x86_64/X86/Guard.lean ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.Micro
2
+
3
+ /-!
4
+ # X86.Guard
5
+
6
+ The operand-count guard `uf`: with enough operands it falls through; otherwise it exits with
7
+ `stackUnderflow`, which is exactly the IR trap.
8
+ -/
9
+
10
+ namespace WordDialect
11
+ namespace X86
12
+
13
+ open Reg
14
+
15
+ theorem ufSize_pos {n : Nat} (hn : 0 < n) : ufSize n = 4 := by simp [ufSize]; omega
16
+
17
+ theorem uf_eq (L : Layout) {n : Nat} (hn : 0 < n) (base : Nat) :
18
+ uf L n base = [.movImm rax (L.dEnd - BitVec.ofNat 64 (8 * n)), .cmp r15 rax, .jcc .be (base + 4),
19
+ .exitTrap .stackUnderflow] := by
20
+ simp [uf]; omega
21
+
22
+ theorem ule_ofN {a b : Nat} (ha : a < 2 ^ 64) (hb : b < 2 ^ 64) :
23
+ (ofN a).ule (ofN b) = decide (a ≤ b) := by
24
+ simp [BitVec.ule_eq_decide, ofN, BitVec.toNat_ofNat, Nat.mod_eq_of_lt ha, Nat.mod_eq_of_lt hb]
25
+
26
+ /-- The comparison inside the guard, in terms of stack depth. -/
27
+ theorem uf_flags {c : Cfg} {p : Prog 64} {s : State 64} {x : M} (hg : Geom c) (hs : Rel0 c p s x)
28
+ {L : Layout} (hL : L.dEnd = ofN c.dEnd) {n : Nat} (hn : 0 < n) (hn3 : n ≤ 3) :
29
+ ((subFlags (x.regs Reg.r15) (L.dEnd - BitVec.ofNat 64 (8 * n))) |> Cc.be.holds) =
30
+ decide (n ≤ s.dstack.length) := by
31
+ have hcap := hs.capD
32
+ have hdE := hg.dEnd_lt
33
+ have hge := hg.dEnd_ge
34
+ rw [holds_be, hs.r15, hL, show BitVec.ofNat 64 (8 * n) = ofN (8 * n) from rfl,
35
+ ofN_sub (by omega), ule_ofN (by omega) (by omega)]
36
+ by_cases h : n ≤ s.dstack.length
37
+ · simp [h]; omega
38
+ · simp [h]; omega
39
+
40
+ /-- Enough operands: the guard falls through to `base + 4`, changing only scratch state. -/
41
+ theorem uf_pass {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)}
42
+ {L : Layout} {base n : Nat} (hg : Geom c) (hs : Rel0 c p s x) (hL : L.dEnd = ofN c.dEnd)
43
+ (hn : 0 < n) (hn3 : n ≤ 3) (hk : n ≤ s.dstack.length) (hpc : x.pc = base)
44
+ (hat : XAt code base (uf L n base)) :
45
+ ∃ x1, XSteps code x x1 ∧ Rel0 c p s x1 ∧ x1.pc = base + 4 ∧ x1.mem = x.mem ∧
46
+ (∀ r, r ≠ rax → x1.regs r = x.regs r) := by
47
+ rw [uf_eq L hn] at hat
48
+ obtain ⟨h1, h2, h3, h4, _⟩ := hat
49
+ let xa := x.mov rax (L.dEnd - BitVec.ofNat 64 (8 * n))
50
+ have hst1 : step code x = .next xa := by rw [xstep_of_fetch (by rw [hpc]; exact h1)]; rfl
51
+ have hxa_pc : xa.pc = base + 1 := by simp [xa, M.mov, M.setReg, M.adv, hpc]
52
+ let xb : M := { xa.adv with flags := some (subFlags (xa.regs r15) (xa.regs rax)) }
53
+ have hst2 : step code xa = .next xb := by
54
+ rw [xstep_of_fetch (m := xa) (by rw [hxa_pc]; exact h2)]; rfl
55
+ have hxb_pc : xb.pc = base + 2 := by simp [xb, M.adv, hxa_pc]
56
+ have hr15 : xa.regs r15 = x.regs r15 := by simp [xa, M.mov, M.setReg, M.adv]
57
+ have hflags : xb.flags = some (subFlags (x.regs r15) (L.dEnd - BitVec.ofNat 64 (8 * n))) := by
58
+ simp [xb, hr15, xa, M.mov, M.setReg, M.adv]
59
+ have hholds : Cc.be.holds (subFlags (x.regs r15) (L.dEnd - BitVec.ofNat 64 (8 * n))) = true := by
60
+ have := uf_flags hg hs hL hn hn3
61
+ simpa [hk] using this
62
+ have hsteps := guard_pass (code := code) (pc := base + 2) (m := xb) (c := .be) (t := .stackUnderflow)
63
+ (f := subFlags (x.regs r15) (L.dEnd - BitVec.ofNat 64 (8 * n)))
64
+ (by simpa [XAt] using And.intro h3 h4) hxb_pc hflags hholds
65
+ refine ⟨{ xb with pc := base + 2 + 2 }, ((XSteps.single hst1).trans (XSteps.single hst2)).trans
66
+ (by simpa using hsteps), ?_, by simp, by simp [xb, xa, M.adv, M.mov, M.setReg], ?_⟩
67
+ · refine hs.congr (by simp [xb, xa, M.adv, M.mov, M.setReg]) ?_ ?_ ?_ ?_ ?_ ?_ <;>
68
+ simp [xb, xa, M.adv, M.mov, M.setReg]
69
+ · intro r hr; simp [xb, xa, M.adv, M.mov, M.setReg, hr]
70
+
71
+ /-- Too few operands: the guard exits with `stackUnderflow`. -/
72
+ theorem uf_trap {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)}
73
+ {L : Layout} {base n : Nat} (hg : Geom c) (hs : Rel0 c p s x) (hL : L.dEnd = ofN c.dEnd)
74
+ (hn : 0 < n) (hn3 : n ≤ 3) (hk : s.dstack.length < n) (hpc : x.pc = base)
75
+ (hat : XAt code base (uf L n base)) : XExec code x (.trapped .stackUnderflow) := by
76
+ rw [uf_eq L hn] at hat
77
+ obtain ⟨h1, h2, h3, h4, _⟩ := hat
78
+ let xa := x.mov rax (L.dEnd - BitVec.ofNat 64 (8 * n))
79
+ have hst1 : step code x = .next xa := by rw [xstep_of_fetch (by rw [hpc]; exact h1)]; rfl
80
+ have hxa_pc : xa.pc = base + 1 := by simp [xa, M.mov, M.setReg, M.adv, hpc]
81
+ let xb : M := { xa.adv with flags := some (subFlags (xa.regs r15) (xa.regs rax)) }
82
+ have hst2 : step code xa = .next xb := by
83
+ rw [xstep_of_fetch (m := xa) (by rw [hxa_pc]; exact h2)]; rfl
84
+ have hxb_pc : xb.pc = base + 2 := by simp [xb, M.adv, hxa_pc]
85
+ have hr15 : xa.regs r15 = x.regs r15 := by simp [xa, M.mov, M.setReg, M.adv]
86
+ have hflags : xb.flags = some (subFlags (x.regs r15) (L.dEnd - BitVec.ofNat 64 (8 * n))) := by
87
+ simp [xb, hr15, xa, M.mov, M.setReg, M.adv]
88
+ have hholds : Cc.be.holds (subFlags (x.regs r15) (L.dEnd - BitVec.ofNat 64 (8 * n))) = false := by
89
+ have := uf_flags hg hs hL hn hn3
90
+ have h : ¬ n ≤ s.dstack.length := by omega
91
+ simpa [h] using this
92
+ exact (XExec.of_steps ((XSteps.single hst1).trans (XSteps.single hst2))
93
+ (guard_trap (code := code) (pc := base + 2) (m := xb) (c := .be) (t := .stackUnderflow)
94
+ (f := subFlags (x.regs r15) (L.dEnd - BitVec.ofNat 64 (8 * n)))
95
+ (by simpa [XAt] using And.intro h3 h4) hxb_pc hflags hholds))
96
+
97
+ section Ovf
98
+
99
+ variable {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)} {L : Layout}
100
+ {base : Nat}
101
+
102
+ theorem ovf_jcc_pass {pc : Nat} {m : M} {f : Flags} (h1 : code[pc]? = some (.jcc .ae (pc + 2)))
103
+ (hpc : m.pc = pc) (hf : m.flags = some f) (hc : Cc.ae.holds f = true) :
104
+ XSteps code m { m with pc := pc + 2 } := by
105
+ apply XSteps.single
106
+ rw [xstep_of_fetch (by rw [hpc]; exact h1)]
107
+ simp [exec, hf, hc]
108
+
109
+ theorem ovf_jcc_trap {pc : Nat} {m : M} {f : Flags} (h1 : code[pc]? = some (.jcc .ae (pc + 2)))
110
+ (h2 : code[pc + 1]? = some .exitOvf) (hpc : m.pc = pc) (hf : m.flags = some f)
111
+ (hc : Cc.ae.holds f = false) : XExec code m .overflow := by
112
+ refine .next (m1 := { m with pc := pc + 1 }) ?_ (.ovf ?_)
113
+ · rw [xstep_of_fetch (by rw [hpc]; exact h1)]
114
+ simp [exec, hf, hc, hpc, M.adv]
115
+ · rw [xstep_of_fetch (m := { m with pc := pc + 1 }) h2]
116
+ simp [exec]
117
+
118
+ /-- The data-stack guard's comparison, in terms of stack depth. -/
119
+ theorem ovfD_flags (hg : Geom c) (hs : Rel0 c p s x) (hLd : L.dLim = ofN (c.dBase + 8)) :
120
+ Cc.ae.holds (subFlags (x.regs Reg.r15) L.dLim) =
121
+ decide (c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) := by
122
+ have hcap := hs.capD
123
+ have hdE := hg.dEnd_lt
124
+ have h8 := hg.dBase_8
125
+ rw [holds_ae, hs.r15, hLd, ule_ofN (by omega) (by omega)]
126
+ by_cases h : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
127
+ · simp [h]; omega
128
+ · simp [h]; omega
129
+
130
+ theorem ovfD_run (hpc : x.pc = base) (hat : XAt code base (ovfD L base)) :
131
+ ∃ xb : M, XSteps code x xb ∧ xb.pc = base + 2 ∧
132
+ xb.flags = some (subFlags (x.regs Reg.r15) L.dLim) ∧ xb.mem = x.mem ∧
133
+ (∀ r, r ≠ rax → xb.regs r = x.regs r) ∧
134
+ code[base + 2]? = some (.jcc .ae (base + 2 + 2)) ∧ code[base + 2 + 1]? = some .exitOvf := by
135
+ obtain ⟨h1, h2, h3, h4, _⟩ := hat
136
+ let xa := x.mov rax L.dLim
137
+ have hst1 : step code x = .next xa := by rw [xstep_of_fetch (by rw [hpc]; exact h1)]; rfl
138
+ have hxa_pc : xa.pc = base + 1 := by simp [xa, M.mov, M.setReg, M.adv, hpc]
139
+ let xb : M := { xa.adv with flags := some (subFlags (xa.regs r15) (xa.regs rax)) }
140
+ have hst2 : step code xa = .next xb := by
141
+ rw [xstep_of_fetch (m := xa) (by rw [hxa_pc]; exact h2)]; rfl
142
+ refine ⟨xb, (XSteps.single hst1).trans (XSteps.single hst2), by simp [xb, M.adv, hxa_pc],
143
+ by simp [xb, xa, M.mov, M.setReg, M.adv], by simp [xb, xa, M.adv, M.mov, M.setReg], ?_,
144
+ fetch_cast h3 (by omega), fetch_cast h4 (by omega)⟩
145
+ intro r hr; simp [xb, xa, M.adv, M.mov, M.setReg, hr]
146
+
147
+ /-- Room for one more data word: the guard falls through to `base + 4`. -/
148
+ theorem ovfD_pass (hg : Geom c) (hs : Rel0 c p s x) (hLd : L.dLim = ofN (c.dBase + 8))
149
+ (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) (hpc : x.pc = base)
150
+ (hat : XAt code base (ovfD L base)) :
151
+ ∃ x1, XSteps code x x1 ∧ Rel0 c p s x1 ∧ x1.pc = base + 4 ∧ x1.mem = x.mem ∧
152
+ (∀ r, r ≠ rax → x1.regs r = x.regs r) := by
153
+ obtain ⟨xb, hsb, hpcb, hfb, hmb, hrb, hj, _⟩ := ovfD_run hpc hat
154
+ have hholds : Cc.ae.holds (subFlags (x.regs Reg.r15) L.dLim) = true := by
155
+ rw [ovfD_flags hg hs hLd]; simpa using hfit
156
+ refine ⟨{ xb with pc := base + 2 + 2 }, hsb.trans (ovf_jcc_pass hj hpcb hfb hholds), ?_, rfl, hmb, hrb⟩
157
+ refine hs.congr hmb ?_ ?_ ?_ ?_ ?_ ?_ <;> exact hrb _ (by decide)
158
+
159
+ /-- Data stack full: the guard exits with `overflow`. -/
160
+ theorem ovfD_trap (hg : Geom c) (hs : Rel0 c p s x) (hLd : L.dLim = ofN (c.dBase + 8))
161
+ (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) (hpc : x.pc = base)
162
+ (hat : XAt code base (ovfD L base)) : XExec code x .overflow := by
163
+ obtain ⟨xb, hsb, hpcb, hfb, _, _, hj, he⟩ := ovfD_run hpc hat
164
+ have hholds : Cc.ae.holds (subFlags (x.regs Reg.r15) L.dLim) = false := by
165
+ rw [ovfD_flags hg hs hLd]; simpa using h
166
+ exact XExec.of_steps hsb (ovf_jcc_trap hj he hpcb hfb hholds)
167
+
168
+ /-- The return-stack guard's comparison, in terms of return-stack depth. -/
169
+ theorem ovfR_flags (hg : Geom c) (hs : Rel0 c p s x) (hLr : L.rGap = ofN (8 * c.rcap - 16)) :
170
+ Cc.ae.holds (subFlags (x.regs Reg.rsp) (x.regs Reg.r12 - L.rGap)) =
171
+ decide (s.rstack.length + 2 ≤ c.rcap) := by
172
+ have hcapR := hs.capR
173
+ have hrc := hg.rcap_le
174
+ have hsp := hg.sp0_lt
175
+ have hge := hg.rcap_ge
176
+ rw [holds_ae, hs.rsp, hs.r12, hLr, ofN_sub (by omega), ule_ofN (by omega) (by omega)]
177
+ by_cases h : s.rstack.length + 2 ≤ c.rcap
178
+ · simp [h]; omega
179
+ · simp [h]; omega
180
+
181
+ theorem ovfR_run (hpc : x.pc = base) (hat : XAt code base (ovfR L base)) :
182
+ ∃ xd : M, XSteps code x xd ∧ xd.pc = base + 4 ∧
183
+ xd.flags = some (subFlags (x.regs Reg.rsp) (x.regs Reg.r12 - L.rGap)) ∧ xd.mem = x.mem ∧
184
+ (∀ r, r ≠ rax → r ≠ rcx → xd.regs r = x.regs r) ∧
185
+ code[base + 4]? = some (.jcc .ae (base + 4 + 2)) ∧ code[base + 4 + 1]? = some .exitOvf := by
186
+ obtain ⟨h1, h2, h3, h4, h5, h6, _⟩ := hat
187
+ let xa := x.mov rax (x.regs r12)
188
+ have hst1 : step code x = .next xa := by rw [xstep_of_fetch (by rw [hpc]; exact h1)]; rfl
189
+ have hxa_pc : xa.pc = base + 1 := by simp [xa, M.mov, M.setReg, M.adv, hpc]
190
+ let xb := xa.mov rcx L.rGap
191
+ have hst2 : step code xa = .next xb := by
192
+ rw [xstep_of_fetch (m := xa) (by rw [hxa_pc]; exact h2)]; rfl
193
+ have hxb_pc : xb.pc = base + 2 := by simp [xb, M.mov, M.setReg, M.adv, hxa_pc]
194
+ let xc := xb.arith rax (xb.regs rax - xb.regs rcx)
195
+ have hst3 : step code xb = .next xc := by
196
+ rw [xstep_of_fetch (m := xb) (by rw [hxb_pc]; exact h3)]; rfl
197
+ have hxc_pc : xc.pc = base + 3 := by simp [xc, M.arith, M.setReg, hxb_pc]
198
+ let xd : M := { xc.adv with flags := some (subFlags (xc.regs rsp) (xc.regs rax)) }
199
+ have hst4 : step code xc = .next xd := by
200
+ rw [xstep_of_fetch (m := xc) (by rw [hxc_pc]; exact h4)]; rfl
201
+ refine ⟨xd, (((XSteps.single hst1).trans (XSteps.single hst2)).trans (XSteps.single hst3)).trans
202
+ (XSteps.single hst4), by simp [xd, M.adv, hxc_pc], ?_, ?_, ?_,
203
+ fetch_cast h5 (by omega), fetch_cast h6 (by omega)⟩
204
+ · simp [xd, xc, xb, xa, M.arith, M.mov, M.setReg, M.adv]
205
+ · simp [xd, xc, xb, xa, M.arith, M.mov, M.setReg, M.adv]
206
+ · intro r hr hr'; simp [xd, xc, xb, xa, M.arith, M.mov, M.setReg, M.adv, hr, hr']
207
+
208
+ /-- Room for one more return address: the guard falls through to `base + 6`. -/
209
+ theorem ovfR_pass (hg : Geom c) (hs : Rel0 c p s x) (hLr : L.rGap = ofN (8 * c.rcap - 16))
210
+ (hfit : s.rstack.length + 2 ≤ c.rcap) (hpc : x.pc = base) (hat : XAt code base (ovfR L base)) :
211
+ ∃ x1, XSteps code x x1 ∧ Rel0 c p s x1 ∧ x1.pc = base + 6 ∧ x1.mem = x.mem := by
212
+ obtain ⟨xd, hsd, hpcd, hfd, hmd, hrd, hj, _⟩ := ovfR_run hpc hat
213
+ have hholds : Cc.ae.holds (subFlags (x.regs Reg.rsp) (x.regs Reg.r12 - L.rGap)) = true := by
214
+ rw [ovfR_flags hg hs hLr]; simpa using hfit
215
+ refine ⟨{ xd with pc := base + 4 + 2 }, hsd.trans (ovf_jcc_pass hj hpcd hfd hholds), ?_, rfl, hmd⟩
216
+ refine hs.congr hmd ?_ ?_ ?_ ?_ ?_ ?_ <;> exact hrd _ (by decide) (by decide)
217
+
218
+ /-- Return stack full: the guard exits with `overflow`. -/
219
+ theorem ovfR_trap (hg : Geom c) (hs : Rel0 c p s x) (hLr : L.rGap = ofN (8 * c.rcap - 16))
220
+ (h : ¬ s.rstack.length + 2 ≤ c.rcap) (hpc : x.pc = base) (hat : XAt code base (ovfR L base)) :
221
+ XExec code x .overflow := by
222
+ obtain ⟨xd, hsd, hpcd, hfd, _, _, hj, he⟩ := ovfR_run hpc hat
223
+ have hholds : Cc.ae.holds (subFlags (x.regs Reg.rsp) (x.regs Reg.r12 - L.rGap)) = false := by
224
+ rw [ovfR_flags hg hs hLr]; simpa using h
225
+ exact XExec.of_steps hsd (ovf_jcc_trap hj he hpcd hfd hholds)
226
+
227
+ end Ovf
228
+
229
+ end X86
230
+ end WordDialect
backend/x86_64/X86/Init.lean ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.Correct
2
+ import X86.Emit
3
+
4
+ /-!
5
+ # X86.Init
6
+
7
+ The state the emitted binary starts the lowered code in, and the end-to-end theorem from it.
8
+
9
+ The runtime prologue (`Emit.prologue`) is five instructions:
10
+
11
+ mov r12, rsp ; lea r13, [rip + irmem] ; lea r14, [rip + regfile] ; movabs r15, dEnd ;
12
+ movabs rbp, aEnd
13
+
14
+ `prologueCode` is the same sequence in the model, with each `lea` as a `movImm` of the address the
15
+ linker resolves `irmem` / `regfile` to (that `lea` computes this address is the trusted reading of
16
+ the instruction). Its effect is proved (`prologue_run`), not assumed.
17
+
18
+ What the operating system's loader provides at `_start` cannot be derived inside the model, so it
19
+ is stated once, explicitly, as `Loader.Holds`:
20
+ * `rsp` is the entry stack pointer `sp0`;
21
+ * the zero-filled `.bss` register file reads as zeros, and `.data` holds the IR memory image word by
22
+ word (the `.quad` directives `Emit.epilogue` prints);
23
+ * the data stack (`.dstack`, placed at `dBase` by the linker), the auxiliary stack (`.astack`,
24
+ placed at `aBase`), register file, IR memory and the `rcap` return-stack entries below `sp0` are
25
+ addressable;
26
+ * the placement is disjoint and in range (`Geom`).
27
+
28
+ `binary_correct`: under those loader facts, after the prologue the lowered code reaches the IR
29
+ outcome from `State.init` on the memory image, or exits with `overflow`. Its other hypotheses are
30
+ only about the program.
31
+ -/
32
+
33
+ namespace WordDialect
34
+ namespace X86
35
+ namespace Emit
36
+
37
+ open Reg
38
+
39
+ /-- Addresses fixed by the linker and the loader for one run. -/
40
+ structure Loader where
41
+ mb : Nat -- link address of `irmem` (`.data`)
42
+ rf : Nat -- link address of `regfile` (`.bss`)
43
+ sp0 : Nat -- `rsp` at `_start`
44
+
45
+ /-- The runtime placement for one run, as a `Cfg`. -/
46
+ def cfgOf (r : Runtime) (ld : Loader) : Cfg :=
47
+ { dEnd := r.dEnd, dBase := r.dBase, rf := ld.rf, nregs := r.nregs, mb := ld.mb,
48
+ M := r.memImage.length, sp0 := ld.sp0, rcap := r.rcap, aEnd := r.aEnd, aBase := r.aBase }
49
+
50
+ /-- What the loader guarantees about the machine state `x0` at `_start`. -/
51
+ structure Loader.Holds (r : Runtime) (ld : Loader) (x0 : M) : Prop where
52
+ rsp : x0.regs Reg.rsp = ofN ld.sp0
53
+ geom : Geom (cfgOf r ld)
54
+ valid : RegionsValid (cfgOf r ld) x0.mem.valid
55
+ regfile : ∀ k, k < r.nregs → x0.mem.read? (ofN (ld.rf + 8 * k)) = some 0#64
56
+ image : ∀ a, a < r.memImage.length →
57
+ x0.mem.read? (ofN (ld.mb + 8 * a)) = some (BitVec.ofNat 64 (r.memImage.getD a 0))
58
+
59
+ /-- The prologue in the model. -/
60
+ def prologueCode (r : Runtime) (ld : Loader) : List (Instr Nat) :=
61
+ [.movRR r12 rsp, .movImm r13 (ofN ld.mb), .movImm r14 (ofN ld.rf), .movImm r15 (ofN r.dEnd),
62
+ .movImm rbp (ofN r.aEnd)]
63
+
64
+ /-- The machine state after the prologue, entering the lowered code at index 0. -/
65
+ def entryState (r : Runtime) (ld : Loader) (x0 : M) : M :=
66
+ { (((((x0.mov r12 (x0.regs rsp)).mov r13 (ofN ld.mb)).mov r14 (ofN ld.rf)).mov r15 (ofN r.dEnd)).mov
67
+ rbp (ofN r.aEnd)) with pc := 0 }
68
+
69
+ theorem prologue_run (r : Runtime) (ld : Loader) (x0 : M) :
70
+ xseq (prologueCode r ld) x0 =
71
+ .next { entryState r ld x0 with pc := x0.pc + 5 } := by
72
+ simp [prologueCode, entryState, xseq, exec, M.mov, M.setReg, M.adv]
73
+
74
+ theorem isize_le (i : WordDialect.Instr 64) : isize i ≤ 17 := by
75
+ cases i <;> simp [isize, ufSize]
76
+
77
+ theorem sum_isize_le : ∀ l : Prog 64, (l.map isize).sum ≤ 17 * l.length
78
+ | [] => by simp
79
+ | i :: l => by
80
+ have := isize_le i
81
+ have := sum_isize_le l
82
+ simp only [List.map_cons, List.sum_cons, List.length_cons]
83
+ omega
84
+
85
+ /-- Code indices are bounded by the program length. -/
86
+ theorem offs_lt {p : Prog 64} (h : 17 * p.length < 2 ^ 64) (k : Nat) : offs p k < 2 ^ 64 := by
87
+ have h1 := sum_isize_le (p.take k)
88
+ have h2 : (p.take k).length ≤ p.length := by rw [List.length_take]; omega
89
+ simp only [offs]
90
+ omega
91
+
92
+ theorem layout_ok (r : Runtime) (ld : Loader) :
93
+ r.layout.dEnd = ofN (cfgOf r ld).dEnd ∧ r.layout.memSize = (cfgOf r ld).M ∧
94
+ r.layout.dLim = ofN ((cfgOf r ld).dBase + 8) ∧ r.layout.rGap = ofN (8 * (cfgOf r ld).rcap - 16) ∧
95
+ r.layout.aEnd = ofN (cfgOf r ld).aEnd ∧ r.layout.aLim = ofN ((cfgOf r ld).aBase + 8) :=
96
+ ⟨rfl, rfl, rfl, rfl, rfl, rfl⟩
97
+
98
+ /-- After the prologue, the machine is related to the IR's initial state. -/
99
+ theorem entry_rel (r : Runtime) (ld : Loader) {x0 : M} (h : ld.Holds r x0) (p : Prog 64) :
100
+ Rel (cfgOf r ld) p (State.init (Memory.ofImage r.memImage)) (entryState r ld x0) := by
101
+ have hg := h.geom
102
+ refine init_rel hg rfl rfl rfl (by have := hg.rcap_ge; simp [cfgOf] at this ⊢; omega)
103
+ ?_ ?_ ?_ ?_ ?_ ?_ ?_ ?_ (fun _ => rfl) ?_ (by simp [entryState, offs, State.init])
104
+ · simp [entryState, M.mov, M.setReg, M.adv, cfgOf]
105
+ · simp [entryState, M.mov, M.setReg, M.adv, cfgOf]
106
+ · simp [entryState, M.mov, M.setReg, M.adv, cfgOf]
107
+ · simp [entryState, M.mov, M.setReg, M.adv, cfgOf, h.rsp]
108
+ · simp [entryState, M.mov, M.setReg, M.adv, cfgOf, h.rsp]
109
+ · simp [entryState, M.mov, M.setReg, M.adv, cfgOf]
110
+ · intro k hk
111
+ simpa [entryState, M.mov, M.setReg, M.adv, State.init, cfgOf] using h.regfile k hk
112
+ · intro a ha
113
+ have ha' : a < r.memImage.length := ha
114
+ have hlt : a < 2 ^ 64 := by
115
+ have := hg.mb_lt; simp [cfgOf] at this ha ⊢; omega
116
+ simpa [entryState, M.mov, M.setReg, M.adv, State.init, Memory.ofImage, cfgOf, ofN,
117
+ BitVec.toNat_ofNat, Nat.mod_eq_of_lt hlt] using h.image a ha'
118
+ · simpa [entryState, M.mov, M.setReg, M.adv] using h.valid
119
+
120
+ /-- End to end: under the loader facts, the prologue runs, and from there the lowered code reaches
121
+ the outcome the IR reaches from `State.init` on the memory image, or exits with `overflow` (exit
122
+ code 6). Program hypotheses: code indices fit in 64 bits and register indices are in range. -/
123
+ theorem binary_correct (r : Runtime) (ld : Loader) (p : Prog 64) (hlen : 17 * p.length < 2 ^ 64)
124
+ (hreg : RegsOk (cfgOf r ld) p) {o : Outcome 64}
125
+ (hx : Exec p (State.init (Memory.ofImage r.memImage)) o) {x0 : M} (h : ld.Holds r x0) :
126
+ xseq (prologueCode r ld) x0 = .next { entryState r ld x0 with pc := x0.pc + 5 } ∧
127
+ (Final (cfgOf r ld) p (lowerProg r.layout p) (entryState r ld x0) o ∨
128
+ XExec (lowerProg r.layout p) (entryState r ld x0) .overflow) := by
129
+ obtain ⟨hL, hMs, hLd, hLr, hLa, hLal⟩ := layout_ok r ld
130
+ exact ⟨prologue_run r ld x0,
131
+ lowerProg_correct_or_overflow h.geom hL hMs hLd hLr hLa hLal (offs_lt hlen) hreg hx (entry_rel r ld h p)⟩
132
+
133
+ end Emit
134
+ end X86
135
+ end WordDialect
backend/x86_64/X86/Isa.lean ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import WordDialect
2
+
3
+ /-!
4
+ # X86.Isa
5
+
6
+ A Lean model of the x86-64 instruction subset the backend emits.
7
+
8
+ Scope and trust boundary:
9
+ * 64-bit registers only; the registers the lowering uses.
10
+ * Memory is the shared `Memory 64` model, indexed by byte address. The lowering only performs
11
+ 8-byte-aligned accesses; no unaligned or sub-word access is modelled.
12
+ * Flags `ZF SF CF OF` are modelled concretely. Instructions that architecturally leave the
13
+ flags undefined (arithmetic, shifts, `imul`, divide) set them to `none`; reading `none`
14
+ faults. So a proof that the machine never faults shows the lowering never relies on
15
+ undefined flags.
16
+ * Faults (invalid memory access, divide error, undefined flags, jumping outside the code)
17
+ are a distinct outcome, never a silent state.
18
+ * `exitHalt` / `exitTrap t` stand for the runtime stubs that dump state and `exit`; they are
19
+ terminal here. `exitOvf` is the stub for a stack-overflow guard (`exit(6)`); it has no IR
20
+ counterpart, since IR stacks are unbounded.
21
+
22
+ That the model matches the hardware is the stated assumption of this backend.
23
+ -/
24
+
25
+ namespace WordDialect
26
+ namespace X86
27
+
28
+ abbrev W := BitVec 64
29
+
30
+ inductive Reg where
31
+ | rax | rcx | rdx | rbx | rsp | rbp | r12 | r13 | r14 | r15
32
+ deriving DecidableEq, Repr
33
+
34
+ /-- Condition codes (the subset used). -/
35
+ inductive Cc where
36
+ | e | ne | b | be | a | ae | l | le | g | ge
37
+ deriving DecidableEq, Repr
38
+
39
+ structure Flags where
40
+ zf : Bool
41
+ sf : Bool
42
+ cf : Bool
43
+ ovf : Bool
44
+
45
+ def Cc.holds : Cc → Flags → Bool
46
+ | .e, f => f.zf
47
+ | .ne, f => !f.zf
48
+ | .b, f => f.cf
49
+ | .be, f => f.cf || f.zf
50
+ | .a, f => !f.cf && !f.zf
51
+ | .ae, f => !f.cf
52
+ | .l, f => f.sf != f.ovf
53
+ | .le, f => f.zf || (f.sf != f.ovf)
54
+ | .g, f => !f.zf && (f.sf == f.ovf)
55
+ | .ge, f => f.sf == f.ovf
56
+
57
+ /-- Flags of `a - b`, as set by `cmp a, b`. -/
58
+ def subFlags (a b : W) : Flags :=
59
+ let d := a - b
60
+ { zf := d == 0#64, sf := d.msb, cf := a.ult b,
61
+ ovf := (a.msb != b.msb) && (a.msb != d.msb) }
62
+
63
+ /-- Flags of a logical result (`test`, `and`, `or`, `xor`): `CF = OF = 0`. -/
64
+ def logicFlags (r : W) : Flags := { zf := r == 0#64, sf := r.msb, cf := false, ovf := false }
65
+
66
+ /-- Instructions, generic in the type of jump targets (`Nat` code indices for the model,
67
+ `String` labels for assembly text). -/
68
+ inductive Instr (L : Type) where
69
+ | movImm (d : Reg) (v : W)
70
+ | movRR (d s : Reg)
71
+ | load (d b : Reg) (disp : Int)
72
+ | store (b : Reg) (disp : Int) (s : Reg)
73
+ | loadIdx (d b i : Reg)
74
+ | storeIdx (b i s : Reg)
75
+ | add (d s : Reg)
76
+ | sub (d s : Reg)
77
+ | imul (d s : Reg)
78
+ | and (d s : Reg)
79
+ | or (d s : Reg)
80
+ | xor (d s : Reg)
81
+ | addImm (d : Reg) (v : Int)
82
+ | not (d : Reg)
83
+ | neg (d : Reg)
84
+ | cmp (a b : Reg)
85
+ | cmpImm (a : Reg) (v : Int)
86
+ | test (a b : Reg)
87
+ | shlCl (d : Reg)
88
+ | shrCl (d : Reg)
89
+ | rolCl (d : Reg)
90
+ | rorCl (d : Reg)
91
+ | cmov (c : Cc) (d s : Reg)
92
+ | div (s : Reg)
93
+ | idiv (s : Reg)
94
+ | cqo
95
+ | jmp (t : L)
96
+ | jcc (c : Cc) (t : L)
97
+ | call (t : L)
98
+ | ret
99
+ | exitHalt
100
+ | exitTrap (t : Trap)
101
+ | exitOvf
102
+
103
+ structure M where
104
+ regs : Reg → W
105
+ flags : Option Flags
106
+ mem : Memory 64
107
+ pc : Nat
108
+
109
+ inductive Out where
110
+ | next (m : M)
111
+ | halted (m : M)
112
+ | trapped (t : Trap)
113
+ | overflow
114
+ | fault
115
+
116
+ def M.setReg (m : M) (r : Reg) (v : W) : M :=
117
+ { m with regs := fun x => if x = r then v else m.regs x }
118
+
119
+ def M.adv (m : M) : M := { m with pc := m.pc + 1 }
120
+
121
+ /-- Write a register and advance; flags become undefined. -/
122
+ def M.arith (m : M) (r : Reg) (v : W) : M :=
123
+ { (m.setReg r v) with flags := none, pc := m.pc + 1 }
124
+
125
+ /-- Write a register and advance; flags untouched (as `mov`). -/
126
+ def M.mov (m : M) (r : Reg) (v : W) : M := (m.setReg r v).adv
127
+
128
+ def M.ea (m : M) (b : Reg) (disp : Int) : W := m.regs b + BitVec.ofInt 64 disp
129
+
130
+ def M.eaIdx (m : M) (b i : Reg) : W := m.regs b + m.regs i * 8#64
131
+
132
+ /-- Semantics of one instruction. -/
133
+ def exec (i : Instr Nat) (m : M) : Out :=
134
+ match i with
135
+ | .movImm d v => .next (m.mov d v)
136
+ | .movRR d s => .next (m.mov d (m.regs s))
137
+ | .load d b disp =>
138
+ match m.mem.read? (m.ea b disp) with
139
+ | some v => .next (m.mov d v)
140
+ | none => .fault
141
+ | .store b disp s =>
142
+ match m.mem.write? (m.ea b disp) (m.regs s) with
143
+ | some mem' => .next { m.adv with mem := mem' }
144
+ | none => .fault
145
+ | .loadIdx d b i =>
146
+ match m.mem.read? (m.eaIdx b i) with
147
+ | some v => .next (m.mov d v)
148
+ | none => .fault
149
+ | .storeIdx b i s =>
150
+ match m.mem.write? (m.eaIdx b i) (m.regs s) with
151
+ | some mem' => .next { m.adv with mem := mem' }
152
+ | none => .fault
153
+ | .add d s => .next (m.arith d (m.regs d + m.regs s))
154
+ | .sub d s => .next (m.arith d (m.regs d - m.regs s))
155
+ | .imul d s => .next (m.arith d (m.regs d * m.regs s))
156
+ | .and d s => .next (m.arith d (m.regs d &&& m.regs s))
157
+ | .or d s => .next (m.arith d (m.regs d ||| m.regs s))
158
+ | .xor d s => .next (m.arith d (m.regs d ^^^ m.regs s))
159
+ | .addImm d v => .next (m.arith d (m.regs d + BitVec.ofInt 64 v))
160
+ | .not d => .next (m.mov d (~~~m.regs d))
161
+ | .neg d => .next (m.arith d (0#64 - m.regs d))
162
+ | .cmp a b => .next { m.adv with flags := some (subFlags (m.regs a) (m.regs b)) }
163
+ | .cmpImm a v => .next { m.adv with flags := some (subFlags (m.regs a) (BitVec.ofInt 64 v)) }
164
+ | .test a b => .next { m.adv with flags := some (logicFlags (m.regs a &&& m.regs b)) }
165
+ | .shlCl d => .next (m.arith d (m.regs d <<< ((m.regs .rcx).toNat % 64)))
166
+ | .shrCl d => .next (m.arith d (m.regs d >>> ((m.regs .rcx).toNat % 64)))
167
+ | .rolCl d => .next (m.arith d ((m.regs d).rotateLeft ((m.regs .rcx).toNat % 64)))
168
+ | .rorCl d => .next (m.arith d ((m.regs d).rotateRight ((m.regs .rcx).toNat % 64)))
169
+ | .cmov c d s =>
170
+ match m.flags with
171
+ | none => .fault
172
+ | some f => .next (if c.holds f then m.mov d (m.regs s) else m.adv)
173
+ | .div s =>
174
+ let dv := m.regs s
175
+ if dv = 0#64 then .fault
176
+ else
177
+ let dividend : Nat := (m.regs .rdx).toNat * 2 ^ 64 + (m.regs .rax).toNat
178
+ let q := dividend / dv.toNat
179
+ if q < 2 ^ 64 then
180
+ .next { ((m.setReg .rax (BitVec.ofNat 64 q)).setReg .rdx
181
+ (BitVec.ofNat 64 (dividend % dv.toNat))) with flags := none, pc := m.pc + 1 }
182
+ else .fault
183
+ | .idiv s =>
184
+ let dv := m.regs s
185
+ if dv = 0#64 then .fault
186
+ else
187
+ let dividend : Int := (m.regs .rdx).toInt * 2 ^ 64 + ((m.regs .rax).toNat : Int)
188
+ let q := Int.tdiv dividend dv.toInt
189
+ if -(2 ^ 63 : Int) ≤ q ∧ q < 2 ^ 63 then
190
+ .next { ((m.setReg .rax (BitVec.ofInt 64 q)).setReg .rdx
191
+ (BitVec.ofInt 64 (Int.tmod dividend dv.toInt))) with flags := none, pc := m.pc + 1 }
192
+ else .fault
193
+ | .cqo => .next (m.mov .rdx (if (m.regs .rax).msb then BitVec.allOnes 64 else 0#64))
194
+ | .jmp t => .next { m with pc := t }
195
+ | .jcc c t =>
196
+ match m.flags with
197
+ | none => .fault
198
+ | some f => .next (if c.holds f then { m with pc := t } else m.adv)
199
+ | .call t =>
200
+ let sp := m.regs .rsp - 8#64
201
+ match m.mem.write? sp (BitVec.ofNat 64 (m.pc + 1)) with
202
+ | some mem' => .next { (m.setReg .rsp sp) with mem := mem', pc := t }
203
+ | none => .fault
204
+ | .ret =>
205
+ match m.mem.read? (m.regs .rsp) with
206
+ | some v => .next { (m.setReg .rsp (m.regs .rsp + 8#64)) with pc := v.toNat }
207
+ | none => .fault
208
+ | .exitHalt => .halted m
209
+ | .exitTrap t => .trapped t
210
+ | .exitOvf => .overflow
211
+
212
+ /-- One machine step over a code array; leaving the code is a fault. -/
213
+ def step (code : List (Instr Nat)) (m : M) : Out :=
214
+ match code[m.pc]? with
215
+ | none => .fault
216
+ | some i => exec i m
217
+
218
+ /-- Fuel-bounded run. `none`: out of fuel. -/
219
+ def run (code : List (Instr Nat)) : Nat → M → Option Out
220
+ | 0, _ => none
221
+ | fuel + 1, m =>
222
+ match step code m with
223
+ | .next m' => run code fuel m'
224
+ | o => some o
225
+
226
+ end X86
227
+ end WordDialect
backend/x86_64/X86/Lower.lean ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.Isa
2
+
3
+ /-!
4
+ # X86.Lower
5
+
6
+ Lowering of Universal Word IR (64-bit words) to x86-64.
7
+
8
+ Runtime conventions (all fixed here; the assembly runtime in `X86.Emit` establishes them):
9
+
10
+ | register | role |
11
+ |----------|------|
12
+ | `r15` | data-stack pointer; the stack grows downward, `[r15]` is the top, empty = `dEnd` |
13
+ | `r14` | base of the virtual register file, cell `r` at `r14 + 8r` |
14
+ | `r13` | base of IR memory; IR word address `a` is at `r13 + 8a` |
15
+ | `r12` | value of `rsp` at entry; the native stack is the IR return stack |
16
+ | `rbp` | auxiliary-stack pointer; grows downward, `[rbp]` is the top, empty = `aEnd` |
17
+ | `rax rcx rdx rbx` | scratch |
18
+
19
+ IR `call`/`ret` are native `call`/`ret`. IR memory is valid exactly on `[0, memSize)`; every
20
+ `load`/`store` is bounds-checked, so an invalid address traps as in the IR semantics. Operand
21
+ counts are checked against `dEnd` before every instruction that pops, so stack underflow
22
+ traps as in the IR semantics. Every instruction that grows the data stack first checks
23
+ `r15 ≥ dBase + 8`, and `call` first checks `rsp ≥ r12 - (8 rcap - 16)`; on overflow the code
24
+ exits 6 (`exitOvf`). `tor` likewise checks `rbp ≥ aBase + 8` before growing the auxiliary
25
+ stack, and `fromr`/`rfetch` trap `returnUnderflow` when `rbp = aEnd` (empty). IR stacks are unbounded, so this exit has no IR counterpart: it reports that
26
+ the run exceeded the target's stack capacity.
27
+
28
+ Every instruction's code has a size that depends only on the instruction, so jump targets are
29
+ absolute code indices computed from prefix sums (`offs`). One definition serves both the
30
+ model and the assembly text.
31
+ -/
32
+
33
+ namespace WordDialect
34
+ namespace X86
35
+
36
+ open Reg
37
+
38
+ structure Layout where
39
+ dEnd : W
40
+ memSize : Nat
41
+ dLim : W -- `dBase + 8`: the data stack has room for one more word iff `r15 ≥ dLim`
42
+ rGap : W -- `8 rcap - 16`: a call has room iff `rsp ≥ r12 - rGap`
43
+ aEnd : W -- empty auxiliary stack: `rbp = aEnd`
44
+ aLim : W -- `aBase + 8`: the auxiliary stack has room for one more word iff `rbp ≥ aLim`
45
+
46
+ /-- Stack-underflow guard for `k` operands. -/
47
+ def uf (L : Layout) (k base : Nat) : List (Instr Nat) :=
48
+ if k = 0 then []
49
+ else [.movImm rax (L.dEnd - BitVec.ofNat 64 (8 * k)), .cmp r15 rax, .jcc .be (base + 4),
50
+ .exitTrap .stackUnderflow]
51
+
52
+ def ufSize (k : Nat) : Nat := if k = 0 then 0 else 4
53
+
54
+ /-- Data-stack overflow guard: exit 6 unless one more word fits. -/
55
+ def ovfD (L : Layout) (base : Nat) : List (Instr Nat) :=
56
+ [.movImm rax L.dLim, .cmp r15 rax, .jcc .ae (base + 4), .exitOvf]
57
+
58
+ /-- Return-stack overflow guard: exit 6 unless a call has room on the native stack. -/
59
+ def ovfR (L : Layout) (base : Nat) : List (Instr Nat) :=
60
+ [.movRR rax r12, .movImm rcx L.rGap, .sub rax rcx, .cmp rsp rax, .jcc .ae (base + 6), .exitOvf]
61
+
62
+ /-- Auxiliary-stack overflow guard: exit 6 unless one more word fits. -/
63
+ def ovfA (L : Layout) (base : Nat) : List (Instr Nat) :=
64
+ [.movImm rax L.aLim, .cmp rbp rax, .jcc .ae (base + 4), .exitOvf]
65
+
66
+ /-- Empty-auxiliary-stack guard: trap `returnUnderflow` when `rbp = aEnd`. -/
67
+ def emptyA (L : Layout) (base : Nat) : List (Instr Nat) :=
68
+ [.movImm rax L.aEnd, .cmp rbp rax, .jcc .ne (base + 4), .exitTrap .returnUnderflow]
69
+
70
+ def ccOf : WordDialect.Cond → Cc
71
+ | .eq => .e | .ne => .ne | .ult => .b | .ule => .be | .ugt => .a | .uge => .ae
72
+ | .slt => .l | .sle => .le | .sgt => .g | .sge => .ge
73
+
74
+ /-- Code size of one IR instruction. -/
75
+ def isize : WordDialect.Instr 64 → Nat
76
+ | .word _ | .ptr _ => 7
77
+ | .load => ufSize 1 + 7
78
+ | .store => ufSize 2 + 8
79
+ | .add | .sub | .mul | .and | .or | .xor => ufSize 2 + 5
80
+ | .div => ufSize 2 + 9
81
+ | .sdiv => ufSize 2 + 13
82
+ | .not => ufSize 1 + 3
83
+ | .shl | .shr => ufSize 2 + 8
84
+ | .rotl | .rotr => ufSize 2 + 5
85
+ | .cmp _ => ufSize 2 + 8
86
+ | .select => ufSize 3 + 7
87
+ | .jmp _ => 1
88
+ | .branch _ => ufSize 1 + 4
89
+ | .call _ => 7
90
+ | .ret => 4
91
+ | .push _ => 7
92
+ | .pop _ => ufSize 1 + 3
93
+ | .dup => ufSize 1 + 7
94
+ | .drop => ufSize 1 + 1
95
+ | .swap => ufSize 2 + 4
96
+ | .over => ufSize 2 + 7
97
+ | .rot => ufSize 3 + 6
98
+ | .tor => ufSize 1 + 8
99
+ | .fromr => 12
100
+ | .rfetch => 11
101
+ | .halt => 1
102
+
103
+ /-- Code for one IR instruction placed at x86 index `base`; `off t` is the x86 index of IR
104
+ instruction `t` (clamped to the bad-pc stub for out-of-range targets). -/
105
+ def lowerInstr (L : Layout) (base : Nat) (off : Nat → Nat) : WordDialect.Instr 64 → List (Instr Nat)
106
+ | .word w => ovfD L base ++ [.movImm rax w, .addImm r15 (-8), .store r15 0 rax]
107
+ | .ptr p => ovfD L base ++ [.movImm rax p.toWord, .addImm r15 (-8), .store r15 0 rax]
108
+ | .load =>
109
+ let b0 := base + ufSize 1
110
+ uf L 1 base ++
111
+ [.load rax r15 0, .movImm rcx (BitVec.ofNat 64 L.memSize), .cmp rax rcx,
112
+ .jcc .b (b0 + 5), .exitTrap .badAddress, .loadIdx rax r13 rax, .store r15 0 rax]
113
+ | .store =>
114
+ let b0 := base + ufSize 2
115
+ uf L 2 base ++
116
+ [.load rax r15 0, .load rcx r15 8, .movImm rdx (BitVec.ofNat 64 L.memSize), .cmp rax rdx,
117
+ .jcc .b (b0 + 6), .exitTrap .badAddress, .storeIdx r13 rax rcx, .addImm r15 16]
118
+ | .add => uf L 2 base ++ [.load rcx r15 0, .load rax r15 8, .add rax rcx, .store r15 8 rax, .addImm r15 8]
119
+ | .sub => uf L 2 base ++ [.load rcx r15 0, .load rax r15 8, .sub rax rcx, .store r15 8 rax, .addImm r15 8]
120
+ | .mul => uf L 2 base ++ [.load rcx r15 0, .load rax r15 8, .imul rax rcx, .store r15 8 rax, .addImm r15 8]
121
+ | .and => uf L 2 base ++ [.load rcx r15 0, .load rax r15 8, .and rax rcx, .store r15 8 rax, .addImm r15 8]
122
+ | .or => uf L 2 base ++ [.load rcx r15 0, .load rax r15 8, .or rax rcx, .store r15 8 rax, .addImm r15 8]
123
+ | .xor => uf L 2 base ++ [.load rcx r15 0, .load rax r15 8, .xor rax rcx, .store r15 8 rax, .addImm r15 8]
124
+ | .div =>
125
+ let b0 := base + ufSize 2
126
+ uf L 2 base ++
127
+ [.load rcx r15 0, .load rax r15 8, .test rcx rcx, .jcc .ne (b0 + 5),
128
+ .exitTrap .divideByZero, .movImm rdx 0#64, .div rcx, .store r15 8 rax, .addImm r15 8]
129
+ | .sdiv =>
130
+ let b0 := base + ufSize 2
131
+ uf L 2 base ++
132
+ [.load rcx r15 0, .load rax r15 8, .test rcx rcx, .jcc .ne (b0 + 5),
133
+ .exitTrap .divideByZero, .cmpImm rcx (-1), .jcc .ne (b0 + 9), .neg rax, .jmp (b0 + 11),
134
+ .cqo, .idiv rcx, .store r15 8 rax, .addImm r15 8]
135
+ | .not => uf L 1 base ++ [.load rax r15 0, .not rax, .store r15 0 rax]
136
+ | .shl =>
137
+ uf L 2 base ++
138
+ [.load rcx r15 0, .load rax r15 8, .movImm rdx 0#64, .shlCl rax, .cmpImm rcx 64,
139
+ .cmov .ae rax rdx, .store r15 8 rax, .addImm r15 8]
140
+ | .shr =>
141
+ uf L 2 base ++
142
+ [.load rcx r15 0, .load rax r15 8, .movImm rdx 0#64, .shrCl rax, .cmpImm rcx 64,
143
+ .cmov .ae rax rdx, .store r15 8 rax, .addImm r15 8]
144
+ | .rotl => uf L 2 base ++ [.load rcx r15 0, .load rax r15 8, .rolCl rax, .store r15 8 rax, .addImm r15 8]
145
+ | .rotr => uf L 2 base ++ [.load rcx r15 0, .load rax r15 8, .rorCl rax, .store r15 8 rax, .addImm r15 8]
146
+ | .cmp c =>
147
+ uf L 2 base ++
148
+ [.load rcx r15 0, .load rax r15 8, .movImm rdx 0#64, .movImm rbx 1#64, .cmp rax rcx,
149
+ .cmov (ccOf c) rdx rbx, .store r15 8 rdx, .addImm r15 8]
150
+ | .select =>
151
+ uf L 3 base ++
152
+ [.load rcx r15 0, .load rax r15 8, .load rbx r15 16, .test rcx rcx, .cmov .ne rax rbx,
153
+ .store r15 16 rax, .addImm r15 16]
154
+ | .jmp t => [.jmp (off t)]
155
+ | .branch t => uf L 1 base ++ [.load rax r15 0, .addImm r15 8, .test rax rax, .jcc .ne (off t)]
156
+ | .call t => ovfR L base ++ [.call (off t)]
157
+ | .ret =>
158
+ [.cmp rsp r12, .jcc .ne (base + 3), .exitTrap .returnUnderflow, .ret]
159
+ | .push r => ovfD L base ++ [.load rax r14 (8 * (r : Int)), .addImm r15 (-8), .store r15 0 rax]
160
+ | .pop r => uf L 1 base ++ [.load rax r15 0, .addImm r15 8, .store r14 (8 * (r : Int)) rax]
161
+ | .dup =>
162
+ uf L 1 base ++ ovfD L (base + ufSize 1) ++ [.load rax r15 0, .addImm r15 (-8), .store r15 0 rax]
163
+ | .drop => uf L 1 base ++ [.addImm r15 8]
164
+ | .swap => uf L 2 base ++ [.load rax r15 0, .load rcx r15 8, .store r15 0 rcx, .store r15 8 rax]
165
+ | .over =>
166
+ uf L 2 base ++ ovfD L (base + ufSize 2) ++ [.load rax r15 8, .addImm r15 (-8), .store r15 0 rax]
167
+ | .rot =>
168
+ uf L 3 base ++
169
+ [.load rax r15 0, .load rcx r15 8, .load rdx r15 16, .store r15 0 rdx, .store r15 8 rax,
170
+ .store r15 16 rcx]
171
+ | .tor =>
172
+ uf L 1 base ++ ovfA L (base + ufSize 1) ++
173
+ [.load rax r15 0, .addImm r15 8, .addImm rbp (-8), .store rbp 0 rax]
174
+ | .fromr =>
175
+ emptyA L base ++ ovfD L (base + 4) ++
176
+ [.load rax rbp 0, .addImm rbp 8, .addImm r15 (-8), .store r15 0 rax]
177
+ | .rfetch =>
178
+ emptyA L base ++ ovfD L (base + 4) ++ [.load rax rbp 0, .addImm r15 (-8), .store r15 0 rax]
179
+ | .halt => [.exitHalt]
180
+
181
+ theorem lowerInstr_length (L : Layout) (base : Nat) (off : Nat → Nat) (i : WordDialect.Instr 64) :
182
+ (lowerInstr L base off i).length = isize i := by
183
+ cases i <;> simp [lowerInstr, isize, uf, ufSize, ovfD, ovfR, ovfA, emptyA] <;> (try split) <;> simp
184
+
185
+ /-- Prefix sums of instruction sizes: `offs p k` is the x86 index of IR instruction `k`.
186
+ For `k` past the end it is the index of the final bad-pc stub. -/
187
+ def offs (p : Prog 64) (k : Nat) : Nat := ((p.take k).map isize).sum
188
+
189
+ def lowerGo (L : Layout) (off : Nat → Nat) : Nat → List (WordDialect.Instr 64) → List (Instr Nat)
190
+ | _, [] => [.exitTrap .badPc]
191
+ | b, i :: is => lowerInstr L b off i ++ lowerGo L off (b + isize i) is
192
+
193
+ /-- The whole program; the last instruction is the bad-pc stub, so jumping past the end of the
194
+ IR program traps `badPc` exactly as the IR semantics does. -/
195
+ def lowerProg (L : Layout) (p : Prog 64) : List (Instr Nat) := lowerGo L (offs p) 0 p
196
+
197
+ end X86
198
+ end WordDialect
backend/x86_64/X86/Micro.lean ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.Seq
2
+
3
+ /-!
4
+ # X86.Micro
5
+
6
+ Micro-lemmas: how single x86 instructions (and the push pair) act on a machine state related to
7
+ an IR state by `Rel0`. Every IR-instruction simulation proof is a chain of these.
8
+ -/
9
+
10
+ namespace WordDialect
11
+ namespace X86
12
+
13
+ open Reg
14
+
15
+ theorem ea_nat (a j : Nat) : ofN a + BitVec.ofInt 64 (8 * (j : Int)) = ofN (a + 8 * j) := by
16
+ rw [show (8 * (j : Int)) = ((8 * j : Nat) : Int) by omega, BitVec.ofInt_natCast]
17
+ exact ofN_add _ _
18
+
19
+ /-- Load stack slot `j` into a scratch register. -/
20
+ theorem ld_stack {c : Cfg} {p : Prog 64} {s : State 64} {x : M} (hs : Rel0 c p s x)
21
+ {j : Nat} {v : W} (hj : s.dstack[j]? = some v) (rd : Reg) {disp : Int}
22
+ (hd : disp = 8 * (j : Int)) :
23
+ exec (.load rd Reg.r15 disp) x = .next (x.mov rd v) := by
24
+ have hread := hs.stack j v hj
25
+ simp only [exec, M.ea, hs.r15, hd, ea_nat]
26
+ rw [hread]
27
+
28
+ theorem mov_rel0 {c : Cfg} {p : Prog 64} {s : State 64} {x : M} (hs : Rel0 c p s x) (rd : Reg)
29
+ (hr : rd ≠ r15 ∧ rd ≠ r14 ∧ rd ≠ r13 ∧ rd ≠ r12 ∧ rd ≠ rsp ∧ rd ≠ rbp) (v : W) :
30
+ Rel0 c p s (x.mov rd v) := by
31
+ obtain ⟨h1, h2, h3, h4, h5, h6⟩ := hr
32
+ refine hs.congr rfl ?_ ?_ ?_ ?_ ?_ ?_ <;>
33
+ simp [M.mov, M.setReg, M.adv, Ne.symm h1, Ne.symm h2, Ne.symm h3, Ne.symm h4, Ne.symm h5,
34
+ Ne.symm h6]
35
+
36
+ theorem arith_rel0 {c : Cfg} {p : Prog 64} {s : State 64} {x : M} (hs : Rel0 c p s x) (rd : Reg)
37
+ (hr : rd ≠ r15 ∧ rd ≠ r14 ∧ rd ≠ r13 ∧ rd ≠ r12 ∧ rd ≠ rsp ∧ rd ≠ rbp) (v : W) :
38
+ Rel0 c p s (x.arith rd v) := by
39
+ obtain ⟨h1, h2, h3, h4, h5, h6⟩ := hr
40
+ refine hs.congr rfl ?_ ?_ ?_ ?_ ?_ ?_ <;>
41
+ simp [M.arith, M.setReg, Ne.symm h1, Ne.symm h2, Ne.symm h3, Ne.symm h4, Ne.symm h5,
42
+ Ne.symm h6]
43
+
44
+ theorem flags_rel0 {c : Cfg} {p : Prog 64} {s : State 64} {x : M} (hs : Rel0 c p s x)
45
+ (f : Option Flags) (q : Nat) : Rel0 c p s { x with flags := f, pc := q } :=
46
+ hs.congr rfl rfl rfl rfl rfl rfl rfl
47
+
48
+ /-- Store a register into stack slot `j`: the relation holds for the stack with that slot replaced. -/
49
+ theorem st_stack {c : Cfg} {p : Prog 64} {s : State 64} {x : M} (hg : Geom c) (hs : Rel0 c p s x)
50
+ {j : Nat} (hj : j < s.dstack.length) (rs : Reg) {disp : Int} (hd : disp = 8 * (j : Int)) :
51
+ ∃ mem', exec (.store Reg.r15 disp rs) x = .next { x.adv with mem := mem' } ∧
52
+ Rel0 c p { s with dstack := s.dstack.set j (x.regs rs) } { x.adv with mem := mem' } := by
53
+ have hcap := hs.capD
54
+ have hcapR := hs.capR
55
+ have hdE := hg.dEnd_lt
56
+ have hA : c.dBase ≤ c.dEnd - 8 * s.dstack.length + 8 * j := by omega
57
+ have hA' : c.dEnd - 8 * s.dstack.length + 8 * j < c.dEnd := by omega
58
+ have hvalid := hs.xvalid.1 _ hA hA'
59
+ obtain ⟨mem', hw⟩ : ∃ m', x.mem.write? (ofN (c.dEnd - 8 * s.dstack.length + 8 * j)) (x.regs rs)
60
+ = some m' := by simp [Memory.write?, hvalid]
61
+ refine ⟨mem', ?_, ?_⟩
62
+ · simp only [exec, M.ea, hs.r15, hd, ea_nat]; rw [hw]
63
+ · have hlen : (s.dstack.set j (x.regs rs)).length = s.dstack.length := by simp
64
+ have hAlt : c.dEnd - 8 * s.dstack.length + 8 * j < 2 ^ 64 := by omega
65
+ refine { r15 := ?_, r14 := ?_, r13 := ?_, r12 := ?_, rsp := ?_, rbp := ?_, stack := ?_,
66
+ regs := ?_, mem := ?_, rs := ?_, aux := ?_, irvalid := hs.irvalid, xvalid := ?_,
67
+ capD := ?_, capR := hs.capR, capA := hs.capA }
68
+ · simpa [M.adv, hlen] using hs.r15
69
+ · simpa [M.adv] using hs.r14
70
+ · simpa [M.adv] using hs.r13
71
+ · simpa [M.adv] using hs.r12
72
+ · simpa [M.adv] using hs.rsp
73
+ · simpa [M.adv] using hs.rbp
74
+ · intro i w hi
75
+ simp only [hlen]
76
+ have hik : i < s.dstack.length := by
77
+ have := (List.getElem?_eq_some_iff.mp hi).1; simpa using this
78
+ by_cases hij : i = j
79
+ · subst hij
80
+ have h1 : (s.dstack.set i (x.regs rs))[i]? = some (x.regs rs) := by simp [hj]
81
+ rw [h1] at hi
82
+ cases hi
83
+ rw [read_write_nat hw hAlt hAlt]; simp
84
+ · rw [List.getElem?_set_ne (Ne.symm hij)] at hi
85
+ have hne : c.dEnd - 8 * s.dstack.length + 8 * j ≠ c.dEnd - 8 * s.dstack.length + 8 * i := by
86
+ omega
87
+ rw [read_write_nat hw hAlt (by omega)]
88
+ simp only [hne, ite_false]
89
+ exact hs.stack i w hi
90
+ · intro r hr
91
+ have hrf := hg.rf_lt
92
+ have hne : c.dEnd - 8 * s.dstack.length + 8 * j ≠ c.rf + 8 * r := by
93
+ rcases hg.dS_F with h | h <;> omega
94
+ rw [read_write_nat hw hAlt (by omega)]
95
+ simp only [hne, ite_false]
96
+ exact hs.regs r hr
97
+ · intro a ha
98
+ have hmb := hg.mb_lt
99
+ have hne : c.dEnd - 8 * s.dstack.length + 8 * j ≠ c.mb + 8 * a := by
100
+ rcases hg.dS_M with h | h <;> omega
101
+ rw [read_write_nat hw hAlt (by omega)]
102
+ simp only [hne, ite_false]
103
+ exact hs.mem a ha
104
+ · intro i a hi
105
+ have hsp := hg.sp0_lt
106
+ have hrc := hg.rcap_le
107
+ have hi' : i < s.rstack.length := by
108
+ by_cases hh : i < s.rstack.length
109
+ · exact hh
110
+ · have hn : s.rstack.length ≤ i := by omega
111
+ rw [List.getElem?_eq_none hn] at hi; simp at hi
112
+ have hne : c.dEnd - 8 * s.dstack.length + 8 * j ≠
113
+ c.sp0 - 8 * s.rstack.length + 8 * i := by
114
+ rcases hg.dS_K with h | h <;> omega
115
+ have hkr : 8 * s.rstack.length ≤ c.sp0 := by omega
116
+ have hB : c.sp0 - 8 * s.rstack.length + 8 * i < 2 ^ 64 := by omega
117
+ rw [read_write_nat hw hAlt hB]
118
+ simp only [hne, ite_false]
119
+ exact hs.rs i a hi
120
+ · exact aux_write_other hg.aEnd_lt hs.capA hs.aux hw hAlt (by rcases hg.dS_A with h | h <;> omega)
121
+ · have := Memory.write?_valid hw
122
+ show RegionsValid c mem'.valid
123
+ rw [this]; exact hs.xvalid
124
+ · simpa [hlen] using hcap
125
+
126
+ /-- Adjust the stack pointer upward by `n` words (popping `n` elements). -/
127
+ theorem adj_drop {c : Cfg} {p : Prog 64} {s : State 64} {x : M} (hg : Geom c) (hs : Rel0 c p s x)
128
+ {n : Nat} (hn : n ≤ s.dstack.length) {δ : Int} (hd : δ = 8 * (n : Int)) :
129
+ exec (.addImm Reg.r15 δ) x = .next (x.arith Reg.r15 (ofN (c.dEnd - 8 * (s.dstack.length - n)))) ∧
130
+ Rel0 c p { s with dstack := s.dstack.drop n }
131
+ (x.arith Reg.r15 (ofN (c.dEnd - 8 * (s.dstack.length - n)))) := by
132
+ have hcap := hs.capD
133
+ have hval : x.regs Reg.r15 + BitVec.ofInt 64 δ = ofN (c.dEnd - 8 * (s.dstack.length - n)) := by
134
+ rw [hs.r15, hd, ea_nat]; congr 1; omega
135
+ refine ⟨by simp only [exec, hval], ?_⟩
136
+ refine { r15 := ?_, r14 := ?_, r13 := ?_, r12 := ?_, rsp := ?_, rbp := ?_, stack := ?_,
137
+ regs := ?_, mem := ?_, rs := ?_, aux := hs.aux, irvalid := hs.irvalid, xvalid := ?_,
138
+ capD := ?_, capR := hs.capR, capA := hs.capA }
139
+ · simp [M.arith, M.setReg]
140
+ · simpa [M.arith, M.setReg] using hs.r14
141
+ · simpa [M.arith, M.setReg] using hs.r13
142
+ · simpa [M.arith, M.setReg] using hs.r12
143
+ · simpa [M.arith, M.setReg] using hs.rsp
144
+ · simpa [M.arith, M.setReg] using hs.rbp
145
+ · intro i w hi
146
+ simp only [List.length_drop]
147
+ rw [List.getElem?_drop] at hi
148
+ have := hs.stack (n + i) w hi
149
+ have e : c.dEnd - 8 * (s.dstack.length - n) + 8 * i =
150
+ c.dEnd - 8 * s.dstack.length + 8 * (n + i) := by omega
151
+ simp only [M.arith, M.setReg]
152
+ rw [e]; exact this
153
+ · exact hs.regs
154
+ · exact hs.mem
155
+ · exact hs.rs
156
+ · exact hs.xvalid
157
+ · simp only [List.length_drop]; omega
158
+
159
+ theorem ea_neg8 {a : Nat} (h : 8 ≤ a) : ofN a + BitVec.ofInt 64 (-8) = ofN (a - 8) := by
160
+ have : BitVec.ofInt 64 (-8) = -(ofN 8) := by
161
+ rw [show (-8 : Int) = -((8 : Nat) : Int) by omega, BitVec.ofInt_neg, BitVec.ofInt_natCast]
162
+ rfl
163
+ rw [this, ← BitVec.sub_eq_add_neg, ofN_sub h]
164
+
165
+ /-- A write inside the data-stack region leaves the register-file, IR-memory and return-stack
166
+ views unchanged. -/
167
+ theorem others_write {c : Cfg} {p : Prog 64} {s : State 64} {x : M} (hg : Geom c)
168
+ (hs : Rel0 c p s x) {A : Nat} {v : W} {mem' : Memory 64} (hA1 : c.dBase ≤ A) (hA2 : A < c.dEnd)
169
+ (hw : x.mem.write? (ofN A) v = some mem') :
170
+ RegRel c mem' s.regs ∧ MemRel c mem' s.mem ∧ RsRel c p mem' s.rstack ∧
171
+ AuxRel c mem' s.astack := by
172
+ have hdE := hg.dEnd_lt
173
+ have hAlt : A < 2 ^ 64 := by omega
174
+ have hcapR := hs.capR
175
+ have hrc := hg.rcap_le
176
+ have hsp := hg.sp0_lt
177
+ refine ⟨?_, ?_, ?_, aux_write_other hg.aEnd_lt hs.capA hs.aux hw hAlt
178
+ (by rcases hg.dS_A with h | h <;> omega)⟩
179
+ · intro r hr
180
+ have hrf := hg.rf_lt
181
+ have hne : A ≠ c.rf + 8 * r := by rcases hg.dS_F with h | h <;> omega
182
+ rw [read_write_nat hw hAlt (by omega)]
183
+ simp only [hne, ite_false]
184
+ exact hs.regs r hr
185
+ · intro a ha
186
+ have hmb := hg.mb_lt
187
+ have hne : A ≠ c.mb + 8 * a := by rcases hg.dS_M with h | h <;> omega
188
+ rw [read_write_nat hw hAlt (by omega)]
189
+ simp only [hne, ite_false]
190
+ exact hs.mem a ha
191
+ · intro i a hi
192
+ have hi' : i < s.rstack.length := by
193
+ by_cases hh : i < s.rstack.length
194
+ · exact hh
195
+ · have hn : s.rstack.length ≤ i := by omega
196
+ rw [List.getElem?_eq_none hn] at hi; simp at hi
197
+ have hkr : 8 * s.rstack.length ≤ c.sp0 := by omega
198
+ have hB : c.sp0 - 8 * s.rstack.length + 8 * i < 2 ^ 64 := by omega
199
+ have hne : A ≠ c.sp0 - 8 * s.rstack.length + 8 * i := by rcases hg.dS_K with h | h <;> omega
200
+ rw [read_write_nat hw hAlt hB]
201
+ simp only [hne, ite_false]
202
+ exact hs.rs i a hi
203
+
204
+ /-- `addImm r15, -8; store [r15], rs` pushes the value of `rs`. -/
205
+ theorem push_pair {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)}
206
+ (hg : Geom c) (hs : Rel0 c p s x) (rs : Reg) (hrs : rs ≠ r15)
207
+ (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd)
208
+ (hat : XAt code x.pc [.addImm Reg.r15 (-8), .store Reg.r15 0 rs]) :
209
+ ∃ x', XSteps code x x' ∧
210
+ Rel0 c p { s with dstack := x.regs rs :: s.dstack } x' ∧ x'.pc = x.pc + 2 ∧
211
+ (∀ r, r ≠ r15 → x'.regs r = x.regs r) := by
212
+ obtain ⟨h1, h2, _⟩ := hat
213
+ have hcap := hs.capD
214
+ have hdE := hg.dEnd_lt
215
+ have hA1 : c.dBase ≤ c.dEnd - 8 * (s.dstack.length + 1) := by omega
216
+ have hA2 : c.dEnd - 8 * (s.dstack.length + 1) < c.dEnd := by omega
217
+ have hAlt : c.dEnd - 8 * (s.dstack.length + 1) < 2 ^ 64 := by omega
218
+ -- step 1: r15 := r15 - 8
219
+ have hval : x.regs Reg.r15 + BitVec.ofInt 64 (-8) = ofN (c.dEnd - 8 * (s.dstack.length + 1)) := by
220
+ rw [hs.r15, ea_neg8 (by omega)]
221
+ congr 1
222
+ have e1 : exec (.addImm Reg.r15 (-8)) x = .next (x.arith Reg.r15 (ofN (c.dEnd - 8 * (s.dstack.length + 1)))) := by
223
+ simp only [exec, hval]
224
+ obtain ⟨x1, hx1⟩ : ∃ x1, x1 = x.arith Reg.r15 (ofN (c.dEnd - 8 * (s.dstack.length + 1))) := ⟨_, rfl⟩
225
+ rw [← hx1] at e1
226
+ have hst1 : step code x = .next x1 := by rw [xstep_of_fetch h1, e1]
227
+ -- step 2: store [r15], rs
228
+ have hr15 : x1.regs Reg.r15 = ofN (c.dEnd - 8 * (s.dstack.length + 1)) := by simp [hx1, M.arith, M.setReg]
229
+ have hrsv : x1.regs rs = x.regs rs := by simp [hx1, M.arith, M.setReg, hrs]
230
+ have hvalid := hs.xvalid.1 _ hA1 hA2
231
+ obtain ⟨mem', hw⟩ : ∃ m', x.mem.write? (ofN (c.dEnd - 8 * (s.dstack.length + 1))) (x.regs rs) = some m' := by
232
+ simp [Memory.write?, hvalid]
233
+ have e2 : exec (.store Reg.r15 0 rs) x1 = .next { x1.adv with mem := mem' } := by
234
+ have : x1.mem = x.mem := by simp [hx1, M.arith, M.setReg]
235
+ simp only [exec, M.ea, hr15, hrsv, this]
236
+ have h0 : ofN (c.dEnd - 8 * (s.dstack.length + 1)) + BitVec.ofInt 64 0 = ofN (c.dEnd - 8 * (s.dstack.length + 1)) := by
237
+ simp
238
+ rw [h0, hw]
239
+ have hpc1 : x1.pc = x.pc + 1 := by simp [hx1, M.arith, M.setReg]
240
+ have hst2 : step code x1 = .next { x1.adv with mem := mem' } := by
241
+ rw [xstep_of_fetch (m := x1) (by rw [hpc1]; exact h2), e2]
242
+ refine ⟨{ x1.adv with mem := mem' }, (XSteps.single hst1).trans (XSteps.single hst2), ?_, ?_, ?_⟩
243
+ · have ⟨hrg, hmm, hrr, hax⟩ := others_write hg hs hA1 hA2 hw
244
+ have hlen : (x.regs rs :: s.dstack).length = s.dstack.length + 1 := by simp
245
+ refine { r15 := ?_, r14 := ?_, r13 := ?_, r12 := ?_, rsp := ?_, rbp := ?_, stack := ?_,
246
+ regs := hrg, mem := hmm, rs := hrr, aux := hax, irvalid := hs.irvalid, xvalid := ?_,
247
+ capD := ?_, capR := hs.capR, capA := hs.capA }
248
+ · simpa [hlen, M.adv, hx1, M.arith, M.setReg] using rfl
249
+ · simpa [M.adv, hx1, M.arith, M.setReg] using hs.r14
250
+ · simpa [M.adv, hx1, M.arith, M.setReg] using hs.r13
251
+ · simpa [M.adv, hx1, M.arith, M.setReg] using hs.r12
252
+ · simpa [M.adv, hx1, M.arith, M.setReg] using hs.rsp
253
+ · simpa [M.adv, hx1, M.arith, M.setReg] using hs.rbp
254
+ · intro i w hi
255
+ simp only [hlen]
256
+ cases i with
257
+ | zero =>
258
+ simp at hi; subst hi
259
+ rw [show c.dEnd - 8 * (s.dstack.length + 1) + 8 * 0 = c.dEnd - 8 * (s.dstack.length + 1) by omega,
260
+ read_write_nat hw hAlt hAlt]; simp
261
+ | succ i =>
262
+ simp only [List.getElem?_cons_succ] at hi
263
+ have hik : i < s.dstack.length := by
264
+ have := (List.getElem?_eq_some_iff.mp hi).1; simpa using this
265
+ have hB : c.dEnd - 8 * (s.dstack.length + 1) + 8 * (i + 1) < 2 ^ 64 := by omega
266
+ have hne : c.dEnd - 8 * (s.dstack.length + 1) ≠ c.dEnd - 8 * (s.dstack.length + 1) + 8 * (i + 1) := by omega
267
+ rw [read_write_nat hw hAlt hB]
268
+ simp only [hne, ite_false]
269
+ have e : c.dEnd - 8 * (s.dstack.length + 1) + 8 * (i + 1) = c.dEnd - 8 * s.dstack.length + 8 * i := by omega
270
+ rw [e]; exact hs.stack i w hi
271
+ · have := Memory.write?_valid hw
272
+ show RegionsValid c mem'.valid
273
+ rw [this]; exact hs.xvalid
274
+ · simpa [hlen] using hfit
275
+ · simp [M.adv, hpc1]
276
+ · intro r hr
277
+ simp [M.adv, hx1, M.arith, M.setReg, hr]
278
+
279
+ end X86
280
+ end WordDialect
backend/x86_64/X86/Rel.lean ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.Run
2
+
3
+ /-!
4
+ # X86.Rel
5
+
6
+ The state relation between the IR machine and the x86 model, and the memory-frame lemmas used
7
+ by the per-instruction simulation proofs.
8
+
9
+ Memory regions (byte intervals, all below `2^64`, pairwise disjoint):
10
+ * data stack `[dBase, dEnd)`, top at `dEnd - 8k` when `k` words are on the stack;
11
+ * register file `[rf, rf + 8 nregs)`;
12
+ * IR memory `[mb, mb + 8 M)`;
13
+ * return stack: the native stack below `sp0`;
14
+ * auxiliary stack `[aBase, aEnd)`, top at `aEnd - 8k` when `k` words are on it (pointer `rbp`).
15
+
16
+ Everything is stated with `BitVec.ofNat 64` of natural-number addresses, so address arithmetic
17
+ is ordinary `omega` reasoning.
18
+ -/
19
+
20
+ namespace WordDialect
21
+ namespace X86
22
+
23
+ open Reg
24
+
25
+ structure Cfg where
26
+ dEnd : Nat
27
+ dBase : Nat
28
+ rf : Nat
29
+ nregs : Nat
30
+ mb : Nat
31
+ M : Nat
32
+ sp0 : Nat
33
+ rcap : Nat
34
+ aEnd : Nat
35
+ aBase : Nat
36
+
37
+ structure Geom (c : Cfg) : Prop where
38
+ dEnd_lt : c.dEnd < 2 ^ 64
39
+ rf_lt : c.rf + 8 * c.nregs ≤ 2 ^ 64
40
+ mb_lt : c.mb + 8 * c.M ≤ 2 ^ 64
41
+ sp0_lt : c.sp0 < 2 ^ 64
42
+ dBase_le : c.dBase ≤ c.dEnd
43
+ dEnd_ge : 24 ≤ c.dEnd
44
+ rcap_le : 8 * c.rcap ≤ c.sp0
45
+ dBase_8 : c.dBase + 8 ≤ c.dEnd
46
+ rcap_ge : 2 ≤ c.rcap
47
+ dS_F : c.dEnd ≤ c.rf ∨ c.rf + 8 * c.nregs ≤ c.dBase
48
+ dS_M : c.dEnd ≤ c.mb ∨ c.mb + 8 * c.M ≤ c.dBase
49
+ dS_K : c.dEnd ≤ c.sp0 - 8 * c.rcap ∨ c.sp0 ≤ c.dBase
50
+ dF_M : c.rf + 8 * c.nregs ≤ c.mb ∨ c.mb + 8 * c.M ≤ c.rf
51
+ dF_K : c.rf + 8 * c.nregs ≤ c.sp0 - 8 * c.rcap ∨ c.sp0 ≤ c.rf
52
+ dM_K : c.mb + 8 * c.M ≤ c.sp0 - 8 * c.rcap ∨ c.sp0 ≤ c.mb
53
+ aEnd_lt : c.aEnd < 2 ^ 64
54
+ aBase_8 : c.aBase + 8 ≤ c.aEnd
55
+ dS_A : c.dEnd ≤ c.aBase ∨ c.aEnd ≤ c.dBase
56
+ dA_F : c.aEnd ≤ c.rf ∨ c.rf + 8 * c.nregs ≤ c.aBase
57
+ dA_M : c.aEnd ≤ c.mb ∨ c.mb + 8 * c.M ≤ c.aBase
58
+ dA_K : c.aEnd ≤ c.sp0 - 8 * c.rcap ∨ c.sp0 ≤ c.aBase
59
+
60
+ def ofN (a : Nat) : W := BitVec.ofNat 64 a
61
+
62
+ theorem ofN_inj {a b : Nat} (ha : a < 2 ^ 64) (hb : b < 2 ^ 64) (h : ofN a = ofN b) : a = b := by
63
+ have := congrArg BitVec.toNat h
64
+ simpa [ofN, BitVec.toNat_ofNat, Nat.mod_eq_of_lt ha, Nat.mod_eq_of_lt hb] using this
65
+
66
+ theorem ofN_add (a b : Nat) : ofN a + ofN b = ofN (a + b) := (BitVec.ofNat_add a b).symm
67
+
68
+ theorem ofN_sub {a b : Nat} (h : b ≤ a) : ofN a - ofN b = ofN (a - b) := by
69
+ apply BitVec.eq_of_toNat_eq
70
+ simp only [ofN, BitVec.toNat_sub, BitVec.toNat_ofNat]
71
+ have h1 : (a - b) % 2 ^ 64 = ((2 ^ 64 - b % 2 ^ 64) + a % 2 ^ 64) % 2 ^ 64 := by
72
+ omega
73
+ exact h1.symm
74
+
75
+ /-- Reading after a write at a natural-number address. -/
76
+ theorem read_write_nat {mem mem' : Memory 64} {A B : Nat} {v : W}
77
+ (hw : mem.write? (ofN A) v = some mem') (hA : A < 2 ^ 64) (hB : B < 2 ^ 64) :
78
+ mem'.read? (ofN B) = if A = B then some v else mem.read? (ofN B) := by
79
+ by_cases h : A = B
80
+ · subst h; simp [Memory.read_write_same hw]
81
+ · have hne : ofN B ≠ ofN A := by
82
+ intro e; exact h (ofN_inj hA hB e.symm)
83
+ rw [Memory.read_write_other hw hne]; simp [h]
84
+
85
+ def StackRel (c : Cfg) (mem : Memory 64) (d : List W) : Prop :=
86
+ ∀ i v, d[i]? = some v → mem.read? (ofN (c.dEnd - 8 * d.length + 8 * i)) = some v
87
+
88
+ def AuxRel (c : Cfg) (mem : Memory 64) (as : List W) : Prop :=
89
+ ∀ i v, as[i]? = some v → mem.read? (ofN (c.aEnd - 8 * as.length + 8 * i)) = some v
90
+
91
+ def RegRel (c : Cfg) (mem : Memory 64) (regs : Nat → W) : Prop :=
92
+ ∀ r, r < c.nregs → mem.read? (ofN (c.rf + 8 * r)) = some (regs r)
93
+
94
+ def MemRel (c : Cfg) (mem : Memory 64) (m : Memory 64) : Prop :=
95
+ ∀ a, a < c.M → mem.read? (ofN (c.mb + 8 * a)) = some (m.cell (ofN a))
96
+
97
+ def RsRel (c : Cfg) (p : Prog 64) (mem : Memory 64) (rs : List Nat) : Prop :=
98
+ ∀ i a, rs[i]? = some a → mem.read? (ofN (c.sp0 - 8 * rs.length + 8 * i)) = some (ofN (offs p a))
99
+
100
+ /-- The x86 memory's addressable set covers every region. -/
101
+ def RegionsValid (c : Cfg) (v : W → Bool) : Prop :=
102
+ (∀ a, c.dBase ≤ a → a < c.dEnd → v (ofN a) = true) ∧
103
+ (∀ a, c.rf ≤ a → a < c.rf + 8 * c.nregs → v (ofN a) = true) ∧
104
+ (∀ a, c.mb ≤ a → a < c.mb + 8 * c.M → v (ofN a) = true) ∧
105
+ (∀ a, c.sp0 - 8 * c.rcap ≤ a → a < c.sp0 → v (ofN a) = true) ∧
106
+ (∀ a, c.aBase ≤ a → a < c.aEnd → v (ofN a) = true)
107
+
108
+ structure Rel0 (c : Cfg) (p : Prog 64) (s : State 64) (x : M) : Prop where
109
+ r15 : x.regs r15 = ofN (c.dEnd - 8 * s.dstack.length)
110
+ r14 : x.regs r14 = ofN c.rf
111
+ r13 : x.regs r13 = ofN c.mb
112
+ r12 : x.regs r12 = ofN c.sp0
113
+ rsp : x.regs rsp = ofN (c.sp0 - 8 * s.rstack.length)
114
+ rbp : x.regs rbp = ofN (c.aEnd - 8 * s.astack.length)
115
+ stack : StackRel c x.mem s.dstack
116
+ regs : RegRel c x.mem s.regs
117
+ mem : MemRel c x.mem s.mem
118
+ rs : RsRel c p x.mem s.rstack
119
+ aux : AuxRel c x.mem s.astack
120
+ irvalid : ∀ a : Word 64, s.mem.valid a = decide (a.toNat < c.M)
121
+ xvalid : RegionsValid c x.mem.valid
122
+ capD : c.dBase + 8 * s.dstack.length ≤ c.dEnd
123
+ capR : s.rstack.length + 1 ≤ c.rcap
124
+ capA : c.aBase + 8 * s.astack.length ≤ c.aEnd
125
+
126
+ /-- Full relation: everything in `Rel0`, and the x86 pc is the start of the IR pc's code. -/
127
+ def Rel (c : Cfg) (p : Prog 64) (s : State 64) (x : M) : Prop :=
128
+ Rel0 c p s x ∧ x.pc = offs p s.pc
129
+
130
+ /-- `Rel0` does not look at scratch registers, flags or the pc. -/
131
+ theorem Rel0.congr {c : Cfg} {p : Prog 64} {s : State 64} {x x' : M} (h : Rel0 c p s x)
132
+ (hm : x'.mem = x.mem) (h15 : x'.regs Reg.r15 = x.regs Reg.r15) (h14 : x'.regs Reg.r14 = x.regs Reg.r14)
133
+ (h13 : x'.regs Reg.r13 = x.regs Reg.r13) (h12 : x'.regs Reg.r12 = x.regs Reg.r12)
134
+ (hsp : x'.regs Reg.rsp = x.regs Reg.rsp) (hbp : x'.regs Reg.rbp = x.regs Reg.rbp) :
135
+ Rel0 c p s x' where
136
+ r15 := by rw [h15]; exact h.r15
137
+ r14 := by rw [h14]; exact h.r14
138
+ r13 := by rw [h13]; exact h.r13
139
+ r12 := by rw [h12]; exact h.r12
140
+ rsp := by rw [hsp]; exact h.rsp
141
+ rbp := by rw [hbp]; exact h.rbp
142
+ stack := by rw [hm]; exact h.stack
143
+ regs := by rw [hm]; exact h.regs
144
+ mem := by rw [hm]; exact h.mem
145
+ rs := by rw [hm]; exact h.rs
146
+ aux := by rw [hm]; exact h.aux
147
+ irvalid := h.irvalid
148
+ xvalid := by rw [hm]; exact h.xvalid
149
+ capD := h.capD
150
+ capR := h.capR
151
+ capA := h.capA
152
+
153
+ /-- A write outside the auxiliary-stack region leaves the auxiliary-stack view unchanged. -/
154
+ theorem aux_write_other {c : Cfg} {mem mem' : Memory 64} {as : List W} (haE : c.aEnd < 2 ^ 64)
155
+ (hcap : c.aBase + 8 * as.length ≤ c.aEnd) (h : AuxRel c mem as) {A : Nat} {v : W}
156
+ (hw : mem.write? (ofN A) v = some mem') (hAlt : A < 2 ^ 64) (hA : A < c.aBase ∨ c.aEnd ≤ A) :
157
+ AuxRel c mem' as := by
158
+ intro i w hi
159
+ have hik : i < as.length := by
160
+ by_cases hh : i < as.length
161
+ · exact hh
162
+ · rw [List.getElem?_eq_none (by omega)] at hi; simp at hi
163
+ have hB : c.aEnd - 8 * as.length + 8 * i < 2 ^ 64 := by omega
164
+ have hne : A ≠ c.aEnd - 8 * as.length + 8 * i := by omega
165
+ rw [read_write_nat hw hAlt hB]
166
+ simp only [hne, ite_false]
167
+ exact h i w hi
168
+
169
+ end X86
170
+ end WordDialect
backend/x86_64/X86/Run.lean ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.Flags
2
+
3
+ /-!
4
+ # X86.Run
5
+
6
+ Run relations for the x86 model (`XSteps`, `XExec`), code placement (`XAt`), and the lemma
7
+ locating each IR instruction's lowered code inside `lowerProg`.
8
+ -/
9
+
10
+ namespace WordDialect
11
+ namespace X86
12
+
13
+ inductive XSteps (code : List (Instr Nat)) : M → M → Prop where
14
+ | refl {m : M} : XSteps code m m
15
+ | cons {m m1 m2 : M} : step code m = .next m1 → XSteps code m1 m2 → XSteps code m m2
16
+
17
+ theorem XSteps.single {code : List (Instr Nat)} {m m1 : M} (h : step code m = .next m1) :
18
+ XSteps code m m1 := .cons h .refl
19
+
20
+ theorem XSteps.trans {code : List (Instr Nat)} {m m1 m2 : M}
21
+ (h₁ : XSteps code m m1) (h₂ : XSteps code m1 m2) : XSteps code m m2 := by
22
+ induction h₁ with
23
+ | refl => exact h₂
24
+ | cons hs _ ih => exact .cons hs (ih h₂)
25
+
26
+ /-- Running to a final outcome. -/
27
+ inductive XExec (code : List (Instr Nat)) : M → Out → Prop where
28
+ | halt {m m' : M} : step code m = .halted m' → XExec code m (.halted m')
29
+ | trap {m : M} {t : Trap} : step code m = .trapped t → XExec code m (.trapped t)
30
+ | ovf {m : M} : step code m = .overflow → XExec code m .overflow
31
+ | next {m m1 : M} {o : Out} : step code m = .next m1 → XExec code m1 o → XExec code m o
32
+
33
+ theorem XExec.of_steps {code : List (Instr Nat)} {m m1 : M} {o : Out}
34
+ (hs : XSteps code m m1) (he : XExec code m1 o) : XExec code m o := by
35
+ induction hs with
36
+ | refl => exact he
37
+ | cons h _ ih => exact .next h (ih he)
38
+
39
+ /-- `seg` occurs in `code` starting at index `pc`. -/
40
+ def XAt : List (Instr Nat) → Nat → List (Instr Nat) → Prop
41
+ | _, _, [] => True
42
+ | code, pc, i :: is => code[pc]? = some i ∧ XAt code (pc + 1) is
43
+
44
+ theorem xat_append {code : List (Instr Nat)} {pc : Nat} {c₁ c₂ : List (Instr Nat)} :
45
+ XAt code pc (c₁ ++ c₂) ↔ XAt code pc c₁ ∧ XAt code (pc + c₁.length) c₂ := by
46
+ induction c₁ generalizing pc with
47
+ | nil => simp [XAt]
48
+ | cons i is ih =>
49
+ simp only [List.cons_append, XAt, ih, List.length_cons]
50
+ constructor
51
+ · rintro ⟨h, h1, h2⟩; refine ⟨⟨h, h1⟩, ?_⟩
52
+ have e : pc + 1 + is.length = pc + (is.length + 1) := by omega
53
+ rw [← e]; exact h2
54
+ · rintro ⟨⟨h, h1⟩, h2⟩; refine ⟨h, h1, ?_⟩
55
+ have e : pc + 1 + is.length = pc + (is.length + 1) := by omega
56
+ rw [e]; exact h2
57
+
58
+ theorem fetch_cast {code : List (Instr Nat)} {i : Instr Nat} {a b : Nat}
59
+ (h : code[a]? = some i) (hab : a = b) : code[b]? = some i := hab ▸ h
60
+
61
+ theorem xstep_of_fetch {code : List (Instr Nat)} {m : M} {i : Instr Nat}
62
+ (h : code[m.pc]? = some i) : step code m = exec i m := by
63
+ simp [step, h]
64
+
65
+ /-- Prefix sums: `offs` of a cons. -/
66
+ theorem offs_cons (i : WordDialect.Instr 64) (is : Prog 64) (k : Nat) :
67
+ offs (i :: is) (k + 1) = isize i + offs is k := by
68
+ simp [offs, List.take_succ_cons]
69
+
70
+ theorem offs_zero (p : Prog 64) : offs p 0 = 0 := by simp [offs]
71
+
72
+ theorem xat_embed (pre seg post : List (Instr Nat)) : XAt (pre ++ seg ++ post) pre.length seg := by
73
+ induction seg generalizing pre with
74
+ | nil => simp [XAt]
75
+ | cons i is ih =>
76
+ refine ⟨by simp, ?_⟩
77
+ have := ih (pre ++ [i])
78
+ simpa [List.append_assoc] using this
79
+
80
+ /-- Where the lowered code of IR instruction `k` sits: at index `pre.length + offs ps k`. -/
81
+ theorem lowerGo_at (L : Layout) (off : Nat → Nat) :
82
+ ∀ (ps : Prog 64) (pre : List (Instr Nat)) (k : Nat) (i : WordDialect.Instr 64),
83
+ ps[k]? = some i →
84
+ XAt (pre ++ lowerGo L off pre.length ps) (pre.length + offs ps k)
85
+ (lowerInstr L (pre.length + offs ps k) off i) := by
86
+ intro ps
87
+ induction ps with
88
+ | nil => intro pre k i h; simp at h
89
+ | cons j js ih =>
90
+ intro pre k i h
91
+ cases k with
92
+ | zero =>
93
+ simp at h; subst h
94
+ simp only [offs_zero, Nat.add_zero, lowerGo]
95
+ have := xat_embed pre (lowerInstr L pre.length off j) (lowerGo L off (pre.length + isize j) js)
96
+ simpa [List.append_assoc] using this
97
+ | succ k =>
98
+ simp only [List.getElem?_cons_succ] at h
99
+ have := ih (pre ++ lowerInstr L pre.length off j) k i h
100
+ have hl : (pre ++ lowerInstr L pre.length off j).length = pre.length + isize j := by
101
+ simp [lowerInstr_length]
102
+ rw [hl] at this
103
+ simp only [lowerGo, offs_cons]
104
+ have e : pre.length + (isize j + offs js k) = pre.length + isize j + offs js k := by omega
105
+ rw [e]
106
+ simpa [List.append_assoc] using this
107
+
108
+ theorem lowerProg_at (L : Layout) (p : Prog 64) (k : Nat) (i : WordDialect.Instr 64)
109
+ (h : p[k]? = some i) :
110
+ XAt (lowerProg L p) (offs p k) (lowerInstr L (offs p k) (offs p) i) := by
111
+ have := lowerGo_at L (offs p) p [] k i h
112
+ simpa [lowerProg] using this
113
+
114
+ /-- The final stub (bad-pc trap) sits at `offs p p.length`. -/
115
+ theorem lowerGo_stub (L : Layout) (off : Nat → Nat) :
116
+ ∀ (ps : Prog 64) (pre : List (Instr Nat)),
117
+ XAt (pre ++ lowerGo L off pre.length ps) (pre.length + offs ps ps.length) [.exitTrap .badPc] := by
118
+ intro ps
119
+ induction ps with
120
+ | nil =>
121
+ intro pre
122
+ simp only [lowerGo, List.length_nil, offs, List.take_nil, List.map_nil, List.sum_nil,
123
+ Nat.add_zero]
124
+ have := xat_embed pre [.exitTrap .badPc] []
125
+ simpa using this
126
+ | cons j js ih =>
127
+ intro pre
128
+ have := ih (pre ++ lowerInstr L pre.length off j)
129
+ have hl : (pre ++ lowerInstr L pre.length off j).length = pre.length + isize j := by
130
+ simp [lowerInstr_length]
131
+ rw [hl] at this
132
+ simp only [lowerGo, List.length_cons, offs_cons]
133
+ have e : pre.length + (isize j + offs js js.length) = pre.length + isize j + offs js js.length := by omega
134
+ rw [e]
135
+ simpa [List.append_assoc] using this
136
+
137
+ theorem lowerProg_stub (L : Layout) (p : Prog 64) :
138
+ XAt (lowerProg L p) (offs p p.length) [.exitTrap .badPc] := by
139
+ have := lowerGo_stub L (offs p) p []
140
+ simpa [lowerProg] using this
141
+
142
+ end X86
143
+ end WordDialect
backend/x86_64/X86/Seq.lean ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.Rel
2
+
3
+ /-!
4
+ # X86.Seq
5
+
6
+ Straight-line x86 execution (`xseq`) with its `XSteps` correctness, and the guard pattern
7
+ `jcc c (pc+2); exitTrap t` used for every trap check in the lowering.
8
+ -/
9
+
10
+ namespace WordDialect
11
+ namespace X86
12
+
13
+ open Reg
14
+
15
+ def Instr.straightX : Instr Nat → Bool
16
+ | .jmp _ | .jcc _ _ | .call _ | .ret | .exitHalt | .exitTrap _ | .exitOvf => false
17
+ | _ => true
18
+
19
+ def xseq : List (Instr Nat) → M → Out
20
+ | [], m => .next m
21
+ | i :: is, m =>
22
+ match exec i m with
23
+ | .next m' => xseq is m'
24
+ | o => o
25
+
26
+ theorem exec_straightX_pc (i : Instr Nat) (m m' : M) (hi : i.straightX = true)
27
+ (h : exec i m = .next m') : m'.pc = m.pc + 1 := by
28
+ cases i <;> simp only [Instr.straightX] at hi <;> (try contradiction) <;>
29
+ simp only [exec] at h <;> (try split at h) <;> (try split at h) <;> (try split at h) <;>
30
+ (try split at h) <;> (try cases h) <;> simp [M.mov, M.arith, M.adv, M.setReg]
31
+
32
+ theorem xseq_steps {code : List (Instr Nat)} :
33
+ ∀ {seg : List (Instr Nat)} {m m' : M},
34
+ (∀ i ∈ seg, i.straightX = true) → XAt code m.pc seg → xseq seg m = .next m' →
35
+ XSteps code m m' ∧ m'.pc = m.pc + seg.length := by
36
+ intro seg
37
+ induction seg with
38
+ | nil => intro m m' _ _ h; simp [xseq] at h; subst h; exact ⟨.refl, by simp⟩
39
+ | cons i is ih =>
40
+ intro m m' hs ha h
41
+ obtain ⟨hf, hrest⟩ := ha
42
+ have hi : i.straightX = true := hs i (by simp)
43
+ simp only [xseq] at h
44
+ cases hx : exec i m with
45
+ | next m1 =>
46
+ rw [hx] at h
47
+ have hpc := exec_straightX_pc i m m1 hi hx
48
+ have hst : step code m = .next m1 := by rw [xstep_of_fetch hf, hx]
49
+ obtain ⟨h1, h2⟩ := ih (fun j hj => hs j (by simp [hj])) (by rw [hpc]; exact hrest) h
50
+ exact ⟨.cons hst h1, by rw [h2, hpc]; simp; omega⟩
51
+ | halted _ => rw [hx] at h; simp at h
52
+ | trapped _ => rw [hx] at h; simp at h
53
+ | overflow => rw [hx] at h; simp at h
54
+ | fault => rw [hx] at h; simp at h
55
+
56
+ theorem guard_pass {code : List (Instr Nat)} {pc : Nat} {m : M} {c : Cc} {t : Trap} {f : Flags}
57
+ (hat : XAt code pc [.jcc c (pc + 2), .exitTrap t]) (hpc : m.pc = pc)
58
+ (hf : m.flags = some f) (hc : c.holds f = true) :
59
+ XSteps code m { m with pc := pc + 2 } := by
60
+ obtain ⟨h1, _⟩ := hat
61
+ apply XSteps.single
62
+ rw [xstep_of_fetch (by rw [hpc]; exact h1)]
63
+ simp [exec, hf, hc, hpc]
64
+
65
+ theorem guard_trap {code : List (Instr Nat)} {pc : Nat} {m : M} {c : Cc} {t : Trap} {f : Flags}
66
+ (hat : XAt code pc [.jcc c (pc + 2), .exitTrap t]) (hpc : m.pc = pc)
67
+ (hf : m.flags = some f) (hc : c.holds f = false) :
68
+ XExec code m (.trapped t) := by
69
+ obtain ⟨h1, h2, _⟩ := hat
70
+ refine .next (m1 := { m with pc := pc + 1 }) ?_ (.trap ?_)
71
+ · rw [xstep_of_fetch (by rw [hpc]; exact h1)]
72
+ simp [exec, hf, hc, hpc, M.adv]
73
+ · rw [xstep_of_fetch (m := { m with pc := pc + 1 }) h2]
74
+ simp [exec]
75
+
76
+ end X86
77
+ end WordDialect
backend/x86_64/X86/SimAux.lean ADDED
@@ -0,0 +1,448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.SimCtl
2
+
3
+ /-!
4
+ # X86.SimAux
5
+
6
+ The auxiliary data stack (`tor fromr rfetch`). It lives in its own region `[aBase, aEnd)`, grows
7
+ downward from `aEnd`, and its pointer is `rbp`, related by `AuxRel`; native `call`/`ret` never
8
+ touch it. This file has the step lemmas for `rbp` (load the top, push, drop), the two guards
9
+ (`ovfA`: room for one more word, else exit 6; `emptyA`: nonempty, else trap `returnUnderflow`) and
10
+ the simulation theorems for the three instructions, for every outcome.
11
+ -/
12
+
13
+ namespace WordDialect
14
+ namespace X86
15
+
16
+ open Reg
17
+
18
+ section Steps
19
+
20
+ variable {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)}
21
+
22
+ /-- A write inside the auxiliary-stack region leaves the data-stack, register-file, IR-memory and
23
+ return-stack views unchanged. -/
24
+ theorem aux_region_write (hg : Geom c) (hs : Rel0 c p s x) {A : Nat} {v : W} {mem' : Memory 64}
25
+ (hA1 : c.aBase ≤ A) (hA2 : A < c.aEnd) (hw : x.mem.write? (ofN A) v = some mem') :
26
+ StackRel c mem' s.dstack ∧ RegRel c mem' s.regs ∧ MemRel c mem' s.mem ∧
27
+ RsRel c p mem' s.rstack := by
28
+ have haE := hg.aEnd_lt
29
+ have hAlt : A < 2 ^ 64 := by omega
30
+ have hcap := hs.capD
31
+ have hdE := hg.dEnd_lt
32
+ have hcapR := hs.capR
33
+ have hrc := hg.rcap_le
34
+ have hsp := hg.sp0_lt
35
+ refine ⟨?_, ?_, ?_, ?_⟩
36
+ · intro i w hi
37
+ have hik : i < s.dstack.length := by
38
+ by_cases hh : i < s.dstack.length
39
+ · exact hh
40
+ · rw [List.getElem?_eq_none (by omega)] at hi; simp at hi
41
+ have hB : c.dEnd - 8 * s.dstack.length + 8 * i < 2 ^ 64 := by omega
42
+ have hne : A ≠ c.dEnd - 8 * s.dstack.length + 8 * i := by
43
+ rcases hg.dS_A with h | h <;> omega
44
+ rw [read_write_nat hw hAlt hB]
45
+ simp only [hne, ite_false]
46
+ exact hs.stack i w hi
47
+ · intro r hr
48
+ have hrf := hg.rf_lt
49
+ have hne : A ≠ c.rf + 8 * r := by rcases hg.dA_F with h | h <;> omega
50
+ rw [read_write_nat hw hAlt (by omega)]
51
+ simp only [hne, ite_false]
52
+ exact hs.regs r hr
53
+ · intro a ha
54
+ have hmb := hg.mb_lt
55
+ have hne : A ≠ c.mb + 8 * a := by rcases hg.dA_M with h | h <;> omega
56
+ rw [read_write_nat hw hAlt (by omega)]
57
+ simp only [hne, ite_false]
58
+ exact hs.mem a ha
59
+ · intro i a hi
60
+ have hi' : i < s.rstack.length := by
61
+ by_cases hh : i < s.rstack.length
62
+ · exact hh
63
+ · rw [List.getElem?_eq_none (by omega)] at hi; simp at hi
64
+ have hkr : 8 * s.rstack.length ≤ c.sp0 := by omega
65
+ have hB : c.sp0 - 8 * s.rstack.length + 8 * i < 2 ^ 64 := by omega
66
+ have hne : A ≠ c.sp0 - 8 * s.rstack.length + 8 * i := by
67
+ rcases hg.dA_K with h | h <;> omega
68
+ rw [read_write_nat hw hAlt hB]
69
+ simp only [hne, ite_false]
70
+ exact hs.rs i a hi
71
+
72
+ /-- Load the auxiliary-stack top into a scratch register. -/
73
+ theorem ld_aux_step (hs : Rel0 c p s x) {a : W} {as : List W} (hd : s.astack = a :: as) {rd : Reg}
74
+ (hrd : rd = rax ∨ rd = rcx ∨ rd = rdx ∨ rd = rbx)
75
+ (hf : code[x.pc]? = some (.load rd Reg.rbp 0)) :
76
+ ∃ x', XSteps code x x' ∧ Rel0 c p s x' ∧ x'.pc = x.pc + 1 ∧ x'.regs rd = a ∧
77
+ (∀ r, r ≠ rd → x'.regs r = x.regs r) ∧ x'.mem = x.mem := by
78
+ have hread := hs.aux 0 a (by simp [hd])
79
+ have e : exec (.load rd Reg.rbp 0) x = .next (x.mov rd a) := by
80
+ simp only [exec, M.ea, hs.rbp]
81
+ have h0 : ofN (c.aEnd - 8 * s.astack.length) + BitVec.ofInt 64 0 =
82
+ ofN (c.aEnd - 8 * s.astack.length + 8 * 0) := by simp
83
+ rw [h0, hread]
84
+ refine ⟨x.mov rd a, XSteps.single (by rw [xstep_of_fetch hf, e]),
85
+ mov_rel0 hs rd (scratch_ne rd hrd) a, ?_, ?_, ?_, ?_⟩
86
+ · simp [M.mov, M.setReg, M.adv]
87
+ · simp [M.mov, M.setReg, M.adv]
88
+ · intro r hr; simp [M.mov, M.setReg, M.adv, hr]
89
+ · simp [M.mov, M.setReg, M.adv]
90
+
91
+ /-- `addImm rbp, 8` drops the auxiliary-stack top. -/
92
+ theorem aux_drop (hs : Rel0 c p s x) {a : W} {as : List W} (hd : s.astack = a :: as)
93
+ (hf : code[x.pc]? = some (.addImm Reg.rbp 8)) :
94
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with astack := as } x' ∧ x'.pc = x.pc + 1 ∧
95
+ (∀ r, r ≠ Reg.rbp → x'.regs r = x.regs r) := by
96
+ have hcap := hs.capA
97
+ rw [hd] at hcap
98
+ simp only [List.length_cons] at hcap
99
+ have hval : x.regs Reg.rbp + BitVec.ofInt 64 8 = ofN (c.aEnd - 8 * as.length) := by
100
+ rw [hs.rbp, show (8 : Int) = 8 * ((1 : Nat) : Int) by simp, ea_nat, hd]
101
+ congr 1
102
+ simp only [List.length_cons]
103
+ omega
104
+ refine ⟨x.arith Reg.rbp (ofN (c.aEnd - 8 * as.length)),
105
+ XSteps.single (by rw [xstep_of_fetch hf]; simp only [exec, hval]), ?_, by simp [M.arith],
106
+ by intro r hr; simp [M.arith, M.setReg, hr]⟩
107
+ refine { r15 := ?_, r14 := ?_, r13 := ?_, r12 := ?_, rsp := ?_, rbp := ?_, stack := hs.stack,
108
+ regs := hs.regs, mem := hs.mem, rs := hs.rs, aux := ?_, irvalid := hs.irvalid,
109
+ xvalid := hs.xvalid, capD := hs.capD, capR := hs.capR, capA := ?_ }
110
+ · simpa [M.arith, M.setReg] using hs.r15
111
+ · simpa [M.arith, M.setReg] using hs.r14
112
+ · simpa [M.arith, M.setReg] using hs.r13
113
+ · simpa [M.arith, M.setReg] using hs.r12
114
+ · simpa [M.arith, M.setReg] using hs.rsp
115
+ · simp [M.arith, M.setReg]
116
+ · intro i w hi
117
+ have hi' : as[i]? = some w := hi
118
+ have h := hs.aux (i + 1) w (by rw [hd]; simpa using hi')
119
+ rw [hd] at h
120
+ simp only [List.length_cons] at h
121
+ have e : c.aEnd - 8 * (as.length + 1) + 8 * (i + 1) = c.aEnd - 8 * as.length + 8 * i := by
122
+ omega
123
+ rw [e] at h
124
+ exact h
125
+ · show c.aBase + 8 * as.length ≤ c.aEnd
126
+ omega
127
+
128
+ /-- `addImm rbp, -8; store [rbp], rs` pushes the value of `rs` onto the auxiliary stack. -/
129
+ theorem aux_push (hg : Geom c) (hs : Rel0 c p s x) (rs : Reg) (hrs : rs ≠ rbp)
130
+ (hfit : c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd)
131
+ (hat : XAt code x.pc [.addImm Reg.rbp (-8), .store Reg.rbp 0 rs]) :
132
+ ∃ x', XSteps code x x' ∧
133
+ Rel0 c p { s with astack := x.regs rs :: s.astack } x' ∧ x'.pc = x.pc + 2 ∧
134
+ (∀ r, r ≠ rbp → x'.regs r = x.regs r) := by
135
+ obtain ⟨h1, h2, _⟩ := hat
136
+ have hcap := hs.capA
137
+ have haE := hg.aEnd_lt
138
+ have hA1 : c.aBase ≤ c.aEnd - 8 * (s.astack.length + 1) := by omega
139
+ have hA2 : c.aEnd - 8 * (s.astack.length + 1) < c.aEnd := by omega
140
+ have hAlt : c.aEnd - 8 * (s.astack.length + 1) < 2 ^ 64 := by omega
141
+ have hval : x.regs Reg.rbp + BitVec.ofInt 64 (-8) = ofN (c.aEnd - 8 * (s.astack.length + 1)) := by
142
+ rw [hs.rbp, ea_neg8 (by omega)]
143
+ congr 1
144
+ obtain ⟨x1, hx1⟩ : ∃ x1, x1 = x.arith Reg.rbp (ofN (c.aEnd - 8 * (s.astack.length + 1))) :=
145
+ ⟨_, rfl⟩
146
+ have e1 : exec (.addImm Reg.rbp (-8)) x = .next x1 := by rw [hx1]; simp only [exec, hval]
147
+ have hst1 : step code x = .next x1 := by rw [xstep_of_fetch h1, e1]
148
+ have hbp1 : x1.regs Reg.rbp = ofN (c.aEnd - 8 * (s.astack.length + 1)) := by
149
+ simp [hx1, M.arith, M.setReg]
150
+ have hrsv : x1.regs rs = x.regs rs := by simp [hx1, M.arith, M.setReg, hrs]
151
+ have hm1 : x1.mem = x.mem := by simp [hx1, M.arith, M.setReg]
152
+ have hvalid := hs.xvalid.2.2.2.2 _ hA1 hA2
153
+ obtain ⟨mem', hw⟩ : ∃ m', x.mem.write? (ofN (c.aEnd - 8 * (s.astack.length + 1))) (x.regs rs)
154
+ = some m' := by simp [Memory.write?, hvalid]
155
+ have e2 : exec (.store Reg.rbp 0 rs) x1 = .next { x1.adv with mem := mem' } := by
156
+ simp only [exec, M.ea, hbp1, hrsv, hm1]
157
+ have h0 : ofN (c.aEnd - 8 * (s.astack.length + 1)) + BitVec.ofInt 64 0 =
158
+ ofN (c.aEnd - 8 * (s.astack.length + 1)) := by simp
159
+ rw [h0, hw]
160
+ have hpc1 : x1.pc = x.pc + 1 := by simp [hx1, M.arith, M.setReg]
161
+ have hst2 : step code x1 = .next { x1.adv with mem := mem' } := by
162
+ rw [xstep_of_fetch (m := x1) (by rw [hpc1]; exact h2), e2]
163
+ refine ⟨{ x1.adv with mem := mem' }, (XSteps.single hst1).trans (XSteps.single hst2), ?_,
164
+ by simp [M.adv, hpc1], by intro r hr; simp [M.adv, hx1, M.arith, M.setReg, hr]⟩
165
+ obtain ⟨hst, hrg, hmm, hrr⟩ := aux_region_write hg hs hA1 hA2 hw
166
+ refine { r15 := ?_, r14 := ?_, r13 := ?_, r12 := ?_, rsp := ?_, rbp := ?_, stack := hst,
167
+ regs := hrg, mem := hmm, rs := hrr, aux := ?_, irvalid := hs.irvalid, xvalid := ?_,
168
+ capD := hs.capD, capR := hs.capR, capA := ?_ }
169
+ · simpa [M.adv, hx1, M.arith, M.setReg] using hs.r15
170
+ · simpa [M.adv, hx1, M.arith, M.setReg] using hs.r14
171
+ · simpa [M.adv, hx1, M.arith, M.setReg] using hs.r13
172
+ · simpa [M.adv, hx1, M.arith, M.setReg] using hs.r12
173
+ · simpa [M.adv, hx1, M.arith, M.setReg] using hs.rsp
174
+ · simp [M.adv, hx1, M.arith, M.setReg]
175
+ · intro i w hi
176
+ show mem'.read? _ = some w
177
+ simp only [List.length_cons]
178
+ cases i with
179
+ | zero =>
180
+ simp at hi; subst hi
181
+ rw [show c.aEnd - 8 * (s.astack.length + 1) + 8 * 0 = c.aEnd - 8 * (s.astack.length + 1) by
182
+ omega, read_write_nat hw hAlt hAlt]
183
+ simp
184
+ | succ i =>
185
+ simp only [List.getElem?_cons_succ] at hi
186
+ have hik : i < s.astack.length := by
187
+ have := (List.getElem?_eq_some_iff.mp hi).1; simpa using this
188
+ have hB : c.aEnd - 8 * (s.astack.length + 1) + 8 * (i + 1) < 2 ^ 64 := by omega
189
+ have hne : c.aEnd - 8 * (s.astack.length + 1) ≠
190
+ c.aEnd - 8 * (s.astack.length + 1) + 8 * (i + 1) := by omega
191
+ rw [read_write_nat hw hAlt hB]
192
+ simp only [hne, ite_false]
193
+ have e : c.aEnd - 8 * (s.astack.length + 1) + 8 * (i + 1) =
194
+ c.aEnd - 8 * s.astack.length + 8 * i := by omega
195
+ rw [e]; exact hs.aux i w hi
196
+ · have := Memory.write?_valid hw
197
+ show RegionsValid c mem'.valid
198
+ rw [this]; exact hs.xvalid
199
+ · simpa using hfit
200
+
201
+ end Steps
202
+
203
+ section Guards
204
+
205
+ variable {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)} {L : Layout}
206
+ {base : Nat}
207
+
208
+ /-- The auxiliary-stack guard's comparison, in terms of its depth. -/
209
+ theorem ovfA_flags (hg : Geom c) (hs : Rel0 c p s x) (hLal : L.aLim = ofN (c.aBase + 8)) :
210
+ Cc.ae.holds (subFlags (x.regs Reg.rbp) L.aLim) =
211
+ decide (c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd) := by
212
+ have hcap := hs.capA
213
+ have haE := hg.aEnd_lt
214
+ have h8 := hg.aBase_8
215
+ rw [holds_ae, hs.rbp, hLal, ule_ofN (by omega) (by omega)]
216
+ by_cases h : c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd
217
+ · simp [h]; omega
218
+ · simp [h]; omega
219
+
220
+ theorem ovfA_run (hpc : x.pc = base) (hat : XAt code base (ovfA L base)) :
221
+ ∃ xb : M, XSteps code x xb ∧ xb.pc = base + 2 ∧
222
+ xb.flags = some (subFlags (x.regs Reg.rbp) L.aLim) ∧ xb.mem = x.mem ∧
223
+ (∀ r, r ≠ rax → xb.regs r = x.regs r) ∧
224
+ code[base + 2]? = some (.jcc .ae (base + 2 + 2)) ∧ code[base + 2 + 1]? = some .exitOvf := by
225
+ obtain ⟨h1, h2, h3, h4, _⟩ := hat
226
+ let xa := x.mov rax L.aLim
227
+ have hst1 : step code x = .next xa := by rw [xstep_of_fetch (by rw [hpc]; exact h1)]; rfl
228
+ have hxa_pc : xa.pc = base + 1 := by simp [xa, M.mov, M.setReg, M.adv, hpc]
229
+ let xb : M := { xa.adv with flags := some (subFlags (xa.regs rbp) (xa.regs rax)) }
230
+ have hst2 : step code xa = .next xb := by
231
+ rw [xstep_of_fetch (m := xa) (by rw [hxa_pc]; exact h2)]; rfl
232
+ refine ⟨xb, (XSteps.single hst1).trans (XSteps.single hst2), by simp [xb, M.adv, hxa_pc],
233
+ by simp [xb, xa, M.mov, M.setReg, M.adv], by simp [xb, xa, M.adv, M.mov, M.setReg], ?_,
234
+ fetch_cast h3 (by omega), fetch_cast h4 (by omega)⟩
235
+ intro r hr; simp [xb, xa, M.adv, M.mov, M.setReg, hr]
236
+
237
+ /-- Room for one more auxiliary word: the guard falls through to `base + 4`. -/
238
+ theorem ovfA_pass (hg : Geom c) (hs : Rel0 c p s x) (hLal : L.aLim = ofN (c.aBase + 8))
239
+ (hfit : c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd) (hpc : x.pc = base)
240
+ (hat : XAt code base (ovfA L base)) :
241
+ ∃ x1, XSteps code x x1 ∧ Rel0 c p s x1 ∧ x1.pc = base + 4 ∧ x1.mem = x.mem ∧
242
+ (∀ r, r ≠ rax → x1.regs r = x.regs r) := by
243
+ obtain ⟨xb, hsb, hpcb, hfb, hmb, hrb, hj, _⟩ := ovfA_run hpc hat
244
+ have hholds : Cc.ae.holds (subFlags (x.regs Reg.rbp) L.aLim) = true := by
245
+ rw [ovfA_flags hg hs hLal]; simpa using hfit
246
+ refine ⟨{ xb with pc := base + 2 + 2 }, hsb.trans (ovf_jcc_pass hj hpcb hfb hholds), ?_, rfl, hmb,
247
+ hrb⟩
248
+ refine hs.congr hmb ?_ ?_ ?_ ?_ ?_ ?_ <;> exact hrb _ (by decide)
249
+
250
+ /-- Auxiliary stack full: the guard exits with `overflow`. -/
251
+ theorem ovfA_trap (hg : Geom c) (hs : Rel0 c p s x) (hLal : L.aLim = ofN (c.aBase + 8))
252
+ (h : ¬ c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd) (hpc : x.pc = base)
253
+ (hat : XAt code base (ovfA L base)) : XExec code x .overflow := by
254
+ obtain ⟨xb, hsb, hpcb, hfb, _, _, hj, he⟩ := ovfA_run hpc hat
255
+ have hholds : Cc.ae.holds (subFlags (x.regs Reg.rbp) L.aLim) = false := by
256
+ rw [ovfA_flags hg hs hLal]; simpa using h
257
+ exact XExec.of_steps hsb (ovf_jcc_trap hj he hpcb hfb hholds)
258
+
259
+ /-- The emptiness check's comparison: `rbp ≠ aEnd` exactly when the auxiliary stack is nonempty. -/
260
+ theorem emptyA_flags (hg : Geom c) (hs : Rel0 c p s x) (hLa : L.aEnd = ofN c.aEnd) :
261
+ Cc.ne.holds (subFlags (x.regs Reg.rbp) L.aEnd) = decide (s.astack ≠ []) := by
262
+ have hcap := hs.capA
263
+ have haE := hg.aEnd_lt
264
+ rw [holds_ne, hs.rbp, hLa]
265
+ by_cases h : s.astack = []
266
+ · simp [h]
267
+ · have hlen : 1 ≤ s.astack.length := by
268
+ cases hh : s.astack with
269
+ | nil => exact absurd hh h
270
+ | cons a as => simp
271
+ have hne : ofN (c.aEnd - 8 * s.astack.length) ≠ ofN c.aEnd := by
272
+ intro e; have := ofN_inj (by omega) haE e; omega
273
+ simp [hne, h]
274
+
275
+ theorem emptyA_run (hpc : x.pc = base) (hat : XAt code base (emptyA L base)) :
276
+ ∃ xb : M, XSteps code x xb ∧ xb.pc = base + 2 ∧
277
+ xb.flags = some (subFlags (x.regs Reg.rbp) L.aEnd) ∧ xb.mem = x.mem ∧
278
+ (∀ r, r ≠ rax → xb.regs r = x.regs r) ∧
279
+ XAt code (base + 2) [.jcc .ne (base + 2 + 2), .exitTrap .returnUnderflow] := by
280
+ obtain ⟨h1, h2, h3, h4, _⟩ := hat
281
+ let xa := x.mov rax L.aEnd
282
+ have hst1 : step code x = .next xa := by rw [xstep_of_fetch (by rw [hpc]; exact h1)]; rfl
283
+ have hxa_pc : xa.pc = base + 1 := by simp [xa, M.mov, M.setReg, M.adv, hpc]
284
+ let xb : M := { xa.adv with flags := some (subFlags (xa.regs rbp) (xa.regs rax)) }
285
+ have hst2 : step code xa = .next xb := by
286
+ rw [xstep_of_fetch (m := xa) (by rw [hxa_pc]; exact h2)]; rfl
287
+ refine ⟨xb, (XSteps.single hst1).trans (XSteps.single hst2), by simp [xb, M.adv, hxa_pc],
288
+ by simp [xb, xa, M.mov, M.setReg, M.adv], by simp [xb, xa, M.adv, M.mov, M.setReg], ?_,
289
+ fetch_cast h3 (by omega), fetch_cast h4 (by omega), trivial⟩
290
+ intro r hr; simp [xb, xa, M.adv, M.mov, M.setReg, hr]
291
+
292
+ /-- Nonempty auxiliary stack: the check falls through to `base + 4`. -/
293
+ theorem emptyA_pass (hg : Geom c) (hs : Rel0 c p s x) (hLa : L.aEnd = ofN c.aEnd)
294
+ (hne : s.astack ≠ []) (hpc : x.pc = base) (hat : XAt code base (emptyA L base)) :
295
+ ∃ x1, XSteps code x x1 ∧ Rel0 c p s x1 ∧ x1.pc = base + 4 ∧ x1.mem = x.mem ∧
296
+ (∀ r, r ≠ rax → x1.regs r = x.regs r) := by
297
+ obtain ⟨xb, hsb, hpcb, hfb, hmb, hrb, hj⟩ := emptyA_run hpc hat
298
+ have hholds : Cc.ne.holds (subFlags (x.regs Reg.rbp) L.aEnd) = true := by
299
+ rw [emptyA_flags hg hs hLa]; simpa using hne
300
+ refine ⟨{ xb with pc := base + 2 + 2 }, hsb.trans (guard_pass hj hpcb hfb hholds), ?_, rfl, hmb,
301
+ hrb⟩
302
+ refine hs.congr hmb ?_ ?_ ?_ ?_ ?_ ?_ <;> exact hrb _ (by decide)
303
+
304
+ /-- Empty auxiliary stack: the check traps `returnUnderflow`. -/
305
+ theorem emptyA_trap (hg : Geom c) (hs : Rel0 c p s x) (hLa : L.aEnd = ofN c.aEnd)
306
+ (he : s.astack = []) (hpc : x.pc = base) (hat : XAt code base (emptyA L base)) :
307
+ XExec code x (.trapped .returnUnderflow) := by
308
+ obtain ⟨xb, hsb, hpcb, hfb, _, _, hj⟩ := emptyA_run hpc hat
309
+ have hholds : Cc.ne.holds (subFlags (x.regs Reg.rbp) L.aEnd) = false := by
310
+ rw [emptyA_flags hg hs hLa]; simp [he]
311
+ exact XExec.of_steps hsb (guard_trap hj hpcb hfb hholds)
312
+
313
+ end Guards
314
+
315
+ section Ops
316
+
317
+ variable {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)}
318
+ {L : Layout} {base : Nat} {off : Nat → Nat}
319
+
320
+ /-- Placement of the `tor` code. -/
321
+ theorem xat_tor (hat : XAt code base (lowerInstr L base off .tor)) :
322
+ XAt code base (uf L 1 base) ∧ XAt code (base + 4) (ovfA L (base + 4)) ∧
323
+ XAt code (base + 8) [.load rax Reg.r15 0, .addImm Reg.r15 8, .addImm Reg.rbp (-8),
324
+ .store Reg.rbp 0 rax] := by
325
+ have hat' : XAt code base (uf L 1 base ++ ovfA L (base + 4) ++
326
+ [.load rax Reg.r15 0, .addImm Reg.r15 8, .addImm Reg.rbp (-8), .store Reg.rbp 0 rax]) := hat
327
+ obtain ⟨hatuo, hatr⟩ := xat_append.mp hat'
328
+ obtain ⟨hatuf, hato⟩ := xat_append.mp hatuo
329
+ have hufl : (uf L 1 base).length = 4 := by simp [uf]
330
+ rw [hufl] at hato
331
+ have hl : (uf L 1 base ++ ovfA L (base + 4)).length = 8 := by simp [hufl, ovfA]
332
+ rw [hl] at hatr
333
+ exact ⟨hatuf, hato, hatr⟩
334
+
335
+ theorem sim_tor_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hLal : L.aLim = ofN (c.aBase + 8))
336
+ (hs : Rel0 c p s x) (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .tor))
337
+ {a : W} {d : List W} (hd : s.dstack = a :: d)
338
+ (hfit : c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd) :
339
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := d, astack := a :: s.astack } x' ∧
340
+ x'.pc = base + isize (.tor : WordDialect.Instr 64) := by
341
+ obtain ⟨hatuf, hato, f0, f1, f2, f3, _⟩ := xat_tor hat
342
+ have hk : 1 ≤ s.dstack.length := by simp [hd]
343
+ obtain ⟨x1, hs1, hr1, hpc1, _, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
344
+ obtain ⟨x1', hs1', hr1', hpc1', _, _⟩ := ovfA_pass hg hr1 hLal hfit hpc1 hato
345
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, _, _⟩ := ld_step (code := code) hr1' (j := 0) (v := a)
346
+ (by simp [hd]) (rd := rax) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
347
+ obtain ⟨x3, hs3, hr3, hpc3, ho3⟩ := adj_step (code := code) hg hr2 (n := 1) (by simp [hd])
348
+ (δ := 8) (by simp) (fetch_cast f1 (by omega))
349
+ have hax3 : x3.regs rax = a := by rw [ho3 rax (by decide), hv2]
350
+ have e : s.dstack.drop 1 = d := by simp [hd]
351
+ rw [e] at hr3
352
+ obtain ⟨x4, hs4, hr4, hpc4, _⟩ := aux_push hg hr3 rax (by decide) hfit
353
+ ⟨fetch_cast f2 (by omega), fetch_cast f3 (by omega), trivial⟩
354
+ rw [hax3] at hr4
355
+ exact ⟨x4, (((hs1.trans hs1').trans hs2).trans hs3).trans hs4, hr4,
356
+ by simp [isize, ufSize]; omega⟩
357
+
358
+ theorem sim_tor_ovf (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hLal : L.aLim = ofN (c.aBase + 8))
359
+ (hs : Rel0 c p s x) (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .tor))
360
+ (hk : 1 ≤ s.dstack.length) (h : ¬ c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd) :
361
+ XExec code x .overflow := by
362
+ obtain ⟨hatuf, hato, _⟩ := xat_tor hat
363
+ obtain ⟨x1, hs1, hr1, hpc1, _, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
364
+ exact XExec.of_steps hs1 (ovfA_trap hg hr1 hLal h hpc1 hato)
365
+
366
+ /-- Placement of `emptyA L base ++ ovfD L (base + 4) ++ rest`. -/
367
+ theorem xat_take {rest : List (Instr Nat)}
368
+ (hat : XAt code base (emptyA L base ++ ovfD L (base + 4) ++ rest)) :
369
+ XAt code base (emptyA L base) ∧ XAt code (base + 4) (ovfD L (base + 4)) ∧
370
+ XAt code (base + 8) rest := by
371
+ obtain ⟨hatuo, hatr⟩ := xat_append.mp hat
372
+ obtain ⟨hate, hato⟩ := xat_append.mp hatuo
373
+ have hel : (emptyA L base).length = 4 := rfl
374
+ rw [hel] at hato
375
+ have hl : (emptyA L base ++ ovfD L (base + 4)).length = 8 := rfl
376
+ rw [hl] at hatr
377
+ exact ⟨hate, hato, hatr⟩
378
+
379
+ theorem sim_fromr_ok (hg : Geom c) (hLa : L.aEnd = ofN c.aEnd) (hLd : L.dLim = ofN (c.dBase + 8))
380
+ (hs : Rel0 c p s x) (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .fromr))
381
+ {a : W} {as : List W} (hd : s.astack = a :: as)
382
+ (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
383
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := a :: s.dstack, astack := as } x' ∧
384
+ x'.pc = base + isize (.fromr : WordDialect.Instr 64) := by
385
+ obtain ⟨hate, hato, f0, f1, f2, f3, _⟩ := xat_take (L := L) (rest := [.load rax Reg.rbp 0,
386
+ .addImm Reg.rbp 8, .addImm Reg.r15 (-8), .store Reg.r15 0 rax]) hat
387
+ obtain ⟨x1, hs1, hr1, hpc1, _, _⟩ := emptyA_pass hg hs hLa (by simp [hd]) hpc hate
388
+ obtain ⟨x1', hs1', hr1', hpc1', _, _⟩ := ovfD_pass hg hr1 hLd hfit hpc1 hato
389
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, _, _⟩ := ld_aux_step (code := code) hr1' hd (rd := rax)
390
+ (by simp) (fetch_cast f0 (by omega))
391
+ obtain ⟨x3, hs3, hr3, hpc3, ho3⟩ := aux_drop (code := code) hr2 hd (fetch_cast f1 (by omega))
392
+ have hax3 : x3.regs rax = a := by rw [ho3 rax (by decide), hv2]
393
+ obtain ⟨x4, hs4, hr4, hpc4, _⟩ := push_pair hg hr3 rax (by decide) hfit
394
+ ⟨fetch_cast f2 (by omega), fetch_cast f3 (by omega), trivial⟩
395
+ rw [hax3] at hr4
396
+ exact ⟨x4, (((hs1.trans hs1').trans hs2).trans hs3).trans hs4, hr4, by simp [isize]; omega⟩
397
+
398
+ theorem sim_fromr_trap (hg : Geom c) (hLa : L.aEnd = ofN c.aEnd) (hs : Rel0 c p s x)
399
+ (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .fromr)) (he : s.astack = []) :
400
+ XExec code x (.trapped .returnUnderflow) :=
401
+ emptyA_trap hg hs hLa he hpc (xat_take (L := L) (rest := [.load rax Reg.rbp 0,
402
+ .addImm Reg.rbp 8, .addImm Reg.r15 (-8), .store Reg.r15 0 rax]) hat).1
403
+
404
+ theorem sim_fromr_ovf (hg : Geom c) (hLa : L.aEnd = ofN c.aEnd) (hLd : L.dLim = ofN (c.dBase + 8))
405
+ (hs : Rel0 c p s x) (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .fromr))
406
+ (hne : s.astack ≠ []) (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
407
+ XExec code x .overflow := by
408
+ obtain ⟨hate, hato, _⟩ := xat_take (L := L) (rest := [.load rax Reg.rbp 0,
409
+ .addImm Reg.rbp 8, .addImm Reg.r15 (-8), .store Reg.r15 0 rax]) hat
410
+ obtain ⟨x1, hs1, hr1, hpc1, _, _⟩ := emptyA_pass hg hs hLa hne hpc hate
411
+ exact XExec.of_steps hs1 (ovfD_trap hg hr1 hLd h hpc1 hato)
412
+
413
+ theorem sim_rfetch_ok (hg : Geom c) (hLa : L.aEnd = ofN c.aEnd) (hLd : L.dLim = ofN (c.dBase + 8))
414
+ (hs : Rel0 c p s x) (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .rfetch))
415
+ {a : W} {as : List W} (hd : s.astack = a :: as)
416
+ (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
417
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := a :: s.dstack } x' ∧
418
+ x'.pc = base + isize (.rfetch : WordDialect.Instr 64) := by
419
+ obtain ⟨hate, hato, f0, f1, f2, _⟩ := xat_take (L := L) (rest := [.load rax Reg.rbp 0,
420
+ .addImm Reg.r15 (-8), .store Reg.r15 0 rax]) hat
421
+ obtain ⟨x1, hs1, hr1, hpc1, _, _⟩ := emptyA_pass hg hs hLa (by simp [hd]) hpc hate
422
+ obtain ⟨x1', hs1', hr1', hpc1', _, _⟩ := ovfD_pass hg hr1 hLd hfit hpc1 hato
423
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, _, _⟩ := ld_aux_step (code := code) hr1' hd (rd := rax)
424
+ (by simp) (fetch_cast f0 (by omega))
425
+ obtain ⟨x3, hs3, hr3, hpc3, _⟩ := push_pair hg hr2 rax (by decide) hfit
426
+ ⟨fetch_cast f1 (by omega), fetch_cast f2 (by omega), trivial⟩
427
+ rw [hv2] at hr3
428
+ exact ⟨x3, ((hs1.trans hs1').trans hs2).trans hs3, hr3, by simp [isize]; omega⟩
429
+
430
+ theorem sim_rfetch_trap (hg : Geom c) (hLa : L.aEnd = ofN c.aEnd) (hs : Rel0 c p s x)
431
+ (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .rfetch)) (he : s.astack = []) :
432
+ XExec code x (.trapped .returnUnderflow) :=
433
+ emptyA_trap hg hs hLa he hpc (xat_take (L := L) (rest := [.load rax Reg.rbp 0,
434
+ .addImm Reg.r15 (-8), .store Reg.r15 0 rax]) hat).1
435
+
436
+ theorem sim_rfetch_ovf (hg : Geom c) (hLa : L.aEnd = ofN c.aEnd) (hLd : L.dLim = ofN (c.dBase + 8))
437
+ (hs : Rel0 c p s x) (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .rfetch))
438
+ (hne : s.astack ≠ []) (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
439
+ XExec code x .overflow := by
440
+ obtain ⟨hate, hato, _⟩ := xat_take (L := L) (rest := [.load rax Reg.rbp 0,
441
+ .addImm Reg.r15 (-8), .store Reg.r15 0 rax]) hat
442
+ obtain ⟨x1, hs1, hr1, hpc1, _, _⟩ := emptyA_pass hg hs hLa hne hpc hate
443
+ exact XExec.of_steps hs1 (ovfD_trap hg hr1 hLd h hpc1 hato)
444
+
445
+ end Ops
446
+
447
+ end X86
448
+ end WordDialect
backend/x86_64/X86/SimBin.lean ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.Guard
2
+
3
+ /-!
4
+ # X86.SimBin
5
+
6
+ Generic simulation of the binary-operation lowering shape
7
+
8
+ uf 2 ; load rcx,[r15] ; load rax,[r15+8] ; <mid> ; store [r15+8],rr ; add r15,8
9
+
10
+ `mid` only has to compute `f a b` into `rr` from `rax = a`, `rcx = b`, touching nothing but
11
+ scratch state. Instantiated below for add/sub/mul/and/or/xor, shifts, rotates and compares.
12
+ -/
13
+
14
+ namespace WordDialect
15
+ namespace X86
16
+
17
+ open Reg
18
+
19
+ def MidSpec (mid : List (Instr Nat)) (rr : Reg) (f : W → W → W) : Prop :=
20
+ ∀ (m : M) (a b : W), m.regs rax = a → m.regs rcx = b →
21
+ ∃ m', xseq mid m = .next m' ∧ m'.regs rr = f a b ∧ m'.mem = m.mem ∧
22
+ m'.regs r15 = m.regs r15 ∧ m'.regs r14 = m.regs r14 ∧ m'.regs r13 = m.regs r13 ∧
23
+ m'.regs r12 = m.regs r12 ∧ m'.regs rsp = m.regs rsp ∧ m'.regs rbp = m.regs rbp ∧
24
+ m'.pc = m.pc + mid.length
25
+
26
+ theorem scratch_ne (r : Reg) (h : r = rax ∨ r = rcx ∨ r = rdx ∨ r = rbx) :
27
+ r ≠ r15 ∧ r ≠ r14 ∧ r ≠ r13 ∧ r ≠ r12 ∧ r ≠ rsp ∧ r ≠ rbp := by
28
+ rcases h with rfl | rfl | rfl | rfl <;> decide
29
+
30
+ theorem sim_bin_ok {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)}
31
+ {L : Layout} {base : Nat} (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x)
32
+ (hpc : x.pc = base) (mid : List (Instr Nat)) (rr : Reg)
33
+ (hrr : rr = rax ∨ rr = rcx ∨ rr = rdx ∨ rr = rbx) (f : W → W → W)
34
+ (hmid : MidSpec mid rr f) (hstr : ∀ i ∈ mid, i.straightX = true)
35
+ (hat : XAt code base (uf L 2 base ++
36
+ ([.load rcx r15 0, .load rax r15 8] ++ mid ++ [.store r15 8 rr, .addImm r15 8])))
37
+ {b a : W} {d : List W} (hd : s.dstack = b :: a :: d) :
38
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := f a b :: d } x' ∧
39
+ x'.pc = base + 4 + (4 + mid.length) := by
40
+ have hk : 2 ≤ s.dstack.length := by simp [hd]
41
+ obtain ⟨hatuf, hatrest⟩ := xat_append.mp hat
42
+ have hufl : (uf L 2 base).length = 4 := by simp [uf]
43
+ rw [hufl] at hatrest
44
+ obtain ⟨hatA, hatB⟩ := xat_append.mp hatrest
45
+ obtain ⟨hatLd, hatMid⟩ := xat_append.mp hatA
46
+ obtain ⟨f0, f1, _⟩ := hatLd
47
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
48
+ have hrcx := scratch_ne rcx (by simp)
49
+ have hrax := scratch_ne rax (by simp)
50
+ -- load rcx, [r15 + 0]
51
+ have e0 := ld_stack hr1 (j := 0) (v := b) (by simp [hd]) rcx (disp := 0) (by simp)
52
+ obtain ⟨x2, hx2⟩ : ∃ x2, x2 = x1.mov rcx b := ⟨_, rfl⟩
53
+ have hst0 : XSteps code x1 x2 := XSteps.single (by
54
+ rw [xstep_of_fetch (m := x1) (by rw [hpc1]; exact f0), e0, hx2])
55
+ have hr2 : Rel0 c p s x2 := by rw [hx2]; exact mov_rel0 hr1 rcx hrcx b
56
+ have hpc2 : x2.pc = base + 4 + 1 := by simp [hx2, M.mov, M.setReg, M.adv, hpc1]
57
+ -- load rax, [r15 + 8]
58
+ have e1 := ld_stack hr2 (j := 1) (v := a) (by simp [hd]) rax (disp := 8) (by simp)
59
+ obtain ⟨x3, hx3⟩ : ∃ x3, x3 = x2.mov rax a := ⟨_, rfl⟩
60
+ have hst1 : XSteps code x2 x3 := XSteps.single (by
61
+ rw [xstep_of_fetch (m := x2) (by rw [hpc2]; exact f1), e1, hx3])
62
+ have hr3 : Rel0 c p s x3 := by rw [hx3]; exact mov_rel0 hr2 rax hrax a
63
+ have hpc3 : x3.pc = base + 4 + 2 := by simp [hx3, M.mov, M.setReg, M.adv, hpc2]
64
+ have hax : x3.regs rax = a := by simp [hx3, M.mov, M.setReg, M.adv]
65
+ have hcx : x3.regs rcx = b := by simp [hx3, hx2, M.mov, M.setReg, M.adv]
66
+ -- mid
67
+ obtain ⟨x4, hx4, hrr4, hm4, h15, h14, h13, h12, hsp, hbp, hpc4⟩ := hmid x3 a b hax hcx
68
+ obtain ⟨hstM, _⟩ := xseq_steps hstr (by rw [hpc3]; exact hatMid) hx4
69
+ have hr4 : Rel0 c p s x4 := hr3.congr hm4 h15 h14 h13 h12 hsp hbp
70
+ -- store [r15 + 8], rr
71
+ have hlen : 1 < s.dstack.length := by simp [hd]
72
+ obtain ⟨mem', e2, hr5⟩ := st_stack hg hr4 (j := 1) hlen rr (disp := 8) (by simp)
73
+ obtain ⟨st, adj, _⟩ := hatB
74
+ have hpc4' : x4.pc = base + 4 + (2 + mid.length) := by rw [hpc4, hpc3]; omega
75
+ obtain ⟨x5, hx5⟩ : ∃ x5, x5 = ({ x4.adv with mem := mem' } : M) := ⟨_, rfl⟩
76
+ have hst2 : XSteps code x4 x5 := XSteps.single (by
77
+ rw [xstep_of_fetch (m := x4) (fetch_cast st (by rw [hpc4']; simp; omega)), e2, hx5])
78
+ rw [← hx5] at hr5
79
+ have hpc5 : x5.pc = base + 4 + (2 + mid.length + 1) := by
80
+ simp [hx5, M.adv]; omega
81
+ -- adjust r15
82
+ have hlen5 : 1 ≤ ({ s with dstack := s.dstack.set 1 (x4.regs rr) } : State 64).dstack.length := by
83
+ simp [hd]
84
+ obtain ⟨e3, hr6⟩ := adj_drop hg hr5 (n := 1) hlen5 (δ := 8) (by simp)
85
+ obtain ⟨x6, hx6⟩ : ∃ x6, x6 = x5.arith Reg.r15
86
+ (ofN (c.dEnd - 8 * (({ s with dstack := s.dstack.set 1 (x4.regs rr) } : State 64).dstack.length - 1))) :=
87
+ ⟨_, rfl⟩
88
+ rw [← hx6] at e3 hr6
89
+ have hst3 : XSteps code x5 x6 := XSteps.single (by
90
+ rw [xstep_of_fetch (m := x5) (fetch_cast adj (by rw [hpc5]; simp; omega)), e3])
91
+ have hrrv : x4.regs rr = f a b := hrr4
92
+ refine ⟨x6, ((((hst0 |> fun h => (hs1.trans h)).trans hst1).trans hstM).trans hst2).trans hst3, ?_, ?_⟩
93
+ · have e : (({ s with dstack := s.dstack.set 1 (x4.regs rr) } : State 64).dstack).drop 1 = f a b :: d := by
94
+ simp [hd, hrrv]
95
+ simpa [e] using hr6
96
+ · simp [hx6, M.arith, hpc5]
97
+ omega
98
+
99
+ end X86
100
+ end WordDialect
backend/x86_64/X86/SimCtl.lean ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.SimDiv
2
+
3
+ /-!
4
+ # X86.SimCtl
5
+
6
+ Control flow: `jmp branch call ret halt`. `call` and `ret` are native, so the IR return stack is
7
+ the native stack below `sp0`, related by `RsRel` (cells hold the x86 code index of the return
8
+ instruction).
9
+ -/
10
+
11
+ namespace WordDialect
12
+ namespace X86
13
+
14
+ open Reg
15
+
16
+ theorem offs_succ {p : Prog 64} {k : Nat} {i : WordDialect.Instr 64} (h : p[k]? = some i) :
17
+ offs p (k + 1) = offs p k + isize i := by
18
+ have hk : k < p.length := (List.getElem?_eq_some_iff.mp h).1
19
+ have hi : p[k] = i := (List.getElem?_eq_some_iff.mp h).2
20
+ simp [offs, List.take_succ, h, List.sum_append]
21
+
22
+ section Ctl
23
+
24
+ variable {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)}
25
+ {L : Layout} {base : Nat} {off : Nat → Nat}
26
+
27
+ theorem sim_jmp_ok (hs : Rel0 c p s x) (hpc : x.pc = base) {t : Nat}
28
+ (hat : XAt code base (lowerInstr L base off (.jmp t))) :
29
+ ∃ x', XSteps code x x' ∧ Rel0 c p s x' ∧ x'.pc = off t := by
30
+ have hat' : XAt code base [.jmp (off t)] := hat
31
+ obtain ⟨f0, _⟩ := hat'
32
+ refine ⟨{ x with pc := off t }, XSteps.single (by rw [xstep_of_fetch (by rw [hpc]; exact f0)]; rfl),
33
+ hs.congr rfl rfl rfl rfl rfl rfl rfl, rfl⟩
34
+
35
+ /-- `branch t`: both outcomes. -/
36
+ theorem sim_branch_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x)
37
+ (hpc : x.pc = base) {t : Nat} (hat : XAt code base (lowerInstr L base off (.branch t)))
38
+ {cc : W} {d : List W} (hd : s.dstack = cc :: d) :
39
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := d } x' ∧
40
+ x'.pc = (if Word.isTrue cc then off t else base + isize (.branch t : WordDialect.Instr 64)) := by
41
+ have hat' : XAt code base (uf L 1 base ++ [.load rax Reg.r15 0, .addImm Reg.r15 8, .test rax rax,
42
+ .jcc .ne (off t)]) := hat
43
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
44
+ have hufl : (uf L 1 base).length = 4 := by simp [uf]
45
+ rw [hufl] at hatr
46
+ obtain ⟨f0, f1, f2, f3, _⟩ := hatr
47
+ have hk : 1 ≤ s.dstack.length := by simp [hd]
48
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
49
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, ho2, _⟩ := ld_step (code := code) hr1 (j := 0) (v := cc)
50
+ (by simp [hd]) (rd := rax) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
51
+ obtain ⟨x3, hs3, hr3, hpc3, ho3⟩ := adj_step (code := code) hg hr2 (n := 1) (by simp [hd])
52
+ (δ := 8) (by simp) (fetch_cast f1 (by omega))
53
+ have hax3 : x3.regs rax = cc := by rw [ho3 rax (by decide), hv2]
54
+ have e : s.dstack.drop 1 = d := by simp [hd]
55
+ rw [e] at hr3
56
+ let x4 : M := { x3.adv with flags := some (logicFlags (x3.regs rax &&& x3.regs rax)) }
57
+ have hst4 : step code x3 = .next x4 := by
58
+ rw [xstep_of_fetch (m := x3) (fetch_cast f2 (by omega))]; rfl
59
+ have hr4 : Rel0 c p { s with dstack := d } x4 := hr3.congr rfl rfl rfl rfl rfl rfl rfl
60
+ have hpc4 : x4.pc = base + 4 + 3 := by simp [x4, M.adv]; omega
61
+ have hfl4 : x4.flags = some (logicFlags (cc &&& cc)) := by simp [x4, hax3]
62
+ by_cases hc : cc = 0#64
63
+ · have hst5 : step code x4 = .next x4.adv := by
64
+ rw [xstep_of_fetch (m := x4) (fetch_cast f3 (by omega))]
65
+ simp [exec, hfl4, hc, Cc.holds, logicFlags]
66
+ refine ⟨x4.adv, (((hs1.trans hs2).trans hs3).trans (XSteps.single hst4)).trans
67
+ (XSteps.single hst5), hr4.congr rfl rfl rfl rfl rfl rfl rfl, ?_⟩
68
+ simp [hc, Word.isTrue, M.adv, hpc4, isize, ufSize]
69
+ · have hst5 : step code x4 = .next { x4 with pc := off t } := by
70
+ rw [xstep_of_fetch (m := x4) (fetch_cast f3 (by omega))]
71
+ simp [exec, hfl4, hc, Cc.holds, logicFlags]
72
+ refine ⟨{ x4 with pc := off t }, (((hs1.trans hs2).trans hs3).trans (XSteps.single hst4)).trans
73
+ (XSteps.single hst5), hr4.congr rfl rfl rfl rfl rfl rfl rfl, ?_⟩
74
+ simp [hc, Word.isTrue]
75
+
76
+ theorem sim_halt (hs : Rel0 c p s x) (hpc : x.pc = base)
77
+ (hat : XAt code base (lowerInstr L base off .halt)) : XExec code x (.halted x) := by
78
+ have hat' : XAt code base [.exitHalt] := hat
79
+ obtain ⟨f0, _⟩ := hat'
80
+ exact XExec.halt (by rw [xstep_of_fetch (by rw [hpc]; exact f0)]; rfl)
81
+
82
+ /-- The native `call` itself: pushes the x86 index of the return instruction onto the native
83
+ stack. -/
84
+ theorem sim_call_core (hg : Geom c) (hs : Rel0 c p s x) (hpc : x.pc = base) {t : Nat}
85
+ (hat : XAt code base [.call (off t)])
86
+ (hret : base + 1 = offs p (s.pc + 1)) (hfitR : s.rstack.length + 2 ≤ c.rcap) :
87
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with rstack := (s.pc + 1) :: s.rstack } x' ∧
88
+ x'.pc = off t := by
89
+ obtain ⟨f0, _⟩ := hat
90
+ have hcapR := hs.capR
91
+ have hrc := hg.rcap_le
92
+ have hsp := hg.sp0_lt
93
+ have hA1 : c.sp0 - 8 * c.rcap ≤ c.sp0 - 8 * (s.rstack.length + 1) := by omega
94
+ have hA2 : c.sp0 - 8 * (s.rstack.length + 1) < c.sp0 := by omega
95
+ have hAlt : c.sp0 - 8 * (s.rstack.length + 1) < 2 ^ 64 := by omega
96
+ have hvalid := hs.xvalid.2.2.2.1 _ hA1 hA2
97
+ have hsub : x.regs rsp - 8#64 = ofN (c.sp0 - 8 * (s.rstack.length + 1)) := by
98
+ rw [hs.rsp]
99
+ show ofN _ - ofN 8 = _
100
+ rw [ofN_sub (by omega)]
101
+ congr 1
102
+ obtain ⟨mem', hw⟩ : ∃ m', x.mem.write? (ofN (c.sp0 - 8 * (s.rstack.length + 1)))
103
+ (BitVec.ofNat 64 (x.pc + 1)) = some m' := by simp [Memory.write?, hvalid]
104
+ have hst : step code x = .next { (x.setReg rsp (ofN (c.sp0 - 8 * (s.rstack.length + 1)))) with
105
+ mem := mem', pc := off t } := by
106
+ rw [xstep_of_fetch (by rw [hpc]; exact f0)]
107
+ simp only [exec, hsub, hw]
108
+ refine ⟨_, XSteps.single hst, ?_, rfl⟩
109
+ have hdE := hg.dEnd_lt
110
+ have hcap := hs.capD
111
+ refine { r15 := ?_, r14 := ?_, r13 := ?_, r12 := ?_, rsp := ?_, stack := ?_, regs := ?_,
112
+ mem := ?_, rs := ?_, irvalid := hs.irvalid, xvalid := ?_, capD := hs.capD, capR := ?_,
113
+ rbp := by simpa [M.setReg] using hs.rbp,
114
+ aux := aux_write_other hg.aEnd_lt hs.capA hs.aux hw hAlt
115
+ (by rcases hg.dA_K with h | h <;> omega),
116
+ capA := hs.capA }
117
+ · simpa [M.setReg] using hs.r15
118
+ · simpa [M.setReg] using hs.r14
119
+ · simpa [M.setReg] using hs.r13
120
+ · simpa [M.setReg] using hs.r12
121
+ · simp [M.setReg]
122
+ · intro i w hi
123
+ have hi : s.dstack[i]? = some w := hi
124
+ have hik : i < s.dstack.length := by
125
+ by_cases hh : i < s.dstack.length
126
+ · exact hh
127
+ · rw [List.getElem?_eq_none (by omega)] at hi; simp at hi
128
+ have hB : c.dEnd - 8 * s.dstack.length + 8 * i < 2 ^ 64 := by omega
129
+ have hne : c.sp0 - 8 * (s.rstack.length + 1) ≠ c.dEnd - 8 * s.dstack.length + 8 * i := by
130
+ rcases hg.dS_K with h | h <;> omega
131
+ show mem'.read? _ = some w
132
+ rw [read_write_nat hw hAlt hB]
133
+ simp only [hne, ite_false]
134
+ exact hs.stack i w hi
135
+ · intro r hr
136
+ have hrf := hg.rf_lt
137
+ have hne : c.sp0 - 8 * (s.rstack.length + 1) ≠ c.rf + 8 * r := by
138
+ rcases hg.dF_K with h | h <;> omega
139
+ show mem'.read? _ = some _
140
+ rw [read_write_nat hw hAlt (by omega)]
141
+ simp only [hne, ite_false]
142
+ exact hs.regs r hr
143
+ · intro a ha
144
+ have hmb := hg.mb_lt
145
+ have hne : c.sp0 - 8 * (s.rstack.length + 1) ≠ c.mb + 8 * a := by
146
+ rcases hg.dM_K with h | h <;> omega
147
+ show mem'.read? _ = some _
148
+ rw [read_write_nat hw hAlt (by omega)]
149
+ simp only [hne, ite_false]
150
+ exact hs.mem a ha
151
+ · intro i a hi
152
+ show mem'.read? _ = some _
153
+ simp only [List.length_cons]
154
+ cases i with
155
+ | zero =>
156
+ simp at hi; subst hi
157
+ rw [show c.sp0 - 8 * (s.rstack.length + 1) + 8 * 0 = c.sp0 - 8 * (s.rstack.length + 1) by omega,
158
+ read_write_nat hw hAlt hAlt]
159
+ simp [hpc, hret, ofN]
160
+ | succ i =>
161
+ simp only [List.getElem?_cons_succ] at hi
162
+ have hik : i < s.rstack.length := by
163
+ have := (List.getElem?_eq_some_iff.mp hi).1; simpa using this
164
+ have hB : c.sp0 - 8 * (s.rstack.length + 1) + 8 * (i + 1) < 2 ^ 64 := by omega
165
+ have hne : c.sp0 - 8 * (s.rstack.length + 1) ≠
166
+ c.sp0 - 8 * (s.rstack.length + 1) + 8 * (i + 1) := by omega
167
+ rw [read_write_nat hw hAlt hB]
168
+ simp only [hne, ite_false]
169
+ have e : c.sp0 - 8 * (s.rstack.length + 1) + 8 * (i + 1) =
170
+ c.sp0 - 8 * s.rstack.length + 8 * i := by omega
171
+ rw [e]; exact hs.rs i a hi
172
+ · have := Memory.write?_valid hw
173
+ show RegionsValid c mem'.valid
174
+ rw [this]; exact hs.xvalid
175
+ · simp only [List.length_cons]; omega
176
+
177
+ theorem sim_ret_ok (hg : Geom c) (hs : Rel0 c p s x) (hpc : x.pc = base)
178
+ (hat : XAt code base (lowerInstr L base off .ret)) {a : Nat} {rs : List Nat}
179
+ (hr : s.rstack = a :: rs) (hsz : offs p a < 2 ^ 64) :
180
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with rstack := rs } x' ∧ x'.pc = offs p a := by
181
+ have hat' : XAt code base [.cmp rsp r12, .jcc .ne (base + 3), .exitTrap .returnUnderflow, .ret] := hat
182
+ obtain ⟨f0, f1, f2, f3, _⟩ := hat'
183
+ have hcapR := hs.capR
184
+ have hrc := hg.rcap_le
185
+ have hsp := hg.sp0_lt
186
+ rw [hr] at hcapR
187
+ simp only [List.length_cons] at hcapR
188
+ let x1 : M := { x.adv with flags := some (subFlags (x.regs rsp) (x.regs r12)) }
189
+ have hst1 : step code x = .next x1 := by
190
+ rw [xstep_of_fetch (by rw [hpc]; exact f0)]; rfl
191
+ have hpc1 : x1.pc = base + 1 := by simp [x1, M.adv, hpc]
192
+ have hrsp : x.regs rsp = ofN (c.sp0 - 8 * (rs.length + 1)) := by rw [hs.rsp, hr]; simp
193
+ have hne : (x.regs rsp != x.regs r12) = true := by
194
+ rw [hrsp, hs.r12]
195
+ have : ofN (c.sp0 - 8 * (rs.length + 1)) ≠ ofN c.sp0 := by
196
+ intro e; have := ofN_inj (by omega) hsp e; omega
197
+ simpa using this
198
+ have hst2 : step code x1 = .next { x1 with pc := base + 3 } := by
199
+ rw [xstep_of_fetch (m := x1) (by rw [hpc1]; exact f1)]
200
+ simp [exec, x1, holds_ne, hne]
201
+ have hread := hs.rs 0 a (by simp [hr])
202
+ rw [hr] at hread
203
+ simp only [List.length_cons, Nat.mul_zero, Nat.add_zero] at hread
204
+ have hst3 : step code { x1 with pc := base + 3 } = .next { (x1.setReg rsp (ofN (c.sp0 - 8 * rs.length))) with
205
+ pc := offs p a } := by
206
+ rw [xstep_of_fetch (m := { x1 with pc := base + 3 }) f3]
207
+ have hx1r : x1.regs rsp = ofN (c.sp0 - 8 * (rs.length + 1)) := hrsp
208
+ have hx1m : x1.mem = x.mem := rfl
209
+ have hadd : ofN (c.sp0 - 8 * (rs.length + 1)) + 8#64 = ofN (c.sp0 - 8 * rs.length) := by
210
+ show ofN _ + ofN 8 = _
211
+ rw [ofN_add]; congr 1; omega
212
+ have hv : (ofN (offs p a)).toNat = offs p a := by
213
+ simp [ofN, BitVec.toNat_ofNat, Nat.mod_eq_of_lt hsz]
214
+ simp only [exec]
215
+ rw [hx1m, hx1r, hread]
216
+ simp only [hadd, hv]
217
+ rfl
218
+ refine ⟨_, ((XSteps.single hst1).trans (XSteps.single hst2)).trans (XSteps.single hst3), ?_, rfl⟩
219
+ refine { r15 := ?_, r14 := ?_, r13 := ?_, r12 := ?_, rsp := ?_, stack := ?_, regs := ?_,
220
+ mem := ?_, rs := ?_, irvalid := hs.irvalid, xvalid := ?_, capD := hs.capD, capR := ?_,
221
+ rbp := by simpa [M.setReg, x1, M.adv] using hs.rbp, aux := hs.aux, capA := hs.capA }
222
+ · simpa [M.setReg, x1, M.adv] using hs.r15
223
+ · simpa [M.setReg, x1, M.adv] using hs.r14
224
+ · simpa [M.setReg, x1, M.adv] using hs.r13
225
+ · simpa [M.setReg, x1, M.adv] using hs.r12
226
+ · simp [M.setReg]
227
+ · exact hs.stack
228
+ · exact hs.regs
229
+ · exact hs.mem
230
+ · intro i a' hi
231
+ have h := hs.rs (i + 1) a' (by simpa [hr] using hi)
232
+ rw [hr] at h
233
+ simp only [List.length_cons] at h
234
+ have e : c.sp0 - 8 * (rs.length + 1) + 8 * (i + 1) = c.sp0 - 8 * rs.length + 8 * i := by omega
235
+ rw [e] at h
236
+ exact h
237
+ · exact hs.xvalid
238
+ · show rs.length + 1 ≤ c.rcap
239
+ omega
240
+
241
+ theorem sim_ret_trap (hs : Rel0 c p s x) (hpc : x.pc = base)
242
+ (hat : XAt code base (lowerInstr L base off .ret)) (hr : s.rstack = []) :
243
+ XExec code x (.trapped .returnUnderflow) := by
244
+ have hat' : XAt code base [.cmp rsp r12, .jcc .ne (base + 3), .exitTrap .returnUnderflow, .ret] := hat
245
+ obtain ⟨f0, f1, f2, f3, _⟩ := hat'
246
+ let x1 : M := { x.adv with flags := some (subFlags (x.regs rsp) (x.regs r12)) }
247
+ have hst1 : step code x = .next x1 := by
248
+ rw [xstep_of_fetch (by rw [hpc]; exact f0)]; rfl
249
+ have hpc1 : x1.pc = base + 1 := by simp [x1, M.adv, hpc]
250
+ have heq : x.regs rsp = x.regs r12 := by rw [hs.rsp, hs.r12, hr]; simp
251
+ have hfl : x1.flags = some (subFlags (x.regs rsp) (x.regs rsp)) := by simp [x1, heq]
252
+ have hhold : Cc.ne.holds (subFlags (x.regs rsp) (x.regs rsp)) = false := by
253
+ rw [holds_ne]; simp
254
+ exact XExec.of_steps (XSteps.single hst1)
255
+ (guard_trap (code := code) (pc := base + 1) (m := x1) (c := .ne) (t := .returnUnderflow)
256
+ (f := subFlags (x.regs rsp) (x.regs rsp)) ⟨f1, f2, trivial⟩ hpc1 hfl hhold)
257
+
258
+ /-- `call t`: the return-stack guard, then the native `call`. -/
259
+ theorem sim_call_ok (hg : Geom c) (hs : Rel0 c p s x) (hpc : x.pc = base) {t : Nat}
260
+ (hLr : L.rGap = ofN (8 * c.rcap - 16)) (hat : XAt code base (lowerInstr L base off (.call t)))
261
+ (hret : base + isize (.call t : WordDialect.Instr 64) = offs p (s.pc + 1))
262
+ (hfitR : s.rstack.length + 2 ≤ c.rcap) :
263
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with rstack := (s.pc + 1) :: s.rstack } x' ∧
264
+ x'.pc = off t := by
265
+ have hat' : XAt code base (ovfR L base ++ [.call (off t)]) := hat
266
+ obtain ⟨hato, hatc⟩ := xat_append.mp hat'
267
+ have hl : (ovfR L base).length = 6 := rfl
268
+ rw [hl] at hatc
269
+ obtain ⟨x1, hs1, hr1, hpc1, _⟩ := ovfR_pass hg hs hLr hfitR hpc hato
270
+ obtain ⟨x', hs', hr', hpc'⟩ := sim_call_core hg hr1 hpc1 hatc (by simpa [isize] using hret) hfitR
271
+ exact ⟨x', hs1.trans hs', hr', hpc'⟩
272
+
273
+ theorem sim_call_ovf (hg : Geom c) (hs : Rel0 c p s x) (hpc : x.pc = base) {t : Nat}
274
+ (hLr : L.rGap = ofN (8 * c.rcap - 16)) (hat : XAt code base (lowerInstr L base off (.call t)))
275
+ (h : ¬ s.rstack.length + 2 ≤ c.rcap) : XExec code x .overflow := by
276
+ have hat' : XAt code base (ovfR L base ++ [.call (off t)]) := hat
277
+ exact ovfR_trap hg hs hLr h hpc (xat_append.mp hat').1
278
+
279
+ end Ctl
280
+
281
+ end X86
282
+ end WordDialect
backend/x86_64/X86/SimDiv.lean ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.SimMemOps
2
+
3
+ /-!
4
+ # X86.SimDiv
5
+
6
+ `select`, `div`, `sdiv`: the three-operand select, and the divisions with their zero guard
7
+ (`divideByZero` trap), the `-1` special case of `sdiv` (avoiding the hardware overflow fault), and
8
+ the relation between `idiv` and `BitVec.sdiv`.
9
+ -/
10
+
11
+ namespace WordDialect
12
+ namespace X86
13
+
14
+ open Reg
15
+
16
+ section Div
17
+
18
+ variable {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)}
19
+ {L : Layout} {base : Nat} {off : Nat → Nat}
20
+
21
+ /-- `Rel0` ignores scratch registers. -/
22
+ theorem rel0_scratch (hs : Rel0 c p s x) {x' : M} (hm : x'.mem = x.mem)
23
+ (hr : ∀ r, r ≠ rax → r ≠ rcx → r ≠ rdx → r ≠ rbx → x'.regs r = x.regs r) : Rel0 c p s x' :=
24
+ hs.congr hm (hr _ (by decide) (by decide) (by decide) (by decide))
25
+ (hr _ (by decide) (by decide) (by decide) (by decide))
26
+ (hr _ (by decide) (by decide) (by decide) (by decide))
27
+ (hr _ (by decide) (by decide) (by decide) (by decide))
28
+ (hr _ (by decide) (by decide) (by decide) (by decide))
29
+ (hr _ (by decide) (by decide) (by decide) (by decide))
30
+
31
+ theorem zg_pass (hs : Rel0 c p s x) {b : W} (hb : x.regs rcx = b) (hne : b ≠ 0#64) {t : Nat}
32
+ (ht : t = x.pc + 3)
33
+ (hat : XAt code x.pc [.test rcx rcx, .jcc .ne t, .exitTrap .divideByZero]) :
34
+ ∃ x', XSteps code x x' ∧ Rel0 c p s x' ∧ x'.pc = x.pc + 3 ∧ x'.regs = x.regs ∧
35
+ x'.mem = x.mem := by
36
+ obtain ⟨f0, f1, f2, _⟩ := hat
37
+ let x1 : M := { x.adv with flags := some (logicFlags (x.regs rcx &&& x.regs rcx)) }
38
+ have hst1 : step code x = .next x1 := by rw [xstep_of_fetch f0]; rfl
39
+ have hpc1 : x1.pc = x.pc + 1 := by simp [x1, M.adv]
40
+ have hfl : x1.flags = some (logicFlags (b &&& b)) := by simp [x1, hb]
41
+ have hhold : Cc.ne.holds (logicFlags (b &&& b)) = true := by simp [Cc.holds, logicFlags, hne]
42
+ have e : x.pc + 1 + 2 = t := by omega
43
+ have hg := guard_pass (code := code) (pc := x.pc + 1) (m := x1) (c := .ne) (t := .divideByZero)
44
+ (f := logicFlags (b &&& b)) (by rw [e]; exact ⟨f1, f2, trivial⟩) hpc1 hfl hhold
45
+ refine ⟨{ x1 with pc := x.pc + 1 + 2 }, (XSteps.single hst1).trans hg, ?_, by simp, rfl, rfl⟩
46
+ exact hs.congr rfl rfl rfl rfl rfl rfl rfl
47
+
48
+ theorem zg_trap (hs : Rel0 c p s x) (hb : x.regs rcx = 0#64) {t : Nat}
49
+ (hat : XAt code x.pc [.test rcx rcx, .jcc .ne t, .exitTrap .divideByZero]) (ht : t = x.pc + 3) :
50
+ XExec code x (.trapped .divideByZero) := by
51
+ obtain ⟨f0, f1, f2, _⟩ := hat
52
+ let x1 : M := { x.adv with flags := some (logicFlags (x.regs rcx &&& x.regs rcx)) }
53
+ have hst1 : step code x = .next x1 := by rw [xstep_of_fetch f0]; rfl
54
+ have hpc1 : x1.pc = x.pc + 1 := by simp [x1, M.adv]
55
+ have hfl : x1.flags = some (logicFlags (0#64 &&& 0#64)) := by simp [x1, hb]
56
+ have hhold : Cc.ne.holds (logicFlags (0#64 &&& 0#64)) = false := by simp [Cc.holds, logicFlags]
57
+ have e : x.pc + 1 + 2 = t := by omega
58
+ exact XExec.of_steps (XSteps.single hst1)
59
+ (guard_trap (code := code) (pc := x.pc + 1) (m := x1) (c := .ne) (t := .divideByZero)
60
+ (f := logicFlags (0#64 &&& 0#64)) (by rw [e]; exact ⟨f1, f2, trivial⟩) hpc1 hfl hhold)
61
+
62
+ theorem div_step (hf : code[x.pc]? = some (.div rcx)) {a b : W} (ha : x.regs rax = a)
63
+ (hb : x.regs rcx = b) (hd : x.regs rdx = 0#64) (hne : b ≠ 0#64) :
64
+ ∃ x', step code x = .next x' ∧ x'.regs rax = a.udiv b ∧ x'.pc = x.pc + 1 ∧
65
+ x'.mem = x.mem ∧ (∀ r, r ≠ rax → r ≠ rdx → x'.regs r = x.regs r) := by
66
+ have hq : a.toNat / b.toNat < 2 ^ 64 := Nat.lt_of_le_of_lt (Nat.div_le_self _ _) a.isLt
67
+ have e : step code x = .next { ((x.setReg rax (BitVec.ofNat 64 (a.toNat / b.toNat))).setReg rdx
68
+ (BitVec.ofNat 64 (a.toNat % b.toNat))) with flags := none, pc := x.pc + 1 } := by
69
+ rw [xstep_of_fetch hf]
70
+ simp [exec, hb, hd, ha, hne, hq]
71
+ refine ⟨_, e, ?_, rfl, rfl, ?_⟩
72
+ · simp [M.setReg, BitVec.udiv_def]
73
+ · intro r h1 h2; simp [M.setReg, h1, h2]
74
+
75
+ theorem sim_div_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
76
+ (hat : XAt code base (lowerInstr L base off .div)) {a b : W} {d : List W}
77
+ (hd : s.dstack = b :: a :: d) (hb : b ≠ 0#64) :
78
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := a.udiv b :: d } x' ∧
79
+ x'.pc = base + isize (.div : WordDialect.Instr 64) := by
80
+ have hat' : XAt code base (uf L 2 base ++ [.load rcx Reg.r15 0, .load rax Reg.r15 8, .test rcx rcx,
81
+ .jcc .ne (base + ufSize 2 + 5), .exitTrap .divideByZero, .movImm rdx 0#64, .div rcx,
82
+ .store Reg.r15 8 rax, .addImm Reg.r15 8]) := hat
83
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
84
+ have hufl : (uf L 2 base).length = 4 := by simp [uf]
85
+ rw [hufl] at hatr
86
+ obtain ⟨f0, f1, f2, f3, f4, f5, f6, f7, f8, _⟩ := hatr
87
+ have hk : 2 ≤ s.dstack.length := by simp [hd]
88
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
89
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, ho2, _⟩ := ld_step (code := code) hr1 (j := 0) (v := b)
90
+ (by simp [hd]) (rd := rcx) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
91
+ obtain ⟨x3, hs3, hr3, hpc3, hv3, ho3, _⟩ := ld_step (code := code) hr2 (j := 1) (v := a)
92
+ (by simp [hd]) (rd := rax) (by simp) (disp := 8) (by simp) (fetch_cast f1 (by omega))
93
+ have hcx3 : x3.regs rcx = b := by rw [ho3 rcx (by decide), hv2]
94
+ obtain ⟨x4, hs4, hr4, hpc4, hreg4, _⟩ := zg_pass (code := code) hr3 hcx3 hb
95
+ (t := base + ufSize 2 + 5) (by simp [ufSize]; omega)
96
+ ⟨fetch_cast f2 (by omega), fetch_cast f3 (by omega), fetch_cast f4 (by omega), trivial⟩
97
+ obtain ⟨x5, hs5, hr5, hpc5, hv5, ho5, hm5⟩ := movimm_step (code := code) hr4 (rd := rdx) (by simp)
98
+ (v := 0#64) (fetch_cast f5 (by omega))
99
+ have hax5 : x5.regs rax = a := by rw [ho5 rax (by decide), hreg4, hv3]
100
+ have hcx5 : x5.regs rcx = b := by rw [ho5 rcx (by decide), hreg4, hcx3]
101
+ obtain ⟨x6, hst6, hax6, hpc6, hm6, ho6⟩ := div_step (code := code) (x := x5)
102
+ (fetch_cast f6 (by omega)) hax5 hcx5 hv5 hb
103
+ have hr6 : Rel0 c p s x6 := rel0_scratch hr5 hm6 (fun r h1 h2 h3 h4 => ho6 r h1 h3)
104
+ obtain ⟨x7, hs7, hr7, hpc7, hreg7⟩ := st_step (code := code) hg hr6 (j := 1) (by simp [hd])
105
+ (rs := rax) (disp := 8) (by simp) (fetch_cast f7 (by omega))
106
+ have hlen : 1 ≤ ({ s with dstack := s.dstack.set 1 (x6.regs rax) } : State 64).dstack.length := by
107
+ simp [hd]
108
+ obtain ⟨x8, hs8, hr8, hpc8, _⟩ := adj_step (code := code) hg hr7 (n := 1) hlen (δ := 8) (by simp)
109
+ (fetch_cast f8 (by omega))
110
+ have hr8' : Rel0 c p { s with dstack := (s.dstack.set 1 (x6.regs rax)).drop 1 } x8 := hr8
111
+ have e : (s.dstack.set 1 (x6.regs rax)).drop 1 = a.udiv b :: d := by simp [hd, hax6]
112
+ rw [e] at hr8'
113
+ exact ⟨x8, ((((hs1.trans hs2).trans hs3).trans hs4).trans hs5).trans
114
+ ((XSteps.single hst6).trans (hs7.trans hs8)), hr8', by simp [isize, ufSize]; omega⟩
115
+
116
+ theorem sim_div_zero (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x)
117
+ (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .div)) {a : W} {d : List W}
118
+ (hd : s.dstack = 0#64 :: a :: d) : XExec code x (.trapped .divideByZero) := by
119
+ have hat' : XAt code base (uf L 2 base ++ [.load rcx Reg.r15 0, .load rax Reg.r15 8, .test rcx rcx,
120
+ .jcc .ne (base + ufSize 2 + 5), .exitTrap .divideByZero, .movImm rdx 0#64, .div rcx,
121
+ .store Reg.r15 8 rax, .addImm Reg.r15 8]) := hat
122
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
123
+ have hufl : (uf L 2 base).length = 4 := by simp [uf]
124
+ rw [hufl] at hatr
125
+ obtain ⟨f0, f1, f2, f3, f4, _⟩ := hatr
126
+ have hk : 2 ≤ s.dstack.length := by simp [hd]
127
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
128
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, ho2, _⟩ := ld_step (code := code) hr1 (j := 0) (v := 0#64)
129
+ (by simp [hd]) (rd := rcx) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
130
+ obtain ⟨x3, hs3, hr3, hpc3, hv3, ho3, _⟩ := ld_step (code := code) hr2 (j := 1) (v := a)
131
+ (by simp [hd]) (rd := rax) (by simp) (disp := 8) (by simp) (fetch_cast f1 (by omega))
132
+ have hcx3 : x3.regs rcx = 0#64 := by rw [ho3 rcx (by decide), hv2]
133
+ exact XExec.of_steps (((hs1.trans hs2).trans hs3)) (zg_trap (code := code) hr3 hcx3
134
+ ⟨fetch_cast f2 (by omega), fetch_cast f3 (by omega), fetch_cast f4 (by omega), trivial⟩
135
+ (t := base + ufSize 2 + 5) (by simp [ufSize]; omega))
136
+
137
+ theorem ofInt_m1 : BitVec.ofInt 64 (-1) = -(1#64) := by decide
138
+
139
+ theorem sdiv_m1 (a : W) : a.sdiv (BitVec.ofInt 64 (-1)) = 0#64 - a := by
140
+ rw [ofInt_m1, BitVec.sdiv_neg (by decide), BitVec.sdiv_one]; simp
141
+
142
+ theorem dividend_eq (a : W) :
143
+ (if a.msb then BitVec.allOnes 64 else 0#64).toInt * 2 ^ 64 + (a.toNat : Int) = a.toInt := by
144
+ rw [BitVec.toInt_eq_msb_cond a]
145
+ cases h : a.msb <;> simp [h] <;> omega
146
+
147
+ theorem idiv_step (hf : code[x.pc]? = some (.idiv rcx)) {a b : W} (ha : x.regs rax = a)
148
+ (hb : x.regs rcx = b) (hd : x.regs rdx = if a.msb then BitVec.allOnes 64 else 0#64)
149
+ (hne : b ≠ 0#64) (hm1 : b ≠ -(1#64)) :
150
+ ∃ x', step code x = .next x' ∧ x'.regs rax = a.sdiv b ∧ x'.pc = x.pc + 1 ∧
151
+ x'.mem = x.mem ∧ (∀ r, r ≠ rax → r ≠ rdx → x'.regs r = x.regs r) := by
152
+ have hq : (a.sdiv b).toInt = Int.tdiv a.toInt b.toInt :=
153
+ BitVec.toInt_sdiv_of_ne_or_ne a b (Or.inr hm1)
154
+ have hlo := BitVec.le_toInt (a.sdiv b)
155
+ have hhi : (a.sdiv b).toInt < 2 ^ (64 - 1) := BitVec.toInt_lt
156
+ rw [hq] at hlo hhi
157
+ have hrange : -(2 ^ 63 : Int) ≤ Int.tdiv a.toInt b.toInt ∧ Int.tdiv a.toInt b.toInt < 2 ^ 63 := by
158
+ constructor <;> simpa using ‹_›
159
+ have hdiv : ((x.regs rdx).toInt * 2 ^ 64 + ((x.regs rax).toNat : Int)) = a.toInt := by
160
+ rw [hd, ha]; exact dividend_eq a
161
+ have hofq : BitVec.ofInt 64 (Int.tdiv a.toInt b.toInt) = a.sdiv b := by
162
+ rw [← hq]; simp
163
+ have e : step code x = .next { ((x.setReg rax (a.sdiv b)).setReg rdx
164
+ (BitVec.ofInt 64 (Int.tmod a.toInt b.toInt))) with flags := none, pc := x.pc + 1 } := by
165
+ rw [xstep_of_fetch hf]
166
+ simp only [exec, hb, hne, ite_false, hdiv, hrange, and_self, ite_true, hofq]
167
+ refine ⟨_, e, ?_, rfl, rfl, ?_⟩
168
+ · simp [M.setReg]
169
+ · intro r h1 h2; simp [M.setReg, h1, h2]
170
+
171
+ /-- Core of `sdiv`, from the `cmpImm rcx, -1` on: both branches end with `rax = a.sdiv b`. -/
172
+ theorem sdiv_core (hs : Rel0 c p s x) {a b : W} (ha : x.regs rax = a) (hb : x.regs rcx = b)
173
+ (hne : b ≠ 0#64) {t1 t2 : Nat} (ht1 : t1 = x.pc + 4) (ht2 : t2 = x.pc + 6)
174
+ (hat : XAt code x.pc [.cmpImm rcx (-1), .jcc .ne t1, .neg rax, .jmp t2, .cqo, .idiv rcx]) :
175
+ ∃ x', XSteps code x x' ∧ Rel0 c p s x' ∧ x'.pc = x.pc + 6 ∧ x'.regs rax = a.sdiv b := by
176
+ obtain ⟨f0, f1, f2, f3, f4, f5, _⟩ := hat
177
+ let x1 : M := { x.adv with flags := some (subFlags (x.regs rcx) (BitVec.ofInt 64 (-1))) }
178
+ have hst1 : step code x = .next x1 := by rw [xstep_of_fetch f0]; rfl
179
+ have hpc1 : x1.pc = x.pc + 1 := by simp [x1, M.adv]
180
+ have hfl : x1.flags = some (subFlags b (BitVec.ofInt 64 (-1))) := by simp [x1, hb]
181
+ have hr1 : Rel0 c p s x1 := hs.congr rfl rfl rfl rfl rfl rfl rfl
182
+ have hax1 : x1.regs rax = a := by simpa [x1, M.adv] using ha
183
+ have hcx1 : x1.regs rcx = b := by simpa [x1, M.adv] using hb
184
+ by_cases hb1 : b = BitVec.ofInt 64 (-1)
185
+ · -- b = -1: jcc falls through, neg rax, jmp to the join point
186
+ have hst2 : step code x1 = .next x1.adv := by
187
+ rw [xstep_of_fetch (m := x1) (fetch_cast f1 (by omega))]
188
+ simp [exec, hfl, holds_ne, hb1, M.adv]
189
+ have hpc2 : x1.adv.pc = x.pc + 2 := by simp [M.adv, hpc1]
190
+ have hr2 : Rel0 c p s x1.adv := hr1.congr rfl rfl rfl rfl rfl rfl rfl
191
+ have hax2 : x1.adv.regs rax = a := by simpa [M.adv] using hax1
192
+ let x3 := x1.adv.arith rax (0#64 - x1.adv.regs rax)
193
+ have hst3 : step code x1.adv = .next x3 := by
194
+ rw [xstep_of_fetch (m := x1.adv) (fetch_cast f2 (by omega))]; rfl
195
+ have hpc3 : x3.pc = x.pc + 3 := by simp [x3, M.arith, M.setReg, hpc2]
196
+ have hr3 : Rel0 c p s x3 := arith_rel0 hr2 rax (scratch_ne rax (by simp)) _
197
+ have hst4 : step code x3 = .next { x3 with pc := t2 } := by
198
+ rw [xstep_of_fetch (m := x3) (fetch_cast f3 (by omega))]; rfl
199
+ refine ⟨{ x3 with pc := t2 }, (((XSteps.single hst1).trans (XSteps.single hst2)).trans
200
+ (XSteps.single hst3)).trans (XSteps.single hst4), hr3.congr rfl rfl rfl rfl rfl rfl rfl,
201
+ by simp [ht2], ?_⟩
202
+ have h3 : x3.regs rax = 0#64 - a := by simp [x3, M.arith, M.setReg, hax2]
203
+ show x3.regs rax = a.sdiv b
204
+ rw [h3, hb1, sdiv_m1]
205
+ · -- b ≠ -1: jump to cqo; idiv
206
+ have hbm : b ≠ -(1#64) := by rwa [← ofInt_m1]
207
+ have hst2 : step code x1 = .next { x1 with pc := t1 } := by
208
+ rw [xstep_of_fetch (m := x1) (fetch_cast f1 (by omega))]
209
+ have hb1' : (b != BitVec.ofInt 64 (-1)) = true := by simpa using hb1
210
+ simp only [exec, hfl, holds_ne, hb1', ite_true]
211
+ have hr2 : Rel0 c p s { x1 with pc := t1 } := hr1.congr rfl rfl rfl rfl rfl rfl rfl
212
+ have hax2 : ({ x1 with pc := t1 } : M).regs rax = a := hax1
213
+ have hcx2 : ({ x1 with pc := t1 } : M).regs rcx = b := hcx1
214
+ let x3 : M := ({ x1 with pc := t1 } : M).mov rdx (if (({ x1 with pc := t1 } : M).regs rax).msb
215
+ then BitVec.allOnes 64 else 0#64)
216
+ have hst3 : step code { x1 with pc := t1 } = .next x3 := by
217
+ rw [xstep_of_fetch (m := { x1 with pc := t1 }) (fetch_cast f4 (by simp; omega))]; rfl
218
+ have hr3 : Rel0 c p s x3 := mov_rel0 hr2 rdx (scratch_ne rdx (by simp)) _
219
+ have hpc3 : x3.pc = x.pc + 5 := by simp [x3, M.mov, M.setReg, M.adv]; omega
220
+ have hax3 : x3.regs rax = a := by simp [x3, M.mov, M.setReg, M.adv]; exact hax2
221
+ have hcx3 : x3.regs rcx = b := by simp [x3, M.mov, M.setReg, M.adv]; exact hcx2
222
+ have hdx3 : x3.regs rdx = if a.msb then BitVec.allOnes 64 else 0#64 := by
223
+ simp [x3, M.mov, M.setReg, M.adv]; rw [hax2]
224
+ obtain ⟨x4, hst4, hax4, hpc4, hm4, ho4⟩ := idiv_step (code := code) (x := x3)
225
+ (fetch_cast f5 (by omega)) hax3 hcx3 hdx3 hne hbm
226
+ refine ⟨x4, (((XSteps.single hst1).trans (XSteps.single hst2)).trans (XSteps.single hst3)).trans
227
+ (XSteps.single hst4), rel0_scratch hr3 hm4 (fun r h1 h2 h3 h4 => ho4 r h1 h3), by omega, hax4⟩
228
+
229
+ theorem sim_sdiv_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
230
+ (hat : XAt code base (lowerInstr L base off .sdiv)) {a b : W} {d : List W}
231
+ (hd : s.dstack = b :: a :: d) (hb : b ≠ 0#64) :
232
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := a.sdiv b :: d } x' ∧
233
+ x'.pc = base + isize (.sdiv : WordDialect.Instr 64) := by
234
+ have hat' : XAt code base (uf L 2 base ++ [.load rcx Reg.r15 0, .load rax Reg.r15 8, .test rcx rcx,
235
+ .jcc .ne (base + ufSize 2 + 5), .exitTrap .divideByZero, .cmpImm rcx (-1),
236
+ .jcc .ne (base + ufSize 2 + 9), .neg rax, .jmp (base + ufSize 2 + 11), .cqo, .idiv rcx,
237
+ .store Reg.r15 8 rax, .addImm Reg.r15 8]) := hat
238
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
239
+ have hufl : (uf L 2 base).length = 4 := by simp [uf]
240
+ rw [hufl] at hatr
241
+ obtain ⟨f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, _⟩ := hatr
242
+ have hk : 2 ≤ s.dstack.length := by simp [hd]
243
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
244
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, ho2, _⟩ := ld_step (code := code) hr1 (j := 0) (v := b)
245
+ (by simp [hd]) (rd := rcx) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
246
+ obtain ⟨x3, hs3, hr3, hpc3, hv3, ho3, _⟩ := ld_step (code := code) hr2 (j := 1) (v := a)
247
+ (by simp [hd]) (rd := rax) (by simp) (disp := 8) (by simp) (fetch_cast f1 (by omega))
248
+ have hcx3 : x3.regs rcx = b := by rw [ho3 rcx (by decide), hv2]
249
+ obtain ⟨x4, hs4, hr4, hpc4, hreg4, _⟩ := zg_pass (code := code) hr3 hcx3 hb
250
+ (t := base + ufSize 2 + 5) (by simp [ufSize]; omega)
251
+ ⟨fetch_cast f2 (by omega), fetch_cast f3 (by omega), fetch_cast f4 (by omega), trivial⟩
252
+ have hax4 : x4.regs rax = a := by rw [hreg4, hv3]
253
+ have hcx4 : x4.regs rcx = b := by rw [hreg4, hcx3]
254
+ obtain ⟨x5, hs5, hr5, hpc5, hax5⟩ := sdiv_core (code := code) hr4 hax4 hcx4 hb
255
+ (t1 := base + ufSize 2 + 9) (t2 := base + ufSize 2 + 11) (by simp [ufSize]; omega)
256
+ (by simp [ufSize]; omega)
257
+ ⟨fetch_cast f5 (by omega), fetch_cast f6 (by omega), fetch_cast f7 (by omega),
258
+ fetch_cast f8 (by omega), fetch_cast f9 (by omega), fetch_cast f10 (by omega), trivial⟩
259
+ obtain ⟨x6, hs6, hr6, hpc6, hreg6⟩ := st_step (code := code) hg hr5 (j := 1) (by simp [hd])
260
+ (rs := rax) (disp := 8) (by simp) (fetch_cast f11 (by omega))
261
+ have hlen : 1 ≤ ({ s with dstack := s.dstack.set 1 (x5.regs rax) } : State 64).dstack.length := by
262
+ simp [hd]
263
+ obtain ⟨x7, hs7, hr7, hpc7, _⟩ := adj_step (code := code) hg hr6 (n := 1) hlen (δ := 8) (by simp)
264
+ (fetch_cast f12 (by omega))
265
+ have hr7' : Rel0 c p { s with dstack := (s.dstack.set 1 (x5.regs rax)).drop 1 } x7 := hr7
266
+ have e : (s.dstack.set 1 (x5.regs rax)).drop 1 = a.sdiv b :: d := by simp [hd, hax5]
267
+ rw [e] at hr7'
268
+ exact ⟨x7, (((((hs1.trans hs2).trans hs3).trans hs4).trans hs5).trans hs6).trans hs7, hr7',
269
+ by simp [isize, ufSize]; omega⟩
270
+
271
+ theorem sim_sdiv_zero (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x)
272
+ (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .sdiv)) {a : W} {d : List W}
273
+ (hd : s.dstack = 0#64 :: a :: d) : XExec code x (.trapped .divideByZero) := by
274
+ have hat' : XAt code base (uf L 2 base ++ [.load rcx Reg.r15 0, .load rax Reg.r15 8, .test rcx rcx,
275
+ .jcc .ne (base + ufSize 2 + 5), .exitTrap .divideByZero]) := by
276
+ have h : XAt code base (uf L 2 base ++ [.load rcx Reg.r15 0, .load rax Reg.r15 8, .test rcx rcx,
277
+ .jcc .ne (base + ufSize 2 + 5), .exitTrap .divideByZero, .cmpImm rcx (-1),
278
+ .jcc .ne (base + ufSize 2 + 9), .neg rax, .jmp (base + ufSize 2 + 11), .cqo, .idiv rcx,
279
+ .store Reg.r15 8 rax, .addImm Reg.r15 8]) := hat
280
+ obtain ⟨h1, h2⟩ := xat_append.mp h
281
+ refine xat_append.mpr ⟨h1, ?_⟩
282
+ obtain ⟨g0, g1, g2, g3, g4, _⟩ := h2
283
+ exact ⟨g0, g1, g2, g3, g4, trivial⟩
284
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
285
+ have hufl : (uf L 2 base).length = 4 := by simp [uf]
286
+ rw [hufl] at hatr
287
+ obtain ⟨f0, f1, f2, f3, f4, _⟩ := hatr
288
+ have hk : 2 ≤ s.dstack.length := by simp [hd]
289
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
290
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, ho2, _⟩ := ld_step (code := code) hr1 (j := 0) (v := 0#64)
291
+ (by simp [hd]) (rd := rcx) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
292
+ obtain ⟨x3, hs3, hr3, hpc3, hv3, ho3, _⟩ := ld_step (code := code) hr2 (j := 1) (v := a)
293
+ (by simp [hd]) (rd := rax) (by simp) (disp := 8) (by simp) (fetch_cast f1 (by omega))
294
+ have hcx3 : x3.regs rcx = 0#64 := by rw [ho3 rcx (by decide), hv2]
295
+ exact XExec.of_steps (((hs1.trans hs2)).trans hs3) (zg_trap (code := code) hr3 hcx3
296
+ ⟨fetch_cast f2 (by omega), fetch_cast f3 (by omega), fetch_cast f4 (by omega), trivial⟩
297
+ (t := base + ufSize 2 + 5) (by simp [ufSize]; omega))
298
+
299
+ theorem cmov_ne_step (hf : code[x.pc]? = some (.cmov .ne rax rbx)) {cc : W}
300
+ (hfl : x.flags = some (logicFlags (cc &&& cc))) :
301
+ ∃ x', step code x = .next x' ∧ x'.pc = x.pc + 1 ∧ x'.mem = x.mem ∧
302
+ x'.regs rax = (if cc = 0#64 then x.regs rax else x.regs rbx) ∧
303
+ (∀ r, r ≠ rax → x'.regs r = x.regs r) := by
304
+ by_cases hc : cc = 0#64
305
+ · subst hc
306
+ refine ⟨x.adv, ?_, by simp [M.adv], by simp [M.adv], by simp [M.adv], ?_⟩
307
+ · rw [xstep_of_fetch hf]; simp [exec, hfl, Cc.holds, logicFlags]
308
+ · intro r _; simp [M.adv]
309
+ · refine ⟨x.mov rax (x.regs rbx), ?_, by simp [M.mov, M.setReg, M.adv],
310
+ by simp [M.mov, M.setReg, M.adv], by simp [M.mov, M.setReg, M.adv, hc], ?_⟩
311
+ · rw [xstep_of_fetch hf]; simp [exec, hfl, Cc.holds, logicFlags, hc]
312
+ · intro r h; simp [M.mov, M.setReg, M.adv, h]
313
+
314
+ theorem sim_select_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x)
315
+ (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .select)) {cc y v : W}
316
+ {d : List W} (hd : s.dstack = cc :: y :: v :: d) :
317
+ ∃ x', XSteps code x x' ∧
318
+ Rel0 c p { s with dstack := (if Word.isTrue cc then v else y) :: d } x' ∧
319
+ x'.pc = base + isize (.select : WordDialect.Instr 64) := by
320
+ have hat' : XAt code base (uf L 3 base ++ [.load rcx Reg.r15 0, .load rax Reg.r15 8,
321
+ .load rbx Reg.r15 16, .test rcx rcx, .cmov .ne rax rbx, .store Reg.r15 16 rax,
322
+ .addImm Reg.r15 16]) := hat
323
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
324
+ have hufl : (uf L 3 base).length = 4 := by simp [uf]
325
+ rw [hufl] at hatr
326
+ obtain ⟨f0, f1, f2, f3, f4, f5, f6, _⟩ := hatr
327
+ have hk : 3 ≤ s.dstack.length := by simp [hd]
328
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
329
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, ho2, _⟩ := ld_step (code := code) hr1 (j := 0) (v := cc)
330
+ (by simp [hd]) (rd := rcx) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
331
+ obtain ⟨x3, hs3, hr3, hpc3, hv3, ho3, _⟩ := ld_step (code := code) hr2 (j := 1) (v := y)
332
+ (by simp [hd]) (rd := rax) (by simp) (disp := 8) (by simp) (fetch_cast f1 (by omega))
333
+ obtain ⟨x4, hs4, hr4, hpc4, hv4, ho4, _⟩ := ld_step (code := code) hr3 (j := 2) (v := v)
334
+ (by simp [hd]) (rd := rbx) (by simp) (disp := 16) (by simp) (fetch_cast f2 (by omega))
335
+ have hcx4 : x4.regs rcx = cc := by rw [ho4 rcx (by decide), ho3 rcx (by decide), hv2]
336
+ have hax4 : x4.regs rax = y := by rw [ho4 rax (by decide), hv3]
337
+ -- test rcx, rcx
338
+ let x5 : M := { x4.adv with flags := some (logicFlags (x4.regs rcx &&& x4.regs rcx)) }
339
+ have hst5 : step code x4 = .next x5 := by
340
+ rw [xstep_of_fetch (m := x4) (fetch_cast f3 (by omega))]; rfl
341
+ have hr5 : Rel0 c p s x5 := hr4.congr rfl rfl rfl rfl rfl rfl rfl
342
+ have hpc5 : x5.pc = base + 4 + 4 := by simp [x5, M.adv]; omega
343
+ have hfl5 : x5.flags = some (logicFlags (cc &&& cc)) := by simp [x5, hcx4]
344
+ have hax5 : x5.regs rax = y := by simpa [x5, M.adv] using hax4
345
+ have hbx5 : x5.regs rbx = v := by simpa [x5, M.adv] using hv4
346
+ obtain ⟨x6, hst6, hpc6, hm6, hax6, ho6⟩ := cmov_ne_step (code := code) (x := x5)
347
+ (fetch_cast f4 (by omega)) hfl5
348
+ have hr6 : Rel0 c p s x6 := rel0_scratch hr5 hm6 (fun r h1 h2 h3 h4 => ho6 r h1)
349
+ have hval : x6.regs rax = (if Word.isTrue cc then v else y) := by
350
+ rw [hax6, hax5, hbx5]
351
+ by_cases hc : cc = 0#64 <;> simp [Word.isTrue, hc]
352
+ obtain ⟨x7, hs7, hr7, hpc7, _⟩ := st_step (code := code) hg hr6 (j := 2) (by simp [hd])
353
+ (rs := rax) (disp := 16) (by simp) (fetch_cast f5 (by omega))
354
+ have hlen : 2 ≤ ({ s with dstack := s.dstack.set 2 (x6.regs rax) } : State 64).dstack.length := by
355
+ simp [hd]
356
+ obtain ⟨x8, hs8, hr8, hpc8, _⟩ := adj_step (code := code) hg hr7 (n := 2) hlen (δ := 16) (by simp)
357
+ (fetch_cast f6 (by omega))
358
+ have hr8' : Rel0 c p { s with dstack := (s.dstack.set 2 (x6.regs rax)).drop 2 } x8 := hr8
359
+ have e : (s.dstack.set 2 (x6.regs rax)).drop 2 = (if Word.isTrue cc then v else y) :: d := by
360
+ simp [hd, hval]
361
+ rw [e] at hr8'
362
+ exact ⟨x8, (((((((hs1.trans hs2).trans hs3).trans hs4).trans (XSteps.single hst5)).trans
363
+ (XSteps.single hst6)).trans hs7).trans hs8), hr8', by simp [isize, ufSize]; omega⟩
364
+
365
+ end Div
366
+
367
+ end X86
368
+ end WordDialect
backend/x86_64/X86/SimMem.lean ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.SimStack
2
+
3
+ /-!
4
+ # X86.SimMem
5
+
6
+ Register-file and IR-memory cells: frame lemmas for writes into those regions, the address
7
+ bounds guard, and the simulation theorems for `push pop load store` (success and `badAddress`).
8
+ -/
9
+
10
+ namespace WordDialect
11
+ namespace X86
12
+
13
+ open Reg
14
+
15
+ theorem ofN_toNat (a : W) : ofN a.toNat = a := by
16
+ apply BitVec.eq_of_toNat_eq
17
+ simp [ofN, BitVec.toNat_ofNat, Nat.mod_eq_of_lt a.isLt]
18
+
19
+ theorem eaIdx_nat (mb : Nat) (a : W) : ofN mb + a * 8#64 = ofN (mb + 8 * a.toNat) := by
20
+ have h : a * 8#64 = ofN (8 * a.toNat) := by
21
+ have e : a = ofN a.toNat := (ofN_toNat a).symm
22
+ calc a * 8#64 = ofN a.toNat * 8#64 := by rw [← e]
23
+ _ = ofN (8 * a.toNat) := by
24
+ simp only [ofN]; rw [Nat.mul_comm 8, BitVec.ofNat_mul]
25
+ rw [h, ofN_add]
26
+
27
+ section Frame
28
+
29
+ variable {c : Cfg} {p : Prog 64} {s : State 64} {x : M}
30
+
31
+ /-- A write into the register file or IR memory leaves the data-stack view unchanged. -/
32
+ theorem wf_stack (hg : Geom c) (hs : Rel0 c p s x) {A : Nat} {v : W} {mem' : Memory 64}
33
+ (hw : x.mem.write? (ofN A) v = some mem') (hAlt : A < 2 ^ 64)
34
+ (hA : (c.rf ≤ A ∧ A < c.rf + 8 * c.nregs) ∨ (c.mb ≤ A ∧ A < c.mb + 8 * c.M)) :
35
+ StackRel c mem' s.dstack := by
36
+ have hcap := hs.capD
37
+ have hdE := hg.dEnd_lt
38
+ intro i w hi
39
+ have hik : i < s.dstack.length := by
40
+ by_cases hh : i < s.dstack.length
41
+ · exact hh
42
+ · rw [List.getElem?_eq_none (by omega)] at hi; simp at hi
43
+ have hB : c.dEnd - 8 * s.dstack.length + 8 * i < 2 ^ 64 := by omega
44
+ have hne : A ≠ c.dEnd - 8 * s.dstack.length + 8 * i := by
45
+ rcases hA with ⟨h1, h2⟩ | ⟨h1, h2⟩ <;> rcases hg.dS_F with h | h <;>
46
+ rcases hg.dS_M with h' | h' <;> omega
47
+ rw [read_write_nat hw hAlt hB]
48
+ simp only [hne, ite_false]
49
+ exact hs.stack i w hi
50
+
51
+ theorem wf_rs (hg : Geom c) (hs : Rel0 c p s x) {A : Nat} {v : W} {mem' : Memory 64}
52
+ (hw : x.mem.write? (ofN A) v = some mem') (hAlt : A < 2 ^ 64)
53
+ (hA : (c.rf ≤ A ∧ A < c.rf + 8 * c.nregs) ∨ (c.mb ≤ A ∧ A < c.mb + 8 * c.M)) :
54
+ RsRel c p mem' s.rstack := by
55
+ have hcapR := hs.capR
56
+ have hrc := hg.rcap_le
57
+ have hsp := hg.sp0_lt
58
+ intro i a hi
59
+ have hi' : i < s.rstack.length := by
60
+ by_cases hh : i < s.rstack.length
61
+ · exact hh
62
+ · rw [List.getElem?_eq_none (by omega)] at hi; simp at hi
63
+ have hB : c.sp0 - 8 * s.rstack.length + 8 * i < 2 ^ 64 := by omega
64
+ have hne : A ≠ c.sp0 - 8 * s.rstack.length + 8 * i := by
65
+ rcases hA with ⟨h1, h2⟩ | ⟨h1, h2⟩ <;> rcases hg.dF_K with h | h <;>
66
+ rcases hg.dM_K with h' | h' <;> omega
67
+ rw [read_write_nat hw hAlt hB]
68
+ simp only [hne, ite_false]
69
+ exact hs.rs i a hi
70
+
71
+ theorem wf_regs_mb (hg : Geom c) (hs : Rel0 c p s x) {A : Nat} {v : W} {mem' : Memory 64}
72
+ (hw : x.mem.write? (ofN A) v = some mem') (hAlt : A < 2 ^ 64)
73
+ (hA : c.mb ≤ A ∧ A < c.mb + 8 * c.M) : RegRel c mem' s.regs := by
74
+ have hrf := hg.rf_lt
75
+ intro r hr
76
+ have hne : A ≠ c.rf + 8 * r := by rcases hg.dF_M with h | h <;> omega
77
+ rw [read_write_nat hw hAlt (by omega)]
78
+ simp only [hne, ite_false]
79
+ exact hs.regs r hr
80
+
81
+ theorem wf_mem_rf (hg : Geom c) (hs : Rel0 c p s x) {A : Nat} {v : W} {mem' : Memory 64}
82
+ (hw : x.mem.write? (ofN A) v = some mem') (hAlt : A < 2 ^ 64)
83
+ (hA : c.rf ≤ A ∧ A < c.rf + 8 * c.nregs) : MemRel c mem' s.mem := by
84
+ have hmb := hg.mb_lt
85
+ intro a ha
86
+ have hne : A ≠ c.mb + 8 * a := by rcases hg.dF_M with h | h <;> omega
87
+ rw [read_write_nat hw hAlt (by omega)]
88
+ simp only [hne, ite_false]
89
+ exact hs.mem a ha
90
+
91
+ /-- Load virtual register `r` (through `r14`). -/
92
+ theorem ld_reg (hs : Rel0 c p s x) {r : Nat} (hr : r < c.nregs) (rd : Reg) {disp : Int}
93
+ (hd : disp = 8 * (r : Int)) :
94
+ exec (.load rd Reg.r14 disp) x = .next (x.mov rd (s.regs r)) := by
95
+ have hread := hs.regs r hr
96
+ simp only [exec, M.ea, hs.r14, hd, ea_nat]
97
+ rw [hread]
98
+
99
+ /-- Store into virtual register `r`. -/
100
+ theorem st_reg (hg : Geom c) (hs : Rel0 c p s x) {r : Nat} (hr : r < c.nregs) (rs : Reg)
101
+ {disp : Int} (hd : disp = 8 * (r : Int)) :
102
+ ∃ mem', exec (.store Reg.r14 disp rs) x = .next { x.adv with mem := mem' } ∧
103
+ Rel0 c p { s with regs := State.setReg s.regs r (x.regs rs) } { x.adv with mem := mem' } := by
104
+ have hrf := hg.rf_lt
105
+ have hA1 : c.rf ≤ c.rf + 8 * r := by omega
106
+ have hA2 : c.rf + 8 * r < c.rf + 8 * c.nregs := by omega
107
+ have hAlt : c.rf + 8 * r < 2 ^ 64 := by omega
108
+ have hvalid := hs.xvalid.2.1 _ hA1 hA2
109
+ obtain ⟨mem', hw⟩ : ∃ m', x.mem.write? (ofN (c.rf + 8 * r)) (x.regs rs) = some m' := by
110
+ simp [Memory.write?, hvalid]
111
+ refine ⟨mem', ?_, ?_⟩
112
+ · simp only [exec, M.ea, hs.r14, hd, ea_nat]; rw [hw]
113
+ · have hA : (c.rf ≤ c.rf + 8 * r ∧ c.rf + 8 * r < c.rf + 8 * c.nregs) ∨
114
+ (c.mb ≤ c.rf + 8 * r ∧ c.rf + 8 * r < c.mb + 8 * c.M) := Or.inl ⟨hA1, hA2⟩
115
+ refine { r15 := ?_, r14 := ?_, r13 := ?_, r12 := ?_, rsp := ?_,
116
+ stack := wf_stack hg hs hw hAlt hA, regs := ?_, mem := wf_mem_rf hg hs hw hAlt ⟨hA1, hA2⟩,
117
+ rs := wf_rs hg hs hw hAlt hA, irvalid := hs.irvalid, xvalid := ?_, capD := hs.capD,
118
+ capR := hs.capR, rbp := by simpa [M.adv] using hs.rbp,
119
+ aux := aux_write_other hg.aEnd_lt hs.capA hs.aux hw hAlt
120
+ (by rcases hg.dA_F with h | h <;> omega),
121
+ capA := hs.capA }
122
+ · simpa [M.adv] using hs.r15
123
+ · simpa [M.adv] using hs.r14
124
+ · simpa [M.adv] using hs.r13
125
+ · simpa [M.adv] using hs.r12
126
+ · simpa [M.adv] using hs.rsp
127
+ · intro r' hr'
128
+ rw [read_write_nat hw hAlt (by omega)]
129
+ by_cases h : r' = r
130
+ · subst h; simp [State.setReg]
131
+ · have hne : c.rf + 8 * r ≠ c.rf + 8 * r' := by omega
132
+ simp only [hne, ite_false, State.setReg, h]
133
+ exact hs.regs r' hr'
134
+ · have := Memory.write?_valid hw
135
+ show RegionsValid c mem'.valid
136
+ rw [this]; exact hs.xvalid
137
+
138
+ /-- Load IR memory cell at word address `a` (`rax = a`). -/
139
+ theorem ld_mem (hs : Rel0 c p s x) {a v : W} (hr : s.mem.read? a = some v) (hrax : x.regs rax = a) :
140
+ exec (.loadIdx rax Reg.r13 rax) x = .next (x.mov rax v) := by
141
+ obtain ⟨hv, hc⟩ := Memory.read?_some hr
142
+ have hlt : a.toNat < c.M := by
143
+ have := hs.irvalid a
144
+ rw [hv] at this
145
+ simpa using this.symm
146
+ have hread := hs.mem a.toNat hlt
147
+ rw [ofN_toNat, hc] at hread
148
+ simp only [exec, M.eaIdx, hs.r13, hrax, eaIdx_nat]
149
+ rw [hread]
150
+
151
+ /-- Store into IR memory cell `a` (`rax = a`, `rcx = v`). -/
152
+ theorem st_mem (hg : Geom c) (hs : Rel0 c p s x) {a v : W} {m : Memory 64}
153
+ (hw : s.mem.write? a v = some m) (hrax : x.regs rax = a) (hrcx : x.regs rcx = v) :
154
+ ∃ mem', exec (.storeIdx Reg.r13 rax rcx) x = .next { x.adv with mem := mem' } ∧
155
+ Rel0 c p { s with mem := m } { x.adv with mem := mem' } := by
156
+ have hv : s.mem.valid a = true := by
157
+ by_cases hh : s.mem.valid a = true
158
+ · exact hh
159
+ · have := (Memory.write?_none_iff s.mem a v).mpr (by simpa using hh); rw [hw] at this; simp at this
160
+ have hlt : a.toNat < c.M := by
161
+ have := hs.irvalid a
162
+ rw [hv] at this
163
+ simpa using this.symm
164
+ have hmb := hg.mb_lt
165
+ have hA1 : c.mb ≤ c.mb + 8 * a.toNat := by omega
166
+ have hA2 : c.mb + 8 * a.toNat < c.mb + 8 * c.M := by omega
167
+ have hAlt : c.mb + 8 * a.toNat < 2 ^ 64 := by omega
168
+ have hvalid := hs.xvalid.2.2.1 _ hA1 hA2
169
+ obtain ⟨mem', hw'⟩ : ∃ m', x.mem.write? (ofN (c.mb + 8 * a.toNat)) v = some m' := by
170
+ simp [Memory.write?, hvalid]
171
+ refine ⟨mem', ?_, ?_⟩
172
+ · simp only [exec, M.eaIdx, hs.r13, hrax, hrcx, eaIdx_nat]; rw [hw']
173
+ · have hA : (c.rf ≤ c.mb + 8 * a.toNat ∧ c.mb + 8 * a.toNat < c.rf + 8 * c.nregs) ∨
174
+ (c.mb ≤ c.mb + 8 * a.toNat ∧ c.mb + 8 * a.toNat < c.mb + 8 * c.M) := Or.inr ⟨hA1, hA2⟩
175
+ refine { r15 := ?_, r14 := ?_, r13 := ?_, r12 := ?_, rsp := ?_,
176
+ stack := wf_stack hg hs hw' hAlt hA, regs := wf_regs_mb hg hs hw' hAlt ⟨hA1, hA2⟩,
177
+ mem := ?_, rs := wf_rs hg hs hw' hAlt hA, irvalid := ?_, xvalid := ?_,
178
+ capD := hs.capD, capR := hs.capR, rbp := by simpa [M.adv] using hs.rbp,
179
+ aux := aux_write_other hg.aEnd_lt hs.capA hs.aux hw' hAlt
180
+ (by rcases hg.dA_M with h | h <;> omega),
181
+ capA := hs.capA }
182
+ · simpa [M.adv] using hs.r15
183
+ · simpa [M.adv] using hs.r14
184
+ · simpa [M.adv] using hs.r13
185
+ · simpa [M.adv] using hs.r12
186
+ · simpa [M.adv] using hs.rsp
187
+ · intro a' ha'
188
+ have hMlt : c.M < 2 ^ 64 := by omega
189
+ rw [read_write_nat hw' hAlt (by omega)]
190
+ by_cases h : a' = a.toNat
191
+ · subst h
192
+ simp only [ite_true]
193
+ rw [ofN_toNat]
194
+ have := (Memory.read?_some (Memory.read_write_same hw)).2
195
+ rw [this]
196
+ · have hne : c.mb + 8 * a.toNat ≠ c.mb + 8 * a' := by omega
197
+ have hne' : ofN a' ≠ a := by
198
+ intro e
199
+ apply h
200
+ have := ofN_inj (by omega) a.isLt (e.trans (ofN_toNat a).symm)
201
+ omega
202
+ simp only [hne, ite_false]
203
+ rw [Memory.cell_write_other hw hne']
204
+ exact hs.mem a' ha'
205
+ · intro a'; rw [Memory.write?_valid hw]; exact hs.irvalid a'
206
+ · have := Memory.write?_valid hw'
207
+ show RegionsValid c mem'.valid
208
+ rw [this]; exact hs.xvalid
209
+
210
+ end Frame
211
+
212
+ end X86
213
+ end WordDialect
backend/x86_64/X86/SimMemOps.lean ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.SimMem
2
+
3
+ /-!
4
+ # X86.SimMemOps
5
+
6
+ The address bounds guard `movImm rt, memSize; cmp rax, rt; jb ok; exitTrap badAddress`, and the
7
+ simulation theorems for `push`, `pop`, `load`, `store`.
8
+ -/
9
+
10
+ namespace WordDialect
11
+ namespace X86
12
+
13
+ open Reg
14
+
15
+ section Ops
16
+
17
+ variable {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)}
18
+ {L : Layout} {base : Nat} {off : Nat → Nat}
19
+
20
+ theorem ult_memSize {a : W} {n : Nat} (hn : n < 2 ^ 64) :
21
+ a.ult (BitVec.ofNat 64 n) = decide (a.toNat < n) := by
22
+ simp [BitVec.ult_eq_decide, BitVec.toNat_ofNat, Nat.mod_eq_of_lt hn]
23
+
24
+ /-- The bounds guard, passing case. -/
25
+ theorem addr_pass (hs : Rel0 c p s x) {rt : Reg} (hrt : rt = rcx ∨ rt = rdx) {n : Nat}
26
+ (hn : n < 2 ^ 64) {t : Nat} (ht : t = x.pc + 4) {a : W} (hrax : x.regs rax = a) (ha : a.toNat < n)
27
+ (hat : XAt code x.pc [.movImm rt (BitVec.ofNat 64 n), .cmp rax rt, .jcc .b t,
28
+ .exitTrap .badAddress]) :
29
+ ∃ x', XSteps code x x' ∧ Rel0 c p s x' ∧ x'.pc = x.pc + 4 ∧ x'.regs rax = a ∧
30
+ (∀ r, r ≠ rt → x'.regs r = x.regs r) ∧ x'.mem = x.mem := by
31
+ subst ht
32
+ obtain ⟨f0, f1, f2, f3, _⟩ := hat
33
+ have hrt' : rt = rax ∨ rt = rcx ∨ rt = rdx ∨ rt = rbx := by rcases hrt with h | h <;> simp [h]
34
+ have hne : rax ≠ rt := by rcases hrt with h | h <;> simp [h]
35
+ obtain ⟨x1, hs1, hr1, hpc1, hv1, ho1, hm1⟩ := movimm_step (code := code) hs (rd := rt) hrt'
36
+ (v := BitVec.ofNat 64 n) f0
37
+ have hax1 : x1.regs rax = a := by rw [ho1 rax hne, hrax]
38
+ let x2 : M := { x1.adv with flags := some (subFlags (x1.regs rax) (x1.regs rt)) }
39
+ have hst2 : step code x1 = .next x2 := by
40
+ rw [xstep_of_fetch (m := x1) (fetch_cast f1 (by omega))]; rfl
41
+ have hpc2 : x2.pc = x.pc + 2 := by simp [x2, M.adv, hpc1]
42
+ have hr2 : Rel0 c p s x2 := hr1.congr (by simp [x2, M.adv]) (by simp [x2, M.adv]) (by simp [x2, M.adv])
43
+ (by simp [x2, M.adv]) (by simp [x2, M.adv]) (by simp [x2, M.adv]) (by simp [x2, M.adv])
44
+ have hfl : x2.flags = some (subFlags a (BitVec.ofNat 64 n)) := by simp [x2, hax1, hv1]
45
+ have hhold : Cc.b.holds (subFlags a (BitVec.ofNat 64 n)) = true := by
46
+ rw [holds_b, ult_memSize hn]; simpa using ha
47
+ have hg := guard_pass (code := code) (pc := x.pc + 2) (m := x2) (c := .b) (t := .badAddress)
48
+ (f := subFlags a (BitVec.ofNat 64 n))
49
+ ⟨fetch_cast f2 (by omega), fetch_cast f3 (by omega), trivial⟩ hpc2 hfl hhold
50
+ refine ⟨{ x2 with pc := x.pc + 2 + 2 }, (hs1.trans (XSteps.single hst2)).trans hg, ?_, by simp,
51
+ by simp [x2, M.adv, hax1], ?_, by simp [x2, M.adv, hm1]⟩
52
+ · exact hr2.congr rfl rfl rfl rfl rfl rfl rfl
53
+ · intro r hr; simp [x2, M.adv, ho1 r hr]
54
+
55
+ /-- The bounds guard, failing case. -/
56
+ theorem addr_trap (hs : Rel0 c p s x) {rt : Reg} (hrt : rt = rcx ∨ rt = rdx) {n : Nat}
57
+ (hn : n < 2 ^ 64) {t : Nat} (ht : t = x.pc + 4) {a : W} (hrax : x.regs rax = a) (ha : ¬ a.toNat < n)
58
+ (hat : XAt code x.pc [.movImm rt (BitVec.ofNat 64 n), .cmp rax rt, .jcc .b t,
59
+ .exitTrap .badAddress]) :
60
+ XExec code x (.trapped .badAddress) := by
61
+ subst ht
62
+ obtain ⟨f0, f1, f2, f3, _⟩ := hat
63
+ have hrt' : rt = rax ∨ rt = rcx ∨ rt = rdx ∨ rt = rbx := by rcases hrt with h | h <;> simp [h]
64
+ have hne : rax ≠ rt := by rcases hrt with h | h <;> simp [h]
65
+ obtain ⟨x1, hs1, hr1, hpc1, hv1, ho1, hm1⟩ := movimm_step (code := code) hs (rd := rt) hrt'
66
+ (v := BitVec.ofNat 64 n) f0
67
+ have hax1 : x1.regs rax = a := by rw [ho1 rax hne, hrax]
68
+ let x2 : M := { x1.adv with flags := some (subFlags (x1.regs rax) (x1.regs rt)) }
69
+ have hst2 : step code x1 = .next x2 := by
70
+ rw [xstep_of_fetch (m := x1) (fetch_cast f1 (by omega))]; rfl
71
+ have hpc2 : x2.pc = x.pc + 2 := by simp [x2, M.adv, hpc1]
72
+ have hfl : x2.flags = some (subFlags a (BitVec.ofNat 64 n)) := by simp [x2, hax1, hv1]
73
+ have hhold : Cc.b.holds (subFlags a (BitVec.ofNat 64 n)) = false := by
74
+ rw [holds_b, ult_memSize hn]; simpa using ha
75
+ exact XExec.of_steps (hs1.trans (XSteps.single hst2))
76
+ (guard_trap (code := code) (pc := x.pc + 2) (m := x2) (c := .b) (t := .badAddress)
77
+ (f := subFlags a (BitVec.ofNat 64 n))
78
+ ⟨fetch_cast f2 (by omega), fetch_cast f3 (by omega), trivial⟩ hpc2 hfl hhold)
79
+
80
+ theorem sim_push_ok (hg : Geom c) (hs : Rel0 c p s x) (hpc : x.pc = base) {r : Nat}
81
+ (hr : r < c.nregs) (hLd : L.dLim = ofN (c.dBase + 8))
82
+ (hat : XAt code base (lowerInstr L base off (.push r)))
83
+ (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
84
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := s.regs r :: s.dstack } x' ∧
85
+ x'.pc = base + isize (.push r : WordDialect.Instr 64) := by
86
+ obtain ⟨hato, f0, f1, f2⟩ := xat_ovf3 (L := L) (by exact hat)
87
+ obtain ⟨x0, hs0, hr0, hpc0, _, _⟩ := ovfD_pass hg hs hLd hfit hpc hato
88
+ have hst : step code x0 = .next (x0.mov rax (s.regs r)) := by
89
+ rw [xstep_of_fetch (by rw [hpc0]; exact f0), ld_reg hr0 hr rax rfl]
90
+ have hr1 : Rel0 c p s (x0.mov rax (s.regs r)) := mov_rel0 hr0 rax (scratch_ne rax (by simp)) _
91
+ have hpc1 : (x0.mov rax (s.regs r)).pc = base + 4 + 1 := by simp [M.mov, M.setReg, M.adv, hpc0]
92
+ obtain ⟨x2, hs2, hr2, hpc2, _⟩ := push_pair hg hr1 rax (by decide) hfit
93
+ ⟨fetch_cast f1 (by omega), fetch_cast f2 (by omega), trivial⟩
94
+ have hv : (x0.mov rax (s.regs r)).regs rax = s.regs r := by simp [M.mov, M.setReg, M.adv]
95
+ rw [hv] at hr2
96
+ exact ⟨x2, (hs0.trans (XSteps.single hst)).trans hs2, hr2, by simp [isize]; omega⟩
97
+
98
+ theorem sim_push_ovf (hg : Geom c) (hs : Rel0 c p s x) (hpc : x.pc = base) {r : Nat}
99
+ (hLd : L.dLim = ofN (c.dBase + 8)) (hat : XAt code base (lowerInstr L base off (.push r)))
100
+ (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) : XExec code x .overflow := by
101
+ obtain ⟨hato, _⟩ := xat_ovf3 (L := L) (by exact hat)
102
+ exact ovfD_trap hg hs hLd h hpc hato
103
+
104
+ theorem sim_pop_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
105
+ {r : Nat} (hr : r < c.nregs) (hat : XAt code base (lowerInstr L base off (.pop r)))
106
+ {a : W} {d : List W} (hd : s.dstack = a :: d) :
107
+ ∃ x', XSteps code x x' ∧
108
+ Rel0 c p { s with dstack := d, regs := State.setReg s.regs r a } x' ∧
109
+ x'.pc = base + isize (.pop r : WordDialect.Instr 64) := by
110
+ have hat' : XAt code base (uf L 1 base ++ [.load rax Reg.r15 0, .addImm Reg.r15 8,
111
+ .store Reg.r14 (8 * (r : Int)) rax]) := hat
112
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
113
+ have hufl : (uf L 1 base).length = 4 := by simp [uf]
114
+ rw [hufl] at hatr
115
+ obtain ⟨f0, f1, f2, _⟩ := hatr
116
+ have hk : 1 ≤ s.dstack.length := by simp [hd]
117
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
118
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, _, _⟩ := ld_step (code := code) hr1 (j := 0) (v := a)
119
+ (by simp [hd]) (rd := rax) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
120
+ obtain ⟨x3, hs3, hr3, hpc3, ho3⟩ := adj_step (code := code) hg hr2 (n := 1) (by simp [hd])
121
+ (δ := 8) (by simp) (fetch_cast f1 (by omega))
122
+ have hax3 : x3.regs rax = a := by rw [ho3 rax (by decide), hv2]
123
+ obtain ⟨mem', e4, hr4⟩ := st_reg hg hr3 hr rax (disp := 8 * (r : Int)) rfl
124
+ have hst4 : step code x3 = .next { x3.adv with mem := mem' } := by
125
+ rw [xstep_of_fetch (fetch_cast f2 (by omega)), e4]
126
+ have hr4' : Rel0 c p { s with dstack := s.dstack.drop 1, regs := State.setReg s.regs r (x3.regs rax) }
127
+ { x3.adv with mem := mem' } := hr4
128
+ have e : s.dstack.drop 1 = d := by simp [hd]
129
+ rw [e, hax3] at hr4'
130
+ exact ⟨_, ((hs1.trans hs2).trans hs3).trans (XSteps.single hst4), hr4',
131
+ by simp [M.adv, isize, ufSize]; omega⟩
132
+
133
+ theorem sim_load_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hMs : L.memSize = c.M)
134
+ (hs : Rel0 c p s x) (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .load))
135
+ {a v : W} {d : List W} (hd : s.dstack = a :: d) (hv : s.mem.read? a = some v) :
136
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := v :: d } x' ∧
137
+ x'.pc = base + isize (.load : WordDialect.Instr 64) := by
138
+ have hat' : XAt code base (uf L 1 base ++ [.load rax Reg.r15 0, .movImm rcx (BitVec.ofNat 64 L.memSize),
139
+ .cmp rax rcx, .jcc .b (base + ufSize 1 + 5), .exitTrap .badAddress, .loadIdx rax Reg.r13 rax,
140
+ .store Reg.r15 0 rax]) := hat
141
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
142
+ have hufl : (uf L 1 base).length = 4 := by simp [uf]
143
+ rw [hufl] at hatr
144
+ obtain ⟨f0, f1, f2, f3, f4, f5, f6, _⟩ := hatr
145
+ have hk : 1 ≤ s.dstack.length := by simp [hd]
146
+ have hMlt : c.M < 2 ^ 64 := by have := hg.mb_lt; omega
147
+ have hlt : a.toNat < c.M := by
148
+ obtain ⟨hvv, _⟩ := Memory.read?_some hv
149
+ have := hs.irvalid a
150
+ rw [hvv] at this
151
+ simpa using this.symm
152
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
153
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, ho2, hm2⟩ := ld_step (code := code) hr1 (j := 0) (v := a)
154
+ (by simp [hd]) (rd := rax) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
155
+ obtain ⟨x3, hs3, hr3, hpc3, hax3, ho3, hm3⟩ := addr_pass (code := code) hr2 (rt := rcx) (by simp)
156
+ (n := c.M) hMlt (t := base + ufSize 1 + 5) (by simp [ufSize]; omega) hv2 hlt (by
157
+ rw [← hMs]
158
+ refine ⟨fetch_cast f1 (by omega), fetch_cast f2 (by omega), fetch_cast f3 (by omega),
159
+ fetch_cast f4 (by omega), trivial⟩)
160
+ have hst4 : step code x3 = .next (x3.mov rax v) := by
161
+ rw [xstep_of_fetch (fetch_cast f5 (by omega)), ld_mem hr3 hv hax3]
162
+ have hr4 : Rel0 c p s (x3.mov rax v) := mov_rel0 hr3 rax (scratch_ne rax (by simp)) v
163
+ have hpc4 : (x3.mov rax v).pc = base + 4 + 1 + 4 + 1 := by simp [M.mov, M.setReg, M.adv]; omega
164
+ obtain ⟨x5, hs5, hr5, hpc5, _⟩ := st_step (code := code) hg hr4 (j := 0) (by simp [hd])
165
+ (rs := rax) (disp := 0) (by simp) (fetch_cast f6 (by omega))
166
+ have e : s.dstack.set 0 ((x3.mov rax v).regs rax) = v :: d := by
167
+ simp [hd, M.mov, M.setReg, M.adv]
168
+ rw [e] at hr5
169
+ exact ⟨x5, (((hs1.trans hs2).trans hs3).trans (XSteps.single hst4)).trans hs5, hr5,
170
+ by simp [isize, ufSize]; omega⟩
171
+
172
+ theorem sim_load_trap (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hMs : L.memSize = c.M)
173
+ (hs : Rel0 c p s x) (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .load))
174
+ {a : W} {d : List W} (hd : s.dstack = a :: d) (hv : s.mem.read? a = none) :
175
+ XExec code x (.trapped .badAddress) := by
176
+ have hat' : XAt code base (uf L 1 base ++ [.load rax Reg.r15 0, .movImm rcx (BitVec.ofNat 64 L.memSize),
177
+ .cmp rax rcx, .jcc .b (base + ufSize 1 + 5), .exitTrap .badAddress, .loadIdx rax Reg.r13 rax,
178
+ .store Reg.r15 0 rax]) := hat
179
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
180
+ have hufl : (uf L 1 base).length = 4 := by simp [uf]
181
+ rw [hufl] at hatr
182
+ obtain ⟨f0, f1, f2, f3, f4, _⟩ := hatr
183
+ have hk : 1 ≤ s.dstack.length := by simp [hd]
184
+ have hMlt : c.M < 2 ^ 64 := by have := hg.mb_lt; omega
185
+ have hge : ¬ a.toNat < c.M := by
186
+ have hvv := (Memory.read?_none_iff s.mem a).mp hv
187
+ have := hs.irvalid a
188
+ rw [hvv] at this
189
+ simpa using this
190
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
191
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, ho2, hm2⟩ := ld_step (code := code) hr1 (j := 0) (v := a)
192
+ (by simp [hd]) (rd := rax) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
193
+ exact XExec.of_steps (hs1.trans hs2) (addr_trap (code := code) hr2 (rt := rcx) (by simp)
194
+ (n := c.M) hMlt (t := base + ufSize 1 + 5) (by simp [ufSize]; omega) hv2 hge (by
195
+ rw [← hMs]
196
+ refine ⟨fetch_cast f1 (by omega), fetch_cast f2 (by omega), fetch_cast f3 (by omega),
197
+ fetch_cast f4 (by omega), trivial⟩))
198
+
199
+ theorem sim_store_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hMs : L.memSize = c.M)
200
+ (hs : Rel0 c p s x) (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .store))
201
+ {a v : W} {d : List W} (hd : s.dstack = a :: v :: d) {m : Memory 64}
202
+ (hw : s.mem.write? a v = some m) :
203
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := d, mem := m } x' ∧
204
+ x'.pc = base + isize (.store : WordDialect.Instr 64) := by
205
+ have hat' : XAt code base (uf L 2 base ++ [.load rax Reg.r15 0, .load rcx Reg.r15 8,
206
+ .movImm rdx (BitVec.ofNat 64 L.memSize), .cmp rax rdx, .jcc .b (base + ufSize 2 + 6),
207
+ .exitTrap .badAddress, .storeIdx Reg.r13 rax rcx, .addImm Reg.r15 16]) := hat
208
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
209
+ have hufl : (uf L 2 base).length = 4 := by simp [uf]
210
+ rw [hufl] at hatr
211
+ obtain ⟨f0, f1, f2, f3, f4, f5, f6, f7, _⟩ := hatr
212
+ have hk : 2 ≤ s.dstack.length := by simp [hd]
213
+ have hMlt : c.M < 2 ^ 64 := by have := hg.mb_lt; omega
214
+ have hv : s.mem.valid a = true := by
215
+ by_cases hh : s.mem.valid a = true
216
+ · exact hh
217
+ · have := (Memory.write?_none_iff s.mem a v).mpr (by simpa using hh); rw [hw] at this; simp at this
218
+ have hlt : a.toNat < c.M := by
219
+ have := hs.irvalid a
220
+ rw [hv] at this
221
+ simpa using this.symm
222
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
223
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, ho2, hm2⟩ := ld_step (code := code) hr1 (j := 0) (v := a)
224
+ (by simp [hd]) (rd := rax) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
225
+ obtain ⟨x3, hs3, hr3, hpc3, hv3, ho3, hm3⟩ := ld_step (code := code) hr2 (j := 1) (v := v)
226
+ (by simp [hd]) (rd := rcx) (by simp) (disp := 8) (by simp) (fetch_cast f1 (by omega))
227
+ have hax3 : x3.regs rax = a := by rw [ho3 rax (by decide), hv2]
228
+ obtain ⟨x4, hs4, hr4, hpc4, hax4, ho4, hm4⟩ := addr_pass (code := code) hr3 (rt := rdx) (by simp)
229
+ (n := c.M) hMlt (t := base + ufSize 2 + 6) (by simp [ufSize]; omega) hax3 hlt (by
230
+ rw [← hMs]
231
+ refine ⟨fetch_cast f2 (by omega), fetch_cast f3 (by omega), fetch_cast f4 (by omega),
232
+ fetch_cast f5 (by omega), trivial⟩)
233
+ have hcx4 : x4.regs rcx = v := by rw [ho4 rcx (by decide), hv3]
234
+ obtain ⟨mem', e5, hr5⟩ := st_mem hg hr4 hw hax4 hcx4
235
+ have hst5 : step code x4 = .next { x4.adv with mem := mem' } := by
236
+ rw [xstep_of_fetch (fetch_cast f6 (by omega)), e5]
237
+ have hlen : 2 ≤ ({ s with mem := m } : State 64).dstack.length := by simp [hd]
238
+ obtain ⟨x6, hs6, hr6, hpc6, _⟩ := adj_step (code := code) hg hr5 (n := 2) hlen (δ := 16) (by simp)
239
+ (fetch_cast f7 (by simp [M.adv]; omega))
240
+ have e : ({ s with mem := m } : State 64).dstack.drop 2 = d := by simp [hd]
241
+ have hr6' : Rel0 c p { s with dstack := d, mem := m } x6 := by
242
+ have : ({ { s with mem := m } with dstack := ({ s with mem := m } : State 64).dstack.drop 2 } : State 64)
243
+ = { s with dstack := d, mem := m } := by simp [hd]
244
+ rw [← this]; exact hr6
245
+ exact ⟨x6, (((((hs1.trans hs2).trans hs3).trans hs4).trans (XSteps.single hst5))).trans hs6, hr6',
246
+ by simp [M.adv, isize, ufSize] at *; omega⟩
247
+
248
+ theorem sim_store_trap (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hMs : L.memSize = c.M)
249
+ (hs : Rel0 c p s x) (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off .store))
250
+ {a v : W} {d : List W} (hd : s.dstack = a :: v :: d) (hw : s.mem.write? a v = none) :
251
+ XExec code x (.trapped .badAddress) := by
252
+ have hat' : XAt code base (uf L 2 base ++ [.load rax Reg.r15 0, .load rcx Reg.r15 8,
253
+ .movImm rdx (BitVec.ofNat 64 L.memSize), .cmp rax rdx, .jcc .b (base + ufSize 2 + 6),
254
+ .exitTrap .badAddress, .storeIdx Reg.r13 rax rcx, .addImm Reg.r15 16]) := hat
255
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
256
+ have hufl : (uf L 2 base).length = 4 := by simp [uf]
257
+ rw [hufl] at hatr
258
+ obtain ⟨f0, f1, f2, f3, f4, f5, _⟩ := hatr
259
+ have hk : 2 ≤ s.dstack.length := by simp [hd]
260
+ have hMlt : c.M < 2 ^ 64 := by have := hg.mb_lt; omega
261
+ have hge : ¬ a.toNat < c.M := by
262
+ have hvv := (Memory.write?_none_iff s.mem a v).mp hw
263
+ have := hs.irvalid a
264
+ rw [hvv] at this
265
+ simpa using this
266
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
267
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, ho2, hm2⟩ := ld_step (code := code) hr1 (j := 0) (v := a)
268
+ (by simp [hd]) (rd := rax) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
269
+ obtain ⟨x3, hs3, hr3, hpc3, hv3, ho3, hm3⟩ := ld_step (code := code) hr2 (j := 1) (v := v)
270
+ (by simp [hd]) (rd := rcx) (by simp) (disp := 8) (by simp) (fetch_cast f1 (by omega))
271
+ have hax3 : x3.regs rax = a := by rw [ho3 rax (by decide), hv2]
272
+ exact XExec.of_steps ((hs1.trans hs2).trans hs3) (addr_trap (code := code) hr3 (rt := rdx)
273
+ (by simp) (n := c.M) hMlt (t := base + ufSize 2 + 6) (by simp [ufSize]; omega) hax3 hge (by
274
+ rw [← hMs]
275
+ refine ⟨fetch_cast f2 (by omega), fetch_cast f3 (by omega), fetch_cast f4 (by omega),
276
+ fetch_cast f5 (by omega), trivial⟩))
277
+
278
+ end Ops
279
+
280
+ end X86
281
+ end WordDialect
backend/x86_64/X86/SimOps.lean ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.SimBin
2
+
3
+ /-!
4
+ # X86.SimOps
5
+
6
+ Per-instruction simulation theorems. Each states, for an IR instruction `i` whose lowered code
7
+ is placed at `base`:
8
+ * success: from a related state, the x86 model reaches a state related to the IR result;
9
+ * trap: when the IR traps with `t`, the x86 model exits with `t`.
10
+ -/
11
+
12
+ namespace WordDialect
13
+ namespace X86
14
+
15
+ open Reg
16
+
17
+ /-- Operand-count trap: any lowering that starts with `uf n` exits with `stackUnderflow`. -/
18
+ theorem sim_uf_trap {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)}
19
+ {L : Layout} {base n : Nat} {rest : List (Instr Nat)} (hg : Geom c) (hs : Rel0 c p s x)
20
+ (hL : L.dEnd = ofN c.dEnd) (hn : 0 < n) (hn3 : n ≤ 3) (hk : s.dstack.length < n)
21
+ (hpc : x.pc = base) (hat : XAt code base (uf L n base ++ rest)) :
22
+ XExec code x (.trapped .stackUnderflow) :=
23
+ uf_trap hg hs hL hn hn3 hk hpc (xat_append.mp hat).1
24
+
25
+ macro "mid_tac" : tactic =>
26
+ `(tactic| (intro m a b ha hb
27
+ simp [xseq, exec, M.arith, M.mov, M.setReg, M.adv, ha, hb, MidSpec]))
28
+
29
+ theorem sim_bin_instr {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)}
30
+ {L : Layout} {base : Nat} {off : Nat → Nat} (hg : Geom c) (hL : L.dEnd = ofN c.dEnd)
31
+ (hs : Rel0 c p s x) (hpc : x.pc = base) (i : WordDialect.Instr 64) (mid : List (Instr Nat))
32
+ (rr : Reg) (hrr : rr = rax ∨ rr = rcx ∨ rr = rdx ∨ rr = rbx) (f : W → W → W)
33
+ (hmid : MidSpec mid rr f) (hstr : ∀ j ∈ mid, j.straightX = true)
34
+ (hlow : lowerInstr L base off i = uf L 2 base ++
35
+ ([.load rcx r15 0, .load rax r15 8] ++ mid ++ [.store r15 8 rr, .addImm r15 8]))
36
+ (hsize : isize i = 4 + (4 + mid.length))
37
+ (hat : XAt code base (lowerInstr L base off i)) {b a : W} {d : List W}
38
+ (hd : s.dstack = b :: a :: d) :
39
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := f a b :: d } x' ∧
40
+ x'.pc = base + isize i := by
41
+ rw [hlow] at hat
42
+ obtain ⟨x', h1, h2, h3⟩ := sim_bin_ok hg hL hs hpc mid rr hrr f hmid hstr hat hd
43
+ exact ⟨x', h1, h2, by rw [h3, hsize]; omega⟩
44
+
45
+ theorem sim_uf2_trap {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)}
46
+ {L : Layout} {base : Nat} {off : Nat → Nat} (hg : Geom c) (hL : L.dEnd = ofN c.dEnd)
47
+ (hs : Rel0 c p s x) (hpc : x.pc = base) (i : WordDialect.Instr 64) (rest : List (Instr Nat))
48
+ (hlow : lowerInstr L base off i = uf L 2 base ++ rest)
49
+ (hat : XAt code base (lowerInstr L base off i)) (hk : s.dstack.length < 2) :
50
+ XExec code x (.trapped .stackUnderflow) := by
51
+ rw [hlow] at hat
52
+ exact sim_uf_trap hg hs hL (by decide) (by decide) hk hpc hat
53
+
54
+ /-! ### Middle computations -/
55
+
56
+ theorem midspec_arith (i : Instr Nat) (f : W → W → W)
57
+ (h : ∀ m : M, exec i m = .next (m.arith rax (f (m.regs rax) (m.regs rcx)))) :
58
+ MidSpec [i] rax f := by
59
+ intro m a b ha hb
60
+ refine ⟨m.arith rax (f (m.regs rax) (m.regs rcx)), by simp [xseq, h], ?_⟩
61
+ simp [M.arith, M.setReg, ha, hb]
62
+
63
+ theorem midspec_add : MidSpec [.add rax rcx] rax (· + ·) :=
64
+ midspec_arith _ _ (fun m => by simp [exec])
65
+ theorem midspec_sub : MidSpec [.sub rax rcx] rax (· - ·) :=
66
+ midspec_arith _ _ (fun m => by simp [exec])
67
+ theorem midspec_mul : MidSpec [.imul rax rcx] rax (· * ·) :=
68
+ midspec_arith _ _ (fun m => by simp [exec])
69
+ theorem midspec_and : MidSpec [.and rax rcx] rax (· &&& ·) :=
70
+ midspec_arith _ _ (fun m => by simp [exec])
71
+ theorem midspec_or : MidSpec [.or rax rcx] rax (· ||| ·) :=
72
+ midspec_arith _ _ (fun m => by simp [exec])
73
+ theorem midspec_xor : MidSpec [.xor rax rcx] rax (· ^^^ ·) :=
74
+ midspec_arith _ _ (fun m => by simp [exec])
75
+
76
+ theorem midspec_shl : MidSpec [.movImm rdx 0#64, .shlCl rax, .cmpImm rcx 64, .cmov .ae rax rdx]
77
+ rax Word.shl := by
78
+ intro m a b ha hb
79
+ by_cases h : 64 ≤ BitVec.toNat b
80
+ · have h' : ¬ BitVec.toNat b < 64 := by omega
81
+ simp [xseq, exec, M.arith, M.mov, M.setReg, M.adv, ha, hb, holds_ae, BitVec.ule_eq_decide,
82
+ Word.shl, h, h']
83
+ · have h' : BitVec.toNat b < 64 := by omega
84
+ simp [xseq, exec, M.arith, M.mov, M.setReg, M.adv, ha, hb, holds_ae, BitVec.ule_eq_decide,
85
+ Word.shl, h, h', Nat.mod_eq_of_lt h']
86
+
87
+ theorem ushiftRight_ge (a : W) (n : Nat) (h : 64 ≤ n) : a >>> n = 0#64 := by
88
+ apply BitVec.eq_of_toNat_eq
89
+ rw [BitVec.toNat_ushiftRight, Nat.shiftRight_eq_div_pow]
90
+ have : a.toNat < 2 ^ n := Nat.lt_of_lt_of_le a.isLt (Nat.pow_le_pow_right (by omega) h)
91
+ simp [Nat.div_eq_of_lt this]
92
+
93
+ theorem midspec_shr : MidSpec [.movImm rdx 0#64, .shrCl rax, .cmpImm rcx 64, .cmov .ae rax rdx]
94
+ rax Word.shr := by
95
+ intro m a b ha hb
96
+ by_cases h : 64 ≤ BitVec.toNat b
97
+ · have h' : ¬ BitVec.toNat b < 64 := by omega
98
+ simp [xseq, exec, M.arith, M.mov, M.setReg, M.adv, ha, hb, holds_ae, BitVec.ule_eq_decide,
99
+ Word.shr, h, h', ushiftRight_ge a _ h]
100
+ · have h' : BitVec.toNat b < 64 := by omega
101
+ simp [xseq, exec, M.arith, M.mov, M.setReg, M.adv, ha, hb, holds_ae, BitVec.ule_eq_decide,
102
+ Word.shr, h, h', Nat.mod_eq_of_lt h']
103
+
104
+ theorem midspec_rotl : MidSpec [.rolCl rax] rax Word.rotl := by
105
+ intro m a b ha hb
106
+ simp [xseq, exec, M.arith, M.setReg, ha, hb, Word.rotl, BitVec.rotateLeft]
107
+
108
+ theorem midspec_rotr : MidSpec [.rorCl rax] rax Word.rotr := by
109
+ intro m a b ha hb
110
+ simp [xseq, exec, M.arith, M.setReg, ha, hb, Word.rotr, BitVec.rotateRight]
111
+
112
+ theorem midspec_cmp (cd : WordDialect.Cond) :
113
+ MidSpec [.movImm rdx 0#64, .movImm rbx 1#64, .cmp rax rcx, .cmov (ccOf cd) rdx rbx] rdx
114
+ (fun a b => Word.ofBool (cd.eval a b)) := by
115
+ intro m a b ha hb
116
+ cases h : cd.eval a b <;>
117
+ simp [xseq, exec, M.arith, M.mov, M.setReg, M.adv, ha, hb, holds_ccOf, Word.ofBool, h]
118
+
119
+ /-! ### Instruction theorems -/
120
+
121
+ section Ops
122
+ variable {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)} {L : Layout}
123
+ {base : Nat} {off : Nat → Nat}
124
+
125
+ theorem sim_add_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
126
+ (hat : XAt code base (lowerInstr L base off .add)) {b a : W} {d : List W}
127
+ (hd : s.dstack = b :: a :: d) :
128
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := (a + b) :: d } x' ∧
129
+ x'.pc = base + isize (.add : WordDialect.Instr 64) :=
130
+ sim_bin_instr hg hL hs hpc .add [.add rax rcx] rax (by simp) (· + ·) midspec_add
131
+ (by simp [Instr.straightX]) rfl (by simp [isize, ufSize]) hat hd
132
+
133
+ theorem sim_sub_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
134
+ (hat : XAt code base (lowerInstr L base off .sub)) {b a : W} {d : List W}
135
+ (hd : s.dstack = b :: a :: d) :
136
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := (a - b) :: d } x' ∧
137
+ x'.pc = base + isize (.sub : WordDialect.Instr 64) :=
138
+ sim_bin_instr hg hL hs hpc .sub [.sub rax rcx] rax (by simp) (· - ·) midspec_sub
139
+ (by simp [Instr.straightX]) rfl (by simp [isize, ufSize]) hat hd
140
+
141
+ theorem sim_mul_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
142
+ (hat : XAt code base (lowerInstr L base off .mul)) {b a : W} {d : List W}
143
+ (hd : s.dstack = b :: a :: d) :
144
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := (a * b) :: d } x' ∧
145
+ x'.pc = base + isize (.mul : WordDialect.Instr 64) :=
146
+ sim_bin_instr hg hL hs hpc .mul [.imul rax rcx] rax (by simp) (· * ·) midspec_mul
147
+ (by simp [Instr.straightX]) rfl (by simp [isize, ufSize]) hat hd
148
+
149
+ theorem sim_and_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
150
+ (hat : XAt code base (lowerInstr L base off .and)) {b a : W} {d : List W}
151
+ (hd : s.dstack = b :: a :: d) :
152
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := (a &&& b) :: d } x' ∧
153
+ x'.pc = base + isize (.and : WordDialect.Instr 64) :=
154
+ sim_bin_instr hg hL hs hpc .and [.and rax rcx] rax (by simp) (· &&& ·) midspec_and
155
+ (by simp [Instr.straightX]) rfl (by simp [isize, ufSize]) hat hd
156
+
157
+ theorem sim_or_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
158
+ (hat : XAt code base (lowerInstr L base off .or)) {b a : W} {d : List W}
159
+ (hd : s.dstack = b :: a :: d) :
160
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := (a ||| b) :: d } x' ∧
161
+ x'.pc = base + isize (.or : WordDialect.Instr 64) :=
162
+ sim_bin_instr hg hL hs hpc .or [.or rax rcx] rax (by simp) (· ||| ·) midspec_or
163
+ (by simp [Instr.straightX]) rfl (by simp [isize, ufSize]) hat hd
164
+
165
+ theorem sim_xor_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
166
+ (hat : XAt code base (lowerInstr L base off .xor)) {b a : W} {d : List W}
167
+ (hd : s.dstack = b :: a :: d) :
168
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := (a ^^^ b) :: d } x' ∧
169
+ x'.pc = base + isize (.xor : WordDialect.Instr 64) :=
170
+ sim_bin_instr hg hL hs hpc .xor [.xor rax rcx] rax (by simp) (· ^^^ ·) midspec_xor
171
+ (by simp [Instr.straightX]) rfl (by simp [isize, ufSize]) hat hd
172
+
173
+ theorem sim_shl_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
174
+ (hat : XAt code base (lowerInstr L base off .shl)) {b a : W} {d : List W}
175
+ (hd : s.dstack = b :: a :: d) :
176
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := Word.shl a b :: d } x' ∧
177
+ x'.pc = base + isize (.shl : WordDialect.Instr 64) :=
178
+ sim_bin_instr hg hL hs hpc .shl _ rax (by simp) Word.shl midspec_shl
179
+ (by simp [Instr.straightX]) rfl (by simp [isize, ufSize]) hat hd
180
+
181
+ theorem sim_shr_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
182
+ (hat : XAt code base (lowerInstr L base off .shr)) {b a : W} {d : List W}
183
+ (hd : s.dstack = b :: a :: d) :
184
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := Word.shr a b :: d } x' ∧
185
+ x'.pc = base + isize (.shr : WordDialect.Instr 64) :=
186
+ sim_bin_instr hg hL hs hpc .shr _ rax (by simp) Word.shr midspec_shr
187
+ (by simp [Instr.straightX]) rfl (by simp [isize, ufSize]) hat hd
188
+
189
+ theorem sim_rotl_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
190
+ (hat : XAt code base (lowerInstr L base off .rotl)) {b a : W} {d : List W}
191
+ (hd : s.dstack = b :: a :: d) :
192
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := Word.rotl a b :: d } x' ∧
193
+ x'.pc = base + isize (.rotl : WordDialect.Instr 64) :=
194
+ sim_bin_instr hg hL hs hpc .rotl _ rax (by simp) Word.rotl midspec_rotl
195
+ (by simp [Instr.straightX]) rfl (by simp [isize, ufSize]) hat hd
196
+
197
+ theorem sim_rotr_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
198
+ (hat : XAt code base (lowerInstr L base off .rotr)) {b a : W} {d : List W}
199
+ (hd : s.dstack = b :: a :: d) :
200
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := Word.rotr a b :: d } x' ∧
201
+ x'.pc = base + isize (.rotr : WordDialect.Instr 64) :=
202
+ sim_bin_instr hg hL hs hpc .rotr _ rax (by simp) Word.rotr midspec_rotr
203
+ (by simp [Instr.straightX]) rfl (by simp [isize, ufSize]) hat hd
204
+
205
+ theorem sim_cmp_ok (cd : WordDialect.Cond) (hg : Geom c) (hL : L.dEnd = ofN c.dEnd)
206
+ (hs : Rel0 c p s x) (hpc : x.pc = base) (hat : XAt code base (lowerInstr L base off (.cmp cd)))
207
+ {b a : W} {d : List W} (hd : s.dstack = b :: a :: d) :
208
+ ∃ x', XSteps code x x' ∧
209
+ Rel0 c p { s with dstack := Word.ofBool (cd.eval a b) :: d } x' ∧
210
+ x'.pc = base + isize (.cmp cd : WordDialect.Instr 64) :=
211
+ sim_bin_instr hg hL hs hpc (.cmp cd) _ rdx (by simp) (fun a b => Word.ofBool (cd.eval a b))
212
+ (midspec_cmp cd) (by simp [Instr.straightX]) rfl (by simp [isize, ufSize]) hat hd
213
+
214
+ end Ops
215
+
216
+ end X86
217
+ end WordDialect
backend/x86_64/X86/SimStack.lean ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.SimOps
2
+
3
+ /-!
4
+ # X86.SimStack
5
+
6
+ Single-instruction step lemmas (`load`/`store`/`addImm` on the data stack, `movImm`, `not`) and
7
+ the simulation theorems for the instructions that only move words on the data stack:
8
+ `word ptr not dup drop swap over rot`.
9
+ -/
10
+
11
+ namespace WordDialect
12
+ namespace X86
13
+
14
+ open Reg
15
+
16
+ section Steps
17
+
18
+ variable {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)}
19
+
20
+ theorem ld_step (hs : Rel0 c p s x) {j : Nat} {v : W} (hj : s.dstack[j]? = some v) {rd : Reg}
21
+ (hrd : rd = rax ∨ rd = rcx ∨ rd = rdx ∨ rd = rbx) {disp : Int} (hd : disp = 8 * (j : Int))
22
+ (hf : code[x.pc]? = some (.load rd Reg.r15 disp)) :
23
+ ∃ x', XSteps code x x' ∧ Rel0 c p s x' ∧ x'.pc = x.pc + 1 ∧ x'.regs rd = v ∧
24
+ (∀ r, r ≠ rd → x'.regs r = x.regs r) ∧ x'.mem = x.mem := by
25
+ refine ⟨x.mov rd v, XSteps.single (by rw [xstep_of_fetch hf, ld_stack hs hj rd hd]),
26
+ mov_rel0 hs rd (scratch_ne rd hrd) v, ?_, ?_, ?_, ?_⟩
27
+ · simp [M.mov, M.setReg, M.adv]
28
+ · simp [M.mov, M.setReg, M.adv]
29
+ · intro r hr; simp [M.mov, M.setReg, M.adv, hr]
30
+ · simp [M.mov, M.setReg, M.adv]
31
+
32
+ theorem st_step (hg : Geom c) (hs : Rel0 c p s x) {j : Nat} (hj : j < s.dstack.length) {rs : Reg}
33
+ {disp : Int} (hd : disp = 8 * (j : Int)) (hf : code[x.pc]? = some (.store Reg.r15 disp rs)) :
34
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := s.dstack.set j (x.regs rs) } x' ∧
35
+ x'.pc = x.pc + 1 ∧ x'.regs = x.regs := by
36
+ obtain ⟨mem', e, hr⟩ := st_stack hg hs hj rs hd
37
+ exact ⟨_, XSteps.single (by rw [xstep_of_fetch hf, e]), hr, by simp [M.adv], by simp [M.adv]⟩
38
+
39
+ theorem adj_step (hg : Geom c) (hs : Rel0 c p s x) {n : Nat} (hn : n ≤ s.dstack.length) {δ : Int}
40
+ (hd : δ = 8 * (n : Int)) (hf : code[x.pc]? = some (.addImm Reg.r15 δ)) :
41
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := s.dstack.drop n } x' ∧
42
+ x'.pc = x.pc + 1 ∧ (∀ r, r ≠ Reg.r15 → x'.regs r = x.regs r) := by
43
+ obtain ⟨e, hr⟩ := adj_drop hg hs hn hd
44
+ exact ⟨_, XSteps.single (by rw [xstep_of_fetch hf, e]), hr, by simp [M.arith, M.setReg],
45
+ by intro r hr; simp [M.arith, M.setReg, hr]⟩
46
+
47
+ theorem movimm_step (hs : Rel0 c p s x) {rd : Reg} (hrd : rd = rax ∨ rd = rcx ∨ rd = rdx ∨ rd = rbx)
48
+ {v : W} (hf : code[x.pc]? = some (.movImm rd v)) :
49
+ ∃ x', XSteps code x x' ∧ Rel0 c p s x' ∧ x'.pc = x.pc + 1 ∧ x'.regs rd = v ∧
50
+ (∀ r, r ≠ rd → x'.regs r = x.regs r) ∧ x'.mem = x.mem := by
51
+ refine ⟨x.mov rd v, XSteps.single (by rw [xstep_of_fetch hf]; rfl),
52
+ mov_rel0 hs rd (scratch_ne rd hrd) v, ?_, ?_, ?_, ?_⟩
53
+ · simp [M.mov, M.setReg, M.adv]
54
+ · simp [M.mov, M.setReg, M.adv]
55
+ · intro r hr; simp [M.mov, M.setReg, M.adv, hr]
56
+ · simp [M.mov, M.setReg, M.adv]
57
+
58
+ theorem not_step (hs : Rel0 c p s x) (hf : code[x.pc]? = some (.not rax)) :
59
+ ∃ x', XSteps code x x' ∧ Rel0 c p s x' ∧ x'.pc = x.pc + 1 ∧ x'.regs rax = ~~~x.regs rax := by
60
+ refine ⟨x.mov rax (~~~x.regs rax), XSteps.single (by rw [xstep_of_fetch hf]; rfl),
61
+ mov_rel0 hs rax (scratch_ne rax (by simp)) _, ?_, ?_⟩
62
+ · simp [M.mov, M.setReg, M.adv]
63
+ · simp [M.mov, M.setReg, M.adv]
64
+
65
+ end Steps
66
+
67
+ /-- Placement of `ovfD L base ++ [i0, i1, i2]`. -/
68
+ theorem xat_ovf3 {code : List (Instr Nat)} {L : Layout} {base : Nat} {i0 i1 i2 : Instr Nat}
69
+ (hat : XAt code base (ovfD L base ++ [i0, i1, i2])) :
70
+ XAt code base (ovfD L base) ∧ code[base + 4]? = some i0 ∧ code[base + 4 + 1]? = some i1 ∧
71
+ code[base + 4 + 1 + 1]? = some i2 := by
72
+ obtain ⟨hato, hatr⟩ := xat_append.mp hat
73
+ have hl : (ovfD L base).length = 4 := rfl
74
+ rw [hl] at hatr
75
+ obtain ⟨f0, f1, f2, _⟩ := hatr
76
+ exact ⟨hato, f0, f1, f2⟩
77
+
78
+ /-- Placement of `uf L n base ++ ovfD L (base + ufSize n) ++ [i0, i1, i2]`. -/
79
+ theorem xat_uf_ovf {code : List (Instr Nat)} {L : Layout} {base n : Nat} (hn : 0 < n)
80
+ {i0 i1 i2 : Instr Nat} {rest : List (Instr Nat)} (hrest : rest = [i0, i1, i2])
81
+ (hat : XAt code base (uf L n base ++ ovfD L (base + ufSize n) ++ rest)) :
82
+ XAt code base (uf L n base) ∧ XAt code (base + 4) (ovfD L (base + 4)) ∧
83
+ code[base + 8]? = some i0 ∧ code[base + 8 + 1]? = some i1 ∧ code[base + 8 + 1 + 1]? = some i2 := by
84
+ subst hrest
85
+ rw [ufSize_pos hn] at hat
86
+ obtain ⟨hatuo, hatr⟩ := xat_append.mp hat
87
+ obtain ⟨hatuf, hato⟩ := xat_append.mp hatuo
88
+ have hufl : (uf L n base).length = 4 := by rw [uf_eq L hn]; rfl
89
+ rw [hufl] at hato
90
+ have hl : (uf L n base ++ ovfD L (base + 4)).length = 8 := by simp [hufl, ovfD]
91
+ rw [hl] at hatr
92
+ obtain ⟨f0, f1, f2, _⟩ := hatr
93
+ exact ⟨hatuf, hato, f0, f1, f2⟩
94
+
95
+ section Ops
96
+
97
+ variable {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {code : List (Instr Nat)}
98
+ {L : Layout} {base : Nat} {off : Nat → Nat}
99
+
100
+ theorem sim_not_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
101
+ (hat : XAt code base (lowerInstr L base off .not)) {a : W} {d : List W}
102
+ (hd : s.dstack = a :: d) :
103
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := (~~~a) :: d } x' ∧
104
+ x'.pc = base + isize (.not : WordDialect.Instr 64) := by
105
+ have hat' : XAt code base (uf L 1 base ++ [.load rax Reg.r15 0, .not rax, .store Reg.r15 0 rax]) := hat
106
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
107
+ have hufl : (uf L 1 base).length = 4 := by simp [uf]
108
+ rw [hufl] at hatr
109
+ obtain ⟨f0, f1, f2, _⟩ := hatr
110
+ have hk : 1 ≤ s.dstack.length := by simp [hd]
111
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
112
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, _, _⟩ := ld_step (code := code) hr1 (j := 0) (v := a)
113
+ (by simp [hd]) (rd := rax) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
114
+ obtain ⟨x3, hs3, hr3, hpc3, hv3⟩ := not_step (code := code) hr2 (fetch_cast f1 (by omega))
115
+ obtain ⟨x4, hs4, hr4, hpc4, _⟩ := st_step (code := code) hg hr3 (j := 0) (by simp [hd])
116
+ (rs := rax) (disp := 0) (by simp) (fetch_cast f2 (by omega))
117
+ have e : s.dstack.set 0 (x3.regs rax) = (~~~a) :: d := by simp [hd, hv3, hv2]
118
+ rw [e] at hr4
119
+ exact ⟨x4, ((hs1.trans hs2).trans hs3).trans hs4, hr4, by simp [isize, ufSize]; omega⟩
120
+
121
+ theorem sim_dup_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
122
+ (hLd : L.dLim = ofN (c.dBase + 8)) (hat : XAt code base (lowerInstr L base off .dup)) {a : W}
123
+ {d : List W} (hd : s.dstack = a :: d) (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
124
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := a :: a :: d } x' ∧
125
+ x'.pc = base + isize (.dup : WordDialect.Instr 64) := by
126
+ obtain ⟨hatuf, hato, f0, f1, f2⟩ := xat_uf_ovf (L := L) (n := 1) (by decide) rfl hat
127
+ have hk : 1 ≤ s.dstack.length := by simp [hd]
128
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
129
+ obtain ⟨x1', hs1', hr1', hpc1', _, _⟩ := ovfD_pass hg hr1 hLd hfit hpc1 hato
130
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, _, _⟩ := ld_step (code := code) hr1' (j := 0) (v := a)
131
+ (by simp [hd]) (rd := rax) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
132
+ obtain ⟨x3, hs3, hr3, hpc3, _⟩ := push_pair hg hr2 rax (by decide) hfit
133
+ ⟨fetch_cast f1 (by omega), fetch_cast f2 (by omega), trivial⟩
134
+ rw [hv2, hd] at hr3
135
+ exact ⟨x3, ((hs1.trans hs1').trans hs2).trans hs3, hr3, by simp [isize, ufSize]; omega⟩
136
+
137
+ theorem sim_dup_ovf (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
138
+ (hLd : L.dLim = ofN (c.dBase + 8)) (hat : XAt code base (lowerInstr L base off .dup)) {a : W}
139
+ {d : List W} (hd : s.dstack = a :: d) (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
140
+ XExec code x .overflow := by
141
+ obtain ⟨hatuf, hato, _⟩ := xat_uf_ovf (L := L) (n := 1) (by decide) rfl hat
142
+ obtain ⟨x1, hs1, hr1, hpc1, _, _⟩ := uf_pass hg hs hL (by decide) (by decide) (by simp [hd]) hpc hatuf
143
+ exact XExec.of_steps hs1 (ovfD_trap hg hr1 hLd h hpc1 hato)
144
+
145
+ theorem sim_drop_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
146
+ (hat : XAt code base (lowerInstr L base off .drop)) {a : W} {d : List W}
147
+ (hd : s.dstack = a :: d) :
148
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := d } x' ∧
149
+ x'.pc = base + isize (.drop : WordDialect.Instr 64) := by
150
+ have hat' : XAt code base (uf L 1 base ++ [.addImm Reg.r15 8]) := hat
151
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
152
+ have hufl : (uf L 1 base).length = 4 := by simp [uf]
153
+ rw [hufl] at hatr
154
+ obtain ⟨f0, _⟩ := hatr
155
+ have hk : 1 ≤ s.dstack.length := by simp [hd]
156
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
157
+ obtain ⟨x2, hs2, hr2, hpc2, _⟩ := adj_step (code := code) hg hr1 (n := 1) (by simp [hd])
158
+ (δ := 8) (by simp) (fetch_cast f0 (by omega))
159
+ have e : (s.dstack).drop 1 = d := by simp [hd]
160
+ rw [e] at hr2
161
+ exact ⟨x2, hs1.trans hs2, hr2, by simp [isize, ufSize]; omega⟩
162
+
163
+ theorem sim_word_ok (hg : Geom c) (hs : Rel0 c p s x) (hpc : x.pc = base) (w : W)
164
+ (hLd : L.dLim = ofN (c.dBase + 8)) (hat : XAt code base (lowerInstr L base off (.word w)))
165
+ (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
166
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := w :: s.dstack } x' ∧
167
+ x'.pc = base + isize (.word w : WordDialect.Instr 64) := by
168
+ obtain ⟨hato, f0, f1, f2⟩ := xat_ovf3 (L := L) (by exact hat)
169
+ obtain ⟨x0, hs0, hr0, hpc0, _, _⟩ := ovfD_pass hg hs hLd hfit hpc hato
170
+ obtain ⟨x1, hs1, hr1, hpc1, hv1, _⟩ := movimm_step (code := code) hr0 (rd := rax) (by simp)
171
+ (v := w) (by rw [hpc0]; exact f0)
172
+ obtain ⟨x2, hs2, hr2, hpc2, _⟩ := push_pair hg hr1 rax (by decide) hfit
173
+ ⟨fetch_cast f1 (by omega), fetch_cast f2 (by omega), trivial⟩
174
+ rw [hv1] at hr2
175
+ exact ⟨x2, (hs0.trans hs1).trans hs2, hr2, by simp [isize]; omega⟩
176
+
177
+ theorem sim_word_ovf (hg : Geom c) (hs : Rel0 c p s x) (hpc : x.pc = base) (w : W)
178
+ (hLd : L.dLim = ofN (c.dBase + 8)) (hat : XAt code base (lowerInstr L base off (.word w)))
179
+ (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) : XExec code x .overflow := by
180
+ obtain ⟨hato, _⟩ := xat_ovf3 (L := L) (by exact hat)
181
+ exact ovfD_trap hg hs hLd h hpc hato
182
+
183
+ theorem sim_ptr_ok (hg : Geom c) (hs : Rel0 c p s x) (hpc : x.pc = base) (q : WordDialect.Ptr 64)
184
+ (hLd : L.dLim = ofN (c.dBase + 8)) (hat : XAt code base (lowerInstr L base off (.ptr q)))
185
+ (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
186
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := q.toWord :: s.dstack } x' ∧
187
+ x'.pc = base + isize (.ptr q : WordDialect.Instr 64) :=
188
+ sim_word_ok (L := L) (off := off) hg hs hpc q.toWord hLd hat hfit
189
+
190
+ theorem sim_ptr_ovf (hg : Geom c) (hs : Rel0 c p s x) (hpc : x.pc = base) (q : WordDialect.Ptr 64)
191
+ (hLd : L.dLim = ofN (c.dBase + 8)) (hat : XAt code base (lowerInstr L base off (.ptr q)))
192
+ (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) : XExec code x .overflow :=
193
+ sim_word_ovf (L := L) (off := off) hg hs hpc q.toWord hLd hat h
194
+
195
+ theorem sim_swap_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
196
+ (hat : XAt code base (lowerInstr L base off .swap)) {b a : W} {d : List W}
197
+ (hd : s.dstack = b :: a :: d) :
198
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := a :: b :: d } x' ∧
199
+ x'.pc = base + isize (.swap : WordDialect.Instr 64) := by
200
+ have hat' : XAt code base (uf L 2 base ++ [.load rax Reg.r15 0, .load rcx Reg.r15 8,
201
+ .store Reg.r15 0 rcx, .store Reg.r15 8 rax]) := hat
202
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
203
+ have hufl : (uf L 2 base).length = 4 := by simp [uf]
204
+ rw [hufl] at hatr
205
+ obtain ⟨f0, f1, f2, f3, _⟩ := hatr
206
+ have hk : 2 ≤ s.dstack.length := by simp [hd]
207
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
208
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, _, _⟩ := ld_step (code := code) hr1 (j := 0) (v := b)
209
+ (by simp [hd]) (rd := rax) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
210
+ obtain ⟨x3, hs3, hr3, hpc3, hv3, ho3, _⟩ := ld_step (code := code) hr2 (j := 1) (v := a)
211
+ (by simp [hd]) (rd := rcx) (by simp) (disp := 8) (by simp) (fetch_cast f1 (by omega))
212
+ have hax3 : x3.regs rax = b := by rw [ho3 rax (by decide), hv2]
213
+ obtain ⟨x4, hs4, hr4, hpc4, hreg4⟩ := st_step (code := code) hg hr3 (j := 0) (by simp [hd])
214
+ (rs := rcx) (disp := 0) (by simp) (fetch_cast f2 (by omega))
215
+ have hlen : 1 < ({ s with dstack := s.dstack.set 0 (x3.regs rcx) } : State 64).dstack.length := by
216
+ simp [hd]
217
+ obtain ⟨x5, hs5, hr5, hpc5, _⟩ := st_step (code := code) hg hr4 (j := 1) hlen
218
+ (rs := rax) (disp := 8) (by simp) (fetch_cast f3 (by omega))
219
+ have hr5' : Rel0 c p { s with dstack := (s.dstack.set 0 (x3.regs rcx)).set 1 (x4.regs rax) } x5 := hr5
220
+ have e : (s.dstack.set 0 (x3.regs rcx)).set 1 (x4.regs rax) = a :: b :: d := by
221
+ simp [hd, hv3, hreg4, hax3]
222
+ rw [e] at hr5'
223
+ exact ⟨x5, (((hs1.trans hs2).trans hs3).trans hs4).trans hs5, hr5', by simp [isize, ufSize]; omega⟩
224
+
225
+ theorem sim_over_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
226
+ (hLd : L.dLim = ofN (c.dBase + 8)) (hat : XAt code base (lowerInstr L base off .over)) {b a : W}
227
+ {d : List W} (hd : s.dstack = b :: a :: d) (hfit : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
228
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := a :: b :: a :: d } x' ∧
229
+ x'.pc = base + isize (.over : WordDialect.Instr 64) := by
230
+ obtain ⟨hatuf, hato, f0, f1, f2⟩ := xat_uf_ovf (L := L) (n := 2) (by decide) rfl hat
231
+ have hk : 2 ≤ s.dstack.length := by simp [hd]
232
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
233
+ obtain ⟨x1', hs1', hr1', hpc1', _, _⟩ := ovfD_pass hg hr1 hLd hfit hpc1 hato
234
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, _, _⟩ := ld_step (code := code) hr1' (j := 1) (v := a)
235
+ (by simp [hd]) (rd := rax) (by simp) (disp := 8) (by simp) (fetch_cast f0 (by omega))
236
+ obtain ⟨x3, hs3, hr3, hpc3, _⟩ := push_pair hg hr2 rax (by decide) hfit
237
+ ⟨fetch_cast f1 (by omega), fetch_cast f2 (by omega), trivial⟩
238
+ rw [hv2, hd] at hr3
239
+ exact ⟨x3, ((hs1.trans hs1').trans hs2).trans hs3, hr3, by simp [isize, ufSize]; omega⟩
240
+
241
+ theorem sim_over_ovf (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
242
+ (hLd : L.dLim = ofN (c.dBase + 8)) (hat : XAt code base (lowerInstr L base off .over)) {b a : W}
243
+ {d : List W} (hd : s.dstack = b :: a :: d) (h : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd) :
244
+ XExec code x .overflow := by
245
+ obtain ⟨hatuf, hato, _⟩ := xat_uf_ovf (L := L) (n := 2) (by decide) rfl hat
246
+ obtain ⟨x1, hs1, hr1, hpc1, _, _⟩ := uf_pass hg hs hL (by decide) (by decide) (by simp [hd]) hpc hatuf
247
+ exact XExec.of_steps hs1 (ovfD_trap hg hr1 hLd h hpc1 hato)
248
+
249
+ theorem sim_rot_ok (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hs : Rel0 c p s x) (hpc : x.pc = base)
250
+ (hat : XAt code base (lowerInstr L base off .rot)) {cc b a : W} {d : List W}
251
+ (hd : s.dstack = cc :: b :: a :: d) :
252
+ ∃ x', XSteps code x x' ∧ Rel0 c p { s with dstack := a :: cc :: b :: d } x' ∧
253
+ x'.pc = base + isize (.rot : WordDialect.Instr 64) := by
254
+ have hat' : XAt code base (uf L 3 base ++ [.load rax Reg.r15 0, .load rcx Reg.r15 8,
255
+ .load rdx Reg.r15 16, .store Reg.r15 0 rdx, .store Reg.r15 8 rax, .store Reg.r15 16 rcx]) := hat
256
+ obtain ⟨hatuf, hatr⟩ := xat_append.mp hat'
257
+ have hufl : (uf L 3 base).length = 4 := by simp [uf]
258
+ rw [hufl] at hatr
259
+ obtain ⟨f0, f1, f2, f3, f4, f5, _⟩ := hatr
260
+ have hk : 3 ≤ s.dstack.length := by simp [hd]
261
+ obtain ⟨x1, hs1, hr1, hpc1, hm1, _⟩ := uf_pass hg hs hL (by decide) (by decide) hk hpc hatuf
262
+ obtain ⟨x2, hs2, hr2, hpc2, hv2, _, _⟩ := ld_step (code := code) hr1 (j := 0) (v := cc)
263
+ (by simp [hd]) (rd := rax) (by simp) (disp := 0) (by simp) (fetch_cast f0 (by omega))
264
+ obtain ⟨x3, hs3, hr3, hpc3, hv3, ho3, _⟩ := ld_step (code := code) hr2 (j := 1) (v := b)
265
+ (by simp [hd]) (rd := rcx) (by simp) (disp := 8) (by simp) (fetch_cast f1 (by omega))
266
+ obtain ⟨x4, hs4, hr4, hpc4, hv4, ho4, _⟩ := ld_step (code := code) hr3 (j := 2) (v := a)
267
+ (by simp [hd]) (rd := rdx) (by simp) (disp := 16) (by simp) (fetch_cast f2 (by omega))
268
+ have hax4 : x4.regs rax = cc := by rw [ho4 rax (by decide), ho3 rax (by decide), hv2]
269
+ have hcx4 : x4.regs rcx = b := by rw [ho4 rcx (by decide), hv3]
270
+ obtain ⟨x5, hs5, hr5, hpc5, hreg5⟩ := st_step (code := code) hg hr4 (j := 0) (by simp [hd])
271
+ (rs := rdx) (disp := 0) (by simp) (fetch_cast f3 (by omega))
272
+ have hl1 : 1 < ({ s with dstack := s.dstack.set 0 (x4.regs rdx) } : State 64).dstack.length := by
273
+ simp [hd]
274
+ obtain ⟨x6, hs6, hr6, hpc6, hreg6⟩ := st_step (code := code) hg hr5 (j := 1) hl1
275
+ (rs := rax) (disp := 8) (by simp) (fetch_cast f4 (by omega))
276
+ have hl2 : 2 < ({ { s with dstack := s.dstack.set 0 (x4.regs rdx) } with
277
+ dstack := (s.dstack.set 0 (x4.regs rdx)).set 1 (x5.regs rax) } : State 64).dstack.length := by
278
+ simp [hd]
279
+ obtain ⟨x7, hs7, hr7, hpc7, _⟩ := st_step (code := code) hg hr6 (j := 2) hl2
280
+ (rs := rcx) (disp := 16) (by simp) (fetch_cast f5 (by omega))
281
+ have hr7' : Rel0 c p { s with dstack := ((s.dstack.set 0 (x4.regs rdx)).set 1 (x5.regs rax)).set 2 (x6.regs rcx) } x7 := hr7
282
+ have e : ((s.dstack.set 0 (x4.regs rdx)).set 1 (x5.regs rax)).set 2 (x6.regs rcx) =
283
+ a :: cc :: b :: d := by
284
+ simp [hd, hv4, hreg5, hreg6, hax4, hcx4]
285
+ rw [e] at hr7'
286
+ exact ⟨x7, (((((hs1.trans hs2).trans hs3).trans hs4).trans hs5).trans hs6).trans hs7, hr7',
287
+ by simp [isize, ufSize]; omega⟩
288
+
289
+ end Ops
290
+
291
+ end X86
292
+ end WordDialect
backend/x86_64/X86/SimStep.lean ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import X86.SimAux
2
+
3
+ /-!
4
+ # X86.SimStep
5
+
6
+ One IR step is simulated by the lowered x86 code, for every instruction and every outcome
7
+ (`next`, `halted`, `trapped`). This is the case analysis over the whole instruction set that the
8
+ whole-program theorem iterates.
9
+
10
+ Every instruction that grows the data stack or the auxiliary stack, and every `call`, first checks
11
+ for room and exits with `overflow` when the stack is full. So `sim_exec` has no capacity hypothesis: the code either
12
+ simulates the IR step, or the state lacks the headroom `Fits` describes and the code overflows.
13
+ -/
14
+
15
+ namespace WordDialect
16
+ namespace X86
17
+
18
+ open Reg
19
+
20
+ /-- Headroom for one step: room to push one data word, two return addresses' worth of native
21
+ stack, and one auxiliary word. -/
22
+ def Fits (c : Cfg) (s : State 64) : Prop :=
23
+ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd ∧ s.rstack.length + 2 ≤ c.rcap ∧
24
+ c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd
25
+
26
+ /-- What simulating an IR outcome means on the x86 side. -/
27
+ def Sim (c : Cfg) (p : Prog 64) (code : List (Instr Nat)) (x : M) : Outcome 64 → Prop
28
+ | .next s' => ∃ x', XSteps code x x' ∧ Rel c p s' x'
29
+ | .halted s' => ∃ x', XExec code x (.halted x') ∧ Rel0 c p s' x'
30
+ | .trapped t => XExec code x (.trapped t)
31
+
32
+ theorem shape1 {l : List W} (h : 1 ≤ l.length) : ∃ a d, l = a :: d := by
33
+ cases l with
34
+ | nil => simp at h
35
+ | cons a d => exact ⟨a, d, rfl⟩
36
+
37
+ theorem shape2 {l : List W} (h : 2 ≤ l.length) : ∃ b a d, l = b :: a :: d := by
38
+ cases l with
39
+ | nil => simp at h
40
+ | cons b l =>
41
+ cases l with
42
+ | nil => simp at h
43
+ | cons a d => exact ⟨b, a, d, rfl⟩
44
+
45
+ theorem shape3 {l : List W} (h : 3 ≤ l.length) : ∃ cc y v d, l = cc :: y :: v :: d := by
46
+ cases l with
47
+ | nil => simp at h
48
+ | cons cc l =>
49
+ cases l with
50
+ | nil => simp at h
51
+ | cons y l =>
52
+ cases l with
53
+ | nil => simp at h
54
+ | cons v d => exact ⟨cc, y, v, d, rfl⟩
55
+
56
+ theorem lowerInstr_uf (L : Layout) (base : Nat) (off : Nat → Nat) (i : WordDialect.Instr 64)
57
+ (h : 0 < i.pops) :
58
+ ∃ rest, lowerInstr L base off i = uf L i.pops base ++ rest ∧ i.pops ≤ 3 := by
59
+ cases i <;> first | exact ⟨_, rfl, by simp [Instr.pops]⟩ | exact absurd h (by simp [Instr.pops])
60
+
61
+ theorem Rel0.setPc {c : Cfg} {p : Prog 64} {s : State 64} {x : M} (h : Rel0 c p s x) (n : Nat) :
62
+ Rel0 c p { s with pc := n } x :=
63
+ { r15 := h.r15, r14 := h.r14, r13 := h.r13, r12 := h.r12, rsp := h.rsp, rbp := h.rbp,
64
+ stack := h.stack, regs := h.regs, mem := h.mem, rs := h.rs, aux := h.aux, irvalid := h.irvalid,
65
+ xvalid := h.xvalid, capD := h.capD, capR := h.capR, capA := h.capA }
66
+
67
+ section Step
68
+
69
+ variable {c : Cfg} {p : Prog 64} {s : State 64} {x : M} {L : Layout}
70
+
71
+ theorem sim_next {code : List (Instr Nat)} {s' : State 64}
72
+ (h : ∃ x', XSteps code x x' ∧ Rel c p s' x') : Sim c p code x (.next s') := h
73
+
74
+ theorem sim_halted {code : List (Instr Nat)} {s' : State 64}
75
+ (h : ∃ x', XExec code x (.halted x') ∧ Rel0 c p s' x') : Sim c p code x (.halted s') := h
76
+
77
+ theorem sim_trapped {code : List (Instr Nat)} {t : Trap} (h : XExec code x (.trapped t)) :
78
+ Sim c p code x (.trapped t) := h
79
+
80
+ theorem rel_fall {i : WordDialect.Instr 64} (hi : p[s.pc]? = some i) {s' : State 64}
81
+ {x' : M} (h0 : Rel0 c p s' x') (hpc : x'.pc = offs p s.pc + isize i) :
82
+ Rel c p { s' with pc := s.pc + 1 } x' :=
83
+ ⟨h0.setPc _, by rw [hpc, offs_succ hi]⟩
84
+
85
+ theorem sim_exec (hg : Geom c) (hL : L.dEnd = ofN c.dEnd) (hMs : L.memSize = c.M)
86
+ (hLd : L.dLim = ofN (c.dBase + 8)) (hLr : L.rGap = ofN (8 * c.rcap - 16))
87
+ (hLa : L.aEnd = ofN c.aEnd) (hLal : L.aLim = ofN (c.aBase + 8))
88
+ (hsz : ∀ k, offs p k < 2 ^ 64) (hs : Rel c p s x)
89
+ {i : WordDialect.Instr 64} (hi : p[s.pc]? = some i)
90
+ (hreg : ∀ r, (i = .push r ∨ i = .pop r) → r < c.nregs) :
91
+ Sim c p (lowerProg L p) x (WordDialect.exec i s) ∨
92
+ (¬ Fits c s ∧ XExec (lowerProg L p) x .overflow) := by
93
+ have nfD : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd → ¬ Fits c s := fun h hf => h hf.1
94
+ have nfR : ¬ s.rstack.length + 2 ≤ c.rcap → ¬ Fits c s := fun h hf => h hf.2.1
95
+ have nfA : ¬ c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd → ¬ Fits c s := fun h hf => h hf.2.2
96
+ have hpc : x.pc = offs p s.pc := hs.2
97
+ have hs0 : Rel0 c p s x := hs.1
98
+ have hat := lowerProg_at L p s.pc i hi
99
+ by_cases hk : s.dstack.length < i.pops
100
+ · rw [exec_underflow i s hk]
101
+ obtain ⟨rest, hlow, h3⟩ := lowerInstr_uf L (offs p s.pc) (offs p) i (by omega)
102
+ rw [hlow] at hat
103
+ exact .inl <| sim_trapped (sim_uf_trap hg hs0 hL (by omega) h3 hk hpc hat)
104
+ · have hk' : i.pops ≤ s.dstack.length := by omega
105
+ cases i with
106
+ | word w =>
107
+ by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
108
+ rotate_left
109
+ · exact .inr ⟨nfD hf, sim_word_ovf hg hs0 hpc w hLd hat hf⟩
110
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_word_ok hg hs0 hpc w hLd hat hf
111
+ rw [exec_word]
112
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
113
+ | ptr q =>
114
+ by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
115
+ rotate_left
116
+ · exact .inr ⟨nfD hf, sim_ptr_ovf hg hs0 hpc q hLd hat hf⟩
117
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_ptr_ok hg hs0 hpc q hLd hat hf
118
+ rw [exec_ptr]
119
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
120
+ | load =>
121
+ obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
122
+ cases hm : s.mem.read? a with
123
+ | none =>
124
+ rw [exec_load_bad hd hm]
125
+ exact .inl <| sim_trapped (sim_load_trap hg hL hMs hs0 hpc hat hd hm)
126
+ | some v =>
127
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_load_ok hg hL hMs hs0 hpc hat hd hm
128
+ rw [exec_load hd hm]
129
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
130
+ | store =>
131
+ obtain ⟨a, v, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
132
+ cases hm : s.mem.write? a v with
133
+ | none =>
134
+ rw [exec_store_bad hd hm]
135
+ exact .inl <| sim_trapped (sim_store_trap hg hL hMs hs0 hpc hat hd hm)
136
+ | some m =>
137
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_store_ok hg hL hMs hs0 hpc hat hd hm
138
+ rw [exec_store hd hm]
139
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
140
+ | add =>
141
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
142
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_add_ok hg hL hs0 hpc hat hd
143
+ rw [exec_add hd]
144
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
145
+ | sub =>
146
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
147
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_sub_ok hg hL hs0 hpc hat hd
148
+ rw [exec_sub hd]
149
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
150
+ | mul =>
151
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
152
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_mul_ok hg hL hs0 hpc hat hd
153
+ rw [exec_mul hd]
154
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
155
+ | and =>
156
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
157
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_and_ok hg hL hs0 hpc hat hd
158
+ rw [exec_and hd]
159
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
160
+ | or =>
161
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
162
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_or_ok hg hL hs0 hpc hat hd
163
+ rw [exec_or hd]
164
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
165
+ | xor =>
166
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
167
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_xor_ok hg hL hs0 hpc hat hd
168
+ rw [exec_xor hd]
169
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
170
+ | shl =>
171
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
172
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_shl_ok hg hL hs0 hpc hat hd
173
+ rw [exec_shl hd]
174
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
175
+ | shr =>
176
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
177
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_shr_ok hg hL hs0 hpc hat hd
178
+ rw [exec_shr hd]
179
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
180
+ | rotl =>
181
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
182
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_rotl_ok hg hL hs0 hpc hat hd
183
+ rw [exec_rotl hd]
184
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
185
+ | rotr =>
186
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
187
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_rotr_ok hg hL hs0 hpc hat hd
188
+ rw [exec_rotr hd]
189
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
190
+ | cmp cd =>
191
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
192
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_cmp_ok cd hg hL hs0 hpc hat hd
193
+ rw [exec_cmp hd]
194
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
195
+ | div =>
196
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
197
+ by_cases hb : b = 0#64
198
+ · subst hb
199
+ rw [exec_div_zero hd]
200
+ exact .inl <| sim_trapped (sim_div_zero hg hL hs0 hpc hat hd)
201
+ · obtain ⟨x', hst, h0, hpc'⟩ := sim_div_ok hg hL hs0 hpc hat hd hb
202
+ rw [exec_div hd hb]
203
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
204
+ | sdiv =>
205
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
206
+ by_cases hb : b = 0#64
207
+ · subst hb
208
+ rw [exec_sdiv_zero hd]
209
+ exact .inl <| sim_trapped (sim_sdiv_zero hg hL hs0 hpc hat hd)
210
+ · obtain ⟨x', hst, h0, hpc'⟩ := sim_sdiv_ok hg hL hs0 hpc hat hd hb
211
+ rw [exec_sdiv hd hb]
212
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
213
+ | not =>
214
+ obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
215
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_not_ok hg hL hs0 hpc hat hd
216
+ rw [exec_not hd]
217
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
218
+ | select =>
219
+ obtain ⟨cc, y, v, d, hd⟩ := shape3 (by simpa [Instr.pops] using hk')
220
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_select_ok hg hL hs0 hpc hat hd
221
+ rw [exec_select hd]
222
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
223
+ | jmp t =>
224
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_jmp_ok hs0 hpc hat
225
+ exact .inl <| sim_next ⟨x', hst, h0.setPc _, hpc'⟩
226
+ | branch t =>
227
+ obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
228
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_branch_ok hg hL hs0 hpc hat hd
229
+ by_cases hc : Word.isTrue a = true
230
+ · have e : WordDialect.exec (WordDialect.Instr.branch t) s = .next { s with pc := t, dstack := d } := by
231
+ simp [WordDialect.exec, hd, hc]
232
+ rw [e]
233
+ simp only [hc, if_true] at hpc'
234
+ exact .inl <| sim_next ⟨x', hst, h0.setPc _, hpc'⟩
235
+ · have hc' : Word.isTrue a = false := by simpa using hc
236
+ have e : WordDialect.exec (WordDialect.Instr.branch t) s = s.fall d := by simp [WordDialect.exec, hd, hc']
237
+ rw [e]
238
+ simp only [hc', Bool.false_eq_true, if_false] at hpc'
239
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
240
+ | call t =>
241
+ have hret : offs p s.pc + isize (.call t : WordDialect.Instr 64) = offs p (s.pc + 1) := by
242
+ rw [offs_succ hi]
243
+ by_cases hf : s.rstack.length + 2 ≤ c.rcap
244
+ rotate_left
245
+ · exact .inr ⟨nfR hf, sim_call_ovf hg hs0 hpc hLr hat hf⟩
246
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_call_ok hg hs0 hpc hLr hat hret hf
247
+ exact .inl <| sim_next ⟨x', hst, h0.setPc _, hpc'⟩
248
+ | ret =>
249
+ cases hr : s.rstack with
250
+ | nil =>
251
+ have e : WordDialect.exec WordDialect.Instr.ret s = .trapped .returnUnderflow := by simp [WordDialect.exec, hr]
252
+ rw [e]
253
+ exact .inl <| sim_trapped (sim_ret_trap hs0 hpc hat hr)
254
+ | cons a rs =>
255
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_ret_ok hg hs0 hpc hat hr (hsz a)
256
+ have e : WordDialect.exec WordDialect.Instr.ret s = .next { s with pc := a, rstack := rs } := by simp [WordDialect.exec, hr]
257
+ rw [e]
258
+ exact .inl <| sim_next ⟨x', hst, h0.setPc _, hpc'⟩
259
+ | push r =>
260
+ by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
261
+ rotate_left
262
+ · exact .inr ⟨nfD hf, sim_push_ovf hg hs0 hpc hLd hat hf⟩
263
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_push_ok hg hs0 hpc (hreg r (Or.inl rfl)) hLd hat hf
264
+ rw [exec_push]
265
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
266
+ | pop r =>
267
+ obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
268
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_pop_ok hg hL hs0 hpc (hreg r (Or.inr rfl)) hat hd
269
+ rw [exec_pop hd]
270
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
271
+ | dup =>
272
+ obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
273
+ by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
274
+ rotate_left
275
+ · exact .inr ⟨nfD hf, sim_dup_ovf hg hL hs0 hpc hLd hat hd hf⟩
276
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_dup_ok hg hL hs0 hpc hLd hat hd hf
277
+ rw [exec_dup hd]
278
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
279
+ | drop =>
280
+ obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
281
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_drop_ok hg hL hs0 hpc hat hd
282
+ rw [exec_drop hd]
283
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
284
+ | swap =>
285
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
286
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_swap_ok hg hL hs0 hpc hat hd
287
+ rw [exec_swap hd]
288
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
289
+ | over =>
290
+ obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
291
+ by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
292
+ rotate_left
293
+ · exact .inr ⟨nfD hf, sim_over_ovf hg hL hs0 hpc hLd hat hd hf⟩
294
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_over_ok hg hL hs0 hpc hLd hat hd hf
295
+ rw [exec_over hd]
296
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
297
+ | rot =>
298
+ obtain ⟨cc, b, a, d, hd⟩ := shape3 (by simpa [Instr.pops] using hk')
299
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_rot_ok hg hL hs0 hpc hat hd
300
+ rw [exec_rot hd]
301
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
302
+ | tor =>
303
+ obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
304
+ by_cases hf : c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd
305
+ rotate_left
306
+ · exact .inr ⟨nfA hf, sim_tor_ovf hg hL hLal hs0 hpc hat (by simp [hd]) hf⟩
307
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_tor_ok hg hL hLal hs0 hpc hat hd hf
308
+ have e : WordDialect.exec WordDialect.Instr.tor s =
309
+ .next { s with pc := s.pc + 1, dstack := d, astack := a :: s.astack } := by
310
+ simp [WordDialect.exec, hd]
311
+ rw [e]
312
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
313
+ | fromr =>
314
+ cases ha : s.astack with
315
+ | nil =>
316
+ have e : WordDialect.exec WordDialect.Instr.fromr s = .trapped .returnUnderflow := by
317
+ simp [WordDialect.exec, ha]
318
+ rw [e]
319
+ exact .inl <| sim_trapped (sim_fromr_trap hg hLa hs0 hpc hat ha)
320
+ | cons a as =>
321
+ by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
322
+ rotate_left
323
+ · exact .inr ⟨nfD hf, sim_fromr_ovf hg hLa hLd hs0 hpc hat (by simp [ha]) hf⟩
324
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_fromr_ok hg hLa hLd hs0 hpc hat ha hf
325
+ have e : WordDialect.exec WordDialect.Instr.fromr s =
326
+ .next { s with pc := s.pc + 1, dstack := a :: s.dstack, astack := as } := by
327
+ simp [WordDialect.exec, ha]
328
+ rw [e]
329
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
330
+ | rfetch =>
331
+ cases ha : s.astack with
332
+ | nil =>
333
+ have e : WordDialect.exec WordDialect.Instr.rfetch s = .trapped .returnUnderflow := by
334
+ simp [WordDialect.exec, ha]
335
+ rw [e]
336
+ exact .inl <| sim_trapped (sim_rfetch_trap hg hLa hs0 hpc hat ha)
337
+ | cons a as =>
338
+ by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
339
+ rotate_left
340
+ · exact .inr ⟨nfD hf, sim_rfetch_ovf hg hLa hLd hs0 hpc hat (by simp [ha]) hf⟩
341
+ obtain ⟨x', hst, h0, hpc'⟩ := sim_rfetch_ok hg hLa hLd hs0 hpc hat ha hf
342
+ have e : WordDialect.exec WordDialect.Instr.rfetch s = s.fall (a :: s.dstack) := by
343
+ simp [WordDialect.exec, ha]
344
+ rw [e]
345
+ exact .inl <| sim_next ⟨x', hst, rel_fall hi h0 hpc'⟩
346
+ | halt =>
347
+ rw [exec_halt]
348
+ exact .inl <| sim_halted ⟨x, sim_halt hs0 hpc hat, hs0⟩
349
+
350
+ end Step
351
+
352
+ end X86
353
+ end WordDialect
bcpl/BCPL.lean ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ import BCPL.Semantics
2
+ import BCPL.Compile
3
+ import BCPL.ExprCorrect
4
+ import BCPL.Correct
5
+ import BCPL.Example
bcpl/BCPL/Compile.lean ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import BCPL.Semantics
2
+
3
+ /-!
4
+ # BCPL.Compile
5
+
6
+ Lowering of BCPL to Universal Word IR. There is no BCPL runtime: variables are memory cells
7
+ at `addr x`, expressions leave their value on the data stack, and statements leave the data
8
+ stack as they found it.
9
+ -/
10
+
11
+ namespace WordDialect
12
+ namespace BCPL
13
+
14
+ open IR
15
+
16
+ def binCode {n : Nat} : BinOp → List (Instr n)
17
+ | .add => [.add]
18
+ | .sub => [.sub]
19
+ | .mul => [.mul]
20
+ | .div => [.sdiv]
21
+ | .and => [.and]
22
+ | .or => [.or]
23
+ | .neqv => [.xor]
24
+ | .shl => [.shl]
25
+ | .shr => [.shr]
26
+ | .eq => cmpFlagCode .eq
27
+ | .ne => cmpFlagCode .ne
28
+ | .lt => cmpFlagCode .slt
29
+ | .gt => cmpFlagCode .sgt
30
+ | .le => cmpFlagCode .sle
31
+ | .ge => cmpFlagCode .sge
32
+
33
+ /-- `neg`: compute `0 - v` as `v 0 SWAP SUB`. -/
34
+ def unCode {n : Nat} : UnOp → List (Instr n)
35
+ | .neg => [.word 0#n, .swap, .sub]
36
+ | .not => [.not]
37
+
38
+ def compileE {n : Nat} (addr : Var → Word n) : Expr → List (Instr n)
39
+ | .num z => [.word (BitVec.ofInt n z)]
40
+ | .var x => [.word (addr x), .load]
41
+ | .addrOf x => [.word (addr x)]
42
+ | .rv e => compileE addr e ++ [.load]
43
+ | .un o e => compileE addr e ++ unCode o
44
+ | .bin o a b => compileE addr a ++ compileE addr b ++ binCode o
45
+
46
+ /-- Right-hand side first, then (for an indirect target) the address, then `STORE`. -/
47
+ def assignCode {n : Nat} (addr : Var → Word n) : LVal → Expr → List (Instr n)
48
+ | .var x, e => compileE addr e ++ [.word (addr x), .store]
49
+ | .rv a, e => compileE addr e ++ compileE addr a ++ [.store]
50
+
51
+ def compile {n : Nat} (addr : Var → Word n) : Stmt → Frag n
52
+ | .skip => Frag.empty
53
+ | .assign l e => Frag.ofCode (assignCode addr l e)
54
+ | .seq a b => (compile addr a).seq (compile addr b)
55
+ | .test c t e => (Frag.ofCode (compileE addr c)).seq (Frag.ite (compile addr t) (compile addr e))
56
+ | .while c body => Frag.whileLoop (Frag.ofCode (compileE addr c)) (compile addr body)
57
+
58
+ /-- A complete program: the statement at address 0, followed by `HALT`. -/
59
+ def compileProgram {n : Nat} (addr : Var → Word n) (s : Stmt) : Prog n :=
60
+ (compile addr s).emit 0 ++ [.halt]
61
+
62
+ end BCPL
63
+ end WordDialect
bcpl/BCPL/Correct.lean ADDED
@@ -0,0 +1,298 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import BCPL.ExprCorrect
2
+
3
+ /-!
4
+ # BCPL.Correct
5
+
6
+ Translation correctness of the BCPL lowering.
7
+
8
+ `compile_correct`: whenever the reference semantics derives result `r` for statement `s`
9
+ from memory `m`, the machine, started at the statement's address with memory `m`, either
10
+ * reaches (via `Steps`) the end of the statement's code with the data stack unchanged and
11
+ memory equal to the BCPL result, or
12
+ * reaches a state whose next step traps with exactly the trap BCPL reports.
13
+
14
+ `compileProgram_correct` lifts this to a whole program run from the initial machine state.
15
+ -/
16
+
17
+ namespace WordDialect
18
+ namespace BCPL
19
+
20
+ open IR
21
+
22
+ theorem assignCode_ok {n : Nat} (addr : Var → Word n) (l : LVal) (e : Expr) (s : State n)
23
+ (m' : Memory n) (h : assignSem addr l e s.mem = .ok m') :
24
+ execSeq (assignCode addr l e) s =
25
+ .next { s with pc := s.pc + (assignCode addr l e).length, mem := m' } := by
26
+ cases l with
27
+ | var x =>
28
+ simp only [assignSem] at h
29
+ split at h
30
+ · cases h
31
+ · rename_i v hv
32
+ simp only [writeW] at h
33
+ split at h
34
+ · rename_i hw; cases h
35
+ rw [assignCode, execSeq_append, compileE_ok addr e s v hv]
36
+ simp [Outcome.andThen, execSeq, exec, State.fall, hw, Nat.add_assoc]
37
+ · cases h
38
+ | rv a =>
39
+ simp only [assignSem] at h
40
+ split at h
41
+ · cases h
42
+ · rename_i v hv
43
+ split at h
44
+ · cases h
45
+ · rename_i a' ha
46
+ simp only [writeW] at h
47
+ split at h
48
+ · rename_i hw; cases h
49
+ rw [assignCode, execSeq_append, execSeq_append, compileE_ok addr e s v hv]
50
+ simp only [Outcome.andThen]
51
+ rw [compileE_ok addr a { s with pc := s.pc + (compileE addr e).length, dstack := v :: s.dstack } a' ha]
52
+ simp [Outcome.andThen, execSeq, exec, State.fall, hw, Nat.add_assoc]
53
+ · cases h
54
+
55
+ theorem assignCode_err {n : Nat} (addr : Var → Word n) (l : LVal) (e : Expr) (s : State n)
56
+ (t : Trap) (h : assignSem addr l e s.mem = .error t) :
57
+ execSeq (assignCode addr l e) s = .trapped t := by
58
+ cases l with
59
+ | var x =>
60
+ simp only [assignSem] at h
61
+ split at h
62
+ · rename_i _ ht; cases h
63
+ rw [assignCode, execSeq_append, compileE_err addr e s _ ht] <;> rfl
64
+ · rename_i v hv
65
+ simp only [writeW] at h
66
+ split at h
67
+ · cases h
68
+ · rename_i hw; cases h
69
+ rw [assignCode, execSeq_append, compileE_ok addr e s v hv]
70
+ simp [Outcome.andThen, execSeq, exec, State.fall, hw]
71
+ | rv a =>
72
+ simp only [assignSem] at h
73
+ split at h
74
+ · rename_i _ ht; cases h
75
+ rw [assignCode, execSeq_append, execSeq_append, compileE_err addr e s _ ht] <;> rfl
76
+ · rename_i v hv
77
+ split at h
78
+ · rename_i _ ht; cases h
79
+ rw [assignCode, execSeq_append, execSeq_append, compileE_ok addr e s v hv]
80
+ simp only [Outcome.andThen]
81
+ rw [compileE_err addr a { s with pc := s.pc + (compileE addr e).length, dstack := v :: s.dstack } _ ht] <;> rfl
82
+ · rename_i a' ha
83
+ simp only [writeW] at h
84
+ split at h
85
+ · cases h
86
+ · rename_i hw; cases h
87
+ rw [assignCode, execSeq_append, execSeq_append, compileE_ok addr e s v hv]
88
+ simp only [Outcome.andThen]
89
+ rw [compileE_ok addr a { s with pc := s.pc + (compileE addr e).length, dstack := v :: s.dstack } a' ha]
90
+ simp [Outcome.andThen, execSeq, exec, State.fall, hw]
91
+ /-- The machine realizes result `r`, ending at address `e` with data stack `d` on success. -/
92
+ def Reaches {n : Nat} (p : Prog n) (s : State n) (d : List (Word n)) (e : Nat) :
93
+ Except Trap (Memory n) → Prop
94
+ | .ok m' => ∃ s', Steps p s s' ∧ s'.pc = e ∧ s'.dstack = d ∧ s'.mem = m'
95
+ | .error t => ∃ s1, Steps p s s1 ∧ step p s1 = .trapped t
96
+
97
+ theorem reaches_trans {n : Nat} {p : Prog n} {s s1 : State n} {d : List (Word n)} {e : Nat}
98
+ {r : Except Trap (Memory n)} (h1 : Steps p s s1) (h2 : Reaches p s1 d e r) :
99
+ Reaches p s d e r := by
100
+ cases r with
101
+ | ok m' =>
102
+ obtain ⟨s', hs, hpc, hd, hm⟩ := h2
103
+ exact ⟨s', h1.trans hs, hpc, hd, hm⟩
104
+ | error t =>
105
+ obtain ⟨s2, hs, ht⟩ := h2
106
+ exact ⟨s2, h1.trans hs, ht⟩
107
+
108
+ theorem reaches_cast {n : Nat} {p : Prog n} {s : State n} {d : List (Word n)} {e e' : Nat}
109
+ {r : Except Trap (Memory n)} (h : e = e') (hr : Reaches p s d e r) : Reaches p s d e' r := by
110
+ subst h; exact hr
111
+
112
+ theorem reaches_dstack {n : Nat} {p : Prog n} {s : State n} {d d' : List (Word n)} {e : Nat}
113
+ {r : Except Trap (Memory n)} (h : d = d') (hr : Reaches p s d e r) : Reaches p s d' e r := by
114
+ subst h; exact hr
115
+
116
+ theorem size_assign {n : Nat} (addr : Var → Word n) (l : LVal) (e : Expr) :
117
+ (compile addr (.assign l e)).size = (assignCode addr l e).length := rfl
118
+
119
+ theorem size_seq {n : Nat} (addr : Var → Word n) (a b : Stmt) :
120
+ (compile addr (.seq a b)).size = (compile addr a).size + (compile addr b).size := rfl
121
+
122
+ theorem size_test {n : Nat} (addr : Var → Word n) (c : Expr) (t e : Stmt) :
123
+ (compile addr (.test c t e)).size =
124
+ (compileE addr c).length + ((compile addr e).size + (compile addr t).size + 2) := rfl
125
+
126
+ theorem size_while {n : Nat} (addr : Var → Word n) (c : Expr) (body : Stmt) :
127
+ (compile addr (.while c body)).size =
128
+ (compileE addr c).length + (compile addr body).size + 3 := rfl
129
+
130
+ theorem compile_correct {n : Nat} {addr : Var → Word n} {s : Stmt} {m : Memory n}
131
+ {r : Except Trap (Memory n)} (h : Exec addr s m r) :
132
+ ∀ (p : Prog n) (base : Nat) (st : State n),
133
+ At p base ((compile addr s).emit base) → st.pc = base → st.mem = m →
134
+ Reaches p st st.dstack (base + (compile addr s).size) r := by
135
+ induction h with
136
+ | skip =>
137
+ intro p base st _ hpc hm
138
+ exact ⟨st, .refl, by simp [compile, Frag.empty, Frag.ofCode, hpc], rfl, hm⟩
139
+ | @assign l e m r hr =>
140
+ intro p base st hat hpc hm
141
+ subst hr
142
+ have hcode : At p base (assignCode addr l e) := by simpa [compile, Frag.ofCode] using hat
143
+ cases hres : assignSem addr l e m with
144
+ | ok m' =>
145
+ have hx := assignCode_ok addr l e st m' (by rw [hm]; exact hres)
146
+ obtain ⟨hst, hpc'⟩ :=
147
+ execSeq_steps (assignCode_straight addr l e) (by rw [hpc]; exact hcode) hx
148
+ exact ⟨_, hst, by rw [size_assign]; simp [hpc], rfl, rfl⟩
149
+ | error t =>
150
+ have hx := assignCode_err addr l e st t (by rw [hm]; exact hres)
151
+ exact execSeq_trap (assignCode_straight addr l e) (by rw [hpc]; exact hcode) hx
152
+ | @seqOk a b m m1 r _ _ iha ihb =>
153
+ intro p base st hat hpc hm
154
+ have hat' := Frag.seq_at (by simpa [compile] using hat)
155
+ obtain ⟨s1, hs1, hpc1, hd1, hm1⟩ := iha p base st hat'.1 hpc hm
156
+ have ih2 := ihb p (base + (compile addr a).size) s1 hat'.2 hpc1 hm1
157
+ rw [hd1] at ih2
158
+ refine reaches_cast ?_ (reaches_trans hs1 ih2)
159
+ rw [size_seq]; omega
160
+ | @seqErr a b m t _ iha =>
161
+ intro p base st hat hpc hm
162
+ have hat' := Frag.seq_at (by simpa [compile] using hat)
163
+ exact iha p base st hat'.1 hpc hm
164
+ | @testErr c t e m x hc =>
165
+ intro p base st hat hpc hm
166
+ have hat' := Frag.seq_at (by simpa [compile] using hat)
167
+ have hcode : At p base (compileE addr c) := by simpa [Frag.ofCode] using hat'.1
168
+ exact execSeq_trap (compileE_straight addr c) (by rw [hpc]; exact hcode)
169
+ (compileE_err addr c st x (by rw [hm]; exact hc))
170
+ | @testTrue c t e m v r hc ht _ iht =>
171
+ intro p base st hat hpc hm
172
+ have hat' := Frag.seq_at (by simpa [compile] using hat)
173
+ have hcode : At p base (compileE addr c) := by simpa [Frag.ofCode] using hat'.1
174
+ have hx := compileE_ok addr c st v (by rw [hm]; exact hc)
175
+ obtain ⟨hst, hpc1⟩ := execSeq_steps (compileE_straight addr c) (by rw [hpc]; exact hcode) hx
176
+ have hite : At p (base + (compileE addr c).length)
177
+ ((Frag.ite (compile addr t) (compile addr e)).emit (base + (compileE addr c).length)) :=
178
+ hat'.2
179
+ obtain ⟨_, _, _, ht'⟩ := ite_decode hite
180
+ have h1 := ite_true_rule hite
181
+ (s := { st with pc := st.pc + (compileE addr c).length, dstack := v :: st.dstack })
182
+ (by simp [hpc]) rfl ht
183
+ have ih2 := iht p (base + (compileE addr c).length + 2 + (compile addr e).size)
184
+ { st with pc := base + (compileE addr c).length + 2 + (compile addr e).size, dstack := st.dstack }
185
+ ht' (by simp [hpc]) hm
186
+ refine reaches_cast ?_ (reaches_trans (hst.trans (by simpa [hpc] using h1)) ih2)
187
+ rw [size_test]; omega
188
+ | @testFalse c t e m v r hc hf _ ihe =>
189
+ intro p base st hat hpc hm
190
+ have hat' := Frag.seq_at (by simpa [compile] using hat)
191
+ have hcode : At p base (compileE addr c) := by simpa [Frag.ofCode] using hat'.1
192
+ have hx := compileE_ok addr c st v (by rw [hm]; exact hc)
193
+ obtain ⟨hst, hpc1⟩ := execSeq_steps (compileE_straight addr c) (by rw [hpc]; exact hcode) hx
194
+ have hite : At p (base + (compileE addr c).length)
195
+ ((Frag.ite (compile addr t) (compile addr e)).emit (base + (compileE addr c).length)) :=
196
+ hat'.2
197
+ obtain ⟨h0, he, _, _⟩ := ite_decode hite
198
+ cases r with
199
+ | ok m1 =>
200
+ obtain ⟨s2, hs2, hpc2, hd2, hm2⟩ := ihe p (base + (compileE addr c).length + 1)
201
+ { st with pc := base + (compileE addr c).length + 1, dstack := st.dstack }
202
+ he (by simp [hpc]) hm
203
+ have h1 := ite_false_rule hite
204
+ (s := { st with pc := st.pc + (compileE addr c).length, dstack := v :: st.dstack })
205
+ (by simp [hpc]) rfl hf
206
+ (s1 := s2) (by simpa [hpc] using hs2) (by simpa using hpc2)
207
+ refine ⟨_, hst.trans h1, ?_, hd2, hm2⟩
208
+ show base + (compileE addr c).length + 2 + (compile addr e).size + (compile addr t).size = _
209
+ rw [size_test]; omega
210
+ | error t' =>
211
+ obtain ⟨s2, hs2, ht2⟩ := ihe p (base + (compileE addr c).length + 1)
212
+ { st with pc := base + (compileE addr c).length + 1, dstack := st.dstack }
213
+ he (by simp [hpc]) hm
214
+ have hfall := step_branch_fall
215
+ (s := { st with pc := st.pc + (compileE addr c).length, dstack := v :: st.dstack })
216
+ (by simp [hpc]; exact h0) rfl hf
217
+ exact ⟨s2, hst.trans (.cons (by simpa [hpc] using hfall) hs2), ht2⟩
218
+ | @whileErr c body m x hc =>
219
+ intro p base st hat hpc hm
220
+ obtain ⟨hc_at, _, _, _, _⟩ := while_decode (by simpa [compile] using hat)
221
+ have hcode : At p base (compileE addr c) := by simpa [Frag.ofCode] using hc_at
222
+ exact execSeq_trap (compileE_straight addr c) (by rw [hpc]; exact hcode)
223
+ (compileE_err addr c st x (by rw [hm]; exact hc))
224
+ | @whileExit c body m v hc hf =>
225
+ intro p base st hat hpc hm
226
+ have hat' : At p base ((Frag.whileLoop (Frag.ofCode (compileE addr c)) (compile addr body)).emit base) := by
227
+ simpa [compile] using hat
228
+ obtain ⟨hc_at, _, _, _, _⟩ := while_decode hat'
229
+ have hcode : At p base (compileE addr c) := by simpa [Frag.ofCode] using hc_at
230
+ have hx := compileE_ok addr c st v (by rw [hm]; exact hc)
231
+ obtain ⟨hst, hpc1⟩ := execSeq_steps (compileE_straight addr c) (by rw [hpc]; exact hcode) hx
232
+ have h1 := while_exit_rule hat'
233
+ (s1 := { st with pc := st.pc + (compileE addr c).length, dstack := v :: st.dstack })
234
+ (by simp [hpc, Frag.ofCode]) rfl hf
235
+ refine ⟨_, hst.trans h1, ?_, rfl, hm⟩
236
+ rw [size_while]; simp [Frag.ofCode]; omega
237
+ | @whileBodyErr c body m v x hc ht _ ihb =>
238
+ intro p base st hat hpc hm
239
+ have hat' : At p base ((Frag.whileLoop (Frag.ofCode (compileE addr c)) (compile addr body)).emit base) := by
240
+ simpa [compile] using hat
241
+ obtain ⟨hc_at, _, _, hb_at, _⟩ := while_decode hat'
242
+ have hcode : At p base (compileE addr c) := by simpa [Frag.ofCode] using hc_at
243
+ have hx := compileE_ok addr c st v (by rw [hm]; exact hc)
244
+ obtain ⟨hst, hpc1⟩ := execSeq_steps (compileE_straight addr c) (by rw [hpc]; exact hcode) hx
245
+ have h1 := while_enter_rule hat'
246
+ (s1 := { st with pc := st.pc + (compileE addr c).length, dstack := v :: st.dstack })
247
+ (by simp [hpc, Frag.ofCode]) rfl ht
248
+ have ih2 := ihb p (base + (Frag.ofCode (compileE addr c)).size + 2)
249
+ { st with pc := base + (Frag.ofCode (compileE addr c)).size + 2, dstack := st.dstack }
250
+ hb_at (by simp [hpc]) hm
251
+ exact reaches_trans (hst.trans h1) ih2
252
+ | @whileAgain c body m v m1 r hc ht _ _ ihb ihl =>
253
+ intro p base st hat hpc hm
254
+ have hat' : At p base ((Frag.whileLoop (Frag.ofCode (compileE addr c)) (compile addr body)).emit base) := by
255
+ simpa [compile] using hat
256
+ obtain ⟨hc_at, _, _, hb_at, _⟩ := while_decode hat'
257
+ have hcode : At p base (compileE addr c) := by simpa [Frag.ofCode] using hc_at
258
+ have hx := compileE_ok addr c st v (by rw [hm]; exact hc)
259
+ obtain ⟨hst, hpc1⟩ := execSeq_steps (compileE_straight addr c) (by rw [hpc]; exact hcode) hx
260
+ have h1 := while_enter_rule hat'
261
+ (s1 := { st with pc := st.pc + (compileE addr c).length, dstack := v :: st.dstack })
262
+ (by simp [hpc, Frag.ofCode]) rfl ht
263
+ obtain ⟨s3, hs3, hpc3, hd3, hm3⟩ := ihb p (base + (Frag.ofCode (compileE addr c)).size + 2)
264
+ { st with pc := base + (Frag.ofCode (compileE addr c)).size + 2, dstack := st.dstack }
265
+ hb_at (by simp [hpc]) hm
266
+ have h2 := while_back_rule hat' (s1 := s3) hpc3
267
+ have hd3' : s3.dstack = st.dstack := hd3
268
+ have ih4 : Reaches p { s3 with pc := base } st.dstack
269
+ (base + (compile addr (Stmt.while c body)).size) r :=
270
+ reaches_dstack hd3' (ihl p base { s3 with pc := base } hat rfl hm3)
271
+ exact reaches_trans (((hst.trans h1).trans hs3).trans h2) ih4
272
+
273
+ /-- Whole-program correctness from the initial machine state (empty stacks, given memory). -/
274
+ theorem compileProgram_correct {n : Nat} {addr : Var → Word n} {s : Stmt} {mem : Memory n}
275
+ {r : Except Trap (Memory n)} (h : Exec addr s mem r) :
276
+ match r with
277
+ | .ok m' =>
278
+ ∃ s', WordDialect.Exec (compileProgram addr s) (State.init mem) (.halted s') ∧
279
+ s'.dstack = [] ∧ s'.mem = m'
280
+ | .error t => WordDialect.Exec (compileProgram addr s) (State.init mem) (.trapped t) := by
281
+ have hat : At (compileProgram addr s) 0 ((compile addr s).emit 0) := by
282
+ have := at_embed ([] : Prog n) ((compile addr s).emit 0) [.halt]
283
+ simpa [compileProgram] using this
284
+ have key := compile_correct h (compileProgram addr s) 0 (State.init mem) hat rfl rfl
285
+ cases r with
286
+ | ok m' =>
287
+ obtain ⟨s', hs, hpc, hd, hm⟩ := key
288
+ have hfetch : (compileProgram addr s)[s'.pc]? = some .halt := by
289
+ rw [hpc]
290
+ simp [compileProgram, (compile addr s).len]
291
+ refine ⟨s', WordDialect.Exec.of_steps hs (.halt ?_), hd, hm⟩
292
+ rw [step_of_fetch hfetch]; rfl
293
+ | error t =>
294
+ obtain ⟨s1, hs, ht⟩ := key
295
+ exact WordDialect.Exec.of_steps hs (.trap ht)
296
+
297
+ end BCPL
298
+ end WordDialect