aboutsummaryrefslogtreecommitdiffstats
path: root/cfrontend/Cop.v
diff options
context:
space:
mode:
authorXavier Leroy <xavier.leroy@college-de-france.fr>2021-05-17 18:07:02 +0200
committerXavier Leroy <xavier.leroy@college-de-france.fr>2021-08-22 13:29:00 +0200
commit47fae389c800034e002c9f8a398e9adc79a14b81 (patch)
tree210933a5a526afe0469a66f59861c13d687c733e /cfrontend/Cop.v
parenta94edc576ca2c288c66f710798ab2ada3c485a40 (diff)
downloadcompcert-kvx-47fae389c800034e002c9f8a398e9adc79a14b81.tar.gz
compcert-kvx-47fae389c800034e002c9f8a398e9adc79a14b81.zip
Native support for bit fields (#400)
This big PR adds support for bit fields in structs and unions to the verified part of CompCert, namely the CompCert C and Clight languages. The compilation of bit field accesses to normal integer accesses + shifts and masks is done and proved correct as part of the Cshmgen pass. The layout of bit fields in memory is done by the functions in module Ctypes. It follows the ELF ABI layout algorithm. As a bonus, basic soundness properties of the layout are shown, such as "two different bit fields do not overlap" or "a bit field and a regular field do not overlap". All this replaces the previous emulation of bit fields by source-to-source rewriting in the unverified front-end of CompCert (module cparse/Bitfield.ml). This emulation was prone to errors (see nonstandard layout instead. The core idea for the PR is that expressions in l-value position denote not just a block, a byte offset and a type, but also a bitfield designator saying whether all the bits of the type are accessed (designator Full) or only some of its bits (designator Bits). Designators of the Bits kind appear when the l-value is a bit field access; the bit width and bit offset in Bits are computed by the functions in Ctypes that implement the layout algorithm. Consequently, both in the semantics of CompCert C and Clight and in the SimplExpr, SimplLocals and Cshmgen compilation passes, pairs of a type and a bitfield designator are used in a number of places where a single type was used before. The introduction of bit fields has a big impact on static initialization (module cfrontend/Initializers.v), which had to be rewritten in large part, along with its soundness proof (cfrontend/Initializersproof.v). Both static initialization and run-time manipulation of bit fields are tested in test/abi using differential testing against GCC and randomly-generated structs. This work exposed subtle interactions between bit fields and the volatile modifier. Currently, the volatile modifier is ignored when accessing a bit field (and a warning is printed at compile-time), just like it is ignored when accessing a struct or union as a r-value. Currently, the natural alignment of bit fields and their storage units cannot be modified with the aligned attribute. _Alignas on bit fields is rejected as per C11, and the packed modifier cannot be applied to a struct containing bit fields.
Diffstat (limited to 'cfrontend/Cop.v')
-rw-r--r--cfrontend/Cop.v54
1 files changed, 54 insertions, 0 deletions
diff --git a/cfrontend/Cop.v b/cfrontend/Cop.v
index f6524124..0d7bcc3a 100644
--- a/cfrontend/Cop.v
+++ b/cfrontend/Cop.v
@@ -1084,6 +1084,60 @@ Definition incrdecr_type (ty: type) :=
| _ => Tvoid
end.
+(** ** Accessing bit fields *)
+
+Definition chunk_for_carrier (sz: intsize) : memory_chunk :=
+ match sz with
+ | I8 | IBool => Mint8unsigned
+ | I16 => Mint16unsigned
+ | I32 => Mint32
+ end.
+
+(** For bitfield accesses, bits are numbered differently on
+ little-endian and on big-endian machines: bit 0 is the least
+ significant bit in little-endian, and the most significant bit in
+ big-endian. *)
+
+Definition bitsize_carrier (sz: intsize) : Z :=
+ match sz with
+ | I8 | IBool => 8
+ | I16 => 16
+ | I32 => 32
+ end.
+
+Definition first_bit (sz: intsize) (pos width: Z) : Z :=
+ if Archi.big_endian
+ then bitsize_carrier sz - pos - width
+ else pos.
+
+Definition bitfield_extract (sz: intsize) (sg: signedness) (pos width: Z) (c: int) : int :=
+ if intsize_eq sz IBool || signedness_eq sg Unsigned
+ then Int.unsigned_bitfield_extract (first_bit sz pos width) width c
+ else Int.signed_bitfield_extract (first_bit sz pos width) width c.
+
+Definition bitfield_normalize (sz: intsize) (sg: signedness) (width: Z) (n: int) : int :=
+ if intsize_eq sz IBool || signedness_eq sg Unsigned
+ then Int.zero_ext width n
+ else Int.sign_ext width n.
+
+Inductive load_bitfield: type -> intsize -> signedness -> Z -> Z -> mem -> val -> val -> Prop :=
+ | load_bitfield_intro: forall sz sg1 attr sg pos width m addr c,
+ 0 <= pos -> 0 < width <= bitsize_intsize sz -> pos + width <= bitsize_carrier sz ->
+ sg1 = (if zlt width (bitsize_intsize sz) then Signed else sg) ->
+ Mem.loadv (chunk_for_carrier sz) m addr = Some (Vint c) ->
+ load_bitfield (Tint sz sg1 attr) sz sg pos width m addr
+ (Vint (bitfield_extract sz sg pos width c)).
+
+Inductive store_bitfield: type -> intsize -> signedness -> Z -> Z -> mem -> val -> val -> mem -> val -> Prop :=
+ | store_bitfield_intro: forall sz sg1 attr sg pos width m addr c n m',
+ 0 <= pos -> 0 < width <= bitsize_intsize sz -> pos + width <= bitsize_carrier sz ->
+ sg1 = (if zlt width (bitsize_intsize sz) then Signed else sg) ->
+ Mem.loadv (chunk_for_carrier sz) m addr = Some (Vint c) ->
+ Mem.storev (chunk_for_carrier sz) m addr
+ (Vint (Int.bitfield_insert (first_bit sz pos width) width c n)) = Some m' ->
+ store_bitfield (Tint sz sg1 attr) sz sg pos width m addr (Vint n)
+ m' (Vint (bitfield_normalize sz sg width n)).
+
(** * Compatibility with extensions and injections *)
Section GENERIC_INJECTION.