Module Memory


This file develops the memory model that is used in the dynamic semantics of all the languages used in the compiler. It defines a type mem of memory states, the following 4 basic operations over memory states, and their properties:

Require Import Zwf.
Require Import Axioms.
Require Import Coqlib.
Require Intv.
Require Import Maps.
Require Archi.
Require Import AST.
Require Import Integers.
Require Import Floats.
Require Import Values.
Require Import Values_symbolictype.
Require Import Values_symbolic.
Require Export Memtype.
Require Import IntFacts.
Require Import Normalise.
Require Import NormaliseSpec.
Require Export Memdata.
Require Import Alloc.
Require Import Permutation.

Local Unset Elimination Schemes.
Local Unset Case Analysis Schemes.

Local Notation "a # b" := (PMap.get b a) (at level 1).

Module Mem <: MEM.

Definition perm_order' (po: option permission) (p: permission) :=
  match po with
  | Some p' => perm_order p' p
  | None => False
 end.

Definition perm_order'' (po1 po2: option permission) :=
  match po1, po2 with
  | Some p1, Some p2 => perm_order p1 p2
  | _, None => True
  | None, Some _ => False
 end.

Record mem' : Type := mkmem {
  mem_contents: PMap.t (ZMap.t memval); (* block -> offset -> memval *)
  mem_access: PMap.t (Z -> perm_kind -> option permission);
  mem_blocksize: PMap.t (option (Z*Z));
  mem_mask: PMap.t (option nat); (* block -> option mask; the number of 0-trailing-bits *)
  nextblock: block;
  access_max:
    forall b ofs, perm_order'' (mem_access#b ofs Max) (mem_access#b ofs Cur);
  nextblock_noaccess:
    forall b ofs k, ~(Plt b nextblock) -> mem_access#b ofs k = None;
  contents_wt_memval:
    forall b ofs, wt_memval (ZMap.get ofs (mem_contents # b));
  access_bounds_ok: (* permissions and bounds agree *)
    forall b ofs k,
      (mem_access#b ofs k) <> None
      -> in_bound ofs (get_bounds mem_blocksize b);
  bs_valid: (* only valid blocks have non-empty bounds *)
    forall b, ~ (Plt b nextblock) -> mem_blocksize#b = None;
  msk_valid: (* only valid blocks have non-empty masks *)
    forall b, ~ (Plt b nextblock) -> mem_mask#b = None;
  wfm_alloc_ok: (* the allocation algorithm succeeds on this memory *)
    alloc_blocks
      (mk_block_list_aux (get_size ((mem_blocksize)))
                         (pred (Pos.to_nat nextblock))) <> None;
  alignment_ok: (* the alignment agrees with the bounds of blocks *)
    forall b al,
      mem_mask#b = Some al ->
      al = alignment_of_size (get_size (mem_blocksize) b);
  bounds_mask_consistency: (* bounds and masks information are present together, or not at all *)
    forall b,
      (mem_mask#b = None /\ mem_blocksize#b = None) \/
      (mem_mask#b <> None /\ mem_blocksize#b <> None);
  bounds_lo_inf0: (* the lower bound of a block is 0 *)
    forall b lo hi,
      mem_blocksize#b = Some (lo,hi) ->
      lo = 0
}.

Definition mem := mem'.


Definition size_block (m: mem) (b:block) : Z :=
  get_size (m.(mem_blocksize)) b.

Definition size_of_block (m:mem) (b:block) :=
  match PMap.get b m.(mem_blocksize) with
      | Some (lo,hi) => (Int.repr (hi-lo))
      | None => (Int.repr 0)
  end.

Definition bounds_of_block (m:mem) (b:block) : Z*Z :=
  match PMap.get b m.(mem_blocksize) with
      | Some (lo,hi) => (lo,hi)
      | None => (0,0)
  end.

Definition mask (m:mem) (b:block) :=
  match PMap.get b m.(mem_mask) with
      | Some mask => mask
      | None => O
  end.

The mask of a block is
Definition nat_mask (m:mem) (b:block) :=
  Int.not (Int.repr (two_power_nat (mask m b) - 1)).

Definition mk_block_list (m:mem) : (list (block * Z)) :=
  mk_block_list_aux (size_block m) (pred (Pos.to_nat (Mem.nextblock m))).

alloc_mem computes a concrete memory for memory m. size_mem is the first available address after the allocation of m.

Definition size_mem (m:mem) : Z :=
  size_mem_aux (mk_block_list m).

Definition alloc_mem (m:mem) : option (block -> int) :=
  alloc_blocks (mk_block_list m).

Normalisation in a given memory. This is what is called normalise in the paper.

Definition mem_norm (m:mem) (e: expr_sym) (r:result) : val :=
  Normalise.normalise (bounds_of_block m) (nat_mask m) e r.
  
Lemma mkmem_ext:
 forall cont1 cont2 acc1 acc2 next1 next2
        a1 a2 b1 b2 c1 c2 bs1 bs2
        mask1 mask2 Pabo1 Pabo2 comp2 comp1 vbs1 vbs2 vm1 vm2
        const1 const2 bb1 bb2
        bli1 bli2,
   cont1=cont2 -> acc1=acc2 -> next1=next2 ->
   bs1=bs2 -> mask1=mask2 ->
   mkmem cont1 acc1 bs1 mask1 next1
         a1 b1 c1 Pabo1 vbs1 vm1
         comp1 bb1 const1 bli1 =
   mkmem cont2 acc2 bs2 mask2 next2
         a2 b2 c2 Pabo2 vbs2 vm2
         comp2 bb2 const2 bli2.
Proof.
  intros. subst.
  rewrite (proof_irr a1 a2).
  rewrite (proof_irr b1 b2).
  rewrite (proof_irr c1 c2).
  rewrite (proof_irr Pabo1 Pabo2).
  rewrite (proof_irr vbs1 vbs2).
  rewrite (proof_irr vm1 vm2).
  rewrite (proof_irr comp1 comp2).
  rewrite (proof_irr bb1 bb2).
  rewrite (proof_irr const1 const2).
  rewrite (proof_irr bli1 bli2).
  reflexivity.
Qed.

Lemma alloc_compat:
  forall m,
  exists al,
    alloc_mem m = Some al /\
    compat al (bounds_of_block m) (nat_mask m).
Proof.
  intros.
  generalize (wfm_alloc_ok m).
  destruct (alloc_blocks
              (mk_block_list_aux (get_size (mem_blocksize m))
                                 (pred (Pos.to_nat (nextblock m))))) eqn:?;
           try congruence.
  intros _.
  exists i.
  split.
  - rewrite <- Heqo; auto.
  - revert Heqo.
    apply compat_alloc.
    + unfold nat_mask, mask. intro b.
      generalize (bounds_mask_consistency m b).
      destr.
      apply alignment_ok in Heqo; subst; auto.
      unfold get_size; rewrite H1.
      unfold alignment_of_size; auto.
    + intros.
      unfold bounds_of_block.
      destr; try omega.
      des p; apply bounds_lo_inf0 in Heqo; auto. subst; omega.
    + intros.
      unfold bounds_of_block, get_size.
      destr. destr; try des p; inv Heqp; auto.
    + intros.
      unfold bounds_of_block.
      rewrite (bs_valid m b); auto.
      intro. zify; xomega.
Qed.

Lemma concrete_mem:
  forall m,
  exists al,
    compat al (bounds_of_block m) (nat_mask m).
Proof.
  intros.
  destruct (alloc_compat m) as [al [A B]]; exists al; auto.
Qed.

Lemma alloc_permut:
  forall l l',
    Permutation l l' ->
    (alloc_blocks l = None <-> alloc_blocks l' = None).
Proof.
  unfold alloc_blocks.
  intros.
  destruct (alloc_mem_aux l) eqn:?.
  destruct (alloc_mem_aux l') eqn:?.
  apply alloc_size_mem_aux in Heqp.
  apply alloc_size_mem_aux in Heqp0.
  rewrite (size_mem_aux_permut _ _ H) in Heqp.
  subst. repeat destr.
Qed.



Lemma norm_ptr_same_eval:
  forall m l b o
         (MN : Mem.mem_norm m (Eval (Eptr l Int.zero)) Ptr = Vptr b o)
         (NIB : ~ NormaliseSpec.in_bound 0 (Mem.bounds_of_block m l)),
    False.
Proof.
  intros.
  destruct (Normalise.norm_correct2 (Mem.bounds_of_block m) (Mem.nat_mask m)
                                    (Eval (Eptr l Int.zero)) Ptr).
  unfold Mem.mem_norm in MN. rewrite MN in *.
  simpl in *.
  destruct (concrete_mem m) as [x COMP].
  destruct (eval_ok eq_refl _ COMP); try discriminate.
  assert (COMP': NormaliseSpec.compat (fun b => if peq b l then Int.add Int.one (x b)
                                         else x b)
                               (Mem.bounds_of_block m)
                               (Mem.nat_mask m)).
  {
    assert (NEX: ~ exists o, NormaliseSpec.in_bound o (Mem.bounds_of_block m l)).
    {
      revert NIB; destruct (Mem.bounds_of_block m l) eqn:?.
      unfold Mem.bounds_of_block in Heqp.
      destruct (Mem.mem_blocksize m) !! l eqn:?.
      destruct p. apply Mem.bounds_lo_inf0 in Heqo0.
      subst. inv Heqp.
      unfold NormaliseSpec.in_bound; simpl.
      intro.
      intros [o0 A]. omega.
      inv Heqp.
      unfold NormaliseSpec.in_bound; simpl.
      intros A [o0 B]. omega.
    }
    assert (GS: Alloc.get_size (Mem.mem_blocksize m) l <= 0).
    {
      unfold Alloc.get_size.
      destr_cond_match; try omega.
      destruct p.
      destruct (zle (z0 - z) 0); auto.
      exfalso; apply NEX.
      unfold Mem.bounds_of_block. rewrite Heqo0.
      unfold NormaliseSpec.in_bound. simpl. exists z; omega.
    }
    inv COMP.
    constructor; simpl; intros; auto.
    - destruct (peq b0 l); subst; auto. exfalso; apply NEX; eexists; eauto.
    - destruct (peq b0 l); subst; auto.
      destruct (peq b' l); subst; auto.
      exfalso; apply NEX; eexists; eauto.
      destruct (peq b' l); subst; auto.
      exfalso; apply NEX; eexists; eauto.
    - destruct (peq b0 l); subst; auto.
      generalize (Mem.alignment_ok m l).
      destruct (Mem.mem_mask m) !! l eqn:?.
      intro A; specialize (A _ eq_refl).
      unfold Mem.nat_mask, Mem.mask.
      rewrite Heqo0; subst.
      replace (Alloc.alignment_of_size (Alloc.get_size (Mem.mem_blocksize m) l))
      with 0%nat.
      simpl.
      change (Int.repr 0) with Int.zero.
      rewrite Int.not_zero. rewrite Int.and_mone. auto.
      unfold Alloc.alignment_of_size.
      destruct zlt; omega.
      intros _.
      unfold Mem.nat_mask, Mem.mask.
      rewrite Heqo0.
      simpl.
      change (Int.repr 0) with Int.zero.
      rewrite Int.not_zero. rewrite Int.and_mone. auto.
  }
  destruct (eval_ok eq_refl _ COMP'); try discriminate.
  destruct H0 as [_ A].
  destruct H as [_ B].
  clear eval_ok.
  rewrite peq_true in A.
  destruct (peq b l); subst; auto.
  * specialize (A Normalise.dummy_em).
    specialize (B Normalise.dummy_em).
    apply eq_lval_inj_i in B.
    apply eq_lval_inj_i in A.
    apply Normalise.int_add_eq_l in A.
    subst. apply NIB; auto.
  * specialize (A Normalise.dummy_em).
    specialize (B Normalise.dummy_em).
    apply eq_lval_inj_i in B.
    apply eq_lval_inj_i in A.
    rewrite <- B in A.
    rewrite ! Int.add_zero in A.
    exfalso; clear - A.
    revert A.
    replace (x l) with (Int.add (x l) Int.zero) at 2 by (apply Int.add_zero).
    rewrite (Int.add_commut Int.one).
    intro A. apply Normalise.int_add_eq_l in A.
    discriminate.
Qed.


Validity of blocks and accesses


A block address is valid if it was previously allocated. It remains valid even after being freed.

Definition valid_block (m: mem) (b: block) := Plt b (nextblock m).

Definition valid_block_dec (m:mem) (b:block) : {valid_block m b} + {~ valid_block m b}.
Proof.
  unfold valid_block; apply plt.
Defined.

Theorem valid_not_valid_diff:
  forall m b b', valid_block m b -> ~(valid_block m b') -> b <> b'.
Proof.
  intros; red; intros. subst b'. contradiction.
Qed.

Hint Local Resolve valid_not_valid_diff: mem.

Permissions

Definition perm (m: mem) (b: block) (ofs: Z) (k: perm_kind) (p: permission) : Prop :=
   perm_order' (m.(mem_access)#b ofs k) p.

Theorem perm_implies:
  forall m b ofs k p1 p2, perm m b ofs k p1 -> perm_order p1 p2 -> perm m b ofs k p2.
Proof.
  unfold perm, perm_order'; intros.
  destruct (m.(mem_access)#b ofs k); auto.
  eapply perm_order_trans; eauto.
Qed.

Hint Local Resolve perm_implies: mem.

Theorem perm_cur_max:
  forall m b ofs p, perm m b ofs Cur p -> perm m b ofs Max p.
Proof.
  assert (forall po1 po2 p,
          perm_order' po2 p -> perm_order'' po1 po2 -> perm_order' po1 p).
  unfold perm_order', perm_order''. intros.
  destruct po2; try contradiction.
  destruct po1; try contradiction.
  eapply perm_order_trans; eauto.
  unfold perm; intros.
  generalize (access_max m b ofs). eauto.
Qed.

Theorem perm_cur:
  forall m b ofs k p, perm m b ofs Cur p -> perm m b ofs k p.
Proof.
  intros. destruct k; auto. apply perm_cur_max. auto.
Qed.

Theorem perm_max:
  forall m b ofs k p, perm m b ofs k p -> perm m b ofs Max p.
Proof.
  intros. destruct k; auto. apply perm_cur_max. auto.
Qed.

Hint Local Resolve perm_cur perm_max: mem.

Theorem perm_valid_block:
  forall m b ofs k p, perm m b ofs k p -> valid_block m b.
Proof.
  unfold perm; intros.
  destruct (plt b m.(nextblock)).
  auto.
  assert (m.(mem_access)#b ofs k = None).
  eapply nextblock_noaccess; eauto.
  rewrite H0 in H.
  contradiction.
Qed.

Hint Local Resolve perm_valid_block: mem.

Remark perm_order_dec:
  forall p1 p2, {perm_order p1 p2} + {~perm_order p1 p2}.
Proof.
  intros. destruct p1; destruct p2; (left; constructor) || (right; intro PO; inversion PO).
Defined.

Remark perm_order'_dec:
  forall op p, {perm_order' op p} + {~perm_order' op p}.
Proof.
  intros. destruct op; unfold perm_order'.
  apply perm_order_dec.
  right; tauto.
Defined.

Theorem perm_dec:
  forall m b ofs k p, {perm m b ofs k p} + {~ perm m b ofs k p}.
Proof.
  unfold perm; intros.
  apply perm_order'_dec.
Defined.

Definition range_perm (m: mem) (b: block) (lo hi: Z) (k: perm_kind) (p: permission) : Prop :=
  forall ofs, lo <= ofs < hi -> perm m b ofs k p.

Theorem range_perm_implies:
  forall m b lo hi k p1 p2,
  range_perm m b lo hi k p1 -> perm_order p1 p2 -> range_perm m b lo hi k p2.
Proof.
  unfold range_perm; intros; eauto with mem.
Qed.

Theorem range_perm_cur:
  forall m b lo hi k p,
  range_perm m b lo hi Cur p -> range_perm m b lo hi k p.
Proof.
  unfold range_perm; intros; eauto with mem.
Qed.

Theorem range_perm_max:
  forall m b lo hi k p,
  range_perm m b lo hi k p -> range_perm m b lo hi Max p.
Proof.
  unfold range_perm; intros; eauto with mem.
Qed.

Hint Local Resolve range_perm_implies range_perm_cur range_perm_max: mem.

Lemma range_perm_dec:
  forall m b lo hi k p, {range_perm m b lo hi k p} + {~ range_perm m b lo hi k p}.
Proof.
  intros.
  induction lo using (well_founded_induction_type (Zwf_up_well_founded hi)).
  destruct (zlt lo hi).
  destruct (perm_dec m b lo k p).
  destruct (H (lo + 1)). red. omega.
  left; red; intros. destruct (zeq lo ofs). congruence. apply r. omega.
  right; red; intros. elim n. red; intros; apply H0; omega.
  right; red; intros. elim n. apply H0. omega.
  left; red; intros. omegaContradiction.
Defined.

valid_access m chunk b ofs p holds if a memory access of the given chunk is possible in m at address b, ofs with current permissions p. This means:

Definition valid_access (m: mem) (chunk: memory_chunk) (b: block) (ofs: Z) (p: permission): Prop :=
  range_perm m b ofs (ofs + size_chunk chunk) Cur p
  /\ (align_chunk chunk | ofs).

Theorem valid_access_implies:
  forall m chunk b ofs p1 p2,
  valid_access m chunk b ofs p1 -> perm_order p1 p2 ->
  valid_access m chunk b ofs p2.
Proof.
  intros. inv H. constructor; eauto with mem.
Qed.

Theorem valid_access_freeable_any:
  forall m chunk b ofs p,
  valid_access m chunk b ofs Freeable ->
  valid_access m chunk b ofs p.
Proof.
  intros.
  eapply valid_access_implies; eauto. constructor.
Qed.

Hint Local Resolve valid_access_implies: mem.

Theorem valid_access_valid_block:
  forall m chunk b ofs,
  valid_access m chunk b ofs Nonempty ->
  valid_block m b.
Proof.
  intros. destruct H.
  assert (perm m b ofs Cur Nonempty).
    apply H. generalize (size_chunk_pos chunk). omega.
  eauto with mem.
Qed.

Hint Local Resolve valid_access_valid_block: mem.

Lemma valid_access_perm:
  forall m chunk b ofs k p,
  valid_access m chunk b ofs p ->
  perm m b ofs k p.
Proof.
  intros. destruct H. apply perm_cur. apply H. generalize (size_chunk_pos chunk). omega.
Qed.

Lemma valid_access_compat:
  forall m chunk1 chunk2 b ofs p,
  size_chunk chunk1 = size_chunk chunk2 ->
  align_chunk chunk2 <= align_chunk chunk1 ->
  valid_access m chunk1 b ofs p->
  valid_access m chunk2 b ofs p.
Proof.
  intros. inv H1. rewrite H in H2. constructor; auto.
  eapply Zdivide_trans; eauto. eapply align_le_divides; eauto.
Qed.

Lemma valid_access_dec:
  forall m chunk b ofs p,
  {valid_access m chunk b ofs p} + {~ valid_access m chunk b ofs p}.
Proof.
  intros.
  destruct (range_perm_dec m b ofs (ofs + size_chunk chunk) Cur p).
  destruct (Zdivide_dec (align_chunk chunk) ofs (align_chunk_pos chunk)).
  left; constructor; auto.
  right; red; intro V; inv V; contradiction.
  right; red; intro V; inv V; contradiction.
Defined.

valid_pointer m b ofs returns true if the address b, ofs is nonempty in m and false if it is empty.
Definition valid_pointer (m: mem) (b: block) (ofs: Z): bool :=
  perm_dec m b ofs Cur Nonempty.

Theorem valid_pointer_nonempty_perm:
  forall m b ofs,
  valid_pointer m b ofs = true <-> perm m b ofs Cur Nonempty.
Proof.
  intros. unfold valid_pointer.
  destruct (perm_dec m b ofs Cur Nonempty); simpl;
  intuition congruence.
Qed.

Theorem valid_pointer_valid_access:
  forall m b ofs,
  valid_pointer m b ofs = true <-> valid_access m Mint8unsigned b ofs Nonempty.
Proof.
  intros. rewrite valid_pointer_nonempty_perm.
  split; intros.
  split. simpl; red; intros. replace ofs0 with ofs by omega. auto.
  simpl. apply Zone_divide.
  destruct H. apply H. simpl. omega.
Qed.

C allows pointers one past the last element of an array. These are not valid according to the previously defined valid_pointer. The property weak_valid_pointer m b ofs holds if address b, ofs is a valid pointer in m, or a pointer one past a valid block in m.

Definition weak_valid_pointer (m: mem) (b: block) (ofs: Z) :=
  valid_pointer m b ofs || valid_pointer m b (ofs - 1).

Lemma weak_valid_pointer_spec:
  forall m b ofs,
  weak_valid_pointer m b ofs = true <->
    valid_pointer m b ofs = true \/ valid_pointer m b (ofs - 1) = true.
Proof.
  intros. unfold weak_valid_pointer. now rewrite orb_true_iff.
Qed.
Lemma valid_pointer_implies:
  forall m b ofs,
  valid_pointer m b ofs = true -> weak_valid_pointer m b ofs = true.
Proof.
  intros. apply weak_valid_pointer_spec. auto.
Qed.

Operations over memory stores


The initial store

Program Definition empty: mem :=
  mkmem (PMap.init (ZMap.init (Symbolic (Eval (Eint Int.zero)) O)))
        (PMap.init (fun ofs k => None))
        (PMap.init (None))
        (PMap.init None)
        2%positive _ _ _ _ _ _ _ _ _ _.
Next Obligation.
  repeat rewrite PMap.gi. red; auto.
Qed.
Next Obligation.
  rewrite PMap.gi. auto.
Qed.
Next Obligation.
  rewrite PMap.gi. rewrite ZMap.gi.
  simpl. exists Tint; simpl; auto.
  constructor.
Qed.
Next Obligation.
  repeat rewrite PMap.gi in H.
  congruence.
Qed.
Next Obligation.
  rewrite PMap.gi. auto.
Qed.
Next Obligation.
  rewrite PMap.gi. auto.
Qed.
Next Obligation.
  rewrite PMap.gi in H. congruence.
Qed.
Next Obligation.
  left.
  rewrite ! PMap.gi. tauto.
Qed.
Next Obligation.
  rewrite PMap.gi in H. congruence.
Qed.

Reading N adjacent bytes in a block content.

Fixpoint getN (n: nat) (p: Z) (c: ZMap.t memval) {struct n}: list memval :=
  match n with
  | O => nil
  | S n' => ZMap.get p c :: getN n' (p + 1) c
  end.

Lemma getN_Nlist : forall n p c, length (getN n p c) = n.
Proof.
    induction n.
    intros; reflexivity.
    intros.
    simpl.
    rewrite IHn.
    reflexivity.
  Qed.


Writing N adjacent bytes in a block content.

Fixpoint setN (vl: list memval) (p: Z) (c: ZMap.t memval) {struct vl}: ZMap.t memval :=
  match vl with
  | nil => c
  | v :: vl' => setN vl' (p + 1) (ZMap.set p v c)
  end.

Remark setN_other:
  forall vl c p q,
  (forall r, p <= r < p + Z_of_nat (length vl) -> r <> q) ->
  ZMap.get q (setN vl p c) = ZMap.get q c.
Proof.
  induction vl; intros; simpl.
  auto.
  simpl length in H. rewrite inj_S in H.
  transitivity (ZMap.get q (ZMap.set p a c)).
  apply IHvl. intros. apply H. omega.
  apply ZMap.gso. apply not_eq_sym. apply H. omega.
Qed.

Remark setN_outside:
  forall vl c p q,
  q < p \/ q >= p + Z_of_nat (length vl) ->
  ZMap.get q (setN vl p c) = ZMap.get q c.
Proof.
  intros. apply setN_other.
  intros. omega.
Qed.

Remark getN_setN_same:
  forall vl p c,
  getN (length vl) p (setN vl p c) = vl.
Proof.
  induction vl; intros; simpl.
  auto.
  decEq.
  rewrite setN_outside. apply ZMap.gss. omega.
  apply IHvl.
Qed.

Remark getN_exten:
  forall c1 c2 n p,
  (forall i, p <= i < p + Z_of_nat n -> ZMap.get i c1 = ZMap.get i c2) ->
  getN n p c1 = getN n p c2.
Proof.
  induction n; intros. auto. rewrite inj_S in H. simpl. decEq.
  apply H. omega. apply IHn. intros. apply H. omega.
Qed.

Remark getN_setN_disjoint:
  forall vl q c n p,
  Intv.disjoint (p, p + Z_of_nat n) (q, q + Z_of_nat (length vl)) ->
  getN n p (setN vl q c) = getN n p c.
Proof.
  intros. apply getN_exten. intros. apply setN_other.
  intros; red; intros; subst r. eelim H; eauto.
Qed.

Remark getN_setN_outside:
  forall vl q c n p,
  p + Z_of_nat n <= q \/ q + Z_of_nat (length vl) <= p ->
  getN n p (setN vl q c) = getN n p c.
Proof.
  intros. apply getN_setN_disjoint. apply Intv.disjoint_range. auto.
Qed.

Remark setN_default:
  forall vl q c, fst (setN vl q c) = fst c.
Proof.
  induction vl; simpl; intros. auto. rewrite IHvl. auto.
Qed.

We initialise the memory of allocated blocks with the appropriate Eundef values.

Fixpoint init_block' (n:nat) (b:block) (cur:int) : list memval :=
  match n with
      O => Symbolic (Eval (Eundef b cur)) O :: nil
    | S m => Symbolic (Eval (Eundef b cur)) O :: (init_block' m b (Int.add cur (Int.repr 1)))
  end.

Definition init_block (lo hi: Z) (b: block) : memval * PTree.t memval :=
  let sz := hi - lo in
  if zlt sz 0
  then (ZMap.init (Symbolic (Eval (Eint Int.zero)) O))
  else setN (init_block' (Z.to_nat sz) b (Int.repr lo)) lo (ZMap.init (Symbolic (Eval (Eint Int.zero)) O)).

Lemma init_block'_wt_memval:
  forall n b cur,
    Forall wt_memval (init_block' n b cur).
Proof.
  induction n; simpl; intuition try congruence.
  constructor; try constructor.
  simpl; eexists; intuition try constructor.
  constructor.
  simpl; eexists; intuition try constructor.
  apply IHn.
Qed.

Lemma init_block_wt_memval:
  forall lo hi cur ofs,
    wt_memval (ZMap.get ofs ( (init_block lo hi cur))).
Proof.
  intros.
  unfold init_block.
  destruct (zlt (hi - lo) 0).
  rewrite ZMap.gi.
  simpl; eexists; intuition try constructor.
  generalize (init_block'_wt_memval (Z.to_nat (hi - lo)) cur (Int.repr lo)).
  generalize (init_block' (Z.to_nat (hi - lo)) cur (Int.repr lo)).
  intros.
  assert (wt_memval (Symbolic (Eval (Eint Int.zero)) 0)).
  simpl; eexists; intuition try constructor.
  generalize H0.
  generalize (Symbolic (Eval (Eint Int.zero)) 0).
  clear H0.
  intros.
  assert (forall o, wt_memval (ZMap.get o (ZMap.init m))).
  {
    intros. rewrite ZMap.gi; auto.
  }
  generalize H1.
  generalize (ZMap.init m).
  clear H1 H0 g.
  revert H.
  clear.
  revert lo.
  revert l.
  induction l; simpl; intuition try congruence.
  + apply H1.
  + apply IHl.
    inv H; auto.
    intros.
    destruct (ZMap.elt_eq o lo).
    subst.
    rewrite ZMap.gss.
    inv H; auto.
    rewrite ZMap.gso; auto.
Qed.

Lemma pos_ex_index:
  forall p,
    exists z,
      ZIndexed.index z = p.
Proof.
  induction p; unfold ZIndexed.index; simpl.
  exists (Z.neg p); auto.
  exists (Z.pos p); auto.
  exists 0; auto.
Qed.

Lemma init_block_wt_memval':
  forall lo hi cur ofs,
    wt_memval ((init_block lo hi cur) # ofs).
Proof.
  intros.
  assert (A:=init_block_wt_memval lo hi cur).
  unfold ZMap.get in A.
  destruct (pos_ex_index ofs).
  rewrite <- H.
  apply A.
Qed.

Lemma zle_zlt:
  forall a b c,
    zle a b && zlt b c = true <->
    a <= b < c.
Proof.
  unfold zle, zlt.
  intros.
  destruct (Z_le_gt_dec a b).
  destruct (Z_lt_dec b c).
  intuition omega.
  split; try discriminate; try omega.
  split; try discriminate; try omega.
Qed.

Run the allocation algorithm on m plus an hypothetical new block.
Definition conc_mem_alloc (m: mem) (lo hi : Z) (alignment: nat) :
  option (block -> int) :=
  alloc_blocks
    (mk_block_list_aux
       (get_size
          (PMap.set (nextblock m) (Some (0, hi)) (mem_blocksize m)))
       (Pos.to_nat (nextblock m))).

Definition alloc_mem_dec:
  forall l ,
    {alloc_blocks l <> None} + {alloc_blocks l = None}.
Proof.
  unfold alloc_blocks. intros.
  destruct (alloc_mem_aux l).
  destruct (zlt z Int.max_unsigned).
  left; congruence.
  right; reflexivity.
Defined.

Definition conc_mem_alloc_dec (m:mem) (lo hi: Z) (alignment: nat) :
  {conc_mem_alloc m lo hi alignment <> None} +
  {conc_mem_alloc m lo hi alignment = None}.
Proof.
  unfold conc_mem_alloc.
  apply alloc_mem_dec.
Defined.

The allocation is now guarded by the success of conc_mem_alloc.
Program Definition low_alloc (m: mem) (lo hi: Z) : option (mem * block) :=
  let lo := 0 in
  let al := alignment_of_size (hi-lo) in
  if conc_mem_alloc_dec m lo hi al
  then
    Some (mkmem (PMap.set m.(nextblock)
                              (init_block lo hi m.(nextblock))
                              m.(mem_contents))
                (PMap.set m.(nextblock)
                              (fun ofs k => if zle lo ofs && zlt ofs hi then Some Freeable else None)
                              m.(mem_access))
                (PMap.set m.(nextblock)
                              (Some (0,hi))
                              m.(mem_blocksize))
                (PMap.set m.(nextblock)
                              (Some al)
                              m.(mem_mask))
                (Psucc m.(nextblock))
                _ _ _ _ _ _ _ _ _ _,
          m.(nextblock))
  else None.
Next Obligation.
  repeat rewrite PMap.gsspec. destruct (peq b (nextblock m)).
  subst b. destruct (zle 0 ofs && zlt ofs hi); red; auto with mem.
  apply access_max.
Qed.
Next Obligation.
  rewrite PMap.gsspec. destruct (peq b (nextblock m)).
  subst b. elim H0. apply Plt_succ.
  apply nextblock_noaccess. red; intros; elim H0.
  apply Plt_trans_succ; auto.
Qed.
Next Obligation.
  rewrite PMap.gsspec.
  destruct (peq b (nextblock m)).
  subst.
  apply init_block_wt_memval'.
  apply contents_wt_memval.
Qed.
Next Obligation.
  unfold get_bounds.
  repeat rewrite PMap.gsspec in *.
  destruct (peq b (nextblock m)); subst.
  unfold in_bound; simpl.
  destruct (zle 0 ofs && zlt ofs hi) eqn:?; try congruence.
  apply zle_zlt in Heqb. auto.
  eapply access_bounds_ok; eauto.
Qed.
Next Obligation.
  rewrite PMap.gsspec.
  destr. subst. xomega.
  apply bs_valid; auto. xomega.
Qed.
Next Obligation.
  rewrite PMap.gsspec.
  destr. subst. xomega.
  apply msk_valid; auto. xomega.
Qed.
Next Obligation.
  unfold conc_mem_alloc in H.
  auto. rewrite Pos2Nat.inj_succ.
  rewrite <- pred_Sn. auto.
Qed.
Next Obligation.
  unfold get_size.
  rewrite PMap.gsspec in *.
  des (peq b (nextblock m)).
  revert H0; apply alignment_ok.
Qed.
Next Obligation.
  rewrite ! PMap.gsspec in *.
  des (peq b (nextblock m)).
  apply bounds_mask_consistency.
Qed.
Next Obligation.
  rewrite ! PMap.gsspec in *.
  des (peq b (nextblock m)).
  apply bounds_lo_inf0 in H0; auto.
Qed.

Definition alloc (m: mem) (lo hi: Z) :=
  low_alloc m lo hi.

Lemma alloc_nat_mask:
  forall m lo hi m' b,
    Mem.alloc m lo hi = Some (m',b) ->
    Mem.nat_mask m' b = Int.not (Int.repr (two_power_nat (alignment_of_size hi) - 1)).
Proof.
  intros m lo hi m' b ALLOC.
  Transparent Mem.alloc Mem.low_alloc.
  unfold Mem.alloc, Mem.low_alloc in ALLOC.
  destruct (Mem.conc_mem_alloc_dec m 0 hi _) eqn:?; try discriminate.
  inv ALLOC.
  unfold Mem.nat_mask, Mem.mask. simpl.
  rewrite PMap.gss. rewrite Z.sub_0_r; auto.
Qed.

Lemma alloc_contents:
  forall m lo hi m' b,
    alloc m lo hi = Some (m',b) ->
    forall b', b <> b' ->
               (mem_contents m') !! b' = (mem_contents m) !! b'.
Proof.
  intros m lo hi m' b ALLOC b' DIFF.
  unfold Mem.alloc, Mem.low_alloc in ALLOC.
  destruct (Mem.conc_mem_alloc_dec m 0 hi _) eqn:?; try discriminate.
  inv ALLOC. simpl.
  rewrite PMap.gso; auto.
Qed.

Freeing a block between the given bounds.
We only free the block when lo and hi corresponds to the exact bounds of b.
Opaque zeq.
Program Definition unchecked_free (m: mem) (b: block) (lo hi: Z): mem :=
  mkmem m.(mem_contents)
            (PMap.set b
                      (fun ofs k => if zle lo ofs && zlt ofs hi then None else m.(mem_access)#b ofs k)
                      m.(mem_access))
            (PMap.set b (match m.(mem_blocksize)#b with
                             Some (l,h) => if zeq l lo && zeq h hi then
                                             None
                                           else m.(mem_blocksize)#b
                           | None => None
                         end) m.(mem_blocksize))
            (PMap.set b (match m.(mem_blocksize)#b with
                             Some (l,h) => if zeq l lo && zeq h hi
                                           then None
                                           else m.(mem_mask)#b
                           | None => None
                         end) m.(mem_mask))
            m.(nextblock) _ _ _ _ _ _ _ _ _ _.
Next Obligation.
  repeat rewrite PMap.gsspec. destruct (peq b0 b).
  destruct (zle lo ofs && zlt ofs hi). red; auto. apply access_max.
  apply access_max.
Qed.
Next Obligation.
  repeat rewrite PMap.gsspec. destruct (peq b0 b). subst.
  destruct (zle lo ofs && zlt ofs hi). auto. apply nextblock_noaccess; auto.
  apply nextblock_noaccess; auto.
Qed.
Next Obligation.
  apply contents_wt_memval.
Qed.
Next Obligation.
  unfold get_bounds in *.
  repeat rewrite PMap.gsspec in *. destruct (peq b0 b). subst.
  destruct (zle lo ofs && zlt ofs hi) eqn:?; try intuition congruence.
  - generalize H; intro H1.
    apply access_bounds_ok in H; auto.
    revert H; destr.
    des p.
    revert Heqo; destr. des p.
    revert Heqo0; destr. inv Heqo0.
    unfold in_bound, get_bounds in *. simpl in *.
    rewrite Heqo in H. simpl in *. auto.
    revert Heqo; destr. des p.
    revert Heqo0; destr. inv Heqo0.
    unfold in_bound, get_bounds in *. simpl in *.
    rewrite Heqo in H. simpl in *. auto.
    cut (z = lo /\ z0 = hi).
    intros [A B]; subst.
    apply zle_zlt in H. congruence.
    apply andb_true_iff in Heqb1.
    destruct Heqb1; split.
    destruct (zeq z lo); intuition try congruence.
    discriminate.
    destruct (zeq z0 hi); intuition try congruence.
    discriminate.
    unfold get_bounds, in_bound in H.
    rewrite Heqo in H. simpl in H; omega.
  - eapply access_bounds_ok; eauto.
Qed.
Next Obligation.
  rewrite PMap.gsspec.
  destr; subst; rewrite bs_valid; auto.
Qed.
Next Obligation.
  rewrite PMap.gsspec.
  destr; subst.
  rewrite bs_valid; auto.
  rewrite msk_valid; auto.
Qed.
Next Obligation.
  generalize (wfm_alloc_ok m).
  eapply (allocs_blocks_free) with (b:=b); intros; eauto.
  - unfold get_size. rewrite PMap.gso by congruence. auto.
  - unfold get_size, get_mask. rewrite ! PMap.gss.
    des (mem_blocksize m) # b. des p.
    destr.
Qed.
Next Obligation.
  rewrite PMap.gsspec in H.
  des (peq b0 b).
  des (mem_blocksize m) # b.
  des p.
  revert H; destr.
  eapply alignment_ok in H; eauto.
  subst.
  unfold get_size. rewrite PMap.gss. rewrite Heqo. auto.
  eapply alignment_ok in H; eauto.
  unfold get_size; rewrite PMap.gso; auto.
Qed.
Next Obligation.
  rewrite ! PMap.gsspec.
  des (peq b0 b).
  - des (mem_blocksize m) # b.
    des p.
    destr. rewrite <- Heqo.
    apply bounds_mask_consistency.
  - apply bounds_mask_consistency.
Qed.
Next Obligation.
  revert H.
  rewrite PMap.gsspec.
  des (peq b0 b).
  - des (mem_blocksize m) # b.
    des p. revert H; destr.
    inv H.
    apply bounds_lo_inf0 in Heqo; auto.
  - apply bounds_lo_inf0 in H; auto.
Qed.

Lemma unchecked_free_mask:
  forall m b lo hi b',
    mask (unchecked_free m b lo hi) b' =
    if eq_block b' b
    then match bounds_of_block m b with
             (lo',hi') =>
                            (if zeq lo' lo && zeq hi' hi
                             then O
                             else mask m b')
         end
    else mask m b'.
Proof.
  intros.
  Opaque zeq.
  unfold unchecked_free, mask, eq_block; simpl.
  rewrite PMap.gsspec.
  des (mem_blocksize m) # b;
    destruct (peq b' b); subst; destr; try des p;
    unfold bounds_of_block; try rewrite Heqo; destr;
    try rewrite Heqo0; auto.
  unfold bounds_of_block in Heqp; rewrite Heqo in Heqp. inv Heqp.
  generalize (bounds_mask_consistency m b). destr.
Qed.

Lemma unchecked_free_nat_mask:
  forall m b lo hi b',
    nat_mask (unchecked_free m b lo hi) b' =
    if eq_block b' b
    then match bounds_of_block m b with
             (lo',hi') => if zeq lo' lo && zeq hi' hi
                          then Int.mone
                          else nat_mask m b'
         end
    else nat_mask m b'.
Proof.
  intros.
  unfold nat_mask.
  rewrite unchecked_free_mask.
  Opaque two_power_nat.
  repeat destr.
  rewrite two_power_nat_O. change (Int.repr (1-1)) with Int.zero. apply Int.not_zero.
Qed.

Definition free (m: mem) (b: block) (lo hi: Z): option mem :=
  if range_perm_dec m b lo hi Cur Freeable
  then Some(unchecked_free m b lo hi)
  else None.

Fixpoint free_list (m: mem) (l: list (block * Z * Z)) {struct l}: option mem :=
  match l with
  | nil => Some m
  | (b, lo, hi) :: l' =>
      match free m b lo hi with
      | None => None
      | Some m' => free_list m' l'
      end
  end.

Lemma bounds_of_block_valid:
  forall m b lo hi,
    bounds_of_block m b = (lo,hi) ->
    lo <> hi ->
    valid_block m b.
Proof.
  intros.
  destruct (valid_block_dec m b); auto.
  unfold bounds_of_block in H.
  rewrite bs_valid in H; auto. inv H; congruence.
Qed.


Lemma size_mem_aux_sup1:
  forall m,
    Alloc.size_mem_aux m >= 8.
Proof.
  unfold Alloc.size_mem_aux.
  intros.
  apply Zle_ge.
  apply Alloc.size_mem_al_pos; omega.
Qed.

Lemma size_mem_sup1:
  forall m,
    Mem.size_mem m >= 8.
Proof.
  unfold Mem.size_mem, Mem.mk_block_list.
  intros. apply size_mem_aux_sup1.
Qed.


Memory reads.

load chunk m b ofs perform a read in memory state m, at address b and offset ofs. It returns the value of the memory chunk at that address. None is returned if the accessed bytes are not readable.

Definition load (chunk: memory_chunk) (m: mem) (b: block) (ofs: Z): option expr_sym :=
  if valid_access_dec m chunk b ofs Readable
  then Some (decode_val chunk (getN (size_chunk_nat chunk) ofs (m.(mem_contents)#b)))
  else None.

loadv chunk m addr is similar, but the address and offset are given as a single value addr, which must be a pointer value. We normalise the address beforehand.

Definition loadv (chunk: memory_chunk) (m: mem) (addr: expr_sym) : option expr_sym :=
  let v := mem_norm m addr Values_symbolictype.Ptr
  in match v with
         Vptr b ofs => load chunk m b (Int.unsigned ofs)
       | _ => None
     end.

loadbytes m b ofs n reads n consecutive bytes starting at location (b, ofs). Returns None if the accessed locations are not readable.

Definition loadbytes (m: mem) (b: block) (ofs n: Z): option (list memval) :=
  if range_perm_dec m b ofs (ofs + n) Cur Readable
  then Some (getN (nat_of_Z n) ofs (m.(mem_contents)#b))
  else None.

Memory stores.


store chunk m b ofs v perform a write in memory state m. Value v is stored at address b and offset ofs. Return the updated memory store, or None if the accessed bytes are not writable.

Definition optForall {A} P (l: option (list A)) :=
  match l with
      Some ll => Forall P ll
    | None => True
  end.

Lemma encode_val_wt':
  forall c v,
    optForall wt_memval (encode_val c v).
Proof.
  intros.
  unfold encode_val.
  destr. destr. subst.
  unfold inj_symbolic.
  apply Forall_rev. rewrite rev_involutive.
  apply Forall_rib.
  generalize (size_chunk_nat c); induction n; simpl; intros;
  constructor; auto.
  simpl.
  econstructor.
  rewrite to_bits_tc; eauto.
  des c; constructor.
Qed.


Lemma wt_memval_setN:
  forall l t ofs ofs0,
  Forall wt_memval l ->
  (forall o, wt_memval (ZMap.get o t)) ->
  wt_memval (ZMap.get ofs0 (setN l ofs t)).
Proof.
  induction l; simpl; intuition try congruence.
  - apply H0.
  - apply IHl.
    inv H; auto.
    intros.
    rewrite ZMap.gsspec.
    destr; subst.
    inv H; auto.
    apply H0.
Qed.

Definition is_encodable chunk v :=
  match encode_val chunk v with
      Some enc => true
    | None => false
  end.

Definition encode_val_tot chunk v :=
  match encode_val chunk v with
      Some enc => enc
    | _ => nil
  end.

Program Definition store (chunk: memory_chunk) (m: mem) (b: block) (ofs: Z) (v: expr_sym): option mem :=
  if valid_access_dec m chunk b ofs Writable then
    if is_encodable chunk v then
      Some (mkmem (PMap.set b
                            (setN (encode_val_tot chunk v) ofs (m.(mem_contents)#b))
                            m.(mem_contents))
                  m.(mem_access)
                      m.(mem_blocksize)
                          m.(mem_mask)
                              m.(nextblock)
                                  _ _ _ _ _ _ _ _ _ _)
    else None
  else
    None.
Next Obligation.
apply access_max. Qed.
Next Obligation.
apply nextblock_noaccess; auto. Qed.
Next Obligation.
  destruct (peq b b0).
  - subst.
    rewrite PMap.gss.
    apply wt_memval_setN.
    generalize (encode_val_wt' chunk v); auto.
    unfold encode_val_tot.
    destruct (encode_val chunk v); simpl in *; auto.
    apply contents_wt_memval.
  - rewrite PMap.gso; auto.
    apply contents_wt_memval.
Qed.
Next Obligation.
  apply access_bounds_ok in H0; auto.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl. eauto.
Qed.

storev chunk m addr v is similar, but the address and offset are given as a single value addr, which must be a pointer value. We normalise the address beforehand.

Definition storev (chunk: memory_chunk) (m: mem) (addr v: expr_sym) : option mem :=
  match mem_norm m addr Values_symbolictype.Ptr with
      Vptr b ofs => store chunk m b (Int.unsigned ofs) v
    | _ => None
  end.

Lemma check_bytes_correct:
  forall l,
    check_bytes l = true ->
    Forall wt_memval l.
Proof.
  induction l; simpl; intuition try congruence.
  - constructor.
  - constructor.
    destruct a; simpl; auto.
    apply andb_true_iff in H.
    destruct (tcheck_expr e) eqn:?; simpl in *; intuition try congruence.
    apply mk_lexpr with (l_styp:=s); auto. des s; constructor.
    des a. apply andb_true_iff in H.
    intuition eauto.
Qed.

storebytes m b ofs bytes stores the given list of bytes bytes starting at location (b, ofs). Returns updated memory state or None if the accessed locations are not writable.

Program Definition storebytes (m: mem) (b: block) (ofs: Z) (bytes: list memval) : option mem :=
  if bool_dec (check_bytes bytes) true then
    if range_perm_dec m b ofs (ofs + Z_of_nat (length bytes)) Cur Writable then
      Some (mkmem
              (PMap.set b (setN bytes ofs (m.(mem_contents)#b)) m.(mem_contents))
              m.(mem_access)
                  m.(mem_blocksize)
                      m.(mem_mask)
                          m.(nextblock)
                              _ _ _ _ _ _ _ _ _ _)
    else None
  else
    None.
Next Obligation.
apply access_max. Qed.
Next Obligation.
apply nextblock_noaccess; auto. Qed.
Next Obligation.
  rewrite PMap.gsspec. destruct (peq b0 b).
  subst.
  apply wt_memval_setN.
  apply check_bytes_correct; auto.
  intro; apply contents_wt_memval.
  apply contents_wt_memval.
Qed.
Next Obligation.
  apply access_bounds_ok in H1; auto.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl in *. eauto.
Qed.

drop_perm m b lo hi p sets the max permissions of the byte range (b, lo) ... (b, hi - 1) to p. These bytes must have current permissions Freeable in the initial memory state m. Returns updated memory state, or None if insufficient permissions.

Program Definition drop_perm (m: mem) (b: block) (lo hi: Z) (p: permission): option mem :=
  if range_perm_dec m b lo hi Cur Freeable then
    Some (mkmem m.(mem_contents)
                (PMap.set b
                        (fun ofs k => if zle lo ofs && zlt ofs hi then Some p else m.(mem_access)#b ofs k)
                        m.(mem_access))
                m.(mem_blocksize)
                m.(mem_mask)
                m.(nextblock) _ _ _ _ _ _ _ _ _ _)
  else None.
Next Obligation.
  repeat rewrite PMap.gsspec. destruct (peq b0 b). subst b0.
  destruct (zle lo ofs && zlt ofs hi). red; auto with mem. apply access_max.
  apply access_max.
Qed.
Next Obligation.
  specialize (nextblock_noaccess m b0 ofs k H0). intros.
  rewrite PMap.gsspec. destruct (peq b0 b). subst b0.
  destruct (zle lo ofs). destruct (zlt ofs hi).
  assert (perm m b ofs k Freeable). apply perm_cur. apply H; auto.
  unfold perm in H2. rewrite H1 in H2. contradiction.
  auto. auto. auto.
Qed.
Next Obligation.
  apply contents_wt_memval.
Qed.
Next Obligation.
  repeat rewrite PMap.gsspec in *.
  destruct (peq b0 b); subst; try
  apply access_bounds_ok in H0; auto.
  unfold range_perm in H.
  unfold perm, perm_order' in H.
  revert H0;destr;try
  apply access_bounds_ok in H0; auto.
  apply zle_zlt in Heqb0.
  specialize (H ofs Heqb0).
  revert H; destr.
  eapply access_bounds_ok with Cur. eauto. congruence.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl. auto.
Qed.
Next Obligation.
  destruct m. simpl in *. eauto.
Qed.


Properties of the memory operations


Properties of the empty store.

Theorem nextblock_empty: nextblock empty = 2%positive.
Proof.
reflexivity. Qed.

Theorem perm_empty: forall b ofs k p, ~perm empty b ofs k p.
Proof.
  intros. unfold perm, empty; simpl. rewrite PMap.gi. simpl. tauto.
Qed.

Theorem valid_access_empty: forall chunk b ofs p, ~valid_access empty chunk b ofs p.
Proof.
  intros. red; intros. elim (perm_empty b ofs Cur p). apply H.
  generalize (size_chunk_pos chunk); omega.
Qed.

Properties related to load


Lemma Forall_rib:
  forall {A} (P : A -> Prop) l,
    Forall P l ->
    Forall P (rev_if_be l).
Proof.
  unfold rev_if_be; destr_cond_match; intuition try congruence.
  apply Forall_rev.
  rewrite rev_involutive; auto.
Qed.

Lemma wt_memval_getN:
  forall n b ofs,
    (forall o, wt_memval (ZMap.get o b) ) ->
    Forall wt_memval (getN n ofs b).
Proof.
  induction n; simpl; auto.
Qed.

Theorem valid_access_load:
  forall m chunk b ofs,
  valid_access m chunk b ofs Readable ->
  exists v, load chunk m b ofs = Some v.
Proof.
  intros. unfold load.
  rewrite pred_dec_true; eauto.
Qed.

Theorem load_valid_access:
  forall m chunk b ofs v,
  load chunk m b ofs = Some v ->
  valid_access m chunk b ofs Readable.
Proof.
  intros until v. unfold load.
  destruct (valid_access_dec m chunk b ofs Readable); intros.
  auto.
  congruence.
Qed.

Lemma load_result:
  forall chunk m b ofs v,
  load chunk m b ofs = Some v ->
  v = decode_val chunk (getN (size_chunk_nat chunk) ofs (m.(mem_contents)#b)).
Proof.
  intros until v. unfold load.
  destruct (valid_access_dec m chunk b ofs Readable); intros.
  congruence.
  congruence.
Qed.

Hint Local Resolve load_valid_access valid_access_load: mem.

Theorem load_type:
  forall m chunk b ofs v,
  load chunk m b ofs = Some v ->
  Val.has_type v ((type_of_chunk chunk)).
Proof.
  intros. exploit load_result; eauto; intros.
  symmetry in H0.
  subst.
  unfold Val.has_type.
  solve_wt.
  rewrite decode_val_wt; auto.
  apply Forall_forall.
  generalize (contents_wt_memval m b ).
  generalize ((mem_contents m)#b).
  generalize ofs.
  generalize (size_chunk_nat chunk).
  clear; induction n; simpl; intros.
  intuition congruence.
  destruct H0; subst.
  apply H.
  apply IHn in H0; auto.
Qed.

Theorem load_cast:
  forall m chunk b ofs v,
  load chunk m b ofs = Some v ->
  match chunk with
  | Mint8signed => v == Val.sign_ext 8 v
  | Mint8unsigned => v == Val.zero_ext 8 v
  | Mint16signed => v == Val.sign_ext 16 v
  | Mint16unsigned => v == Val.zero_ext 16 v
  | _ => True
  end.
Proof.
  intros. exploit load_result; eauto.
  set (l := getN (size_chunk_nat chunk) ofs m.(mem_contents)#b).
  intro; subst.
  generalize (decode_val_cast chunk l).
  des chunk.
Qed.

Lemma ps_int8_signed_unsigned:
  forall l,
    proj_symbolic_le l Mint8signed =
    proj_symbolic_le l Mint8unsigned.
Proof.
  unfold proj_symbolic_le.
  intros; repeat destr_cond_match; intuition try congruence.
Qed.

Lemma ps_int16_signed_unsigned:
  forall l,
    proj_symbolic_le l Mint16signed =
    proj_symbolic_le l Mint16unsigned.
Proof.
  unfold proj_symbolic_le.
  intros; repeat destr_cond_match; intuition try congruence.
Qed.

Theorem load_int8_signed_unsigned:
  forall m b ofs v1 v2,
  load Mint8signed m b ofs = Some v1 ->
  option_map (Val.sign_ext 8) (load Mint8unsigned m b ofs) = Some v2 ->
  v1 == v2.
Proof.
  intros until v2. unfold load.
  change (size_chunk_nat Mint8signed) with (size_chunk_nat Mint8unsigned).
  set (cl := getN (size_chunk_nat Mint8unsigned) ofs m.(mem_contents)#b).
  destruct (valid_access_dec m Mint8signed b ofs Readable); try discriminate.
  rewrite pred_dec_true; auto. unfold decode_val. simpl.
  intros A B; inv A; inv B.
  symmetry; apply (normalise_sign_ext_zero_ext).
  omega.
Qed.

Theorem load_int8_signed_unsigned_none:
  forall m b ofs,
    load Mint8signed m b ofs = None ->
    load Mint8unsigned m b ofs = None.
Proof.
  intros m b ofs.
  unfold load.
  destr.
  rewrite pred_dec_false; auto.
Qed.

Theorem load_int16_signed_unsigned:
  forall m b ofs v1 v2,
  load Mint16signed m b ofs = Some v1 ->
  option_map (Val.sign_ext 16) (load Mint16unsigned m b ofs) = Some v2 ->
  v1 == v2.
Proof.
  intros until v2. unfold load.
  change (size_chunk_nat Mint16signed) with (size_chunk_nat Mint16unsigned).
  set (cl := getN (size_chunk_nat Mint16unsigned) ofs m.(mem_contents)#b).
  destruct (valid_access_dec m Mint16signed b ofs Readable).
  - rewrite pred_dec_true; auto. unfold decode_val.
    intros A B; inv A; inv B.
    symmetry; apply (normalise_sign_ext_zero_ext ).
    omega.
  - rewrite pred_dec_false; auto. destr.
Qed.

Theorem load_int16_signed_unsigned_none:
  forall m b ofs,
    load Mint16signed m b ofs = None ->
    load Mint16unsigned m b ofs = None.
Proof.
  intros m b ofs.
  unfold load.
  destr. rewrite pred_dec_false; auto.
Qed.

Properties related to loadbytes


Theorem range_perm_loadbytes:
  forall m b ofs len,
  range_perm m b ofs (ofs + len) Cur Readable ->
  exists bytes, loadbytes m b ofs len = Some bytes.
Proof.
  intros. econstructor. unfold loadbytes. rewrite pred_dec_true; eauto.
Qed.

Theorem loadbytes_range_perm:
  forall m b ofs len bytes,
  loadbytes m b ofs len = Some bytes ->
  range_perm m b ofs (ofs + len) Cur Readable.
Proof.
  intros until bytes. unfold loadbytes.
  destruct (range_perm_dec m b ofs (ofs + len) Cur Readable). auto. congruence.
Qed.

Theorem loadbytes_load:
  forall chunk m b ofs bytes,
  loadbytes m b ofs (size_chunk chunk) = Some bytes ->
  (align_chunk chunk | ofs) ->
  load chunk m b ofs = Some (decode_val chunk bytes).
Proof.
  unfold loadbytes, load; intros.
  destruct (range_perm_dec m b ofs (ofs + size_chunk chunk) Cur Readable);
  try congruence.
  inv H. rewrite pred_dec_true. auto.
  split; auto.
Qed.

Theorem load_loadbytes:
  forall chunk m b ofs v,
  load chunk m b ofs = Some v ->
  exists bytes, loadbytes m b ofs (size_chunk chunk) = Some bytes
             /\ v = decode_val chunk bytes.
Proof.
  intros. exploit load_valid_access; eauto. intros [A B].
  exploit load_result; eauto. intros.
  exists (getN (size_chunk_nat chunk) ofs m.(mem_contents)#b); split.
  unfold loadbytes. rewrite pred_dec_true; auto.
  auto.
Qed.

Lemma getN_length:
  forall c n p, length (getN n p c) = n.
Proof.
  induction n; simpl; intros. auto. decEq; auto.
Qed.

Theorem loadbytes_length:
  forall m b ofs n bytes,
  loadbytes m b ofs n = Some bytes ->
  length bytes = nat_of_Z n.
Proof.
  unfold loadbytes; intros.
  destruct (range_perm_dec m b ofs (ofs + n) Cur Readable); try congruence.
  inv H. apply getN_length.
Qed.

Theorem loadbytes_empty:
  forall m b ofs n,
  n <= 0 -> loadbytes m b ofs n = Some nil.
Proof.
  intros. unfold loadbytes. rewrite pred_dec_true. rewrite nat_of_Z_neg; auto.
  red; intros. omegaContradiction.
Qed.
  
Lemma getN_concat:
  forall c n1 n2 p,
  getN (n1 + n2)%nat p c = getN n1 p c ++ getN n2 (p + Z_of_nat n1) c.
Proof.
  induction n1; intros.
  simpl. decEq. omega.
  rewrite inj_S. simpl. decEq.
  replace (p + Zsucc (Z_of_nat n1)) with ((p + 1) + Z_of_nat n1) by omega.
  auto.
Qed.

Theorem loadbytes_concat:
  forall m b ofs n1 n2 bytes1 bytes2,
  loadbytes m b ofs n1 = Some bytes1 ->
  loadbytes m b (ofs + n1) n2 = Some bytes2 ->
  n1 >= 0 -> n2 >= 0 ->
  loadbytes m b ofs (n1 + n2) = Some(bytes1 ++ bytes2).
Proof.
  unfold loadbytes; intros.
  destruct (range_perm_dec m b ofs (ofs + n1) Cur Readable); try congruence.
  destruct (range_perm_dec m b (ofs + n1) (ofs + n1 + n2) Cur Readable); try congruence.
  rewrite pred_dec_true. rewrite nat_of_Z_plus; auto.
  rewrite getN_concat. rewrite nat_of_Z_eq; auto.
  congruence.
  red; intros.
  assert (ofs0 < ofs + n1 \/ ofs0 >= ofs + n1) by omega.
  destruct H4. apply r; omega. apply r0; omega.
Qed.

Theorem loadbytes_split:
  forall m b ofs n1 n2 bytes,
  loadbytes m b ofs (n1 + n2) = Some bytes ->
  n1 >= 0 -> n2 >= 0 ->
  exists bytes1, exists bytes2,
     loadbytes m b ofs n1 = Some bytes1
  /\ loadbytes m b (ofs + n1) n2 = Some bytes2
  /\ bytes = bytes1 ++ bytes2.
Proof.
  unfold loadbytes; intros.
  destruct (range_perm_dec m b ofs (ofs + (n1 + n2)) Cur Readable);
  try congruence.
  rewrite nat_of_Z_plus in H; auto. rewrite getN_concat in H.
  rewrite nat_of_Z_eq in H; auto.
  repeat rewrite pred_dec_true.
  econstructor; econstructor.
  split. reflexivity. split. reflexivity. congruence.
  red; intros; apply r; omega.
  red; intros; apply r; omega.
Qed.

Lemma Forall_dec:
  forall {A} (P: A -> Prop)
         (Pdec: forall a, P a \/ ~ P a)
         l,
    Forall P l \/ ~ Forall P l.
Proof.
  induction l; simpl; intros.
  left; constructor.
  destruct (Pdec a); destruct IHl; intuition try congruence.
  left; constructor; auto.
  right; intro C; inv C; congruence.
  right; intro C; inv C; congruence.
  right; intro C; inv C; congruence.
Qed.

Lemma wt_memval_dec:
  forall a,
    wt_memval a \/ ~ wt_memval a.
Proof.
  intros.
  des a.
  des (tcheck_expr e).
  des (is_lval_typ_dec s).
  left; econstructor; eauto.
  right; intro A; inv A; destr.
  right; intro A; inv A; destr.
Qed.

Lemma Forall_app_l:
  forall {A} (P: A -> Prop) l1 l2,
    Forall P (l1 ++ l2) ->
    Forall P l1.
Proof.
  induction l1; try constructor.
  change ((a::l1)++ l2) with (a:: (l1 ++ l2)) in H.
  inv H; auto.
  change ((a::l1)++ l2) with (a:: (l1 ++ l2)) in H.
  inv H; auto.
  apply IHl1 with (l2:=l2); auto.
Qed.

Lemma Forall_rev':
  forall {A} (P: A -> Prop) l,
    Forall P l ->
    Forall P (rev l).
Proof.
  intros; apply Forall_rev.
  rewrite rev_involutive; auto.
Qed.

Lemma Forall_app_r:
  forall {A} (P: A -> Prop) l1 l2,
    Forall P (l1 ++ l2) ->
    Forall P l2.
Proof.
  intros.
  apply Forall_rev' in H.
  rewrite rev_app_distr in H.
  apply Forall_app_l in H.
  apply Forall_rev; auto.
Qed.

Lemma loadbytes_wt_memval:
  forall m b ofs n bytes,
    loadbytes m b ofs n = Some bytes ->
    Forall wt_memval bytes.
Proof.
  unfold loadbytes.
  intros.
  revert H.
  destr_no_dep.
  inv H.
  generalize (contents_wt_memval m b ).
  generalize ((mem_contents m)#b).
  generalize ofs.
  generalize (nat_of_Z n).
  clear; induction n; simpl; intros.
  constructor.
  constructor; eauto.
Qed.

Theorem load_int64_split:
  forall m b ofs v,
  load Mint64 m b ofs = Some v ->
  exists v1 v2,
     load Mint32 m b ofs = Some (if Archi.big_endian then v1 else v2)
  /\ load Mint32 m b (ofs + 4) = Some (if Archi.big_endian then v2 else v1)
  /\ v == Val.longofwords v1 v2.
Proof.
  Opaque decode_val.
  intros.
  exploit load_valid_access; eauto. intros [A B]. simpl in *.
  exploit load_loadbytes. eexact H. simpl. intros [bytes [LB EQ]].
  change 8 with (4 + 4) in LB.
  exploit loadbytes_split. eexact LB. omega. omega.
  intros (bytes1 & bytes2 & LB1 & LB2 & APP).
  change 4 with (size_chunk Mint32) in LB1.
  exploit loadbytes_load. eexact LB1.
  simpl. apply Zdivides_trans with 8; auto. exists 2; auto.
  intros L1.
  change 4 with (size_chunk Mint32) in LB2.
  exploit loadbytes_load. eexact LB2.
  simpl. apply Zdivide_plus_r. apply Zdivides_trans with 8; auto. exists 2; auto. exists 1; auto.
  intros L2.
  rewrite L1.
  simpl in L2.
  rewrite L2.
  rewrite APP in EQ.
  exists (if Archi.big_endian then decode_val Mint32 bytes1 else decode_val Mint32 bytes2).
  exists (if Archi.big_endian then decode_val Mint32 bytes2 else decode_val Mint32 bytes1).
  split. destruct Archi.big_endian; auto.
  split. destruct Archi.big_endian; auto. subst.
  generalize (loadbytes_wt_memval _ _ _ _ _ LB1);
  generalize (loadbytes_wt_memval _ _ _ _ _ LB2);
  generalize (loadbytes_wt_memval _ _ _ _ _ LB).
  intros.
  generalize (decode_val_wt Mint32 _ H1).
  generalize (decode_val_wt Mint32 _ H2).
  generalize (decode_val_wt Mint64 _ H0).
  intros.
  rewrite decode_val_int64'; auto.
  intro; intros; apply norm_eq'; simpl; auto.
  des big_endian; rewrite H4; rewrite H5; simpl; auto.
  des (Archi.big_endian). rewrite be_rew in Heqb0.
  congruence.
  rewrite (loadbytes_length m b ofs (size_chunk Mint32)); auto.
  rewrite (loadbytes_length m b (ofs+size_chunk Mint32) (size_chunk Mint32)); auto.
Qed.

Properties related to store

 
Theorem valid_access_store:
  forall m1 chunk b ofs v,
  valid_access m1 chunk b ofs Writable ->
  tcheck_expr v = Some ((type_of_chunk chunk)) ->
  { m2: mem | store chunk m1 b ofs v = Some m2 }.
Proof.
  intros.
  unfold store.
  destruct (valid_access_dec m1 chunk b ofs Writable); [|contradiction].
  destruct ( (is_encodable chunk v) ) eqn:?.
  eauto.
  unfold is_encodable in Heqb0.
  revert Heqb0; destr_cond_match; try congruence.
  unfold encode_val in Heqo.
  rewrite H0 in Heqo.
  rewrite styp_eq_refl in Heqo.
  destruct v; destruct s; destruct chunk; simpl in *; intuition try congruence.
Defined.

Hint Local Resolve valid_access_store: mem.
 
Section STORE.
Variable chunk: memory_chunk.
Variable m1: mem.
Variable b: block.
Variable ofs: Z.
Variable v: expr_sym.
Variable m2: mem.
Hypothesis STORE: store chunk m1 b ofs v = Some m2.
 
Lemma store_access: mem_access m2 = mem_access m1.
Proof.
  unfold store in STORE.
  destruct ( valid_access_dec m1 chunk b ofs Writable); inv STORE.
  destruct ( (is_encodable chunk v) ); inv H0.
  auto.
Qed.

Lemma store_mem_contents:
  mem_contents m2 = PMap.set b (setN (encode_val_tot chunk v) ofs m1.(mem_contents)#b) m1.(mem_contents).
Proof.
  unfold store in STORE. destruct (valid_access_dec m1 chunk b ofs Writable); inv STORE.
  destruct (is_encodable chunk v); inv H0.
  auto.
Qed.


Lemma store_encode_some:
  encode_val chunk v <> None.
Proof.
  unfold store in STORE; revert STORE.
  repeat destr_cond_match; try congruence.
  intro A; inv A.
  revert Heqb0; unfold is_encodable; destr_cond_match; intuition congruence.
Qed.


Lemma encode_val_tot_length:
  length (encode_val_tot chunk v) = size_chunk_nat chunk.
Proof.
  generalize (store_encode_some).
  unfold encode_val_tot.
  destruct (encode_val chunk v) eqn:?; simpl in *; intuition try congruence.
  eapply encode_val_length; eauto.
Qed.

Theorem perm_store_1:
  forall b' ofs' k p, perm m1 b' ofs' k p -> perm m2 b' ofs' k p.
Proof.
  intros.
 unfold perm in *. rewrite store_access; auto.
Qed.

Theorem perm_store_2:
  forall b' ofs' k p, perm m2 b' ofs' k p -> perm m1 b' ofs' k p.
Proof.
  intros. unfold perm in *. rewrite store_access in H; auto.
Qed.

Local Hint Resolve perm_store_1 perm_store_2: mem.

Theorem size_of_block_store:
  forall b',
    size_of_block m1 b' = size_of_block m2 b'.
Proof.
  intros.
  revert STORE.
  unfold store.
  destr_cond_match; try congruence.
  destr_cond_match; try congruence.
  intro A; inv A.
  unfold size_of_block; simpl.
  reflexivity.
  destr_cond_match; congruence.
Qed.

Theorem mask_store:
  forall b',
    mask m1 b' = mask m2 b'.
Proof.
  intros.
  revert STORE.
  unfold store.
  destr_cond_match; try congruence.
  destr_cond_match; try congruence.
  intro A; inv A; unfold mask; reflexivity.
  destr_cond_match; congruence.
Qed.

Theorem bounds_of_block_store:
  forall b',
    bounds_of_block m1 b' = bounds_of_block m2 b'.
Proof.
  intros; revert STORE; unfold store; repeat (destr_cond_match; try congruence).
  intro A; inv A; reflexivity.
Qed.

Theorem nat_mask_store:
  forall b',
    nat_mask m1 b' = nat_mask m2 b'.
Proof.
  intros; revert STORE; unfold store; repeat destr_cond_match; try congruence.
  intro A; inv A; reflexivity.
Qed.


Theorem nextblock_store:
  nextblock m2 = nextblock m1.
Proof.
  intros.
  unfold store in STORE. destruct ( valid_access_dec m1 chunk b ofs Writable); inv STORE.
  destruct (is_encodable chunk v); inv H0.
  auto.
Qed.

Theorem store_valid_block_1:
  forall b', valid_block m1 b' -> valid_block m2 b'.
Proof.
  unfold valid_block; intros. rewrite nextblock_store; auto.
Qed.

Theorem store_valid_block_2:
  forall b', valid_block m2 b' -> valid_block m1 b'.
Proof.
  unfold valid_block; intros. rewrite nextblock_store in H; auto.
Qed.

Local Hint Resolve store_valid_block_1 store_valid_block_2: mem.

Theorem store_valid_access_1:
  forall chunk' b' ofs' p,
  valid_access m1 chunk' b' ofs' p -> valid_access m2 chunk' b' ofs' p.
Proof.
  intros. inv H. constructor; try red; auto with mem.
Qed.

Theorem store_valid_access_2:
  forall chunk' b' ofs' p,
  valid_access m2 chunk' b' ofs' p -> valid_access m1 chunk' b' ofs' p.
Proof.
  intros. inv H. constructor; try red; auto with mem.
Qed.

Theorem store_valid_access_3:
  valid_access m1 chunk b ofs Writable.
Proof.
  unfold store in STORE. destruct (valid_access_dec m1 chunk b ofs Writable).
  auto.
  congruence.
Qed.

Local Hint Resolve store_valid_access_1 store_valid_access_2 store_valid_access_3: mem.

Lemma tcheck_expr_store_chunk:
  tcheck_expr v = Some ((type_of_chunk chunk)).
Proof.
  revert STORE; unfold store.
  repeat destr_cond_match; try congruence.
  intro A; inv A.
  unfold is_encodable in Heqb0.
  apply encode_val_wt.
  destruct (encode_val chunk v); simpl in *; congruence.
Qed.

Theorem load_store_similar:
  forall chunk',
  size_chunk chunk' = size_chunk chunk ->
  align_chunk chunk' <= align_chunk chunk ->
  exists v', load chunk' m2 b ofs = Some v' /\ decode_encode_expr_sym v chunk chunk' v'.
Proof.
  intros.
  destruct (valid_access_load m2 chunk' b ofs).
  eapply valid_access_compat. symmetry; eauto. auto. eauto with mem.
  exists x; split; auto.
  exploit load_result; eauto. intros B.
  rewrite store_mem_contents in B; simpl in B.
  rewrite PMap.gss in B.
  replace (size_chunk_nat chunk') with (length (encode_val_tot chunk v)) in B.
  rewrite getN_setN_same in B.
  subst.
  unfold encode_val_tot in*.
  generalize (store_encode_some).
  destr.
  apply decode_encode_val_general with (v_enc:=l); auto.
  unfold encode_val_tot.
  generalize (store_encode_some). destr.
  apply encode_val_length in Heqo. rewrite Heqo. repeat rewrite size_chunk_conv in H.
  apply inj_eq_rev; auto.
Qed.

Lemma store_some_type:
  tcheck_expr v = Some (type_of_chunk chunk).
Proof.
  unfold store in STORE.
  repeat (revert STORE; destr).
  clear STORE Heqs v0.
  unfold is_encodable in Heqb0.
  apply encode_val_wt.
  destruct (encode_val chunk v); simpl in *; congruence.
Qed.

Theorem load_store_similar_2:
  forall chunk' v1,
  size_chunk chunk' = size_chunk chunk ->
  align_chunk chunk' <= align_chunk chunk ->
  type_of_chunk chunk' = type_of_chunk chunk ->
  load chunk' m2 b ofs = Some v1 ->
  chunk' <> Many32 /\ chunk' <> Many64 ->
  v1 == Val.load_result_expr chunk' v.
Proof.
  intros. destruct (load_store_similar chunk') as [v' [A B]]; auto.
  rewrite A in H2; inv H2.
  assert (WT:=store_some_type).
  unfold Val.load_result_expr.
  des chunk; des chunk';
  try (rewrite Val.sign_ext_norm; auto; fail);
  try (rewrite Val.zero_ext_norm; auto; fail).
  - intro; intros. specialize (B resType sz msk); rewrite B.
    apply norm_eq'; simpl; try rewrite WT; simpl; auto; try tauto.
    intros; simpl_eval'.
    rewrite Int.sign_ext_above by (vm_compute; intuition congruence); auto.
  - intro; intros. specialize (B resType sz msk); rewrite B.
    apply norm_eq'; simpl; try rewrite WT; simpl; auto; try tauto.
    intros; simpl_eval'.
    rewrite Int64.sign_ext_above by (vm_compute; intuition congruence); auto.
  - intro; intros. specialize (B resType sz msk); rewrite B.
    apply norm_eq'; simpl; try rewrite WT; simpl; auto; try tauto.
    intros; simpl_eval'. simpl.
    generalize (eSexpr_wt alloc0 em eb eu v Tsingle _ eq_refl).
    des (eSexpr alloc0 em eb eu Tsingle v).
  - intro; intros. specialize (B resType sz msk); rewrite B.
    apply norm_eq'; simpl; try rewrite WT; simpl; auto; try tauto.
    intros; simpl_eval'. simpl.
    inv_expr_type (eSexpr alloc0 em eb eu Tfloat v).
  - rewrite WT in B; auto. symmetry; auto.
  - omega.
Qed.

Theorem load_store_same:
  exists v1, load chunk m2 b ofs = Some v1 /\
             v1 == (Val.load_result chunk v).
Proof.
  intros.
  assert (Val.has_type v (styp_of_typ (AST.type_of_chunk chunk))).
  unfold Val.has_type. apply tcheck_expr_correct.
  rewrite tcheck_expr_store_chunk. des chunk.
  exploit load_store_similar; eauto. omega.
  intros [v' [A B]]. rewrite A. exists v'; split; auto.
  rewrite Val.load_result_eq_expr'; auto.
  assert (Val.has_type v' (type_of_chunk chunk)). eapply load_type; eauto.
  unfold Val.has_type in *.
  solve_wt.
  des chunk.
   - intro; intros. specialize (B resType sz msk); rewrite B.
    apply norm_eq'; simpl; auto; try rewrite H0; try rewrite H; simpl; auto; try tauto.
    intros; simpl_eval'.
    rewrite Int.sign_ext_above by (vm_compute; intuition congruence); auto.
   - intro; intros. specialize (B resType sz msk); rewrite B.
    apply norm_eq'; simpl; auto; try rewrite H0; try rewrite H; simpl; auto; try tauto.
    intros; simpl_eval'.
    rewrite Int64.sign_ext_above by (vm_compute; intuition congruence); auto.
   - intro; intros. specialize (B resType sz msk); rewrite B.
    apply norm_eq'; simpl; auto; try rewrite H0; try rewrite H; simpl; auto; try tauto.
    intros; simpl_eval'.
    simpl.
    generalize (eSexpr_wt alloc0 em eb eu v Tsingle _ eq_refl).
    des (eSexpr alloc0 em eb eu Tsingle v).
  - intro; intros. specialize (B resType sz msk); rewrite B.
    apply norm_eq'; simpl; auto; try rewrite H0; try rewrite H; simpl; auto; try tauto.
    intros; simpl_eval'. simpl.
    inv_expr_type (eSexpr alloc0 em eb eu Tfloat v).
  - rewrite H in B; auto. symmetry; auto.
Qed.

Theorem load_store_other:
  forall chunk' b' ofs',
  b' <> b
  \/ ofs' + size_chunk chunk' <= ofs
  \/ ofs + size_chunk chunk <= ofs' ->
  load chunk' m2 b' ofs' = load chunk' m1 b' ofs'.
Proof.
  intros. unfold load.
  destruct (valid_access_dec m1 chunk' b' ofs' Readable).
  rewrite pred_dec_true.
  decEq. rewrite store_mem_contents; simpl; auto.
  rewrite PMap.gsspec. destruct (peq b' b); auto. subst b'. f_equal.
  apply getN_setN_outside. rewrite encode_val_tot_length. repeat rewrite <- size_chunk_conv.
  intuition.
  auto.
  eauto with mem.
  rewrite pred_dec_false. auto.
  eauto with mem.
Qed.

Theorem loadbytes_store_same:
  loadbytes m2 b ofs (size_chunk chunk) = encode_val chunk v.
Proof.
 intros.
  assert (valid_access m2 chunk b ofs Readable) by eauto with mem.
  unfold loadbytes. rewrite pred_dec_true. rewrite store_mem_contents; simpl.
  rewrite PMap.gss.
  replace (nat_of_Z (size_chunk chunk)) with (length (encode_val_tot chunk v)).
  rewrite getN_setN_same. generalize (store_encode_some); unfold encode_val_tot; destruct (encode_val chunk v) eqn:?; simpl; intuition try congruence.
  rewrite encode_val_tot_length. auto.
  apply H.
Qed.

Theorem loadbytes_store_other:
  forall b' ofs' n,
  b' <> b
  \/ n <= 0
  \/ ofs' + n <= ofs
  \/ ofs + size_chunk chunk <= ofs' ->
  loadbytes m2 b' ofs' n = loadbytes m1 b' ofs' n.
Proof.
   intros. unfold loadbytes.
  destruct (range_perm_dec m1 b' ofs' (ofs' + n) Cur Readable).
  rewrite pred_dec_true.
  decEq. rewrite store_mem_contents; simpl.
  rewrite PMap.gsspec. destruct (peq b' b). subst b'.
  destruct H. congruence.
  destruct (zle n 0) as [z | n0].
  rewrite (nat_of_Z_neg _ z). auto.
  destruct H. omegaContradiction.
  apply getN_setN_outside. rewrite encode_val_tot_length. rewrite <- size_chunk_conv.
  rewrite nat_of_Z_eq. auto. omega.
  auto.
  red; intros. eauto with mem.
  rewrite pred_dec_false. auto.
  red; intro; elim n0; red; intros; eauto with mem.
Qed.

Lemma setN_property:
  forall (P: memval -> Prop) vl p q c,
  (forall v, In v vl -> P v) ->
  p <= q < p + Z_of_nat (length vl) ->
  P(ZMap.get q (setN vl p c)).
Proof.
  induction vl; intros.
  simpl in H0. omegaContradiction.
  simpl length in H0. rewrite inj_S in H0. simpl.
  destruct (zeq p q). subst q. rewrite setN_outside. rewrite ZMap.gss.
  auto with coqlib. omega.
  apply IHvl. auto with coqlib. omega.
Qed.

Lemma getN_in:
  forall c q n p,
  p <= q < p + Z_of_nat n ->
  In (ZMap.get q c) (getN n p c).
Proof.
  induction n; intros.
  simpl in H; omegaContradiction.
  rewrite inj_S in H. simpl. destruct (zeq p q).
  subst q. auto.
  right. apply IHn. omega.
Qed.

Theorem load_pointer_store:
  forall chunk' b' ofs' v_b v_o,
    load chunk' m2 b' ofs' = Some(Eval (Eptr v_b v_o)) ->
    (chunk = Mint32 /\ v = Eval (Eptr v_b v_o) /\ chunk' = Mint32 /\ b' = b /\ ofs' = ofs)
    \/ (b' <> b \/ ofs' + size_chunk chunk' <= ofs \/ ofs + size_chunk chunk <= ofs').
Proof.
  intros.
  assert (True) by (destruct m1; auto).
  contradict H.
  unfold load.
  destr_cond_match; try congruence.
  Transparent decode_val.
  des chunk'.
Qed.

End STORE.

Local Hint Resolve perm_store_1 perm_store_2: mem.
Local Hint Resolve store_valid_block_1 store_valid_block_2: mem.
Local Hint Resolve store_valid_access_1 store_valid_access_2
             store_valid_access_3: mem.



Lemma is_encodable_eq:
  forall v1 v2 chunk1 chunk2 ,
    encode_val chunk1 v1 = encode_val chunk2 v2 ->
    align_chunk chunk1 = align_chunk chunk2 ->
    size_chunk chunk1 = size_chunk chunk2 ->
    is_encodable chunk1 v1 = is_encodable chunk2 v2.
Proof.
  intros; unfold is_encodable.
  rewrite H.
  destr_cond_match; auto.
Qed.

Lemma store_similar_chunks:
  forall chunk1 chunk2 v1 v2 m b ofs,
  encode_val chunk1 v1 = encode_val chunk2 v2 ->
  align_chunk chunk1 = align_chunk chunk2 ->
  store chunk1 m b ofs v1 = store chunk2 m b ofs v2.
Proof.
  intros. unfold store.
  destruct (encode_val chunk1 v1) eqn:?;
           destruct (encode_val chunk2 v2) eqn:?;
           simpl in *; intuition try congruence.
  inv H; simpl in *.
  assert (size_chunk chunk1 = size_chunk chunk2).
    repeat rewrite size_chunk_conv.
    erewrite <- (encode_val_length chunk1 v1); eauto.
    erewrite <- (encode_val_length chunk2 v2); eauto.
  destruct (valid_access_dec m chunk1 b ofs Writable);
  destruct (valid_access_dec m chunk2 b ofs Writable); auto.
  erewrite is_encodable_eq with (v2:=v2) (chunk2:=chunk2); intuition try congruence.
  destruct ( (is_encodable chunk2 v2)); auto.
  erewrite (mkmem_ext); eauto.
  unfold encode_val_tot; rewrite Heqo; rewrite Heqo0; auto.
  elim n. apply valid_access_compat with chunk1; auto. omega.
  elim n. apply valid_access_compat with chunk2; auto. omega.
  destruct (valid_access_dec m chunk1 b ofs Writable);
  destruct (valid_access_dec m chunk2 b ofs Writable); auto.
  unfold is_encodable; rewrite Heqo; rewrite Heqo0; auto.
  unfold is_encodable; rewrite Heqo; auto.
  unfold is_encodable; rewrite Heqo0; auto.
Qed.

Theorem store_signed_unsigned_8:
  forall m b ofs v,
  store Mint8signed m b ofs v = store Mint8unsigned m b ofs v.
Proof.
  intros.
  apply store_similar_chunks.
  apply encode_val_int8_signed_unsigned.
  simpl. omega.
Qed.

Theorem store_signed_unsigned_16:
  forall m b ofs v,
  store Mint16signed m b ofs v = store Mint16unsigned m b ofs v.
Proof.
  intros.
  apply store_similar_chunks.
  apply encode_val_int16_signed_unsigned.
  simpl. omega.
Qed.

Definition memval_equiv_typ (e1 e2: memval) :=
  match tcheck_memval e1, tcheck_memval e2 with
      Some t1, Some t2 =>
      if styp_eq t1 t2
      then forall alloc em,
             eSexpr alloc em eb eu t1 (expr_of_memval e1 (ltyp_of_typ t1)) =
             eSexpr alloc em eb eu t2 (expr_of_memval e2 (ltyp_of_typ t2))
      else False
    | _,_ => False
  end.

Record mem_equiv (m1 m2: mem) : Prop :=
  mk_mem_equiv {
      mem_contents_eq:
        forall b ofs,
          memval_equiv_typ (ZMap.get ofs ((mem_contents m1) # b))
                           (ZMap.get ofs ((mem_contents m2) # b));
      mem_access_eq:
        (mem_access m1) = (mem_access m2);
      mem_blocksize_eq:
        (mem_blocksize m1) = (mem_blocksize m2);
      mem_mask_eq:
        (mem_mask m1) = (mem_mask m2);
      nextblock_eq:
        (nextblock m1) = (nextblock m2) }.

Definition opt_mem_equiv (m1 m2: option mem) : Prop :=
  match m1, m2 with
      None, None => True
    | Some mm1 , Some mm2 => mem_equiv mm1 mm2
    | _ , _ => False
  end.

Lemma memval_equiv_typ_refl:
  forall ofs0 m b,
    memval_equiv_typ (ZMap.get ofs0 (mem_contents m) # b)
                     (ZMap.get ofs0 (mem_contents m) # b).
Proof.
  unfold memval_equiv_typ; intros.
  destr.
  rewrite styp_eq_refl; intros; auto.
  generalize (contents_wt_memval m b ofs0).
  unfold wt_memval; destr.
  inv H; congruence.
Qed.

Lemma get_setN_not_last:
  forall al ofs0 ofs o a t,
    ofs0 <> ofs ->
    (ZMap.get ofs0 (setN al o (ZMap.set ofs a t))) = ZMap.get ofs0 (setN al o t).
Proof.
  induction al; simpl; intros.
  rewrite ZMap.gso; auto.
  destruct (Z_eq_dec o ofs); subst.
  rewrite ZMap.set2. auto.
  destruct (Z_eq_dec o ofs0); subst.
  repeat rewrite setN_outside by omega.
  repeat rewrite ZMap.gss. auto.
  repeat rewrite IHal; auto.
Qed.

Lemma met_eq:
  forall i1 i2,
    list_forall2 memval_equiv_typ i1 i2 ->
    forall m b ofs b0 ofs0,
    memval_equiv_typ
      (ZMap.get ofs0 ((PMap.set b (setN i1 ofs (mem_contents m) # b) (mem_contents m)) # b0))
      (ZMap.get ofs0 ((PMap.set b (setN i2 ofs (mem_contents m) # b) (mem_contents m)) # b0)).
Proof.
  intros.
  repeat rewrite PMap.gsspec.
  destr; subst.
  assert ((ofs0 < ofs \/ ofs0 >= ofs + Z.of_nat (length i1)) \/
          ofs <= ofs0 < ofs + Z.of_nat (length i1)).
  intuition omega.
  destruct H0.
  rewrite setN_outside; auto.
  rewrite setN_outside.
  apply memval_equiv_typ_refl.
  rewrite <- (list_forall2_length H). auto.
  generalize (contents_wt_memval m b).
  generalize (mem_contents m) # b.
  revert i1 i2 H ofs ofs0 H0.
  clear. induction 1; simpl; intros.
  - unfold memval_equiv_typ; destr.
    rewrite styp_eq_refl; intros; auto.
    specialize (H ofs0). unfold wt_memval in H.
    revert H; destr. inv H. congruence.
  - destruct (Z_eq_dec ofs0 ofs); subst.
    + repeat rewrite setN_outside by omega.
      repeat rewrite ZMap.gss. auto.
    + repeat rewrite get_setN_not_last; auto; try omega.
      apply IHlist_forall2. zify; omega.
      auto.
  - apply memval_equiv_typ_refl.
Qed.

Lemma memval_equiv_tint_typ:
  forall i1 i2,
    Forall (fun x => tcheck_memval x = Some Tint) i1 ->
    Forall (fun x => tcheck_memval x = Some Tint) i2 ->
    (list_forall2 memval_equiv_tint i1 i2 <->
    list_forall2 memval_equiv_typ i1 i2).
Proof.
  split; induction 1; simpl; intros; try constructor; auto.
  inv H; inv H0.
  unfold memval_equiv_typ. unfold memval_equiv_tint in H1.
  rewrite H5. rewrite H4. simpl. auto.
  apply IHlist_forall2; eauto.
  inv H; inv H0; auto.
  inv H; inv H0; auto.
  unfold memval_equiv_typ in H1. unfold memval_equiv_tint.
  inv H; inv H0.
  rewrite H5 in H1. rewrite H4 in H1. simpl in H1. auto.
  apply IHlist_forall2; eauto.
  inv H; inv H0; auto.
  inv H; inv H0; auto.
Qed.

Theorem store_int8_zero_ext:
  forall m b ofs n,
    opt_mem_equiv
      (store Mint8unsigned m b ofs (Eval (Eint (Int.zero_ext 8 n))))
      (store Mint8unsigned m b ofs (Eval (Eint n))).
Proof.
  intros.
  unfold store.
  unfold is_encodable. simpl.
  destr. simpl.
  generalize encode_val_int8_zero_ext.
  unfold encode_val_tot. simpl.
  intros; apply met_eq.
  unfold inj_symbolic.
  apply list_forall2_rev.
  apply list_forall2_rib.
  simpl.
  apply memval_equiv_tint_typ.
  constructor; simpl; auto.
  constructor; simpl; auto.
  specialize (H n).
  unfold inj_symbolic in H.
  apply list_forall2_rev in H.
  repeat rewrite rev_involutive in H.
  apply list_forall2_rib in H.
  repeat rewrite rev_if_be_involutive in H.
  simpl in H. apply H; auto.
Qed.

Theorem store_int8_sign_ext:
  forall m b ofs n,
    opt_mem_equiv
      (store Mint8signed m b ofs (Eval (Eint (Int.sign_ext 8 n))))
      (store Mint8signed m b ofs (Eval (Eint n))).
Proof.
  intros.
  unfold store.
  unfold is_encodable. simpl.
  destr. simpl.
  generalize encode_val_int8_sign_ext.
  unfold encode_val_tot. simpl.
  intros; apply met_eq.
  unfold inj_symbolic.
  apply list_forall2_rev.
  apply list_forall2_rib.
  simpl.
  apply memval_equiv_tint_typ.
  constructor; simpl; auto.
  constructor; simpl; auto.
  specialize (H n).
  unfold inj_symbolic in H.
  apply list_forall2_rev in H.
  repeat rewrite rev_involutive in H.
  apply list_forall2_rib in H.
  repeat rewrite rev_if_be_involutive in H.
  simpl in H. apply H; auto.
Qed.

Theorem store_int16_zero_ext:
  forall m b ofs n,
    opt_mem_equiv
      (store Mint16unsigned m b ofs (Eval (Eint (Int.zero_ext 16 n))))
      (store Mint16unsigned m b ofs (Eval (Eint n))).
Proof.
  intros.
  unfold store.
  unfold is_encodable. simpl.
  destr. simpl.
  generalize encode_val_int16_zero_ext.
  unfold encode_val_tot. simpl.
  intros; apply met_eq.
  unfold inj_symbolic.
  apply list_forall2_rev.
  apply list_forall2_rib.
  simpl.
  apply memval_equiv_tint_typ.
  constructor; simpl; auto.
  constructor; simpl; auto.
  specialize (H n).
  unfold inj_symbolic in H.
  apply list_forall2_rev in H.
  repeat rewrite rev_involutive in H.
  apply list_forall2_rib in H.
  repeat rewrite rev_if_be_involutive in H.
  simpl in H. apply H; auto.
Qed.

Theorem store_int16_sign_ext:
  forall m b ofs n,
    opt_mem_equiv
      (store Mint16signed m b ofs (Eval (Eint (Int.sign_ext 16 n))))
      (store Mint16signed m b ofs (Eval (Eint n))).
Proof.
  intros.
  unfold store.
  unfold is_encodable. simpl.
  destr. simpl.
  generalize encode_val_int16_sign_ext.
  unfold encode_val_tot. simpl.
  intros; apply met_eq.
  unfold inj_symbolic.
  apply list_forall2_rev.
  apply list_forall2_rib.
  simpl.
  apply memval_equiv_tint_typ.
  constructor; simpl; auto.
  constructor; simpl; auto.
  specialize (H n).
  unfold inj_symbolic in H.
  apply list_forall2_rev in H.
  repeat rewrite rev_involutive in H.
  apply list_forall2_rib in H.
  repeat rewrite rev_if_be_involutive in H.
  simpl in H. apply H; auto.
Qed.

Properties related to storebytes.


Theorem range_perm_storebytes:
  forall m1 b ofs bytes,
  range_perm m1 b ofs (ofs + Z_of_nat (length bytes)) Cur Writable ->
  check_bytes bytes = true ->
  { m2 : mem | storebytes m1 b ofs bytes = Some m2 }.
Proof.
  intros. unfold storebytes.
  destruct (range_perm_dec m1 b ofs (ofs + Z_of_nat (length bytes)) Cur Writable).
  destruct (bool_dec (check_bytes bytes) true).
  econstructor; reflexivity.
  contradiction.
  contradiction.
Defined.

Lemma check_bytes_complete:
  forall l : list memval, Forall wt_memval l -> check_bytes l = true.
Proof.
  induction l; simpl; intuition try congruence.
  inv H.
  specialize (IHl H3).
  rewrite IHl.
  destruct a; simpl in *; intuition try congruence.
  destruct H2 as [s TE IT].
  rewrite TE.
  inv IT; ring.
Qed.

Theorem storebytes_store:
  forall m1 b ofs chunk v m2 enc,
    encode_val chunk v = Some enc ->
  storebytes m1 b ofs enc = Some m2 ->
  (align_chunk chunk | ofs) ->
  store chunk m1 b ofs v = Some m2.
Proof.
  unfold storebytes, store. intros.
  destruct (range_perm_dec m1 b ofs (ofs + Z_of_nat (length enc)) Cur Writable); inv H;
  destruct (bool_dec (check_bytes enc) true); inv H0.
  destruct (valid_access_dec m1 chunk b ofs Writable).
  destruct (is_encodable chunk v) eqn:?.
  - erewrite mkmem_ext; eauto.
    unfold encode_val_tot; rewrite H3; auto.
  - unfold is_encodable in Heqb0; rewrite H3 in Heqb0; congruence.
  - elim n. constructor; auto.
    erewrite encode_val_length in r; eauto. rewrite size_chunk_conv. auto.
Qed.

Lemma check_bytes_encode:
  forall chunk v l,
  encode_val chunk v = Some l ->
  check_bytes l = true.
Proof.
  intros.
  generalize (encode_val_wt' chunk v).
  rewrite H; simpl.
  apply check_bytes_complete.
Qed.

Theorem store_storebytes:
  forall m1 b ofs chunk v m2 enc,
  store chunk m1 b ofs v = Some m2 ->
  encode_val chunk v = Some enc ->
  storebytes m1 b ofs enc = Some m2.
Proof.
  unfold storebytes, store. intros.
  destruct (valid_access_dec m1 chunk b ofs Writable); inv H.
  destruct (range_perm_dec m1 b ofs (ofs + Z_of_nat (length enc)) Cur Writable).
  destruct (bool_dec (check_bytes enc) true).
  unfold is_encodable; rewrite H0.
  erewrite mkmem_ext; eauto.
  unfold encode_val_tot; rewrite H0; simpl; auto.
  apply check_bytes_encode in H0; congruence.
  unfold is_encodable in *; rewrite H0 in *; simpl in *.
  inv H2.
  destruct v0. elim n.
  erewrite encode_val_length;eauto. rewrite <- size_chunk_conv. auto.
Qed.
  
Section STOREBYTES.
Variable m1: mem.
Variable b: block.
Variable ofs: Z.
Variable bytes: list memval.
Variable m2: mem.
Hypothesis STORE: storebytes m1 b ofs bytes = Some m2.

Lemma storebytes_access: mem_access m2 = mem_access m1.
Proof.
  unfold storebytes in STORE.
  destruct (bool_dec (check_bytes bytes) true); try congruence.
  destruct (range_perm_dec m1 b ofs (ofs + Z_of_nat (length bytes)) Cur Writable);

  inv STORE.
  auto.
Qed.

Lemma storebytes_mem_contents:
   mem_contents m2 = PMap.set b (setN bytes ofs m1.(mem_contents)#b) m1.(mem_contents).
Proof.
  unfold storebytes in STORE.
  destruct (bool_dec (check_bytes bytes) true); try congruence.
  destruct (range_perm_dec m1 b ofs (ofs + Z_of_nat (length bytes)) Cur Writable);
  inv STORE.
  auto.
Qed.

Theorem perm_storebytes_1:
  forall b' ofs' k p, perm m1 b' ofs' k p -> perm m2 b' ofs' k p.
Proof.
  intros. unfold perm in *. rewrite storebytes_access; auto.
Qed.

Theorem perm_storebytes_2:
  forall b' ofs' k p, perm m2 b' ofs' k p -> perm m1 b' ofs' k p.
Proof.
  intros. unfold perm in *. rewrite storebytes_access in H; auto.
Qed.

Local Hint Resolve perm_storebytes_1 perm_storebytes_2: mem.

Theorem storebytes_valid_access_1:
  forall chunk' b' ofs' p,
  valid_access m1 chunk' b' ofs' p -> valid_access m2 chunk' b' ofs' p.
Proof.
  intros. inv H. constructor; try red; auto with mem.
Qed.

Theorem storebytes_valid_access_2:
  forall chunk' b' ofs' p,
  valid_access m2 chunk' b' ofs' p -> valid_access m1 chunk' b' ofs' p.
Proof.
  intros. inv H. constructor; try red; auto with mem.
Qed.

Local Hint Resolve storebytes_valid_access_1 storebytes_valid_access_2: mem.

Theorem size_of_block_storebytes:
  forall b',
    size_of_block m1 b' = size_of_block m2 b'.
Proof.
  intros; revert STORE.
  unfold storebytes; repeat destr_cond_match; try congruence.
  intro A; inv A; unfold size_of_block; reflexivity.
Qed.

Theorem mask_storebytes:
  forall b',
    mask m1 b' = mask m2 b'.
Proof.
  intros; revert STORE.
  unfold storebytes; repeat destr_cond_match; try congruence.
  intro A; inv A; unfold mask; reflexivity.
Qed.

Theorem bounds_of_block_storebytes:
  forall b',
    bounds_of_block m1 b' = bounds_of_block m2 b'.
Proof.
  intros; revert STORE; unfold storebytes; repeat destr_cond_match; try congruence.
  intro A; inv A; reflexivity.
Qed.

Theorem nat_mask_storebytes:
  forall b',
    nat_mask m1 b' = nat_mask m2 b'.
Proof.
  intros; revert STORE; unfold storebytes; repeat destr_cond_match; try congruence.
  intro A; inv A; reflexivity.
Qed.

Theorem nextblock_storebytes:
  nextblock m2 = nextblock m1.
Proof.
  intros.
  unfold storebytes in STORE.
  destruct (bool_dec (check_bytes bytes) true); try congruence.
  destruct (range_perm_dec m1 b ofs (ofs + Z_of_nat (length bytes)) Cur Writable);
  inv STORE.
  auto.
Qed.

Theorem storebytes_valid_block_1:
  forall b', valid_block m1 b' -> valid_block m2 b'.
Proof.
  unfold valid_block; intros. rewrite nextblock_storebytes; auto.
Qed.

Theorem storebytes_valid_block_2:
  forall b', valid_block m2 b' -> valid_block m1 b'.
Proof.
  unfold valid_block; intros. rewrite nextblock_storebytes in H; auto.
Qed.

Local Hint Resolve storebytes_valid_block_1 storebytes_valid_block_2: mem.

Theorem storebytes_range_perm:
  range_perm m1 b ofs (ofs + Z_of_nat (length bytes)) Cur Writable.
Proof.
  intros.
  unfold storebytes in STORE.
  destruct (bool_dec (check_bytes bytes) true); try congruence.
  destruct (range_perm_dec m1 b ofs (ofs + Z_of_nat (length bytes)) Cur Writable);
  inv STORE.
  auto.
Qed.

Theorem loadbytes_storebytes_same:
  loadbytes m2 b ofs (Z_of_nat (length bytes)) = Some bytes.
Proof.
  intros. unfold storebytes in STORE. unfold loadbytes.
  destruct (bool_dec (check_bytes bytes) true); try congruence.
  destruct (range_perm_dec m1 b ofs (ofs + Z_of_nat (length bytes)) Cur Writable);
  try discriminate.
  rewrite pred_dec_true.
  decEq. inv STORE; simpl. rewrite PMap.gss. rewrite nat_of_Z_of_nat.
  apply getN_setN_same.
  red; eauto with mem.
Qed.

Theorem loadbytes_storebytes_disjoint:
  forall b' ofs' len,
  len >= 0 ->
  b' <> b \/ Intv.disjoint (ofs', ofs' + len) (ofs, ofs + Z_of_nat (length bytes)) ->
  loadbytes m2 b' ofs' len = loadbytes m1 b' ofs' len.
Proof.
  intros. unfold loadbytes.
  destruct (range_perm_dec m1 b' ofs' (ofs' + len) Cur Readable).
  rewrite pred_dec_true.
  rewrite storebytes_mem_contents. decEq.
  rewrite PMap.gsspec. destruct (peq b' b). subst b'.
  apply getN_setN_disjoint. rewrite nat_of_Z_eq; auto. intuition congruence.
  auto.
  red; auto with mem.
  apply pred_dec_false.
  red; intros; elim n. red; auto with mem.
Qed.

Theorem loadbytes_storebytes_other:
  forall b' ofs' len,
  len >= 0 ->
  b' <> b
  \/ ofs' + len <= ofs
  \/ ofs + Z_of_nat (length bytes) <= ofs' ->
  loadbytes m2 b' ofs' len = loadbytes m1 b' ofs' len.
Proof.
  intros. apply loadbytes_storebytes_disjoint; auto.
  destruct H0; auto. right. apply Intv.disjoint_range; auto.
Qed.

Theorem load_storebytes_other:
  forall chunk b' ofs',
  b' <> b
  \/ ofs' + size_chunk chunk <= ofs
  \/ ofs + Z_of_nat (length bytes) <= ofs' ->
  load chunk m2 b' ofs' = load chunk m1 b' ofs'.
Proof.
  intros. unfold load.
  destruct (valid_access_dec m1 chunk b' ofs' Readable).
  rewrite pred_dec_true.
  rewrite storebytes_mem_contents. decEq.
  rewrite PMap.gsspec. destruct (peq b' b). subst b'.
  rewrite getN_setN_outside. auto. rewrite <- size_chunk_conv. intuition congruence.
  auto.
  destruct v; split; auto. red; auto with mem.
  apply pred_dec_false.
  red; intros; elim n. destruct H0. split; auto. red; auto with mem.
Qed.

End STOREBYTES.

Lemma setN_concat:
  forall bytes1 bytes2 ofs c,
  setN (bytes1 ++ bytes2) ofs c = setN bytes2 (ofs + Z_of_nat (length bytes1)) (setN bytes1 ofs c).
Proof.
  induction bytes1; intros.
  simpl. decEq. omega.
  simpl length. rewrite inj_S. simpl. rewrite IHbytes1. decEq. omega.
Qed.

Lemma check_bytes_Forall:
  forall b,
  check_bytes b = true <-> Forall wt_memval b.
Proof.
  intros; split.
  apply check_bytes_correct.
  apply check_bytes_complete.
Qed.

Lemma check_bytes_false_Forall:
  forall b,
  check_bytes b = false <-> ~ Forall wt_memval b.
Proof.
  intros; split; intros.
  destruct (Forall_dec wt_memval wt_memval_dec b); auto.
  apply check_bytes_complete in H0; congruence.
  destruct (check_bytes b) eqn:?; auto.
  apply check_bytes_correct in Heqb0; congruence.
Qed.

Lemma Forall_app_not:
  forall {A} P (a b:list A),
  (~ Forall P a) \/ (~ Forall P b) ->
  ~ Forall P (a++b).
Proof.
  intros.
  destruct H.
  intro.
  apply Forall_app_l in H0; congruence.
  intro.
  apply Forall_app_r in H0; congruence.
Qed.

Lemma check_bytes_append:
  forall a b,
    check_bytes (a ++ b) = check_bytes a && check_bytes b.
Proof.
  intros.
  destruct (check_bytes a) eqn:?;
           destruct (check_bytes b) eqn:?;
           simpl.
  - generalize (check_bytes_correct a Heqb0).
    generalize (check_bytes_correct b Heqb1).
    intros.
    apply check_bytes_complete.
    apply Forall_app; auto.
  - apply check_bytes_false_Forall.
    apply Forall_app_not.
    right.
    apply check_bytes_false_Forall; auto.
  - apply check_bytes_false_Forall.
    apply Forall_app_not.
    left.
    apply check_bytes_false_Forall; auto.
  - apply check_bytes_false_Forall.
    apply Forall_app_not.
    left.
    apply check_bytes_false_Forall; auto.
Qed.

Theorem storebytes_concat:
  forall m b ofs bytes1 m1 bytes2 m2,
  storebytes m b ofs bytes1 = Some m1 ->
  storebytes m1 b (ofs + Z_of_nat(length bytes1)) bytes2 = Some m2 ->
  storebytes m b ofs (bytes1 ++ bytes2) = Some m2.
Proof.
  intros. generalize H; intro ST1. generalize H0; intro ST2.
  unfold storebytes; unfold storebytes in ST1; unfold storebytes in ST2.
  destruct (bool_dec (check_bytes bytes2) true);
  destruct (bool_dec (check_bytes bytes1) true); try congruence.
  destruct (range_perm_dec m b ofs (ofs + Z_of_nat(length bytes1)) Cur Writable); try congruence.
  destruct (range_perm_dec m1 b (ofs + Z_of_nat(length bytes1)) (ofs + Z_of_nat(length bytes1) + Z_of_nat(length bytes2)) Cur Writable); try congruence.
  destruct (range_perm_dec m b ofs (ofs + Z_of_nat (length (bytes1 ++ bytes2))) Cur Writable).
  destr_cond_match.
  inv ST1; inv ST2; simpl.
  erewrite mkmem_ext; eauto.
  simpl.
  rewrite PMap.gss. rewrite setN_concat. symmetry. apply PMap.set2.
  clear Heqs. rewrite check_bytes_append in n.
  rewrite e in n.
  rewrite e0 in n.
  contradict n; ring.
  elim n.
  rewrite app_length. rewrite inj_plus. red; intros.
  destruct (zlt ofs0 (ofs + Z_of_nat(length bytes1))).
  apply r. omega.
  eapply perm_storebytes_2; eauto. apply r0. omega.
Qed.

Theorem storebytes_check_bytes:
  forall m b ofs bytes m2,
    storebytes m b ofs bytes = Some m2 ->
    check_bytes bytes = true.
Proof.
  intros; unfold storebytes in H.
  destruct (bool_dec (check_bytes bytes) true); try congruence.
Qed.

Theorem storebytes_split:
  forall m b ofs bytes1 bytes2 m2,
  storebytes m b ofs (bytes1 ++ bytes2) = Some m2 ->
  exists m1,
     storebytes m b ofs bytes1 = Some m1
  /\ storebytes m1 b (ofs + Z_of_nat(length bytes1)) bytes2 = Some m2.
Proof.
  intros.
  destruct (range_perm_storebytes m b ofs bytes1) as [m1 ST1].
  red; intros. exploit storebytes_range_perm; eauto. rewrite app_length.
  rewrite inj_plus. omega.
  apply storebytes_check_bytes in H.
  rewrite check_bytes_append in H.
  destruct (check_bytes bytes1); auto; contradict H; ring.
  destruct (range_perm_storebytes m1 b (ofs + Z_of_nat (length bytes1)) bytes2) as [m2' ST2].
  red; intros. eapply perm_storebytes_1; eauto. exploit storebytes_range_perm.
  eexact H. instantiate (1 := ofs0). rewrite app_length. rewrite inj_plus. omega.
  auto.
  apply storebytes_check_bytes in H.
  apply storebytes_check_bytes in ST1.
  rewrite check_bytes_append in H.
  destruct (check_bytes bytes2); auto; contradict H. rewrite ST1; simpl. congruence.
  assert (Some m2 = Some m2').
  rewrite <- H. eapply storebytes_concat; eauto.
  inv H0.
  exists m1; split; auto.
Qed.

Definition mem_eq_modulo_normalise (m1 m2:mem) :=
  forall chunk b ofs v1 v2,
    load chunk m1 b ofs = Some v1 ->
    load chunk m2 b ofs = Some v2 ->
    v1 == v2.

Notation "a ==m b" := (mem_eq_modulo_normalise a b) (at level 80).

Lemma load_align_chunk:
  forall m b ofs chunk v,
    load chunk m b ofs = Some v ->
    (align_chunk chunk | ofs).
Proof.
  intros until v.
  unfold load.
  destr_cond_match; try congruence.
  destruct v0.
  intros; auto.
Qed.

Lemma loadbytes_one:
  forall m b o n bytes,
    loadbytes m b o n = Some bytes ->
    n > 0 ->
    exists b1 b2,
      loadbytes m b o 1 = Some b1 /\
      loadbytes m b (o+1) (n-1) = Some b2 /\
      bytes = b1 ++ b2.
Proof.
  intros.
  generalize (loadbytes_range_perm _ _ _ _ _ H); intro r.
  assert (r1 : range_perm m b o (o + 1) Cur Readable).
  {
    unfold range_perm in *.
    intros.
    apply r; omega.
  }
  assert (r2 : range_perm m b (o + 1) (o + n) Cur Readable).
  {
    unfold range_perm in *.
    intros.
    apply r; omega.
  }
  generalize (range_perm_loadbytes m b o 1 r1).
  generalize (range_perm_loadbytes m b (o+1) (n-1)).
  replace (o+1 +(n-1)) with (o+n) by omega.
  intros A B; specialize (A r2).
  destruct A; destruct B.
  rewrite H2; rewrite H1.
  repeat eexists; repeat split; eauto.
  replace n with (1 + (n - 1)) in H by omega.
  erewrite loadbytes_concat in H; eauto.
  inv H; auto.
  omega.
  omega.
Qed.

Lemma expr_of_memval_zero_ext_eq:
  forall a1 a2 alloc0 i i0 em,
    wt_memval a1 -> wt_memval a2 ->
    tcheck_expr (expr_of_memval a2 Lint) = Some Tint ->
    tcheck_expr (expr_of_memval a1 Lint) = Some Tint ->
    eSexpr alloc0 em eb eu Tint (expr_of_memval a1 Lint) = Vi i ->
    eSexpr alloc0 em eb eu Tint (expr_of_memval a2 Lint) = Vi i0 ->
    Int.zero_ext 8 i = Int.zero_ext 8 i0 ->
    i = i0.
Proof.
  intros.
  apply Int.same_bits_eq; intros.
  apply (f_equal (fun x => Int.testbit x i1)) in H5.
  do 2 rewrite Int.bits_zero_ext in H5 by omega.
  destruct (zlt i1 8); auto.
  des a1; des a2.
  inv H. inv H0.
  inv l_ilt.
  - unfold cast in H3, H4.
    rewrite stes_wt in * by (econstructor; eauto).
    rewrite stes_wt in H3 by (econstructor; simpl; eauto; constructor).
    inv_expr_type (eSexpr alloc0 em eb eu Tint e).
    erewrite stes_rew_int in H3; eauto. inv H3.
    rewrite get_nth_part_int_bnds; auto.
    inv l_ilt0.
    + generalize (eSexpr_wt alloc0 em eb eu e0 Tint _ eq_refl).
      des (eSexpr alloc0 em eb eu Tint e0).
      erewrite stes_rew_int in H4; eauto. inv H4.
      rewrite get_nth_part_int_bnds; auto.
    + generalize (eSexpr_wt alloc0 em eb eu e0 Tlong _ eq_refl).
      des (eSexpr alloc0 em eb eu Tlong e0).
      unfold symb_to_expr_symb in H4.
      rewrite l_wt0 in H4. simpl in H4.
      unfold cast in H4.
      rewrite gnp_wt in H4; simpl in *; auto.
      generalize (eSexpr_wt alloc0 em eb eu (get_nth_part e0 n0 Llong) Tlong _ eq_refl).
      des (eSexpr alloc0 em eb eu Tlong (get_nth_part e0 n0 Llong)).
      simpl_eval'. rewrite Heql1; simpl.
      intro A; inv A.
      erewrite gnp_rew_long in Heql1; eauto. inv Heql1.
      rewrite Int64.bits_loword; auto.
      rewrite get_nth_part_long_bnds. auto. omega. rewrite int64_zwordsize. rewrite int_zwordsize in H8. omega.
  - unfold cast in H3, H4.
    rewrite stes_wt in * by (econstructor; eauto).
    rewrite stes_wt in H3 by (econstructor; simpl; eauto; constructor).
    inv_expr_type (eSexpr alloc0 em eb eu Tlong e).
    unfold symb_to_expr_symb in H3.
    rewrite l_wt in H3. simpl in H3.
    unfold cast in H3.
    rewrite gnp_wt in H3; simpl in *; auto.
    generalize (eSexpr_wt alloc0 em eb eu (get_nth_part e n Llong) Tlong _ eq_refl).
    des (eSexpr alloc0 em eb eu Tlong (get_nth_part e n Llong)).
    simpl_eval'. rewrite Heql0; simpl.
    intro A; inv A.
    erewrite gnp_rew_long in Heql0; eauto. inv Heql0.
    rewrite Int64.bits_loword; auto.
    rewrite get_nth_part_long_bnds by (try rewrite int64_zwordsize; try rewrite int_zwordsize in *; omega).
    inv l_ilt0.
    + generalize (eSexpr_wt alloc0 em eb eu e0 Tint _ eq_refl).
      des (eSexpr alloc0 em eb eu Tint e0).
      erewrite stes_rew_int in H4; eauto. inv H4.
      rewrite get_nth_part_int_bnds; auto.
    + generalize (eSexpr_wt alloc0 em eb eu e0 Tlong _ eq_refl).
      des (eSexpr alloc0 em eb eu Tlong e0).
      unfold symb_to_expr_symb in H4.
      rewrite l_wt0 in H4. simpl in H4.
      unfold cast in H4.
      rewrite gnp_wt in H4; simpl in *; auto.
      generalize (eSexpr_wt alloc0 em eb eu (get_nth_part e0 n0 Llong) Tlong _ eq_refl).
      des (eSexpr alloc0 em eb eu Tlong (get_nth_part e0 n0 Llong)).
      simpl_eval'. rewrite Heql1; simpl.
      intro A; inv A.
      erewrite gnp_rew_long in Heql1; eauto. inv Heql1.
      rewrite Int64.bits_loword; auto.
      rewrite get_nth_part_long_bnds. auto. omega. rewrite int64_zwordsize. rewrite int_zwordsize in H8. omega.
Qed.

Lemma decode_val_spec':
  forall l l',
    Forall wt_memval l ->
    Forall wt_memval l' ->
    list_forall2 memval_equiv_tint l l' ->
      forall c alloc em,
        eSexpr alloc em eb eu ((type_of_chunk c)) (decode_val c l) =
        eSexpr alloc em eb eu ((type_of_chunk c)) (decode_val c l').
Proof.
  intros.
  eapply decode_val_spec; eauto.
  apply list_forall2_rib.
  apply list_forall2_memval_equiv_int_eq; auto.
Qed.

Transparent decode_val.
Lemma mem_normalise_byte :
  forall m1 m2,
    (forall b ofs v1 v2,
       load Mint8unsigned m1 b ofs = Some v1 ->
       load Mint8unsigned m2 b ofs = Some v2 ->
       forall alloc em t,
         eSexpr alloc em eb eu t v1 =
         eSexpr alloc em eb eu t v2) ->
     (forall chunk b ofs v1 v2,
       load chunk m1 b ofs = Some v1 ->
       load chunk m2 b ofs = Some v2 ->
       forall alloc em,
         eSexpr alloc em eb eu ((type_of_chunk chunk)) v1 =
         eSexpr alloc em eb eu ((type_of_chunk chunk)) v2).
Proof.
  intros.
  destruct (load_loadbytes _ _ _ _ _ H0) as [bytes1 [LB1 DV1]].
  destruct (load_loadbytes _ _ _ _ _ H1) as [bytes2 [LB2 DV2]].
  generalize (loadbytes_wt_memval _ _ _ _ _ LB1);
  generalize (loadbytes_wt_memval _ _ _ _ _ LB2); intros.
  cut (list_forall2 memval_equiv_tint bytes1 bytes2).
  - clear LB1 LB2 H0 H1 H.
    symmetry in DV1; symmetry in DV2.
    intros. subst.
    apply (decode_val_spec' bytes1 bytes2) ; auto.
  - assert (size_chunk chunk > 0).
    des chunk; omega.
    revert LB1 LB2.
    revert H4.
    generalize (size_chunk chunk).
    destruct z; simpl; intuition try congruence; try zify; try omega.
    rewrite <- positive_nat_Z in *.
    revert LB1 LB2 H6 H4.
    generalize (Pos.to_nat p).
    intros. clear H0 H1. subst. clear H4.
    revert LB1 LB2 H2 H3 H6.
    revert n b ofs bytes1 bytes2 .
    induction n; simpl; intuition try omega.
    apply loadbytes_one in LB1; auto; try omega.
    apply loadbytes_one in LB2; auto; try omega.
    destruct LB1 as [b11 [b12 [LB11 [LB12 APP1]]]].
    destruct LB2 as [b21 [b22 [LB21 [LB22 APP2]]]].
    subst.
  assert (lb_one_list: forall m b o b11, loadbytes m b o 1 = Some b11 -> exists a1, b11 = a1 :: nil).
  {
    clear; intros.
    assert (length b11 = S O).
    erewrite loadbytes_length; simpl; eauto.
    vm_compute; auto.
    destruct b11; simpl in *; intuition try congruence.
    destruct b11; simpl in *; intuition try congruence.
    exists m0; auto.
  }
  destruct (lb_one_list _ _ _ _ LB11) as [a1 EQ1]; subst.
  destruct (lb_one_list _ _ _ _ LB21) as [a2 EQ2]; subst.
  simpl. constructor.
    + change 1 with (size_chunk Mint8unsigned) in LB11. apply loadbytes_load in LB11; auto.
      change 1 with (size_chunk Mint8unsigned) in LB21. apply loadbytes_load in LB21; auto.
      generalize (H _ _ _ _ LB11 LB21). simpl.
      intros.
      unfold memval_equiv_tint.
      intros.
      specialize (H0 alloc1 em Tint).
      clear - H0 H2 H3.
      simpl_eval'.
      unfold proj_symbolic_le in *.
      rewrite rev_if_be_one in Heql, Heql0. simpl in *.
      simpl_eval'.
      rewrite shl_zero_l in *.
      rewrite ! Int.add_zero in *.
      apply proof_irr_int in H5.
      inv H2; inv H3.
      unfold cast in Heql, Heql0.
      rewrite expr_of_memval_wt in *; auto. destr.
      rewrite Heql. rewrite Heql0.
      rewrite (expr_of_memval_zero_ext_eq a1 a2 alloc1 i1 i em); auto.
      rewrite expr_of_memval_wt; simpl; auto.
      rewrite expr_of_memval_wt; simpl; auto.
      simpl. apply Z.divide_1_l.
      simpl. apply Z.divide_1_l.
  + replace (Z.pos (Pos.of_succ_nat n) - 1) with (Z.of_nat n) in *.
    destruct (zlt 0 (Z.of_nat n)).
    * eapply IHn; eauto. inv H2; auto. inv H3; auto.
    * assert (n = O).
      {
        destruct n; auto.
        simpl in g.
        contradict g.
        zify; omega.
      }
      subst.
      rewrite loadbytes_empty in LB12, LB22 by omega.
      inv LB12; inv LB22.
      constructor.
    * generalize n; clear; induction n.
      simpl; auto.
      rewrite Nat2Z.inj_succ.
      rewrite IHn.
      rewrite Zminus_succ_l.
      f_equal.
      simpl.
      f_equal.
      apply Pos.add_1_r.
Qed.

Lemma bytewise_eq_all :
  forall m1 m2
         ,
    (forall b ofs v1 v2,
       load Mint8unsigned m1 b ofs = Some v1 ->
       load Mint8unsigned m2 b ofs = Some v2 ->
       forall alloc em t,
         eSexpr alloc em eb eu t v1 =
         eSexpr alloc em eb eu t v2) ->
    m1 ==m m2.
Proof.
  intros.
  - intro; intros.
    intro; intros.
    generalize (mem_normalise_byte m1 m2 H _ _ _ _ _ H0 H1).
    generalize (load_type _ _ _ _ _ H0).
    generalize (load_type _ _ _ _ _ H1).
    unfold Val.has_type in *.
    intros. destr_wt; solve_wt.
    apply norm_eq'. intuition try congruence.
    rewrite e. intros; auto.
    rewrite e in H3; inv H3.
    rewrite H7 in *. auto.
    apply norm_eq_not_wt; auto.
    intro; solve_wt.
    contradict n; solve_wt.
    congruence.
Qed.

Lemma eSexpr_eq_int_eq_all:
  forall x1 x2 alloc em,
    memval_equiv x1 x2 ->
    eSexpr alloc em eb eu Tint (decode_val Mint8unsigned (x1::nil)) =
    eSexpr alloc em eb eu Tint (decode_val Mint8unsigned (x2::nil)) ->
    forall t,
    eSexpr alloc em eb eu t (decode_val Mint8unsigned (x1::nil)) = eSexpr alloc em eb eu t (decode_val Mint8unsigned (x2::nil)).
Proof.
  destruct x1; destruct x2; simpl in *; intuition try congruence.
  unfold decode_val in *; simpl in *.
  unfold proj_symbolic_le in *; simpl in *.
  repeat rewrite rev_if_be_one in *; simpl in *.
  repeat rewrite rev_if_be_one in H0; simpl in *.
  simpl_eval'.
Qed.

Require Import Fappli_IEEE_bits.
Require Import Psatz.


Lemma int_range_int64:
  forall i,
  0 <= Int.unsigned i <= Int64.max_unsigned.
Proof.
  intros.
  generalize (Int.unsigned_range i). split. omega.
  transitivity Int.modulus. omega.
  vm_compute. congruence.
Qed.

Lemma int_inf_two_p_32:
  forall i,
    0 <= Int.unsigned i < two_power_nat 32.
Proof.
  intros.
  change (two_power_nat 32) with Int.modulus.
  generalize (Int.unsigned_range i). omega.
Qed.

Lemma decode_val_eq_memval_equiv:
  forall l l'
         (FWMl: Forall wt_memval l)
         (FWMl': Forall wt_memval l')
         (EVAL: forall alloc em ,
                  eSexpr alloc em eb eu Tlong (decode_val Mint64 l) =
                  eSexpr alloc em eb eu Tlong (decode_val Mint64 l')),
    length l = length l' ->
    (length l <= 8)%nat ->
    list_forall2 memval_equiv_tint l l'.
Proof.
  intros.
  assert (forall (alloc0 : NormaliseSpec.mem) (em : block -> int -> byte),
            eSexpr alloc0 em eb eu Tlong
                   (proj_symbolic_aux_le (rev_if_be l) Llong) =
            eSexpr alloc0 em eb eu Tlong
                   (proj_symbolic_aux_le (rev_if_be l') Llong)).
  {
    intros.
    specialize (EVAL alloc0 em).
    repeat rewrite decode_val_info with (c:=Mint64) in EVAL.
    simpl in EVAL.
    simpl_eval'.
    apply proof_irr_int64 in H4.
    repeat rewrite Int64.sign_ext_above with (n:=64) in H4 by (vm_compute; congruence).
    subst; auto.
  }
  clear EVAL.
  apply list_forall2_rib_inv.
  revert H H0 H1.
  rewrite <- (rev_if_be_length _ l).
    rewrite <- (rev_if_be_length _ l').
  apply Forall_rib in FWMl.
  apply Forall_rib in FWMl'.
  revert FWMl FWMl'.
  generalize (rev_if_be l).
  generalize (rev_if_be l').
  clear l l'.
  induction l; simpl; intros.
  - des l. constructor.
  - des l0.
    Opaque expr_of_memval.
    assert (length l1 <= 7)%nat by omega.
    inv FWMl; inv FWMl'. inv H. clear H0.
    specialize (IHl _ H6 H8 H4).
    assert (eom_il:forall alloc em a i1,
                     wt_memval a ->
                     eSexpr alloc em eb eu Tlong (expr_of_memval a Llong) = Vl i1 ->
                     eSexpr alloc em eb eu Tint (expr_of_memval a Lint) = Vi (Int64.loword i1)).
    {
      clear; intros.
      generalize (eSexpr_wt alloc0 em eb eu (expr_of_memval a Lint) Tint _ eq_refl).
      des (eSexpr alloc0 em eb eu Tint (expr_of_memval a Lint)).
      f_equal. eapply eom_int_long_same; eauto.
    }
    assert (forall alloc0 em,
            exists i i0 i1 i2,
              eSexpr alloc0 em eb eu Tlong (cast Llong (expr_of_memval m Llong)) = Vl i /\
              eSexpr alloc0 em eb eu Tlong (proj_symbolic_aux_le l1 Llong) = Vl i0 /\
              eSexpr alloc0 em eb eu Tlong (cast Llong (expr_of_memval a Llong)) = Vl i1 /\
              eSexpr alloc0 em eb eu Tlong (proj_symbolic_aux_le l Llong) = Vl i2 /\
              Int64.add i (Int64.shl' i0 (Int.repr 8)) =
              Int64.add i1 (Int64.shl' i2 (Int.repr 8))).
    {
      intros.
      specialize (H1 alloc0 em).
      simpl_eval'.
      apply proof_irr_int64 in H11. repeat eexists; repeat split; eauto.
    }
    clear H1.
     assert (forall alloc0 em i i0 i1 i2 i3,
               eSexpr alloc0 em eb eu Tlong (cast Llong (expr_of_memval m Llong)) = Vl i ->
               eSexpr alloc0 em eb eu Tlong (proj_symbolic_aux_le l1 Llong) = Vl i0 ->
               eSexpr alloc0 em eb eu Tlong (cast Llong (expr_of_memval a Llong)) = Vl i1 ->
               eSexpr alloc0 em eb eu Tlong (proj_symbolic_aux_le l Llong) = Vl i2 ->
               Int64.add i (Int64.shl' i0 (Int.repr 8)) =
               Int64.add i1 (Int64.shl' i2 (Int.repr 8)) ->
               0 <= i3 < Int64.zwordsize ->
               Int64.testbit i i3 = Int64.testbit i1 i3 /\ Int64.testbit i0 i3 = Int64.testbit i2 i3).
    {
      revert H5 H6 H7 H8 H2 H4 eom_il.
      clear. intros WTm WTl1 WTa WTl l7 len_l1_l eom_il alloc0 em i i0 i1 i2 i3 EOMm PSAl1 EOMa PSAl i64eq i3val.
      generalize (eSexpr_wt alloc0 em eb eu (expr_of_memval m Llong) Tint _ eq_refl).
      destruct (eSexpr alloc0 em eb eu Tint (expr_of_memval m Llong)) eqn:?; simpl; intro A; try (exfalso; auto; fail); clear A.
      generalize (eSexpr_wt alloc0 em eb eu (expr_of_memval a Llong) Tint _ eq_refl).
      destruct (eSexpr alloc0 em eb eu Tint (expr_of_memval a Llong)) eqn:?; simpl; intro A; try (exfalso; auto; fail); clear A.
      unfold cast in EOMm, EOMa.
      rewrite expr_of_memval_wt in *; simpl; auto.
      generalize (expr_of_memval_bound_int_long _ _ _ _ WTm Heql0).
      generalize (expr_of_memval_bound_int_long _ _ _ _ WTa Heql1). intros EOM_bnd_a EOM_bnd_m.
      generalize (psa_le_n_bnds _ WTl1 7 l7 _ _ _ PSAl1).
      rewrite len_l1_l in l7.
      generalize (psa_le_n_bnds _ WTl 7 l7 _ _ _ PSAl). intros PSA_bnd_l PSA_bnd_l1.
      simpl in *.
      unfold eunop in *.
      rewrite Heql0 in EOMm.
      rewrite Heql1 in EOMa.
      unfold unop, fun_of_unop, fun_of_unop' in *; simpl in *.
      inv EOMm.
      inv EOMa.
      clear Heql1 PSAl1 Heql0 PSAl.
      generalize i64eq; intro i64eq'.
      repeat rewrite Int64.testbit_repr; auto.
      assert (forall ii, 0 <= ii < Int64.zwordsize ->
                         Z.testbit (Int.unsigned i4) ii = Z.testbit (Int.unsigned i5) ii).
      {
        intros.
        des (zlt ii Int.zwordsize).
        repeat rewrite <- Int.testbit_repr; auto.
        repeat rewrite Int.repr_unsigned.
        apply (f_equal (fun x => Int64.testbit x ii)) in i64eq.
        rewrite Int64_add_is_or_lt_256 in i64eq.
        rewrite Int64_add_is_or_lt_256 in i64eq.
        repeat rewrite Int64.bits_or in i64eq by omega.
        repeat rewrite shl'_shl in i64eq by (vm_compute; intuition congruence).
        change (Int.unsigned (Int.repr 8)) with 8 in i64eq.
        repeat rewrite Int64.bits_shl in i64eq by omega.
        change (Int64.unsigned (Int64.repr 8)) with 8 in i64eq.
        des (zlt ii 8).
        repeat rewrite orb_false_r in i64eq; auto.
        repeat rewrite Int64.testbit_repr in i64eq by auto. auto.
        rewrite (int_testbit_lt_256 i4); auto.
        rewrite (int_testbit_lt_256 i5); auto. destr.
        rewrite Int64.unsigned_repr; auto.
        apply int_range_int64.
        rewrite Int64.unsigned_repr; auto.
        apply int_range_int64.
        repeat rewrite Int.Ztestbit_above with (n:=32%nat); auto.
        apply int_inf_two_p_32.
        apply int_inf_two_p_32.
      }
      assert (forall ii, 0 <= ii < Int64.zwordsize ->
                         Int64.testbit i0 ii = Int64.testbit i2 ii).
      {
        intros.
        apply (f_equal (fun x => Int64.testbit x (ii+8))) in i64eq.
        rewrite Int64_add_is_or_lt_256 in i64eq.
        rewrite Int64_add_is_or_lt_256 in i64eq.
        des (zlt (ii+8) Int64.zwordsize).
        repeat rewrite Int64.bits_or in i64eq by omega.
        repeat rewrite shl'_shl in i64eq by (vm_compute; intuition congruence).
        change (Int.unsigned (Int.repr 8)) with 8 in i64eq.
        repeat rewrite Int64.bits_shl in i64eq by omega.
        change (Int64.unsigned (Int64.repr 8)) with 8 in i64eq.
        repeat rewrite Int64.testbit_repr in i64eq by omega.
        rewrite H in i64eq; auto; try omega.
        rewrite Int.Ztestbit_above with (n:=8%nat) in i64eq; simpl; auto; try omega.
        simpl in i64eq.
        des (zlt (ii + 8) 8);try omega.
        replace (ii + 8 - 8) with ii in i64eq by omega. auto.
        change (two_power_nat 8) with 256; split; try omega.
        generalize (Int.unsigned_range i5); omega.
        rewrite (int64_testbit_lt_256_7 i0) ; auto.
        rewrite (int64_testbit_lt_256_7 i2) ; auto.
        rewrite Heqs; auto.
        rewrite Int64.unsigned_repr; auto.
        apply int_range_int64.
        rewrite Int64.unsigned_repr; auto.
        apply int_range_int64.
      }
      split.
      apply H; auto.
      apply H0; auto.
    }
    assert (forall (alloc0 : NormaliseSpec.mem) (em : block -> int -> byte),
      exists i i0 i1 i2 : int64,
        eSexpr alloc0 em eb eu Tlong (cast Llong (expr_of_memval m Llong)) = Vl i /\
        eSexpr alloc0 em eb eu Tlong (proj_symbolic_aux_le l1 Llong) = Vl i0 /\
        eSexpr alloc0 em eb eu Tlong (cast Llong (expr_of_memval a Llong)) = Vl i1 /\
        eSexpr alloc0 em eb eu Tlong (proj_symbolic_aux_le l Llong) = Vl i2 /\
        Int64.add i (Int64.shl' i0 (Int.repr 8)) =
        Int64.add i1 (Int64.shl' i2 (Int.repr 8)) /\
        forall i3, 0 <= i3 < Int64.zwordsize ->
                   Int64.testbit i i3 = Int64.testbit i1 i3 /\
                   Int64.testbit i0 i3 = Int64.testbit i2 i3).
    {
      intros.
      destruct (H alloc0 em) as (i & i0 & i1 & i2 & A & B & C & D & E).
      rewrite A; rewrite B; rewrite C; rewrite D.
      do 4 eexists. do 4 split; eauto.
    }
    clear H H0.
    constructor.
    + unfold memval_equiv_tint.
      intros.
      destruct (H1 alloc0 em) as (i & i0 & i1 & i2 & A & B & C & D & E & F).
      rewrite eom_il with (i1:=i); auto.
      rewrite eom_il with (i1:=i1); auto.
      f_equal; f_equal.
      solve_long.
      destruct (F _ (conj H0 H3)); auto.
      clear - C H7.
      inv_expr_type (eSexpr alloc0 em eb eu Tint (expr_of_memval a Llong)).
      inv_expr_type (eSexpr alloc0 em eb eu Tlong (expr_of_memval a Llong)).
      unfold cast in C.
      rewrite expr_of_memval_wt in C; auto. destr.
      repeat (simpl_eval'; simpl).
      rewrite <- eom_int_long_same' in Heql1; auto.
      generalize (eom_int_long_same _ _ _ _ _ H7 Heql1 Heql0) ; eauto.
      intro; subst.
      generalize (expr_of_memval_bound_long _ _ _ _ H7 Heql0). intros.
      f_equal. unfold Int64.loword. rewrite Int.unsigned_repr.
      rewrite Int64.repr_unsigned; auto.
      generalize (Int64.unsigned_range i0). split. omega.
      transitivity 256. omega. vm_compute. congruence.
      clear - A H5.
      inv_expr_type (eSexpr alloc0 em eb eu Tint (expr_of_memval m Llong)).
      inv_expr_type (eSexpr alloc0 em eb eu Tlong (expr_of_memval m Llong)).
      unfold cast in A.
      rewrite expr_of_memval_wt in A; auto. destr.
      repeat (simpl_eval'; simpl).
      rewrite <- eom_int_long_same' in Heql1; auto.
      generalize (eom_int_long_same _ _ _ _ _ H5 Heql1 Heql0) ; eauto.
      intro; subst.
      generalize (expr_of_memval_bound_long _ _ _ _ H5 Heql0). intros.
      f_equal. unfold Int64.loword. rewrite Int.unsigned_repr.
      rewrite Int64.repr_unsigned; auto.
      generalize (Int64.unsigned_range i1). split. omega.
      transitivity 256. omega. vm_compute. congruence.
    + apply IHl.
      omega.
      intros.
      destruct (H1 alloc0 em) as (i & i0 & i1 & i2 & A & B & C & D & E & F).
      rewrite B; rewrite D. f_equal.
      solve_long.
      destruct (F _ (conj H0 H3)); auto.
Qed.

Lemma memval_equiv_tint_sym:
  forall a b,
    memval_equiv_tint a b ->
    memval_equiv_tint b a.
Proof.
  unfold memval_equiv_tint; repeat destr.
Qed.

Lemma eSexpr_eq_int_eq_all_int:
  forall v1 v2 x1 x2 alloc em,
    wt_memval x1 -> wt_memval x2 ->
    decode_val Mint8unsigned (x1::nil) = v1 ->
    decode_val Mint8unsigned (x2::nil) = v2 ->
    memval_equiv_tint x1 x2 ->
    eSexpr alloc em eb eu Tint v1 = eSexpr alloc em eb eu Tint v2 ->
    forall t,
    eSexpr alloc em eb eu t v1 = eSexpr alloc em eb eu t v2.
Proof.
  intros. subst.
  eapply eSexpr_eq_int_eq_all in H4; eauto.
  apply memval_equiv_int; auto.
Qed.

Require Import Setoid.

Theorem storebytes_int64_split:
  forall m b ofs v m' x x0 enc64 encLoword encHiword
         (Hwt: tcheck_expr v = Some Tlong)
         (H : store Mint64 m b ofs v = Some m')
         (Enc64: encode_val Mint64 v = Some enc64)
         (EncLoword: encode_val Mint32 (if big_endian then Val.hiword v else Val.loword v) = Some encLoword)
         (EncHiword: encode_val Mint32 (if big_endian then Val.loword v else Val.hiword v) = Some encHiword)
         (SB : storebytes m b ofs enc64 = Some m')
         (e : storebytes m b ofs encLoword = Some x)
         (e0 : storebytes x b (ofs + 4) encHiword = Some x0),
    m' ==m x0.
Proof.
  intros.
  apply bytewise_eq_all.
  intros until v2.
  assert (Decidable.decidable (b0 <> b \/
  ofs0 + size_chunk Mint8unsigned <= ofs \/ ofs + Z.of_nat (length enc64) <= ofs0)).
  unfold Decidable.decidable.
  destruct (eq_block b b0); subst; simpl; auto.
  destruct (Z_le_dec (ofs0 + size_chunk Mint8unsigned) ofs); simpl; auto.
  destruct (Z_le_dec (ofs + Z.of_nat (length enc64)) ofs0); simpl; auto.
  intuition congruence.
  destruct H0.
  rewrite (load_storebytes_other _ _ _ _ _ SB); auto.
  rewrite (load_storebytes_other _ _ _ _ _ e0); auto.
  rewrite (load_storebytes_other _ _ _ _ _ e); auto.
  intros A B; rewrite A in B; inv B.
  reflexivity.
  rewrite (encode_val_length _ _ _ Enc64) in H0; auto; simpl in *.
  rewrite (encode_val_length _ _ _ EncLoword); auto; simpl in *.
  intuition.
  rewrite (encode_val_length _ _ _ Enc64) in H0; auto; simpl in *.
  rewrite (encode_val_length _ _ _ EncHiword); auto; simpl in *.
  intuition.
  assert (b = b0 /\ ofs0 + size_chunk Mint8unsigned > ofs /\ ofs + Z.of_nat (length enc64) > ofs0).
  {
    clear - H0.
    destruct (peq b0 b); subst; auto.
    destruct (zle (ofs0 + size_chunk Mint8unsigned) ofs);
    destruct (zle (ofs + Z.of_nat (length enc64)) ofs0); auto;
    intuition.
    intuition.
  }
  clear H0; intuition.
  subst.
  apply load_loadbytes in H0.
  apply load_loadbytes in H2.
  destruct H0 as [bytes [C D]].
  destruct H2 as [bytes' [C' D']].
  generalize (loadbytes_storebytes_same _ _ _ _ _ SB).
  generalize (loadbytes_storebytes_same _ _ _ _ _ e).
  generalize (loadbytes_storebytes_same _ _ _ _ _ e0).
  assert (length enc64 = 8%nat) by (rewrite (encode_val_length _ _ _ Enc64); auto).
  assert (length encLoword = 4%nat) by (rewrite (encode_val_length _ _ _ EncLoword); auto).
  assert (length encHiword = 4%nat) by (rewrite (encode_val_length _ _ _ EncHiword); auto).
  Opaque decode_val.
  rewrite H0, H2, H3 in *; simpl in *.
  clear H0 H2 H3.
  clear H.
  assert (ofs <= ofs0 <= ofs + 7) by omega.
  clear H1 H5.
  subst. simpl.
  generalize (encode_val_int64 v _ _ _ Hwt Enc64 EncLoword EncHiword).
  intros.
  simpl in H0.
  rewrite <- (loadbytes_storebytes_other _ _ _ _ _ e0 b0 ofs 4) in H2 by (intuition omega).
  assert (loadbytes x0 b0 ofs 8 = Some (encLoword ++ encHiword)).
  apply (loadbytes_concat _ _ _ _ _ _ _ H2 H1); try omega.
  clear H2 H1.
  assert (list_forall2 memval_equiv_tint (encLoword ++ encHiword) enc64).
  {
    eapply (decode_val_eq_memval_equiv) ; eauto.
    apply loadbytes_wt_memval in H4; auto.
    apply loadbytes_wt_memval in H3; auto.
    simpl. intros; auto.
    repeat erewrite loadbytes_length; eauto.
    repeat erewrite loadbytes_length; simpl; eauto. vm_compute; omega.
  }
  assert (exists x, bytes = x :: nil).
  {
    generalize (loadbytes_length _ _ _ _ _ C); simpl.
    destruct bytes; simpl in *; intuition try congruence; try (zify; omega).
    destruct bytes; simpl in *; intuition try congruence; try (zify; omega).
    eexists; eauto.
  }
  assert (exists y, bytes' = y :: nil).
  {
    generalize (loadbytes_length _ _ _ _ _ C'); simpl.
    destruct bytes'; simpl in *; intuition try congruence; try (zify; omega).
    destruct bytes'; simpl in *; intuition try congruence; try (zify; omega).
    eexists; eauto.
  }
  destruct H2; destruct H5. subst; simpl in *.
  assert (forall {A} (l1 l2: list A) n, (n >= length l1)%nat -> nth_error (l1 ++ l2) n = nth_error l2 (n - length l1)).
  {
    clear; induction l1; simpl; auto.
    intros. f_equal. omega.
    intros. destruct n; simpl in *; try omega.
    rewrite IHl1; auto.
    omega.
  }
  assert (nth_error (encLoword ++ encHiword) (Z.to_nat (ofs0 - ofs)) = Some x2).
  {
    revert H1 H4; generalize (encLoword ++ encHiword); intros.
    replace 8 with ((ofs0-ofs) + (1 + (8-ofs0+ofs-1))) in H4 by omega.
    apply loadbytes_split in H4; try (zify;omega).
    destruct H4 as [b1 [b2 [LB [LB2 APP]]]].
    apply loadbytes_split in LB2; try (zify;omega).
    destruct LB2 as [b3 [b4 [LB3 [LB4 APP2]]]].
    rewrite APP2 in APP.
    clear APP2.
    replace (ofs + (ofs0 - ofs)) with ofs0 in LB3 by omega.
    rewrite C' in LB3.
    inv LB3.
    assert (length b1 = Z.to_nat (ofs0 - ofs)).
    {
      eapply loadbytes_length; eauto.
    }
    rewrite H2; rewrite <- H4; auto.
    simpl.
    replace (length b1 - length b1)%nat with O by omega.
    simpl; auto.
  }
  assert (nth_error enc64 (Z.to_nat (ofs0 - ofs)) = Some x1).
  {
    replace 8 with ((ofs0-ofs) + (1 + (8-ofs0+ofs-1))) in H3 by omega.
    apply loadbytes_split in H3; try (zify;omega).
    destruct H3 as [b1 [b2 [LB [LB2 APP]]]].
    apply loadbytes_split in LB2; try (zify;omega).
    destruct LB2 as [b3 [b4 [LB3 [LB4 APP2]]]].
    rewrite APP2 in APP.
    clear APP2.
    replace (ofs + (ofs0 - ofs)) with ofs0 in LB3 by omega.
    rewrite C in LB3.
    inv LB3.
    assert (length b1 = Z.to_nat (ofs0 - ofs)).
    {
      eapply loadbytes_length; eauto.
    }
    rewrite H2; rewrite <- H3; auto.
    simpl.
    replace (length b1 - length b1)%nat with O by omega.
    simpl; auto.
  }
  assert (forall n l1 l2 a b,
            list_forall2 memval_equiv_tint l1 l2 ->
            nth_error l1 n = Some a ->
            nth_error l2 n = Some b ->
            memval_equiv_tint a b).
  {
    clear; induction n; simpl.
    - intros.
      unfold error, value in *.
      destruct l1; destruct l2; simpl in *; intuition try congruence.
      inv H0; inv H1.
      inv H; auto.
    - intros.
      unfold error, value in *.
      destruct l1; destruct l2; simpl in *; intuition try congruence.
      inv H.
      apply (IHn _ _ _ _ H7 H0 H1).
  }
  specialize (H7 _ _ _ _ _ H1 H5 H6).
  apply eSexpr_eq_int_eq_all_int with (x1:=x1) (x2:=x2); auto.
  apply loadbytes_wt_memval in C; inv C; auto.
  apply loadbytes_wt_memval in C'; inv C'; auto.
  apply memval_equiv_tint_sym; auto.
  apply loadbytes_wt_memval in C; auto.
  apply loadbytes_wt_memval in C'; auto.
  eapply (decode_val_spec' (x1::nil) (x2::nil) C C') with (c:=Mint8unsigned).
  repeat constructor.
  apply memval_equiv_tint_sym; auto.
Qed.

Lemma encode_val_wt'':
  forall c v,
    tcheck_expr v = Some ((type_of_chunk c)) ->
    encode_val c v <> None.
Proof.
  unfold encode_val.
  intros; rewrite H.
  rewrite styp_eq_refl.
  des v; try des s; des c.
Qed.

 Lemma ex_enc32_64:
   forall (b:bool) v enc64,
     encode_val Mint64 v = Some enc64 ->
     (exists enc32_1, encode_val Mint32 (if b then Val.hiword v else Val.loword v) = Some enc32_1).
Proof.
  intros.
  generalize (encode_val_wt Mint64 v); rewrite H; intuition try congruence.
  des (encode_val Mint32 (if b then Val.hiword v else Val.loword v)); try (eexists; eauto; fail).
  exfalso.
  generalize (encode_val_wt'' Mint32 (if b then Val.hiword v else Val.loword v)).
  rewrite Heqo; intuition try congruence.
  apply H0; simpl; auto.
  generalize (tc_hiword v).
  generalize (tc_loword v).
  rewrite H1; simpl.
  intuition try congruence.
  des b.
Qed.

Theorem store_int64_split:
  forall m b ofs v m',
  store Mint64 m b ofs v = Some m' ->
  exists m1 m2,
     store Mint32 m b ofs (if Archi.big_endian then Val.hiword v else Val.loword v) = Some m1
     /\ store Mint32 m1 b (ofs + 4) (if Archi.big_endian then Val.loword v else Val.hiword v) = Some m2
     /\ m' ==m m2.
Proof.
  intros.
  exploit store_valid_access_3; eauto. intros [A B]. simpl in *.
  generalize (store_encode_some _ _ _ _ _ _ H).
  destruct (encode_val Mint64 v) eqn:?; intuition try congruence.
  clear H0.
  exploit store_storebytes. eexact H. eexact Heqo. intros SB.
  assert (exists enc32_1, encode_val Mint32 (if Archi.big_endian then Val.hiword v else Val.loword v) = Some enc32_1).
  {
    eapply ex_enc32_64; eauto.
  }
  assert (exists enc32_2, encode_val Mint32 (if Archi.big_endian then Val.loword v else Val.hiword v) = Some enc32_2). {
    change (if Archi.big_endian then Val.loword v else Val.hiword v) with
    (if negb Archi.big_endian then Val.hiword v else Val.loword v).
    eapply ex_enc32_64; eauto.
  }
  destruct H0 as [enc32_1 EQ1].
  destruct H1 as [enc32_2 EQ2].
  cut (range_perm m b ofs (ofs+Z.of_nat (length enc32_1)) Cur Writable). intro.
  apply range_perm_storebytes in H0; auto. destruct H0.
  cut (range_perm x b (ofs+4) ((ofs+4)+Z.of_nat (length enc32_2)) Cur Writable). intro.
  apply range_perm_storebytes in H0. destruct H0.
  erewrite storebytes_store with (m2:=x); eauto.
  exists x.
  erewrite storebytes_store with (m2:=x0); eauto.
  exists x0.
  repeat (split; try reflexivity).
  - rewrite <- be_rew in *.
    eapply storebytes_int64_split; eauto.
    apply store_encode_some in H.
    apply encode_val_wt in H.
    rewrite H. reflexivity.
  - simpl.
    apply Zdivide_plus_r.
    apply Zdivides_trans with 8; auto. exists 2; auto.
    exists 1; auto.
  - simpl.
    apply Zdivides_trans with 8; auto. exists 2; auto.
  - eapply check_bytes_encode; eauto.
  - assert (forall b ofs n, range_perm m b ofs (ofs+n) Cur Writable ->
                            range_perm x b ofs (ofs+n) Cur Writable).
    intros.
    revert e; unfold storebytes. destruct range_perm_dec; try congruence.
    destruct (bool_dec (check_bytes enc32_1) true); intuition try congruence.
    inv e0; unfold range_perm, perm in *; simpl in *. tauto.
    destr_cond_match; intuition congruence.
    apply H0; clear H0; unfold range_perm in *; intros.
    apply A.
    replace (Z.of_nat _) with 4%Z in H0. split; omega.
    erewrite encode_val_length; eauto.
    reflexivity.
  - eapply check_bytes_encode; eauto.
  - unfold range_perm in *; intros.
    apply A.
    replace (Z.of_nat _) with 4 in H0.
    simpl in H0.
    split; try omega.
    erewrite encode_val_length; eauto.
    reflexivity.
Qed.

Lemma store_int64_eq_add:
  forall m b i v m',
    store Mint64 m b (Int.unsigned i) v = Some m' ->
    Int.unsigned (Int.add i (Int.repr 4)) = Int.unsigned i + 4.
Proof.
  intros.
  rewrite Int.add_unsigned. rewrite Int.unsigned_repr. auto.
  exploit store_valid_access_3. eexact H. intros [P Q]. simpl in Q.
  exploit (Zdivide_interval (Int.unsigned i) Int.modulus 8).
  omega. apply Int.unsigned_range. auto. exists (two_p (32-3)); reflexivity.
  change (Int.unsigned (Int.repr 4)) with 4. unfold Int.max_unsigned. omega.
Qed.

Properties related to alloc.


Section ALLOC.

Variable m1: Mem.mem.
Variables lo hi: Z.
Variable m2: Mem.mem.
Variable b: block.
Hypothesis ALLOC: alloc m1 lo hi = Some (m2, b).

Theorem mask_alloc_other:
  forall b',
    b <> b' ->
    mask m1 b' = mask m2 b'.
Proof.
  unfold alloc, low_alloc in ALLOC.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  injection ALLOC; intros; subst; unfold mask; simpl in *.
  rewrite PMap.gso by congruence.
  reflexivity.
Qed.

Theorem size_of_block_alloc_other:
  forall b',
    b <> b' ->
    size_of_block m1 b' = size_of_block m2 b'.
Proof.
  unfold alloc, low_alloc in ALLOC.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  injection ALLOC; intros; subst; unfold size_of_block; simpl.
  rewrite PMap.gso by congruence.
  reflexivity.
Qed.

Theorem bounds_of_block_alloc_other:
  forall b',
    b <> b' ->
    bounds_of_block m1 b' = bounds_of_block m2 b'.
Proof.
  unfold alloc, low_alloc in ALLOC.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  injection ALLOC; intros; subst; unfold bounds_of_block; simpl.
  rewrite PMap.gso by congruence.
  reflexivity.
Qed.

Theorem nat_mask_alloc_other:
  forall b',
    b <> b' ->
    nat_mask m1 b' = nat_mask m2 b'.
Proof.
  intros; unfold nat_mask.
  f_equal; f_equal. f_equal. f_equal.
  apply mask_alloc_other; auto.
Qed.

Theorem nextblock_alloc:
  nextblock m2 = Psucc (nextblock m1).
Proof.
  unfold alloc, low_alloc in ALLOC.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  injection ALLOC; intros. rewrite <- H0; auto.
Qed.

Theorem alloc_result:
  b = nextblock m1.
Proof.
  unfold alloc, low_alloc in ALLOC.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  injection ALLOC; auto.
Qed.

Theorem valid_block_alloc:
  forall b', valid_block m1 b' -> valid_block m2 b'.
Proof.
  unfold valid_block; intros. rewrite nextblock_alloc.
  apply Plt_trans_succ; auto.
Qed.

Theorem fresh_block_alloc:
  ~(valid_block m1 b).
Proof.
  unfold valid_block. rewrite alloc_result. apply Plt_strict.
Qed.

Theorem valid_new_block:
  valid_block m2 b.
Proof.
  unfold valid_block. rewrite alloc_result. rewrite nextblock_alloc. apply Plt_succ.
Qed.

Local Hint Resolve valid_block_alloc fresh_block_alloc valid_new_block: mem.

Theorem valid_block_alloc_inv:
  forall b', valid_block m2 b' -> b' = b \/ valid_block m1 b'.
Proof.
  unfold valid_block; intros.
  rewrite nextblock_alloc in H. rewrite alloc_result.
  exploit Plt_succ_inv; eauto. tauto.
Qed.

Theorem perm_alloc_1:
  forall b' ofs k p, perm m1 b' ofs k p -> perm m2 b' ofs k p.
Proof.
  unfold perm; intros.
  unfold alloc, low_alloc in ALLOC.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  injection ALLOC; intros. rewrite <- H1; simpl.
  subst b. rewrite PMap.gsspec. destruct (peq b' (nextblock m1)); auto.
  rewrite nextblock_noaccess in H. contradiction. subst b'. apply Plt_strict.
Qed.

Theorem perm_alloc_2:
  forall ofs k, 0 <= ofs < hi -> perm m2 b ofs k Freeable.
Proof.
  unfold perm; intros.
  unfold alloc, low_alloc in ALLOC.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  injection ALLOC; intros. rewrite <- H1; simpl.
  subst b. rewrite PMap.gss. unfold proj_sumbool. rewrite zle_true.
  rewrite zlt_true. simpl. auto with mem. omega. omega.
Qed.

Theorem perm_alloc_inv:
  forall b' ofs k p,
  perm m2 b' ofs k p ->
  if eq_block b' b then 0 <= ofs < hi else perm m1 b' ofs k p.
Proof.
  intros until p; unfold perm.
  unfold alloc, low_alloc in ALLOC.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  inv ALLOC. simpl.
  rewrite PMap.gsspec. unfold eq_block. destruct (peq b' (nextblock m1)); intros.
  destruct (zle 0 ofs); try contradiction. destruct (zlt ofs hi); try contradiction.
  split; auto.
  auto.
Qed.

Theorem perm_alloc_3:
  forall ofs k p, perm m2 b ofs k p -> 0 <= ofs < hi.
Proof.
  intros. exploit perm_alloc_inv; eauto. rewrite dec_eq_true; auto.
Qed.

Theorem perm_alloc_4:
  forall b' ofs k p, perm m2 b' ofs k p -> b' <> b -> perm m1 b' ofs k p.
Proof.
  intros. exploit perm_alloc_inv; eauto. rewrite dec_eq_false; auto.
Qed.

Local Hint Resolve perm_alloc_1 perm_alloc_2 perm_alloc_3 perm_alloc_4: mem.

Theorem valid_access_alloc_other:
  forall chunk b' ofs p,
  valid_access m1 chunk b' ofs p ->
  valid_access m2 chunk b' ofs p.
Proof.
  intros. inv H. constructor; auto with mem.
  red; auto with mem.
Qed.

Theorem valid_access_alloc_same:
  forall chunk ofs,
  0 <= ofs -> ofs + size_chunk chunk <= hi -> (align_chunk chunk | ofs) ->
  valid_access m2 chunk b ofs Freeable.
Proof.
  intros. constructor; auto with mem.
  red; intros. apply perm_alloc_2. omega.
Qed.

Local Hint Resolve valid_access_alloc_other valid_access_alloc_same: mem.

Theorem valid_access_alloc_inv:
  forall chunk b' ofs p,
  valid_access m2 chunk b' ofs p ->
  if eq_block b' b
  then 0 <= ofs /\ ofs + size_chunk chunk <= hi /\ (align_chunk chunk | ofs)
  else valid_access m1 chunk b' ofs p.
Proof.
  intros. inv H.
  generalize (size_chunk_pos chunk); intro.
  destruct (eq_block b' b). subst b'.
  assert (perm m2 b ofs Cur p). apply H0. omega.
  assert (perm m2 b (ofs + size_chunk chunk - 1) Cur p). apply H0. omega.
  exploit perm_alloc_inv. eexact H2. rewrite dec_eq_true. intro.
  exploit perm_alloc_inv. eexact H3. rewrite dec_eq_true. intro.
  intuition omega.
  split; auto. red; intros.
  exploit perm_alloc_inv. apply H0. eauto. rewrite dec_eq_false; auto.
Qed.

Theorem load_alloc_unchanged:
  forall chunk b' ofs,
  valid_block m1 b' ->
  load chunk m2 b' ofs = load chunk m1 b' ofs.
Proof.
  intros. unfold load.
  destruct (valid_access_dec m2 chunk b' ofs Readable).
  exploit valid_access_alloc_inv; eauto. destruct (eq_block b' b); intros.
  subst b'. elimtype False. eauto with mem.
  rewrite pred_dec_true; auto.
  unfold alloc, low_alloc in ALLOC.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  injection ALLOC; intros. rewrite <- H2; simpl.
  rewrite PMap.gso. auto. rewrite H1. apply sym_not_equal; eauto with mem.
  rewrite pred_dec_false. auto.
  eauto with mem.
Qed.

Theorem load_alloc_other:
  forall chunk b' ofs v,
  load chunk m1 b' ofs = Some v ->
  load chunk m2 b' ofs = Some v.
Proof.
  intros. rewrite <- H. apply load_alloc_unchanged. eauto with mem.
Qed.

Require Import Classical.

Lemma init_block'_eundef:
  forall n b cur o,
    (0 <= o <= n)%nat ->
    nth_error (init_block' n b cur) o =
    Some (Symbolic
            (Eval (Eundef b (Int.add cur (Int.repr (Z.of_nat o)))))
            O).
Proof.
  induction n; simpl; intros. assert (o=O) by omega.
  subst. simpl.
  rewrite Int.add_zero. auto.
  destruct H.
  destruct o.
  simpl.
  rewrite Int.add_zero; auto.
  simpl.
  rewrite IHn; auto.
  rewrite Int.add_assoc.
  f_equal. f_equal. f_equal.
  f_equal. f_equal.
  rewrite Val.int_add_repr. f_equal.
  xomega. omega.
Qed.

Lemma in_nth_error:
  forall (A: Type) (vl: list A) (x:A),
    In x vl ->
    exists n, nth_error vl n = Some x.
Proof.
  induction vl; simpl; intros.
  exfalso; auto.
  destruct H; subst; auto.
  exists O; auto.
  destruct (IHvl _ H).
  exists (S x0); auto.
Qed.

Lemma nth_error_length:
  forall (A:Type) (l:list A) x v,
    nth_error l x = Some v ->
    (length l > x)%nat.
Proof.
  induction l; simpl; intros.
  rewrite nth_error_nil in H; congruence.
  destruct x. omega.
  simpl in H.
  apply IHl in H. omega.
Qed.

Lemma init_block'_length:
  forall n b c,
    length (init_block' n b c) = S n.
Proof.
  induction n; simpl; intros; eauto.
Qed.

Lemma init_block_eundef:
  forall n o lo hi c,
    lo <= o -> o + Z.of_nat n <= hi ->
    Forall (fun x => exists y z, x = Symbolic (Eval (Eundef y z)) O) (getN n o (init_block lo hi c)).
Proof.
  induction n; simpl; intros.
  constructor.
  constructor; auto.
  - unfold init_block.
    zify.
    destr; try omega.
    set (P:=fun mv => exists y z, mv = Symbolic (Eval (Eundef y z)) O).
    refine (setN_property P _ _ _ _ _ _).
    intros. apply in_nth_error in H2.
    destruct H2.
    rewrite init_block'_eundef in H2. inv H2.
    unfold P; simpl.
    eauto.
    split. omega.
    apply nth_error_length in H2.
    rewrite init_block'_length in H2. omega.
    rewrite init_block'_length.
    split. omega.
    rewrite <- Z2Nat.inj_succ; try omega.
    rewrite Z2Nat.id; try omega.
  - apply IHn. omega. rewrite Zpos_P_of_succ_nat in H0.
    omega.
Qed.

Lemma decode_list_undef_is_undef:
  forall l chunk,
    length l = size_chunk_nat chunk ->
    Forall
      (fun x => exists y z, x = Symbolic (Eval (Eundef y z)) 0) l ->
    is_undef (decode_val chunk (rev_if_be l)).
Proof.
  unfold is_undef; intros; exists (fun _ _ => Byte.zero); exists (fun _ _ => Byte.mone).
  exists (type_of_chunk chunk). intros.
  repeat rewrite decode_val_info.
  rewrite rev_if_be_involutive.
  des chunk; unfold size_chunk_nat in H; simpl in H;
  unfold Pos.to_nat in H; simpl in H;
  repeat match goal with
             H : context [length ?l] |- _ =>
             des l; try omega
         end;
  repeat match goal with
             H: Forall _ _ |- _ => inv H
         end;
  repeat match goal with
             H: exists _, _ |- _ =>
             destruct H as [? ?]; subst
         end;
  vm_compute in H1; congruence.
Qed.

Theorem load_alloc_same:
  forall chunk ofs v,
  load chunk m2 b ofs = Some v ->
  is_undef v.
Proof.
  intros. exploit load_result; eauto. intro.
  unfold alloc, low_alloc in ALLOC.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  injection ALLOC; intros. revert H0. rewrite <- H2; simpl. rewrite <- H1.
  rewrite PMap.gss.
  generalize (init_block_eundef (size_chunk_nat chunk) ofs 0 hi (nextblock m1)).
  generalize (getN_length (init_block 0 hi (nextblock m1)) (size_chunk_nat chunk) ofs).
  generalize (getN (size_chunk_nat chunk) ofs (init_block 0 hi (nextblock m1))).
  intros l L.
  rewrite <- rev_if_be_length in L.
  intros.
  assert (v = decode_val chunk (rev_if_be (rev_if_be l))).
  rewrite rev_if_be_involutive; auto.
  rewrite H4.
  apply decode_list_undef_is_undef; auto.
  apply Forall_rib; auto.
  apply H0.
  apply load_valid_access in H.
  - clear - H ALLOC; destruct H. inv ALLOC.
    unfold range_perm, perm in H; simpl in H.
    rewrite PMap.gss in H.
    specialize (H ofs).
    trim H. des chunk; omega.
    des (zle 0 ofs).
  - apply load_valid_access in H.
    clear - H ALLOC; destruct H. inv ALLOC.
    unfold range_perm, perm in H; simpl in H.
    rewrite PMap.gss in H.
    specialize (H (ofs + size_chunk chunk - 1)).
    trim H. des chunk; omega.
    des (zlt (ofs + size_chunk chunk - 1 ) hi).
    rewrite <- size_chunk_conv. omega.
    rewrite andb_comm in H. simpl in H.
    exfalso; auto.
Qed.

Theorem load_alloc_same':
  forall chunk ofs,
    0 <= ofs -> ofs + size_chunk chunk <= hi -> (align_chunk chunk | ofs) ->
  exists v, load chunk m2 b ofs = Some v /\
            is_undef v.
Proof.
  intros.
  exploit (valid_access_load m2 chunk b ofs).
  constructor; auto.
  red; intros. eapply perm_implies. apply perm_alloc_2. omega. auto with mem.
  intros (v & A); exists v; split; auto.
  eapply load_alloc_same; eauto.
Qed.

Theorem loadbytes_alloc_unchanged:
  forall b' ofs n,
  valid_block m1 b' ->
  loadbytes m2 b' ofs n = loadbytes m1 b' ofs n.
Proof.
  intros. unfold loadbytes.
  destruct (range_perm_dec m1 b' ofs (ofs + n) Cur Readable).
  rewrite pred_dec_true.
  unfold alloc, low_alloc in ALLOC.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  injection ALLOC; intros A B. rewrite <- B; simpl.
  rewrite PMap.gso. auto. rewrite A. eauto with mem.
  red; intros. eapply perm_alloc_1; eauto.
  rewrite pred_dec_false; auto.
  red; intros; elim n0. red; intros. eapply perm_alloc_4; eauto. eauto with mem.
Qed.

Theorem loadbytes_alloc_same:
  forall n ofs bytes byte,
  loadbytes m2 b ofs n = Some bytes ->
  In byte bytes -> exists y z, byte = Symbolic (Eval (Eundef y z)) O.
Proof.
  unfold loadbytes; intros. destruct (range_perm_dec m2 b ofs (ofs + n) Cur Readable); inv H.
  revert H0.
  unfold alloc, low_alloc in ALLOC.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  injection ALLOC; intros A B. rewrite <- A; rewrite <- B; simpl. rewrite PMap.gss.
  revert byte.
  rewrite <- Forall_forall.
  destruct (zle n 0).
  rewrite nat_of_Z_neg. simpl; constructor. auto.
  apply init_block_eundef.
  - inv ALLOC. clear - r g.
    unfold range_perm, perm in r; simpl in r.
    rewrite PMap.gss in r.
    specialize (r ofs).
    trim r. omega.
    des (zle 0 ofs).
  - inv ALLOC. clear - r g.
    unfold range_perm, perm in r; simpl in r.
    rewrite PMap.gss in r.
    specialize (r (ofs + n - 1)).
    trim r. omega.
    des (zlt (ofs + n - 1 ) hi).
    rewrite nat_of_Z_eq; auto; omega.
    rewrite andb_comm in r. simpl in r.
    exfalso; auto.
Qed.

End ALLOC.


Lemma bounds_of_block_alloc:
  forall m1 lo hi m1' b1,
    alloc m1 lo hi = Some (m1', b1) ->
    bounds_of_block m1' b1 = (0,hi).
Proof.
  intros.
  unfold alloc, low_alloc in H.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  unfold bounds_of_block; inv H; simpl.
  rewrite PMap.gss. auto.
Qed.


Lemma bounds_of_block_alloc_old:
  forall m1 lo hi m1' b1,
    alloc m1 lo hi = Some (m1', b1) ->
    bounds_of_block m1 b1 = (0,0).
Proof.
  intros.
  unfold alloc, low_alloc in H.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  unfold bounds_of_block; inv H; simpl.
  rewrite bs_valid; auto. xomega.
Qed.


Lemma alloc_mask:
  forall m1 lo hi m1' b1,
    alloc m1 lo hi = Some (m1',b1) ->
    mask m1' b1 = alignment_of_size (hi).
Proof.
  unfold alloc, low_alloc.
  intros m1 lo hi m1' b1.
  destruct (conc_mem_alloc_dec); try discriminate.
  intro A; inv A.
  unfold mask; simpl.
  rewrite PMap.gss. auto.
  rewrite Z.sub_0_r; auto.
Qed.

Lemma pos2nat_id:
  forall p,
    Pos.of_nat (S (pred (Pos.to_nat p))) = p.
Proof.
  intro.
  rewrite <- (S_pred (Pos.to_nat p) (pred (Pos.to_nat p))).
  rewrite Pos2Nat.id; auto.
  zify. omega.
Qed.

Lemma size_mem_al_aligned:
  forall l z,
    z = align z 8 ->
    size_mem_al 8 l z = align (size_mem_al 8 l z) 8.
Proof.
  induction l; simpl; intros; auto.
  apply IHl.
  destruct a.
  rewrite <- ! align_distr.
  rewrite align_align; omega.
Qed.

Lemma size_mem_aux_aligned:
  forall l,
    size_mem_aux l = align (size_mem_aux l) 8.
Proof.
  intros; unfold size_mem_aux.
  apply size_mem_al_aligned; auto.
Qed.

Opaque Pos.of_nat.
Lemma alloc_size_mem':
  forall m1 lo hi m1' b1
         (ALLOC: alloc m1 lo hi = Some (m1', b1)),
    size_mem m1' = size_mem m1 + align (Z.max 0 hi) 8.
Proof.
  intros.
  unfold alloc, low_alloc in ALLOC.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  injection ALLOC; intros; subst.
  unfold size_mem.
  unfold mk_block_list, mask.
  unfold size_block at 1.
  simpl in *. clear ALLOC.
  rewrite Pos2Nat.inj_succ.
  rewrite <- pred_Sn.
  rewrite (S_pred (Pos.to_nat (nextblock m1)) (pred (Pos.to_nat (nextblock m1)))) at 1
          by (zify; omega).
  rewrite mbla_rew; simpl.
  rewrite pos2nat_id.
  rewrite get_size_same. simpl.
  rewrite Z.sub_0_r.
  rewrite size_mem_aux_first. f_equal.
  rewrite mk_block_list_aux_ext' with (sz':=size_block m1); auto.
  intro. unfold get_size.
  rewrite Pos2Nat.id. intro. rewrite PMap.gso.
  reflexivity.
  xomega.
Qed.

Lemma size_mem_alloc_0:
  forall tm1 tm2 sp
         (ALLOC: alloc tm1 0 0 = Some (tm2, sp)),
    size_mem tm1 = size_mem tm2.
Proof.
  intros.
  apply alloc_size_mem' in ALLOC.
  rewrite Z.max_id in ALLOC.
  rewrite ALLOC. change (align 0 8) with 0.
  omega.
Qed.

Lemma size_mem_aligned:
  forall m z,
    Mem.size_mem m = z ->
    align z 8 = z.
Proof.
  intros; subst; symmetry; apply size_mem_aux_aligned.
Qed.

Lemma alloc_lo_0:
  forall m lo hi,
    alloc m lo hi = alloc m 0 hi.
Proof.
  unfold alloc, low_alloc.
  repeat destr.
  f_equal. f_equal.
  apply mkmem_ext; auto.
Qed.

Local Hint Resolve valid_block_alloc fresh_block_alloc valid_new_block: mem.
Local Hint Resolve valid_access_alloc_other valid_access_alloc_same: mem.

Properties related to free.


Theorem range_perm_free:
  forall m1 b lo hi,
  range_perm m1 b lo hi Cur Freeable ->
  { m2: mem | free m1 b lo hi = Some m2 }.
Proof.
  intros; unfold free. rewrite pred_dec_true; auto. econstructor; eauto.
Defined.

Section FREE.

Variable m1: mem.
Variable bf: block.
Variables lo hi: Z.
Variable m2: mem.
Hypothesis FREE: free m1 bf lo hi = Some m2.

Theorem free_range_perm:
  range_perm m1 bf lo hi Cur Freeable.
Proof.
  unfold free in FREE. destruct (range_perm_dec m1 bf lo hi Cur Freeable); auto.
  congruence.
Qed.

Lemma free_result:
  m2 = unchecked_free m1 bf lo hi.
Proof.
  unfold free in FREE. destruct (range_perm_dec m1 bf lo hi Cur Freeable).
  congruence. congruence.
Qed.

Theorem mask_free:
  forall b',
    bf <> b' ->
    mask m1 b' = mask m2 b'.
Proof.
  intros.
  rewrite free_result.
  unfold mask, unchecked_free; try reflexivity.
  simpl.
  rewrite PMap.gso by congruence.
  reflexivity.
Qed.

Theorem size_of_block_free:
  forall b',
    bf <> b' ->
    size_of_block m1 b' = size_of_block m2 b'.
Proof.
  intros.
  rewrite free_result.
  unfold size_of_block, unchecked_free; try reflexivity.
  simpl.
  rewrite PMap.gso by congruence; reflexivity.
Qed.

Theorem nextblock_free:
  nextblock m2 = nextblock m1.
Proof.
  rewrite free_result; reflexivity.
Qed.

Theorem valid_block_free_1:
  forall b, valid_block m1 b -> valid_block m2 b.
Proof.
  intros. rewrite free_result. assumption.
Qed.

Theorem valid_block_free_2:
  forall b, valid_block m2 b -> valid_block m1 b.
Proof.
  intros. rewrite free_result in H. assumption.
Qed.

Local Hint Resolve valid_block_free_1 valid_block_free_2: mem.

Theorem perm_free_1:
  forall b ofs k p,
  b <> bf \/ ofs < lo \/ hi <= ofs ->
  perm m1 b ofs k p ->
  perm m2 b ofs k p.
Proof.
  intros. rewrite free_result. unfold perm, unchecked_free; simpl.
  rewrite PMap.gsspec. destruct (peq b bf). subst b.
  destruct (zle lo ofs); simpl.
  destruct (zlt ofs hi); simpl.
  elimtype False; intuition.
  auto. auto.
  auto.
Qed.

Theorem perm_free_2:
  forall ofs k p, lo <= ofs < hi -> ~ perm m2 bf ofs k p.
Proof.
  intros. rewrite free_result. unfold perm, unchecked_free; simpl.
  rewrite PMap.gss. unfold proj_sumbool. rewrite zle_true. rewrite zlt_true.
  simpl. tauto. omega. omega.
Qed.

Theorem perm_free_3:
  forall b ofs k p,
  perm m2 b ofs k p -> perm m1 b ofs k p.
Proof.
  intros until p. rewrite free_result. unfold perm, unchecked_free; simpl.
  rewrite PMap.gsspec. destruct (peq b bf). subst b.
  destruct (zle lo ofs); simpl.
  destruct (zlt ofs hi); simpl. tauto.
  auto. auto. auto.
Qed.

Theorem perm_free_inv:
  forall b ofs k p,
  perm m1 b ofs k p ->
  (b = bf /\ lo <= ofs < hi) \/ perm m2 b ofs k p.
Proof.
  intros. rewrite free_result. unfold perm, unchecked_free; simpl.
  rewrite PMap.gsspec. destruct (peq b bf); auto. subst b.
  destruct (zle lo ofs); simpl; auto.
  destruct (zlt ofs hi); simpl; auto.
Qed.

Theorem valid_access_free_1:
  forall chunk b ofs p,
  valid_access m1 chunk b ofs p ->
  b <> bf \/ lo >= hi \/ ofs + size_chunk chunk <= lo \/ hi <= ofs ->
  valid_access m2 chunk b ofs p.
Proof.
  intros. inv H. constructor; auto with mem.
  red; intros. eapply perm_free_1; eauto.
  destruct (zlt lo hi). intuition. right. omega.
Qed.

Theorem valid_access_free_2:
  forall chunk ofs p,
  lo < hi -> ofs + size_chunk chunk > lo -> ofs < hi ->
  ~(valid_access m2 chunk bf ofs p).
Proof.
  intros; red; intros. inv H2.
  generalize (size_chunk_pos chunk); intros.
  destruct (zlt ofs lo).
  elim (perm_free_2 lo Cur p).
  omega. apply H3. omega.
  elim (perm_free_2 ofs Cur p).
  omega. apply H3. omega.
Qed.

Theorem valid_access_free_inv_1:
  forall chunk b ofs p,
  valid_access m2 chunk b ofs p ->
  valid_access m1 chunk b ofs p.
Proof.
  intros. destruct H. split; auto.
  red; intros. generalize (H ofs0 H1).
  rewrite free_result. unfold perm, unchecked_free; simpl.
  rewrite PMap.gsspec. destruct (peq b bf). subst b.
  destruct (zle lo ofs0); simpl.
  destruct (zlt ofs0 hi); simpl.
  tauto. auto. auto. auto.
Qed.

Theorem valid_access_free_inv_2:
  forall chunk ofs p,
  valid_access m2 chunk bf ofs p ->
  lo >= hi \/ ofs + size_chunk chunk <= lo \/ hi <= ofs.
Proof.
  intros.
  destruct (zlt lo hi); auto.
  destruct (zle (ofs + size_chunk chunk) lo); auto.
  destruct (zle hi ofs); auto.
  elim (valid_access_free_2 chunk ofs p); auto. omega.
Qed.

Theorem load_free:
  forall chunk b ofs,
  b <> bf \/ lo >= hi \/ ofs + size_chunk chunk <= lo \/ hi <= ofs ->
  load chunk m2 b ofs = load chunk m1 b ofs.
Proof.
  intros. unfold load.
  destruct (valid_access_dec m2 chunk b ofs Readable).
  rewrite pred_dec_true.
  rewrite free_result; auto.
  eapply valid_access_free_inv_1; eauto.
  rewrite pred_dec_false; auto.
  red; intro; elim n. eapply valid_access_free_1; eauto.
Qed.

Theorem load_free_2:
  forall chunk b ofs v,
  load chunk m2 b ofs = Some v -> load chunk m1 b ofs = Some v.
Proof.
  intros. unfold load. rewrite pred_dec_true.
  rewrite (load_result _ _ _ _ _ H). rewrite free_result; auto.
  apply valid_access_free_inv_1. eauto with mem.
Qed.

Theorem loadbytes_free:
  forall b ofs n,
  b <> bf \/ lo >= hi \/ ofs + n <= lo \/ hi <= ofs ->
  loadbytes m2 b ofs n = loadbytes m1 b ofs n.
Proof.
  intros. unfold loadbytes.
  destruct (range_perm_dec m2 b ofs (ofs + n) Cur Readable).
  rewrite pred_dec_true.
  rewrite free_result; auto.
  red; intros. eapply perm_free_3; eauto.
  rewrite pred_dec_false; auto.
  red; intros. elim n0; red; intros.
  eapply perm_free_1; eauto. destruct H; auto. right; omega.
Qed.

Theorem loadbytes_free_2:
  forall b ofs n bytes,
  loadbytes m2 b ofs n = Some bytes -> loadbytes m1 b ofs n = Some bytes.
Proof.
  intros. unfold loadbytes in *.
  destruct (range_perm_dec m2 b ofs (ofs + n) Cur Readable); inv H.
  rewrite pred_dec_true. rewrite free_result; auto.
  red; intros. apply perm_free_3; auto.
Qed.

End FREE.



Lemma unchecked_free_bounds_of_block:
  forall m b lo hi,
    bounds_of_block (unchecked_free m b lo hi) b =
    match (bounds_of_block m b) with
        (lo',hi') => if zeq lo' lo && zeq hi' hi then (0,0) else (lo',hi')
    end.
Proof.
  intros; unfold unchecked_free, bounds_of_block; simpl.
  rewrite PMap.gss.
  des ((mem_blocksize m) # b). des p.
  destr.
  repeat destr.
Qed.

Lemma unchecked_free_bounds_of_block_other:
  forall m b lo hi b', b <> b' ->
    bounds_of_block (unchecked_free m b lo hi) b' =
    bounds_of_block m b'.
Proof.
  intros; unfold unchecked_free, bounds_of_block; simpl.
  rewrite PMap.gso; auto.
Qed.

Lemma unchecked_free_bounds:
  forall m b lo hi b',
    bounds_of_block (unchecked_free m b lo hi) b' =
    if eq_block b' b
    then match bounds_of_block m b with
             (lo',hi') => (if zeq lo' lo && zeq hi' hi
                           then (0,0)
                           else (lo',hi'))
         end
    else bounds_of_block m b'.
Proof.
  intros.
  Opaque zeq.
  unfold unchecked_free, bounds_of_block, eq_block; simpl.
  rewrite PMap.gsspec.
  unfold get_size.
  des (mem_blocksize m) # b;
    destruct (peq b' b); subst; destr; try des p;
    unfold bounds_of_block; try rewrite Heqo; destr;
    try rewrite Heqo0; auto.
  inv Heqo0. auto.
Qed.

Lemma bounds_free:
  forall m m' b b' lo hi ,
  Mem.free m b lo hi = Some m' ->
  b <> b' ->
  Mem.bounds_of_block m' b' = Mem.bounds_of_block m b'.
Proof.
  intros.
  unfold Mem.free in H.
  destruct (Mem.range_perm_dec m b lo hi Cur Freeable); try congruence.
  inv H.
  unfold Mem.unchecked_free. unfold Mem.bounds_of_block; simpl.
  rewrite PMap.gso by congruence. auto.
Qed.


Lemma bounds_freelist:
  forall fbl m m' b ,
  Mem.free_list m fbl = Some m' ->
  ~ In b (map (fun a => fst (fst a)) fbl) ->
  Mem.bounds_of_block m' b = Mem.bounds_of_block m b.
Proof.
  induction fbl; simpl; intros.
  inv H. auto.
  destruct a as [[b' lo] hi]. destruct (Mem.free m b' lo hi) eqn:?; try congruence.
  intros. intuition try congruence. simpl in *.
  generalize (IHfbl _ _ _ H H2).
  intro A; rewrite A.
  erewrite bounds_free; eauto.
Qed.

Lemma range_perm_freelist:
  forall fbl m m' b,
    Mem.free_list m fbl = Some m' ->
    ~ In b (map (fun a => fst (fst a)) fbl) ->
    forall lo hi k p,
      Mem.range_perm m' b lo hi k p ->
      Mem.range_perm m b lo hi k p.
Proof.
  induction fbl; simpl; intros.
  inv H. auto.
  destruct a as [[b' lo'] hi']. destruct (Mem.free m b' lo' hi') eqn:?; try congruence.
  intros. intuition try congruence. simpl in *.
  generalize (IHfbl _ _ _ H H3 _ _ _ _ H1).
  unfold Mem.range_perm. intros.
  specialize (H0 ofs H4).
  eapply Mem.perm_free_3; eauto.
Qed.


Lemma mask_freelist:
  forall fbl m m' b ,
  Mem.free_list m fbl = Some m' ->
  ~ In b (map (fun a => fst (fst a)) fbl) ->
  Mem.mask m' b = Mem.mask m b.
Proof.
  induction fbl; simpl; intros.
  inv H. auto.
  destruct a as [[b' lo] hi]. destruct (Mem.free m b' lo hi) eqn:?; try congruence.
  intros. intuition try congruence. simpl in *.
  generalize (IHfbl _ _ _ H H2).
  intro A; rewrite A.
  erewrite <- Mem.mask_free; eauto.
Qed.



Lemma contents_free:
  forall m b lo hi m' b',
    Mem.free m b lo hi = Some m' ->
    b <> b' ->
    (Mem.mem_contents m) !! b' = (Mem.mem_contents m') !! b'.
Proof.
  intros.
  unfold free in H.
  destruct Mem.range_perm_dec; try congruence.
  inv H.
  unfold Mem.unchecked_free; simpl. auto.
Qed.


Lemma contents_freelist:
  forall fbl m m' b',
    Mem.free_list m fbl = Some m' ->
    ~ In b' (map (fun a => fst (fst a)) fbl) ->
    (Mem.mem_contents m) !! b' = (Mem.mem_contents m') !! b'.
Proof.
  induction fbl; simpl; intros.
  inv H; auto.
  destruct a as [[b lo'] hi'].
  destruct (Mem.free m b lo' hi') eqn:?; intuition try congruence.
  simpl in *.
  rewrite (contents_free _ _ _ _ _ _ Heqo H1).
  eauto.
Qed.

Lemma freelist_valid:
  forall fbl m m' b,
    Mem.free_list m fbl = Some m' ->
    ~ Mem.valid_block m' b ->
    ~ Mem.valid_block m b.
Proof.
  induction fbl; simpl; intros.
  inv H; auto.
  destruct a as [[b0 lo] hi].
  destruct (Mem.free m b0 lo hi) eqn:?; try congruence.
  generalize (IHfbl _ _ _ H H0).
  intros.
  intro.
  generalize (Mem.valid_block_free_1 _ _ _ _ _ Heqo _ H2); auto.
Qed.

Lemma free_size_mem:
  forall m1 lo hi m1' b1
         (FREE: free m1 b1 lo hi = Some m1')
         (bnds: bounds_of_block m1 b1 = (lo,hi)),
    size_mem m1 =
    size_mem m1' + align (Z.max 0 (hi-lo)) 8.
Proof.
  intros.
  unfold free in FREE.
  revert FREE; destr.
  clear Heqs. inv FREE.
  unfold size_mem, mk_block_list.
  rewrite (Alloc.size_mem_free_eq'
                (Mem.size_block (Mem.unchecked_free m1 b1 lo hi))
                (Mem.size_block m1)
                _
                b1); intros; eauto.
  - destr.
    + f_equal. f_equal. unfold size_block.
      unfold get_size.
      unfold bounds_of_block in bnds.
      destr; try des p; inv bnds.
      rewrite Z.sub_0_r; auto.
    + des (zlt lo hi).
      * exfalso.
        specialize (r lo). trim r. omega.
        apply perm_valid_block in r.
        unfold valid_block in r.
        clear -r n. xomega.
      * replace (align (Z.max 0 (hi - lo)) 8) with 0. auto.
        rewrite Zmax_spec; destr; simpl; auto.
        replace (hi - lo) with 0 by omega. auto.
  - generalize (unchecked_free_bounds_of_block_other m1 b1 lo hi b').
    unfold bounds_of_block, size_block, Alloc.get_size.
    destr; try des p;
    destr; try des p;
    inv H1; omega.
  - generalize (Mem.unchecked_free_bounds_of_block m1 b1 lo hi).
    rewrite bnds.
    des (zeq lo lo); des (zeq hi hi).
    revert H.
    unfold bounds_of_block, size_block, get_size. destr.
    rewrite PMap.gss in Heqo.
    des p. inv H; omega.
Qed.

Definition size_mem_blocks (bl: list (block * Z * Z)) (z:Z) : Z :=
  fold_left
    (fun acc x =>
       align acc 8 + Z.max 0 (snd x - snd (fst x))) bl z.

Definition list_of_blocks (bl: list (block*Z*Z)) : list (block*Z) :=
  map (fun a : ident * Z * Z => (fst (fst a), snd a - snd (fst a))) bl.

Lemma size_mem_blocks_al:
  forall l z,
    size_mem_al 8 (list_of_blocks l) (align z 8) =
    align (size_mem_blocks l z) 8.
Proof.
  induction l; simpl; intros. auto.
  rewrite IHl.
  rewrite Alloc.align_align. auto. omega.
Qed.

Lemma size_mem_al_rev:
  forall l z,
    size_mem_al 8 l z = size_mem_al 8 (rev l) z.
Proof.
  intros.
  apply size_mem_al_permut; try omega.
  apply Permutation.Permutation_rev.
Qed.

Lemma size_mem_app:
  forall a b c,
    size_mem_al 8 (a++b) c = size_mem_al 8 b (size_mem_al 8 a c).
Proof.
  intros.
  unfold size_mem_al.
  rewrite fold_left_app. auto.
Qed.

Lemma freelist_size_mem:
  forall bl m m'
         (bnds: Forall (fun bbnds : ident*Z*Z => Mem.bounds_of_block m (fst (fst bbnds))
                                                 = (snd (fst bbnds), snd (bbnds)) ) bl)
         (FL: Mem.free_list m bl = Some m')
         (NR: list_norepet (map fst (map fst bl))),
    size_mem m = align (size_mem_blocks bl (size_mem m')) 8.
Proof.
  induction bl; simpl; intros.
  inv FL. symmetry. eapply size_mem_aligned; eauto.
  des a; des p.
  revert FL; destr.
  inv bnds. inv NR.
  apply IHbl in FL; auto. simpl in *.
  apply free_size_mem in Heqo; auto.
  rewrite Heqo.
  rewrite FL.
  rewrite align_distr.
  rewrite <- ! size_mem_blocks_al.
  setoid_rewrite (size_mem_al_rev ((xH,z-z0)::(list_of_blocks bl)) (size_mem m')).
  simpl.
  rewrite size_mem_app. simpl.
  rewrite <- (size_mem_al_rev (list_of_blocks bl) (size_mem m')).
  rewrite <- align_distr.
  erewrite (size_mem_aligned _ _ eq_refl); eauto.
  rewrite (size_mem_al_aligned _ _) at 1.
  rewrite <- align_distr. auto.
  symmetry. eapply size_mem_aligned; eauto.
  simpl in H1.
    rewrite Forall_forall in *.
    intros.
    generalize (H2 _ H); intro A.
    erewrite bounds_free; eauto.
    intro; subst.
    apply in_map with (f:=fst) in H.
    apply in_map with (f:=fst) in H. auto.
Qed.

Local Hint Resolve valid_block_free_1 valid_block_free_2
             perm_free_1 perm_free_2 perm_free_3
             valid_access_free_1 valid_access_free_inv_1: mem.

Properties related to drop_perm


Theorem range_perm_drop_1:
  forall m b lo hi p m', drop_perm m b lo hi p = Some m' -> range_perm m b lo hi Cur Freeable.
Proof.
  unfold drop_perm; intros.
  destruct (range_perm_dec m b lo hi Cur Freeable). auto. discriminate.
Qed.

Theorem range_perm_drop_2:
  forall m b lo hi p,
  range_perm m b lo hi Cur Freeable -> {m' | drop_perm m b lo hi p = Some m' }.
Proof.
  unfold drop_perm; intros.
  destruct (range_perm_dec m b lo hi Cur Freeable). econstructor. eauto. contradiction.
Defined.

Section DROP.

Variable m: mem.
Variable b: block.
Variable lo hi: Z.
Variable p: permission.
Variable m': mem.
Hypothesis DROP: drop_perm m b lo hi p = Some m'.

Theorem mask_drop_perm:
  forall b',
    mask m b' = mask m' b'.
Proof.
  revert DROP; unfold drop_perm; destr_cond_match; try congruence.
  intro A; inv A.
  intros.
  unfold mask; reflexivity.
Qed.

Theorem size_of_block_drop_perm:
  forall b',
    size_of_block m b' = size_of_block m' b'.
Proof.
  revert DROP; unfold drop_perm; destr_cond_match; try congruence.
  intro A; inv A.
  intros.
  unfold size_of_block; reflexivity.
Qed.

Theorem nextblock_drop:
  nextblock m' = nextblock m.
Proof.
  unfold drop_perm in DROP. destruct (range_perm_dec m b lo hi Cur Freeable); inv DROP; auto.
Qed.

Theorem drop_perm_valid_block_1:
  forall b', valid_block m b' -> valid_block m' b'.
Proof.
  unfold valid_block; rewrite nextblock_drop; auto.
Qed.

Theorem drop_perm_valid_block_2:
  forall b', valid_block m' b' -> valid_block m b'.
Proof.
  unfold valid_block; rewrite nextblock_drop; auto.
Qed.

Theorem perm_drop_1:
  forall ofs k, lo <= ofs < hi -> perm m' b ofs k p.
Proof.
  intros.
  unfold drop_perm in DROP. destruct (range_perm_dec m b lo hi Cur Freeable); inv DROP.
  unfold perm. simpl. rewrite PMap.gss. unfold proj_sumbool.
  rewrite zle_true. rewrite zlt_true. simpl. constructor.
  omega. omega.
Qed.
  
Theorem perm_drop_2:
  forall ofs k p', lo <= ofs < hi -> perm m' b ofs k p' -> perm_order p p'.
Proof.
  intros.
  unfold drop_perm in DROP. destruct (range_perm_dec m b lo hi Cur Freeable); inv DROP.
  revert H0. unfold perm; simpl. rewrite PMap.gss. unfold proj_sumbool.
  rewrite zle_true. rewrite zlt_true. simpl. auto.
  omega. omega.
Qed.

Theorem perm_drop_3:
  forall b' ofs k p', b' <> b \/ ofs < lo \/ hi <= ofs -> perm m b' ofs k p' -> perm m' b' ofs k p'.
Proof.
  intros.
  unfold drop_perm in DROP. destruct (range_perm_dec m b lo hi Cur Freeable); inv DROP.
  unfold perm; simpl. rewrite PMap.gsspec. destruct (peq b' b). subst b'.
  unfold proj_sumbool. destruct (zle lo ofs). destruct (zlt ofs hi).
  byContradiction. intuition omega.
  auto. auto. auto.
Qed.

Theorem perm_drop_4:
  forall b' ofs k p', perm m' b' ofs k p' -> perm m b' ofs k p'.
Proof.
  intros.
  unfold drop_perm in DROP. destruct (range_perm_dec m b lo hi Cur Freeable); inv DROP.
  revert H. unfold perm; simpl. rewrite PMap.gsspec. destruct (peq b' b).
  subst b'. unfold proj_sumbool. destruct (zle lo ofs). destruct (zlt ofs hi).
  simpl. intros. apply perm_implies with p. apply perm_implies with Freeable. apply perm_cur.
  apply r. tauto. auto with mem. auto.
  auto. auto. auto.
Qed.

Lemma valid_access_drop_1:
  forall chunk b' ofs p',
  b' <> b \/ ofs + size_chunk chunk <= lo \/ hi <= ofs \/ perm_order p p' ->
  valid_access m chunk b' ofs p' -> valid_access m' chunk b' ofs p'.
Proof.
  intros. destruct H0. split; auto.
  red; intros.
  destruct (eq_block b' b). subst b'.
  destruct (zlt ofs0 lo). eapply perm_drop_3; eauto.
  destruct (zle hi ofs0). eapply perm_drop_3; eauto.
  apply perm_implies with p. eapply perm_drop_1; eauto. omega.
  generalize (size_chunk_pos chunk); intros. intuition.
  eapply perm_drop_3; eauto.
Qed.

Lemma valid_access_drop_2:
  forall chunk b' ofs p',
  valid_access m' chunk b' ofs p' -> valid_access m chunk b' ofs p'.
Proof.
  intros. destruct H; split; auto.
  red; intros. eapply perm_drop_4; eauto.
Qed.

Theorem load_drop:
  forall chunk b' ofs,
  b' <> b \/ ofs + size_chunk chunk <= lo \/ hi <= ofs \/ perm_order p Readable ->
  load chunk m' b' ofs = load chunk m b' ofs.
Proof.
  intros.
  unfold load.
  destruct (valid_access_dec m chunk b' ofs Readable).
  rewrite pred_dec_true.
  unfold drop_perm in DROP. destruct (range_perm_dec m b lo hi Cur Freeable); inv DROP. simpl. auto.
  eapply valid_access_drop_1; eauto.
  rewrite pred_dec_false. auto.
  red; intros; elim n. eapply valid_access_drop_2; eauto.
Qed.

Theorem loadbytes_drop:
  forall b' ofs n,
  b' <> b \/ ofs + n <= lo \/ hi <= ofs \/ perm_order p Readable ->
  loadbytes m' b' ofs n = loadbytes m b' ofs n.
Proof.
  intros.
  unfold loadbytes.
  destruct (range_perm_dec m b' ofs (ofs + n) Cur Readable).
  rewrite pred_dec_true.
  unfold drop_perm in DROP. destruct (range_perm_dec m b lo hi Cur Freeable); inv DROP. simpl. auto.
  red; intros.
  destruct (eq_block b' b). subst b'.
  destruct (zlt ofs0 lo). eapply perm_drop_3; eauto.
  destruct (zle hi ofs0). eapply perm_drop_3; eauto.
  apply perm_implies with p. eapply perm_drop_1; eauto. omega. intuition.
  eapply perm_drop_3; eauto.
  rewrite pred_dec_false; eauto.
  red; intros; elim n0; red; intros.
  eapply perm_drop_4; eauto.
Qed.

End DROP.

Generic injections


A memory state m1 generically injects into another memory state m2 via the memory injection f if the following conditions hold:

Record mem_inj (map_undef: Val.partial_undef_allocation) (f: meminj) (m1 m2: mem) : Prop :=
  mk_mem_inj {
    mi_perm:
      forall b1 b2 delta ofs k p,
      f b1 = Some(b2, delta) ->
      perm m1 b1 ofs k p ->
      perm m2 b2 (ofs + delta) k p;
    mi_bounds: (* we mimic mi_perm with bounds instead of permissions. *)
      forall b1 b2 delta ofs,
      f b1 = Some(b2, delta) ->
      in_bound ofs (bounds_of_block m1 b1) ->
      in_bound (ofs+delta) (bounds_of_block m2 b2);
    mi_align':
      forall b b0 z, (* we can now talk of the alignment of blocks *)
        f b = Some (b0, z) ->
        (mask m2 b0 >= mask m1 b)%nat /\
        Z.divide (two_p (Z.of_nat (mask m1 b))) z;
    mi_memval:
      forall b1 ofs b2 delta,
      f b1 = Some(b2, delta) ->
      perm m1 b1 ofs Cur Readable ->
      memval_inject map_undef f
                    (ZMap.get ofs m1.(mem_contents)#b1)
                    (ZMap.get (ofs+delta) m2.(mem_contents)#b2);
    mi_size_mem: (* m2 must be ``smaller'' than m1. *)
      (size_mem m2) <= (size_mem m1)
  }.

Lemma perm_bounds:
  forall m b ofs p k,
    Mem.perm m b ofs k p ->
    in_bound ofs (Mem.bounds_of_block m b).
Proof.
  unfold Mem.perm, Mem.perm_order'.
  intros.
  destruct ((Mem.mem_access m) !! b ofs k) eqn:?; intuition try congruence.
  generalize (Mem.access_bounds_ok m b ofs k).
  rewrite Heqo.
  intros. unfold Mem.bounds_of_block, get_bounds.
  destruct ((Mem.mem_blocksize m) !! b) eqn:?; eauto.
  destruct p1.
  unfold get_bounds in H0.
  rewrite Heqo0 in H0.
  apply H0; congruence.
  unfold get_bounds in H0.
  rewrite Heqo0 in H0.
  exfalso; exploit H0; eauto. congruence. unfold in_bound. simpl. omega.
Qed.


Lemma load_in_bound:
  forall chunk b ofs m v,
    load chunk m b ofs = Some v ->
    in_bound ofs (Mem.bounds_of_block m b).
Proof.
  intros.
  apply load_valid_access in H.
  destruct H as [A B].
  eapply perm_bounds.
  apply A; auto. destruct chunk; simpl; omega.
Qed.

Lemma mi_align:
  forall f um m1 m2,
    mem_inj um f m1 m2 ->
  forall (b1 b2: block) delta,
    f b1 = Some (b2, delta) ->
    forall chunk ofs p,
      range_perm m1 b1 ofs (ofs + size_chunk chunk) Max p ->
      (align_chunk chunk | delta).
Proof.
  intros.
  assert (size_block m1 b1 >= size_chunk chunk).
  {
    generalize (H1 ofs).
    generalize (H1 (ofs + size_chunk chunk - 1)).
    intros A B.
    trim A. des chunk; omega.
    trim B. des chunk; omega.
    apply perm_bounds in A.
    apply perm_bounds in B.
    unfold bounds_of_block in *.
    unfold size_block, get_size.
    destr; try des p0; unfold in_bound in *; des chunk; simpl in *; try omega.
  }
  generalize (mi_align' _ _ _ _ H _ _ _ H0).
  intros [_ A].
  eapply Zdivides_trans; eauto.
  unfold mask.
  generalize (bounds_mask_consistency m1 b1).
  destr.
  - apply alignment_ok in Heqo. subst.
    unfold size_block in H2. unfold get_size in *.
    destr.
    des p0.
    apply Zdivides_trans with (y:=two_p (Z.of_nat (alignment_of_size (size_chunk chunk)))).
    des chunk;
      try (exists 1; simpl; auto; fail);
      try (exists 2; simpl; auto; fail).
    des chunk;
      try apply Z.divide_1_l;
    unfold alignment_of_size; repeat destruct zlt; try omega;
    try (exists 1; simpl; auto; fail);
    try (exists 2; simpl; auto; fail);
    try (exists 3; simpl; auto; fail);
    try (exists 4; simpl; auto).
  - unfold size_block, get_size in H2.
    rewrite H5 in H2.
    des chunk; omega.
Qed.

Preservation of permissions

Lemma perm_inj:
  forall m f m1 m2 b1 ofs k p b2 delta,
  mem_inj m f m1 m2 ->
  perm m1 b1 ofs k p ->
  f b1 = Some(b2, delta) ->
  perm m2 b2 (ofs + delta) k p.
Proof.
  intros. eapply mi_perm; eauto.
Qed.

Lemma range_perm_inj:
  forall m f m1 m2 b1 lo hi k p b2 delta,
  mem_inj m f m1 m2 ->
  range_perm m1 b1 lo hi k p ->
  f b1 = Some(b2, delta) ->
  range_perm m2 b2 (lo + delta) (hi + delta) k p.
Proof.
  intros; red; intros.
  replace ofs with ((ofs - delta) + delta) by omega.
  eapply perm_inj; eauto. apply H0. omega.
Qed.

Lemma valid_access_inj:
  forall m f m1 m2 b1 b2 delta chunk ofs p,
  mem_inj m f m1 m2 ->
  f b1 = Some(b2, delta) ->
  valid_access m1 chunk b1 ofs p ->
  valid_access m2 chunk b2 (ofs + delta) p.
Proof.
  intros. destruct H1 as [A B]. constructor.
  replace (ofs + delta + size_chunk chunk)
     with ((ofs + size_chunk chunk) + delta) by omega.
  eapply range_perm_inj; eauto.
  apply Z.divide_add_r; auto. eapply mi_align; eauto with mem.
Qed.

Preservation of loads.

Lemma getN_inj:
  forall m f m1 m2 b1 b2 delta,
  mem_inj m f m1 m2 ->
  f b1 = Some(b2, delta) ->
  forall n ofs,
  range_perm m1 b1 ofs (ofs + Z_of_nat n) Cur Readable ->
  list_forall2 (memval_inject m f)
               (getN n ofs (m1.(mem_contents)#b1))
               (getN n (ofs + delta) (m2.(mem_contents)#b2)).
Proof.
  induction n; intros; simpl.
  constructor.
  rewrite inj_S in H1.
  constructor.
  eapply mi_memval; eauto.
  apply H1. omega.
  replace (ofs + delta + 1) with ((ofs + 1) + delta) by omega.
  apply IHn. red; intros; apply H1; omega.
Qed.

Lemma load_inj:
  forall m f m1 m2 chunk b1 ofs b2 delta v1,
  mem_inj m f m1 m2 ->
  load chunk m1 b1 ofs = Some v1 ->
  f b1 = Some (b2, delta) ->
  exists v2, load chunk m2 b2 (ofs + delta) = Some v2 /\ Val.expr_inject m f v1 v2.
Proof.
  intros.
  exists (decode_val chunk (getN (size_chunk_nat chunk) (ofs + delta) (m2.(mem_contents)#b2))).
  split. unfold load. apply pred_dec_true.
  eapply valid_access_inj; eauto with mem.
  exploit load_result; eauto. intro. rewrite H2.
  apply decode_val_inject.
  apply wt_memval_getN. intros; apply contents_wt_memval.
  apply wt_memval_getN. intros; apply contents_wt_memval.
  apply getN_inj; auto.
  rewrite <- size_chunk_conv. exploit load_valid_access; eauto. intros [A B]. auto.
Qed.

Lemma loadbytes_inj:
  forall m f m1 m2 len b1 ofs b2 delta bytes1,
  mem_inj m f m1 m2 ->
  loadbytes m1 b1 ofs len = Some bytes1 ->
  f b1 = Some (b2, delta) ->
  exists bytes2, loadbytes m2 b2 (ofs + delta) len = Some bytes2
              /\ list_forall2 (memval_inject m f) bytes1 bytes2.
Proof.
  intros. unfold loadbytes in *.
  destruct (range_perm_dec m1 b1 ofs (ofs + len) Cur Readable); inv H0.
  exists (getN (nat_of_Z len) (ofs + delta) (m2.(mem_contents)#b2)).
  split. apply pred_dec_true.
  replace (ofs + delta + len) with ((ofs + len) + delta) by omega.
  eapply range_perm_inj; eauto with mem.
  apply getN_inj; auto.
  destruct (zle 0 len). rewrite nat_of_Z_eq; auto. omega.
  rewrite nat_of_Z_neg. simpl. red; intros; omegaContradiction. omega.
Qed.

Preservation of stores.

Lemma setN_inj:
  forall (access: Z -> Prop) delta m f vl1 vl2,
  list_forall2 (memval_inject m f) vl1 vl2 ->
  forall p c1 c2,
  (forall q, access q -> memval_inject m f (ZMap.get q c1) (ZMap.get (q + delta) c2)) ->
  (forall q, access q -> memval_inject m f (ZMap.get q (setN vl1 p c1))
                                       (ZMap.get (q + delta) (setN vl2 (p + delta) c2))).
Proof.
  induction 1; intros; simpl.
  auto.
  replace (p + delta + 1) with ((p + 1) + delta) by omega.
  apply IHlist_forall2; auto.
  intros. rewrite ZMap.gsspec at 1. destruct (ZIndexed.eq q0 p). subst q0.
  rewrite ZMap.gss. auto.
  rewrite ZMap.gso. auto. unfold ZIndexed.t in *. omega.
Qed.

Definition meminj_no_overlap (f: meminj) (m: mem) : Prop :=
  forall b1 b1' delta1 b2 b2' delta2 ofs1 ofs2,
  b1 <> b2 ->
  f b1 = Some (b1', delta1) ->
  f b2 = Some (b2', delta2) ->
  perm m b1 ofs1 Max Nonempty ->
  perm m b2 ofs2 Max Nonempty ->
  b1' <> b2' \/ ofs1 + delta1 <> ofs2 + delta2.

Definition meminj_no_overlap' (f: meminj) (m: mem) : Prop :=
  forall b1 b1' delta1 b2 b2' delta2 ofs1 ofs2,
  b1 <> b2 ->
  f b1 = Some (b1', delta1) ->
  f b2 = Some (b2', delta2) ->
  in_bound ofs1 (bounds_of_block m b1) ->
  in_bound ofs2 (bounds_of_block m b2) ->
  b1' <> b2' \/ ofs1 + delta1 <> ofs2 + delta2.


Lemma encode_val_tot_inject :
  forall (m : Val.partial_undef_allocation) (f : meminj)
         (v1 v2 : expr_sym) (chunk : memory_chunk),
    Val.expr_inject m f v1 v2 ->
    list_forall2 (memval_inject m f) (encode_val_tot chunk v1)
                 (encode_val_tot chunk v2).
Proof.
  intros.
  generalize (encode_val_inject m f v1 v2 chunk H).
  unfold encode_val_tot.
  repeat destr.
  constructor.
Qed.

Lemma store_size_block:
  forall chunk m b o v m',
    store chunk m b o v = Some m' ->
    size_block m' = size_block m.
Proof.
  unfold store; intros.
  repeat (revert H; destr).
  inv H; simpl.
  unfold size_block. simpl. auto.
Qed.


Lemma store_mask:
  forall chunk m b o v m',
    store chunk m b o v = Some m' ->
    mask m' = mask m.
Proof.
  unfold store; intros.
  repeat (revert H; destr).
  inv H; simpl.
  unfold mask. simpl. auto.
Qed.


Lemma store_mk_block_list:
  forall chunk m b o v m',
    store chunk m b o v = Some m' ->
    mk_block_list m' = mk_block_list m.
Proof.
  intros.
  exploit store_size_block; eauto.
  exploit store_mask; eauto.
  unfold store in H.
  repeat (revert H; destr).
  inv H; simpl.
  unfold mk_block_list. simpl.
  rewrite H1. auto.
Qed.

Lemma store_size_mem:
  forall chunk m b o v m',
    store chunk m b o v = Some m' ->
    size_mem m' = size_mem m.
Proof.
  intros.
  exploit store_mk_block_list; eauto.
  unfold store in H; intros.
  repeat (revert H; destr).
  inv H; simpl.
  unfold size_mem.
  rewrite H0. auto.
Qed.




Lemma size_block_store:
  forall chunk m1 b1 ofs v1 n1,
    store chunk m1 b1 ofs v1 = Some n1 ->
    forall b',
      size_block n1 b' = size_block m1 b'.
Proof.
  intros.
  unfold size_block, get_size.
  unfold store in H.
  repeat (revert H; destr).
  inv H; simpl in *. congruence.
  inv H; simpl in *; congruence.
  inv H. simpl in *. congruence.
Qed.

Lemma store_mapped_inj:
  forall m f chunk m1 b1 ofs v1 n1 m2 b2 delta v2,
  mem_inj m f m1 m2 ->
  store chunk m1 b1 ofs v1 = Some n1 ->
  meminj_no_overlap f m1 ->
  f b1 = Some (b2, delta) ->
  Val.expr_inject m f v1 v2 ->
  exists n2,
    store chunk m2 b2 (ofs + delta) v2 = Some n2
    /\ mem_inj m f n1 n2.
Proof.
  intros.
  assert (valid_access m2 chunk b2 (ofs + delta) Writable).
  { eapply valid_access_inj; eauto with mem. }
  destruct (valid_access_store _ _ _ _ v2 H4) as [n2 STORE].
  { generalize(store_some_type _ _ _ _ _ _ H0).
    apply Val.same_type in H3. congruence. }
  exists n2; split. auto.
  constructor.
  -
    intros. eapply perm_store_1; [eexact STORE|].
    eapply mi_perm; eauto.
    eapply perm_store_2; eauto.
  -
    intros.
    erewrite <- bounds_of_block_store; eauto.
    erewrite <- bounds_of_block_store in H6; eauto.
    eapply mi_bounds; eauto.
  - intros. erewrite store_mask; eauto.
    erewrite store_mask with (m':=n1); eauto.
    eapply mi_align'; eauto.
  -
    intros.
    rewrite (store_mem_contents _ _ _ _ _ _ H0).
    rewrite (store_mem_contents _ _ _ _ _ _ STORE).
    rewrite ! PMap.gsspec.
    destruct (peq b0 b1). subst b0.
    +
      assert (b3 = b2) by congruence. subst b3.
      assert (delta0 = delta) by congruence. subst delta0.
      rewrite peq_true.
      apply setN_inj with (access := fun ofs => perm m1 b1 ofs Cur Readable).
      apply encode_val_tot_inject; auto. intros. eapply mi_memval; eauto. eauto with mem.
    + destruct (peq b3 b2). subst b3.
     *
       rewrite setN_other. eapply mi_memval; eauto. eauto with mem.
       erewrite encode_val_tot_length; eauto. rewrite <- size_chunk_conv. intros.
       assert (b2 <> b2 \/ ofs0 + delta0 <> (r - delta) + delta).
       eapply H1; eauto. eauto 6 with mem.
       exploit store_valid_access_3. eexact H0. intros [A B].
       eapply perm_implies. apply perm_cur_max. apply A. omega. auto with mem.
       destruct H8. congruence. omega.
     *
       eapply mi_memval; eauto. eauto with mem.
  - apply store_size_mem in H0.
    apply store_size_mem in STORE.
    rewrite STORE. rewrite H0.
    inv H; auto.
Qed.

Lemma store_unmapped_inj:
  forall m f chunk m1 b1 ofs v1 n1 m2,
  mem_inj m f m1 m2 ->
  store chunk m1 b1 ofs v1 = Some n1 ->
  f b1 = None ->
  mem_inj m f n1 m2.
Proof.
  intros. constructor.
-
  intros. eapply mi_perm; eauto with mem.
-
    intros.
    erewrite <- bounds_of_block_store in H3; eauto.
    eapply mi_bounds; eauto.
  - intros.
    erewrite store_mask with (m':=n1); eauto.
    eapply mi_align'; eauto.
-
  intros.
  rewrite (store_mem_contents _ _ _ _ _ _ H0).
  rewrite PMap.gso. eapply mi_memval; eauto with mem.
  congruence.
- apply store_size_mem in H0.
  rewrite H0.
  inv H; auto.
Qed.

Lemma store_outside_inj:
  forall m f m1 m2 chunk b ofs v m2',
  mem_inj m f m1 m2 ->
  (forall b' delta ofs',
    f b' = Some(b, delta) ->
    perm m1 b' ofs' Cur Readable ->
    ofs <= ofs' + delta < ofs + size_chunk chunk -> False) ->
  store chunk m2 b ofs v = Some m2' ->
  mem_inj m f m1 m2'.
Proof.
  intros. inv H. constructor; eauto with mem.
  -
    intros.
    erewrite <- bounds_of_block_store; eauto.
  - intros. erewrite store_mask; eauto.
  -
    intros.
    rewrite (store_mem_contents _ _ _ _ _ _ H1).
    rewrite PMap.gsspec. destruct (peq b2 b). subst b2.
    rewrite setN_outside. auto.
    erewrite encode_val_tot_length; eauto. rewrite <- size_chunk_conv.
    destruct (zlt (ofs0 + delta) ofs); auto.
    destruct (zle (ofs + size_chunk chunk) (ofs0 + delta)). omega.
    byContradiction. eapply H0; eauto. omega.
    eauto with mem.
  - apply store_size_mem in H1.
     rewrite H1.
     auto.
Qed.

Lemma storebytes_size_block:
  forall m b o v m',
    storebytes m b o v = Some m' ->
    size_block m' = size_block m.
Proof.
  unfold storebytes; intros.
  repeat (revert H; destr).
  inv H; simpl.
  unfold size_block. simpl. auto.
Qed.


Lemma storebytes_mask:
  forall m b o v m',
    storebytes m b o v = Some m' ->
    mask m' = mask m.
Proof.
  unfold storebytes; intros.
  repeat (revert H; destr).
  inv H; simpl.
  unfold mask. simpl. auto.
Qed.


Lemma storebytes_mk_block_list:
  forall m b o v m',
    storebytes m b o v = Some m' ->
    mk_block_list m' = mk_block_list m.
Proof.
  intros.
  exploit storebytes_size_block; eauto.
  exploit storebytes_mask; eauto.
  unfold storebytes in H.
  repeat (revert H; destr).
  inv H; simpl.
  unfold mk_block_list. simpl.
  rewrite H1. auto.
Qed.

Lemma storebytes_size_mem:
  forall m b o v m',
    storebytes m b o v = Some m' ->
    size_mem m' = size_mem m.
Proof.
  intros.
  exploit storebytes_mk_block_list; eauto.
  unfold storebytes in H; intros.
  repeat (revert H; destr).
  inv H; simpl.
  unfold size_mem.
  rewrite H0. auto.
Qed.

Lemma size_block_storebytes:
  forall m1 b1 ofs lv1 n1,
    storebytes m1 b1 ofs lv1 = Some n1 ->
    forall b',
      size_block n1 b' = size_block m1 b'.
Proof.
  intros.
  unfold size_block, get_size.
  unfold storebytes in H.
  repeat (revert H; destr).
  inv H; simpl in *. congruence.
  inv H; simpl in *; congruence.
  inv H. simpl in *. congruence.
Qed.

Lemma storebytes_mapped_inj:
  forall m f m1 b1 ofs bytes1 n1 m2 b2 delta bytes2,
  mem_inj m f m1 m2 ->
  storebytes m1 b1 ofs bytes1 = Some n1 ->
  meminj_no_overlap f m1 ->
  f b1 = Some (b2, delta) ->
  list_forall2 (memval_inject m f) bytes1 bytes2 ->
  Forall wt_memval bytes2 ->
  exists n2,
    storebytes m2 b2 (ofs + delta) bytes2 = Some n2
    /\ mem_inj m f n1 n2.
Proof.
  intros. inversion H.
  assert (range_perm m2 b2 (ofs + delta) (ofs + delta + Z_of_nat (length bytes2)) Cur Writable).
  {
    replace (ofs + delta + Z_of_nat (length bytes2))
    with ((ofs + Z_of_nat (length bytes1)) + delta).
    eapply range_perm_inj; eauto with mem.
    eapply storebytes_range_perm; eauto.
    rewrite (list_forall2_length H3). omega.
  }
  destruct (range_perm_storebytes _ _ _ _ H5) as [n2 STORE].
  {
    rewrite check_bytes_Forall. auto.
  }
  exists n2; split. eauto.
  constructor.
  -
    intros.
    eapply perm_storebytes_1; [apply STORE |].
    eapply mi_perm0; eauto.
    eapply perm_storebytes_2; eauto.
  - intros.
    erewrite <- bounds_of_block_storebytes; eauto.
    erewrite <- bounds_of_block_storebytes in H7; eauto.
  - intros. erewrite storebytes_mask; eauto.
    erewrite storebytes_mask with (m':=n1); eauto.
  -
    intros.
    assert (perm m1 b0 ofs0 Cur Readable). eapply perm_storebytes_2; eauto.
    rewrite (storebytes_mem_contents _ _ _ _ _ H0).
    rewrite (storebytes_mem_contents _ _ _ _ _ STORE).
    rewrite ! PMap.gsspec. destruct (peq b0 b1). subst b0.
    +
      assert (b3 = b2) by congruence. subst b3.
      assert (delta0 = delta) by congruence. subst delta0.
      rewrite peq_true.
      apply setN_inj with (access := fun ofs => perm m1 b1 ofs Cur Readable); auto.
    + destruct (peq b3 b2). subst b3.
      *
        rewrite setN_other. auto.
        intros.
        assert (b2 <> b2 \/ ofs0 + delta0 <> (r - delta) + delta).
        eapply H1; eauto 6 with mem.
        exploit storebytes_range_perm. eexact H0.
        instantiate (1 := r - delta).
        rewrite (list_forall2_length H3). omega.
        eauto 6 with mem.
        destruct H10. congruence. omega.
      *
        eauto.
  - apply storebytes_size_mem in H0.
    apply storebytes_size_mem in STORE.
    rewrite STORE. rewrite H0.
    inv H; auto.
Qed.

Lemma storebytes_unmapped_inj:
  forall m f m1 b1 ofs bytes1 n1 m2,
    mem_inj m f m1 m2 ->
    storebytes m1 b1 ofs bytes1 = Some n1 ->
    f b1 = None ->
    mem_inj m f n1 m2.
Proof.
  intros. inversion H.
  constructor.
  -
    intros. eapply mi_perm0; eauto. eapply perm_storebytes_2; eauto.
  - intros.
     erewrite <- bounds_of_block_storebytes in H3; eauto.
  - intros.
    erewrite storebytes_mask with (m':=n1); eauto.
  -
    intros.
    rewrite (storebytes_mem_contents _ _ _ _ _ H0).
    rewrite PMap.gso. eapply mi_memval0; eauto. eapply perm_storebytes_2; eauto.
    congruence.
  - apply storebytes_size_mem in H0.
    rewrite H0. auto.
Qed.

Lemma storebytes_outside_inj:
  forall m f m1 m2 b ofs bytes2 m2',
    mem_inj m f m1 m2 ->
    (forall b' delta ofs',
       f b' = Some(b, delta) ->
       perm m1 b' ofs' Cur Readable ->
       ofs <= ofs' + delta < ofs + Z_of_nat (length bytes2) -> False) ->
    storebytes m2 b ofs bytes2 = Some m2' ->
    mem_inj m f m1 m2'.
Proof.
  intros. inversion H. constructor.
  -
    intros. eapply perm_storebytes_1; eauto with mem.
  - intros.
     erewrite <- bounds_of_block_storebytes; eauto.
  - intros. erewrite storebytes_mask; eauto.
  -
    intros.
    rewrite (storebytes_mem_contents _ _ _ _ _ H1).
    rewrite PMap.gsspec. destruct (peq b2 b). subst b2.
    rewrite setN_outside. auto.
    destruct (zlt (ofs0 + delta) ofs); auto.
    destruct (zle (ofs + Z_of_nat (length bytes2)) (ofs0 + delta)). omega.
    byContradiction. eapply H0; eauto. omega.
    eauto with mem.
  - apply storebytes_size_mem in H1.
    rewrite H1. auto.
Qed.

Lemma storebytes_empty_inj:
  forall m f m1 b1 ofs1 m1' m2 b2 ofs2 m2',
    mem_inj m f m1 m2 ->
    storebytes m1 b1 ofs1 nil = Some m1' ->
    storebytes m2 b2 ofs2 nil = Some m2' ->
    mem_inj m f m1' m2'.
Proof.
  intros. destruct H. constructor.
  -
    intros.
    eapply perm_storebytes_1; eauto.
    eapply mi_perm0; eauto.
    eapply perm_storebytes_2; eauto.
  - intros.
     erewrite <- bounds_of_block_storebytes; eauto.
     erewrite <- bounds_of_block_storebytes in H2; eauto.
  - intros.
    erewrite storebytes_mask with (m':=m2'); eauto.
    erewrite storebytes_mask with (m':=m1'); eauto.
  -
    intros.
    assert (perm m1 b0 ofs Cur Readable). eapply perm_storebytes_2; eauto.
    rewrite (storebytes_mem_contents _ _ _ _ _ H0).
    rewrite (storebytes_mem_contents _ _ _ _ _ H1).
    simpl. rewrite ! PMap.gsspec.
    destruct (peq b0 b1); destruct (peq b3 b2); subst; eapply mi_memval0; eauto.
  - apply storebytes_size_mem in H0.
    apply storebytes_size_mem in H1.
    rewrite H0. rewrite H1. auto.
Qed.

Preservation of allocations


Lemma alloc_size_mem:
  forall m1 lo hi m1' b1
         (ALLOC: alloc m1 lo hi = Some (m1', b1)),
    (size_mem m1) <= (size_mem m1').
Proof.
  intros.
  rewrite (alloc_size_mem' _ _ _ _ _ ALLOC).
  generalize (Zmax_bound_l 0 0 hi).
  generalize (align_le (Z.max 0 hi) 8).
  omega.
Qed.

Lemma alloc_left_unmapped_inj:
  forall m f m1 m2 lo hi m1' b1,
  mem_inj m f m1 m2 ->
  alloc m1 lo hi = Some (m1', b1) ->
  f b1 = None ->
  mem_inj m f m1' m2.
Proof.
  intros. inversion H. constructor.
-
  intros. exploit perm_alloc_inv; eauto. intros.
  destruct (eq_block b0 b1). congruence. eauto.
- intros.
  erewrite <- bounds_of_block_alloc_other in H3; eauto.
  congruence.
- intros.
  erewrite <- mask_alloc_other with (m2:=m1'); eauto.
  congruence.
-
  generalize H0; intro ALLOC.
  unfold alloc, low_alloc in ALLOC.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  injection ALLOC; intros NEXT MEM. intros.
  rewrite <- MEM; simpl. rewrite NEXT.
  exploit perm_alloc_inv; eauto. intros.
  rewrite PMap.gsspec. unfold eq_block in H4. destruct (peq b0 b1). subst.
  congruence.
  eauto.
- generalize (alloc_size_mem _ _ _ _ _ H0).
  omega.
Qed.

Definition inj_offset_aligned (delta: Z) (size: Z) : Prop :=
  forall chunk, size_chunk chunk <= size -> (align_chunk chunk | delta).


Lemma get_setN_inside:
  forall vl ofs lo t,
    lo <= ofs < lo + Z.of_nat (length vl) ->
    Some (ZMap.get ofs (setN vl lo t)) = nth_error vl (Z.to_nat (ofs - lo)).
Proof.
  induction vl; simpl; intros.
  - omega.
  - des (zlt lo ofs).
    rewrite IHvl by (zify; omega).
    replace (Z.to_nat (ofs - lo)) with (S (Z.to_nat (ofs - (lo + 1)))).
    simpl. auto.
    zify. repeat rewrite Z2Nat.id by omega. omega.
    replace lo with ofs by omega.
    rewrite setN_outside by omega.
    rewrite ZMap.gss.
    replace (ofs - ofs) with 0 by omega. simpl.
    reflexivity.
Qed.

Lemma nth_error_init_block':
  forall n b cur ofs,
    (ofs < n)%nat ->
    nth_error (init_block' n b cur) ofs = Some (Symbolic (Eval (Eundef b (Int.add cur (Int.repr (Z.of_nat ofs))))) O).
Proof.
  induction n; simpl; intros.
  - omega.
  - des (eq_nat_dec ofs O).
    + rewrite Int.add_zero. reflexivity.
    + des ofs.
      rewrite IHn by omega.
      f_equal. f_equal. f_equal.
      f_equal.
      rewrite Int.add_assoc.
      f_equal.
      zify. rewrite <- Z.add_1_l.
      rewrite Val.int_add_repr.
      auto.
Qed.

Lemma init_block_length:
  forall n b i,
    length (init_block' n b i) = S n.
Proof.
  induction n; simpl; intros; try omega.
  rewrite IHn; omega.
Qed.

Lemma get_init_block:
  forall ofs lo hi b,
    lo <= ofs < hi ->
    ZMap.get ofs (init_block lo hi b) = Symbolic (Eval (Eundef b (Int.repr ofs))) O.
Proof.
  unfold init_block.
  repeat destr; try omega.
  clear Heqs.
  cut ( Some (ZMap.get ofs
     (setN (init_block' (Z.to_nat (hi - lo)) b (Int.repr lo)) lo
        (ZMap.init (Symbolic (Eval (Eint Int.zero)) 0)))) =
        Some (Symbolic (Eval (Eundef b (Int.repr ofs))) 0)).
  intro A; inv A; auto.
  rewrite get_setN_inside.
  rewrite nth_error_init_block'.
  f_equal. f_equal. f_equal. f_equal.
  rewrite Val.int_add_repr.
  zify.
  rewrite Z2Nat.id by omega.
  f_equal. omega.
  zify.
  repeat rewrite Z2Nat.id by omega.
  omega.
  rewrite init_block_length.
  simpl. zify. rewrite Z2Nat.id by omega.
  simpl. omega.
Qed.

Lemma free_size_mem_wrong_bounds:
  forall m1 lo hi m1' b1
         (FREE: free m1 b1 lo hi = Some m1')
         (bnds: bounds_of_block m1 b1 <> (lo,hi)),
    size_mem m1 = size_mem m1'.
Proof.
  intros.
  unfold free, unchecked_free in FREE.
  repeat (revert FREE; destr).
  clear Heqs. inv FREE.
  unfold size_mem, mk_block_list, size_block.
  simpl.
  f_equal.
  apply mk_block_list_aux_ext.
  intros.
  unfold get_size.
  rewrite PMap.gsspec.
  unfold bounds_of_block in bnds.
  destr. subst.
  destr; try des p.
  des (zeq z lo); des (zeq z0 hi).
Qed.

Lemma bounds_dec:
  forall m b lo hi,
    {bounds_of_block m b = (lo,hi)} + {bounds_of_block m b <> (lo,hi)}.
Proof.
  decide equality.
Qed.

Preservation of drop_perm operations.

Lemma drop_perm_bounds:
  forall m b lo hi p m',
    drop_perm m b lo hi p = Some m' ->
    forall b',
      bounds_of_block m b' = bounds_of_block m' b'.
Proof.
  unfold drop_perm. intros. revert H; destr.
  inv H; unfold bounds_of_block; simpl. auto.
Qed.

Lemma nat_mask_drop_perm:
  forall m b lo hi p m' b',
    drop_perm m b lo hi p = Some m' ->
    nat_mask m b' = nat_mask m' b'.
Proof.
  unfold drop_perm; intros m b lo hi p m' b'.
  destr. inv H. unfold nat_mask.
  f_equal.
Qed.


Lemma drop_perm_size_block:
  forall m b o v p m',
    drop_perm m b o v p = Some m' ->
    size_block m' = size_block m.
Proof.
  unfold drop_perm; intros.
  repeat (revert H; destr).
  inv H; simpl.
  unfold size_block. simpl. auto.
Qed.


Lemma drop_perm_mask:
  forall m b o v m' p,
    drop_perm m b o v p = Some m' ->
    mask m' = mask m.
Proof.
  unfold drop_perm; intros.
  repeat (revert H; destr).
  inv H; simpl.
  unfold mask. simpl. auto.
Qed.


Lemma drop_perm_nextblock:
  forall m b o v m' p,
    drop_perm m b o v p = Some m' ->
    nextblock m' = nextblock m.
Proof.
  unfold drop_perm; intros.
  repeat (revert H; destr).
  inv H; simpl.
  unfold nextblock. simpl. auto.
Qed.



Lemma drop_perm_mk_block_list:
  forall (m m' : mem) (b : block) (lo hi : Z) (p : permission),
    drop_perm m b lo hi p = Some m' ->
    mk_block_list m = mk_block_list m'.
Proof.
  unfold mk_block_list. intros.
  erewrite <- drop_perm_size_block; eauto.
  erewrite <- drop_perm_nextblock; eauto.
Qed.


Lemma drop_perm_size_mem:
  forall m m' b lo hi p,
    drop_perm m b lo hi p = Some m' ->
    (size_mem m) = (size_mem m').
Proof.
  unfold size_mem.
  intros.
  f_equal.
  eapply drop_perm_mk_block_list; eauto.
Qed.

Lemma size_block_drop:
  forall m b lo hi p m',
    drop_perm m b lo hi p = Some m' ->
    forall b',
      size_block m' b' = size_block m b'.
Proof.
  intros.
  unfold size_block, get_size.
  unfold drop_perm in H.
  revert H; destr. inv H. simpl. auto.
Qed.

Lemma drop_unmapped_inj:
  forall m f m1 m2 b lo hi p m1',
  mem_inj m f m1 m2 ->
  drop_perm m1 b lo hi p = Some m1' ->
  f b = None ->
  mem_inj m f m1' m2.
Proof.
  intros. inv H. constructor.
-
  intros. eapply mi_perm0; eauto. eapply perm_drop_4; eauto.
- intros. eapply mi_bounds0; eauto. erewrite drop_perm_bounds; eauto.
- intros. erewrite <- mask_drop_perm with (m':=m1'); eauto.
-
  intros.
  replace (ZMap.get ofs m1'.(mem_contents)#b1) with (ZMap.get ofs m1.(mem_contents)#b1).
  apply mi_memval0; auto. eapply perm_drop_4; eauto.
  unfold drop_perm in H0; destruct (range_perm_dec m1 b lo hi Cur Freeable); inv H0; auto.
- erewrite <- drop_perm_size_mem with (m':=m1'); eauto.
Qed.

Lemma drop_mapped_inj:
  forall m f m1 m2 b1 b2 delta lo hi p m1',
    mem_inj m f m1 m2 ->
    drop_perm m1 b1 lo hi p = Some m1' ->
    meminj_no_overlap f m1 ->
    f b1 = Some(b2, delta) ->
    exists m2',
      drop_perm m2 b2 (lo + delta) (hi + delta) p = Some m2'
      /\ mem_inj m f m1' m2'.
Proof.
  intros.
  assert ({ m2' | drop_perm m2 b2 (lo + delta) (hi + delta) p = Some m2' }).
  apply range_perm_drop_2. red; intros.
  replace ofs with ((ofs - delta) + delta) by omega.
  eapply perm_inj; eauto. eapply range_perm_drop_1; eauto. omega.
  destruct X as [m2' DROP]. exists m2'; split; auto.
  inv H.
  constructor.
  -
    intros.
    assert (perm m2 b3 (ofs + delta0) k p0).
    eapply mi_perm0; eauto. eapply perm_drop_4; eauto.
    destruct (eq_block b1 b0).
    +
      subst b0. rewrite H2 in H; inv H.
      destruct (zlt (ofs + delta0) (lo + delta0)). eapply perm_drop_3; eauto.
      destruct (zle (hi + delta0) (ofs + delta0)). eapply perm_drop_3; eauto.
      assert (perm_order p p0).
      eapply perm_drop_2. eexact H0. instantiate (1 := ofs). omega. eauto.
      apply perm_implies with p; auto.
      eapply perm_drop_1. eauto. omega.
    +
      eapply perm_drop_3; eauto.
      destruct (eq_block b3 b2); auto.
      destruct (zlt (ofs + delta0) (lo + delta)); auto.
      destruct (zle (hi + delta) (ofs + delta0)); auto.
      exploit H1; eauto.
      instantiate (1 := ofs + delta0 - delta).
      apply perm_cur_max. apply perm_implies with Freeable.
      eapply range_perm_drop_1; eauto. omega. auto with mem.
      eapply perm_drop_4; eauto. eapply perm_max. apply perm_implies with p0. eauto.
      eauto with mem.
      intuition.
  - intros.
    erewrite <- drop_perm_bounds in H3; eauto.
    erewrite <- drop_perm_bounds; eauto.
  - intros. erewrite <- mask_drop_perm with (m':=m1'); eauto.
    erewrite <- mask_drop_perm with (m':=m2'); eauto.
  -
    intros.
    replace (m1'.(mem_contents)#b0) with (m1.(mem_contents)#b0).
    replace (m2'.(mem_contents)#b3) with (m2.(mem_contents)#b3).
    apply mi_memval0; auto. eapply perm_drop_4; eauto.
    unfold drop_perm in DROP; destruct (range_perm_dec m2 b2 (lo + delta) (hi + delta) Cur Freeable); inv DROP; auto.
    unfold drop_perm in H0; destruct (range_perm_dec m1 b1 lo hi Cur Freeable); inv H0; auto.
  - erewrite <- drop_perm_size_mem with (m':=m1'); eauto.
    erewrite <- drop_perm_size_mem with (m':=m2'); eauto.
Qed.

Lemma drop_outside_inj:
  forall m f m1 m2 b lo hi p m2',
    mem_inj m f m1 m2 ->
    drop_perm m2 b lo hi p = Some m2' ->
    (forall b' delta ofs' k p,
       f b' = Some(b, delta) ->
       perm m1 b' ofs' k p ->
       lo <= ofs' + delta < hi -> False) ->
    mem_inj m f m1 m2'.
Proof.
  intros. inv H. constructor.
  -
  intros. eapply perm_drop_3; eauto.
  destruct (eq_block b2 b); auto. subst b2. right.
  destruct (zlt (ofs + delta) lo); auto.
  destruct (zle hi (ofs + delta)); auto.
  byContradiction. exploit H1; eauto. omega.
  - intros.
    erewrite <- drop_perm_bounds; eauto.
  - intros; erewrite <- mask_drop_perm with (m':=m2'); eauto.
  -
  intros.
  replace (m2'.(mem_contents)#b2) with (m2.(mem_contents)#b2).
  apply mi_memval0; auto.
  unfold drop_perm in H0; destruct (range_perm_dec m2 b lo hi Cur Freeable); inv H0; auto.
  - erewrite <- drop_perm_size_mem with (m':=m2'); eauto.
Qed.

Memory extensions


A store m2 extends a store m1 if m2 can be obtained from m1 by increasing the sizes of the memory blocks of m1 (decreasing the low bounds, increasing the high bounds), and replacing some of the Vundef values stored in m1 by more defined values stored in m2 at the same locations.

Record extends' (m: Val.partial_undef_allocation) (m1 m2: mem) : Prop :=
  mk_extends {
    mext_next: nextblock m1 = nextblock m2;
    mext_inj: mem_inj m inject_id m1 m2
  }.

Definition extends := extends'.

Theorem extends_refl:
  forall m, extends (fun _ _ => None) m m.
Proof.
  intros. constructor. auto. constructor.
  - intros. unfold inject_id in H; inv H. replace (ofs + 0) with ofs by omega. auto.
  - intros. unfold inject_id in H; inv H. replace (ofs + 0) with (ofs) by omega. auto.
  - intros. unfold inject_id in H; inv H.
    split. omega.
    apply Z.divide_0_r.
  - intros. unfold inject_id in H; inv H. replace (ofs + 0) with ofs by omega.
    generalize (contents_wt_memval m b2 ofs). unfold wt_memval. destr.
    inv H.
    econstructor; eauto.
    apply Val.ei_id.
  - omega.
Qed.

Lemma expr_inject_lessdef:
  forall v1 v2 m,
    Val.expr_inject m inject_id v1 v2 <->
    Val.lessdef m v1 v2.
Proof.
  intros. unfold Val.lessdef. tauto.
Qed.

Theorem load_extends:
  forall m chunk m1 m2 b ofs v1,
  extends m m1 m2 ->
  load chunk m1 b ofs = Some v1 ->
  exists v2, load chunk m2 b ofs = Some v2 /\ Val.lessdef m v1 v2.
Proof.
  intros. inv H. exploit load_inj; eauto. reflexivity.
  intros [v2 [A B]]. exists v2; split.
  replace (ofs + 0) with ofs in A by omega. auto.
  auto.
Qed.

Theorem loadbytes_extends:
  forall m m1 m2 b ofs len bytes1,
  extends m m1 m2 ->
  loadbytes m1 b ofs len = Some bytes1 ->
  exists bytes2, loadbytes m2 b ofs len = Some bytes2
              /\ list_forall2 (memval_lessdef m) bytes1 bytes2.
Proof.
  intros. inv H.
  replace ofs with (ofs + 0) by omega. eapply loadbytes_inj; eauto.
Qed.

Theorem store_within_extends:
  forall m chunk m1 m2 b ofs v1 m1' v2,
  extends m m1 m2 ->
  store chunk m1 b ofs v1 = Some m1' ->
  Val.lessdef m v1 v2 ->
  exists m2',
     store chunk m2 b ofs v2 = Some m2'
  /\ extends m m1' m2'.
Proof.
  intros. inversion H.
  exploit store_mapped_inj; eauto.
    unfold inject_id; red; intros. inv H3; inv H4. auto.
    unfold inject_id; reflexivity.
  intros [m2' [A B]].
  exists m2'; split.
  replace (ofs + 0) with ofs in A by omega. auto.
  split; auto.
  rewrite (nextblock_store _ _ _ _ _ _ H0).
  rewrite (nextblock_store _ _ _ _ _ _ A).
  auto.
Qed.

Theorem store_outside_extends:
  forall m chunk m1 m2 b ofs v m2',
  extends m m1 m2 ->
  store chunk m2 b ofs v = Some m2' ->
  (forall ofs', perm m1 b ofs' Cur Readable -> ofs <= ofs' < ofs + size_chunk chunk -> False) ->
  extends m m1 m2'.
Proof.
  intros. inversion H. constructor.
  rewrite (nextblock_store _ _ _ _ _ _ H0). auto.
  eapply store_outside_inj; eauto.
  unfold inject_id; intros. inv H2. eapply H1; eauto. omega.
Qed.

Theorem storebytes_within_extends:
  forall m m1 m2 b ofs bytes1 m1' bytes2,
  extends m m1 m2 ->
  storebytes m1 b ofs bytes1 = Some m1' ->
  list_forall2 (memval_lessdef m) bytes1 bytes2 ->
  Forall wt_memval bytes2 ->
  exists m2',
     storebytes m2 b ofs bytes2 = Some m2'
  /\ extends m m1' m2'.
Proof.
  intros. inversion H.
  exploit storebytes_mapped_inj; eauto.
    unfold inject_id; red; intros. inv H4; inv H5. auto.
    unfold inject_id; reflexivity.
  intros [m2' [A B]].
  exists m2'; split.
  replace (ofs + 0) with ofs in A by omega. auto.
  split; auto.
  rewrite (nextblock_storebytes _ _ _ _ _ H0).
  rewrite (nextblock_storebytes _ _ _ _ _ A).
  auto.
Qed.

Theorem storebytes_outside_extends:
  forall m m1 m2 b ofs bytes2 m2',
  extends m m1 m2 ->
  storebytes m2 b ofs bytes2 = Some m2' ->
  (forall ofs', perm m1 b ofs' Cur Readable -> ofs <= ofs' < ofs + Z_of_nat (length bytes2) -> False) ->
  extends m m1 m2'.
Proof.
  intros. inversion H. constructor.
  rewrite (nextblock_storebytes _ _ _ _ _ H0). auto.
  eapply storebytes_outside_inj; eauto.
  unfold inject_id; intros. inv H2. eapply H1; eauto. omega.
Qed.

Memory injections


A memory state m1 injects into another memory state m2 via the memory injection f if the following conditions hold:

Opaque Z.add.
Lemma size_sup_one_block:
  forall m bs b z0
         (BS: bs # b = Some (0, z0))
         (PLE: ((Pos.to_nat b) <= m)%nat)
,
    z0 <=
    size_mem_aux
      (mk_block_list_aux (get_size bs) m).
Proof.
  induction m; simpl; intros.
  xomega.
  rewrite mbla_rew; simpl.
  apply le_lt_or_eq in PLE.
  destruct PLE.
  + unfold size_mem_aux. simpl.
    specialize (IHm _ _ _ BS).
    trim IHm. omega.
    unfold size_mem_aux in IHm.
    etransitivity; eauto.
    refine (proj2 (size_mem_al_add _ _ _ _ _ _)). omega.
    split. omega.
    refine (Z.le_trans _ _ _ _ (align_le _ 8 _)).
    change (align 8 8) with 8.
    apply Zle_add.
    apply Zmax_bound_l. omega. omega.
  + rewrite <- H. rewrite Pos2Nat.id.
    unfold get_size. rewrite BS.
    rewrite Z.sub_0_r.
    unfold size_mem_aux. simpl.
    refine (Z.le_trans _ _ _ _ (size_mem_al_pos _ _ _ _ _)).
    rewrite <- align_distr.
    generalize (Zmax_bound_r z0 0 z0).
    generalize (align_le (Z.max 0 z0) 8). intros. change (align 8 8) with 8. omega.
    omega.
    rewrite <- align_distr. change (align 8 8) with 8.
    generalize (Zmax_bound_l 0 0 z0).
    generalize (align_le (Z.max 0 z0) 8). intros.
    omega.
Qed.

Lemma in_bound_rep:
  forall m b o,
    in_bound o (bounds_of_block m b) ->
    0 <= o < Int.max_unsigned.
Proof.
  intros m b o IB.
  unfold in_bound, bounds_of_block in IB.
  revert IB; destr_cond_match; simpl; intros; try omega.
  destruct p.
  generalize (bounds_lo_inf0 _ _ _ _ Heqo0). simpl in IB. intro; subst.
  split. omega.
  generalize (wfm_alloc_ok m).
  unfold alloc_blocks.
  destruct (alloc_mem_aux _ ) eqn:?.
  destr_cond_match; try congruence.
  intros _.
  apply alloc_size_mem_aux in Heqp.
  etransitivity; eauto.
  clear l Heqs.
  subst.
  apply Zlt_le_trans with z0; try omega.
  eapply size_sup_one_block; eauto.
  destruct (plt b (nextblock m)).
  + zify; xomega.
  + rewrite bs_valid in Heqo0; auto. congruence.
Qed.

This is now provable and not needed as an invariant.
Lemma mi_representable':
  forall m f m1 m2
         (MI: mem_inj m f m1 m2),
  forall b b' delta ofs
         (FB: f b = Some(b', delta))
         (PERM: in_bound (Int.unsigned ofs) (bounds_of_block m1 b) \/
                in_bound (Int.unsigned ofs - 1) (bounds_of_block m1 b))
         (delta_pos: delta >= 0),
    0 <= Int.unsigned ofs + delta <= Int.max_unsigned.
Proof.
  intros.
  assert (PERM': in_bound (Int.unsigned ofs + delta) (bounds_of_block m2 b') \/
                 in_bound (Int.unsigned ofs - 1 + delta) (bounds_of_block m2 b')).
  inv MI.
  destruct PERM as [PERM|PERM]; [left|right];
  eapply (mi_bounds0 _ _ _ _ FB) in PERM; eauto.
  destruct PERM' as [PERM'|PERM'];
  eapply in_bound_rep in PERM'; eauto; omega.
Qed.

Lemma mi_representable:
  forall m f m1 m2
         (MI: mem_inj m f m1 m2),
  forall b b' delta ofs
         (FB: f b = Some(b', delta))
         (PERM: perm m1 b (Int.unsigned ofs) Max Nonempty \/
                perm m1 b (Int.unsigned ofs - 1) Max Nonempty)
         (delta_pos: delta >= 0),
    0 <= Int.unsigned ofs + delta <= Int.max_unsigned.
Proof.
  intros.
  eapply mi_representable'; eauto.
  destruct PERM as [PERM|PERM]; [left|right];
  apply perm_bounds in PERM; auto.
Qed.

Record inject' (m:Val.partial_undef_allocation) (f: meminj) (m1 m2: mem) : Prop :=
  mk_inject {
    mi_inj:
      mem_inj m f m1 m2;
    mi_freeblocks:
      forall b, ~(valid_block m1 b) -> f b = None;
    mi_mappedblocks:
      forall b b' delta, f b = Some(b', delta) -> valid_block m2 b';
    mi_freeblocks_undef: (* freeblocks may not be specialized *)
      forall b o,
        ~ valid_block m1 b ->
        m b o = None;
    mi_no_overlap':
      meminj_no_overlap' f m1;
    mi_delta_pos: (* the mi_representable is derivable from mi_delta_pos and mi_inj *)
      forall b b' delta,
        f b = Some(b', delta) ->
        delta >= 0
  }.

Definition inject := inject'.

Local Hint Resolve mi_mappedblocks: mem.

Lemma meminj_no_overlap_impl:
  forall f m,
    Mem.meminj_no_overlap' f m ->
    Mem.meminj_no_overlap f m.
Proof.
  intros f m MNO.
  red in MNO; red.
  intros b1 b1' delta1 b2 b2' delta2 ofs1 ofs2 diff FB1 FB2 P1 P2.
  apply Mem.perm_bounds in P1.
  apply Mem.perm_bounds in P2.
  specialize (MNO b1 b1' delta1 b2 b2' delta2 ofs1 ofs2 diff FB1 FB2 P1 P2).
  auto.
Qed.

Lemma inject_meminj_no_overlap:
  forall p f m1 m2,
    inject p f m1 m2 ->
    meminj_no_overlap f m1.
Proof.
  intros p f m1 m2 INJ.
  apply meminj_no_overlap_impl.
  inv INJ; auto.
Qed.

Preservation of access validity and pointer validity

Theorem valid_block_inject_1:
  forall m f m1 m2 b1 b2 delta,
  f b1 = Some(b2, delta) ->
  inject m f m1 m2 ->
  valid_block m1 b1.
Proof.
  intros. inv H. destruct (plt b1 (nextblock m1)). auto.
  assert (f b1 = None). eapply mi_freeblocks; eauto. congruence.
Qed.

Theorem valid_block_inject_2:
  forall m f m1 m2 b1 b2 delta,
  f b1 = Some(b2, delta) ->
  inject m f m1 m2 ->
  valid_block m2 b2.
Proof.
  intros. eapply mi_mappedblocks; eauto.
Qed.

Local Hint Resolve valid_block_inject_1 valid_block_inject_2: mem.

Theorem perm_inject:
  forall m f m1 m2 b1 b2 delta ofs k p,
  f b1 = Some(b2, delta) ->
  inject m f m1 m2 ->
  perm m1 b1 ofs k p -> perm m2 b2 (ofs + delta) k p.
Proof.
  intros. inv H0. eapply perm_inj; eauto.
Qed.

Theorem range_perm_inject:
  forall m f m1 m2 b1 b2 delta lo hi k p,
  f b1 = Some(b2, delta) ->
  inject m f m1 m2 ->
  range_perm m1 b1 lo hi k p -> range_perm m2 b2 (lo + delta) (hi + delta) k p.
Proof.
  intros. inv H0. eapply range_perm_inj; eauto.
Qed.

Theorem valid_access_inject:
  forall m f m1 m2 chunk b1 ofs b2 delta p,
  f b1 = Some(b2, delta) ->
  inject m f m1 m2 ->
  valid_access m1 chunk b1 ofs p ->
  valid_access m2 chunk b2 (ofs + delta) p.
Proof.
  intros. eapply valid_access_inj; eauto. apply mi_inj; auto.
  eauto.
Qed.

Theorem valid_pointer_inject:
  forall m f m1 m2 b1 ofs b2 delta,
  f b1 = Some(b2, delta) ->
  inject m f m1 m2 ->
  valid_pointer m1 b1 ofs = true ->
  valid_pointer m2 b2 (ofs + delta) = true.
Proof.
  intros.
  rewrite valid_pointer_valid_access in H1.
  rewrite valid_pointer_valid_access.
  eapply valid_access_inject; eauto.
Qed.

Theorem weak_valid_pointer_inject:
  forall m f m1 m2 b1 ofs b2 delta,
  f b1 = Some(b2, delta) ->
  inject m f m1 m2 ->
  weak_valid_pointer m1 b1 ofs = true ->
  weak_valid_pointer m2 b2 (ofs + delta) = true.
Proof.
  intros until 2. unfold weak_valid_pointer. rewrite !orb_true_iff.
  replace (ofs + delta - 1) with ((ofs - 1) + delta) by omega.
  intros []; eauto using valid_pointer_inject.
Qed.

The following lemmas establish the absence of machine integer overflow during address computations.

Lemma address_inject:
  forall m f m1 m2 b1 ofs1 b2 delta p,
  inject m f m1 m2 ->
  perm m1 b1 (Int.unsigned ofs1) Cur p ->
  f b1 = Some (b2, delta) ->
  Int.unsigned (Int.add ofs1 (Int.repr delta)) = Int.unsigned ofs1 + delta.
Proof.
  intros.
  assert (perm m1 b1 (Int.unsigned ofs1) Max Nonempty) by eauto with mem.
  exploit mi_representable; eauto. inv H; eauto. inv H; eauto. intros [A B].
  inv H.
  specialize (mi_delta_pos0 _ _ _ H1).
  assert (0 <= delta <= Int.max_unsigned).
    generalize (Int.unsigned_range ofs1). intros. omega.
  unfold Int.add. repeat rewrite Int.unsigned_repr; omega.
Qed.

Lemma address_inject':
  forall m f m1 m2 chunk b1 ofs1 b2 delta,
  inject m f m1 m2 ->
  valid_access m1 chunk b1 (Int.unsigned ofs1) Nonempty ->
  f b1 = Some (b2, delta) ->
  Int.unsigned (Int.add ofs1 (Int.repr delta)) = Int.unsigned ofs1 + delta.
Proof.
  intros. destruct H0. eapply address_inject; eauto.
  apply H0. generalize (size_chunk_pos chunk). omega.
Qed.

Theorem weak_valid_pointer_inject_no_overflow:
  forall m f m1 m2 b ofs b' delta,
  inject m f m1 m2 ->
  weak_valid_pointer m1 b (Int.unsigned ofs) = true ->
  f b = Some(b', delta) ->
  0 <= Int.unsigned ofs + Int.unsigned (Int.repr delta) <= Int.max_unsigned.
Proof.
  intros. rewrite weak_valid_pointer_spec in H0.
  rewrite ! valid_pointer_nonempty_perm in H0.
  inv H. generalize (mi_delta_pos0 _ _ _ H1). intros.
  generalize (mi_representable _ _ _ _ mi_inj0 _ _ _ ofs H1); eauto.
  intro C.
  trim C.
  destruct H0; eauto with mem.
  trim C; auto.
  pose proof (Int.unsigned_range ofs).
  rewrite Int.unsigned_repr; omega.
Qed.

Theorem valid_pointer_inject_no_overflow:
  forall m f m1 m2 b ofs b' delta,
  inject m f m1 m2 ->
  valid_pointer m1 b (Int.unsigned ofs) = true ->
  f b = Some(b', delta) ->
  0 <= Int.unsigned ofs + Int.unsigned (Int.repr delta) <= Int.max_unsigned.
Proof.
  eauto using weak_valid_pointer_inject_no_overflow, valid_pointer_implies.
Qed.

Theorem valid_pointer_inject_val:
  forall m f m1 m2 b ofs b' ofs',
  inject m f m1 m2 ->
  valid_pointer m1 b (Int.unsigned ofs) = true ->
  Val.expr_inject m f (Eval (Eptr b ofs)) (Eval (Eptr b' ofs')) ->
  valid_pointer m2 b' (Int.unsigned ofs') = true.
Proof.
  intros. inv H1.
  simpl in *.
  unfold Val.inj_ptr in inj_ok.
  des (f b). des p.
  inv inj_ok. destr.
  replace (Int.unsigned (Int.add ofs (Int.repr z))) with
  (Int.unsigned ofs + z).
  eapply valid_pointer_inject; eauto.
  erewrite <- address_inject; eauto.
  rewrite valid_pointer_valid_access in H0. unfold valid_access in H0.
  simpl in H0. unfold range_perm in H0. destruct H0.
  specialize (H0 (Int.unsigned ofs)). exploit H0; eauto. omega.
Qed.

Theorem weak_valid_pointer_inject_val:
  forall m f m1 m2 b ofs b' ofs',
  inject m f m1 m2 ->
  weak_valid_pointer m1 b (Int.unsigned ofs) = true ->
  Val.expr_inject m f (Eval (Eptr b ofs)) (Eval (Eptr b' ofs')) ->
  weak_valid_pointer m2 b' (Int.unsigned ofs') = true.
Proof.
  intros. inv H1.
  destr.
  unfold Val.inj_ptr in *; des (f b); des p. inv inj_ok; destr.
  exploit weak_valid_pointer_inject; eauto.
  rewrite weak_valid_pointer_spec in H0.
  rewrite ! valid_pointer_nonempty_perm in H0.
  inv H. specialize (mi_delta_pos0 _ _ _ Heqo).
  exploit (mi_representable _ _ _ _ mi_inj0 _ _ _ ofs Heqo); eauto. destruct H0; eauto with mem.
  intros.
  pose proof (Int.unsigned_range_2 ofs).
  unfold Int.add. repeat rewrite Int.unsigned_repr; auto; omega.
Qed.

Theorem inject_no_overlap:
  forall m f m1 m2 b1 b2 b1' b2' delta1 delta2 ofs1 ofs2,
  inject m f m1 m2 ->
  b1 <> b2 ->
  f b1 = Some (b1', delta1) ->
  f b2 = Some (b2', delta2) ->
  perm m1 b1 ofs1 Max Nonempty ->
  perm m1 b2 ofs2 Max Nonempty ->
  b1' <> b2' \/ ofs1 + delta1 <> ofs2 + delta2.
Proof.
  intros m f m1 m2 b1 b2 b1' b2' delta1 delta2 ofs1 ofs2 INJ.
  eapply inject_meminj_no_overlap in INJ; eauto.
Qed.

Theorem different_pointers_inject:
  forall p f m m' b1 ofs1 b2 ofs2 b1' delta1 b2' delta2,
  inject p f m m' ->
  b1 <> b2 ->
  valid_pointer m b1 (Int.unsigned ofs1) = true ->
  valid_pointer m b2 (Int.unsigned ofs2) = true ->
  f b1 = Some (b1', delta1) ->
  f b2 = Some (b2', delta2) ->
  b1' <> b2' \/
  Int.unsigned (Int.add ofs1 (Int.repr delta1)) <>
  Int.unsigned (Int.add ofs2 (Int.repr delta2)).
Proof.
  intros.
  rewrite valid_pointer_valid_access in H1.
  rewrite valid_pointer_valid_access in H2.
  rewrite (address_inject' _ _ _ _ _ _ _ _ _ H H1 H3).
  rewrite (address_inject' _ _ _ _ _ _ _ _ _ H H2 H4).
  inv H1. simpl in H5. inv H2. simpl in H1.
  eapply inject_meminj_no_overlap; eauto.
  apply perm_cur_max. apply (H5 (Int.unsigned ofs1)). omega.
  apply perm_cur_max. apply (H1 (Int.unsigned ofs2)). omega.
Qed.

Require Intv.

Theorem disjoint_or_equal_inject:
  forall p f m m' b1 b1' delta1 b2 b2' delta2 ofs1 ofs2 sz,
  inject p f m m' ->
  f b1 = Some(b1', delta1) ->
  f b2 = Some(b2', delta2) ->
  range_perm m b1 ofs1 (ofs1 + sz) Max Nonempty ->
  range_perm m b2 ofs2 (ofs2 + sz) Max Nonempty ->
  sz > 0 ->
  b1 <> b2 \/ ofs1 = ofs2 \/ ofs1 + sz <= ofs2 \/ ofs2 + sz <= ofs1 ->
  b1' <> b2' \/ ofs1 + delta1 = ofs2 + delta2
             \/ ofs1 + delta1 + sz <= ofs2 + delta2
             \/ ofs2 + delta2 + sz <= ofs1 + delta1.
Proof.
  intros.
  destruct (eq_block b1 b2).
  assert (b1' = b2') by congruence. assert (delta1 = delta2) by congruence. subst.
  destruct H5. congruence. right. destruct H5. left; congruence. right. omega.
  destruct (eq_block b1' b2'); auto. subst. right. right.
  set (i1 := (ofs1 + delta1, ofs1 + delta1 + sz)).
  set (i2 := (ofs2 + delta2, ofs2 + delta2 + sz)).
  change (snd i1 <= fst i2 \/ snd i2 <= fst i1).
  apply Intv.range_disjoint'; simpl; try omega.
  unfold Intv.disjoint, Intv.In; simpl; intros. red; intros.
  exploit inject_meminj_no_overlap; eauto.
  instantiate (1 := x - delta1). apply H2. omega.
  instantiate (1 := x - delta2). apply H3. omega.
  intuition.
Qed.

Theorem aligned_area_inject:
  forall p f m m' b ofs al sz b' delta,
  inject p f m m' ->
  al = 1 \/ al = 2 \/ al = 4 \/ al = 8 -> sz > 0 ->
  (al | sz) ->
  range_perm m b ofs (ofs + sz) Cur Nonempty ->
  (al | ofs) ->
  f b = Some(b', delta) ->
  (al | ofs + delta).
Proof.
  intros.
  assert (P: al > 0) by omega.
  assert (Q: Zabs al <= Zabs sz). apply Zdivide_bounds; auto. omega.
  rewrite Zabs_eq in Q; try omega. rewrite Zabs_eq in Q; try omega.
  assert (R: exists chunk, al = align_chunk chunk /\ al = size_chunk chunk).
    destruct H0. subst; exists Mint8unsigned; auto.
    destruct H0. subst; exists Mint16unsigned; auto.
    destruct H0. subst; exists Mint32; auto.
    subst; exists Mint64; auto.
  destruct R as [chunk [A B]].
  assert (valid_access m chunk b ofs Nonempty).
    split. red; intros; apply H3. omega. congruence.
  exploit valid_access_inject; eauto. intros [C D].
  congruence.
Qed.

Preservation of loads

Theorem load_inject:
  forall p f m1 m2 chunk b1 ofs b2 delta v1,
  inject p f m1 m2 ->
  load chunk m1 b1 ofs = Some v1 ->
  f b1 = Some (b2, delta) ->
  exists v2, load chunk m2 b2 (ofs + delta) = Some v2 /\ Val.expr_inject p f v1 v2.
Proof.
  intros. inv H. eapply load_inj; eauto.
Qed.
 
Definition id_pua : Val.partial_undef_allocation :=
  fun _ _ => None.

Lemma bounds_of_block_alloc_eq:
  forall m lo hi b m',
    alloc m lo hi = Some (m',b) ->
    forall b',
      bounds_of_block m' b' = if eq_block b' b
                              then (0,hi)
                              else if plt b' (nextblock m')
                                   then bounds_of_block m b'
                                   else (0,0).
Proof.
  intros.
  destr.
  subst.
  erewrite bounds_of_block_alloc; eauto.
  destr.
  erewrite <- bounds_of_block_alloc_other; eauto.
  unfold bounds_of_block; rewrite bs_valid; auto.
Qed.


Lemma alloc_0_inject:
  forall tm1 tm2 sp f1 m2,
    alloc tm1 0 0 = Some (tm2, sp) ->
    inject id_pua f1 m2 tm1 ->
    inject id_pua f1 m2 tm2.
Proof.
  intros.
  inv H0; constructor; eauto.
  - inv mi_inj0; constructor; intros; eauto.
    + eapply perm_alloc_1; eauto.
    + erewrite bounds_of_block_alloc_eq; eauto.
      generalize (mi_mappedblocks0 _ _ _ H0). intros.
      destruct (eq_block b2 sp).
      * subst.
        generalize (alloc_result _ _ _ _ _ H). intro; subst.
        unfold valid_block in H2; xomega.
      * destruct (plt b2 (nextblock tm2)).
        eapply mi_bounds0; eauto.
        erewrite nextblock_alloc in n0; eauto.
        unfold valid_block in H2; xomega.
    + specialize (mi_align'0 _ _ _ H0); intuition try congruence.
      generalize (mi_mappedblocks0 _ _ _ H0). intros.
      destruct (eq_block b0 sp); subst.
      generalize (alloc_result _ _ _ _ _ H). intro; subst.
      unfold valid_block in H3; xomega.
      erewrite <- mask_alloc_other with (m2:=tm2); eauto.
    + generalize (mi_mappedblocks0 _ _ _ H0). intros.
      destruct (eq_block b2 sp); subst.
      generalize (alloc_result _ _ _ _ _ H). intro; subst. unfold valid_block in H2; xomega.
      erewrite (alloc_contents) with (m':=tm2); eauto.
    + erewrite <- size_mem_alloc_0 with (tm2:=tm2); eauto.
  - intros.
    apply mi_mappedblocks0 in H0.
    unfold valid_block in *.
    erewrite nextblock_alloc; eauto. xomega.
Qed.


Lemma alignment_inj:
  forall i i0 i1 i2
         (A : Int.and i (Int.not (Int.repr (two_power_nat i0 - 1))) = i)
         (B : Int.and (Int.not (Int.repr (two_power_nat i0 - 1)))
                      (Int.not (Int.repr (two_power_nat i1 - 1))) =
              Int.not (Int.repr (two_power_nat i0 - 1)))
         (C : Int.and i2 (Int.not (Int.repr (two_power_nat i1 - 1))) = i2),
   Int.and (Int.add i i2) (Int.not (Int.repr (two_power_nat i1 - 1))) =
   Int.add i i2.
Proof.
  intros.
  assert (AND_ZEROS: forall i1 i,
                       Int.and i (Int.not (Int.repr (two_power_nat i1 - 1))) = i <->
                       (forall j, 0 <= j < Z.of_nat i1 ->
                                  Int.testbit i j = false)).
  {
    clear; split; intros.
    - apply (f_equal (fun x => Int.testbit x j)) in H.
      destruct (zlt j Int.zwordsize).
      + rewrite Int.bits_and in H by omega.
        rewrite Int.bits_not in H by omega.
        rewrite Int.testbit_repr in H by omega.
        rewrite two_power_nat_two_p in H.
        rewrite Int.Ztestbit_two_p_m1 in H by omega.
        revert H; destr.
        des (Int.testbit i j).
      + rewrite Int.bits_above; auto.
    - apply Int.same_bits_eq; intros.
      rewrite Int.bits_and; auto.
      rewrite Int.bits_not; auto.
      rewrite Int.testbit_repr; auto.
      rewrite two_power_nat_two_p.
      rewrite Int.Ztestbit_two_p_m1; try omega.
      destr.
      rewrite H; auto.
      ring.
  }
  rewrite AND_ZEROS in A.
  rewrite AND_ZEROS in B.
  rewrite AND_ZEROS in C.
  rewrite AND_ZEROS.
  intros.
  generalize (B j H).
  intros.
  destruct (zlt j Int.zwordsize); try (rewrite Int.bits_above by omega; auto).
  rewrite Int.bits_not in H0 by omega.
  rewrite Int.testbit_repr in H0 by omega.
  rewrite two_power_nat_two_p in H0.
  rewrite Int.Ztestbit_two_p_m1 in H0 by omega.
  revert H0; destr.
  unfold Int.add.
  rewrite Int.testbit_repr by omega.
  rewrite Int.Z_add_is_or; try omega.
  rewrite <- ! Int.testbit_repr by omega.
  rewrite ! Int.repr_unsigned.
  rewrite C; auto.
  rewrite A; auto.
  intros.
  rewrite <- ! Int.testbit_repr by omega.
  rewrite ! Int.repr_unsigned.
  rewrite C; auto. ring.
  omega.
Qed.


Lemma int_add_rew:
  forall a b,
    Int.repr (Int.unsigned a + Int.unsigned b) = Int.add a b.
Proof.
  unfold Int.add; auto.
Qed.

Lemma two_p_abs:
  forall n n0,
    (n >= n0)%nat ->
    Int.and (Int.not (Int.repr (two_power_nat n - 1)))
            (Int.not (Int.repr (two_power_nat n0 - 1))) =
    Int.not (Int.repr (two_power_nat n - 1)).
Proof.
  intros.
  rewrite <- ! Int.not_or_and_not in *.
  f_equal.
  rewrite ! two_power_nat_two_p.
  solve_int.
  rewrite ! Int.testbit_repr; auto.
  rewrite ! Int.Ztestbit_two_p_m1; auto; try omega.
  apply inj_ge in H.
  repeat destr; omega.
Qed.

Definition all_blocks_injected (f:meminj) (m:mem) : Prop :=
  forall b lo hi,
    bounds_of_block m b = (lo,hi) ->
    hi - lo <> 0 ->
    f b <> None.

Lemma abi_in_bound:
  forall f m b o
         (ABI : all_blocks_injected f m)
         (IB : in_bound (Int.unsigned o) (bounds_of_block m b))
         (Fnone : f b = None),
    False.
Proof.
  intros.
  destruct (bounds_of_block m b) eqn:?.
  specialize (ABI b _ _ Heqp ).
  destruct (zeq (z0 - z) 0).
  unfold in_bound in IB. simpl in IB. omega.
  apply ABI; auto.
Qed.


Lemma int_add_diff_range_plus:
  forall o z o' z0 i,
    o + z <> o' + z0 ->
    z0 >= 0 -> z >= 0 ->
    0 <= o' + z0 <= Int.modulus - 1 ->
    0 <= o + z <= Int.modulus - 1 ->
    (( i + z) mod Int.modulus + o) mod Int.modulus <>
    (( i + z0) mod Int.modulus + o') mod Int.modulus.
Proof.
  intros.
  rewrite !Zplus_mod_idemp_l.
  revert H. revert H2 H3.
  replace (i+z+o) with (i + (o + z)) by omega.
  replace (i+z0+o') with (i + (o' + z0)) by omega.
  generalize (o + z) (o' + z0). clear.
  intros.
  intro.
  unf_modulus.
  elim_div. lia.
Qed.

Lemma compat_inject:
  forall p f m m'
         (ABI: all_blocks_injected f m)
         (INJ : inject p f m m')
         al
         (COMP: compat al (bounds_of_block m') (nat_mask m')),
    compat (Val.inj_alloc al f) (bounds_of_block m) (nat_mask m).
Proof.
  intros.
  constructor; simpl; intros; auto.
  - unfold Val.inj_alloc.
    destruct (f b) eqn:?.
    + destruct p0.
      generalize (mi_bounds p f _ _ (mi_inj _ _ _ _ INJ) _ _ _ _ Heqo0 H).
      intro A.
      inv INJ.
      generalize (mi_representable' _ _ _ _ mi_inj0 _ _ _ (o) Heqo0 (or_introl H)).
      intro B.
      assert (z <= Int.max_unsigned). clear - B.
      generalize (Int.unsigned_range o).
      unfold Int.max_unsigned in *.
      {
        intro.
        destruct (zlt z 0). omega.
        apply B in g.
        omega.
      }
      generalize (addr_space _ _ _ COMP b0 (Int.add o (Int.repr z))).
      unfold Int.add.
      specialize (mi_delta_pos0 _ _ _ Heqo0).
      rewrite (Int.unsigned_repr z) by omega.
      intro C.
      rewrite Int.unsigned_repr in C by (intuition omega).
      specialize (C A).
      replace ( Int.unsigned
                  (Int.repr
                     (Int.unsigned
                        (Int.repr (Int.unsigned (al b0) + z)) +
                      Int.unsigned o))) with
      (Int.unsigned (Int.repr (Int.unsigned (al b0) + (Int.unsigned o + z)))).
      auto.
      rewrite <- (Int.unsigned_repr (Int.unsigned o + z)) by (intuition omega).
      rewrite ! int_add_rew.
      rewrite <- (Int.unsigned_repr z) by omega.
      rewrite ! int_add_rew.
      rewrite Int.add_assoc.
      rewrite (Int.add_commut o).
      auto.
    + exfalso.
      eapply abi_in_bound; eauto.
  - unfold Val.inj_alloc.
    destruct (f b) eqn:?. destruct ( f b') eqn:?.
    + destruct p0; destruct p1.
      generalize (mi_bounds p f _ _ (mi_inj _ _ _ _ INJ) _ _ _ _ Heqo0 H0).
      generalize (mi_bounds p f _ _ (mi_inj _ _ _ _ INJ) _ _ _ _ Heqo1 H1).
      intros A B.
      generalize (mi_no_overlap' p f _ _ INJ b b0 z b' b1 z0 (Int.unsigned o) (Int.unsigned o') H Heqo0 Heqo1 H0 H1).
      intro C.
      destruct (eq_block b0 b1). subst.
      * destruct C; try congruence.
        inv INJ. generalize (mi_delta_pos0 _ _ _ Heqo1)
                            (mi_delta_pos0 _ _ _ Heqo0). intros.
        generalize (mi_representable' _ _ _ _ mi_inj0 _ _ _ o Heqo0 (or_introl H0)).
        generalize (mi_representable' _ _ _ _ mi_inj0 _ _ _ o' Heqo1 (or_introl H1)).
        revert H2 H3 H4. clear.
        unfold Int.max_unsigned.
        intros.
        rewrite <- (Int.repr_unsigned (al b1)).
        rewrite ! Val.int_add_repr.
        unfold Int.add.
        generalize (al b1).
        intros. rewrite ! Int.unsigned_repr_eq.
        {
          specialize (H H3). specialize (H0 H4).
          apply int_add_diff_range_plus; auto.
        }
      * inv COMP. clear C.
        inv INJ. generalize (mi_delta_pos0 _ _ _ Heqo1)
                            (mi_delta_pos0 _ _ _ Heqo0). intros.
        generalize (mi_representable' _ _ _ _ mi_inj0 _ _ _ o Heqo0 (or_introl H0)).
        generalize (mi_representable' _ _ _ _ mi_inj0 _ _ _ o' Heqo1 (or_introl H1)).
        intros.
        rewrite <- (Int.unsigned_repr (Int.unsigned o' + z0)) in A by intuition omega.
        rewrite <- (Int.unsigned_repr (Int.unsigned o + z)) in B by intuition omega.
        generalize (addr_space _ _ A).
        generalize (addr_space _ _ B).
        assert (z <= Int.max_unsigned). clear - H3 H5.
        {
          generalize (Int.unsigned_range o).
          unfold Int.max_unsigned in *.
          intros.
          specialize (H5 H3).
          omega.
        }
        assert (z0 <= Int.max_unsigned). clear - H2 H4.
        {
          generalize (Int.unsigned_range o').
          unfold Int.max_unsigned in *.
          intros.
          specialize (H4 H2).
          omega.
        }
        rewrite <- (Int.unsigned_repr z) in B by omega.
        rewrite <- (Int.unsigned_repr z0) in A by omega.
        rewrite int_add_rew in A, B.
        generalize (overlap _ _ (Int.add o (Int.repr z)) (Int.add o' (Int.repr z0)) n B A).
        intros. clear - H8.
        rewrite ! Int.add_assoc. rewrite (Int.add_commut _ o).
        rewrite (Int.add_commut _ o'). auto.
    + exfalso.
      eapply abi_in_bound; eauto.
    + exfalso.
      eapply abi_in_bound with (b:=b); eauto.
  - inv COMP.
    unfold Val.inj_alloc. des (f b). des p0.
    clear addr_space overlap.
    generalize (mi_align' _ _ _ _ (mi_inj _ _ _ _ INJ) _ _ _ Heqo).
    clear Heqo INJ.
    specialize (alignment b0).
    revert alignment.
    unfold nat_mask.
    intros alignment [A B ].
    eapply alignment_inj; eauto.
    + rewrite two_p_abs; auto.
    + unfold mask in *.
      destr.
      apply alignment_ok in Heqo. subst.
      revert A; destr.
      * apply alignment_ok in Heqo. subst.
        red in B.
        destruct B; subst z. clear alignment.
        rewrite <- (Int.not_involutive (Int.repr (x*two_p _))).
        rewrite <- Int.not_or_and_not.
        f_equal.
        solve_int.
        rewrite ! Int.bits_not; auto.
        rewrite ! Int.testbit_repr; auto.
        rewrite two_power_nat_two_p.
        rewrite Int.Ztestbit_two_p_m1; auto; try omega.
        intros.
        destr; try rewrite orb_false_r; auto.
        rewrite orb_true_r.
        generalize (Int.Ztestbit_mod_two_p (Z.of_nat (alignment_of_size (get_size (mem_blocksize m) b)))
                                         (x*two_p (Z.of_nat (alignment_of_size (get_size (mem_blocksize m) b)))) i).
        rewrite Heqs.
        clear A; intro A; rewrite <- A; try omega.
        rewrite Z_mod_mult.
        rewrite Int.Ztestbit_0. auto.
      * assert (alignment_of_size (get_size (mem_blocksize m) b) = O) by omega.
        rewrite H in *.
        change (two_power_nat 0 - 1) with 0.
        setoid_rewrite Int.not_zero.
        apply Int.and_mone.
      * change (two_power_nat 0 - 1) with 0.
        setoid_rewrite Int.not_zero.
        apply Int.and_mone.
Qed.

Lemma norm_inject:
  forall p f m m' e1 v1 e1'
         (ABI: all_blocks_injected f m)
         (INJ: inject p f m m')
         (EI: Val.expr_inject p f e1 e1')
         (NU: v1 <> Vundef),
  forall r (MN:mem_norm m e1 r = v1),
    mem_norm m' e1' r <> Vundef /\
    Val.expr_inject p f (Eval (sval_of_val v1)) (Eval (sval_of_val (mem_norm m' e1' r))).
Proof.
  intros.
  destruct (norm_correct2 (bounds_of_block m) (nat_mask m) e1 r).
  destruct (tcheck_expr_dec e1 (typ_of_result r));
    [|unfold mem_norm in *; subst; intuition try congruence].
  clear no_alloc_undef expr_wt.
  specialize (eval_ok e).
  assert (eval' :
            forall alloc em,
              compat alloc (bounds_of_block m) (nat_mask m) ->
              eSexpr alloc em eb eu (typ_of_result r) e1 =
              eEval alloc (Vi Int.zero) (typ_of_result r)
                    (Normalise.normalise (bounds_of_block m) (nat_mask m) e1 r)).
  {
    intros; edestruct eval_ok; eauto; unfold mem_norm in *; destr.
  } clear eval_ok.
  unfold mem_norm in MN; rewrite MN in *. clear MN.
  destruct (Val.apply_inj f (Val.subst p (Eval (sval_of_val v1)))) eqn:?; try congruence.
  - replace (Val.subst p (Eval (sval_of_val v1))) with (Eval (sval_of_val v1)) in Heqo by (des v1) .
    assert (EV: exists v, e0 = Eval (sval_of_val v)).
    { unfold Val.apply_inj, Val.inj_ptr in Heqo.
         exists (match sval_of_val v1 with
                Eptr b i =>
                match f b with
                    Some (b',delta) => Vptr b' (Int.add i (Int.repr delta))
                  | _ => v1
                end
              | _ => v1
            end).
    des v1. des (f b). des p0. }
    destruct EV as [v EQ]; subst.
    cut (mem_norm m' e1' r = v).
    intro MNV; subst.
    + split.
      * unfold Val.apply_inj, Val.inj_ptr in Heqo.
        intro H. rewrite H in Heqo.
        des v1. des (f b). des p0.
      * constructor. des v1.
    + assert (IsNorm.t eu eb (bounds_of_block m') (nat_mask m') e1' r v).
      {
        constructor.
        - intros TC alloc0 em v' COMPAT ESEXPR.
          inv ESEXPR.
          exists (Vi Int.zero).
          inv EI.
          erewrite <- Val.apply_inj_eSexpr; eauto.
          rewrite Val.eSexpr_subst_em.
          rewrite eval'.
          + des v1; des r; des v;
            unfold Val.inj_ptr in Heqo;
            des (f b); des p0.
            inv Heqo.
            unfold Val.inj_alloc. rewrite Heqo0.
            rewrite Int.add_assoc.
            f_equal. f_equal. apply Int.add_commut.
          + eapply compat_inject; eauto.
        - des v1; inv Heqo; des v; des r;
          unfold Val.inj_ptr in H0;
          des (f b); des p0.
          inv H0.
          inv INJ. inversion mi_inj0.
          generalize (mi_bounds0 _ _ _ _ Heqo result_wt).
          rewrite <- (Int.repr_unsigned i) at 2.
          rewrite Val.int_add_repr.
          rewrite Int.unsigned_repr. auto.
          apply (mi_representable' _ _ _ _ mi_inj0 _ _ _ i Heqo (or_introl result_wt)).
          eapply mi_delta_pos0; eauto.
        - apply Val.same_type in EI. congruence.
        - intro A; exfalso; apply A.
          apply concrete_mem.
      }
      eapply (norm_complete (bounds_of_block m') (nat_mask m') e1' r v) in H; eauto.
      inv H; auto.
      simpl in Heqo. unfold Val.inj_ptr in Heqo. des v1. des (f b). des p0.
  - replace (Val.subst p (Eval (sval_of_val v1))) with (Eval (sval_of_val v1)) in Heqo by (des v1).
    exfalso.
    des v1; des r.
    unfold Val.inj_ptr in Heqo.
    des (f b); try des p0.
    red in ABI.
    destruct (bounds_of_block m b) eqn:?.
    apply ABI in Heqp0.
    congruence.
    intro; subst.
    assert (z0 = z) by omega; subst.
    unfold in_bound in result_wt; simpl in *; omega.
Qed.

Lemma expr_val_inject:
  forall p f v1 v2,
    Val.expr_inject p f (Eval (sval_of_val v1))
                    (Eval (sval_of_val v2)) ->
    val_inject f v1 v2.
Proof.
  intros p f v1 v2 EI.
  destruct v1; destruct v2; inv EI; simpl in *;
  try constructor; inv inj_ok; try constructor;
  unfold Val.inj_ptr in *; destruct (f b) as [[b' delta]|] eqn:?; simpl in *; inv H0.
  econstructor; eauto.
Qed.
  
Lemma norm_inject_val':
  forall p f m m' e1 e1' r
         (ABI: all_blocks_injected f m)
         (INJ: inject p f m m')
         (EI: Val.expr_inject p f e1 e1'),
    val_inject f (mem_norm m e1 r) (mem_norm m' e1' r).
Proof.
  intros p f m m' e1 e1' r ABI INJ EI.
  assert (mem_norm m e1 r = Vundef \/ mem_norm m e1 r <> Vundef).
  {
    destruct (mem_norm m e1 r); tauto.
  }
  destruct H as [H|H].
  - rewrite H. constructor.
  - eapply norm_inject in H; eauto.
    destruct H as [H1 H2].
    eapply expr_val_inject; eauto.
Qed.

Theorem loadv_inject:
  forall p f m1 m2 chunk a1 a2 v1
    (ABI: all_blocks_injected f m1),
    inject p f m1 m2 ->
    loadv chunk m1 a1 = Some v1 ->
    Val.expr_inject p f a1 a2 ->
    exists v2, loadv chunk m2 a2 = Some v2 /\ Val.expr_inject p f v1 v2.
Proof.
  intros.
  unfold loadv in H0.
  destruct (mem_norm m1 a1 Ptr) eqn:?; try discriminate.
  unfold loadv.
  exploit norm_inject; eauto.
  congruence.
  intros [A B].
  unfold mem_norm in *.
  destruct B. simpl in inj_ok.
  unfold Val.inj_ptr in inj_ok.
  des (f b). des p0.
  inv inj_ok.
  destr. inv H3.
  exploit load_inject; eauto. intros [v2 [LOAD INJ]].
  exists v2; split; auto.
  replace (Int.unsigned (Int.add i (Int.repr z)))
  with (Int.unsigned i + z).
  auto. symmetry. eapply address_inject'; eauto with mem.
Qed.

Theorem loadbytes_inject:
  forall p f m1 m2 b1 ofs len b2 delta bytes1,
  inject p f m1 m2 ->
  loadbytes m1 b1 ofs len = Some bytes1 ->
  f b1 = Some (b2, delta) ->
  exists bytes2, loadbytes m2 b2 (ofs + delta) len = Some bytes2
              /\ list_forall2 (memval_inject p f) bytes1 bytes2.
Proof.
  intros. inv H. eapply loadbytes_inj; eauto.
Qed.

Preservation of stores

Theorem store_mapped_inject:
  forall p f chunk m1 b1 ofs v1 n1 m2 b2 delta v2,
  inject p f m1 m2 ->
  store chunk m1 b1 ofs v1 = Some n1 ->
  f b1 = Some (b2, delta) ->
  Val.expr_inject p f v1 v2 ->
  exists n2,
    store chunk m2 b2 (ofs + delta) v2 = Some n2
    /\ inject p f n1 n2.
Proof.
  intros. inversion H.
  exploit store_mapped_inj; eauto.
  eapply inject_meminj_no_overlap; eauto.
  intros [n2 [STORE MI]].
  exists n2; split. eauto. constructor; eauto; eauto with mem.
- red; intros. erewrite <- bounds_of_block_store in H6; eauto.
  erewrite <- bounds_of_block_store in H7; eauto.
Qed.

Theorem storev_inject:
  forall p f m1 m2 chunk a1 a2 b1 b2 v1
    (ABI: all_blocks_injected f m1),
    inject p f m1 m2 ->
    storev chunk m1 a1 b1 = Some v1 ->
    Val.expr_inject p f a1 a2 ->
    Val.expr_inject p f b1 b2 ->
    exists v2, storev chunk m2 a2 b2 = Some v2 /\ inject p f v1 v2.
Proof.
  intros.
  unfold storev in H0.
  destruct (mem_norm m1 a1 Ptr) eqn:?; try discriminate.
  unfold storev.
  exploit (norm_inject _ _ _ _ _ (Vptr b i) _ ABI H H1); eauto.
  congruence.
  intros [A B].
  unfold mem_norm in *.
  destruct B. simpl in inj_ok.
  unfold Val.inj_ptr in inj_ok.
  des (f b). des p0.
  inv inj_ok.
  destr. inv H4.
  exploit store_mapped_inject; eauto.
  intros [v2 [STORE INJ]].
  exists v2; split; auto.
  replace (Int.unsigned (Int.add i (Int.repr z)))
  with (Int.unsigned i + z).
  auto. symmetry. eapply address_inject'; eauto with mem.
Qed.

Theorem store_unmapped_inject:
  forall p f chunk m1 b1 ofs v1 n1 m2,
    inject p f m1 m2 ->
    store chunk m1 b1 ofs v1 = Some n1 ->
    f b1 = None ->
    inject p f n1 m2.
Proof.
  intros. inversion H.
  constructor; eauto with mem.
  -
    eapply store_unmapped_inj; eauto.
  -
    red; intros. erewrite <- bounds_of_block_store in H6; eauto.
    erewrite <- bounds_of_block_store in H5; eauto.
Qed.

Theorem store_outside_inject:
  forall p f m1 m2 chunk b ofs v m2',
  inject p f m1 m2 ->
  (forall b' delta ofs',
    f b' = Some(b, delta) ->
    perm m1 b' ofs' Cur Readable ->
    ofs <= ofs' + delta < ofs + size_chunk chunk -> False) ->
  store chunk m2 b ofs v = Some m2' ->
  inject p f m1 m2'.
Proof.
  intros. inversion H. constructor; eauto with mem.
  eapply store_outside_inj; eauto.
Qed.

Theorem storev_mapped_inject:
  forall p f chunk m1 a1 v1 n1 m2 a2 v2
         (ABI: all_blocks_injected f m1),
  inject p f m1 m2 ->
  storev chunk m1 a1 v1 = Some n1 ->
  Val.expr_inject p f a1 a2 ->
  Val.expr_inject p f v1 v2 ->
  exists n2,
    storev chunk m2 a2 v2 = Some n2 /\ inject p f n1 n2.
Proof.
  intros.
  unfold storev in *.
  revert H0; destr.
  generalize (norm_inject _ _ _ _ _ (Vptr b i) _ ABI H H1).
  intuition try congruence.
  specialize (H4 Ptr Heqv).
  destruct H4.
  inv H4. simpl in inj_ok.
  unfold Val.inj_ptr in inj_ok.
  des (f b). des p0.
  destr. inv inj_ok.
  replace (Int.unsigned (Int.add i (Int.repr z)))
  with (Int.unsigned i + z).
  eapply store_mapped_inject; eauto.
  symmetry. eapply address_inject'; eauto with mem.
Qed.

Theorem storebytes_mapped_inject:
  forall p f m1 b1 ofs bytes1 n1 m2 b2 delta bytes2,
  inject p f m1 m2 ->
  storebytes m1 b1 ofs bytes1 = Some n1 ->
  f b1 = Some (b2, delta) ->
  list_forall2 (memval_inject p f) bytes1 bytes2 ->
  Forall wt_memval bytes2 ->
  exists n2,
    storebytes m2 b2 (ofs + delta) bytes2 = Some n2
    /\ inject p f n1 n2.
Proof.
  intros. inversion H.
  exploit storebytes_mapped_inj; eauto.
  eapply inject_meminj_no_overlap; eauto. intros [n2 [STORE MI]].
  exists n2; split. eauto. constructor; eauto with mem.
  - intros. apply mi_freeblocks0.
    red; intros; elim H4; eapply storebytes_valid_block_1; eauto.
  - intros. eapply storebytes_valid_block_1; eauto.
  - intros. apply mi_freeblocks_undef0. intro A.
    eapply storebytes_valid_block_1 in A; eauto.
  - red; intros. erewrite <- bounds_of_block_storebytes in H7; eauto.
    erewrite <- bounds_of_block_storebytes in H8; eauto.
Qed.

Theorem storebytes_unmapped_inject:
  forall p f m1 b1 ofs bytes1 n1 m2,
  inject p f m1 m2 ->
  storebytes m1 b1 ofs bytes1 = Some n1 ->
  f b1 = None ->
  inject p f n1 m2.
Proof.
  intros. inversion H.
  constructor; eauto with mem.
  - eapply storebytes_unmapped_inj; eauto.
  - intros. apply mi_freeblocks0. red; intros; elim H2; eapply storebytes_valid_block_1; eauto.
  - intros. apply mi_freeblocks_undef0.
    intro A. eapply storebytes_valid_block_1 in A; eauto.
  - red; intros. erewrite <- bounds_of_block_storebytes in H5; eauto.
    erewrite <- bounds_of_block_storebytes in H6; eauto.
Qed.

Theorem storebytes_outside_inject:
  forall p f m1 m2 b ofs bytes2 m2',
  inject p f m1 m2 ->
  (forall b' delta ofs',
    f b' = Some(b, delta) ->
    perm m1 b' ofs' Cur Readable ->
    ofs <= ofs' + delta < ofs + Z_of_nat (length bytes2) -> False) ->
  storebytes m2 b ofs bytes2 = Some m2' ->
  inject p f m1 m2'.
Proof.
  intros. inversion H. constructor; eauto.
  - eapply storebytes_outside_inj; eauto.
  - intros. eapply storebytes_valid_block_1; eauto.
Qed.

Theorem storebytes_empty_inject:
  forall p f m1 b1 ofs1 m1' m2 b2 ofs2 m2',
  inject p f m1 m2 ->
  storebytes m1 b1 ofs1 nil = Some m1' ->
  storebytes m2 b2 ofs2 nil = Some m2' ->
  inject p f m1' m2'.
Proof.
  intros. inversion H. constructor; intros; eauto.
  - eapply storebytes_empty_inj; eauto.
  - intros. apply mi_freeblocks0. red; intros; elim H2; eapply storebytes_valid_block_1; eauto.
  - intros. eapply storebytes_valid_block_1; eauto.
  - intros. apply mi_freeblocks_undef0.
    intro A; eapply storebytes_valid_block_1 in A; eauto.
  - red; intros. erewrite <- bounds_of_block_storebytes in H5; eauto.
    erewrite <- bounds_of_block_storebytes in H6; eauto.
Qed.


Theorem alloc_left_unmapped_inject:
  forall p f m1 m2 lo hi m1' b1,
  inject p f m1 m2 ->
  alloc m1 lo hi = Some (m1', b1) ->
  exists f',
     inject p f' m1' m2
  /\ inject_incr f f'
  /\ f' b1 = None
  /\ (forall b, b <> b1 -> f' b = f b).
Proof.
  intros. inversion H.
  set (f' := fun b => if eq_block b b1 then None else f b).
  assert (inject_incr f f').
  {
    red; unfold f'; intros. destruct (eq_block b b1). subst b.
    assert (f b1 = None). eauto with mem. congruence.
    auto.
  }
  assert (mem_inj p f' m1 m2).
  {
    inversion mi_inj0; constructor; eauto with mem.
    - unfold f'; intros. destruct (eq_block b0 b1). congruence. eauto.
    - unfold f'; intros. destruct (eq_block b0 b1). congruence. eauto.
    - unfold f'; intros. destruct (eq_block b b1). congruence. eauto.
    - unfold f'; intros. destruct (eq_block b0 b1). congruence.
      apply memval_inject_incr with f; auto.
  }
  exists f'; split. constructor; eauto.
  - eapply alloc_left_unmapped_inj; eauto. unfold f'; apply dec_eq_true.
  - intros. unfold f'. destruct (eq_block b b1). auto.
    apply mi_freeblocks0. red; intro; elim H3. eauto with mem.
  - unfold f'; intros. destruct (eq_block b b1). congruence. eauto.
  - intros. apply mi_freeblocks_undef0.
    intro A. eapply valid_block_alloc in A; eauto.
  - unfold f'; red; intros.
    destruct (eq_block b0 b1); destruct (eq_block b2 b1); try congruence.
    eapply mi_no_overlap'0. eexact H3. eauto. eauto.
    erewrite <- bounds_of_block_alloc_other in H6; eauto.
    erewrite <- bounds_of_block_alloc_other in H7; eauto.
  -
    unfold f'; intros.
    destruct (eq_block b b1); try discriminate. eauto.
  -
    split; auto.
    split. unfold f'; apply dec_eq_true.
    intros; unfold f'; apply dec_eq_false; auto.
Qed.

Lemma alignment_of_size_inj:
  forall n n0,
    Z.max 0 n >= Z.max 0 n0 ->
    (alignment_of_size n >= alignment_of_size n0)%nat.
Proof.
  intros.
  rewrite ! Zmax_spec in H.
  unfold alignment_of_size.
  repeat (revert H; destr); omega.
Qed.

Lemma ioa_div:
  forall sz delta,
    inj_offset_aligned delta sz ->
    (two_p (Z.of_nat (alignment_of_size sz)) | delta).
Proof.
  intros.
  red in H.
  generalize (H Mint8unsigned)
             (H Mint16unsigned)
             (H Mint32)
             (H Mint64).
  simpl. clear H.
  unfold alignment_of_size.
  repeat (destr); try omega.
  apply Z.divide_1_l.
  change (two_power_pos 1) with (2). apply H0; omega.
  change (two_power_pos 2) with (4). apply H1; omega.
  change (two_power_pos 3) with (8). apply H2; omega.
Qed.

Lemma alloc_left_mapped_inj:
  forall m f m1 m2 lo hi m1' b1 b2 delta
         (Hmi: forall ofs b2 delta,
                 lo <= ofs < hi ->
                 f (nextblock m1) = Some (b2, delta) ->
                 memval_inject
                   m f
                   (Symbolic (Eval (Eundef (nextblock m1) (Int.repr ofs))) 0)
                   (ZMap.get (ofs + delta) (mem_contents m2) # b2))
         (msk_b2: (mem_mask m2) # b2 <> None),
    mem_inj m f m1 m2 ->
    lo = 0 ->
    alloc m1 lo hi = Some (m1', b1) ->
    valid_block m2 b2 ->
    inj_offset_aligned delta 8 ->
    (forall ofs k p, lo <= ofs < hi -> perm m2 b2 (ofs + delta) k p) ->
    (forall ofs, lo <= ofs < hi -> in_bound (ofs+delta) (bounds_of_block m2 b2)) ->
    f b1 = Some(b2, delta) ->
    mem_inj m f m1' m2.
Proof.
  intros m f m1 m2 lo hi m1' b1 b2 delta Hmi msk_b2 MI lo0 ALL VB IOA PRM BND FB.
  inversion MI. constructor; auto.
  -
    intros.
    exploit perm_alloc_inv; eauto. intros. destruct (eq_block b0 b1). subst b0.
    rewrite H in FB; inv FB. eauto. eauto.
  - intros.
    destruct (eq_block b0 b1). subst b1.
    rewrite H in FB ; inv FB. apply BND.
    unfold alloc, low_alloc in ALL.
    destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
    inv ALL. unfold bounds_of_block in H0. simpl in H0.
    rewrite PMap.gss in H0.
    unfold in_bound in H0. simpl in H0. auto.
    erewrite <- bounds_of_block_alloc_other in H0; eauto.
  - intros.
    destruct (eq_block b b1); eauto.
    + subst b1.
      rewrite H in FB; inv FB.
      erewrite alloc_result with (b:=b) in H; eauto.
      erewrite alloc_mask with (m1':=m1'); eauto.
      destruct (bounds_of_block m2 b2) eqn:?.
      assert (Z.max 0 (z0-z) >= Z.max 0 (hi - 0)).
      {
        destruct (zlt 0 hi).
        assert (z <= 0 + delta < z0).
        apply (BND 0). omega.
        assert (z <= hi - 1 + delta < z0).
        apply (BND). omega.
        rewrite ! Zmax_spec.
        repeat destr; try omega.
        rewrite ! Zmax_spec.
        repeat destr; try omega.
      }
      unfold mask.
      destr.
      * apply alignment_ok in Heqo.
        subst.
        apply alignment_of_size_inj; auto.
        unfold get_size, bounds_of_block in *.
        destr; try des p; inv Heqp.
        rewrite Z.sub_0_r in H0; auto.
        rewrite ! Z.sub_0_r in H0; auto.
      * apply ioa_div in IOA. simpl in IOA.
        apply Zdivides_trans with (y:=8).
        unfold alignment_of_size; repeat destruct zlt; simpl.
        exists 8; omega.
        change (two_power_pos 1) with 2. exists 4; omega.
        change (two_power_pos 2) with 4. exists 2; omega.
        change (two_power_pos 3) with 8. exists 1; omega.
        change (two_power_pos 3) with 8 in IOA. auto.
    + apply mi_align'0 in H.
      erewrite <- mask_alloc_other with (m2:=m1'); eauto.
  -
    unfold alloc, low_alloc in ALL.
    destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
    injection ALL; intros NEXT MEM.
    intros. rewrite <- MEM; simpl. rewrite NEXT.
    rewrite PMap.gsspec.
    destruct (peq b0 b1); subst; eauto.
    + unfold perm in H0; simpl in H0.
      rewrite PMap.gss in H0. unfold perm_order' in H0. revert H0; destr.
      rewrite FB in H. inv H.
      rewrite get_init_block; auto.
      apply Hmi; auto.
      des (zle 0 ofs); des (zlt ofs hi).
      des (zle 0 ofs); des (zlt ofs hi).
    + apply mi_memval0; auto.
      unfold perm in H0; simpl in H0. unfold perm.
      rewrite PMap.gso in H0; auto.
  - generalize (alloc_size_mem _ _ _ _ _ ALL). omega.
Qed.

Theorem memval_inject_alloc_left_mapped:
  forall p f m1 m2 lo hi m1' b1 b2 delta
         (ALLOC: alloc m1 lo hi = Some (m1', b1))
         (VB: valid_block m2 b2)
         (RANGE: 0 <= delta <= Int.max_unsigned)
         (INJ: mem_inj p f m1 m2)
         (VB_none: forall b : block, ~ valid_block m1 b -> f b = None)
         (vb_none: forall b o, ~ valid_block m1 b -> p b o = None)
         (INJ': mem_inj p (fun b : positive => if eq_block b b1 then Some (b2, delta) else f b) m1 m2)
         (ofs : Z) (b0 : block) (delta0 : Z)
         (RANGE': lo <= ofs < hi)
         (f'eq: (if eq_block (nextblock m1) b1 then Some (b2, delta) else f (nextblock m1)) =
                Some (b0, delta0))
         (contents: forall ofs,
                      lo <= ofs < hi ->
                      ZMap.get (ofs + delta) (mem_contents m2) # b0 =
                      Symbolic (Eval (Eundef b0 (Int.repr (ofs+delta)))) O),
    memval_inject p
                  (fun b : block => if eq_block b b1 then Some (b2, delta) else f b)
                  (Symbolic (Eval (Eundef (nextblock m1) (Int.repr ofs))) 0)
                  (ZMap.get (ofs + delta0) (mem_contents m2) # b0).
Proof.
  intros.
  assert (nextblock m1 = b1).
  {
    unfold alloc, low_alloc in ALLOC.
    destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
    inv ALLOC. reflexivity.
  }
  subst.
  rewrite dec_eq_true in f'eq.
  inv f'eq.
  rewrite contents; auto.
  apply memval_inject_symb; simpl; auto.
  constructor ; simpl.
  simpl. destr.
  * rewrite vb_none in Heqo.
    congruence.
    unfold valid_block ; xomega.
  * unfold Val.inj_undef.
    rewrite dec_eq_true.
    rewrite Val.int_add_repr.
    reflexivity.
Qed.

Lemma mask_alloc_old:
  forall m1 lo hi m1' b1,
    alloc m1 lo hi = Some (m1', b1) ->
    mask m1 b1 = O.
Proof.
  intros.
  unfold alloc, low_alloc in H.
  destruct (conc_mem_alloc_dec m1 0 hi _); try discriminate.
  unfold nat_mask, mask; inv H; simpl.
  rewrite msk_valid; auto. simpl. xomega.
Qed.

Lemma nat_mask_alloc_old:
  forall m1 lo hi m1' b1,
    alloc m1 lo hi = Some (m1', b1) ->
    nat_mask m1 b1 = Int.mone.
Proof.
  intros.
  unfold nat_mask.
  erewrite mask_alloc_old; eauto.
  rewrite two_power_nat_O. change (Int.repr (1-1)) with Int.zero.
  apply Int.not_zero.
Qed.

Theorem alloc_left_mapped_inject_aux_mem_inj:
  forall p f m1 m2 lo hi m1' b1 b2 delta
         (INJ: inject p f m1 m2)
         (lo0: lo = 0)
         (ALLOC: alloc m1 lo hi = Some (m1', b1))
         (VB: valid_block m2 b2)
         (MSKb2: (mem_mask m2) # b2 <> None)
         (DMU: 0 <= delta <= Int.max_unsigned)
         (PERM_MU:
            forall ofs k p, perm m2 b2 ofs k p -> delta = 0 \/ 0 <= ofs < Int.max_unsigned)
         (BND_PERM: forall ofs k p, lo <= ofs < hi -> perm m2 b2 (ofs + delta) k p)
         (IOA: inj_offset_aligned delta 8)
         (space_free:
            forall b delta' ofs k p,
              f b = Some (b2, delta') ->
              perm m1 b ofs k p ->
              lo + delta <= ofs + delta' < hi + delta ->
              False)
         (Hmi:
            forall ofs, lo <= ofs < hi ->
                        ZMap.get (ofs + delta) (mem_contents m2) # b2 =
                        Symbolic (Eval (Eundef b2 (Int.repr (ofs+delta)))) O),
    mem_inj p (fun b => if eq_block b b1 then Some(b2, delta) else f b) m1' m2.
Proof.
  intros. inversion INJ.
  set (f' := (fun b => if eq_block b b1 then Some(b2, delta) else f b)).
  assert (mem_inj p f' m1 m2).
  {
    inversion mi_inj0; constructor; eauto with mem.
    - unfold f'; intros b0 b3 delta0 ofs k p0 INJ' PERM'. destruct (eq_block b0 b1).
      inversion INJ'. subst b0 b3 delta0.
      elim (fresh_block_alloc _ _ _ _ _ ALLOC). eauto with mem.
      eauto.
    - unfold f'; intros b0 b3 delta0 ofs INJ' BND. destruct (eq_block b0 b1).
      inversion INJ'. subst b0 b3 delta0.
      generalize (BND_PERM (ofs) Cur Freeable).
      erewrite bounds_of_block_alloc_old in BND; eauto.
      unfold in_bound in BND; simpl in BND; omega.
      eauto.
    - unfold f'; intros.
      destruct (eq_block b b1); eauto. subst b1. inv H.
      erewrite (mask_alloc_old m1); eauto.
      split.
      + unfold mask. destr; omega.
      + simpl. apply Z.divide_1_l.
    - unfold f'; intros b0 ofs b3 delta0 INJ' PERM. destruct (eq_block b0 b1).
      inversion INJ'. subst b0 b3 delta0.
      elim (fresh_block_alloc _ _ _ _ _ ALLOC). eauto with mem.
      apply memval_inject_incr with f; auto.
      red; unfold f'; intros. destruct (eq_block b b1). subst b.
      assert (f b1 = None). eauto with mem. congruence.
      auto.
  }
  eapply alloc_left_mapped_inj with (lo:=lo) (hi:=hi); eauto.
  - intros; eapply memval_inject_alloc_left_mapped; eauto.
    assert (b1 = nextblock m1).
    {
      apply alloc_result in ALLOC; auto.
    }
    unfold f' in *. subst.
    rewrite dec_eq_true in H1. inv H1.
    apply Hmi.
  - intros.
    generalize (BND_PERM _ Cur Freeable H0).
    intro A. apply perm_bounds in A. auto.
  - rewrite dec_eq_true; auto.
Qed.

Theorem alloc_left_mapped_inject_aux:
  forall p f m1 m2 lo hi m1' b1 b2 delta
         (INJ: inject p f m1 m2)
         (lo0: lo = 0)
         (ALLOC: alloc m1 lo hi = Some (m1', b1))
         (VB: valid_block m2 b2)
         (MSKb2: (mem_mask m2) #b2 <> None)
         (DMU: 0 <= delta <= Int.max_unsigned)
         (PERM_MU:
            forall ofs k p, perm m2 b2 ofs k p -> delta = 0 \/ 0 <= ofs < Int.max_unsigned)
         (BND_PERM:
            forall ofs k p, lo <= ofs < hi -> perm m2 b2 (ofs + delta) k p)
           (IOA: inj_offset_aligned delta 8)
           (space_free_perm:
              forall b delta' ofs k p,
                f b = Some (b2, delta') ->
                perm m1 b ofs k p ->
                lo + delta <= ofs + delta' < hi + delta -> False)
           (space_free_bnd:
              forall b delta' ofs,
                f b = Some (b2, delta') ->
                in_bound ofs (bounds_of_block m1 b) ->
                lo + delta <= ofs + delta' < hi + delta -> False)
           (Hmi:
              forall ofs : Z,
                lo <= ofs < hi ->
                ZMap.get (ofs + delta) (mem_contents m2) # b2 =
                Symbolic (Eval (Eundef b2 (Int.repr (ofs + delta)))) 0)
           (BND_MU:
              forall ofs, in_bound ofs (bounds_of_block m2 b2) ->
                          delta = 0 \/ 0 <= ofs < Int.max_unsigned)
           (BND_bound:
              forall ofs, lo <= ofs < hi -> in_bound (ofs+delta) (bounds_of_block m2 b2)),
  inject p (fun b => if eq_block b b1 then Some(b2, delta) else f b) m1' m2.
Proof.
  intros. inversion INJ.
  set (f' := (fun b => if eq_block b b1 then Some(b2, delta) else f b)).
  constructor; auto.
  - eapply alloc_left_mapped_inject_aux_mem_inj; eauto.
  -
    unfold f'; intros b NVB. destruct (eq_block b b1). subst b.
    elim NVB. eauto with mem.
    eauto with mem.
  -
    unfold f'; intros. destruct (eq_block b b1). congruence. eauto.
  - intros. apply mi_freeblocks_undef0.
    intro A; eapply valid_block_alloc in A; eauto.
  - unfold f'; red. intros b0 b1' delta1 b3 b2' delta2 ofs1 ofs2 b03 INJ1 INJ2 PERM1 PERM2.
    erewrite bounds_of_block_alloc_eq in PERM1; eauto.
    erewrite bounds_of_block_alloc_eq in PERM2; eauto.
    destruct (eq_block b0 b1); destruct (eq_block b3 b1).
    + congruence.
    + inv INJ1. destruct (plt b3 (nextblock m1')).
      destruct (eq_block b1' b2'); auto. subst b2'. right; red; intros.
      eapply space_free_bnd; eauto. rewrite <- H.
      unfold in_bound in PERM1. revert PERM1.
      simpl. intros; omega.
      unfold in_bound in PERM2. revert PERM2.
      simpl. intros; omega.
    + inv INJ2. destruct (plt b0 (nextblock m1')).
      destruct (eq_block b1' b2'); auto. subst b2'. right; red; intros.
      eapply space_free_bnd; eauto. rewrite H.
      unfold in_bound in PERM2. revert PERM2.
      simpl. intros; omega.
      unfold in_bound in PERM1. simpl in PERM1; omega.
    + destruct (plt b0 (nextblock m1')).
      destruct (plt b3 (nextblock m1')).
      eauto.
      unfold in_bound in PERM2. simpl in PERM2; omega.
      unfold in_bound in PERM1. simpl in PERM1; omega.
  -
    intros b b' delta0 INJ'.
    destruct (eq_block b b1).
    subst. injection INJ'; intros; subst b' delta0. omega. eauto.
Qed.

Definition block_undefs lo hi delta m2 b2 :=
  forall ofs : Z,
    lo <= ofs < hi ->
    ZMap.get (ofs + delta) (mem_contents m2) # b2 =
    Symbolic (Eval (Eundef b2 (Int.repr (ofs + delta)))) 0.

Theorem alloc_left_mapped_inject:
  forall p f m1 m2 lo hi m1' b1 b2 delta,
  inject p f m1 m2 ->
  lo = 0 ->
  alloc m1 lo hi = Some (m1', b1) ->
  valid_block m2 b2 ->
  (mem_mask m2) # b2 <> None ->
  0 <= delta <= Int.max_unsigned ->
  (forall ofs, in_bound ofs (bounds_of_block m2 b2) -> delta = 0 \/ 0 <= ofs < Int.max_unsigned) ->
  (forall ofs k p, lo <= ofs < hi -> perm m2 b2 (ofs + delta) k p) ->
  inj_offset_aligned delta 8 ->
  (forall b delta' ofs,
   f b = Some (b2, delta') ->
   in_bound ofs (bounds_of_block m1 b) ->
   lo + delta <= ofs + delta' < hi + delta -> False) ->
  block_undefs lo (Z.max 0 hi) delta m2 b2 ->
  exists f',
     inject p f' m1' m2
  /\ inject_incr f f'
  /\ f' b1 = Some(b2, delta)
  /\ (forall b, b <> b1 -> f' b = f b).
Proof.
  intros p f m1 m2 lo hi m1' b1 b2 delta INJ lo0 ALLOC VB MSK_m2 delta_rep bnd_rep bnd_perm
         ioa bnd_inj bu.
 inversion INJ.
  set (f' := fun b => if eq_block b b1 then Some(b2, delta) else f b).
  assert (inject_incr f f').
  {
    red; unfold f'; intros. destruct (eq_block b b1). subst b.
    assert (f b1 = None). eauto with mem. congruence.
    auto.
  }
  exists f'.
  split.
  eapply alloc_left_mapped_inject_aux; eauto.
  - intros; apply bnd_rep.
    apply perm_bounds in H0. auto.
  - intros. eapply bnd_inj in H0; eauto.
    apply perm_bounds in H1; auto.
  - intros. eapply bu; eauto.
    split; try omega.
    cut (hi <= Z.max 0 hi). omega.
    apply Zmax_bound_r. omega.
  - intros.
    eapply bnd_perm with (k:=Cur) (p:=Freeable) in H0; eauto.
    apply perm_bounds in H0. auto.
  - unfold f'. split; auto.
    split; auto.
    rewrite dec_eq_true; auto.
    intros; rewrite dec_eq_false; auto.
Qed.

Lemma mem_alloc_block_undefs:
  forall m lo hi m' b,
    alloc m lo hi = Some (m', b) ->
    block_undefs 0 hi 0 m' b.
Proof.
  intros m lo hi m' b ALLOC; unfold Mem.alloc, Mem.low_alloc in ALLOC.
  destruct (Mem.conc_mem_alloc_dec m 0 hi); try discriminate.
  inv ALLOC.
  red; simpl. intros.
  rewrite PMap.gss.
  intros; rewrite Z.add_0_r.
  apply Mem.get_init_block; auto.
Qed.

Lemma alloc_contents_old:
  forall m1 lo hi m2 b
         (ALLOC: alloc m1 lo hi = Some (m2, b))
         b'
         (DIFF: b' <> b),
    (mem_contents m2) # b' = (mem_contents m1) # b'.
Proof.
  unfold alloc, low_alloc.
  intros; revert ALLOC; destr. inv ALLOC. simpl.
  rewrite PMap.gso; auto.
Qed.
 
Lemma align_distr:
  forall z z1,
    align z 8 + align z1 8 = align (align z 8 + z1) 8.
Proof.
  exact Alloc.align_distr.
Qed.

Opaque align.

Lemma mk_block_list_aux_eq:
  forall lo hi b' bs ,
    mk_block_list_aux
      (fun b0 : block =>
         get_size
           (PMap.set b' (Some (lo, hi)) bs)
           b0)
      (pred (Pos.to_nat (Pos.succ b'))) =
    (b',hi-lo):: mk_block_list_aux
               (fun b0 => get_size bs b0)
               (pred (Pos.to_nat b')).
Proof.
  intros.
  replace (pred (Pos.to_nat (Pos.succ b'))) with (S (pred (Pos.to_nat b'))) by (zify; omega).
  rewrite mbla_rew; simpl.
  replace (Pos.of_nat (S (pred (Pos.to_nat b')))) with b'.
  unfold get_size. rewrite PMap.gss.
  f_equal.
  erewrite mk_block_list_aux_ext'; eauto.
  intro i.
  rewrite Pos2Nat.id; auto. intro.
  rewrite PMap.gso; auto. intro; subst. xomega.
  rewrite <- S_pred with (n:=Pos.to_nat b') (m:=O) by xomega.
  rewrite Pos2Nat.id; auto.
Qed.

Lemma size_mem_inf_add_larger:
  forall (m1 m2 : mem) (p1 p2: block) (lo hi lo' hi': Z),
    hi >= 0 -> hi' >= 0 -> hi >= lo ->
    hi' - lo' >= hi - lo ->
    size_mem_aux
      (mk_block_list_aux (size_block m2)
                         (pred (Pos.to_nat p2))) <=
   size_mem_aux
     (mk_block_list_aux (size_block m1)
                        (pred (Pos.to_nat p1))) ->
   size_mem_aux
     (mk_block_list_aux
        (get_size
           (PMap.set (p2) (Some (lo, hi)) (mem_blocksize m2)))
        (Pos.to_nat p2)) <=
   size_mem_aux
     (mk_block_list_aux
        (get_size
           (PMap.set (p1) (Some (lo', hi')) (mem_blocksize m1)))
        (Pos.to_nat p1)).
Proof.
  intros m1 m2 p1 p2 lo hi lo' hi' Hpos Hpos' Hhilo Hsizes sizes.
  replace (Pos.to_nat p2) with (pred (Pos.to_nat (Pos.succ p2))) by (zify; omega).
  replace (Pos.to_nat p1) with (pred (Pos.to_nat (Pos.succ p1))) by (zify; omega).
  rewrite ! mk_block_list_aux_eq; auto.
  rewrite ! size_mem_aux_first.
  apply Z.add_le_mono; auto.
  apply align_add; auto.
  omega.
  split.
  apply Zmax_bound_l; omega.
  apply Z.max_le_compat_l. omega.
Qed.

Lemma alloc_less_ok:
  forall m z z0,
    Mem.alloc m 0 z = None ->
    Mem.alloc m 0 z0 <> None ->
    0 <= z <= z0 ->
    False.
Proof.
  intros m z z0 ALLOC ALLOC' INF.
  revert ALLOC ALLOC'.
  unfold Mem.alloc, Mem.low_alloc.
  destr.
  revert ALLOC'; destr. clear ALLOC' Heqs Heqs0 ALLOC.
  unfold Mem.conc_mem_alloc, Alloc.alloc_blocks in *.
  repeat (revert e; destr).
  repeat (revert n; destr).
  clear n Heqs0 e Heqs.
  apply Alloc.alloc_size_mem_aux in Heqp.
  apply Alloc.alloc_size_mem_aux in Heqp0.
  cut (z1 <= z3); try omega.
  subst.
  apply (size_mem_inf_add_larger m m _ _ 0 z 0 z0); try omega.
Qed.

Lemma size_mem_inf_add':
  forall (m1 m2 : mem) (p1 p2: block) (lo hi : Z),
    hi >= 0 -> lo <= hi ->
    size_mem_aux
      (mk_block_list_aux (size_block m2)
                         (pred (Pos.to_nat p2))) <=
   size_mem_aux
     (mk_block_list_aux (size_block m1)
                        (pred (Pos.to_nat p1))) ->
   size_mem_aux
     (mk_block_list_aux
        (get_size
           (PMap.set (p2) (Some (lo, hi)) (mem_blocksize m2)))
         (Pos.to_nat (p2))) <=
   size_mem_aux
     (mk_block_list_aux
        (get_size
           (PMap.set (p1) (Some (lo, hi)) (mem_blocksize m1)))
        (Pos.to_nat (p1))).
Proof.
  intros.
  apply size_mem_inf_add_larger; auto. omega. omega.
Qed.

Lemma alloc_same_inj_inf:
  forall m1 m2 lo hi m1' b1
         (ALLOC : alloc m1 lo hi = Some (m1', b1))
         m b
         (ALLOC2 : alloc m2 lo hi = Some (m, b))
         (inf : size_mem m2 <= size_mem m1)
         (Hpos: hi >= 0),
    size_mem m <= size_mem m1'.
Proof.
  intros.
  apply alloc_size_mem' in ALLOC; auto.
  apply alloc_size_mem' in ALLOC2; auto.
  rewrite ALLOC. rewrite ALLOC2.
  omega.
Qed.

Lemma alloc_parallel_inj:
  forall p f m1 m2 lo hi m1' b1 m b
         (INJ: inject p f m1 m2)
         (ALLOC: alloc m1 lo hi = Some (m1', b1))
         (HIpos : hi >= 0)
         (ALLOC2 : alloc m2 lo hi = Some (m, b)),
    mem_inj p (fun b0 : positive => if peq b0 b1 then Some (b, 0) else f b0) m1' m.
Proof.
  intros. inv INJ. inv mi_inj0.
  constructor; simpl; intros; eauto.
  - des (peq b0 b1).
    + inv H.
      apply perm_implies with (p1:=Freeable).
      apply (perm_alloc_2 _ _ _ _ _ ALLOC2); auto.
      apply perm_bounds in H0.
      rewrite (bounds_of_block_alloc _ _ _ _ _ ALLOC) in H0.
      unfold in_bound in H0; simpl in H0. omega.
      apply perm_F_any.
    + apply (perm_alloc_4 _ _ _ _ _ ALLOC) in H0; auto.
      generalize (mi_perm0 _ _ _ _ _ _ H H0).
      eapply perm_alloc_1; eauto.
  - des (peq b0 b1).
    + inv H.
      rewrite (bounds_of_block_alloc _ _ _ _ _ ALLOC2).
      rewrite (bounds_of_block_alloc _ _ _ _ _ ALLOC) in H0.
      unfold in_bound in *; simpl in *; omega.
    + rewrite <- (bounds_of_block_alloc_other _ _ _ _ _ ALLOC2); auto.
      rewrite <- (bounds_of_block_alloc_other _ _ _ _ _ ALLOC) in H0; auto.
      revert H0; apply mi_bounds0; auto.
      intro; subst.
      apply mi_mappedblocks0 in H.
      apply fresh_block_alloc in ALLOC2. auto.
  - destruct (peq b0 b1); subst; auto; try inv H.
    rewrite (alloc_mask _ _ _ _ _ ALLOC).
    rewrite (alloc_mask _ _ _ _ _ ALLOC2).
    split. omega. apply Z.divide_0_r.
    rewrite <- (mask_alloc_other _ _ _ _ _ ALLOC); auto.
    rewrite <- (mask_alloc_other _ _ _ _ _ ALLOC2).
    apply mi_align'0. auto.
    apply mi_mappedblocks0 in H1.
    apply fresh_block_alloc in ALLOC2; auto.
    intro; subst; auto.
  - des (peq b0 b1).
    + inv H.
      generalize (mem_alloc_block_undefs _ _ _ _ _ ALLOC)
                 (mem_alloc_block_undefs _ _ _ _ _ ALLOC2).
      unfold block_undefs; intros B1 B2.
      rewrite <- (Z.add_0_r ofs) at 1.
      apply perm_bounds in H0.
      rewrite (bounds_of_block_alloc _ _ _ _ _ ALLOC) in H0.
      unfold in_bound in H0; simpl in H0.
      auto.
      rewrite B1; auto.
      rewrite B2; auto.
      constructor. constructor.
      simpl. rewrite mi_freeblocks_undef0; auto.
      simpl; unfold Val.inj_undef; simpl.
      rewrite peq_true.
      rewrite Int.add_zero. auto.
      eapply fresh_block_alloc; eauto.
    + rewrite (alloc_contents_old _ _ _ _ _ ALLOC2).
      rewrite (alloc_contents_old _ _ _ _ _ ALLOC); auto.
      apply (perm_alloc_4 _ _ _ _ _ ALLOC) in H0; auto.
      specialize (mi_memval0 _ _ _ _ H H0).
      inv mi_memval0. constructor.
      apply Val.expr_inject_incr with (f1:=f); auto.
      red; intros.
      destr. subst.
      rewrite mi_freeblocks0 in H1. congruence.
      eapply (fresh_block_alloc); eauto.
      apply mi_mappedblocks0 in H; auto.
      apply (fresh_block_alloc) in ALLOC2; auto.
      intro; subst; congruence.
  - eapply alloc_same_inj_inf; eauto.
Qed.

Lemma less_alloc_less:
  forall m1 m2 hi
         (ALLOC : alloc m1 0 hi <> None)
         (HIpos : hi >= 0)
         (NALLOC : alloc m2 0 hi = None)
         (sizes: size_mem m2 <= size_mem m1),
    False.
Proof.
  intros m1 m2 hi.
  unfold alloc, low_alloc.
  destr. revert NALLOC; destr. clear ALLOC.
  clear Heqs Heqs0 NALLOC.
  unfold conc_mem_alloc in n, e.
  unfold alloc_blocks in n, e.
  repeat (revert n; destr).
  repeat (revert e; destr).
  clear e Heqs0 n Heqs.
  apply alloc_size_mem_aux in Heqp;
    apply alloc_size_mem_aux in Heqp0.
  cut (z1 <= z); try omega.
  subst. clear g l.
  apply size_mem_inf_add'; auto; try omega; try xomega.
Qed.

Theorem alloc_parallel_inject:
  forall p f m1 m2 lo1 hi1 m1' b1 (ORD: lo1 <= hi1)
         (INJ: inject p f m1 m2)
         (ALLOC: alloc m1 lo1 hi1 = Some (m1', b1))
         (HIpos: hi1 >= 0),
  exists f', exists m2', exists b2,
                           alloc m2 lo1 hi1 = Some (m2', b2)
                           /\ inject p f' m1' m2'
                           /\ inject_incr f f'
                           /\ f' b1 = Some(b2, 0)
                           /\ (forall b, b <> b1 -> f' b = f b).
Proof.
  intros.
  destruct (alloc m2 lo1 hi1) eqn:?.
  - destruct p0.
    exists (fun b0 => if peq b0 b1 then Some (b,0) else f b0).
    exists m. exists b.
    intuition.
    + constructor; simpl; intros; eauto.
      * eapply alloc_parallel_inj; eauto.
      * destr. subst.
        contradict H. eapply valid_new_block; eauto.
        inv INJ; auto.
        apply mi_freeblocks0; auto.
        intro A. apply (valid_block_alloc _ _ _ _ _ ALLOC) in A. congruence.
      * inv INJ; eauto.
        revert H; destr; subst.
        inv H.
        apply valid_new_block in Heqo; auto.
        apply mi_mappedblocks0 in H; auto.
        eapply valid_block_alloc in H; eauto.
      * inv INJ. apply mi_freeblocks_undef0.
        intro A.
        eapply valid_block_alloc in A; eauto.
      * inv INJ. red. intros b0 b1' delta1 b2 b2' delta2 ofs1 ofs2 diff F0 F2 i0 i2.
        {
          des (peq b0 b1).
          des (peq b2 b1).
          - left. inv F0.
            intro; subst.
            apply mi_mappedblocks0 in F2.
            apply (fresh_block_alloc _ _ _ _ _ Heqo) in F2; auto.
          - des (peq b2 b1). inv F2.
            + left.
              intro; subst.
              apply mi_mappedblocks0 in F0.
              apply (fresh_block_alloc _ _ _ _ _ Heqo) in F0; auto.
            + red in mi_no_overlap'0.
              apply (mi_no_overlap'0 _ _ _ _ _ _ _ _ diff); eauto.
              rewrite <- (bounds_of_block_alloc_other _ _ _ _ _ ALLOC) in i0; auto.
              rewrite <-(bounds_of_block_alloc_other _ _ _ _ _ ALLOC) in i2; auto.
        }
      * inv INJ.
        destruct (peq b0 b1); subst; auto.
        inv H. omega. eauto.
    + unfold inject_incr.
      intros.
      des (peq b0 b1).
      rewrite mi_freeblocks with (m:=p) (m1:=m1) (m2:=m2) in H; auto.
      congruence.
      eapply fresh_block_alloc; eauto.
    + rewrite peq_true. auto.
    + rewrite peq_false; auto.
  - exfalso. subst.
    rewrite alloc_lo_0 in *.
    generalize (mi_size_mem _ _ _ _ (mi_inj _ _ _ _ INJ)).
    refine (less_alloc_less m1 m2 hi1 _ HIpos Heqo).
    rewrite ALLOC; congruence.
Qed.

Preservation of free operations

Lemma free_bounds:
  forall m b lo hi m' b',
    free m b lo hi = Some m' ->
    bounds_of_block m' b' = bounds_of_block m b' \/ bounds_of_block m' b' = (0,0).
Proof.
  intros m b lo hi m' b'.
  unfold free. destr. inv H.
  rewrite unchecked_free_bounds.
  destr. destr. destr.
Qed.

Lemma free_mask:
  forall m b lo hi m' b',
    free m b lo hi = Some m' ->
    mask m' b' = mask m b' \/ (mask m' b' = O /\
                               bounds_of_block m b' = (lo,hi)).
Proof.
  intros m b lo hi m' b'.
  unfold free. destr. inv H.
  rewrite unchecked_free_mask.
  destr. destr. destr.
  subst. rewrite Heqp.
  des (zeq z lo); des (zeq z0 hi).
Qed.

Lemma perm_free_list:
  forall l m m' b ofs k p,
  free_list m l = Some m' ->
  perm m' b ofs k p ->
  perm m b ofs k p /\
  (forall lo hi, In (b, lo, hi) l -> lo <= ofs < hi -> False).
Proof.
  induction l; simpl; intros.
  inv H. auto.
  destruct a as [[b1 lo1] hi1].
  destruct (free m b1 lo1 hi1) as [m1|] eqn:E; try discriminate.
  exploit IHl; eauto. intros [A B].
  split. eauto with mem.
  intros. destruct H1. inv H1.
  elim (perm_free_2 _ _ _ _ _ E ofs k p). auto. auto.
  eauto.
Qed.

Lemma drop_outside_inject:
  forall m f m1 m2 b lo hi p m2',
    inject m f m1 m2 ->
    drop_perm m2 b lo hi p = Some m2' ->
    (forall b' delta ofs k p,
       f b' = Some(b, delta) ->
       perm m1 b' ofs k p -> lo <= ofs + delta < hi -> False) ->
    inject m f m1 m2'.
Proof.
  intros. destruct H. constructor; eauto.
  eapply drop_outside_inj; eauto.
  intros. unfold valid_block in *. erewrite nextblock_drop; eauto.
Qed.

Composing two memory injections.

Lemma mem_inj_compose:
  forall p p' f f' m1 m2 m3
         (Ffree: forall b, ~ valid_block m1 b -> match f b with
                                                     | None => True
                                                     | Some (b',delta) => ~ valid_block m2 b'
                                                 end),
  mem_inj p f m1 m2 -> mem_inj p' f' m2 m3 -> mem_inj (Val.inj_m f p p') (compose_meminj f f') m1 m3.
Proof.
  intros. unfold compose_meminj. inv H; inv H0; constructor; intros; eauto.
  -
    destruct (f b1) as [[b' delta'] |] eqn:?; try discriminate.
    destruct (f' b') as [[b'' delta''] |] eqn:?; inv H.
    replace (ofs + (delta' + delta'')) with ((ofs + delta') + delta'') by omega.
    eauto.
  - destruct (f b1) as [[b' delta'] |] eqn:?; try discriminate.
    destruct (f' b') as [[b'' delta''] |] eqn:?; inv H.
    replace ( ofs + (delta' + delta'')) with ((ofs + delta') + delta'') by omega.
    eauto.
  - destruct (f b) as [[b' delta'] |] eqn:?; try discriminate.
    destruct (f' b') as [[b'' delta''] |] eqn:?; inv H.
    clear - mi_align'0 mi_align'1 Heqo Heqo0 Ffree.
    unfold nat_mask in *.
    specialize (mi_align'0 _ _ _ Heqo).
    specialize (mi_align'1 _ _ _ Heqo0).
    destruct mi_align'1; destruct mi_align'0.
    split; try omega.
    apply Z.divide_add_r; auto.
    apply Z.divide_trans with (two_p (Z.of_nat (mask m2 b'))); auto.
    red.
    exists (two_p (Z.of_nat (mask m2 b') - Z.of_nat (mask m1 b))).
    rewrite <- two_p_is_exp by omega.
    replace (Z.of_nat (mask m2 b') - Z.of_nat (mask m1 b) + Z.of_nat (mask m1 b))
           with (Z.of_nat (mask m2 b')) by omega. auto.
-
    destruct (f b1) as [[b' delta'] |] eqn:?; try discriminate.
    destruct (f' b') as [[b'' delta''] |] eqn:?; inv H.
    replace (ofs + (delta' + delta'')) with ((ofs + delta') + delta'') by omega.
    eapply memval_inject_compose; eauto.
- omega.
Qed.

Theorem inject_compose:
  forall p p' f f' m1 m2 m3,
  inject p f m1 m2 -> inject p' f' m2 m3 ->
  inject (Val.inj_m f p p') (compose_meminj f f') m1 m3.
Proof.
  unfold compose_meminj; intros.
  inv H; inv H0. constructor.
  -
    eapply mem_inj_compose; eauto. intros; destr.
    des p0. rewrite mi_freeblocks0 in Heqo; auto.
    congruence.
  -
    intros. erewrite mi_freeblocks0; eauto.
  -
    intros.
    destruct (f b) as [[b1 delta1] |] eqn:?; try discriminate.
    destruct (f' b1) as [[b2 delta2] |] eqn:?; inv H.
    eauto.
  - intros.
    unfold Val.inj_m.
    rewrite mi_freeblocks_undef0; auto.
    rewrite mi_freeblocks0; eauto.
  -
    red; intros.
    destruct (f b1) as [[b1x delta1x] |] eqn:?; try discriminate.
    destruct (f' b1x) as [[b1y delta1y] |] eqn:?; inv H0.
    destruct (f b2) as [[b2x delta2x] |] eqn:?; try discriminate.
    destruct (f' b2x) as [[b2y delta2y] |] eqn:?; inv H1.
    exploit mi_no_overlap'0; eauto. intros A.
    destruct (eq_block b1x b2x).
    subst b1x. destruct A. congruence.
    assert (delta1y = delta2y) by congruence. right; omega.
    exploit mi_no_overlap'1. eauto. eauto. eauto.
    eapply mi_bounds. eauto. eauto. eauto.
    eapply mi_bounds. eauto. eauto. eauto.
    intuition omega.
  -
    intros.
    destruct (f b) as [[b1 delta1] |] eqn:?; try discriminate.
    destruct (f' b1) as [[b2 delta2] |] eqn:?; inv H.
    specialize (mi_delta_pos0 _ _ _ Heqo).
    specialize (mi_delta_pos1 _ _ _ Heqo0). omega.
Qed.

Lemma val_lessdef_inject_compose:
  forall p p' f v1 v2 v3,
  Val.lessdef p v1 v2 -> Val.expr_inject p' f v2 v3 -> Val.expr_inject (Val.inj_m inject_id p p') f v1 v3.
Proof.
  intros. unfold Val.lessdef in H.
  eapply Val.expr_inject_ext with (f:= compose_meminj inject_id f).
  intros; eauto.
  unfold compose_meminj. simpl. repeat destr.
  eapply Val.expr_inject_compose; eauto.
Qed.

Lemma val_inject_lessdef_compose:
  forall p p' f v1 v2 v3,
    Val.expr_inject p f v1 v2 ->
    Val.lessdef p' v2 v3 ->
    Val.expr_inject (Val.inj_m f p p') f v1 v3.
Proof.
  intros. unfold Val.lessdef in H0.
  eapply Val.expr_inject_ext with (f:= compose_meminj f inject_id).
  intros; eauto.
  unfold compose_meminj. simpl. repeat destr.
  f_equal. f_equal. omega.
  eapply Val.expr_inject_compose; eauto.
Qed.

Injecting a memory into itself.

Definition flat_inj (thr: block) : meminj :=
  fun (b: block) => if plt b thr then Some(b, 0) else None.

Definition inject_neutral (thr: block) (m: mem) :=
  mem_inj id_pua (flat_inj thr) m m.

Remark flat_inj_no_overlap:
  forall thr m, meminj_no_overlap (flat_inj thr) m.
Proof.
  unfold flat_inj; intros; red; intros.
  destruct (plt b1 thr); inversion H0; subst.
  destruct (plt b2 thr); inversion H1; subst.
  auto.
Qed.

Theorem neutral_inject:
  forall m, inject_neutral (nextblock m) m ->
            inject id_pua (flat_inj (nextblock m)) m m.
Proof.
  intros. constructor.
  -
    auto.
  -
    unfold flat_inj, valid_block; intros.
    apply pred_dec_false. auto.
  -
    unfold flat_inj, valid_block; intros.
    destruct (plt b (nextblock m)); inversion H0; subst. auto.
  - intros.
    unfold id_pua; auto.
  -
    red; intros.
    unfold flat_inj in *.
    des (plt b1 (nextblock m));
      des (plt b2 (nextblock m)).
  -
    unfold flat_inj; intros.
    destruct (plt b (nextblock m)); inv H0. omega.
Qed.

Theorem empty_inject_neutral:
  forall thr, inject_neutral thr empty.
Proof.
  intros thr; red; constructor; eauto.
-
  unfold flat_inj; intros. destruct (plt b1 thr); inv H.
  replace (ofs + 0) with ofs by omega; auto.

-
  unfold flat_inj; intros. destruct (plt b1 thr); inv H.
  replace (ofs + 0) with ofs by omega; auto.

-
  unfold flat_inj; intros. destruct (plt b thr); inv H.
  split; try omega.
  unfold mask; simpl.
  rewrite PMap.gi. simpl. apply Z.divide_1_l.
-
  intros.
  apply perm_empty in H0. exfalso; auto.
- omega.
Qed.

Theorem alloc_inject_neutral:
  forall thr m lo hi b m'
         (ALLOC: alloc m lo hi = Some (m', b))
         (LO: lo <= 0)
         (INJ: inject_neutral thr m)
         (LT: Plt (nextblock m) thr),
    inject_neutral thr m'.
Proof.
  intros; inv INJ.
  constructor; simpl; intros; eauto.
  - unfold flat_inj in H.
    des (plt b1 thr). inv H.
    rewrite Z.add_0_r; auto.
  - unfold flat_inj in H.
    des (plt b1 thr). inv H.
    rewrite Z.add_0_r; auto.
  - unfold flat_inj in H.
    des (plt b0 thr). inv H. omega.
    inv H.
    apply Z.divide_0_r.
  - unfold flat_inj in H.
    des (plt b1 thr). inv H.
    rewrite Z.add_0_r; auto.
    destruct (peq b2 b); subst; auto.
    + apply perm_bounds in H0.
      rewrite (bounds_of_block_alloc _ _ _ _ _ ALLOC) in H0. unfold in_bound in H0; simpl in H0.
      apply mem_alloc_block_undefs in ALLOC.
      red in ALLOC.
      rewrite <- (Z.add_0_r ofs).
      rewrite ALLOC; auto.
      constructor.
      constructor. simpl. unfold Val.inj_undef; simpl.
      unfold flat_inj. rewrite Heqs.
      rewrite Int.add_zero; auto.
    + apply (perm_alloc_4 _ _ _ _ _ ALLOC) in H0; auto.
      rewrite (alloc_contents_old _ _ _ _ _ ALLOC); auto.
      rewrite <- (Z.add_0_r ofs) at 2.
      apply mi_memval0; auto.
      unfold flat_inj; destr.
  - omega.
Qed.

Theorem store_inject_neutral:
  forall chunk m b ofs v m' thr,
  store chunk m b ofs v = Some m' ->
  inject_neutral thr m ->
  Plt b thr ->
  Val.expr_inject id_pua (flat_inj thr) v v ->
  inject_neutral thr m'.
Proof.
  intros; red.
  exploit store_mapped_inj. eauto. eauto. apply flat_inj_no_overlap.
  unfold flat_inj. apply pred_dec_true; auto. eauto.
  replace (ofs + 0) with ofs by omega.
  intros [m'' [A B]]. congruence.
Qed.

Theorem drop_inject_neutral:
  forall m b lo hi p m' thr,
  drop_perm m b lo hi p = Some m' ->
  inject_neutral thr m ->
  Plt b thr ->
  inject_neutral thr m'.
Proof.
  unfold inject_neutral; intros.
  exploit drop_mapped_inj; eauto. apply flat_inj_no_overlap.
  unfold flat_inj. apply pred_dec_true; eauto.
  repeat rewrite Zplus_0_r. intros [m'' [A B]]. congruence.
Qed.

Invariance properties between two memory states


Section UNCHANGED_ON.

Variable P: block -> Z -> Prop.

Record unchanged_on (m_before m_after: mem) : Prop := mk_unchanged_on {
  unchanged_on_perm:
    forall b ofs k p,
    P b ofs -> valid_block m_before b ->
    (perm m_before b ofs k p <-> perm m_after b ofs k p);
  unchanged_on_contents:
    forall b ofs,
    P b ofs -> perm m_before b ofs Cur Readable ->
    ZMap.get ofs (PMap.get b m_after.(mem_contents)) =
    ZMap.get ofs (PMap.get b m_before.(mem_contents))
}.

Lemma unchanged_on_refl:
  forall m, unchanged_on m m.
Proof.
  intros; constructor; tauto.
Qed.

Lemma perm_unchanged_on:
  forall m m' b ofs k p,
  unchanged_on m m' -> P b ofs -> valid_block m b ->
  perm m b ofs k p -> perm m' b ofs k p.
Proof.
  intros. destruct H. apply unchanged_on_perm0; auto.
Qed.

Lemma perm_unchanged_on_2:
  forall m m' b ofs k p,
  unchanged_on m m' -> P b ofs -> valid_block m b ->
  perm m' b ofs k p -> perm m b ofs k p.
Proof.
  intros. destruct H. apply unchanged_on_perm0; auto.
Qed.

Lemma loadbytes_unchanged_on_1:
  forall m m' b ofs n,
  unchanged_on m m' ->
  valid_block m b ->
  (forall i, ofs <= i < ofs + n -> P b i) ->
  loadbytes m' b ofs n = loadbytes m b ofs n.
Proof.
  intros.
  destruct (zle n 0).
+ erewrite ! loadbytes_empty by assumption. auto.
+ unfold loadbytes. destruct H.
  destruct (range_perm_dec m b ofs (ofs + n) Cur Readable).
  rewrite pred_dec_true. f_equal.
  apply getN_exten. intros. rewrite nat_of_Z_eq in H by omega.
  apply unchanged_on_contents0; auto.
  red; intros. apply unchanged_on_perm0; auto.
  rewrite pred_dec_false. auto.
  red; intros; elim n0; red; intros. apply <- unchanged_on_perm0; auto.
Qed.

Lemma loadbytes_unchanged_on:
  forall m m' b ofs n bytes,
  unchanged_on m m' ->
  (forall i, ofs <= i < ofs + n -> P b i) ->
  loadbytes m b ofs n = Some bytes ->
  loadbytes m' b ofs n = Some bytes.
Proof.
  intros.
  destruct (zle n 0).
+ erewrite loadbytes_empty in * by assumption. auto.
+ rewrite <- H1. apply loadbytes_unchanged_on_1; auto.
  exploit loadbytes_range_perm; eauto. instantiate (1 := ofs). omega.
  intros. eauto with mem.
Qed.

Lemma load_unchanged_on_1:
  forall m m' chunk b ofs,
  unchanged_on m m' ->
  valid_block m b ->
  (forall i, ofs <= i < ofs + size_chunk chunk -> P b i) ->
  load chunk m' b ofs = load chunk m b ofs.
Proof.
  intros. unfold load. destruct (valid_access_dec m chunk b ofs Readable).
  destruct v. rewrite pred_dec_true. f_equal. f_equal. apply getN_exten. intros.
  rewrite <- size_chunk_conv in H4. eapply unchanged_on_contents; eauto.
  split; auto. red; intros. eapply perm_unchanged_on; eauto.
  rewrite pred_dec_false. auto.
  red; intros [A B]; elim n; split; auto. red; intros; eapply perm_unchanged_on_2; eauto.
Qed.

Lemma load_unchanged_on:
  forall m m' chunk b ofs v,
  unchanged_on m m' ->
  (forall i, ofs <= i < ofs + size_chunk chunk -> P b i) ->
  load chunk m b ofs = Some v ->
  load chunk m' b ofs = Some v.
Proof.
  intros. rewrite <- H1. eapply load_unchanged_on_1; eauto with mem.
Qed.

Lemma store_unchanged_on:
  forall chunk m b ofs v m',
  store chunk m b ofs v = Some m' ->
  (forall i, ofs <= i < ofs + size_chunk chunk -> ~ P b i) ->
  unchanged_on m m'.
Proof.
  intros; constructor; intros.
- split; intros; eauto with mem.
- erewrite store_mem_contents; eauto. rewrite PMap.gsspec.
  destruct (peq b0 b); auto. subst b0. apply setN_outside.
  erewrite encode_val_tot_length; eauto. rewrite <- size_chunk_conv.
  destruct (zlt ofs0 ofs); auto.
  destruct (zlt ofs0 (ofs + size_chunk chunk)); auto.
  elim (H0 ofs0). omega. auto.
Qed.

Lemma storebytes_unchanged_on:
  forall m b ofs bytes m',
  storebytes m b ofs bytes = Some m' ->
  (forall i, ofs <= i < ofs + Z_of_nat (length bytes) -> ~ P b i) ->
  unchanged_on m m'.
Proof.
  intros; constructor; intros.
- split; intros. eapply perm_storebytes_1; eauto. eapply perm_storebytes_2; eauto.
- erewrite storebytes_mem_contents; eauto. rewrite PMap.gsspec.
  destruct (peq b0 b); auto. subst b0. apply setN_outside.
  destruct (zlt ofs0 ofs); auto.
  destruct (zlt ofs0 (ofs + Z_of_nat (length bytes))); auto.
  elim (H0 ofs0). omega. auto.
Qed.

Lemma alloc_unchanged_on:
  forall m lo hi m' b,
  alloc m lo hi = Some (m', b) ->
  unchanged_on m m'.
Proof.
  intros; constructor; intros.
- split; intros.
  eapply perm_alloc_1; eauto.
  eapply perm_alloc_4; eauto.
  eapply valid_not_valid_diff; eauto with mem.
- unfold alloc, low_alloc in H.
  destruct (conc_mem_alloc_dec m 0 hi _); try discriminate.
  injection H; intros A B. rewrite <- B; simpl.
  rewrite PMap.gso; auto. rewrite A. eapply valid_not_valid_diff; eauto with mem.
  subst. unfold valid_block; xomega.
Qed.

Lemma free_unchanged_on:
  forall m b lo hi m',
  free m b lo hi = Some m' ->
  (forall i, lo <= i < hi -> ~ P b i) ->
  unchanged_on m m'.
Proof.
  intros; constructor; intros.
- split; intros.
  eapply perm_free_1; eauto.
  destruct (eq_block b0 b); auto. destruct (zlt ofs lo); auto. destruct (zle hi ofs); auto.
  subst b0. elim (H0 ofs). omega. auto.
  eapply perm_free_3; eauto.
- unfold free in H. destruct (range_perm_dec m b lo hi Cur Freeable); inv H.
  simpl. auto.
Qed.

End UNCHANGED_ON.



End Mem.

Notation mem := Mem.mem.



Global Opaque Mem.alloc Mem.free Mem.store Mem.load Mem.storebytes Mem.loadbytes.

Hint Resolve
  Mem.valid_not_valid_diff
  Mem.perm_implies
  Mem.perm_cur
  Mem.perm_max
  Mem.perm_valid_block
  Mem.range_perm_implies
  Mem.range_perm_cur
  Mem.range_perm_max
  Mem.valid_access_implies
  Mem.valid_access_valid_block
  Mem.valid_access_perm
  Mem.valid_access_load
  Mem.load_valid_access
  Mem.loadbytes_range_perm
  Mem.valid_access_store
  Mem.perm_store_1
  Mem.perm_store_2
  Mem.nextblock_store
  Mem.store_valid_block_1
  Mem.store_valid_block_2
  Mem.store_valid_access_1
  Mem.store_valid_access_2
  Mem.store_valid_access_3
  Mem.storebytes_range_perm
  Mem.perm_storebytes_1
  Mem.perm_storebytes_2
  Mem.storebytes_valid_access_1
  Mem.storebytes_valid_access_2
  Mem.nextblock_storebytes
  Mem.storebytes_valid_block_1
  Mem.storebytes_valid_block_2
  Mem.nextblock_alloc
  Mem.alloc_result
  Mem.valid_block_alloc
  Mem.fresh_block_alloc
  Mem.valid_new_block
  Mem.perm_alloc_1
  Mem.perm_alloc_2
  Mem.perm_alloc_3
  Mem.perm_alloc_4
  Mem.perm_alloc_inv
  Mem.valid_access_alloc_other
  Mem.valid_access_alloc_same
  Mem.valid_access_alloc_inv
  Mem.range_perm_free
  Mem.free_range_perm
  Mem.nextblock_free
  Mem.valid_block_free_1
  Mem.valid_block_free_2
  Mem.perm_free_1
  Mem.perm_free_2
  Mem.perm_free_3
  Mem.valid_access_free_1
  Mem.valid_access_free_2
  Mem.valid_access_free_inv_1
  Mem.valid_access_free_inv_2
  Mem.unchanged_on_refl
: mem.