aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Forney <mforney@mforney.org>2019-04-17 19:13:08 -0700
committerMichael Forney <mforney@mforney.org>2019-04-17 20:11:06 -0700
commit7569343621ce0257a239e24290557e11e31dc119 (patch)
treecef551648642c3ee7888d18440c77a752118de66
parent2833c0041d669bc1ba7a6d13e7f92c23b84e04ae (diff)
Fix bit-field offsets in unions
-rw-r--r--decl.c38
-rw-r--r--test/bitfield-union.c4
-rw-r--r--test/bitfield-union.qbe1
3 files changed, 29 insertions, 14 deletions
diff --git a/decl.c b/decl.c
index 12e8029..06ded9e 100644
--- a/decl.c
+++ b/decl.c
@@ -684,21 +684,31 @@ addmember(struct structbuilder *b, struct qualtype mt, char *name, int align, ui
error(&tok.loc, "bit-field with zero width must not have declarator");
if (width > mt.type->size * 8)
error(&tok.loc, "bit-field exceeds width of underlying type");
- /* calculate end of the storage-unit for this bit-field */
- end = ALIGNUP(t->size, mt.type->size);
- if (!width || width > (end - t->size) * 8 + b->bits) {
- /* no room, allocate a new storage-unit */
- t->size = end;
- b->bits = 0;
- }
- if (m) {
- m->offset = ALIGNDOWN(t->size - !!b->bits, mt.type->size);
- m->bits.before = (t->size - m->offset) * 8 - b->bits;
- m->bits.after = mt.type->size * 8 - width - m->bits.before;
- }
- t->size += (width - b->bits + 7) / 8;
- b->bits = (b->bits - width) % 8;
align = mt.type->align;
+ if (t->kind == TYPESTRUCT) {
+ /* calculate end of the storage-unit for this bit-field */
+ end = ALIGNUP(t->size, mt.type->size);
+ if (!width || width > (end - t->size) * 8 + b->bits) {
+ /* no room, allocate a new storage-unit */
+ t->size = end;
+ b->bits = 0;
+ }
+ if (m) {
+ m->offset = ALIGNDOWN(t->size - !!b->bits, mt.type->size);
+ m->bits.before = (t->size - m->offset) * 8 - b->bits;
+ m->bits.after = mt.type->size * 8 - width - m->bits.before;
+ }
+ t->size += (width - b->bits + 7) / 8;
+ b->bits = (b->bits - width) % 8;
+ } else {
+ if (m) {
+ m->offset = 0;
+ m->bits.before = 0;
+ m->bits.after = mt.type->size * 8 - width;
+ }
+ if (t->size < mt.type->size)
+ t->size = mt.type->size;
+ }
}
if (t->align < align)
t->align = align;
diff --git a/test/bitfield-union.c b/test/bitfield-union.c
new file mode 100644
index 0000000..f366ec8
--- /dev/null
+++ b/test/bitfield-union.c
@@ -0,0 +1,4 @@
+union {
+ int x : 5;
+ int y : 10;
+} u = {.y = 123};
diff --git a/test/bitfield-union.qbe b/test/bitfield-union.qbe
new file mode 100644
index 0000000..95d3861
--- /dev/null
+++ b/test/bitfield-union.qbe
@@ -0,0 +1 @@
+export data $u = align 4 { b 123, z 3 }