Module Type MEM.
(** The abstract type of memory states. *)
Parameter mem: Type.
(** * Operations on memory states *)
(** [empty] is the initial memory state. *)
Parameter empty: mem.
(** [alloc m lo hi] allocates a fresh block of size [hi - lo] bytes. Valid offsets in this block are between [lo] included and [hi] excluded. These offsets are writable in the returned memory state. This block is not initialized: its contents are initially undefined. Returns a pair [(m', b)] of the updated memory state [m'] and the identifier [b] of the newly-allocated block. Note that [alloc] never fails: we are modeling an infinite memory. *)
Parameter alloc: forall (m: mem) (lo hi: Z), mem * block.
(** [free m b lo hi] frees (deallocates) the range of offsets from [lo] included to [hi] excluded in block [b].  Returns the updated memory state, or [None] if the freed addresses are not freeable. *)
Parameter free: forall (m: mem) (b: block) (lo hi: Z), option mem.
(** [load chunk m b ofs] reads a memory quantity [chunk] from addresses [b, ofs] to [b, ofs + size_chunk chunk - 1] in memory state [m].  Returns the value read, or [None] if the accessed addresses are not readable. *)
Parameter load: forall (chunk: memory_chunk) (m: mem) (b: block) (ofs: Z), option val.
(** [store chunk m b ofs v] writes value [v] as memory quantity [chunk] from addresses [b, ofs] to [b, ofs + size_chunk chunk - 1] in memory state [m].  Returns the updated memory state, or [None] if the accessed addresses are not writable. *)
Parameter store: forall (chunk: memory_chunk) (m: mem) (b: block) (ofs: Z) (v: val), option mem.
(** [loadv] and [storev] are variants of [load] and [store] where the address being accessed is passed as a value (of the [Vptr] kind). *)
Definition loadv (chunk: memory_chunk) (m: mem) (addr: val) : option val :=
match addr with
| Vptr b ofs => load chunk m b (Ptrofs.unsigned ofs)
| _ => None
end.
Definition storev (chunk: memory_chunk) (m: mem) (addr v: val) : option mem :=
match addr with
| Vptr b ofs => store chunk m b (Ptrofs.unsigned ofs) v
| _ => None
end.
(** [loadbytes m b ofs n] reads and returns the byte-level representation of the values contained at offsets [ofs] to [ofs + n - 1] within block [b] in memory state [m]. [None] is returned if the accessed addresses are not readable. *)
Parameter loadbytes: forall (m: mem) (b: block) (ofs n: Z), option (list memval).
(** [storebytes m b ofs bytes] stores the given list of bytes [bytes] starting at location [(b, ofs)].  Returns updated memory state or [None] if the accessed locations are not writable. *)
Parameter storebytes: forall (m: mem) (b: block) (ofs: Z) (bytes: list memval), option mem.
(** [free_list] frees all the given (block, lo, hi) triples. *)
Fixpoint free_list (m: mem) (l: list (block * Z * Z)) {struct l}: option mem :=
match l with
| nil => Some m
| (b, lo, hi) :: l' =>
match free m b lo hi with
| None => None
| Some m' => free_list m' l'
end
end.
(** [drop_perm m b lo hi p] sets the permissions of the byte range [(b, lo) ... (b, hi - 1)] to [p].  These bytes must have [Freeable] permissions in the initial memory state [m]. Returns updated memory state, or [None] if insufficient permissions. *)
Parameter drop_perm: forall (m: mem) (b: block) (lo hi: Z) (p: permission), option mem.
(** * Permissions, block validity, access validity, and bounds *)
(** The next block of a memory state is the block identifier for the next allocation.  It increases by one at each allocation. Block identifiers below [nextblock] are said to be valid, meaning that they have been allocated previously.  Block identifiers above [nextblock] are fresh or invalid, i.e. not yet allocated.  Note that a block identifier remains valid after a [free] operation over this block. *)
Parameter nextblock: mem -> block.
Definition valid_block (m: mem) (b: block) := Plt b (nextblock m).
(** [perm m b ofs k p] holds if the address [b, ofs] in memory state [m] has permission [p]: one of freeable, writable, readable, and nonempty. If the address is empty, [perm m b ofs p] is false for all values of [p]. [k] is the kind of permission we are interested in: either the current permissions or the maximal permissions. *)
Parameter perm: forall (m: mem) (b: block) (ofs: Z) (k: perm_kind) (p: permission), Prop.
(** Logical implications between permissions *)
(** The current permission is always less than or equal to the maximal permission. *)
*)
(** [range_perm m b lo hi p] holds iff the addresses [b, lo] to [b, hi-1] all have permission [p] of kind [k]. *)
Definition range_perm (m: mem) (b: block) (lo hi: Z) (k: perm_kind) (p: permission) : Prop :=
forall ofs, lo <= ofs < hi -> perm m b ofs k p.
(** An access to a memory quantity [chunk] at address [b, ofs] with permission [p] is valid in [m] if the accessed addresses all have current permission [p] and moreover the offset is properly aligned. *)
Definition valid_access (m: mem) (chunk: memory_chunk) (b: block) (ofs: Z) (p: permission): Prop :=
range_perm m b ofs (ofs + size_chunk chunk) Cur p
/\ (align_chunk chunk | ofs).
(** [valid_pointer m b ofs] returns [true] if the address [b, ofs] is nonempty in [m] and [false] if it is empty. *)
Parameter valid_pointer: forall (m: mem) (b: block) (ofs: Z), bool.
(** C allows pointers one past the last element of an array.  These are not valid according to the previously defined [valid_pointer]. The property [weak_valid_pointer m b ofs] holds if address [b, ofs] is a valid pointer in [m], or a pointer one past a valid block in [m].  *)
Definition weak_valid_pointer (m: mem) (b: block) (ofs: Z) :=
valid_pointer m b ofs || valid_pointer m b (ofs - 1).
(** * Properties of the memory operations *)
(** ** Properties of the initial memory state. *)
(** ** Properties of [load]. *)
(** ** Properties of [loadbytes]. *)
(** [loadbytes] succeeds if and only if we have read permissions on the accessed memory area. *)
(** ** Properties of [store]. *)
(** [store] preserves block validity, permissions, access validity, and bounds. Moreover, a [store] succeeds if and only if the corresponding access is valid for writing. *)
(** Load-store properties. *)
(** Integrity of pointer values. *)
Definition compat_pointer_chunks (chunk1 chunk2: memory_chunk) : Prop :=
match chunk1, chunk2 with
| (Mint32 | Many32), (Mint32 | Many32) => True
| (Mint64 | Many64), (Mint64 | Many64) => True
| _, _ => False
end.
(** Load-store properties for [loadbytes]. *)
(** [store] is insensitive to the signedness or the high bits of small integer quantities. *)
(** ** Properties of [storebytes]. *)
(** [storebytes] preserves block validity, permissions, access validity, and bounds. Moreover, a [storebytes] succeeds if and only if we have write permissions on the addressed memory area. *)
(** Connections between [store] and [storebytes]. *)
(** Load-store properties. *)
(** Composing or decomposing [storebytes] operations at adjacent addresses. *)
(** ** Properties of [alloc]. *)
(** The identifier of the freshly allocated block is the next block of the initial memory state. *)
(** Effect of [alloc] on block validity. *)
(** Effect of [alloc] on permissions. *)
(** Effect of [alloc] on access validity. *)
(** Load-alloc properties. *)
(** ** Properties of [free]. *)
(** [free] succeeds if and only if the correspond range of addresses has [Freeable] current permission. *)
(** Block validity is preserved by [free]. *)
(** Effect of [free] on permissions. *)
(** Effect of [free] on access validity. *)
(** Load-free properties *)
(** ** Properties of [drop_perm]. *)
(** * Relating two memory states. *)
(** ** Memory extensions *)
(**  A store [m2] extends a store [m1] if [m2] can be obtained from [m1] by relaxing the permissions of [m1] (for instance, allocating larger blocks) and replacing some of the [Vundef] values stored in [m1] by more defined values stored in [m2] at the same addresses. *)
Parameter extends: mem -> mem -> Prop.
(** * Memory injections *)
(** A memory injection [f] is a function from addresses to either [None] or [Some] of an address and an offset.  It defines a correspondence between the blocks of two memory states [m1] and [m2]: - if [f b = None], the block [b] of [m1] has no equivalent in [m2]; - if [f b = Some(b', ofs)], the block [b] of [m2] corresponds to a sub-block at offset [ofs] of the block [b'] in [m2].  A memory injection [f] defines a relation [Val.inject] between values that is the identity for integer and float values, and relocates pointer values as prescribed by [f].  (See module [Values].)  Likewise, a memory injection [f] defines a relation between memory states that we now axiomatize. *)
Parameter inject: meminj -> mem -> mem -> Prop.

Axiom 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'.