ref: ddb327c92ebe3f96c5c816ec61996440551de401
dir: /6/simp.c/
#include <stdlib.h> #include <stdio.h> #include <stdarg.h> #include <inttypes.h> #include <ctype.h> #include <string.h> #include <assert.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include "util.h" #include "parse.h" #include "mi.h" #include "asm.h" #include "../config.h" /* takes a list of nodes, and reduces it (and it's subnodes) to a list * following these constraints: * - All nodes are expression nodes * - Nodes with side effects are root nodes * - All nodes operate on machine-primitive types and tuples */ typedef struct Simp Simp; struct Simp { int isglobl; Node **stmts; size_t nstmts; /* return handling */ Node *endlbl; Node *ret; int hasenv; int isbigret; /* location handling */ Node **blobs; size_t nblobs; Htab *globls; Htab *stkoff; Htab *envoff; size_t stksz; Node **args; size_t nargs; }; static int envcmp(const void *pa, const void *pb); static Node *simp(Simp *s, Node *n); static Node *rval(Simp *s, Node *n, Node *dst); static Node *lval(Simp *s, Node *n); static Node *assign(Simp *s, Node *lhs, Node *rhs); static Node *assignat(Simp *s, Node *r, size_t off, Node *val); static Node *getcode(Simp *s, Node *n); static void simpconstinit(Simp *s, Node *dcl); static Node *simpcast(Simp *s, Node *val, Type *to); static Node *simpslice(Simp *s, Node *n, Node *dst); static Node *idxaddr(Simp *s, Node *seq, Node *idx); /* useful constants */ Type *tyintptr; Type *tyword; Type *tyvoid; Node *abortoob; static void append(Simp *s, Node *n) { if (debugopt['S']) dump(n, stdout); assert(n->type != Ndecl); lappend(&s->stmts, &s->nstmts, n); } static int ispure(Node *n) { return opispure[exprop(n)]; } size_t alignto(size_t sz, Type *t) { return align(sz, tyalign(t)); } static Type *base(Type *t) { assert(t->nsub == 1); return t->sub[0]; } static Node *add(Node *a, Node *b) { Node *n; assert(size(a) == size(b)); n = mkexpr(a->loc, Oadd, a, b, NULL); n->expr.type = a->expr.type; return n; } static Node *addk(Node *n, uvlong v) { Node *k; k = mkintlit(n->loc, v); k->expr.type = exprtype(n); return add(n, k); } static Node *sub(Node *a, Node *b) { Node *n; n = mkexpr(a->loc, Osub, a, b, NULL); n->expr.type = a->expr.type; return n; } static Node *mul(Node *a, Node *b) { Node *n; n = mkexpr(a->loc, Omul, a, b, NULL); n->expr.type = a->expr.type; return n; } static int addressable(Simp *s, Node *a) { if (a->type == Ndecl || (a->type == Nexpr && exprop(a) == Ovar)) return hthas(s->envoff, a) || hthas(s->stkoff, a) || hthas(s->globls, a); else return stacknode(a); } int stacknode(Node *n) { if (n->type == Nexpr) return isstacktype(n->expr.type); else return isstacktype(n->decl.type); } int floatnode(Node *n) { if (n->type == Nexpr) return istyfloat(n->expr.type); else return istyfloat(n->decl.type); } static void forcelocal(Simp *s, Node *n) { assert(n->type == Ndecl || (n->type == Nexpr && exprop(n) == Ovar)); s->stksz += size(n); s->stksz = align(s->stksz, min(size(n), Ptrsz)); if (debugopt['i']) { dump(n, stdout); printf("declared at %zd, size = %zd\n", s->stksz, size(n)); } htput(s->stkoff, n, itop(s->stksz)); } static void declarelocal(Simp *s, Node *n) { if (stacknode(n)) forcelocal(s, n); } /* takes the address of a node, possibly converting it to * a pointer to the base type 'bt' */ static Node *addr(Simp *s, Node *a, Type *bt) { Node *n; n = mkexpr(a->loc, Oaddr, a, NULL); if (!addressable(s, a)) forcelocal(s, a); if (!bt) n->expr.type = mktyptr(a->loc, a->expr.type); else n->expr.type = mktyptr(a->loc, bt); return n; } static Node *load(Node *a) { Node *n; assert(a->expr.type->type == Typtr); n = mkexpr(a->loc, Oderef, a, NULL); n->expr.type = base(a->expr.type); return n; } static Node *deref(Node *a, Type *t) { Node *n; assert(a->expr.type->type == Typtr); n = mkexpr(a->loc, Oderef, a, NULL); if (t) n->expr.type = t; else n->expr.type = base(a->expr.type); return n; } static Node *set(Node *a, Node *b) { Node *n; assert(a != NULL && b != NULL); assert(exprop(a) == Ovar || exprop(a) == Oderef); if (tybase(exprtype(a))->type == Tyvoid) return a; n = mkexpr(a->loc, Oset, a, b, NULL); n->expr.type = exprtype(a); return n; } static void def(Simp *s, Node *var) { Node *d; d = mkexpr(var->loc, Odef, var, NULL); d->expr.type = mktype(var->loc, Tyvoid); append(s, d); } static Node *disp(Srcloc loc, uint v) { Node *n; n = mkintlit(loc, v); n->expr.type = tyintptr; return n; } static Node *word(Srcloc loc, uint v) { Node *n; n = mkintlit(loc, v); n->expr.type = tyword; return n; } static Node *temp(Simp *simp, Node *e) { Node *t, *dcl; assert(e->type == Nexpr); t = gentemp(e->loc, e->expr.type, &dcl); if (stacknode(e)) declarelocal(simp, dcl); return t; } static void cjmp(Simp *s, Node *cond, Node *iftrue, Node *iffalse) { Node *jmp; jmp = mkexpr(cond->loc, Ocjmp, cond, iftrue, iffalse, NULL); append(s, jmp); } static Node *slicelen(Simp *s, Node *sl) { /* *(&sl + sizeof(size_t)) */ return load(addk(addr(s, rval(s, sl, NULL), tyintptr), Ptrsz)); } Node *loadvar(Simp *s, Node *n, Node *dst) { Node *p, *f, *r; if (isconstfn(n)) { if (dst) r = dst; else r = temp(s, n); f = getcode(s, n); p = addr(s, r, exprtype(n)); assignat(s, p, Ptrsz, f); } else { r = n; } return r; } static Node *seqlen(Simp *s, Node *n, Type *ty) { Node *t, *r; r = NULL; if (exprtype(n)->type == Tyslice) { t = slicelen(s, n); r = simpcast(s, t, ty); } else if (exprtype(n)->type == Tyarray) { t = exprtype(n)->asize; if (t) r = simpcast(s, t, ty); } return r; } static Node *uconid(Simp *s, Node *n) { Ucon *uc; n = rval(s, n, NULL); if (exprop(n) != Oucon) return load(addr(s, n, mktype(n->loc, Tyuint))); uc = finducon(exprtype(n), n->expr.args[0]); return word(uc->loc, uc->id); } static void simpblk(Simp *s, Node *n) { size_t i; for (i = 0; i < n->block.nstmts; i++) { n->block.stmts[i] = fold(n->block.stmts[i], 0); simp(s, n->block.stmts[i]); } } static Node *geninitdecl(Node *init, Type *ty, Node **dcl) { Node *n, *d, *r; char lbl[128]; n = mkname(init->loc, genlblstr(lbl, 128, "")); d = mkdecl(init->loc, n, ty); r = mkexpr(init->loc, Ovar, n, NULL); d->decl.init = init; d->decl.type = ty; d->decl.isconst = 1; d->decl.isglobl = 1; r->expr.did = d->decl.did; r->expr.type = ty; r->expr.isconst = 1; if (dcl) *dcl = d; return r; } static Node *simpcode(Simp *s, Node *fn) { Node *r, *d; r = geninitdecl(fn, codetype(exprtype(fn)), &d); htput(s->globls, d, asmname(d)); lappend(&file->file.stmts, &file->file.nstmts, d); return r; } static Node *simpblob(Simp *s, Node *blob) { Node *r, *d; r = geninitdecl(blob, exprtype(blob), &d); htput(s->globls, d, asmname(d)); lappend(&s->blobs, &s->nblobs, d); return r; } static Node *ptrsized(Simp *s, Node *v) { if (size(v) == Ptrsz) return v; else if (size(v) < Ptrsz) v = mkexpr(v->loc, Ozwiden, v, NULL); else if (size(v) > Ptrsz) v = mkexpr(v->loc, Otrunc, v, NULL); v->expr.type = tyintptr; return v; } static Node *membaddr(Simp *s, Node *n) { Node *t, *u, *r; Node **args; Type *ty; args = n->expr.args; ty = tybase(exprtype(args[0])); if (ty->type == Typtr) { t = lval(s, args[0]); } else { t = addr(s, lval(s, args[0]), exprtype(n)); } u = disp(n->loc, offset(args[0], args[1])); r = add(t, u); r->expr.type = mktyptr(n->loc, n->expr.type); return r; } static void checkidx(Simp *s, Op op, Node *len, Node *idx) { Node *cmp, *die; Node *ok, *fail; if (!len) return; /* create expressions */ cmp = mkexpr(idx->loc, op, ptrsized(s, idx), ptrsized(s, len), NULL); cmp->expr.type = mktype(len->loc, Tybool); ok = genlbl(len->loc); fail = genlbl(len->loc); die = mkexpr(idx->loc, Ocall, abortoob, NULL); die->expr.type = mktype(len->loc, Tyvoid); /* insert them */ cjmp(s, cmp, ok, fail); append(s, fail); append(s, die); append(s, ok); } static Node *idxaddr(Simp *s, Node *seq, Node *idx) { Node *a, *t, *u, *v, *w; /* temps */ Node *r; /* result */ Type *ty, *seqty; size_t sz; a = rval(s, seq, NULL); seqty = tybase(exprtype(seq)); ty = seqty->sub[0]; if (seqty->type == Tyarray) { t = addr(s, a, ty); w = seqty->asize; } else if (seqty->type == Tyslice) { t = load(addr(s, a, mktyptr(seq->loc, ty))); w = slicelen(s, a); } else { die("can't index type %s", tystr(seq->expr.type)); } assert(exprtype(t)->type == Typtr); u = rval(s, idx, NULL); u = ptrsized(s, u); checkidx(s, Olt, w, u); sz = tysize(ty); v = mul(u, disp(seq->loc, sz)); r = add(t, v); return r; } static Node *slicebase(Simp *s, Node *n, Node *off) { Node *u, *v; Type *ty; int sz; u = NULL; ty = tybase(exprtype(n)); switch (ty->type) { case Typtr: u = n; break; case Tyslice: u = load(addr(s, n, mktyptr(n->loc, base(exprtype(n))))); break; case Tyarray: u = addr(s, n, base(exprtype(n))); break; default: die("Unslicable type %s", tystr(n->expr.type)); } /* safe: all types we allow here have a sub[0] that we want to grab */ if (off) { off = ptrsized(s, rval(s, off, NULL)); sz = tysize(n->expr.type->sub[0]); v = mul(off, disp(n->loc, sz)); return add(u, v); } else { return u; } } static Node *loadidx(Simp *s, Node *arr, Node *idx) { Node *v, *a; a = rval(s, arr, NULL); v = deref(idxaddr(s, a, idx), NULL); return v; } static Node *lval(Simp *s, Node *n) { Node *r; Node **args; args = n->expr.args; switch (exprop(n)) { case Ovar: r = loadvar(s, n, NULL); break; case Oidx: r = loadidx(s, args[0], args[1]); break; case Oderef: r = deref(rval(s, args[0], NULL), NULL); break; case Omemb: r = rval(s, n, NULL); break; case Ostruct: r = rval(s, n, NULL); break; case Oucon: r = rval(s, n, NULL); break; case Oarr: r = rval(s, n, NULL); break; case Ogap: r = temp(s, n); break; /* not actually expressible as lvalues in syntax, but we generate them */ case Oudata: r = rval(s, n, NULL); break; case Outag: r = rval(s, n, NULL); break; case Otupget: r = rval(s, n, NULL); break; default: fatal(n, "%s cannot be an lvalue", opstr[exprop(n)]); break; } return r; } static Node *intconvert(Simp *s, Node *from, Type *to, int issigned) { Node *r; size_t fromsz, tosz; fromsz = size(from); tosz = tysize(to); r = rval(s, from, NULL); if (fromsz > tosz) { r = mkexpr(from->loc, Otrunc, r, NULL); } else if (tosz > fromsz) { if (issigned) r = mkexpr(from->loc, Oswiden, r, NULL); else r = mkexpr(from->loc, Ozwiden, r, NULL); } r->expr.type = to; return r; } static Node *simpcast(Simp *s, Node *val, Type *to) { Node *r; Type *t; r = NULL; t = tybase(exprtype(val)); if (tyeq(tybase(to), tybase(t))) { r = rval(s, val, NULL); r->expr.type = to; return r; } /* do the type conversion */ switch (tybase(to)->type) { case Tybool: case Tyint8: case Tyint16: case Tyint32: case Tyint64: case Tyuint8: case Tyuint16: case Tyuint32: case Tyuint64: case Tyint: case Tyuint: case Tychar: case Tybyte: case Typtr: switch (t->type) { /* ptr -> slice conversion is disallowed */ case Tyslice: /* FIXME: we should only allow casting to pointers. */ if (tysize(to) != Ptrsz) fatal(val, "bad cast from %s to %s", tystr(exprtype(val)), tystr(to)); val = rval(s, val, NULL); r = slicebase(s, val, NULL); break; case Tyfunc: if (to->type != Typtr) fatal(val, "bad cast from %s to %s", tystr(exprtype(val)), tystr(to)); r = getcode(s, val); break; /* signed conversions */ case Tyint8: case Tyint16: case Tyint32: case Tyint64: case Tyint: r = intconvert(s, val, to, 1); break; /* unsigned conversions */ case Tybool: case Tyuint8: case Tyuint16: case Tyuint32: case Tyuint64: case Tyuint: case Tychar: case Tybyte: case Typtr: r = intconvert(s, val, to, 0); break; case Tyflt32: case Tyflt64: if (tybase(to)->type == Typtr) fatal(val, "bad cast from %s to %s", tystr(exprtype(val)), tystr(to)); r = mkexpr(val->loc, Oflt2int, rval(s, val, NULL), NULL); r->expr.type = to; break; default: fatal(val, "bad cast from %s to %s", tystr(exprtype(val)), tystr(to)); } break; case Tyflt32: case Tyflt64: switch (t->type) { case Tyint8: case Tyint16: case Tyint32: case Tyint64: case Tyuint8: case Tyuint16: case Tyuint32: case Tyuint64: case Tyint: case Tyuint: case Tychar: case Tybyte: r = mkexpr(val->loc, Oint2flt, rval(s, val, NULL), NULL); r->expr.type = to; break; case Tyflt32: case Tyflt64: r = mkexpr(val->loc, Oflt2flt, rval(s, val, NULL), NULL); r->expr.type = to; break; default: fatal(val, "bad cast from %s to %s", tystr(exprtype(val)), tystr(to)); break; } break; /* no other destination types are handled as things stand */ default: fatal(val, "bad cast from %s to %s", tystr(exprtype(val)), tystr(to)); } return r; } /* Simplifies taking a slice of an array, pointer, * or other slice down to primitive pointer operations */ static Node *simpslice(Simp *s, Node *n, Node *dst) { Node *t; Node *start, *end, *arg; Node *seq, *base, *sz, *len, *max; Node *stbase, *stlen; if (dst) t = dst; else t = temp(s, n); arg = n->expr.args[0]; if (exprop(arg) == Oarr && arg->expr.nargs == 0) { seq = arg; base = mkintlit(n->loc, 0); base->expr.type = tyintptr; } else { /* *(&slice) = (void*)base + off*sz */ seq = rval(s, arg, NULL); base = slicebase(s, seq, n->expr.args[1]); } start = ptrsized(s, rval(s, n->expr.args[1], NULL)); end = ptrsized(s, rval(s, n->expr.args[2], NULL)); len = sub(end, start); /* we can be storing through a pointer, in the case * of '*foo = bar'. */ max = seqlen(s, seq, tyword); if (max) checkidx(s, Ole, max, end); if (tybase(exprtype(t))->type == Typtr) { stbase = set(simpcast(s, t, mktyptr(t->loc, tyintptr)), base); sz = addk(simpcast(s, t, mktyptr(t->loc, tyintptr)), Ptrsz); } else { stbase = set(deref(addr(s, t, tyintptr), NULL), base); sz = addk(addr(s, t, tyintptr), Ptrsz); } /* *(&slice + ptrsz) = len */ stlen = set(deref(sz, NULL), len); append(s, stbase); append(s, stlen); return t; } static Node *visit(Simp *s, Node *n) { size_t i; Node *r; for (i = 0; i < n->expr.nargs; i++) n->expr.args[i] = rval(s, n->expr.args[i], NULL); if (ispure(n)) { r = n; } else { if (exprtype(n)->type == Tyvoid) { r = NULL; append(s, n); } else { r = temp(s, n); append(s, set(r, n)); } } return r; } static Node *tupget(Simp *s, Node *tup, size_t idx, Node *dst) { Node *plv, *prv, *sz, *stor, *dcl; size_t off, i; Type *ty; off = 0; ty = exprtype(tup); for (i = 0; i < ty->nsub; i++) { off = alignto(off, ty->sub[i]); if (i == idx) break; off += tysize(ty->sub[i]); } if (!dst) { dst = gentemp(tup->loc, ty->sub[idx], &dcl); if (isstacktype(ty->sub[idx])) declarelocal(s, dcl); } prv = add(addr(s, tup, ty->sub[i]), disp(tup->loc, off)); if (stacknode(dst)) { sz = disp(dst->loc, size(dst)); plv = addr(s, dst, exprtype(dst)); stor = mkexpr(dst->loc, Oblit, plv, prv, sz, NULL); } else { stor = set(dst, load(prv)); } append(s, stor); return dst; } static Node *assign(Simp *s, Node *lhs, Node *rhs) { Node *t, *u, *v, *r; t = lval(s, lhs); u = rval(s, rhs, t); if (tybase(exprtype(lhs))->type == Tyvoid) return u; /* hack: we're assigning to lhs, but blitting shit over doesn't * trigger that */ if (stacknode(lhs)) def(s, lhs); /* if we stored the result into t, rval() should return that, * so we know our work is done. */ if (u == t) { r = t; } else if (stacknode(lhs)) { t = addr(s, t, exprtype(lhs)); u = addr(s, u, exprtype(lhs)); v = disp(lhs->loc, size(lhs)); r = mkexpr(lhs->loc, Oblit, t, u, v, NULL); } else { r = set(t, u); } return r; } static Node *assignat(Simp *s, Node *r, size_t off, Node *val) { Node *pval, *pdst; Node *sz; Node *st; pdst = add(r, disp(val->loc, off)); if (stacknode(val)) { sz = disp(val->loc, size(val)); pval = addr(s, val, exprtype(val)); st = mkexpr(val->loc, Oblit, pdst, pval, sz, NULL); } else { st = set(deref(pdst, val->expr.type), val); } append(s, st); return r; } /* Simplify tuple construction to a stack allocated * value by evaluating the rvalue of each node on the * rhs and assigning it to the correct offset from the * head of the tuple. */ static Node *simptup(Simp *s, Node *n, Node *dst) { Node **args; Node *r; size_t i, off; args = n->expr.args; if (!dst) dst = temp(s, n); r = addr(s, dst, exprtype(dst)); off = 0; for (i = 0; i < n->expr.nargs; i++) { off = alignto(off, exprtype(args[i])); assignat(s, r, off, rval(s, args[i], NULL)); off += size(args[i]); } return dst; } static Node *simpucon(Simp *s, Node *n, Node *dst) { Node *tmp, *u, *tag, *elt, *sz; Node *r; Type *ty; Ucon *uc; size_t i, o; /* find the ucon we're constructing here */ ty = tybase(n->expr.type); uc = NULL; for (i = 0; i < ty->nmemb; i++) { if (!strcmp(namestr(n->expr.args[0]), namestr(ty->udecls[i]->name))) { uc = ty->udecls[i]; break; } } if (!uc) die("Couldn't find union constructor"); if (dst) tmp = dst; else tmp = temp(s, n); /* Set the tag on the ucon */ u = addr(s, tmp, mktype(n->loc, Tyuint)); tag = mkintlit(n->loc, uc->id); tag->expr.type = mktype(n->loc, Tyuint); append(s, set(deref(u, tyword), tag)); /* fill the value, if needed */ if (!uc->etype) return tmp; elt = rval(s, n->expr.args[1], NULL); o = alignto(Wordsz, uc->etype); u = addk(u, o); if (isstacktype(uc->etype)) { elt = addr(s, elt, uc->etype); sz = disp(n->loc, tysize(uc->etype)); r = mkexpr(n->loc, Oblit, u, elt, sz, NULL); } else { r = set(deref(u, uc->etype), elt); } append(s, r); return tmp; } static Node *simpuget(Simp *s, Node *n, Node *dst) { Node *u, *p, *l; size_t o; if (!dst) dst = temp(s, n); u = rval(s, n->expr.args[0], NULL); o = alignto(Wordsz, exprtype(n)); p = addk(addr(s, u, exprtype(n)), o); l = assign(s, dst, load(p)); append(s, l); return dst; } static Node *vatypeinfo(Simp *s, Node *n) { Node *ti, *tp, *td, *tn; Type *ft, *vt, **st; size_t nst, i; char buf[1024]; st = NULL; nst = 0; ft = exprtype(n->expr.args[0]); /* The structure of ft->sub: * [return, normal, args, ...] * * The structure of n->expr.sub: * [fn, normal, args, , variadic, args] * * We want to start at variadic, so we want * to count from ft->nsub - 1, up to n->expr.nsub. */ for (i = ft->nsub - 1; i < n->expr.nargs; i++) { lappend(&st, &nst, exprtype(n->expr.args[i])); } vt = mktytuple(n->loc, st, nst); tagreflect(vt); /* make the decl */ tn = mkname(Zloc, tydescid(buf, sizeof buf, vt)); td = mkdecl(Zloc, tn, mktype(n->loc, Tybyte)); td->decl.isglobl = 1; td->decl.isconst = 1; td->decl.ishidden = 1; /* and the var */ ti = mkexpr(Zloc, Ovar, tn, NULL); ti->expr.type = td->decl.type; ti->expr.did = td->decl.did; /* and the pointer */ tp = mkexpr(Zloc, Oaddr, ti, NULL); tp->expr.type = mktyptr(n->loc, td->decl.type); htput(s->globls, td, asmname(td)); return tp; } static Node *capture(Simp *s, Node *n, Node *dst) { Node *fn, *t, *f, *e, *val, *dcl, *fp, *envsz; size_t nenv, nenvt, off, i; Type **envt; Node **env; f = simpcode(s, n); fn = n->expr.args[0]; fn = fn->lit.fnval; if (!dst) { dst = gentemp(n->loc, closuretype(exprtype(f)), &dcl); forcelocal(s, dcl); } fp = addr(s, dst, exprtype(dst)); env = getclosure(fn->func.scope, &nenv); if (env) { /* we need these in a deterministic order so that we can put them in the right place both when we use them and when we capture them. */ qsort(env, nenv, sizeof(Node*), envcmp); /* make the tuple that will hold the environment */ envt = NULL; nenvt = 0; /* reserve space for size */ lappend(&envt, &nenvt, tyintptr); for (i = 0; i < nenv; i++) lappend(&envt, &nenvt, decltype(env[i])); t = gentemp(n->loc, mktytuple(n->loc, envt, nenvt), &dcl); forcelocal(s, dcl); e = addr(s, t, exprtype(t)); off = Ptrsz; /* we start with the size of the env */ for (i = 0; i < nenv; i++) { off = alignto(off, decltype(env[i])); val = mkexpr(n->loc, Ovar, env[i]->decl.name, NULL); val->expr.type = env[i]->decl.type; val->expr.did = env[i]->decl.did; assignat(s, e, off, rval(s, val, NULL)); off += size(env[i]); } free(env); envsz = mkintlit(n->loc, off); envsz->expr.type = tyintptr; assignat(s, e, 0, envsz); assignat(s, fp, 0, e); } assignat(s, fp, Ptrsz, f); return dst; } static Node *getenvptr(Simp *s, Node *n) { assert(tybase(exprtype(n))->type == Tyfunc); return load(addr(s, n, tyintptr)); } static Node *getcode(Simp *s, Node *n) { Node *r, *p, *d; Type *ty; if (isconstfn(n)) { d = decls[n->expr.did]; r = mkexpr(n->loc, Ovar, n->expr.args[0], NULL); r->expr.did = d->decl.did; r->expr.type = codetype(exprtype(n)); } else { ty = tybase(exprtype(n)); assert(ty->type == Tyfunc); p = addr(s, rval(s, n, NULL), codetype(ty)); r = load(addk(p, Ptrsz)); } return r; } static Node *simpcall(Simp *s, Node *n, Node *dst) { Node *r, *call, *fn; size_t i, nargs; Node **args; Type *ft; Op op; /* NB: If we called rval() on a const function, we would end up with a stack allocated closure. We don't want to do this. */ fn = n->expr.args[0]; if (!isconstfn(fn)) fn = rval(s, fn, NULL); ft = tybase(exprtype(fn)); if (exprtype(n)->type == Tyvoid) r = NULL; else if (isstacktype(exprtype(n)) && dst) r = dst; else r = temp(s, n); args = NULL; nargs = 0; op = Ocall; lappend(&args, &nargs, getcode(s, fn)); if (!isconstfn(fn)) { op = Ocallind; lappend(&args, &nargs, getenvptr(s, fn)); } if (exprtype(n)->type != Tyvoid && isstacktype(exprtype(n))) lappend(&args, &nargs, addr(s, r, exprtype(n))); for (i = 1; i < n->expr.nargs; i++) { if (i < ft->nsub && tybase(ft->sub[i])->type == Tyvalist) lappend(&args, &nargs, vatypeinfo(s, n)); if (tybase(exprtype(n->expr.args[i]))->type == Tyvoid) continue; lappend(&args, &nargs, rval(s, n->expr.args[i], NULL)); if (exprop(n->expr.args[i]) == Oaddr) if (exprop(n->expr.args[i]->expr.args[0]) == Ovar) def(s, n->expr.args[i]->expr.args[0]); } if (i < ft->nsub && tybase(ft->sub[i])->type == Tyvalist) lappend(&args, &nargs, vatypeinfo(s, n)); if (r) def(s, r); call = mkexprl(n->loc, op, args, nargs); call->expr.type = exprtype(n); if (r && !isstacktype(exprtype(n))) { append(s, set(r, call)); } else { append(s, call); } return r; } static Node *rval(Simp *s, Node *n, Node *dst) { Node *t, *u, *v; /* temporary nodes */ Node *r; /* expression result */ Node **args; size_t i; Type *ty; r = NULL; args = n->expr.args; switch (exprop(n)) { case Osize: r = mkintlit(n->loc, size(args[0])); r->expr.type = exprtype(n); break; case Oslice: r = simpslice(s, n, dst); break; case Oidx: t = rval(s, n->expr.args[0], NULL); u = idxaddr(s, t, n->expr.args[1]); r = load(u); break; /* array.len slice.len are magic 'virtual' members. * they need to be special cased. */ case Omemb: t = membaddr(s, n); r = load(t); break; case Osllen: r = seqlen(s, args[0], exprtype(n)); break; case Oucon: r = simpucon(s, n, dst); break; case Outag: r = uconid(s, args[0]); break; case Oudata: r = simpuget(s, n, dst); break; case Otup: r = simptup(s, n, dst); break; case Oarr: if (!dst) dst = temp(s, n); t = addr(s, dst, exprtype(dst)); for (i = 0; i < n->expr.nargs; i++) assignat(s, t, size(n->expr.args[i])*i, rval(s, n->expr.args[i], NULL)); r = dst; break; case Ostruct: if (!dst) dst = temp(s, n); u = mkexpr(dst->loc, Odef, dst, NULL); u->expr.type = mktype(u->loc, Tyvoid); append(s, u); t = addr(s, dst, exprtype(dst)); ty = exprtype(n); /* we only need to clear if we don't have things fully initialized */ if (tybase(ty)->nmemb != n->expr.nargs) append(s, mkexpr(n->loc, Oclear, t, mkintlit(n->loc, size(n)), NULL)); for (i = 0; i < n->expr.nargs; i++) assignat(s, t, offset(n, n->expr.args[i]->expr.idx), rval(s, n->expr.args[i], NULL)); r = dst; break; case Ocast: r = simpcast(s, args[0], exprtype(n)); break; /* ++expr(x) * => args[0] = args[0] + 1 * expr(x) */ case Olit: switch (args[0]->lit.littype) { case Lvoid: case Lchr: case Lbool: case Llbl: r = n; break; case Lint: /* we can only have up to 4 byte immediates, but they * can be moved into 64 bit regs */ if ((uint64_t)args[0]->lit.intval < 0x7fffffffULL) r = n; else r = simpblob(s, n); break; case Lstr: case Lflt: r = simpblob(s, n); break; case Lfunc: r = capture(s, n, dst); break; } break; case Ovar: r = loadvar(s, n, dst); break;; case Ogap: fatal(n, "'_' may not be an rvalue"); break; case Oret: if (s->isbigret) { t = rval(s, args[0], NULL); t = addr(s, t, exprtype(args[0])); u = disp(n->loc, size(args[0])); v = mkexpr(n->loc, Oblit, s->ret, t, u, NULL); append(s, v); } else { t = s->ret; u = rval(s, args[0], NULL); /* void calls return nothing */ if (t) { t = set(t, u); append(s, t); } } append(s, mkexpr(n->loc, Oret, NULL)); break; case Oasn: r = assign(s, args[0], args[1]); break; case Ocall: r = simpcall(s, n, dst); break; case Oaddr: t = lval(s, args[0]); if (exprop(t) == Ovar) /* Ovar is the only one that doesn't return Oderef(Oaddr(...)) */ r = addr(s, t, exprtype(t)); else r = t->expr.args[0]; break; case Oneg: if (istyfloat(exprtype(n))) { t = mkfloat(n->loc, -1.0); u = mkexpr(n->loc, Olit, t, NULL); t->lit.type = n->expr.type; u->expr.type = n->expr.type; v = simpblob(s, u); r = mkexpr(n->loc, Ofmul, v, rval(s, args[0], NULL), NULL); r->expr.type = n->expr.type; } else { r = visit(s, n); } break; case Otupget: assert(exprop(args[1]) == Olit); i = args[1]->expr.args[0]->lit.intval; t = rval(s, args[0], NULL); r = tupget(s, t, i, dst); break; case Olor: case Oland: case Oaddeq: case Osubeq: case Omuleq: case Odiveq: case Omodeq: case Oboreq: case Obandeq: case Obxoreq: case Obsleq: case Obsreq: case Opreinc: case Opredec: case Opostinc: case Opostdec: die("invalid operator: should have been removed"); case Obad: die("bad operator"); break; default: if (istyfloat(exprtype(n))) { switch (exprop(n)) { case Oadd: n->expr.op = Ofadd; break; case Osub: n->expr.op = Ofsub; break; case Omul: n->expr.op = Ofmul; break; case Odiv: n->expr.op = Ofdiv; break; default: break; } } r = visit(s, n); break; } return r; } static void declarearg(Simp *s, Node *n) { assert(n->type == Ndecl || (n->type == Nexpr && exprop(n) == Ovar)); lappend(&s->args, &s->nargs, n); } static int islbl(Node *n) { Node *l; if (exprop(n) != Olit) return 0; l = n->expr.args[0]; return l->type == Nlit && l->lit.littype == Llbl; } static Node *simp(Simp *s, Node *n) { Node *r; if (!n) return NULL; r = NULL; switch (n->type) { case Nblock: simpblk(s, n); break; case Ndecl: declarelocal(s, n); break; case Nexpr: if (islbl(n)) append(s, n); else r = rval(s, n, NULL); if (r) append(s, r); break; default: dump(n, stderr); die("bad node passsed to simp()"); break; } return r; } /* * Turns a deeply nested function body into a flatter * and simpler representation, which maps easily and * directly to assembly instructions. */ static void simpinit(Simp *s, Node *f) { Node *dcl; Type *ty; size_t i; assert(f->type == Nfunc); s->nstmts = 0; s->stmts = NULL; s->endlbl = genlbl(f->loc); s->ret = NULL; /* make a temp for the return type */ ty = f->func.type->sub[0]; if (isstacktype(ty)) { s->isbigret = 1; s->ret = gentemp(f->loc, mktyptr(f->loc, ty), &dcl); declarearg(s, dcl); } else if (tybase(ty)->type != Tyvoid) { s->isbigret = 0; s->ret = gentemp(f->loc, ty, &dcl); } for (i = 0; i < f->func.nargs; i++) { declarearg(s, f->func.args[i]); } simp(s, f->func.body); append(s, s->endlbl); } static int isexport(Node *dcl) { Node *n; /* Vishidden should also be exported. */ if (dcl->decl.vis != Visintern) return 1; n = dcl->decl.name; if (!n->name.ns && streq(n->name.name, "main")) return 1; if (streq(n->name.name, "__init__")) return 1; return 0; } static int envcmp(const void *pa, const void *pb) { const Node *a, *b; a = *(const Node**)pa; b = *(const Node**)pb; return b->decl.did - a->decl.did; } static void collectenv(Simp *s, Node *fn) { size_t nenv, i; Node **env; size_t off; env = getclosure(fn->func.scope, &nenv); if (!env) return; /* we need these in a deterministic order so that we can put them in the right place both when we use them and when we capture them. */ s->hasenv = 1; qsort(env, nenv, sizeof(Node*), envcmp); off = Ptrsz; /* we start with the size of the env */ for (i = 0; i < nenv; i++) { off = alignto(off, decltype(env[i])); htput(s->envoff, env[i], itop(off)); off += size(env[i]); } free(env); } static Func *simpfn(Simp *s, char *name, Node *dcl) { Node *n; size_t i; Func *fn; Cfg *cfg; n = dcl->decl.init; if(debugopt['i'] || debugopt['F'] || debugopt['f']) printf("\n\nfunction %s\n", name); /* set up the simp context */ /* unwrap to the function body */ n = n->expr.args[0]; n = n->lit.fnval; collectenv(s, n); simpinit(s, n); if (debugopt['f'] || debugopt['F']) for (i = 0; i < s->nstmts; i++) dump(s->stmts[i], stdout); for (i = 0; i < s->nstmts; i++) { if (s->stmts[i]->type != Nexpr) continue; if (debugopt['f']) { printf("FOLD FROM ----------\n"); dump(s->stmts[i], stdout); } s->stmts[i] = fold(s->stmts[i], 0); if (debugopt['f']) { printf("TO ------------\n"); dump(s->stmts[i], stdout); printf("DONE ----------------\n"); } } cfg = mkcfg(dcl, s->stmts, s->nstmts); check(cfg); if (debugopt['t'] || debugopt['s']) dumpcfg(cfg, stdout); fn = zalloc(sizeof(Func)); fn->name = strdup(name); fn->loc = dcl->loc; fn->type = dcl->decl.type; fn->isexport = isexport(dcl); fn->stksz = align(s->stksz, 8); fn->stkoff = s->stkoff; fn->envoff = s->envoff; fn->ret = s->ret; fn->args = s->args; fn->nargs = s->nargs; fn->cfg = cfg; fn->hasenv = s->hasenv; return fn; } static void extractsub(Simp *s, Node *e) { size_t i; Node *sub; assert(e != NULL); switch (exprop(e)) { case Oslice: sub = e->expr.args[0]; if (exprop(sub) == Oarr) { if (sub->expr.nargs > 0) { e->expr.args[0] = simpblob(s, e->expr.args[0]); } else { e->expr.args[0] = mkintlit(e->loc, 0); e->expr.args[0]->expr.type = tyintptr; } } break; case Oarr: case Ostruct: for (i = 0; i < e->expr.nargs; i++) extractsub(s, e->expr.args[i]); break; case Oucon: if (e->expr.nargs == 2) extractsub(s, e->expr.args[1]); break; default: break; } } static void simpconstinit(Simp *s, Node *dcl) { Node *e; dcl->decl.init = fold(dcl->decl.init, 1);; e = dcl->decl.init; if (e && exprop(e) == Olit) { if (e->expr.args[0]->lit.littype == Lfunc) simpcode(s, e); else lappend(&s->blobs, &s->nblobs, dcl); } else if (!dcl->decl.isconst && !e) { lappend(&s->blobs, &s->nblobs, dcl); } else if (e->expr.isconst) { switch (exprop(e)) { case Oarr: case Ostruct: case Oslice: case Oucon: extractsub(s, e); lappend(&s->blobs, &s->nblobs, dcl); break; default: fatal(dcl, "unsupported initializer for %s", declname(dcl)); break; } } else { die("Non-constant initializer for %s\n", declname(dcl)); } } void simpglobl(Node *dcl, Htab *globls, Func ***fn, size_t *nfn, Node ***blob, size_t *nblob) { Simp s = {0,}; char *name; Func *f; s.stkoff = mkht(varhash, vareq); s.envoff = mkht(varhash, vareq); s.globls = globls; s.blobs = *blob; s.nblobs = *nblob; s.hasenv = 0; if (dcl->decl.isextern || dcl->decl.isgeneric) return; name = asmname(dcl); if (isconstfn(dcl)) { f = simpfn(&s, name, dcl); lappend(fn, nfn, f); } else { simpconstinit(&s, dcl); } *blob = s.blobs; *nblob = s.nblobs; free(name); }