(** * Memory injections *)

(** A memory state [m1] injects into another memory state [m2] via the
  memory injection [f] if the following conditions hold:
- each access in [m2] that corresponds to a valid access in [m1]
  is itself valid;
- the memory value associated in [m1] to an accessible address
  must inject into [m2]'s memory value at the corersponding address;
- unallocated blocks in [m1] must be mapped to [None] by [f];
- if [f b = Some(b', delta)], [b'] must be valid in [m2];
- distinct blocks in [m1] are mapped to non-overlapping sub-blocks in [m2];
- the sizes of [m2]'s blocks are representable with unsigned machine integers;
- pointers that could be represented using unsigned machine integers remain
  representable after the injection.
*)

Record inject' (f: meminj) (m1 m2: mem) : Prop :=
  mk_inject {
    mi_inj:
      mem_inj 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_no_overlap:
      meminj_no_overlap f m1;
    mi_representable:
      forall b b' delta ofs,
      f b = Some(b', delta) ->
      perm m1 b (Ptrofs.unsigned ofs) Max Nonempty \/ perm m1 b (Ptrofs.unsigned ofs - 1) Max Nonempty ->
      delta >= 0 /\ 0 <= Ptrofs.unsigned ofs + delta <= Ptrofs.max_unsigned;
    mi_perm_inv:
      forall b1 ofs b2 delta k p,
      f b1 = Some(b2, delta) ->
      perm m2 b2 (ofs + delta) k p ->
      perm m1 b1 ofs k p \/ ~perm m1 b1 ofs Max Nonempty
  }.(** * Memory injections *)

(** A memory state [m1] injects into another memory state [m2] via the
  memory injection [f] if the following conditions hold:
- each access in [m2] that corresponds to a valid access in [m1]
  is itself valid;
- the memory value associated in [m1] to an accessible address
  must inject into [m2]'s memory value at the corersponding address;
- unallocated blocks in [m1] must be mapped to [None] by [f];
- if [f b = Some(b', delta)], [b'] must be valid in [m2];
- distinct blocks in [m1] are mapped to non-overlapping sub-blocks in [m2];
- the sizes of [m2]'s blocks are representable with unsigned machine integers;
- pointers that could be represented using unsigned machine integers remain
  representable after the injection.
*)

Record inject' (f: meminj) (m1 m2: mem) : Prop :=
  mk_inject {
    mi_inj:
      mem_inj 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_no_overlap:
      meminj_no_overlap f m1;
    mi_representable:
      forall b b' delta ofs,
      f b = Some(b', delta) ->
      perm m1 b (Ptrofs.unsigned ofs) Max Nonempty \/ perm m1 b (Ptrofs.unsigned ofs - 1) Max Nonempty ->
      delta >= 0 /\ 0 <= Ptrofs.unsigned ofs + delta <= Ptrofs.max_unsigned;
    mi_perm_inv:
      forall b1 ofs b2 delta k p,
      f b1 = Some(b2, delta) ->
      perm m2 b2 (ofs + delta) k p ->
      perm m1 b1 ofs k p \/ ~perm m1 b1 ofs Max Nonempty
  }.(** * Memory injections *)

(** A memory state [m1] injects into another memory state [m2] via the
  memory injection [f] if the following conditions hold:
- each access in [m2] that corresponds to a valid access in [m1]
  is itself valid;
- the memory value associated in [m1] to an accessible address
  must inject into [m2]'s memory value at the corersponding address;
- unallocated blocks in [m1] must be mapped to [None] by [f];
- if [f b = Some(b', delta)], [b'] must be valid in [m2];
- distinct blocks in [m1] are mapped to non-overlapping sub-blocks in [m2];
- the sizes of [m2]'s blocks are representable with unsigned machine integers;
- pointers that could be represented using unsigned machine integers remain
  representable after the injection.
*)

Record inject' (f: meminj) (m1 m2: mem) : Prop :=
  mk_inject {
    mi_inj:
      mem_inj 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_no_overlap:
      meminj_no_overlap f m1;
    mi_representable:
      forall b b' delta ofs,
      f b = Some(b', delta) ->
      perm m1 b (Ptrofs.unsigned ofs) Max Nonempty \/ perm m1 b (Ptrofs.unsigned ofs - 1) Max Nonempty ->
      delta >= 0 /\ 0 <= Ptrofs.unsigned ofs + delta <= Ptrofs.max_unsigned;
    mi_perm_inv:
      forall b1 ofs b2 delta k p,
      f b1 = Some(b2, delta) ->
      perm m2 b2 (ofs + delta) k p ->
      perm m1 b1 ofs k p \/ ~perm m1 b1 ofs Max Nonempty
  }.(** * Memory injections *)

(** A memory state [m1] injects into another memory state [m2] via the
  memory injection [f] if the following conditions hold:
- each access in [m2] that corresponds to a valid access in [m1]
  is itself valid;
- the memory value associated in [m1] to an accessible address
  must inject into [m2]'s memory value at the corersponding address;
- unallocated blocks in [m1] must be mapped to [None] by [f];
- if [f b = Some(b', delta)], [b'] must be valid in [m2];
- distinct blocks in [m1] are mapped to non-overlapping sub-blocks in [m2];
- the sizes of [m2]'s blocks are representable with unsigned machine integers;
- pointers that could be represented using unsigned machine integers remain
  representable after the injection.
*)

Record inject' (f: meminj) (m1 m2: mem) : Prop :=
  mk_inject {
    mi_inj:
      mem_inj 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_no_overlap:
      meminj_no_overlap f m1;
    mi_representable:
      forall b b' delta ofs,
      f b = Some(b', delta) ->
      perm m1 b (Ptrofs.unsigned ofs) Max Nonempty \/ perm m1 b (Ptrofs.unsigned ofs - 1) Max Nonempty ->
      delta >= 0 /\ 0 <= Ptrofs.unsigned ofs + delta <= Ptrofs.max_unsigned;
    mi_perm_inv:
      forall b1 ofs b2 delta k p,
      f b1 = Some(b2, delta) ->
      perm m2 b2 (ofs + delta) k p ->
      perm m1 b1 ofs k p \/ ~perm m1 b1 ofs Max Nonempty
  }.(** * Memory injections *)

(** A memory state [m1] injects into another memory state [m2] via the
  memory injection [f] if the following conditions hold:
- each access in [m2] that corresponds to a valid access in [m1]
  is itself valid;
- the memory value associated in [m1] to an accessible address
  must inject into [m2]'s memory value at the corersponding address;
- unallocated blocks in [m1] must be mapped to [None] by [f];
- if [f b = Some(b', delta)], [b'] must be valid in [m2];
- distinct blocks in [m1] are mapped to non-overlapping sub-blocks in [m2];
- the sizes of [m2]'s blocks are representable with unsigned machine integers;
- pointers that could be represented using unsigned machine integers remain
  representable after the injection.
*)

Record inject' (f: meminj) (m1 m2: mem) : Prop :=
  mk_inject {
    mi_inj:
      mem_inj 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_no_overlap:
      meminj_no_overlap f m1;
    mi_representable:
      forall b b' delta ofs,
      f b = Some(b', delta) ->
      perm m1 b (Ptrofs.unsigned ofs) Max Nonempty \/ perm m1 b (Ptrofs.unsigned ofs - 1) Max Nonempty ->
      delta >= 0 /\ 0 <= Ptrofs.unsigned ofs + delta <= Ptrofs.max_unsigned;
    mi_perm_inv:
      forall b1 ofs b2 delta k p,
      f b1 = Some(b2, delta) ->
      perm m2 b2 (ofs + delta) k p ->
      perm m1 b1 ofs k p \/ ~perm m1 b1 ofs Max Nonempty
  }.(** * Memory injections *)

(** A memory state [m1] injects into another memory state [m2] via the
  memory injection [f] if the following conditions hold:
- each access in [m2] that corresponds to a valid access in [m1]
  is itself valid;
- the memory value associated in [m1] to an accessible address
  must inject into [m2]'s memory value at the corersponding address;
- unallocated blocks in [m1] must be mapped to [None] by [f];
- if [f b = Some(b', delta)], [b'] must be valid in [m2];
- distinct blocks in [m1] are mapped to non-overlapping sub-blocks in [m2];
- the sizes of [m2]'s blocks are representable with unsigned machine integers;
- pointers that could be represented using unsigned machine integers remain
  representable after the injection.
*)

Record inject' (f: meminj) (m1 m2: mem) : Prop :=
  mk_inject {
    mi_inj:
      mem_inj 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_no_overlap:
      meminj_no_overlap f m1;
    mi_representable:
      forall b b' delta ofs,
      f b = Some(b', delta) ->
      perm m1 b (Ptrofs.unsigned ofs) Max Nonempty \/ perm m1 b (Ptrofs.unsigned ofs - 1) Max Nonempty ->
      delta >= 0 /\ 0 <= Ptrofs.unsigned ofs + delta <= Ptrofs.max_unsigned;
    mi_perm_inv:
      forall b1 ofs b2 delta k p,
      f b1 = Some(b2, delta) ->
      perm m2 b2 (ofs + delta) k p ->
      perm m1 b1 ofs k p \/ ~perm m1 b1 ofs Max Nonempty
  }.(** * Memory injections *)

(** A memory state [m1] injects into another memory state [m2] via the
  memory injection [f] if the following conditions hold:
- each access in [m2] that corresponds to a valid access in [m1]
  is itself valid;
- the memory value associated in [m1] to an accessible address
  must inject into [m2]'s memory value at the corersponding address;
- unallocated blocks in [m1] must be mapped to [None] by [f];
- if [f b = Some(b', delta)], [b'] must be valid in [m2];
- distinct blocks in [m1] are mapped to non-overlapping sub-blocks in [m2];
- the sizes of [m2]'s blocks are representable with unsigned machine integers;
- pointers that could be represented using unsigned machine integers remain
  representable after the injection.
*)

Record inject' (f: meminj) (m1 m2: mem) : Prop :=
  mk_inject {
    mi_inj:
      mem_inj 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_no_overlap:
      meminj_no_overlap f m1;
    mi_representable:
      forall b b' delta ofs,
      f b = Some(b', delta) ->
      perm m1 b (Ptrofs.unsigned ofs) Max Nonempty \/ perm m1 b (Ptrofs.unsigned ofs - 1) Max Nonempty ->
      delta >= 0 /\ 0 <= Ptrofs.unsigned ofs + delta <= Ptrofs.max_unsigned;
    mi_perm_inv:
      forall b1 ofs b2 delta k p,
      f b1 = Some(b2, delta) ->
      perm m2 b2 (ofs + delta) k p ->
      perm m1 b1 ofs k p \/ ~perm m1 b1 ofs Max Nonempty
  }.(** * Memory injections *)

(** A memory state [m1] injects into another memory state [m2] via the
  memory injection [f] if the following conditions hold:
- each access in [m2] that corresponds to a valid access in [m1]
  is itself valid;
- the memory value associated in [m1] to an accessible address
  must inject into [m2]'s memory value at the corersponding address;
- unallocated blocks in [m1] must be mapped to [None] by [f];
- if [f b = Some(b', delta)], [b'] must be valid in [m2];
- distinct blocks in [m1] are mapped to non-overlapping sub-blocks in [m2];
- the sizes of [m2]'s blocks are representable with unsigned machine integers;
- pointers that could be represented using unsigned machine integers remain
  representable after the injection.
*)

Record inject' (f: meminj) (m1 m2: mem) : Prop :=
  mk_inject {
    mi_inj:
      mem_inj 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_no_overlap:
      meminj_no_overlap f m1;
    mi_representable:
      forall b b' delta ofs,
      f b = Some(b', delta) ->
      perm m1 b (Ptrofs.unsigned ofs) Max Nonempty \/ perm m1 b (Ptrofs.unsigned ofs - 1) Max Nonempty ->
      delta >= 0 /\ 0 <= Ptrofs.unsigned ofs + delta <= Ptrofs.max_unsigned;
    mi_perm_inv:
      forall b1 ofs b2 delta k p,
      f b1 = Some(b2, delta) ->
      perm m2 b2 (ofs + delta) k p ->
      perm m1 b1 ofs k p \/ ~perm m1 b1 ofs Max Nonempty
  }.Definition inject := inject'.

(* Specifications: *)
Theorem valid_block_inject_1: forall f m1 m2 b1 b2 delta, f b1 = Some(b2, delta) -> inject f m1 m2 -> valid_block m1 b1.
Theorem valid_block_inject_2: forall f m1 m2 b1 b2 delta, f b1 = Some(b2, delta) -> inject f m1 m2 -> valid_block m2 b2.
Theorem perm_inject: forall f m1 m2 b1 b2 delta ofs k p, f b1 = Some(b2, delta) -> inject f m1 m2 -> perm m1 b1 ofs k p -> perm m2 b2 (ofs + delta) k p.
Theorem perm_inject_inv: forall f m1 m2 b1 ofs b2 delta k p, inject f m1 m2 -> f b1 = Some(b2, delta) -> perm m2 b2 (ofs + delta) k p -> perm m1 b1 ofs k p \/ ~perm m1 b1 ofs Max Nonempty.
Theorem range_perm_inject: forall f m1 m2 b1 b2 delta lo hi k p, f b1 = Some(b2, delta) -> inject f m1 m2 -> range_perm m1 b1 lo hi k p -> range_perm m2 b2 (lo + delta) (hi + delta) k p.
Theorem valid_access_inject: forall f m1 m2 chunk b1 ofs b2 delta p, f b1 = Some(b2, delta) -> inject f m1 m2 -> valid_access m1 chunk b1 ofs p -> valid_access m2 chunk b2 (ofs + delta) p.
Theorem valid_pointer_inject: forall f m1 m2 b1 ofs b2 delta, f b1 = Some(b2, delta) -> inject f m1 m2 -> valid_pointer m1 b1 ofs = true -> valid_pointer m2 b2 (ofs + delta) = true.
Theorem weak_valid_pointer_inject: forall f m1 m2 b1 ofs b2 delta, f b1 = Some(b2, delta) -> inject f m1 m2 -> weak_valid_pointer m1 b1 ofs = true -> weak_valid_pointer m2 b2 (ofs + delta) = true.
Lemma address_inject: forall f m1 m2 b1 ofs1 b2 delta p, inject f m1 m2 -> perm m1 b1 (Ptrofs.unsigned ofs1) Cur p -> f b1 = Some (b2, delta) -> Ptrofs.unsigned (Ptrofs.add ofs1 (Ptrofs.repr delta)) = Ptrofs.unsigned ofs1 + delta.
Lemma address_inject': forall f m1 m2 chunk b1 ofs1 b2 delta, inject f m1 m2 -> valid_access m1 chunk b1 (Ptrofs.unsigned ofs1) Nonempty -> f b1 = Some (b2, delta) -> Ptrofs.unsigned (Ptrofs.add ofs1 (Ptrofs.repr delta)) = Ptrofs.unsigned ofs1 + delta.
Theorem weak_valid_pointer_inject_no_overflow: forall f m1 m2 b ofs b' delta, inject f m1 m2 -> weak_valid_pointer m1 b (Ptrofs.unsigned ofs) = true -> f b = Some(b', delta) -> 0 <= Ptrofs.unsigned ofs + Ptrofs.unsigned (Ptrofs.repr delta) <= Ptrofs.max_unsigned.
Theorem valid_pointer_inject_no_overflow: forall f m1 m2 b ofs b' delta, inject f m1 m2 -> valid_pointer m1 b (Ptrofs.unsigned ofs) = true -> f b = Some(b', delta) -> 0 <= Ptrofs.unsigned ofs + Ptrofs.unsigned (Ptrofs.repr delta) <= Ptrofs.max_unsigned.
Theorem valid_pointer_inject_val: forall f m1 m2 b ofs b' ofs', inject f m1 m2 -> valid_pointer m1 b (Ptrofs.unsigned ofs) = true -> Val.inject f (Vptr b ofs) (Vptr b' ofs') -> valid_pointer m2 b' (Ptrofs.unsigned ofs') = true.
Theorem weak_valid_pointer_inject_val: forall f m1 m2 b ofs b' ofs', inject f m1 m2 -> weak_valid_pointer m1 b (Ptrofs.unsigned ofs) = true -> Val.inject f (Vptr b ofs) (Vptr b' ofs') -> weak_valid_pointer m2 b' (Ptrofs.unsigned ofs') = true.
Theorem inject_no_overlap: forall f m1 m2 b1 b2 b1' b2' delta1 delta2 ofs1 ofs2, inject 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.
Theorem different_pointers_inject: forall f m m' b1 ofs1 b2 ofs2 b1' delta1 b2' delta2, inject f m m' -> b1 <> b2 -> valid_pointer m b1 (Ptrofs.unsigned ofs1) = true -> valid_pointer m b2 (Ptrofs.unsigned ofs2) = true -> f b1 = Some (b1', delta1) -> f b2 = Some (b2', delta2) -> b1' <> b2' \/ Ptrofs.unsigned (Ptrofs.add ofs1 (Ptrofs.repr delta1)) <> Ptrofs.unsigned (Ptrofs.add ofs2 (Ptrofs.repr delta2)).
Theorem disjoint_or_equal_inject: forall f m m' b1 b1' delta1 b2 b2' delta2 ofs1 ofs2 sz, inject 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.
Theorem load_inject: forall f m1 m2 chunk b1 ofs b2 delta v1, inject 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.inject f v1 v2.
Theorem loadv_inject: forall f m1 m2 chunk a1 a2 v1, inject f m1 m2 -> loadv chunk m1 a1 = Some v1 -> Val.inject f a1 a2 -> exists v2, loadv chunk m2 a2 = Some v2 /\ Val.inject f v1 v2.
Theorem loadbytes_inject: forall f m1 m2 b1 ofs len b2 delta bytes1, inject 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 f) bytes1 bytes2.
Theorem store_mapped_inject: forall f chunk m1 b1 ofs v1 n1 m2 b2 delta v2, inject f m1 m2 -> store chunk m1 b1 ofs v1 = Some n1 -> f b1 = Some (b2, delta) -> Val.inject f v1 v2 -> exists n2, store chunk m2 b2 (ofs + delta) v2 = Some n2 /\ inject f n1 n2.
Theorem store_unmapped_inject: forall f chunk m1 b1 ofs v1 n1 m2, inject f m1 m2 -> store chunk m1 b1 ofs v1 = Some n1 -> f b1 = None -> inject f n1 m2.
Theorem store_outside_inject: forall f m1 m2 chunk b ofs v m2', inject 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 f m1 m2'.
Theorem storev_mapped_inject: forall f chunk m1 a1 v1 n1 m2 a2 v2, inject f m1 m2 -> storev chunk m1 a1 v1 = Some n1 -> Val.inject f a1 a2 -> Val.inject f v1 v2 -> exists n2, storev chunk m2 a2 v2 = Some n2 /\ inject f n1 n2.
Theorem storebytes_mapped_inject: forall f m1 b1 ofs bytes1 n1 m2 b2 delta bytes2, inject f m1 m2 -> storebytes m1 b1 ofs bytes1 = Some n1 -> f b1 = Some (b2, delta) -> list_forall2 (memval_inject f) bytes1 bytes2 -> exists n2, storebytes m2 b2 (ofs + delta) bytes2 = Some n2 /\ inject f n1 n2.
Theorem storebytes_unmapped_inject: forall f m1 b1 ofs bytes1 n1 m2, inject f m1 m2 -> storebytes m1 b1 ofs bytes1 = Some n1 -> f b1 = None -> inject f n1 m2.
Theorem storebytes_outside_inject: forall f m1 m2 b ofs bytes2 m2', inject 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 f m1 m2'.
Theorem storebytes_empty_inject: forall f m1 b1 ofs1 m1' m2 b2 ofs2 m2', inject f m1 m2 -> storebytes m1 b1 ofs1 nil = Some m1' -> storebytes m2 b2 ofs2 nil = Some m2' -> inject f m1' m2'.
Theorem alloc_right_inject: forall f m1 m2 lo hi b2 m2', inject f m1 m2 -> alloc m2 lo hi = (m2', b2) -> inject f m1 m2'.
Theorem alloc_left_unmapped_inject: forall f m1 m2 lo hi m1' b1, inject f m1 m2 -> alloc m1 lo hi = (m1', b1) -> exists f', inject f' m1' m2 /\ inject_incr f f' /\ f' b1 = None /\ (forall b, b <> b1 -> f' b = f b).
Theorem alloc_left_mapped_inject: forall f m1 m2 lo hi m1' b1 b2 delta, inject f m1 m2 -> alloc m1 lo hi = (m1', b1) -> valid_block m2 b2 -> 0 <= delta <= Ptrofs.max_unsigned -> (forall ofs k p, perm m2 b2 ofs k p -> delta = 0 \/ 0 <= ofs < Ptrofs.max_unsigned) -> (forall ofs k p, lo <= ofs < hi -> perm m2 b2 (ofs + delta) k p) -> inj_offset_aligned delta (hi-lo) -> (forall b delta' ofs k p, f b = Some (b2, delta') -> perm m1 b ofs k p -> lo + delta <= ofs + delta' < hi + delta -> False) -> exists f', inject f' m1' m2 /\ inject_incr f f' /\ f' b1 = Some(b2, delta) /\ (forall b, b <> b1 -> f' b = f b).
Theorem alloc_parallel_inject: forall f m1 m2 lo1 hi1 m1' b1 lo2 hi2, inject f m1 m2 -> alloc m1 lo1 hi1 = (m1', b1) -> lo2 <= lo1 -> hi1 <= hi2 -> exists f', exists m2', exists b2, alloc m2 lo2 hi2 = (m2', b2) /\ inject f' m1' m2' /\ inject_incr f f' /\ f' b1 = Some(b2, 0) /\ (forall b, b <> b1 -> f' b = f b).
Lemma free_left_inject: forall f m1 m2 b lo hi m1', inject f m1 m2 -> free m1 b lo hi = Some m1' -> inject f m1' m2.
Lemma free_list_left_inject: forall f m2 l m1 m1', inject f m1 m2 -> free_list m1 l = Some m1' -> inject f m1' m2.
Lemma free_right_inject: forall f m1 m2 b lo hi m2', inject f m1 m2 -> free m2 b lo hi = Some m2' -> (forall b1 delta ofs k p, f b1 = Some(b, delta) -> perm m1 b1 ofs k p -> lo <= ofs + delta < hi -> False) -> inject f m1 m2'.
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).
Theorem free_inject: forall f m1 l m1' m2 b lo hi m2', inject f m1 m2 -> free_list m1 l = Some m1' -> free m2 b lo hi = Some m2' -> (forall b1 delta ofs k p, f b1 = Some(b, delta) -> perm m1 b1 ofs k p -> lo <= ofs + delta < hi -> exists lo1, exists hi1, In (b1, lo1, hi1) l /\ lo1 <= ofs < hi1) -> inject f m1' m2'.
Theorem free_parallel_inject: forall f m1 m2 b lo hi m1' b' delta, inject f m1 m2 -> free m1 b lo hi = Some m1' -> f b = Some(b', delta) -> exists m2', free m2 b' (lo + delta) (hi + delta) = Some m2' /\ inject f m1' m2'.
Lemma drop_outside_inject: forall f m1 m2 b lo hi p m2', inject 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 f m1 m2'.
Lemma mem_inj_compose: forall f f' m1 m2 m3, mem_inj f m1 m2 -> mem_inj f' m2 m3 -> mem_inj (compose_meminj f f') m1 m3.
Theorem inject_compose: forall f f' m1 m2 m3, inject f m1 m2 -> inject f' m2 m3 -> inject (compose_meminj f f') m1 m3.
Lemma val_lessdef_inject_compose: forall f v1 v2 v3, Val.lessdef v1 v2 -> Val.inject f v2 v3 -> Val.inject f v1 v3.
Lemma val_inject_lessdef_compose: forall f v1 v2 v3, Val.inject f v1 v2 -> Val.lessdef v2 v3 -> Val.inject f v1 v3.
Lemma extends_inject_compose: forall f m1 m2 m3, extends m1 m2 -> inject f m2 m3 -> inject f m1 m3.
Lemma inject_extends_compose: forall f m1 m2 m3, inject f m1 m2 -> extends m2 m3 -> inject f m1 m3.
Lemma extends_extends_compose: forall m1 m2 m3, extends m1 m2 -> extends m2 m3 -> extends m1 m3.