Bit-field union bug

B

Ben Bacarisse

Ian Collins said:
Or the code is written based on knowledge of the compiler being used.
I have often used bit fields to map hardware registers or bits in
messages headers.

Absolutely. Hence "often" and "usually". If you can rely on the
compiler maintaining the layout you need there is no problem. None the
less, bit fields seem to have a fascination for people learning C that
makes them dangerously seductive.
 
P

Phil Carmody

Ben Bacarisse said:
Absolutely. Hence "often" and "usually". If you can rely on the
compiler maintaining the layout you need there is no problem.

This warning reminds me of the lack of trust I can have in that
every day:

In file included from drivers/input/touchscreen/atmel_mxt.c:34:
drivers/input/touchscreen/atmel_mxt_objects.h:45: note: Offset of packed bit-field 'bit' has changed in GCC 4.4

I'm not sure why there's the incompatibility, it seems a strange
thing to change.

Phil
 
D

David Thompson

If we replace this with...

struct foo {
unsigned proto:4;
union {
struct s_skip_ind skip_ind;
struct s_trans_id trans_id;
} u;
};

...then you should realise that a conforming C compiler must be
able to support the following...

struct foo f;
union {
struct s_skip_ind skip_ind;
struct s_trans_id trans_id;
} *up = &f.u;

It would make the compiler's life a tad difficult if it had to
support that _and_ coalesce bit-fields in the way you would like.
Yes but no. Yes foo.u must be addressable preventing bit-field
packing. No you can't use &f.u that way.

<topic shift>
*Within a translation unit*, two declarations/definitions of struct or
union types with the same contents are *not* formally compatible
types. In practice a sane compiler will lay them out the same (unless
you alter something like #pragma pack) and so accesses will work if
you cast, but you need a tag or typedef to cast.

*Between translation units* if the tag, member names and types are the
same, it is completely compatible.
 
T

Tim Rentsch

David Thompson said:
<topic shift>
*Within a translation unit*, two declarations/definitions of struct or
union types with the same contents are *not* formally compatible
types. In practice a sane compiler will lay them out the same (unless
you alter something like #pragma pack) and so accesses will work if
you cast, but you need a tag or typedef to cast.

Identical layout is not enough to make things work in
the presence of type-sensitive optimization. In the
past one could generally get away with such things,
but that's becoming less true as time goes on.
 
T

Tim Rentsch

Ben Bacarisse said:
Quentin Pope said:
[snip]

For example, imagine
struct foo {
unsigned short a:2;
unsigned short b:2;
unsigned short c:2;
unsigned short d:2;
};

If sizeOf(foo) is 3, then there is no way of telling whether a and b are
packed into 1 byte, or if it's b and c, or if it's c and d.

[snip]

In the "unsigned int" version, the C standard says that a, b, c and d
must be packed together, consecutively, in the first byte of the struct.
The "addressable storage unit" into which they get packed can't be less
than 1 byte in size and a byte can't be less than 8 bits wide. Which
one goes in the high-significance position is not specified but it is
must be either a or d and the other must follow in order.

AFAIK the Standard does not require them to be in the first
byte, just the first addressable unit. For example, they
might be the low-order bits in a four byte unsigned int
on a machine that is "big endian", ie, they would be in
the fourth byte rather than the first.
 
B

Ben Bacarisse

Tim Rentsch said:
Ben Bacarisse said:
Quentin Pope said:
[snip]

For example, imagine
struct foo {
unsigned short a:2;
unsigned short b:2;
unsigned short c:2;
unsigned short d:2;
};

If sizeOf(foo) is 3, then there is no way of telling whether a and b are
packed into 1 byte, or if it's b and c, or if it's c and d.

[snip]

In the "unsigned int" version, the C standard says that a, b, c and d
must be packed together, consecutively, in the first byte of the struct.
The "addressable storage unit" into which they get packed can't be less
than 1 byte in size and a byte can't be less than 8 bits wide. Which
one goes in the high-significance position is not specified but it is
must be either a or d and the other must follow in order.

AFAIK the Standard does not require them to be in the first
byte, just the first addressable unit. For example, they
might be the low-order bits in a four byte unsigned int
on a machine that is "big endian", ie, they would be in
the fourth byte rather than the first.

Yes, I think you are right. They must be packed consecutively in the
storage unit chosen by the implementation but that need not be a byte.
 
E

ec429

Identical layout is not enough to make things work in
the presence of type-sensitive optimization. In the
past one could generally get away with such things,
but that's becoming less true as time goes on.
Why is this? If the two struct (say) types have been defined
identically, what optimisation can the compiler possibly make that'll be
valid for one but not the other?
-e
 
K

Kaz Kylheku

Why is this? If the two struct (say) types have been defined
identically, what optimisation can the compiler possibly make that'll be
valid for one but not the other?

It can assume that foo->x = 42 has no effect on the value of bar->x, continuing
to use a stale cached value, because foo and bar are pointers to different
declared structure types.
 
T

Tim Rentsch

ec429 said:
Why is this? If the two struct (say) types have been defined
identically, what optimisation can the compiler possibly make that'll
be valid for one but not the other?

What Kaz Kylheku said -- stores into one kind of struct can
be assumed not to affect members in a different kind of
struct, if the two structs involved are not both in a single
union object. Note that this latter condition can happen
even if both struct types are included in a union but one
of the actual struct objects is known not to be in a union.
For example, compiling (I haven't actually compiled this,
please excuse any minor mistakes):

struct foo { int x; ... };
struct bas { int x; ... };
union foobas { struct foo f; struct bas b; };
extern struct foo global_foo = { 1 };

int
mumble( union foobas *p, struct bas *q ){
global_foo.x = 7;

q->x = 13;
p->b.x = 14;
p->f.x = 15;

return global_foo.x;
}

the compiler is allowed to assume the function will return
the value 7, because none of the intermediate assignments
can affect global_foo.x. However, the three intermediate
assignments cannot be rearranged relative to each other,
because any one of them might affect the others.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,082
Messages
2,570,589
Members
47,212
Latest member
JaydenBail

Latest Threads

Top