aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Forney <mforney@mforney.org>2019-07-05 23:19:34 -0700
committerMichael Forney <mforney@mforney.org>2019-07-05 23:32:44 -0700
commit114623a84c8165d7bf2acc4ac1e912c2f69e2a7c (patch)
treefadc72ca21806a20d075a0855a416d6144093ae9
parent31a928c187847238e84f8b02a73e70b256840108 (diff)
qbe: Handle K&R function definition argument promotion
Parameters for K&R function definitions that are affected by default argument promotion are passed as their promoted type, so need to be converted back before storing into memory allocated for the parameter.
-rw-r--r--qbe.c288
-rw-r--r--test/kr-function-argument-promotion.c10
-rw-r--r--test/kr-function-argument-promotion.qbe18
3 files changed, 173 insertions, 143 deletions
diff --git a/qbe.c b/qbe.c
index 1cd1a6c..f342723 100644
--- a/qbe.c
+++ b/qbe.c
@@ -376,6 +376,142 @@ funcload(struct func *f, struct type *t, struct lvalue lval)
return funcbits(f, t, v, lval.bits);
}
+/* TODO: move these conversions to QBE */
+static struct value *
+utof(struct func *f, struct repr *r, struct value *v)
+{
+ struct value *odd, *big, *phi[5] = {0}, *join;
+
+ if (v->repr->base == 'w') {
+ v = funcinst(f, IEXTUW, &i64, v);
+ return funcinst(f, ISLTOF, r, v);
+ }
+
+ phi[0] = mkblock("utof_small");
+ phi[2] = mkblock("utof_big");
+ join = mkblock("utof_join");
+
+ big = funcinst(f, ICSLTL, &i32, v, mkintconst(&i64, 0));
+ funcjnz(f, big, phi[2], phi[0]);
+
+ funclabel(f, phi[0]);
+ phi[1] = funcinst(f, ISLTOF, r, v);
+ funcjmp(f, join);
+
+ funclabel(f, phi[2]);
+ odd = funcinst(f, IAND, &i64, v, mkintconst(&i64, 1));
+ v = funcinst(f, ISHR, &i64, v, mkintconst(&i64, 1));
+ v = funcinst(f, IOR, &i64, v, odd); /* round to odd */
+ v = funcinst(f, ISLTOF, r, v);
+ phi[3] = funcinst(f, IADD, r, v, v);
+
+ funclabel(f, join);
+ return funcinstn(f, IPHI, r, phi);
+}
+
+static struct value *
+ftou(struct func *f, struct repr *r, struct value *v)
+{
+ struct value *big, *phi[5] = {0}, *join, *maxflt, *maxint;
+ enum instkind op = v->repr->base == 's' ? ISTOSI : IDTOSI;
+
+ if (r->base == 'w') {
+ v = funcinst(f, op, &i64, v);
+ return funcinst(f, ICOPY, r, v);
+ }
+
+ phi[0] = mkblock("ftou_small");
+ phi[2] = mkblock("ftou_big");
+ join = mkblock("ftou_join");
+
+ maxflt = mkfltconst(v->repr, 0x1p63);
+ maxint = mkintconst(&i64, 1ull<<63);
+
+ big = funcinst(f, v->repr->base == 's' ? ICGES : ICGED, &i32, v, maxflt);
+ funcjnz(f, big, phi[2], phi[0]);
+
+ funclabel(f, phi[0]);
+ phi[1] = funcinst(f, op, r, v);
+ funcjmp(f, join);
+
+ funclabel(f, phi[2]);
+ v = funcinst(f, ISUB, v->repr, v, maxflt);
+ v = funcinst(f, op, r, v);
+ phi[3] = funcinst(f, IXOR, r, v, maxint);
+
+ funclabel(f, join);
+ return funcinstn(f, IPHI, r, phi);
+}
+
+static struct value *
+extend(struct func *f, struct type *t, struct value *v)
+{
+ enum instkind op;
+
+ switch (t->size) {
+ case 1: op = t->basic.issigned ? IEXTSB : IEXTUB; break;
+ case 2: op = t->basic.issigned ? IEXTSH : IEXTUH; break;
+ default: return v;
+ }
+ return funcinst(f, op, &i32, v);
+}
+
+static struct value *
+convert(struct func *f, struct type *dst, struct type *src, struct value *l)
+{
+ enum instkind op;
+ struct value *r = NULL;
+
+ if (src->kind == TYPEPOINTER)
+ src = &typeulong;
+ if (dst->kind == TYPEPOINTER)
+ dst = &typeulong;
+ if (dst->kind == TYPEVOID)
+ return NULL;
+ if (!(src->prop & PROPREAL) || !(dst->prop & PROPREAL))
+ fatal("internal error; unsupported conversion");
+ if (dst->kind == TYPEBOOL) {
+ l = extend(f, src, l);
+ r = mkintconst(src->repr, 0);
+ if (src->prop & PROPINT)
+ op = src->size == 8 ? ICNEL : ICNEW;
+ else
+ op = src->size == 8 ? ICNED : ICNES;
+ } else if (dst->prop & PROPINT) {
+ if (src->prop & PROPINT) {
+ if (dst->size <= src->size) {
+ op = ICOPY;
+ } else {
+ switch (src->size) {
+ case 4: op = src->basic.issigned ? IEXTSW : IEXTUW; break;
+ case 2: op = src->basic.issigned ? IEXTSH : IEXTUH; break;
+ case 1: op = src->basic.issigned ? IEXTSB : IEXTUB; break;
+ default: fatal("internal error; unknown int conversion");
+ }
+ }
+ } else {
+ if (!dst->basic.issigned)
+ return ftou(f, dst->repr, l);
+ op = src->size == 8 ? IDTOSI : ISTOSI;
+ }
+ } else {
+ if (src->prop & PROPINT) {
+ if (!src->basic.issigned)
+ return utof(f, dst->repr, l);
+ op = src->size == 8 ? ISLTOF : ISWTOF;
+ } else {
+ if (src->size < dst->size)
+ op = IEXTS;
+ else if (src->size > dst->size)
+ op = ITRUNCD;
+ else
+ op = ICOPY;
+ }
+ }
+
+ return funcinst(f, op, dst->repr, l, r);
+}
+
/*
XXX: If a function declared without a prototype is declared with a
parameter affected by default argument promotion, we need to emit a QBE
@@ -388,6 +524,8 @@ mkfunc(struct decl *decl, char *name, struct type *t, struct scope *s)
struct func *f;
struct param *p;
struct decl *d;
+ struct type *pt;
+ struct value *v;
f = xmalloc(sizeof(*f));
f->decl = decl;
@@ -402,19 +540,19 @@ mkfunc(struct decl *decl, char *name, struct type *t, struct scope *s)
for (p = t->func.params; p; p = p->next) {
if (!p->name)
error(&tok.loc, "parameter name omitted in function definition");
- if (!t->func.isprototype && !typecompatible(p->type, typepromote(p->type, -1)))
- error(&tok.loc, "old-style function definition with parameter type incompatible with promoted type is not yet supported");
- emittype(p->type);
- d = mkdecl(DECLOBJECT, p->type, p->qual, LINKNONE);
+ pt = t->func.isprototype ? p->type : typepromote(p->type, -1);
+ emittype(pt);
p->value = xmalloc(sizeof(*p->value));
- functemp(f, p->value, p->type->repr);
+ functemp(f, p->value, pt->repr);
+ d = mkdecl(DECLOBJECT, p->type, p->qual, LINKNONE);
if (p->type->repr->abi.id) {
d->value = xmalloc(sizeof(*d->value));
*d->value = *p->value;
d->value->repr = &iptr;
} else {
+ v = typecompatible(p->type, pt) ? p->value : convert(f, pt, p->type, p->value);
funcinit(f, d, NULL);
- funcstore(f, p->type, QUALNONE, (struct lvalue){d->value}, p->value);
+ funcstore(f, p->type, QUALNONE, (struct lvalue){d->value}, v);
}
scopeputdecl(s, p->name, d);
}
@@ -551,142 +689,6 @@ funclval(struct func *f, struct expr *e)
return lval;
}
-/* TODO: move these conversions to QBE */
-static struct value *
-utof(struct func *f, struct repr *r, struct value *v)
-{
- struct value *odd, *big, *phi[5] = {0}, *join;
-
- if (v->repr->base == 'w') {
- v = funcinst(f, IEXTUW, &i64, v);
- return funcinst(f, ISLTOF, r, v);
- }
-
- phi[0] = mkblock("utof_small");
- phi[2] = mkblock("utof_big");
- join = mkblock("utof_join");
-
- big = funcinst(f, ICSLTL, &i32, v, mkintconst(&i64, 0));
- funcjnz(f, big, phi[2], phi[0]);
-
- funclabel(f, phi[0]);
- phi[1] = funcinst(f, ISLTOF, r, v);
- funcjmp(f, join);
-
- funclabel(f, phi[2]);
- odd = funcinst(f, IAND, &i64, v, mkintconst(&i64, 1));
- v = funcinst(f, ISHR, &i64, v, mkintconst(&i64, 1));
- v = funcinst(f, IOR, &i64, v, odd); /* round to odd */
- v = funcinst(f, ISLTOF, r, v);
- phi[3] = funcinst(f, IADD, r, v, v);
-
- funclabel(f, join);
- return funcinstn(f, IPHI, r, phi);
-}
-
-static struct value *
-ftou(struct func *f, struct repr *r, struct value *v)
-{
- struct value *big, *phi[5] = {0}, *join, *maxflt, *maxint;
- enum instkind op = v->repr->base == 's' ? ISTOSI : IDTOSI;
-
- if (r->base == 'w') {
- v = funcinst(f, op, &i64, v);
- return funcinst(f, ICOPY, r, v);
- }
-
- phi[0] = mkblock("ftou_small");
- phi[2] = mkblock("ftou_big");
- join = mkblock("ftou_join");
-
- maxflt = mkfltconst(v->repr, 0x1p63);
- maxint = mkintconst(&i64, 1ull<<63);
-
- big = funcinst(f, v->repr->base == 's' ? ICGES : ICGED, &i32, v, maxflt);
- funcjnz(f, big, phi[2], phi[0]);
-
- funclabel(f, phi[0]);
- phi[1] = funcinst(f, op, r, v);
- funcjmp(f, join);
-
- funclabel(f, phi[2]);
- v = funcinst(f, ISUB, v->repr, v, maxflt);
- v = funcinst(f, op, r, v);
- phi[3] = funcinst(f, IXOR, r, v, maxint);
-
- funclabel(f, join);
- return funcinstn(f, IPHI, r, phi);
-}
-
-static struct value *
-extend(struct func *f, struct type *t, struct value *v)
-{
- enum instkind op;
-
- switch (t->size) {
- case 1: op = t->basic.issigned ? IEXTSB : IEXTUB; break;
- case 2: op = t->basic.issigned ? IEXTSH : IEXTUH; break;
- default: return v;
- }
- return funcinst(f, op, &i32, v);
-}
-
-static struct value *
-convert(struct func *f, struct type *dst, struct type *src, struct value *l)
-{
- enum instkind op;
- struct value *r = NULL;
-
- if (src->kind == TYPEPOINTER)
- src = &typeulong;
- if (dst->kind == TYPEPOINTER)
- dst = &typeulong;
- if (dst->kind == TYPEVOID)
- return NULL;
- if (!(src->prop & PROPREAL) || !(dst->prop & PROPREAL))
- fatal("internal error; unsupported conversion");
- if (dst->kind == TYPEBOOL) {
- l = extend(f, src, l);
- r = mkintconst(src->repr, 0);
- if (src->prop & PROPINT)
- op = src->size == 8 ? ICNEL : ICNEW;
- else
- op = src->size == 8 ? ICNED : ICNES;
- } else if (dst->prop & PROPINT) {
- if (src->prop & PROPINT) {
- if (dst->size <= src->size) {
- op = ICOPY;
- } else {
- switch (src->size) {
- case 4: op = src->basic.issigned ? IEXTSW : IEXTUW; break;
- case 2: op = src->basic.issigned ? IEXTSH : IEXTUH; break;
- case 1: op = src->basic.issigned ? IEXTSB : IEXTUB; break;
- default: fatal("internal error; unknown int conversion");
- }
- }
- } else {
- if (!dst->basic.issigned)
- return ftou(f, dst->repr, l);
- op = src->size == 8 ? IDTOSI : ISTOSI;
- }
- } else {
- if (src->prop & PROPINT) {
- if (!src->basic.issigned)
- return utof(f, dst->repr, l);
- op = src->size == 8 ? ISLTOF : ISWTOF;
- } else {
- if (src->size < dst->size)
- op = IEXTS;
- else if (src->size > dst->size)
- op = ITRUNCD;
- else
- op = ICOPY;
- }
- }
-
- return funcinst(f, op, dst->repr, l, r);
-}
-
struct value *
funcexpr(struct func *f, struct expr *e)
{
@@ -1204,7 +1206,7 @@ emitfunc(struct func *f, bool global)
for (p = f->type->func.params; p; p = p->next) {
if (p != f->type->func.params)
fputs(", ", stdout);
- emitrepr(p->type->repr, true, false);
+ emitrepr(p->value->repr, true, false);
putchar(' ');
emitvalue(p->value);
}
diff --git a/test/kr-function-argument-promotion.c b/test/kr-function-argument-promotion.c
new file mode 100644
index 0000000..7592da1
--- /dev/null
+++ b/test/kr-function-argument-promotion.c
@@ -0,0 +1,10 @@
+int f(c)
+ unsigned char c;
+{
+ return c;
+}
+
+int main(void)
+{
+ return f(0x100);
+}
diff --git a/test/kr-function-argument-promotion.qbe b/test/kr-function-argument-promotion.qbe
new file mode 100644
index 0000000..6d2f2b8
--- /dev/null
+++ b/test/kr-function-argument-promotion.qbe
@@ -0,0 +1,18 @@
+export
+function w $f(w %.1) {
+@start.1
+ %.2 =w extub %.1
+ %.3 =l alloc4 1
+ storeb %.2, %.3
+@body.2
+ %.4 =w loadub %.3
+ %.5 =w extub %.4
+ ret %.5
+}
+export
+function w $main() {
+@start.3
+@body.4
+ %.1 =w call $f(w 256)
+ ret %.1
+}