Skip to content

Commit 47fae38

Browse files
committed
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.
1 parent a94edc5 commit 47fae38

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+3866
-2299
lines changed

cfrontend/C2C.ml

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ let checkFunctionType env tres targs =
612612
end
613613
end
614614

615-
let rec convertTyp env t =
615+
let rec convertTyp env ?bitwidth t =
616616
match t with
617617
| C.TVoid a -> Tvoid
618618
| C.TInt(ik, a) ->
@@ -643,7 +643,21 @@ let rec convertTyp env t =
643643
| C.TUnion(id, a) ->
644644
Tunion(intern_string id.name, convertAttr a)
645645
| C.TEnum(id, a) ->
646-
convertIkind Cutil.enum_ikind (convertAttr a)
646+
let ik =
647+
match bitwidth with
648+
| None -> Cutil.enum_ikind
649+
| Some w ->
650+
let info = Env.find_enum env id in
651+
let representable sg =
652+
List.for_all (fun (_, v, _) -> Cutil.int_representable v w sg)
653+
info.Env.ei_members in
654+
if representable false then
655+
Cutil.unsigned_ikind_of Cutil.enum_ikind
656+
else if representable true then
657+
Cutil.signed_ikind_of Cutil.enum_ikind
658+
else
659+
Cutil.enum_ikind in
660+
convertIkind ik (convertAttr a)
647661

648662
and convertParams env = function
649663
| [] -> Tnil
@@ -679,9 +693,16 @@ let rec convertTypAnnotArgs env = function
679693
convertTypAnnotArgs env el)
680694

681695
let convertField env f =
682-
if f.fld_bitfield <> None then
683-
unsupported "bit field in struct or union (consider adding option [-fbitfields])";
684-
(intern_string f.fld_name, convertTyp env f.fld_typ)
696+
let id = intern_string f.fld_name
697+
and ty = convertTyp env ?bitwidth: f.fld_bitfield f.fld_typ in
698+
match f.fld_bitfield with
699+
| None -> Member_plain(id, ty)
700+
| Some w ->
701+
match ty with
702+
| Tint(sz, sg, attr) ->
703+
Member_bitfield(id, sz, sg, attr, Z.of_uint w, f.fld_name = "")
704+
| _ ->
705+
fatal_error "bitfield must have type int"
685706

686707
let convertCompositedef env su id attr members =
687708
if Cutil.find_custom_attributes ["packed";"__packed__"] attr <> [] then
@@ -784,6 +805,11 @@ let convertFloat f kind =
784805

785806
(** Expressions *)
786807

808+
let check_volatile_bitfield env e =
809+
if Cutil.is_bitfield env e
810+
&& List.mem AVolatile (Cutil.attributes_of_type env e.etyp) then
811+
warning Diagnostics.Unnamed "access to a volatile bit field, the 'volatile' qualifier is ignored"
812+
787813
let ezero = Eval(Vint(coqint_of_camlint 0l), type_int32s)
788814

789815
let ewrap = function
@@ -798,6 +824,7 @@ let rec convertExpr env e =
798824
| C.EUnop((C.Oderef|C.Odot _|C.Oarrow _), _)
799825
| C.EBinop(C.Oindex, _, _, _) ->
800826
let l = convertLvalue env e in
827+
check_volatile_bitfield env e;
801828
ewrap (Ctyping.evalof l)
802829

803830
| C.EConst(C.CInt(i, k, _)) ->
@@ -867,6 +894,7 @@ let rec convertExpr env e =
867894
if Cutil.is_composite_type env e2.etyp
868895
&& List.mem AVolatile (Cutil.attributes_of_type env e2.etyp) then
869896
warning Diagnostics.Unnamed "assignment of a value of volatile composite type, the 'volatile' qualifier is ignored";
897+
check_volatile_bitfield env e1;
870898
ewrap (Ctyping.eassign e1' e2')
871899
| C.EBinop((C.Oadd_assign|C.Osub_assign|C.Omul_assign|C.Odiv_assign|
872900
C.Omod_assign|C.Oand_assign|C.Oor_assign|C.Oxor_assign|
@@ -887,6 +915,7 @@ let rec convertExpr env e =
887915
| _ -> assert false in
888916
let e1' = convertLvalue env e1 in
889917
let e2' = convertExpr env e2 in
918+
check_volatile_bitfield env e1;
890919
ewrap (Ctyping.eassignop op' e1' e2')
891920
| C.EBinop(C.Ocomma, e1, e2, _) ->
892921
ewrap (Ctyping.ecomma (convertExpr env e1) (convertExpr env e2))

0 commit comments

Comments
 (0)