October 2026 main drop: mirror from GitHub
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .github/workflows/lean.yml +32 -0
- .gitignore +1 -0
- License +110 -0
- NEXT_STEPS.md +36 -0
- README.md +492 -0
- backend/common/Harness.lean +307 -0
- backend/wasm/Wasm.lean +17 -0
- backend/wasm/Wasm/Correct.lean +292 -0
- backend/wasm/Wasm/Emit.lean +117 -0
- backend/wasm/Wasm/Frame.lean +355 -0
- backend/wasm/Wasm/Guard.lean +163 -0
- backend/wasm/Wasm/Init.lean +156 -0
- backend/wasm/Wasm/Isa.lean +256 -0
- backend/wasm/Wasm/Lower.lean +185 -0
- backend/wasm/Wasm/Mem.lean +89 -0
- backend/wasm/Wasm/Rel.lean +116 -0
- backend/wasm/Wasm/Seq.lean +65 -0
- backend/wasm/Wasm/SimAlu.lean +159 -0
- backend/wasm/Wasm/SimAlu2.lean +189 -0
- backend/wasm/Wasm/SimAux.lean +229 -0
- backend/wasm/Wasm/SimCtl.lean +146 -0
- backend/wasm/Wasm/SimExec.lean +329 -0
- backend/wasm/Wasm/SimMem.lean +188 -0
- backend/wasm/Wasm/SimStack.lean +398 -0
- backend/wasm/WasmMain.lean +135 -0
- backend/x86_64/Main.lean +149 -0
- backend/x86_64/X86.lean +20 -0
- backend/x86_64/X86/Correct.lean +181 -0
- backend/x86_64/X86/Emit.lean +143 -0
- backend/x86_64/X86/Flags.lean +128 -0
- backend/x86_64/X86/Guard.lean +230 -0
- backend/x86_64/X86/Init.lean +135 -0
- backend/x86_64/X86/Isa.lean +227 -0
- backend/x86_64/X86/Lower.lean +198 -0
- backend/x86_64/X86/Micro.lean +280 -0
- backend/x86_64/X86/Rel.lean +170 -0
- backend/x86_64/X86/Run.lean +143 -0
- backend/x86_64/X86/Seq.lean +77 -0
- backend/x86_64/X86/SimAux.lean +448 -0
- backend/x86_64/X86/SimBin.lean +100 -0
- backend/x86_64/X86/SimCtl.lean +282 -0
- backend/x86_64/X86/SimDiv.lean +368 -0
- backend/x86_64/X86/SimMem.lean +213 -0
- backend/x86_64/X86/SimMemOps.lean +281 -0
- backend/x86_64/X86/SimOps.lean +217 -0
- backend/x86_64/X86/SimStack.lean +292 -0
- backend/x86_64/X86/SimStep.lean +353 -0
- bcpl/BCPL.lean +5 -0
- bcpl/BCPL/Compile.lean +63 -0
- 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
|