Module BTL_SEsimplifyMem


Require Import Coqlib AST Values Integers Memory Maps.
Require Import RelationClasses.
Require Import OptionMonad.
Require Import Op.
Require Import BTL BTL_SEtheory BTL_SEsimuref.
Require Import ValueDomain.
Require Import Impure.ImpHCons.

Import SvalNotations.
Import ListNotations.
Local Open Scope option_monad_scope.
Local Open Scope lazy_bool_scope.


Section IsIndexedAddr.

  Definition is_indexed_addr (addr : addressing) (ofs : ptrofs) : Prop :=
    forall F V (genv: Globalenvs.Genv.t F V) (sp: val) (args: list val)
(rb : Values.block) (rofs : ptrofs),
    eval_addressing genv sp addr args = Some (Vptr rb rofs) <->
    exists bofs, args = Vptr rb bofs :: nil /\ rofs = Ptrofs.add bofs ofs.

  Local Obligation Tactic := simpl; intros.
  Local Transparent Archi.ptr64.
  Program Definition test_is_indexed (addr : addressing):
    {ofs : ptrofs | is_indexed_addr addr ofs} + {True} :=
    match addr with
    | Aindexed iofs => inleft (exist _ _ _)
    | _ => inright _
    end.
  Next Obligation.
    (* Depending on the architecture, we may need to convert [iofs].
       An alternative to this test would be to define [test_is_indexed] for each architecture. *)

    solve [exact iofs
          |exact (Ptrofs.of_int iofs)
          |exact (Ptrofs.of_int64 iofs)
          |exact (if Archi.ptr64
                  then Ptrofs.of_int64 (Int64.repr iofs)
                  else Ptrofs.of_int (Int.repr iofs))].
  Defined.
  Local Obligation Tactic := program_simpl.
  Solve All Obligations.
  Next Obligation.
    unfold test_is_indexed_obligation_1, is_indexed_addr, eval_addressing;
      try unfold Archi.ptr64;
      try unfold eval_addressing32, eval_addressing64;
      unfold Val.offset_ptr, Val.add, Val.addl;
      intros.
    split.
    2:intros [bofs [-> ->]]; reflexivity.
    repeat autodestruct; repeat first [injection 1 as <- <- | intro]; eauto.
  Qed.
  Local Opaque Archi.ptr64.
  Global Opaque test_is_indexed.

  Lemma is_indexed_addr_case [addr ofs] (ADDR : is_indexed_addr addr ofs)
    [F V] (genv: Globalenvs.Genv.t F V) sp args
    :
    (exists b bofs,
      eval_addressing genv sp addr args = Some (Vptr b (Ptrofs.add bofs ofs)) /\
      args = Vptr b bofs :: nil) \/
    (match eval_addressing genv sp addr args with
     | Some (Vptr b rofs) => False
     | _ => True
     end).
  Proof.
    specialize (ADDR F V genv sp args). revert ADDR.
    destruct (eval_addressing genv sp addr args) as [[]|]; simpl; eauto.
    intro EQV.
    destruct (EQV b i) as [[bofs EQ] _]. reflexivity.
    case EQ as [-> ->]; eauto.
  Qed.

  Lemma is_indexed_addr_eval [addr ofs] (ADDR : is_indexed_addr addr ofs)
    [F V] (genv: Globalenvs.Genv.t F V) sp b bofs:
    eval_addressing genv sp addr (Vptr b bofs :: nil) = Some (Vptr b (Ptrofs.add bofs ofs)).
  Proof.
    eapply proj2 in ADDR. rewrite ADDR; eauto.
  Qed.

End IsIndexedAddr.


