Module ProductSimulation


Require VCGen.
Require RTLperm.
Require AssocList.
Import Sylvie.
Import Utf8.
Import Coqlib Maps.
Import Smallstep.
Import MoreRTL.
Import AST Globalenvs Memory Integers Floats Values.
Import RTL.
Import RTLperm.
Import Extra AssocList.
Import Product ProductProof.
Import MoreRelations.
Import MoreSemantics.

Lemma eq_fst {A B} {p: A * B} {a b} (H: p = (a, b)) : a = fst p.
Proof.
now subst. Qed.

Definition not_None_ex {X} (o: option X) :
  oNone → ∃ x, o = Some x :=
  match o with
  | Some x => λ _, ex_intro _ x eq_refl
  | None => λ H, False_ind _ (H eq_refl)
  end.

Lemma list_norepet_rev {A} (m: list A) :
  list_norepet (rev m) → list_norepet m.
Proof.
  elim m; clear m. vauto.
  intros a m IH. simpl. intros K.
  apply list_norepet_app in K.
  destruct K as (K & _ & N).
  constructor.
  intros IN.
  refine (N a a _ (or_introl _ eq_refl) eq_refl).
  rewrite <- in_rev. exact IN.
  auto.
Qed.

Lemma last_cons {A} {a d: A} {m: list A} :
  last (a :: m) d = last m a.
