ai-free / backend /wasm /Wasm /SimExec.lean
SNAPKITTYWEST's picture
October 2026 main drop: mirror from GitHub
f5deffb verified
Raw History Blame Contribute Delete
16.3 kB
import Wasm.SimAux
/-!
# Wasm.SimExec
One IR step is simulated by the function the lowering emits for the instruction, for every
instruction and every outcome (`next`, `halted`, `trapped`). This is the case analysis over the
whole instruction set that the dispatch-loop theorem (`Wasm.Correct`) iterates.
Trap codes (the argument of `proc_exit`): 1 `stackUnderflow`, 2 `badAddress`, 3 `divideByZero`,
4 `badPc`, 5 `returnUnderflow` (an empty return stack on `ret`, or an empty auxiliary stack on
`fromr`/`rfetch`).
Every instruction that grows a stack first checks for room, and exits with code 6 when the stack
is full. So `sim_step` has no capacity hypothesis: the code either simulates the IR step, or the
stack lacks the headroom `Fits` describes and the code exits 6. Code 6 has no IR counterpart (IR
stacks are unbounded); it reports that the run exceeded the target's capacity.
-/
namespace WordDialect
namespace Wasm
def trapCode : Trap → Nat
| .stackUnderflow => 1
| .badAddress => 2
| .divideByZero => 3
| .badPc => 4
| .returnUnderflow => 5
/-- Headroom for one step: room to push one data word, one return index and one auxiliary word.
With this headroom no overflow guard fires. -/
def Fits (c : Cfg) (s : State 64) : Prop :=
c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd ∧ c.rBase + 8 * (s.rstack.length + 1) ≤ c.rEnd ∧
c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd
/-- What simulating one IR outcome means for the body of one dispatched function. On `next`, the
function returns the (clamped) next IR index, and every return index stays within the program. -/
def StepSim (c : Cfg) (fs : Funcs) (n : Nat) (body : List WI) (w : WState) : Outcome 64 → Prop
| .next s' => (∀ a ∈ s'.rstack, a ≤ n) ∧
∃ w', Run fs body w (.normal w') ∧ Rel0 c s' w' ∧ w'.stack = [.i32 (ofN (min s'.pc n))]
| .halted s' => ∃ w', Run fs body w (.halted w') ∧ Rel0 c s' w'
| .trapped t => Run fs body w (.exit (trapCode t))
section Step
variable {c : Cfg} {s : State 64} {w : WState} {fs : Funcs} {n : Nat}
theorem Rel0.setPc (h : Rel0 c s w) (q : Nat) : Rel0 c { s with pc := q } w :=
{ sp := h.sp, rp := h.rp, stack := h.stack, regs := h.regs, mem := h.mem, rs := h.rs,
irvalid := h.irvalid, size := h.size, capD := h.capD, capR := h.capR, ap := h.ap, aux := h.aux,
capA := h.capA }
theorem next_sim {body : List WI} {s1 : State 64} {q : Nat} (hrs : ∀ a ∈ s1.rstack, a ≤ n)
(h : Sim1 c fs body s1 w (min q n)) : StepSim c fs n body w (.next { s1 with pc := q }) := by
obtain ⟨w', hr, h0, hst⟩ := h
exact ⟨hrs, w', hr, h0.setPc q, hst⟩
theorem fall_sim {body : List WI} {d : List W} (hlt : s.pc < n) (hrs : ∀ a ∈ s.rstack, a ≤ n)
(h : Sim1 c fs body { s with dstack := d } w (s.pc + 1)) : StepSim c fs n body w (s.fall d) := by
rw [← Nat.min_eq_left (show s.pc + 1 ≤ n by omega)] at h
exact next_sim (s1 := { s with dstack := d }) hrs h
theorem shape1 {l : List W} (h : 1 ≤ l.length) : ∃ a d, l = a :: d := by
cases l with
| nil => simp at h
| cons a d => exact ⟨a, d, rfl⟩
theorem shape2 {l : List W} (h : 2 ≤ l.length) : ∃ b a d, l = b :: a :: d := by
rcases l with _ | ⟨b, _ | ⟨a, d⟩⟩
· simp at h
· simp at h
· exact ⟨b, a, d, rfl⟩
theorem shape3 {l : List W} (h : 3 ≤ l.length) : ∃ x y z d, l = x :: y :: z :: d := by
rcases l with _ | ⟨x, _ | ⟨y, _ | ⟨z, d⟩⟩⟩
· simp at h
· simp at h
· simp at h
· exact ⟨x, y, z, d, rfl⟩
/-- Every instruction that pops operands starts with the operand-count guard. -/
theorem lowerInstr_uf (L : Layout) (n k : Nat) (i : WordDialect.Instr 64) (h : 0 < i.pops) :
∃ rest, lowerInstr L n k i = uf L i.pops ++ rest ∧ i.pops ≤ 3 := by
cases i <;> simp only [Instr.pops] at h ⊢ <;>
first
| exact absurd h (by decide)
| exact ⟨_, by simp only [lowerInstr, binOp, shiftOp, List.append_assoc]; rfl, by decide⟩
theorem sim_step (hg : Geom c) (hs : Rel0 c s w) (hst : w.stack = [])
{p : Prog 64} (hn : p.length < 2 ^ 32) (hrs : ∀ a ∈ s.rstack, a ≤ p.length)
{i : WordDialect.Instr 64} (hi : p[s.pc]? = some i)
(hreg : ∀ r, (i = .push r ∨ i = .pop r) → r < c.nregs) :
StepSim c fs p.length (lowerInstr c.layout p.length s.pc i) w (exec i s) ∨
(¬ Fits c s ∧ Run fs (lowerInstr c.layout p.length s.pc i) w (.exit 6)) := by
have nfD : ¬ c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd → ¬ Fits c s := fun h hf => h hf.1
have nfR : ¬ c.rBase + 8 * (s.rstack.length + 1) ≤ c.rEnd → ¬ Fits c s := fun h hf => h hf.2.1
have nfA : ¬ c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd → ¬ Fits c s := fun h hf => h hf.2.2
have hlt : s.pc < p.length := by
by_cases h : s.pc < p.length
· exact h
· rw [List.getElem?_eq_none (by omega)] at hi; simp at hi
by_cases hk : s.dstack.length < i.pops
· rw [exec_underflow i s hk]
obtain ⟨rest, hlow, h3⟩ := lowerInstr_uf c.layout p.length s.pc i (by omega)
rw [hlow]
exact .inl <| run_append_abrupt _ (uf_trap hg hs (by omega) h3 hk) trivial
have hk' : i.pops ≤ s.dstack.length := by omega
cases i with
| word v =>
by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
· exact .inl <| fall_sim hlt hrs (sim_word _ _ hg hs hst v hf)
· exact .inr ⟨nfD hf, sim_word_ovf _ _ hg hs v hf⟩
| ptr q =>
by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
· exact .inl <| fall_sim hlt hrs (sim_ptr _ _ hg hs hst q hf)
· exact .inr ⟨nfD hf, sim_ptr_ovf _ _ hg hs q hf⟩
| load =>
obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
by_cases ha : a.toNat < c.M
· have hv : s.mem.valid a = true := (valid_iff hs a).mpr ha
have hex : exec .load s = s.fall (s.mem.cell a :: d) := by
simp [exec, hd, Memory.read?, hv]
rw [hex]
exact .inl <| fall_sim hlt hrs (sim_load _ _ hg hs hst hd ha)
· have hv : s.mem.valid a = false := by
cases h : s.mem.valid a
· rfl
· exact absurd ((valid_iff hs a).mp h) ha
have hex : exec .load s = .trapped .badAddress := by
simp [exec, hd, Memory.read?, hv]
rw [hex]
exact .inl <| sim_load_trap _ _ hg hs hd (by omega)
| store =>
obtain ⟨a, v, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
cases hw : s.mem.write? a v with
| none =>
have hv : s.mem.valid a = false := (Memory.write?_none_iff s.mem a v).mp hw
have ha : ¬ a.toNat < c.M := fun h => by rw [(valid_iff hs a).mpr h] at hv; simp at hv
have hex : exec .store s = .trapped .badAddress := by simp [exec, hd, hw]
rw [hex]
exact .inl <| sim_store_trap _ _ hg hs hd (by omega)
| some im =>
have hex : exec .store s = .next { { s with dstack := d, mem := im } with pc := s.pc + 1 } := by
simp [exec, hd, hw]
rw [hex]
have h := sim_store (fs := fs) p.length s.pc hg hs hst hd hw
rw [← Nat.min_eq_left (show s.pc + 1 ≤ p.length by omega)] at h
exact .inl <| next_sim hrs h
| add =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
rw [show exec .add s = s.fall ((a + b) :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_add _ _ hg hs hst hd)
| sub =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
rw [show exec .sub s = s.fall ((a - b) :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_sub _ _ hg hs hst hd)
| mul =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
rw [show exec .mul s = s.fall ((a * b) :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_mul _ _ hg hs hst hd)
| div =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
by_cases hb : b = 0#64
· subst hb
rw [show exec .div s = .trapped .divideByZero by simp [exec, hd]]
exact .inl <| sim_div_trap _ _ hg hs hd
· rw [show exec .div s = s.fall (a.udiv b :: d) by simp [exec, hd, hb]]
exact .inl <| fall_sim hlt hrs (sim_div _ _ hg hs hst hd hb)
| sdiv =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
by_cases hb : b = 0#64
· subst hb
rw [show exec .sdiv s = .trapped .divideByZero by simp [exec, hd]]
exact .inl <| sim_sdiv_trap _ _ hg hs hd
· rw [show exec .sdiv s = s.fall (a.sdiv b :: d) by simp [exec, hd, hb]]
exact .inl <| fall_sim hlt hrs (sim_sdiv _ _ hg hs hst hd hb)
| and =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
rw [show exec .and s = s.fall ((a &&& b) :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_and _ _ hg hs hst hd)
| or =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
rw [show exec .or s = s.fall ((a ||| b) :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_or _ _ hg hs hst hd)
| xor =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
rw [show exec .xor s = s.fall ((a ^^^ b) :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_xor _ _ hg hs hst hd)
| not =>
obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
rw [show exec .not s = s.fall ((~~~a) :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_not _ _ hg hs hst hd)
| shl =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
rw [show exec .shl s = s.fall (Word.shl a b :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_shl _ _ hg hs hst hd)
| shr =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
rw [show exec .shr s = s.fall (Word.shr a b :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_shr _ _ hg hs hst hd)
| rotl =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
rw [show exec .rotl s = s.fall (Word.rotl a b :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_rotl _ _ hg hs hst hd)
| rotr =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
rw [show exec .rotr s = s.fall (Word.rotr a b :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_rotr _ _ hg hs hst hd)
| cmp cd =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
rw [show exec (.cmp cd) s = s.fall (Word.ofBool (cd.eval a b) :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_cmp _ _ cd hg hs hst hd)
| select =>
obtain ⟨cc, y, x, d, hd⟩ := shape3 (by simpa [Instr.pops] using hk')
rw [show exec .select s = s.fall ((if Word.isTrue cc then x else y) :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_select _ _ hg hs hst hd)
| jmp t =>
rw [show exec (.jmp t) s = .next { s with pc := t } by simp [exec]]
exact .inl <| next_sim hrs (sim_jmp _ _ t hs hst)
| branch t =>
obtain ⟨cv, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
have h := sim_branch (fs := fs) p.length s.pc t hg hs hst hd
by_cases hc : Word.isTrue cv = true
· rw [show exec (.branch t) s = .next { { s with dstack := d } with pc := t } by simp [exec, hd, hc]]
simp only [hc, ↓reduceIte] at h
exact .inl <| next_sim hrs h
· rw [show exec (.branch t) s = s.fall d by simp [exec, hd, hc]]
have hc' : Word.isTrue cv = false := by simpa using hc
simp only [hc', Bool.false_eq_true, ↓reduceIte] at h
exact .inl <| fall_sim hlt hrs h
| call t =>
rw [show exec (.call t) s = .next { { s with rstack := (s.pc + 1) :: s.rstack } with pc := t } by simp [exec]]
by_cases hf : c.rBase + 8 * (s.rstack.length + 1) ≤ c.rEnd
· refine .inl (next_sim ?_ (sim_call _ _ t hg hs hst hf))
intro a ha
simp only [List.mem_cons] at ha
rcases ha with ha | ha
· omega
· exact hrs a ha
· exact .inr ⟨nfR hf, sim_call_ovf _ _ t hg hs hf⟩
| ret =>
cases hr : s.rstack with
| nil =>
rw [show exec .ret s = .trapped .returnUnderflow by simp [exec, hr]]
exact .inl <| sim_ret_trap _ _ hs hst hr
| cons a rs =>
rw [show exec .ret s = .next { { s with rstack := rs } with pc := a } by simp [exec, hr]]
have ha : a ≤ p.length := hrs a (by simp [hr])
have h := sim_ret (fs := fs) p.length s.pc hg hs hst hr (by omega)
rw [← Nat.min_eq_left ha] at h
exact .inl <| next_sim (fun b hb => hrs b (by simp [hr, hb])) h
| push r =>
rw [show exec (.push r) s = s.fall (s.regs r :: s.dstack) by simp [exec]]
by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
· exact .inl <| fall_sim hlt hrs (sim_push _ _ hg hs hst (hreg r (.inl rfl)) hf)
· exact .inr ⟨nfD hf, sim_push_ovf _ _ hg hs r hf⟩
| pop r =>
obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
rw [show exec (.pop r) s =
.next { { s with dstack := d, regs := State.setReg s.regs r a } with pc := s.pc + 1 } by
simp [exec, hd]]
have h := sim_pop (fs := fs) p.length s.pc hg hs hst (hreg r (.inr rfl)) hd
rw [← Nat.min_eq_left (show s.pc + 1 ≤ p.length by omega)] at h
exact .inl <| next_sim hrs h
| dup =>
obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
rw [show exec .dup s = s.fall (a :: a :: d) by simp [exec, hd]]
by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
· exact .inl <| fall_sim hlt hrs (sim_dup _ _ hg hs hst hd hf)
· exact .inr ⟨nfD hf, sim_dup_ovf _ _ hg hs hd hf⟩
| drop =>
obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
rw [show exec .drop s = s.fall d by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_drop _ _ hg hs hst hd)
| swap =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
rw [show exec .swap s = s.fall (a :: b :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_swap _ _ hg hs hst hd)
| over =>
obtain ⟨b, a, d, hd⟩ := shape2 (by simpa [Instr.pops] using hk')
rw [show exec .over s = s.fall (a :: b :: a :: d) by simp [exec, hd]]
by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
· exact .inl <| fall_sim hlt hrs (sim_over _ _ hg hs hst hd hf)
· exact .inr ⟨nfD hf, sim_over_ovf _ _ hg hs hd hf⟩
| rot =>
obtain ⟨x, y, z, d, hd⟩ := shape3 (by simpa [Instr.pops] using hk')
rw [show exec .rot s = s.fall (z :: x :: y :: d) by simp [exec, hd]]
exact .inl <| fall_sim hlt hrs (sim_rot _ _ hg hs hst hd)
| tor =>
obtain ⟨a, d, hd⟩ := shape1 (by simpa [Instr.pops] using hk')
rw [show exec .tor s = .next { { s with dstack := d, astack := a :: s.astack } with pc := s.pc + 1 } by
simp [exec, hd]]
by_cases hf : c.aBase + 8 * (s.astack.length + 1) ≤ c.aEnd
· have h := sim_tor (fs := fs) p.length s.pc hg hs hst hd hf
rw [← Nat.min_eq_left (show s.pc + 1 ≤ p.length by omega)] at h
exact .inl <| next_sim hrs h
· exact .inr ⟨nfA hf, sim_tor_ovf _ _ hg hs hd hf⟩
| fromr =>
cases ha : s.astack with
| nil =>
rw [show exec .fromr s = .trapped .returnUnderflow by simp [exec, ha]]
exact .inl <| sim_fromr_trap _ _ hs ha
| cons a as =>
rw [show exec .fromr s =
.next { { s with dstack := a :: s.dstack, astack := as } with pc := s.pc + 1 } by
simp [exec, ha]]
by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
· have h := sim_fromr (fs := fs) p.length s.pc hg hs hst ha hf
rw [← Nat.min_eq_left (show s.pc + 1 ≤ p.length by omega)] at h
exact .inl <| next_sim hrs h
· exact .inr ⟨nfD hf, sim_fromr_ovf _ _ hg hs ha hf⟩
| rfetch =>
cases ha : s.astack with
| nil =>
rw [show exec .rfetch s = .trapped .returnUnderflow by simp [exec, ha]]
exact .inl <| sim_rfetch_trap _ _ hs ha
| cons a as =>
rw [show exec .rfetch s = s.fall (a :: s.dstack) by simp [exec, ha]]
by_cases hf : c.dBase + 8 * (s.dstack.length + 1) ≤ c.dEnd
· exact .inl <| fall_sim hlt hrs (sim_rfetch _ _ hg hs hst ha hf)
· exact .inr ⟨nfD hf, sim_rfetch_ovf _ _ hg hs ha hf⟩
| halt =>
rw [show exec .halt s = .halted s by simp [exec]]
exact .inl <| ⟨w, sim_halt _ _, hs⟩
end Step
end Wasm
end WordDialect