aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--decl.c23
-rw-r--r--test/struct-packed.c9
-rw-r--r--test/struct-packed.qbe17
3 files changed, 42 insertions, 7 deletions
diff --git a/decl.c b/decl.c
index 5f32061..56c4cbe 100644
--- a/decl.c
+++ b/decl.c
@@ -57,6 +57,7 @@ struct structbuilder {
struct type *type;
struct member **last;
unsigned bits; /* number of bits remaining in the last byte */
+ bool pack;
};
struct decl *
@@ -159,20 +160,24 @@ tagspec(struct scope *s)
enum typekind kind;
struct decl *d, *enumconsts;
struct expr *e;
+ struct attr a;
+ enum attrkind allowedattr;
struct structbuilder b;
unsigned long long value, max, min;
bool sign;
int i;
+ allowedattr = 0;
switch (tok.kind) {
- case TSTRUCT: kind = TYPESTRUCT; break;
- case TUNION: kind = TYPEUNION; break;
- case TENUM: kind = TYPEENUM; break;
+ case TSTRUCT: kind = TYPESTRUCT, allowedattr |= ATTRPACKED; break;
+ case TUNION: kind = TYPEUNION; break;
+ case TENUM: kind = TYPEENUM; break;
default: fatal("internal error: unknown tag kind");
}
next();
- attr(NULL, 0);
- gnuattr(NULL, 0);
+ a.kind = 0;
+ attr(&a, allowedattr);
+ gnuattr(&a, allowedattr);
tag = NULL;
t = NULL;
et = NULL;
@@ -216,12 +221,14 @@ tagspec(struct scope *s)
b.type = t;
b.last = &t->u.structunion.members;
b.bits = 0;
+ b.pack = a.kind & ATTRPACKED;
do structdecl(s, &b);
while (tok.kind != TRBRACE);
if (!t->u.structunion.members)
error(&tok.loc, "struct/union has no members");
next();
- t->size = ALIGNUP(t->size, t->align);
+ if (!b.pack)
+ t->size = ALIGNUP(t->size, t->align);
break;
case TYPEENUM:
enumconsts = NULL;
@@ -795,7 +802,7 @@ addmember(struct structbuilder *b, struct qualtype mt, char *name, int align, un
if (align < mt.type->align) {
if (align)
error(&tok.loc, "specified alignment of struct member is less strict than is required by type");
- align = mt.type->align;
+ align = b->pack ? 1 : mt.type->align;
}
if (t->kind == TYPESTRUCT) {
m->offset = ALIGNUP(t->size, align);
@@ -811,6 +818,8 @@ addmember(struct structbuilder *b, struct qualtype mt, char *name, int align, un
error(&tok.loc, "bit-field has invalid type");
if (align)
error(&tok.loc, "alignment specified for bit-field");
+ if (b->pack)
+ error(&tok.loc, "bit-field in packed struct is not supported");
if (!width && name)
error(&tok.loc, "bit-field with zero width must not have declarator");
if (width > mt.type->size * 8)
diff --git a/test/struct-packed.c b/test/struct-packed.c
new file mode 100644
index 0000000..d05bf91
--- /dev/null
+++ b/test/struct-packed.c
@@ -0,0 +1,9 @@
+struct [[gnu::packed]] {
+ char a;
+ long long b;
+ short c;
+} s = {1, 2, 3};
+
+int main(void) {
+ return s.a + s.b - s.c;
+}
diff --git a/test/struct-packed.qbe b/test/struct-packed.qbe
new file mode 100644
index 0000000..ee79301
--- /dev/null
+++ b/test/struct-packed.qbe
@@ -0,0 +1,17 @@
+export data $s = align 1 { b 1, l 2, h 3, }
+export
+function w $main() {
+@start.1
+@body.2
+ %.1 =l add $s, 0
+ %.2 =w loadsb %.1
+ %.3 =l extsb %.2
+ %.4 =l add $s, 1
+ %.5 =l loadl %.4
+ %.6 =l add %.3, %.5
+ %.7 =l add $s, 9
+ %.8 =w loadsh %.7
+ %.9 =l extsh %.8
+ %.10 =l sub %.6, %.9
+ ret %.10
+}