Proof.
  revert d a. elim m; clear m.
  reflexivity.
  intros a m IH d a'.
  rewrite (IH a' a).
  exact (IH d a).
Qed.

Section FL.
Context (A B C: Type) (f: CABC)
        (eA: Clist AC) (eB: Clist BC).
Inductive FoldLeft2 : list Alist BCCProp :=
| FLnil c : FoldLeft2 nil nil c c
| FLeA ma c `(match ma with nil => False | _ => True end) : FoldLeft2 ma nil c (eA c ma)
| FLeB mb c `(match mb with nil => False | _ => True end) : FoldLeft2 nil mb c (eB c mb)
| FL a ma b mb c d `(FoldLeft2 ma mb (f c a b) d) :
    FoldLeft2 (a :: ma) (b :: mb) c d
.

Lemma fold_left2P ma mb acc :
  FoldLeft2 ma mb acc (Util.fold_left2 f eA eB ma mb acc).
Proof.
  revert mb acc.
  elim ma; clear ma.
  - intros [ | b mb ]; vauto.
  - intros a ma IH [ | b mb ]; vauto.
Qed.

End FL.

Definition init_data_dec (i i': init_data) : { i = i' } + { ii' }.
Proof.
  refine match i, i' return { i = i' } + { ii' } with
         | Init_int8 n, Init_int8 n' => match Int.eq_dec n n' with left EQ => left (f_equal Init_int8 EQ) | right NE => right _ end
         | Init_int16 n, Init_int16 n' => match Int.eq_dec n n' with left EQ => left (f_equal Init_int16 EQ) | right NE => right _ end
         | Init_int32 n, Init_int32 n' => match Int.eq_dec n n' with left EQ => left (f_equal Init_int32 EQ) | right NE => right _ end
         | Init_int64 n, Init_int64 n' => match Int64.eq_dec n n' with left EQ => left (f_equal Init_int64 EQ) | right NE => right _ end
         | Init_float32 n, Init_float32 n' => match Float32.eq_dec n n' with left EQ => left (f_equal Init_float32 EQ) | right NE => right _ end
         | Init_float64 n, Init_float64 n' => match Float.eq_dec n n' with left EQ => left (f_equal Init_float64 EQ) | right NE => right _ end
         | Init_space n, Init_space n' => match Z.eq_dec n n' with left EQ => left (f_equal Init_space EQ) | right NE => right _ end
         | Init_addrof s o, Init_addrof s' o' =>
           match ident_eq s s' with
           | right NEs => right _
           | left EQs => match Int.eq_dec o o' with left EQ => left (f_equal2 Init_addrof EQs EQ) | right NE => right _ end end
         | _, _ => right _
         end; try abstract congruence.
Defined.

Definition gvar_beq (v v': globvar unit) : bool :=
  let 'mkglobvar _ idl ro vol := v in
  let 'mkglobvar _ idl' ro' vol' := v' in
  if eqb ro ro'
  then if eqb vol vol'
       then if list_eq_dec init_data_dec idl idl'
            then true
            else false
       else false
  else false.

Lemma gvarP v v' : reflect (v = v') (gvar_beq v v').
Proof.
  destruct v as [() idl ro vol].
  destruct v' as [() idl' ro' vol'].
  simpl.
  generalize (eqb_true_iff ro ro'). case (eqb ro ro').
  intros [ RO _ ]. specialize (RO eq_refl). subst ro'.
  generalize (eqb_true_iff vol vol'). case (eqb vol vol').
  intros [ VOL _ ]. specialize (VOL eq_refl). subst vol'.
  case list_eq_dec. intros ->. left. reflexivity.
  intros NE. right; congruence.
  intros NE. right. intros K. apply false_not_true, NE. congruence.
  intros NE. right. intros K. apply false_not_true, NE. congruence.
Qed.

Definition valid_pointer_of_permission (pe: perm_t) : blockZbool :=
  λ b o, Mem.perm_order'_dec (pe b o Cur) Nonempty.

Lemma valid_pointer_of_permission_ext pe m :
  pe =3 mem_perm m
  ∀ b o, valid_pointer_of_permission pe b o = Mem.valid_pointer m b o.
Proof.
  intros E b o.
  unfold valid_pointer_of_permission.
  rewrite E. reflexivity.
Qed.

Section REGISTER.

Context (reg: Type).
Context (reg_dec: EqDec reg).
Context (left right: Registers.regreg).
Context (ger: regRegisters.reg + Registers.reg + unit).

Hypothesis ger_left : ∀ x, ger (left x) = inl (inl x).
Hypothesis ger_right : ∀ x, ger (right x) = inl (inr x).

Remark left_injective x y :
  left x = left yx = y.
Proof.
  intros EQ.
  generalize (ger_left y). rewrite <- EQ, (ger_left x).
  intros K. eapply inl_eq_inv, inl_eq_inv, K.
Qed.

Remark right_injective x y :
  right x = right yx = y.
Proof.
  intros EQ.
  generalize (ger_right y). rewrite <- EQ, (ger_right x).
  intros K. eapply inr_eq_inv, inl_eq_inv, K.
Qed.

Remark left_not_right x y :
  left xright y.
Proof.
  intros EQ.
  generalize (ger_left x). rewrite EQ, ger_right. intros; eq_some_inv.
Qed.

  Section SKIP_FAKE_BRANCHES.

    Context (funL funR: RTL.function) (funP: Sylvie.function reg).
    Context (ppmap: PPTree.t (list node)) (deco: hashmap (decoration reg)).
    Context (cps: code_product_spec reg left right funL funR funP ppmap deco).

    Definition is_fake (pc: pc_triple) : Prop :=
      ∃ pc', PPPTree.get pc (cut_points cps) = Some (FB pc').

    Variable pc: RTL.node * RTL.node.

    Remark skip_fake_branches'_decreases {π} π' :
      code_product_branch_witness_denote
        reg left right (fn_code funL) (fn_code funR) (Sylvie.fn_code funP) ppmap deco
        (pc, π) (FB π') →
      Plt (snd π') π.
Proof.
      destruct pc as (pcL, pcR). destruct π' as ((pcL', pcR'), π').
      intros ( -> & -> & _ & _ & K). exact K.
    Qed.

    Definition get_fake (π: Sylvie.node) : { π' | PPPTree.get (pc, π) (cut_points cps) = Some (FB π') ∧ fake_branch reg (Sylvie.fn_code funP) ppmap (pc, π) π' } + { ¬ is_fake (pc, π) }.
Proof.
      refine (
      match PPPTree.get (pc, π) (cut_points cps) as x
      return (∀ w, x = Some wcode_product_branch_witness_denote _ _ _ _ _ _ _ _ (pc, π) w) → { π' | x = Some (FB π') ∧ fake_branch reg _ ppmap (pc, π) π' } + { ¬ ∃ pc', x = Some (FB pc') }
      with
      | Some (FB π') => λ K, inleft (exist _ π' (conj eq_refl (K (FB π') eq_refl)))
      | Some _ => λ _, inrightK, let 'ex_intro pc' EQ := K return False in _ (some_eq_inv EQ))
      | None => λ _, inrightK, let 'ex_intro pc' EQ := K in None_not_Some EQ False)
      end (code_product_spec_at cps (pc, π)));
      clear; abstract discriminate.
    Defined.

    Fixpoint skip_fake_branches' (jumps: list node) (π: node) (H: Acc Plt π) {struct H} : node * list node :=
      let 'Acc_intro H' := H in
      match get_fake π with
      | inleft Fake =>
        let 'exist π' (conj Hw Hfake) := Fake in
        skip_fake_branches' (π :: jumps) (snd π') (H' _ (skip_fake_branches'_decreases π' Hfake))
      | inright _ => (π, jumps)
      end.

    Definition skip_fake_branches π : node * list node := skip_fake_branches' nil π (Plt_wf π).

    Fixpoint skip_fake_branches'_not_fake jumps π (H: Acc Plt π) {struct H} :
      ¬ is_fake (fst pc, snd pc, fst (skip_fake_branches' jumps π H)).
Proof.
      destruct H as [H']. simpl.
      case get_fake.
      - intros (π' & Hw & Hfake).
        apply skip_fake_branches'_not_fake.
      - intros X. rewrite <- surjective_pairing. exact X.
    Qed.

    Corollary skip_fake_branches_not_fake π :
      ¬ is_fake (fst pc, snd pc, fst (skip_fake_branches π)).
Proof.
apply skip_fake_branches'_not_fake. Qed.

    Fixpoint goto_sequence (tgt: node) (seq: list node) : Prop :=
      match seq with
      | nil => True
      | src :: seq' =>
        (Sylvie.fn_code funP) ! src = Some (Egoto tgt) ∧ goto_sequence src seq'
      end.

    Fixpoint skip_fake_branches'_goto_sequence jumps π H {struct H} :
      let '(π', seq) := skip_fake_branches' jumps π H in
      goto_sequence π jumps
      goto_sequence π' seq.
Proof.
      destruct H as [H']. simpl.
      case get_fake.
      - intros (π' & Hw & Hfake).
          generalize (skip_fake_branches'_goto_sequence (π :: jumps) _ (H' _ (skip_fake_branches'_decreases π' Hfake))).
          case skip_fake_branches'. simpl.
          intros n gs H K. apply H. split. 2: exact K.
          destruct pc as (pcL, pcR). destruct π' as ((?, ?), π').
          destruct Hfake as ( -> & -> & _ & Hgoto & _).
          exact Hgoto.
      - intros _; exact id.
    Qed.

    Corollary skip_fake_branches_goto_sequence π :
      let '(π', seq) := skip_fake_branches π in
      goto_sequence π' seq.
Proof.
      generalize (skip_fake_branches'_goto_sequence nil π (Plt_wf π)).
      unfold skip_fake_branches. simpl.
      case skip_fake_branches'. auto.
    Qed.

    Fixpoint skip_fake_branches'_start jumps π H {struct H} :
      let '(π', seq) := skip_fake_branches' jumps π H in
      (seq = niljumps = nil) ∧ last seq π' = last jumps π.
Proof.
      destruct H as [H']. simpl.
      case get_fake.
      - intros (π' & Hw & Hfake).
          generalize (skip_fake_branches'_start (π :: jumps) _ (H' _ (skip_fake_branches'_decreases π' Hfake))).
          case skip_fake_branches'. intros π'' seq.
          intros [ Hseq Hlast ].
          rewrite last_cons in Hlast.
          assert (∀ P : Prop, seq = nilP) as Hseq'.
          { intros P H. specialize (Hseq H). eq_some_inv. }
          clear Hseq.
          split. intros H; apply Hseq', H.
          rewrite <- Hlast.
          destruct seq. apply Hseq', eq_refl.
          rewrite ! last_cons.
          reflexivity.
      - intros _. split. exact id. reflexivity.
    Qed.

    Corollary skip_fake_branches_start π :
      let '(π', seq) := skip_fake_branches π in
      last seq π' = π.
Proof.
      generalize (skip_fake_branches'_start nil π (Plt_wf π)).
      unfold skip_fake_branches.
      case skip_fake_branches'. simpl. intuition.
    Qed.

    Fixpoint skip_fake_branches'_cut_points jumps π (H: Acc Plt π) {struct H} :
      (fst pc, snd pc, π) ∈ keys (cut_points cps) →
      (fst pc, snd pc, fst (skip_fake_branches' jumps π H)) ∈ keys (cut_points cps).
Proof.
      destruct H as [H']. simpl.
      case get_fake.
      - intros (π' & Hw & Hfake) K K'.
        specialize (cut_points_stable cps _ _ Hw _ eq_refl).
        assert (π' = (pc, snd π')) as X.
        clear - Hfake.
        destruct pc as (pcL, pcR). destruct π' as ((pcL', pcR'), π').
        destruct Hfake as ( <- & <- & Hppmap & Hgoto & LT ).
        reflexivity.
        rewrite X, (surjective_pairing pc). intros K''.
        generalize (skip_fake_branches'_cut_points (π :: jumps) (snd π') (H' _ (skip_fake_branches'_decreases _ Hfake)) K'').
        intros Y.
        exact (Y K').
      - intros _. rewrite <- surjective_pairing. exact id.
    Qed.

    Corollary skip_fake_branches_cut_points π :
      (fst pc, snd pc, π) ∈ keys (cut_points cps) →
      (fst pc, snd pc, fst (skip_fake_branches π)) ∈ keys (cut_points cps).
Proof.
apply skip_fake_branches'_cut_points. Qed.

  End SKIP_FAKE_BRANCHES.

  Arguments is_fake {_ _ _ _ _ } cps pc.
  Arguments skip_fake_branches {_ _ _ _ _} cps pc π.
  Arguments skip_fake_branches_goto_sequence {_ _ _ _ _} cps pc π.
  Arguments skip_fake_branches_start {_ _ _ _ _} cps pc π.
  Arguments skip_fake_branches_cut_points {_ _ _ _ _} cps pc π _ _.
  Arguments skip_fake_branches_not_fake {_ _ _ _ _} _ _ _ _.

Section RELATION.

  Context (find_symbol: identoption block).

  Definition pred f : Sylvie.hashmap _ := Kildall.make_predecessors f RTL.successors_instr.

  Definition match_pc idx funL funR funP ppmap deco cps (pc pc' π: node) : Prop :=
      (pc, pc', π) ∈ keys (@cut_points reg left right funL funR funP ppmap deco cps) ∧
      get_height (cut_point_heights cps) (pc, pc', π) = idx.

  Definition match_function (funP: Sylvie.function reg) ppmap (deco: hashmap _) epa (fn fn': RTL.function)
    (cps: ProductProof.code_product_spec reg left right fn fn' funP ppmap deco)
    : Prop :=
    list_norepet (fn_params fn) ∧
    list_norepet (fn_params fn') ∧
    fn_stacksize fn = fn_stacksize fn' ∧
    ep_annot left right (fn_params fn) (fn_params fn') = Errors.OK epa
    ∀ sp pe,
    well_annotated_function reg_dec find_symbol sp
                            (valid_pointer_of_permission pe) funP
                            (get_assertions deco)
                            (pre_of_assertion_list _ find_symbol sp (valid_pointer_of_permission pe) epa)
  .

  Definition match_fundef (f f': fundef) : Prop :=
    funsig f = funsig f' ∧
    match f, f' with
    | Internal fn, Internal fn' =>
      ∃ prod ppmap deco epa cps, match_function prod ppmap deco epa fn fn' cps
    | External ef, External ef' => ef = ef'
    | _, _ => False
    end.

  Definition match_sp (sp sp': Values.val) : Prop :=
    sp = sp'.

  Definition joint_regset (rs rs': regset) (x: reg) : Values.val :=
    match ger x with
    | inl (inl y) => PMap.get y rs
    | inl (inr y) => PMap.get y rs'
    | inr tt => Vundef
    end.

  Definition regset_agree (ρ: env reg) (ι: Registers.regreg) (rs: regset) : Prop :=
    ∀ r, rs !! r = ρ (ι r).

  Lemma regset_agree_map {ρ ι rs} :
    regset_agree ρ ι rs
    ∀ m, mapr, rs !! r) m = map ρ (map ι m).
Proof.
    intros E.
    intros m; elim m; clear m.
    reflexivity.
    intros x m IH. simpl. f_equal. apply E. apply IH.
  Qed.

  Definition match_regset (sp: Values.val) epa (pe: perm_t) (fn: Sylvie.function reg) (π: node) (rs rs': regset) : Prop :=
  ∃ ρ : env reg,
    Sylvie.reachable reg_dec find_symbol sp (valid_pointer_of_permission pe) fn
                     (pre_of_assertion_list _ find_symbol sp (valid_pointer_of_permission pe) epa)
                     (Run (Sylvie.State π ρ))
    ∧ regset_agree ρ left rs
    ∧ regset_agree ρ right rs'
.

  Definition match_stackframe (s: RTLperm.stackframe) (s': RTL.stackframe) : Prop :=
    let 'RTLperm.SF pe (RTL.Stackframe r f sp pc rs) := s in
    let 'RTL.Stackframe r' f' sp' pc' rs' := s' in
    match_sp sp sp' ∧
    ∃ funP ppmap deco epa cps,
      match_function funP ppmap deco epa f f' cps
      ∃ pc'' π' π'',
        right_branch reg right (RTL.fn_code f') (Sylvie.fn_code funP) ((pc, pc''), π'') ((pc, pc'), π') ∧
        (pc, pc'', π'') ∈ keys (cut_points cps) ∧
        ∀ v,
          match_regset sp epa pe funP π' (Registers.Regmap.set r v rs) (Registers.Regmap.set r' v rs').

  Inductive match_stack : list RTLperm.stackframelist RTL.stackframeProp :=
  | MS_nil : match_stack nil nil
  | MS_push σ σ'
            `(match_stack σ σ')
            s s'
            `(match_stackframe s s')
    : match_stack (s :: σ) (s' :: σ')
  .

  Lemma match_stack_cons x stk stk' :
    match_stack (x :: stk) stk' →
    ∃ y stk'',
      stk' = y :: stk'' ∧
      match_stack stk stk'' ∧
      match_stackframe x y.
Proof.
    intros H.
    seta stk' := match a with nil => Logic.True | x :: stk =>
    ∃ y stk'',
      stk' = y :: stk'' ∧
      match_stack stk stk'' ∧
      match_stackframe x y end).
    change (Δ (x :: stk) stk').
    case H; vauto.
  Qed.

  Definition match_mem (m m': mem) : Prop :=
    m = m'.

  Definition match_args (args args': list val) : Prop :=
    Forall2a a', match a, a' with Vundef, _ | _, Vundef => Logic.True | _, _ => a = a' end)
            args args'.

  Definition match_state p (idx: nat) (x: RTLperm.state) (x': RTL.state) : Prop :=
    reachable (RTLperm.semantics p) x
    match x with
    | RTLperm.State σ pe fn sp pc rs m =>
      match x' with
      | RTL.State σ' fn' sp' pc' rs' m' =>
        match_stack σ σ' ∧
        match_sp sp sp' ∧
        match_mem m m' ∧
        ∃ prod ppmap deco epa cps,
          match_function prod ppmap deco epa fn fn' cps
          ∃ π, match_pc idx fn fn' prod ppmap deco cps pc pc' π ∧
               match_regset sp epa pe prod π rs rs' ∧
               ¬ is_fake cps (pc, pc', π)
      | _ => False
      end
    | RTLperm.Callstate σ fd args m =>
      match x' with
      | RTL.Callstate σ' fd' args' m' =>
        match_stack σ σ' ∧
        match_fundef fd fd' ∧
        args = args' ∧
        match_mem m m'
      | _ => False
      end
    | RTLperm.Returnstate σ v m =>
      match x' with
      | RTL.Returnstate σ' v' m' =>
        match_stack σ σ' ∧
        v = v' ∧
        match_mem m m'
      | _ => False
      end
    end.

End RELATION.

Unset Elimination Schemes.

Section Main.
  Import String.
  Import Errors.
  Local Open Scope error_monad_scope.

  Context (time : ∀ {A B: Type} (name: string) (f: AB), AB).
  Arguments time {A B} _ _ _.
  Hypothesis time_id : ∀ A B n (f: AB) a, time n f a = f a.

  Context (reg_max: regregreg).
  Context (reg_succ: regreg).
  Context (reg_le: regregProp).
  Hypothesis reg_le_refl : ∀ x, reg_le x x.
  Hypothesis reg_le_trans : ∀ x y z, reg_le x yreg_le y zreg_le x z.
  Hypothesis reg_max_le : ∀ x y z, reg_le z xreg_le z yreg_le z (reg_max x y).
  Hypothesis reg_succ_le_ne : ∀ x y, reg_le x yxreg_succ y.

  Context (progL progR : RTL.program).

  Context (fuel: nat).

  Let geL := Genv.globalenv progL.
  Let geR := Genv.globalenv progR.

  Let find_symbol: identoption block := Senv.find_symbol geL.

Definition build (f1 f2: RTL.function) : Errors.res ({ pmd : _ & code_product_spec _ left right f1 f2 (fst (fst pmd)) (snd (fst pmd)) (snd pmd) } * _) :=
  let '(rel, pred1, pred2) := Rel.guess f1 f2 in
  do st <-
     time "Product generation"%string
     (mk_prod left right (RTL.fn_code f1) (RTL.fn_code f2) pred1 pred2
              (RTL.fn_entrypoint f1) (RTL.fn_entrypoint f2)
              init_state)
     fuel;
  do st <- add_invariants left right rel st;
  let f := Sylvie.Function st.(Product.code) xH in
  do cps <-
     time "Product validation"%string
     (validate _ _ left right f1 f2 f st.(ppmap) st.(deco) st.(hints))
     fuel;
  do epa <- ep_annot left right (RTL.fn_params f1) (RTL.fn_params f2);
  do vc <-
     time "VC generation"%string
       (VCGen.doit _ _ reg_max reg_succ st.(Product.code) st.(deco) epa)
       fuel;
  if vc
  then OK (existT _ (f, st.(ppmap), st.(deco)) cps, epa)
  else Error (msg "Cannot prove all VC")
.

Lemma build_correct f1 f2 funP ppmap deco cps epa :
  build f1 f2 = OK (existT _ (funP, ppmap, deco) cps, epa) →
  ep_annot left right (RTL.fn_params f1) (RTL.fn_params f2) = Errors.OK epa
  ∀ sp pe,
    Sylvie.well_annotated_function
      _ find_symbol sp pe funP (get_assertions deco)
      (pre_of_assertion_list _ find_symbol sp pe epa).
Proof.
  unfold build.
  destruct (Rel.guess _ _) as ((rel, pred1), pred2).
  rewrite time_id. intros H.
  apply bind_inversion in H. destruct H as (st & Hst & H).
  apply bind_inversion in H. destruct H as (st' & Hst' & H).
  rewrite time_id in H.
  apply bind_inversion in H. destruct H as (cps' & _ & H).
  apply bind_inversion in H. destruct H as (epa' & Hepa & H).
  rewrite time_id in H.
  apply bind_inversion in H. destruct H as (vcs & Hvcs & H).
  destruct vcs; inv H.
  split. exact Hepa.
  eapply VCGen.correct, Hvcs; eauto.
Qed.

  Context (print_product : identSylvie.function reghashmap (decoration reg) → bool).

  Definition match_fundef_b (i: ident) (fd fd': fundef) : res unit :=
    do _ <- if eq_dec (funsig fd) (funsig fd') then OK tt else Error (msg "funsig mismatch");
    match fd, fd' with
    | Internal fn, Internal fn' =>
      do _ <- if list_norepet_dec eq_dec (fn_params fn) then OK tt else Error (msg "repet in params");
      do _ <- if list_norepet_dec eq_dec (fn_params fn') then OK tt else Error (msg "repet in params'");
      do _ <- if eq_dec (fn_stacksize fn) (fn_stacksize fn') then OK tt else Error (msg "stacksize mismatch");
      do p <- build fn fn';
      let '(existT (f, _, d) _, _) := p in
      if print_product i f d then OK tt else Error (msg "printer should return true")
    | External ef, External ef' => if eq_dec ef ef' then OK tt else Error (msg "external function mismatch")
    | _, _ => Error (msg "fundef mismatch")
    end.

  Lemma match_fundef_b_true i fd fd' :
    match_fundef_b i fd fd' = OK tt
    match_fundef find_symbol fd fd'.
Proof.
    unfold match_fundef, match_fundef_b.
    case eq_dec. 2: discriminate.
    intros Hsig H. split. exact Hsig.
    destruct fd as [ fn | ef ], fd' as [ fn' | ef' ]; try discriminate;
    revert H.
    - case list_norepet_dec. 2: discriminate. intro Hparams.
      case list_norepet_dec. 2: discriminate. intro Hparams'.
      case eq_dec. 2: discriminate. intros Hstack.
      generalize (build_correct fn fn').
      case build.
      2: discriminate.
      intros ((((prod, ppmap), deco) & cps) & epa) H _.
      specialize (H _ _ _ _ _ eq_refl). destruct H as (Hepa & WAF).
      exists prod, ppmap, deco, epa, cps. vauto.
    - case eq_dec; easy.
  Qed.

  Definition match_globdef (gd gd': ident * globdef fundef unit) : Prop :=
    let '(i, gd) := gd in
    let '(i', gd') := gd' in
    i = i' ∧
    match gd with
    | Gfun fn =>
      match gd' with
      | Gfun fn' => match_fundef find_symbol fn fn'
      | Gvar _ => False
      end
    | Gvar v => gd' = Gvar v
    end.

  Definition match_globdef_b (gd gd': ident * globdef fundef unit) : res unit :=
    let '(i, gd) := gd in
    let '(i', gd') := gd' in
    if ident_eq i i'
    then
      match gd, gd' with
      | Gfun fn, Gfun fn' => match_fundef_b i fn fn'
      | Gvar v, Gvar v' =>
        if gvar_beq v v' then OK tt else Error (msg "gvar mismatch")
      | _, _ => Error (msg "globdef mismatch")
      end
    else Error (msg "identifier mismatch in globdef").

  Definition match_globdef_b_true (gd gd': ident * globdef fundef unit) :
    match_globdef_b gd gd' = OK tt
    match_globdef gd gd'.
Proof.
    destruct gd as (i, gd).
    destruct gd' as (i', gd').
    simpl. case ident_eq. 2: discriminate. intros -> H.
    split. reflexivity.
    destruct gd as [ f | gv ]; destruct gd' as [ f' | gv' ]; try discriminate.
    exact (match_fundef_b_true i' f f' H).
    f_equal.
    revert H.
    generalize (reflect_iff _ _ (gvarP gv gv')).
    case gvar_beq. intuition. discriminate.
  Qed.

  Definition forall2_res' {A B} (f: ABres unit) (m: list A) (n: list B) : res unitres unit :=
    fold_left2r a b, do _ <- r; f a b)
               (λ _ _, Error (msg "list length mismatch"))
               (λ _ _, Error (msg "list length mismatch"))
               m n.

  Lemma forall2_res'_forall {A B} (f: ABres unit) m n i :
    match forall2_res' f m n i with
    | Error _ => True
    | OK _ => i = OK ttForall2a b, f a b = OK tt) m n
    end.
Proof.
    unfold forall2_res'. elim fold_left2P.
    - intros (). intros (). vauto. vauto.
    - vauto. - vauto.
    - intros a ma b mb c [ u | e ]. 2: vauto.
      case c. 2: intuition discriminate.
      simpl. intros () _ [ Hab IH ]. vauto.
  Qed.

  Definition forall2_res {A B} (f: ABres unit) (m: list A) (n: list B) : res unit :=
    forall2_res' f m n (OK tt).

  Lemma forall2_res_forall {A B} f (m: list A) (n: list B) u :
    forall2_res f m n = OK u
    Forall2a b, f a b = OK tt) m n.
Proof.
    unfold forall2_res.
    intros H. generalize (forall2_res'_forall f m n (OK tt)). rewrite H. intros (). auto.
  Qed.

  Definition validate : res unit :=
    do _ <- if Pos.eqb (prog_main progR) (prog_main progL) then OK tt else Error (msg "main functions differ");
    do _ <- if proj_sumbool (list_eq_dec ident_eq (Genv.genv_public geR) (Genv.genv_public geL))
                            then OK tt else Error (msg "public symbols differ");
    do _ <- forall2_res match_globdef_b (prog_defs progL) (prog_defs progR);
    do _ <- if PTree.beq ident_eq (Genv.genv_symb geL) (Genv.genv_symb geR) then OK tt else Error (msg "symbols differ");
    if list_norepet_dec ident_eq (rev_map fst (prog_defs progL)) then OK tt else Error (msg "repet in prog defs").

  Hypothesis validated : validate = OK tt.

  Record hypotheses_t : Prop := {
      same_main: prog_main progR = prog_main progL;
      same_genv_public : Genv.genv_public geR = Genv.genv_public geL;
      same_prog_defs : Forall2a b, match_globdef_b a b = OK tt) (prog_defs progL) (prog_defs progR);
      same_find_symbol_L : ∀ s, Senv.find_symbol geL s = find_symbol s;
      same_find_symbol_R : ∀ s, Senv.find_symbol geR s = find_symbol s;
      defs_norepet_L : list_norepet (map fst (prog_defs progL));
      defs_norepet_R : list_norepet (map fst (prog_defs progR))
                               }.

  Lemma hypotheses : hypotheses_t.
Proof.
    generalize validated.
    unfold validate. clear.
    case Pos.eqb_spec. 2: discriminate. intros same_main.
    case list_eq_dec. 2: discriminate. intros same_genv_public.
    simpl.
    destruct (forall2_res _ _ _) eqn: same_prog_defs. 2: discriminate.
    apply forall2_res_forall in same_prog_defs.
    destruct (PTree.beq _ _ _) eqn: SFS. 2: discriminate.
    rewrite PTree.beq_correct in SFS.
    case list_norepet_dec; simpl. 2: discriminate.
    intros defs_norepet_L _.
    rewrite rev_map_correct in defs_norepet_L.
    apply list_norepet_rev in defs_norepet_L.
    constructor; try assumption.
    - reflexivity.
    - intros s. generalize (SFS s). clear.
      unfold find_symbol. simpl. unfold Genv.find_symbol. unfold block.
      case ((Genv.genv_symb geL) ! _); case ((Genv.genv_symb geR) ! _).
      intros b b'. case ident_eq. intros -> _; reflexivity. simpl; intros; eq_some_inv.
      intros ? ().
      intros ? ().
      reflexivity.
    - revert defs_norepet_L.
      clear - same_prog_defs.
      elim same_prog_defs. vauto.
      intros (i, gd) (i', gd') defs defs'.
      simpl. case ident_eq. 2: discriminate.
      intros <- _ IH IH' NR.
      apply list_norepet_cons in NR. destruct NR as (NI & NR).
      constructor; auto.
      intros IN. elim NI. revert IN NR.
      clear -IH. elim IH; clear. exactH _, H).
      intros (j, gd) (j', gd') defs defs'.
      simpl. case ident_eq. 2: discriminate.
      intros <- _ IH NI.
      intros [ EQ | IN ] NR. left; exact EQ.
      right. apply NI. exact IN. apply list_norepet_cons in NR. apply NR.
  Qed.

  Global Opaque validate.

  Lemma same_find_symbol :
   ∀ s, Senv.find_symbol geR s = Senv.find_symbol geL s.
Proof.
    intros s.
    rewrite same_find_symbol_L by apply hypotheses.
    apply same_find_symbol_R, hypotheses.
  Qed.

  Lemma same_public_symbols :
    ∀ s, Senv.public_symbol geR s = Senv.public_symbol geL s.
Proof.
    intros s. unfold Genv.to_senv. simpl.
    unfold Genv.public_symbol.
    generalize (same_find_symbol s). simpl. intros ->.
    case (Genv.find_symbol). 2: reflexivity.
    intros _.
    rewrite (same_genv_public hypotheses). reflexivity.
  Qed.

  Lemma match_genvs_is_implied:
    Genv.match_genvs (match_fundef find_symbol) (@eq _) nil geL geR.
Proof.
    eapply Genv.add_globals_match.
    - elim (same_prog_defs hypotheses).
      vauto.
      intros x y m m' H _ IH.
      constructor. 2: exact IH.
      generalize (match_globdef_b_true _ _ H). clear H IH.
      destruct x as (x, gvx), y as (y, gvy). intros (); intros ->.
      destruct gvx as [ f | v ]. destruct gvy. vauto. intros ().
      intros ->. destruct v; vauto.
    - constructor; simpl; auto; intros; rewrite PTree.gempty in H; try congruence.
  Qed.

  Lemma same_funs :
    ∀ s fd,
      (Genv.genv_funs geL) ! s = Some fd
      ∃ fd',
        (Genv.genv_funs geR) ! s = Some fd' ∧
        match_fundef find_symbol fd fd'.
Proof.
    eapply Genv.mge_funs, match_genvs_is_implied.
  Qed.

  Lemma same_volatiles b:
    Genv.block_is_volatile geR b = Genv.block_is_volatile geL b.
Proof.
    intros. generalize match_genvs_is_implied. intros XA.
    unfold Genv.block_is_volatile, Genv.find_var_info.
    case_eq ((Genv.genv_vars geL) ! b); intros.
    - inv XA. exploit mge_vars; try (eapply H).
      intros [tv [XB XC]]. inv XC; rewrite XB; eauto.
    - case_eq ((Genv.genv_vars geR) ! b); intros.
      + inv XA. exploit mge_rev_vars; try (eapply H0).
        simpl. destruct (plt b (Genv.genv_next geL)); intros; try tauto.
        destruct H1 as [v [XB XC]]; try congruence.
      + auto.
  Qed.

  Lemma symbol_preserved_L :
    ∀ s o, Genv.symbol_address geL s o = Sexpr.symbol_address find_symbol s o.
Proof.
    intros s o.
    unfold Genv.symbol_address, Sexpr.symbol_address.
    generalize (same_find_symbol_L hypotheses s).
    simpl. intros ->. reflexivity.
  Qed.

  Lemma symbol_preserved_R :
    ∀ s o, Genv.symbol_address geR s o = Sexpr.symbol_address find_symbol s o.
Proof.
    intros s o.
    unfold Genv.symbol_address, Sexpr.symbol_address.
    generalize (same_find_symbol_R hypotheses s).
    simpl. intros ->. reflexivity.
  Qed.

  Ltac precious i H :=
    assert (H := exist _ i eq_refl).

  Ltac mapeq :=
    repeat match goal with
           | H : _ ! _ = Some _, K : _ ! _ = Some _ |- _ =>
             rewrite H in K; eq_some_inv
           end.

  Lemma match_regset_egoto sp epa pe prod pc rs rs' pc' :
    match_regset find_symbol sp epa pe prod pc rs rs' →
    (Sylvie.fn_code prod) ! pc = Some (Sylvie.Egoto pc') →
    match_regset find_symbol sp epa pe prod pc' rs rs'.
Proof.
    intros (ρ & REACH & Hreg) GOTO.
    exists ρ. split. 2: exact Hreg.
    eapply Sylvie.reachable_step. exact REACH.
    simpl. rewrite GOTO. eexists. split. apply Sylvie.eq_env_equiv.
    reflexivity.
  Qed.

  Lemma match_regset_goto_sequence sp epa pe prod seq rs rs' tgt :
    match_regset find_symbol sp epa pe prod (last seq tgt) rs rs' →
    goto_sequence prod tgt seq
    match_regset find_symbol sp epa pe prod tgt rs rs'.
Proof.
    revert tgt.
    elim seq; clear seq.
    simpl. easy.
    intros src seq IH tgt. rewrite last_cons.
    intros MR [Hgoto GS].
    eapply match_regset_egoto.
    apply IH; eauto.
    exact Hgoto.
  Qed.

  Lemma match_eval_addressing (geX: Genv.t fundef unit) sp rs addr args v :
    (∀ s o, Genv.symbol_address geX s o = Sexpr.symbol_address find_symbol s o) →
    ∀ ρ ι,
      regset_agree ρ ι rs
      ∀ pe,
        Op.eval_addressing geX sp addr (mapr, rs !! r) args) = Some v
        Sexpr.eval_sexpr find_symbol sp pe ρ (Sexpr.Oper (Op.Olea addr) (map (Sexpr.Reg ∘ ι) args)) = Some v.
Proof.
    intros symbol_preserved ρ ι Hρ pe.
    simpl.
    replace (map_o (Sexpr.eval_sexpr find_symbol sp pe ρ) (map (Sexpr.Reg ∘ ι) args))
            with (Some (mapr, rs !! r) args)).
    destruct addr;
    destruct args as [ | α args ]; try (split; exact id);
    try (destruct args as [ | β args ]; try (split; exact id));
    simpl; try rewrite symbol_preserved;
    split; exact id.
    elim args; clear args. reflexivity.
    intros a args IH. simpl. rewrite <- IH. simpl. f_equal. f_equal. apply Hρ.
  Qed.

  Arguments match_eval_addressing [_ _ _ _ _ _] _ [ρ ι] _ _.

  Lemma match_eval_condition rs m cond args b :
    ∀ ρ ι,
      regset_agree ρ ι rs
      Op.eval_condition cond (mapr, rs !! r) args) m = Some b
      Sexpr.eval_condition (Mem.valid_pointer m) cond (map ρ (map ι args)) = Some b.
Proof.
    intros ρ ι Hρ.
    destruct args as [ | α args ]. split; exact id.
    simpl in *. rewrite (Hρ α).
    destruct args as [ | β args ]. split; exact id.
    simpl in *. rewrite (Hρ β).
    destruct args as [ | γ args ]; split; exact id.
  Qed.

  Arguments match_eval_condition [_ _ _ _ _ ρ ι] _.

  Lemma match_eval_operation (geX: Genv.t fundef unit) sp rs m op args v :
    (∀ s o, Genv.symbol_address geX s o = Sexpr.symbol_address find_symbol s o) →
    ∀ ρ ι,
      regset_agree ρ ι rs
      Op.eval_operation geX sp op (mapr, rs !! r) args) m = Some v
      Sexpr.eval_operation find_symbol sp (Mem.valid_pointer m) op (map ρ (map ι args)) = Some v.
Proof.
    intros symbol_preserved ρ ι Hρ.
    rewrite (regset_agree_map Hρ).
    destruct op; try (split; exact id);
    repeat match goal with
    | x : Op.addressing |- _ => destruct x
    end;
    simpl; try rewrite symbol_preserved; reflexivity.
  Qed.

  Arguments match_eval_operation [_ _ _ _ _ _ _] _ [ρ ι] _.

  Lemma match_regset_eop_left sp epa pe prod pc rs rs' res op args m v pc' :
    match_regset find_symbol sp epa pe prod pc rs rs' →
    Op.eval_operation geL sp op (mapr, rs !! r) args) m = Some v
    (Sylvie.fn_code prod) ! pc = Some (Sylvie.Eop (left res) (Operation op) (map left args) pc') →
    (∀ b o, valid_pointer_of_permission pe b o = Mem.valid_pointer m b o) →
    match_regset find_symbol sp epa pe prod pc' (Registers.Regmap.set res v rs) rs'.
Proof.
    intros (ρ & REACH & HρL & HρR) EV EOP PE.
    exists (ρ +[ left res => v ]).
    split.
    eapply Sylvie.reachable_step. exact REACH.
    simpl. rewrite EOP.
    exists v. split.
    rewrite (Sexpr.eval_operation_m _ _ (Mem.valid_pointer m)).
    apply (match_eval_operation symbol_preserved_L HρL).
    exact EV. exact PE.
     eexists. split. apply Sylvie.eq_env_equiv.
    reflexivity.
    split; intros r.
    rewrite Registers.Regmap.gsspec.
    case peq. intros ->. symmetry. apply upd_s.
    intros NE. rewrite upd_o. auto. intros NE'. apply NE, left_injective, NE'.
    rewrite upd_o. auto. auto using left_not_right.
  Qed.

  Lemma match_regset_eop_right sp epa pe prod pc rs rs' res op args m v pc' :
    match_regset find_symbol sp epa pe prod pc rs rs' →
    Op.eval_operation geR sp op (mapr, rs' !! r) args) m = Some v
    (Sylvie.fn_code prod) ! pc = Some (Sylvie.Eop (right res) (Operation op) (map right args) pc') →
    (∀ b o, valid_pointer_of_permission pe b o = Mem.valid_pointer m b o) →
    match_regset find_symbol sp epa pe prod pc' rs (Registers.Regmap.set res v rs').
Proof.
    intros (ρ & REACH & HρL & HρR) EV EOP PE.
    exists (ρ +[ right res => v ]).
    split.
    eapply Sylvie.reachable_step. exact REACH.
    simpl. rewrite EOP.
    exists v. split.
    rewrite (Sexpr.eval_operation_m _ _ (Mem.valid_pointer m)).
    apply (match_eval_operation symbol_preserved_R HρR).
    exact EV. exact PE.
     eexists. split. apply Sylvie.eq_env_equiv.
    reflexivity.
    split; intros r.
    rewrite upd_o. auto. apply left_not_right.
    rewrite Registers.Regmap.gsspec.
    case peq. intros ->. symmetry. apply upd_s.
    intros NE. rewrite upd_o. auto. intros NE'. apply NE, right_injective, NE'.
  Qed.

  Opaque is_dangerous.

  Local Open Scope string_scope.
  Lemma ep_annot_aux p p' acc :
    match fold_left2 (ep_annot_fun left right) (ep_annot_err _ "1") (ep_annot_err _ "2") p p' acc with
    | Errors.OK epa => ∀ a, In a epa
                            match acc with Errors.OK acc => In a acc | _ => False end
                            ∨ ∃ n x x', nth_error p n = Some xnth_error p' n = Some x' ∧ a = Sexpr.assert_eq_reg (left x) (right x')
    | _ => True
    end.
Proof.
    elim (fold_left2P).
    - intros [ epa | err ]. 2: exact I.
      intros a IN; left; exact IN.
    - intros; exact I.
    - intros; exact I.
    - intros a ma b mb c d H.
      destruct d as [ epa | err ]. 2: exact id.
      intros IH a' Ha'.
      specialize (IH _ Ha').
      destruct IH as [ IH | (n & x & x' & Hx & Hx' & -> ) ].
      + destruct c as [ c | err ]. 2: elim IH.
        destruct IH as [ <- | IH ].
        * right. exists O, a, b. simpl. vauto.
        * vauto.
      + right. exists (S n), x, x'. simpl. vauto.
  Qed.

  Lemma ep_annot_joint_regset params params' epa :
    list_norepet params
    list_norepet params' →
    ep_annot left right params params' = Errors.OK epa
    ∀ sp pe args,
      pre_of_assertion_list _ find_symbol sp pe epa (joint_regset (init_regs args params) (init_regs args params')).
Proof.
    intros NR NR' H sp pe args a IN.
    generalize (ep_annot_aux params params' (Errors.OK nil)).
    fold (ep_annot left right params params'). rewrite H. intros X.
    destruct (X _ IN) as [ () | (n & x & x' & Hx & Hx' & Ha) ]. clear X.
    subst a.
    simpl. f_equal. unfold joint_regset. rewrite ger_left, ger_right.
    clear H IN.
    revert params x Hx NR params' x' Hx' NR' args.
    clear. elim n; clear n.
    - intros [ | ? ? ] x X.
      exact (None_not_Some X _).
      apply some_eq_inv in X. subst.
      intros _ [ | ? ? ] x' X.
      exact (None_not_Some X _).
      apply some_eq_inv in X. subst.
      intros _ [ | ? ? ]; simpl.
      rewrite ! Registers.Regmap.gi. reflexivity.
      rewrite ! Registers.Regmap.gss. reflexivity.
    - intros n IH.
      intros [ | ? ? ] x X.
      exact (None_not_Some X _).
      specialize (IH _ _ X).
      simpl in X. apply nth_error_in in X.
      intros NR. apply list_norepet_cons in NR.
      destruct NR as (NI & NR).
      specialize (IH NR).
      intros [ | ? ? ] x' X'.
      exact (None_not_Some X' _).
      specialize (IH _ _ X').
      simpl in X'. apply nth_error_in in X'.
      intros NR'. apply list_norepet_cons in NR'.
      destruct NR' as (NI' & NR').
      specialize (IH NR').
      intros [ | ? ? ]; simpl.
      rewrite ! Registers.Regmap.gi. reflexivity.
      rewrite ! Registers.Regmap.gso. apply IH.
      intros ->. apply NI', X'.
      intros ->. apply NI, X.
  Qed.

  Hypothesis consistent_permissions : MoreSemantics.invariant (semantics progL) RTLperm.invariant.

  Lemma goto_sequence_reachable π gs sp vp prod pre ρ π' :
    goto_sequence prod π' gs
    last gs π' = π →
    Sylvie.reachable reg_dec find_symbol sp vp prod pre (Run {| pc := π; e := ρ |}) →
    Sylvie.reachable reg_dec find_symbol sp vp prod pre (Run {| pc := π'; e := ρ |}).
Proof.
    clear.
    revert π'.
    elim gs; clear gs.
    simpl. congruence.
    intros π'' gs IH π' (Hgoto & GS) Hgs REACH.
    rewrite last_cons in Hgs. specialize (IH _ GS Hgs REACH).
    eapply Sylvie.reachable_step. exact IH.
    red. simpl. rewrite Hgoto.
    eexists. split. apply eq_env_equiv. reflexivity.
  Qed.

  Lemma same_init_mem :
    Forall2x y, match_globdef_b x y = OK tt) (prog_defs progL) (prog_defs progR) →
    Genv.init_mem progL = Genv.init_mem progR.
Proof.
    unfold Genv.init_mem.
    intros H.
    generalize Mem.empty.
    revert H.
    generalize (prog_defs progL), (prog_defs progR).
    pose proof (same_find_symbol) as SFS.
    clear - find_symbol SFS. induction 1. reflexivity.
    simpl. intros m.
    cut (Genv.alloc_global (Genv.globalenv progL) m x = Genv.alloc_global (Genv.globalenv progR) m y).
    intros ->.
    case (Genv.alloc_global). assumption. reflexivity.
    apply match_globdef_b_true in H.
    destruct y as (i', gd').
    destruct x as (i, [ fd | gv ]);
      simpl in *; hsplit; subst.
    now destruct gd'.
    case Mem.alloc. intros m' b.
    case store_zeros. 2: reflexivity.
    intros m''.
    cut (Genv.store_init_data_list (Genv.globalenv progL) m'' b 0 (gvar_init gv) = Genv.store_init_data_list (Genv.globalenv progR) m'' b 0 (gvar_init gv)).
    intros ->; reflexivity.
    generalize 0.
    revert m''. generalize (gvar_init gv); intros idl.
    elim idl; clear idl. reflexivity.
    intros a idl IH m'' z.
    simpl.
    cut (Genv.store_init_data (Genv.globalenv progL) m'' b z a = Genv.store_init_data (Genv.globalenv progR) m'' b z a).
    intros ->. case Genv.store_init_data. 2: reflexivity. auto.
    destruct a; try reflexivity.
    simpl. generalize (SFS i). unfold geR, geL. simpl. intros ->. reflexivity.
  Qed.

  Lemma run_right_branch' stack pe epa rsL f sp rs m prod n to from :
    pe =3 mem_perm m
    iter_rel n (right_branch_rel reg right (RTL.fn_code f) (Sylvie.fn_code prod)) to from
    match_regset find_symbol sp epa pe prod (snd from) rsL rs
    ∃ pcR' pc',
      to = (fst (fst from), pcR', pc') ∧
      ∃ rs',
        iter_rel' n (λ α, RTL.step geR α nil)
                 (RTL.State stack f sp (snd (fst from)) rs m) (RTL.State stack f sp pcR' rs' m) ∧
        match_regset find_symbol sp epa pe prod pc' rsL rs'.
Proof.
    intros PE.
    revert to from.
    elim n; clear n; simpl.
    - intros ((pcL, pcR), pc) ? <- MR.
      eexists _, _. split. reflexivity. exists rs. vauto.
    - intros n IH ((pcL', pcR'), pc') to (((pcL, pcR), pc) & RB & STEPS) MR.
      destruct RB as ( -> & iR & HiR & MSI ).
      destruct MSI as (j & Hj & MSI).
      specialize (IH _ _ STEPS MR).
      destruct IH as (x & y & X & rs' & STAR & MR').
      eq_some_inv. subst x y pcL.
      eexists _, _. split. reflexivity.
      destruct iR; try (exfalso; exact MSI);
      destruct j; try (exfalso; exact MSI).
      + (* nop *)
        exists rs'.
        destruct MSI as ( -> & -> & LE ).
        split. eexists. split. eassumption. vauto.
        eauto using match_regset_egoto.
      + (* op — not dangerous *)
        destruct MSI as ( -> & -> & -> & -> & -> & SAFE ).
        destruct (not_dangerous_eval_operation geR sp rs' m SAFE) as (v & Hv).
        eexists. split.
        eexists. split. eassumption. vauto.
        eapply match_regset_eop_right; try eassumption.
        apply valid_pointer_of_permission_ext, PE.
  Qed.

  Lemma run_right_branch stack pe epa rsL f sp rs m prod to from :
    pe =3 mem_perm m
    right_branch reg right (RTL.fn_code f) (Sylvie.fn_code prod) to from
    match_regset find_symbol sp epa pe prod (snd from) rsL rs
    ∃ pcR' pc',
      to = (fst (fst from), pcR', pc') ∧
      ∃ rs',
        star RTL.step geR (RTL.State stack f sp (snd (fst from)) rs m) nil (RTL.State stack f sp pcR' rs' m) ∧
        match_regset find_symbol sp epa pe prod pc' rsL rs'.
Proof.
    intros PE RB MR.
    apply clos_refl_trans_iter in RB. destruct RB as (n & RB).
    destruct (run_right_branch' stack pe epa rsL f sp rs m prod _ _ _ PE RB MR) as (pcR' & pc' & Hto & rs' & H & MR').
    exists pcR', pc'. split. exact Hto. exists rs'. split. 2: exact MR'.
    eapply iter_rel'_star, H.
  Qed.

  Lemma right_branch_same_left {f p pc pc'} :
    right_branch reg right f p pc pc' →
    fst (fst pc) = fst (fst pc').
Proof.
    intros H. apply clos_refl_trans_iter in H.
    destruct H as (n & H). revert pc H.
    elim n; clear.
    simpl. intros pc. exact (@f_equal _ _x, fst (fst x)) _ _).
    intros n IH pc ( q & Hq & Hn ).
    etransitivity. 2: apply IH; eauto.
    clear - Hq.
    destruct pc as ((?,?),?), q as ((?,?),?).
    exact (proj1 Hq).
  Qed.

  Hypothesis external_calls_do_not_change_the_permissions :
    ∀ name sg args m tr vres m',
    Events.external_functions_sem name sg geL args m tr vres m' →
    mem_perm m =3 mem_perm m'.

  Lemma allowed_builtin_same_perm ef vargs m t vres m' :
    RTLperm.allowed_builtin ef = true
    Events.external_call ef geL vargs m t vres m' →
    mem_perm m =3 mem_perm m'.
Proof.
    assert (mem_perm m' =3 mem_perm m') as E by (repeat intro; reflexivity).
    destruct ef; simpl; intros Hal Hec; eauto;
    try exact (false_not_true Hal _);
    inversion Hec;
    try match goal with
        | H: Events.volatile_store _ _ _ _ _ _ _ _ |- _ => inversion H
        | H: Mem.storebytes _ _ _ _ = _ |- _ =>
          unfold mem_perm;
            rewrite <- (Mem.storebytes_access _ _ _ _ _ H)
        end; eauto using mem_store_perm.
  Qed.

  Theorem simulation :
    forward_simulation (RTLperm.semantics progL) (RTL.semantics progR).
Proof.
    refine (Forward_simulation (RTLperm.semantics progL) (RTL.semantics progR)
                               lt_wf
                               (match_state find_symbol progL)
                               _
                               _
                               _
                               same_public_symbols).
    - simpl.
      intros s1 H. inv H.
      destruct s1; inv H0.
      symmetry in H5. unfold erase_stack in H5. apply map_eq_nil in H5. subst stk.
      subst ge. fold geL in H2, H3.
      generalize (same_funs _ _ H3). intros (fd' & Hfd' & Hsig & MFD).
      exists O, (RTL.Callstate nil fd' nil m).
      split.
      econstructor. rewrite <- same_init_mem. assumption.
      apply hypotheses.
      generalize (same_find_symbol (prog_main progR)). simpl. rewrite same_main. fold geR. intros ->. eassumption. apply hypotheses.
      exact Hfd'. congruence.
      split.
      eexists _, nil. split. 2: left. econstructor; eauto.
      split. vauto. split; vauto.
    - intros i s1 s2 r H H0. simpl in H0.
      destruct s1; inv H0.
      destruct s2;
      try now destruct H.
      destruct stk. 2: simpl in *; eq_some_inv.
      destruct H as (_ & H & H' & _ ). inv H. constructor.
    - simpl.
      intros s1 t s1' (STEP & PE) i s2 (REACH & MS).
      precious s2 EQs2.
      destruct s1, s2; try (exfalso; assumption).
      + (* State *)
        destruct MS as ( STK & <- & <- & MS ).
        destruct MS as (prod & ppmap & deco & epa & cps & MF & π & (Hw & <-) & REG & Hfake).
        apply not_None_ex in Hw. destruct Hw as (w & Hw).
        pose proof (code_product_spec_at cps _ _ Hw) as Hw'.
        pose proof (cut_points_stable cps _ _ Hw) as Hstable.
        pose proof (witness_sequences cps _ _ Hw) as Hww'.
        pose proof (valid_heights cps _ _ Hw) as Hidx.

        destruct w as [ next | h rb to | next | | rb to | rb1 rb2 to1 to2 ].
        * { (* Left Step *)
            rename Hw' into LB.
            specialize (Hstable _ eq_refl).
            destruct (get_height _ _) as [ | h ]. exact (false_not_true Hidx _).
            destruct Hidx as [ _ Hidx ].
            specialize (Hidx _ (or_introl _ eq_refl)).
            exists h.
            destruct next as ((pcL', pcR), π').
            destruct LB as (<- & iL & HiL & MSI).
            destruct MSI as (j & Hj & MSI).
            destruct s1';
              inversion STEP; try (elim PE; fail);
              subst; mapeq; subst; try (destruct MSI; fail);
              destruct j; try (exfalso; assumption); hsplit; subst;

            destruct EQs2 as [s2 EQs2];
            (exists s2; subst s2; split; [ right; split; [ left | auto ] | ] ).

          - (* Inop *)
            split. eapply reachable_step; eauto; vauto.
            split. exact STK.
            split. reflexivity.
            split. reflexivity.
            eexists _, _, _, _, cps. split. exact MF.
            exists π'.
            split. split. exact Hstable. reflexivity.
            split. eauto using match_regset_egoto.

            intros (pc' & Hpc').
            specialize (Hww' _ (or_introl _ eq_refl) _ Hpc').
            exact (false_not_true Hww' _).

          - (* Iop — not dangerous *)
            split. eapply reachable_step; eauto; vauto.
            split. exact STK.
            split. reflexivity.
            split. reflexivity.
            eexists _, _, _, _, _. split. exact MF.
            exists π'.
            split. vauto.
            split.
            eapply match_regset_eop_left; try eassumption.
            generalize (consistent_permissions _ REACH). intros [PE _].
            apply valid_pointer_of_permission_ext, PE.

            intros (pc' & Hpc').
            specialize (Hww' _ (or_introl _ eq_refl) _ Hpc').
            exact (false_not_true Hww' _).
          }

        * { (* Left step followed by optional right branch *)
            generalize (consistent_permissions _ REACH). intros [PE' _].
            destruct Hw' as (LB & RB).
            specialize (Hstable _ eq_refl).
            destruct(get_height _ _) as [ | h' ] eqn: Hheight.
            - (* Last left instruction: right branch *)
              clear EQs2.
              destruct h as [ | h ]. exact (false_not_true Hidx _).
              destruct to as ((pcL', pcR'), π').
              destruct rb as ((pcL'', pcR), π'').
              destruct LB as (<- & iL & HiL & MSI).
              destruct MSI as (j & Hj & MSI).
              destruct s1';
                inversion STEP; try (elim PE; fail);
                subst; mapeq; subst; try (destruct MSI; fail);
                destruct j; try (exfalso; assumption); hsplit; subst.

              + (* Inop *)
              apply iter_rel'_rel in RB.
              eapply run_right_branch' in RB.
              2: exact PE'.
              2: eauto using match_regset_egoto.

              destruct RB as (pcR'' & pc' & X & rs' & STEPS & MR').
              apply iter_rel'_plus in STEPS.
              eq_some_inv. simpl in *. subst pc' pcL'' pcR''.
              destruct (skip_fake_branches cps (pcL', pcR') π') as (π''' & gs) eqn: Hπ.
              generalize (skip_fake_branches_goto_sequence cps (pcL', pcR') π').
              generalize (skip_fake_branches_start cps (pcL', pcR') π').
              generalize (skip_fake_branches_cut_points cps (pcL', pcR') π').
              rewrite Hπ.
              intros CP Hgs GS.

              exists (get_height (cut_point_heights cps) (pcL', pcR', π''')).
              eexists. split. vauto.
              split. eapply reachable_step; eauto; vauto.
              split. exact STK.
              split. reflexivity.
              split. reflexivity.
              eexists _, _, _, _, cps. split. exact MF.
              exists π'''.
              split. split. auto. reflexivity.
              split. eapply match_regset_goto_sequence. 2: exact GS.
              congruence.
              rewrite (eq_fst Hπ).
              apply (skip_fake_branches_not_fake cps (pcL', pcR')).

              + (* Iop — not dangerous *)
              apply iter_rel'_rel in RB.
              eapply run_right_branch' in RB.
              2: exact PE'.
              2: eapply match_regset_eop_left; try eassumption;
              apply valid_pointer_of_permission_ext, PE'.

              destruct RB as (pcR'' & pc' & X & rs' & STEPS & MR').
              apply iter_rel'_plus in STEPS.
              eq_some_inv. simpl in *. subst pc' pcL'' pcR''.
              destruct (skip_fake_branches cps (pcL', pcR') π') as (π''' & gs) eqn: Hπ.
              generalize (skip_fake_branches_goto_sequence cps (pcL', pcR') π').
              generalize (skip_fake_branches_start cps (pcL', pcR') π').
              generalize (skip_fake_branches_cut_points cps (pcL', pcR') π').
              rewrite Hπ.
              intros CP Hgs GS.

              exists (get_height (cut_point_heights cps) (pcL', pcR', π''')).
              eexists. split. vauto.
              split. eapply reachable_step; eauto; vauto.
              split. exact STK.
              split. reflexivity.
              split. reflexivity.
              eexists _, _, _, _, cps. split. exact MF.
              exists π'''.
              split. split. auto. reflexivity.
              split. eapply match_regset_goto_sequence. 2: exact GS.
              congruence.
              rewrite (eq_fst Hπ).
              apply (skip_fake_branches_not_fake cps (pcL', pcR')).

            - (* No right branch *)
              destruct Hidx as [ Hhz Hidx ].
              destruct h. 2: exact (false_not_true (eq_sym Hhz) _).
              clear Hhz.
              simpl in RB. subst to.
              exists h'.
              specialize (Hidx _ (or_introl _ eq_refl)).
              destruct rb as ((pcL', pcR), π').
              destruct LB as (<- & iL & HiL & MSI).
              destruct MSI as (j & Hj & MSI).
              destruct s1';
                inversion STEP; try (elim PE; fail);
                subst; mapeq; subst; try (destruct MSI; fail);
                destruct j; try (exfalso; assumption); hsplit; subst;

                destruct EQs2 as [s2 EQs2];
                (exists s2; subst s2; split; [ right; split; [ left | auto ] | ] ).

              + (* Inop *)
                split. eapply reachable_step; eauto; vauto.
                split. exact STK.
                split. reflexivity.
                split. reflexivity.
                eexists _, _, _, _, cps. split. exact MF.
                exists π'.
                split. split. exact Hstable. reflexivity.
                split. eauto using match_regset_egoto.

                intros (pc' & Hpc').
                specialize (Hww' _ (or_introl _ eq_refl) _ Hpc').
                exact (false_not_true Hww' _).

              + (* Iop — not dangerous *)
                split. eapply reachable_step; eauto; vauto.
                split. exact STK.
                split. reflexivity.
                split. reflexivity.
                eexists _, _, _, _, _. split. exact MF.
                exists π'.
                split. vauto.
                split.
                eapply match_regset_eop_left; try eassumption.
                generalize (consistent_permissions _ REACH). intros [PE _].
                apply valid_pointer_of_permission_ext, PE.

                intros (pc' & Hpc').
                specialize (Hww' _ (or_introl _ eq_refl) _ Hpc').
                exact (false_not_true Hww' _).
          }

        * { (* Fake Branch *)
            elim Hfake. red. vauto.
          }

        * { (* Synch Ret *)
            clear EQs2.
            destruct Hw' as (rL & rR & HrL & HrR & Hstop & MR).
            destruct s1'; inversion STEP; try congruence.
            subst. mapeq.
            match goal with H : Ireturn _ = Ireturn _ |- _ => inv H end.
            destruct MF as ( _ & _ & SZ & EPA & WAF).
            rewrite SZ in *.
            eexists O, _.
            split. left. vauto.
            split. eapply reachable_step; eauto; vauto.
            split. exact STK.
            split. 2: reflexivity.
            destruct or, rR; try (exfalso; assumption).
            2: reflexivity.
            simpl in *.
            destruct REG as (ρ & Hρ & HρL & HρR).
            rewrite (HρL r).
            rewrite (HρR r0).
            generalize (WAF _ _ _ Hρ _ MR).
            apply some_eq_inv.
          }

        * { (* Synch One *)
            clear EQs2.
            generalize (consistent_permissions _ REACH). intros [PE' _].
            destruct to as ((pcL'', pcR''), π'').
            destruct rb as ((pcL', pcR'), π').
            destruct Hw' as ((iL & iR & HiL & HiR & MI) & RB).
            generalize (right_branch_same_left RB). simpl. intros <-.
            destruct (skip_fake_branches cps (pcL'', pcR'') π'') as (π''' & gs) eqn: Hπ.
            generalize (skip_fake_branches_goto_sequence cps (pcL'', pcR'') π'').
            generalize (skip_fake_branches_start cps (pcL'', pcR'') π'').
            generalize (skip_fake_branches_cut_points cps (pcL'', pcR'') π'').
            rewrite Hπ.
            red in MI.
            destruct s1';
            inversion STEP; subst; mapeq; subst;
            try (exfalso; assumption);
            destruct iR; try (exfalso; assumption);
            hsplit; subst;
            intros CP Hgs GS.

            - (* Iop - dangerous *)
              assert (Op.eval_operation geR sp0 o (mapr : positive, rs !! r) l) m0 = Some v) as Hv.
              {
                destruct MF as (_ & _ & ? & ? & WAF).
                destruct REG as (ρ & Hρ & HρL & HρR).
                specialize (WAF _ _ _ Hρ).
                apply (match_eval_operation symbol_preserved_R HρR).
                apply (match_eval_operation symbol_preserved_L HρL) in H13.
                rewrite <- H13.
                f_equal.
                clear - HρL HρR H1 WAF.
                induction H1. reflexivity.
                simpl. f_equal. 2: auto.
                specialize (WAF _ H).
                symmetry. exact (some_eq_inv WAF).
              }
            exists (get_height (cut_point_heights cps) (pcL'', pcR'', π''')).
            eapply run_right_branch in RB. 2: exact PE'.
            destruct RB as (x & y & X & rs' & STEPS & MR).
            simpl in X. eq_some_inv. subst x y.
            eexists. split.
            left. eapply plus_left. vauto. exact STEPS. reflexivity.
            split. eapply reachable_step; vauto. eauto.
            split. assumption.
            split. reflexivity.
            split. reflexivity.
            exists prod, ppmap, deco, epa, cps.
            split. assumption.
            exists π'''. split. split. simpl in *. eauto. reflexivity.
            split.
            eapply match_regset_goto_sequence. 2: exact GS.
            rewrite Hgs. exact MR.
            rewrite (eq_fst Hπ);
              apply (skip_fake_branches_not_fake cps (pcL'', pcR'')).

            destruct REG as (ρ & Hρ & HρL & HρR).
            exists (ρ +[ left res => v ] +[ right r => v ]).
            split.
            eapply Sylvie.reachable_step.
            eapply Sylvie.reachable_step.
            eassumption.
            red. simpl.
            match goal with H : (Sylvie.fn_code prod) ! _ = _ |- _ => rewrite H end.
            exists v. split.
            rewrite (Sexpr.eval_operation_m _ _ (Mem.valid_pointer m0)).
            apply (match_eval_operation symbol_preserved_L HρL).
            assumption.
            generalize (consistent_permissions _ REACH). intros [PE _].
            apply valid_pointer_of_permission_ext, PE.
            eexists. split. apply Sylvie.eq_env_equiv. reflexivity.
            simpl.
            match goal with H : (Sylvie.fn_code prod) ! _ = _ |- _ => rewrite H end.
            exists v. split.
            simpl. rewrite eq_dec_true; reflexivity.
            eexists. split. apply Sylvie.eq_env_equiv. reflexivity.
            split; intros q.
            rewrite upd_o. 2: apply left_not_right.
            rewrite Registers.Regmap.gsspec. case peq. intros ->. symmetry. apply upd_s.
            intros NE. rewrite upd_o. auto. intros NE'; apply NE, left_injective, NE'.
            rewrite Registers.Regmap.gsspec. case peq. intros ->. symmetry. apply upd_s.
            intros NE. rewrite upd_o. rewrite upd_o. auto. auto using left_not_right.
            intros NE'; apply NE, right_injective, NE'.

            - (* Iload *)
              destruct REG as (ρ & Hρ & HρL & HρR).
              assert (Op.eval_addressing geR sp0 a1 (mapr0 : positive, rs !! r0) l) = Some a).
              {
                destruct MF as (_ & _ & ? & ? & WAF).
                match goal with H : δ _ _ _ _ |- _ =>
                specialize (WAF _ _ _ Hρ _ H); clear H end.
                rewrite (match_eval_addressing symbol_preserved_R HρR).
                symmetry. eapply eq_trans. 2: exact WAF. symmetry.
                apply (match_eval_addressing symbol_preserved_L HρL).
                assumption.
              }
              exists (get_height (cut_point_heights cps) (pcL'', pcR'', π''')).
              eapply run_right_branch in RB. 2: exact PE'.
              destruct RB as (x & y & X & rs' & STEPS & MR).
              simpl in X. eq_some_inv. subst x y.
              eexists. split.
              left. eapply plus_left. vauto. exact STEPS. reflexivity.
              split. eapply reachable_step; eauto; vauto.
              split. exact STK.
              split. reflexivity.
              split. reflexivity.
              exists prod, ppmap, deco, epa, cps.
              split. exact MF.
              exists π'''. split. split. simpl in *. eauto. reflexivity.
              split.
              eapply match_regset_goto_sequence. 2: exact GS.
              rewrite Hgs. exact MR.
              rewrite (eq_fst Hπ);
                apply (skip_fake_branches_not_fake cps (pcL'', pcR'')).

              exists (ρ +[ left dst => v ] +[ right r => v ]).
              split.
              eapply Sylvie.reachable_step.
              eapply Sylvie.reachable_step.
              eassumption.
              red. simpl.
              match goal with H : (Sylvie.fn_code prod) ! _ = _ |- _ => rewrite H end.
              eexists _, v. split. apply Sylvie.eq_env_equiv. reflexivity.
              simpl.
              match goal with H : (Sylvie.fn_code prod) ! _ = _ |- _ => rewrite H end.
              exists v. split.
              simpl. rewrite eq_dec_true; reflexivity.
              eexists. split. apply Sylvie.eq_env_equiv. reflexivity.
              split; intros q.
              rewrite upd_o. 2: apply left_not_right.
              rewrite Registers.Regmap.gsspec. case peq. intros ->. symmetry. apply upd_s.
              intros NE. rewrite upd_o. auto. intros NE'; apply NE, left_injective, NE'.
              rewrite Registers.Regmap.gsspec. case peq. intros ->. symmetry. apply upd_s.
              intros NE. rewrite upd_o. rewrite upd_o. auto. auto using left_not_right.
              intros NE'; apply NE, right_injective, NE'.

            - (* Istore *)
              destruct REG as (ρ & Hρ & HρL & HρR).
              assert (Mem.storev m1 m a rs !! r = Some m0).
              {
                destruct MF as (_ & _ & ? & ? & WAF).
                specialize (WAF _ _ _ Hρ _ H1); clear H1.
                simpl in WAF. apply some_eq_inv in WAF.
                rewrite HρL, WAF, <- HρR in H14.
                exact H14.
              }
              assert (Op.eval_addressing geR sp0 a1 (mapr0 : positive, rs !! r0) l) = Some a).
              {
                destruct MF as (_ & _ & ? & ? & WAF).
                specialize (WAF _ _ _ Hρ _ H2). clear H2.
                rewrite (match_eval_addressing symbol_preserved_R HρR).
                symmetry. eapply eq_trans. 2: exact WAF. symmetry.
                apply (match_eval_addressing symbol_preserved_L HρL).
                assumption.
              }
              exists (get_height (cut_point_heights cps) (pcL'', pcR'', π''')).
              eapply run_right_branch in RB.
              destruct RB as (x & y & X & rs' & STEPS & MR).
              simpl in X. eq_some_inv. subst x y.
              eexists. split.
              left. eapply plus_left. vauto. exact STEPS. reflexivity.
              split. eapply reachable_step; eauto; vauto.
              split. exact STK.
              split. reflexivity.
              split. reflexivity.
              exists prod, ppmap, deco, epa, cps.
              split. exact MF.
              exists π'''. split. split. simpl in *. eauto. reflexivity.
              split.
              eapply match_regset_goto_sequence. 2: exact GS.
              rewrite Hgs. exact MR.
              rewrite (eq_fst Hπ);
                apply (skip_fake_branches_not_fake cps (pcL'', pcR'')).
              eapply eq3_trans. exact PE'.
              destruct a; simpl in *; eq_some_inv. eauto using mem_store_perm.

              exists ρ.
              split.
              eapply Sylvie.reachable_step; eauto. red. simpl.
              match goal with H : (Sylvie.fn_code prod) ! _ = _ |- _ => rewrite H end.
              eexists. split. apply Sylvie.eq_env_equiv. reflexivity.
              exact (conj HρL HρR).

            - (* Ibuiltin *)
              clear Hfake.
              destruct REG as (ρ & Hρ & HρL & HρR).
              assert (Events.eval_builtin_args geRr, rs !! r) sp0 m l vargs).
              {
                destruct MF as ( _ & _ & SZ & EPA & WAF).
                specialize (WAF sp0 perm).
                clear H14.
                revert vargs H13. elim H2; clear - WAF Hρ HρL HρR.
                intros vargs X; inversion X. vauto.
                intros x y args args' Hxy Hargs IH vargs X.
                inv X.
                econstructor.
                - destruct x, y; try (exfalso; exact Hxy).
                  + cut (b1 = rs !! x0). intros ->. constructor.
                    specialize (WAF _ Hρ _ Hxy).
                    inv H1. rewrite (HρL x), (HρR x0).
                    exact (some_eq_inv WAF).
                  + simpl in Hxy. inv H1. constructor.
                  + destruct Hxy. inv H1.
                    fold (geL). unfold Senv.symbol_address.
                    rewrite (same_find_symbol_L hypotheses),
                      <- (same_find_symbol_R hypotheses).
                    constructor.
                - apply IH. assumption.
              }
              exists (get_height (cut_point_heights cps) (pcL'', pcR'', π''')).
              eapply run_right_branch in RB.
              destruct RB as (x & y & X & rs' & STEPS & MR).
              simpl in X. eq_some_inv. subst x y.
              eexists. split.
              left. eapply plus_left. vauto. 2: exact STEPS.
              simpl.
              eapply RTL.exec_Ibuiltin. eassumption. eassumption.
              eauto.
              apply (Events.external_call_symbols_preserved_gen _ geL).
                apply same_find_symbol.
                apply same_public_symbols.
                apply same_volatiles.
                eassumption.
              symmetry; apply app_nil_r.
              split. eapply reachable_step; eauto; vauto.
              split. exact STK.
              split. reflexivity.
              split. reflexivity.
              exists prod, ppmap, deco, epa, cps.
              split. exact MF.
              exists π'''. split. split. simpl in *. eauto. reflexivity.
              split.
              eapply match_regset_goto_sequence. 2: exact GS.
              rewrite Hgs. exact MR.
              rewrite (eq_fst Hπ);
                apply (skip_fake_branches_not_fake cps (pcL'', pcR'')).
              eapply eq3_trans. exact PE'.
              eauto using allowed_builtin_same_perm.

              destruct res, b; try (exfalso; exact H3).
              + destruct H3 as (pci & Hl & Hr).
                eexists (ρ +[ _ => _ ] +[ _ => _ ]).
                split.
                eapply Sylvie.reachable_step.
                eapply Sylvie.reachable_step.
                eassumption.
                red. simpl.
                match goal with H : (Sylvie.fn_code prod) ! _ = _ |- _ => rewrite H end.
                eexists _, _. split. apply Sylvie.eq_env_equiv. reflexivity.
                simpl.
                match goal with H : (Sylvie.fn_code prod) ! _ = _ |- _ => rewrite H end.
                eexists _. split.
                simpl. rewrite eq_dec_true; reflexivity.
                eexists. split. apply Sylvie.eq_env_equiv. reflexivity.
                split; intros q; simpl.
                rewrite upd_o. 2: apply left_not_right.
                rewrite Registers.Regmap.gsspec. case peq. intros ->. symmetry. apply upd_s.
                intros NE. rewrite upd_o. auto. intros NE'; apply NE, left_injective, NE'.
                rewrite Registers.Regmap.gsspec. case peq. intros ->. symmetry. apply upd_s.
                intros NE. rewrite upd_o. rewrite upd_o. auto. auto using left_not_right.
                intros NE'; apply NE, right_injective, NE'.
              + exists ρ. simpl in H3.
                split.
                eapply Sylvie.reachable_step.
                eassumption.
                red. simpl.
                match goal with H : (Sylvie.fn_code prod) ! _ = _ |- _ => rewrite H end.
                eexists. split. apply Sylvie.eq_env_equiv. reflexivity.
                simpl. eauto.

            - (* Icall *)
              destruct PE as [ -> | PE ].
              refine (no_infinite_list _ (eq_sym _)); eassumption.
              destruct stk0 as [ | (pe & s) stk' ]. elim PE.
              hsplit. subst.
              simpl in *. eq_some_inv. subst.
              assert (∃ fd', find_function geR s0 rs = Some fd' ∧ match_fundef find_symbol fd fd') as Hfd'.
              {
                destruct MF as (_ & _ & ? & ? & WAF).
                destruct REG as (ρ & Hρ & HρL & HρR).
                match goal with H : δ _ _ _ _ |- _ =>
                specialize (WAF _ _ _ Hρ _ H); clear H end.
                destruct ros, s0; simpl in *;
                eq_some_inv.
                rewrite (HρL r0), WAF, <- (HρR r1) in H11.
                destruct (rs !! r1); simpl in *; eq_some_inv. revert H11.
                case (Integers.Int.eq_dec). 2: intros; eq_some_inv.
                intros -> FFP.
                generalize (same_funs _ _ FFP). intros (fd' & Hfd' & MFD).
                vauto.
                generalize (same_find_symbol_R hypotheses i). simpl. intros ->.
                revert WAF. destruct (find_symbol _); simpl; intros; eq_some_inv.
                generalize (HρL r0). rewrite WAF.
                intros EQ. rewrite EQ in H11.
                generalize (same_funs _ _ H11). intros (fd' & Hfd' & MFD).
                vauto.
                fold (geL) in H11.
                generalize (same_find_symbol_L hypotheses i). simpl. intros X. rewrite X in H11. clear X.
                destruct (find_symbol _); eq_some_inv.
                generalize (HρR r0). rewrite <- WAF. intros ->.
                exact (same_funs _ _ H11).
                fold (geL) in H11.
                generalize (same_find_symbol_L hypotheses i). simpl. intros X. rewrite X in H11. clear X.
                generalize (same_find_symbol_R hypotheses i0). simpl. intros ->.
                destruct (find_symbol i); simpl in *; eq_some_inv.
                destruct (find_symbol _); simpl in *; eq_some_inv.
                inv WAF.
                exact (same_funs _ _ H11).
              }
              destruct Hfd' as (fd' & Hfd' & Hsig & MF').
              eexists O, _.
              split. left. vauto.
              split. eapply reachable_step; eauto; vauto.
              split. constructor. exact STK.
                split. reflexivity.
                exists prod, ppmap, deco, epa, cps.
                split. exact MF.
                eexists _, _, _. split. exact RB. split. auto.
                intros v.
                destruct REG as (ρ & Hρ & HρL & HρR).
                exists (ρ +[ left res => v ] +[ right r => v ]).
                split.
                eapply Sylvie.reachable_step.
                eapply Sylvie.reachable_step.
                eassumption.
                red. simpl.
                match goal with H : (Sylvie.fn_code prod) ! _ = _ |- _ => rewrite H end.
                eexists _, v. split. apply Sylvie.eq_env_equiv. reflexivity.
                simpl.
                match goal with H : (Sylvie.fn_code prod) ! _ = _ |- _ => rewrite H end.
                exists v. split.
                simpl. rewrite eq_dec_true; reflexivity.
                eexists. split. apply Sylvie.eq_env_equiv. reflexivity.
                split; intros q.
                rewrite upd_o. 2: apply left_not_right.
                rewrite Registers.Regmap.gsspec. case peq. intros ->. symmetry. apply upd_s.
                intros NE. rewrite upd_o. auto. intros NE'; apply NE, left_injective, NE'.
                rewrite Registers.Regmap.gsspec. case peq. intros ->. symmetry. apply upd_s.
                intros NE. rewrite upd_o. rewrite upd_o. auto. auto using left_not_right.
                intros NE'; apply NE, right_injective, NE'.
              split. split. exact Hsig. exact MF'.
              split. 2: reflexivity.
              destruct REG as (ρ & Hρ & HρL & HρR).
              rewrite (regset_agree_map HρL), (regset_agree_map HρR).
              destruct MF as (_ & _ & ? & ? & WAF).
              specialize (WAF _ _ _ Hρ). clear -WAF H1.
              induction H1. reflexivity.
              simpl. f_equal. 2: assumption.
              specialize (WAF _ H). exact (some_eq_inv WAF).
          }

        * { (* Synch Two *)
            clear EQs2.
            generalize (consistent_permissions _ REACH). intros [PE' _].
            destruct rb1 as ((pcL1, pcR1), π1), rb2 as ((pcL2, pcR2), π2).
            destruct Hw' as (SB2 & RB1 & RB2).
            destruct SB2 as (iL & iR & HiL & HiR & MI).
            destruct s1';
            inversion STEP; subst; mapeq; subst;
            try (exfalso; assumption);
            destruct iR; try (exfalso; assumption);
            hsplit; subst.
 Icond *)            simpl in *. hsplit. subst. eq_some_inv. subst.
            destruct REG as (ρ & Hρ & Hρ').
            assert (Sexpr.eval_condition (valid_pointer_of_permission perm) cond (map ρ (map left args)) = Some b) as Hb.
            {
              destruct Hρ' as (HρL & HρR).
              rewrite (Sexpr.eval_condition_m (Mem.valid_pointer m0)); auto.
              apply (match_eval_condition HρL); assumption.
              apply valid_pointer_of_permission_ext, PE'.
            }
            assert (Op.eval_condition c (mapr : positive, rs !! r) l) m0 = Some b) as Hb'.
            {
              destruct MF as (_ & _ & Hsz & EPA & WAF).
              match goal with H : δ _ _ _ _ |- _ =>
              specialize (WAF _ _ _ Hρ _ H); clear H end.
              revert WAF.
              clear - Hb Hρ' PE'.
              simpl. rewrite ! Sexpr.eval_boolexpr, Hb.
              intros X; apply some_eq_inv in X. simpl in X.
              apply (match_eval_condition (proj2 Hρ')).
              rewrite (Sexpr.eval_condition_m (Mem.valid_pointer m0)) in X; auto.
              revert X.
              case (Sexpr.eval_condition); destruct b; simpl; (intros () ? || intros ?); eq_some_inv; auto;
              discriminate.
              apply valid_pointer_of_permission_ext, PE'.
            }

            destruct b.
            - (* Then *)
            eapply run_right_branch in RB1. 2: exact PE'.
            destruct RB1 as (pcR1 & pc1 & -> & rs1 & STEPS1 & MR1).
            destruct (skip_fake_branches cps (ifso, pcR1) pc1) as (π'1 & gs1) eqn: Hπ1.
            generalize (skip_fake_branches_goto_sequence cps (ifso, pcR1) pc1).
            generalize (skip_fake_branches_start cps (ifso, pcR1) pc1).
            generalize (skip_fake_branches_cut_points cps (ifso, pcR1) pc1).
            rewrite Hπ1.
            intros CP1 Hgs1 GS1.

            exists (get_height (cut_point_heights cps) (ifso, pcR1, π'1)).
            eexists.
            split. left. eapply plus_left. vauto. exact STEPS1. reflexivity.
            split. eapply reachable_step; eauto; vauto.
            split. exact STK.
            split. reflexivity.
            split. reflexivity.
            exists prod, ppmap, deco, epa, cps.
            split. exact MF.
            exists π'1.
            split. split. auto. reflexivity.
            split. eapply match_regset_goto_sequence. 2: exact GS1.
            rewrite Hgs1. exact MR1.
            rewrite (eq_fst Hπ1);
              apply (skip_fake_branches_not_fake cps (ifso, pcR1)).

            exists ρ. split.
            2: exact Hρ'.
            eapply Sylvie.reachable_step. exact Hρ.
            red. simpl.
            match goal with H : (Sylvie.fn_code prod) ! _ = _ |- _ => rewrite H end.
            eexists. split. exact Hb.
            exists ρ. split. apply eq_env_equiv. reflexivity.

            - (* Else *)
            eapply run_right_branch in RB2. 2: exact PE'.
            destruct RB2 as (pcR2 & pc2 & -> & rs2 & STEPS2 & MR2).
            destruct (skip_fake_branches cps (ifnot, pcR2) pc2) as (π'2 & gs2) eqn: Hπ2.
            generalize (skip_fake_branches_goto_sequence cps (ifnot, pcR2) pc2).
            generalize (skip_fake_branches_start cps (ifnot, pcR2) pc2).
            generalize (skip_fake_branches_cut_points cps (ifnot, pcR2) pc2).
            rewrite Hπ2.
            intros CP2 Hgs2 GS2.

            exists (get_height (cut_point_heights cps) (ifnot, pcR2, π'2)).
            eexists.
            split. left. eapply plus_left. vauto. exact STEPS2. reflexivity.
            split. eapply reachable_step; eauto; vauto.
            split. exact STK.
            split. reflexivity.
            split. reflexivity.
            exists prod, ppmap, deco, epa, cps.
            split. exact MF.
            exists π'2.
            split. split. auto. reflexivity.
            split. eapply match_regset_goto_sequence. 2: exact GS2.
            rewrite Hgs2. exact MR2.
            rewrite (eq_fst Hπ2);
              apply (skip_fake_branches_not_fake cps (ifnot, pcR2)).

            exists ρ. split.
            2: exact Hρ'.
            eapply Sylvie.reachable_step. exact Hρ.
            red. simpl.
            match goal with H : (Sylvie.fn_code prod) ! _ = _ |- _ => rewrite H end.
            eexists. split. exact Hb.
            exists ρ. split. apply eq_env_equiv. reflexivity.
          }

      + (* Callstate *)
        destruct MS as ( STK & FD & <- & <- ).
        destruct s1'; inversion STEP; subst.
        * (* Internal call *)
          destruct PE as ( <- & PE ).
          destruct f as [ fn' | ? ]. 2: destruct FD as [ ? () ].
          destruct FD as (Hsig & prod & ppmap & deco & epa & cps & FD).
          precious FD MF.
          destruct FD as (Hpnr & Hpnr' & Hsz & EPA & WAF).
          rewrite Hsz in *.
          destruct (skip_fake_branches cps (fn_entrypoint fn, fn_entrypoint fn') (Sylvie.fn_entrypoint prod)) as (π & gs) eqn: Hπ.
          generalize (skip_fake_branches_goto_sequence cps (fn_entrypoint fn, fn_entrypoint fn') (Sylvie.fn_entrypoint prod)).
          generalize (skip_fake_branches_start cps (fn_entrypoint fn, fn_entrypoint fn') (Sylvie.fn_entrypoint prod)).
          generalize (skip_fake_branches_cut_points cps (fn_entrypoint fn, fn_entrypoint fn') (Sylvie.fn_entrypoint prod)).
          rewrite Hπ. simpl.
          intros CP Hgs GS.
          exists (get_height (cut_point_heights cps) (fn_entrypoint fn, fn_entrypoint fn', π)).
          eexists.
          split. left. vauto.
          split. eapply reachable_step; eauto. vauto.
          split. assumption.
          split. reflexivity.
          split. reflexivity.
          destruct MF as [MF _].
          eexists _, _, _, _, _. split. exact MF.
          exists π. split.
          split. apply CP. apply related_entrypoints. reflexivity.
          split.
          exists (joint_regset (init_regs args (fn_params fn)) (init_regs args (fn_params fn'))).
          split. eapply goto_sequence_reachable. exact GS. exact Hgs.
          eexists. split. 2: vauto.
          eexists. split. reflexivity.
          split. reflexivity. simpl.
          apply ep_annot_joint_regset, EPA; auto.
          clear -ger_left ger_right.
          split; intros r; unfold joint_regset.
          rewrite ger_left; reflexivity.
          rewrite ger_right; reflexivity.
          rewrite (eq_fst Hπ);
            apply (skip_fake_branches_not_fake cps (fn_entrypoint fn, fn_entrypoint fn')).

        * (* External call *)
          destruct f as [ ? | ef' ]; destruct FD as [ Hsig FD ].
          elim FD.
          subst ef'.
          inversion STEP; subst.
          eexists O, _. split.
          left. apply plus_one. econstructor.
          apply (Events.external_call_symbols_preserved_gen _ geL).
            apply same_find_symbol.
            apply same_public_symbols.
            apply same_volatiles.
            eassumption.
          split. eapply reachable_step; eauto. vauto.
          split. assumption.
          split. reflexivity.
          reflexivity.

      + (* Returnstate *)
        destruct stk as [ | s stk ]. elim PE.
        destruct s1'; try (elim PE; fail).
        destruct PE as ( -> & <- ).
        destruct MS as ( STK & <- & <- ).
        apply match_stack_cons in STK.
        destruct STK as (s' & stk' & -> & STK & SF).
        inversion STEP; subst.
        destruct s as (perm & s). simpl in *. subst s.
        destruct s' as [ r' f' sp' pc' rs' ].
        destruct SF as ( SP & SF ).
        red in SP; subst.
        destruct SF as (prod & ppmap & deco & epa & cps & MF & pc'' & π' & π'' & RB & Hcp & HPC).
        eapply run_right_branch in RB.
        2: apply (consistent_permissions _ REACH).
        2: apply HPC.
        destruct RB as (pcR' & pc''' & X & rs'' & STEPS & MR).
        eq_some_inv. subst pc''' pc''.
        destruct (skip_fake_branches cps (pc, pcR') π'') as (π''' & gs) eqn: Hπ.
        generalize (skip_fake_branches_goto_sequence cps (pc, pcR') π'').
        generalize (skip_fake_branches_start cps (pc, pcR') π'').
        generalize (skip_fake_branches_cut_points cps (pc, pcR') π'').
        rewrite Hπ.
        intros CP Hgs GS.

        exists (get_height (cut_point_heights cps) (pc, pcR', π''')).
        eexists. split. left. eapply plus_left. vauto. eauto. reflexivity.
        split. eapply reachable_step; eauto; vauto.
        split. exact STK.
        split. reflexivity.
        split. reflexivity.
        exists prod, ppmap, deco, epa, cps.
        split. exact MF.
        eexists. split.
        split. 2: reflexivity. eauto.
        split.
        eapply match_regset_goto_sequence; eauto.
        rewrite Hgs. eauto.
        rewrite (eq_fst Hπ);
          apply (skip_fake_branches_not_fake cps (pc, pcR')).
  Qed.

End Main.

End REGISTER.