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. *)

Axiom load_store_other:
forall chunk m1 b ofs v m2, store chunk m1 b ofs v = Some m2 ->
forall chunk' b' ofs',
b' <> b
\/ ofs' + size_chunk chunk' <= ofs
\/ ofs + size_chunk chunk <= ofs' ->
load chunk' m2 b' ofs' = load chunk' m1 b' ofs'.