Module RTLdefgenspec

Require Import Coqlib.
Require Import Errors.
Require Import Maps.
Require Import AST.
Require Import Events.
Require Import Integers.
Require Import Floats.
Require Import Values.
Require Import Memory.
Require Import Globalenvs.
Require Import Smallstep.
Require Import Op.
Require Import Registers.
Require Import Annotations.
Require Import RTL.
Require Import RTLdefgen.
Import Utf8.
Import ArithLib.
Import MoreRTL.


Section SPEC.

  Variable prog: program.
  Let ge := Genv.globalenv prog.
  Variable STK: ident.
  Let SIZE := Psucc STK.
  
  Definition regs_agree (j: meminj) (n: reg) (rs trs: regset) :=
    forall r, (Ple r n /\ Val.inject j rs#r trs#r) \/ (Plt n r /\ Val.lessdef rs#r trs#r).

  Lemma regs_agree_ple:
    forall j n rs trs args,
      (forall r, In r args -> Ple r n) ->
      regs_agree j n rs trs ->
      Val.inject_list j rs##args trs##args.
Proof.
    induction args; intros.
    - simpl; auto.
    - simpl. generalize (H0 a). generalize (H a (in_eq _ _)). intros.
      destruct H2 as [[A B] | [A B]]; try xomega.
      econstructor; eauto. eapply IHargs; eauto.
      intros. eapply H; eapply in_cons; eauto.
  Qed.

  Lemma regs_agree_inject_incr:
    forall j j' n rs trs,
      inject_incr j j' ->
      regs_agree j n rs trs ->
      regs_agree j' n rs trs.
Proof.
    unfold regs_agree; intros. destruct (H0 r).
    - destruct H1; left; split; auto.
      eapply val_inject_incr; eauto.
    - right; auto.
  Qed.

  Lemma regs_agree_update:
    forall j n rs trs r v tv,
      Ple r n ->
      regs_agree j n rs trs ->
      Val.inject j v tv ->
      regs_agree j n (rs # r <- v) (trs # r <- tv).
Proof.
    unfold regs_agree; intros. destruct (peq r r0).
    - subst r0. left; split; auto.
      repeat rewrite PMap.gss; auto.
    - generalize (H0 r0); intros [[A B] | [A B]].
      + left; split; auto. repeat rewrite PMap.gso; eauto.
      + right; split; auto. repeat rewrite PMap.gso; eauto.
  Qed.

  Inductive tr_local_regs kappa: RTL.function -> code -> node -> Int.int -> nat -> nat -> list reg -> list reg -> node -> list node -> Prop :=
  | tr_local_regs_S_divides:
      forall f c pc1 ofs depth n reg regs regs' pc2 pcs (Hdivides: ((align_chunk kappa) | (Int.unsigned ofs))),
        c!pc1 = Some (Iload (xH, nil) Mint32 (Aglobal SIZE Int.zero) nil (Psucc (Psucc reg)) (Psucc pc1)) ->
        c!(Psucc pc1) = Some (Iload (xH, nil) Mint32 (Abased STK (Int.repr (-4 * Z.of_nat depth))) ((Psucc (Psucc reg))::nil) (Psucc reg) (Psucc (Psucc pc1))) ->
        c!(Psucc (Psucc pc1)) = Some (Iop (if Int.eq_dec ofs Int.zero then Omove else Oaddimm ofs) ((Psucc reg)::nil) reg (Psucc (Psucc (Psucc pc1)))) ->
        tr_local_regs kappa f c (Psucc (Psucc (Psucc pc1))) (Int.add ofs Int.one) depth n regs regs' pc2 pcs ->
        Plt (max_reg_function f) reg ->
        ~ In reg (regs ++ regs') ->
        ~ In (Psucc reg) (regs ++ regs') ->
        ~ In (Psucc (Psucc reg)) (regs ++ regs') ->
        list_disjoint regs regs' ->
        tr_local_regs kappa f c pc1 ofs depth (S n) (reg::regs) ((Psucc reg)::(Psucc (Psucc reg))::regs') pc2 (pc1::(Psucc pc1)::(Psucc (Psucc pc1))::pcs)
  | tr_local_regs_S_not_divides:
      forall f c pc1 ofs depth n regs regs' pc2 pcs (Hnotdivides: ~ ((align_chunk kappa) | (Int.unsigned ofs))),
        tr_local_regs kappa f c pc1 (Int.add ofs Int.one) depth n regs regs' pc2 pcs ->
        tr_local_regs kappa f c pc1 ofs depth (S n) regs regs' pc2 pcs
  | tr_local_regs_O:
      forall f c pc ofs depth,
        tr_local_regs kappa f c pc ofs depth O nil nil pc nil.

  Inductive tr_local_regs': RTL.function -> code -> node -> nat -> Int.int -> Int.int -> (reg + (reg * reg)) -> list reg -> node -> list node -> Prop :=
  | tr_local_regs_eq:
      forall f c pc1 base bound depth r pc2,
        base = bound ->
        c!pc1 = Some (Iload (xH, nil) Mint32 (Aglobal SIZE Int.zero) nil (Psucc (Psucc r)) (Psucc pc1)) ->
        c!(Psucc pc1) = Some (Iload (xH, nil) Mint32 (Abased STK (Int.repr (-4 * Z.of_nat depth))) ((Psucc (Psucc r))::nil) (Psucc r) (Psucc (Psucc pc1))) ->
        c!(Psucc (Psucc pc1)) = Some (Iop (if Int.eq_dec base Int.zero then Omove else Oaddimm base) ((Psucc r)::nil) r pc2) ->
        pc2 = Pos.succ (Pos.succ (Pos.succ pc1)) →
        Plt (max_reg_function f) r ->
        tr_local_regs' f c pc1 depth base bound (inl r) ((Psucc r)::(Psucc (Psucc r))::nil) pc2 (pc1::(Psucc pc1)::(Psucc (Psucc pc1))::nil)
  | tr_local_regs_neq:
      forall f c pc1 base bound depth r pc2,
        base <> bound ->
        c!pc1 = Some (Iload (xH, nil) Mint32 (Aglobal SIZE Int.zero) nil (Psucc (Psucc (Psucc r))) (Psucc pc1)) ->
        c!(Psucc pc1) = Some (Iload (xH, nil) Mint32 (Abased STK (Int.repr (-4 * Z.of_nat depth))) ((Psucc (Psucc (Psucc r)))::nil) (Psucc (Psucc r)) (Psucc (Psucc pc1))) ->
        c!(Psucc (Psucc pc1)) = Some (Iop (if Int.eq_dec base Int.zero then Omove else Oaddimm base) ((Psucc (Psucc r))::nil) r (Psucc (Psucc (Psucc pc1)))) ->
        c!(Psucc (Psucc (Psucc pc1))) = Some (Iload (xH, nil) Mint32 (Aglobal SIZE Int.zero) nil (Psucc (Psucc (Psucc r))) (Psucc (Psucc (Psucc (Psucc pc1))))) ->
        c!(Psucc (Psucc (Psucc (Psucc pc1)))) = Some (Iload (xH, nil) Mint32 (Abased STK (Int.repr (-4 * Z.of_nat depth))) ((Psucc (Psucc (Psucc r)))::nil) (Psucc (Psucc r)) (Psucc (Psucc (Psucc (Psucc (Psucc pc1)))))) ->
        c!(Psucc (Psucc (Psucc (Psucc (Psucc pc1))))) = Some (Iop (if Int.eq_dec bound Int.zero then Omove else Oaddimm bound) ((Psucc (Psucc r))::nil) (Psucc r) pc2) ->
        pc2 = Pos.succ (Pos.succ (Pos.succ (Pos.succ (Pos.succ (Pos.succ pc1))))) →
        Plt (max_reg_function f) r ->
        tr_local_regs' f c pc1 depth base bound (inr (r, Psucc r)) ((Psucc (Psucc r))::(Psucc (Psucc (Psucc r)))::nil) pc2 (pc1::(Psucc pc1)::(Psucc (Psucc pc1))::(Psucc (Psucc (Psucc pc1)))::(Psucc (Psucc (Psucc (Psucc pc1))))::(Psucc (Psucc (Psucc (Psucc (Psucc pc1)))))::nil).

  Inductive tr_global_regs kappa: RTL.function -> code -> node -> ident -> Int.int -> nat -> list reg -> node -> list node -> Prop :=
  | tr_global_regs_S_divides:
      forall f c pc1 id ofs n reg regs pc2 pcs (Hdivides: ((align_chunk kappa) | (Int.unsigned ofs))),
        c!pc1 = Some (Iop (Oaddrsymbol id ofs) nil reg (Psucc pc1)) ->
        tr_global_regs kappa f c (Psucc pc1) id (Int.add ofs Int.one) n regs pc2 pcs ->
        Plt (max_reg_function f) reg ->
        ~ In reg regs ->
        tr_global_regs kappa f c pc1 id ofs (S n) (reg::regs) pc2 (pc1::pcs)
  | tr_global_regs_S_not_divides:
      forall f c pc1 id ofs n regs pc2 pcs (Hdivides: ~ ((align_chunk kappa) | (Int.unsigned ofs))),
        tr_global_regs kappa f c pc1 id (Int.add ofs Int.one) n regs pc2 pcs ->
        tr_global_regs kappa f c pc1 id ofs (S n) regs pc2 pcs
  | tr_global_regs_O:
      forall f c pc id ofs,
        tr_global_regs kappa f c pc id ofs O nil pc nil.

  Inductive tr_global_regs': RTL.function -> code -> node -> ident -> Int.int -> Int.int -> (reg + (reg * reg)) -> node -> list node -> Prop :=
  | tr_global_regs_eq:
      forall f c pc1 base bound id r pc2,
        base = bound ->
        c!pc1 = Some (Iop (Oaddrsymbol id base) nil r pc2) ->
        pc2 = Pos.succ pc1
        Plt (max_reg_function f) r ->
        tr_global_regs' f c pc1 id base bound (inl r) pc2 (pc1::nil)
  | tr_global_regs_neq:
      forall f c pc1 base bound id r pc2,
        base <> bound ->
        c!pc1 = Some (Iop (Oaddrsymbol id base) nil r (Psucc pc1)) ->
        c!(Psucc pc1) = Some (Iop (Oaddrsymbol id bound) nil (Psucc r) pc2) ->
        pc2 = Pos.succ (Pos.succ pc1) →
        Plt (max_reg_function f) r ->
        tr_global_regs' f c pc1 id base bound (inr (r, Psucc r)) pc2 (pc1::(Psucc pc1)::nil).

  Inductive tr_regs_annot kappa : RTL.function -> code -> node -> list ablock -> list reg -> list reg -> node -> list node -> Prop :=
  | tr_regs_annot_nil:
      forall f c pc,
        tr_regs_annot kappa f c pc nil nil nil pc nil
  | tr_regs_annot_local_cons:
      forall f c pc1 pc2 pc3 depth varname base bound alpha regs1 regs1' regs2 regs2' pcs1 pcs2,
        tr_regs_annot kappa f c pc2 alpha regs2 regs2' pc3 pcs2 ->
        tr_local_regs kappa f c pc1 base depth (Z.to_nat (Int.unsigned (Int.sub bound base) + 1)) regs1 regs1' pc2 pcs1 ->
        list_disjoint regs1 regs2 ->
        list_disjoint regs1' regs2' ->
        list_disjoint regs1 regs2' ->
        list_disjoint regs1' regs2 ->
        list_disjoint regs2 regs2' ->
        tr_regs_annot kappa f c pc1 ((ABlocal depth varname (base, bound))::alpha) (regs1 ++ regs2) (regs1' ++ regs2') pc3 (pcs1 ++ pcs2)
  | tr_regs_annot_global_cons:
      forall f c pc1 pc2 pc3 id b base bound alpha regs1 regs2 regs2' pcs1 pcs2,
        Genv.find_symbol ge b = Some id ->
        tr_regs_annot kappa f c pc2 alpha regs2 regs2' pc3 pcs2 ->
        tr_global_regs kappa f c pc1 b base (Z.to_nat (Int.unsigned (Int.sub bound base) + 1)) regs1 pc2 pcs1 ->
        list_disjoint regs1 regs2 ->
        list_disjoint regs1 regs2' ->
        list_disjoint regs2 regs2' ->
        tr_regs_annot kappa f c pc1 ((ABglobal b (base, bound))::alpha) (regs1 ++ regs2) regs2' pc3 (pcs1 ++ pcs2).

  Inductive tr_regs_annot': RTL.function -> code -> node -> list ablock -> list (reg + (reg * reg)) -> list reg -> node -> list node -> Prop :=
  | tr_regs_annot_nil':
      forall f c pc,
      tr_regs_annot' f c pc nil nil nil pc nil
  | tr_regs_annot_local_cons':
      forall f c pc1 pc2 pc3 depth varname base bound alpha regs1 regs1' regs2 regs2' pcs1 pcs2,
        tr_regs_annot' f c pc2 alpha regs2 regs2' pc3 pcs2 ->
        tr_local_regs' f c pc1 depth base bound regs1 regs1' pc2 pcs1 ->
        list_norepet (fold_right (fun x acc => match x with inl r => r::acc | inr (r, r') => r::r'::acc end) nil (regs1::regs2)) ->
        list_disjoint regs1' regs2' ->
        list_disjoint (match regs1 with |inl r => r ::nil | inr (r, r') => r::r'::nil end) regs2' ->
        list_disjoint regs1' (fold_right (fun x acc => match x with inl r => r::acc | inr (r, r') => r::r'::acc end) nil regs2) ->
        list_disjoint (fold_right (fun x acc => match x with inl r => r::acc | inr (r, r') => r::r'::acc end) nil regs2) regs2' ->
        tr_regs_annot' f c pc1 ((ABlocal depth varname (base, bound))::alpha) (regs1::regs2) (regs1' ++ regs2') pc3 (pcs1 ++ pcs2)
  | tr_regs_annot_global_cons':
      forall f c pc1 pc2 pc3 b id base bound alpha regs1 regs2 regs2' pcs1 pcs2,
        Genv.find_symbol ge id = Some b ->
        tr_regs_annot' f c pc2 alpha regs2 regs2' pc3 pcs2 ->
        tr_global_regs' f c pc1 id base bound regs1 pc2 pcs1 ->
        list_norepet (fold_right (fun x acc => match x with inl r => r::acc | inr (r, r') => r::r'::acc end) nil (regs1::regs2)) ->
        list_disjoint (match regs1 with |inl r => r ::nil | inr (r, r') => r::r'::nil end) regs2' ->
        list_disjoint (fold_right (fun x acc => match x with inl r => r::acc | inr (r, r') => r::r'::acc end) nil regs2) regs2' ->
        tr_regs_annot' f c pc1 ((ABglobal id (base, bound))::alpha) (regs1::regs2) regs2' pc3 (pcs1 ++ pcs2).

  Inductive tr_cmp_regs: code -> node -> reg -> list reg -> node -> list node -> Prop :=
  | tr_comp_regs_nil:
      forall c pc reg0,
        tr_cmp_regs c pc reg0 nil pc nil
  | tr_comp_regs_cons:
      forall c pc1 pc2 reg0 reg1 regs pcs,
        c!pc1 = Some (Iop (Ocmp (Ccompu Ceq)) (reg0::reg1::nil) reg1 (Psucc pc1)) ->
        tr_cmp_regs c (Psucc pc1) reg0 regs pc2 pcs ->
        tr_cmp_regs c pc1 reg0 (reg1::regs) pc2 (pc1::pcs).

  Inductive tr_cmp_regs': code -> node -> reg -> list (reg + (reg * reg))-> node -> list node -> Prop :=
  | tr_comp_regs_nil':
      forall c pc reg0,
        tr_cmp_regs' c pc reg0 nil pc nil
  | tr_comp_regs_cons1':
      forall c pc1 pc2 reg0 reg1 regs pcs,
        c!pc1 = Some (Iop (Ocmp (Ccompu Ceq)) (reg1::reg0::nil) reg1 (Psucc pc1)) ->
        tr_cmp_regs' c (Psucc pc1) reg0 regs pc2 pcs ->
        tr_cmp_regs' c pc1 reg0 ((inl reg1)::regs) pc2 (pc1::pcs)
  | tr_comp_regs_cons2':
      forall c pc1 pc2 reg0 reg1 reg2 regs pcs,
        c!pc1 = Some (Iop (Ocmp (Ccompu Cle)) (reg1::reg0::nil) reg1 (Psucc pc1)) ->
        c!(Psucc pc1) = Some (Iop (Ocmp (Ccompu Cle)) (reg0::reg2::nil) reg2 (Psucc (Psucc pc1))) ->
        c!(Psucc (Psucc pc1)) = Some (Iop Oand (reg1::reg2::nil) reg1 (Psucc (Psucc (Psucc pc1)))) ->
        tr_cmp_regs' c (Psucc (Psucc (Psucc pc1))) reg0 regs pc2 pcs ->
        tr_cmp_regs' c pc1 reg0 ((inr (reg1, reg2))::regs) pc2 (pc1 :: Pos.succ pc1 :: Pos.succ (Pos.succ pc1) :: pcs).

  Inductive tr_or_regs: code -> node -> reg -> list reg -> node -> list node -> Prop :=
  | tr_or_regs_nil:
      forall c pc reg0,
        tr_or_regs c pc reg0 nil pc nil
  | tr_or_regs_cons:
      forall c pc1 pc2 reg0 reg1 regs pcs,
        c!pc1 = Some (Iop Oor (reg0::reg1::nil) reg0 (Psucc pc1)) ->
        tr_or_regs c (Psucc pc1) reg0 regs pc2 pcs ->
        tr_or_regs c pc1 reg0 (reg1::regs) pc2 (pc1::pcs).

  Inductive tr_return_undef: code -> node -> ident -> list node -> Prop :=
  | tr_return_undef_intro:
      forall c pc n reg0 reg1 reg,
        c!pc = Some (Iop (Ointconst Int.zero) nil reg0 (Psucc pc)) ->
        c!(Psucc pc) = Some (Iop (Ointconst (Int.repr (Zpos n))) nil reg1 (Psucc (Psucc pc))) ->
        c!(Psucc (Psucc pc)) = Some (Iop Odiv (reg1::reg0::nil) reg (Psucc (Psucc (Psucc pc)))) ->
        reg0 <> reg1 ->
        tr_return_undef c pc n (pc::(Psucc pc)::(Psucc (Psucc pc))::nil).

  Inductive tr_checks: RTL.function -> code -> node -> (annotation -> memory_chunk -> addressing -> list reg -> instruction) -> annotation -> memory_chunk -> addressing -> list reg -> list node -> Prop :=
  | tr_checks_intro:
      forall f c pc1 i alpha kappa addr args reg0 reg1 regs regs' regs2 pc3 pc4 pc5 pcs1 pcs2 pcs3 pcs4,
        c!pc1 = Some (Iop (match addr with | Aindexed n => if Int.eq_dec n Int.zero then Omove else Olea addr | _ => Olea addr end) args reg0 (Psucc pc1)) ->
        tr_regs_annot kappa f c (Psucc pc1) (snd alpha) regs regs2 pc3 pcs1->
        tr_cmp_regs c pc3 reg0 regs pc4 pcs2 ->
        regs = reg1::regs' ->
        c!pc4 = Some (Iop Omove (reg1::nil) reg0 (Psucc pc4)) ->
        tr_or_regs c (Psucc pc4) reg0 regs' pc5 pcs3 ->
        c!pc5 = Some (Icond (Ccompuimm Cne Int.zero) (reg0::nil) (Psucc pc5) (Psucc (Psucc pc5))) ->
        c!(Psucc pc5) = Some (i alpha kappa addr args) ->
        tr_return_undef c (Psucc (Psucc pc5)) (fst alpha) pcs4 ->
        Plt (max_reg_function f) reg0 ->
        ~ In reg0 regs ->
        ~ In reg0 regs2 ->
        tr_checks f c pc1 i alpha kappa addr args (pc1::pc4::pc5::(Psucc pc5)::(pcs1++pcs2++pcs3++pcs4)).

  Inductive tr_checks': RTL.function -> code -> node -> (annotation -> memory_chunk -> addressing -> list reg -> instruction) -> annotation -> memory_chunk -> addressing -> list reg -> list node -> Prop :=
  | tr_checks_intro':
      forall f c pc1 i alpha kappa addr args reg0 reg1 regs regs' regs2 pc3 pc4 pc5 pcs1 pcs2 pcs3 pcs4,
        c!pc1 = Some (Iop (match addr with | Aindexed n => if Int.eq_dec n Int.zero then Omove else Olea addr | _ => Olea addr end) args reg0 (Psucc pc1)) ->
        tr_regs_annot' f c (Psucc pc1) (snd alpha) regs regs2 pc3 pcs1->
        tr_cmp_regs' c pc3 reg0 regs pc4 pcs2 ->
        map (fun x => match x with | inl r => r | inr r => fst r end) regs = reg1::regs' ->
        c!pc4 = Some (Iop Omove (reg1::nil) reg0 (Psucc pc4)) ->
        tr_or_regs c (Psucc pc4) reg0 regs' pc5 pcs3 ->
        c!pc5 = Some (Icond (Ccompuimm Cne Int.zero) (reg0::nil) (Psucc pc5) (Psucc (Psucc pc5))) ->
        c!(Psucc pc5) = Some (i alpha kappa addr args) ->
        tr_return_undef c (Psucc (Psucc pc5)) (fst alpha) pcs4 ->
        Plt (max_reg_function f) reg0 ->
        ~ In reg0 (fold_right (fun x acc => match x with | inl r => r::acc | inr (r, r') => r::r'::acc end) nil regs) ->
        ~ In reg0 regs2 ->
        tr_checks' f c pc1 i alpha kappa addr args (pc1::pc4::pc5::(Psucc pc5)::(pcs1++pcs2++pcs3++pcs4)).

  Inductive tr_epilogue: RTL.function -> code -> node -> instruction -> list node -> Prop :=
  | tr_epilogue_intro:
      forall f c pc1 r r' i,
        c!pc1 = Some (Iload (xH, nil) Mint32 (Aglobal SIZE Int.zero) nil r (Psucc pc1)) ->
        c!(Psucc pc1) = Some (Iop (Ointconst (Int.repr 4)) nil r' (Psucc (Psucc pc1))) ->
        c!(Psucc (Psucc pc1)) = Some (Iop Osub (r::r'::nil) r (Psucc (Psucc (Psucc pc1)))) ->
        c!(Psucc (Psucc (Psucc pc1))) = Some (Istore (xH, nil) Mint32 (Aglobal SIZE Int.zero) nil r (Psucc (Psucc (Psucc (Psucc pc1))))) ->
        c!(Psucc (Psucc (Psucc (Psucc pc1)))) = Some i ->
        r <> r' ->
        Plt (max_reg_function f) r ->
        Plt (max_reg_function f) r' ->
        tr_epilogue f c pc1 i (pc1::(Psucc pc1)::(Psucc (Psucc pc1))::(Psucc (Psucc (Psucc pc1)))::(Psucc (Psucc (Psucc (Psucc pc1))))::nil).

  Fixpoint int_ranges (n: nat) (ofs: int) :=
    match n with
    | O => nil
    | S n => (Int.unsigned ofs)::(int_ranges n (Int.add ofs Int.one))
    end.
  
  Inductive tr_instrs: RTL.function -> node -> instruction -> code -> list node -> Prop :=
  | tr_Inop:
      forall f pc s c,
        c!pc = Some (Inop s) ->
        tr_instrs f pc (Inop s) c (pc::nil)
  | tr_Iop:
      forall f pc op args dst s c,
        c!pc = Some (Iop op args dst s) ->
        tr_instrs f pc (Iop op args dst s) c (pc::nil)
  | tr_Iload:
      forall f pc alpha kappa addr args dst s c pc1 pcs
        (Hlocrange: forall depth varname base bound, In (ABlocal depth varname (base, bound)) (snd alpha) ->
                                     Int.unsigned base <= Int.unsigned bound < Int.modulus - 1)
        (Hglobrange: forall id base bound, In (ABglobal id (base, bound)) (snd alpha) -> Int.unsigned base <= Int.unsigned bound < Int.modulus - 1)
        (Hdivides: check_annotations_divides kappa (snd alpha) = OK tt),
        c!pc = Some (Inop pc1) ->
        (if is_trivial_annotation prog alpha kappa addr then
           c!pc1 = Some (Iload alpha kappa addr args dst s)
         else if is_singleton (snd alpha) then
                tr_checks' f c pc1 (fun α κ addr args => Iload α κ addr args dst s) alpha kappa addr args pcs
              else tr_checks f c pc1 (fun α κ addr args => Iload α κ addr args dst s) alpha kappa addr args pcs) ->
        (forall depth varname range, In (ABlocal depth varname range) (snd alpha) -> (depth < 128)%nat) ->
        (forall id range, In (ABglobal id range) (snd alpha) -> (id <> STK /\ id <> SIZE)) ->
        (forall depth varname base bound, In (ABlocal depth varname (base, bound)) (snd alpha) ->
                                     forall size, Int.unsigned (Int.sub bound base) + 1 = Z.pos size ->
                                             exists x, In x (int_ranges (Pos.to_nat size) base) /\ ((align_chunk kappa) | x)) ->
        tr_instrs f pc (Iload alpha kappa addr args dst s) c (if is_trivial_annotation prog alpha kappa addr then pc::pc1::nil else pc::pcs)
  | tr_Istore:
      forall f pc alpha kappa addr args src s c pc1 pcs
        (Hlocrange: forall depth varname base bound, In (ABlocal depth varname (base, bound)) (snd alpha) ->
                                     Int.unsigned base <= Int.unsigned bound < Int.modulus - 1)
        (Hglobrange: forall id base bound, In (ABglobal id (base, bound)) (snd alpha) -> Int.unsigned base <= Int.unsigned bound < Int.modulus - 1)
        (Hdivides: check_annotations_divides kappa (snd alpha) = OK tt),
        c!pc = Some (Inop pc1) ->
        (if is_trivial_annotation prog alpha kappa addr then
           c!pc1 = Some (Istore alpha kappa addr args src s)
         else if is_singleton (snd alpha) then
                tr_checks' f c pc1 (fun α κ addr args => Istore α κ addr args src s) alpha kappa addr args pcs
              else tr_checks f c pc1 (fun α κ addr args => Istore α κ addr args src s) alpha kappa addr args pcs) ->
        (forall depth varname range, In (ABlocal depth varname range) (snd alpha) -> (depth < 128)%nat) ->
        (forall id range, In (ABglobal id range) (snd alpha) -> (id <> STK /\ id <> SIZE)) ->
        (forall depth varname base bound, In (ABlocal depth varname (base, bound)) (snd alpha) ->
                                     forall size, Int.unsigned (Int.sub bound base) + 1 = Z.pos size ->
                                             exists x, In x (int_ranges (Pos.to_nat size) base) /\ ((align_chunk kappa) | x)) ->
        tr_instrs f pc (Istore alpha kappa addr args src s) c (if is_trivial_annotation prog alpha kappa addr then pc::pc1::nil else pc::pcs)
  | tr_Icall:
      forall f pc sig fn args dst s c,
        c!pc = Some (Icall sig fn args dst s) ->
        tr_instrs f pc (Icall sig fn args dst s) c (pc::nil)
  | tr_Itailcall:
      forall f pc sig fn args c pc1 pcs,
        c!pc = Some (Inop pc1) ->
        tr_epilogue f c pc1 (Itailcall sig fn args) pcs ->
        tr_instrs f pc (Itailcall sig fn args) c (pc::pcs)
  | tr_Ibuiltin:
      forall f pc ef args dst s c,
        c!pc = Some (Ibuiltin ef args dst s) ->
        ~ In STK (globals_of_builtin_args args) ->
        ~ In SIZE (globals_of_builtin_args args) ->
        tr_instrs f pc (Ibuiltin ef args dst s) c (pc::nil)
  | tr_Icond:
      forall f pc cond args ifso ifnot c,
        c!pc = Some (Icond cond args ifso ifnot) ->
        tr_instrs f pc (Icond cond args ifso ifnot) c (pc::nil)
  | tr_Ijumptable:
      forall f pc arg tbl c,
        c!pc = Some (Ijumptable arg tbl) ->
        tr_instrs f pc (Ijumptable arg tbl) c (pc::nil)
  | tr_Ireturn:
      forall f pc r c pc1 pcs,
        c!pc = Some (Inop pc1) ->
        tr_epilogue f c pc1 (Ireturn r) pcs ->
        tr_instrs f pc (Ireturn r) c (pc::pcs).

  Inductive sincr (s1 s2: state): Prop :=
  | sincr_intro:
      forall (NEXTREG: Ple s1.(st_nextreg) s2.(st_nextreg))
        (NEXTNODE: Ple s1.(st_nextnode) s2.(st_nextnode)),
        sincr s1 s2.

  Lemma sincr_refl:
    forall s, sincr s s.
Proof.
    econstructor; xomega.
  Qed.
  
  Lemma sincr_transitive:
    forall s1 s2 s3,
      sincr s1 s2 ->
      sincr s2 s3 ->
      sincr s1 s3.
Proof.
    intros; inv H; inv H0; econstructor; xomega.
  Qed.

  Definition unchanged s s' : Prop :=
      forall pc,
        Plt pc s.(st_nextnode) ->
        s.(st_code)!pc = s'.(st_code)!pc.

  Lemma unchanged_refl {s} : unchanged s s.
Proof.
intros ? ?; reflexivity. Qed.

  Lemma unchanged_and_sincr_refl {s} : unchanged s ssincr s s.
Proof.
split. apply unchanged_refl. apply sincr_refl. Qed.

  Lemma sincr_nextnode x y : sincr x y → (st_nextnode x <= st_nextnode y)%positive.
Proof.
intros (); auto. Qed.

  Lemma unchanged_trans x y z : unchanged y zunchanged x ysincr x yunchanged x z.
Proof.
    intros Hyz Hxy Hi pc Hpc.
    etransitivity. apply Hxy, Hpc.
    apply Hyz. apply sincr_nextnode in Hi. unfold Plt in *. Psatz.lia.
  Qed.

  Lemma unchanged_and_sincr_trans x y z :
    unchanged y zsincr y z
    unchanged x ysincr x y
    unchanged x zsincr x z.
Proof.
    intros [ Uyz Syz ] [ Uxy Sxy ].
    split. eauto using unchanged_trans. eauto using sincr_transitive.
  Qed.

  Lemma add_instr_sincr:
    forall i s s',
      add_instr i s = s' ->
      sincr s s'.
Proof.
    simpl; intros.
    unfold add_instr in H. inv H.
    simpl; constructor; simpl; xomega.
  Qed.

  Lemma add_instr_unchanged:
    forall i s s',
      add_instr i s = s' ->
      forall pc,
        Plt pc s.(st_nextnode) ->
        s.(st_code)!pc = s'.(st_code)!pc.
Proof.
    simpl; intros.
    unfold add_instr in H. inv H. simpl.
    rewrite PTree.gso; auto; xomega.
  Qed.

  Lemma add_instr_unchanged_and_sincr :
    ∀ {i s},
      unchanged s (add_instr i s) ∧ sincr s (add_instr i s).
Proof.
    intros i s. split.
    refine (add_instr_unchanged _ _ _ _). reflexivity.
    refine (add_instr_sincr _ _ _ _). reflexivity.
  Qed.

  Lemma make_zero_for_type_unchanged_and_sincr {ty r s} :
    unchanged s (make_zero_for_type ty r s) ∧ sincr s (make_zero_for_type ty r s).
Proof.
    unfold make_zero_for_type.
    destruct ty;
      try (apply unchanged_and_sincr_refl);
      apply add_instr_unchanged_and_sincr.
  Qed.
    
  Lemma new_reg_unchanged_and_sincr :
    ∀ {s r s'},
      new_reg s = (r, s') →
      unchanged s s' ∧ sincr s s'.
Proof.
    unfold new_reg.
    intros s r s' H. Util.eq_some_inv. subst s' r. split.
    intros ? ?; reflexivity.
    constructor; simpl; unfold Ple; Psatz.lia.
  Qed.

  Lemma add_return_undef_unchanged_and_sincr :
    ∀ {t x s},
      unchanged s (add_return_undef t x s) ∧ sincr s (add_return_undef t x s).
Proof.
    intros t x s. unfold add_return_undef.
    destruct (new_reg s) as [ r0 s0] eqn: NR0.
    destruct (new_reg s0) as [ r1 s1] eqn: NR1.
    destruct (new_reg s1) as [ r2 s2] eqn: NR2.
    destruct t.
    - eapply unchanged_and_sincr_trans. apply add_instr_unchanged_and_sincr.
      eapply unchanged_and_sincr_trans. apply make_zero_for_type_unchanged_and_sincr.
      repeat (eapply unchanged_and_sincr_trans; [ apply add_instr_unchanged_and_sincr |]).
      eapply unchanged_and_sincr_trans. eapply new_reg_unchanged_and_sincr; eassumption.
      eapply unchanged_and_sincr_trans. eapply new_reg_unchanged_and_sincr; eassumption.
      eapply new_reg_unchanged_and_sincr; eassumption.
    - repeat (eapply unchanged_and_sincr_trans; [ apply add_instr_unchanged_and_sincr |]).
      eapply unchanged_and_sincr_trans. eapply new_reg_unchanged_and_sincr; eassumption.
      eapply unchanged_and_sincr_trans. eapply new_reg_unchanged_and_sincr; eassumption.
      eapply new_reg_unchanged_and_sincr; eassumption.
  Qed.

  Lemma put_stack_range_in_regs_unchanged_and_sincr:
    forall {kappa n ofs depth s regs s'},
      put_stack_range_in_regs STK SIZE kappa ofs depth n s = OK (regs, s') ->
      unchanged s s' ∧ sincr s s'.
Proof.
    Opaque new_reg.
    intros kappa n ofs depth. revert ofs.
    elim n; clear n.
    - intros ofs s regs s' H; inv H. apply unchanged_and_sincr_refl.
    - intros n IH ofs s regs s'.
      simpl.
      case Zdivides_dec; intros D;
        repeat match goal with |- appcontext[ new_reg ?x ] => destruct (new_reg x) eqn: ? end;
        intros H; monadInv H.
      + eapply unchanged_and_sincr_trans. eapply IH; eauto.
        repeat (eapply unchanged_and_sincr_trans; [ apply add_instr_unchanged_and_sincr |]).
        eapply unchanged_and_sincr_trans. eapply new_reg_unchanged_and_sincr; eauto.
        eapply unchanged_and_sincr_trans. eapply new_reg_unchanged_and_sincr; eauto.
        eapply new_reg_unchanged_and_sincr; eauto.
      + eapply IH; eauto.
    Transparent new_reg.
  Qed.

  Lemma put_symbol_range_in_regs_unchanged_and_sincr:
    ∀ {kappa n id ofs s regs s'},
      put_symbol_range_in_regs kappa id ofs n s = OK (regs, s') ->
      unchanged s s' ∧ sincr s s'.
Proof.
    Opaque new_reg.
    intros kappa n id.
    elim n; clear n.
    - intros ofs s regs s' H; inv H. apply unchanged_and_sincr_refl.
    - intros n IH ofs s regs s'.
      simpl.
      case Zdivides_dec; intros D;
        repeat match goal with |- appcontext[ new_reg ?x ] => destruct (new_reg x) eqn: ? end;
        intros H; monadInv H.
      + eapply unchanged_and_sincr_trans. eapply IH; eauto.
        repeat (eapply unchanged_and_sincr_trans; [ apply add_instr_unchanged_and_sincr |]).
        eapply new_reg_unchanged_and_sincr; eauto.
      + eapply IH; eauto.
    Transparent new_reg.
  Qed.

  Lemma put_all_in_regs_unchanged_and_sincr:
    forall kappa alpha s regs s',
      put_all_in_regs ge STK SIZE kappa alpha s = OK (regs, s') ->
      unchanged s s' ∧ sincr s s'.
Proof.
    intros ϰ α; elim α; clear α.
    intros s regs s' H; inv H; apply unchanged_and_sincr_refl.
    simpl.
    intros [ δ _ (base, bound) | b (base, bound) ] α IH s regs s';
      case (_ + _); try discriminate;
        intros p.
    - intros H; monadInv H.
      eapply unchanged_and_sincr_trans. eapply IH; eauto.
      eapply put_stack_range_in_regs_unchanged_and_sincr; eauto.
    - case Genv.find_symbol. 2: discriminate.
      intros _ H; monadInv H.
      eapply unchanged_and_sincr_trans. eapply IH; eauto.
      eapply put_symbol_range_in_regs_unchanged_and_sincr; eauto.
  Qed.

  Lemma put_checks_unchanged_and_sincr {r rs s s'} :
      put_checks r rs s = OK s' →
      unchanged s s' ∧ sincr s s'.
Proof.
    revert s. elim rs; clear rs.
    intros s H; inv H; apply unchanged_and_sincr_refl.
    intros a rs IH s H.
    eapply unchanged_and_sincr_trans. exact (IH _ H).
    apply add_instr_unchanged_and_sincr.
  Qed.

  Corollary put_checks_sincr r rs s s' :
      put_checks r rs s = OK s' →
      sincr s s'.
Proof.
exactH, proj2 (put_checks_unchanged_and_sincr H)). Qed.

  Lemma put_ors_sincr:
    forall reg0 regs s s',
      put_ors reg0 regs s = OK s' ->
      sincr s s'.
Proof.
    induction regs; intros.
    - inv H; constructor; xomega.
    - simpl in H. eapply IHregs in H.
      eapply sincr_transitive; eauto.
      eapply add_instr_sincr; eauto.
  Qed.

  Lemma put_ors_unchanged:
    forall reg0 regs s s',
      put_ors reg0 regs s = OK s' ->
      forall pc,
        Plt pc s.(st_nextnode) ->
        s.(st_code)!pc = s'.(st_code)!pc.
Proof.
    induction regs; intros.
    - inv H. reflexivity.
    - simpl in H. generalize (IHregs _ _ H pc). intros.
      rewrite <- H1.
      + eapply add_instr_unchanged; eauto.
      + generalize (add_instr_sincr (Iop Oor (reg0 :: a :: nil) reg0 (Pos.succ (st_nextnode s))) s _ eq_refl).
        intros A; inv A. xomega.
  Qed.

  Lemma put_ors_unchanged_and_sincr :
    ∀ {r rs s s'},
      put_ors r rs s = OK s' →
      unchanged s s' ∧ sincr s s'.
Proof.
    intros r rs s s' H. split.
    refine (put_ors_unchanged _ _ _ _ H).
    refine (put_ors_sincr _ _ _ _ H).
  Qed.

  Lemma put_stack_range_in_regs'_unchanged_and_sincr :
    ∀ {base bound δ s out s'},
      put_stack_range_in_regs' STK SIZE base bound δ s = OK (out, s') →
      unchanged s s' ∧ sincr s s'.
Proof.
    intros base bound δ s out s'.
    unfold put_stack_range_in_regs'. case Int.eq_dec.
    - intros ->.
      destruct (new_reg s) as [ r0 σ0 ] eqn: NR0.
      destruct (new_reg σ0) as [ r1 σ1 ] eqn: NR1.
      destruct (new_reg σ1) as [ r2 σ2 ] eqn: NR2.
      intros H; inv H.
      repeat (eapply unchanged_and_sincr_trans; [ apply add_instr_unchanged_and_sincr |]).
      eapply unchanged_and_sincr_trans. eapply new_reg_unchanged_and_sincr; eassumption.
      eapply unchanged_and_sincr_trans. eapply new_reg_unchanged_and_sincr; eassumption.
      eapply new_reg_unchanged_and_sincr; eassumption.
    - intros NE.
      destruct (new_reg s) as [ r0 σ0 ] eqn: NR0.
      destruct (new_reg σ0) as [ r1 σ1 ] eqn: NR1.
      destruct (new_reg σ1) as [ r2 σ2 ] eqn: NR2.
      destruct (new_reg σ2) as [ r3 σ3 ] eqn: NR3.
      intros H; inv H.
      repeat (eapply unchanged_and_sincr_trans; [ apply add_instr_unchanged_and_sincr |]).
      eapply unchanged_and_sincr_trans. eapply new_reg_unchanged_and_sincr; eassumption.
      eapply unchanged_and_sincr_trans. eapply new_reg_unchanged_and_sincr; eassumption.
      eapply unchanged_and_sincr_trans. eapply new_reg_unchanged_and_sincr; eassumption.
      eapply new_reg_unchanged_and_sincr; eassumption.
  Qed.

  Lemma put_symbol_range_in_regs'_unchanged_and_sincr :
    ∀ {b base bound s out s'},
      put_symbol_range_in_regs' b base bound s = OK (out, s') →
      unchanged s s' ∧ sincr s s'.
Proof.
    intros b base bound s out s'.
    unfold put_symbol_range_in_regs'.
    case Int.eq_dec.
    - intros ->. destruct (new_reg s) eqn: NR.
      intros H; inv H.
      eapply unchanged_and_sincr_trans. apply add_instr_unchanged_and_sincr.
      eapply new_reg_unchanged_and_sincr; eassumption.
    - intros NE.
      destruct (new_reg s) as [ r0 σ0 ] eqn: NR0.
      destruct (new_reg σ0) as [ r1 σ1 ] eqn: NR1.
      intros H; inv H.
      eapply unchanged_and_sincr_trans. apply add_instr_unchanged_and_sincr.
      eapply unchanged_and_sincr_trans. apply add_instr_unchanged_and_sincr.
      eapply unchanged_and_sincr_trans. eapply new_reg_unchanged_and_sincr; eassumption.
      eapply new_reg_unchanged_and_sincr; eassumption.
  Qed.

  Lemma put_all_in_regs'_unchanged_and_sincr :
    ∀ {α s out s'},
      put_all_in_regs' ge STK SIZE α s = OK (out, s') →
      unchanged s s' ∧ sincr s s'.
Proof.
    intros α; elim α; clear α.
    intros s out s' H; inv H. split. apply unchanged_refl. apply sincr_refl.
    simpl.
    intros [ δ _ (base, bound) | b (base, bound) ] α IH s out s' H; monadInv H;
      (eapply unchanged_and_sincr_trans; [ eapply IH; eauto |]).
    - eapply put_stack_range_in_regs'_unchanged_and_sincr; eassumption.
    - eapply put_symbol_range_in_regs'_unchanged_and_sincr; eassumption.
  Qed.

  Lemma put_checks'_unchanged_and_sincr :
    ∀ {r rs s s'} ,
      put_checks' r rs s = OK s' →
      unchanged s s' ∧ sincr s s'.
Proof.
    intros r rs. elim rs; clear rs.
    intros ? ? H; inv H. split. apply unchanged_refl. apply sincr_refl.
    intros [ x | [x x'] ] rs IH s s' H; specialize (IH _ _ H);
      eapply unchanged_and_sincr_trans; eauto.
    - apply add_instr_unchanged_and_sincr.
    - eapply unchanged_and_sincr_trans. apply add_instr_unchanged_and_sincr.
      eapply unchanged_and_sincr_trans. apply add_instr_unchanged_and_sincr.
      apply add_instr_unchanged_and_sincr.
  Qed.

  Lemma add_checks_unchanged_and_sincr :
    ∀ {opt i α ϰ addr args s s'},
      add_checks prog ge STK SIZE opt i α ϰ addr args s = OK s' →
      unchanged s s' ∧ sincr s s'.
Proof.
    intros opt i α ϰ addr args s s'.
    unfold add_checks.
    destruct (new_reg s) as [ r σ ] eqn: NR.
    apply new_reg_unchanged_and_sincr in NR.
    destruct is_trivial_annotation.
    { intros H; inv H. split. refine (add_instr_unchanged _ _ _ eq_refl). refine (add_instr_sincr _ _ _ eq_refl). }
    case is_singleton; intros H; monadInv H.
    - destruct map. discriminate. monadInv EQ2.
      eapply unchanged_and_sincr_trans. apply add_return_undef_unchanged_and_sincr.
      eapply unchanged_and_sincr_trans. apply add_instr_unchanged_and_sincr.
      eapply unchanged_and_sincr_trans. apply add_instr_unchanged_and_sincr.
      eapply unchanged_and_sincr_trans. eapply put_ors_unchanged_and_sincr; eauto.
      eapply unchanged_and_sincr_trans. apply add_instr_unchanged_and_sincr.
      eapply unchanged_and_sincr_trans. eapply put_checks'_unchanged_and_sincr; eauto.
      eapply unchanged_and_sincr_trans. eapply put_all_in_regs'_unchanged_and_sincr; eauto.
      eapply unchanged_and_sincr_trans. apply add_instr_unchanged_and_sincr.
      exact NR.
    - destruct x. discriminate. monadInv EQ2.
      eapply unchanged_and_sincr_trans. apply add_return_undef_unchanged_and_sincr.
      eapply unchanged_and_sincr_trans. apply add_instr_unchanged_and_sincr.
      eapply unchanged_and_sincr_trans. apply add_instr_unchanged_and_sincr.
      eapply unchanged_and_sincr_trans. eapply put_ors_unchanged_and_sincr; eauto.
      eapply unchanged_and_sincr_trans. apply add_instr_unchanged_and_sincr.
      eapply unchanged_and_sincr_trans. eapply put_checks_unchanged_and_sincr; eauto.
      eapply unchanged_and_sincr_trans. eapply put_all_in_regs_unchanged_and_sincr; eauto.
      eapply unchanged_and_sincr_trans. apply add_instr_unchanged_and_sincr.
      exact NR.
  Qed.

  Lemma add_checks_unchanged:
    forall opt i alpha kappa addr args s s',
      add_checks prog ge STK SIZE opt i alpha kappa addr args s = OK s' ->
      forall pc,
        Plt pc s.(st_nextnode) ->
        s.(st_code)!pc = s'.(st_code)!pc.
Proof.
    intros opt i alpha kappa addr args s s' H. eapply add_checks_unchanged_and_sincr; eauto.
  Qed.

  Lemma put_stack_range_in_regs_regs_spec:
    forall kappa n ofs depth s regs s',
      put_stack_range_in_regs STK SIZE kappa ofs depth n s = OK (regs, s') ->
      forall r, In r regs -> Ple (st_nextreg s) r /\ Plt r (st_nextreg s').
Proof.
    induction n; intros.
    - simpl in H; inv H; inv H0.
    - simpl in H. destruct (Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)).
      + monadInv H.
        simpl in H0. destruct H0 as [H0 | H0].
        * split; inv H0.
          { xomega. }
          { generalize ( put_stack_range_in_regs_unchanged_and_sincr EQ). intros [_ HA]; inv HA; simpl in NEXTREG; xomega. }
        * eapply IHn in EQ; eauto; simpl in EQ; xomega.
      + monadInv H. eapply IHn; eauto.
  Qed.

  Lemma not_in_cons:
    forall (A: Type) (x a: A) (l: list A),
      ~ In x (a::l) <-> x <> a /\ ~ In x l.
Proof.
    simpl; intuition.
  Qed.

  Lemma not_in_app:
    forall (A: Type) (x: A) (l1 l2: list A),
      ~ In x (l1 ++ l2) <-> ~ In x l1 /\ ~ In x l2.
Proof.
    simpl; intuition.
    eapply in_app_or in H0.
    destruct H0; auto.
  Qed.

  Lemma tr_local_regs_is_disjoint:
    forall kappa f c pc1 ofs depth n regs regs' pc2 pcs,
      tr_local_regs kappa f c pc1 ofs depth n regs regs' pc2 pcs ->
      list_disjoint regs regs'.
Proof.
    induction 1; intros.
    - unfold list_disjoint in *; intros.
      destruct H8 as [H8 | H8].
      + destruct H9 as [H9 | [H9 | H9]].
        * subst. eapply Pos.succ_discr; auto.
        * subst. eapply Plt_ne; xomega.
        * subst. rewrite not_in_app in H4. destruct H4.
          intro. rewrite H10 in H8. eapply H8; eauto.
      + destruct H9 as [H9 | [H9 | H9]].
        * subst. rewrite not_in_app in H5; destruct H5.
          intro. rewrite <- H10 in H5; eapply H5; eauto.
        * subst. rewrite not_in_app in H6; destruct H6.
          intro. rewrite <- H10 in H6; eapply H6; eauto.
        * subst. eapply H7; eauto.
    - eauto.
    - unfold list_disjoint; intros. inv H.
  Qed.

  Lemma put_stack_range_in_regs_spec':
    forall kappa f n ofs depth s regs s',
      put_stack_range_in_regs STK SIZE kappa ofs depth n s = OK (regs, s') ->
      Plt (max_reg_function f) s.(st_nextreg) ->
      exists regs' pcs, tr_local_regs kappa f s'.(st_code) s.(st_nextnode) ofs depth n regs regs' s'.(st_nextnode) pcs
                   /\ forall r, In r regs' -> Plt s.(st_nextreg) r /\ Plt r s'.(st_nextreg).
Proof.
    induction n; intros.
    - simpl in H; inv H.
      exists nil; exists nil; split; try econstructor.
      intros. inv H. inv H.
    - rename H0 into HPlt.
      simpl in H. destruct (Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)).
      { monadInv H.
        generalize (IHn _ _ _ _ _ EQ (Plt_trans_succ _ _ (Plt_trans_succ _ _ (Plt_trans_succ _ _ HPlt)))); intros A. destruct A as [regs' [pcs [A B]]]. simpl in B.
        eexists. eexists. split.
        { econstructor.
          + eauto.
          + erewrite <- (proj1 (put_stack_range_in_regs_unchanged_and_sincr EQ)).
            * simpl. rewrite PTree.gso; eauto. rewrite PTree.gso; eauto. rewrite PTree.gss; eauto. xomega. xomega.
            * simpl. xomega.
          + erewrite <- (proj1 (put_stack_range_in_regs_unchanged_and_sincr EQ)).
            * simpl. rewrite PTree.gso; eauto. rewrite PTree.gss; eauto. xomega.
            * simpl. xomega.
          + erewrite <- (proj1 (put_stack_range_in_regs_unchanged_and_sincr EQ)).
            * simpl. rewrite PTree.gss; eauto.
            * simpl. xomega.
          + replace (Pos.succ (st_nextnode s)) with (st_nextnode (add_instr
                                                                  (Iop (Oaddrstack ofs) nil (st_nextreg s)
                                                                       (Pos.succ (st_nextnode s)))
                                                                  {|
                                                                    st_nextreg := Pos.succ (st_nextreg s);
                                                                    st_nextnode := st_nextnode s;
                                                                    st_code := st_code s;
                                                                    st_wf := st_wf s |})); try reflexivity.
            eapply A.
          + assumption.
          + eapply not_in_app. split.
            * unfold not; intros. generalize (put_stack_range_in_regs_regs_spec _ _ _ _ _ _ _ EQ _ H).
              simpl. intros. xomega.
            * unfold not; intros. eapply B in H. xomega.
          + eapply not_in_app. split.
            * unfold not; intros. generalize (put_stack_range_in_regs_regs_spec _ _ _ _ _ _ _ EQ _ H).
              simpl. intros. xomega.
            * unfold not; intros. eapply B in H. xomega.
          + eapply not_in_app. split.
            * unfold not; intros. generalize (put_stack_range_in_regs_regs_spec _ _ _ _ _ _ _ EQ _ H).
              simpl. intros. xomega.
            * unfold not; intros. eapply B in H. xomega.
          + eapply tr_local_regs_is_disjoint; eauto. }
        { intros. destruct H as [H | [H | H]].
          - inv H; split; try xomega.
            generalize (put_stack_range_in_regs_unchanged_and_sincr EQ). intros [_ C]; inv C; simpl in *. xomega.
          - inv H; split; try xomega.
            generalize (put_stack_range_in_regs_unchanged_and_sincr EQ). intros [_ C]; inv C; simpl in *. xomega.
          - eapply B in H; try xomega. } }
      { monadInv H. exploit IHn; eauto. intros [regs' [pcs [HA HB]]].
        eexists. eexists. split; eauto.
        econstructor 2; eauto. }
  Qed.

  Lemma put_stack_range_in_regs_spec:
    forall kappa f n ofs depth s regs s',
      put_stack_range_in_regs STK SIZE kappa ofs depth n s = OK (regs, s') ->
      Plt (max_reg_function f) s.(st_nextreg) ->
      exists regs' pcs, tr_local_regs kappa f s'.(st_code) s.(st_nextnode) ofs depth n regs regs' s'.(st_nextnode) pcs.
Proof.
    intros. exploit put_stack_range_in_regs_spec'; eauto.
    intros [regs' [pcs [A B]]]; eauto.
  Qed.

  Lemma put_symbol_range_in_regs_regs_spec:
    forall kappa n id ofs s regs s',
      put_symbol_range_in_regs kappa id ofs n s = OK (regs, s') ->
      forall r, In r regs -> Ple (st_nextreg s) r /\ Plt r (st_nextreg s').
Proof.
    induction n; intros.
    - simpl in H; inv H; inv H0.
    - simpl in H. destruct (Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)); monadInv H.
      + simpl in H0. destruct H0 as [H0 | H0].
        * split; inv H0.
          { eapply Ple_refl. }
          { generalize (put_symbol_range_in_regs_unchanged_and_sincr EQ). intros [_ HA]; inv HA; simpl in NEXTREG; xomega. }
        * eapply IHn in EQ; eauto; simpl in EQ; xomega.
      + eapply IHn; eauto.
  Qed.

  Lemma put_symbol_range_in_regs_spec:
    forall kappa f n id ofs s regs s',
      put_symbol_range_in_regs kappa id ofs n s = OK (regs, s') ->
      Plt (max_reg_function f) s.(st_nextreg) ->
      exists pcs, tr_global_regs kappa f s'.(st_code) s.(st_nextnode) id ofs n regs s'.(st_nextnode) pcs.
Proof.
    induction n; intros.
    - simpl in H; inv H.
      exists nil; econstructor.
    - rename H0 into HPlt.
      simpl in H. destruct (Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)); monadInv H.
      { generalize (IHn id _ _ _ _ EQ (Plt_trans_succ _ _ HPlt)); intros A. destruct A as [pcs A].
        eexists. econstructor.
        + eauto.
        + erewrite <- (proj1 (put_symbol_range_in_regs_unchanged_and_sincr EQ)).
          * simpl. rewrite PTree.gss; eauto.
          * simpl. xomega.
        + replace (Pos.succ (st_nextnode s)) with (st_nextnode (add_instr
                                                                  (Iop (Oaddrsymbol id ofs) nil (st_nextreg s)
                                                                     (Pos.succ (st_nextnode s)))
                                                                {|
                                                                  st_nextreg := Pos.succ (st_nextreg s);
                                                                  st_nextnode := st_nextnode s;
                                                                  st_code := st_code s;
                                                                  st_wf := st_wf s |})); try reflexivity.
          eapply A.
        + assumption.
        + unfold not; intros; generalize (put_symbol_range_in_regs_regs_spec _ _ _ _ _ _ _ EQ _ H); simpl; intros; xomega. }
      { exploit IHn; eauto. intros [pcs A].
        eexists; econstructor 2; eauto. }
  Qed.

  Lemma tr_local_regs_incr:
    forall kappa f c n pc1 ofs depth regs regs' pc2 pcs,
      tr_local_regs kappa f c pc1 ofs depth n regs regs' pc2 pcs ->
      Ple pc1 pc2.
Proof.
    induction n; intros.
    - inv H; xomega.
    - inv H.
      + generalize (IHn _ _ _ _ _ _ _ H4); intros.
        xomega.
      + eapply IHn; eauto.
  Qed.

  Lemma tr_local_regs_unchanged:
    forall kappa f c c' n pc1 ofs depth regs regs' pc2 pcs,
      (forall pc, Plt pc pc2 ->
             c!pc = c'!pc) ->
      tr_local_regs kappa f c pc1 ofs depth n regs regs' pc2 pcs ->
      tr_local_regs kappa f c' pc1 ofs depth n regs regs' pc2 pcs.
Proof.
    induction n; intros.
    - inv H0. econstructor.
    - inv H0.
      { econstructor; eauto.
        + generalize (tr_local_regs_incr _ _ _ _ _ _ _ _ _ _ _ H5); intros.
          rewrite <- H; try xomega; eauto.
        + generalize (tr_local_regs_incr _ _ _ _ _ _ _ _ _ _ _ H5); intros.
          rewrite <- H; try xomega; eauto.
        + generalize (tr_local_regs_incr _ _ _ _ _ _ _ _ _ _ _ H5); intros.
          rewrite <- H; try xomega; eauto. }
      { econstructor 2; eauto. }
  Qed.

  Lemma tr_global_regs_incr:
    forall kappa f c id n pc1 ofs regs pc2 pcs,
      tr_global_regs kappa f c pc1 id ofs n regs pc2 pcs ->
      Ple pc1 pc2.
Proof.
    induction n; intros.
    - inv H; xomega.
    - inv H.
      + generalize (IHn _ _ _ _ _ H2); intros.
        xomega.
      + eapply IHn; eauto.
  Qed.

  Lemma tr_global_regs_unchanged:
    forall kappa f c c' id n pc1 ofs regs pc2 pcs,
      (forall pc, Plt pc pc2 ->
             c!pc = c'!pc) ->
      tr_global_regs kappa f c pc1 id ofs n regs pc2 pcs ->
      tr_global_regs kappa f c' pc1 id ofs n regs pc2 pcs.
Proof.
    induction n; intros.
    - inv H0. econstructor.
    - inv H0.
      + econstructor; eauto.
        generalize (tr_global_regs_incr _ _ _ _ _ _ _ _ _ _ H3); intros.
        rewrite <- H; try xomega; eauto.
      + econstructor 2; eauto.
  Qed.

  Lemma tr_regs_annot_incr:
    forall kappa f c alpha pc1 regs regs' pc2 pcs,
      tr_regs_annot kappa f c pc1 alpha regs regs' pc2 pcs ->
      Ple pc1 pc2.
Proof.
    induction alpha; intros.
    - inv H. xomega.
    - inv H.
      + eapply Ple_trans.
        * eapply tr_local_regs_incr; eauto.
        * eapply IHalpha; eauto.
      + eapply Ple_trans.
        * eapply tr_global_regs_incr; eauto.
        * eapply IHalpha; eauto.
  Qed.
    
  Lemma tr_regs_annot_unchanged:
    forall kappa f c c' alpha pc1 regs regs' pc2 pcs,
      (forall pc, Plt pc pc2 ->
             c!pc = c'!pc) ->
      tr_regs_annot kappa f c pc1 alpha regs regs' pc2 pcs ->
      tr_regs_annot kappa f c' pc1 alpha regs regs' pc2 pcs .
Proof.
    induction alpha; intros.
    - inv H0; econstructor; eauto.
    - inv H0.
      + econstructor; eauto.
        eapply (tr_local_regs_unchanged _ _ _ _ _ _ _ _ _ _ _ _ _ H4).
      + econstructor; eauto.
        eapply (tr_global_regs_unchanged _ _ _ _ _ _ _ _ _ _ _ _ H5).
    Grab Existential Variables.
    { generalize (tr_global_regs_incr _ _ _ _ _ _ _ _ _ _ H5). intros.
      generalize (tr_regs_annot_incr _ _ _ _ _ _ _ _ _ H4). intros.
      intros; eapply H; try xomega. }
    { generalize (tr_local_regs_incr _ _ _ _ _ _ _ _ _ _ _ H4).
      generalize (tr_regs_annot_incr _ _ _ _ _ _ _ _ _ H3). intros.
      intros; eapply H; xomega. }
  Qed.

  Lemma put_all_in_regs_regs_spec:
    forall kappa alpha s regs s',
      put_all_in_regs ge STK SIZE kappa alpha s = OK (regs, s') ->
      forall r, In r regs -> Ple (st_nextreg s) r /\ Plt r (st_nextreg s').
Proof.
    induction alpha; intros.
    - simpl in H; inv H; inv H0.
    - simpl in H; destruct a.
      + destruct range as [base bound]. destruct (Int.unsigned (Int.sub bound base) + 1); monadInv H.
        eapply in_app_or in H0. destruct H0.
        * generalize (put_stack_range_in_regs_regs_spec _ _ _ _ _ _ _ EQ r H). intros [HA HB]. split; eauto.
          generalize (put_all_in_regs_unchanged_and_sincr _ _ _ _ _ EQ1); intros [_ HC]; inv HC; xomega.
        * generalize (IHalpha _ _ _ EQ1 r H); intros [HA HB].
          split; eauto. generalize (put_stack_range_in_regs_unchanged_and_sincr EQ); intros [_ HC]; inv HC; xomega.
      + destruct range as [base bound]. destruct (Int.unsigned (Int.sub bound base) + 1); try monadInv H.
        destruct (Genv.find_symbol ge b); monadInv H.
        eapply in_app_or in H0. destruct H0.
        * generalize (put_symbol_range_in_regs_regs_spec _ _ _ _ _ _ _ EQ r H). intros [HA HB]. split; eauto.
          generalize (put_all_in_regs_unchanged_and_sincr _ _ _ _ _ EQ1); intros [_ HC]; inv HC; xomega.
        * generalize (IHalpha _ _ _ EQ1 r H); intros [HA HB].
          split; eauto. generalize (put_symbol_range_in_regs_unchanged_and_sincr EQ); intros [_ HC]; inv HC; xomega.
  Qed.

  Lemma tr_regs_annot_is_disjoint:
    forall kappa f c pc1 alpha regs regs' pc2 pcs,
      tr_regs_annot kappa f c pc1 alpha regs regs' pc2 pcs ->
      list_disjoint regs regs'.
Proof.
    unfold list_disjoint; induction 1; intros.
    - inv H.
    - rewrite in_app in H6, H7.
      destruct H6, H7.
      + eapply tr_local_regs_is_disjoint; eauto.
      + eapply H3; eauto.
      + generalize (H4 _ _ H7 H6); auto.
      + eapply H5; auto.
    - rewrite in_app in H5. destruct H5.
      + eapply H3; eauto.
      + eapply H4; eauto.
  Qed.

  Lemma put_all_in_regs_spec':
    forall kappa f alpha s regs s',
      put_all_in_regs ge STK SIZE kappa alpha s = OK (regs, s') ->
      Plt (max_reg_function f) (st_nextreg s) ->
      exists regs' pcs, tr_regs_annot kappa f s'.(st_code) s.(st_nextnode) alpha regs regs' s'.(st_nextnode) pcs
                   /\ forall r, In r regs' -> Ple (st_nextreg s) r /\ Plt r s'.(st_nextreg).
Proof.
    induction alpha; intros.
    - inv H. exists nil; exists nil; split; try econstructor; eauto.
      intros. inv H. inv H.
    - rename H0 into HPlt.
      simpl in H. destruct a.
      + destruct range as [base bound].
        case_eq (Int.unsigned (Int.sub bound base) + 1); intros; rewrite H0 in H; monadInv H.
        assert (HA: Plt (max_reg_function f) (st_nextreg x0)).
        { exploit put_all_in_regs_unchanged_and_sincr; eauto. intros [_ D]; inv D.
          generalize (put_stack_range_in_regs_unchanged_and_sincr EQ). intros [_ E]; inv E; xomega. }
        generalize (IHalpha _ _ _ EQ1 HA); intros A. destruct A as [regs' [pcs [A E]]].
        generalize (put_stack_range_in_regs_spec' kappa f _ _ _ _ _ _ EQ HPlt); intros B. destruct B as [regs'' [pcs' [B C]]].
        eexists. eexists. split.
        { econstructor; eauto.
          * rewrite H0. simpl. refine (tr_local_regs_unchanged _ _ _ _ _ _ _ _ _ _ _ _ _ B).
            eapply put_all_in_regs_unchanged_and_sincr; eauto.
          * unfold list_disjoint; intros.
            exploit put_stack_range_in_regs_regs_spec; eauto. intros [A1 A2].
            exploit put_all_in_regs_regs_spec; eauto. intros [A3 A4]. unfold not; intros.
            rewrite H2 in A2. xomega.
          * unfold list_disjoint; intros. eapply E in H1. eapply C in H.
            destruct H; destruct H1; intro; eapply (Plt_ne y x2); eauto.
            rewrite H4 in H2; xomega.
          * unfold list_disjoint; intros.
            exploit put_stack_range_in_regs_regs_spec; eauto. intros [A1 A2].
            eapply E in H1. unfold not; intros. rewrite H2 in A2. destruct H1; xomega.
          * unfold list_disjoint; intros.
            exploit put_all_in_regs_regs_spec; eauto. intros [A3 A4]. unfold not; intros.
            eapply C in H. rewrite H2 in H. xomega.
          * eapply tr_regs_annot_is_disjoint; eauto. }
        { intros. rewrite in_app in H. destruct H.
          - generalize (C _ H). intros [X Y]. split; eauto.
            + generalize (put_all_in_regs_unchanged_and_sincr _ _ _ _ _ EQ1). intros [_ QQ]; inv QQ; try xomega.
            + generalize (put_all_in_regs_unchanged_and_sincr _ _ _ _ _ EQ1). intros [_ QQ]; inv QQ; try xomega.
          - eapply E in H. destruct H. generalize (put_stack_range_in_regs_unchanged_and_sincr EQ); eauto.
            intros [_ QQ]; inv QQ; split; xomega. }
      + destruct range as [base bound].
        case_eq (Int.unsigned (Int.sub bound base) + 1); intros; rewrite H0 in H; try monadInv H.
        case_eq (Genv.find_symbol ge b); intros; rewrite H1 in H; monadInv H.
        assert (HA: Plt (max_reg_function f) (st_nextreg x0)).
        { exploit put_all_in_regs_unchanged_and_sincr; eauto. intros [_ D]; inv D.
          generalize (put_symbol_range_in_regs_unchanged_and_sincr EQ). intros [_ E]; inv E; xomega. }
        generalize (IHalpha _ _ _ EQ1 HA); intros A. destruct A as [regs' [pcs [A A']]].
        generalize (put_symbol_range_in_regs_spec _ _ _ _ _ _ _ _ EQ HPlt); intros B. destruct B as [pcs' B].
        eexists. eexists. split.
        { econstructor; eauto.
          * rewrite H0. simpl. refine (tr_global_regs_unchanged _ _ _ _ _ _ _ _ _ _ _ _ B).
            eapply put_all_in_regs_unchanged_and_sincr; eauto.
          * unfold list_disjoint; intros.
            exploit put_symbol_range_in_regs_regs_spec; eauto. intros [A1 A2].
            exploit put_all_in_regs_regs_spec; eauto. intros [A3 A4]. unfold not; intros.
            rewrite H3 in A2. xomega.
          * unfold list_disjoint; intros.
            exploit put_symbol_range_in_regs_regs_spec; eauto. intros [A1 A2].
            eapply A' in H2. unfold not; intros. rewrite H3 in A2; xomega.
          * eapply tr_regs_annot_is_disjoint; eauto. }
        { intros. eapply A' in H. generalize (put_symbol_range_in_regs_unchanged_and_sincr EQ).
          intros [_ AA]; inv AA; xomega. }
  Qed.

  Lemma put_all_in_regs_spec:
    forall kappa f alpha s regs s',
      put_all_in_regs ge STK SIZE kappa alpha s = OK (regs, s') ->
      Plt (max_reg_function f) (st_nextreg s) ->
      exists regs' pcs, tr_regs_annot kappa f s'.(st_code) s.(st_nextnode) alpha regs regs' s'.(st_nextnode) pcs.
Proof.
    intros; exploit put_all_in_regs_spec'; eauto.
    intros [regs' [pcs [A B]]]; eauto.
  Qed.
  
  Lemma put_checks_spec:
    forall reg0 regs s s',
      put_checks reg0 regs s = OK s' ->
      exists pcs, tr_cmp_regs s'.(st_code) s.(st_nextnode) reg0 regs s'.(st_nextnode) pcs.
Proof.
    induction regs; intros.
    - inv H. exists nil; econstructor.
    - simpl in H. generalize (IHregs _ _ H). intros A; simpl in A. destruct A as [pcs A].
      eexists. econstructor; eauto.
      generalize (put_checks_unchanged_and_sincr H). intros [B _].
      rewrite <- B. apply PTree.gss. apply Plt_succ.
  Qed.

  Lemma tr_cmp_regs_incr:
    forall c reg0 regs pc1 pc2 pcs,
      tr_cmp_regs c pc1 reg0 regs pc2 pcs ->
      Ple pc1 pc2.
Proof.
    induction regs; intros.
    - inv H; xomega.
    - inv H. generalize (IHregs _ _ _ H8); intros. xomega.
  Qed.
  
  Lemma tr_cmp_regs_unchanged:
    forall c c' reg0 regs pc1 pc2 pcs,
      (forall pc, Plt pc pc2 ->
             c!pc = c'!pc) ->
      tr_cmp_regs c pc1 reg0 regs pc2 pcs ->
      tr_cmp_regs c' pc1 reg0 regs pc2 pcs.
Proof.
    induction regs; intros.
    - inv H0; econstructor.
    - inv H0; econstructor; eauto.
      generalize (tr_cmp_regs_incr _ _ _ _ _ _ H9); intros.
      rewrite <- H6; symmetry; eapply H; xomega.
  Qed.

  Lemma put_ors_spec:
    forall reg0 regs s s',
      put_ors reg0 regs s = OK s' ->
      exists pcs, tr_or_regs s'.(st_code) s.(st_nextnode) reg0 regs s'.(st_nextnode) pcs.
Proof.
    induction regs; intros.
    - inv H; exists nil; econstructor.
    - simpl in H. generalize (IHregs _ _ H); intros A; simpl in A. destruct A as [pcs A].
      eexists; econstructor; eauto.
      exploit put_ors_unchanged; eauto.
      + instantiate (1 := st_nextnode s). simpl. xomega.
      + simpl; intros B. rewrite <- B.
        rewrite PTree.gss; eauto.
  Qed.

  Lemma tr_or_regs_incr:
    forall c reg0 regs pc1 pc2 pcs,
      tr_or_regs c pc1 reg0 regs pc2 pcs ->
      Ple pc1 pc2.
Proof.
    induction regs; intros.
    - inv H; xomega.
    - inv H. generalize (IHregs _ _ _ H8); intros. xomega.
  Qed.
  
  Lemma tr_or_regs_unchanged:
    forall c c' reg0 regs pc1 pc2 pcs,
      (forall pc, Plt pc pc2 ->
             c!pc = c'!pc) ->
      tr_or_regs c pc1 reg0 regs pc2 pcs ->
      tr_or_regs c' pc1 reg0 regs pc2 pcs.
Proof.
    induction regs; intros.
    - inv H0; econstructor.
    - inv H0; econstructor; eauto.
      generalize (tr_or_regs_incr _ _ _ _ _ _ H9); intros.
      rewrite <- H6; symmetry; eapply H; xomega.
  Qed.

  Lemma st_code_unchanged f s x :
    (∀ s, unchanged s (f s) ∧ sincr s (f s)) →
    (x < st_nextnode s)%positive
    (st_code (f s)) ! x = (st_code s) ! x.
Proof.
    intros U LT. symmetry. refine (proj1 (U _) _ LT).
  Qed.

  Lemma st_code_add_return_undef_0 {ty r s} :
    let c := st_code (add_return_undef ty r s) in
    ∀ pc,
      pc = st_nextnode s
      c ! pc = Some (Iop (Ointconst Int.zero) nil (st_nextreg s) (Pos.succ (st_nextnode s))).
Proof.
    simpl. intros pc Hpc.
    unfold add_return_undef.
    simpl. destruct ty as [ ty | ].
    rewrite st_code_unchanged. 2: auto using add_instr_unchanged_and_sincr.
    2: destruct ty; simpl; Psatz.lia.
    repeat (rewrite st_code_unchanged; [ | auto using add_instr_unchanged_and_sincr, make_zero_for_type_unchanged_and_sincr | simpl; Psatz.lia ]).
    subst pc; apply PTree.gss.
    repeat (rewrite st_code_unchanged; [ | auto using add_instr_unchanged_and_sincr | simpl; Psatz.lia ]).
    subst pc; apply PTree.gss.
  Qed.

  Lemma st_code_add_return_undef_1 {ty r s} :
    let c := st_code (add_return_undef ty r s) in
    ∀ pc,
      pc = Pos.succ (st_nextnode s) →
      c ! pc = Some (Iop (Ointconst (Int.repr (Zpos r))) nil (Pos.succ (st_nextreg s)) (Pos.succ (Pos.succ (st_nextnode s)))).
Proof.
    simpl. intros pc Hpc.
    unfold add_return_undef.
    simpl. destruct ty as [ ty | ].
    rewrite st_code_unchanged. 2: auto using add_instr_unchanged_and_sincr.
    2: destruct ty; simpl; Psatz.lia.
    repeat (rewrite st_code_unchanged; [ | auto using add_instr_unchanged_and_sincr, make_zero_for_type_unchanged_and_sincr | simpl; Psatz.lia ]).
    subst pc; apply PTree.gss.
    repeat (rewrite st_code_unchanged; [ | auto using add_instr_unchanged_and_sincr | simpl; Psatz.lia ]).
    subst pc; apply PTree.gss.
  Qed.

  Lemma st_code_add_return_undef_2 {ty r s} :
    let c := st_code (add_return_undef ty r s) in
    ∀ pc,
      pc = Pos.succ (Pos.succ (st_nextnode s)) →
      c ! pc = Some (Iop Odiv (Pos.succ (st_nextreg s) :: st_nextreg s :: nil) (Pos.succ (Pos.succ (st_nextreg s))) (Pos.succ (Pos.succ (Pos.succ (st_nextnode s))))).
Proof.
    simpl. intros pc Hpc.
    unfold add_return_undef.
    simpl. destruct ty as [ ty | ].
    rewrite st_code_unchanged. 2: auto using add_instr_unchanged_and_sincr.
    2: destruct ty; simpl; Psatz.lia.
    repeat (rewrite st_code_unchanged; [ | auto using add_instr_unchanged_and_sincr, make_zero_for_type_unchanged_and_sincr | simpl; Psatz.lia ]).
    subst pc; apply PTree.gss.
    repeat (rewrite st_code_unchanged; [ | auto using add_instr_unchanged_and_sincr | simpl; Psatz.lia ]).
    subst pc; apply PTree.gss.
  Qed.
  Opaque add_return_undef.

  Lemma add_checks_spec:
    forall f opt i alpha kappa addr args s s' (Hnottrivial: is_trivial_annotation prog alpha kappa addr = false) (Hnot_singleton: is_singleton (snd alpha) = false),
      add_checks prog ge STK SIZE opt i alpha kappa addr args s = OK s' ->
      Plt (max_reg_function f) (st_nextreg s) ->
      exists pcs, tr_checks f (st_code s') (st_nextnode s) i alpha kappa addr args pcs.
Proof.
    intros; unfold add_checks in H. simpl in H. rename H0 into HPlt.
    rewrite Hnottrivial in H; rewrite Hnot_singleton in H; simpl in H.
    monadInv H.
    generalize (put_all_in_regs_spec' kappa f _ _ _ _ EQ (Plt_trans_succ _ _ HPlt)). intros HA. destruct HA as [regs1' [pcs1 [HA HZ]]].
    generalize (put_checks_spec _ _ _ _ EQ1). intros HB. destruct HB as [pcs2 HB].
    destruct x; monadInv EQ2.
    generalize (put_ors_spec _ _ _ _ EQ0). intros HC. destruct HC as [pcs3 HC].
    set (pcs4 := (Pos.succ (Pos.succ (st_nextnode x2)))::(Psucc (Pos.succ (Pos.succ (st_nextnode x2))))::(Psucc (Psucc (Pos.succ (Pos.succ (st_nextnode x2)))))::nil).
    exists ((st_nextnode s)::(st_nextnode x1)::(st_nextnode x2)::(Psucc (st_nextnode x2))::(pcs1 ++ pcs2 ++ pcs3 ++ pcs4)).
    econstructor; eauto.
    { erewrite <- (proj1 (add_return_undef_unchanged_and_sincr)).
      - erewrite <- add_instr_unchanged; try reflexivity.
        + erewrite <- add_instr_unchanged; try reflexivity.
          * erewrite <- put_ors_unchanged; eauto.
            { erewrite <- add_instr_unchanged; try reflexivity.
              - erewrite <- (proj1 (put_checks_unchanged_and_sincr EQ1)).
                + erewrite <- (proj1 (put_all_in_regs_unchanged_and_sincr _ _ _ _ _ EQ)).
                  * simpl. rewrite PTree.gss. eauto.
                  * simpl; xomega.
                + exploit put_all_in_regs_unchanged_and_sincr; eauto. intros [_ A]; inv A. simpl in NEXTNODE. xomega.
              - exploit put_all_in_regs_unchanged_and_sincr; eauto. intros [_ A]; inv A. simpl in NEXTNODE.
                exploit put_checks_sincr; eauto. intros B; inv B. simpl in NEXTNODE0. xomega. }
            { simpl. exploit put_all_in_regs_unchanged_and_sincr; eauto. intros [_ A]; inv A. simpl in NEXTNODE.
              exploit put_checks_sincr; eauto. intros B; inv B. simpl in NEXTNODE0. xomega. }
          * exploit put_all_in_regs_unchanged_and_sincr; eauto. intros [_ A]; inv A. simpl in NEXTNODE.
            exploit put_checks_sincr; eauto. intros B; inv B. simpl in NEXTNODE0.
            exploit put_ors_sincr; eauto. intros C; inv C. simpl in NEXTNODE1. xomega.
        + simpl. exploit put_all_in_regs_unchanged_and_sincr; eauto. intros [_ A]; inv A. simpl in NEXTNODE.
          exploit put_checks_sincr; eauto. intros B; inv B. simpl in NEXTNODE0.
          exploit put_ors_sincr; eauto. intros C; inv C. simpl in NEXTNODE1. xomega.
      - simpl. exploit put_all_in_regs_unchanged_and_sincr; eauto. intros [_ A]; inv A. simpl in NEXTNODE.
        exploit put_checks_sincr; eauto. intros B; inv B. simpl in NEXTNODE0.
        exploit put_ors_sincr; eauto. intros C; inv C. simpl in NEXTNODE1. xomega. }
    { refine (tr_regs_annot_unchanged _ _ _ _ _ _ _ _ _ _ _ HA).
      intros. symmetry. erewrite <- (proj1 (add_return_undef_unchanged_and_sincr)).
      - erewrite <- add_instr_unchanged. Focus 2. reflexivity.
        + erewrite <- add_instr_unchanged. Focus 2. reflexivity.
          * exploit put_ors_unchanged; eauto.
            { instantiate (1 := pc). simpl.
              exploit put_checks_sincr; eauto. intros B; inv B; xomega. }
            { intros B. rewrite <- B.
              erewrite <- add_instr_unchanged. Focus 2. reflexivity.
              - symmetry. eapply put_checks_unchanged_and_sincr; eauto.
              - exploit put_checks_sincr; eauto. intros C; inv C; xomega. }
          * exploit put_checks_sincr; eauto. intros B; inv B.
            exploit put_ors_sincr; eauto. intros C; inv C.
            simpl in NEXTNODE0. xomega.
        + simpl. exploit put_checks_sincr; eauto. intros B; inv B.
          exploit put_ors_sincr; eauto. intros C; inv C.
          simpl in NEXTNODE0. xomega.
      - simpl. exploit put_checks_sincr; eauto. intros B; inv B.
        exploit put_ors_sincr; eauto. intros C; inv C.
        simpl in NEXTNODE0. xomega. }
    { refine (tr_cmp_regs_unchanged _ _ _ _ _ _ _ _ HB).
      intros. symmetry. erewrite <- (proj1 (add_return_undef_unchanged_and_sincr)).
      - erewrite <- add_instr_unchanged. Focus 2. reflexivity.
        + erewrite <- add_instr_unchanged. Focus 2. reflexivity.
          * exploit put_ors_unchanged; eauto.
            { simpl. instantiate (1 := pc). xomega. }
            { intros. rewrite <- H0. simpl. rewrite PTree.gso; try xomega. eauto. }
          * exploit put_ors_sincr; eauto. intros B; inv B.
            simpl in NEXTNODE; xomega.
        + simpl. exploit put_ors_sincr; eauto. intros B; inv B.
          simpl in NEXTNODE; xomega.
      - simpl. exploit put_ors_sincr; eauto. intros B; inv B.
        simpl in NEXTNODE; xomega. }
    { erewrite <- (proj1 (add_return_undef_unchanged_and_sincr)).
      simpl. exploit put_ors_sincr; eauto. intros A; inv A.
      simpl in NEXTNODE.
      rewrite PTree.gso; try xomega.
      rewrite PTree.gso; try xomega.
      erewrite <- put_ors_unchanged; eauto. simpl.
      rewrite PTree.gss; eauto. simpl. xomega.
      simpl. exploit put_ors_sincr; eauto. intros A; inv A.
      simpl in NEXTNODE. xomega. }
    { refine (tr_or_regs_unchanged _ _ _ _ _ _ _ _ HC).
      intros. symmetry. erewrite <- (proj1 (add_return_undef_unchanged_and_sincr)).
      - erewrite <- add_instr_unchanged. Focus 2. reflexivity.
        + erewrite <- add_instr_unchanged. Focus 2. reflexivity.
          * reflexivity.
          * assumption.
        + simpl. xomega.
      - simpl. xomega. }
    { erewrite <- (proj1 (add_return_undef_unchanged_and_sincr)).
      simpl. rewrite PTree.gso; try xomega.
      rewrite PTree.gss; eauto.
      simpl. xomega. }
    { erewrite <- (proj1 (add_return_undef_unchanged_and_sincr)).
      simpl. rewrite PTree.gss; eauto.
      simpl. xomega. }
    { econstructor.
      - rewrite st_code_add_return_undef_0; reflexivity.
      - rewrite st_code_add_return_undef_1; reflexivity.
      - rewrite st_code_add_return_undef_2; reflexivity.
      - apply Pos.succ_discr. }
    { unfold not; intros.
      generalize (put_all_in_regs_regs_spec _ _ _ _ _ EQ _ H); simpl. intros [A B]; xomega. }
    { unfold not; intros.
      eapply HZ in H. simpl in H; destruct H. xomega. }
  Qed.

  Lemma add_epilogue_spec:
    forall f i s s',
      add_epilogue SIZE i s = OK s' ->
      Plt (max_reg_function f) (st_nextreg s) ->
      exists pcs, tr_epilogue f (st_code s') (st_nextnode s) i pcs.
Proof.
    intros. unfold add_epilogue in H. simpl in H. monadInv H.
    econstructor. econstructor.
    - simpl. rewrite PTree.gso; eauto; try xomega.
      rewrite PTree.gso; eauto; try xomega.
      rewrite PTree.gso; eauto; try xomega.
      rewrite PTree.gso; eauto; try xomega.
      rewrite PTree.gss; eauto.
    - simpl. rewrite PTree.gso; eauto; try xomega.
      rewrite PTree.gso; eauto; try xomega.
      rewrite PTree.gso; eauto; try xomega.
      rewrite PTree.gss; eauto.
    - simpl. rewrite PTree.gso; eauto; try xomega.
      rewrite PTree.gso; eauto; try xomega.
      rewrite PTree.gss; eauto.
    - simpl. rewrite PTree.gso; eauto; try xomega.
      rewrite PTree.gss; eauto.
    - simpl. rewrite PTree.gss; eauto.
    - eapply Pos.succ_discr.
    - eauto.
    - xomega.
  Qed.

  Lemma add_epilogue_unchanged_and_sincr:
    forall i s s',
      add_epilogue SIZE i s = OK s' →
      unchanged s s' ∧ sincr s s'.
Proof.
    intros i s s'. unfold add_epilogue.
    destruct (new_reg s) as [ r s0 ] eqn: NR.
    destruct (new_reg s0) as [ r1 s1 ] eqn: NR1.
    intros H; monadInv H.
    repeat (eapply unchanged_and_sincr_trans; [ apply add_instr_unchanged_and_sincr |]).
    eapply unchanged_and_sincr_trans. eapply new_reg_unchanged_and_sincr; eassumption.
    eapply new_reg_unchanged_and_sincr; eassumption.
  Qed.

  Lemma check_annotations_depth_spec:
    forall alpha x,
      check_annotations_depth STK SIZE alpha = OK x ->
      forall depth varname range,
        In (ABlocal depth varname range) alpha ->
        (depth < 128)%nat.
Proof.
    induction alpha; intros.
    - inv H0.
    - destruct a.
      + monadInv H.
        destruct (lt_dec depth0 128); inv EQ0.
        destruct H0.
        * inv H; auto.
        * eapply IHalpha; eauto.
      + simpl in H.
        destruct H0; try (inv H0; fail).
        eapply IHalpha; eauto.
        monadInv H. destruct (peq b STK); inv EQ0.
        destruct (peq b SIZE); inv H1. destruct x0. eapply EQ.
  Qed.

  Lemma check_annotations_depth_spec':
    forall alpha x,
      check_annotations_depth STK SIZE alpha = OK x ->
      forall id range,
        In (ABglobal id range) alpha ->
        id <> STK /\ id <> SIZE.
Proof.
    induction alpha; intros.
    - inv H0.
    - destruct a.
      + destruct H0; try (inv H0; fail).
        eapply IHalpha; eauto. monadInv H.
        destruct (lt_dec depth 128); inv EQ0; eauto. destruct x0; eauto.
      + monadInv H. destruct (peq b STK); inv EQ0.
        destruct (peq b SIZE); inv H1.
        destruct H0.
        * inv H; eauto.
        * eapply IHalpha; eauto.
  Qed.

  Lemma check_globals_of_builtin_args_spec:
    forall id al x,
      check_globals_of_builtin_args id al = OK x ->
      ~ In id (globals_of_builtin_args al).
Proof.
    intros. unfold check_globals_of_builtin_args in H.
    destruct (in_dec peq id (globals_of_builtin_args al)); inv H.
    auto.
  Qed.

  Lemma check_annotations_divides_spec':
    forall kappa n ofs x,
      check_annotations_divides' kappa ofs n = OK x ->
      exists x, In x (int_ranges n ofs) /\ (align_chunk kappa | x).
Proof.
    induction n; intros.
    - simpl in H; inv H.
    - simpl in H. destruct (Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)).
      + simpl; exists (Int.unsigned ofs). split; eauto.
      + exploit IHn; eauto. intros [x1 [A B]].
        exists x1. split; auto. simpl; right; auto.
  Qed.

  Lemma check_annotations_divides_spec:
    forall kappa alpha x,
      check_annotations_divides kappa alpha = OK x ->
      (forall depth varname base bound, In (ABlocal depth varname (base, bound)) alpha ->
                                   forall size, Int.unsigned (Int.sub bound base) + 1 = Z.pos size ->
                                           exists x, In x (int_ranges (Pos.to_nat size) base) /\ ((align_chunk kappa) | x)).
Proof.
    induction alpha; intros.
    - inv H0.
    - destruct H0 as [H0 | H0].
      + subst a. simpl in H. monadInv H.
        rewrite H1 in EQ0. eapply check_annotations_divides_spec'; eauto.
      + simpl in H. destruct a; try monadInv H; eauto.
        destruct range; monadInv H.
        eapply IHalpha; eauto.
        destruct range; monadInv H. eapply IHalpha; eauto.
  Qed.

  Lemma check_annotations_range_spec:
    forall alpha x,
      check_annotations_range alpha = OK x ->
      forall depth varname base bound, In (ABlocal depth varname (base, bound)) alpha ->
                                  Int.unsigned base <= Int.unsigned bound < Int.modulus - 1.
Proof.
    induction alpha; intros.
    - inv H0.
    - simpl in H. destruct H0.
      + subst a. destruct (zle (Int.unsigned base) (Int.unsigned bound)); inv H.
        replace (Int.modulus - 1) with 4294967295; try reflexivity.
        destruct (zlt (Int.unsigned bound) 4294967295); inv H1.
        omega.
      + destruct a.
        * destruct range. destruct (zle (Int.unsigned i) (Int.unsigned i0)); inv H.
          replace (Int.modulus - 1) with 4294967295; try reflexivity.
          destruct (zlt (Int.unsigned i0) 4294967295); inv H2.
          eapply IHalpha; eauto.
        * destruct range. destruct (zle (Int.unsigned i) (Int.unsigned i0)); inv H.
          replace (Int.modulus - 1) with 4294967295; try reflexivity.
          destruct (zlt (Int.unsigned i0) 4294967295); inv H2.
          eapply IHalpha; eauto.
  Qed.
          
  Lemma check_annotations_range_spec':
    forall alpha x,
      check_annotations_range alpha = OK x ->
      forall id base bound, In (ABglobal id (base, bound)) alpha -> Int.unsigned base <= Int.unsigned bound < Int.modulus - 1.
    induction alpha; intros.
    - inv H0.
    - simpl in H. destruct H0.
      + subst a. destruct (zle (Int.unsigned base) (Int.unsigned bound)); inv H.
        replace (Int.modulus - 1) with 4294967295; try reflexivity.
        destruct (zlt (Int.unsigned bound) 4294967295); inv H1.
        omega.
      + destruct a.
        * destruct range. destruct (zle (Int.unsigned i) (Int.unsigned i0)); inv H.
          replace (Int.modulus - 1) with 4294967295; try reflexivity.
          destruct (zlt (Int.unsigned i0) 4294967295); inv H2.
          eapply IHalpha; eauto.
        * destruct range. destruct (zle (Int.unsigned i) (Int.unsigned i0)); inv H.
          replace (Int.modulus - 1) with 4294967295; try reflexivity.
          destruct (zlt (Int.unsigned i0) 4294967295); inv H2.
          eapply IHalpha; eauto.
  Qed.
  
  Lemma st_code_unchanged' {f s s' x} :
    f s = OK s' →
    (∀ s s', f s = OK s' → unchanged s s' ∧ sincr s s') →
    (x < st_nextnode s)%positive
    (st_code s') ! x = (st_code s) ! x.
Proof.
    intros H U LT. symmetry. refine (proj1 (U _ _ H) _ LT).
  Qed.

  Lemma st_code_unchanged2 {Q} {f s} {q: Q} {s' x} :
    f s = OK (q, s') →
    (∀ s q s', f s = OK (q, s') → unchanged s s' ∧ sincr s s') →
    (x < st_nextnode s)%positive
    (st_code s') ! x = (st_code s) ! x.
Proof.
    intros H U LT. symmetry. refine (proj1 (U _ _ _ H) _ LT).
  Qed.

  Lemma put_stack_range_in_regs'_spec f { base bound δ s regs s' } :
    (max_reg_function f < st_nextreg s
     put_stack_range_in_regs' STK SIZE base bound δ s = OK (regs, s') →
     ∃ regs' pcs,
       tr_local_regs' f (st_code s') (st_nextnode s) δ base bound regs regs' (st_nextnode s') pcs
       ∧ match regs with inl x => x = st_nextreg s | inr (u, v) => u = st_nextreg sv = Pos.succ (st_nextreg s) end
       ∧ st_nextreg s + 2 < st_nextreg s'
       ∧ (∀ r, In r regs' → match regs with inl _ => st_nextreg s | inr _ => Pos.succ (st_nextreg s) end < r < st_nextreg s')
       ∧ ∀ pc, In pc pcsst_nextnode s <= pc
    )%positive.
Proof.
    intros LT. unfold put_stack_range_in_regs'.
    case Int.eq_dec.
    - intros ->.
      simpl. intros H. inv H. simpl.
      eexists _, _. split. econstructor; eauto.
      repeat ( apply PTree.gss || rewrite PTree.gso ); Psatz.lia.
      repeat ( apply PTree.gss || rewrite PTree.gso ); Psatz.lia.
      repeat ( apply PTree.gss || rewrite PTree.gso ); Psatz.lia.
      split. reflexivity.
      split. Psatz.lia.
      simpl. split. intros ? [ <- | [ <- | () ] ]; Psatz.lia.
      intuition (subst; Psatz.lia).
    - intros NE.
      simpl. intros H. inv H. simpl.
      eexists _, _. split. econstructor; eauto.
      repeat ( apply PTree.gss || rewrite PTree.gso ); Psatz.lia.
      repeat ( apply PTree.gss || rewrite PTree.gso ); Psatz.lia.
      repeat ( apply PTree.gss || rewrite PTree.gso ); Psatz.lia.
      repeat ( apply PTree.gss || rewrite PTree.gso ); Psatz.lia.
      repeat ( apply PTree.gss || rewrite PTree.gso ); Psatz.lia.
      repeat ( apply PTree.gss || rewrite PTree.gso ); Psatz.lia.
      split. auto.
      split. Psatz.lia.
      split. intros ? [ <- | [ <- | () ] ]; Psatz.lia.
      simpl. intuition (subst; Psatz.lia).
  Qed.

  Lemma put_symbol_range_in_regs'_spec f { g base bound s regs s' } :
    (max_reg_function f < st_nextreg s
    put_symbol_range_in_regs' g base bound s = OK (regs, s') →
    match regs with
    | inl x => x = st_nextreg sst_nextreg s' = Pos.succ x
    | inr (x, y) => x = st_nextreg sy = Pos.succ xst_nextreg s' = Pos.succ y
    end
    ∃ pcs,
      tr_global_regs' f (st_code s') (st_nextnode s) g base bound regs (st_nextnode s') pcs
      ∧ ∀ pc, In pc pcsst_nextnode s <= pc
    )%positive.
Proof.
    intros LT. unfold put_symbol_range_in_regs'.
    case Int.eq_dec.
    - intros ->.
      intros H; inv H. simpl.
      split. auto.
      eexists. split. econstructor; auto. apply PTree.gss. simpl. intuition (subst; Psatz.lia).
    - intros NE.
      intros H; inv H. simpl.
      split. auto.
      eexists. split. econstructor; auto.
      rewrite PTree.gso. apply PTree.gss. apply Pos.succ_discr.
      apply PTree.gss.
      simpl. intuition (subst; Psatz.lia).
  Qed.

  Lemma tr_local_regs'_unchanged c {f c' pc δ base bound r regs pc' pcs}:
    (∀ pc, pc < pc' → c ! pc = c' ! pc)%positive
    tr_local_regs' f c pc δ base bound r regs pc' pcs
    tr_local_regs' f c' pc δ base bound r regs pc' pcs.
Proof.
    intros H X; inv X; econstructor; eauto; rewrite <- H; auto; Psatz.lia.
  Qed.

  Lemma tr_cmp_regs'_pc_out {c pc r regs pc' pcs} :
    tr_cmp_regs' c pc r regs pc' pcs
    (pc <= pc')%positive.
Proof.
    intros X; elim X; clear; intros; Psatz.lia.
  Qed.

  Lemma tr_cmp_regs'_unchanged c {c' pc r regs pc' pcs}:
    (∀ pc, pc < pc' → c ! pc = c' ! pc)%positive
    tr_cmp_regs' c pc r regs pc' pcs
    tr_cmp_regs' c' pc r regs pc' pcs.
Proof.
    intros X Y; revert X; elim Y; clear.
    - intros c pc reg0 X. constructor.
    - intros c pc1 pc2 reg0 reg1 regs pcs H H0 H1 X. constructor; auto.
      rewrite <- X; auto.
      apply tr_cmp_regs'_pc_out in H0. Psatz.lia.
    - intros c pc1 pc2 reg0 reg1 reg2 regs pcs H H0 H1 H2 H3 X.
      apply tr_cmp_regs'_pc_out in H2.
      constructor; auto; rewrite <- X; auto; Psatz.lia.
  Qed.

  Lemma tr_global_regs'_unchanged c {f c' pc δ base bound r pc' pcs}:
    (∀ pc, pc < pc' → c ! pc = c' ! pc)%positive
    tr_global_regs' f c pc δ base bound r pc' pcs
    tr_global_regs' f c' pc δ base bound r pc' pcs.
Proof.
    intros H X; inv X; econstructor; eauto; rewrite <- H; auto; Psatz.lia.
  Qed.

  Definition fold_right_etc m : list reg :=
    fold_rightu a, match u with inl x => x :: a | inr (x, y) => x :: y :: a end) nil m.

  Remark in_fold_right_etc {r: reg} {m} :
    In r (fold_right_etc m) →
    ∃ r', In r' mmatch r' with inl x => r = x | inr (x, y) => r = xr = y end.
Proof.
    elim m; clear m. intros ().
    intros [ x | (x, y) ] m IH [ -> | IN ];
      try destruct IN as [ -> | IN ];
      try (eexists; split; [ left; reflexivity | simpl; auto; fail ]);
    destruct (IH IN) as (r' & Hr' & Q); exists r'; split; simpl; auto.
  Qed.

  Lemma find_symbol_inv {g msg b} :
    find_symbol ge g msg = OK b
    Genv.find_symbol ge g = Some b.
Proof.
    unfold find_symbol. case Genv.find_symbol; congruence.
  Qed.

  Lemma put_all_in_regs'_spec fs regs s'} :
      put_all_in_regs' ge STK SIZE α s = OK (regs, s') →
      Plt (max_reg_function f) (st_nextreg s) →
      (∀ r r', In r' regsmatch r' with inl x => r = x | inr (x, y) => r = xr = y endst_nextreg s <= r < st_nextreg s')%positive
      (∃ regs' pcs,
          list_disjoint (fold_right_etc regs) regs'
          ∧ tr_regs_annot' f s'.(st_code) s.(st_nextnode) α regs regs' s'.(st_nextnode) pcs
          ∧ (∀ r, In r regs' → st_nextreg s < r < st_nextreg s')
          ∧ ∀ pc, In pc pcsst_nextnode s <= pc
      )%positive.
Proof.
    revert regs s. elim α; clear α.
    - intros s regs H; inv H. intros H. split.
      intros ? ? (). eexists _, _. split. intros ? ? (). split. constructor. split; intros ? ().
    - intros [ δ ? (base, bound) | a (base, bound) ] α IH regs s; simpl; intros H HPlt; monadInv H.
      + specialize (IH _ _ EQ1).
        generalize (put_stack_range_in_regs'_unchanged_and_sincr EQ). intros [U S].
        destruct IH as (IH1 & IH2 ). destruct S; xomega.
        generalize (put_all_in_regs'_unchanged_and_sincr EQ1). simpl. intros [_ X]. destruct X.
        split.
        * intros r r' [ -> | H ].
          revert EQ. unfold put_stack_range_in_regs'.
          case Int.eq_dec; simpl. intros -> H. inv H. intros ->. simpl in *. unfold Ple in *.
          Psatz.lia.
          intros NE H; inv H. simpl in *. unfold Ple in *. intros [ -> | -> ]; Psatz.lia.
          intros Hr'. specialize (IH1 _ _ H Hr').
          destruct S. unfold Ple in *. Psatz.lia.
        * { destruct IH2 as (regs' & pcs & IHdis & IH2 & IH2' & IH2le).
            generalize (put_stack_range_in_regs'_spec f HPlt EQ).
            intros (rs & pcs' & Hl & N & P & Q & R).
            exists (rs ++ regs'), (pcs' ++ pcs).
            split.
            {
              intros r r' Hr Hr' ->.
              destruct x as [ x | (x, y) ].
              - destruct Hr as [ -> | Hr ]. subst r'.
                rewrite in_app in Hr'. destruct Hr' as [ Hr' | Hr' ].
                + specialize (Q _ Hr'). Psatz.lia.
                + specialize (IH2' _ Hr'). Psatz.lia.
                + specializer', IHdis _ r' Hr).
                  subst. apply in_fold_right_etc in Hr. destruct Hr as ( r & Hr & Hrr ).
                  specialize (IH1 _ _ Hr Hrr).
                  rewrite in_app in Hr'. destruct Hr' as [ Hr' | Hr' ].
                  specialize (Q _ Hr'). Psatz.lia.
                  elim (IHdis _ Hr'). reflexivity.
              - destruct N as [ -> -> ].
                rewrite in_app in Hr'. destruct Hr' as [ Hr' | Hr' ].
                + specialize (Q _ Hr').
                  destruct Hr as [ <- | [ <- | Hr ] ]. Psatz.lia. Psatz.lia.
                  apply in_fold_right_etc in Hr. destruct Hr as (r & Hr & Hr'').
                  specialize (IH1 _ _ Hr Hr''). Psatz.lia.
                + specialize ( IH2' _ Hr').
                  destruct Hr as [ <- | [ <- | Hr ] ]. Psatz.lia. Psatz.lia.
                  specializer', IHdis _ r' Hr).
                  apply in_fold_right_etc in Hr. destruct Hr as ( r & Hr & Hrr ).
                  elim (IHdis _ Hr'). reflexivity.
            }
            split; [ | split ]. econstructor.
            - eassumption.
            - revert Hl. apply tr_local_regs'_unchanged.
              apply (put_all_in_regs'_unchanged_and_sincr EQ1).
            - destruct x as [ x | (x, y) ]; constructor; auto.
              + intros H. apply in_fold_right_etc in H. destruct H as (r' & Hr' & H).
                specialize (IH1 _ _ Hr' H). subst. Psatz.lia.
              + inv IH2; auto. constructor.
              + intros [ -> | IN ]. destruct N as [ -> N ]. revert N. apply Pos.succ_discr.
                apply in_fold_right_etc in IN. destruct IN as (r' & Hr' & H).
                specialize (IH1 _ _ Hr' H). destruct N as [ -> -> ]. Psatz.lia.
              + constructor.
                * intros H. apply in_fold_right_etc in H. destruct H as (r' & Hr' & H).
                  specialize (IH1 _ _ Hr' H). destruct N as [ -> -> ]. Psatz.lia.
                * inv IH2; auto. constructor.
            - intros r r' Hr Hr' ->. specialize (IH2' _ Hr'). specialize (Q _ Hr). Psatz.lia.
            - destruct S.
              intros r r' Hr Hr' ->. specialize (IH2' _ Hr').
              destruct x as [ ? | (?, ?) ].
              destruct Hr as [ -> | () ]. subst r'. Psatz.lia.
              destruct Hr as [ -> | [ -> | () ] ]; destruct N; subst r'; Psatz.lia.
            - intros r r' Hr Hr' ->. apply in_fold_right_etc in Hr'. destruct Hr' as (r & Hrrr & Hrr).
              specialize (IH1 _ _ Hrrr Hrr). specialize (Q _ Hr). Psatz.lia.
            - exact IHdis.
            - intros r; rewrite in_app; intros [ IN | IN ].
              specialize (Q _ IN). destruct x; xomega.
              specialize (IH2' _ IN). destruct S. xomega.
            - intros pc. rewrite in_app. intros [ H | H ].
              eauto.
              specialize (IH2le _ H). destruct S. xomega.
          }
      + specialize (IH _ _ EQ0).
        generalize (put_symbol_range_in_regs'_unchanged_and_sincr EQ1). intros [U S].
        destruct IH as [IH1 IH2]. destruct S; xomega.
        generalize (put_all_in_regs'_unchanged_and_sincr EQ0). simpl. intros [_ X]. destruct X.
        apply find_symbol_inv in EQ.
        split.
        * intros r r' [ -> | H ].
          revert EQ1. unfold put_symbol_range_in_regs'.
          case Int.eq_dec; simpl. intros -> H. inv H. intros ->. simpl in *. unfold Ple in *. Psatz.lia.
          intros NE H; inv H. simpl in *. unfold Ple in *. intros [ -> | -> ]; Psatz.lia.
          intros Hr'. specialize (IH1 _ _ H Hr').
          destruct S. unfold Ple in *. Psatz.lia.
        * { destruct IH2 as (regs' & pcs & IHdis & IH2 & IH2' & IH2le).
          generalize (put_symbol_range_in_regs'_spec f HPlt EQ1).
          intros (LT & pcs' & Hpcs' & LE).
          eexists regs', (pcs' ++ pcs).
          split.
          {
            intros r r' Hr Hr' <-.
            specialize (IH2' _ Hr').
            destruct x0 as [ y | (y, y') ].
            destruct Hr as [ -> | Hr ]. Psatz.lia.
            exact (IHdis r r Hr Hr' eq_refl).
            destruct Hr as [ -> | [ -> | Hr ] ].
            destruct LT as ( -> & -> & LT ). Psatz.lia.
            destruct LT as ( -> & -> & LT ). Psatz.lia.
            exact (IHdis r r Hr Hr' eq_refl).
          }
          split; [ | split ].
            - econstructor.
              + eassumption.
              + eassumption.
              + revert Hpcs'.
                apply tr_global_regs'_unchanged.
                apply (put_all_in_regs'_unchanged_and_sincr EQ0).
              + destruct x0 as [ y | (y, y') ]; destruct LT as ( -> & LT ); constructor; auto.
                * intros H. apply in_fold_right_etc in H. destruct H as (r' & Hr' & H).
                  specialize (IH1 _ _ Hr' H). Psatz.lia.
                * inv IH2; auto. constructor.
                * intros [ -> | H ]. exact (Pos.succ_discr _ (proj1 LT)).
                  destruct LT as [ -> LT ].
                  apply in_fold_right_etc in H. destruct H as (r' & Hr' & H).
                  specialize (IH1 _ _ Hr' H). Psatz.lia.
                * destruct LT as [ -> LT ]. constructor.
                  intros H. apply in_fold_right_etc in H. destruct H as (r' & Hr' & H).
                  specialize (IH1 _ _ Hr' H). Psatz.lia.
                  inv IH2; auto. constructor.
              + destruct x0 as [ y | (y, y') ]; destruct LT as [ -> LT ].
                intros r r' [ <- | () ] Hr <-.
                specialize (IH2' _ Hr). Psatz.lia.
                destruct LT as [ -> LT ].
                intros r r' [ <- | [ <- | () ] ] Hr <-; specialize (IH2' _ Hr); Psatz.lia.
              + intros r r' H Hr <-. elim (IHdis _ _ H Hr eq_refl).
            - intros r Hr; specialize (IH2' _ Hr). destruct S. unfold Ple in *. Psatz.lia.
            - intros pc. rewrite in_app. intros [ H | H ].
              eauto.
              specialize (IH2le _ H). destruct S. xomega.
          }
  Qed.

  Lemma tr_regs_annot'_pc_out {f c pc α regs regs' pc' pcs} :
    tr_regs_annot' f c pc α regs regs' pc' pcs
    (pc <= pc')%positive.
Proof.
    intros X; elim X; clear. reflexivity.
    - intros f c pc1 pc2 pc3 depth varname base bound alpha regs1 regs1' regs2 regs2' pcs1 pcs2 H H0 H1 H2 H3 H4 H5
             H6.
      inv H1; Psatz.lia.
    - intros f c pc1 pc2 pc3 b id base bound alpha regs1 regs2 regs2' pcs1 pcs2 H H0 H1 H2 H3 H4 H5.
      inv H2; Psatz.lia.
  Qed.

  Lemma tr_regs_annot'_unchanged c {f c' pc α regs regs' pc' pcs} :
    (∀ pc, pc < pc' → c' ! pc = c ! pc)%positive
    tr_regs_annot' f c pc α regs regs' pc' pcs
    tr_regs_annot' f c' pc α regs regs' pc' pcs.
Proof.
    intros X Y; revert X; elim Y; clear.
    constructor.
    - intros f c pc1 pc2 pc3 depth varname base bound alpha regs1 regs1' regs2 regs2' pcs1 pcs2 H H0 H1 H2 H3 H4 H5
             H6 X.
      econstructor; eauto.
      revert H1. apply tr_local_regs'_unchanged.
      apply tr_regs_annot'_pc_out in H.
      intros pc H1; symmetry; apply X; Psatz.lia.
    - intros f c pc1 pc2 pc3 b id base bound alpha regs1 regs2 regs2' pcs1 pcs2 H H0 H1 H2 H3 H4 H5 X.
      econstructor; eauto.
      revert H2. apply tr_global_regs'_unchanged.
      apply tr_regs_annot'_pc_out in H0.
      intros pc Hpc; symmetry; apply X; Psatz.lia.
  Qed.

  Lemma put_checks'_spec {r regs s s'}:
    put_checks' r regs s = OK s' →
    ∃ pcs, tr_cmp_regs' s'.(st_code) s.(st_nextnode) r regs s'.(st_nextnode) pcs
      ∧ ∀ pc, In pc pcs → (st_nextnode s <= pc)%positive .
Proof.
    revert s.
    elim regs; clear regs.
    intros s H; inv H. eexists. split. econstructor. intros ? ().
    intros [a | (x, y) ] regs IH s H;
      specialize (IH _ H);
      destruct IH as (pcs & IH & IH');
      simpl in H; apply put_checks'_unchanged_and_sincr in H; destruct H as [H _];
        eexists; (split; [ constructor; eauto | ]).
    rewrite <- H. apply PTree.gss. apply Plt_succ.
    intros pc [ -> | Hpc ]. Psatz.lia. generalize (IH' _ Hpc). clear. simpl. Psatz.lia.
    rewrite <- H.
    rewrite st_code_unchanged. 2: auto using add_instr_unchanged_and_sincr. 2: simpl; Psatz.lia.
    rewrite st_code_unchanged. 2: auto using add_instr_unchanged_and_sincr. 2: simpl; Psatz.lia.
    apply PTree.gss. simpl. xomega.
    rewrite <- H.
    rewrite st_code_unchanged. 2: auto using add_instr_unchanged_and_sincr. 2: simpl; Psatz.lia.
    apply PTree.gss. simpl. xomega.
    rewrite <- H. apply PTree.gss. apply Plt_succ.
    intros pc [ <- | [ <- | [ <- | Hpc ] ] ]; try Psatz.lia.
    generalize (IH' _ Hpc). clear. simpl. Psatz.lia.
  Qed.

  Lemma tr_or_regs_ple:
    forall c regs pc1 reg0 pc2 pcs,
      tr_or_regs c pc1 reg0 regs pc2 pcs ->
      Ple pc1 pc2 /\ forall pc, In pc pcs -> Ple pc1 pc.
Proof.
    induction regs; intros.
    - inv H. split; try xomega.
      simpl; intros. exfalso; auto.
    - inv H. exploit IHregs; eauto.
      intros [A B]. split; try xomega.
      intros. simpl in H. destruct H.
      + inv H; xomega.
      + generalize (B _ H); intros. xomega.
  Qed.
  
  Lemma add_checks'_spec { f ty i α ϰ addr args s s' } :
    is_trivial_annotation prog α ϰ addr = false
    is_singleton (snd α) = true
    add_checks prog ge STK SIZE ty i α ϰ addr args s = OK s' ->
    Plt (max_reg_function f) (st_nextreg s) ->
    ∃ pcs,
      tr_checks' f (st_code s') (st_nextnode s) i α ϰ addr args pcs
      ∧ ∀ pc, In pc pcs → (st_nextnode s <= pc)%positive .
Proof.
    intros Hnt Hs.
    unfold add_checks. rewrite Hnt, Hs. simpl.
    intros H HPlt.
    monadInv H.
    destruct (map _ _) as [ | r0 regs ] eqn: X. discriminate.
    monadInv EQ2.
    assert (st_nextnode s < Pos.succ (st_nextnode s))%positive as X0 by Psatz.lia.
    assert (st_nextnode s < st_nextnode x0)%positive as X1.
    { apply put_all_in_regs'_unchanged_and_sincr in EQ. destruct EQ as [_ EQ].
      apply sincr_nextnode in EQ. simpl in EQ. Psatz.lia. }
    assert (st_nextnode x0 <= st_nextnode x1)%positive as X2.
    { apply put_checks'_unchanged_and_sincr in EQ1. destruct EQ1 as [_ EQ1].
      apply sincr_nextnode in EQ1. auto. }
    assert (st_nextnode x1 < st_nextnode x2)%positive as X3.
    { apply put_ors_sincr, sincr_nextnode in EQ0. simpl in EQ0. Psatz.lia. }

    destruct (put_all_in_regs'_spec f EQ) as ( Hpair & regs' & pcs & Hdis & Hpair' & Hpair'' & LE).
    { simpl. revert HPlt. apply Plt_trans_succ. }
    destruct (put_checks'_spec EQ1) as ( pcs' & Hpc' & Hpc'le ).
    destruct (put_ors_spec _ _ _ _ EQ0) as ( pcs'' & Hpos ).

    match goal with |- appcontext[ (st_code ?q) ] =>
                    assert (∀ pc, pc < st_nextnode x2 → (st_code q) ! pc = (st_code x2) ! pc)%positive as Hcode2; [|
                    assert (∀ pc, pc < st_nextnode x1 → (st_code q) ! pc = (st_code x1) ! pc)%positive as Hcode1; [|
                    assert (∀ pc, pc < st_nextnode x0 → (st_code q) ! pc = (st_code x0) ! pc)%positive as Hcode ] ]
    end.
    { intros pc Hpc.
      rewrite st_code_unchanged. 2: auto using add_return_undef_unchanged_and_sincr. 2: simpl; Psatz.lia.
      rewrite st_code_unchanged. 2: auto using add_instr_unchanged_and_sincr. 2: simpl; Psatz.lia.
      rewrite st_code_unchanged. 2: auto using add_instr_unchanged_and_sincr. 2: Psatz.lia.
      reflexivity. }
    { intros pc Hpc. rewrite Hcode2.
      rewrite (st_code_unchanged' EQ0). 2: eauto using put_ors_unchanged_and_sincr. 2: simpl; Psatz.lia.
      rewrite st_code_unchanged. 2: auto using add_instr_unchanged_and_sincr. 2: Psatz.lia.
      reflexivity. Psatz.lia. }
    { intros pc Hpc. rewrite Hcode1.
      rewrite (st_code_unchanged' EQ1). 2: eauto using put_checks'_unchanged_and_sincr. 2: auto.
      reflexivity. Psatz.lia. }

    eexists. split. econstructor; eauto.

    - rewrite Hcode. 2: Psatz.lia.
      rewrite (st_code_unchanged2 EQ). 2: eauto using put_all_in_regs'_unchanged_and_sincr. 2: auto.
      apply PTree.gss.
    - revert Hpair'. refine (tr_regs_annot'_unchanged _ _). auto.
    - revert Hpc'. refine (tr_cmp_regs'_unchanged _ _). intros; rewrite Hcode1; auto.
    - rewrite Hcode2. 2: Psatz.lia.
      rewrite (st_code_unchanged' EQ0). 2: eauto using put_ors_unchanged_and_sincr. 2: simpl; Psatz.lia.
      apply PTree.gss.
    - revert Hpos. refine (tr_or_regs_unchanged _ _ _ _ _ _ _ _).
      intros pc Hpc. symmetry. apply Hcode2. xomega.
    - rewrite st_code_unchanged. 2: auto using add_return_undef_unchanged_and_sincr. 2: simpl; Psatz.lia.
      rewrite st_code_unchanged. 2: auto using add_instr_unchanged_and_sincr. 2: simpl; Psatz.lia.
      apply PTree.gss.
    - rewrite st_code_unchanged. 2: auto using add_return_undef_unchanged_and_sincr. 2: simpl; Psatz.lia.
      apply PTree.gss.
    - econstructor.
      + rewrite st_code_add_return_undef_0; reflexivity.
      + rewrite st_code_add_return_undef_1; reflexivity.
      + rewrite st_code_add_return_undef_2; reflexivity.
      + apply Pos.succ_discr.
    - clear - Hpair. simpl in *.
      intros X. apply in_fold_right_etc in X.
      destruct X as (r' & B & A). specialize (Hpair _ _ B A). Psatz.lia.
    - clear - Hpair''. simpl in *.
      intros H; specialize (Hpair'' _ H).
      Psatz.lia.

    - clear Hcode Hcode1 Hcode2. intros pc. simpl. rewrite ! in_app. simpl.
      intuition (subst; try Psatz.lia).
      specialize (LE _ H0). simpl in LE. Psatz.lia.
      specialize (Hpc'le _ H); Psatz.lia.
      clear -Hpos H0 X1 X2. apply tr_or_regs_ple in Hpos. simpl in Hpos. destruct Hpos as [ _ LE ].
      specialize (LE _ H0). unfold Ple in LE. Psatz.lia.
  Qed.

  Lemma transf_instr_spec:
    forall f opt st pc i s,
      transf_instr prog ge STK SIZE opt (OK st) pc i = OK s ->
      st.(st_code)!pc = Some i ->
      Plt (max_reg_function f) (st_nextreg st) ->
      exists pcs, tr_instrs f pc i s.(st_code) pcs.
Proof.
    intros. rename H1 into HPlt. destruct i; simpl in H; try (inv H; eexists; econstructor; eauto; fail).
    - monadInv H. unfold update_instr in EQ2.
      destruct (plt pc (st_nextnode st)).
      + monadInv EQ2. case_eq (is_trivial_annotation prog a m a0); intros.
        * unfold add_checks in EQ4. rewrite H in EQ4. simpl in EQ4. inv EQ4.
          econstructor. econstructor; simpl; eauto.
          { eapply check_annotations_range_spec; eauto. }
          { eapply check_annotations_range_spec'; eauto. }
          { destruct x0; auto. }
          { rewrite PTree.gso; eauto.
            rewrite PTree.gss; eauto. }
          { rewrite H; rewrite PTree.gss; eauto. }
          { intros. eapply check_annotations_depth_spec; eauto. }
          { intros. eapply check_annotations_depth_spec'; eauto. }
          { eapply check_annotations_divides_spec; eauto. }
        * generalize (add_checks_unchanged _ _ _ _ _ _ _ _ EQ4 pc p). simpl; intros.
          case_eq (is_singleton (snd a)); intros.
          { generalize (add_checks'_spec H H2 EQ4 HPlt).
            intros (pcs & Hpcs & LE).
            eexists. apply tr_Iload.
            eauto using check_annotations_range_spec.
            eauto using check_annotations_range_spec'.
            destruct x0; auto.
            rewrite <- H1, PTree.gss; reflexivity.
            rewrite H, H2. exact Hpcs.
            eauto using check_annotations_depth_spec.
            eauto using check_annotations_depth_spec'.
            eauto using check_annotations_divides_spec. }
          generalize (add_checks_spec f _ _ _ _ _ _ _ _ H H2 EQ4 HPlt). simpl. intros A. destruct A as [pcs A].
          unfold add_checks in EQ4. rewrite H in EQ4. rewrite H2 in EQ4. simpl in EQ4. monadInv EQ4.
          eexists. econstructor; simpl; eauto.
          { eapply check_annotations_range_spec; eauto. }
          { eapply check_annotations_range_spec'; eauto. }
          { destruct x0; auto. }
          { destruct x2; simpl in EQ5; monadInv EQ5.
            rewrite <- H1. rewrite PTree.gss; eauto. }
          { rewrite H. rewrite H2. eauto. }
          { intros; eapply check_annotations_depth_spec; eauto; simpl; eauto. }
          { intros; eapply check_annotations_depth_spec'; eauto; simpl; eauto. }
          { eapply check_annotations_divides_spec; eauto. }
      + monadInv EQ2.
    - monadInv H. unfold update_instr in EQ2.
      destruct (plt pc (st_nextnode st)).
      + monadInv EQ2. case_eq (is_trivial_annotation prog a m a0); intros.
        * unfold add_checks in EQ4. rewrite H in EQ4. simpl in EQ4. inv EQ4.
          econstructor. econstructor; simpl; eauto.
          { eapply check_annotations_range_spec; eauto. }
          { eapply check_annotations_range_spec'; eauto. }
          { destruct x0; auto. }
          { rewrite PTree.gso; eauto.
            rewrite PTree.gss; eauto. }
          { rewrite H; rewrite PTree.gss; eauto. }
          { intros. eapply check_annotations_depth_spec; eauto. }
          { intros. eapply check_annotations_depth_spec'; eauto. }
          { eapply check_annotations_divides_spec; eauto. }
        * generalize (add_checks_unchanged _ _ _ _ _ _ _ _ EQ4 pc p). simpl; intros.
          case_eq (is_singleton (snd a)); intros.
          { generalize (add_checks'_spec H H2 EQ4 HPlt).
            intros (pcs & Hpcs & LE).
            eexists. apply tr_Istore.
            eauto using check_annotations_range_spec.
            eauto using check_annotations_range_spec'.
            destruct x0; auto.
            rewrite <- H1, PTree.gss; reflexivity.
            rewrite H, H2. exact Hpcs.
            eauto using check_annotations_depth_spec.
            eauto using check_annotations_depth_spec'.
            eauto using check_annotations_divides_spec. }
          generalize (add_checks_spec f _ _ _ _ _ _ _ _ H H2 EQ4 HPlt). simpl. intros A. destruct A as [pcs A].
          unfold add_checks in EQ4. rewrite H in EQ4. rewrite H2 in EQ4. simpl in EQ4. monadInv EQ4.
          eexists. econstructor; simpl; eauto.
          { eapply check_annotations_range_spec; eauto. }
          { eapply check_annotations_range_spec'; eauto. }
          { destruct x0; auto. }
          { destruct x2; simpl in EQ5; monadInv EQ5.
            rewrite <- H1. rewrite PTree.gss; eauto. }
          { rewrite H. rewrite H2. eauto. }
          { intros; eapply check_annotations_depth_spec; eauto; simpl; eauto. }
          { intros; eapply check_annotations_depth_spec'; eauto; simpl; eauto. }
          { eapply check_annotations_divides_spec; eauto. }
      + monadInv EQ2.
    - monadInv H. unfold update_instr in EQ.
      destruct (plt pc (st_nextnode st)).
      + monadInv EQ.
        generalize (proj1 (add_epilogue_unchanged_and_sincr _ _ _ EQ0) pc p). simpl; intros.
        generalize (add_epilogue_spec f _ _ s EQ0 HPlt). intros A. destruct A as [pcs A].
        exists (pc::pcs). econstructor; eauto. simpl.
        rewrite <- H. rewrite PTree.gss. eauto.
      + monadInv EQ.
    - monadInv H. eexists; econstructor; eauto; eapply check_globals_of_builtin_args_spec; eauto.
    - monadInv H. unfold update_instr in EQ.
      destruct (plt pc (st_nextnode st)).
      + monadInv EQ.
        generalize (proj1 (add_epilogue_unchanged_and_sincr _ _ _ EQ0) pc p). simpl; intros.
        generalize (add_epilogue_spec f _ _ s EQ0 HPlt). intros A. destruct A as [pcs A].
        exists (pc::pcs). econstructor; eauto. simpl.
        rewrite <- H. rewrite PTree.gss. eauto.
      + monadInv EQ.
  Grab Existential Variables. eapply nil. eapply nil.
  Qed.

  Lemma add_checks_sincr:
    forall opt i alpha kappa addr args s s',
      add_checks prog ge STK SIZE opt i alpha kappa addr args s = OK s' ->
      sincr s s'.
Proof.
    intros opt i alpha kappa addr args s s' H. eapply add_checks_unchanged_and_sincr; eauto.
  Qed.

  Lemma transf_instr_sincr:
    forall s pc opt i s',
      transf_instr prog ge STK SIZE opt (OK s) pc i = OK s' ->
      sincr s s'.
Proof.
    destruct i; intros; try (simpl in H; inv H; econstructor; xomega; fail).
    - simpl in H; monadInv H.
      unfold update_instr in EQ2. destruct (plt pc (st_nextnode s)); monadInv EQ2.
      exploit add_checks_sincr; eauto. intros A; inv A. simpl in NEXTREG, NEXTNODE.
      econstructor; eauto.
    - simpl in H; monadInv H.
      unfold update_instr in EQ2. destruct (plt pc (st_nextnode s)); monadInv EQ2.
      exploit add_checks_sincr; eauto. intros A; inv A. simpl in NEXTREG, NEXTNODE.
      econstructor; eauto.
    - simpl in H; monadInv H.
      unfold update_instr in EQ. destruct (plt pc (st_nextnode s)); monadInv EQ.
      exploit add_epilogue_unchanged_and_sincr; eauto. intros [_ A]; inv A. simpl in NEXTREG, NEXTNODE.
      econstructor; eauto.
    - simpl in H; monadInv H. eapply sincr_refl.
    - simpl in H; monadInv H.
      unfold update_instr in EQ. destruct (plt pc (st_nextnode s)); monadInv EQ.
      exploit add_epilogue_unchanged_and_sincr; eauto. intros [_ A]; inv A. simpl in NEXTREG, NEXTNODE.
      econstructor; eauto.
  Qed.

  Instance annotation_eq_dec : Util.EqDec annotation.
Proof.
    red. decide equality.
    apply list_eq_dec, ablock_eq.
    apply Pos.eq_dec.
  Defined.

  Instance sum_eq_dec X Y `(Util.EqDec X) `(Util.EqDec Y) : Util.EqDec (X + Y).
Proof.
red. decide equality. Qed.

  Instance instruction_eq_dec : Util.EqDec instruction.
Proof.
    red.
    decide equality;
      try (apply list_eq_dec || apply option_eq || (apply sum_eq_dec; red));
      auto using Pos.eq_dec, eq_operation, eq_addressing, chunk_eq, annotation_eq_dec, signature_eq, external_function_eq, eq_condition.
    decide equality; apply Pos.eq_dec.
    decide equality; auto using Pos.eq_dec, Int.eq_dec, Int64.eq_dec, Float.eq_dec, Float32.eq_dec, chunk_eq.
  Defined.

  Lemma transf_instr_unchanged:
    forall pc opt i s s',
      transf_instr prog ge STK SIZE opt (OK s) pc i = OK s' ->
      forall pc', Plt pc' s.(st_nextnode) /\ pc <> pc' ->
             s.(st_code)!pc' = s'.(st_code)!pc'.
Proof.
    intros; destruct i; try (simpl in H; inv H; eauto; fail).
    - simpl in H. monadInv H. destruct H0 as [A B].
      unfold update_instr in EQ2. destruct (plt pc (st_nextnode s)); monadInv EQ2.
      generalize (add_checks_unchanged _ _ _ _ _ _ _ _ EQ4 pc' A); eauto.
      simpl. rewrite PTree.gso; eauto; xomega.
    - simpl in H. monadInv H. destruct H0 as [A B].
      unfold update_instr in EQ2. destruct (plt pc (st_nextnode s)); monadInv EQ2.
      generalize (add_checks_unchanged _ _ _ _ _ _ _ _ EQ4 pc' A); eauto.
      simpl. rewrite PTree.gso; eauto; xomega.
    - simpl in H. monadInv H. destruct H0 as [A B].
      unfold update_instr in EQ. destruct (plt pc (st_nextnode s)); monadInv EQ.
      generalize (proj1 (add_epilogue_unchanged_and_sincr _ _ _ EQ0) pc' A); eauto.
      simpl. rewrite PTree.gso; eauto; xomega.
    - simpl in H. monadInv H. reflexivity.
    - simpl in H. monadInv H. destruct H0 as [A B].
      unfold update_instr in EQ. destruct (plt pc (st_nextnode s)); monadInv EQ.
      generalize (proj1 (add_epilogue_unchanged_and_sincr _ _ _ EQ0) pc' A); eauto.
      simpl. rewrite PTree.gso; eauto; xomega.
  Qed.

  Lemma tr_local_regs_unchanged':
    forall kappa f c c' n pc1 ofs depth regs regs' pc2 pcs,
      tr_local_regs kappa f c pc1 ofs depth n regs regs' pc2 pcs ->
      (forall pc, In pc pcs -> c!pc = c'!pc) ->
      tr_local_regs kappa f c' pc1 ofs depth n regs regs' pc2 pcs.
Proof.
    induction n; intros.
    - inv H; econstructor.
    - inv H; econstructor; eauto.
      + rewrite <- H0; eauto. eapply in_eq.
      + rewrite <- H0; eauto. eapply in_cons. eapply in_eq.
      + rewrite <- H0; eauto. eapply in_cons. eapply in_cons. eapply in_eq.
      + eapply IHn; eauto.
        intros. eapply H0. eapply in_cons; eauto.
        eapply in_cons. eapply in_cons. exact H.
  Qed.

  Lemma tr_global_regs_unchanged':
    forall kappa f c c' n pc1 id ofs regs pc2 pcs,
      tr_global_regs kappa f c pc1 id ofs n regs pc2 pcs ->
      (forall pc, In pc pcs -> c!pc = c'!pc) ->
      tr_global_regs kappa f c' pc1 id ofs n regs pc2 pcs.
Proof.
    induction n; intros.
    - inv H; econstructor.
    - inv H; econstructor; eauto.
      + rewrite <- H0; eauto. eapply in_eq.
      + eapply IHn; eauto.
        intros. eapply H0. eapply in_cons; eauto.
  Qed.

  Lemma tr_regs_annot_unchanged':
        forall kappa f c c' alpha pc1 regs regs' pc2 pcs,
          tr_regs_annot kappa f c pc1 alpha regs regs' pc2 pcs ->
          (forall pc, In pc pcs -> c!pc = c'!pc) ->
          tr_regs_annot kappa f c' pc1 alpha regs regs' pc2 pcs.
Proof.
    induction alpha; intros.
    - inv H; econstructor; eauto.
    - inv H.
      + econstructor; eauto.
        * eapply IHalpha; eauto.
          intros. eapply H0. eapply in_or_app. right; eauto.
        * eapply tr_local_regs_unchanged'; eauto.
          intros. eapply H0. eapply in_or_app; left; eauto.
      + econstructor; eauto.
        * eapply IHalpha; eauto.
          intros. eapply H0. eapply in_or_app. right; eauto.
        * eapply tr_global_regs_unchanged'; eauto.
          intros. eapply H0. eapply in_or_app; left; eauto.
  Qed.

  Lemma tr_cmp_regs_unchanged':
    forall c c' regs pc1 reg0 pc2 pcs,
      tr_cmp_regs c pc1 reg0 regs pc2 pcs ->
      (forall pc, In pc pcs -> c!pc = c'!pc) ->
      tr_cmp_regs c' pc1 reg0 regs pc2 pcs.
Proof.
    induction regs; intros.
    - inv H; econstructor.
    - inv H; econstructor.
      + rewrite <- H0; eauto. eapply in_eq.
      + eapply IHregs; eauto.
        intros. eapply H0. eapply in_cons; eauto.
  Qed.

  Lemma tr_or_regs_unchanged':
    forall c c' regs pc1 reg0 pc2 pcs,
      tr_or_regs c pc1 reg0 regs pc2 pcs ->
      (forall pc, In pc pcs -> c!pc = c'!pc) ->
      tr_or_regs c' pc1 reg0 regs pc2 pcs.
Proof.
    induction regs; intros.
    - inv H; econstructor.
    - inv H; econstructor.
      + rewrite <- H0; eauto. eapply in_eq.
      + eapply IHregs; eauto.
        intros. eapply H0. eapply in_cons; eauto.
  Qed.
    
  Lemma tr_checks_unchanged:
    forall f c c' pc1 i alpha kappa addr args pcs,
      tr_checks f c pc1 i alpha kappa addr args pcs ->
      (forall pc, In pc pcs -> c!pc = c'!pc) ->
      tr_checks f c' pc1 i alpha kappa addr args pcs.
Proof.
    intros. inv H.
    econstructor.
    - rewrite <- H0; eauto.
      eapply in_eq.
    - eapply tr_regs_annot_unchanged'; eauto.
      intros. eapply H0. repeat eapply in_cons.
      eapply in_or_app. left; eauto.
    - eapply tr_cmp_regs_unchanged'; eauto.
      intros. eapply H0. repeat eapply in_cons.
      eapply in_or_app. right. eapply in_or_app. left; eauto.
    - reflexivity.
    - rewrite <- H0; eauto.
      eapply in_cons. eapply in_eq.
    - eapply tr_or_regs_unchanged'; eauto.
      intros. eapply H0. repeat eapply in_cons.
      eapply in_or_app. right. eapply in_or_app. right; eauto.
      eapply in_or_app. left; eauto.
    - rewrite <- H0; eauto.
      eapply in_cons. eapply in_cons. eapply in_eq.
    - rewrite <- H0; eauto.
      eapply in_cons. eapply in_cons. eapply in_cons. eapply in_eq.
    - inv H9; econstructor; eauto.
      + rewrite <- H0; eauto.
        repeat eapply in_cons. eapply in_or_app. right; eauto.
        eapply in_or_app. right; eauto. eapply in_or_app; right; eauto.
        eapply in_eq.
      + rewrite <- H0; eauto.
        repeat eapply in_cons. eapply in_or_app. right; eauto.
        eapply in_or_app. right; eauto. eapply in_or_app; right; eauto.
        eapply in_cons. eapply in_eq.
      + rewrite <- H0; eauto.
        repeat eapply in_cons. eapply in_or_app. right; eauto.
        eapply in_or_app. right; eauto. eapply in_or_app; right; eauto.
        eapply in_cons. eapply in_cons. eapply in_eq.
    - eauto.
    - eauto.
    - eauto.
  Qed.

  Lemma tr_local_regs'_unchanged' c {f c' pc δ base bound r regs pc' pcs}:
    (∀ pc, In pc pcsc' ! pc = c ! pc)%positive
    tr_local_regs' f c pc δ base bound r regs pc' pcs
    tr_local_regs' f c' pc δ base bound r regs pc' pcs.
Proof.
    intros H X; inv X; econstructor; eauto; rewrite H; auto;
    simpl; auto 42.
  Qed.

  Lemma tr_global_regs'_unchanged' c {f c' pc δ base bound r pc' pcs}:
    (∀ pc, In pc pcsc' ! pc = c ! pc)%positive
    tr_global_regs' f c pc δ base bound r pc' pcs
    tr_global_regs' f c' pc δ base bound r pc' pcs.
Proof.
    intros H X; inv X; econstructor; eauto; rewrite H; auto;
    simpl; auto.
  Qed.

  Lemma tr_regs_annot'_unchanged' c {f c' pc α regs regs' pc' pcs} :
    (∀ pc, In pc pcsc' ! pc = c ! pc)%positive
    tr_regs_annot' f c pc α regs regs' pc' pcs
    tr_regs_annot' f c' pc α regs regs' pc' pcs.
Proof.
    intros X Y; revert X; elim Y; clear.
    constructor.
    - intros f c pc1 pc2 pc3 depth varname base bound alpha regs1 regs1' regs2 regs2' pcs1 pcs2 H H0 H1 H2 H3 H4 H5
             H6 X.
      econstructor; eauto.
      apply H0. intros pc Hpc; apply X. rewrite in_app; auto.
      revert H1. apply tr_local_regs'_unchanged'.
      intros pc Hpc; apply X; rewrite in_app; auto.
    - intros f c pc1 pc2 pc3 b id base bound alpha regs1 regs2 regs2' pcs1 pcs2 H H0 H1 H2 H3 H4 H5 X.
      econstructor; eauto.
      apply H1.
      intros pc Hpc; apply X; rewrite in_app; auto.
      revert H2. apply tr_global_regs'_unchanged'.
      intros pc Hpc; apply X; rewrite in_app; auto.
  Qed.

  Lemma tr_cmp_regs'_unchanged' c {c' pc r regs pc' pcs}:
    (∀ pc, In pc pcsc' ! pc = c ! pc)%positive
    tr_cmp_regs' c pc r regs pc' pcs
    tr_cmp_regs' c' pc r regs pc' pcs.
Proof.
    intros X Y; revert X; elim Y; clear.
    - intros c pc reg0 X. constructor.
    - intros c pc1 pc2 reg0 reg1 regs pcs H H0 H1 X. constructor; auto.
      rewrite X; simpl; auto.
      apply H1. intros pc Hpc; apply X; simpl; auto.
    - intros c pc1 pc2 reg0 reg1 reg2 regs pcs H H0 H1 H2 H3 X.
      constructor; auto; try (rewrite X; simpl; auto; fail).
      apply H3. intros pc Hpc; apply X; simpl; auto.
  Qed.

  Lemma tr_checks'_unchanged c {f c' pc1 i α ϰ addr args pcs} :
    (∀ pc, In pc pcsc' ! pc = c ! pc) →
    tr_checks' f c pc1 i α ϰ addr args pcs
    tr_checks' f c' pc1 i α ϰ addr args pcs.
Proof.
    intros E H; inv H. econstructor; eauto.
    - rewrite E; auto. left; reflexivity.
    - eapply tr_regs_annot'_unchanged'. 2: eassumption.
      intros pc Hpc; apply E. simpl; rewrite ! in_app; auto 42.
    - eapply tr_cmp_regs'_unchanged'. 2: eassumption.
      intros pc Hpc; apply E; simpl; rewrite ! in_app; auto 42.
    - rewrite E; simpl; auto.
    - eapply tr_or_regs_unchanged'. eassumption.
      intros pc Hpc; symmetry; apply E; simpl; rewrite ! in_app; auto 42.
    - rewrite E; simpl; auto.
    - rewrite E; simpl; auto.
    - inv H8. econstructor; eauto; try (rewrite E; eauto; simpl; rewrite ! in_app; simpl; auto 42).
  Qed.

  Lemma tr_epilogue_unchanged:
    forall f c c' pc i pcs,
      tr_epilogue f c pc i pcs ->
      (forall pc, In pc pcs -> c!pc = c'!pc) ->
      tr_epilogue f c' pc i pcs.
Proof.
    intros; inv H. econstructor; eauto.
    - rewrite <- H0; eauto. eapply in_eq.
    - rewrite <- H0; eauto. eapply in_cons. eapply in_eq.
    - rewrite <- H0; eauto. eapply in_cons. eapply in_cons. eapply in_eq.
    - rewrite <- H0; eauto. eapply in_cons. eapply in_cons. eapply in_cons. eapply in_eq.
    - rewrite <- H0; eauto. eapply in_cons. eapply in_cons. eapply in_cons. eapply in_cons. eapply in_eq.
  Qed.
  
  Lemma tr_instrs_unchanged:
    forall f c c' pc i pcs,
      tr_instrs f pc i c pcs ->
      (forall pc, In pc pcs -> c!pc = c'!pc) ->
      tr_instrs f pc i c' pcs.
Proof.
    intros; inv H; try (econstructor; rewrite <- H0; eauto; eapply in_eq; fail).
    - econstructor; eauto.
      + rewrite <- H0; eauto. destruct (is_trivial_annotation prog alpha kappa addr); eapply in_eq.
      + destruct (is_trivial_annotation prog alpha kappa addr).
        * rewrite <- H0; eauto. eapply in_cons; eapply in_eq.
        * destruct (is_singleton (snd alpha)).
          { revert H2. apply tr_checks'_unchanged.
            intros pc' Hpc; symmetry; apply H0; simpl; auto. }
          { eapply tr_checks_unchanged; eauto.
            intros. eapply H0. eapply in_cons. eauto. }
    - econstructor; eauto.
      + rewrite <- H0; eauto. destruct (is_trivial_annotation prog alpha kappa addr); eapply in_eq.
      + destruct (is_trivial_annotation prog alpha kappa addr).
        * rewrite <- H0; eauto. eapply in_cons; eapply in_eq.
        * destruct (is_singleton (snd alpha)).
          { revert H2. apply tr_checks'_unchanged.
            intros pc' Hpc; symmetry; apply H0; simpl; auto. }
          { eapply tr_checks_unchanged; eauto.
            intros. eapply H0. eapply in_cons. eauto. }
    - econstructor; eauto.
      + rewrite <- H0; eauto. eapply in_eq.
      + eapply tr_epilogue_unchanged; eauto.
        intros. eapply H0. eapply in_cons. eauto.
    - econstructor; eauto. rewrite <- H0; eauto. eapply in_eq.
    - econstructor; eauto.
      + rewrite <- H0; eauto. eapply in_eq.
      + eapply tr_epilogue_unchanged; eauto.
        intros. eapply H0. eapply in_cons. eauto.
  Qed.

  Lemma tr_local_regs_plt:
    forall kappa f s n pc1 ofs depth regs regs' pc2 pcs,
      tr_local_regs kappa f (st_code s) pc1 ofs depth n regs regs' pc2 pcs ->
      forall pc, In pc pcs -> Plt pc (st_nextnode s).
Proof.
    induction n; intros.
    - inv H. inv H0.
    - inv H. destruct H0.
      + inv H. generalize (st_wf s pc); intros A.
        destruct A; congruence.
      + simpl in H. destruct H as [H | [H | H]].
        * inv H. generalize (st_wf s (Psucc pc1)); intros A.
          destruct A; try congruence.
        * inv H. generalize (st_wf s (Psucc (Psucc pc1))); intros A.
          destruct A; try congruence.
        * eapply IHn; eauto.
      + eapply IHn; eauto.
  Qed.

  Lemma tr_global_regs_plt:
    forall kappa f s n pc1 id ofs regs pc2 pcs,
      tr_global_regs kappa f (st_code s) pc1 id ofs n regs pc2 pcs ->
      forall pc, In pc pcs -> Plt pc (st_nextnode s).
Proof.
    induction n; intros.
    - inv H. inv H0.
    - inv H. destruct H0.
      + inv H. generalize (st_wf s pc); intros A.
        destruct A; congruence.
      + eapply IHn; eauto.
      + eapply IHn; eauto.
  Qed.
  
  Lemma tr_regs_annot_plt:
    forall kappa f s alpha pc1 regs regs' pc2 pcs,
      tr_regs_annot kappa f (st_code s) pc1 alpha regs regs' pc2 pcs ->
      forall pc, In pc pcs -> Plt pc (st_nextnode s).
Proof.
    induction alpha; intros.
    - inv H. inv H0.
    - inv H.
      + eapply in_app_or in H0.
        destruct H0.
        * eapply tr_local_regs_plt; eauto.
        * eapply IHalpha; eauto.
      + eapply in_app_or in H0.
        destruct H0.
        * eapply tr_global_regs_plt; eauto.
        * eapply IHalpha; eauto.
  Qed.

  Lemma tr_cmp_regs_plt:
    forall s regs pc1 reg0 pc2 pcs,
      tr_cmp_regs (st_code s) pc1 reg0 regs pc2 pcs ->
      forall pc, In pc pcs -> Plt pc (st_nextnode s).
Proof.
    induction regs; intros.
    - inv H. inv H0.
    - inv H.
      destruct H0.
      + inv H. generalize (st_wf s pc); intros A. destruct A; congruence.
      + eapply IHregs; eauto.
  Qed.

  Lemma tr_or_regs_plt:
    forall s regs pc1 reg0 pc2 pcs,
      tr_or_regs (st_code s) pc1 reg0 regs pc2 pcs ->
      forall pc, In pc pcs -> Plt pc (st_nextnode s).
Proof.
    induction regs; intros.
    - inv H. inv H0.
    - inv H.
      destruct H0.
      + inv H. generalize (st_wf s pc); intros A. destruct A; congruence.
      + eapply IHregs; eauto.
  Qed.
          
  Lemma tr_checks_plt:
    forall f s pc i alpha kappa addr args pcs,
      tr_checks f (st_code s) pc i alpha kappa addr args pcs ->
      forall pc, In pc pcs -> Plt pc (st_nextnode s).
Proof.
    intros. inv H.
    simpl in H0. destruct H0.
    - inv H. generalize (st_wf s pc0); intros.
      destruct H; congruence.
    - destruct H.
      + inv H. generalize (st_wf s pc0); intros.
        destruct H; congruence.
      + destruct H.
        * inv H. generalize (st_wf s pc0); intros.
          destruct H; congruence.
        * destruct H.
          { inv H. generalize (st_wf s (Psucc pc5)); intros.
            destruct H; congruence. }
          { eapply in_app_or in H.
            destruct H.
            - eapply tr_regs_annot_plt; eauto.
            - eapply in_app_or in H.
              destruct H.
              + eapply tr_cmp_regs_plt; eauto.
              + eapply in_app_or in H. destruct H.
                * eapply tr_or_regs_plt; eauto.
                * inv H9. simpl in H. destruct H as [H | [H | H]]; generalize (st_wf s pc0); inv H; intros; destruct H; try congruence.
                  exfalso; assumption. }
  Qed.

  Definition has_code_plt {s pc i} :
    (st_code s) ! pc = Some i
    (pc < st_nextnode s)%positive.
Proof.
    generalize (st_wf s pc). intuition congruence.
  Qed.

  Ltac has_code_plt :=
    repeat match goal with H : _ |- _ => apply has_code_plt in H end.

  Lemma tr_local_regs'_plt {f s pc1 δ base bound regs regs' pc2 pcs} :
    tr_local_regs' f (st_code s) pc1 δ base bound regs regs' pc2 pcs
    ∀ pc, In pc pcs → (pc < st_nextnode s)%positive.
Proof.
    intros X; inv X; simpl; has_code_plt; unfold Plt in *; intuition (subst; Psatz.lia).
  Qed.

  Lemma tr_global_regs'_plt {f s pc1 g base bound regs pc2 pcs} :
    tr_global_regs' f (st_code s) pc1 g base bound regs pc2 pcs
    ∀ pc, In pc pcs → (pc < st_nextnode s)%positive.
Proof.
    intros X; inv X; simpl; has_code_plt; unfold Plt in *; intuition (subst; Psatz.lia).
  Qed.

  Lemma tr_regs_annot'_plt {f s pc1 α regs regs' pc2 pcs} :
    tr_regs_annot' f (st_code s) pc1 α regs regs' pc2 pcs
    ∀ pc, In pc pcs → (pc < st_nextnode s)%positive.
Proof.
    remember (st_code s) as c eqn: Y.
    intros X; revert Y; elim X; clear X.
    intros _ _ _ _ ? ().
    intros f0 c0 pc0 pc3 pc4 depth varname base bound alpha regs1 regs1' regs2 regs2' pcs1 pcs2 H H0 H1 H2 H3 H4
           H5 H6 Y pc H7.
    apply in_app in H7; destruct H7 as [ X | X ]; auto.
    subst. exact (tr_local_regs'_plt H1 _ X).
    intros f0 c0 pc0 pc3 pc4 b id base bound alpha regs1 regs2 regs2' pcs1 pcs2 H H0 H1 H2 H3 H4 H5 Y pc H6.
    apply in_app in H6; destruct H6 as [ X | X ]; auto.
    subst. exact (tr_global_regs'_plt H2 _ X).
  Qed.

  Lemma tr_cmp_regs'_plt {s pc r regs pc' pcs}:
    tr_cmp_regs' (st_code s) pc r regs pc' pcs
    ∀ pc, In pc pcs → (pc < st_nextnode s)%positive.
Proof.
    intros X; remember (st_code s) as c eqn: Y; revert Y; elim X; clear X; simpl. easy.
    intros c0 pc1 pc2 reg0 reg1 regs0 pcs0 H H0 H1 Y pc0 [ -> | H2 ]; auto.
    subst c0. eauto using has_code_plt.
    intros c0 pc1 pc2 reg0 reg1 reg2 regs0 pcs0 H H0 H1 H2 H3 Y pc0 X.
    subst c0. has_code_plt.
    repeat (destruct X as [ ? | X]; [ subst; assumption | ]). auto.
  Qed.

  Lemma tr_checks'_plt {f s pc i α ϰ addr args pcs} :
    tr_checks' f (st_code s) pc i α ϰ addr args pcs
    ∀ pc, In pc pcs → (pc < st_nextnode s)%positive.
Proof.
    intros H; inv H. simpl in *.
    has_code_plt.
    intros n X.
    repeat (destruct X as [ ? | X ]; [ subst n; assumption | ]).
    rewrite ! in_app in X. destruct X as [ X | [ X | [ X | X ] ] ].
    refine (tr_regs_annot'_plt _ n X); eassumption.
    refine (tr_cmp_regs'_plt _ n X); eassumption.
    refine (tr_or_regs_plt _ _ _ _ _ _ _ n X); eassumption.
    inv H8; has_code_plt. simpl in X.
    repeat (destruct X as [ ? | X ]; [ subst n; assumption | ]).
    elim X.
  Qed.

  Lemma tr_epilogue_plt:
    forall f s pc i pcs,
      tr_epilogue f (st_code s) pc i pcs ->
      forall pc, In pc pcs -> Plt pc (st_nextnode s).
Proof.
    intros f s pc i pcs H; inv H. has_code_plt. intros pc'; simpl. intuition (subst; auto).
  Qed.

  Lemma tr_instrs_plt:
    forall f pc i s pcs,
      tr_instrs f pc i (st_code s) pcs ->
      forall pc, In pc pcs -> Plt pc (st_nextnode s).
Proof.
    destruct i; intros; inv H; try (generalize (st_wf s pc); intros; simpl in H0; destruct H0; try (exfalso; assumption); inv H0; destruct H; congruence; fail).
    - generalize (st_wf s pc); intros.
      destruct (is_trivial_annotation prog a m a0).
      { simpl in H0; destruct H0.
        + inv H0; destruct H; congruence.
        + destruct H0; inv H0.
          generalize (st_wf s pc0); intros.
          destruct H0; congruence. }
      { simpl in H0; destruct H0.
        + inv H0; destruct H; congruence.
        + destruct (is_singleton (snd a)).
          * refine (tr_checks'_plt _ _ _); eassumption.
          * eapply tr_checks_plt; eauto. }
    - generalize (st_wf s pc); intros.
      destruct (is_trivial_annotation prog a m a0).
      { simpl in H0; destruct H0.
        + inv H0; destruct H; congruence.
        + destruct H0; inv H0.
          generalize (st_wf s pc0); intros.
          destruct H0; congruence. }
      { simpl in H0; destruct H0.
        + inv H0; destruct H; congruence.
        + destruct (is_singleton (snd a)).
          * refine (tr_checks'_plt _ _ _); eassumption.
          * eapply tr_checks_plt; eauto. }
    - generalize (st_wf s1 pc); intros; simpl in H0; destruct H0; try (exfalso; assumption); inv H0; destruct H; congruence.
    - generalize (st_wf s1 pc); intros.
      simpl in H0; destruct H0.
      + inv H0; destruct H; congruence.
      + eapply tr_epilogue_plt; eauto.
    - generalize (st_wf s pc); intros.
      simpl in H0; destruct H0.
      + inv H0; destruct H; congruence.
      + eapply tr_epilogue_plt; eauto.
  Qed.
    
  Lemma tr_instrs_unchanged':
    forall f pc pc' pcs opt i i' s s',
      tr_instrs f pc i (st_code s) pcs ->
      (forall pc, In pc pcs -> pc' <> pc) ->
      (st_code s)!pc' = Some i' ->
      transf_instr prog ge STK SIZE opt (OK s) pc' i' = OK s' ->
      tr_instrs f pc i (st_code s') pcs.
Proof.
    intros. eapply tr_instrs_unchanged; eauto.
    intros. eapply transf_instr_unchanged; eauto.
    split.
    - eapply tr_instrs_plt; eauto.
    - eapply H0; eauto.
  Qed.

  Lemma tr_local_regs_determ:
    forall kappa f c n pc1 ofs depth regs regs' regsb regsb' pc2 pc2' pcs pcs',
      tr_local_regs kappa f c pc1 ofs depth n regs regsb pc2 pcs ->
      tr_local_regs kappa f c pc1 ofs depth n regs' regsb' pc2' pcs' ->
      regs = regs' /\ regsb = regsb' /\ pc2 = pc2' /\ pcs = pcs'.
Proof.
    induction n; intros.
    - inv H; inv H0; eauto.
    - inv H; inv H0; try congruence.
      { generalize (IHn _ _ _ _ _ _ _ _ _ _ _ H5 H12); intros.
        destruct H as [A [B [C D]]]. subst.
        rewrite H2 in H1. inv H1.
        repeat split; try reflexivity.
        + assert (Pos.pred (Pos.pred (Psucc (Psucc reg))) = Pos.pred (Pos.pred (Psucc (Psucc reg0)))) by congruence.
          repeat (rewrite Pos.pred_succ in H). congruence.
        + assert (Pos.pred (Pos.pred (Psucc (Psucc reg))) = Pos.pred (Pos.pred (Psucc (Psucc reg0)))) by congruence.
          repeat (rewrite Pos.pred_succ in H). congruence. }
      { eapply IHn; eauto. }
  Qed.

  Lemma tr_global_regs_determ:
    forall kappa f c id n pc1 ofs regs regs' pc2 pc2' pcs pcs',
      tr_global_regs kappa f c pc1 id ofs n regs pc2 pcs ->
      tr_global_regs kappa f c pc1 id ofs n regs' pc2' pcs' ->
      regs = regs' /\ pc2 = pc2' /\ pcs = pcs'.
Proof.
    induction n; intros.
    - inv H; inv H0; eauto.
    - inv H; inv H0; try congruence.
      + generalize (IHn _ _ _ _ _ _ _ _ H3 H5); intros.
        destruct H as [A [B C]]. subst.
        rewrite H2 in H1. inv H1.
        repeat split; reflexivity.
      + eapply IHn; eauto.
  Qed.
  
  Lemma tr_regs_annot_determ:
    forall kappa f c alpha pc1 regs regs' regs2 regs2' pc2 pc2' pcs pcs',
      tr_regs_annot kappa f c pc1 alpha regs regs2 pc2 pcs ->
      tr_regs_annot kappa f c pc1 alpha regs' regs2' pc2' pcs' ->
      regs = regs' /\ regs2 = regs2' /\ pc2 = pc2' /\ pcs = pcs'.
Proof.
    induction alpha; intros.
    - inv H; inv H0; eauto.
    - inv H; inv H0.
      + generalize (tr_local_regs_determ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ H12 H4).
        intros [A [B [C D]]]. subst.
        generalize (IHalpha pc3 _ _ _ _ _ _ pcs2 pcs3 H3 H10).
        intros [A [B [C D]]]. subst. repeat split; reflexivity.
      + generalize (tr_global_regs_determ _ _ _ _ _ _ _ _ _ _ _ _ _ H5 H14).
        intros [A [B C]]. subst.
        generalize (IHalpha _ _ _ _ _ _ _ _ _ H4 H9).
        intros [A [B [C D]]]. subst.
        rewrite H8 in H3; inv H3.
        repeat split; reflexivity.
  Qed.
  
  Lemma tr_cmp_regs_determ:
    forall c regs pc1 reg0 pc2 pc2' pcs pcs',
      tr_cmp_regs c pc1 reg0 regs pc2 pcs ->
      tr_cmp_regs c pc1 reg0 regs pc2' pcs' ->
      pc2 = pc2' /\ pcs = pcs'.
Proof.
    induction regs; intros.
    - inv H; inv H0; eauto.
    - inv H; inv H0. rewrite H6 in H5; inv H5.
      generalize (IHregs _ _ _ _ _ _ H9 H10); intros [A B]. subst. eauto.
  Qed.

  Lemma tr_or_regs_determ:
    forall c regs pc1 reg0 pc2 pc2' pcs pcs',
      tr_or_regs c pc1 reg0 regs pc2 pcs ->
      tr_or_regs c pc1 reg0 regs pc2' pcs' ->
      pc2 = pc2' /\ pcs = pcs'.
Proof.
    induction regs; intros.
    - inv H; inv H0; eauto.
    - inv H; inv H0. rewrite H6 in H5; inv H5.
      generalize (IHregs _ _ _ _ _ _ H9 H10); intros [A B]. subst. eauto.
  Qed.
  
  Lemma tr_checks_determ:
    forall f c pc i alpha kappa addr args pcs pcs',
      tr_checks f c pc i alpha kappa addr args pcs ->
      tr_checks f c pc i alpha kappa addr args pcs' ->
      pcs = pcs'.
Proof.
    intros; inv H; inv H0; eauto.
    rewrite H in H1; inv H1.
    generalize (tr_regs_annot_determ _ _ _ _ _ _ _ _ _ _ _ _ _ H4 H2).
    intros [A [B [C D]]]. subst. inv A.
    generalize (tr_cmp_regs_determ _ _ _ _ _ _ _ _ H3 H13).
    intros [A B]. subst.
    generalize (tr_or_regs_determ _ _ _ _ _ _ _ _ H6 H16).
    intros [A B]. subst. inv H9; inv H19. reflexivity.
  Qed.

  Lemma tr_epilogue_determ:
    forall f c pc i pcs pcs',
      tr_epilogue f c pc i pcs ->
      tr_epilogue f c pc i pcs' ->
      pcs = pcs'.
Proof.
    intros; inv H; inv H0; eauto.
  Qed.

  Lemma tr_cmp_regs'_determ {c pc r regs pc' pcs pc'' pcs'}:
    tr_cmp_regs' c pc r regs pc' pcs
    tr_cmp_regs' c pc r regs pc'' pcs' →
    pc' = pc'' ∧ pcs = pcs'.
Proof.
    revert pc pc' pc'' pcs pcs'.
    elim regs; clear regs.
    - intros pc pc' pc'' pcs pcs' H; inv H; intros H; inv H. auto.
    - intros a regs IH pc pc' pc'' pcs pcs' H; inv H; intros H; inv H;
        match goal with X : _, Y : _ |- _ => specialize (IH _ _ _ _ _ X Y); destruct IH end;
      intuition congruence.
  Qed.

  Lemma tr_local_regs'_determ {f c pc1 δ base bound regs regs' pc2 pcs regs'' regs''' pc2' pcs'} :
    tr_local_regs' f c pc1 δ base bound regs regs' pc2 pcs
    tr_local_regs' f c pc1 δ base bound regs'' regs''' pc2' pcs' →
    regs = regs'' ∧ regs' = regs''' ∧ pc2 = pc2' ∧ pcs' = pcs.
Proof.
    intros H; inv H; intros H; inv H; try (elim H0; reflexivity);
    repeat
    match goal with
    | H : _ ! _ = Some _, K : _ ! _ = Some _ |- _ => rewrite H in K; clear H; inv K
    end; auto.
  Qed.

  Lemma tr_global_regs'_determ {f c pc1 g base bound regs pc2 pcs regs' pc2' pcs'} :
    tr_global_regs' f c pc1 g base bound regs pc2 pcs
    tr_global_regs' f c pc1 g base bound regs' pc2' pcs' →
    regs = regs' ∧ pc2 = pc2' ∧ pcs = pcs'.
Proof.
    intros H; inv H; intros H; inv H; try (elim H0; reflexivity);
    repeat
    match goal with
    | H : _ ! _ = Some _, K : _ ! _ = Some _ |- _ => rewrite H in K; clear H; inv K
    end; auto.
  Qed.

  Lemma tr_regs_annot'_determ {f c pc1 α regs regs' pc2 pcs regs'' regs''' pc2' pcs'} :
    tr_regs_annot' f c pc1 α regs regs' pc2 pcs
    tr_regs_annot' f c pc1 α regs'' regs''' pc2' pcs' →
    regs = regs'' ∧ regs' = regs''' ∧ pc2' = pc2pcs = pcs'.
Proof.
    revert pc1 regs regs' pc2 pcs regs'' regs''' pc2' pcs'.
    elim α; clear α.
    intros pc1 regs regs' pc2 pcs regs'' regs''' pc2' pcs'.
    intros H; inv H; intros H; inv H. auto.
    intros ab α IH pc1 regs regs' pc2 pcs regs'' regs''' pc2' pcs'.
    intros H; inv H; intros H; inv H.
    generalize (tr_local_regs'_determ H3 H12). intros ( -> & -> & -> & -> ).
    match goal with X : _, Y : _ |- _ => specialize (IH _ _ _ _ _ _ _ _ _ X Y); clear X Y end.
    intuition congruence.
    generalize (tr_global_regs'_determ H4 H15). intros ( -> & -> & -> ).
    match goal with X : _, Y : _ |- _ => specialize (IH _ _ _ _ _ _ _ _ _ X Y); clear X Y end.
    intuition congruence.
  Qed.

  Lemma tr_checks'_determ {f c pc i α ϰ addr args pcs pcs'} :
    tr_checks' f c pc i α ϰ addr args pcs
    tr_checks' f c pc i α ϰ addr args pcs' →
    pcs' = pcs.
Proof.
    intros H; inv H; intros H; inv H.
    f_equal.
    repeat
      match goal with
      | H : _ ! _ = Some _, K : _ ! _ = Some _ |- _ => rewrite H in K; clear H; inv K
      | H : ?a = _ :: _, K : ?a = _ :: _ |- _ => rewrite H in K; inv K
      | H : __ |- _ => destruct H as [ ? ? ]
      | H : ?a = ?b |- _ => subst a || subst b
      | X : tr_regs_annot' _ _ _ _ _ _ _ _
      , Y : tr_regs_annot' _ _ _ _ _ _ _ _ |- _ => generalize (tr_regs_annot'_determ X Y); clear X Y; intros ?
      | X : tr_or_regs _ _ _ _ _ _
      , Y : tr_or_regs _ _ _ _ _ _ |- _ => generalize (tr_or_regs_determ _ _ _ _ _ _ _ _ X Y); clear X Y; intros ?
      | X : tr_cmp_regs' _ _ _ _ _ _
      , Y : tr_cmp_regs' _ _ _ _ _ _ |- _ => generalize (tr_cmp_regs'_determ X Y); clear X Y; intros ?
      | H : tr_return_undef _ _ _ _ |- _ => inv H
      end.
    reflexivity.
  Qed.
    
  Lemma tr_instrs_determ:
    forall f pc i c pcs pcs',
      tr_instrs f pc i c pcs ->
      tr_instrs f pc i c pcs' ->
      pcs = pcs'.
Proof.
    intros. inv H; inv H0; eauto.
    - rewrite H1 in H15; inv H15.
      destruct (is_trivial_annotation prog alpha kappa addr).
      + reflexivity.
      + destruct (is_singleton (snd alpha)).
        * f_equal. refine (tr_checks'_determ _ _); eassumption.
        * generalize (tr_checks_determ f _ _ _ _ _ _ _ _ _ H2 H16).
          intros A. inv A. eauto.
    - rewrite H1 in H15; inv H15.
      destruct (is_trivial_annotation prog alpha kappa addr).
      + reflexivity.
      + destruct (is_singleton (snd alpha)).
        * f_equal. refine (tr_checks'_determ _ _); eassumption.
        * generalize (tr_checks_determ f _ _ _ _ _ _ _ _ _ H2 H16).
          intros A. inv A. eauto.
    - rewrite H1 in H9; inv H9.
      generalize (tr_epilogue_determ f _ _ _ _ _ H2 H10).
      intros A; inv A. eauto.
    - rewrite H1 in H3; inv H3.
      generalize (tr_epilogue_determ f _ _ _ _ _ H2 H6).
      intros A; inv A. eauto.
  Qed.

  Lemma tr_local_regs_ple:
    forall kappa f c n pc1 ofs depth regs regs' pc2 pcs,
      tr_local_regs kappa f c pc1 ofs depth n regs regs' pc2 pcs ->
      Ple pc1 pc2 /\ forall pc, In pc pcs -> Ple pc1 pc.
Proof.
    induction n; intros.
    - inv H. split; try xomega.
      simpl; intros. exfalso; eauto.
    - inv H.
      { exploit IHn; eauto.
        intros [A B]. split; try xomega. intros.
        simpl in H. destruct H as [H | [H | [H | H]]].
        + inv H. xomega.
        + inv H. xomega.
        + inv H. xomega.
        + generalize (B pc H); intros; xomega. }
      { eapply IHn; eauto. }
  Qed.

  Lemma tr_global_regs_ple:
    forall kappa f c n pc1 id ofs regs pc2 pcs,
      tr_global_regs kappa f c pc1 id ofs n regs pc2 pcs ->
      Ple pc1 pc2 /\ forall pc, In pc pcs -> Ple pc1 pc.
Proof.
    induction n; intros.
    - inv H. split; try xomega.
      simpl; intros. exfalso; auto.
    - inv H.
      { exploit IHn; eauto.
        intros [A B]. split; try xomega. intros.
        simpl in H; destruct H.
        + inv H. xomega.
        + generalize (B pc H); intros; xomega. }
      { eapply IHn; eauto. }
  Qed.
  
  Lemma tr_regs_annot_ple:
    forall kappa (f : function) (c : code) (alpha : list ablock) (pc1 : node)
      (regs regs' : list reg) (pc2 : node) (pcs : list node),
      tr_regs_annot kappa f c pc1 alpha regs regs' pc2 pcs ->
      Ple pc1 pc2 /\ forall pc : node, In pc pcs -> Ple pc1 pc.
Proof.
    induction alpha; intros.
    - inv H. split; try xomega.
      simpl; intros. exfalso; auto.
    - inv H.
      + exploit IHalpha; eauto. intros [A B].
        exploit tr_local_regs_ple; eauto. intros [C D].
        split; try xomega. intros. eapply in_app_or in H.
        destruct H.
        * eapply D; eauto.
        * generalize (B pc H). intros. xomega.
      + exploit IHalpha; eauto. intros [A B].
        exploit tr_global_regs_ple; eauto. intros [C D].
        split; try xomega. intros. eapply in_app_or in H.
        destruct H.
        * eapply D; eauto.
        * generalize (B pc H). intros. xomega.
  Qed.

  Lemma tr_cmp_regs_ple:
    forall c regs pc1 reg0 pc2 pcs,
      tr_cmp_regs c pc1 reg0 regs pc2 pcs ->
      Ple pc1 pc2 /\ forall pc, In pc pcs -> Ple pc1 pc.
Proof.
    induction regs; intros.
    - inv H. split; try xomega.
      simpl; intros. exfalso; auto.
    - inv H. exploit IHregs; eauto.
      intros [A B]. split; try xomega.
      intros. simpl in H. destruct H.
      + inv H; xomega.
      + generalize (B _ H); intros. xomega.
  Qed.

  Lemma tr_checks_ple:
    forall f c pc i alpha kappa addr args pcs,
      tr_checks f c pc i alpha kappa addr args pcs ->
      forall pc', In pc' pcs -> Ple pc pc'.
Proof.
    intros. inv H. exploit tr_regs_annot_ple; eauto. intros [A B].
    exploit tr_cmp_regs_ple; eauto. intros [C D].
    exploit tr_or_regs_ple; eauto. intros [E F].
    simpl in H0. destruct H0.
    - inv H; xomega.
    - repeat destruct H; try xomega.
      eapply in_app_or in H.
      destruct H.
      + generalize (B _ H). intros; xomega.
      + eapply in_app_or in H.
        destruct H.
        * generalize (D _ H); intros. xomega.
        * eapply in_app_or in H; destruct H.
          { generalize (F _ H); intros. xomega. }
          { inv H9. simpl in H. repeat destruct H as [H | [H | H]]; inv H; xomega. }
  Qed.

  Lemma add_checks_spec':
    forall (f: function) i opt (alpha : annotation) (kappa : memory_chunk)
      (addr : addressing) (args : list reg)
      (s s' : state) (Hnottrivial: is_trivial_annotation prog alpha kappa addr = false)
      (Hnot_singleton: is_singleton (snd alpha) = false),
      add_checks prog ge STK SIZE opt i alpha kappa addr args s = OK s' ->
      Plt (max_reg_function f) (st_nextreg s) ->
      exists pcs : list node,
        tr_checks f (st_code s') (st_nextnode s) i alpha kappa addr args pcs
        /\ forall pc, In pc pcs -> Ple (st_nextnode s) pc.
Proof.
    intros. generalize (add_checks_spec f _ _ _ _ _ _ _ _ Hnottrivial Hnot_singleton H H0).
    intros [pcs A]. exists pcs. split; auto.
    eapply tr_checks_ple; eauto.
  Qed.

  Lemma tr_epilogue_ple:
    forall (f : function) (s : state) (pc : node) (i : instruction) (pcs : list node),
      tr_epilogue f (st_code s) pc i pcs ->
      forall pc' : node, In pc' pcs -> Ple pc pc' .
Proof.
    intros. inv H. simpl in H0.
    destruct H0 as [H0 | [H0 | [H0 | [H0 | [H0 | H0]]]]]; inv H0; try xomega.
  Qed.

  Lemma add_epilogue_spec':
    forall (f : function) (i : instruction) (s s' : state),
      add_epilogue SIZE i s = OK s' ->
      Plt (max_reg_function f) (st_nextreg s) ->
      exists pcs : list node,
        tr_epilogue f (st_code s') (st_nextnode s) i pcs
        /\ forall pc, In pc pcs -> Ple (st_nextnode s) pc.
Proof.
    intros. generalize (add_epilogue_spec f _ _ _ H H0).
    intros [pcs A]. exists pcs. split; auto.
    eapply tr_epilogue_ple; eauto.
  Qed.
    
  Lemma transf_instr_spec':
    forall (f: function) (st : state) (pc : positive) opt (i : instruction) (s : state),
      transf_instr prog ge STK SIZE opt (OK st) pc i = OK s ->
      (st_code st) ! pc = Some i ->
      Plt (max_reg_function f) (st_nextreg st) ->
      exists pcs, tr_instrs f pc i (st_code s) pcs /\ forall pc', In pc' pcs -> pc' = pc \/ Ple (st_nextnode st) pc'.
Proof.
    intros. destruct i; try (simpl in H; inv H; exists (pc::nil); split; try (econstructor; eauto); simpl in H; inv H; eauto; exfalso; assumption; fail).
    - monadInv H. unfold update_instr in EQ2; destruct (plt pc (st_nextnode st)); try monadInv EQ2.
      case_eq (is_trivial_annotation prog a m a0); intros; rename H into HOH.
      { unfold add_checks in EQ4. rewrite HOH in EQ4. simpl in EQ4. inv EQ4.
        econstructor; split.
        - econstructor.
          + eapply check_annotations_range_spec; eauto.
          + eapply check_annotations_range_spec'; eauto.
          + destruct x0; auto.
          + simpl. rewrite PTree.gso; eauto. rewrite PTree.gss; eauto.
          + simpl. rewrite HOH. rewrite PTree.gss; eauto.
          + simpl; intros. eapply check_annotations_depth_spec; eauto.
          + simpl; intros. eapply check_annotations_depth_spec'; eauto.
          + eapply check_annotations_divides_spec; eauto.
        - simpl. intros. rewrite HOH in H. destruct H as [H | [H | H]]; inv H; eauto.
          right; xomega. }
      case_eq (is_singleton (snd a)); intros; rename H into HOH'.
      { generalize (add_checks'_spec HOH HOH' EQ4 H1). simpl.
        intros (pcs & Hpcs & LE).
        eexists. split.
        - econstructor.
          eauto using check_annotations_range_spec.
          eauto using check_annotations_range_spec'.
          destruct x0; assumption.
          generalize (add_checks_unchanged _ _ _ _ _ _ _ _ EQ4 pc p). simpl; intros.
          rewrite <- H. apply PTree.gss.
          rewrite HOH, HOH'. eassumption.
          eauto using check_annotations_depth_spec.
          eauto using check_annotations_depth_spec'.
          eauto using check_annotations_divides_spec.
        - intros pc'. rewrite HOH. intros [ H | H ]. left; auto. right.
          apply LE, H.
      }
      exploit add_checks_spec'; eauto; simpl; try discriminate. intros [pcs [A B]].
      eexists. split.
      + econstructor; eauto.
        * eapply check_annotations_range_spec; eauto.
        * eapply check_annotations_range_spec'; eauto.
        * destruct x0; auto.
        * generalize (add_checks_unchanged _ _ _ _ _ _ _ _ EQ4 pc p). simpl; intros.
          rewrite <- H. rewrite PTree.gss; eauto.
        * rewrite HOH; rewrite HOH'; simpl. eauto.
        * intros; eapply check_annotations_depth_spec; eauto.
        * intros; eapply check_annotations_depth_spec'; eauto.
        * eapply check_annotations_divides_spec; eauto.
      + intros. rewrite HOH in H. simpl in H; destruct H.
        * left; auto.
        * right. simpl in B. eapply B; eauto.
    - monadInv H. unfold update_instr in EQ2; destruct (plt pc (st_nextnode st)); try monadInv EQ2.
      case_eq (is_trivial_annotation prog a m a0); intros; rename H into HOH.
      { unfold add_checks in EQ4. rewrite HOH in EQ4. simpl in EQ4. inv EQ4.
        econstructor; split.
        - econstructor.
          + eapply check_annotations_range_spec; eauto.
          + eapply check_annotations_range_spec'; eauto.
          + destruct x0; auto.
          + simpl. rewrite PTree.gso; eauto. rewrite PTree.gss; eauto.
          + simpl. rewrite HOH. rewrite PTree.gss; eauto.
          + simpl; intros. eapply check_annotations_depth_spec; eauto.
          + simpl; intros. eapply check_annotations_depth_spec'; eauto.
          + eapply check_annotations_divides_spec; eauto.
        - simpl. intros. rewrite HOH in H. destruct H as [H | [H | H]]; inv H; eauto.
          right; xomega. }
      destruct (is_singleton (snd a)) eqn: HOH'.
      { generalize (add_checks'_spec HOH HOH' EQ4 H1). simpl.
        intros (pcs & Hpcs & LE).
        eexists. split.
        - econstructor.
          eauto using check_annotations_range_spec.
          eauto using check_annotations_range_spec'.
          destruct x0; assumption.
          generalize (add_checks_unchanged _ _ _ _ _ _ _ _ EQ4 pc p). simpl; intros.
          rewrite <- H. apply PTree.gss.
          rewrite HOH, HOH'. eassumption.
          eauto using check_annotations_depth_spec.
          eauto using check_annotations_depth_spec'.
          eauto using check_annotations_divides_spec.
        - intros pc'. rewrite HOH. intros [ H | H ]. left; auto. right.
          apply LE, H.
      }
      exploit add_checks_spec'; eauto; simpl; try discriminate. intros [pcs [A B]].
      eexists. split.
      + econstructor; eauto.
        * eapply check_annotations_range_spec; eauto.
        * eapply check_annotations_range_spec'; eauto.
        * destruct x0; auto.
        * generalize (add_checks_unchanged _ _ _ _ _ _ _ _ EQ4 pc p). simpl; intros.
          rewrite <- H. rewrite PTree.gss; eauto.
        * rewrite HOH; rewrite HOH'; simpl. eauto.
        * intros; eapply check_annotations_depth_spec; eauto.
        * intros; eapply check_annotations_depth_spec'; eauto.
        * eapply check_annotations_divides_spec; eauto.
      + intros. rewrite HOH in H. simpl in H; destruct H.
        * left; auto.
        * right. simpl in B. eapply B; eauto.
    - monadInv H. unfold update_instr in EQ. destruct (plt pc (st_nextnode st)); try monadInv EQ.
      exploit add_epilogue_spec'; eauto. intros [pcs [A B]].
      exists (pc::pcs). split.
      + econstructor; eauto.
        generalize (proj1 (add_epilogue_unchanged_and_sincr _ _ _ EQ0) pc p). simpl; intros.
        rewrite <- H. rewrite PTree.gss; eauto.
      + intros. simpl in H; destruct H.
        * left; auto.
        * right. simpl in B. eapply B; eauto.
    - monadInv H. eexists. split; auto.
      + econstructor; eauto; eapply check_globals_of_builtin_args_spec; eauto.
      + intros; simpl in H; destruct H; auto.
        inv H.
    - monadInv H. unfold update_instr in EQ. destruct (plt pc (st_nextnode st)); try monadInv EQ.
      exploit add_epilogue_spec'; eauto. intros [pcs [A B]].
      exists (pc::pcs). split.
      + econstructor; eauto.
        generalize (proj1 (add_epilogue_unchanged_and_sincr _ _ _ EQ0) pc p). simpl; intros.
        rewrite <- H. rewrite PTree.gss; eauto.
      + intros. simpl in H; destruct H.
        * left; auto.
        * right. simpl in B. eapply B; eauto.
  Grab Existential Variables. eapply nil. eapply nil.
  Qed.

  Lemma transf_instr_error:
    forall e pc opt i,
      exists e', transf_instr prog ge STK SIZE opt (Error e) pc i = Error e'.
Proof.
    intros; unfold transf_instr; simpl; destruct i; eauto.
    - destruct (check_annotations_depth STK SIZE (snd a)); destruct (check_annotations_divides m (snd a)); destruct (check_annotations_range (snd a)); simpl; eauto.
    - destruct (check_annotations_depth STK SIZE (snd a)); destruct (check_annotations_divides m (snd a)); destruct (check_annotations_range (snd a)); simpl; eauto.
    - destruct (check_globals_of_builtin_args STK l); destruct (check_globals_of_builtin_args SIZE l); simpl; eauto.
  Qed.
  
  Lemma fold_transf_instr_error:
    forall l e opt,
      exists e', fold_left (fun acc pc_i => transf_instr prog ge STK SIZE opt acc (fst pc_i) (snd pc_i)) l (Error e) = Error e'.
Proof.
    induction l; intros.
    - simpl; eauto.
    - simpl. destruct (transf_instr_error e (fst a) opt (snd a)).
      rewrite H. eapply IHl; eauto.
  Qed.

  Lemma transf_function_spec_aux:
    forall f l s s' c opt
      (Hcomplete: forall pc i, In (pc, i) l -> c!pc = Some i)
      (Hcorrect: forall pc i, c!pc = Some i /\ ~ In (pc, i) l -> exists pcs, tr_instrs f pc i (st_code s) pcs)
      (Hkeys_norepet: list_norepet (map fst l))
      (Hkeys_plt: forall pc i, In (pc, i) l -> Plt pc (st_nextnode s))
      (Hkeys: forall pc i, In (pc, i) l -> forall pc' i', c!pc' = Some i' -> ~ In (pc', i') l -> forall pcs, tr_instrs f pc' i' (st_code s) pcs -> forall pc'', In pc'' pcs -> pc <> pc'')
      (Hinit: forall pc i, In (pc, i) l -> (st_code s)!pc = Some i)
      (HPlt: Plt (max_reg_function f) (st_nextreg s)),
      fold_left (fun acc pc_i => transf_instr prog ge STK SIZE opt acc (fst pc_i) (snd pc_i)) l (OK s) = OK s' ->
      (forall pc i, c!pc = Some i -> exists pcs, tr_instrs f pc i (st_code s') pcs).
Proof.
    induction l; intros.
    - simpl in H. inv H.
      eapply Hcorrect; split; auto.
    - destruct a as [pc' i']. simpl in H.
      case_eq (transf_instr prog ge STK SIZE opt (OK s) pc' i'); intros.
      + rewrite H1 in H.
        eapply IHl with (s := s0); eauto.
        * intros. eapply Hcomplete.
          simpl; right; auto.
        * intros pc0 i0. intros [A B].
          { destruct (Util.eq_dec (pc', i') (pc0, i0)) as [ e | n ].
            - inv e. exploit (Hinit pc0 i0).
              + left; auto.
              + intros. eapply (transf_instr_spec f _ _ _ _ _ H1 H2 HPlt).
            - exploit Hcorrect.
              + split; eauto. simpl.
                unfold not. intros. destruct H2; auto.
              + intros. destruct H2 as [pcs H2].
                exists pcs. eapply tr_instrs_unchanged'; eauto.
                * eapply (Hkeys pc' i' (in_eq _ _) _ _ A).
                  { unfold not; intros. destruct H3; try congruence. }
                  { exact H2. }
                * eapply Hinit; eapply in_eq. }
        * simpl in Hkeys_norepet. inv Hkeys_norepet; eauto.
        * intros. generalize (Hkeys_plt pc0 i0 (in_cons _ _ _ H2)).
          intros. exploit transf_instr_sincr; eauto. intros A; inv A.
          xomega.
        * intros.
          { destruct (Util.eq_dec (pc', i') (pc'0, i'0)) as [ e | n ].
            - inv e.
              exploit transf_instr_spec'; eauto.
              + eapply Hinit; eauto. eapply in_eq.
              + intros [pcs0 [A B]].
                generalize (tr_instrs_determ f _ _ _ _ _ H5 A). intros C. subst pcs0.
                generalize (B pc'' H6). intros [C | C].
                * subst pc'0.
                  simpl in Hkeys_norepet. inv Hkeys_norepet.
                  generalize (in_map fst _ _ H2). simpl; intros.
                  unfold not. intros. rewrite H8 in H7. congruence.
                * generalize (Hkeys_plt pc0 i0 (in_cons _ _ _ H2)). intros. xomega.
            - eapply Hkeys; eauto.
              + eapply in_cons. exact H2.
              + unfold not; intros. simpl in H7. destruct H7; try congruence.
              + assert (~ In (pc'0, i'0) ((pc', i')::l)).
                * unfold not; intros. simpl in H7; destruct H7; congruence.
                * generalize (Hcorrect pc'0 i'0 (conj H3 H7)). intros [pcs' A].
                  assert (tr_instrs f pc'0 i'0 (st_code s0) pcs').
                  eapply tr_instrs_unchanged'; eauto.
                  { eapply (Hkeys pc' i' (in_eq _ _) _ _ H3 H7 _ A). }
                  { eapply Hinit; eapply in_eq. }
                  { generalize (tr_instrs_determ f _ _ _ _ _ H5 H8). intros. subst pcs'. assumption. } }
        * intros. erewrite <- transf_instr_unchanged; eauto.
          { eapply Hinit; eapply in_cons; eauto. }
          { split.
            - eapply Hkeys_plt; eapply in_cons; eauto.
            - simpl in Hkeys_norepet. inv Hkeys_norepet.
              generalize (in_map fst _ _ H2). simpl; intros.
              unfold not; intros. rewrite H4 in H5. congruence. }
        * generalize (transf_instr_sincr _ _ _ _ _ H1). intros HA; inv HA; xomega.
      + rewrite H1 in H. exploit fold_transf_instr_error; eauto. intros [e' GG].
        rewrite GG in H. monadInv H.
  Qed.
  
  Theorem transf_function_spec:
    forall f tf pc i,
      transf_function prog ge STK SIZE f = OK tf ->
      (fn_code f)!pc = Some i ->
      exists pcs, tr_instrs f pc i (fn_code tf) pcs.
Proof.
    intros. monadInv H. simpl.
    rewrite PTree.fold_spec in EQ.
    eapply transf_function_spec_aux; eauto.
    - eapply PTree.elements_complete.
    - intros. destruct H as [A B].
      generalize (PTree.elements_correct _ _ A). intros. congruence.
    - eapply PTree.elements_keys_norepet.
    - intros. simpl. generalize (PTree.elements_complete _ _ _ H). intros.
      generalize (max_pc_function_sound _ _ _ H1). intros. xomega.
    - intros. generalize (PTree.elements_correct _ _ H1). intros.
      congruence.
    - simpl. intros. eapply PTree.elements_complete in H.
      generalize (max_pc_function_sound f pc0 i0 H). intros.
      rewrite PTree.gso; try xomega.
      rewrite PTree.gso; try xomega.
      rewrite PTree.gso; try xomega.
      rewrite PTree.gso; try xomega.
      rewrite PTree.gso; try xomega.
      rewrite PTree.gso; try xomega.
      rewrite PTree.gso; try xomega.
      assumption.
    - unfold init_state; simpl. xomega.
  Qed.

  Lemma init_regs_spec:
    forall rl vl r,
      ~ In r rl ->
      (init_regs vl rl)#r = Vundef.
Proof.
    induction rl; intros.
    - simpl; rewrite PMap.gi; reflexivity.
    - simpl. destruct vl.
      + rewrite PMap.gi; reflexivity.
      + rewrite PMap.gso; eauto.
        * unfold not in H; simpl in H.
          eapply IHrl; eauto.
        * unfold not in H; simpl in H.
          unfold not; eauto.
  Qed.

  Lemma list_forall2_list_disjoint:
    forall regs1 regs2 trs' trs'' vals,
      list_forall2 (fun (reg : positive) (val : val) => trs' # reg = val) regs2 vals ->
      list_disjoint regs1 regs2 ->
      (forall reg : reg, ~ In reg regs1 -> trs' # reg = trs'' # reg) ->
      list_forall2 (fun (reg : positive) (val : val) => trs'' # reg = val) regs2 vals.
Proof.
    induction regs2; intros.
    - inv H; econstructor.
    - inv H; econstructor.
      + rewrite H1; eauto.
        unfold not; intros. generalize (H0 a a H (in_eq _ _)).
        auto.
      + eapply IHregs2; eauto. unfold list_disjoint; intros.
        eapply (H0 x y H (in_cons _ _ _ H2)).
  Qed.

  Lemma list_disjoint_app:
    forall A (l1 l2 l3: list A),
      list_disjoint l1 l3 ->
      list_disjoint l2 l3 ->
      list_disjoint (l1 ++ l2) l3.
Proof.
    intros. induction l1; eauto.
    rewrite <- app_comm_cons.
    eapply list_disjoint_cons_l.
    - eapply IHl1. unfold list_disjoint in *.
      intros. eapply H; eauto.
      eapply in_cons; eauto.
    - intro. unfold list_disjoint in H. generalize (H a a (in_eq _ _) H1).
      congruence.
  Qed.

  Fixpoint pop (n: nat) (stk: list val): option val :=
    match n with
    | O => match stk with
          | sp::_ => Some sp
          | nil => None
          end
    | S n => match stk with
            | _::stk => pop n stk
            | nil => None
            end
    end.
  
  Lemma pop_is_in:
    forall n sps sp,
      pop n sps = Some sp ->
      In sp sps.
Proof.
    induction n; intros.
    - simpl in H. destruct sps; inv H.
      eapply in_eq.
    - simpl in H. destruct sps; inv H.
      eapply in_cons; eauto.
  Qed.
  
  Lemma tr_local_regs_regs_plt:
    forall kappa f c n pc1 ofs depth regs regs' pc2 pcs,
      tr_local_regs kappa f c pc1 ofs depth n regs regs' pc2 pcs ->
      forall r, In r regs -> Plt (max_reg_function f) r.
Proof.
    induction n; intros.
    - inv H; inv H0.
    - inv H; simpl in H0.
      { destruct H0.
        + inv H; assumption.
        + eapply IHn; eauto. }
      { eapply IHn; eauto. }
  Qed.

  Lemma tr_local_regs_regs'_plt:
    forall kappa f c n pc1 ofs depth regs regs' pc2 pcs,
      tr_local_regs kappa f c pc1 ofs depth n regs regs' pc2 pcs ->
      forall r, In r regs' -> Plt (max_reg_function f) r.
Proof.
    induction n; intros.
    - inv H; inv H0.
    - inv H; simpl in H0.
      { destruct H0.
        + inv H. xomega.
        + destruct H.
          * inv H; xomega.
          * eapply IHn; eauto. }
      { eapply IHn; eauto. }
  Qed.
  
  Lemma tr_global_regs_regs_plt:
    forall kappa f c id n pc1 ofs regs pc2 pcs,
      tr_global_regs kappa f c pc1 id ofs n regs pc2 pcs ->
      forall r, In r regs -> Plt (max_reg_function f) r.
Proof.
    induction n; intros.
    - inv H; inv H0.
    - inv H. simpl in H0.
      { destruct H0.
        + inv H; assumption.
        + eapply IHn; eauto. }
      { eapply IHn; eauto. }
  Qed.
  
  Lemma tr_regs_annot_regs_plt:
    forall kappa f c alpha pc1 regs regs' pc2 pcs,
      tr_regs_annot kappa f c pc1 alpha regs regs' pc2 pcs ->
      forall r, In r regs -> Plt (max_reg_function f) r.
Proof.
    induction alpha; intros.
    - inv H; inv H0.
    - inv H.
      + eapply in_app_or in H0. destruct H0.
        * eapply tr_local_regs_regs_plt; eauto.
        * eapply IHalpha; eauto.
      + eapply in_app_or in H0. destruct H0.
        * eapply tr_global_regs_regs_plt; eauto.
        * eapply IHalpha; eauto.
  Qed.

  Lemma tr_regs_annot_regs'_plt:
    forall kappa f c alpha pc1 regs regs' pc2 pcs,
      tr_regs_annot kappa f c pc1 alpha regs regs' pc2 pcs ->
      forall r, In r regs' -> Plt (max_reg_function f) r.
Proof.
    induction alpha; intros.
    - inv H; inv H0.
    - inv H.
      + eapply in_app_or in H0. destruct H0.
        * eapply tr_local_regs_regs'_plt; eauto.
        * eapply IHalpha; eauto.
      + eapply IHalpha; eauto.
  Qed.
  
  Lemma tr_local_regs_regs_norepet:
    forall kappa f c n pc1 ofs depth regs regs' pc2 pcs,
      tr_local_regs kappa f c pc1 ofs depth n regs regs' pc2 pcs ->
      list_norepet regs.
Proof.
    induction n; intros.
    - inv H; econstructor.
    - inv H.
      + econstructor; eauto.
        eapply not_in_app in H6; destruct H6; auto.
      + eapply IHn; eauto.
  Qed.

  Lemma tr_local_regs_regs'_norepet:
    forall kappa f c n pc1 ofs depth regs regs' pc2 pcs,
      tr_local_regs kappa f c pc1 ofs depth n regs regs' pc2 pcs ->
      list_norepet regs'.
Proof.
    induction n; intros.
    - inv H; econstructor.
    - inv H.
      { econstructor; eauto.
        + eapply not_in_cons; split.
          * xomega.
          * eapply not_in_app in H7; destruct H7; auto.
        + econstructor; eauto.
          eapply not_in_app in H8; destruct H8; auto. }
      { eapply IHn; eauto. }
  Qed.
  
  Lemma tr_global_regs_regs_norepet:
    forall kappa f c id n pc1 ofs regs pc2 pcs,
      tr_global_regs kappa f c pc1 id ofs n regs pc2 pcs ->
      list_norepet regs.
Proof.
    induction n; intros.
    - inv H; econstructor.
    - inv H.
      + econstructor; eauto.
      + eapply IHn; eauto.
  Qed.

  Lemma tr_regs_annot_regs_norepet:
    forall kappa f c alpha pc1 regs regs' pc2 pcs,
      tr_regs_annot kappa f c pc1 alpha regs regs' pc2 pcs ->
      list_norepet regs.
Proof.
    induction alpha; intros.
    - inv H; econstructor.
    - inv H.
      + eapply list_norepet_append; eauto.
        eapply tr_local_regs_regs_norepet; eauto.
      + eapply list_norepet_append; eauto.
        eapply tr_global_regs_regs_norepet; eauto.
  Qed.

  Lemma tr_regs_annot_regs'_norepet:
    forall kappa f c alpha pc1 regs regs' pc2 pcs,
      tr_regs_annot kappa f c pc1 alpha regs regs' pc2 pcs ->
      list_norepet regs'.
Proof.
    induction alpha; intros.
    - inv H; econstructor.
    - inv H.
      + eapply list_norepet_append; eauto.
        eapply tr_local_regs_regs'_norepet; eauto.
      + eapply IHalpha; eauto.
  Qed.
  
  Lemma list_forall2_not_in:
    forall B trs regs vl a (v: B),
      list_forall2 (fun reg val => trs # reg = val) regs vl ->
      ~ In a regs ->
      list_forall2 (fun reg val => (trs # a <- v) # reg = val) regs vl.
Proof.
    induction regs; intros.
    - inv H; econstructor.
    - rewrite not_in_cons in H0; destruct H0.
      inv H; econstructor; eauto.
      rewrite PMap.gso; eauto.
  Qed.

  Lemma list_forall2_not_in':
    forall B trs regs (vals: list B) f reg0 (v: B),
      list_forall2 (fun reg val => trs # reg = f val) regs vals ->
      ~ In reg0 regs ->
      list_forall2 (fun reg val => (trs # reg0 <- v) # reg = f val) regs vals.
Proof.
    induction regs; intros.
    - inv H; econstructor.
    - rewrite not_in_cons in H0; destruct H0.
      inv H; econstructor; eauto.
      rewrite PMap.gso; eauto.
  Qed.

  Lemma load_valid_pointer:
    forall m kappa b ofs v,
      Mem.load kappa m b ofs = Some v ->
      Mem.valid_pointer m b ofs = true.
Proof.
    intros; generalize (Mem.load_valid_access _ _ _ _ _ H); intros.
    generalize (Mem.valid_access_implies _ _ _ _ _ _ H0 (Memtype.perm_any_N Readable)); intros.
    rewrite Mem.valid_pointer_valid_access.
    unfold Mem.valid_access in *. split.
    red; intros. apply H1. destruct kappa; unfold size_chunk in *; Psatz.lia.
    exists ofs. simpl. omega.
  Qed.

  Lemma store_valid_pointer:
    forall m kappa b ofs v m',
      Mem.store kappa m b ofs v = Some m' ->
      Mem.valid_pointer m b ofs = true.
Proof.
    intros; generalize (Mem.store_valid_access_3 _ _ _ _ _ _ H); intros.
    generalize (Mem.valid_access_implies _ _ _ _ _ _ H0 (Memtype.perm_any_N Writable)); intros.
    rewrite Mem.valid_pointer_valid_access.
    unfold Mem.valid_access in *. split.
    red; intros. apply H1. destruct kappa; unfold size_chunk in *; Psatz.lia.
    exists ofs. simpl. omega.
  Qed.
  
  Lemma regs_are_bools:
    forall m kappa a v trs regs vals,
      Mem.loadv kappa m a = Some v ->
      list_forall2 (fun reg val => trs # reg = Val.of_optbool (eval_condition (Ccompu Ceq) (a::val::nil) m)) regs vals ->
      (forall v, In v vals -> exists b ofs, v = Vptr b ofs) ->
      (forall b ofs, In (Vptr b ofs) vals -> (eval_condition (Ccompu Ceq) (a :: Vptr b ofs :: nil) m = Some true \/ eval_condition (Ccompu Ceq) (a :: Vptr b ofs :: nil) m = Some false)) ->
      forall r, In r regs -> trs#r = Vtrue \/ trs#r = Vfalse.
Proof.
    induction regs; intros.
    - inv H0. inv H3.
    - destruct a; simpl in H; inv H.
      generalize (load_valid_pointer _ _ _ _ _ H5); intros.
      inv H0. simpl in H3. destruct H3.
      + inv H0. rewrite H7.
        generalize (H1 b1 (in_eq _ _)). intros [b' [ofs HA]]. inv HA.
        generalize (H2 _ _ (in_eq _ _)). intros [HA | HA]; rewrite HA; auto.
      + eapply IHregs; eauto.
        * intros. eapply H1; eauto. eapply in_cons; eauto.
        * intros. eapply H2; eauto. eapply in_cons; eauto.
  Qed.

  Lemma regs_are_bools':
    forall m kappa a v m' trs regs vals,
      Mem.storev kappa m a v = Some m' ->
      list_forall2 (fun reg val => trs # reg = Val.of_optbool (eval_condition (Ccompu Ceq) (a::val::nil) m)) regs vals ->
      (forall v, In v vals -> exists b ofs, v = Vptr b ofs) ->
      (forall b ofs, In (Vptr b ofs) vals -> (eval_condition (Ccompu Ceq) (a :: Vptr b ofs :: nil) m = Some true \/ eval_condition (Ccompu Ceq) (a :: Vptr b ofs :: nil) m = Some false)) ->
      forall r, In r regs -> trs#r = Vtrue \/ trs#r = Vfalse.
Proof.
    induction regs; intros.
    - inv H0. inv H3.
    - destruct a; simpl in H; inv H.
      generalize (store_valid_pointer _ _ _ _ _ _ H5); intros.
      inv H0. simpl in H3. destruct H3.
      + inv H0. rewrite H7.
        generalize (H1 b1 (in_eq _ _)). intros [b' [ofs HA]]. inv HA.
        generalize (H2 _ _ (in_eq _ _)). intros [HA | HA]; rewrite HA; auto.
      + eapply IHregs; eauto.
        * intros. eapply H1; eauto. eapply in_cons; eauto.
        * intros. eapply H2; eauto. eapply in_cons; eauto.
  Qed.
  
  Lemma true_in_regs:
    forall m kappa a v trs regs vals,
      Mem.loadv kappa m a = Some v ->
      list_forall2 (fun reg val => trs # reg = Val.of_optbool (eval_condition (Ccompu Ceq) (a::val::nil) m)) regs vals ->
      (forall v, In v vals -> exists b ofs, v = Vptr b ofs) ->
      (forall b ofs, In (Vptr b ofs) vals -> (eval_condition (Ccompu Ceq) (a :: Vptr b ofs :: nil) m = Some true \/ eval_condition (Ccompu Ceq) (a :: Vptr b ofs :: nil) m = Some false)) ->
      In a vals ->
      exists r, In r regs /\ trs#r = Vtrue.
Proof.
    induction regs; intros.
    - inv H0; inv H3.
    - destruct a; simpl in H; inv H.
      generalize (load_valid_pointer _ _ _ _ _ H5); intros.
      inv H0. simpl in H3. destruct H3.
      + inv H0. exists a0; split.
        * eapply in_eq.
        * rewrite H7. simpl. rewrite H. simpl.
          destruct (eq_block b b); try congruence.
          rewrite Int.eq_true. reflexivity.
      + exploit IHregs; eauto.
        * intros. eapply H1; eapply in_cons; eauto.
        * intros. eapply H2; eapply in_cons; eauto.
        * intros [r [A B]]. exists r; split; auto. eapply in_cons; eauto.
  Qed.

  Lemma true_in_regs':
    forall m kappa a v m' trs regs vals,
      Mem.storev kappa m a v = Some m' ->
      list_forall2 (fun reg val => trs # reg = Val.of_optbool (eval_condition (Ccompu Ceq) (a::val::nil) m)) regs vals ->
      (forall v, In v vals -> exists b ofs, v = Vptr b ofs) ->
      (forall b ofs, In (Vptr b ofs) vals -> (eval_condition (Ccompu Ceq) (a :: Vptr b ofs :: nil) m = Some true \/ eval_condition (Ccompu Ceq) (a :: Vptr b ofs :: nil) m = Some false)) ->
      In a vals ->
      exists r, In r regs /\ trs#r = Vtrue.
Proof.
    induction regs; intros.
    - inv H0; inv H3.
    - destruct a; simpl in H; inv H.
      generalize (store_valid_pointer _ _ _ _ _ _ H5); intros.
      inv H0. simpl in H3. destruct H3.
      + inv H0. exists a0; split.
        * eapply in_eq.
        * rewrite H7. simpl. rewrite H. simpl.
          destruct (eq_block b b); try congruence.
          rewrite Int.eq_true. reflexivity.
      + exploit IHregs; eauto.
        * intros. eapply H1; eapply in_cons; eauto.
        * intros. eapply H2; eapply in_cons; eauto.
        * intros [r [A B]]. exists r; split; auto. eapply in_cons; eauto.
  Qed.

  Lemma pop_inject:
    forall j sps tsps sp,
      list_forall2 (fun sp tsp => exists bsp btsp, sp = Vptr bsp Int.zero /\ tsp = Vptr btsp Int.zero /\ j bsp = Some (btsp, 0)) sps tsps ->
      forall n, pop n sps = Some sp ->
      exists tsp, pop n tsps = Some tsp /\ exists bsp btsp, sp = Vptr bsp Int.zero /\ tsp = Vptr btsp Int.zero /\ j bsp = Some (btsp, 0).
Proof.
    induction 1; intros.
    - destruct n; simpl in H; inv H.
    - destruct n.
      + simpl in H1; simpl. eexists; split; eauto.
        inv H1; eauto.
      + simpl in H1. simpl. eapply IHlist_forall2; eauto.
  Qed.

  Lemma max_reg_function_use':
    forall f pc i,
      (fn_code f)!pc = Some i ->
      (forall r, In r (instr_uses i) -> Ple r (max_reg_function f)).
Proof.
intros. eapply max_reg_function_use; eauto. Qed.

  Lemma regs_agree_inject_list:
    forall j n rs trs args,
      (forall r, In r args -> Ple r n) ->
      regs_agree j n rs trs ->
      Val.inject_list j rs##args trs##args.
Proof.
    induction args; intros.
    - econstructor.
    - simpl. econstructor; eauto.
      + destruct (H0 a).
        * destruct H1; auto.
        * destruct H1. generalize (H a (in_eq _ _)). xomega.
      + eapply IHargs; eauto.
        intros; eapply H; eapply in_cons; eauto.
  Qed.

  Lemma inject_in_list:
    forall j vals tvals a ta,
      list_forall2 (Val.inject j) vals tvals ->
      (forall v, In v vals -> exists b ofs, v = Vptr b ofs) ->
      Val.inject j a ta ->
      In a vals ->
      In ta tvals.
Proof.
    induction 1; intros.
    - inv H1.
    - destruct H3.
      + subst a1. destruct (H1 a (in_eq _ _)) as [b [ofs A]].
        subst a. inv H2. inv H. rewrite H4 in H5; inv H5.
        eapply in_eq.
      + right. eapply IHlist_forall2; eauto.
        intros; eapply H1; simpl; eauto.
  Qed.

  Lemma regs_rewrite_list:
    forall A (rs rs': Regmap.t A) regs1 regs2,
      (forall r, ~ In r regs1 -> rs#r = rs'#r) ->
      list_disjoint regs1 regs2 ->
      rs##regs2 = rs'##regs2.
Proof.
    induction regs2; intros.
    - simpl; auto.
    - simpl. rewrite H; try rewrite IHregs2; eauto.
      + unfold list_disjoint in *; intros.
        eapply H0; eauto. eapply in_cons; eauto.
      + unfold list_disjoint in *. intro.
        eapply H0; eauto. eapply in_eq.
  Qed.

  Lemma regs_rewrite_one:
    forall A (rs rs': Regmap.t A) regs reg,
      (forall r, r <> reg -> rs#r = rs'#r) ->
      (forall r, In r regs -> r <> reg) ->
      rs##regs = rs'##regs.
Proof.
    intros; eapply regs_rewrite_list; eauto.
    - instantiate (1 := reg::nil). intros.
      eapply not_in_cons in H1. destruct H1; eapply H; auto.
    - unfold list_disjoint; intros.
      destruct H1; auto. subst x. eapply H0 in H2; auto.
  Qed.

  Lemma regs_rewrite_one':
    forall A (rs: Regmap.t A) regs reg a,
      (forall r, In r regs -> r <> reg) ->
      (rs#reg <- a)##regs = rs##regs.
Proof.
    intros. eapply regs_rewrite_one; eauto.
    intros. rewrite PMap.gso; eauto.
  Qed.

End SPEC.

Section ADDR.

  Inductive addr_of_local kappa (sp: val): Int.int -> nat -> list val -> Prop :=
  | addr_of_local_S:
      forall ofs n vals,
        addr_of_local kappa sp (Int.add ofs Int.one) n vals ->
        addr_of_local kappa sp ofs (S n) (if Zdivides_dec (align_chunk kappa) (Int.unsigned ofs) then (Val.add sp (Vint ofs))::vals else vals)
  | addr_of_local_O:
      forall ofs,
        addr_of_local kappa sp ofs O nil.

  Inductive addr_of_global kappa (b: block): Int.int -> nat -> list val -> Prop :=
  | addr_of_global_S:
      forall ofs n vals,
        addr_of_global kappa b (Int.add ofs Int.one) n vals ->
        addr_of_global kappa b ofs (S n) (if Zdivides_dec (align_chunk kappa) (Int.unsigned ofs) then (Vptr b ofs)::vals else vals)
  | addr_of_global_O:
      forall ofs,
        addr_of_global kappa b ofs O nil.

  Inductive addr_of_annotations kappa (ge: Genv.t fundef unit) (sps: list val): list ablock -> list val -> Prop :=
  | addr_of_annotations_local:
      forall depth varname sp base bound alpha size vals vals',
        Int.unsigned (Int.sub bound base) + 1 = Zpos size ->
        pop depth sps = Some sp ->
        addr_of_local kappa sp base (Pos.to_nat size) vals ->
        addr_of_annotations kappa ge sps alpha vals' ->
        addr_of_annotations kappa ge sps ((ABlocal depth varname (base, bound))::alpha) (vals ++ vals')
  | addr_of_annotations_global:
      forall id b base bound alpha size vals vals',
        Genv.find_symbol ge b = Some id ->
        Int.unsigned (Int.sub bound base) + 1 = Zpos size ->
        addr_of_global kappa id base (Pos.to_nat size) vals ->
        addr_of_annotations kappa ge sps alpha vals' ->
        addr_of_annotations kappa ge sps ((ABglobal b (base, bound))::alpha) (vals ++ vals')
  | addr_of_annotations_nil:
      addr_of_annotations kappa ge sps nil nil.
  
  Lemma addr_of_local_are_ptrs:
    forall kappa sp n ofs vals,
      addr_of_local kappa (Vptr sp Int.zero) ofs n vals ->
      forall v, In v vals -> exists b ofs, v = Vptr b ofs.
Proof.
    induction n; intros.
    - inv H; inv H0.
    - inv H. destruct (Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)).
      { simpl in H0; destruct H0.
        + inv H; eauto.
        + eapply IHn; eauto. }
      { eapply IHn; eauto. }
  Qed.

  Lemma addr_of_global_are_ptrs:
    forall kappa b n ofs vals,
      addr_of_global kappa b ofs n vals ->
      forall v, In v vals -> exists b ofs, v = Vptr b ofs.
Proof.
    induction n; intros.
    - inv H; inv H0.
    - inv H. destruct (Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)).
      { simpl in H0; destruct H0.
        + inv H; eauto.
        + eapply IHn; eauto. }
      { eapply IHn; eauto. }
  Qed.

  Lemma addr_of_annotations_are_ptrs:
    forall kappa ge sps alpha vals,
      addr_of_annotations kappa ge sps alpha vals ->
      (forall sp, In sp sps -> exists b, sp = Vptr b Int.zero) ->
      forall v, In v vals -> exists b ofs, v = Vptr b ofs.
Proof.
    induction alpha; intros.
    - inv H; inv H1.
    - inv H.
      + eapply pop_is_in in H5. eapply H0 in H5.
        destruct H5 as [b A]. inv A.
        eapply in_app_or in H1. destruct H1.
        * eapply addr_of_local_are_ptrs; eauto.
        * eapply IHalpha; eauto.
      + eapply in_app_or in H1. destruct H1.
        * eapply addr_of_global_are_ptrs; eauto.
        * eapply IHalpha; eauto.
  Qed.
  
  Lemma addr_of_local_translated:
    forall kappa j sp tsp n ofs vals,
      j sp = Some (tsp, 0) ->
      addr_of_local kappa (Vptr sp Int.zero) ofs n vals ->
      exists tvals, addr_of_local kappa (Vptr tsp Int.zero) ofs n tvals /\ list_forall2 (Val.inject j) vals tvals.
Proof.
    induction 2; intros.
    - destruct IHaddr_of_local as [tvals [A B]].
      exists (if (Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)) then (Val.add (Vptr tsp Int.zero) (Vint ofs))::tvals else tvals); split.
      + econstructor; eauto.
      + destruct (Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)); eauto.
        econstructor; eauto. simpl. econstructor.
        * inv H; eauto.
        * rewrite Int.add_zero_l. rewrite Int.add_zero. reflexivity.
    - exists nil; split; eauto.
      + econstructor.
      + econstructor.
  Qed.

  Lemma addr_of_global_translated:
    forall kappa j b n ofs vals,
      j b = Some (b, 0) ->
      addr_of_global kappa b ofs n vals ->
      exists tvals, addr_of_global kappa b ofs n tvals /\ list_forall2 (Val.inject j) vals tvals.
Proof.
    induction 2; intros.
    - destruct IHaddr_of_global as [tvals [A B]].
      exists (if (Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)) then (Vptr b ofs)::tvals else tvals); split.
      + econstructor; eauto.
      + destruct (Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)); eauto.
        econstructor; eauto. econstructor; eauto.
        rewrite Int.add_zero; eauto.
    - exists nil; split; econstructor; eauto.
  Qed.

    Lemma int_plus_minus:
    forall i1 i2,
      i2 = Int.add i1 (Int.sub i2 i1).
Proof.
    intros.
    rewrite Int.sub_add_opp.
    rewrite Int.add_commut.
    rewrite Int.add_assoc.
    replace i2 with (Int.add i2 Int.zero) at 1.
    - f_equal; eauto.
      rewrite Int.add_commut.
      rewrite Int.add_neg_zero. reflexivity.
    - eapply Int.add_zero; eauto.
  Qed.

  Lemma unsigned_le_sub:
    forall i1 i2 i3,
      Int.unsigned i1 <= Int.unsigned i2 <= Int.unsigned i3 ->
      Int.unsigned (Int.sub i2 i1) <= Int.unsigned (Int.sub i3 i1).
Proof.
    intros. rewrite ! Int.unsigned_sub_borrow.
    unfold Int.sub_borrow. rewrite ! Int.unsigned_zero.
    destruct (zlt (Int.unsigned i2 - Int.unsigned i1 - 0) 0); try Psatz.nia.
    destruct (zlt (Int.unsigned i3 - Int.unsigned i1 - 0) 0); try Psatz.nia.
  Qed.

  Lemma int_add_sub:
    forall i1 i2,
      Int.unsigned i1 <= Int.unsigned i2 < Int.modulus - 1 ->
      Int.unsigned i1 + (Int.unsigned (Int.sub i2 i1) + 1) < Int.modulus.
Proof.
    intros. rewrite Int.unsigned_sub_borrow.
    unfold Int.sub_borrow.
    rewrite Int.unsigned_zero.
    destruct (zlt (Int.unsigned i2 - Int.unsigned i1 - 0)); try Psatz.lia.
    rewrite Int.unsigned_zero.
    generalize (Int.unsigned_range i2). Psatz.nia.
  Qed.

  Lemma int_add_S_one:
    forall i n,
      Int.unsigned i + Z.of_nat (S n) < Int.modulus ->
      Int.unsigned i + Z.of_nat (S n) = Int.unsigned (Int.add i Int.one) + Z.of_nat n.
Proof.
    intros. assert (forall x n, x + Z.of_nat (S n) = x + 1 + Z.of_nat n) by (intros; xomega).
    rewrite H0 in H. rewrite H0.
    simpl. rewrite Int.unsigned_add_carry.
    unfold Int.add_carry. rewrite Int.unsigned_one. rewrite Int.unsigned_zero.
    destruct (zlt (Int.unsigned i + 1 + 0)); try Psatz.nia.
    rewrite Int.unsigned_zero. Psatz.nia.
  Qed.
  
  Lemma addr_of_local_in:
    forall kappa n sp ofs vals,
      addr_of_local kappa sp ofs n vals ->
      Int.unsigned ofs + Z.of_nat n < Int.modulus ->
      (forall v, In v vals <-> exists i, v = Val.add sp (Vint i) /\ Int.unsigned ofs <= Int.unsigned i < Int.unsigned ofs + Z.of_nat n /\ (align_chunk kappa | Int.unsigned i)).
Proof.
    induction n; intros.
    - split; intros.
      + inv H; inv H1.
      + inv H. destruct H1 as [i [A [B C]]].
        Psatz.nia.
    - split; intros.
      + inv H. destruct (ArithLib.Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)).
        * inv H1.
          { eexists. repeat split.
            - omega.
            - simpl; xomega.
            - eauto. }
          { generalize H0; intros HA.
            rewrite int_add_S_one in HA; try assumption.
            generalize (proj1 (IHn _ _ _ H4 HA v) H). intros [i [A [B C]]].
            exists i; repeat split; eauto.
            - assert (Int.unsigned (Int.add ofs Int.one) = Int.unsigned ofs + 1); try xomega.
              replace 1 with (Z.of_nat (S O)); try xomega.
              rewrite int_add_S_one; try xomega.
            - rewrite <- int_add_S_one in B; auto. xomega. }
        * generalize H0; intros HA.
          rewrite int_add_S_one in HA; try assumption.
          generalize (proj1 (IHn _ _ _ H4 HA v) H1). intros [i [A [B C]]].
          exists i; repeat split; eauto.
          { assert (Int.unsigned (Int.add ofs Int.one) = Int.unsigned ofs + 1); try xomega.
            replace 1 with (Z.of_nat (S O)); try xomega.
            rewrite int_add_S_one; try xomega. }
          { rewrite <- int_add_S_one in B; auto. xomega. }
      + inv H. destruct H1 as [i [A [B C]]].
        destruct (ArithLib.Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)).
        * destruct (Int.eq_dec i ofs).
          { subst i. subst v; eapply in_eq. }
          { right. generalize H0; intros HA.
            rewrite int_add_S_one in HA; try assumption.
            eapply (proj2 (IHn _ _ _ H4 HA v)).
            exists i. repeat split; try assumption.
            - assert (Int.unsigned ofs <> Int.unsigned i); try congruence.
              { red; intros. eapply n0. destruct i; destruct ofs; eapply Int.mkint_eq. simpl in H. congruence. }
              assert (Int.unsigned ofs + 1 <= Int.unsigned i); try xomega.
              assert (Int.unsigned (Int.add ofs Int.one) = Int.unsigned ofs + 1); try xomega.
              replace 1 with (Z.of_nat (S O)); try xomega.
              rewrite int_add_S_one; try xomega.
            - rewrite <- int_add_S_one; try xomega. }
        * destruct (Int.eq_dec i ofs); try congruence.
          generalize H0; intros HA.
          rewrite int_add_S_one in HA; try assumption.
          eapply (proj2 (IHn _ _ _ H4 HA v)).
          exists i. repeat split; try assumption.
          { assert (Int.unsigned ofs <> Int.unsigned i); try congruence.
            assert (Int.unsigned ofs + 1 <= Int.unsigned i); try xomega.
            assert (Int.unsigned (Int.add ofs Int.one) = Int.unsigned ofs + 1); try xomega.
            replace 1 with (Z.of_nat (S O)); try xomega.
            rewrite int_add_S_one; try xomega. }
          { rewrite <- int_add_S_one; try xomega. }
  Qed.

  Lemma addr_of_global_in:
    forall kappa b n ofs vals,
      addr_of_global kappa b ofs n vals ->
      Int.unsigned ofs + Z.of_nat n < Int.modulus ->
      (forall v, In v vals <-> exists i, v = Vptr b i /\ Int.unsigned ofs <= Int.unsigned i < Int.unsigned ofs + Z.of_nat n /\ (align_chunk kappa | Int.unsigned i)).
Proof.
    induction n; intros.
    - split; intros.
      + inv H; inv H1.
      + inv H. destruct H1 as [i [A [B C]]].
        Psatz.nia.
    - split; intros.
      + inv H. destruct (ArithLib.Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)).
        * inv H1.
          { eexists. repeat split.
            - omega.
            - simpl; xomega.
            - eauto. }
          { generalize H0; intros HA.
            rewrite int_add_S_one in HA; try assumption.
            generalize (proj1 (IHn _ _ H4 HA v) H). intros [i [A [B C]]].
            exists i; repeat split; eauto.
            - assert (Int.unsigned (Int.add ofs Int.one) = Int.unsigned ofs + 1); try xomega.
              replace 1 with (Z.of_nat (S O)); try xomega.
              rewrite int_add_S_one; try xomega.
            - rewrite <- int_add_S_one in B; auto. xomega. }
        * generalize H0; intros HA.
          rewrite int_add_S_one in HA; try assumption.
          generalize (proj1 (IHn _ _ H4 HA v) H1). intros [i [A [B C]]].
          exists i; repeat split; eauto.
          { assert (Int.unsigned (Int.add ofs Int.one) = Int.unsigned ofs + 1); try xomega.
            replace 1 with (Z.of_nat (S O)); try xomega.
            rewrite int_add_S_one; try xomega. }
          { rewrite <- int_add_S_one in B; auto. xomega. }
      + inv H. destruct H1 as [i [A [B C]]].
        destruct (ArithLib.Zdivides_dec (align_chunk kappa) (Int.unsigned ofs)).
        * destruct (Int.eq_dec i ofs).
          { subst i. subst v; eapply in_eq. }
          { right. generalize H0; intros HA.
            rewrite int_add_S_one in HA; try assumption.
            eapply (proj2 (IHn _ _ H4 HA v)).
            exists i. repeat split; try assumption.
            - assert (Int.unsigned ofs <> Int.unsigned i); try congruence.
              { red; intros. eapply n0. destruct i; destruct ofs; eapply Int.mkint_eq. simpl in H. congruence. }
              assert (Int.unsigned ofs + 1 <= Int.unsigned i); try xomega.
              assert (Int.unsigned (Int.add ofs Int.one) = Int.unsigned ofs + 1); try xomega.
              replace 1 with (Z.of_nat (S O)); try xomega.
              rewrite int_add_S_one; try xomega.
            - rewrite <- int_add_S_one; try xomega. }
        * destruct (Int.eq_dec i ofs); try congruence.
          generalize H0; intros HA.
          rewrite int_add_S_one in HA; try assumption.
          eapply (proj2 (IHn _ _ H4 HA v)).
          exists i. repeat split; try assumption.
          { assert (Int.unsigned ofs <> Int.unsigned i); try congruence.
            assert (Int.unsigned ofs + 1 <= Int.unsigned i); try xomega.
            assert (Int.unsigned (Int.add ofs Int.one) = Int.unsigned ofs + 1); try xomega.
            replace 1 with (Z.of_nat (S O)); try xomega.
            rewrite int_add_S_one; try xomega. }
          { rewrite <- int_add_S_one; try xomega. }
  Qed.

  Lemma pop_is_nth:
    forall n sps,
      pop n sps = nth_error sps n.
Proof.
    induction n; intros; simpl; try reflexivity; eauto.
    destruct sps; eauto.
  Qed.
  
  Lemma addr_of_annotations_to_annot_sem:
    forall kappa ge sps alpha vals a,
      (forall depth varname base bound, In (ABlocal depth varname (base, bound)) alpha ->
                                   Int.unsigned base <= Int.unsigned bound < Int.modulus - 1) ->
      (forall id base bound, In (ABglobal id (base, bound)) alpha -> Int.unsigned base <= Int.unsigned bound < Int.modulus - 1) ->
      addr_of_annotations kappa ge sps alpha vals ->
      In a vals ->
      annot_sem (Genv.find_symbol ge) sps alpha a.
Proof.
    induction alpha; intros.
    - inv H1. inv H2.
    - inv H1.
      + eapply in_app in H2. destruct H2 as [H2 | H2].
        * exploit addr_of_local_in; eauto.
          { rewrite positive_nat_Z. rewrite <- H5.
            eapply int_add_sub. eapply H. eapply in_eq. }
          { intros [A B]. eapply A in H2. destruct H2 as [ofs [HA [HB HC]]].
            right. left. eexists. eexists. eexists. eexists. split.
            - eapply in_eq.
            - rewrite pop_is_nth in H6.
              exists sp. exists ofs. repeat split; eauto; try Psatz.nia.
              rewrite positive_nat_Z in HB. rewrite <- H5 in HB.
              rewrite Int.unsigned_sub_borrow in HB. unfold Int.sub_borrow in HB.
              rewrite Int.unsigned_zero in HB. generalize (H _ _ _ _ (in_eq _ _)). intros [XA XB].
              destruct (zlt (Int.unsigned bound - Int.unsigned base - 0) 0); try Psatz.nia.
              rewrite Int.unsigned_zero in HB. omega. }
        * exploit IHalpha; eauto.
          { intros; eapply H; eapply in_cons; eauto. }
          { intros; eapply H0; eapply in_cons; eauto. }
          { intros [A | [A | A]].
            - subst alpha. right. inv H9. inv H2.
            - right. left. destruct A as [d [nm [b1 [b2 [Hin [sp' [ofs [Hpop [Heq Hrange]]]]]]]]].
              eexists. eexists. eexists. eexists. repeat split; eauto.
              eapply in_cons; eauto.
            - right. right. destruct A as [id [b1 [b2 [b [ofs [Hfind [Heq Hrange]]]]]]].
              eexists. eexists. eexists. repeat split; eauto.
              eapply in_cons; eauto. }
      + eapply in_app in H2. destruct H2 as [H2 | H2].
        * exploit addr_of_global_in; eauto.
          { rewrite positive_nat_Z. rewrite <- H6.
            eapply int_add_sub. eapply H0. eapply in_eq. }
          { intros [A B]. eapply A in H2. destruct H2 as [ofs [HA [HB HC]]].
            right. right. eexists. eexists. eexists. split.
            - eapply in_eq.
            - exists id. exists ofs. repeat split; eauto; try Psatz.nia.
              rewrite positive_nat_Z in HB. rewrite <- H6 in HB.
              rewrite Int.unsigned_sub_borrow in HB. unfold Int.sub_borrow in HB.
              rewrite Int.unsigned_zero in HB. generalize (H0 _ _ _ (in_eq _ _)). intros [XA XB].
              destruct (zlt (Int.unsigned bound - Int.unsigned base - 0) 0); try Psatz.nia.
              rewrite Int.unsigned_zero in HB. omega. }
        * exploit IHalpha; eauto.
          { intros; eapply H; eapply in_cons; eauto. }
          { intros; eapply H0; eapply in_cons; eauto. }
          { intros [A | [A | A]].
            - subst alpha. inv H9; inv H2.
            - right. left. destruct A as [d [nm [b1 [b2 [Hin [sp' [ofs [Hpop [Heq Hrange]]]]]]]]].
              eexists. eexists. eexists. eexists. repeat split; eauto.
              eapply in_cons; eauto.
            - right. right. destruct A as [id' [b1 [b2 [b' [ofs [Hfind [Heq Hrange]]]]]]].
              eexists. eexists. eexists. repeat split; eauto.
              eapply in_cons; eauto. }
  Qed.
            
End ADDR.

Section RELSEM.

  Variable ge: genv.
  
  Inductive step_block': RTL.state -> trace -> RTL.state -> Prop :=
  | exec_Inop_block:
      forall s f sp pc rs m pc',
      (fn_code f)!pc = Some(Inop pc') ->
      step_block' (State s f sp pc rs m)
               E0 (State s f sp pc' rs m)
  | exec_Iop_block':
      forall s f sp pc rs m op args res pc' v,
      (fn_code f)!pc = Some(Iop op args res pc') ->
      eval_operation ge sp op rs##args m = Some v ->
      step_block' (State s f sp pc rs m)
               E0 (State s f sp pc' (rs#res <- v) m)
  | exec_Iload_block':
      forall s f sp pc rs m alpha chunk addr args dst pc' a v
        (Hlocrange: forall depth varname base bound, In (ABlocal depth varname (base, bound)) (snd alpha) ->
                                                Int.unsigned base <= Int.unsigned bound < Int.modulus - 1)
        (Hglobrange: forall id base bound, In (ABglobal id (base, bound)) (snd alpha) -> Int.unsigned base <= Int.unsigned bound < Int.modulus - 1),
        (fn_code f)!pc = Some(Iload alpha chunk addr args dst pc') ->
        eval_addressing ge sp addr rs##args = Some a ->
        Mem.loadv chunk m a = Some v ->
        annot_sem (Genv.find_symbol ge) (sp::(List.map (fun s => match s with Stackframe res f sp pc rs => sp end) s)) (snd alpha) a ->
        step_block' (State s f sp pc rs m)
                 E0 (State s f sp pc' (rs#dst <- v) m)
  | exec_Istore_block':
      forall s f sp pc rs m alpha chunk addr args src pc' a m'
        (Hlocrange: forall depth varname base bound, In (ABlocal depth varname (base, bound)) (snd alpha) ->
                                                Int.unsigned base <= Int.unsigned bound < Int.modulus - 1)
        (Hglobrange: forall id base bound, In (ABglobal id (base, bound)) (snd alpha) -> Int.unsigned base <= Int.unsigned bound < Int.modulus - 1),
        (fn_code f)!pc = Some(Istore alpha chunk addr args src pc') ->
        eval_addressing ge sp addr rs##args = Some a ->
        Mem.storev chunk m a rs#src = Some m' ->
        annot_sem (Genv.find_symbol ge) (sp::(List.map (fun s => match s with Stackframe res f sp pc rs => sp end) s)) (snd alpha) a ->
        step_block' (State s f sp pc rs m)
                 E0 (State s f sp pc' rs m')
  | exec_Icall_block':
      forall s f sp pc rs m sig ros args res pc' fd,
      (fn_code f)!pc = Some(Icall sig ros args res pc') ->
      find_function ge ros rs = Some fd ->
      funsig fd = sig ->
      step_block' (State s f sp pc rs m)
               E0 (Callstate (Stackframe res f sp pc' rs :: s) fd rs##args m)
  | exec_Itailcall_block':
      forall s f stk pc rs m sig ros args fd m',
      (fn_code f)!pc = Some(Itailcall sig ros args) ->
      find_function ge ros rs = Some fd ->
      funsig fd = sig ->
      Mem.free m stk 0 f.(fn_stacksize) = Some m' ->
      step_block' (State s f (Vptr stk Int.zero) pc rs m)
               E0 (Callstate s fd rs##args m')
  | exec_Ibuiltin_block':
      forall s f sp pc rs m ef args res pc' vargs t vres m',
      (fn_code f)!pc = Some(Ibuiltin ef args res pc') ->
      eval_builtin_args ge (fun r => rs#r) sp m args vargs ->
      external_call ef ge vargs m t vres m' ->
      step_block' (State s f sp pc rs m)
                t (State s f sp pc' (regmap_setres res vres rs) m')
  | exec_Icond_block':
      forall s f sp pc rs m cond args ifso ifnot b pc',
      (fn_code f)!pc = Some(Icond cond args ifso ifnot) ->
      eval_condition cond rs##args m = Some b ->
      pc' = (if b then ifso else ifnot) ->
      step_block' (State s f sp pc rs m)
               E0 (State s f sp pc' rs m)
  | exec_Ijumptable_block':
      forall s f sp pc rs m arg tbl n pc',
      (fn_code f)!pc = Some(Ijumptable arg tbl) ->
      rs#arg = Vint n ->
      list_nth_z tbl (Int.unsigned n) = Some pc' ->
      step_block' (State s f sp pc rs m)
               E0 (State s f sp pc' rs m)
  | exec_Ireturn_block':
      forall s f stk pc rs m or m',
      (fn_code f)!pc = Some(Ireturn or) ->
      Mem.free m stk 0 f.(fn_stacksize) = Some m' ->
      step_block' (State s f (Vptr stk Int.zero) pc rs m)
               E0 (Returnstate s (regmap_optget or Vundef rs) m')
  | exec_function_internal_block':
      forall s f args m m' stk,
      Mem.alloc m 0 f.(fn_stacksize) = (m', stk) ->
      step_block' (Callstate s (Internal f) args m)
               E0 (State s f (Vptr stk Int.zero) f.(fn_entrypoint) (init_regs args f.(fn_params)) m')
  | exec_function_external_block':
      forall s ef args res t m m',
      external_call ef ge args m t res m' ->
      step_block' (Callstate s (External ef) args m)
                t (Returnstate s res m')
  | exec_return_block':
      forall res f sp pc rs s vres m,
      step_block' (Returnstate (Stackframe res f sp pc rs :: s) vres m)
               E0 (State s f sp pc (rs#res <- vres) m).

End RELSEM.

Definition semantics_block' (p: program) :=
  Semantics step_block' (initial_state p) final_state (Genv.globalenv p).

Lemma semantics_block'_receptive:
  forall (p: program), receptive (semantics_block' p).
Proof.
  intros. constructor; simpl; intros.
 receptiveness *)  assert (t1 = E0 -> exists s2, step_block' (Genv.globalenv p) s t2 s2).
    intros. subst. inv H0. exists s1; auto.
  inversion H; subst; auto.
  exploit external_call_receptive; eauto. intros [vres2 [m2 EC2]].
  exists (State s0 f sp pc' (regmap_setres res vres2 rs) m2). eapply exec_Ibuiltin_block'; eauto.
  exploit external_call_receptive; eauto. intros [vres2 [m2 EC2]].
  exists (Returnstate s0 vres2 m2). econstructor; eauto.
 trace length *)  red; intros; inv H; simpl; try omega.
  eapply external_call_trace_length; eauto.
  eapply external_call_trace_length; eauto.
Qed.

Lemma semantics_block'_determinate:
  forall p, determinate (semantics_block' p).
Proof.
  intros; constructor; simpl; intros.
  - inv H; inv H0; Equalities; try (split; constructor; auto; fail).
    + exploit external_call_determ.
      * eapply H3.
      * generalize (eval_builtin_args_determ H12 H2). intros A; rewrite <- A in H13; eapply H13.
      * intros [A B]; split; auto.
        intros. eapply B in H. destruct H; subst; auto.
    + exploit external_call_determ.
      * eapply H1.
      * eapply H7.
      * intros [A B]; split; auto.
        intros. eapply B in H. destruct H; subst; auto.
  - red; intros. inv H; simpl; try omega.
    + eapply external_call_trace_length; eauto.
    + eapply external_call_trace_length; eauto.
  - inv H; inv H0. rewrite H1 in H; inv H.
    f_equal. subst ge; subst ge0. rewrite H2 in H5; inv H5. congruence.
  - inv H. red; intros; red; intros. inv H.
  - inv H; inv H0; congruence.
Qed.

Theorem FSIM_block'_safe:
  forall p, forward_simulation (semantics_block' p) (semantics_safe p).
Proof.
  intros. eapply forward_simulation_step.
  - reflexivity.
  - intros. exists s1. split; eauto. instantiate (1 := fun s1 s2 => s1 = s2). simpl. reflexivity.
  - simpl; intros. subst s1; eauto.
  - simpl; intros. subst s1.
    exists s1'. split; eauto. inv H; try (split; try (econstructor; eauto; fail); try (split; intros; congruence; fail)).
    + eapply exec_Iload; eauto.
    + eapply exec_Icond; eauto.
    + eapply exec_Ijumptable; eauto.
Qed.

Theorem BSIM_block'_safe:
  forall p, backward_simulation (semantics_block' p) (semantics_safe p).
Proof.
  intros. eapply forward_to_backward_simulation; eauto.
  - eapply FSIM_block'_safe.
  - eapply semantics_block'_receptive.
  - eapply semantics_safe_determinate.
Qed.