theory Native imports Compiler begin datatype register = Rax | Rcx | Rbx | Rdx | R8 | R9 datatype opcode = Immediate pixel | Push register | Pop register | Peek nat | Arithmetic operator | Conditional | Move register register record machine = registers :: "register \ pixel" stack :: "pixel list" fun input_register :: "input \ register" where "input_register X = R8" | "input_register Y = R9" | "input_register T = Rdx" definition inputs :: "machine \ input \ pixel" where "inputs s i = registers s (input_register i)" fun exchanges :: "operator \ bool" where "exchanges Sub = True" | "exchanges Shl = True" | "exchanges Shr = True" | "exchanges Less = True" | "exchanges _ = False" fun transition :: "opcode \ machine \ machine option" where "transition (Immediate v) s = Some (s\registers := (registers s)(Rax := v)\)" | "transition (Push r) s = Some (s\stack := registers s r # stack s\)" | "transition (Pop r) s = (case stack s of [] \ None | v # vs \ Some (s\registers := (registers s)(r := v), stack := vs\))" | "transition (Peek i) s = (if i < length (stack s) then Some (s\registers := (registers s)(Rax := stack s ! i)\) else None)" | "transition (Arithmetic op) s = Some (s\registers := (registers s)(Rcx := if exchanges op then registers s Rax else registers s Rcx, Rax := operate op (registers s Rcx) (registers s Rax))\)" | "transition Conditional s = Some (s\registers := (registers s)(Rax := if registers s Rbx = 0 then registers s Rax else registers s Rcx)\)" | "transition (Move dst src) s = Some (s\registers := (registers s)(dst := registers s src)\)" fun run :: "opcode list \ machine \ machine option" where "run [] s = Some s" | "run (i # code) s = Option.bind (transition i s) (run code)" lemma run_append: "run (a @ b) s = Option.bind (run a s) (run b)" by (induction a arbitrary: s) (auto split: option.splits bind_splits) fun lower :: "instruction \ opcode list" where "lower (Constant v) = [Immediate v, Push Rax]" | "lower (Fetch i) = [Push (input_register i)]" | "lower (Pick i) = [Peek i, Push Rax]" | "lower (Calculate op) = [Pop Rax, Pop Rcx, Arithmetic op, Push Rax]" | "lower Choose = [Pop Rax, Pop Rcx, Pop Rbx, Conditional, Push Rax]" | "lower Slide = [Pop Rax, Pop Rcx, Push Rax]" definition observe :: "machine \ (input \ pixel) \ pixel list" where "observe s = (inputs s, stack s)" lemma input_scratch [simp]: "input_register i \ Rax" "input_register i \ Rcx" "input_register i \ Rbx" by (cases i; simp)+ lemma inputs_update [simp]: "inputs (s\registers := (registers s)(Rax := a)\) = inputs s" "inputs (s\registers := (registers s)(Rcx := a)\) = inputs s" "inputs (s\registers := (registers s)(Rbx := a)\) = inputs s" "inputs (s\stack := vs\) = inputs s" by (auto simp: inputs_def fun_eq_iff split: input.splits) theorem lower_correct: "map_option observe (run (lower i) s) = map_option (\vs. (inputs s, vs)) (step (inputs s) i (stack s))" proof (cases i) case (Constant v) then show ?thesis by (simp add: observe_def) next case (Fetch i) then show ?thesis by (simp add: observe_def inputs_def) next case (Pick i) then show ?thesis by (simp add: observe_def) next case (Calculate op) then show ?thesis by (cases "stack s"; simp add: observe_def inputs_def fun_eq_iff split: list.splits input.splits) next case Choose then show ?thesis by (cases "stack s"; simp add: observe_def inputs_def fun_eq_iff split: list.splits input.splits) next case Slide then show ?thesis by (cases "stack s"; simp add: observe_def inputs_def fun_eq_iff split: list.splits input.splits) qed definition native :: "instruction list \ opcode list" where "native code = concat (map lower code)" theorem native_correct: "map_option observe (run (native code) s) = map_option (\vs. (inputs s, vs)) (execute (inputs s) code (stack s))" proof (induction code arbitrary: s) case Nil then show ?case by (simp add: native_def observe_def) next case (Cons i code) show ?case using Cons.IH lower_correct[of i s] by (auto simp: native_def run_append observe_def split: option.splits bind_splits) qed corollary native_expression: assumes "scoped 0 e" shows "map_option observe (run (native (compile (optimise e) [])) s) = Some (inputs s, evaluate e [] (inputs s) # stack s)" proof - have code: "execute (inputs s) (compile (optimise e) []) (stack s) = Some (evaluate e [] (inputs s) # stack s)" using compile_correct[where e="optimise e" and env="[]" and slots="[]" and s="stack s" and inputs="inputs s"] assms optimise_scoped by (simp add: represents_def optimise_correct) show ?thesis using native_correct[of "compile (optimise e) []" s] code by simp qed theorem native_return: assumes "scoped 0 e" shows "\q. run (native (compile (optimise e) []) @ [Pop Rax]) s = Some q \ registers q Rax = evaluate e [] (inputs s) \ stack q = stack s \ inputs q = inputs s" proof - obtain q where run: "run (native (compile (optimise e) [])) s = Some q" and ins: "inputs q = inputs s" and stk: "stack q = evaluate e [] (inputs s) # stack s" using native_expression[OF assms, of s] by (auto simp: observe_def split: option.splits) show ?thesis using run ins stk by (auto simp: run_append inputs_def fun_eq_iff) qed fun writes :: "opcode \ register set" where "writes (Immediate v) = {Rax}" | "writes (Pop r) = {r}" | "writes (Move dst src) = {dst}" | "writes (Peek i) = {Rax}" | "writes (Arithmetic op) = {Rax,Rcx}" | "writes Conditional = {Rax}" | "writes _ = {}" lemma transition_preserves: "transition i s = Some t \ r \ writes i \ registers t r = registers s r" by (cases i) (auto split: list.splits if_splits) theorem run_preserves: "run code s = Some t \ list_all (\i. r \ writes i) code \ registers t r = registers s r" by (induction code arbitrary: s) (auto dest: transition_preserves split: option.splits bind_splits) lemma lower_scratch: "list_all (\i. writes i \ {Rax,Rcx,Rbx}) (lower op)" by (cases op) auto fun transfers :: "opcode list \ opcode list" where "transfers (Push src # Pop dst # code) = (if src = dst then transfers code else Move dst src # transfers code)" | "transfers (op # code) = op # transfers code" | "transfers [] = []" theorem transfers_correct: "run (transfers code) s = run code s" by (induction code arbitrary: s rule: transfers.induct) (auto simp: fun_upd_idem split: if_splits option.splits bind_splits) lemma transfers_length: "length (transfers code) \ length code" by (induction code rule: transfers.induct) auto lemma transfers_property: "list_all P code \ (\a b. P (Push a) \ P (Pop b) \ P (Move b a)) \ list_all P (transfers code)" by (induction code rule: transfers.induct) auto definition program :: "expression \ opcode list" where "program e = transfers (native (compile (optimise e) []) @ [Pop Rax])" theorem program_return: "scoped 0 e \ \q. run (program e) s = Some q \ registers q Rax = evaluate e [] (inputs s) \ stack q = stack s \ inputs q = inputs s" unfolding program_def using native_return transfers_correct by metis end