(* *********************************************************************)
(*                                                                     *)
(*              The Compcert verified compiler                         *)
(*                                                                     *)
(*          Xavier Leroy, INRIA Paris-Rocquencourt                     *)
(*          Sandrine Blazy, ENSIIE and INRIA Paris-Rocquencourt        *)
(*                                                                     *)
(*  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.     *)
(*                                                                     *)
(* *********************************************************************)
(** In-memory representation of values. *)
Require Archi.
(** * Properties of memory chunks *)
(** Memory reads and writes are performed by quantities called memory chunks, encoding the type, size and signedness of the chunk being addressed. The following functions extract the size information from a chunk. *)
Definition size_chunk (chunk: memory_chunk) : Z :=
match chunk with
| Mint8signed => 1
| Mint8unsigned => 1
| Mint16signed => 2
| Mint16unsigned => 2
| Mint32 => 4
| Mint64 => 8
| Mfloat32 => 4
| Mfloat64 => 8
| Many32 => 4
| Many64 => 8
end.
Definition size_chunk_nat (chunk: memory_chunk) : nat :=
Z.to_nat(size_chunk chunk).
(** Memory reads and writes must respect alignment constraints: the byte offset of the location being addressed should be an exact multiple of the natural alignment for the chunk being addressed. This natural alignment is defined by the following [align_chunk] function.  Some target architectures (e.g. PowerPC and x86) have no alignment constraints, which we could reflect by taking [align_chunk chunk = 1].  However, other architectures have stronger alignment requirements.  The following definition is appropriate for PowerPC, ARM and x86. *)
Definition align_chunk (chunk: memory_chunk) : Z :=
match chunk with
| Mint8signed => 1
| Mint8unsigned => 1
| Mint16signed => 2
| Mint16unsigned => 2
| Mint32 => 4
| Mint64 => 8
| Mfloat32 => 4
| Mfloat64 => 4
| Many32 => 4
| Many64 => 4
end.
Inductive quantity : Type := Q32 | Q64.
Definition quantity_eq (q1 q2: quantity) : {q1 = q2} + {q1 <> q2}.
(** * Memory values *)
(** A ``memory value'' is a byte-sized quantity that describes the current content of a memory cell.  It can be either: - a concrete 8-bit integer; - a byte-sized fragment of an opaque value; - the special constant [Undef] that represents uninitialized memory. *)
(** Values stored in memory cells. *)
Inductive memval: Type :=
| Undef: memval
| Byte: byte -> memval
| Fragment: val -> quantity -> nat -> memval.
(** * Encoding and decoding integers *)
(** We define functions to convert between integers and lists of bytes of a given length *)
Fixpoint bytes_of_int (n: nat) (x: Z) {struct n}: list byte :=
match n with
| O => nil
| S m => Byte.repr x :: bytes_of_int m (x / 256)
end.
Fixpoint int_of_bytes (l: list byte): Z :=
match l with
| nil => 0
| b :: l' => Byte.unsigned b + int_of_bytes l' * 256
end.
Definition rev_if_be (l: list byte) : list byte :=
if Archi.big_endian then List.rev l else l.
Definition encode_int (sz: nat) (x: Z) : list byte :=
rev_if_be (bytes_of_int sz x).
Definition decode_int (b: list byte) : Z :=
int_of_bytes (rev_if_be b).
(** Length properties *)
(** Decoding after encoding *)
(** A length-[n] encoding depends only on the low [8*n] bits of the integer. *)

Lemma bytes_of_int_mod:
forall n x y,
eqmod (two_p (Z.of_nat n * 8)) x y ->
bytes_of_int n x = bytes_of_int n y.