aboutsummaryrefslogtreecommitdiffstats
path: root/common/Separation.v
diff options
context:
space:
mode:
Diffstat (limited to 'common/Separation.v')
-rw-r--r--common/Separation.v916
1 files changed, 916 insertions, 0 deletions
diff --git a/common/Separation.v b/common/Separation.v
new file mode 100644
index 00000000..4d87443b
--- /dev/null
+++ b/common/Separation.v
@@ -0,0 +1,916 @@
+(* *********************************************************************)
+(* *)
+(* The Compcert verified compiler *)
+(* *)
+(* Xavier Leroy, INRIA Paris *)
+(* *)
+(* Copyright Institut National de Recherche en Informatique et en *)
+(* Automatique. All rights reserved. This file is distributed *)
+(* under the terms of the GNU General Public License as published by *)
+(* the Free Software Foundation, either version 2 of the License, or *)
+(* (at your option) any later version. This file is also distributed *)
+(* under the terms of the INRIA Non-Commercial License Agreement. *)
+(* *)
+(* *********************************************************************)
+
+(** Assertions on memory states, in the style of separation logic *)
+
+(** This library defines a number of useful logical assertions about
+ CompCert memory states, such as "block [b] at offset [ofs] contains
+ value [v]". Assertions can be grouped using a separating conjunction
+ operator in the style of separation logic.
+
+ Currently, this library is used only in module [Stackingproof]
+ to reason about the shapes of stack frames generated during the
+ [Stacking] pass.
+
+ This is not a full-fledged separation logic because there is no
+ program logic (Hoare triples) to speak of. Also, there is no general
+ frame rule; instead, a weak form of the frame rule is provided
+ by the lemmas that help us reason about the logical assertions. *)
+
+Require Import Setoid Program.Basics.
+Require Import Coqlib Decidableplus.
+Require Import AST Integers Values Memory Events Globalenvs.
+
+(** * Assertions about memory *)
+
+(** An assertion is composed of:
+- a predicate over memory states (of type [mem -> Prop])
+- a set of (block, offset) memory locations that represents the memory footprint of the assertion
+- a proof that the predicate is invariant by changes of memory outside of the footprint
+- a proof that the footprint contains only valid memory blocks.
+
+This presentation (where the footprint is part of the assertion) makes
+it possible to define separating conjunction without explicitly
+defining a separation algebra over CompCert memory states (i.e. the
+notion of splitting a memory state into two disjoint halves). *)
+
+Record massert : Type := {
+ m_pred : mem -> Prop;
+ m_footprint: block -> Z -> Prop;
+ m_invar: forall m m', m_pred m -> Mem.unchanged_on m_footprint m m' -> m_pred m';
+ m_valid: forall m b ofs, m_pred m -> m_footprint b ofs -> Mem.valid_block m b
+}.
+
+Notation "m |= p" := (m_pred p m) (at level 74, no associativity) : sep_scope.
+
+(** Implication and logical equivalence between memory predicates *)
+
+Definition massert_imp (P Q: massert) : Prop :=
+ (forall m, m_pred P m -> m_pred Q m) /\ (forall b ofs, m_footprint Q b ofs -> m_footprint P b ofs).
+Definition massert_eqv (P Q: massert) : Prop :=
+ massert_imp P Q /\ massert_imp Q P.
+
+Remark massert_imp_refl: forall p, massert_imp p p.
+Proof.
+ unfold massert_imp; auto.
+Qed.
+
+Remark massert_imp_trans: forall p q r, massert_imp p q -> massert_imp q r -> massert_imp p r.
+Proof.
+ unfold massert_imp; intros; firstorder auto.
+Qed.
+
+Remark massert_eqv_refl: forall p, massert_eqv p p.
+Proof.
+ unfold massert_eqv, massert_imp; intros. tauto.
+Qed.
+
+Remark massert_eqv_sym: forall p q, massert_eqv p q -> massert_eqv q p.
+Proof.
+ unfold massert_eqv, massert_imp; intros. tauto.
+Qed.
+
+Remark massert_eqv_trans: forall p q r, massert_eqv p q -> massert_eqv q r -> massert_eqv p r.
+Proof.
+ unfold massert_eqv, massert_imp; intros. firstorder auto.
+Qed.
+
+(** Record [massert_eqv] and [massert_imp] as relations so that they can be used by rewriting tactics. *)
+Add Relation massert massert_imp
+ reflexivity proved by massert_imp_refl
+ transitivity proved by massert_imp_trans
+as massert_imp_prel.
+
+Add Relation massert massert_eqv
+ reflexivity proved by massert_eqv_refl
+ symmetry proved by massert_eqv_sym
+ transitivity proved by massert_eqv_trans
+as massert_eqv_prel.
+
+Add Morphism m_pred
+ with signature massert_imp ==> eq ==> impl
+ as m_pred_morph_1.
+Proof.
+ intros P Q [A B]. auto.
+Qed.
+
+Add Morphism m_pred
+ with signature massert_eqv ==> eq ==> iff
+ as m_pred_morph_2.
+Proof.
+ intros P Q [[A B] [C D]]. split; auto.
+Qed.
+
+Hint Resolve massert_imp_refl massert_eqv_refl.
+
+(** * Separating conjunction *)
+
+Definition disjoint_footprint (P Q: massert) : Prop :=
+ forall b ofs, m_footprint P b ofs -> m_footprint Q b ofs -> False.
+
+Program Definition sepconj (P Q: massert) : massert := {|
+ m_pred := fun m => m_pred P m /\ m_pred Q m /\ disjoint_footprint P Q;
+ m_footprint := fun b ofs => m_footprint P b ofs \/ m_footprint Q b ofs
+|}.
+Next Obligation.
+ repeat split; auto.
+ apply (m_invar P) with m; auto. eapply Mem.unchanged_on_implies; eauto. simpl; auto.
+ apply (m_invar Q) with m; auto. eapply Mem.unchanged_on_implies; eauto. simpl; auto.
+Qed.
+Next Obligation.
+ destruct H0; [eapply (m_valid P) | eapply (m_valid Q)]; eauto.
+Qed.
+
+Add Morphism sepconj
+ with signature massert_imp ==> massert_imp ==> massert_imp
+ as sepconj_morph_1.
+Proof.
+ intros P1 P2 [A B] Q1 Q2 [C D].
+ red; simpl; split; intros.
+- intuition auto. red; intros. apply (H2 b ofs); auto.
+- intuition auto.
+Qed.
+
+Add Morphism sepconj
+ with signature massert_eqv ==> massert_eqv ==> massert_eqv
+ as sepconj_morph_2.
+Proof.
+ intros. destruct H, H0. split; apply sepconj_morph_1; auto.
+Qed.
+
+Infix "**" := sepconj (at level 60, right associativity) : sep_scope.
+
+Local Open Scope sep_scope.
+
+Lemma sep_imp:
+ forall P P' Q Q' m,
+ m |= P ** Q -> massert_imp P P' -> massert_imp Q Q' -> m |= P' ** Q'.
+Proof.
+ intros. rewrite <- H0, <- H1; auto.
+Qed.
+
+Lemma sep_comm_1:
+ forall P Q, massert_imp (P ** Q) (Q ** P).
+Proof.
+ unfold massert_imp; simpl; split; intros.
+- intuition auto. red; intros; eapply H2; eauto.
+- intuition auto.
+Qed.
+
+Lemma sep_comm:
+ forall P Q, massert_eqv (P ** Q) (Q ** P).
+Proof.
+ intros; split; apply sep_comm_1.
+Qed.
+
+Lemma sep_assoc_1:
+ forall P Q R, massert_imp ((P ** Q) ** R) (P ** (Q ** R)).
+Proof.
+ intros. unfold massert_imp, sepconj, disjoint_footprint; simpl; firstorder auto.
+Qed.
+
+Lemma sep_assoc_2:
+ forall P Q R, massert_imp (P ** (Q ** R)) ((P ** Q) ** R).
+Proof.
+ intros. unfold massert_imp, sepconj, disjoint_footprint; simpl; firstorder auto.
+Qed.
+
+Lemma sep_assoc:
+ forall P Q R, massert_eqv ((P ** Q) ** R) (P ** (Q ** R)).
+Proof.
+ intros; split. apply sep_assoc_1. apply sep_assoc_2.
+Qed.
+
+Lemma sep_swap:
+ forall P Q R, massert_eqv (P ** Q ** R) (Q ** P ** R).
+Proof.
+ intros. rewrite <- sep_assoc. rewrite (sep_comm P). rewrite sep_assoc. reflexivity.
+Qed.
+
+Definition sep_swap12 := sep_swap.
+
+Lemma sep_swap23:
+ forall P Q R S, massert_eqv (P ** Q ** R ** S) (P ** R ** Q ** S).
+Proof.
+ intros. rewrite (sep_swap Q). reflexivity.
+Qed.
+
+Lemma sep_swap34:
+ forall P Q R S T, massert_eqv (P ** Q ** R ** S ** T) (P ** Q ** S ** R ** T).
+Proof.
+ intros. rewrite (sep_swap R). reflexivity.
+Qed.
+
+Lemma sep_swap45:
+ forall P Q R S T U, massert_eqv (P ** Q ** R ** S ** T ** U) (P ** Q ** R ** T ** S ** U).
+Proof.
+ intros. rewrite (sep_swap S). reflexivity.
+Qed.
+
+Definition sep_swap2 := sep_swap.
+
+Lemma sep_swap3:
+ forall P Q R S, massert_eqv (P ** Q ** R ** S) (R ** Q ** P ** S).
+Proof.
+ intros. rewrite sep_swap. rewrite (sep_swap P). rewrite sep_swap. reflexivity.
+Qed.
+
+Lemma sep_swap4:
+ forall P Q R S T, massert_eqv (P ** Q ** R ** S ** T) (S ** Q ** R ** P ** T).
+Proof.
+ intros. rewrite sep_swap. rewrite (sep_swap3 P). rewrite sep_swap. reflexivity.
+Qed.
+
+Lemma sep_swap5:
+ forall P Q R S T U, massert_eqv (P ** Q ** R ** S ** T ** U) (T ** Q ** R ** S ** P ** U).
+Proof.
+ intros. rewrite sep_swap. rewrite (sep_swap4 P). rewrite sep_swap. reflexivity.
+Qed.
+
+Lemma sep_drop:
+ forall P Q m, m |= P ** Q -> m |= Q.
+Proof.
+ simpl; intros. tauto.
+Qed.
+
+Lemma sep_drop2:
+ forall P Q R m, m |= P ** Q ** R -> m |= P ** R.
+Proof.
+ intros. rewrite sep_swap in H. eapply sep_drop; eauto.
+Qed.
+
+Lemma sep_proj1:
+ forall Q P m, m |= P ** Q -> m |= P.
+Proof.
+ intros. destruct H; auto.
+Qed.
+
+Lemma sep_proj2:
+ forall P Q m, m |= P ** Q -> m |= Q.
+Proof sep_drop.
+
+Definition sep_pick1 := sep_proj1.
+
+Lemma sep_pick2:
+ forall P Q R m, m |= P ** Q ** R -> m |= Q.
+Proof.
+ intros. eapply sep_proj1; eapply sep_proj2; eauto.
+Qed.
+
+Lemma sep_pick3:
+ forall P Q R S m, m |= P ** Q ** R ** S -> m |= R.
+Proof.
+ intros. eapply sep_pick2; eapply sep_proj2; eauto.
+Qed.
+
+Lemma sep_pick4:
+ forall P Q R S T m, m |= P ** Q ** R ** S ** T -> m |= S.
+Proof.
+ intros. eapply sep_pick3; eapply sep_proj2; eauto.
+Qed.
+
+Lemma sep_pick5:
+ forall P Q R S T U m, m |= P ** Q ** R ** S ** T ** U -> m |= T.
+Proof.
+ intros. eapply sep_pick4; eapply sep_proj2; eauto.
+Qed.
+
+Lemma sep_preserved:
+ forall m m' P Q,
+ m |= P ** Q ->
+ (m |= P -> m' |= P) ->
+ (m |= Q -> m' |= Q) ->
+ m' |= P ** Q.
+Proof.
+ simpl; intros. intuition auto.
+Qed.
+
+(** * Basic memory assertions. *)
+
+(** Pure logical assertion *)
+
+Program Definition pure (P: Prop) : massert := {|
+ m_pred := fun m => P;
+ m_footprint := fun b ofs => False
+|}.
+Next Obligation.
+ tauto.
+Qed.
+
+Lemma sep_pure:
+ forall P Q m, m |= pure P ** Q <-> P /\ m |= Q.
+Proof.
+ simpl; intros. intuition auto. red; simpl; tauto.
+Qed.
+
+(** A range of bytes, with full permissions and unspecified contents. *)
+
+Program Definition range (b: block) (lo hi: Z) : massert := {|
+ m_pred := fun m =>
+ 0 <= lo /\ hi <= Int.modulus
+ /\ (forall i k p, lo <= i < hi -> Mem.perm m b i k p);
+ m_footprint := fun b' ofs' => b' = b /\ lo <= ofs' < hi
+|}.
+Next Obligation.
+ split; auto. split; auto. intros. eapply Mem.perm_unchanged_on; eauto. simpl; auto.
+Qed.
+Next Obligation.
+ apply Mem.perm_valid_block with ofs Cur Freeable; auto.
+Qed.
+
+Lemma alloc_rule:
+ forall m lo hi b m' P,
+ Mem.alloc m lo hi = (m', b) ->
+ 0 <= lo -> hi <= Int.modulus ->
+ m |= P ->
+ m' |= range b lo hi ** P.
+Proof.
+ intros; simpl. split; [|split].
+- split; auto. split; auto. intros.
+ apply Mem.perm_implies with Freeable; auto with mem.
+ eapply Mem.perm_alloc_2; eauto.
+- apply (m_invar P) with m; auto. eapply Mem.alloc_unchanged_on; eauto.
+- red; simpl. intros. destruct H3; subst b0.
+ eelim Mem.fresh_block_alloc; eauto. eapply (m_valid P); eauto.
+Qed.
+
+Lemma range_split:
+ forall b lo hi P mid m,
+ lo <= mid <= hi ->
+ m |= range b lo hi ** P ->
+ m |= range b lo mid ** range b mid hi ** P.
+Proof.
+ intros. rewrite <- sep_assoc. eapply sep_imp; eauto.
+ split; simpl; intros.
+- intuition auto.
++ omega.
++ apply H5; omega.
++ omega.
++ apply H5; omega.
++ red; simpl; intros; omega.
+- intuition omega.
+Qed.
+
+Lemma range_drop_left:
+ forall b lo hi P mid m,
+ lo <= mid <= hi ->
+ m |= range b lo hi ** P ->
+ m |= range b mid hi ** P.
+Proof.
+ intros. apply sep_drop with (range b lo mid). apply range_split; auto.
+Qed.
+
+Lemma range_drop_right:
+ forall b lo hi P mid m,
+ lo <= mid <= hi ->
+ m |= range b lo hi ** P ->
+ m |= range b lo mid ** P.
+Proof.
+ intros. apply sep_drop2 with (range b mid hi). apply range_split; auto.
+Qed.
+
+Lemma range_split_2:
+ forall b lo hi P mid al m,
+ lo <= align mid al <= hi ->
+ al > 0 ->
+ m |= range b lo hi ** P ->
+ m |= range b lo mid ** range b (align mid al) hi ** P.
+Proof.
+ intros. rewrite <- sep_assoc. eapply sep_imp; eauto.
+ assert (mid <= align mid al) by (apply align_le; auto).
+ split; simpl; intros.
+- intuition auto.
++ omega.
++ apply H7; omega.
++ omega.
++ apply H7; omega.
++ red; simpl; intros; omega.
+- intuition omega.
+Qed.
+
+Lemma range_preserved:
+ forall m m' b lo hi,
+ m |= range b lo hi ->
+ (forall i k p, lo <= i < hi -> Mem.perm m b i k p -> Mem.perm m' b i k p) ->
+ m' |= range b lo hi.
+Proof.
+ intros. destruct H as (A & B & C). simpl; intuition auto.
+Qed.
+
+(** A memory area that contains a value sastifying a given predicate *)
+
+Program Definition contains (chunk: memory_chunk) (b: block) (ofs: Z) (spec: val -> Prop) : massert := {|
+ m_pred := fun m =>
+ 0 <= ofs <= Int.max_unsigned
+ /\ Mem.valid_access m chunk b ofs Freeable
+ /\ exists v, Mem.load chunk m b ofs = Some v /\ spec v;
+ m_footprint := fun b' ofs' => b' = b /\ ofs <= ofs' < ofs + size_chunk chunk
+|}.
+Next Obligation.
+ rename H2 into v. split;[|split].
+- auto.
+- destruct H1; split; auto. red; intros; eapply Mem.perm_unchanged_on; eauto. simpl; auto.
+- exists v. split; auto. eapply Mem.load_unchanged_on; eauto. simpl; auto.
+Qed.
+Next Obligation.
+ eauto with mem.
+Qed.
+
+Lemma contains_no_overflow:
+ forall spec m chunk b ofs,
+ m |= contains chunk b ofs spec ->
+ 0 <= ofs <= Int.max_unsigned.
+Proof.
+ intros. simpl in H. tauto.
+Qed.
+
+Lemma load_rule:
+ forall spec m chunk b ofs,
+ m |= contains chunk b ofs spec ->
+ exists v, Mem.load chunk m b ofs = Some v /\ spec v.
+Proof.
+ intros. destruct H as (D & E & v & F & G).
+ exists v; auto.
+Qed.
+
+Lemma loadv_rule:
+ forall spec m chunk b ofs,
+ m |= contains chunk b ofs spec ->
+ exists v, Mem.loadv chunk m (Vptr b (Int.repr ofs)) = Some v /\ spec v.
+Proof.
+ intros. exploit load_rule; eauto. intros (v & A & B). exists v; split; auto.
+ simpl. rewrite Int.unsigned_repr; auto. eapply contains_no_overflow; eauto.
+Qed.
+
+Lemma store_rule:
+ forall chunk m b ofs v (spec1 spec: val -> Prop) P,
+ m |= contains chunk b ofs spec1 ** P ->
+ spec (Val.load_result chunk v) ->
+ exists m',
+ Mem.store chunk m b ofs v = Some m' /\ m' |= contains chunk b ofs spec ** P.
+Proof.
+ intros. destruct H as (A & B & C). destruct A as (D & E & v0 & F & G).
+ assert (H: Mem.valid_access m chunk b ofs Writable) by eauto with mem.
+ destruct (Mem.valid_access_store _ _ _ _ v H) as [m' STORE].
+ exists m'; split; auto. simpl. intuition auto.
+- eapply Mem.store_valid_access_1; eauto.
+- exists (Val.load_result chunk v); split; auto. eapply Mem.load_store_same; eauto.
+- apply (m_invar P) with m; auto.
+ eapply Mem.store_unchanged_on; eauto.
+ intros; red; intros. apply (C b i); simpl; auto.
+Qed.
+
+Lemma storev_rule:
+ forall chunk m b ofs v (spec1 spec: val -> Prop) P,
+ m |= contains chunk b ofs spec1 ** P ->
+ spec (Val.load_result chunk v) ->
+ exists m',
+ Mem.storev chunk m (Vptr b (Int.repr ofs)) v = Some m' /\ m' |= contains chunk b ofs spec ** P.
+Proof.
+ intros. exploit store_rule; eauto. intros (m' & A & B). exists m'; split; auto.
+ simpl. rewrite Int.unsigned_repr; auto. eapply contains_no_overflow. eapply sep_pick1; eauto.
+Qed.
+
+Lemma range_contains:
+ forall chunk b ofs P m,
+ m |= range b ofs (ofs + size_chunk chunk) ** P ->
+ (align_chunk chunk | ofs) ->
+ m |= contains chunk b ofs (fun v => True) ** P.
+Proof.
+ intros. destruct H as (A & B & C). destruct A as (D & E & F).
+ split; [|split].
+- assert (Mem.valid_access m chunk b ofs Freeable).
+ { split; auto. red; auto. }
+ split. generalize (size_chunk_pos chunk). unfold Int.max_unsigned. omega.
+ split. auto.
++ destruct (Mem.valid_access_load m chunk b ofs) as [v LOAD].
+ eauto with mem.
+ exists v; auto.
+- auto.
+- auto.
+Qed.
+
+Lemma contains_imp:
+ forall (spec1 spec2: val -> Prop) chunk b ofs,
+ (forall v, spec1 v -> spec2 v) ->
+ massert_imp (contains chunk b ofs spec1) (contains chunk b ofs spec2).
+Proof.
+ intros; split; simpl; intros.
+- intuition auto. destruct H4 as (v & A & B). exists v; auto.
+- auto.
+Qed.
+
+(** A memory area that contains a given value *)
+
+Definition hasvalue (chunk: memory_chunk) (b: block) (ofs: Z) (v: val) : massert :=
+ contains chunk b ofs (fun v' => v' = v).
+
+Lemma store_rule':
+ forall chunk m b ofs v (spec1: val -> Prop) P,
+ m |= contains chunk b ofs spec1 ** P ->
+ exists m',
+ Mem.store chunk m b ofs v = Some m' /\ m' |= hasvalue chunk b ofs (Val.load_result chunk v) ** P.
+Proof.
+ intros. eapply store_rule; eauto.
+Qed.
+
+Lemma storev_rule':
+ forall chunk m b ofs v (spec1: val -> Prop) P,
+ m |= contains chunk b ofs spec1 ** P ->
+ exists m',
+ Mem.storev chunk m (Vptr b (Int.repr ofs)) v = Some m' /\ m' |= hasvalue chunk b ofs (Val.load_result chunk v) ** P.
+Proof.
+ intros. eapply storev_rule; eauto.
+Qed.
+
+(** Non-separating conjunction *)
+
+Program Definition mconj (P Q: massert) : massert := {|
+ m_pred := fun m => m_pred P m /\ m_pred Q m;
+ m_footprint := fun b ofs => m_footprint P b ofs \/ m_footprint Q b ofs
+|}.
+Next Obligation.
+ split.
+ apply (m_invar P) with m; auto. eapply Mem.unchanged_on_implies; eauto. simpl; auto.
+ apply (m_invar Q) with m; auto. eapply Mem.unchanged_on_implies; eauto. simpl; auto.
+Qed.
+Next Obligation.
+ destruct H0; [eapply (m_valid P) | eapply (m_valid Q)]; eauto.
+Qed.
+
+Lemma mconj_intro:
+ forall P Q R m,
+ m |= P ** R -> m |= Q ** R -> m |= mconj P Q ** R.
+Proof.
+ intros. destruct H as (A & B & C). destruct H0 as (D & E & F).
+ split; [|split].
+- simpl; auto.
+- auto.
+- red; simpl; intros. destruct H; [eelim C | eelim F]; eauto.
+Qed.
+
+Lemma mconj_proj1:
+ forall P Q R m, m |= mconj P Q ** R -> m |= P ** R.
+Proof.
+ intros. destruct H as (A & B & C); simpl in A.
+ simpl. intuition auto.
+ red; intros; eapply C; eauto; simpl; auto.
+Qed.
+
+Lemma mconj_proj2:
+ forall P Q R m, m |= mconj P Q ** R -> m |= Q ** R.
+Proof.
+ intros. destruct H as (A & B & C); simpl in A.
+ simpl. intuition auto.
+ red; intros; eapply C; eauto; simpl; auto.
+Qed.
+
+Lemma frame_mconj:
+ forall P P' Q R m m',
+ m |= mconj P Q ** R ->
+ m' |= P' ** R ->
+ m' |= Q ->
+ m' |= mconj P' Q ** R.
+Proof.
+ intros. destruct H as (A & B & C); simpl in A.
+ destruct H0 as (D & E & F).
+ simpl. intuition auto.
+ red; simpl; intros. destruct H2. eapply F; eauto. eapply C; simpl; eauto.
+Qed.
+
+Add Morphism mconj
+ with signature massert_imp ==> massert_imp ==> massert_imp
+ as mconj_morph_1.
+Proof.
+ intros P1 P2 [A B] Q1 Q2 [C D].
+ red; simpl; intuition auto.
+Qed.
+
+Add Morphism mconj
+ with signature massert_eqv ==> massert_eqv ==> massert_eqv
+ as mconj_morph_2.
+Proof.
+ intros. destruct H, H0. split; apply mconj_morph_1; auto.
+Qed.
+
+(** The image of a memory injection *)
+
+Program Definition minjection (j: meminj) (m0: mem) : massert := {|
+ m_pred := fun m => Mem.inject j m0 m;
+ m_footprint := fun b ofs => exists b0 delta, j b0 = Some(b, delta) /\ Mem.perm m0 b0 (ofs - delta) Max Nonempty
+|}.
+Next Obligation.
+ set (img := fun b' ofs => exists b delta, j b = Some(b', delta) /\ Mem.perm m0 b (ofs - delta) Max Nonempty) in *.
+ assert (IMG: forall b1 b2 delta ofs k p,
+ j b1 = Some (b2, delta) -> Mem.perm m0 b1 ofs k p -> img b2 (ofs + delta)).
+ { intros. red. exists b1, delta; split; auto.
+ replace (ofs + delta - delta) with ofs by omega.
+ eauto with mem. }
+ destruct H. constructor.
+- destruct mi_inj. constructor; intros.
++ eapply Mem.perm_unchanged_on; eauto. eapply IMG; eauto.
++ eauto.
++ rewrite (Mem.unchanged_on_contents _ _ _ H0); eauto.
+- assumption.
+- intros. eapply Mem.valid_block_unchanged_on; eauto.
+- assumption.
+- assumption.
+Qed.
+Next Obligation.
+ eapply Mem.valid_block_inject_2; eauto.
+Qed.
+
+Lemma loadv_parallel_rule:
+ forall j m1 m2 chunk addr1 v1 addr2,
+ m2 |= minjection j m1 ->
+ Mem.loadv chunk m1 addr1 = Some v1 ->
+ Val.inject j addr1 addr2 ->
+ exists v2, Mem.loadv chunk m2 addr2 = Some v2 /\ Val.inject j v1 v2.
+Proof.
+ intros. simpl in H. eapply Mem.loadv_inject; eauto.
+Qed.
+
+Lemma storev_parallel_rule:
+ forall j m1 m2 P chunk addr1 v1 m1' addr2 v2,
+ m2 |= minjection j m1 ** P ->
+ Mem.storev chunk m1 addr1 v1 = Some m1' ->
+ Val.inject j addr1 addr2 ->
+ Val.inject j v1 v2 ->
+ exists m2', Mem.storev chunk m2 addr2 v2 = Some m2' /\ m2' |= minjection j m1' ** P.
+Proof.
+ intros. destruct H as (A & B & C). simpl in A.
+ exploit Mem.storev_mapped_inject; eauto. intros (m2' & STORE & INJ).
+ inv H1; simpl in STORE; try discriminate.
+ assert (VALID: Mem.valid_access m1 chunk b1 (Int.unsigned ofs1) Writable)
+ by eauto with mem.
+ assert (EQ: Int.unsigned (Int.add ofs1 (Int.repr delta)) = Int.unsigned ofs1 + delta).
+ { eapply Mem.address_inject'; eauto with mem. }
+ exists m2'; split; auto.
+ split; [|split].
+- exact INJ.
+- apply (m_invar P) with m2; auto.
+ eapply Mem.store_unchanged_on; eauto.
+ intros; red; intros. eelim C; eauto. simpl.
+ exists b1, delta; split; auto. destruct VALID as [V1 V2].
+ apply Mem.perm_cur_max. apply Mem.perm_implies with Writable; auto with mem.
+ apply V1. omega.
+- red; simpl; intros. destruct H1 as (b0 & delta0 & U & V).
+ eelim C; eauto. simpl. exists b0, delta0; eauto with mem.
+Qed.
+
+Lemma alloc_parallel_rule:
+ forall m1 sz1 m1' b1 m2 sz2 m2' b2 P j lo hi delta,
+ m2 |= minjection j m1 ** P ->
+ Mem.alloc m1 0 sz1 = (m1', b1) ->
+ Mem.alloc m2 0 sz2 = (m2', b2) ->
+ (8 | delta) ->
+ lo = delta ->
+ hi = delta + Zmax 0 sz1 ->
+ 0 <= sz2 <= Int.max_unsigned ->
+ 0 <= delta -> hi <= sz2 ->
+ exists j',
+ m2' |= range b2 0 lo ** range b2 hi sz2 ** minjection j' m1' ** P
+ /\ inject_incr j j'
+ /\ j' b1 = Some(b2, delta)
+ /\ (forall b, b <> b1 -> j' b = j b).
+Proof.
+ intros until delta; intros SEP ALLOC1 ALLOC2 ALIGN LO HI RANGE1 RANGE2 RANGE3.
+ assert (RANGE4: lo <= hi) by xomega.
+ assert (FRESH1: ~Mem.valid_block m1 b1) by (eapply Mem.fresh_block_alloc; eauto).
+ assert (FRESH2: ~Mem.valid_block m2 b2) by (eapply Mem.fresh_block_alloc; eauto).
+ destruct SEP as (INJ & SP & DISJ). simpl in INJ.
+ exploit Mem.alloc_left_mapped_inject.
+- eapply Mem.alloc_right_inject; eauto.
+- eexact ALLOC1.
+- instantiate (1 := b2). eauto with mem.
+- instantiate (1 := delta). xomega.
+- intros. assert (0 <= ofs < sz2) by (eapply Mem.perm_alloc_3; eauto). omega.
+- intros. apply Mem.perm_implies with Freeable; auto with mem.
+ eapply Mem.perm_alloc_2; eauto. xomega.
+- red; intros. apply Zdivides_trans with 8; auto.
+ exists (8 / align_chunk chunk). destruct chunk; reflexivity.
+- intros. elim FRESH2. eapply Mem.valid_block_inject_2; eauto.
+- intros (j' & INJ' & J1 & J2 & J3).
+ exists j'; split; auto.
+ rewrite <- ! sep_assoc.
+ split; [|split].
++ simpl. intuition auto; try (unfold Int.max_unsigned in *; omega).
+* apply Mem.perm_implies with Freeable; auto with mem.
+ eapply Mem.perm_alloc_2; eauto. omega.
+* apply Mem.perm_implies with Freeable; auto with mem.
+ eapply Mem.perm_alloc_2; eauto. omega.
+* red; simpl; intros. destruct H1, H2. omega.
+* red; simpl; intros.
+ assert (b = b2) by tauto. subst b.
+ assert (0 <= ofs < lo \/ hi <= ofs < sz2) by tauto. clear H1.
+ destruct H2 as (b0 & delta0 & D & E).
+ eapply Mem.perm_alloc_inv in E; eauto.
+ destruct (eq_block b0 b1).
+ subst b0. rewrite J2 in D. inversion D; clear D; subst delta0. xomega.
+ rewrite J3 in D by auto. elim FRESH2. eapply Mem.valid_block_inject_2; eauto.
++ apply (m_invar P) with m2; auto. eapply Mem.alloc_unchanged_on; eauto.
++ red; simpl; intros.
+ assert (VALID: Mem.valid_block m2 b) by (eapply (m_valid P); eauto).
+ destruct H as [A | (b0 & delta0 & A & B)].
+* assert (b = b2) by tauto. subst b. contradiction.
+* eelim DISJ; eauto. simpl.
+ eapply Mem.perm_alloc_inv in B; eauto.
+ destruct (eq_block b0 b1).
+ subst b0. rewrite J2 in A. inversion A; clear A; subst b delta0. contradiction.
+ rewrite J3 in A by auto. exists b0, delta0; auto.
+Qed.
+
+Lemma free_parallel_rule:
+ forall j m1 b1 sz1 m1' m2 b2 sz2 lo hi delta P,
+ m2 |= range b2 0 lo ** range b2 hi sz2 ** minjection j m1 ** P ->
+ Mem.free m1 b1 0 sz1 = Some m1' ->
+ j b1 = Some (b2, delta) ->
+ lo = delta -> hi = delta + Zmax 0 sz1 ->
+ exists m2',
+ Mem.free m2 b2 0 sz2 = Some m2'
+ /\ m2' |= minjection j m1' ** P.
+Proof.
+ intros. rewrite <- ! sep_assoc in H.
+ destruct H as (A & B & C).
+ destruct A as (D & E & F).
+ destruct D as (J & K & L).
+ destruct J as (_ & _ & J). destruct K as (_ & _ & K).
+ simpl in E.
+ assert (PERM: Mem.range_perm m2 b2 0 sz2 Cur Freeable).
+ { red; intros.
+ destruct (zlt ofs lo). apply J; omega.
+ destruct (zle hi ofs). apply K; omega.
+ replace ofs with ((ofs - delta) + delta) by omega.
+ eapply Mem.perm_inject; eauto.
+ eapply Mem.free_range_perm; eauto. xomega.
+ }
+ destruct (Mem.range_perm_free _ _ _ _ PERM) as [m2' FREE].
+ exists m2'; split; auto. split; [|split].
+- simpl. eapply Mem.free_right_inject; eauto. eapply Mem.free_left_inject; eauto.
+ intros. apply (F b2 (ofs + delta0)).
++ simpl.
+ destruct (zlt (ofs + delta0) lo). intuition auto.
+ destruct (zle hi (ofs + delta0)). intuition auto.
+ destruct (eq_block b0 b1).
+* subst b0. rewrite H1 in H; inversion H; clear H; subst delta0.
+ eelim (Mem.perm_free_2 m1); eauto. xomega.
+* exploit Mem.mi_no_overlap; eauto.
+ apply Mem.perm_max with k. apply Mem.perm_implies with p; auto with mem.
+ eapply Mem.perm_free_3; eauto.
+ apply Mem.perm_cur_max. apply Mem.perm_implies with Freeable; auto with mem.
+ eapply (Mem.free_range_perm m1); eauto.
+ instantiate (1 := ofs + delta0 - delta). xomega.
+ intros [X|X]. congruence. omega.
++ simpl. exists b0, delta0; split; auto.
+ replace (ofs + delta0 - delta0) with ofs by omega.
+ apply Mem.perm_max with k. apply Mem.perm_implies with p; auto with mem.
+ eapply Mem.perm_free_3; eauto.
+- apply (m_invar P) with m2; auto.
+ eapply Mem.free_unchanged_on; eauto.
+ intros; red; intros. eelim C; eauto. simpl.
+ destruct (zlt i lo). intuition auto.
+ destruct (zle hi i). intuition auto.
+ right; exists b1, delta; split; auto.
+ apply Mem.perm_cur_max. apply Mem.perm_implies with Freeable; auto with mem.
+ eapply Mem.free_range_perm; eauto. xomega.
+- red; simpl; intros. eelim C; eauto.
+ simpl. right. destruct H as (b0 & delta0 & U & V).
+ exists b0, delta0; split; auto.
+ eapply Mem.perm_free_3; eauto.
+Qed.
+
+(** Preservation of a global environment by a memory injection *)
+
+Inductive globalenv_preserved {F V: Type} (ge: Genv.t F V) (j: meminj) (bound: block) : Prop :=
+ | globalenv_preserved_intro
+ (DOMAIN: forall b, Plt b bound -> j b = Some(b, 0))
+ (IMAGE: forall b1 b2 delta, j b1 = Some(b2, delta) -> Plt b2 bound -> b1 = b2)
+ (SYMBOLS: forall id b, Genv.find_symbol ge id = Some b -> Plt b bound)
+ (FUNCTIONS: forall b fd, Genv.find_funct_ptr ge b = Some fd -> Plt b bound)
+ (VARINFOS: forall b gv, Genv.find_var_info ge b = Some gv -> Plt b bound).
+
+Program Definition globalenv_inject {F V: Type} (ge: Genv.t F V) (j: meminj) : massert := {|
+ m_pred := fun m => exists bound, Ple bound (Mem.nextblock m) /\ globalenv_preserved ge j bound;
+ m_footprint := fun b ofs => False
+|}.
+Next Obligation.
+ rename H into bound. exists bound; split; auto. eapply Ple_trans; eauto. eapply Mem.unchanged_on_nextblock; eauto.
+Qed.
+Next Obligation.
+ tauto.
+Qed.
+
+Lemma globalenv_inject_preserves_globals:
+ forall (F V: Type) (ge: Genv.t F V) j m,
+ m |= globalenv_inject ge j ->
+ meminj_preserves_globals ge j.
+Proof.
+ intros. destruct H as (bound & A & B). destruct B.
+ split; [|split]; intros.
+- eauto.
+- eauto.
+- symmetry; eauto.
+Qed.
+
+Lemma globalenv_inject_incr:
+ forall j m0 (F V: Type) (ge: Genv.t F V) m j' P,
+ inject_incr j j' ->
+ inject_separated j j' m0 m ->
+ m |= globalenv_inject ge j ** P ->
+ m |= globalenv_inject ge j' ** P.
+Proof.
+ intros. destruct H1 as (A & B & C). destruct A as (bound & D & E).
+ split; [|split]; auto.
+ exists bound; split; auto.
+ inv E; constructor; intros.
+- eauto.
+- destruct (j b1) as [[b0 delta0]|] eqn:JB1.
++ erewrite H in H1 by eauto. inv H1. eauto.
++ exploit H0; eauto. intros (X & Y). elim Y. apply Plt_le_trans with bound; auto.
+- eauto.
+- eauto.
+- eauto.
+Qed.
+
+Lemma external_call_parallel_rule:
+ forall (F V: Type) ef (ge: Genv.t F V) vargs1 m1 t vres1 m1' m2 j P vargs2,
+ external_call ef ge vargs1 m1 t vres1 m1' ->
+ m2 |= minjection j m1 ** globalenv_inject ge j ** P ->
+ Val.inject_list j vargs1 vargs2 ->
+ exists j' vres2 m2',
+ external_call ef ge vargs2 m2 t vres2 m2'
+ /\ Val.inject j' vres1 vres2
+ /\ m2' |= minjection j' m1' ** globalenv_inject ge j' ** P
+ /\ inject_incr j j'
+ /\ inject_separated j j' m1 m2.
+Proof.
+ intros until vargs2; intros CALL SEP ARGS.
+ destruct SEP as (A & B & C). simpl in A.
+ exploit external_call_mem_inject; eauto.
+ eapply globalenv_inject_preserves_globals. eapply sep_pick1; eauto.
+ intros (j' & vres2 & m2' & CALL' & RES & INJ' & UNCH1 & UNCH2 & INCR & ISEP).
+ assert (MAXPERMS: forall b ofs p,
+ Mem.valid_block m1 b -> Mem.perm m1' b ofs Max p -> Mem.perm m1 b ofs Max p).
+ { intros. eapply external_call_max_perm; eauto. }
+ exists j', vres2, m2'; intuition auto.
+ split; [|split].
+- exact INJ'.
+- apply m_invar with (m0 := m2).
++ apply globalenv_inject_incr with j m1; auto.
++ eapply Mem.unchanged_on_implies; eauto.
+ intros; red; intros; red; intros.
+ eelim C; eauto. simpl. exists b0, delta; auto.
+- red; intros. destruct H as (b0 & delta & J' & E).
+ destruct (j b0) as [[b' delta'] | ] eqn:J.
++ erewrite INCR in J' by eauto. inv J'.
+ eelim C; eauto. simpl. exists b0, delta; split; auto. apply MAXPERMS; auto.
+ eapply Mem.valid_block_inject_1; eauto.
++ exploit ISEP; eauto. intros (X & Y). elim Y. eapply m_valid; eauto.
+Qed.
+
+Lemma alloc_parallel_rule_2:
+ forall (F V: Type) (ge: Genv.t F V) m1 sz1 m1' b1 m2 sz2 m2' b2 P j lo hi delta,
+ m2 |= minjection j m1 ** globalenv_inject ge j ** P ->
+ Mem.alloc m1 0 sz1 = (m1', b1) ->
+ Mem.alloc m2 0 sz2 = (m2', b2) ->
+ (8 | delta) ->
+ lo = delta ->
+ hi = delta + Zmax 0 sz1 ->
+ 0 <= sz2 <= Int.max_unsigned ->
+ 0 <= delta -> hi <= sz2 ->
+ exists j',
+ m2' |= range b2 0 lo ** range b2 hi sz2 ** minjection j' m1' ** globalenv_inject ge j' ** P
+ /\ inject_incr j j'
+ /\ j' b1 = Some(b2, delta).
+Proof.
+ intros.
+ set (j1 := fun b => if eq_block b b1 then Some(b2, delta) else j b).
+ assert (X: inject_incr j j1).
+ { unfold j1; red; intros. destruct (eq_block b b1); auto.
+ subst b. eelim Mem.fresh_block_alloc. eexact H0.
+ eapply Mem.valid_block_inject_1. eauto. apply sep_proj1 in H. eexact H. }
+ assert (Y: inject_separated j j1 m1 m2).
+ { unfold j1; red; intros. destruct (eq_block b0 b1).
+ - inversion H9; clear H9; subst b3 delta0 b0. split; eapply Mem.fresh_block_alloc; eauto.
+ - congruence. }
+ rewrite sep_swap in H. eapply globalenv_inject_incr with (j' := j1) in H; eauto. rewrite sep_swap in H.
+ clear X Y.
+ exploit alloc_parallel_rule; eauto.
+ intros (j' & A & B & C & D).
+ exists j'; split; auto.
+ rewrite sep_swap4 in A. rewrite sep_swap4. apply globalenv_inject_incr with j1 m1; auto.
+- red; unfold j1; intros. destruct (eq_block b b1). congruence. rewrite D; auto.
+- red; unfold j1; intros. destruct (eq_block b0 b1). congruence. rewrite D in H9 by auto. congruence.
+Qed.