ref: 7096d0e99d9deb053a7763f49b2b1b7b1df20a6a
parent: eba5edcf79813be921d8116ece30c10ca65c9fb3
author: Ori Bernstein <ori@eigenstate.org>
date: Sat Feb 13 09:44:00 EST 2016
Start refactoring Split flattening and lowering.
--- a/6/asm.h
+++ b/6/asm.h
@@ -282,7 +282,6 @@
Loc *loclit(long val, Mode m);
Loc *loclitl(char *lbl);
char *asmname(Node *dcl);
-int isconstfn(Node *n);
char *tydescid(char *buf, size_t bufsz, Type *ty);
Loc *coreg(Reg r, Mode m);
int isfloatmode(Mode m);
--- a/6/gen.c
+++ b/6/gen.c
@@ -83,31 +83,6 @@
return genlblstr(buf, 128, "");
}
-int isconstfn(Node *n)
-{
- Node *d, *e;
- Type *t;
-
- if (n->type == Nexpr) {
- if (exprop(n) != Ovar)
- return 0;
- d = decls[n->expr.did];
- } else {
- d = n;
- }
- t = tybase(decltype(d));
- if (!d || !d->decl.isconst || !d->decl.isglobl || d->decl.isgeneric)
- return 0;
- if (t->type != Tyfunc && t->type != Tycode)
- return 0;
- e = d->decl.init;
- if (e && (exprop(e) != Olit || e->expr.args[0]->lit.littype != Lfunc))
- return 0;
- if (!e && !d->decl.isextern && !d->decl.isimport)
- return 0;
- return 1;
-}
-
/*
* For x86, the assembly names are generated as follows:
* local symbols: .name
--- a/6/gengas.c
+++ b/6/gengas.c
@@ -412,6 +412,7 @@
case Nimpl:
break;
case Ndecl:
+ n = flattenfn(n);
simpglobl(n, globls, &fn, &nfn, &blob, &nblob);
break;
default:
--- a/6/isel.c
+++ b/6/isel.c
@@ -108,7 +108,9 @@
} else if (hthas(s->stkoff, n)) {
off = ptoi(htget(s->stkoff, n));
l = locmem(-off, locphysreg(Rrbp), NULL, mode(n));
- } else {
+ } else if (stacknode(n)) {
+ assert(0);
+ } else {
l = htget(s->reglocs, n);
if (!l) {
l = locreg(mode(n));
--- a/6/simp.c
+++ b/6/simp.c
@@ -35,10 +35,6 @@
int hasenv;
int isbigret;
- /* pre/postinc handling */
- Node **incqueue;
- size_t nqueue;
-
/* break/continue handling */
Node **loopstep;
size_t nloopstep;
@@ -65,7 +61,7 @@
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 simpcond(Simp *s, Node *n, Node *ltrue, Node *lfalse);
+//static void simpcond(Simp *s, Node *n, Node *ltrue, Node *lfalse);
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);
@@ -81,6 +77,7 @@
{
if (debugopt['S'])
dump(n, stdout);
+ assert(n->type != Ndecl);
lappend(&s->stmts, &s->nstmts, n);
}
@@ -332,276 +329,7 @@
return r;
}
-/* if foo; bar; else baz;;
- * => cjmp (foo) :bar :baz */
-static void simpif(Simp *s, Node *n, Node *exit)
-{
- Node *l1, *l2, *l3;
- Node *iftrue, *iffalse;
- l1 = genlbl(n->loc);
- l2 = genlbl(n->loc);
- if (exit)
- l3 = exit;
- else
- l3 = genlbl(n->loc);
-
- iftrue = n->ifstmt.iftrue;
- iffalse = n->ifstmt.iffalse;
-
- simpcond(s, n->ifstmt.cond, l1, l2);
- simp(s, l1);
- simp(s, iftrue);
- jmp(s, l3);
- simp(s, l2);
- /* because lots of bunched up end labels are ugly,
- * coalesce them by handling 'elif'-like constructs
- * separately */
- if (iffalse && iffalse->type == Nifstmt) {
- simpif(s, iffalse, exit);
- } else {
- simp(s, iffalse);
- jmp(s, l3);
- }
-
- if (!exit)
- simp(s, l3);
-}
-
-/* init; while cond; body;;
- * => init
- * jmp :cond
- * :body
- * ...body...
- * ...step...
- * :cond
- * ...cond...
- * cjmp (cond) :body :end
- * :end
- */
-static void simploop(Simp *s, Node *n)
-{
- Node *lbody;
- Node *lend;
- Node *lcond;
- Node *lstep;
-
- lbody = genlbl(n->loc);
- lcond = genlbl(n->loc);
- lstep = genlbl(n->loc);
- lend = genlbl(n->loc);
-
- lappend(&s->loopstep, &s->nloopstep, lstep);
- lappend(&s->loopexit, &s->nloopexit, lend);
-
- simp(s, n->loopstmt.init); /* init */
- jmp(s, lcond); /* goto test */
- simp(s, lbody); /* body lbl */
- simp(s, n->loopstmt.body); /* body */
- simp(s, lstep); /* test lbl */
- simp(s, n->loopstmt.step); /* step */
- simp(s, lcond); /* test lbl */
- simpcond(s, n->loopstmt.cond, lbody, lend); /* repeat? */
- simp(s, lend); /* exit */
-
- s->nloopstep--;
- s->nloopexit--;
-}
-
-static void simploopmatch(Simp *s, Node *pat, Node *val, Node *ltrue, Node *lfalse)
-{
- Node **cap, **out, *lload;
- size_t i, ncap, nout;
-
- /* pattern match */
- lload = genlbl(pat->loc);
- out = NULL;
- nout = 0;
- cap = NULL;
- ncap = 0;
- genonematch(pat, val, lload, lfalse, &out, &nout, &cap, &ncap);
- for (i = 0; i < nout; i++)
- simp(s, out[i]);
- simp(s, lload);
- for (i = 0; i < ncap; i++)
- simp(s, cap[i]);
- jmp(s, ltrue);
-}
-
-/* pat; seq;
- * body;;
- *
- * =>
- * .pseudo = seqinit
- * jmp :cond
- * :body
- * ...body...
- * :step
- * ...step...
- * :cond
- * ...cond...
- * cjmp (cond) :match :end
- * :match
- * ...match...
- * cjmp (match) :load :step
- * :load
- * matchval = load
- * :end
- */
-static void simpidxiter(Simp *s, Node *n)
-{
- Node *lbody, *lstep, *lcond, *lmatch, *lend;
- Node *idx, *len, *dcl, *seq, *val, *done;
- Node *zero;
-
- lbody = genlbl(n->loc);
- lstep = genlbl(n->loc);
- lcond = genlbl(n->loc);
- lmatch = genlbl(n->loc);
- lend = genlbl(n->loc);
-
- lappend(&s->loopstep, &s->nloopstep, lstep);
- lappend(&s->loopexit, &s->nloopexit, lend);
-
- zero = mkintlit(n->loc, 0);
- zero->expr.type = tyintptr;
-
- seq = rval(s, n->iterstmt.seq, NULL);
- idx = gentemp(n->loc, tyintptr, &dcl);
- declarelocal(s, dcl);
-
- /* setup */
- append(s, assign(s, idx, zero));
- jmp(s, lcond);
- simp(s, lbody);
-
- /* body */
- simp(s, n->iterstmt.body);
- /* step */
- simp(s, lstep);
- simp(s, assign(s, idx, addk(idx, 1)));
- /* condition */
- simp(s, lcond);
- len = seqlen(s, seq, tyintptr);
- done = mkexpr(n->loc, Olt, idx, len, NULL);
- cjmp(s, done, lmatch, lend);
- simp(s, lmatch);
- val = load(idxaddr(s, seq, idx));
-
- /* pattern match */
- simploopmatch(s, n->iterstmt.elt, val, lbody, lstep);
- jmp(s, lbody);
- simp(s, lend);
-
- s->nloopstep--;
- s->nloopexit--;
-}
-
-static Node *itertraitfn(Srcloc loc, Trait *tr, char *fn, Type *ty)
-{
- Node *proto, *dcl, *var;
- char *name;
- size_t i;
-
- for (i = 0; i < tr->nfuncs; i++) {
- name = declname(tr->funcs[i]);
- if (!strcmp(fn, name)) {
- proto = tr->funcs[i];
- dcl = htget(proto->decl.impls, ty);
- var = mkexpr(loc, Ovar, dcl->decl.name, NULL);
- var->expr.type = codetype(dcl->decl.type);
- var->expr.did = dcl->decl.did;
- return var;
- }
- }
- return NULL;
-}
-
-/* for pat in seq
- * body;;
- * =>
- * .seq = seq
- * .elt = elt
- * :body
- * ..body..
- * :step
- * __iterfin__(&seq, &elt)
- * cond = __iternext__(&seq, &eltout)
- * cjmp (cond) :match :end
- * :match
- * ...match...
- * cjmp (match) :load :step
- * :load
- * ...load matches...
- * :end
- */
-static void simptraititer(Simp *s, Node *n)
-{
- Node *lbody, *lclean, *lstep, *lmatch, *lend;
- Node *done, *val, *iter, *valptr, *iterptr;
- Node *func, *call, *asn;
- Trait *tr;
-
- val = temp(s, n->iterstmt.elt);
- valptr = mkexpr(val->loc, Oaddr, val, NULL);
- valptr->expr.type = mktyptr(n->loc, exprtype(val));
- iter = temp(s, n->iterstmt.seq);
- iterptr = mkexpr(val->loc, Oaddr, iter, NULL);
- iterptr->expr.type = mktyptr(n->loc, exprtype(iter));
- tr = traittab[Tciter];
-
- /* create labels */
- lbody = genlbl(n->loc);
- lclean = genlbl(n->loc);
- lstep = genlbl(n->loc);
- lmatch = genlbl(n->loc);
- lend = genlbl(n->loc);
- lappend(&s->loopstep, &s->nloopstep, lstep);
- lappend(&s->loopexit, &s->nloopexit, lend);
-
- asn = assign(s, iter, n->iterstmt.seq);
- append(s, asn);
- jmp(s, lstep);
- simp(s, lbody);
- /* body */
- simp(s, n->iterstmt.body);
- simp(s, lclean);
-
- /* call iterator cleanup */
- func = itertraitfn(n->loc, tr, "__iterfin__", exprtype(iter));
- call = mkexpr(n->loc, Ocall, func, iterptr, valptr, NULL);
- call->expr.type = mktype(n->loc, Tyvoid);
- append(s, call);
-
- simp(s, lstep);
- /* call iterator step */
- func = itertraitfn(n->loc, tr, "__iternext__", exprtype(iter));
- call = mkexpr(n->loc, Ocall, func, iterptr, valptr, NULL);
- done = gentemp(n->loc, mktype(n->loc, Tybool), NULL);
- call->expr.type = exprtype(done);
- asn = assign(s, done, call);
- append(s, asn);
- cjmp(s, done, lmatch, lend);
-
- /* pattern match */
- simp(s, lmatch);
- simploopmatch(s, n->iterstmt.elt, val, lbody, lclean);
- jmp(s, lbody);
- simp(s, lend);
-
- s->nloopstep--;
- s->nloopexit--;
-}
-
-static void simpiter(Simp *s, Node *n)
-{
- switch (tybase(exprtype(n->iterstmt.seq))->type) {
- case Tyarray: simpidxiter(s, n); break;
- case Tyslice: simpidxiter(s, n); break;
- default: simptraititer(s, n); break;
- }
-}
-
static Node *uconid(Simp *s, Node *n)
{
Ucon *uc;
@@ -809,35 +537,6 @@
return r;
}
-static void simpcond(Simp *s, Node *n, Node *ltrue, Node *lfalse)
-{
- Node **args;
- Node *v, *lnext;
-
- args = n->expr.args;
- switch (exprop(n)) {
- case Oland:
- lnext = genlbl(n->loc);
- simpcond(s, args[0], lnext, lfalse);
- append(s, lnext);
- simpcond(s, args[1], ltrue, lfalse);
- break;
- case Olor:
- lnext = genlbl(n->loc);
- simpcond(s, args[0], ltrue, lnext);
- append(s, lnext);
- simpcond(s, args[1], ltrue, lfalse);
- break;
- case Olnot:
- simpcond(s, args[0], lfalse, ltrue);
- break;
- default:
- v = rval(s, n, NULL);
- cjmp(s, v, ltrue, lfalse);
- break;
- }
-}
-
static Node *intconvert(Simp *s, Node *from, Type *to, int issigned)
{
Node *r;
@@ -1193,50 +892,7 @@
return dst;
}
-/* simplifies
- * a || b
- * to
- * if a || b
- * t = true
- * else
- * t = false
- * ;;
- */
-static Node *simplazy(Simp *s, Node *n)
-{
- Node *r, *t, *u;
- Node *ltrue, *lfalse, *ldone;
- /* set up temps and labels */
- r = temp(s, n);
- ltrue = genlbl(n->loc);
- lfalse = genlbl(n->loc);
- ldone = genlbl(n->loc);
-
- /* simp the conditional */
- simpcond(s, n, ltrue, lfalse);
-
- /* if true */
- append(s, ltrue);
- u = mkexpr(n->loc, Olit, mkbool(n->loc, 1), NULL);
- u->expr.type = mktype(n->loc, Tybool);
- t = set(r, u);
- append(s, t);
- jmp(s, ldone);
-
- /* if false */
- append(s, lfalse);
- u = mkexpr(n->loc, Olit, mkbool(n->loc, 0), NULL);
- u->expr.type = mktype(n->loc, Tybool);
- t = set(r, u);
- append(s, t);
- jmp(s, ldone);
-
- /* finish */
- append(s, ldone);
- return r;
-}
-
static Node *comparecomplex(Simp *s, Node *n, Op op)
{
fatal(n, "Complex comparisons not yet supported\n");
@@ -1493,9 +1149,6 @@
r = NULL;
args = n->expr.args;
switch (exprop(n)) {
- case Olor: case Oland:
- r = simplazy(s, n);
- break;
case Osize:
r = mkintlit(n->loc, size(args[0]));
r->expr.type = exprtype(n);
@@ -1511,13 +1164,12 @@
/* array.len slice.len are magic 'virtual' members.
* they need to be special cased. */
case Omemb:
- if (exprtype(args[0])->type == Tyslice || exprtype(args[0])->type == Tyarray) {
- r = seqlen(s, args[0], exprtype(n));
- } else {
- t = membaddr(s, n);
- r = load(t);
- }
+ 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;
@@ -1589,16 +1241,6 @@
* => expr
* x = x + 1
*/
- case Opostinc:
- r = lval(s, args[0]);
- t = assign(s, r, addk(r, 1));
- lappend(&s->incqueue, &s->nqueue, t);
- break;
- case Opostdec:
- r = lval(s, args[0]);
- t = assign(s, r, subk(r, 1));
- lappend(&s->incqueue, &s->nqueue, t);
- break;
case Olit:
switch (args[0]->lit.littype) {
case Lvoid:
@@ -1645,10 +1287,6 @@
append(s, t);
}
}
- /* drain the increment queue before we return */
- for (i = 0; i < s->nqueue; i++)
- append(s, s->incqueue[i]);
- lfree(&s->incqueue, &s->nqueue);
append(s, mkexpr(n->loc, Oret, NULL));
break;
case Oasn:
@@ -1677,6 +1315,7 @@
r = visit(s, n);
}
break;
+ /* FIXME: remove this after custom iters are done. */
case Obreak:
if (s->nloopexit == 0)
fatal(n, "trying to break when not in loop");
@@ -1699,6 +1338,9 @@
t = rval(s, args[0], NULL);
r = tupget(s, t, i, dst);
break;
+ case Olor: case Oland:
+ case Opostinc: case Opostdec:
+ die("invalid operator: should have been removed");
case Obad:
die("bad operator");
break;
@@ -1733,25 +1375,9 @@
return l->type == Nlit && l->lit.littype == Llbl;
}
-static void simpmatch(Simp *s, Node *n)
-{
- Node *val;
- Node **match;
- size_t i, nmatch;
-
- val = rval(s, n->matchstmt.val, NULL);
-
- match = NULL;
- nmatch = 0;
- genmatch(n, val, &match, &nmatch);
- for (i = 0; i < nmatch; i++)
- simp(s, match[i]);
-}
-
static Node *simp(Simp *s, Node *n)
{
- Node *r, *t, *u;
- size_t i;
+ Node *r;
if (!n)
return NULL;
@@ -1758,10 +1384,7 @@
r = NULL;
switch (n->type) {
case Nblock: simpblk(s, n); break;
- case Nloopstmt: simploop(s, n); break;
- case Niterstmt: simpiter(s, n); break;
- case Nifstmt: simpif(s, n, NULL); break;
- case Nmatchstmt: simpmatch(s, n); break;
+ case Ndecl: declarelocal(s, n); break;
case Nexpr:
if (islbl(n))
append(s, n);
@@ -1769,23 +1392,7 @@
r = rval(s, n, NULL);
if (r)
append(s, r);
- /* drain the increment queue for this expr */
- for (i = 0; i < s->nqueue; i++)
- append(s, s->incqueue[i]);
- lfree(&s->incqueue, &s->nqueue);
break;
-
- case Ndecl:
- declarelocal(s, n);
- t = mkexpr(n->loc, Ovar, n->decl.name, NULL);
- if (n->decl.init) {
- u = mkexpr(n->loc, Oasn, t, n->decl.init, NULL);
- u->expr.type = n->decl.type;
- t->expr.type = n->decl.type;
- t->expr.did = n->decl.did;
- simp(s, u);
- }
- break;
default:
dump(n, stderr);
die("bad node passsed to simp()");
@@ -1799,7 +1406,7 @@
* and simpler representation, which maps easily and
* directly to assembly instructions.
*/
-static void flatten(Simp *s, Node *f)
+static void simpinit(Simp *s, Node *f)
{
Node *dcl;
Type *ty;
@@ -1864,10 +1471,10 @@
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.
- */
+ 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 */
@@ -1895,7 +1502,7 @@
n = n->expr.args[0];
n = n->lit.fnval;
collectenv(s, n);
- flatten(s, n);
+ simpinit(s, n);
if (debugopt['f'] || debugopt['F'])
for (i = 0; i < s->nstmts; i++)
@@ -1993,16 +1600,6 @@
}
}
-int ismain(Node *dcl)
-{
- Node *n;
-
- n = dcl->decl.name;
- if (n->name.ns)
- return 0;
- return strcmp(n->name.name, "main") == 0;
-}
-
void simpglobl(Node *dcl, Htab *globls, Func ***fn, size_t *nfn, Node ***blob, size_t *nblob)
{
Simp s = {0,};
@@ -2009,8 +1606,6 @@
char *name;
Func *f;
- if (ismain(dcl))
- dcl->decl.vis = Vishidden;
s.stkoff = mkht(varhash, vareq);
s.envoff = mkht(varhash, vareq);
s.globls = globls;
--- a/mi/Makefile
+++ b/mi/Makefile
@@ -1,9 +1,11 @@
LIB=libmi.a
-OBJ=cfg.o \
- dfcheck.o \
- match.o \
- reaching.o \
-
+OBJ=\
+ cfg.o \
+ flatten.o \
+ dfcheck.o \
+ match.o \
+ reaching.o \
+
DEPS=../parse/libparse.a
--- a/mi/cfg.c
+++ b/mi/cfg.c
@@ -258,6 +258,8 @@
bsput(targ->pred, bb->id);
}
}
+ if (debugopt['C'])
+ dumpcfg(cfg, stdout);
trim(cfg);
return cfg;
}
--- /dev/null
+++ b/mi/flatten.c
@@ -1,0 +1,958 @@
+#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 "parse.h"
+#include "mi.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 Flattenctx Flattenctx;
+struct Flattenctx {
+ int isglobl;
+
+ /* return handling */
+ Node **stmts;
+ size_t nstmts;
+
+ /* return handling */
+ int hasenv;
+ int isbigret;
+
+ /* pre/postinc handling */
+ Node **incqueue;
+ size_t nqueue;
+
+ /* break/continue handling */
+ Node **loopstep;
+ size_t nloopstep;
+ Node **loopexit;
+ size_t nloopexit;
+
+ /* location handling */
+ Htab *globls;
+
+ size_t stksz;
+};
+
+static Node *flatten(Flattenctx *s, Node *n);
+static Node *rval(Flattenctx *s, Node *n);
+static Node *lval(Flattenctx *s, Node *n);
+
+static void append(Flattenctx *s, Node *n)
+{
+ if (debugopt['F'])
+ dump(n, stdout);
+ lappend(&s->stmts, &s->nstmts, n);
+}
+
+static void cjmp(Flattenctx *s, Node *cond, Node *iftrue, Node *iffalse)
+{
+ Node *jmp;
+
+ jmp = mkexpr(cond->loc, Ocjmp, cond, iftrue, iffalse, NULL);
+ jmp->expr.type = mktype(cond->loc, Tyvoid);
+ append(s, jmp);
+}
+
+static void jmp(Flattenctx *s, Node *lbl)
+{
+ Node *n;
+
+ n = mkexpr(lbl->loc, Ojmp, lbl, NULL);
+ n->expr.type = mktype(n->loc, Tyvoid);
+ append(s, n);
+}
+
+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 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 *temp(Flattenctx *flatten, Node *e)
+{
+ Node *t, *dcl;
+
+ assert(e->type == Nexpr);
+ t = gentemp(e->loc, e->expr.type, &dcl);
+ return t;
+}
+
+static Node *add(Node *a, Node *b)
+{
+ Node *n;
+
+ 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 *subk(Node *n, uvlong v)
+{
+ Node *k;
+
+ k = mkintlit(n->loc, v);
+ k->expr.type = exprtype(n);
+ return sub(n, k);
+}
+
+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;
+ }
+ assert(r != NULL);
+ return r;
+}
+
+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;
+}
+
+static void flattencond(Flattenctx *s, Node *n, Node *ltrue, Node *lfalse)
+{
+ Node **args;
+ Node *v, *lnext;
+
+ args = n->expr.args;
+ switch (exprop(n)) {
+ case Oland:
+ lnext = genlbl(n->loc);
+ flattencond(s, args[0], lnext, lfalse);
+ append(s, lnext);
+ flattencond(s, args[1], ltrue, lfalse);
+ break;
+ case Olor:
+ lnext = genlbl(n->loc);
+ flattencond(s, args[0], ltrue, lnext);
+ append(s, lnext);
+ flattencond(s, args[1], ltrue, lfalse);
+ break;
+ case Olnot:
+ flattencond(s, args[0], lfalse, ltrue);
+ break;
+ default:
+ v = rval(s, n);
+ cjmp(s, v, ltrue, lfalse);
+ break;
+ }
+}
+
+/* flattenlifies
+ * a || b
+ * to
+ * if a || b
+ * t = true
+ * else
+ * t = false
+ * ;;
+ */
+static Node *flattenlazy(Flattenctx *s, Node *n)
+{
+ Node *r, *t, *u;
+ Node *ltrue, *lfalse, *ldone;
+
+ /* set up temps and labels */
+ r = temp(s, n);
+ ltrue = genlbl(n->loc);
+ lfalse = genlbl(n->loc);
+ ldone = genlbl(n->loc);
+
+ /* flatten the conditional */
+ flattencond(s, n, ltrue, lfalse);
+
+ /* if true */
+ append(s, ltrue);
+ u = mkexpr(n->loc, Olit, mkbool(n->loc, 1), NULL);
+ u->expr.type = mktype(n->loc, Tybool);
+ t = asn(r, u);
+ append(s, t);
+ jmp(s, ldone);
+
+ /* if false */
+ append(s, lfalse);
+ u = mkexpr(n->loc, Olit, mkbool(n->loc, 0), NULL);
+ u->expr.type = mktype(n->loc, Tybool);
+ t = asn(r, u);
+ append(s, t);
+ jmp(s, ldone);
+
+ /* finish */
+ append(s, ldone);
+ return r;
+}
+
+static Node *destructure(Flattenctx *s, Node *lhs, Node *rhs)
+{
+ Node *lv, *rv, *idx;
+ Node **args;
+ size_t i;
+
+ args = lhs->expr.args;
+ rhs = rval(s, rhs);
+ for (i = 0; i < lhs->expr.nargs; i++) {
+ lv = lval(s, args[i]);
+
+ idx = mkintlit(rhs->loc, i);
+ idx->expr.type = mktype(rhs->loc, Tyuint64);
+ rv = mkexpr(rhs->loc, Otupget, rhs, idx, NULL);
+ rv->expr.type = lhs->expr.type;
+
+ append(s, asn(lv, rv));
+ }
+ return rhs;
+}
+
+static Node *rval(Flattenctx *s, Node *n)
+{
+ Node *t, *u; /* temporary nodes */
+ Node *r; /* expression result */
+ Node **args;
+ size_t i;
+ Type *ty;
+
+// const Op fusedmap[Numops] = {
+// [Oaddeq] = Oadd,
+// [Osubeq] = Osub,
+// [Omuleq] = Omul,
+// [Odiveq] = Odiv,
+// [Omodeq] = Omod,
+// [Oboreq] = Obor,
+// [Obandeq] = Oband,
+// [Obxoreq] = Obxor,
+// [Obsleq] = Obsl,
+// [Obsreq] = Obsr,
+// };
+
+ r = NULL;
+ args = n->expr.args;
+ switch (exprop(n)) {
+ case Osize:
+ r = n; /* don't touch subexprs; they're a pseudo decl */
+ break;
+ case Olor: case Oland:
+ r = flattenlazy(s, n);
+ break;
+ case Oidx:
+ t = rval(s, n->expr.args[0]);
+ u = rval(s, n->expr.args[1]);
+ r = mkexpr(n->loc, Oidx, t, u, NULL);
+ r->expr.type = n->expr.type;
+ break;
+ /* array.len slice.len are magic 'virtual' members.
+ * they need to be special cased. */
+ case Omemb:
+ ty = tybase(exprtype(args[0]));
+ if (ty->type == Tyslice || ty->type == Tyarray) {
+ r = seqlen(s, args[0], exprtype(n));
+ } else {
+ t = rval(s, args[0]);
+ r = mkexpr(n->loc, Omemb, t, args[1], NULL);
+ r->expr.type = n->expr.type;
+ }
+ break;
+ case Oucon:
+ if (n->expr.nargs > 1)
+ t = rval(s, args[1]);
+ else
+ t = NULL;
+ r = mkexpr(n->loc, Oucon, args[0], t, NULL);
+ r->expr.type = n->expr.type;
+ break;
+// case Outag:
+// r = uconid(s, args[0]);
+// break;
+// case Oudata:
+// r = flattenuget(s, n, dst);
+// break;
+// case Otup:
+// r = flattentup(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 = flattencast(s, args[0], exprtype(n));
+// break;
+//
+// /* fused ops:
+// * foo ?= blah
+// * =>
+// * foo = foo ? blah*/
+// case Oaddeq: case Osubeq: case Omuleq: case Odiveq: case Omodeq:
+// case Oboreq: case Obandeq: case Obxoreq: case Obsleq: case Obsreq:
+// assert(fusedmap[exprop(n)] != Obad);
+// u = lval(s, args[0]);
+// v = rval(s, args[1], NULL);
+// v = mkexpr(n->loc, fusedmap[exprop(n)], u, v, NULL);
+// v->expr.type = u->expr.type;
+// r = set(u, v);
+// break;
+//
+// /* ++expr(x)
+// * => args[0] = args[0] + 1
+// * expr(x) */
+// case Opreinc:
+// v = assign(s, args[0], addk(args[0], 1));
+// append(s, v);
+// r = rval(s, args[0], NULL);
+// break;
+// case Opredec:
+// v = assign(s, args[0], subk(args[0], 1));
+// append(s, v);
+// r = rval(s, args[0], NULL);
+// break;
+//
+// /* expr(x++)
+// * => expr
+// * x = x + 1
+// */
+ case Opostinc:
+ r = lval(s, args[0]);
+ t = asn(r, addk(r, 1));
+ lappend(&s->incqueue, &s->nqueue, t);
+ break;
+ case Opostdec:
+ r = lval(s, args[0]);
+ t = asn(r, subk(r, 1));
+ lappend(&s->incqueue, &s->nqueue, t);
+ break;
+ case Olit:
+ switch (args[0]->lit.littype) {
+ case Lvoid:
+ case Lchr:
+ case Lbool:
+ case Llbl:
+ case Lint:
+ case Lstr:
+ case Lflt:
+ case Lfunc:
+ r = n; //capture(s, n, dst);
+ break;
+ }
+ break;
+ case Ovar:
+ r = n;
+ break;;
+// case Ogap:
+// fatal(n, "'_' may not be an rvalue");
+// break;
+ case Oret:
+ /* drain the increment queue before we return */
+ t = rval(s, args[0]);
+ for (i = 0; i < s->nqueue; i++)
+ append(s, s->incqueue[i]);
+ lfree(&s->incqueue, &s->nqueue);
+ append(s, mkexpr(n->loc, Oret, t, NULL));
+ break;
+ case Oasn:
+ if (exprop(args[0]) == Otup) {
+ r = destructure(s, args[0], args[1]);
+ } else if (tybase(exprtype(n))->type != Tyvoid) {
+ t = lval(s, args[0]);
+ u = rval(s, args[1]);
+ r = asn(t, u);
+ } else {
+ r = rval(s, args[1]);
+ }
+ break;
+// case Ocall:
+// r = flattencall(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 = flattenblob(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 Obreak:
+ r = NULL;
+ if (s->nloopexit == 0)
+ fatal(n, "trying to break when not in loop");
+ jmp(s, s->loopexit[s->nloopexit - 1]);
+ break;
+ case Ocontinue:
+ r = NULL;
+ if (s->nloopstep == 0)
+ fatal(n, "trying to continue when not in loop");
+ jmp(s, s->loopstep[s->nloopstep - 1]);
+ break;
+// case Oeq: case One:
+// r = compare(s, n, 1);
+// break;
+// case Ogt: case Oge: case Olt: case Ole:
+// r = compare(s, n, 0);
+// 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 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;
+ }
+ if (r && n->expr.idx)
+ r->expr.idx = n->expr.idx;
+ return r;
+}
+
+static Node *lval(Flattenctx *s, Node *n)
+{
+ Node *r;
+
+ switch (exprop(n)) {
+ case Ovar: r = n; break;
+ case Oidx: r = rval(s, n); break;//loadidx(s, args[0], args[1]); break;
+ case Oderef: r = rval(s, n); break;
+ case Omemb: r = rval(s, n); break;
+ case Ostruct: r = rval(s, n); break;
+ case Oucon: r = rval(s, n); break;
+ case Oarr: r = rval(s, n); 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); break;
+ case Outag: r = rval(s, n); break;
+ case Otupget: r = rval(s, n); break;
+ default:
+ fatal(n, "%s cannot be an lvalue", opstr[exprop(n)]);
+ break;
+ }
+ return r;
+}
+
+static void flattenblk(Flattenctx *fc, Node *n)
+{
+ size_t i;
+
+ for (i = 0; i < n->block.nstmts; i++) {
+ n->block.stmts[i] = fold(n->block.stmts[i], 0);
+ flatten(fc, n->block.stmts[i]);
+ }
+}
+
+/* init; while cond; body;;
+ * => init
+ * jmp :cond
+ * :body
+ * ...body...
+ * ...step...
+ * :cond
+ * ...cond...
+ * cjmp (cond) :body :end
+ * :end
+ */
+static void flattenloop(Flattenctx *s, Node *n)
+{
+ Node *lbody;
+ Node *lend;
+ Node *lcond;
+ Node *lstep;
+
+ lbody = genlbl(n->loc);
+ lcond = genlbl(n->loc);
+ lstep = genlbl(n->loc);
+ lend = genlbl(n->loc);
+
+ lappend(&s->loopstep, &s->nloopstep, lstep);
+ lappend(&s->loopexit, &s->nloopexit, lend);
+
+ flatten(s, n->loopstmt.init); /* init */
+ jmp(s, lcond); /* goto test */
+ flatten(s, lbody); /* body lbl */
+ flatten(s, n->loopstmt.body); /* body */
+ flatten(s, lstep); /* test lbl */
+ flatten(s, n->loopstmt.step); /* step */
+ flatten(s, lcond); /* test lbl */
+ flattencond(s, n->loopstmt.cond, lbody, lend); /* repeat? */
+ flatten(s, lend); /* exit */
+
+ s->nloopstep--;
+ s->nloopexit--;
+}
+
+/* if foo; bar; else baz;;
+ * => cjmp (foo) :bar :baz */
+static void flattenif(Flattenctx *s, Node *n, Node *exit)
+{
+ Node *l1, *l2, *l3;
+ Node *iftrue, *iffalse;
+
+ l1 = genlbl(n->loc);
+ l2 = genlbl(n->loc);
+ if (exit)
+ l3 = exit;
+ else
+ l3 = genlbl(n->loc);
+
+ iftrue = n->ifstmt.iftrue;
+ iffalse = n->ifstmt.iffalse;
+
+ flattencond(s, n->ifstmt.cond, l1, l2);
+ flatten(s, l1);
+ flatten(s, iftrue);
+ jmp(s, l3);
+ flatten(s, l2);
+ /* because lots of bunched up end labels are ugly,
+ * coalesce them by handling 'elif'-like constructs
+ * separately */
+ if (iffalse && iffalse->type == Nifstmt) {
+ flattenif(s, iffalse, exit);
+ } else {
+ flatten(s, iffalse);
+ jmp(s, l3);
+ }
+
+ if (!exit)
+ flatten(s, l3);
+}
+
+static void flattenloopmatch(Flattenctx *s, Node *pat, Node *val, Node *ltrue, Node *lfalse)
+{
+ Node **cap, **out, *lload;
+ size_t i, ncap, nout;
+
+ /* pattern match */
+ lload = genlbl(pat->loc);
+ out = NULL;
+ nout = 0;
+ cap = NULL;
+ ncap = 0;
+ genonematch(pat, val, lload, lfalse, &out, &nout, &cap, &ncap);
+ for (i = 0; i < nout; i++)
+ flatten(s, out[i]);
+ flatten(s, lload);
+ for (i = 0; i < ncap; i++)
+ flatten(s, cap[i]);
+ jmp(s, ltrue);
+}
+
+/* pat; seq;
+ * body;;
+ *
+ * =>
+ * .pseudo = seqinit
+ * jmp :cond
+ * :body
+ * ...body...
+ * :step
+ * ...step...
+ * :cond
+ * ...cond...
+ * cjmp (cond) :match :end
+ * :match
+ * ...match...
+ * cjmp (match) :load :step
+ * :load
+ * matchval = load
+ * :end
+ */
+static void flattenidxiter(Flattenctx *s, Node *n)
+{
+ Node *lbody, *lstep, *lcond, *lmatch, *lend;
+ Node *idx, *len, *dcl, *seq, *val, *done;
+ Node *zero;
+ Type *idxtype;
+
+ lbody = genlbl(n->loc);
+ lstep = genlbl(n->loc);
+ lcond = genlbl(n->loc);
+ lmatch = genlbl(n->loc);
+ lend = genlbl(n->loc);
+
+ lappend(&s->loopstep, &s->nloopstep, lstep);
+ lappend(&s->loopexit, &s->nloopexit, lend);
+
+ /* FIXME: pass this in from main() */
+ idxtype = mktype(n->loc, Tyuint64);
+ zero = mkintlit(n->loc, 0);
+ zero->expr.type = idxtype;
+
+ seq = rval(s, n->iterstmt.seq);
+ idx = gentemp(n->loc, idxtype, &dcl);
+
+ /* setup */
+ append(s, asn(idx, zero));
+ jmp(s, lcond);
+ flatten(s, lbody);
+
+ /* body */
+ flatten(s, n->iterstmt.body);
+ /* step */
+ flatten(s, lstep);
+ flatten(s, asn(idx, addk(idx, 1)));
+ /* condition */
+ flatten(s, lcond);
+ len = seqlen(s, seq, idxtype);
+ done = mkexpr(n->loc, Olt, idx, len, NULL);
+ cjmp(s, done, lmatch, lend);
+ flatten(s, lmatch);
+ val = mkexpr(n->loc, Oidx, seq, idx);
+ val->expr.type = tybase(exprtype(seq))->sub[0];
+
+ /* pattern match */
+ flattenloopmatch(s, n->iterstmt.elt, val, lbody, lstep);
+ jmp(s, lbody);
+ flatten(s, lend);
+
+ s->nloopstep--;
+ s->nloopexit--;
+}
+
+static Node *itertraitfn(Srcloc loc, Trait *tr, char *fn, Type *ty)
+{
+ Node *proto, *dcl, *var;
+ char *name;
+ size_t i;
+
+ for (i = 0; i < tr->nfuncs; i++) {
+ name = declname(tr->funcs[i]);
+ if (!strcmp(fn, name)) {
+ proto = tr->funcs[i];
+ dcl = htget(proto->decl.impls, ty);
+ var = mkexpr(loc, Ovar, dcl->decl.name, NULL);
+ var->expr.type = dcl->decl.type;
+ var->expr.did = dcl->decl.did;
+ return var;
+ }
+ }
+ return NULL;
+}
+
+/* for pat in seq
+ * body;;
+ * =>
+ * .seq = seq
+ * .elt = elt
+ * :body
+ * ..body..
+ * :step
+ * __iterfin__(&seq, &elt)
+ * cond = __iternext__(&seq, &eltout)
+ * cjmp (cond) :match :end
+ * :match
+ * ...match...
+ * cjmp (match) :load :step
+ * :load
+ * ...load matches...
+ * :end
+ */
+static void flattentraititer(Flattenctx *s, Node *n)
+{
+ Node *lbody, *lclean, *lstep, *lmatch, *lend;
+ Node *done, *val, *iter, *valptr, *iterptr;
+ Node *func, *call;
+ Trait *tr;
+
+ val = temp(s, n->iterstmt.elt);
+ valptr = mkexpr(val->loc, Oaddr, val, NULL);
+ valptr->expr.type = mktyptr(n->loc, exprtype(val));
+ iter = temp(s, n->iterstmt.seq);
+ iterptr = mkexpr(val->loc, Oaddr, iter, NULL);
+ iterptr->expr.type = mktyptr(n->loc, exprtype(iter));
+ tr = traittab[Tciter];
+
+ /* create labels */
+ lbody = genlbl(n->loc);
+ lclean = genlbl(n->loc);
+ lstep = genlbl(n->loc);
+ lmatch = genlbl(n->loc);
+ lend = genlbl(n->loc);
+ lappend(&s->loopstep, &s->nloopstep, lstep);
+ lappend(&s->loopexit, &s->nloopexit, lend);
+
+ append(s, asn(iter, n->iterstmt.seq));
+ jmp(s, lstep);
+ flatten(s, lbody);
+ /* body */
+ flatten(s, n->iterstmt.body);
+ flatten(s, lclean);
+
+ /* call iterator cleanup */
+ func = itertraitfn(n->loc, tr, "__iterfin__", exprtype(iter));
+ call = mkexpr(n->loc, Ocall, func, iterptr, valptr, NULL);
+ call->expr.type = mktype(n->loc, Tyvoid);
+ append(s, call);
+
+ flatten(s, lstep);
+ /* call iterator step */
+ func = itertraitfn(n->loc, tr, "__iternext__", exprtype(iter));
+ call = mkexpr(n->loc, Ocall, func, iterptr, valptr, NULL);
+ done = gentemp(n->loc, mktype(n->loc, Tybool), NULL);
+ call->expr.type = exprtype(done);
+ append(s, asn(done, call));
+ cjmp(s, done, lmatch, lend);
+
+ /* pattern match */
+ flatten(s, lmatch);
+ flattenloopmatch(s, n->iterstmt.elt, val, lbody, lclean);
+ jmp(s, lbody);
+ flatten(s, lend);
+
+ s->nloopstep--;
+ s->nloopexit--;
+}
+
+static void flatteniter(Flattenctx *s, Node *n)
+{
+ switch (tybase(exprtype(n->iterstmt.seq))->type) {
+ case Tyarray: flattenidxiter(s, n); break;
+ case Tyslice: flattenidxiter(s, n); break;
+ default: flattentraititer(s, n); break;
+ }
+}
+static void flattenmatch(Flattenctx *fc, Node *n)
+{
+ Node *val;
+ Node **match;
+ size_t i, nmatch;
+
+ val = rval(fc, n->matchstmt.val);
+
+ match = NULL;
+ nmatch = 0;
+ genmatch(n, val, &match, &nmatch);
+ for (i = 0; i < nmatch; i++)
+ flatten(fc, match[i]);
+}
+
+static void flattenexpr(Flattenctx *fc, Node *n)
+{
+ Node *r;
+ size_t i;
+
+ if (islbl(n)) {
+ append(fc, n);
+ return;
+ }
+
+ r = rval(fc, n);
+ if (r)
+ append(fc, r);
+ for (i = 0; i < fc->nqueue; i++)
+ append(fc, fc->incqueue[i]);
+ lfree(&fc->incqueue, &fc->nqueue);
+}
+
+static Node *flatten(Flattenctx *fc, Node *n)
+{
+ Node *r, *u, *t;
+
+ if (!n)
+ return NULL;
+ r = NULL;
+ switch (n->type) {
+ case Nblock: flattenblk(fc, n); break;
+ case Nloopstmt: flattenloop(fc, n); break;
+ case Niterstmt: flatteniter(fc, n); break;
+ case Nifstmt: flattenif(fc, n, NULL); break;
+ case Nmatchstmt: flattenmatch(fc, n); break;
+ case Nexpr: flattenexpr(fc, n); break;
+ case Ndecl:
+ append(fc, n);
+ r = mkexpr(n->loc, Ovar, n->decl.name, NULL);
+ if (n->decl.init) {
+ t = rval(fc, n->decl.init);
+ u = mkexpr(n->loc, Oasn, r, t, NULL);
+ u->expr.type = n->decl.type;
+ r->expr.type = n->decl.type;
+ r->expr.did = n->decl.did;
+ flatten(fc, u);
+ }
+ break;
+ default:
+ dump(n, stderr);
+ die("bad node passsed to flatten()");
+ break;
+ }
+ return r;
+}
+
+static Node *flatteninit(Node *dcl)
+{
+ Flattenctx fc = {0,};
+ Node *lit, *fn, *blk, *body;
+
+ lit = dcl->decl.init->expr.args[0];
+ fn = lit->lit.fnval;
+ body = fn->func.body;
+ flatten(&fc, fn->func.body);
+ blk = mkblock(fn->loc, body->block.scope);
+ blk->block.stmts = fc.stmts;
+ blk->block.nstmts = fc.nstmts;
+ fn->func.body = blk;
+
+ return dcl;
+}
+
+static int ismain(Node *n)
+{
+ n = n->decl.name;
+ if (n->name.ns)
+ return 0;
+ return strcmp(n->name.name, "main") == 0;
+}
+
+Node *flattenfn(Node *dcl)
+{
+ if (ismain(dcl))
+ dcl->decl.vis = Vishidden;
+ if (dcl->decl.isextern || dcl->decl.isgeneric)
+ return dcl;
+ if (isconstfn(dcl)) {
+ dcl = flatteninit(dcl);
+ //lappend(fn, nfn, f);
+ }
+
+ //lappend(fn, nfn, f);
+ return dcl;
+}
+
+int isconstfn(Node *n)
+{
+ Node *d, *e;
+ Type *t;
+
+ if (n->type == Nexpr) {
+ if (exprop(n) != Ovar)
+ return 0;
+ d = decls[n->expr.did];
+ } else {
+ d = n;
+ }
+ t = tybase(decltype(d));
+ if (!d || !d->decl.isconst || !d->decl.isglobl || d->decl.isgeneric)
+ return 0;
+ if (t->type != Tyfunc && t->type != Tycode)
+ return 0;
+ e = d->decl.init;
+ if (e && (exprop(e) != Olit || e->expr.args[0]->lit.littype != Lfunc))
+ return 0;
+ if (!e && !d->decl.isextern && !d->decl.isimport)
+ return 0;
+ return 1;
+}
+
--- a/mi/mi.h
+++ b/mi/mi.h
@@ -47,3 +47,7 @@
/* pattern matching */
void genmatch(Node *m, Node *val, Node ***out, size_t *nout);
void genonematch(Node *pat, Node *val, Node *iftrue, Node *iffalse, Node ***out, size_t *nout, Node ***cap, size_t *ncap);
+
+/* tree flattening */
+Node *flattenfn(Node *dcl);
+int isconstfn(Node *n);