(* *********************************************************************)
(*                                                                     *)
(*              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. *)
(** * Encoding and decoding values *)
Definition inj_bytes (bl: list byte) : list memval :=
List.map Byte bl.
Fixpoint proj_bytes (vl: list memval) : option (list byte) :=
match vl with
| nil => Some nil
| Byte b :: vl' =>
match proj_bytes vl' with None => None | Some bl => Some(b :: bl) end
| _ => None
end.
Remark length_inj_bytes:
forall bl, length (inj_bytes bl) = length bl.
Remark proj_inj_bytes:
forall bl, proj_bytes (inj_bytes bl) = Some bl.
Fixpoint inj_value_rec (n: nat) (v: val) (q: quantity) {struct n}: list memval :=
match n with
| O => nil
| S m => Fragment v q m :: inj_value_rec m v q
end.
Definition inj_value (q: quantity) (v: val): list memval :=
inj_value_rec (size_quantity_nat q) v q.
Fixpoint check_value (n: nat) (v: val) (q: quantity) (vl: list memval)
{struct n} : bool :=
match n, vl with
| O, nil => true
| S m, Fragment v' q' m' :: vl' =>
Val.eq v v' && quantity_eq q q' && Nat.eqb m m' && check_value m v q vl'
| _, _ => false
end.
Definition proj_value (q: quantity) (vl: list memval) : val :=
match vl with
| Fragment v q' n :: vl' =>
if check_value (size_quantity_nat q) v q vl then v else Vundef
| _ => Vundef
end.
Definition encode_val (chunk: memory_chunk) (v: val) : list memval :=
match v, chunk with
| Vint n, (Mint8signed | Mint8unsigned) => inj_bytes (encode_int 1%nat (Int.unsigned n))
| Vint n, (Mint16signed | Mint16unsigned) => inj_bytes (encode_int 2%nat (Int.unsigned n))
| Vint n, Mint32 => inj_bytes (encode_int 4%nat (Int.unsigned n))
| Vptr b ofs, Mint32 => if Archi.ptr64 then list_repeat 4%nat Undef else inj_value Q32 v
| Vlong n, Mint64 => inj_bytes (encode_int 8%nat (Int64.unsigned n))
| Vptr b ofs, Mint64 => if Archi.ptr64 then inj_value Q64 v else list_repeat 8%nat Undef
| Vsingle n, Mfloat32 => inj_bytes (encode_int 4%nat (Int.unsigned (Float32.to_bits n)))
| Vfloat n, Mfloat64 => inj_bytes (encode_int 8%nat (Int64.unsigned (Float.to_bits n)))
| _, Many32 => inj_value Q32 v
| _, Many64 => inj_value Q64 v
| _, _ => list_repeat (size_chunk_nat chunk) Undef
end.
Definition decode_val (chunk: memory_chunk) (vl: list memval) : val :=
match proj_bytes vl with
| Some bl =>
match chunk with
| Mint8signed => Vint(Int.sign_ext 8 (Int.repr (decode_int bl)))
| Mint8unsigned => Vint(Int.zero_ext 8 (Int.repr (decode_int bl)))
| Mint16signed => Vint(Int.sign_ext 16 (Int.repr (decode_int bl)))
| Mint16unsigned => Vint(Int.zero_ext 16 (Int.repr (decode_int bl)))
| Mint32 => Vint(Int.repr(decode_int bl))
| Mint64 => Vlong(Int64.repr(decode_int bl))
| Mfloat32 => Vsingle(Float32.of_bits (Int.repr (decode_int bl)))
| Mfloat64 => Vfloat(Float.of_bits (Int64.repr (decode_int bl)))
| Many32 => Vundef
| Many64 => Vundef
end
| None =>
match chunk with
| Mint32 => if Archi.ptr64 then Vundef else Val.load_result chunk (proj_value Q32 vl)
| Many32 => Val.load_result chunk (proj_value Q32 vl)
| Mint64 => if Archi.ptr64 then Val.load_result chunk (proj_value Q64 vl) else Vundef
| Many64 => Val.load_result chunk (proj_value Q64 vl)
| _ => Vundef
end
end.
Ltac solve_encode_val_length :=
match goal with
| [ |- length (inj_bytes _) = _ ] => rewrite length_inj_bytes; solve_encode_val_length
| [ |- length (encode_int _ _) = _ ] => apply encode_int_length
| [ |- length (if ?x then _ else _) = _ ] => destruct x eqn:?; solve_encode_val_length
| _ => reflexivity
end.

Lemma encode_val_length:
forall chunk v, length(encode_val chunk v) = size_chunk_nat chunk.