aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--decl.c79
-rw-r--r--type.h6
-rw-r--r--util.h3
3 files changed, 65 insertions, 23 deletions
diff --git a/decl.c b/decl.c
index 4ba9a92..70cc3aa 100644
--- a/decl.c
+++ b/decl.c
@@ -60,6 +60,7 @@ enum funcspec {
struct structbuilder {
struct type *type;
struct member **last;
+ int bits; /* number of bits remaining in the last byte */
};
struct decl *
@@ -206,6 +207,7 @@ tagspec(struct scope *s)
case TYPEUNION:
b.type = t;
b.last = &t->structunion.members;
+ b.bits = 0;
do structdecl(s, &b);
while (tok.kind != TRBRACE);
next();
@@ -640,29 +642,59 @@ paramdecl(struct scope *s, struct param *params)
}
static void
-addmember(struct structbuilder *b, struct type *mt, char *name, int align)
+addmember(struct structbuilder *b, struct type *mt, char *name, int align, uint64_t width)
{
struct type *t = b->type;
struct member *m;
-
- m = xmalloc(sizeof(*m));
- m->type = mt;
- m->name = name;
- m->next = NULL;
- *b->last = m;
- b->last = &m->next;
+ size_t end;
assert(mt->align > 0);
- if (align < mt->align)
+ if (name || width == -1) {
+ m = xmalloc(sizeof(*m));
+ m->type = mt;
+ m->name = name;
+ m->next = NULL;
+ *b->last = m;
+ b->last = &m->next;
+ }
+ if (width == -1) {
+ m->bits.before = 0;
+ m->bits.after = 0;
+ if (align < mt->align)
+ align = mt->align;
+ t->size = ALIGNUP(t->size, align);
+ if (t->kind == TYPESTRUCT) {
+ m->offset = t->size;
+ t->size += mt->size;
+ } else {
+ m->offset = 0;
+ if (t->size < mt->size)
+ t->size = mt->size;
+ }
+ } else { /* bit-field */
+ if (!(typeprop(mt) & PROPINT))
+ error(&tok.loc, "bit-field has invalid type");
+ if (align)
+ error(&tok.loc, "alignment specified for bit-field");
+ if (!width && name)
+ error(&tok.loc, "bit-field with zero width must not have declarator");
+ if (width > mt->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->size);
+ if (!width || width > (end - t->size) * 8 + b->bits) {
+ /* no room, allocate a new storage-unit */
+ t->size = end;
+ b->bits = 0;
+ }
+ if (width) {
+ m->offset = ALIGNDOWN(t->size - !!b->bits, mt->size);
+ m->bits.before = (t->size - m->offset) * 8 - b->bits;
+ m->bits.after = mt->size * 8 - width - m->bits.before;
+ t->size += (width - b->bits + 7) / 8;
+ b->bits = m->bits.after % 8;
+ }
align = mt->align;
- t->size = ALIGNUP(t->size, align);
- if (t->kind == TYPESTRUCT) {
- m->offset = t->size;
- t->size += mt->size;
- } else {
- m->offset = 0;
- if (t->size < mt->size)
- t->size = mt->size;
}
if (t->align < align)
t->align = align;
@@ -673,6 +705,7 @@ structdecl(struct scope *s, struct structbuilder *b)
{
struct type *base, *mt;
char *name;
+ uint64_t width;
int align;
base = declspecs(s, NULL, NULL, &align);
@@ -682,16 +715,18 @@ structdecl(struct scope *s, struct structbuilder *b)
if ((base->kind != TYPESTRUCT && base->kind != TYPEUNION) || base->structunion.tag)
error(&tok.loc, "struct declaration must declare at least one member");
next();
- addmember(b, base, NULL, align);
+ addmember(b, base, NULL, align, -1);
return;
}
for (;;) {
- if (tok.kind != TCOLON) {
+ if (consume(TCOLON)) {
+ width = intconstexpr(s, false);
+ addmember(b, base, NULL, 0, width);
+ } else {
mt = declarator(s, base, &name, false);
- addmember(b, mt, name, align);
+ width = consume(TCOLON) ? intconstexpr(s, false) : -1;
+ addmember(b, mt, name, align, width);
}
- if (tok.kind == TCOLON)
- error(&tok.loc, "bit-fields are not yet supported");
if (tok.kind == TSEMICOLON)
break;
expect(TCOMMA, "or ';' after declarator");
diff --git a/type.h b/type.h
index 728806a..c6e0f46 100644
--- a/type.h
+++ b/type.h
@@ -41,10 +41,16 @@ struct param {
struct param *next;
};
+struct bitfield {
+ short before; /* number of bits in the storage unit before the bit-field */
+ short after; /* number of bits in the storage unit after the bit-field */
+};
+
struct member {
char *name;
struct type *type;
uint64_t offset;
+ struct bitfield bits;
struct member *next;
};
diff --git a/util.h b/util.h
index ae70938..f60dc73 100644
--- a/util.h
+++ b/util.h
@@ -10,7 +10,8 @@ struct array {
extern char *argv0;
#define LEN(a) (sizeof(a) / sizeof((a)[0]))
-#define ALIGNUP(x, n) (((x) + (n) - 1) & ~((n) - 1))
+#define ALIGNDOWN(x, n) ((x) & -(n))
+#define ALIGNUP(x, n) ALIGNDOWN((x) + (n) - 1, n)
void warn(const char *, ...);
_Noreturn void fatal(const char *fmt, ...);