(* *********************************************************************)
(*                                                                     *)
(*              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.
(** Second, two distinct fields do not overlap *)
(** Third, if a struct is a prefix of another, the offsets of common fields are the same. *)
(** Fourth, the position of each field respects its alignment. *)
(** ** Access modes *)
(** The [access_mode] function describes how a l-value of the given type must be accessed: - [By_value ch]: access by value, i.e. by loading from the address of the l-value using the memory chunk [ch]; - [By_reference]: access by reference, i.e. by just returning the address of the l-value (used for arrays and functions); - [By_copy]: access is by reference, assignment is by copy (used for [struct] and [union] types) - [By_nothing]: no access is possible, e.g. for the [void] type. *)
Inductive mode: Type :=
| By_value: memory_chunk -> mode
| By_reference: mode
| By_copy: mode
| By_nothing: mode.
Definition access_mode (ty: type) : mode :=
match ty with
| Tint I8 Signed _ => By_value Mint8signed
| Tint I8 Unsigned _ => By_value Mint8unsigned
| Tint I16 Signed _ => By_value Mint16signed
| Tint I16 Unsigned _ => By_value Mint16unsigned
| Tint I32 _ _ => By_value Mint32
| Tint IBool _ _ => By_value Mint8unsigned
| Tlong _ _ => By_value Mint64
| Tfloat F32 _ => By_value Mfloat32
| Tfloat F64 _ => By_value Mfloat64
| Tvoid => By_nothing
| Tpointer _ _ => By_value Mptr
| Tarray _ _ _ => By_reference
| Tfunction _ _ _ => By_reference
| Tstruct _ _ => By_copy
| Tunion _ _ => By_copy
end.
(** For the purposes of the semantics and the compiler, a type denotes a volatile access if it carries the [volatile] attribute and it is accessed by value. *)
Definition type_is_volatile (ty: type) : bool :=
match access_mode ty with
| By_value _ => attr_volatile (attr_of_type ty)
| _          => false
end.
(** ** Alignment for block copy operations *)
(** A variant of [alignof] for use in block copy operations. Block copy operations do not support alignments greater than 8, and require the size to be an integral multiple of the alignment. *)
Fixpoint alignof_blockcopy (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' _ _ => alignof_blockcopy env t'
| Tfunction _ _ _ => 1
| Tstruct id _ | Tunion id _ =>
match env!id with
| Some co => Z.min 8 (co_alignof co)
| None => 1
end
end.
(** Type ranks *)
(** The rank of a type is a nonnegative integer that measures the direct nesting of arrays, struct and union types.  It does not take into account indirect nesting such as a struct type that appears under a pointer or function type. Type ranks ensure that type expressions (ignoring pointer and function types) have an inductive structure. *)
Fixpoint rank_type (ce: composite_env) (t: type) : nat :=
match t with
| Tarray t' _ _ => S (rank_type ce t')
| Tstruct id _ | Tunion id _ =>
match ce!id with
| None => O
| Some co => S (co_rank co)
end
| _ => O
end.
Fixpoint rank_members (ce: composite_env) (m: members) : nat :=
match m with
| nil => 0%nat
| (id, t) :: m => Init.Nat.max (rank_type ce t) (rank_members ce m)
end.
(** ** C types and back-end types *)
(** Extracting a type list from a function parameter declaration. *)
Fixpoint type_of_params (params: list (ident * type)) : typelist :=
match params with
| nil => Tnil
| (id, ty) :: rem => Tcons ty (type_of_params rem)
end.
(** Translating C types to Cminor types and function signatures. *)
Definition typ_of_type (t: type) : AST.typ :=
match t with
| Tvoid => AST.Tint
| Tint _ _ _ => AST.Tint
| Tlong _ _ => AST.Tlong
| Tfloat F32 _ => AST.Tsingle
| Tfloat F64 _ => AST.Tfloat
| Tpointer _ _ | Tarray _ _ _ | Tfunction _ _ _ | Tstruct _ _ | Tunion _ _ => AST.Tptr
end.
Definition rettype_of_type (t: type) : AST.rettype :=
match t with
| Tvoid => AST.Tvoid
| Tint I32 _ _ => AST.Tint
| Tint I8 Signed _ => AST.Tint8signed
| Tint I8 Unsigned _ => AST.Tint8unsigned
| Tint I16 Signed _ => AST.Tint16signed
| Tint I16 Unsigned _ => AST.Tint16unsigned
| Tint IBool _ _ => AST.Tint8unsigned
| Tlong _ _ => AST.Tlong
| Tfloat F32 _ => AST.Tsingle
| Tfloat F64 _ => AST.Tfloat
| Tpointer _ _ => AST.Tptr
| Tarray _ _ _ | Tfunction _ _ _ | Tstruct _ _ | Tunion _ _ => AST.Tvoid
end.
Fixpoint typlist_of_typelist (tl: typelist) : list AST.typ :=
match tl with
| Tnil => nil
| Tcons hd tl => typ_of_type hd :: typlist_of_typelist tl
end.
Definition signature_of_type (args: typelist) (res: type) (cc: calling_convention): signature :=
mksignature (typlist_of_typelist args) (rettype_of_type res) cc.
(** * Construction of the composite environment *)
Definition sizeof_composite (env: composite_env) (su: struct_or_union) (m: members) : Z :=
match su with
| Struct => sizeof_struct env 0 m
| Union  => sizeof_union env m
end.

Lemma sizeof_composite_pos:
forall env su m, 0 <= sizeof_composite env su m.