@@ -189,4 +189,157 @@ theorem ObservationallyEquiv.trans {env : HostEnv α}
189189 ObservationallyEquiv env m₁ id₁ m₃ id₃ initial args :=
190190 ObservationallyEquivOn.trans h₁₂ h₂₃
191191
192+ /-! ## Authoritative small-step equivalence
193+
194+ This is the cutover form of the relation above. It observes finite executions
195+ of `SmallStep.Step` directly and therefore has no fuel or dependency on the
196+ legacy `run` function. -/
197+
198+ namespace SmallStep
199+
200+ /-- Terminal outcomes observed by equivalence. Traps remain structural and
201+ are not collapsed into divergence. -/
202+ inductive ObservableOutcome where
203+ | done (values : List Value)
204+ | trapped (reason : TrapReason)
205+ deriving BEq, Repr
206+
207+ def ObservableOutcome.toExpr : ObservableOutcome → Expr α
208+ | .done values => .done values
209+ | .trapped reason => .trapped reason
210+
211+ /-- A finite authoritative trace reaches a particular terminal outcome. -/
212+ def Reaches (config : Config α)
213+ (outcome : ObservableOutcome) (store : MachineStore α) : Prop :=
214+ ∃ trace, Steps config trace ⟨outcome.toExpr, store⟩
215+
216+ /-- Two initialized configurations have exactly the same successful or
217+ trapping observable outcomes. -/
218+ def ObservationallyEquivOn
219+ (config₁ config₂ : Config α) (obs : MachineStore α → β) : Prop :=
220+ ∀ (outcome : ObservableOutcome) (o : β),
221+ (∃ store, Reaches config₁ outcome store ∧ obs store = o) ↔
222+ (∃ store, Reaches config₂ outcome store ∧ obs store = o)
223+
224+ private theorem ObservableOutcome.toExpr_injective :
225+ Function.Injective (ObservableOutcome.toExpr : ObservableOutcome → Expr α) := by
226+ intro first second heq
227+ cases first <;> cases second <;>
228+ simp_all [ObservableOutcome.toExpr]
229+
230+ /-- Determinism and terminal irreducibility make a reached outcome unique. -/
231+ theorem Reaches.outcome_unique_on
232+ {config : Config α} {obs : MachineStore α → β}
233+ {outcome outcome' : ObservableOutcome}
234+ {store store' : MachineStore α} {o o' : β}
235+ (first : Reaches config outcome store) (hfirst : obs store = o)
236+ (second : Reaches config outcome' store') (hsecond : obs store' = o') :
237+ outcome = outcome' ∧ o = o' := by
238+ obtain ⟨trace, execution⟩ := first
239+ obtain ⟨trace', execution'⟩ := second
240+ have terminal (terminalOutcome : ObservableOutcome)
241+ (kind : StepKind) (next : Config α) :
242+ ¬Step ⟨terminalOutcome.toExpr, store⟩ kind next := by
243+ cases terminalOutcome with
244+ | done => exact done_terminal
245+ | trapped => exact trapped_terminal
246+ have terminal' (terminalOutcome : ObservableOutcome)
247+ (kind : StepKind) (next : Config α) :
248+ ¬Step ⟨terminalOutcome.toExpr, store'⟩ kind next := by
249+ cases terminalOutcome with
250+ | done => exact done_terminal
251+ | trapped => exact trapped_terminal
252+ have hconfig := steps_irreducible_deterministic execution execution'
253+ (terminal outcome) (terminal' outcome')
254+ have hparts := Config.mk.inj hconfig
255+ have houtcome := ObservableOutcome.toExpr_injective hparts.1
256+ exact
257+ ⟨houtcome,
258+ hfirst.symm.trans ((congrArg obs hparts.2 ).trans hsecond)⟩
259+
260+ /-- A common reached terminal outcome discharges equivalence. -/
261+ theorem ObservationallyEquivOn.of_common_reached
262+ {config₁ config₂ : Config α} {obs : MachineStore α → β}
263+ {outcome : ObservableOutcome} {store₁ store₂ : MachineStore α} {o : β}
264+ (first : Reaches config₁ outcome store₁) (hfirst : obs store₁ = o)
265+ (second : Reaches config₂ outcome store₂) (hsecond : obs store₂ = o) :
266+ ObservationallyEquivOn config₁ config₂ obs := by
267+ intro candidate observed
268+ constructor
269+ · rintro ⟨store, reached, hobs⟩
270+ obtain ⟨rfl, rfl⟩ :=
271+ Reaches.outcome_unique_on reached hobs first hfirst
272+ exact ⟨store₂, second, hsecond⟩
273+ · rintro ⟨store, reached, hobs⟩
274+ obtain ⟨rfl, rfl⟩ :=
275+ Reaches.outcome_unique_on reached hobs second hsecond
276+ exact ⟨store₁, first, hfirst⟩
277+
278+ /-- A common normally terminating result is the main success-specialized
279+ discharge rule. -/
280+ theorem ObservationallyEquivOn.of_common_outcome
281+ {config₁ config₂ : Config α} {obs : MachineStore α → β}
282+ {r : List Value} {o : β}
283+ (first : TerminatesWith config₁
284+ (fun values store => values = r ∧ obs store = o))
285+ (second : TerminatesWith config₂
286+ (fun values store => values = r ∧ obs store = o)) :
287+ ObservationallyEquivOn config₁ config₂ obs := by
288+ obtain ⟨trace₁, values₁, store₁, execution₁, hvalues₁, hobs₁⟩ := first
289+ obtain ⟨trace₂, values₂, store₂, execution₂, hvalues₂, hobs₂⟩ := second
290+ subst values₁
291+ subst values₂
292+ apply ObservationallyEquivOn.of_common_reached
293+ (outcome := .done r) (store₁ := store₁) (store₂ := store₂)
294+ · exact ⟨trace₁, execution₁⟩
295+ · exact hobs₁
296+ · exact ⟨trace₂, execution₂⟩
297+ · exact hobs₂
298+
299+ /-- A common structural trap is an observable common outcome. -/
300+ theorem ObservationallyEquivOn.of_common_trap
301+ {config₁ config₂ : Config α} {obs : MachineStore α → β}
302+ {reason : TrapReason} {store₁ store₂ : MachineStore α} {o : β}
303+ (first : Reaches config₁ (.trapped reason) store₁)
304+ (hfirst : obs store₁ = o)
305+ (second : Reaches config₂ (.trapped reason) store₂)
306+ (hsecond : obs store₂ = o) :
307+ ObservationallyEquivOn config₁ config₂ obs :=
308+ ObservationallyEquivOn.of_common_reached first hfirst second hsecond
309+
310+ theorem ObservationallyEquivOn.refl
311+ (config : Config α) (obs : MachineStore α → β) :
312+ ObservationallyEquivOn config config obs :=
313+ fun _ _ => Iff.rfl
314+
315+ theorem ObservationallyEquivOn.symm
316+ {config₁ config₂ : Config α} {obs : MachineStore α → β}
317+ (equivalent : ObservationallyEquivOn config₁ config₂ obs) :
318+ ObservationallyEquivOn config₂ config₁ obs :=
319+ fun outcome observed => (equivalent outcome observed).symm
320+
321+ theorem ObservationallyEquivOn.trans
322+ {config₁ config₂ config₃ : Config α} {obs : MachineStore α → β}
323+ (first : ObservationallyEquivOn config₁ config₂ obs)
324+ (second : ObservationallyEquivOn config₂ config₃ obs) :
325+ ObservationallyEquivOn config₁ config₃ obs :=
326+ fun outcome observed =>
327+ (first outcome observed).trans (second outcome observed)
328+
329+ /-- Host-state observation, deliberately ignoring module-private scratch
330+ memory as in the legacy relation. -/
331+ def ObservationallyEquiv (config₁ config₂ : Config α) : Prop :=
332+ ObservationallyEquivOn config₁ config₂ (fun store => store.wasm.host)
333+
334+ theorem ObservationallyEquiv.of_common_outcome
335+ {config₁ config₂ : Config α} {r : List Value} {h : α}
336+ (first : TerminatesWith config₁
337+ (fun values store => values = r ∧ store.wasm.host = h))
338+ (second : TerminatesWith config₂
339+ (fun values store => values = r ∧ store.wasm.host = h)) :
340+ ObservationallyEquiv config₁ config₂ :=
341+ ObservationallyEquivOn.of_common_outcome first second
342+
343+ end SmallStep
344+
192345end Wasm
0 commit comments