(* *********************************************************************)
(*                                                                     *)
(*              The Compcert verified compiler                         *)
(*                                                                     *)
(*          Xavier Leroy, 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.     *)
(*                                                                     *)
(* *********************************************************************)
(** Type expressions for the Compcert C and Clight languages *)
Require Archi.
(** * Syntax of types *)
(** Compcert C types are similar to those of C.  They include numeric types, pointers, arrays, function types, and composite types (struct and union).  Numeric types (integers and floats) fully specify the bit size of the type.  An integer type is a pair of a signed/unsigned flag and a bit size: 8, 16, or 32 bits, or the special [IBool] size standing for the C99 [_Bool] type.  64-bit integers are treated separately. *)
Inductive signedness : Type :=
| Signed: signedness
| Unsigned: signedness.
Inductive intsize : Type :=
| I8: intsize
| I16: intsize
| I32: intsize
| IBool: intsize.
(** Float types come in two sizes: 32 bits (single precision) and 64-bit (double precision). *)
Inductive floatsize : Type :=
| F32: floatsize
| F64: floatsize.
(** Every type carries a set of attributes.  Currently, only two attributes are modeled: [volatile] and [_Alignas(n)] (from ISO C 2011). *)
Record attr : Type := mk_attr {
attr_volatile: bool;
attr_alignas: option N         (**r log2 of required alignment *)
}.
Definition noattr := {| attr_volatile := false; attr_alignas := None |}.
(** The syntax of type expressions.  Some points to note: - Array types [Tarray n] carry the size [n] of the array. Arrays with unknown sizes are represented by pointer types. - Function types [Tfunction targs tres] specify the number and types of the function arguments (list [targs]), and the type of the function result ([tres]).  Variadic functions and old-style unprototyped functions are not supported. *)
Inductive type : Type :=
| Tvoid: type                                    (**r the [void] type *)
| Tint: intsize -> signedness -> attr -> type    (**r integer types *)
| Tlong: signedness -> attr -> type              (**r 64-bit integer types *)
| Tfloat: floatsize -> attr -> type              (**r floating-point types *)
| Tpointer: type -> attr -> type                 (**r pointer types ([*ty]) *)
| Tarray: type -> Z -> attr -> type              (**r array types ([ty[len]]) *)
| Tfunction: typelist -> type -> calling_convention -> type    (**r function types *)
| Tstruct: ident -> attr -> type                 (**r struct types *)
| Tunion: ident -> attr -> type                  (**r union types *)
with typelist : Type :=
| Tnil: typelist
| Tcons: type -> typelist -> typelist.
(** ** Size of a type *)
(** In the ISO C standard, size is defined only for complete types.  However, it is convenient that [sizeof] is a total function.  For [void] and function types, we follow GCC and define their size to be 1.  For undefined structures and unions, the size is arbitrarily taken to be 0. *)
Fixpoint sizeof (env: composite_env) (t: type) : Z :=
match t with
| Tvoid => 1
| Tint I8 _ _ => 1
| Tint I16 _ _ => 2
| Tint I32 _ _ => 4
| Tint IBool _ _ => 1
| Tlong _ _ => 8
| Tfloat F32 _ => 4
| Tfloat F64 _ => 8
| Tpointer _ _ => if Archi.ptr64 then 8 else 4
| Tarray t' n _ => sizeof env t' * Z.max 0 n
| Tfunction _ _ _ => 1
| Tstruct id _ | Tunion id _ =>
match env!id with Some co => co_sizeof co | None => 0 end
end.
(** The size of a type is an integral multiple of its alignment, unless the alignment was artificially increased with the [__Alignas] attribute. *)
Fixpoint naturally_aligned (t: type) : Prop :=
attr_alignas (attr_of_type t) = None /\
match t with
| Tarray t' _ _ => naturally_aligned t'
| _ => True
end.
(** ** Size and alignment for composite definitions *)
(** The alignment for a structure or union is the max of the alignment of its members. *)
Fixpoint alignof_composite (env: composite_env) (m: members) : Z :=
match m with
| nil => 1
| (id, t) :: m' => Z.max (alignof env t) (alignof_composite env m')
end.
(** The size of a structure corresponds to its layout: fields are laid out consecutively, and padding is inserted to align each field to the alignment for its type. *)
Fixpoint sizeof_struct (env: composite_env) (cur: Z) (m: members) : Z :=
match m with
| nil => cur
| (id, t) :: m' => sizeof_struct env (align cur (alignof env t) + sizeof env t) m'
end.
(** The size of an union is the max of the sizes of its members. *)
Fixpoint sizeof_union (env: composite_env) (m: members) : Z :=
match m with
| nil => 0
| (id, t) :: m' => Z.max (sizeof env t) (sizeof_union env m')
end.
(** ** Byte offset for a field of a structure *)
(** [field_offset env id fld] returns the byte offset for field [id] in a structure whose members are [fld].  Fields are laid out consecutively, and padding is inserted to align each field to the alignment for its type. *)
Fixpoint field_offset_rec (env: composite_env) (id: ident) (fld: members) (pos: Z)
{struct fld} : res Z :=
match fld with
| nil => Error (MSG "Unknown field " :: CTX id :: nil)
| (id', t) :: fld' =>
if ident_eq id id'
then OK (align pos (alignof env t))
else field_offset_rec env id fld' (align pos (alignof env t) + sizeof env t)
end.
Definition field_offset (env: composite_env) (id: ident) (fld: members) : res Z :=
field_offset_rec env id fld 0.
Fixpoint field_type (id: ident) (fld: members) {struct fld} : res type :=
match fld with
| nil => Error (MSG "Unknown field " :: CTX id :: nil)
| (id', t) :: fld' => if ident_eq id id' then OK t else field_type id fld'
end.
(** Some sanity checks about field offsets.  First, field offsets are within the range of acceptable offsets. *)
Remark field_offset_rec_in_range:
forall env id ofs ty fld pos,
field_offset_rec env id fld pos = OK ofs -> field_type id fld = OK ty ->
pos <= ofs /\ ofs + sizeof env ty <= sizeof_struct env pos fld.

Lemma field_offset_in_range:
forall env fld id ofs ty,
field_offset env id fld = OK ofs -> field_type id fld = OK ty ->
0 <= ofs /\ ofs + sizeof env ty <= sizeof_struct env 0 fld.