@@ -556,4 +556,262 @@ theorem run_eq
556556 · cases n <;> rfl
557557 all_goals rfl
558558
559+
560+ /-! ## Environment independence
561+
562+ When a module declares no imported functions (`imports = []`), the host
563+ environment is never consulted: the host arm of `run` is dead, and `env`
564+ is otherwise only threaded, never inspected. So `run` (hence `exec` /
565+ `execOne`) gives the same result under any two environments. This is what
566+ lets fuel-free specs and `iris`-style adequacy results quantify over an
567+ arbitrary `env` yet be discharged at the canonical empty one. Proved by
568+ the same joint fuel induction as `fuel_mono_aux`. -/
569+ set_option maxHeartbeats 1600000 in
570+ /-- Joint env-independence for `execOne`, `exec`, and `run`, over any module
571+ with no imported functions. Proved by induction on fuel, mirroring
572+ `fuel_mono_aux`. The three projections follow. -/
573+ theorem env_indep_aux {α : Type } : ∀ (f : Nat),
574+ (∀ (m : Module) (_ : m.imports.length = 0 ) (st : Store α) (s : Locals)
575+ (inst : Instruction) (env env' : HostEnv α),
576+ execOne f m st s inst env = execOne f m st s inst env') ∧
577+ (∀ (m : Module) (_ : m.imports.length = 0 ) (st : Store α) (s : Locals)
578+ (p : Program) (env env' : HostEnv α),
579+ exec f m st s p env = exec f m st s p env') ∧
580+ (∀ (m : Module) (_ : m.imports.length = 0 ) (id : Nat) (initial : Store α)
581+ (args : List Value) (env env' : HostEnv α),
582+ run f m id initial args env = run f m id initial args env') := by
583+ intro f
584+ induction f with
585+ | zero =>
586+ have hOne : ∀ (m : Module) (_ : m.imports.length = 0 ) (st : Store α) (s : Locals)
587+ (inst : Instruction) (env env' : HostEnv α),
588+ execOne 0 m st s inst env = execOne 0 m st s inst env' := by
589+ intro m _ st s inst env env'
590+ simp only [execOne.eq_def]
591+ have hExec : ∀ (m : Module) (_ : m.imports.length = 0 ) (st : Store α) (s : Locals)
592+ (p : Program) (env env' : HostEnv α),
593+ exec 0 m st s p env = exec 0 m st s p env' := by
594+ intro m hh st s p env env'
595+ cases p with
596+ | nil => simp only [exec]
597+ | cons inst rest => simp only [exec, execOne.eq_def]
598+ refine ⟨hOne, hExec, ?_⟩
599+ intro m hh id initial args env env'
600+ have hnone : m.imports[id]? = none := by
601+ rw [List.eq_nil_of_length_eq_zero hh]; rfl
602+ simp only [run, hnone]
603+ rcases h : m.funcs[id - m.imports.length]? with _ | fn
604+ · rfl
605+ · simp only
606+ rw [hExec m hh initial (fn.toLocals (args.take fn.numParams).reverse) fn.body env env']
607+ rcases hres : exec 0 m initial
608+ (fn.toLocals (args.take fn.numParams).reverse) fn.body env' with
609+ _ | ⟨n, _, _⟩ | _ | _ | _ | _ | ⟨id', st', vs⟩ | _
610+ · rfl
611+ · cases n <;> rfl
612+ · rfl
613+ · rfl
614+ · rfl
615+ · rfl
616+ · simp only [runTail]
617+ · rfl
618+ | succ k ih =>
619+ obtain ⟨ihOne, ihExec, ihRun⟩ := ih
620+ -- Step 1: execOne at fuel k+1.
621+ have hOne : ∀ (m : Module) (_ : m.imports.length = 0 ) (st : Store α) (s : Locals)
622+ (inst : Instruction) (env env' : HostEnv α),
623+ execOne (k + 1 ) m st s inst env = execOne (k + 1 ) m st s inst env' := by
624+ intro m hh st s inst env env'
625+ cases inst with
626+ | block ps rs body =>
627+ simp only [execOne.eq_def]
628+ rw [ihExec m hh st s body env env']
629+ | loop ps rs body =>
630+ simp only [execOne_loop_succ]
631+ rw [ihExec m hh st s body env env']
632+ rcases hres : exec k m st s body env' with
633+ ⟨st', s'⟩ | ⟨n, st', s'⟩ | ⟨st', vs⟩ | msg | msg | _ | ⟨id', st', vs⟩
634+ | ⟨tag, targs, st', s'⟩
635+ · rfl
636+ · cases n with
637+ | zero =>
638+ exact ihOne m hh st'
639+ { s' with values := s'.values.take ps ++ s.values.drop ps }
640+ (.loop ps rs body) env env'
641+ | succ _ => rfl
642+ · rfl
643+ · rfl
644+ · rfl
645+ · rfl
646+ · rfl
647+ · rfl
648+ | iff ps rs thn els =>
649+ simp only [execOne.eq_def]
650+ rcases hvals : s.values with _ | ⟨v, vs⟩
651+ · rfl
652+ · cases v with
653+ | i32 c =>
654+ by_cases hc : c ≠ 0
655+ · simp only [if_pos hc]
656+ rw [ihExec m hh st { s with values := vs } thn env env']
657+ · simp only [if_neg hc]
658+ rw [ihExec m hh st { s with values := vs } els env env']
659+ | i64 _ => rfl
660+ | f32 _ => rfl
661+ | f64 _ => rfl
662+ | funcref _ => rfl
663+ | externref _ => rfl
664+ | exnref _ => rfl
665+ | v128 _ => rfl
666+ | anyref _ => rfl
667+ | call id =>
668+ simp only [execOne.eq_def]
669+ rw [ihRun m hh id st s.values env env']
670+ | callIndirect ti tj =>
671+ rcases hvals : s.values with _ | ⟨v, rest⟩
672+ · simp only [execOne.eq_def, hvals]
673+ · cases hv : v with
674+ | i64 i =>
675+ rcases htbl : st.tables[tj]? with _ | tbl
676+ · simp only [execOne.eq_def, hvals, hv, htbl]
677+ · rcases hslot : tbl[i.toNat]? with _ | slot
678+ · simp only [execOne.eq_def, hvals, hv, htbl, hslot]
679+ · cases hslot' : slot with
680+ | i32 _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
681+ | i64 _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
682+ | f32 _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
683+ | f64 _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
684+ | externref _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
685+ | exnref _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
686+ | v128 _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
687+ | funcref r =>
688+ rcases hr : r with _ | fid
689+ · simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot', hr]
690+ · rcases hfn : m.funcSig? fid with _ | fnsig
691+ · simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot', hr, hfn]
692+ · rcases hty : m.types[ti]? with _ | ty
693+ · simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot', hr, hfn, hty]
694+ · by_cases hsig : m.indirectCallTypeOk fid ti fnsig ty = true
695+ · simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot', hr,
696+ hfn, hty, if_pos hsig, ihRun m hh fid st rest env env']
697+ · simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot', hr,
698+ hfn, hty, if_neg hsig]
699+ | anyref _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
700+ | f32 _ => simp only [execOne.eq_def, hvals, hv]
701+ | f64 _ => simp only [execOne.eq_def, hvals, hv]
702+ | funcref _ => simp only [execOne.eq_def, hvals, hv]
703+ | externref _ => simp only [execOne.eq_def, hvals, hv]
704+ | exnref _ => simp only [execOne.eq_def, hvals, hv]
705+ | v128 _ => simp only [execOne.eq_def, hvals, hv]
706+ | i32 i =>
707+ rcases htbl : st.tables[tj]? with _ | tbl
708+ · simp only [execOne.eq_def, hvals, hv, htbl]
709+ · rcases hslot : tbl[i.toNat]? with _ | slot
710+ · simp only [execOne.eq_def, hvals, hv, htbl, hslot]
711+ · cases hslot' : slot with
712+ | i32 _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
713+ | i64 _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
714+ | f32 _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
715+ | f64 _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
716+ | externref _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
717+ | exnref _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
718+ | v128 _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
719+ | funcref r =>
720+ rcases hr : r with _ | fid
721+ · simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot', hr]
722+ · rcases hfn : m.funcSig? fid with _ | fnsig
723+ · simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot', hr, hfn]
724+ · rcases hty : m.types[ti]? with _ | ty
725+ · simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot', hr, hfn, hty]
726+ · by_cases hsig : m.indirectCallTypeOk fid ti fnsig ty = true
727+ · simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot', hr,
728+ hfn, hty, if_pos hsig, ihRun m hh fid st rest env env']
729+ · simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot', hr,
730+ hfn, hty, if_neg hsig]
731+ | anyref _ => simp only [execOne.eq_def, hvals, hv, htbl, hslot, hslot']
732+ | anyref _ => simp only [execOne.eq_def, hvals, hv]
733+ | tryTable ps rs catches body =>
734+ simp only [execOne.eq_def]
735+ rw [ihExec m hh st s body env env']
736+ | callRef ti =>
737+ rcases hvals : s.values with _ | ⟨v, rest⟩
738+ · simp only [execOne.eq_def, hvals]
739+ · cases hv : v with
740+ | i32 _ => simp only [execOne.eq_def, hvals, hv]
741+ | i64 _ => simp only [execOne.eq_def, hvals, hv]
742+ | f32 _ => simp only [execOne.eq_def, hvals, hv]
743+ | f64 _ => simp only [execOne.eq_def, hvals, hv]
744+ | externref _ => simp only [execOne.eq_def, hvals, hv]
745+ | exnref _ => simp only [execOne.eq_def, hvals, hv]
746+ | v128 _ => simp only [execOne.eq_def, hvals, hv]
747+ | funcref r =>
748+ rcases hr : r with _ | fid
749+ · simp only [execOne.eq_def, hvals, hv, hr]
750+ · simp only [execOne.eq_def, hvals, hv, hr, ihRun m hh fid st rest env env']
751+ | anyref _ => simp only [execOne.eq_def, hvals, hv]
752+ | memOp kIdx inner =>
753+ rcases hmem : st.extraMems[kIdx - 1 ]? with _ | memK
754+ · simp only [execOne_memOp_succ, hmem]
755+ · rcases hdecl : m.extraMemories[kIdx - 1 ]? with _ | declK
756+ · simp only [execOne_memOp_succ, hmem, hdecl]
757+ · simp only [execOne_memOp_succ, hmem, hdecl,
758+ ihOne { m with memory := some declK } hh { st with mem := memK }
759+ s inner env env']
760+ | _ => simp only [execOne.eq_def]
761+ -- Step 2: exec at fuel k+1 using hOne.
762+ have hExec : ∀ (m : Module) (_ : m.imports.length = 0 ) (st : Store α) (s : Locals)
763+ (p : Program) (env env' : HostEnv α),
764+ exec (k + 1 ) m st s p env = exec (k + 1 ) m st s p env' := by
765+ intro m hh st s p env env'
766+ induction p generalizing st s with
767+ | nil => simp only [exec]
768+ | cons inst rest ihRest =>
769+ simp only [exec]
770+ rw [hOne m hh st s inst env env']
771+ rcases hres : execOne (k+1 ) m st s inst env' with
772+ ⟨st', s'⟩ | ⟨n, st', s'⟩ | ⟨st', vs⟩ | msg | msg | _ | ⟨id', st', vs⟩
773+ | ⟨tag, targs, st', s'⟩
774+ · exact ihRest st' s'
775+ all_goals rfl
776+ refine ⟨hOne, hExec, ?_⟩
777+ -- Step 3: run at fuel k+1.
778+ intro m hh id initial args env env'
779+ have hnone : m.imports[id]? = none := by
780+ rw [List.eq_nil_of_length_eq_zero hh]; rfl
781+ simp only [run, hnone]
782+ rcases h : m.funcs[id - m.imports.length]? with _ | fn
783+ · rfl
784+ · simp only
785+ rw [hExec m hh initial (fn.toLocals (args.take fn.numParams).reverse) fn.body env env']
786+ rcases hres : exec (k+1 ) m initial
787+ (fn.toLocals (args.take fn.numParams).reverse) fn.body env' with
788+ _ | ⟨n, _, _⟩ | _ | _ | _ | _ | ⟨id', st', vs⟩ | _
789+ · rfl
790+ · cases n <;> rfl
791+ · rfl
792+ · rfl
793+ · rfl
794+ · rfl
795+ · simp only [runTail, ihRun m hh id' st' vs env env']
796+ · rfl
797+
798+ theorem execOne_env_indep
799+ {m : Module} (hm : m.imports.length = 0 ) {st : Store α} {s : Locals}
800+ {inst : Instruction} {fuel : Nat} {env env' : HostEnv α} :
801+ execOne fuel m st s inst env = execOne fuel m st s inst env' :=
802+ (env_indep_aux fuel).1 m hm st s inst env env'
803+
804+ theorem exec_env_indep
805+ {m : Module} (hm : m.imports.length = 0 ) {st : Store α} {s : Locals}
806+ {p : Program} {fuel : Nat} {env env' : HostEnv α} :
807+ exec fuel m st s p env = exec fuel m st s p env' :=
808+ (env_indep_aux fuel).2 .1 m hm st s p env env'
809+
810+ /-- With no imported functions, `run` does not depend on the host environment. -/
811+ theorem run_env_indep
812+ {m : Module} (hm : m.imports.length = 0 ) {id : Nat} {initial : Store α}
813+ {args : List Value} {fuel : Nat} {env env' : HostEnv α} :
814+ run fuel m id initial args env = run fuel m id initial args env' :=
815+ (env_indep_aux fuel).2 .2 m hm id initial args env env'
816+
559817end Wasm
0 commit comments