ref: dda657081b032dc653f669ca85f2c09984f0cb35
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 node
* - Nodes with side effects are root node
* - All nodes operate on machine-primitive types and tuple
*/
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 (n->type == Nexpr)
n = decls[n->decl.did];
if (n->decl.isconst && n->decl.isextern)
htput(s->globls, n, asmname(n));
else 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 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) {
if (isenum(tybase(exprtype(n))))
return n;
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);
r->decl.isconst = 1;
r->decl.isglobl = 1;
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, *jmp;
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 */
jmp = mkexpr(idx->loc, Ocjmp, cmp, ok, fail, NULL);
append(s, jmp);
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;
/*
not expressible as lvalues in syntax, but
we take their address in matches
*/
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 o;
/* find the ucon we're constructing here */
ty = tybase(exprtype(n));
uc = finducon(ty, n->expr.args[0]);
if (!uc)
die("Couldn't find union constructor");
if (dst)
tmp = dst;
else
tmp = temp(s, n);
if (isenum(ty)) {
/* enums are treated as integers
* by the backend */
append(s, set(tmp, n));
return tmp;
}
/* 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;
s->stmts[i] = fold(s->stmts[i], 0);
}
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)
dcl->decl.init = simpcode(s, e);
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:
case Ovar:
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);
}