diff options
Diffstat (limited to 'qbe.c')
-rw-r--r-- | qbe.c | 1153 |
1 files changed, 1153 insertions, 0 deletions
@@ -0,0 +1,1153 @@ +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <inttypes.h> +#include "util.h" +#include "decl.h" +#include "emit.h" +#include "eval.h" +#include "expr.h" +#include "htab.h" +#include "init.h" +#include "scope.h" +#include "token.h" +#include "tree.h" +#include "type.h" + +struct name { + char *str; + uint64_t id; +}; + +struct representation { + char base; + char ext; + struct name abi; +}; + +struct value { + enum { + VALNONE, + VALGLOBAL, + VALCONST, + VALLABEL, + VALTEMP, + VALELLIPSIS, + } kind; + struct representation *repr; + union { + struct name name; + uint64_t i; + double f; + }; +}; + +enum instructionkind { + INONE, + +#define OP(op, ret, arg, name) op, +#include "ops.h" +#undef OP +}; + +struct instruction { + enum instructionkind kind; + struct value res, *arg[]; +}; + +struct block { + struct value label; + _Bool terminated; + struct array insts; + + struct block *next; +}; + +struct switchcase { + struct treenode node; + struct value *body; +}; + +struct representation i8 = {'w', 'b'}; +struct representation i16 = {'w', 'h'}; +struct representation i32 = {'w', 'w'}; +struct representation i64 = {'l', 'l'}; +struct representation f32 = {'s', 's'}; +struct representation f64 = {'d', 'd'}; +struct representation iptr = {'l', 'l'}; + +void +switchcase(struct switchcases *cases, uint64_t i, struct value *v) +{ + struct switchcase *c; + + c = treeinsert(&cases->root, i, sizeof(*c)); + if (!c->node.new) + error(&tok.loc, "multiple 'case' labels with same value"); + c->body = v; +} + +/* functions */ + +struct value * +mkblock(char *name) +{ + static uint64_t id; + struct block *b; + + b = xmalloc(sizeof(*b)); + b->label.kind = VALLABEL; + b->label.name.str = name; + b->label.name.id = ++id; + b->terminated = false; + b->insts = (struct array){0}; + b->next = NULL; + + return &b->label; +} + +static void emittype(struct type *); +static void emitvalue(struct value *); + +static void +funcname(struct function *f, struct name *n, char *s) +{ + n->id = ++f->lastid; + n->str = s; +} + +static void +functemp(struct function *f, struct value *v, struct representation *repr) +{ + if (!repr) + fatal("temp has no type"); + v->kind = VALTEMP; + funcname(f, &v->name, NULL); + v->repr = repr; +} + +static struct { + int ret, arg; + char *str; +} instdesc[] = { +#define OP(op, ret, arg, name) [op] = {ret, arg, name}, +#include "ops.h" +#undef OP +}; + +static struct value * +funcinst(struct function *f, int op, struct representation *repr, struct value *args[]) +{ + struct instruction *inst; + size_t n; + + if (f->end->terminated) + return NULL; + n = instdesc[op].arg; + if (n == -1) { + for (n = 0; args[n]; ++n) + ; + ++n; + } + if (n > (SIZE_MAX - sizeof(*inst)) / sizeof(args[0])) { + errno = ENOMEM; + fatal("malloc:"); + } + inst = xmalloc(sizeof(*inst) + n * sizeof(args[0])); + inst->kind = op; + if (repr) + functemp(f, &inst->res, repr); + else + inst->res.kind = VALNONE; + memcpy(inst->arg, args, n * sizeof(args[0])); + arrayaddptr(&f->end->insts, inst); + + return instdesc[op].ret ? &inst->res : NULL; +} + +struct value * +mkintconst(struct representation *r, uint64_t n) +{ + struct value *v; + + v = xmalloc(sizeof(*v)); + v->kind = VALCONST; + v->repr = r; + v->i = n; + + return v; +} + +uint64_t +intconstvalue(struct value *v) +{ + assert(v->kind = VALCONST); + return v->i; +} + +static struct value * +mkfltconst(struct representation *r, double n) +{ + struct value *v; + + v = xmalloc(sizeof(*v)); + v->kind = VALCONST; + v->repr = r; + v->f = n; + + return v; +} + +static void +funcalloc(struct function *f, struct declaration *d) +{ + enum instructionkind op; + struct instruction *inst; + + assert(!d->type->incomplete); + assert(d->type->size > 0); + if (!d->align) + d->align = d->type->align; + else if (d->align < d->type->align) + error(&tok.loc, "object requires alignment %d, which is stricter than %d", d->type->align, d->align); + switch (d->align) { + case 1: + case 2: + case 4: op = IALLOC4; break; + case 8: op = IALLOC8; break; + case 16: op = IALLOC16; break; + default: + fatal("internal error: invalid alignment: %d\n", d->align); + } + inst = xmalloc(sizeof(*inst) + sizeof(inst->arg[0])); + inst->kind = op; + functemp(f, &inst->res, &iptr); + inst->arg[0] = mkintconst(&i64, d->type->size); + d->value = &inst->res; + arrayaddptr(&f->start->insts, inst); +} + +static void +funcstore(struct function *f, struct type *t, struct value *addr, struct value *v) +{ + enum instructionkind op; + enum typequalifier tq = QUALNONE; + + t = typeunqual(t, &tq); + if (tq & QUALVOLATILE) + error(&tok.loc, "volatile store is not yet supported"); + if (tq & QUALCONST) + error(&tok.loc, "cannot store to 'const' object"); + switch (t->kind) { + case TYPEBASIC: + switch (t->basic.kind) { + case BASICBOOL: + case BASICCHAR: op = ISTOREB; break; + case BASICSHORT: op = ISTOREH; break; + case BASICINT: op = ISTOREW; break; + case BASICLONG: op = ISTOREL; break; + case BASICLONGLONG: op = ISTOREL; break; + case BASICFLOAT: op = ISTORES; break; + case BASICDOUBLE: op = ISTORED; break; + case BASICLONGDOUBLE: + fatal("'long double' store is not yet supported"); + default: + fatal("internal error; unknown basic type %d", t->basic.kind); + } + break; + case TYPEPOINTER: + op = ISTOREL; + break; + case TYPESTRUCT: + case TYPEUNION: + case TYPEARRAY: { + enum instructionkind 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; + default: + fatal("internal error; invalid alignment %d", t->align); + } + src = v; + dst = addr; + align = mkintconst(&iptr, t->align); + for (offset = 0; offset < t->size; offset += t->align) { + tmp = funcinst(f, loadop, &iptr, (struct value *[]){src}); + funcinst(f, op, NULL, (struct value *[]){tmp, dst}); + src = funcinst(f, IADD, &iptr, (struct value *[]){src, align}); + dst = funcinst(f, IADD, &iptr, (struct value *[]){dst, align}); + } + return; + } + default: + fatal("unimplemented store"); + } + funcinst(f, op, NULL, (struct value *[]){v, addr}); +} + +static struct value * +funcload(struct function *f, struct type *t, struct value *addr) +{ + enum instructionkind op; + enum typequalifier tq; + + t = typeunqual(t, &tq); + switch (t->kind) { + case TYPEBASIC: + switch (t->size) { + case 1: op = t->basic.issigned ? ILOADSB : ILOADUB; break; + case 2: op = t->basic.issigned ? ILOADSH : ILOADUH; break; + case 4: op = typeprop(t) & PROPFLOAT ? ILOADS : t->basic.issigned ? ILOADSW : ILOADUW; break; + case 8: op = typeprop(t) & PROPFLOAT ? ILOADD : ILOADL; break; + default: + fatal("internal error; unimplemented load"); + } + break; + case TYPEPOINTER: + op = ILOADL; + break; + case TYPESTRUCT: + case TYPEUNION: + case TYPEARRAY: + return addr; + default: + fatal("unimplemented load %d", t->kind); + } + return funcinst(f, op, t->repr, (struct value *[]){addr}); +} + +struct value * +mkglobal(char *name, bool private) +{ + static uint64_t id; + struct value *v; + + v = xmalloc(sizeof(*v)); + v->kind = VALGLOBAL; + v->repr = &iptr; + v->name.str = name; + v->name.id = private ? ++id : 0; + + return v; +} + +struct function * +mkfunc(char *name, struct type *t, struct scope *s) +{ + struct function *f; + struct parameter *p; + struct declaration *d; + + f = xmalloc(sizeof(*f)); + f->name = name; + f->type = t; + f->start = f->end = (struct block *)mkblock("start"); + f->gotos = mkhtab(8); + f->lastid = 0; + + /* allocate space for parameters */ + for (p = t->func.params; p; p = p->next) { + if (!p->name) + error(&tok.loc, "parameter name omitted in function definition"); + emittype(p->type); + d = mkdecl(DECLOBJECT, p->type, LINKNONE); + p->value = xmalloc(sizeof(*p->value)); + functemp(f, p->value, p->type->repr); + if (p->type->repr->abi.id) { + d->value = p->value; + } else { + funcinit(f, d, NULL); + funcstore(f, p->type, d->value, p->value); + } + scopeputdecl(s, p->name, d); + } + + t = mkarraytype(mkqualifiedtype(&typechar, QUALCONST), strlen(name) + 1); + d = mkdecl(DECLOBJECT, t, LINKNONE); + d->value = mkglobal("__func__", true); + scopeputdecl(s, "__func__", d); + f->namedecl = d; + + funclabel(f, mkblock("body")); + + return f; +} + +void +funclabel(struct function *f, struct value *v) +{ + assert(v->kind == VALLABEL); + f->end->next = (struct block *)v; + f->end = f->end->next; +} + +void +funcjmp(struct function *f, struct value *v) +{ + funcinst(f, IJMP, NULL, (struct value *[]){v}); + f->end->terminated = true; +} + +void +funcjnz(struct function *f, struct value *v, struct value *l1, struct value *l2) +{ + funcinst(f, IJNZ, NULL, (struct value *[]){v, l1, l2}); + f->end->terminated = true; +} + +void +funcret(struct function *f, struct value *v) +{ + funcinst(f, IRET, NULL, (struct value *[]){v}); + f->end->terminated = true; +} + +struct gotolabel * +funcgoto(struct function *f, char *name) +{ + void **entry; + struct gotolabel *g; + struct hashtablekey key; + + htabstrkey(&key, name); + entry = htabput(f->gotos, &key); + g = *entry; + if (!g) { + g = xmalloc(sizeof(*g)); + g->label = mkblock(name); + *entry = g; + } + + return g; +} + +static struct value * +objectaddr(struct function *f, struct expression *e) +{ + struct declaration *d; + + switch (e->kind) { + case EXPRIDENT: + d = e->ident.decl; + if (d->kind != DECLOBJECT && d->kind != DECLFUNC) + error(&tok.loc, "identifier is not an object or function"); // XXX: fix location, var name + if (d == f->namedecl) { + fputs("data ", stdout); + emitvalue(d->value); + printf(" = { b \"%s\", b 0 }\n", f->name); + f->namedecl = NULL; + } + return d->value; + case EXPRSTRING: + d = stringdecl(e); + return d->value; + case EXPRCOMPOUND: + d = mkdecl(DECLOBJECT, e->type, LINKNONE); + funcinit(f, d, e->compound.init); + return d->value; + case EXPRUNARY: + if (e->unary.op == TMUL) + return funcexpr(f, e->unary.base); + } + error(&tok.loc, "expression is not an object"); +} + +/* TODO: move these conversions to QBE */ +static struct value * +utof(struct function *f, struct representation *r, struct value *v) +{ + struct value *odd, *big, *phi[5] = {0}, *join; + + if (v->repr->base == 'w') { + v = funcinst(f, IEXTUW, &i64, (struct value *[]){v}); + return funcinst(f, ISLTOF, r, (struct value *[]){v}); + } + + phi[0] = mkblock("utof_small"); + phi[2] = mkblock("utof_big"); + join = mkblock("utof_join"); + + big = funcinst(f, ICSLTL, &i32, (struct value *[]){v, mkintconst(&i64, 0)}); + funcjnz(f, big, phi[2], phi[0]); + + funclabel(f, phi[0]); + phi[1] = funcinst(f, ISLTOF, r, (struct value *[]){v}); + funcjmp(f, join); + + funclabel(f, phi[2]); + odd = funcinst(f, IAND, &i64, (struct value *[]){v, mkintconst(&i64, 1)}); + v = funcinst(f, ISHR, &i64, (struct value *[]){v, mkintconst(&i64, 1)}); + v = funcinst(f, IOR, &i64, (struct value *[]){v, odd}); /* round to odd */ + v = funcinst(f, ISLTOF, r, (struct value *[]){v}); + phi[3] = funcinst(f, IADD, r, (struct value *[]){v, v}); + + funclabel(f, join); + return funcinst(f, IPHI, r, phi); +} + +static struct value * +ftou(struct function *f, struct representation *r, struct value *v) +{ + uint64_t max = 1ull << ((32 << (r->base == 'l')) - 1); + struct value *big, *phi[5] = {0}, *join, *maxflt, *maxint; + enum instructionkind op = v->repr->base == 's' ? ISTOSI : IDTOSI; + + if (r->base == 'w') { + v = funcinst(f, op, &i64, (struct value *[]){v}); + return funcinst(f, ICOPY, r, (struct value *[]){v}); + } + + phi[0] = mkblock("ftou_small"); + phi[2] = mkblock("ftou_big"); + join = mkblock("ftou_join"); + + maxflt = mkfltconst(v->repr, r->base == 'w' ? 0x1p31 : 0x1p63); + maxint = mkintconst(&i64, r->base == 'w' ? 1ull<<31 : 1ull<<63); + + big = funcinst(f, v->repr->base == 's' ? ICGES : ICGED, &i32, (struct value *[]){v, maxflt}); + funcjnz(f, big, phi[2], phi[0]); + + funclabel(f, phi[0]); + phi[1] = funcinst(f, op, r, (struct value *[]){v}); + funcjmp(f, join); + + funclabel(f, phi[2]); + v = funcinst(f, ISUB, v->repr, (struct value *[]){v, maxflt}); + v = funcinst(f, op, r, (struct value *[]){v}); + phi[3] = funcinst(f, IXOR, r, (struct value *[]){v, maxint}); + + funclabel(f, join); + return funcinst(f, IPHI, r, phi); +} + +struct value * +funcexpr(struct function *f, struct expression *e) +{ + static struct value ellipsis = {.kind = VALELLIPSIS}; + enum instructionkind op = INONE; + struct declaration *d; + struct value *l, *r, *v, *addr, **argvals, **argval; + struct expression *arg; + struct value *label[5]; + struct type *t; + + switch (e->kind) { + case EXPRIDENT: + d = e->ident.decl; + switch (d->kind) { + case DECLOBJECT: return funcload(f, d->type, d->value); + case DECLCONST: return d->value; + default: + fatal("unimplemented declaration kind %d", d->kind); + } + break; + case EXPRCONST: + 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 EXPRCOMPOUND: + l = objectaddr(f, e); + return funcload(f, e->type, l); + case EXPRINCDEC: + addr = objectaddr(f, e->incdec.base); + l = funcload(f, e->incdec.base->type, addr); + if (e->type->kind == TYPEPOINTER) + r = mkintconst(e->type->repr, e->type->base->size); + else if (typeprop(e->type) & PROPINT) + r = mkintconst(e->type->repr, 1); + else if (typeprop(e->type) & PROPFLOAT) + r = mkfltconst(e->type->repr, 1); + else + fatal("not a scalar"); + v = funcinst(f, e->incdec.op == TINC ? IADD : ISUB, e->type->repr, (struct value *[]){l, r}); + funcstore(f, e->type, addr, v); + return e->incdec.post ? l : v; + case EXPRCALL: + argvals = xreallocarray(NULL, e->call.nargs + 3, sizeof(argvals[0])); + argvals[0] = funcexpr(f, e->call.func); + for (argval = &argvals[1], arg = e->call.args; arg; ++argval, arg = arg->next) { + emittype(arg->type); + *argval = funcexpr(f, arg); + } + if (e->call.func->type->base->func.isvararg) + *argval++ = &ellipsis; + *argval = NULL; + return funcinst(f, ICALL, e->type == &typevoid ? NULL : e->type->repr, argvals); + //if (e->call.func->type->base->func.isnoreturn) + // funcret(f, NULL); + case EXPRUNARY: + switch (e->unary.op) { + case TBAND: + return objectaddr(f, e->unary.base); + case TMUL: + r = funcexpr(f, e->unary.base); + return funcload(f, e->type, r); + } + fatal("internal error; unknown unary expression"); + break; + case EXPRCAST: { + struct type *src, *dst; + int srcprop, dstprop; + + l = funcexpr(f, e->cast.e); + r = NULL; + + src = typeunqual(e->cast.e->type, NULL); + if (src->kind == TYPEPOINTER) + src = &typeulong; + dst = typeunqual(e->type, NULL); + if (dst->kind == TYPEPOINTER) + dst = &typeulong; + if (dst->kind == TYPEVOID) + return NULL; + if (src->kind != TYPEBASIC || dst->kind != TYPEBASIC) + fatal("internal error; unsupported conversion"); + srcprop = typeprop(src); + dstprop = typeprop(dst); + if (dst->basic.kind == BASICBOOL) { + r = mkintconst(src->repr, 0); + if (srcprop & PROPINT) + op = src->size == 8 ? ICNEL : ICNEW; + else + op = src->size == 8 ? ICNED : ICNES; + } else if (dstprop & PROPINT) { + if (srcprop & PROPINT) { + if (dst->size == 8 && src->size <= 4) + op = src->basic.issigned ? IEXTSW : IEXTUW; + else + op = ICOPY; + } else { + if (!dst->basic.issigned) + return ftou(f, dst->repr, l); + op = src->size == 8 ? IDTOSI : ISTOSI; + } + } else { + if (srcprop & 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, (struct value *[]){l, r}); + } + case EXPRBINARY: + if (e->binary.op == TLOR || e->binary.op == TLAND) { + label[0] = mkblock("logic_right"); + label[1] = mkblock("logic_join"); + + l = funcexpr(f, e->binary.l); + label[2] = (struct value *)f->end; + if (e->binary.op == TLOR) + funcjnz(f, l, label[1], label[0]); + else + funcjnz(f, l, label[0], label[1]); + funclabel(f, label[0]); + r = funcexpr(f, e->binary.r); + label[3] = (struct value *)f->end; + funclabel(f, label[1]); + return funcinst(f, IPHI, e->type->repr, (struct value *[]){label[2], l, label[3], r, NULL}); + } + l = funcexpr(f, e->binary.l); + r = funcexpr(f, e->binary.r); + t = e->binary.l->type; + if (t->kind == TYPEPOINTER) + t = &typeulong; + switch (e->binary.op) { + case TMUL: + op = IMUL; + break; + case TDIV: + op = !(typeprop(e->type) & PROPINT) || e->type->basic.issigned ? IDIV : IUDIV; + break; + case TMOD: + op = e->type->basic.issigned ? IREM : IUREM; + break; + case TADD: + op = IADD; + break; + case TSUB: + op = ISUB; + break; + case TSHL: + op = ISHL; + break; + case TSHR: + op = t->basic.issigned ? ISAR : ISHR; + break; + case TBOR: + op = IOR; + break; + case TBAND: + op = IAND; + break; + case TXOR: + op = IXOR; + break; + case TLESS: + if (t->size <= 4) + op = typeprop(t) & PROPFLOAT ? ICLTS : t->basic.issigned ? ICSLTW : ICULTW; + else + op = typeprop(t) & PROPFLOAT ? ICLTD : t->basic.issigned ? ICSLTL : ICULTL; + break; + case TGREATER: + if (t->size <= 4) + op = typeprop(t) & PROPFLOAT ? ICGTS : t->basic.issigned ? ICSGTW : ICUGTW; + else + op = typeprop(t) & PROPFLOAT ? ICGTD : t->basic.issigned ? ICSGTL : ICUGTL; + break; + case TLEQ: + if (t->size <= 4) + op = typeprop(t) & PROPFLOAT ? ICLES : t->basic.issigned ? ICSLEW : ICULEW; + else + op = typeprop(t) & PROPFLOAT ? ICLED : t->basic.issigned ? ICSLEL : ICULEL; + break; + case TGEQ: + if (t->size <= 4) + op = typeprop(t) & PROPFLOAT ? ICGES : t->basic.issigned ? ICSGEW : ICUGEW; + else + op = typeprop(t) & PROPFLOAT ? ICGED : t->basic.issigned ? ICSGEL : ICUGEL; + break; + case TEQL: + if (t->size <= 4) + op = typeprop(t) & PROPFLOAT ? ICEQS : ICEQW; + else + op = typeprop(t) & PROPFLOAT ? ICEQD : ICEQL; + break; + case TNEQ: + if (t->size <= 4) + op = typeprop(t) & PROPFLOAT ? ICNES : ICNEW; + else + op = typeprop(t) & PROPFLOAT ? ICNED : ICNEL; + break; + } + if (op == INONE) + fatal("internal error; unimplemented binary expression"); + return funcinst(f, op, e->type->repr, (struct value *[]){l, r}); + case EXPRCOND: + label[0] = mkblock("cond_true"); + label[1] = mkblock("cond_false"); + label[2] = mkblock("cond_join"); + + v = funcexpr(f, e->cond.e); + funcjnz(f, v, label[0], label[1]); + + funclabel(f, label[0]); + l = funcexpr(f, e->cond.t); + label[3] = (struct value *)f->end; + funcjmp(f, label[2]); + + funclabel(f, label[1]); + r = funcexpr(f, e->cond.f); + label[4] = (struct value *)f->end; + + funclabel(f, label[2]); + if (e->type == &typevoid) + return NULL; + return funcinst(f, IPHI, e->type->repr, (struct value *[]){label[3], l, label[4], r, NULL}); + case EXPRASSIGN: + r = funcexpr(f, e->assign.r); + if (e->assign.l->kind == EXPRTEMP) { + e->assign.l->temp = r; + } else { + l = objectaddr(f, e->assign.l); + funcstore(f, e->assign.l->type, l, r); + } + return r; + case EXPRCOMMA: + for (e = e->comma.exprs; e->next; e = e->next) + funcexpr(f, e); + return funcexpr(f, e); + case EXPRBUILTIN: + switch (e->builtin.kind) { + case BUILTINVASTART: + l = funcexpr(f, e->builtin.arg); + funcinst(f, IVASTART, NULL, (struct value *[]){l}); + break; + case BUILTINVAEND: + /* no-op */ + break; + default: + fatal("internal error: unimplemented builtin"); + } + return NULL; + case EXPRTEMP: + assert(e->temp); + return e->temp; + default: + fatal("unimplemented expression %d", e->kind); + } +} + +static void +zero(struct function *func, struct value *addr, int align, uint64_t offset, uint64_t end) +{ + enum instructionkind store[] = { + [1] = ISTOREB, + [2] = ISTOREH, + [4] = ISTOREW, + [8] = ISTOREL, + }; + struct value *tmp; + static struct value z = {.kind = VALCONST, .repr = &i64}; + int a = 1; + + while (offset < end) { + if ((align - (offset & align - 1)) & a) { + tmp = funcinst(func, IADD, &iptr, (struct value *[]){addr, mkintconst(&iptr, offset)}); + funcinst(func, store[a], NULL, (struct value *[]){&z, tmp}); + offset += a; + } + if (a < align) + a <<= 1; + } +} + +void +funcinit(struct function *func, struct declaration *d, struct initializer *init) +{ + struct value *src, *dst; + uint64_t offset = 0; + size_t i; + + funcalloc(func, d); + if (!init) + return; + for (; init; init = init->next) { + zero(func, d->value, d->type->align, offset, init->start); + if (init->expr->kind == EXPRSTRING) { + for (i = 0; i <= init->expr->string.size && i < init->end - init->start; ++i) { + dst = funcinst(func, IADD, &iptr, (struct value *[]){d->value, mkintconst(&iptr, init->start + i)}); + funcinst(func, ISTOREB, NULL, (struct value *[]){mkintconst(&i8, init->expr->string.data[i]), dst}); + } + } else { + dst = funcinst(func, IADD, &iptr, (struct value *[]){d->value, mkintconst(&iptr, init->start)}); + src = funcexpr(func, init->expr); + funcstore(func, init->expr->type, dst, src); + } + offset = init->end; + } + zero(func, d->value, d->type->align, offset, d->type->size); +} + +static void +casesearch(struct function *f, struct value *v, struct switchcase *c, struct value *defaultlabel) +{ + struct value *res, *label[3], *key; + + if (!c) { + funcjmp(f, defaultlabel); + return; + } + label[0] = mkblock("switch_lt"); + label[1] = mkblock("switch_ge"); + label[2] = mkblock("switch_gt"); + + // XXX: linear search if c->node.height < 4 + key = mkintconst(&i64, c->node.key); + res = funcinst(f, ICULTW, &i32, (struct value *[]){v, key}); + funcjnz(f, res, label[0], label[1]); + funclabel(f, label[0]); + casesearch(f, v, c->node.child[0], defaultlabel); + funclabel(f, label[1]); + res = funcinst(f, ICUGTW, typeint.repr, (struct value *[]){v, key}); + funcjnz(f, res, label[2], c->body); + funclabel(f, label[2]); + casesearch(f, v, c->node.child[1], defaultlabel); +} + +void +funcswitch(struct function *f, struct value *v, struct switchcases *c, struct value *defaultlabel) +{ + casesearch(f, v, c->root, defaultlabel); +} + +/* emit */ + +static void +emitname(struct name *n) +{ + if (n->str) + fputs(n->str, stdout); + if (n->id) + printf(".%" PRIu64, n->id); +} + +static void +emitvalue(struct value *v) +{ + static char sigil[] = { + [VALLABEL] = '@', + [VALTEMP] = '%', + [VALGLOBAL] = '$', + }; + + switch (v->kind) { + case VALCONST: + if (v->repr->base == 's' || v->repr->base == 'd') + printf("%c_%a", v->repr->base, v->f); + else + printf("%" PRIu64, v->i); + break; + case VALELLIPSIS: + fputs("...", stdout); + break; + default: + if (v->kind >= LEN(sigil) || !sigil[v->kind]) + fatal("invalid value"); + putchar(sigil[v->kind]); + if (v->kind == VALGLOBAL && v->name.id) + fputs(".L", stdout); + emitname(&v->name); + } +} + +static void +aggr(struct type *t) +{ + uint64_t i; + struct member *m; + + switch (t->kind) { + case TYPEARRAY: + if (typeprop(t->base) & PROPAGGR) { + for (i = 0; i < t->array.length; ++i) + aggr(t->base); + } else { + printf("%c %" PRIu64 ", ", t->base->repr->ext, t->array.length); + } + break; + case TYPESTRUCT: + arrayforeach (&t->structunion.members, m) { + if (typeprop(m->type) & PROPAGGR) + aggr(m->type); + else + printf("%c, ", m->type->repr->ext); + } + break; + default: + fatal("internal error; not an aggregate type"); + } +} + +static void +emittype(struct type *t) +{ + static uint64_t id; + + if (t->repr->abi.id || !(typeprop(t) & PROPAGGR)) + return; + t->repr = xmalloc(sizeof(*t->repr)); + t->repr->base = 'l'; + if (t->kind == TYPESTRUCT || t->kind == TYPEUNION) + t->repr->abi.str = t->structunion.tag; + t->repr->abi.id = ++id; + fputs("type :", stdout); + emitname(&t->repr->abi); + fputs(" = { ", stdout); + aggr(t); + puts("}"); +} + +static void +emitrepr(struct representation *r, _Bool abi) +{ + if (abi && r->abi.id) { + putchar(':'); + emitname(&r->abi); + } else { + putchar(r->base); + } +} + +static void +emitinst(struct instruction *inst) +{ + struct value **arg; + + putchar('\t'); + assert(inst->kind < LEN(instdesc)); + if (instdesc[inst->kind].ret && inst->res.kind) { + emitvalue(&inst->res); + fputs(" =", stdout); + emitrepr(inst->res.repr, inst->kind == ICALL); + putchar(' '); + } + fputs(instdesc[inst->kind].str, stdout); + switch (inst->kind) { + case ICALL: + putchar(' '); + emitvalue(inst->arg[0]); + putchar('('); + for (arg = &inst->arg[1]; *arg; ++arg) { + if (arg != &inst->arg[1]) + fputs(", ", stdout); + if ((*arg)->kind != VALELLIPSIS) { + emitrepr((*arg)->repr, true); + putchar(' '); + } + emitvalue(*arg); + } + putchar(')'); + break; + case IPHI: + putchar(' '); + for (arg = inst->arg; *arg; ++arg) { + if (arg != inst->arg) + fputs(", ", stdout); + emitvalue(*arg); + putchar(' '); + emitvalue(*++arg); + } + break; + case IRET: + if (!inst->arg[0]) + break; + /* fallthrough */ + default: + putchar(' '); + emitvalue(inst->arg[0]); + if (instdesc[inst->kind].arg > 1) { + fputs(", ", stdout); + emitvalue(inst->arg[1]); + } + if (instdesc[inst->kind].arg > 2) { + fputs(", ", stdout); + emitvalue(inst->arg[2]); + } + } + putchar('\n'); +} + +void +emitfunc(struct function *f, _Bool global) +{ + struct block *b; + struct instruction **inst; + struct parameter *p; + size_t n; + + if (!f->end->terminated) + funcret(f, NULL); + if (global) + puts("export"); + fputs("function ", stdout); + if (f->type->base != &typevoid) { + emitrepr(f->type->base->repr, true); + putchar(' '); + } + printf("$%s(", f->name); + for (p = f->type->func.params; p; p = p->next) { + if (p != f->type->func.params) + fputs(", ", stdout); + emitrepr(p->type->repr, true); + putchar(' '); + emitvalue(p->value); + } + if (f->type->func.isvararg) + fputs(", ...", stdout); + puts(") {"); + for (b = f->start; b; b = b->next) { + emitvalue(&b->label); + putchar('\n'); + n = b->insts.len / sizeof(*inst); + for (inst = b->insts.val; n; --n, ++inst) + emitinst(*inst); + } + puts("}"); +} + +static void +dataitem(struct expression *expr, uint64_t size) +{ + struct declaration *decl; + size_t i; + char c; + + switch (expr->kind) { + case EXPRUNARY: + if (expr->unary.op != TBAND) + fatal("not a address expr"); + expr = expr->unary.base; + if (expr->kind != EXPRIDENT) + error(&tok.loc, "initializer is not a constant expression"); + decl = expr->ident.decl; + if (decl->value->kind != VALGLOBAL) + fatal("not a global"); + emitvalue(decl->value); + break; + case EXPRBINARY: + if (expr->binary.l->kind != EXPRUNARY || expr->binary.r->kind != EXPRCONST) + error(&tok.loc, "initializer is not a constant expression"); + dataitem(expr->binary.l, 0); + fputs(" + ", stdout); + dataitem(expr->binary.r, 0); + break; + case EXPRCONST: + if (typeprop(expr->type) & PROPFLOAT) + printf("%c_%a", expr->type->size == 4 ? 's' : 'd', expr->constant.f); + else + printf("%" PRIu64, expr->constant.i); + break; + case EXPRSTRING: + fputc('"', stdout); + for (i = 0; i < expr->string.size && i < size; ++i) { + c = expr->string.data[i]; + if (isprint(c) && c != '"' && c != '\\') + putchar(c); + else + printf("\\%03hho", c); + } + fputc('"', stdout); + if (i < size) + printf(", z %" PRIu64, size - i); + break; + default: + fatal("unimplemented initdata"); + } +} + +void +emitdata(struct declaration *d, struct initializer *init) +{ + uint64_t offset = 0; + struct initializer *cur; + + if (!d->align) + d->align = d->type->align; + else if (d->align < d->type->align) + error(&tok.loc, "object requires alignment %d, which is stricter than %d", d->type->align, d->align); + for (cur = init; cur; cur = cur->next) + cur->expr = eval(cur->expr); + if (d->linkage == LINKEXTERN) + fputs("export ", stdout); + fputs("data ", stdout); + emitvalue(d->value); + printf(" = align %d { ", d->align); + + for (; init; offset = init->end, init = init->next) { + if (offset < init->start) + printf("z %" PRIu64 ", ", init->start - offset); + printf("%c ", init->expr->type->kind == TYPEARRAY ? init->expr->type->base->repr->ext : init->expr->type->repr->ext); + dataitem(init->expr, init->end - init->start); + fputs(", ", stdout); + } + assert(offset <= d->type->size); + if (offset < d->type->size) + printf("z %" PRIu64 " ", d->type->size - offset); + puts("}"); +} |