ai-free / formal /WordDialect /Exec.lean
SNAPKITTYWEST's picture
October 2026 main drop: mirror from GitHub
f5deffb verified
Raw History Blame Contribute Delete
7.57 kB
import WordDialect.Invariants
/-!
# WordDialect.Exec
Execution semantics built on `step`:
* `Exec p s o` β€” running `p` from `s` terminates with final outcome `o` (halted or trapped).
* `Steps p s s'` β€” `s'` is reached from `s` by finitely many non-terminal steps.
* `run` β€” fuel-bounded executable interpreter, proved equivalent to `Exec`.
`Steps` composed with `Exec` is the tool used to prove translation correctness: a
translated fragment is shown to take the machine from one state to another with `Steps`,
and the rest of the program is handled by `Exec`.
-/
namespace WordDialect
inductive Exec {n : Nat} (p : Prog n) : State n β†’ Outcome n β†’ Prop where
| halt {s s' : State n} : step p s = .halted s' β†’ Exec p s (.halted s')
| trap {s : State n} {t : Trap} : step p s = .trapped t β†’ Exec p s (.trapped t)
| next {s s' : State n} {o : Outcome n} :
step p s = .next s' β†’ Exec p s' o β†’ Exec p s o
inductive Steps {n : Nat} (p : Prog n) : State n β†’ State n β†’ Prop where
| refl {s : State n} : Steps p s s
| cons {s s' s'' : State n} : step p s = .next s' β†’ Steps p s' s'' β†’ Steps p s s''
namespace Steps
theorem single {n : Nat} {p : Prog n} {s s' : State n} (h : step p s = .next s') :
Steps p s s' := .cons h .refl
theorem trans {n : Nat} {p : Prog n} {s s' s'' : State n}
(h₁ : Steps p s s') (hβ‚‚ : Steps p s' s'') : Steps p s s'' := by
induction h₁ with
| refl => exact hβ‚‚
| cons hs _ ih => exact .cons hs (ih hβ‚‚)
end Steps
theorem Exec.of_steps {n : Nat} {p : Prog n} {s s' : State n} {o : Outcome n}
(hs : Steps p s s') (he : Exec p s' o) : Exec p s o := by
induction hs with
| refl => exact he
| cons h _ ih => exact .next h (ih he)
/-- A final outcome is never `next`. -/
theorem Exec.terminal {n : Nat} {p : Prog n} {s : State n} {o : Outcome n}
(h : Exec p s o) : βˆ€ s', o β‰  .next s' := by
induction h with
| halt _ => intro _ h; cases h
| trap _ => intro _ h; cases h
| next _ _ ih => exact ih
/-- The machine is deterministic: a program and an initial state fix the final outcome. -/
theorem Exec.deterministic {n : Nat} {p : Prog n} {s : State n} {o₁ oβ‚‚ : Outcome n}
(h₁ : Exec p s o₁) (hβ‚‚ : Exec p s oβ‚‚) : o₁ = oβ‚‚ := by
induction h₁ generalizing oβ‚‚ with
| halt hs =>
cases hβ‚‚ with
| halt hs' => rw [hs] at hs'; cases hs'; rfl
| trap hs' => rw [hs] at hs'; cases hs'
| next hs' _ => rw [hs] at hs'; cases hs'
| trap hs =>
cases hβ‚‚ with
| halt hs' => rw [hs] at hs'; cases hs'
| trap hs' => rw [hs] at hs'; cases hs'; rfl
| next hs' _ => rw [hs] at hs'; cases hs'
| next hs _ ih =>
cases hβ‚‚ with
| halt hs' => rw [hs] at hs'; cases hs'
| trap hs' => rw [hs] at hs'; cases hs'
| next hs' hβ‚‚' => rw [hs] at hs'; cases hs'; exact ih hβ‚‚'
/-- Fuel-bounded interpreter. `none` means the fuel ran out before a final outcome. -/
def run {n : Nat} (p : Prog n) : Nat β†’ State n β†’ Option (Outcome n)
| 0, _ => none
| fuel + 1, s =>
match step p s with
| .next s' => run p fuel s'
| o => some o
theorem run_sound {n : Nat} {p : Prog n} :
βˆ€ {fuel : Nat} {s : State n} {o : Outcome n}, run p fuel s = some o β†’ Exec p s o := by
intro fuel
induction fuel with
| zero => intro s o h; simp [run] at h
| succ f ih =>
intro s o h
simp only [run] at h
cases hs : step p s with
| next s' => rw [hs] at h; exact .next hs (ih h)
| halted s' => rw [hs] at h; cases h; exact .halt hs
| trapped t => rw [hs] at h; cases h; exact .trap hs
theorem run_complete {n : Nat} {p : Prog n} {s : State n} {o : Outcome n}
(h : Exec p s o) : βˆƒ fuel, run p fuel s = some o := by
induction h with
| halt hs => exact ⟨1, by simp [run, hs]⟩
| trap hs => exact ⟨1, by simp [run, hs]⟩
| next hs _ ih =>
obtain ⟨f, hf⟩ := ih
exact ⟨f + 1, by simp [run, hs, hf]⟩
theorem run_mono {n : Nat} {p : Prog n} {o : Outcome n} :
βˆ€ {fuel : Nat} {s : State n}, run p fuel s = some o β†’ run p (fuel + 1) s = some o := by
intro fuel
induction fuel with
| zero => intro s h; simp [run] at h
| succ f ih =>
intro s h
simp only [run] at h ⊒
cases hs : step p s with
| next s' => rw [hs] at h; simp only []; exact ih h
| halted s' => rw [hs] at h; exact h
| trapped t => rw [hs] at h; exact h
theorem exec_iff_run {n : Nat} {p : Prog n} {s : State n} {o : Outcome n} :
Exec p s o ↔ βˆƒ fuel, run p fuel s = some o :=
⟨run_complete, fun ⟨_, h⟩ => run_sound h⟩
/-! ## Control flow -/
theorem step_of_fetch {n : Nat} {p : Prog n} {s : State n} {i : Instr n}
(h : p[s.pc]? = some i) : step p s = exec i s := by
simp [step, h]
/-- Fetching outside the program traps; there is no fall-off-the-end behaviour. -/
theorem step_pc_oob {n : Nat} {p : Prog n} {s : State n} (h : p.length ≀ s.pc) :
step p s = .trapped .badPc := by
simp [step, List.getElem?_eq_none h]
theorem step_jmp {n : Nat} {p : Prog n} {s : State n} {t : Nat}
(h : p[s.pc]? = some (.jmp t)) : step p s = .next { s with pc := t } := by
rw [step_of_fetch h]; simp [exec]
theorem step_branch_taken {n : Nat} {p : Prog n} {s : State n} {t : Nat} {c : Word n}
{d : List (Word n)} (h : p[s.pc]? = some (.branch t)) (hs : s.dstack = c :: d)
(hc : Word.isTrue c = true) : step p s = .next { s with pc := t, dstack := d } := by
rw [step_of_fetch h]; simp [exec, hs, hc]
theorem step_branch_fall {n : Nat} {p : Prog n} {s : State n} {t : Nat} {c : Word n}
{d : List (Word n)} (h : p[s.pc]? = some (.branch t)) (hs : s.dstack = c :: d)
(hc : Word.isTrue c = false) : step p s = .next { s with pc := s.pc + 1, dstack := d } := by
rw [step_of_fetch h]; simp [exec, hs, hc, State.fall]
theorem step_call {n : Nat} {p : Prog n} {s : State n} {t : Nat}
(h : p[s.pc]? = some (.call t)) :
step p s = .next { s with pc := t, rstack := (s.pc + 1) :: s.rstack } := by
rw [step_of_fetch h]; simp [exec]
theorem step_ret {n : Nat} {p : Prog n} {s : State n} {a : Nat} {rs : List Nat}
(h : p[s.pc]? = some .ret) (hs : s.rstack = a :: rs) :
step p s = .next { s with pc := a, rstack := rs } := by
rw [step_of_fetch h]; simp [exec, hs]
theorem step_ret_empty {n : Nat} {p : Prog n} {s : State n}
(h : p[s.pc]? = some .ret) (hs : s.rstack = []) :
step p s = .trapped .returnUnderflow := by
rw [step_of_fetch h]; simp [exec, hs]
/-- A call whose target is an immediate `ret` returns to the instruction after the call,
with every component of the state unchanged except the program counter. -/
theorem call_ret_roundtrip {n : Nat} {p : Prog n} {s : State n} {t : Nat}
(hc : p[s.pc]? = some (.call t)) (hr : p[t]? = some .ret) :
Steps p s { s with pc := s.pc + 1 } := by
refine .cons (step_call hc) (.single ?_)
exact step_ret (s := { s with pc := t, rstack := (s.pc + 1) :: s.rstack }) hr rfl
/-- Fetched instructions are unaffected by appending code after them. -/
theorem step_append_right {n : Nat} {p q : Prog n} {s : State n} {o : Outcome n}
(hlt : s.pc < p.length) (h : step p s = o) : step (p ++ q) s = o := by
simp [step, List.getElem?_append_left hlt] at h ⊒
exact h
theorem Steps.append_right {n : Nat} {p q : Prog n} {s s' : State n}
(hs : Steps p s s') (hb : βˆ€ x, Steps p s x β†’ x.pc < p.length) :
Steps (p ++ q) s s' := by
induction hs with
| refl => exact .refl
| cons h _ ih =>
refine .cons (step_append_right (hb _ .refl) h) (ih fun x hx => hb x (.cons h hx))
end WordDialect