shithub: mc

Download patch

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);