Section LemmasMemory.

  Lemma store_success_iff chk m b ofs v:
    Mem.store chk m b ofs v <> None <-> Mem.valid_access m chk b ofs Writable.
  Proof.
    split; intro H.
    - case Mem.store as [|] eqn:STORE. 2:congruence.
      eapply Mem.store_valid_access_3. eauto.
    - eapply Mem.valid_access_store in H as [? ->].
      congruence.
  Qed.

  Lemma store_fail_iff chk m b ofs v:
    Mem.store chk m b ofs v = None <-> ~Mem.valid_access m chk b ofs Writable.
  Proof.
    rewrite <- (not_iff_compat (store_success_iff chk m b ofs v)).
    destruct Mem.store; split; intro H1; try congruence.
    exfalso. apply H1. intro. congruence.
  Qed.
  
  Lemma load_success_iff chk m b ofs:
    Mem.load chk m b ofs <> None <-> Mem.valid_access m chk b ofs Readable.
  Proof.
    split; intro H.
    - case Mem.load as [|] eqn:LOAD. 2:congruence.
      eapply Mem.load_valid_access. eauto.
    - eapply Mem.valid_access_load in H as [? ->].
      congruence.
  Qed.

  Lemma perm_store_iff [chunk m1 b ofs v m2]:
    Mem.store chunk m1 b ofs v = Some m2 ->
    forall (b' : Values.block) (ofs' : Z) (k : perm_kind) (p : permission),
    Mem.perm m1 b' ofs' k p <-> Mem.perm m2 b' ofs' k p.
  Proof.
    split; [eapply Mem.perm_store_1|eapply Mem.perm_store_2]; eassumption.
  Qed.

  Lemma store_valid_access_iff [chunk m1 b ofs v m2]:
    Mem.store chunk m1 b ofs v = Some m2 ->
    forall (chunk' : memory_chunk) (b' : Values.block) (ofs' : Z) (p : permission),
    Mem.valid_access m1 chunk' b' ofs' p <-> Mem.valid_access m2 chunk' b' ofs' p.
  Proof.
    split; [eapply Mem.store_valid_access_1|eapply Mem.store_valid_access_2]; eassumption.
  Qed.

  Section Equiv_on.

    Definition memct_t := PMap.t (ZMap.t memval).

    Definition memct_get (b : Values.block) (ofs : Z) (m : memct_t) : memval :=
      ZMap.get ofs (PMap.get b m).

    Definition is_Some [A] (x : option A) : bool :=
      match x with
      | Some _ => true
      | None => false
      end.

    Definition memct_get_str1 (b : Values.block) (m : memct_t) : bool :=
      is_Some (PTree.get b (snd m)).

    Definition memct_get_str2 (b : Values.block) (ofs: Z) (m : memct_t) : bool :=
       is_Some (PTree.get (ZIndexed.index ofs) (snd (PMap.get b m))).

    Definition memct_set (b : Values.block) (ofs : Z) (v : memval) (m : memct_t) : memct_t :=
      PMap.set b (ZMap.set ofs v (PMap.get b m)) m.


    Definition eq_on_frame : Type := (Values.block -> Prop) * (Values.block -> Z -> Prop).
    Variable P : eq_on_frame.
  
    Record memct_equiv_on (m0 m1 : memct_t) : Prop :=
      mk_memct_equiv_on {
        EQ_MCT1 : forall b, fst P b -> memct_get_str1 b m0 = memct_get_str1 b m1;
        EQ_MCT2 : forall b ofs, snd P b ofs -> memct_get b ofs m0 = memct_get b ofs m1 /\
                                               memct_get_str2 b ofs m0 = memct_get_str2 b ofs m1;
      }.

    Record mem_equiv_on (m0 m1 : mem) : Prop :=
      mk_mem_equiv_on {
        EQ_ACCESS : Mem.mem_access m0 = Mem.mem_access m1;
        EQ_NEXTBLOCK : Mem.nextblock m0 = Mem.nextblock m1;
        EQ_CTDEF : fst (Mem.mem_contents m0) = fst (Mem.mem_contents m1);
        EQ_CTNT : memct_equiv_on (Mem.mem_contents m0) (Mem.mem_contents m1);
    }.

    Lemma mem_equiv_on_perm [m0 m1] (E: mem_equiv_on m0 m1) b ofs k p:
      Mem.perm m0 b ofs k p <-> Mem.perm m1 b ofs k p.
    Proof.
      unfold Mem.perm; apply EQ_ACCESS in E as ->; reflexivity.
    Qed.

    Lemma mem_equiv_on_valid_access [m0 m1] (E : mem_equiv_on m0 m1) chk b ofs p:
      Mem.valid_access m0 chk b ofs p <-> Mem.valid_access m1 chk b ofs p.
    Proof.
      unfold Mem.valid_access, Mem.range_perm.
      setoid_rewrite (mem_equiv_on_perm E).
      reflexivity.
    Qed.

    Lemma mem_equiv_on_get [m0 m1] (E : mem_equiv_on m0 m1) b ofs
      (H : snd P b ofs):
      ZMap.get ofs (PMap.get b (Mem.mem_contents m0)) =
      ZMap.get ofs (PMap.get b (Mem.mem_contents m1)).
    Proof.
      apply E in H as (H & _). apply H.
    Qed.

    Lemma zindex_surjective n:
      exists i, ZIndexed.index i = n.
    Proof.
      destruct n; unshelve eexists; econstructor.
    Qed.

    Lemma mem_equiv_on_all m0 m1
      (ALL1 : forall b, fst P b)
      (ALL2 : forall b i,snd P b i)
      (EQ : mem_equiv_on m0 m1):
      m0 = m1.
    Proof.
      apply Mem.mem_same_proof_irr; try apply EQ.
      case EQ as [_ _ DEF1 [CT1 CT2]].
      pose proof (CT1 := fun b => CT1 b (ALL1 b)).
      pose proof (CT2 := fun b ofs => CT2 b ofs (ALL2 b ofs)).
      pose proof (DEF2 := fun b => conj (Mem.contents_default m0 b) (Mem.contents_default m1 b)).
      case Mem.mem_contents as (d0, t0), Mem.mem_contents as (d1, t1).
      simpl in DEF1; rewrite DEF1; f_equal.
      apply PTree.extensionality; intro b.
      specialize (DEF2 b). specialize (CT1 b). specialize (CT2 b).
      unfold memct_get, memct_get_str1, memct_get_str2, ZMap.get, PMap.get in DEF2, CT1, CT2;
      simpl in DEF2, CT1, CT2.
      destruct (t0 ! b) as [mb0|], (t1 ! b) as [mb1|]; simpl in CT1; try congruence.
      f_equal.
      destruct mb0 as (db0, tb0), mb1 as (db1, tb1).
      simpl in DEF2; case DEF2 as (-> & ->); f_equal.
      apply PTree.extensionality; intro i.
      case (zindex_surjective i) as (ofs & <-).
      case (CT2 ofs); unfold is_Some; simpl.
      repeat autodestruct.
    Qed.

    Lemma load_equiv_on m0 m1 chk b ofs:
      mem_equiv_on m0 m1 ->
      (forall i, ofs <= i < ofs + size_chunk chk -> snd P b i) ->
      Mem.load chk m0 b ofs = Mem.load chk m1 b ofs.
    Proof.
      intros EQ HP.
      specialize (load_success_iff chk m0 b ofs).
        rewrite (mem_equiv_on_valid_access EQ).
        rewrite <- load_success_iff.
      case (Mem.load chk m0 b ofs) as [v0|] eqn:L0, (Mem.load chk m1 b ofs) as [v1|] eqn:L1; cycle 1;
        try solve [reflexivity | intros [F0 F1]; exfalso; (apply F0 + apply F1); congruence].
      intros _; apply Mem.load_result in L0 as ->, L1 as ->; do 2 f_equal.
      apply Mem.getN_exten; rewrite <- size_chunk_conv.
      intros; apply mem_equiv_on_get; auto.
    Qed.
  End Equiv_on.

  Local Instance memct_equiv_on_Equivalence {P}:
    Equivalence (memct_equiv_on P).
  Proof.
    constructor.
    - repeat constructor.
    - repeat constructor; symmetry; apply H; auto.
    - repeat constructor; (etransitivity; [apply H|apply H0]); auto.
  Qed.

  Global Instance mem_equiv_on_Equivalence {P}:
    Equivalence (mem_equiv_on P).
  Proof.
    constructor.
    - constructor; reflexivity.
    - constructor; symmetry; apply H.
    - constructor; (etransitivity; [apply H|apply H0]).
  Qed.

  Lemma memct_equiv_on_weaken (P0 P1 : eq_on_frame) m0 m1
    (E : memct_equiv_on P0 m0 m1)
    (HP0 : forall b, fst P1 b -> fst P0 b)
    (HP1 : forall b p, snd P1 b p -> snd P0 b p):
    memct_equiv_on P1 m0 m1.
  Proof.
    case E; split; auto.
  Qed.

  Lemma mem_equiv_on_weaken (P0 P1 : eq_on_frame) m0 m1
    (E : mem_equiv_on P0 m0 m1)
    (HP0 : forall b, fst P1 b -> fst P0 b)
    (HP1 : forall b p, snd P1 b p -> snd P0 b p):
    mem_equiv_on P1 m0 m1.
  Proof.
    case E; split; eauto using memct_equiv_on_weaken.
  Qed.


  Lemma memct_gsspec b ofs v m0 b' ofs':
    memct_get b' ofs' (memct_set b ofs v m0) =
    if peq b' b && zeq ofs' ofs then v else memct_get b' ofs' m0.
  Proof.
    unfold memct_get, memct_set.
    rewrite PMap.gsspec; case peq as [->|]. 2:reflexivity.
    rewrite ZMap.gsspec; unfold ZIndexed.eq; case zeq as [->|]; reflexivity.
  Qed.

  Lemma memct_gs1spec b ofs v m0 b':
    memct_get_str1 b' (memct_set b ofs v m0) =
    peq b' b || memct_get_str1 b' m0.
  Proof.
    unfold memct_get_str1, memct_set; simpl.
    rewrite PTree.gsspec; autodestruct.
  Qed.
  
  Lemma memct_gs2spec b ofs v m0 b' ofs':
    memct_get_str2 b' ofs' (memct_set b ofs v m0) =
    (peq b' b && zeq ofs' ofs) || memct_get_str2 b' ofs' m0.
  Proof.
    unfold memct_get_str2, memct_set.
    rewrite PMap.gsspec; case peq as [->|]. 2:reflexivity.
    simpl; rewrite PTree.gsspec.
    destruct peq, zeq; simpl; auto; subst; exfalso; eauto using ZIndexed.index_inj.
  Qed.

  Lemma memct_set_eq1 b ofs v m0:
    memct_equiv_on (fun b' => b' <> b, fun b' ofs' => ~(b' = b /\ ofs' = ofs)) m0 (memct_set b ofs v m0).
  Proof.
    constructor.
    - intros b' NEQ.
      rewrite memct_gs1spec.
      unfold proj_sumbool; autodestruct.
    - intros b' ofs' NEQ; simpl in NEQ.
      rewrite memct_gsspec, memct_gs2spec.
      unfold proj_sumbool; do 2 (autodestruct; simpl; auto).
      tauto.
  Qed.

  Lemma memct_set_eq2 P b ofs v m0 m1
    (E : memct_equiv_on P m0 m1):
    memct_equiv_on
      (fun b' => fst P b' \/ b' = b, fun b' ofs' => snd P b' ofs' \/ (b' = b /\ ofs' = ofs))
      (memct_set b ofs v m0) (memct_set b ofs v m1).
  Proof.
    constructor.
    - intros b' H; simpl in H.
      rewrite !memct_gs1spec.
      unfold proj_sumbool; autodestruct; simpl; intros _.
      apply E; tauto.
    - intros b' ofs' H; simpl in H.
      rewrite !memct_gsspec, !memct_gs2spec.
      unfold proj_sumbool. do 2 try (autodestruct; simpl; intros _).
      auto.
      all:apply E; tauto.
  Qed.

  Fixpoint memct_setN (b : Values.block) (vl : list memval) (p : Z) (m : memct_t) : memct_t :=
    match vl with
    | nil => m
    | v :: vl => memct_setN b vl (p + 1) (memct_set b p v m)
    end.

  Lemma mem_setN_ctnt (m : memct_t) (mb : ZMap.t memval) (b : Values.block) (vl : list memval) (p : Z):
    PMap.set b (Mem.setN vl p mb) m = memct_setN b vl p (PMap.set b mb m).
  Proof.
    revert mb p; induction vl; simpl; intros.
    - reflexivity.
    - rewrite IHvl; f_equal.
      unfold memct_set.
      rewrite PMap.gss, PMap.set2.
      reflexivity.
  Qed.

  Lemma store_equiv_on1 chk m b p v m'
    (ST : Mem.store chk m b p v = Some m'):
    mem_equiv_on (fun b' => b' <> b, fun b' p' => ~(b' = b /\ p <= p' < p + size_chunk chk)) m m'.
  Proof.
    constructor.
    - apply Mem.store_access in ST; congruence.
    - apply Mem.nextblock_store in ST; congruence.
    - apply Mem.store_mem_contents in ST as ->; reflexivity.
    - apply Mem.store_mem_contents in ST as ->.
      rewrite mem_setN_ctnt; generalize (Mem.mem_contents m); intro c.
      set (c0 := PMap.set b c!!b c <: memct_t).
      transitivity c0. {
        unfold c0; split; simpl; intros.
        - unfold memct_get_str1; simpl.
          rewrite PTree.gso; auto.
        - unfold memct_get, memct_get_str2; simpl.
          rewrite PMap.gsspec; case peq as [->|]; auto.
      }
      clearbody c0; clear c.
      erewrite size_chunk_conv, <-(encode_val_length chk v).
      revert p c0; induction (encode_val chk v); intros.
      + reflexivity.
      + simpl length; rewrite Nat2Z.inj_succ; simpl.
        etransitivity; cycle 1.
        * eapply memct_equiv_on_weaken. apply IHl.
          all:simpl; lia.
        * eapply memct_equiv_on_weaken. apply memct_set_eq1.
          all:simpl; lia.
  Qed.
  
  Lemma store_equiv_on2 P chk m0 m1 b p v m0' m1'
    (E : mem_equiv_on P m0 m1)
    (ST0 : Mem.store chk m0 b p v = Some m0')
    (ST1 : Mem.store chk m1 b p v = Some m1'):
    mem_equiv_on
      (fun b' => fst P b' \/ b' = b,
       fun b' p' => snd P b' p' \/ (b' = b /\ p <= p' < p + size_chunk chk)) m0' m1'.
  Proof.
    destruct E; constructor.
    - apply Mem.store_access in ST0, ST1; congruence.
    - apply Mem.nextblock_store in ST0, ST1; congruence.
    - apply Mem.store_mem_contents in ST0 as ->, ST1 as ->; assumption.
    - apply Mem.store_mem_contents in ST0 as ->, ST1 as ->.
      rewrite !mem_setN_ctnt.
      set (c0 := PMap.set b _ _).
      set (c1 := PMap.set b _ _).
      set (P1 := (fun b' => fst P b' \/ b' = b, snd P)).
      assert (E : memct_equiv_on P1 c0 c1). {
        unfold P1, c0, c1; split; simpl; intros.
        - unfold memct_get_str1; simpl.
          rewrite !PTree.gsspec; case peq as [->|]; auto.
          apply EQ_CTNT0; tauto.
        - unfold memct_get, memct_get_str2; simpl.
          rewrite !PMap.gsspec; case peq as [->|];
          apply EQ_CTNT0; auto.
      }
      change (fun b' => fst P b' \/ b' = b) with (fst P1).
      change (snd P) with (snd P1).
      clearbody c0 c1 P1; revert E; clear.
      erewrite size_chunk_conv, <-(encode_val_length chk v).
      revert P1 p c0 c1; induction (encode_val chk v); intros.
      + eapply memct_equiv_on_weaken. apply E.
        all:simpl; intuition.
      + simpl length; rewrite Nat2Z.inj_succ; simpl.
        eapply memct_equiv_on_weaken. {
          eapply IHl, memct_set_eq2, E.
        }
        all:simpl; intuition lia.
  Qed.
  
  Lemma store_morph_equiv_on P chk m0 m1 b ofs v m0' m1'
    (E : mem_equiv_on P m0 m1)
    (ST0 : Mem.store chk m0 b ofs v = Some m0')
    (ST1 : Mem.store chk m1 b ofs v = Some m1'):
    mem_equiv_on P m0' m1'.
  Proof.
    eapply mem_equiv_on_weaken. eapply store_equiv_on2; eauto.
    all:simpl; tauto.
  Qed.

End LemmasMemory.

Record address := mk_addr {
  a_chunk: memory_chunk;
  a_addr: addressing;
  a_args: list_sval;
  a_aval: option aval
}.

Definition eval_address (ctx : iblock_common_context) (a : address): option (Values.block * ptrofs) :=
  SOME args <- eval_list_sval ctx (a_args a) IN
  SOME v <- eval_addressing (cge ctx) (csp ctx) (a_addr a) args IN
  ASSERT (if_Some_dec (a_aval a) (vmatch_dec (cbc ctx) v)) IN
  match v with
  | Vptr b o => Some (b, o)
  | _ => None
  end.

Section DisjointnessCriteria.


  Definition vaddr_disjoint (v0 : Values.block * ptrofs) (sz0 : Z)
                            (v1 : Values.block * ptrofs) (sz1 : Z): Prop :=
    let (b0, ofs0) := v0 in
    let (b1, ofs1) := v1 in
    b0 <> b1 \/
    Ptrofs.unsigned ofs0 + sz0 <= Ptrofs.unsigned ofs1 \/
    Ptrofs.unsigned ofs1 + sz1 <= Ptrofs.unsigned ofs0.

  Definition addr_disjoint (ctx : iblock_common_context) (a0 a1 : address) :=
    if_Some (eval_address ctx a0) (fun v0 =>
    if_Some (eval_address ctx a1) (fun v1 =>
    vaddr_disjoint v0 (size_chunk (a_chunk a0)) v1 (size_chunk (a_chunk a1)))).



  Let u := Ptrofs.unsigned.

  Definition offset_chunk_lt (ofs0 : ptrofs) (chk0 : memory_chunk)
                             (ofs1 : ptrofs) (chk1 : memory_chunk)
    := (u ofs0 + size_chunk chk0) <= u ofs1 /\
       (u ofs1 + size_chunk chk1) <= u ofs0 + Ptrofs.modulus.

  Definition offset_disjoint (ofs0 : ptrofs) (chk0 : memory_chunk)
                                   (ofs1 : ptrofs) (chk1 : memory_chunk)
    := offset_chunk_lt ofs0 chk0 ofs1 chk1 \/ offset_chunk_lt ofs1 chk1 ofs0 chk0.

  Lemma disjoint_interval_modulo (m : Z) (a b : Z) (al bl : Z) (c : Z):
    0 < m ->
    0 <= al -> 0 <= bl ->
    a + al <= b ->
    b + bl <= a + m ->
    (c + a) mod m + al <= (c + b) mod m \/ (c + b) mod m + bl <= (c + a) mod m.
  Proof.
    intros Hmpos Le0 Le1 Le2 Le3.
    rewrite !Z.mod_eq by lia.
    case (Z_le_lt_eq_dec ((c + a) / m) ((c + b) / m)) as [Wrap|NoWrap].
      { apply Z.div_le_mono; lia. }
    - right.
      assert (Le: (c + a) / m + 1 <= (c + b) / m) by lia.
      apply Zmult_le_compat_l with (p:=m) in Le; lia.
    - left. lia.
  Qed.

  Lemma offset_disjoint_computed_addr (bo : Z) ofs0 chk0 ofs1 chk1:
    offset_disjoint ofs0 chk0 ofs1 chk1 ->
    (bo + u ofs0) mod Ptrofs.modulus + size_chunk chk0 <= (bo + u ofs1) mod Ptrofs.modulus \/
    (bo + u ofs1) mod Ptrofs.modulus + size_chunk chk1 <= (bo + u ofs0) mod Ptrofs.modulus.
  Proof.
    specialize Ptrofs.modulus_pos as ?.
    specialize (size_chunk_pos chk0) as ?. specialize (size_chunk_pos chk1) as ?.
    intros [Lt|Lt]; [|apply or_comm];
      apply disjoint_interval_modulo; solve [apply Lt|lia].
  Qed.

  Lemma offset_disjoint_sound ctx a0 ofs0 a1 ofs1
    (A0: is_indexed_addr (a_addr a0) ofs0)
    (A1: is_indexed_addr (a_addr a1) ofs1)
    (B: list_sval_equiv ctx (a_args a0) (a_args a1))
    (D: offset_disjoint ofs0 (a_chunk a0) ofs1 (a_chunk a1)):
    addr_disjoint ctx a0 a1.
  Proof.
    unfold is_indexed_addr in A0, A1.
    unfold addr_disjoint, eval_address; rewrite <- B.
    repeat (autodestruct; simpl; try trivial); intros; right.
    eapply proj1 in A0, A1.
    exploit A0. eassumption.
    exploit A1. eassumption.
    intros (cofs1 & B1 & ->) (cofs0 & B0 & ->).
    replace cofs1 with cofs0 by congruence.
    unfold Ptrofs.add. rewrite !Ptrofs.unsigned_repr_eq. fold u.
    apply offset_disjoint_computed_addr; assumption.
  Qed.


  Lemma abs_disjoint_sound ctx a0 a1 aa0 aa1
    (AA0 : a_aval a0 = Some aa0)
    (AA1 : a_aval a1 = Some aa1)
    (D: vdisjoint aa0 (size_chunk (a_chunk a0))
                  aa1 (size_chunk (a_chunk a1)) = true):
    addr_disjoint ctx a0 a1.
  Proof.
    unfold addr_disjoint, eval_address; rewrite AA0, AA1.
    repeat (autodestruct; simpl; trivial; intro).
    eapply vdisjoint_sound; eauto.
  Qed.

End DisjointnessCriteria.

Section RewritingRules.

Definition aSload sm trap chk addr args aaddr :=
  fSmayUndef_opt
    (SOME aaddr <- aaddr IN Some (fOKaddrMatch addr args aaddr))
    (fSload sm trap chk addr args).

Definition eval_aSload_def_0 trap (v : option val) : option val :=
  match v with
  | Some v => Some v
  | None =>
      match trap with
      | TRAP => None
      | NOTRAP => Some Vundef
      end
  end.

Definition eval_aSload_def_1 ctx trap chk addr args m :=
  SOME args <- eval_list_sval ctx args IN
  match trap with
  | TRAP =>
      SOME v <- eval_addressing (cge ctx) (csp ctx) addr args IN
      match v with
      | Vptr b ofs =>
          match Mem.load chk m b (Ptrofs.unsigned ofs) with
          | Some v => Some Vundef
          | None => None
          end
      | _ => None
      end
  | NOTRAP => Some Vundef
  end.

Lemma eval_aSload_def_1_unchanged ctx trap chk addr args m0 m1
  (EQ_PERM : forall b ofs k p, Mem.perm m0 b ofs k p <-> Mem.perm m1 b ofs k p):
  eval_aSload_def_1 ctx trap chk addr args m0 = eval_aSload_def_1 ctx trap chk addr args m1.
Proof.
  unfold eval_aSload_def_1; repeat autodestruct; [intros LKO LOK|intros LOK LKO]; exfalso;
  (eapply load_success_iff; [|eapply LKO]);
  apply Mem.load_valid_access in LOK; revert LOK;
  unfold Mem.valid_access, Mem.range_perm; setoid_rewrite EQ_PERM; auto.
Qed.

Lemma eval_aSload_eq ctx sm m trap chk addr args aaddr
  (MEM : eval_smem ctx sm = Some m):
  eval_sval ctx (aSload sm trap chk addr args aaddr) =
  match eval_address ctx (mk_addr chk addr args aaddr) with
  | Some (b, ofs) => eval_aSload_def_0 trap (Mem.load chk m b (Ptrofs.unsigned ofs))
  | None => eval_aSload_def_1 ctx trap chk addr args m
  end.
Proof.
  unfold aSload, eval_address, eval_aSload_def_0, eval_aSload_def_1, if_Some_dec; simpl.
  destruct aaddr; simpl;
  unfold adp_addr_match_dec, adp_addr_match, if_Some_dec, Mem.loadv, proj_sumbool; simpl;
  rewrite MEM;
  repeat (autodestruct; simpl).
Qed.

Definition eval_aSstore_eq ctx sm chk addr args sinfo sv hc:
  eval_smem ctx (Sstore sm chk addr args sinfo sv hc) =
  SOME a <- eval_address ctx (mk_addr chk addr args (store_aaddr sinfo)) IN
  let (b, ofs) := a in
  SOME v <- eval_sval ctx sv IN
  SOME m <- eval_smem ctx sm IN
  Mem.store chk m b (Ptrofs.unsigned ofs) v.
Proof.
  simpl; unfold eval_address, Mem.storev, if_Some_dec.
  repeat (simpl; autodestruct).
Qed.

Lemma rewrite_load_store ctx (sm : smem) (trap : trapping_mode) (chk0 chk1 : memory_chunk)
  (addr0 addr1 : addressing) (args0 args1 : list_sval) (sv1 : sval) aaddr0 sinfo1 hc0
  (DJ : addr_disjoint ctx (mk_addr chk0 addr0 args0 aaddr0)
                          (mk_addr chk1 addr1 args1 (store_aaddr sinfo1))):
  let sm' := Sstore sm chk1 addr1 args1 sinfo1 sv1 hc0 in
  alive (eval_smem ctx sm') ->
  sval_equiv ctx
    (aSload sm' trap chk0 addr0 args0 aaddr0)
    (aSload sm trap chk0 addr0 args0 aaddr0).
Proof.
  intros sm' MEM'.
  apply elim_alive in MEM' as (m' & MEM').
  erewrite eval_aSload_eq by eassumption.
  subst sm'; revert MEM' DJ.
  rewrite eval_aSstore_eq; unfold addr_disjoint; simpl.
  do 4 (autodestruct; intro); simpl; intro STORE.
  erewrite eval_aSload_eq by eassumption.
  case eval_address as [[]|] in |-*; simpl.
  - intro; f_equal. eapply Mem.load_store_other; eauto.
  - intros. apply eval_aSload_def_1_unchanged.
    symmetry; eapply perm_store_iff; eauto.
Qed.

Lemma rewrite_store_store ctx (sm : smem) (chk0 chk1 : memory_chunk)
  addr0 addr1 (args0 args1 : list_sval) (sv0 sv1 : sval) sinfo0 sinfo1 hc0 hc1
  (DJ : addr_disjoint ctx (mk_addr chk0 addr0 args0 (store_aaddr sinfo0))
                          (mk_addr chk1 addr1 args1 (store_aaddr sinfo1))):
  smem_equiv ctx
    (Sstore (Sstore sm chk1 addr1 args1 sinfo1 sv1 hc1) chk0 addr0 args0 sinfo0 sv0 hc0)
    (fSstore (fSstore sm chk0 addr0 args0 sinfo0 sv0 ) chk1 addr1 args1 sinfo1 sv1).
Proof.
  unfold fSstore.
  rewrite !eval_aSstore_eq.
  unfold addr_disjoint in DJ.
  do 2 (case eval_address as [[]|]; [|solve [repeat autodestruct]]).
  simpl in DJ.
  repeat autodestruct.
  2,3:
    repeat (let H := fresh "H" in intro H; try apply store_fail_iff in H);
    try ((idtac + symmetry); apply store_fail_iff);
    eauto using Mem.store_valid_access_2.
  intros.
  eapply Mem.store_store_other; eauto.
Qed.


Inductive smem_equiv_on (P : eq_on_frame) (ctx : iblock_common_context) (sm0 sm1 : smem)
  : Prop
  := Smem_equiv_on m0 m1
      (EVAL_M0 : eval_smem ctx sm0 = Some m0)
      (EVAL_M1 : eval_smem ctx sm1 = Some m1)
      (EQ_ON : mem_equiv_on P m0 m1).

Lemma smem_equiv_on_refl P ctx sm:
  alive (eval_smem ctx sm) ->
  smem_equiv_on P ctx sm sm.
Proof.
  case eval_smem as [m|] eqn:M. 2:congruence.
  exists m m; auto. reflexivity.
Qed.

Global Instance smem_equiv_on_PER {P ctx}: PER (smem_equiv_on P ctx).
Proof.
  split.
  - intros ? ? []. econstructor; eauto. symmetry; assumption.
  - intros ? ? ? [m0 m1 E0 E1 EQ0] [m1' m2 E1' E2 EQ1].
    rewrite E1 in E1'. injection E1' as ?. subst m1'.
    exists m0 m2; eauto.
    etransitivity; eassumption.
Qed.

Lemma apply_store_smem_equiv_on P ctx sm0 sm1 chk addr sargs sinfo sv hc0 hc1:
  smem_equiv_on P ctx sm0 sm1 ->
  let sm0' := Sstore sm0 chk addr sargs sinfo sv hc0 in
  let sm1' := Sstore sm1 chk addr sargs sinfo sv hc1 in
  alive (eval_smem ctx sm0') ->
  smem_equiv_on P ctx sm0' sm1'.
Proof.
  intros [m0 m1 M0 M1 EM].
  cbn zeta; rewrite eval_aSstore_eq, M0.
  repeat autodestruct; intros EVAL _ EADDR.
  intros M0'. apply elim_alive in M0' as (m0' & M0').
  apply Mem.store_valid_access_3 in M0' as A0.
  apply (mem_equiv_on_valid_access _ EM) in A0.
  eapply Mem.valid_access_store in A0 as [m1' M1'].
  exists m0' m1'.
    1,2:rewrite eval_aSstore_eq, EADDR, EVAL, ?M0, ?M1; eassumption.
  eapply store_morph_equiv_on; eauto.
Qed.

Definition store_crange (ctx : iblock_common_context) (a : address) : eq_on_frame :=
  (fun b' => if_Some (eval_address ctx a) (fun av => let (b, _) := av in
                b' <> b),
   fun b' p' => if_Some (eval_address ctx a) (fun av => let (b, ofs) := av in
                let p := Ptrofs.unsigned ofs in
                ~(b' = b /\ p <= p' < p + size_chunk (a_chunk a)))).

Lemma rewrite_store_equiv_on ctx sm0 sm1 chk addr sargs sinfo sv hc0 hc1:
  let a := mk_addr chk addr sargs (store_aaddr sinfo) in
  let sm0' := Sstore sm0 chk addr sargs sinfo sv hc0 in
  let sm1' := Sstore sm1 chk addr sargs sinfo sv hc1 in
  smem_equiv_on (store_crange ctx a) ctx sm0 sm1 ->
  smem_equiv ctx sm0' sm1'.
Proof.
  cbn zeta; rewrite !eval_aSstore_eq.
  do 3 autodestruct; intros EVAL _ EADDR.
  intros [m0 m1 -> -> EM].
  do 2 destruct Mem.store eqn:? in |-*;
    (progress f_equal ||
    (exfalso;
     exploit Mem.store_valid_access_3; eauto;
     intro V; eapply (mem_equiv_on_valid_access _ EM) in V;
     eapply Mem.valid_access_store with (v := v) in V as [? ?]; congruence)).
  eapply mem_equiv_on_all; cycle -1.
  eapply store_equiv_on2; eauto.
  all:simpl; rewrite EADDR; simpl.
  all:intro b'; intros; case (peq b' b); tauto.
Qed.

Lemma remove_store_smem_equiv_on ctx sm0 chk addr sargs sinfo sv hc:
  let a := mk_addr chk addr sargs (store_aaddr sinfo) in
  let sm1 := Sstore sm0 chk addr sargs sinfo sv hc in
  forall (MALIVE: alive (eval_smem ctx sm1)),
  smem_equiv_on (store_crange ctx a) ctx sm0 sm1.
Proof.
  cbn zeta. rewrite eval_aSstore_eq.
  repeat autodestruct; intros EMEM EVAL _ EADDR.
  intros M1; apply elim_alive in M1 as (m1 & STORE).
  eexists _ m1; eauto.
  - rewrite eval_aSstore_eq, EADDR, EVAL, EMEM. exact STORE.
  - eapply mem_equiv_on_weaken. eapply store_equiv_on1, STORE.
    all:simpl; rewrite EADDR; simpl; auto.
Qed.

Definition load_range (ctx : iblock_common_context) (a : address) : eq_on_frame :=
  (fun _ => False,
   fun b i => exists ofs, eval_address ctx a = Some (b, ofs) /\
                          Ptrofs.unsigned ofs <= i < Ptrofs.unsigned ofs + size_chunk (a_chunk a)).

Lemma rewrite_load_equiv_on ctx sm0 sm1 trap chk addr sargs aaddr:
  let a := mk_addr chk addr sargs aaddr in
  smem_equiv_on (load_range ctx a) ctx sm0 sm1 ->
  sval_equiv ctx (aSload sm0 trap chk addr sargs aaddr)
                 (aSload sm1 trap chk addr sargs aaddr).
Proof.
  intros a [].
  erewrite !eval_aSload_eq by eassumption.
  case eval_address as [[]|] eqn:ADDR.
    2:{ apply eval_aSload_def_1_unchanged. eapply mem_equiv_on_perm, EQ_ON. }
  f_equal.
  eapply load_equiv_on. apply EQ_ON.
  intros; eexists; eauto.
Qed.

Lemma disjoint_store_smem_equiv_on ctx a sm0 chk addr sargs sinfo sv hc
  (DJ : addr_disjoint ctx a (mk_addr chk addr sargs (store_aaddr sinfo))):
  let range := load_range ctx a in
  let sm1 := Sstore sm0 chk addr sargs sinfo sv hc in
  forall (MALIVE: alive (eval_smem ctx sm1)),
  smem_equiv_on range ctx sm0 sm1.
Proof.
  cbn zeta; intro.
  apply remove_store_smem_equiv_on in MALIVE as [m0 m1 E0 E1 EQ].
  eexists m0 m1; auto.
  eapply mem_equiv_on_weaken. apply EQ.
  all:simpl; try tauto.
  setoid_rewrite if_Some_iff.
  intros b' i' (ofs & EADDR0 & RANGE0) (?, ?) EADDR1 [? RANGE1]; subst b'.
  unfold addr_disjoint in DJ; rewrite EADDR0, EADDR1 in DJ; simpl in DJ.
  case DJ as [|DJ]; [congruence|lia].
Qed.

End RewritingRules.


Section DisjointnessTest.


Program Definition test_offset_lt (ofs0 : ptrofs) (chk0 : memory_chunk)
                                  (ofs1 : ptrofs) (chk1 : memory_chunk)
  : {offset_chunk_lt ofs0 chk0 ofs1 chk1}+{True} :=
  if zle (Ptrofs.unsigned ofs0 + size_chunk chk0) (Ptrofs.unsigned ofs1)
  then if zle (Ptrofs.unsigned ofs1 + size_chunk chk1) (Ptrofs.unsigned ofs0 + Ptrofs.modulus)
  then left _ else right _ else right _.
Next Obligation.
unfold offset_chunk_lt. auto. Qed.

Program Definition test_offset_disjoint (ofs0 : ptrofs) (chk0 : memory_chunk)
                                        (ofs1 : ptrofs) (chk1 : memory_chunk)
  : {offset_disjoint ofs0 chk0 ofs1 chk1}+{True} :=
  if test_offset_lt ofs0 chk0 ofs1 chk1
  then left _
  else if test_offset_lt ofs1 chk1 ofs0 chk0
  then left _ else right _.
Solve All Obligations with (unfold offset_disjoint; auto).

Definition pure_fast_eq [A] (h : A -> hashcode) (x0 x1 : A) : {x0 = x1}+{True}.
Proof.
  case (PureComparisons.fast_eq h x0 x1) eqn:T.
  - left; eapply PureComparisons.fast_eq_correct, T.
  - right; constructor.
Qed.

Program Definition test_disjoint_rel (a0 a1 : address)
  : {forall ctx, addr_disjoint ctx a0 a1}+{True} :=
  if pure_fast_eq list_sval_get_hid (a_args a0) (a_args a1)
  then
    match test_is_indexed (a_addr a0) with
    | inright _ => right _
    | inleft ofs0 =>
    match test_is_indexed (a_addr a1) with
    | inright _ => right _
    | inleft ofs1 => if test_offset_disjoint ofs0 (a_chunk a0) ofs1 (a_chunk a1)
                     then left _ else right _
    end end
  else right _.
Next Obligation.
  eapply offset_disjoint_sound; eauto.
  congruence.
Qed.
Global Opaque test_disjoint_rel.


Definition test_disjoint_abs (a0 a1 : address)
  : {forall ctx, addr_disjoint ctx a0 a1}+{True}.
  case (a_aval a0) as [aa0|] eqn:AA0; [|right; trivial].
  case (a_aval a1) as [aa1|] eqn:AA1; [|right; trivial].
  case (vdisjoint aa0 (size_chunk (a_chunk a0))
                  aa1 (size_chunk (a_chunk a1))) eqn:D;
    [|right; trivial].
  left; intros.
  eapply abs_disjoint_sound; eauto.
Defined.
Global Opaque test_disjoint_abs.


Program Definition test_disjoint (rel abs : bool) (a0 a1 : address)
  : {forall ctx, addr_disjoint ctx a0 a1}+{True} :=
  if (if rel then test_disjoint_rel a0 a1 else right _)
  then left _
  else if abs
  then test_disjoint_abs a0 a1
  else right _.

End DisjointnessTest.


Section RRImplAlias.


  Variable p_test_disjoint : forall (a0 a1 : address),
    {forall ctx : iblock_common_context, addr_disjoint ctx a0 a1} + {True}.

  Local Obligation Tactic := try solve [program_simpl]; cbn beta; intros; subst.


Program Fixpoint rewrite_stores_under_load (sm: smem) (a0 : address) {struct sm}
  : {sm' : smem |
      forall ctx (MALIVE : alive (eval_smem ctx sm)), smem_equiv_on (load_range ctx a0) ctx sm' sm}
  := match sm with
  | Sstore sm1 chk1 addr1 sargs1 sinfo1 sv1 hc =>
      let sm1' := rewrite_stores_under_load sm1 a0 in
      let a1 := mk_addr chk1 addr1 sargs1 (store_aaddr sinfo1) in
      if p_test_disjoint a0 a1
      then sm1'
      else fSstore sm1' chk1 addr1 sargs1 sinfo1 sv1
  | _ => sm
  end.
Next Obligation.
  etransitivity.
  - apply (proj2_sig sm1'); auto. revert MALIVE. simpl; repeat autodestruct.
  - apply disjoint_store_smem_equiv_on; auto.
Defined.
Next Obligation.
  symmetry. apply apply_store_smem_equiv_on.
  - symmetry. apply (proj2_sig sm1'); auto.
    revert MALIVE; simpl; repeat autodestruct.
  - exact MALIVE.
Defined.
Next Obligation.
  apply smem_equiv_on_refl. exact MALIVE.
Qed.
Global Opaque rewrite_stores_under_load.

Program Definition rewrite_load_impl (sm: smem)
  (trap : trapping_mode) (chk: memory_chunk) (addr: addressing) (sargs : list_sval) aaddr
  : {sv : sval |
      forall ctx (MALIVE : alive (eval_smem ctx sm)),
        sval_equiv ctx sv (aSload sm trap chk addr sargs aaddr)}
  := aSload (rewrite_stores_under_load sm (mk_addr chk addr sargs aaddr)) trap chk addr sargs aaddr.
Next Obligation.
  case rewrite_stores_under_load as [sm' SM].
  eapply rewrite_load_equiv_on; auto.
Qed.
Global Opaque rewrite_load_impl.

Definition store_num_lt (i0 i1 : store_num_t) : bool :=
  match i0, i1 with
  | SNumNone, _ | _, SNumNone => false
  | SNumInv i0, SNumInv i1 | SNumSrc i0, SNumSrc i1 => Pos.ltb i0 i1
  | SNumInv _, SNumSrc _ => true
  | SNumSrc _, SNumInv _ => false
  end.

Program Fixpoint reorder_stores (sm: smem)
  (chk: memory_chunk) (addr: addressing) (sargs : list_sval) (sinfo : store_info) (sv : sval) {struct sm}
  : option {sm' : smem | forall ctx, smem_equiv ctx sm' (fSstore sm chk addr sargs sinfo sv)}
  := match sm with
  | Sstore sm1 chk1 addr1 sargs1 sinfo1 sv1 _ =>
      let a0 := mk_addr chk addr sargs (store_aaddr sinfo ) in
      let a1 := mk_addr chk1 addr1 sargs1 (store_aaddr sinfo1) in
      if p_test_disjoint a0 a1
      then if store_num_lt (store_num sinfo) (store_num sinfo1)
      then let sm2 := match reorder_stores sm1 chk addr sargs sinfo sv with
                      | Some sm2 => `sm2
                      | None => fSstore sm1 chk addr sargs sinfo sv
                      end
           in Some (fSstore sm2 chk1 addr1 sargs1 sinfo1 sv1)
      else None else None
  | _ => None
  end.
Next Obligation.
  assert (SM2: smem_equiv ctx sm2 (fSstore sm1 chk addr sargs sinfo sv)). {
    case reorder_stores as [[? IH]|] in sm2; simpl in sm2.
    - apply IH; auto.
    - reflexivity.
  }
  symmetry; etransitivity.
  - apply rewrite_store_store; auto.
  - simpl; rewrite SM2; reflexivity.
Defined.
Global Opaque reorder_stores.

End RRImplAlias.

Section RRImplStoreMotion.
  Local Obligation Tactic := try solve [program_simpl]; cbn beta; intros; subst.

Definition ands [A B C D : Prop] := Sumbool.sumbool_and A B C D.


Program Definition test_address_eq (a0 a1 : address): {a0=a1}+{True} :=
  if ands (chunk_eq (a_chunk a0) (a_chunk a1)) (
     ands (eq_addressing (a_addr a0) (a_addr a1)) (
     ands (pure_fast_eq list_sval_get_hid (a_args a0) (a_args a1))
          (option_eq eq_aval (a_aval a0) (a_aval a1))))
  then in_left else in_right.
Next Obligation.
  intuition.
  destruct a0, a1; simpl in *; subst; reflexivity.
Qed.

Program Fixpoint rewrite_stores_under_store (sm: smem) (a0 : address) {struct sm}
  : {sm' : smem |
     forall ctx (MALIVE : alive (eval_smem ctx sm)), smem_equiv_on (store_crange ctx a0) ctx sm' sm}
  := match sm with
  | Sstore sm1 chk1 addr1 sargs1 sinfo1 sv1 hc =>
      let a1 := mk_addr chk1 addr1 sargs1 (store_aaddr sinfo1) in
      if test_address_eq a0 a1
      then sm1
      else
        let sm1' := rewrite_stores_under_store sm1 a0 in
        fSstore sm1' chk1 addr1 sargs1 sinfo1 sv1
  | Sinit _ => sm
  end.
Next Obligation.
  apply remove_store_smem_equiv_on; auto.
Qed.
Next Obligation.
  symmetry; apply apply_store_smem_equiv_on; auto.
  symmetry; apply (proj2_sig sm1').
  revert MALIVE; simpl; repeat autodestruct.
Defined.
Next Obligation.
  apply smem_equiv_on_refl; auto.
Qed.
Global Opaque rewrite_stores_under_store.

Program Definition remove_overriden_store
  (sm : smem) (chk : memory_chunk) (addr : addressing) (sargs : list_sval) (sinfo : store_info)
  : {sm1 : smem |
     forall ctx (MALIVE : alive (eval_smem ctx sm)) sv,
     smem_equiv ctx (fSstore sm1 chk addr sargs sinfo sv) (fSstore sm chk addr sargs sinfo sv)}
  :=
    let a0 := mk_addr chk addr sargs (store_aaddr sinfo) in
    let sm' := rewrite_stores_under_store sm a0 in
    sm'.
Next Obligation.
  apply rewrite_store_equiv_on, (proj2_sig sm'), MALIVE.
Qed.

Program Definition rewrite_store_motion (reorder : bool)
  (sm : smem) (chk : memory_chunk) (addr : addressing) (sargs : list_sval) (sinfo : store_info)
  (sv : sval)
  : {sm1 : smem |
     forall ctx (MALIVE : alive (eval_smem ctx sm)),
     smem_equiv ctx sm1 (fSstore sm chk addr sargs sinfo sv)}
  :=
    let sm' := remove_overriden_store sm chk addr sargs sinfo in
    if reorder
    then match reorder_stores test_disjoint_abs sm' chk addr sargs sinfo sv with
         | Some sm1 => `sm1
         | None => fSstore sm' chk addr sargs sinfo sv
         end
    else
      fSstore sm' chk addr sargs (set_store_num SNumNone sinfo) sv.
Next Obligation.
  rewrite (proj2_sig sm1), (proj2_sig sm'); auto.
Qed.
Next Obligation.
  rewrite (proj2_sig sm'); auto.
Qed.
Next Obligation.
  rewrite <- (proj2_sig sm'); auto.
Qed.
Global Opaque rewrite_store_motion.

End RRImplStoreMotion.


Definition rrules_filter (RRULES: rrules_set) :=
  match RRULES with
  | RRschedule rel abs => Some (rel, abs)
  | RRlct _ true => Some (false, true)
  | RRstoreMotion true _ => Some (false, true)
  | _ => None
  end.

Definition rewrite_load (RRULES: rrules_set) (sm: smem)
  (trap : trapping_mode) (chunk: memory_chunk) (addr: addressing) (lsv: list_sval) (aaddr: option aval)
  : option sval
  := match rrules_filter RRULES with
  | Some (rel, abs) => Some (proj1_sig
        (rewrite_load_impl (test_disjoint rel abs) sm trap chunk addr lsv aaddr))
  | _ => None
  end.

Lemma rewrite_load_correct
   (ctx : iblock_common_context) (RRULES: rrules_set) sm trap chunk addr sargs aaddr rew
   (RW: rewrite_load RRULES sm trap chunk addr sargs aaddr = Some rew):
   alive (eval_smem ctx sm) ->
   sval_equiv ctx rew (root_apply (Rload trap chunk addr aaddr) sargs sm).
Proof.
  unfold rewrite_load in RW.
  destruct rrules_filter as [[]|] in RW; [injection RW as <-|discriminate RW].
  case rewrite_load_impl as [rew H]; apply H.
Qed.
Global Opaque rewrite_load.

Definition rewrite_store (RRULES: rrules_set) (sm: smem)
  (chk: memory_chunk) (addr: addressing) (sargs : list_sval) (sinfo : store_info) (sv : sval)
  : option smem
  := match RRULES with
  | RRschedule rel abs =>
      match reorder_stores (test_disjoint rel abs) sm chk addr sargs sinfo sv with
      | Some rw => Some (proj1_sig rw)
      | None => None
      end
  | RRstoreMotion _ alias_store =>
      Some (proj1_sig (rewrite_store_motion alias_store sm chk addr sargs sinfo sv))
  | _ => None
  end.

Lemma rewrite_store_correct
   (ctx : iblock_common_context) (RRULES: rrules_set) sm chunk addr sargs sinfo sv rew:
   let sm1 := fSstore sm chunk addr sargs sinfo sv in forall
   (MALIVE : alive (eval_smem ctx sm1))
   (RW: rewrite_store RRULES sm chunk addr sargs sinfo sv = Some rew),
   smem_equiv ctx rew sm1.
Proof.
  intros.
  destruct RRULES; simpl in RW; try congruence.
  - (*Case RRschedule*)
    case reorder_stores as [[rw H]|] in RW; [injection RW as <-|discriminate RW].
    apply H.
  - (*Case RRstoreMotion*)
    injection RW as <-; case rewrite_store_motion as [rw H]; apply H.
    intro DEAD; apply MALIVE; simpl; rewrite DEAD; repeat autodestruct.
Qed.
Global Opaque rewrite_store.