| @@ -0,0 +1,169 @@ |
| 1 | +theory Branches |
| 2 | + imports Allocation |
| 3 | +begin |
| 4 | + |
| 5 | +datatype flow = Basic "opcode list" | Sequence flow flow | Choice flow flow |
| 6 | + |
| 7 | +fun flowrun :: "flow \<Rightarrow> machine \<Rightarrow> machine option" where |
| 8 | + "flowrun (Basic code) s = run code s" |
| 9 | +| "flowrun (Sequence a b) s = Option.bind (flowrun a s) (flowrun b)" |
| 10 | +| "flowrun (Choice a b) s = flowrun (if registers s Rax = 0 then b else a) s" |
| 11 | + |
| 12 | +fun chain :: "flow list \<Rightarrow> flow" where |
| 13 | + "chain [] = Basic []" |
| 14 | +| "chain (a # code) = Sequence a (chain code)" |
| 15 | + |
| 16 | +fun branch_allocate :: "bool \<Rightarrow> expression \<Rightarrow> nat list \<Rightarrow> flow" where |
| 17 | + "branch_allocate free (Literal v) slots = Basic [Immediate v]" |
| 18 | +| "branch_allocate free (Input i) slots = Basic [Move Rax (input_register i)]" |
| 19 | +| "branch_allocate free (Variable i) slots = Basic [Peek (slots ! i)]" |
| 20 | +| "branch_allocate free (Binary op a b) slots = (if free then |
| 21 | + chain [branch_allocate True a slots, Basic [Move Rbx Rax], branch_allocate False b slots, |
| 22 | + Basic [Move Rcx Rbx, Arithmetic op]] |
| 23 | + else chain [branch_allocate False a slots, Basic [Push Rax], branch_allocate False b (map Suc slots), |
| 24 | + Basic [Pop Rcx, Arithmetic op]])" |
| 25 | +| "branch_allocate free (Cond c a b) slots = Sequence (branch_allocate free c slots) |
| 26 | + (Choice (branch_allocate free a slots) (branch_allocate free b slots))" |
| 27 | +| "branch_allocate free (Let a b) slots = chain [branch_allocate free a slots, Basic [Push Rax], |
| 28 | + branch_allocate free b (0 # map Suc slots), Basic [Pop Rcx]]" |
| 29 | + |
| 30 | +theorem branch_allocate_correct: |
| 31 | + assumes "scoped (length env) e" "represents slots (stack s) env" |
| 32 | + shows "\<exists>q. flowrun (branch_allocate free e slots) s = Some q \<and> allocated_result free e env s q" |
| 33 | + using assms |
| 34 | +proof (induction e arbitrary: free slots env s) |
| 35 | + case (Literal v) |
| 36 | + then show ?case by (auto simp: allocated_result_def chain.simps) |
| 37 | +next |
| 38 | + case (Input i) |
| 39 | + then show ?case by (auto simp: allocated_result_def inputs_def fun_eq_iff) |
| 40 | +next |
| 41 | + case (Variable i) |
| 42 | + then show ?case by (auto simp: allocated_result_def represents_def) |
| 43 | +next |
| 44 | + case (Binary op a b) |
| 45 | + obtain p where a: "flowrun (branch_allocate free a slots) s = Some p" |
| 46 | + and p: "allocated_result free a env s p" |
| 47 | + using Binary.IH(1)[of env slots s free] Binary.prems by auto |
| 48 | + show ?case |
| 49 | + proof (cases free) |
| 50 | + case True |
| 51 | + let ?p = "p\<lparr>registers := (registers p)(Rbx := registers p Rax)\<rparr>" |
| 52 | + obtain q where b: "flowrun (branch_allocate False b slots) ?p = Some q" |
| 53 | + and q: "allocated_result False b env ?p q" |
| 54 | + using Binary.IH(2)[of env slots ?p False] Binary.prems p |
| 55 | + by (auto simp: allocated_result_def chain.simps) |
| 56 | + show ?thesis using a b p q True |
| 57 | + by (auto simp: allocated_result_def run_append flowrun.simps chain.simps; auto simp: inputs_def fun_eq_iff) |
| 58 | + next |
| 59 | + case False |
| 60 | + let ?p = "p\<lparr>stack := registers p Rax # stack p\<rparr>" |
| 61 | + obtain q where b: "flowrun (branch_allocate False b (map Suc slots)) ?p = Some q" |
| 62 | + and q: "allocated_result False b env ?p q" |
| 63 | + using Binary.IH(2)[of env "map Suc slots" ?p False] Binary.prems p |
| 64 | + represents_push[OF Binary.prems(2), of "registers p Rax"] |
| 65 | + by (auto simp: allocated_result_def chain.simps) |
| 66 | + show ?thesis using a b p q False |
| 67 | + by (auto simp: allocated_result_def run_append flowrun.simps chain.simps; auto simp: inputs_def fun_eq_iff) |
| 68 | + qed |
| 69 | +next |
| 70 | + case (Cond c a b) |
| 71 | + obtain p where c: "flowrun (branch_allocate free c slots) s = Some p" |
| 72 | + and p: "allocated_result free c env s p" |
| 73 | + using Cond.IH(1)[of env slots s free] Cond.prems by auto |
| 74 | + show ?case |
| 75 | + using Cond.IH(2)[of env slots p free] Cond.IH(3)[of env slots p free] |
| 76 | + Cond.prems c p |
| 77 | + by (auto simp: allocated_result_def split: if_splits) |
| 78 | +next |
| 79 | + case (Let a b) |
| 80 | + obtain p where a: "flowrun (branch_allocate free a slots) s = Some p" |
| 81 | + and p: "allocated_result free a env s p" |
| 82 | + using Let.IH(1)[of env slots s free] Let.prems by auto |
| 83 | + let ?p = "p\<lparr>stack := registers p Rax # stack p\<rparr>" |
| 84 | + obtain q where b: "flowrun (branch_allocate free b (0 # map Suc slots)) ?p = Some q" |
| 85 | + and q: "allocated_result free b (registers p Rax # env) ?p q" |
| 86 | + using Let.IH(2)[of "registers p Rax # env" "0 # map Suc slots" ?p free] |
| 87 | + Let.prems p represents_bind[OF Let.prems(2), of "registers p Rax"] |
| 88 | + by (auto simp: allocated_result_def chain.simps) |
| 89 | + show ?case using a b p q |
| 90 | + by (auto simp: allocated_result_def run_append flowrun.simps chain.simps; auto simp: inputs_def fun_eq_iff) |
| 91 | +qed |
| 92 | + |
| 93 | +definition branching :: "expression \<Rightarrow> flow" where |
| 94 | + "branching e = branch_allocate True (optimise e) []" |
| 95 | + |
| 96 | +theorem branching_return: |
| 97 | + "scoped 0 e \<Longrightarrow> \<exists>q. flowrun (branching e) s = Some q \<and> |
| 98 | + registers q Rax = evaluate e [] (inputs s) \<and> stack q = stack s \<and> inputs q = inputs s" |
| 99 | + using branch_allocate_correct[where env="[]" and slots="[]" and free=True and e="optimise e"] |
| 100 | + optimise_scoped |
| 101 | + by (auto simp: branching_def allocated_result_def represents_def optimise_correct) |
| 102 | + |
| 103 | +fun flowaudit :: "flow \<Rightarrow> nat \<Rightarrow> nat option" where |
| 104 | + "flowaudit (Basic code) n = audit code n" |
| 105 | +| "flowaudit (Sequence a b) n = Option.bind (flowaudit a n) (flowaudit b)" |
| 106 | +| "flowaudit (Choice a b) n = (case (flowaudit a n, flowaudit b n) of |
| 107 | + (Some x,Some y) \<Rightarrow> if x = y then Some x else None | _ \<Rightarrow> None)" |
| 108 | + |
| 109 | +theorem flowaudit_sound: |
| 110 | + "flowaudit code (length (stack s)) = Some n \<Longrightarrow> |
| 111 | + \<exists>q. flowrun code s = Some q \<and> length (stack q) = n \<and> inputs q = inputs s" |
| 112 | +proof (induction code arbitrary: s n) |
| 113 | + case (Basic code) |
| 114 | + then show ?case using audit_sound audit_inputs by fastforce |
| 115 | +next |
| 116 | + case (Sequence a b) |
| 117 | + then show ?case by (auto split: option.splits bind_splits; fastforce) |
| 118 | +next |
| 119 | + case (Choice a b) |
| 120 | + then show ?case by (auto split: option.splits if_splits; fastforce) |
| 121 | +qed |
| 122 | + |
| 123 | +fun flowbytes :: "flow \<Rightarrow> nat list" where |
| 124 | + "flowbytes (Basic code) = concat (map encode code)" |
| 125 | +| "flowbytes (Sequence a b) = flowbytes a @ flowbytes b" |
| 126 | +| "flowbytes (Choice a b) = [72,133,192,15,132] @ octets 4 (length (flowbytes a) + 5) @ |
| 127 | + flowbytes a @ [233] @ octets 4 (length (flowbytes b)) @ flowbytes b" |
| 128 | + |
| 129 | +fun flowvalid :: "flow \<Rightarrow> bool" where |
| 130 | + "flowvalid (Basic code) = list_all encodable code" |
| 131 | +| "flowvalid (Sequence a b) = (flowvalid a \<and> flowvalid b)" |
| 132 | +| "flowvalid (Choice a b) = (flowvalid a \<and> flowvalid b \<and> |
| 133 | + length (flowbytes a) + 5 < 2 ^ 31 \<and> length (flowbytes b) < 2 ^ 31)" |
| 134 | + |
| 135 | +lemma flowbytes_bytes: "set (flowbytes code) \<subseteq> {..<256}" |
| 136 | + by (induction code) (auto dest: subsetD[OF encode_bytes] subsetD[OF octets_bytes]) |
| 137 | + |
| 138 | +function unpack :: "nat \<Rightarrow> nat list \<Rightarrow> flow option" where |
| 139 | + "unpack 0 bytes = (if bytes = [] then Some (Basic []) else None)" |
| 140 | +| "unpack (Suc fuel) bytes = (if bytes = [] then Some (Basic []) else |
| 141 | + if take 5 bytes = [72,133,192,15,132] then |
| 142 | + let offset = integer (take 4 (drop 5 bytes)); pos = 9 + (offset - 5); |
| 143 | + count = integer (take 4 (drop (Suc pos) bytes)) in |
| 144 | + if 9 \<le> length bytes \<and> 5 \<le> offset \<and> pos + 5 + count \<le> length bytes \<and> |
| 145 | + bytes ! pos = 233 then |
| 146 | + Option.bind (unpack fuel (take (offset - 5) (drop 9 bytes))) (\<lambda>a. |
| 147 | + Option.bind (unpack fuel (take count (drop (pos + 5) bytes))) (\<lambda>b. |
| 148 | + map_option (Sequence (Choice a b)) (unpack fuel (drop (pos + 5 + count) bytes)))) |
| 149 | + else None |
| 150 | + else Option.bind (decode bytes) (\<lambda>(op,rest). |
| 151 | + map_option (Sequence (Basic [op])) (unpack fuel rest)))" |
| 152 | + by pat_completeness auto |
| 153 | +termination by (relation "measure (\<lambda>(fuel,bytes). fuel)") auto |
| 154 | + |
| 155 | +declare unpack.simps [simp del] |
| 156 | + |
| 157 | +definition inspect :: "nat list \<Rightarrow> flow option" where |
| 158 | + "inspect bytes = (if bytes = [] \<or> last bytes \<noteq> 195 \<or> \<not> list_all (\<lambda>b. b < 256) bytes then None else |
| 159 | + Option.bind (unpack (length bytes) (butlast bytes)) (\<lambda>code. |
| 160 | + if flowvalid code \<and> flowbytes code = butlast bytes \<and> flowaudit code 0 = Some 0 |
| 161 | + then Some code else None))" |
| 162 | + |
| 163 | +theorem inspected_sound: |
| 164 | + "inspect bytes = Some code \<Longrightarrow> \<exists>q. flowrun code (s\<lparr>stack := []\<rparr>) = Some q \<and> |
| 165 | + stack q = [] \<and> inputs q = inputs s" |
| 166 | + using flowaudit_sound[where s="s\<lparr>stack := []\<rparr>" and n=0] |
| 167 | + by (auto simp: inspect_def split: if_splits option.splits bind_splits) |
| 168 | + |
| 169 | +end |