From 7569343621ce0257a239e24290557e11e31dc119 Mon Sep 17 00:00:00 2001 From: Michael Forney Date: Wed, 17 Apr 2019 19:13:08 -0700 Subject: Fix bit-field offsets in unions --- decl.c | 38 ++++++++++++++++++++++++-------------- test/bitfield-union.c | 4 ++++ test/bitfield-union.qbe | 1 + 3 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 test/bitfield-union.c create mode 100644 test/bitfield-union.qbe 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 } -- cgit v1.2.3