diff options
author | Michael Forney <mforney@mforney.org> | 2019-07-05 23:19:34 -0700 |
---|---|---|
committer | Michael Forney <mforney@mforney.org> | 2019-07-05 23:32:44 -0700 |
commit | 114623a84c8165d7bf2acc4ac1e912c2f69e2a7c (patch) | |
tree | fadc72ca21806a20d075a0855a416d6144093ae9 | |
parent | 31a928c187847238e84f8b02a73e70b256840108 (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.c | 288 | ||||
-rw-r--r-- | test/kr-function-argument-promotion.c | 10 | ||||
-rw-r--r-- | test/kr-function-argument-promotion.qbe | 18 |
3 files changed, 173 insertions, 143 deletions
@@ -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 +} |