(* *********************************************************************)
(*                                                                     *)
(*              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.
Remark in_inj_value:
forall mv v q, In mv (inj_value q v) -> exists n, mv = Fragment v q n.
Definition decode_encode_val (v1: val) (chunk1 chunk2: memory_chunk) (v2: val) : Prop :=
match v1, chunk1, chunk2 with
| Vundef, _, _ => v2 = Vundef
| Vint n, Mint8signed, Mint8signed => v2 = Vint(Int.sign_ext 8 n)
| Vint n, Mint8unsigned, Mint8signed => v2 = Vint(Int.sign_ext 8 n)
| Vint n, Mint8signed, Mint8unsigned => v2 = Vint(Int.zero_ext 8 n)
| Vint n, Mint8unsigned, Mint8unsigned => v2 = Vint(Int.zero_ext 8 n)
| Vint n, Mint16signed, Mint16signed => v2 = Vint(Int.sign_ext 16 n)
| Vint n, Mint16unsigned, Mint16signed => v2 = Vint(Int.sign_ext 16 n)
| Vint n, Mint16signed, Mint16unsigned => v2 = Vint(Int.zero_ext 16 n)
| Vint n, Mint16unsigned, Mint16unsigned => v2 = Vint(Int.zero_ext 16 n)
| Vint n, Mint32, Mint32 => v2 = Vint n
| Vint n, Many32, Many32 => v2 = Vint n
| Vint n, Mint32, Mfloat32 => v2 = Vsingle(Float32.of_bits n)
| Vint n, Many64, Many64 => v2 = Vint n
| Vint n, (Mint64 | Mfloat32 | Mfloat64 | Many64), _ => v2 = Vundef
| Vint n, _, _ => True (**r nothing meaningful to say about v2 *)
| Vptr b ofs, (Mint32 | Many32), (Mint32 | Many32) => v2 = if Archi.ptr64 then Vundef else Vptr b ofs
| Vptr b ofs, Mint64, (Mint64 | Many64) => v2 = if Archi.ptr64 then Vptr b ofs else Vundef
| Vptr b ofs, Many64, Many64 => v2 = Vptr b ofs
| Vptr b ofs, Many64, Mint64 => v2 = if Archi.ptr64 then Vptr b ofs else Vundef
| Vptr b ofs, _, _ => v2 = Vundef
| Vlong n, Mint64, Mint64 => v2 = Vlong n
| Vlong n, Mint64, Mfloat64 => v2 = Vfloat(Float.of_bits n)
| Vlong n, Many64, Many64 => v2 = Vlong n
| Vlong n, (Mint8signed|Mint8unsigned|Mint16signed|Mint16unsigned|Mint32|Mfloat32|Mfloat64|Many32), _ => v2 = Vundef
| Vlong n, _, _ => True (**r nothing meaningful to say about v2 *)
| Vfloat f, Mfloat64, Mfloat64 => v2 = Vfloat f
| Vfloat f, Mfloat64, Mint64 => v2 = Vlong(Float.to_bits f)
| Vfloat f, Many64, Many64 => v2 = Vfloat f
| Vfloat f, (Mint8signed|Mint8unsigned|Mint16signed|Mint16unsigned|Mint32|Mfloat32|Mint64|Many32), _ => v2 = Vundef
| Vfloat f, _, _ => True   (* nothing interesting to say about v2 *)
| Vsingle f, Mfloat32, Mfloat32 => v2 = Vsingle f
| Vsingle f, Mfloat32, Mint32 => v2 = Vint(Float32.to_bits f)
| Vsingle f, Many32, Many32 => v2 = Vsingle f
| Vsingle f, Many64, Many64 => v2 = Vsingle f
| Vsingle f, (Mint8signed|Mint8unsigned|Mint16signed|Mint16unsigned|Mint32|Mint64|Mfloat64|Many64), _ => v2 = Vundef
| Vsingle f, _, _ => True (* nothing interesting to say about v2 *)
end.
Remark decode_val_undef:
forall bl chunk, decode_val chunk (Undef :: bl) = Vundef.
Remark proj_bytes_inj_value:
forall q v, proj_bytes (inj_value q v) = None.
Ltac solve_decode_encode_val_general :=
exact I || reflexivity ||
match goal with
| |- context [ if Archi.ptr64 then _ else _ ] => destruct Archi.ptr64 eqn:?
| |- context [ proj_bytes (inj_bytes _) ] => rewrite proj_inj_bytes
| |- context [ proj_bytes (inj_value _ _) ] => rewrite proj_bytes_inj_value
| |- context [ proj_value _ (inj_value _ _) ] => rewrite ?proj_inj_value, ?proj_inj_value_mismatch by congruence
| |- context [ Int.repr(decode_int (encode_int 1 (Int.unsigned _))) ] => rewrite decode_encode_int_1
| |- context [ Int.repr(decode_int (encode_int 2 (Int.unsigned _))) ] => rewrite decode_encode_int_2
| |- context [ Int.repr(decode_int (encode_int 4 (Int.unsigned _))) ] => rewrite decode_encode_int_4
| |- context [ Int64.repr(decode_int (encode_int 8 (Int64.unsigned _))) ] => rewrite decode_encode_int_8
| |- Vint (Int.sign_ext _ (Int.sign_ext _ _)) = Vint _ => f_equal; apply Int.sign_ext_idem; omega
| |- Vint (Int.zero_ext _ (Int.zero_ext _ _)) = Vint _ => f_equal; apply Int.zero_ext_idem; omega
| |- Vint (Int.sign_ext _ (Int.zero_ext _ _)) = Vint _ => f_equal; apply Int.sign_ext_zero_ext; omega
end.
(** Pointers cannot be forged. *)
Definition quantity_chunk (chunk: memory_chunk) :=
match chunk with
| Mint64 | Mfloat64 | Many64 => Q64
| _ => Q32
end.
Inductive shape_encoding (chunk: memory_chunk) (v: val): list memval -> Prop :=
| shape_encoding_f: forall q i mvl,
(chunk = Mint32 \/ chunk = Many32 \/ chunk = Mint64 \/ chunk = Many64) ->
q = quantity_chunk chunk ->
S i = size_quantity_nat q ->
(forall mv, In mv mvl -> exists j, mv = Fragment v q j /\ S j <> size_quantity_nat q) ->
shape_encoding chunk v (Fragment v q i :: mvl)
| shape_encoding_b: forall b mvl,
match v with Vint _ => True | Vlong _ => True | Vfloat _ => True | Vsingle _ => True | _ => False end ->
(forall mv, In mv mvl -> exists b', mv = Byte b') ->
shape_encoding chunk v (Byte b :: mvl)
| shape_encoding_u: forall mvl,
(forall mv, In mv mvl -> mv = Undef) ->
shape_encoding chunk v (Undef :: mvl).
Inductive shape_decoding (chunk: memory_chunk): list memval -> val -> Prop :=
| shape_decoding_f: forall v q i mvl,
(chunk = Mint32 \/ chunk = Many32 \/ chunk = Mint64 \/ chunk = Many64) ->
q = quantity_chunk chunk ->
S i = size_quantity_nat q ->
(forall mv, In mv mvl -> exists j, mv = Fragment v q j /\ S j <> size_quantity_nat q) ->
shape_decoding chunk (Fragment v q i :: mvl) (Val.load_result chunk v)
| shape_decoding_b: forall b mvl v,
match v with Vint _ => True | Vlong _ => True | Vfloat _ => True | Vsingle _ => True |  _ => False end ->
(forall mv, In mv mvl -> exists b', mv = Byte b') ->
shape_decoding chunk (Byte b :: mvl) v
| shape_decoding_u: forall mvl,
shape_decoding chunk mvl Vundef.
(** * Compatibility with memory injections *)
(** Relating two memory values according to a memory injection. *)
Inductive memval_inject (f: meminj): memval -> memval -> Prop :=
| memval_inject_byte:
forall n, memval_inject f (Byte n) (Byte n)
| memval_inject_frag:
forall v1 v2 q n,
Val.inject f v1 v2 ->
memval_inject f (Fragment v1 q n) (Fragment v2 q n)
| memval_inject_undef:
forall mv, memval_inject f Undef mv.
(** [decode_val], applied to lists of memory values that are pairwise related by [memval_inject], returns values that are related by [Val.inject]. *)
(** Symmetrically, [encode_val], applied to values related by [Val.inject], returns lists of memory values that are pairwise related by [memval_inject]. *)
Definition memval_lessdef: memval -> memval -> Prop := memval_inject inject_id.
(** [memval_inject] and compositions *)
(** * Breaking 64-bit memory accesses into two 32-bit accesses *)

Lemma decode_val_int64:
forall l1 l2,
length l1 = 4%nat -> length l2 = 4%nat -> Archi.ptr64 = false ->
Val.lessdef
(decode_val Mint64 (l1 ++ l2))
(Val.longofwords (decode_val Mint32 (if Archi.big_endian then l1 else l2))
(decode_val Mint32 (if Archi.big_endian then l2 else l1))).