static Node *assign(Simp *s, Node *lhs, Node *rhs) { Node *t, *u, *v, *r; if (exprop(lhs) == Otup) { r = destructure(s, lhs, rhs); } else { t = lval(s, lhs); u = rval(s, rhs, t); /* 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->line, size(lhs)); r = mkexpr(lhs->line, Oblit, t, u, v, NULL); } else { r = set(t, u); } } return r; }
static Node *slicebase(Simp *s, Node *n, Node *off) { Node *t, *u, *v; Type *ty; int sz; t = rval(s, n, NULL); u = NULL; ty = tybase(exprtype(n)); switch (ty->type) { case Typtr: u = t; break; case Tyarray: u = addr(s, t, base(exprtype(n))); break; case Tyslice: u = load(addr(s, t, mktyptr(n->line, 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->line, sz)); return add(u, v); } else { return u; } }
static Node *foldcast(Node *n) { Type *to, *from; Node *sub; sub = n->expr.args[0]; to = exprtype(n); from = exprtype(sub); 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 (tybase(from)->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: if (exprop(sub) == Olit || tybase(from)->type == tybase(to)->type) { sub->expr.type = to; return sub; } else { return n; } default: return n; } default: return n; } return n; }
/* Takes a tuple and binds the i'th element of it to the * i'th name on the rhs of the assignment. */ static Node *destructure(Simp *s, Node *lhs, Node *rhs) { Node *plv, *prv, *lv, *sz, *stor, **args; size_t off, i; args = lhs->expr.args; rhs = rval(s, rhs, NULL); off = 0; for (i = 0; i < lhs->expr.nargs; i++) { lv = lval(s, args[i]); off = tyalign(off, size(lv)); prv = add(addr(s, rhs, exprtype(args[i])), disp(rhs->line, off)); if (stacknode(args[i])) { sz = disp(lhs->line, size(lv)); plv = addr(s, lv, exprtype(lv)); stor = mkexpr(lhs->line, Oblit, plv, prv, sz, NULL); } else { stor = set(lv, load(prv)); } append(s, stor); off += size(lv); } return NULL; }
static Node * comparecomplex(Flattenctx *s, Node *n, Op op) { Type *ty; Node *l, *r, *e; /* special case: unions with nullary constructors can be compared easily. */ ty = tybase(exprtype(n->expr.args[0])); if (ty->type == Tyunion && isenum(ty)) { l = mkexpr(n->loc, Outag, rval(s, n->expr.args[0]), NULL); r = mkexpr(n->loc, Outag, rval(s, n->expr.args[1]), NULL); l->expr.type = mktype(n->loc, Tyuint32); r->expr.type = mktype(n->loc, Tyuint32); if (op == Oeq) e = mkexpr(n->loc, Oueq, l, r, NULL); else if (op == One) e = mkexpr(n->loc, Oune, l, r, NULL); else fatal(n, "unsupported operator %s for enum union", opstr[op]); e->expr.type = mktype(n->loc, Tybool); return e; } fatal(n, "cannot compare values of type %s for equality\n", tystr(exprtype(n->expr.args[0]))); return NULL; }
static Node * asn(Node *a, Node *b) { Node *n; assert(a != NULL && b != NULL); if (tybase(exprtype(a))->type == Tyvoid) return a; n = mkexpr(a->loc, Oasn, a, b, NULL); n->expr.type = exprtype(a); return n; }
static Node *seqlen(Simp *s, Node *n, Type *ty) { Node *t, *r; if (exprtype(n)->type == Tyslice) { t = slicelen(s, n); r = simpcast(s, t, ty); } else if (exprtype(n)->type == Tyarray) { t = exprtype(n)->asize; r = simpcast(s, t, ty); } else { r = NULL; } return r; }
Node *gensimpmatch(Node *m) { Dtree *t, *leaf; Node **pat, **cap; size_t npat, ncap; size_t i; Node *n; pat = m->matchstmt.matches; npat = m->matchstmt.nmatches; t = mkdtree(); for (i = 0; i < npat; i++) { cap = NULL; ncap = 0; leaf = addpat(t, pat[i]->match.pat, m->matchstmt.val, &cap, &ncap); /* TODO: NULL is returned by unsupported patterns. */ if (!leaf) return NULL; if (leaf->act) fatal(pat[i], "pattern matched by earlier case on line %d", leaf->act->loc.line); leaf->act = pat[i]->match.block; leaf->cap = cap; leaf->ncap = ncap; } if (!exhaustivematch(m, t, exprtype(m->matchstmt.val))) fatal(m, "nonexhaustive pattern set in match statement"); n = genmatch(m->loc, t); dump(n, stdout); return n; }
/* 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; Node *base, *sz, *len; Node *stbase, *stlen; if (dst) t = dst; else t = temp(s, n); /* *(&slice) = (void*)base + off*sz */ base = slicebase(s, n->expr.args[0], 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'. */ if (tybase(exprtype(t))->type == Typtr) { stbase = set(simpcast(s, t, mktyptr(t->line, tyintptr)), base); sz = addk(simpcast(s, t, mktyptr(t->line, tyintptr)), Ptrsz); } else { stbase = set(deref(addr(s, t, tyintptr)), base); sz = addk(addr(s, t, tyintptr), Ptrsz); } /* *(&slice + ptrsz) = len */ stlen = set(deref(sz), len); append(s, stbase); append(s, stlen); return t; }
static Node *addk(Node *n, uvlong v) { Node *k; k = mkintlit(n->line, v); k->expr.type = exprtype(n); return add(n, k); }
static Dtree *addunion(Dtree *t, Node *pat, Node *val, Node ***cap, size_t *ncap) { Node *elt, *tag, *id; int32_t t1, t2; Dtree *sub; Ucon *uc; size_t i; if (t->any) return t->any; uc = finducon(tybase(exprtype(pat)), pat->expr.args[0]); t2 = uc->id; /* if we have the value already... */ sub = NULL; for (i = 0; i < t->nval; i++) { tag = t->val[i]->expr.args[0]; t1 = tag->lit.intval; if (t1 == t2) { if (pat->expr.nargs > 1) { elt = uvalue(val, exprtype(pat->expr.args[1])); return addpat(t->sub[i], pat->expr.args[1], elt, cap, ncap); } else { return t->sub[i]; } } } /* otherwise create a new match */ sub = mkdtree(); sub->patexpr = pat; tag = mkexpr(pat->loc, Outag, val, NULL); tag->expr.type = mktype(pat->loc, Tyint32); id = mkintlit(pat->loc, uc->id); id->expr.type = mktype(pat->loc, Tyint32); lappend(&t->val, &t->nval, id); lappend(&t->sub, &t->nsub, sub); lappend(&t->load, &t->nload, tag); if (pat->expr.nargs == 2) { elt = uvalue(val, exprtype(pat->expr.args[1])); sub = addpat(sub, pat->expr.args[1], elt, cap, ncap); } return sub; }
static Node * seqlen(Flattenctx *s, Node *n, Type *ty) { Node *r; if (!ty) ty = n->expr.type; if (exprtype(n)->type == Tyarray) { r = mkexpr(n->loc, Osllen, rval(s, n), NULL); r->expr.type = ty; } else if (exprtype(n)->type == Tyslice) { r = mkexpr(n->loc, Osllen, rval(s, n), NULL); r->expr.type = ty; } else { die("invalid seq type for len"); } return r; }
static Node * subk(Node *n, uvlong v) { Node *k; k = mkintlit(n->loc, v); k->expr.type = exprtype(n); return sub(n, k); }
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->line, offset(args[0], args[1])); r = add(t, u); r->expr.type = mktyptr(n->line, n->expr.type); return r; }
static Node *set(Node *a, Node *b) { Node *n; assert(a != NULL && b != NULL); assert(exprop(a) == Ovar || exprop(a) == Oderef); n = mkexpr(a->line, Oset, a, b, NULL); n->expr.type = exprtype(a); return n; }
static Node *arrayelt(Node *n, size_t i) { Node *idx, *elt; idx = mkintlit(n->loc, i); idx->expr.type = mktype(n->loc, Tyuint64); elt = mkexpr(n->loc, Oidx, n, idx, NULL); elt->expr.type = tybase(exprtype(n))->sub[0]; return elt; }
static int issmallconst(Node *dcl) { Type *t; if (!dcl->decl.isconst) return 0; if (!dcl->decl.init) return 0; t = tybase(exprtype(dcl->decl.init)); if (t->type <= Tyfloat64) return 1; return 0; }
static Node *idxaddr(Simp *s, Node *seq, Node *idx) { Node *a, *t, *u, *v; /* temps */ Node *r; /* result */ Type *ty; size_t sz; a = rval(s, seq, NULL); ty = exprtype(seq)->sub[0]; if (exprtype(seq)->type == Tyarray) t = addr(s, a, ty); else if (seq->expr.type->type == Tyslice) t = load(addr(s, a, mktyptr(seq->line, ty))); else die("Can't index type %s\n", tystr(seq->expr.type)); assert(t->expr.type->type == Typtr); u = rval(s, idx, NULL); u = ptrsized(s, u); sz = tysize(ty); v = mul(u, disp(seq->line, sz)); r = add(t, v); return r; }
static int isexhaustive(Dtree *dt) { Type *subt; size_t i; if (dt->any) return 1; if (dt->nsub > 0) { subt = tybase(exprtype(dt->sub[0]->patexpr)); if (dt->nsub != nconstructors(subt)) return 0; } switch (exprop(dt->patexpr)) { case Ovar: return 1; case Olit: return 1; case Oucon: for (i = 0; i < dt->nsub; i++) if (!isexhaustive(dt->sub[i])) return 0; return 1; break; case Otup: for (i = 0; i < dt->nsub; i++) if (!isexhaustive(dt->sub[i])) return 0; return 1; break; case Oarr: for (i = 0; i < dt->nsub; i++) if (!isexhaustive(dt->sub[i])) return 0; return 1; break; case Ostruct: for (i = 0; i < dt->nsub; i++) if (!isexhaustive(dt->sub[i])) return 0; return 1; break; default: die("Invalid pattern in exhaustivenes check. BUG."); break; } return 0; }
static void checkundef(Node *n, Reaching *r, Bitset *reach, Bitset *kill) { size_t i, j, did; Node *def; Type *t; if (n->type != Nexpr) return; if (exprop(n) == Ovar) { did = n->expr.did; for (j = 0; j < r->ndefs[did]; j++) { t = tybase(exprtype(n)); if (t->type == Tystruct || t->type == Tyunion || t->type == Tyarray || t->type == Tytuple) continue; if (bshas(kill, r->defs[did][j])) continue; if (!bshas(reach, r->defs[did][j])) continue; def = nodes[r->defs[did][j]]; if (exprop(def) == Oundef) fatal(n, "%s used before definition", namestr(n->expr.args[0])); } } else { switch (exprop(n)) { case Oset: case Oasn: case Oblit: checkundef(n->expr.args[1], r, reach, kill); break; case Oaddr: case Oslice: /* these don't actually look at the of args[0], so they're ok. */ for (i = 1; i < n->expr.nargs; i++) checkundef(n->expr.args[i], r, reach, kill); break; case Ocall: for (i = 1; i < n->expr.nargs; i++) if (exprop(n->expr.args[i]) != Oaddr) checkundef(n->expr.args[i], r, reach, kill); break; default: for (i = 0; i < n->expr.nargs; i++) checkundef(n->expr.args[i], r, reach, kill); break; } } }
static Node *assignat(Simp *s, Node *r, size_t off, Node *val) { Node *pval, *pdst; Node *sz; Node *st; val = rval(s, val, NULL); pdst = add(r, disp(val->line, off)); if (stacknode(val)) { sz = disp(val->line, size(val)); pval = addr(s, val, exprtype(val)); st = mkexpr(val->line, Oblit, pdst, pval, sz, NULL); } else { st = set(deref(pdst), 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 = tyalign(off, size(args[i])); assignat(s, r, off, args[i]); off += size(args[i]); } return dst; }
static void dispose(Flattenctx *s, Stab *st, size_t n) { Node *e, *call, *func; Trait *tr; Type *ty; size_t i; tr = traittab[Tcdisp]; /* dispose in reverse order of appearance */ for (i = st->nautotmp; i-- > n;) { e = st->autotmp[i]; ty = exprtype(e); func = traitfn(Zloc, tr, "__dispose__", ty); call = mkexpr(Zloc, Ocall, func, e, NULL); call->expr.type = mktype(Zloc, Tyvoid); flatten(s, call); } }
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 Dtree *addstruct(Dtree *t, Node *pat, Node *val, Node ***cap, size_t *ncap) { Node *elt, *memb; Type *ty; size_t i, j; if (t->any) return t->any; for (i = 0; i < pat->expr.nargs; i++) { elt = pat->expr.args[i]; for (j = 0; j < t->nval; j++) { if (!strcmp(namestr(elt->expr.idx), namestr(t->val[j]->expr.idx))) { ty = exprtype(pat->expr.args[i]); memb = structmemb(val, elt->expr.idx, ty); t = addpat(t, pat->expr.args[i], memb, cap, ncap); break; } } } return t; }
void dtdumpnode(Dtree *dt, FILE *f, int depth, int iswild) { Node *e; size_t i; char *s; if (dt->patexpr) { e = dt->patexpr; s = tystr(exprtype(e)); indentf(depth, "%s%s %s : %s\n", iswild ? "WILDCARD " : "", opstr[exprop(e)], dtnodestr(e), s); free(s); } if (dt->cap) for (i = 0; i < dt->ncap; i++) indentf(depth + 1, "capture %s\n", dtnodestr(dt->cap[i])); if (dt->act) indentf(depth + 1, "action\n"); for (i = 0; i < dt->nsub; i++) dtdumpnode(dt->sub[i], f, depth + 1, 0); if (dt->any) dtdumpnode(dt->any, f, depth + 1, 1); }
static Node * visit(Flattenctx *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]); if (opispure[exprop(n)]) { r = n; } else { if (exprtype(n)->type == Tyvoid) { r = mkexpr(n->loc, Olit, mkvoid(n->loc), NULL); r->expr.type = mktype(n->loc, Tyvoid); append(s, n); } else { r = temp(s, n); append(s, asn(r, n)); } } return r; }
/* gets the byte offset of 'memb' within the aggregate type 'aggr' */ static size_t offset(Node *aggr, Node *memb) { Type *ty; Node **nl; size_t i; size_t off; ty = tybase(exprtype(aggr)); if (ty->type == Typtr) ty = tybase(ty->sub[0]); assert(ty->type == Tystruct); nl = ty->sdecls; off = 0; for (i = 0; i < ty->nmemb; i++) { off = tyalign(off, size(nl[i])); if (!strcmp(namestr(memb), declname(nl[i]))) return off; off += size(nl[i]); } die("Could not find member %s in struct", namestr(memb)); return -1; }
static Dtree *addpat(Dtree *t, Node *pat, Node *val, Node ***cap, size_t *ncap) { Dtree *ret; Node *dcl; if (pat == NULL) return t; pat = fold(pat, 1); switch (exprop(pat)) { case Ovar: dcl = decls[pat->expr.did]; if (dcl->decl.isconst) ret = addpat(t, dcl->decl.init, val, cap, ncap); else ret = addwild(t, pat, val, cap, ncap); break; case Oucon: ret = addunion(t, pat, val, cap, ncap); break; case Olit: ret = addlit(t, pat, val, cap, ncap); break; case Otup: ret = addtup(t, pat, val, cap, ncap); break; case Oarr: ret = addarr(t, pat, val, cap, ncap); break; case Ostruct: ret = addstruct(t, pat, val, cap, ncap); break; case Ogap: ret = addwild(t, pat, val, NULL, NULL); break; default: ret = NULL; fatal(pat, "unsupported pattern %s of type %s", opstr[exprop(pat)], tystr(exprtype(pat))); break; } return ret; }
static Node *simpcast(Simp *s, Node *val, Type *to) { Node *r; Type *t; r = NULL; /* 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 Tylong: case Tyulong: case Tychar: case Tybyte: case Typtr: t = tybase(exprtype(val)); switch (t->type) { /* ptr -> slice conversion is disallowed */ case Tyslice: if (t->type == Typtr) fatal(val->line, "Bad cast from %s to %s", tystr(exprtype(val)), tystr(to)); r = slicebase(s, val, NULL); break; /* signed conversions */ case Tyint8: case Tyint16: case Tyint32: case Tyint64: case Tyint: case Tylong: r = intconvert(s, val, to, 1); break; /* unsigned conversions */ case Tybool: case Tyuint8: case Tyuint16: case Tyuint32: case Tyuint64: case Tyuint: case Tyulong: case Tychar: case Tybyte: case Typtr: r = intconvert(s, val, to, 0); break; case Tyfloat32: case Tyfloat64: if (tybase(to)->type == Typtr) fatal(val->line, "Bad cast from %s to %s", tystr(exprtype(val)), tystr(to)); r = mkexpr(val->line, Oflt2int, rval(s, val, NULL), NULL); r->expr.type = to; break; default: fatal(val->line, "Bad cast from %s to %s", tystr(exprtype(val)), tystr(to)); } break; case Tyfloat32: case Tyfloat64: t = tybase(exprtype(val)); switch (t->type) { case Tyint8: case Tyint16: case Tyint32: case Tyint64: case Tyuint8: case Tyuint16: case Tyuint32: case Tyuint64: case Tyint: case Tyuint: case Tylong: case Tyulong: case Tychar: case Tybyte: r = mkexpr(val->line, Oflt2int, rval(s, val, NULL), NULL); r->expr.type = to; break; default: fatal(val->line, "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->line, "Bad cast from %s to %s", tystr(exprtype(val)), tystr(to)); } return r; }