Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does GCC pad this bit-field?

Program is in C using std=c99, this is on a 64-bit machine.

struct epochs {
    volatile unsigned int epoch    : 1;
    volatile unsigned int pulse    : 1;
    volatile unsigned int active0  : 7;
    volatile unsigned int active1  : 7;
    volatile unsigned int counter0 : 24; 
    volatile unsigned int counter1 : 24; 
};

when I check sizeof(epochs) it gives me 12.

I can tell gcc not to pad it by adding __attribute((packed)); so I can work around it. However I would really like to know why 4 bytes are added to pad this 64-bit structure?

The main thing here is that this structure NEEDS to be 64 bits because it is updated all at once in 64-bit atomic swap operations, which of course will not work on a 12-byte value.

like image 901
Exodist Avatar asked Dec 09 '22 12:12

Exodist


2 Answers

volatile unsigned int epoch    : 1;
volatile unsigned int pulse    : 1;
volatile unsigned int active0  : 7;
volatile unsigned int active1  : 7;

^ 32-bit (4 bytes)

volatile unsigned int counter0 : 24; 

^ 32-bit (4 bytes)

volatile unsigned int counter1 : 24; 

^ 32-bit (4 bytes)

So 4 bytes more.

C says :

(C99, 6.7.2.1p10) "If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit"

There is not enough space to put 24-bit (counter0) more in a 32-bit unit (likely the size of unsigned int in your system) that already holds 16-bit (epoch, pulse, active0, active1).

You can use uin64_t instead of using unsigned int to pack your bit-fields in a 64-bit unit but it is implementation-defined whether your system supports it or not.

(C99, 6.7.2.1p4) "A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type."

like image 155
ouah Avatar answered Dec 11 '22 10:12

ouah


While some older compilers used to regard int foo:3; as synonymous with e.g. long foo:3, or short foo:3, and simply place foo in whatever manner was convenient, the present C standard specifies that each bit field must fit entirely within a storage unit of the appropriate size. I have no idea what the rationale was for that specification, since the way bit fields are specified remains too vague to allow their use in portable code, but sometimes makes it impossible to pack things optimally. For example, the only way a 24-bit value could be stored efficiently within a structure would be to either have a machine which on a machine which supports 32-bit integers, or else have a 8 bits of data which could be placed adjacent to the 24-bit value (preceding or following) so as to "fill out" a 32-bit word.

Fortunately, in your particular case, it's possible to avoid the inefficiency by rearranging your fields. It may also be possible to avoid the inefficiency by changing the declared type of each field to unsigned long long if your compiler supports bitfields using such a type [in that case, bit fields would be allowed to straddle 32-bit boundaries provided they didn't straddle a 64-bit boundary].

like image 26
supercat Avatar answered Dec 11 '22 11:12

supercat