Theory VeriComp.Compiler
section ‹Compiler Between Static Representations›
theory Compiler
  imports Language Simulation
begin
definition option_comp :: "('a ⇒ 'b option) ⇒ ('c ⇒ 'a option) ⇒ 'c ⇒ 'b option" (infix ‹⇚› 50) where
  "(f ⇚ g) x ≡ Option.bind (g x) f"
context
  fixes f :: "('a ⇒ 'a option)"
begin
fun option_comp_pow :: "nat ⇒ 'a ⇒ 'a option" where
  "option_comp_pow 0 = (λ_. None)" |
  "option_comp_pow (Suc 0) = f" |
  "option_comp_pow (Suc n) = (option_comp_pow n ⇚ f)"
end
locale compiler =
  L1: language step1 final1 load1 +
  L2: language step2 final2 load2 +
  backward_simulation step1 final1 step2 final2 match order
  for
    step1 :: "'state1 ⇒ 'state1 ⇒ bool" and final1 and load1 :: "'prog1 ⇒ 'state1 ⇒ bool" and
    step2 :: "'state2 ⇒ 'state2 ⇒ bool" and final2 and load2 :: "'prog2 ⇒ 'state2 ⇒ bool" and
    match and
    order :: "'index ⇒ 'index ⇒ bool" +
  fixes
    compile :: "'prog1 ⇒ 'prog2 option"
  assumes
    compile_load:
      "compile p1 = Some p2 ⟹ load2 p2 s2 ⟹ ∃s1 i. load1 p1 s1 ∧ match i s1 s2"
begin
text ‹
The @{locale compiler} locale relates two languages, L1 and L2, by a backward simulation and provides a @{term compile} partial function from a concrete program in L1 to a concrete program in L2.
The only assumption is that a successful compilation results in a program which, when loaded, is equivalent to the loaded initial program.
›
subsection ‹Preservation of behaviour›
corollary behaviour_preservation:
  assumes
    compiles: "compile p1 = Some p2" and
    behaves: "L2.prog_behaves p2 b2" and
    not_wrong: "¬ is_wrong b2"
  shows "∃b1 i.  L1.prog_behaves p1 b1 ∧ rel_behaviour (match i) b1 b2"
proof -
  obtain s2 where "load2 p2 s2" and "L2.state_behaves s2 b2"
    using behaves L2.prog_behaves_def by auto
  obtain i s1 where "load1 p1 s1" "match i s1 s2"
    using compile_load[OF compiles ‹load2 p2 s2›]
    by auto
  then show ?thesis
    using simulation_behaviour[OF ‹L2.state_behaves s2 b2› not_wrong ‹match i s1 s2›]
    by (auto simp: L1.prog_behaves_def)
qed
end
subsection ‹Composition of compilers›
lemma compiler_composition:
  assumes
    "compiler step1 final1 load1 step2 final2 load2 match1 order1 compile1" and
    "compiler step2 final2 load2 step3 final3 load3 match2 order2 compile2"
  shows "compiler step1 final1 load1 step3 final3 load3
    (rel_comp match1 match2) (lex_prodp order1⇧+⇧+ order2) (compile2 ⇚ compile1)"
proof (rule compiler.intro) 
  show "language step1 final1"
    using assms(1)[THEN compiler.axioms(1)] .
next
  show "language step3 final3"
    using assms(2)[THEN compiler.axioms(2)] .
next
  show "backward_simulation step1 final1 step3 final3
     (rel_comp match1 match2) (lex_prodp order1⇧+⇧+ order2)"
    using backward_simulation_composition[OF assms[THEN compiler.axioms(3)]] .
next
  show "compiler_axioms load1 load3 (rel_comp match1 match2) (compile2 ⇚ compile1)"
  proof unfold_locales
    fix p1 p3 s3
    assume
      compile: "(compile2 ⇚ compile1) p1 = Some p3" and
      load: "load3 p3 s3"
    obtain p2 where c1: "compile1 p1 = Some p2" and c2: "compile2 p2 = Some p3"
      using compile by (auto simp: bind_eq_Some_conv option_comp_def)
    obtain s2 i' where l2: "load2 p2 s2" and "match2 i' s2 s3"
      using assms(2)[THEN compiler.compile_load, OF c2 load]
      by auto
    moreover obtain s1 i where "load1 p1 s1" and "match1 i s1 s2"
      using assms(1)[THEN compiler.compile_load, OF c1 l2]
      by auto
    ultimately show "∃s1 i. load1 p1 s1 ∧ rel_comp match1 match2 i s1 s3"
      unfolding rel_comp_def by auto
  qed
qed
lemma compiler_composition_pow:
  assumes
    "compiler step final load step final load match order compile"
  shows "compiler step final load step final load
    (rel_comp_pow match) (lexp order⇧+⇧+) (option_comp_pow compile n)"
proof (induction n rule: option_comp_pow.induct)
  case 1
  show ?case
    using assms
    by (auto intro: compiler.axioms compiler.intro compiler_axioms.intro backward_simulation_pow)
next
  case 2
  show ?case
  proof (rule compiler.intro)
    show "compiler_axioms load load (rel_comp_pow match) (option_comp_pow compile (Suc 0))"
    proof unfold_locales
      fix p1 p2 s2
      assume
        "option_comp_pow compile (Suc 0) p1 = Some p2" and
        "load p2 s2"
      thus "∃s1 i. load p1 s1 ∧ rel_comp_pow match i s1 s2"
        using compiler.compile_load[OF assms(1)]
        by (metis option_comp_pow.simps(2) rel_comp_pow.simps(2))
    qed
  qed (auto intro: assms compiler.axioms backward_simulation_pow)
next
  case (3 n')
  show ?case
  proof (rule compiler.intro)
    show "compiler_axioms load load (rel_comp_pow match) (option_comp_pow compile (Suc (Suc n')))"
    proof unfold_locales
      fix p1 p3 s3
      assume
        "option_comp_pow compile  (Suc (Suc n')) p1 = Some p3" and
        "load p3 s3"
      then obtain p2 where
        comp: "compile p1 = Some p2" and
        comp_IH: "option_comp_pow compile (Suc n') p2 = Some p3"
        by (auto simp: option_comp_def bind_eq_Some_conv)
      obtain s2 i' where "load p2 s2" and "rel_comp_pow match i' s2 s3"
        using compiler.compile_load[OF "3.IH" comp_IH ‹load p3 s3›]
        by auto
      moreover obtain s1 i where "load p1 s1" and "match i s1 s2"
        using compiler.compile_load[OF assms comp ‹load p2 s2›]
        by auto
      moreover have "rel_comp_pow match (i # i') s1 s3"
        using ‹rel_comp_pow match i' s2 s3› ‹match i s1 s2› rel_comp_pow.elims(2) by fastforce
      ultimately show "∃s1 i. load p1 s1 ∧ rel_comp_pow match i s1 s3"
        by blast
    qed
  qed (auto intro: assms compiler.axioms backward_simulation_pow)
qed
end