diff --git a/thys/Interpreter_Optimizations/Inca_to_Ubx_compiler.thy b/thys/Interpreter_Optimizations/Inca_to_Ubx_compiler.thy --- a/thys/Interpreter_Optimizations/Inca_to_Ubx_compiler.thy +++ b/thys/Interpreter_Optimizations/Inca_to_Ubx_compiler.thy @@ -1,548 +1,558 @@ theory Inca_to_Ubx_compiler imports Inca_to_Ubx_simulation Result "VeriComp.Compiler" "HOL-Library.Monad_Syntax" begin section \Generic program rewriting\ context fixes rewrite_instr :: "nat \ 'a \ 'stack \ ('err, 'b \ 'stack) result" begin fun rewrite_prog :: "nat \ 'a list \ 'stack \ ('err, 'b list \ 'stack) result" where "rewrite_prog _ [] \ = Ok ([], \)" | "rewrite_prog n (x # xs) \ = do { (x', \') \ rewrite_instr n x \; (xs', \'') \ rewrite_prog (Suc n) xs \'; Ok (x' # xs', \'') }" lemma rewrite_prog_map_f: assumes "\x \1 n x' \2. rewrite_instr n x \1 = Ok (x', \2) \ f x' = x" shows "rewrite_prog n xs \1 = Ok (ys, \2) \ map f ys = xs" by (induction xs arbitrary: \1 n ys; auto simp: assms) end fun gen_pop_push where "gen_pop_push instr (domain, codomain) \ = ( let ar = length domain in if ar \ length \ \ take ar \ = domain then Ok (instr, codomain # drop ar \) else Error () )" context inca_to_ubx_simulation begin lemma sp_rewrite_prog: assumes "rewrite_prog f n p1 \1 = Ok (p2, \2)" and "\x \1 n x' \2. f n x \1 = Ok (x', \2) \ Subx.sp_instr F x' \1 = Ok \2" shows "Subx.sp F p2 \1 = Ok \2" using assms(1) by (induction p1 arbitrary: \1 n p2; auto simp: assms(2)) section \Lifting\ context fixes get_arity :: "'fun \ nat option" and load_oracle :: "nat \ type option" begin fun lift_instr where "lift_instr (Inca.IPush x) \ = Ok (IPush x, None # \)" | "lift_instr Inca.IPop (_ # \) = Ok (IPop, \)" | "lift_instr (Inca.ILoad x) (None # \) = Ok (ILoad x, None # \)" | "lift_instr (Inca.IStore x) (None # None # \) = Ok (IStore x, \)" | "lift_instr (Inca.IOp op) \ = gen_pop_push (IOp op) (replicate (\\\\\ op) None, None) \" | "lift_instr (Inca.IOpInl opinl) \ = gen_pop_push (IOpInl opinl) (replicate (\\\\\ (\
\\\\ opinl)) None, None) \" | "lift_instr (Inca.ICall f) \ = do { ar \ Result.of_option () (get_arity f); gen_pop_push (ICall f) (replicate ar None, None) \ }" | "lift_instr _ _ = Error ()" definition lift where "lift = rewrite_prog (\_. lift_instr)" lemma sp_lift_instr: assumes "lift_instr instr \1 = Ok (instr', \2)" and "\x. rel_option (\ar fd. arity fd = ar) (get_arity x) (F x)" shows "Subx.sp_instr F instr' \1 = Ok \2" using assms(1) proof (induction instr \1 rule: lift_instr.induct) fix f \ n assume "lift_instr (Inca.ICall f) \ = Ok (instr', \2)" thus "Subx.sp_instr F instr' \ = Ok \2" using assms(2)[of f] by (auto simp: option_rel_Some1) qed (auto simp add: Let_def) lemma sp_lift: assumes "lift n p1 \1 = Ok (p2, \2)" and "\x. rel_option (\ar fd. arity fd = ar) (get_arity x) (F x)" shows "Subx.sp F p2 \1 = Ok \2" using assms by (auto elim!: sp_rewrite_prog sp_lift_instr simp: lift_def) lemma norm_lift_instr: "lift_instr x \1 = Ok (x', \2) \ norm_instr x' = x" by (induction x \1 rule: lift_instr.induct; auto simp: Let_def ) lemma norm_lift: assumes "lift n xs \1 = Ok (ys, \2)" shows "map norm_instr ys = xs" by (auto intro!: rewrite_prog_map_f[OF _ assms[unfolded lift_def]] simp: norm_lift_instr) section \Optimization\ fun result_alternative :: "('a, 'b) result \ ('a, 'b) result \ ('a, 'b) result" (infixr "<|>" 51) where "result_alternative (Ok x) _ = Ok x" | "result_alternative _ (Ok x) = Ok x" | "result_alternative (Error e) _ = Error e" definition try_unbox where "try_unbox \ x \ unbox mk_instr \ (case unbox x of Some n \ Ok (mk_instr n, Some \ # \) | None \ Error ())" fun optim_instr where "optim_instr _ (IPush d) \ = try_unbox Ubx1 d \ unbox_ubx1 IPushUbx1 <|> try_unbox Ubx2 d \ unbox_ubx2 IPushUbx2 <|> Ok (IPush d, None # \) " | "optim_instr _ (IPushUbx1 n) \ = Ok (IPushUbx1 n, Some Ubx1 # \)" | "optim_instr _ (IPushUbx2 b) \ = Ok (IPushUbx2 b, Some Ubx2 # \)" | "optim_instr _ IPop (_ # \) = Ok (IPop, \)" | "optim_instr n (ILoad x) (None # \) = ( case load_oracle n of Some \ \ Ok (ILoadUbx \ x, Some \ # \) | _ \ Ok (ILoad x, None # \) )" | "optim_instr _ (ILoadUbx \ x) (None # \) = Ok (ILoadUbx \ x, Some \ # \)" | "optim_instr _ (IStore x) (None # None # \) = Ok (IStore x, \)" | + "optim_instr _ (IStore x) (None # Some \ # \) = Ok (IStoreUbx \ x, \)" | "optim_instr _ (IStoreUbx \\<^sub>1 x) (None # Some \\<^sub>2 # \) = (if \\<^sub>1 = \\<^sub>2 then Ok (IStoreUbx \\<^sub>1 x, \) else Error ())" | "optim_instr _ (IOp op) \ = gen_pop_push (IOp op) (replicate (\\\\\ op) None, None) \" | "optim_instr _ (IOpInl opinl) \ = ( let ar = \\\\\ (\
\\\\ opinl) in if ar \ length \ then case \\\ opinl (take ar \) of None \ gen_pop_push (IOpInl opinl) (replicate ar None, None) \ | Some opubx \ Ok (IOpUbx opubx, snd (\\\\\\\\ opubx) # drop ar \) else Error () )" | "optim_instr _ (IOpUbx opubx) \ = gen_pop_push (IOpUbx opubx) (\\\\\\\\ opubx) \" | "optim_instr _ (ICall f) \ = do { ar \ Result.of_option () (get_arity f); gen_pop_push (ICall f) (replicate ar None, None) \ }" | "optim_instr _ _ _ = Error ()" definition optim where "optim \ rewrite_prog optim_instr" +lemma + assumes + "optim 0 [IPush d\<^sub>1, IPush d\<^sub>2, IStore y] [] = Ok (xs, ys)" + "unbox_ubx1 d\<^sub>1 = Some x" + "unbox_ubx1 d\<^sub>2 = None" "unbox_ubx2 d\<^sub>2 = None" + shows "xs = [IPushUbx1 x, IPush d\<^sub>2, IStoreUbx Ubx1 y] \ ys = []" + using assms(1) + by (simp add: assms optim_def try_unbox_def) + lemma norm_optim_instr: "optim_instr n x \1 = Ok (x', \2) \ norm_instr x' = norm_instr x" for x \1 n x' \2 proof (induction n x \1 rule: optim_instr.induct) fix d \1 n assume "optim_instr n (Ubx.IPush d) \1 = Ok (x', \2)" thus "norm_instr x' = norm_instr (Ubx.instr.IPush d)" using Subx.box_unbox_inverse by (auto elim!: result_alternative.elims simp: try_unbox_def option.case_eq_if) next fix x \1 n assume assms: "optim_instr n (Ubx.ILoad x) (None # \1) = Ok (x', \2)" thus "norm_instr x' = norm_instr (Ubx.ILoad x)" proof (cases "load_oracle n") case None then show ?thesis using assms by simp next case (Some x) then show ?thesis using assms by (cases x) auto qed next fix opinl \1 n assume assms: "optim_instr n (IOpInl opinl) \1 = Ok (x', \2)" then have arity_le_\1: "\\\\\ (\
\\\\ opinl) \ length \1" using prod.inject by fastforce show "norm_instr x' = norm_instr (IOpInl opinl)" proof (cases "\\\ opinl (take (\\\\\ (\
\\\\ opinl)) \1)") case None then show ?thesis using assms arity_le_\1 by (simp add: Let_def) next case (Some opubx) then show ?thesis using assms arity_le_\1 by (auto simp: case_prod_beta Subx.\\\_invertible) qed next fix opubx \1 n assume assms: "optim_instr n (IOpUbx opubx) \1 = Ok (x', \2)" then show "norm_instr x' = norm_instr (IOpUbx opubx)" by (cases "\\\\\\\\ opubx"; auto simp: Let_def) qed (auto simp: Let_def) lemma norm_optim: assumes "optim n xs \1 = Ok (ys, \2)" shows "map norm_instr ys = map norm_instr xs" using assms unfolding optim_def by (induction xs arbitrary: \1 n ys \2; auto simp: norm_optim_instr) lemma sp_optim_instr: assumes "optim_instr n instr \1 = Ok (instr', \2)" and "\x. rel_option (\ar fd. arity fd = ar) (get_arity x) (F x) " shows "Subx.sp_instr F instr' \1 = Ok \2" using assms(1) apply (induction n instr \1 rule: optim_instr.induct; (auto simp: Let_def; fail)?) subgoal for _ d by (auto elim!: result_alternative.elims simp: try_unbox_def option.case_eq_if) subgoal for n by (cases "load_oracle n"; auto) subgoal for _ opinl \ apply (simp add: Let_def) apply (cases "\\\ opinl (take (\\\\\ (\
\\\\ opinl)) \)") apply (auto dest: list_all_eq_const_imp_replicate simp: case_prod_beta Let_def min.absorb2) subgoal for opubx using Subx.\\\\\\\\_\\\\\[of opubx] by (cases "\\\\\\\\ opubx"; auto simp: Subx.\\\_invertible dest: Subx.\\\_opubx_type) done subgoal for _ opubx by (cases "\\\\\\\\ opubx"; auto simp add: Let_def) subgoal for _ f using assms(2)[of f] by (auto simp: option_rel_Some1) done lemma sp_optim: assumes "optim n p1 \1 = Ok (p2, \2)" and "\x. rel_option (\ar fd. arity fd = ar) (get_arity x) (F x)" shows "Subx.sp F p2 \1 = Ok \2" using assms by (auto elim!: sp_rewrite_prog sp_optim_instr simp: optim_def) section \Compilation of function definition\ fun compile_fundef where "compile_fundef (Fundef instrs ar) = do { (xs, \\<^sub>1) \ lift 0 instrs (replicate ar None :: type option list); \ \Ensure that the function returns a single dynamic result\ () \ if \\<^sub>1 = [None] then Ok () else Error (); (ys, \\<^sub>2) \ optim 0 xs (replicate ar None); Ok (Fundef ( if \\<^sub>2 = [None] then ys \ \use optimization\ else xs \ \cancel optimization\ ) ar) }" lemma rel_compile_fundef: assumes "compile_fundef fd1 = Ok fd2" shows "rel_fundef norm_eq fd1 fd2" proof (cases fd1) case (Fundef xs ar) obtain ys zs \2 where lift_xs: "lift 0 xs (replicate ar None :: type option list) = Ok (ys, [None])" and optim_ys: "optim 0 ys (replicate ar None :: type option list) = Ok (zs, \2)" and check: "fd2 = Fundef (if \2 = [None] then zs else ys) ar" using assms unfolding Fundef unfolding compile_fundef.simps by (auto simp: Nitpick.case_unit_unfold if_then_else_distributive) then have "map norm_instr ys = xs" by (auto intro: norm_lift) show ?thesis proof (cases "\2 = [None]") case True then show ?thesis using True Fundef using check norm_lift[OF lift_xs, symmetric] using norm_optim[OF optim_ys, symmetric] by (simp add: list.rel_map list.rel_refl) next case False then show ?thesis using Fundef check norm_lift[OF lift_xs, symmetric] by (simp add: list.rel_map list.rel_refl) qed qed lemma sp_compile_fundef: assumes "compile_fundef fd1 = Ok fd2" and "\x. rel_option (\ar fd. arity fd = ar) (get_arity x) (F x)" shows "sp_fundef F fd2 (body fd2) = Ok [None]" proof (cases fd1) case (Fundef xs ar) with assms show ?thesis by (auto elim!: sp_optim sp_lift simp: sp_fundef_def Nitpick.case_unit_unfold if_eq_const_conv) qed end end locale inca_ubx_compiler = inca_to_ubx_simulation Finca_empty Finca_get for Finca_empty and Finca_get :: "_ \ 'fun \ _ option" + fixes load_oracle :: "'fun \ nat \ type option" begin section \Compilation of function environment\ definition compile_env_entry where "compile_env_entry \ \ x = map_result id (Pair (fst x)) (compile_fundef \ (\ (fst x)) (snd x))" lemma rel_compile_env_entry: assumes "compile_env_entry \ \ (f, fd1) = Ok (f, fd2)" shows "rel_fundef norm_eq fd1 fd2" using assms unfolding compile_env_entry_def using rel_compile_fundef by auto lemma sp_compile_env_entry: assumes "compile_env_entry \ \ (f, fd1) = Ok (f, fd2)" and "\x. rel_option (\ar fd. arity fd = ar) (\ x) (F x)" shows "sp_fundef F fd2 (body fd2) = Ok [None]" using assms unfolding compile_env_entry_def by (auto elim: sp_compile_fundef) definition compile_env where "compile_env \ \ e \ map_result id Subx.Fenv.from_list (Result.those (map (compile_env_entry \ \) (Finca_to_list e))) " lemma list_assoc_those_compile_env_entries: "Result.those (map (compile_env_entry \ \) xs) = Ok ys \ rel_option (\fd1 fd2. compile_fundef \ (\ f) fd1 = Ok fd2) (map_of xs f) (map_of ys f)" proof (induction xs arbitrary: ys) case Nil then show ?case by simp next case (Cons x xs) then obtain y ys' where xs_def: "ys = y # ys'" and comp_x: "compile_env_entry \ \ x = Ok y" and comp_xs: "Result.those (map (compile_env_entry \ \) xs) = Ok ys'" by auto obtain g fd1 fd2 where prods: "x = (g, fd1)" "y = (g, fd2)" using comp_x by (cases x) (auto simp: compile_env_entry_def) have "compile_fundef \ (\ g) fd1 = Ok fd2" using comp_x unfolding prods compile_env_entry_def by auto then show ?case unfolding prods xs_def using Cons.IH[OF comp_xs] by simp qed lemma compile_env_rel_compile_fundef: assumes "compile_env \ \ F1 = Ok F2" shows "rel_option (\fd1 fd2. compile_fundef \ (\ f) fd1 = Ok fd2) (Finca_get F1 f) (Fubx_get F2 f)" proof - obtain xs where those_xs: "Result.those (map (compile_env_entry \ \) (Finca_to_list F1)) = Ok xs" using assms[unfolded compile_env_def] by auto hence F2_def: "F2 = Subx.Fenv.from_list xs" using assms[unfolded compile_env_def] by simp show ?thesis proof (cases "map_of (Finca_to_list F1) f") case None then show ?thesis unfolding F2_def Subx.Fenv.from_list_correct[symmetric] unfolding Sinca.Fenv.to_list_correct[symmetric] using list_assoc_those_compile_env_entries[OF those_xs, of f] by simp next case (Some fd1) then show ?thesis unfolding F2_def Subx.Fenv.from_list_correct[symmetric] unfolding Sinca.Fenv.to_list_correct[symmetric] using list_assoc_those_compile_env_entries[OF those_xs, of f] by simp qed qed lemma rel_those_compile_env_entries: "Result.those (map (compile_env_entry \ \) xs) = Ok ys \ rel_fundefs (Finca_get (Sinca.Fenv.from_list xs)) (Fubx_get (Subx.Fenv.from_list ys))" proof (induction xs arbitrary: ys) case Nil then show ?case using rel_fundefs_empty by simp next case (Cons x xs) then obtain y ys' where xs_def: "ys = y # ys'" and comp_x: "compile_env_entry \ \ x = Ok y" and comp_xs: "Result.those (map (compile_env_entry \ \) xs) = Ok ys'" by auto obtain f fd1 fd2 where prods: "x = (f, fd1)" "y = (f, fd2)" using comp_x by (cases x) (auto simp: compile_env_entry_def) have "rel_fundef norm_eq fd1 fd2" using rel_compile_env_entry[OF comp_x[unfolded prods]] . then show ?case unfolding prods xs_def unfolding Sinca.Fenv.from_list_correct[symmetric] Subx.Fenv.from_list_correct[symmetric] unfolding rel_fundefs_def apply auto using Cons.IH[OF comp_xs] unfolding Sinca.Fenv.from_list_correct[symmetric] Subx.Fenv.from_list_correct[symmetric] by (simp add: rel_fundefs_def) qed lemma rel_fundefs_compile_env: assumes "compile_env \ \ e = Ok e'" shows "rel_fundefs (Finca_get e) (Fubx_get e')" proof - obtain xs where FOO: "Result.those (map (compile_env_entry \ \) (Finca_to_list e)) = Ok xs" and BAR: "e' = Subx.Fenv.from_list xs" using assms by (auto simp: compile_env_def) show ?thesis using rel_those_compile_env_entries[OF FOO] unfolding BAR unfolding Sinca.Fenv.get_from_list_to_list . qed lemma sp_fundefs_compile_env: assumes "compile_env \ \ F1 = Ok F2" and "\x. rel_option (\ar fd. arity fd = ar) (\ x) (Finca_get F1 x)" shows "sp_fundefs (Fubx_get F2)" unfolding sp_fundefs_def proof (intro allI impI) fix f fd2 assume F2_f: "Fubx_get F2 f = Some fd2" then obtain fd1 where "Finca_get F1 f = Some fd1" and compile_fd1: "compile_fundef \ (\ f) fd1 = Ok fd2" using compile_env_rel_compile_fundef[OF assms(1), of f] by (auto simp: option_rel_Some2) note rel_F1_F2 = rel_fundefs_compile_env[OF assms(1)] have "rel_option (\ar fd. arity fd = ar) (\ f) (Fubx_get F2 f)" for f using assms(2)[of f] proof (cases rule: option.rel_cases) case None then show ?thesis using rel_fundefs_None1[OF rel_F1_F2] by simp next case (Some ar fd1) then obtain fd2 where "Fubx_get F2 f = Some fd2" and rel_fd1_fd2: "rel_fundef (\x y. x = norm_instr y) fd1 fd2" using rel_fundefs_Some1[OF rel_F1_F2] by (meson rel_fundefs_Some1) with Some show ?thesis by (simp add: rel_fundef_arities) qed thus "sp_fundef (Fubx_get F2) fd2 (body fd2) = Ok [None]" by (auto intro!: sp_compile_fundef[OF compile_fd1]) qed section \Compilation of program\ fun compile where "compile (Prog F H f) = Result.to_option (do { F' \ compile_env (map_option arity \ Finca_get F) load_oracle F; Ok (Prog F' H f) })" lemma compile_load: assumes compile_p1: "compile p1 = Some p2" and load: "Subx.load p2 s2" shows "\s1. Sinca.load p1 s1 \ match s1 s2" proof - obtain F1 H main where p1_def: "p1 = Prog F1 H main" by (cases p1) simp then obtain F2 where compile_F1: "compile_env (map_option arity \ Finca_get F1) load_oracle F1 = Ok F2" and p2_def: "p2 = Prog F2 H main" using compile_p1 by auto note rel_F1_F2 = rel_fundefs_compile_env[OF compile_F1] show ?thesis using assms(2) unfolding p2_def proof (cases _ s2 rule: Subx.load.cases) case (1 fd2) let ?s1 = "State F1 H [Frame main 0 []]" from 1 obtain fd1 where Fstd_main: "Finca_get F1 main = Some fd1" and "compile_fundef (map_option arity \ Finca_get F1) (load_oracle main) fd1 = Ok fd2" using compile_env_rel_compile_fundef[OF compile_F1, of main] by (auto simp: option_rel_Some2) with 1 have "arity fd1 = 0" using fundef.rel_sel rel_compile_fundef by metis hence "Sinca.load p1 ?s1" unfolding p1_def by (auto intro!: Sinca.load.intros[OF Fstd_main]) moreover have "?s1 \ s2" proof - have "sp_fundefs (Fubx_get F2)" by (auto intro: sp_fundefs_compile_env[OF compile_F1] simp: option.rel_map option.rel_refl) moreover have "rel_stacktraces (Fubx_get F2) [Frame main 0 []] [Frame main 0 []] None" using 1 by (auto intro!: rel_stacktraces.intros simp: sp_fundef_def) ultimately show ?thesis unfolding 1 using rel_F1_F2 by (auto intro!: match.intros) qed ultimately show ?thesis by auto qed qed interpretation std_to_inca_compiler: compiler Sinca.step Subx.step Sinca.final Subx.final Sinca.load Subx.load "\_ _. False" "\_. match" compile using compile_load by unfold_locales auto end end