aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Forney <mforney@mforney.org>2019-04-14 12:28:14 -0700
committerMichael Forney <mforney@mforney.org>2019-04-14 12:42:12 -0700
commitea4a14c7a4943498f49c26824d68a8d4776c5ddc (patch)
tree27c7b45778132a2badbecf9b3dc0b4d9ada4d44e
parentc662950252c041e01b64b829921cb5e79a2e2bb6 (diff)
Initial support for loading/storing bit-fields
-rw-r--r--cc.h5
-rw-r--r--expr.c14
-rw-r--r--qbe.c124
-rw-r--r--tests/bitfield-load-signed.c7
-rw-r--r--tests/bitfield-load-signed.qbe14
-rw-r--r--tests/bitfield-load-unsigned.c7
-rw-r--r--tests/bitfield-load-unsigned.qbe14
7 files changed, 139 insertions, 46 deletions
diff --git a/cc.h b/cc.h
index bc37221..6cf3f94 100644
--- a/cc.h
+++ b/cc.h
@@ -280,6 +280,7 @@ enum exprkind {
/* postfix expression */
EXPRCALL,
/* member E.M gets transformed to *(typeof(E.M) *)((char *)E + offsetof(typeof(E), M)) */
+ EXPRBITFIELD,
EXPRINCDEC,
EXPRCOMPOUND,
/* subscript E1[E2] gets transformed to *((E1)+(E2)) */
@@ -323,6 +324,10 @@ struct expr {
size_t nargs;
} call;
struct {
+ struct expr *base;
+ struct bitfield bits;
+ } bitfield;
+ struct {
struct init *init;
} compound;
struct {
diff --git a/expr.c b/expr.c
index a824873..4a746bc 100644
--- a/expr.c
+++ b/expr.c
@@ -562,12 +562,18 @@ postfixexpr(struct scope *s, struct expr *r)
m = typemember(t, tok.lit, &offset);
if (!m)
error(&tok.loc, "struct/union has no member named '%s'", tok.lit);
- if (m->bits.before || m->bits.after)
- error(&tok.loc, "bit-field access is not yet supported");
r = mkbinaryexpr(&tok.loc, TADD, r, mkconstexpr(&typeulong, offset));
r = exprconvert(r, mkpointertype(m->type, tq | m->qual));
- e = mkunaryexpr(TMUL, r);
- e->lvalue = lvalue;
+ r = mkunaryexpr(TMUL, r);
+ r->lvalue = lvalue;
+ if (m->bits.before || m->bits.after) {
+ e = mkexpr(EXPRBITFIELD, r->type);
+ e->lvalue = lvalue;
+ e->bitfield.base = r;
+ e->bitfield.bits = m->bits;
+ } else {
+ e = r;
+ }
next();
break;
case TINC:
diff --git a/qbe.c b/qbe.c
index f41e28c..7130002 100644
--- a/qbe.c
+++ b/qbe.c
@@ -37,6 +37,11 @@ struct value {
};
};
+struct lvalue {
+ struct value *addr;
+ struct bitfield bits;
+};
+
enum instkind {
INONE,
@@ -234,62 +239,78 @@ funcalloc(struct func *f, struct decl *d)
}
static void
-funcstore(struct func *f, struct type *t, enum typequal tq, struct value *addr, struct value *v)
+funcstore(struct func *f, struct type *t, enum typequal tq, struct lvalue lval, struct value *v)
{
- enum instkind op;
+ enum instkind loadop, storeop;
+ enum typeprop tp;
+ unsigned long long mask;
if (tq & QUALVOLATILE)
error(&tok.loc, "volatile store is not yet supported");
if (tq & QUALCONST)
error(&tok.loc, "cannot store to 'const' object");
+ tp = typeprop(t);
+ assert(!lval.bits.before && !lval.bits.after || tp & PROPINT);
switch (t->kind) {
+ case TYPEPOINTER:
+ t = &typeulong;
+ /* fallthrough */
case TYPEBASIC:
switch (t->size) {
- case 1: op = ISTOREB; break;
- case 2: op = ISTOREH; break;
- case 4: op = typeprop(t) & PROPFLOAT ? ISTORES : ISTOREW; break;
- case 8: op = typeprop(t) & PROPFLOAT ? ISTORED : ISTOREL; break;
+ case 1: loadop = ILOADUB; storeop = ISTOREB; break;
+ case 2: loadop = ILOADUH; storeop = ISTOREH; break;
+ case 4: loadop = ILOADUW; storeop = tp & PROPFLOAT ? ISTORES : ISTOREW; break;
+ case 8: loadop = ILOADL; storeop = tp & PROPFLOAT ? ISTORED : ISTOREL; break;
default:
fatal("internal error; unimplemented store");
}
- break;
- case TYPEPOINTER:
- op = ISTOREL;
+ if (lval.bits.before || lval.bits.after) {
+ mask = 0xffffffffffffffffu >> lval.bits.after + 64 - t->size * 8 ^ (1 << lval.bits.before) - 1;
+ v = funcinst(f, IOR, t->repr,
+ funcinst(f, IAND, t->repr,
+ funcinst(f, loadop, t->repr, lval.addr),
+ mkintconst(t->repr, ~mask),
+ ),
+ funcinst(f, IAND, t->repr,
+ funcinst(f, ISHL, t->repr, v, mkintconst(&i32, lval.bits.before)),
+ mkintconst(t->repr, mask),
+ ),
+ );
+ }
+ funcinst(f, storeop, NULL, v, lval.addr);
break;
case TYPESTRUCT:
case TYPEUNION:
case TYPEARRAY: {
- enum instkind loadop, op;
struct value *src, *dst, *tmp, *align;
uint64_t offset;
switch (t->align) {
- case 1: loadop = ILOADUB, op = ISTOREB; break;
- case 2: loadop = ILOADUH, op = ISTOREH; break;
- case 4: loadop = ILOADUW, op = ISTOREW; break;
- case 8: loadop = ILOADL, op = ISTOREL; break;
+ case 1: loadop = ILOADUB, storeop = ISTOREB; break;
+ case 2: loadop = ILOADUH, storeop = ISTOREH; break;
+ case 4: loadop = ILOADUW, storeop = ISTOREW; break;
+ case 8: loadop = ILOADL, storeop = ISTOREL; break;
default:
fatal("internal error; invalid alignment %d", t->align);
}
src = v;
- dst = addr;
+ dst = lval.addr;
align = mkintconst(&iptr, t->align);
for (offset = 0; offset < t->size; offset += t->align) {
tmp = funcinst(f, loadop, &iptr, src);
- funcinst(f, op, NULL, tmp, dst);
+ funcinst(f, storeop, NULL, tmp, dst);
src = funcinst(f, IADD, &iptr, src, align);
dst = funcinst(f, IADD, &iptr, dst, align);
}
- return;
+ break;
}
default:
fatal("unimplemented store");
}
- funcinst(f, op, NULL, v, addr);
}
static struct value *
-funcload(struct func *f, struct type *t, struct value *addr)
+funcload(struct func *f, struct type *t, struct lvalue lval)
{
struct value *v;
enum instkind op;
@@ -312,13 +333,18 @@ funcload(struct func *f, struct type *t, struct value *addr)
case TYPEUNION:
case TYPEARRAY:
v = xmalloc(sizeof(*v));
- *v = *addr;
+ *v = *lval.addr;
v->repr = t->repr;
return v;
default:
fatal("unimplemented load %d", t->kind);
}
- return funcinst(f, op, t->repr, addr);
+ v = funcinst(f, op, t->repr, lval.addr);
+ if (lval.bits.after)
+ v = funcinst(f, ISHL, t->repr, v, mkintconst(&i32, lval.bits.after));
+ if (lval.bits.before + lval.bits.after)
+ v = funcinst(f, t->basic.issigned ? ISAR : ISHR, t->repr, v, mkintconst(&i32, lval.bits.before + lval.bits.after));
+ return v;
}
struct value *
@@ -373,7 +399,7 @@ mkfunc(char *name, struct type *t, struct scope *s)
d->value->repr = &iptr;
} else {
funcinit(f, d, NULL);
- funcstore(f, p->type, QUALNONE, d->value, p->value);
+ funcstore(f, p->type, QUALNONE, (struct lvalue){d->value}, p->value);
}
scopeputdecl(s, p->name, d);
}
@@ -448,11 +474,16 @@ funcgoto(struct func *f, char *name)
return g;
}
-static struct value *
-objectaddr(struct func *f, struct expr *e)
+static struct lvalue
+funclval(struct func *f, struct expr *e)
{
+ struct lvalue lval = {0};
struct decl *d;
+ if (e->kind == EXPRBITFIELD) {
+ lval.bits = e->bitfield.bits;
+ e = e->bitfield.base;
+ }
switch (e->kind) {
case EXPRIDENT:
d = e->ident.decl;
@@ -464,24 +495,30 @@ objectaddr(struct func *f, struct expr *e)
printf(" = { b \"%s\", b 0 }\n", f->name);
f->namedecl = NULL;
}
- return d->value;
+ lval.addr = d->value;
+ break;
case EXPRSTRING:
d = stringdecl(e);
- return d->value;
+ lval.addr = d->value;
+ break;
case EXPRCOMPOUND:
d = mkdecl(DECLOBJECT, e->type, e->qual, LINKNONE);
funcinit(f, d, e->compound.init);
- return d->value;
+ lval.addr = d->value;
+ break;
case EXPRUNARY:
if (e->unary.op != TMUL)
break;
- return funcexpr(f, e->unary.base);
+ lval.addr = funcexpr(f, e->unary.base);
+ break;
default:
if (e->type->kind != TYPESTRUCT && e->type->kind != TYPEUNION)
break;
- return funcinst(f, ICOPY, &iptr, funcexpr(f, e));
+ lval.addr = funcinst(f, ICOPY, &iptr, funcexpr(f, e));
}
- error(&tok.loc, "expression is not an object");
+ if (!lval.addr)
+ error(&tok.loc, "expression is not an object");
+ return lval;
}
/* TODO: move these conversions to QBE */
@@ -569,7 +606,8 @@ funcexpr(struct func *f, struct expr *e)
{
enum instkind op = INONE;
struct decl *d;
- struct value *l, *r, *v, *addr, **argvals, **argval;
+ struct value *l, *r, *v, **argvals, **argval;
+ struct lvalue lval;
struct expr *arg;
struct value *label[5];
struct type *t;
@@ -578,7 +616,7 @@ funcexpr(struct func *f, struct expr *e)
case EXPRIDENT:
d = e->ident.decl;
switch (d->kind) {
- case DECLOBJECT: return funcload(f, d->type, d->value);
+ case DECLOBJECT: return funcload(f, d->type, (struct lvalue){d->value});
case DECLCONST: return d->value;
default:
fatal("unimplemented declaration kind %d", d->kind);
@@ -588,12 +626,13 @@ funcexpr(struct func *f, struct expr *e)
if (typeprop(e->type) & PROPINT || e->type->kind == TYPEPOINTER)
return mkintconst(e->type->repr, e->constant.i);
return mkfltconst(e->type->repr, e->constant.f);
+ case EXPRBITFIELD:
case EXPRCOMPOUND:
- l = objectaddr(f, e);
- return funcload(f, e->type, l);
+ lval = funclval(f, e);
+ return funcload(f, e->type, lval);
case EXPRINCDEC:
- addr = objectaddr(f, e->incdec.base);
- l = funcload(f, e->incdec.base->type, addr);
+ lval = funclval(f, e->incdec.base);
+ l = funcload(f, e->incdec.base->type, lval);
if (e->type->kind == TYPEPOINTER)
r = mkintconst(e->type->repr, e->type->base->size);
else if (typeprop(e->type) & PROPINT)
@@ -603,7 +642,7 @@ funcexpr(struct func *f, struct expr *e)
else
fatal("not a scalar");
v = funcinst(f, e->incdec.op == TINC ? IADD : ISUB, e->type->repr, l, r);
- funcstore(f, e->type, e->qual, addr, v);
+ funcstore(f, e->type, e->qual, lval, v);
return e->incdec.post ? l : v;
case EXPRCALL:
argvals = xreallocarray(NULL, e->call.nargs + 3, sizeof(argvals[0]));
@@ -621,10 +660,11 @@ funcexpr(struct func *f, struct expr *e)
case EXPRUNARY:
switch (e->unary.op) {
case TBAND:
- return objectaddr(f, e->unary.base);
+ lval = funclval(f, e->unary.base);
+ return lval.addr;
case TMUL:
r = funcexpr(f, e->unary.base);
- return funcload(f, e->type, r);
+ return funcload(f, e->type, (struct lvalue){r});
}
fatal("internal error; unknown unary expression");
break;
@@ -818,8 +858,8 @@ funcexpr(struct func *f, struct expr *e)
if (e->assign.l->kind == EXPRTEMP) {
e->assign.l->temp = r;
} else {
- l = objectaddr(f, e->assign.l);
- funcstore(f, e->assign.l->type, e->assign.l->qual, l, r);
+ lval = funclval(f, e->assign.l);
+ funcstore(f, e->assign.l->type, e->assign.l->qual, lval, r);
}
return r;
case EXPRCOMMA:
@@ -898,7 +938,7 @@ funcinit(struct func *func, struct decl *d, struct init *init)
} else {
dst = funcinst(func, IADD, &iptr, d->value, mkintconst(&iptr, init->start));
src = funcexpr(func, init->expr);
- funcstore(func, init->expr->type, QUALNONE, dst, src);
+ funcstore(func, init->expr->type, QUALNONE, (struct lvalue){dst}, src);
offset = init->end;
}
if (max < offset)
diff --git a/tests/bitfield-load-signed.c b/tests/bitfield-load-signed.c
new file mode 100644
index 0000000..837ad24
--- /dev/null
+++ b/tests/bitfield-load-signed.c
@@ -0,0 +1,7 @@
+struct {
+ signed : 4, x : 15, : 13;
+} s;
+
+void f(void) {
+ s.x;
+}
diff --git a/tests/bitfield-load-signed.qbe b/tests/bitfield-load-signed.qbe
new file mode 100644
index 0000000..bc3f457
--- /dev/null
+++ b/tests/bitfield-load-signed.qbe
@@ -0,0 +1,14 @@
+export
+function $f() {
+@start.1
+@body.2
+ %.1 =l copy $s
+ %.2 =l mul 0, 1
+ %.3 =l add %.1, %.2
+ %.4 =l copy %.3
+ %.5 =w loadsw %.4
+ %.6 =w shl %.5, 13
+ %.7 =w sar %.6, 17
+ ret
+}
+export data $s = align 4 { z 4 }
diff --git a/tests/bitfield-load-unsigned.c b/tests/bitfield-load-unsigned.c
new file mode 100644
index 0000000..5a9e5fa
--- /dev/null
+++ b/tests/bitfield-load-unsigned.c
@@ -0,0 +1,7 @@
+struct {
+ unsigned : 4, x : 15, : 13;
+} s;
+
+void f(void) {
+ s.x;
+}
diff --git a/tests/bitfield-load-unsigned.qbe b/tests/bitfield-load-unsigned.qbe
new file mode 100644
index 0000000..c4ea94b
--- /dev/null
+++ b/tests/bitfield-load-unsigned.qbe
@@ -0,0 +1,14 @@
+export
+function $f() {
+@start.1
+@body.2
+ %.1 =l copy $s
+ %.2 =l mul 0, 1
+ %.3 =l add %.1, %.2
+ %.4 =l copy %.3
+ %.5 =w loaduw %.4
+ %.6 =w shl %.5, 13
+ %.7 =w shr %.6, 17
+ ret
+}
+export data $s = align 4 { z 4 }