Built go1.4.2 from source.

Change-Id: If9a300f7aacf0d8fb5e720945e0aa2fc68193270
diff --git a/src/cmd/gc/Makefile b/src/cmd/gc/Makefile
new file mode 100644
index 0000000..58e25fa
--- /dev/null
+++ b/src/cmd/gc/Makefile
@@ -0,0 +1,17 @@
+# Copyright 2012 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+include ../../Make.dist
+
+install: y.tab.h builtin.c
+
+y.tab.h: go.y go.errors bisonerrors
+	bison -v -y -d go.y
+	# make yystate global, yytname mutable
+	cat y.tab.c | sed '/ int yystate;/d; s/int yychar;/int yychar, yystate;/; s/static const char \*const yytname/const char *yytname/; s/char const \*yymsgp/char *yymsgp/' >y1.tab.c
+	mv y1.tab.c y.tab.c
+	awk -f bisonerrors y.output go.errors >yerr.h
+
+builtin.c: runtime.go unsafe.go
+	./mkbuiltin
diff --git a/src/cmd/gc/align.c b/src/cmd/gc/align.c
new file mode 100644
index 0000000..63ed2e5
--- /dev/null
+++ b/src/cmd/gc/align.c
@@ -0,0 +1,680 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+/*
+ * machine size and rounding
+ * alignment is dictated around
+ * the size of a pointer, set in betypeinit
+ * (see ../6g/galign.c).
+ */
+
+static int defercalc;
+
+vlong
+rnd(vlong o, vlong r)
+{
+	if(r < 1 || r > 8 || (r&(r-1)) != 0)
+		fatal("rnd");
+	return (o+r-1)&~(r-1);
+}
+
+static void
+offmod(Type *t)
+{
+	Type *f;
+	int32 o;
+
+	o = 0;
+	for(f=t->type; f!=T; f=f->down) {
+		if(f->etype != TFIELD)
+			fatal("offmod: not TFIELD: %lT", f);
+		f->width = o;
+		o += widthptr;
+		if(o >= MAXWIDTH) {
+			yyerror("interface too large");
+			o = widthptr;
+		}
+	}
+}
+
+static vlong
+widstruct(Type *errtype, Type *t, vlong o, int flag)
+{
+	Type *f;
+	int64 w;
+	int32 maxalign;
+	
+	maxalign = flag;
+	if(maxalign < 1)
+		maxalign = 1;
+	for(f=t->type; f!=T; f=f->down) {
+		if(f->etype != TFIELD)
+			fatal("widstruct: not TFIELD: %lT", f);
+		if(f->type == T) {
+			// broken field, just skip it so that other valid fields
+			// get a width.
+			continue;
+		}
+		dowidth(f->type);
+		if(f->type->align > maxalign)
+			maxalign = f->type->align;
+		if(f->type->width < 0)
+			fatal("invalid width %lld", f->type->width);
+		w = f->type->width;
+		if(f->type->align > 0)
+			o = rnd(o, f->type->align);
+		f->width = o;	// really offset for TFIELD
+		if(f->nname != N) {
+			// this same stackparam logic is in addrescapes
+			// in typecheck.c.  usually addrescapes runs after
+			// widstruct, in which case we could drop this,
+			// but function closure functions are the exception.
+			if(f->nname->stackparam) {
+				f->nname->stackparam->xoffset = o;
+				f->nname->xoffset = 0;
+			} else
+				f->nname->xoffset = o;
+		}
+		o += w;
+		if(o >= MAXWIDTH) {
+			yyerror("type %lT too large", errtype);
+			o = 8;  // small but nonzero
+		}
+	}
+	// final width is rounded
+	if(flag)
+		o = rnd(o, maxalign);
+	t->align = maxalign;
+
+	// type width only includes back to first field's offset
+	if(t->type == T)
+		t->width = 0;
+	else
+		t->width = o - t->type->width;
+	return o;
+}
+
+void
+dowidth(Type *t)
+{
+	int32 et;
+	int64 w;
+	int lno;
+	Type *t1;
+
+	if(widthptr == 0)
+		fatal("dowidth without betypeinit");
+
+	if(t == T)
+		return;
+
+	if(t->width > 0)
+		return;
+
+	if(t->width == -2) {
+		lno = lineno;
+		lineno = t->lineno;
+		if(!t->broke) {
+			t->broke = 1;
+			yyerror("invalid recursive type %T", t);
+		}
+		t->width = 0;
+		lineno = lno;
+		return;
+	}
+
+	// break infinite recursion if the broken recursive type
+	// is referenced again
+	if(t->broke && t->width == 0)
+		return;
+
+	// defer checkwidth calls until after we're done
+	defercalc++;
+
+	lno = lineno;
+	lineno = t->lineno;
+	t->width = -2;
+	t->align = 0;
+
+	et = t->etype;
+	switch(et) {
+	case TFUNC:
+	case TCHAN:
+	case TMAP:
+	case TSTRING:
+		break;
+
+	default:
+		/* simtype == 0 during bootstrap */
+		if(simtype[t->etype] != 0)
+			et = simtype[t->etype];
+		break;
+	}
+
+	w = 0;
+	switch(et) {
+	default:
+		fatal("dowidth: unknown type: %T", t);
+		break;
+
+	/* compiler-specific stuff */
+	case TINT8:
+	case TUINT8:
+	case TBOOL:		// bool is int8
+		w = 1;
+		break;
+	case TINT16:
+	case TUINT16:
+		w = 2;
+		break;
+	case TINT32:
+	case TUINT32:
+	case TFLOAT32:
+		w = 4;
+		break;
+	case TINT64:
+	case TUINT64:
+	case TFLOAT64:
+	case TCOMPLEX64:
+		w = 8;
+		t->align = widthreg;
+		break;
+	case TCOMPLEX128:
+		w = 16;
+		t->align = widthreg;
+		break;
+	case TPTR32:
+		w = 4;
+		checkwidth(t->type);
+		break;
+	case TPTR64:
+		w = 8;
+		checkwidth(t->type);
+		break;
+	case TUNSAFEPTR:
+		w = widthptr;
+		break;
+	case TINTER:		// implemented as 2 pointers
+		w = 2*widthptr;
+		t->align = widthptr;
+		offmod(t);
+		break;
+	case TCHAN:		// implemented as pointer
+		w = widthptr;
+		checkwidth(t->type);
+
+		// make fake type to check later to
+		// trigger channel argument check.
+		t1 = typ(TCHANARGS);
+		t1->type = t;
+		checkwidth(t1);
+		break;
+	case TCHANARGS:
+		t1 = t->type;
+		dowidth(t->type);	// just in case
+		if(t1->type->width >= (1<<16))
+			yyerror("channel element type too large (>64kB)");
+		t->width = 1;
+		break;
+	case TMAP:		// implemented as pointer
+		w = widthptr;
+		checkwidth(t->type);
+		checkwidth(t->down);
+		break;
+	case TFORW:		// should have been filled in
+		if(!t->broke)
+			yyerror("invalid recursive type %T", t);
+		w = 1;	// anything will do
+		break;
+	case TANY:
+		// dummy type; should be replaced before use.
+		if(!debug['A'])
+			fatal("dowidth any");
+		w = 1;	// anything will do
+		break;
+	case TSTRING:
+		if(sizeof_String == 0)
+			fatal("early dowidth string");
+		w = sizeof_String;
+		t->align = widthptr;
+		break;
+	case TARRAY:
+		if(t->type == T)
+			break;
+		if(t->bound >= 0) {
+			uint64 cap;
+
+			dowidth(t->type);
+			if(t->type->width != 0) {
+				cap = (MAXWIDTH-1) / t->type->width;
+				if(t->bound > cap)
+					yyerror("type %lT larger than address space", t);
+			}
+			w = t->bound * t->type->width;
+			t->align = t->type->align;
+		}
+		else if(t->bound == -1) {
+			w = sizeof_Array;
+			checkwidth(t->type);
+			t->align = widthptr;
+		}
+		else if(t->bound == -100) {
+			if(!t->broke) {
+				yyerror("use of [...] array outside of array literal");
+				t->broke = 1;
+			}
+		}
+		else
+			fatal("dowidth %T", t);	// probably [...]T
+		break;
+
+	case TSTRUCT:
+		if(t->funarg)
+			fatal("dowidth fn struct %T", t);
+		w = widstruct(t, t, 0, 1);
+		break;
+
+	case TFUNC:
+		// make fake type to check later to
+		// trigger function argument computation.
+		t1 = typ(TFUNCARGS);
+		t1->type = t;
+		checkwidth(t1);
+
+		// width of func type is pointer
+		w = widthptr;
+		break;
+
+	case TFUNCARGS:
+		// function is 3 cated structures;
+		// compute their widths as side-effect.
+		t1 = t->type;
+		w = widstruct(t->type, *getthis(t1), 0, 0);
+		w = widstruct(t->type, *getinarg(t1), w, widthreg);
+		w = widstruct(t->type, *getoutarg(t1), w, widthreg);
+		t1->argwid = w;
+		if(w%widthreg)
+			warn("bad type %T %d\n", t1, w);
+		t->align = 1;
+		break;
+	}
+
+	if(widthptr == 4 && w != (int32)w)
+		yyerror("type %T too large", t);
+
+	t->width = w;
+	if(t->align == 0) {
+		if(w > 8 || (w&(w-1)) != 0)
+			fatal("invalid alignment for %T", t);
+		t->align = w;
+	}
+	lineno = lno;
+
+	if(defercalc == 1)
+		resumecheckwidth();
+	else
+		--defercalc;
+}
+
+/*
+ * when a type's width should be known, we call checkwidth
+ * to compute it.  during a declaration like
+ *
+ *	type T *struct { next T }
+ *
+ * it is necessary to defer the calculation of the struct width
+ * until after T has been initialized to be a pointer to that struct.
+ * similarly, during import processing structs may be used
+ * before their definition.  in those situations, calling
+ * defercheckwidth() stops width calculations until
+ * resumecheckwidth() is called, at which point all the
+ * checkwidths that were deferred are executed.
+ * dowidth should only be called when the type's size
+ * is needed immediately.  checkwidth makes sure the
+ * size is evaluated eventually.
+ */
+typedef struct TypeList TypeList;
+struct TypeList {
+	Type *t;
+	TypeList *next;
+};
+
+static TypeList *tlfree;
+static TypeList *tlq;
+
+void
+checkwidth(Type *t)
+{
+	TypeList *l;
+
+	if(t == T)
+		return;
+
+	// function arg structs should not be checked
+	// outside of the enclosing function.
+	if(t->funarg)
+		fatal("checkwidth %T", t);
+
+	if(!defercalc) {
+		dowidth(t);
+		return;
+	}
+	if(t->deferwidth)
+		return;
+	t->deferwidth = 1;
+
+	l = tlfree;
+	if(l != nil)
+		tlfree = l->next;
+	else
+		l = mal(sizeof *l);
+
+	l->t = t;
+	l->next = tlq;
+	tlq = l;
+}
+
+void
+defercheckwidth(void)
+{
+	// we get out of sync on syntax errors, so don't be pedantic.
+	if(defercalc && nerrors == 0)
+		fatal("defercheckwidth");
+	defercalc = 1;
+}
+
+void
+resumecheckwidth(void)
+{
+	TypeList *l;
+
+	if(!defercalc)
+		fatal("resumecheckwidth");
+	for(l = tlq; l != nil; l = tlq) {
+		l->t->deferwidth = 0;
+		tlq = l->next;
+		dowidth(l->t);
+		l->next = tlfree;
+		tlfree = l;
+	}
+	defercalc = 0;
+}
+
+void
+typeinit(void)
+{
+	int i, etype, sameas;
+	Type *t;
+	Sym *s, *s1;
+
+	if(widthptr == 0)
+		fatal("typeinit before betypeinit");
+
+	for(i=0; i<NTYPE; i++)
+		simtype[i] = i;
+
+	types[TPTR32] = typ(TPTR32);
+	dowidth(types[TPTR32]);
+
+	types[TPTR64] = typ(TPTR64);
+	dowidth(types[TPTR64]);
+	
+	t = typ(TUNSAFEPTR);
+	types[TUNSAFEPTR] = t;
+	t->sym = pkglookup("Pointer", unsafepkg);
+	t->sym->def = typenod(t);
+	
+	dowidth(types[TUNSAFEPTR]);
+
+	tptr = TPTR32;
+	if(widthptr == 8)
+		tptr = TPTR64;
+
+	for(i=TINT8; i<=TUINT64; i++)
+		isint[i] = 1;
+	isint[TINT] = 1;
+	isint[TUINT] = 1;
+	isint[TUINTPTR] = 1;
+
+	isfloat[TFLOAT32] = 1;
+	isfloat[TFLOAT64] = 1;
+
+	iscomplex[TCOMPLEX64] = 1;
+	iscomplex[TCOMPLEX128] = 1;
+
+	isptr[TPTR32] = 1;
+	isptr[TPTR64] = 1;
+
+	isforw[TFORW] = 1;
+
+	issigned[TINT] = 1;
+	issigned[TINT8] = 1;
+	issigned[TINT16] = 1;
+	issigned[TINT32] = 1;
+	issigned[TINT64] = 1;
+
+	/*
+	 * initialize okfor
+	 */
+	for(i=0; i<NTYPE; i++) {
+		if(isint[i] || i == TIDEAL) {
+			okforeq[i] = 1;
+			okforcmp[i] = 1;
+			okforarith[i] = 1;
+			okforadd[i] = 1;
+			okforand[i] = 1;
+			okforconst[i] = 1;
+			issimple[i] = 1;
+			minintval[i] = mal(sizeof(*minintval[i]));
+			maxintval[i] = mal(sizeof(*maxintval[i]));
+		}
+		if(isfloat[i]) {
+			okforeq[i] = 1;
+			okforcmp[i] = 1;
+			okforadd[i] = 1;
+			okforarith[i] = 1;
+			okforconst[i] = 1;
+			issimple[i] = 1;
+			minfltval[i] = mal(sizeof(*minfltval[i]));
+			maxfltval[i] = mal(sizeof(*maxfltval[i]));
+		}
+		if(iscomplex[i]) {
+			okforeq[i] = 1;
+			okforadd[i] = 1;
+			okforarith[i] = 1;
+			okforconst[i] = 1;
+			issimple[i] = 1;
+		}
+	}
+
+	issimple[TBOOL] = 1;
+
+	okforadd[TSTRING] = 1;
+
+	okforbool[TBOOL] = 1;
+
+	okforcap[TARRAY] = 1;
+	okforcap[TCHAN] = 1;
+
+	okforconst[TBOOL] = 1;
+	okforconst[TSTRING] = 1;
+
+	okforlen[TARRAY] = 1;
+	okforlen[TCHAN] = 1;
+	okforlen[TMAP] = 1;
+	okforlen[TSTRING] = 1;
+
+	okforeq[TPTR32] = 1;
+	okforeq[TPTR64] = 1;
+	okforeq[TUNSAFEPTR] = 1;
+	okforeq[TINTER] = 1;
+	okforeq[TCHAN] = 1;
+	okforeq[TSTRING] = 1;
+	okforeq[TBOOL] = 1;
+	okforeq[TMAP] = 1;	// nil only; refined in typecheck
+	okforeq[TFUNC] = 1;	// nil only; refined in typecheck
+	okforeq[TARRAY] = 1;	// nil slice only; refined in typecheck
+	okforeq[TSTRUCT] = 1;	// it's complicated; refined in typecheck
+
+	okforcmp[TSTRING] = 1;
+
+	for(i=0; i<nelem(okfor); i++)
+		okfor[i] = okfornone;
+
+	// binary
+	okfor[OADD] = okforadd;
+	okfor[OAND] = okforand;
+	okfor[OANDAND] = okforbool;
+	okfor[OANDNOT] = okforand;
+	okfor[ODIV] = okforarith;
+	okfor[OEQ] = okforeq;
+	okfor[OGE] = okforcmp;
+	okfor[OGT] = okforcmp;
+	okfor[OLE] = okforcmp;
+	okfor[OLT] = okforcmp;
+	okfor[OMOD] = okforand;
+	okfor[OMUL] = okforarith;
+	okfor[ONE] = okforeq;
+	okfor[OOR] = okforand;
+	okfor[OOROR] = okforbool;
+	okfor[OSUB] = okforarith;
+	okfor[OXOR] = okforand;
+	okfor[OLSH] = okforand;
+	okfor[ORSH] = okforand;
+
+	// unary
+	okfor[OCOM] = okforand;
+	okfor[OMINUS] = okforarith;
+	okfor[ONOT] = okforbool;
+	okfor[OPLUS] = okforarith;
+
+	// special
+	okfor[OCAP] = okforcap;
+	okfor[OLEN] = okforlen;
+
+	// comparison
+	iscmp[OLT] = 1;
+	iscmp[OGT] = 1;
+	iscmp[OGE] = 1;
+	iscmp[OLE] = 1;
+	iscmp[OEQ] = 1;
+	iscmp[ONE] = 1;
+
+	mpatofix(maxintval[TINT8], "0x7f");
+	mpatofix(minintval[TINT8], "-0x80");
+	mpatofix(maxintval[TINT16], "0x7fff");
+	mpatofix(minintval[TINT16], "-0x8000");
+	mpatofix(maxintval[TINT32], "0x7fffffff");
+	mpatofix(minintval[TINT32], "-0x80000000");
+	mpatofix(maxintval[TINT64], "0x7fffffffffffffff");
+	mpatofix(minintval[TINT64], "-0x8000000000000000");
+
+	mpatofix(maxintval[TUINT8], "0xff");
+	mpatofix(maxintval[TUINT16], "0xffff");
+	mpatofix(maxintval[TUINT32], "0xffffffff");
+	mpatofix(maxintval[TUINT64], "0xffffffffffffffff");
+
+	/* f is valid float if min < f < max.  (min and max are not themselves valid.) */
+	mpatoflt(maxfltval[TFLOAT32], "33554431p103");	/* 2^24-1 p (127-23) + 1/2 ulp*/
+	mpatoflt(minfltval[TFLOAT32], "-33554431p103");
+	mpatoflt(maxfltval[TFLOAT64], "18014398509481983p970");	/* 2^53-1 p (1023-52) + 1/2 ulp */
+	mpatoflt(minfltval[TFLOAT64], "-18014398509481983p970");
+
+	maxfltval[TCOMPLEX64] = maxfltval[TFLOAT32];
+	minfltval[TCOMPLEX64] = minfltval[TFLOAT32];
+	maxfltval[TCOMPLEX128] = maxfltval[TFLOAT64];
+	minfltval[TCOMPLEX128] = minfltval[TFLOAT64];
+
+	/* for walk to use in error messages */
+	types[TFUNC] = functype(N, nil, nil);
+
+	/* types used in front end */
+	// types[TNIL] got set early in lexinit
+	types[TIDEAL] = typ(TIDEAL);
+	types[TINTER] = typ(TINTER);
+
+	/* simple aliases */
+	simtype[TMAP] = tptr;
+	simtype[TCHAN] = tptr;
+	simtype[TFUNC] = tptr;
+	simtype[TUNSAFEPTR] = tptr;
+
+	/* pick up the backend typedefs */
+	for(i=0; typedefs[i].name; i++) {
+		s = lookup(typedefs[i].name);
+		s1 = pkglookup(typedefs[i].name, builtinpkg);
+
+		etype = typedefs[i].etype;
+		if(etype < 0 || etype >= nelem(types))
+			fatal("typeinit: %s bad etype", s->name);
+		sameas = typedefs[i].sameas;
+		if(sameas < 0 || sameas >= nelem(types))
+			fatal("typeinit: %s bad sameas", s->name);
+		simtype[etype] = sameas;
+		minfltval[etype] = minfltval[sameas];
+		maxfltval[etype] = maxfltval[sameas];
+		minintval[etype] = minintval[sameas];
+		maxintval[etype] = maxintval[sameas];
+
+		t = types[etype];
+		if(t != T)
+			fatal("typeinit: %s already defined", s->name);
+
+		t = typ(etype);
+		t->sym = s1;
+
+		dowidth(t);
+		types[etype] = t;
+		s1->def = typenod(t);
+	}
+
+	Array_array = rnd(0, widthptr);
+	Array_nel = rnd(Array_array+widthptr, widthint);
+	Array_cap = rnd(Array_nel+widthint, widthint);
+	sizeof_Array = rnd(Array_cap+widthint, widthptr);
+
+	// string is same as slice wo the cap
+	sizeof_String = rnd(Array_nel+widthint, widthptr);
+
+	dowidth(types[TSTRING]);
+	dowidth(idealstring);
+}
+
+/*
+ * compute total size of f's in/out arguments.
+ */
+int
+argsize(Type *t)
+{
+	Iter save;
+	Type *fp;
+	int64 w, x;
+
+	w = 0;
+
+	fp = structfirst(&save, getoutarg(t));
+	while(fp != T) {
+		x = fp->width + fp->type->width;
+		if(x > w)
+			w = x;
+		fp = structnext(&save);
+	}
+
+	fp = funcfirst(&save, t);
+	while(fp != T) {
+		x = fp->width + fp->type->width;
+		if(x > w)
+			w = x;
+		fp = funcnext(&save);
+	}
+
+	w = (w+widthptr-1) & ~(widthptr-1);
+	if((int)w != w)
+		fatal("argsize too big");
+	return w;
+}
diff --git a/src/cmd/gc/array.c b/src/cmd/gc/array.c
new file mode 100644
index 0000000..611fc9f
--- /dev/null
+++ b/src/cmd/gc/array.c
@@ -0,0 +1,115 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+enum {
+	DEFAULTCAPACITY = 16,
+};
+
+struct Array
+{
+	int32	length;  // number of elements
+	int32	size;  // element size
+	int32	capacity;  // size of data in elements
+	char	*data;  // element storage
+};
+
+Array*
+arraynew(int32 capacity, int32 size)
+{
+	Array *result;
+
+	if(capacity < 0)
+		fatal("arraynew: capacity %d is not positive", capacity);
+	if(size < 0)
+		fatal("arraynew: size %d is not positive\n", size);
+	result = malloc(sizeof(*result));
+	if(result == nil)
+		fatal("arraynew: malloc failed\n");
+	result->length = 0;
+	result->size = size;
+	result->capacity = capacity == 0 ? DEFAULTCAPACITY : capacity;
+	result->data = malloc(result->capacity * result->size);
+	if(result->data == nil)
+		fatal("arraynew: malloc failed\n");
+	return result;
+}
+
+void
+arrayfree(Array *array)
+{
+	if(array == nil)
+		return;
+	free(array->data);
+	free(array);
+}
+
+int32
+arraylength(Array *array)
+{
+	return array->length;
+}
+
+void*
+arrayget(Array *array, int32 index)
+{
+	if(array == nil)
+		fatal("arrayget: array is nil\n");
+	if(index < 0 || index >= array->length)
+		fatal("arrayget: index %d is out of bounds for length %d\n", index, array->length);
+	return array->data + index * array->size;
+}
+
+void
+arrayset(Array *array, int32 index, void *element)
+{
+	if(array == nil)
+		fatal("arrayset: array is nil\n");
+	if(element == nil)
+		fatal("arrayset: element is nil\n");
+	if(index < 0 || index >= array->length)
+		fatal("arrayget: index %d is out of bounds for length %d\n", index, array->length);
+	memmove(array->data + index * array->size, element, array->size);
+}
+
+static void
+ensurecapacity(Array *array, int32 capacity)
+{
+	int32 newcapacity;
+	char *newdata;
+
+	if(array == nil)
+		fatal("ensurecapacity: array is nil\n");
+	if(capacity < 0)
+		fatal("ensurecapacity: capacity %d is not positive", capacity);
+	if(capacity >= array->capacity) {
+		newcapacity = capacity + (capacity >> 1);
+		newdata = realloc(array->data, newcapacity * array->size);
+		if(newdata == nil)
+			fatal("ensurecapacity: realloc failed\n");
+		array->capacity = newcapacity;
+		array->data = newdata;
+	}
+}
+
+void
+arrayadd(Array *array, void *element)
+{
+	if(array == nil)
+		fatal("arrayset: array is nil\n");
+	if(element == nil)
+		fatal("arrayset: element is nil\n");
+	ensurecapacity(array, array->length + 1);
+	array->length++;
+	arrayset(array, array->length - 1, element);
+}
+
+void
+arraysort(Array *array, int (*cmp)(const void*, const void*))
+{
+	qsort(array->data, array->length, array->size, cmp);
+}
diff --git a/src/cmd/gc/bisonerrors b/src/cmd/gc/bisonerrors
new file mode 100755
index 0000000..fa74c67
--- /dev/null
+++ b/src/cmd/gc/bisonerrors
@@ -0,0 +1,156 @@
+#!/usr/bin/awk -f
+# Copyright 2010 The Go Authors.  All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# This program implements the core idea from
+#
+#	Clinton L. Jeffery, Generating LR syntax error messages from examples,
+#	ACM TOPLAS 25(5) (September 2003).  http://doi.acm.org/10.1145/937563.937566
+# 
+# It reads Bison's summary of a grammar followed by a file
+# like go.errors, replacing lines beginning with % by the 
+# yystate and yychar that will be active when an error happens
+# while parsing that line.  
+#
+# Unlike the system described in the paper, the lines in go.errors
+# give grammar symbol name lists, not actual program fragments.
+# This is a little less programmer-friendly but doesn't require being
+# able to run the text through lex.c.
+
+BEGIN{
+	bison = 1
+	grammar = 0
+	states = 0
+	open = 0
+}
+
+# In Grammar section of y.output,
+# record lhs and length of rhs for each rule.
+bison && /^Grammar/ { grammar = 1 }
+bison && /^(Terminals|state 0)/ { grammar = 0 }
+grammar && NF>0 {
+	if($2 != "|") {
+		r = $2
+		sub(/:$/, "", r)
+	}
+	rulelhs[$1] = r
+	rulesize[$1] = NF-2
+	if(rulesize[$1] == 1 && $3 == "%empty") {
+		rulesize[$1] = 0
+	}
+	if(rulesize[$1] == 3 && $3 $4 $5 == "/*empty*/") {
+		rulesize[$1] = 0
+	}
+}
+
+# In state dumps, record shift/reduce actions.
+bison && /^[Ss]tate 0/ { grammar = 0; states = 1 }
+
+states && /^[Ss]tate / { state = $2 }
+states { statetext[state] = statetext[state] $0 "\n" }
+
+states && / shift/ {
+	n = nshift[state]++
+	if($0 ~ /and go to/)
+		shift[state,n] = $7 # GNU Bison
+	else
+		shift[state,n] = $3 # Plan 9 Yacc
+	shifttoken[state,n] = $1
+	next
+}
+states && / (go to|goto)/ {
+	n = nshift[state]++
+	if($0 ~ /go to/)
+		shift[state,n] = $5 # GNU Bison
+	else
+		shift[state,n] = $3 # Plan 9 Yacc
+	shifttoken[state,n] = $1
+	next
+}
+states && / reduce/ {
+	n = nreduce[state]++
+	if($0 ~ /reduce using rule/)
+		reduce[state,n] = $5 # GNU Bison
+	else
+		reduce[state,n] = $3 # Plan 9 yacc
+	reducetoken[state,n] = $1
+	next
+}
+
+# Skip over the summary information printed by Plan 9 yacc.
+/nonterminals$/,/^maximum spread/ { next }
+
+# First // comment marks the beginning of the pattern file.
+/^\/\// { bison = 0; grammar = 0; state = 0 }
+bison { next }
+
+# Treat % as first field on line as introducing a pattern (token sequence).
+# Run it through the LR machine and print the induced "yystate, yychar,"
+# at the point where the error happens.
+$1 == "%" {
+	nstack = 0
+	state = 0
+	f = 2
+	tok = ""
+	for(;;) {
+		if(tok == "" && f <= NF) {
+			tok = $f
+			f++
+		}
+		found = 0
+		for(j=0; j<nshift[state]; j++) {
+			if(shifttoken[state,j] == tok) {
+				# print "SHIFT " tok " " state " -> " shift[state,j]
+				stack[nstack++] = state
+				state = shift[state,j]
+				found = 1
+				tok = ""
+				break
+			}
+		}
+		if(found)
+			continue
+		for(j=0; j<nreduce[state]; j++) {
+			t = reducetoken[state,j]
+			if(t == tok || t == "$default" || t == ".") {
+				stack[nstack++] = state
+				rule = reduce[state,j]
+				nstack -= rulesize[rule]
+				state = stack[--nstack]
+				lhs = rulelhs[rule]
+				if(tok != "")
+					--f
+				tok = rulelhs[rule]
+				# print "REDUCE " nstack " " state " " tok " rule " rule " size " rulesize[rule]
+				found = 1
+				break
+			}
+		}
+		if(found)
+			continue
+
+		# No shift or reduce applied - found the error.
+		printf("\t{%s, %s,\n", state, tok);
+		open = 1;
+		break
+	}
+	next
+}
+
+# Print other lines verbatim.
+open && /,$/ {
+	s = $0;
+	sub(",", "},", s)
+	print s
+	open = 0
+	next
+}
+
+open && /"$/ {
+	print $0 "}"
+	open = 0
+	next
+}
+
+{print}
diff --git a/src/cmd/gc/bits.c b/src/cmd/gc/bits.c
new file mode 100644
index 0000000..2e79f6f
--- /dev/null
+++ b/src/cmd/gc/bits.c
@@ -0,0 +1,163 @@
+// Inferno utils/cc/bits.c
+// http://code.google.com/p/inferno-os/source/browse/utils/cc/bits.c
+//
+//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+//	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+//	Portions Copyright © 1997-1999 Vita Nuova Limited
+//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+//	Portions Copyright © 2004,2006 Bruce Ellis
+//	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+/*
+Bits
+bor(Bits a, Bits b)
+{
+	Bits c;
+	int i;
+
+	for(i=0; i<BITS; i++)
+		c.b[i] = a.b[i] | b.b[i];
+	return c;
+}
+
+Bits
+band(Bits a, Bits b)
+{
+	Bits c;
+	int i;
+
+	for(i=0; i<BITS; i++)
+		c.b[i] = a.b[i] & b.b[i];
+	return c;
+}
+
+Bits
+bnot(Bits a)
+{
+	Bits c;
+	int i;
+
+	for(i=0; i<BITS; i++)
+		c.b[i] = ~a.b[i];
+	return c;
+}
+*/
+
+int
+bany(Bits *a)
+{
+	int i;
+
+	for(i=0; i<BITS; i++)
+		if(a->b[i])
+			return 1;
+	return 0;
+}
+
+/*
+int
+beq(Bits a, Bits b)
+{
+	int i;
+
+	for(i=0; i<BITS; i++)
+		if(a.b[i] != b.b[i])
+			return 0;
+	return 1;
+}
+*/
+
+int
+bnum(Bits a)
+{
+	int i;
+	int32 b;
+
+	for(i=0; i<BITS; i++)
+		if(b = a.b[i])
+			return 32*i + bitno(b);
+	fatal("bad in bnum");
+	return 0;
+}
+
+Bits
+blsh(uint n)
+{
+	Bits c;
+
+	c = zbits;
+	c.b[n/32] = 1L << (n%32);
+	return c;
+}
+
+/*
+int
+bset(Bits a, uint n)
+{
+	if(a.b[n/32] & (1L << (n%32)))
+		return 1;
+	return 0;
+}
+*/
+
+int
+bitno(int32 b)
+{
+	int i;
+
+	for(i=0; i<32; i++)
+		if(b & (1L<<i))
+			return i;
+	fatal("bad in bitno");
+	return 0;
+}
+
+int
+Qconv(Fmt *fp)
+{
+	Bits bits;
+	int i, first;
+
+	first = 1;
+	bits = va_arg(fp->args, Bits);
+	while(bany(&bits)) {
+		i = bnum(bits);
+		if(first)
+			first = 0;
+		else
+			fmtprint(fp, " ");
+		if(var[i].node == N || var[i].node->sym == S)
+			fmtprint(fp, "$%d", i);
+		else {
+			fmtprint(fp, "%s(%d)", var[i].node->sym->name, i);
+			if(var[i].offset != 0)
+				fmtprint(fp, "%+lld", (vlong)var[i].offset);
+		}
+		bits.b[i/32] &= ~(1L << (i%32));
+	}
+	return 0;
+}
diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c
new file mode 100644
index 0000000..fbca4ee
--- /dev/null
+++ b/src/cmd/gc/builtin.c
@@ -0,0 +1,137 @@
+// AUTO-GENERATED by mkbuiltin; DO NOT EDIT
+char *runtimeimport =
+	"package runtime\n"
+	"import runtime \"runtime\"\n"
+	"func @\"\".newobject (@\"\".typ·2 *byte) (? *any)\n"
+	"func @\"\".panicindex ()\n"
+	"func @\"\".panicslice ()\n"
+	"func @\"\".panicdivide ()\n"
+	"func @\"\".throwreturn ()\n"
+	"func @\"\".throwinit ()\n"
+	"func @\"\".panicwrap (? string, ? string, ? string)\n"
+	"func @\"\".gopanic (? interface {})\n"
+	"func @\"\".gorecover (? *int32) (? interface {})\n"
+	"func @\"\".printbool (? bool)\n"
+	"func @\"\".printfloat (? float64)\n"
+	"func @\"\".printint (? int64)\n"
+	"func @\"\".printhex (? uint64)\n"
+	"func @\"\".printuint (? uint64)\n"
+	"func @\"\".printcomplex (? complex128)\n"
+	"func @\"\".printstring (? string)\n"
+	"func @\"\".printpointer (? any)\n"
+	"func @\"\".printiface (? any)\n"
+	"func @\"\".printeface (? any)\n"
+	"func @\"\".printslice (? any)\n"
+	"func @\"\".printnl ()\n"
+	"func @\"\".printsp ()\n"
+	"func @\"\".concatstring2 (? string, ? string) (? string)\n"
+	"func @\"\".concatstring3 (? string, ? string, ? string) (? string)\n"
+	"func @\"\".concatstring4 (? string, ? string, ? string, ? string) (? string)\n"
+	"func @\"\".concatstring5 (? string, ? string, ? string, ? string, ? string) (? string)\n"
+	"func @\"\".concatstrings (? []string) (? string)\n"
+	"func @\"\".cmpstring (? string, ? string) (? int)\n"
+	"func @\"\".eqstring (? string, ? string) (? bool)\n"
+	"func @\"\".intstring (? int64) (? string)\n"
+	"func @\"\".slicebytetostring (? []byte) (? string)\n"
+	"func @\"\".slicebytetostringtmp (? []byte) (? string)\n"
+	"func @\"\".slicerunetostring (? []rune) (? string)\n"
+	"func @\"\".stringtoslicebyte (? string) (? []byte)\n"
+	"func @\"\".stringtoslicerune (? string) (? []rune)\n"
+	"func @\"\".stringiter (? string, ? int) (? int)\n"
+	"func @\"\".stringiter2 (? string, ? int) (@\"\".retk·1 int, @\"\".retv·2 rune)\n"
+	"func @\"\".slicecopy (@\"\".to·2 any, @\"\".fr·3 any, @\"\".wid·4 uintptr) (? int)\n"
+	"func @\"\".slicestringcopy (@\"\".to·2 any, @\"\".fr·3 any) (? int)\n"
+	"func @\"\".typ2Itab (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte) (@\"\".ret·1 *byte)\n"
+	"func @\"\".convI2E (@\"\".elem·2 any) (@\"\".ret·1 any)\n"
+	"func @\"\".convI2I (@\"\".typ·2 *byte, @\"\".elem·3 any) (@\"\".ret·1 any)\n"
+	"func @\"\".convT2E (@\"\".typ·2 *byte, @\"\".elem·3 *any) (@\"\".ret·1 any)\n"
+	"func @\"\".convT2I (@\"\".typ·2 *byte, @\"\".typ2·3 *byte, @\"\".cache·4 **byte, @\"\".elem·5 *any) (@\"\".ret·1 any)\n"
+	"func @\"\".assertE2E (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n"
+	"func @\"\".assertE2E2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n"
+	"func @\"\".assertE2I (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n"
+	"func @\"\".assertE2I2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n"
+	"func @\"\".assertE2T (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n"
+	"func @\"\".assertE2T2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n"
+	"func @\"\".assertI2E (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n"
+	"func @\"\".assertI2E2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n"
+	"func @\"\".assertI2I (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n"
+	"func @\"\".assertI2I2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n"
+	"func @\"\".assertI2T (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ret·1 any)\n"
+	"func @\"\".assertI2T2 (@\"\".typ·3 *byte, @\"\".iface·4 any) (@\"\".ret·1 any, @\"\".ok·2 bool)\n"
+	"func @\"\".assertI2TOK (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ok·1 bool)\n"
+	"func @\"\".assertE2TOK (@\"\".typ·2 *byte, @\"\".iface·3 any) (@\"\".ok·1 bool)\n"
+	"func @\"\".ifaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n"
+	"func @\"\".efaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n"
+	"func @\"\".ifacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n"
+	"func @\"\".efacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n"
+	"func @\"\".makemap (@\"\".mapType·2 *byte, @\"\".hint·3 int64) (@\"\".hmap·1 map[any]any)\n"
+	"func @\"\".mapaccess1 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 *any) (@\"\".val·1 *any)\n"
+	"func @\"\".mapaccess1_fast32 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n"
+	"func @\"\".mapaccess1_fast64 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n"
+	"func @\"\".mapaccess1_faststr (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n"
+	"func @\"\".mapaccess2 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 *any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n"
+	"func @\"\".mapaccess2_fast32 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n"
+	"func @\"\".mapaccess2_fast64 (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n"
+	"func @\"\".mapaccess2_faststr (@\"\".mapType·3 *byte, @\"\".hmap·4 map[any]any, @\"\".key·5 any) (@\"\".val·1 *any, @\"\".pres·2 bool)\n"
+	"func @\"\".mapassign1 (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 *any, @\"\".val·4 *any)\n"
+	"func @\"\".mapiterinit (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".hiter·3 *any)\n"
+	"func @\"\".mapdelete (@\"\".mapType·1 *byte, @\"\".hmap·2 map[any]any, @\"\".key·3 *any)\n"
+	"func @\"\".mapiternext (@\"\".hiter·1 *any)\n"
+	"func @\"\".makechan (@\"\".chanType·2 *byte, @\"\".hint·3 int64) (@\"\".hchan·1 chan any)\n"
+	"func @\"\".chanrecv1 (@\"\".chanType·1 *byte, @\"\".hchan·2 <-chan any, @\"\".elem·3 *any)\n"
+	"func @\"\".chanrecv2 (@\"\".chanType·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any) (? bool)\n"
+	"func @\"\".chansend1 (@\"\".chanType·1 *byte, @\"\".hchan·2 chan<- any, @\"\".elem·3 *any)\n"
+	"func @\"\".closechan (@\"\".hchan·1 any)\n"
+	"func @\"\".writebarrierptr (@\"\".dst·1 *any, @\"\".src·2 any)\n"
+	"func @\"\".writebarrierstring (@\"\".dst·1 *any, @\"\".src·2 any)\n"
+	"func @\"\".writebarrierslice (@\"\".dst·1 *any, @\"\".src·2 any)\n"
+	"func @\"\".writebarrieriface (@\"\".dst·1 *any, @\"\".src·2 any)\n"
+	"func @\"\".writebarrierfat2 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n"
+	"func @\"\".writebarrierfat3 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n"
+	"func @\"\".writebarrierfat4 (@\"\".dst·1 *any, _ *byte, @\"\".src·3 any)\n"
+	"func @\"\".writebarrierfat (@\"\".typ·1 *byte, @\"\".dst·2 *any, @\"\".src·3 *any)\n"
+	"func @\"\".selectnbsend (@\"\".chanType·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (? bool)\n"
+	"func @\"\".selectnbrecv (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".hchan·4 <-chan any) (? bool)\n"
+	"func @\"\".selectnbrecv2 (@\"\".chanType·2 *byte, @\"\".elem·3 *any, @\"\".received·4 *bool, @\"\".hchan·5 <-chan any) (? bool)\n"
+	"func @\"\".newselect (@\"\".sel·1 *byte, @\"\".selsize·2 int64, @\"\".size·3 int32)\n"
+	"func @\"\".selectsend (@\"\".sel·2 *byte, @\"\".hchan·3 chan<- any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n"
+	"func @\"\".selectrecv (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any) (@\"\".selected·1 bool)\n"
+	"func @\"\".selectrecv2 (@\"\".sel·2 *byte, @\"\".hchan·3 <-chan any, @\"\".elem·4 *any, @\"\".received·5 *bool) (@\"\".selected·1 bool)\n"
+	"func @\"\".selectdefault (@\"\".sel·2 *byte) (@\"\".selected·1 bool)\n"
+	"func @\"\".selectgo (@\"\".sel·1 *byte)\n"
+	"func @\"\".block ()\n"
+	"func @\"\".makeslice (@\"\".typ·2 *byte, @\"\".nel·3 int64, @\"\".cap·4 int64) (@\"\".ary·1 []any)\n"
+	"func @\"\".growslice (@\"\".typ·2 *byte, @\"\".old·3 []any, @\"\".n·4 int64) (@\"\".ary·1 []any)\n"
+	"func @\"\".memmove (@\"\".to·1 *any, @\"\".frm·2 *any, @\"\".length·3 uintptr)\n"
+	"func @\"\".memequal (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+	"func @\"\".memequal8 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+	"func @\"\".memequal16 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+	"func @\"\".memequal32 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+	"func @\"\".memequal64 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+	"func @\"\".memequal128 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n"
+	"func @\"\".int64div (? int64, ? int64) (? int64)\n"
+	"func @\"\".uint64div (? uint64, ? uint64) (? uint64)\n"
+	"func @\"\".int64mod (? int64, ? int64) (? int64)\n"
+	"func @\"\".uint64mod (? uint64, ? uint64) (? uint64)\n"
+	"func @\"\".float64toint64 (? float64) (? int64)\n"
+	"func @\"\".float64touint64 (? float64) (? uint64)\n"
+	"func @\"\".int64tofloat64 (? int64) (? float64)\n"
+	"func @\"\".uint64tofloat64 (? uint64) (? float64)\n"
+	"func @\"\".complex128div (@\"\".num·2 complex128, @\"\".den·3 complex128) (@\"\".quo·1 complex128)\n"
+	"func @\"\".racefuncenter (? uintptr)\n"
+	"func @\"\".racefuncexit ()\n"
+	"func @\"\".raceread (? uintptr)\n"
+	"func @\"\".racewrite (? uintptr)\n"
+	"func @\"\".racereadrange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n"
+	"func @\"\".racewriterange (@\"\".addr·1 uintptr, @\"\".size·2 uintptr)\n"
+	"\n"
+	"$$\n";
+char *unsafeimport =
+	"package unsafe\n"
+	"import runtime \"runtime\"\n"
+	"type @\"\".Pointer uintptr\n"
+	"func @\"\".Offsetof (? any) (? uintptr)\n"
+	"func @\"\".Sizeof (? any) (? uintptr)\n"
+	"func @\"\".Alignof (? any) (? uintptr)\n"
+	"\n"
+	"$$\n";
diff --git a/src/cmd/gc/bv.c b/src/cmd/gc/bv.c
new file mode 100644
index 0000000..cfd1cd2
--- /dev/null
+++ b/src/cmd/gc/bv.c
@@ -0,0 +1,213 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+enum {
+	WORDSIZE = sizeof(uint32),
+	WORDBITS = 32,
+	WORDMASK = WORDBITS - 1,
+	WORDSHIFT = 5,
+};
+
+static uintptr
+bvsize(uintptr n)
+{
+	return ((n + WORDBITS - 1) / WORDBITS) * WORDSIZE;
+}
+
+int32
+bvbits(Bvec *bv)
+{
+	return bv->n;
+}
+
+int32
+bvwords(Bvec *bv)
+{
+	return (bv->n + WORDBITS - 1) / WORDBITS;
+}
+
+Bvec*
+bvalloc(int32 n)
+{
+	Bvec *bv;
+	uintptr nbytes;
+
+	if(n < 0)
+		fatal("bvalloc: initial size is negative\n");
+	nbytes = sizeof(Bvec) + bvsize(n);
+	bv = malloc(nbytes);
+	if(bv == nil)
+		fatal("bvalloc: malloc failed\n");
+	memset(bv, 0, nbytes);
+	bv->n = n;
+	return bv;
+}
+
+/* difference */
+void
+bvandnot(Bvec *dst, Bvec *src1, Bvec *src2)
+{
+	int32 i, w;
+
+	if(dst->n != src1->n || dst->n != src2->n)
+		fatal("bvand: lengths %d, %d, and %d are not equal", dst->n, src1->n, src2->n);
+	for(i = 0, w = 0; i < dst->n; i += WORDBITS, w++)
+		dst->b[w] = src1->b[w] & ~src2->b[w];
+}
+
+int
+bvcmp(Bvec *bv1, Bvec *bv2)
+{
+	uintptr nbytes;
+
+	if(bv1->n != bv2->n)
+		fatal("bvequal: lengths %d and %d are not equal", bv1->n, bv2->n);
+	nbytes = bvsize(bv1->n);
+	return memcmp(bv1->b, bv2->b, nbytes);
+}
+
+void
+bvcopy(Bvec *dst, Bvec *src)
+{
+	memmove(dst->b, src->b, bvsize(dst->n));
+}
+
+Bvec*
+bvconcat(Bvec *src1, Bvec *src2)
+{
+	Bvec *dst;
+	int32 i;
+
+	dst = bvalloc(src1->n + src2->n);
+	for(i = 0; i < src1->n; i++)
+		if(bvget(src1, i))
+			bvset(dst, i);
+	for(i = 0; i < src2->n; i++)
+		if(bvget(src2, i))
+			bvset(dst, i + src1->n);
+	return dst;
+}
+
+int
+bvget(Bvec *bv, int32 i)
+{
+	if(i < 0 || i >= bv->n)
+		fatal("bvget: index %d is out of bounds with length %d\n", i, bv->n);
+	return (bv->b[i>>WORDSHIFT] >> (i&WORDMASK)) & 1;
+}
+
+// bvnext returns the smallest index >= i for which bvget(bv, i) == 1.
+// If there is no such index, bvnext returns -1.
+int
+bvnext(Bvec *bv, int32 i)
+{
+	uint32 w;
+
+	if(i >= bv->n)
+		return -1;
+
+	// Jump i ahead to next word with bits.
+	if((bv->b[i>>WORDSHIFT]>>(i&WORDMASK)) == 0) {
+		i &= ~WORDMASK;
+		i += WORDBITS;
+		while(i < bv->n && bv->b[i>>WORDSHIFT] == 0)
+			i += WORDBITS;
+	}
+	if(i >= bv->n)
+		return -1;
+
+	// Find 1 bit.
+	w = bv->b[i>>WORDSHIFT]>>(i&WORDMASK);
+	while((w&1) == 0) {
+		w>>=1;
+		i++;
+	}
+	return i;
+}
+
+int
+bvisempty(Bvec *bv)
+{
+	int32 i;
+
+	for(i = 0; i < bv->n; i += WORDBITS)
+		if(bv->b[i>>WORDSHIFT] != 0)
+			return 0;
+	return 1;
+}
+
+void
+bvnot(Bvec *bv)
+{
+	int32 i, w;
+
+	for(i = 0, w = 0; i < bv->n; i += WORDBITS, w++)
+		bv->b[w] = ~bv->b[w];
+}
+
+/* union */
+void
+bvor(Bvec *dst, Bvec *src1, Bvec *src2)
+{
+	int32 i, w;
+
+	if(dst->n != src1->n || dst->n != src2->n)
+		fatal("bvor: lengths %d, %d, and %d are not equal", dst->n, src1->n, src2->n);
+	for(i = 0, w = 0; i < dst->n; i += WORDBITS, w++)
+		dst->b[w] = src1->b[w] | src2->b[w];
+}
+
+/* intersection */
+void
+bvand(Bvec *dst, Bvec *src1, Bvec *src2)
+{
+	int32 i, w;
+
+	if(dst->n != src1->n || dst->n != src2->n)
+		fatal("bvor: lengths %d, %d, and %d are not equal", dst->n, src1->n, src2->n);
+	for(i = 0, w = 0; i < dst->n; i += WORDBITS, w++)
+		dst->b[w] = src1->b[w] & src2->b[w];
+}
+
+void
+bvprint(Bvec *bv)
+{
+	int32 i;
+
+	print("#*");
+	for(i = 0; i < bv->n; i++)
+		print("%d", bvget(bv, i));
+}
+
+void
+bvreset(Bvec *bv, int32 i)
+{
+	uint32 mask;
+
+	if(i < 0 || i >= bv->n)
+		fatal("bvreset: index %d is out of bounds with length %d\n", i, bv->n);
+	mask = ~(1 << (i % WORDBITS));
+	bv->b[i / WORDBITS] &= mask;
+}
+
+void
+bvresetall(Bvec *bv)
+{
+	memset(bv->b, 0x00, bvsize(bv->n));
+}
+
+void
+bvset(Bvec *bv, int32 i)
+{
+	uint32 mask;
+
+	if(i < 0 || i >= bv->n)
+		fatal("bvset: index %d is out of bounds with length %d\n", i, bv->n);
+	mask = 1U << (i % WORDBITS);
+	bv->b[i / WORDBITS] |= mask;
+}
diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c
new file mode 100644
index 0000000..ad4e5bd
--- /dev/null
+++ b/src/cmd/gc/closure.c
@@ -0,0 +1,467 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * function literals aka closures
+ */
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+void
+closurehdr(Node *ntype)
+{
+	Node *n, *name, *a;
+	NodeList *l;
+
+	n = nod(OCLOSURE, N, N);
+	n->ntype = ntype;
+	n->funcdepth = funcdepth;
+
+	funchdr(n);
+
+	// steal ntype's argument names and
+	// leave a fresh copy in their place.
+	// references to these variables need to
+	// refer to the variables in the external
+	// function declared below; see walkclosure.
+	n->list = ntype->list;
+	n->rlist = ntype->rlist;
+	ntype->list = nil;
+	ntype->rlist = nil;
+	for(l=n->list; l; l=l->next) {
+		name = l->n->left;
+		if(name)
+			name = newname(name->sym);
+		a = nod(ODCLFIELD, name, l->n->right);
+		a->isddd = l->n->isddd;
+		if(name)
+			name->isddd = a->isddd;
+		ntype->list = list(ntype->list, a);
+	}
+	for(l=n->rlist; l; l=l->next) {
+		name = l->n->left;
+		if(name)
+			name = newname(name->sym);
+		ntype->rlist = list(ntype->rlist, nod(ODCLFIELD, name, l->n->right));
+	}
+}
+
+Node*
+closurebody(NodeList *body)
+{
+	Node *func, *v;
+	NodeList *l;
+
+	if(body == nil)
+		body = list1(nod(OEMPTY, N, N));
+
+	func = curfn;
+	func->nbody = body;
+	func->endlineno = lineno;
+	funcbody(func);
+
+	// closure-specific variables are hanging off the
+	// ordinary ones in the symbol table; see oldname.
+	// unhook them.
+	// make the list of pointers for the closure call.
+	for(l=func->cvars; l; l=l->next) {
+		v = l->n;
+		v->closure->closure = v->outer;
+		v->heapaddr = nod(OADDR, oldname(v->sym), N);
+	}
+
+	return func;
+}
+
+static Node* makeclosure(Node *func);
+
+void
+typecheckclosure(Node *func, int top)
+{
+	Node *oldfn;
+	NodeList *l;
+	Node *v;
+
+	oldfn = curfn;
+	typecheck(&func->ntype, Etype);
+	func->type = func->ntype->type;
+	
+	// Type check the body now, but only if we're inside a function.
+	// At top level (in a variable initialization: curfn==nil) we're not
+	// ready to type check code yet; we'll check it later, because the
+	// underlying closure function we create is added to xtop.
+	if(curfn && func->type != T) {
+		curfn = func;
+		typechecklist(func->nbody, Etop);
+		curfn = oldfn;
+	}
+
+	// type check the & of closed variables outside the closure,
+	// so that the outer frame also grabs them and knows they
+	// escape.
+	func->enter = nil;
+	for(l=func->cvars; l; l=l->next) {
+		v = l->n;
+		if(v->type == T) {
+			// if v->type is nil, it means v looked like it was
+			// going to be used in the closure but wasn't.
+			// this happens because when parsing a, b, c := f()
+			// the a, b, c gets parsed as references to older
+			// a, b, c before the parser figures out this is a
+			// declaration.
+			v->op = 0;
+			continue;
+		}
+		// For a closure that is called in place, but not
+		// inside a go statement, avoid moving variables to the heap.
+		if ((top & (Ecall|Eproc)) == Ecall)
+			v->heapaddr->etype = 1;
+		typecheck(&v->heapaddr, Erv);
+		func->enter = list(func->enter, v->heapaddr);
+		v->heapaddr = N;
+	}
+
+	// Create top-level function 
+	xtop = list(xtop, makeclosure(func));
+}
+
+static Node*
+makeclosure(Node *func)
+{
+	Node *xtype, *v, *addr, *xfunc, *cv;
+	NodeList *l, *body;
+	static int closgen;
+	char *p;
+	vlong offset;
+
+	/*
+	 * wrap body in external function
+	 * that begins by reading closure parameters.
+	 */
+	xtype = nod(OTFUNC, N, N);
+	xtype->list = func->list;
+	xtype->rlist = func->rlist;
+
+	// create the function
+	xfunc = nod(ODCLFUNC, N, N);
+	snprint(namebuf, sizeof namebuf, "func·%.3d", ++closgen);
+	xfunc->nname = newname(lookup(namebuf));
+	xfunc->nname->sym->flags |= SymExported; // disable export
+	xfunc->nname->ntype = xtype;
+	xfunc->nname->defn = xfunc;
+	declare(xfunc->nname, PFUNC);
+	xfunc->nname->funcdepth = func->funcdepth;
+	xfunc->funcdepth = func->funcdepth;
+	xfunc->endlineno = func->endlineno;
+	
+	// declare variables holding addresses taken from closure
+	// and initialize in entry prologue.
+	body = nil;
+	offset = widthptr;
+	xfunc->needctxt = func->cvars != nil;
+	for(l=func->cvars; l; l=l->next) {
+		v = l->n;
+		if(v->op == 0)
+			continue;
+		addr = nod(ONAME, N, N);
+		p = smprint("&%s", v->sym->name);
+		addr->sym = lookup(p);
+		free(p);
+		addr->ntype = nod(OIND, typenod(v->type), N);
+		addr->class = PAUTO;
+		addr->addable = 1;
+		addr->ullman = 1;
+		addr->used = 1;
+		addr->curfn = xfunc;
+		xfunc->dcl = list(xfunc->dcl, addr);
+		v->heapaddr = addr;
+		cv = nod(OCLOSUREVAR, N, N);
+		cv->type = ptrto(v->type);
+		cv->xoffset = offset;
+		body = list(body, nod(OAS, addr, cv));
+		offset += widthptr;
+	}
+	typechecklist(body, Etop);
+	walkstmtlist(body);
+	xfunc->enter = body;
+
+	xfunc->nbody = func->nbody;
+	xfunc->dcl = concat(func->dcl, xfunc->dcl);
+	if(xfunc->nbody == nil)
+		fatal("empty body - won't generate any code");
+	typecheck(&xfunc, Etop);
+
+	xfunc->closure = func;
+	func->closure = xfunc;
+	
+	func->nbody = nil;
+	func->list = nil;
+	func->rlist = nil;
+
+	return xfunc;
+}
+
+Node*
+walkclosure(Node *func, NodeList **init)
+{
+	Node *clos, *typ;
+	NodeList *l;
+	char buf[20];
+	int narg;
+
+	// If no closure vars, don't bother wrapping.
+	if(func->cvars == nil)
+		return func->closure->nname;
+
+	// Create closure in the form of a composite literal.
+	// supposing the closure captures an int i and a string s
+	// and has one float64 argument and no results,
+	// the generated code looks like:
+	//
+	//	clos = &struct{F uintptr; A0 *int; A1 *string}{func·001, &i, &s}
+	//
+	// The use of the struct provides type information to the garbage
+	// collector so that it can walk the closure. We could use (in this case)
+	// [3]unsafe.Pointer instead, but that would leave the gc in the dark.
+	// The information appears in the binary in the form of type descriptors;
+	// the struct is unnamed so that closures in multiple packages with the
+	// same struct type can share the descriptor.
+
+	narg = 0;
+	typ = nod(OTSTRUCT, N, N);
+	typ->list = list1(nod(ODCLFIELD, newname(lookup("F")), typenod(types[TUINTPTR])));
+	for(l=func->cvars; l; l=l->next) {
+		if(l->n->op == 0)
+			continue;
+		snprint(buf, sizeof buf, "A%d", narg++);
+		typ->list = list(typ->list, nod(ODCLFIELD, newname(lookup(buf)), l->n->heapaddr->ntype));
+	}
+
+	clos = nod(OCOMPLIT, N, nod(OIND, typ, N));
+	clos->esc = func->esc;
+	clos->right->implicit = 1;
+	clos->list = concat(list1(nod(OCFUNC, func->closure->nname, N)), func->enter);
+
+	// Force type conversion from *struct to the func type.
+	clos = nod(OCONVNOP, clos, N);
+	clos->type = func->type;
+
+	typecheck(&clos, Erv);
+	// typecheck will insert a PTRLIT node under CONVNOP,
+	// tag it with escape analysis result.
+	clos->left->esc = func->esc;
+	// non-escaping temp to use, if any.
+	// orderexpr did not compute the type; fill it in now.
+	if(func->alloc != N) {
+		func->alloc->type = clos->left->left->type;
+		func->alloc->orig->type = func->alloc->type;
+		clos->left->right = func->alloc;
+		func->alloc = N;
+	}
+	walkexpr(&clos, init);
+
+	return clos;
+}
+
+static Node *makepartialcall(Node*, Type*, Node*);
+
+void
+typecheckpartialcall(Node *fn, Node *sym)
+{
+	switch(fn->op) {
+	case ODOTINTER:
+	case ODOTMETH:
+		break;
+	default:
+		fatal("invalid typecheckpartialcall");
+	}
+
+	// Create top-level function.
+	fn->nname = makepartialcall(fn, fn->type, sym);
+	fn->right = sym;
+	fn->op = OCALLPART;
+	fn->type = fn->nname->type;
+}
+
+static Node*
+makepartialcall(Node *fn, Type *t0, Node *meth)
+{
+	Node *ptr, *n, *fld, *call, *xtype, *xfunc, *cv, *savecurfn;
+	Type *rcvrtype, *basetype, *t;
+	NodeList *body, *l, *callargs, *retargs;
+	char *p;
+	Sym *sym;
+	Pkg *spkg;
+	static Pkg* gopkg;
+	int i, ddd;
+
+	// TODO: names are not right
+	rcvrtype = fn->left->type;
+	if(exportname(meth->sym->name))
+		p = smprint("%-hT.%s·fm", rcvrtype, meth->sym->name);
+	else
+		p = smprint("%-hT.(%-S)·fm", rcvrtype, meth->sym);
+	basetype = rcvrtype;
+	if(isptr[rcvrtype->etype])
+		basetype = basetype->type;
+	if(basetype->etype != TINTER && basetype->sym == S)
+		fatal("missing base type for %T", rcvrtype);
+
+	spkg = nil;
+	if(basetype->sym != S)
+		spkg = basetype->sym->pkg;
+	if(spkg == nil) {
+		if(gopkg == nil)
+			gopkg = mkpkg(strlit("go"));
+		spkg = gopkg;
+	}
+	sym = pkglookup(p, spkg);
+	free(p);
+	if(sym->flags & SymUniq)
+		return sym->def;
+	sym->flags |= SymUniq;
+	
+	savecurfn = curfn;
+	curfn = N;
+
+	xtype = nod(OTFUNC, N, N);
+	i = 0;
+	l = nil;
+	callargs = nil;
+	ddd = 0;
+	xfunc = nod(ODCLFUNC, N, N);
+	curfn = xfunc;
+	for(t = getinargx(t0)->type; t; t = t->down) {
+		snprint(namebuf, sizeof namebuf, "a%d", i++);
+		n = newname(lookup(namebuf));
+		n->class = PPARAM;
+		xfunc->dcl = list(xfunc->dcl, n);
+		callargs = list(callargs, n);
+		fld = nod(ODCLFIELD, n, typenod(t->type));
+		if(t->isddd) {
+			fld->isddd = 1;
+			ddd = 1;
+		}
+		l = list(l, fld);
+	}
+	xtype->list = l;
+	i = 0;
+	l = nil;
+	retargs = nil;
+	for(t = getoutargx(t0)->type; t; t = t->down) {
+		snprint(namebuf, sizeof namebuf, "r%d", i++);
+		n = newname(lookup(namebuf));
+		n->class = PPARAMOUT;
+		xfunc->dcl = list(xfunc->dcl, n);
+		retargs = list(retargs, n);
+		l = list(l, nod(ODCLFIELD, n, typenod(t->type)));
+	}
+	xtype->rlist = l;
+
+	xfunc->dupok = 1;
+	xfunc->nname = newname(sym);
+	xfunc->nname->sym->flags |= SymExported; // disable export
+	xfunc->nname->ntype = xtype;
+	xfunc->nname->defn = xfunc;
+	declare(xfunc->nname, PFUNC);
+
+	// Declare and initialize variable holding receiver.
+	body = nil;
+	xfunc->needctxt = 1;
+	cv = nod(OCLOSUREVAR, N, N);
+	cv->xoffset = widthptr;
+	cv->type = rcvrtype;
+	if(cv->type->align > widthptr)
+		cv->xoffset = cv->type->align;
+	ptr = nod(ONAME, N, N);
+	ptr->sym = lookup("rcvr");
+	ptr->class = PAUTO;
+	ptr->addable = 1;
+	ptr->ullman = 1;
+	ptr->used = 1;
+	ptr->curfn = xfunc;
+	xfunc->dcl = list(xfunc->dcl, ptr);
+	if(isptr[rcvrtype->etype] || isinter(rcvrtype)) {
+		ptr->ntype = typenod(rcvrtype);
+		body = list(body, nod(OAS, ptr, cv));
+	} else {
+		ptr->ntype = typenod(ptrto(rcvrtype));
+		body = list(body, nod(OAS, ptr, nod(OADDR, cv, N)));
+	}
+
+	call = nod(OCALL, nod(OXDOT, ptr, meth), N);
+	call->list = callargs;
+	call->isddd = ddd;
+	if(t0->outtuple == 0) {
+		body = list(body, call);
+	} else {
+		n = nod(OAS2, N, N);
+		n->list = retargs;
+		n->rlist = list1(call);
+		body = list(body, n);
+		n = nod(ORETURN, N, N);
+		body = list(body, n);
+	}
+
+	xfunc->nbody = body;
+
+	typecheck(&xfunc, Etop);
+	sym->def = xfunc;
+	xtop = list(xtop, xfunc);
+	curfn = savecurfn;
+
+	return xfunc;
+}
+
+Node*
+walkpartialcall(Node *n, NodeList **init)
+{
+	Node *clos, *typ;
+
+	// Create closure in the form of a composite literal.
+	// For x.M with receiver (x) type T, the generated code looks like:
+	//
+	//	clos = &struct{F uintptr; R T}{M.T·f, x}
+	//
+	// Like walkclosure above.
+
+	if(isinter(n->left->type)) {
+		// Trigger panic for method on nil interface now.
+		// Otherwise it happens in the wrapper and is confusing.
+		n->left = cheapexpr(n->left, init);
+		checknil(n->left, init);
+	}
+
+	typ = nod(OTSTRUCT, N, N);
+	typ->list = list1(nod(ODCLFIELD, newname(lookup("F")), typenod(types[TUINTPTR])));
+	typ->list = list(typ->list, nod(ODCLFIELD, newname(lookup("R")), typenod(n->left->type)));
+
+	clos = nod(OCOMPLIT, N, nod(OIND, typ, N));
+	clos->esc = n->esc;
+	clos->right->implicit = 1;
+	clos->list = list1(nod(OCFUNC, n->nname->nname, N));
+	clos->list = list(clos->list, n->left);
+
+	// Force type conversion from *struct to the func type.
+	clos = nod(OCONVNOP, clos, N);
+	clos->type = n->type;
+
+	typecheck(&clos, Erv);
+	// typecheck will insert a PTRLIT node under CONVNOP,
+	// tag it with escape analysis result.
+	clos->left->esc = n->esc;
+	// non-escaping temp to use, if any.
+	// orderexpr did not compute the type; fill it in now.
+	if(n->alloc != N) {
+		n->alloc->type = clos->left->left->type;
+		n->alloc->orig->type = n->alloc->type;
+		clos->left->right = n->alloc;
+		n->alloc = N;
+	}
+	walkexpr(&clos, init);
+
+	return clos;
+}
diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c
new file mode 100644
index 0000000..e418b9c
--- /dev/null
+++ b/src/cmd/gc/const.c
@@ -0,0 +1,1674 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include	<u.h>
+#include	<libc.h>
+#include	"go.h"
+#define	TUP(x,y)	(((x)<<16)|(y))
+/*c2go int TUP(int, int); */
+
+static	Val	tocplx(Val);
+static	Val	toflt(Val);
+static	Val	tostr(Val);
+static	Val	copyval(Val);
+static	void	cmplxmpy(Mpcplx*, Mpcplx*);
+static	void	cmplxdiv(Mpcplx*, Mpcplx*);
+
+/*
+ * truncate float literal fv to 32-bit or 64-bit precision
+ * according to type; return truncated value.
+ */
+Mpflt*
+truncfltlit(Mpflt *oldv, Type *t)
+{
+	double d;
+	Mpflt *fv;
+	Val v;
+
+	if(t == T)
+		return oldv;
+
+	memset(&v, 0, sizeof v);
+	v.ctype = CTFLT;
+	v.u.fval = oldv;
+	overflow(v, t);
+
+	fv = mal(sizeof *fv);
+	*fv = *oldv;
+
+	// convert large precision literal floating
+	// into limited precision (float64 or float32)
+	switch(t->etype) {
+	case TFLOAT64:
+		d = mpgetflt(fv);
+		mpmovecflt(fv, d);
+		break;
+
+	case TFLOAT32:
+		d = mpgetflt32(fv);
+		mpmovecflt(fv, d);
+
+		break;
+	}
+	return fv;
+}
+
+/*
+ * convert n, if literal, to type t.
+ * implicit conversion.
+ */
+void
+convlit(Node **np, Type *t)
+{
+	convlit1(np, t, 0);
+}
+
+/*
+ * convert n, if literal, to type t.
+ * return a new node if necessary
+ * (if n is a named constant, can't edit n->type directly).
+ */
+void
+convlit1(Node **np, Type *t, int explicit)
+{
+	int ct, et;
+	Node *n, *nn;
+
+	n = *np;
+	if(n == N || t == T || n->type == T || isideal(t) || n->type == t)
+		return;
+	if(!explicit && !isideal(n->type))
+		return;
+
+	if(n->op == OLITERAL) {
+		nn = nod(OXXX, N, N);
+		*nn = *n;
+		n = nn;
+		*np = n;
+	}
+
+	switch(n->op) {
+	default:
+		if(n->type == idealbool) {
+			if(t->etype == TBOOL)
+				n->type = t;
+			else
+				n->type = types[TBOOL];
+		}
+		if(n->type->etype == TIDEAL) {
+			convlit(&n->left, t);
+			convlit(&n->right, t);
+			n->type = t;
+		}
+		return;
+	case OLITERAL:
+		// target is invalid type for a constant?  leave alone.
+		if(!okforconst[t->etype] && n->type->etype != TNIL) {
+			defaultlit(&n, T);
+			*np = n;
+			return;
+		}
+		break;
+	case OLSH:
+	case ORSH:
+		convlit1(&n->left, t, explicit && isideal(n->left->type));
+		t = n->left->type;
+		if(t != T && t->etype == TIDEAL && n->val.ctype != CTINT)
+			n->val = toint(n->val);
+		if(t != T && !isint[t->etype]) {
+			yyerror("invalid operation: %N (shift of type %T)", n, t);
+			t = T;
+		}
+		n->type = t;
+		return;
+	case OCOMPLEX:
+		if(n->type->etype == TIDEAL) {
+			switch(t->etype) {
+			default:
+				// If trying to convert to non-complex type,
+				// leave as complex128 and let typechecker complain.
+				t = types[TCOMPLEX128];
+				//fallthrough
+			case TCOMPLEX128:
+				n->type = t;
+				convlit(&n->left, types[TFLOAT64]);
+				convlit(&n->right, types[TFLOAT64]);
+				break;
+			case TCOMPLEX64:
+				n->type = t;
+				convlit(&n->left, types[TFLOAT32]);
+				convlit(&n->right, types[TFLOAT32]);
+				break;
+			}
+		}
+		return;
+	}
+
+	// avoided repeated calculations, errors
+	if(eqtype(n->type, t))
+		return;
+
+	ct = consttype(n);
+	if(ct < 0)
+		goto bad;
+
+	et = t->etype;
+	if(et == TINTER) {
+		if(ct == CTNIL && n->type == types[TNIL]) {
+			n->type = t;
+			return;
+		}
+		defaultlit(np, T);
+		return;
+	}
+
+	switch(ct) {
+	default:
+		goto bad;
+
+	case CTNIL:
+		switch(et) {
+		default:
+			n->type = T;
+			goto bad;
+
+		case TSTRING:
+			// let normal conversion code handle it
+			return;
+
+		case TARRAY:
+			if(!isslice(t))
+				goto bad;
+			break;
+
+		case TPTR32:
+		case TPTR64:
+		case TINTER:
+		case TMAP:
+		case TCHAN:
+		case TFUNC:
+		case TUNSAFEPTR:
+			break;
+
+		case TUINTPTR:
+			// A nil literal may be converted to uintptr
+			// if it is an unsafe.Pointer
+			if(n->type->etype == TUNSAFEPTR) {
+				n->val.u.xval = mal(sizeof(*n->val.u.xval));
+				mpmovecfix(n->val.u.xval, 0);
+				n->val.ctype = CTINT;
+			} else
+				goto bad;
+		}
+		break;
+
+	case CTSTR:
+	case CTBOOL:
+		if(et != n->type->etype)
+			goto bad;
+		break;
+
+	case CTINT:
+	case CTRUNE:
+	case CTFLT:
+	case CTCPLX:
+		ct = n->val.ctype;
+		if(isint[et]) {
+			switch(ct) {
+			default:
+				goto bad;
+			case CTCPLX:
+			case CTFLT:
+			case CTRUNE:
+				n->val = toint(n->val);
+				// flowthrough
+			case CTINT:
+				overflow(n->val, t);
+				break;
+			}
+		} else
+		if(isfloat[et]) {
+			switch(ct) {
+			default:
+				goto bad;
+			case CTCPLX:
+			case CTINT:
+			case CTRUNE:
+				n->val = toflt(n->val);
+				// flowthrough
+			case CTFLT:
+				n->val.u.fval = truncfltlit(n->val.u.fval, t);
+				break;
+			}
+		} else
+		if(iscomplex[et]) {
+			switch(ct) {
+			default:
+				goto bad;
+			case CTFLT:
+			case CTINT:
+			case CTRUNE:
+				n->val = tocplx(n->val);
+				break;
+			case CTCPLX:
+				overflow(n->val, t);
+				break;
+			}
+		} else
+		if(et == TSTRING && (ct == CTINT || ct == CTRUNE) && explicit)
+			n->val = tostr(n->val);
+		else
+			goto bad;
+		break;
+	}
+	n->type = t;
+	return;
+
+bad:
+	if(!n->diag) {
+		if(!t->broke)
+			yyerror("cannot convert %N to type %T", n, t);
+		n->diag = 1;
+	}
+	if(isideal(n->type)) {
+		defaultlit(&n, T);
+		*np = n;
+	}
+	return;
+}
+
+static Val
+copyval(Val v)
+{
+	Mpint *i;
+	Mpflt *f;
+	Mpcplx *c;
+
+	switch(v.ctype) {
+	case CTINT:
+	case CTRUNE:
+		i = mal(sizeof(*i));
+		mpmovefixfix(i, v.u.xval);
+		v.u.xval = i;
+		break;
+	case CTFLT:
+		f = mal(sizeof(*f));
+		mpmovefltflt(f, v.u.fval);
+		v.u.fval = f;
+		break;
+	case CTCPLX:
+		c = mal(sizeof(*c));
+		mpmovefltflt(&c->real, &v.u.cval->real);
+		mpmovefltflt(&c->imag, &v.u.cval->imag);
+		v.u.cval = c;
+		break;
+	}
+	return v;
+}
+
+static Val
+tocplx(Val v)
+{
+	Mpcplx *c;
+
+	switch(v.ctype) {
+	case CTINT:
+	case CTRUNE:
+		c = mal(sizeof(*c));
+		mpmovefixflt(&c->real, v.u.xval);
+		mpmovecflt(&c->imag, 0.0);
+		v.ctype = CTCPLX;
+		v.u.cval = c;
+		break;
+	case CTFLT:
+		c = mal(sizeof(*c));
+		mpmovefltflt(&c->real, v.u.fval);
+		mpmovecflt(&c->imag, 0.0);
+		v.ctype = CTCPLX;
+		v.u.cval = c;
+		break;
+	}
+	return v;
+}
+
+static Val
+toflt(Val v)
+{
+	Mpflt *f;
+
+	switch(v.ctype) {
+	case CTINT:
+	case CTRUNE:
+		f = mal(sizeof(*f));
+		mpmovefixflt(f, v.u.xval);
+		v.ctype = CTFLT;
+		v.u.fval = f;
+		break;
+	case CTCPLX:
+		f = mal(sizeof(*f));
+		mpmovefltflt(f, &v.u.cval->real);
+		if(mpcmpfltc(&v.u.cval->imag, 0) != 0)
+			yyerror("constant %#F%+#Fi truncated to real", &v.u.cval->real, &v.u.cval->imag);
+		v.ctype = CTFLT;
+		v.u.fval = f;
+		break;
+	}
+	return v;
+}
+
+Val
+toint(Val v)
+{
+	Mpint *i;
+
+	switch(v.ctype) {
+	case CTRUNE:
+		v.ctype = CTINT;
+		break;
+	case CTFLT:
+		i = mal(sizeof(*i));
+		if(mpmovefltfix(i, v.u.fval) < 0)
+			yyerror("constant %#F truncated to integer", v.u.fval);
+		v.ctype = CTINT;
+		v.u.xval = i;
+		break;
+	case CTCPLX:
+		i = mal(sizeof(*i));
+		if(mpmovefltfix(i, &v.u.cval->real) < 0)
+			yyerror("constant %#F%+#Fi truncated to integer", &v.u.cval->real, &v.u.cval->imag);
+		if(mpcmpfltc(&v.u.cval->imag, 0) != 0)
+			yyerror("constant %#F%+#Fi truncated to real", &v.u.cval->real, &v.u.cval->imag);
+		v.ctype = CTINT;
+		v.u.xval = i;
+		break;
+	}
+	return v;
+}
+
+int
+doesoverflow(Val v, Type *t)
+{
+	switch(v.ctype) {
+	case CTINT:
+	case CTRUNE:
+		if(!isint[t->etype])
+			fatal("overflow: %T integer constant", t);
+		if(mpcmpfixfix(v.u.xval, minintval[t->etype]) < 0 ||
+		   mpcmpfixfix(v.u.xval, maxintval[t->etype]) > 0)
+			return 1;
+		break;
+	case CTFLT:
+		if(!isfloat[t->etype])
+			fatal("overflow: %T floating-point constant", t);
+		if(mpcmpfltflt(v.u.fval, minfltval[t->etype]) <= 0 ||
+		   mpcmpfltflt(v.u.fval, maxfltval[t->etype]) >= 0)
+			return 1;
+		break;
+	case CTCPLX:
+		if(!iscomplex[t->etype])
+			fatal("overflow: %T complex constant", t);
+		if(mpcmpfltflt(&v.u.cval->real, minfltval[t->etype]) <= 0 ||
+		   mpcmpfltflt(&v.u.cval->real, maxfltval[t->etype]) >= 0 ||
+		   mpcmpfltflt(&v.u.cval->imag, minfltval[t->etype]) <= 0 ||
+		   mpcmpfltflt(&v.u.cval->imag, maxfltval[t->etype]) >= 0)
+			return 1;
+		break;
+	}
+	return 0;
+}
+
+void
+overflow(Val v, Type *t)
+{
+	// v has already been converted
+	// to appropriate form for t.
+	if(t == T || t->etype == TIDEAL)
+		return;
+
+	if(!doesoverflow(v, t))
+		return;
+
+	switch(v.ctype) {
+	case CTINT:
+	case CTRUNE:
+		yyerror("constant %B overflows %T", v.u.xval, t);
+		break;
+	case CTFLT:
+		yyerror("constant %#F overflows %T", v.u.fval, t);
+		break;
+	case CTCPLX:
+		yyerror("constant %#F overflows %T", v.u.fval, t);
+		break;
+	}
+}
+
+static Val
+tostr(Val v)
+{
+	Rune rune;
+	int l;
+	Strlit *s;
+
+	switch(v.ctype) {
+	case CTINT:
+	case CTRUNE:
+		if(mpcmpfixfix(v.u.xval, minintval[TINT]) < 0 ||
+		   mpcmpfixfix(v.u.xval, maxintval[TINT]) > 0)
+			yyerror("overflow in int -> string");
+		rune = mpgetfix(v.u.xval);
+		l = runelen(rune);
+		s = mal(sizeof(*s)+l);
+		s->len = l;
+		runetochar((char*)s->s, &rune);
+		memset(&v, 0, sizeof v);
+		v.ctype = CTSTR;
+		v.u.sval = s;
+		break;
+
+	case CTFLT:
+		yyerror("no float -> string");
+
+	case CTNIL:
+		memset(&v, 0, sizeof v);
+		v.ctype = CTSTR;
+		v.u.sval = mal(sizeof *s);
+		break;
+	}
+	return v;
+}
+
+int
+consttype(Node *n)
+{
+	if(n == N || n->op != OLITERAL)
+		return -1;
+	return n->val.ctype;
+}
+
+int
+isconst(Node *n, int ct)
+{
+	int t;
+	
+	t = consttype(n);
+	// If the caller is asking for CTINT, allow CTRUNE too.
+	// Makes life easier for back ends.
+	return t == ct || (ct == CTINT && t == CTRUNE);
+}
+
+static Node*
+saveorig(Node *n)
+{
+	Node *n1;
+
+	if(n == n->orig) {
+		// duplicate node for n->orig.
+		n1 = nod(OLITERAL, N, N);
+		n->orig = n1;
+		*n1 = *n;
+	}
+	return n->orig;
+}
+
+/*
+ * if n is constant, rewrite as OLITERAL node.
+ */
+void
+evconst(Node *n)
+{
+	Node *nl, *nr, *norig;
+	int32 len;
+	Strlit *str;
+	int wl, wr, lno, et;
+	Val v, rv;
+	Mpint b;
+	NodeList *l1, *l2;
+
+	// pick off just the opcodes that can be
+	// constant evaluated.
+	switch(n->op) {
+	default:
+		return;
+	case OADD:
+	case OAND:
+	case OANDAND:
+	case OANDNOT:
+	case OARRAYBYTESTR:
+	case OCOM:
+	case ODIV:
+	case OEQ:
+	case OGE:
+	case OGT:
+	case OLE:
+	case OLSH:
+	case OLT:
+	case OMINUS:
+	case OMOD:
+	case OMUL:
+	case ONE:
+	case ONOT:
+	case OOR:
+	case OOROR:
+	case OPLUS:
+	case ORSH:
+	case OSUB:
+	case OXOR:
+		break;
+	case OCONV:
+		if(n->type == T)
+			return;
+		if(!okforconst[n->type->etype] && n->type->etype != TNIL)
+			return;
+		break;
+	
+	case OADDSTR:
+		// merge adjacent constants in the argument list.
+		for(l1=n->list; l1 != nil; l1= l1->next) {
+			if(isconst(l1->n, CTSTR) && l1->next != nil && isconst(l1->next->n, CTSTR)) {
+				l2 = l1;
+				len = 0;
+				while(l2 != nil && isconst(l2->n, CTSTR)) {
+					nr = l2->n;
+					len += nr->val.u.sval->len;
+					l2 = l2->next;
+				}
+				// merge from l1 up to but not including l2
+				str = mal(sizeof(*str) + len);
+				str->len = len;
+				len = 0;
+				l2 = l1;
+				while(l2 != nil && isconst(l2->n, CTSTR)) {
+					nr = l2->n;
+					memmove(str->s+len, nr->val.u.sval->s, nr->val.u.sval->len);
+					len += nr->val.u.sval->len;
+					l2 = l2->next;
+				}
+				nl = nod(OXXX, N, N);
+				*nl = *l1->n;
+				nl->orig = nl;
+				nl->val.ctype = CTSTR;
+				nl->val.u.sval = str;
+				l1->n = nl;
+				l1->next = l2;
+			}
+		}
+		// fix list end pointer.
+		for(l2=n->list; l2 != nil; l2=l2->next)
+			n->list->end = l2;
+		// collapse single-constant list to single constant.
+		if(count(n->list) == 1 && isconst(n->list->n, CTSTR)) {
+			n->op = OLITERAL;
+			n->val = n->list->n->val;
+		}
+		return;
+	}
+
+	nl = n->left;
+	if(nl == N || nl->type == T)
+		return;
+	if(consttype(nl) < 0)
+		return;
+	wl = nl->type->etype;
+	if(isint[wl] || isfloat[wl] || iscomplex[wl])
+		wl = TIDEAL;
+
+	nr = n->right;
+	if(nr == N)
+		goto unary;
+	if(nr->type == T)
+		return;
+	if(consttype(nr) < 0)
+		return;
+	wr = nr->type->etype;
+	if(isint[wr] || isfloat[wr] || iscomplex[wr])
+		wr = TIDEAL;
+
+	// check for compatible general types (numeric, string, etc)
+	if(wl != wr)
+		goto illegal;
+
+	// check for compatible types.
+	switch(n->op) {
+	default:
+		// ideal const mixes with anything but otherwise must match.
+		if(nl->type->etype != TIDEAL) {
+			defaultlit(&nr, nl->type);
+			n->right = nr;
+		}
+		if(nr->type->etype != TIDEAL) {
+			defaultlit(&nl, nr->type);
+			n->left = nl;
+		}
+		if(nl->type->etype != nr->type->etype)
+			goto illegal;
+		break;
+
+	case OLSH:
+	case ORSH:
+		// right must be unsigned.
+		// left can be ideal.
+		defaultlit(&nr, types[TUINT]);
+		n->right = nr;
+		if(nr->type && (issigned[nr->type->etype] || !isint[nr->type->etype]))
+			goto illegal;
+		if(nl->val.ctype != CTRUNE)
+			nl->val = toint(nl->val);
+		nr->val = toint(nr->val);
+		break;
+	}
+
+	// copy numeric value to avoid modifying
+	// n->left, in case someone still refers to it (e.g. iota).
+	v = nl->val;
+	if(wl == TIDEAL)
+		v = copyval(v);
+
+	rv = nr->val;
+
+	// convert to common ideal
+	if(v.ctype == CTCPLX || rv.ctype == CTCPLX) {
+		v = tocplx(v);
+		rv = tocplx(rv);
+	}
+	if(v.ctype == CTFLT || rv.ctype == CTFLT) {
+		v = toflt(v);
+		rv = toflt(rv);
+	}
+
+	// Rune and int turns into rune.
+	if(v.ctype == CTRUNE && rv.ctype == CTINT)
+		rv.ctype = CTRUNE;
+	if(v.ctype == CTINT && rv.ctype == CTRUNE) {
+		if(n->op == OLSH || n->op == ORSH)
+			rv.ctype = CTINT;
+		else
+			v.ctype = CTRUNE;
+	}
+
+	if(v.ctype != rv.ctype) {
+		// Use of undefined name as constant?
+		if((v.ctype == 0 || rv.ctype == 0) && nerrors > 0)
+			return;
+		fatal("constant type mismatch %T(%d) %T(%d)", nl->type, v.ctype, nr->type, rv.ctype);
+	}
+
+	// run op
+	switch(TUP(n->op, v.ctype)) {
+	default:
+	illegal:
+		if(!n->diag) {
+			yyerror("illegal constant expression: %T %O %T",
+				nl->type, n->op, nr->type);
+			n->diag = 1;
+		}
+		return;
+
+	case TUP(OADD, CTINT):
+	case TUP(OADD, CTRUNE):
+		mpaddfixfix(v.u.xval, rv.u.xval, 0);
+		break;
+	case TUP(OSUB, CTINT):
+	case TUP(OSUB, CTRUNE):
+		mpsubfixfix(v.u.xval, rv.u.xval);
+		break;
+	case TUP(OMUL, CTINT):
+	case TUP(OMUL, CTRUNE):
+		mpmulfixfix(v.u.xval, rv.u.xval);
+		break;
+	case TUP(ODIV, CTINT):
+	case TUP(ODIV, CTRUNE):
+		if(mpcmpfixc(rv.u.xval, 0) == 0) {
+			yyerror("division by zero");
+			mpmovecfix(v.u.xval, 1);
+			break;
+		}
+		mpdivfixfix(v.u.xval, rv.u.xval);
+		break;
+	case TUP(OMOD, CTINT):
+	case TUP(OMOD, CTRUNE):
+		if(mpcmpfixc(rv.u.xval, 0) == 0) {
+			yyerror("division by zero");
+			mpmovecfix(v.u.xval, 1);
+			break;
+		}
+		mpmodfixfix(v.u.xval, rv.u.xval);
+		break;
+
+	case TUP(OLSH, CTINT):
+	case TUP(OLSH, CTRUNE):
+		mplshfixfix(v.u.xval, rv.u.xval);
+		break;
+	case TUP(ORSH, CTINT):
+	case TUP(ORSH, CTRUNE):
+		mprshfixfix(v.u.xval, rv.u.xval);
+		break;
+	case TUP(OOR, CTINT):
+	case TUP(OOR, CTRUNE):
+		mporfixfix(v.u.xval, rv.u.xval);
+		break;
+	case TUP(OAND, CTINT):
+	case TUP(OAND, CTRUNE):
+		mpandfixfix(v.u.xval, rv.u.xval);
+		break;
+	case TUP(OANDNOT, CTINT):
+	case TUP(OANDNOT, CTRUNE):
+		mpandnotfixfix(v.u.xval, rv.u.xval);
+		break;
+	case TUP(OXOR, CTINT):
+	case TUP(OXOR, CTRUNE):
+		mpxorfixfix(v.u.xval, rv.u.xval);
+		break;
+
+	case TUP(OADD, CTFLT):
+		mpaddfltflt(v.u.fval, rv.u.fval);
+		break;
+	case TUP(OSUB, CTFLT):
+		mpsubfltflt(v.u.fval, rv.u.fval);
+		break;
+	case TUP(OMUL, CTFLT):
+		mpmulfltflt(v.u.fval, rv.u.fval);
+		break;
+	case TUP(ODIV, CTFLT):
+		if(mpcmpfltc(rv.u.fval, 0) == 0) {
+			yyerror("division by zero");
+			mpmovecflt(v.u.fval, 1.0);
+			break;
+		}
+		mpdivfltflt(v.u.fval, rv.u.fval);
+		break;
+	case TUP(OMOD, CTFLT):
+		// The default case above would print 'ideal % ideal',
+		// which is not quite an ideal error.
+		if(!n->diag) {
+			yyerror("illegal constant expression: floating-point %% operation");
+			n->diag = 1;
+		}
+		return;
+
+	case TUP(OADD, CTCPLX):
+		mpaddfltflt(&v.u.cval->real, &rv.u.cval->real);
+		mpaddfltflt(&v.u.cval->imag, &rv.u.cval->imag);
+		break;
+	case TUP(OSUB, CTCPLX):
+		mpsubfltflt(&v.u.cval->real, &rv.u.cval->real);
+		mpsubfltflt(&v.u.cval->imag, &rv.u.cval->imag);
+		break;
+	case TUP(OMUL, CTCPLX):
+		cmplxmpy(v.u.cval, rv.u.cval);
+		break;
+	case TUP(ODIV, CTCPLX):
+		if(mpcmpfltc(&rv.u.cval->real, 0) == 0 &&
+		   mpcmpfltc(&rv.u.cval->imag, 0) == 0) {
+			yyerror("complex division by zero");
+			mpmovecflt(&rv.u.cval->real, 1.0);
+			mpmovecflt(&rv.u.cval->imag, 0.0);
+			break;
+		}
+		cmplxdiv(v.u.cval, rv.u.cval);
+		break;
+
+	case TUP(OEQ, CTNIL):
+		goto settrue;
+	case TUP(ONE, CTNIL):
+		goto setfalse;
+
+	case TUP(OEQ, CTINT):
+	case TUP(OEQ, CTRUNE):
+		if(mpcmpfixfix(v.u.xval, rv.u.xval) == 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(ONE, CTINT):
+	case TUP(ONE, CTRUNE):
+		if(mpcmpfixfix(v.u.xval, rv.u.xval) != 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(OLT, CTINT):
+	case TUP(OLT, CTRUNE):
+		if(mpcmpfixfix(v.u.xval, rv.u.xval) < 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(OLE, CTINT):
+	case TUP(OLE, CTRUNE):
+		if(mpcmpfixfix(v.u.xval, rv.u.xval) <= 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(OGE, CTINT):
+	case TUP(OGE, CTRUNE):
+		if(mpcmpfixfix(v.u.xval, rv.u.xval) >= 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(OGT, CTINT):
+	case TUP(OGT, CTRUNE):
+		if(mpcmpfixfix(v.u.xval, rv.u.xval) > 0)
+			goto settrue;
+		goto setfalse;
+
+	case TUP(OEQ, CTFLT):
+		if(mpcmpfltflt(v.u.fval, rv.u.fval) == 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(ONE, CTFLT):
+		if(mpcmpfltflt(v.u.fval, rv.u.fval) != 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(OLT, CTFLT):
+		if(mpcmpfltflt(v.u.fval, rv.u.fval) < 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(OLE, CTFLT):
+		if(mpcmpfltflt(v.u.fval, rv.u.fval) <= 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(OGE, CTFLT):
+		if(mpcmpfltflt(v.u.fval, rv.u.fval) >= 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(OGT, CTFLT):
+		if(mpcmpfltflt(v.u.fval, rv.u.fval) > 0)
+			goto settrue;
+		goto setfalse;
+
+	case TUP(OEQ, CTCPLX):
+		if(mpcmpfltflt(&v.u.cval->real, &rv.u.cval->real) == 0 &&
+		   mpcmpfltflt(&v.u.cval->imag, &rv.u.cval->imag) == 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(ONE, CTCPLX):
+		if(mpcmpfltflt(&v.u.cval->real, &rv.u.cval->real) != 0 ||
+		   mpcmpfltflt(&v.u.cval->imag, &rv.u.cval->imag) != 0)
+			goto settrue;
+		goto setfalse;
+
+	case TUP(OEQ, CTSTR):
+		if(cmpslit(nl, nr) == 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(ONE, CTSTR):
+		if(cmpslit(nl, nr) != 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(OLT, CTSTR):
+		if(cmpslit(nl, nr) < 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(OLE, CTSTR):
+		if(cmpslit(nl, nr) <= 0)
+			goto settrue;
+		goto setfalse;
+	case TUP(OGE, CTSTR):
+		if(cmpslit(nl, nr) >= 0l)
+			goto settrue;
+		goto setfalse;
+	case TUP(OGT, CTSTR):
+		if(cmpslit(nl, nr) > 0)
+			goto settrue;
+		goto setfalse;
+
+	case TUP(OOROR, CTBOOL):
+		if(v.u.bval || rv.u.bval)
+			goto settrue;
+		goto setfalse;
+	case TUP(OANDAND, CTBOOL):
+		if(v.u.bval && rv.u.bval)
+			goto settrue;
+		goto setfalse;
+	case TUP(OEQ, CTBOOL):
+		if(v.u.bval == rv.u.bval)
+			goto settrue;
+		goto setfalse;
+	case TUP(ONE, CTBOOL):
+		if(v.u.bval != rv.u.bval)
+			goto settrue;
+		goto setfalse;
+	}
+	goto ret;
+
+unary:
+	// copy numeric value to avoid modifying
+	// nl, in case someone still refers to it (e.g. iota).
+	v = nl->val;
+	if(wl == TIDEAL)
+		v = copyval(v);
+
+	switch(TUP(n->op, v.ctype)) {
+	default:
+		if(!n->diag) {
+			yyerror("illegal constant expression %O %T", n->op, nl->type);
+			n->diag = 1;
+		}
+		return;
+
+	case TUP(OCONV, CTNIL):
+	case TUP(OARRAYBYTESTR, CTNIL):
+		if(n->type->etype == TSTRING) {
+			v = tostr(v);
+			nl->type = n->type;
+			break;
+		}
+		// fall through
+	case TUP(OCONV, CTINT):
+	case TUP(OCONV, CTRUNE):
+	case TUP(OCONV, CTFLT):
+	case TUP(OCONV, CTSTR):
+		convlit1(&nl, n->type, 1);
+		v = nl->val;
+		break;
+
+	case TUP(OPLUS, CTINT):
+	case TUP(OPLUS, CTRUNE):
+		break;
+	case TUP(OMINUS, CTINT):
+	case TUP(OMINUS, CTRUNE):
+		mpnegfix(v.u.xval);
+		break;
+	case TUP(OCOM, CTINT):
+	case TUP(OCOM, CTRUNE):
+		et = Txxx;
+		if(nl->type != T)
+			et = nl->type->etype;
+
+		// calculate the mask in b
+		// result will be (a ^ mask)
+		switch(et) {
+		default:
+			// signed guys change sign
+			mpmovecfix(&b, -1);
+			break;
+
+		case TUINT8:
+		case TUINT16:
+		case TUINT32:
+		case TUINT64:
+		case TUINT:
+		case TUINTPTR:
+			// unsigned guys invert their bits
+			mpmovefixfix(&b, maxintval[et]);
+			break;
+		}
+		mpxorfixfix(v.u.xval, &b);
+		break;
+
+	case TUP(OPLUS, CTFLT):
+		break;
+	case TUP(OMINUS, CTFLT):
+		mpnegflt(v.u.fval);
+		break;
+
+	case TUP(OPLUS, CTCPLX):
+		break;
+	case TUP(OMINUS, CTCPLX):
+		mpnegflt(&v.u.cval->real);
+		mpnegflt(&v.u.cval->imag);
+		break;
+
+	case TUP(ONOT, CTBOOL):
+		if(!v.u.bval)
+			goto settrue;
+		goto setfalse;
+	}
+
+ret:
+	norig = saveorig(n);
+	*n = *nl;
+	// restore value of n->orig.
+	n->orig = norig;
+	n->val = v;
+
+	// check range.
+	lno = setlineno(n);
+	overflow(v, n->type);
+	lineno = lno;
+
+	// truncate precision for non-ideal float.
+	if(v.ctype == CTFLT && n->type->etype != TIDEAL)
+		n->val.u.fval = truncfltlit(v.u.fval, n->type);
+	return;
+
+settrue:
+	norig = saveorig(n);
+	*n = *nodbool(1);
+	n->orig = norig;
+	return;
+
+setfalse:
+	norig = saveorig(n);
+	*n = *nodbool(0);
+	n->orig = norig;
+	return;
+}
+
+Node*
+nodlit(Val v)
+{
+	Node *n;
+
+	n = nod(OLITERAL, N, N);
+	n->val = v;
+	switch(v.ctype) {
+	default:
+		fatal("nodlit ctype %d", v.ctype);
+	case CTSTR:
+		n->type = idealstring;
+		break;
+	case CTBOOL:
+		n->type = idealbool;
+		break;
+	case CTINT:
+	case CTRUNE:
+	case CTFLT:
+	case CTCPLX:
+		n->type = types[TIDEAL];
+		break;
+	case CTNIL:
+		n->type = types[TNIL];
+		break;
+	}
+	return n;
+}
+
+Node*
+nodcplxlit(Val r, Val i)
+{
+	Node *n;
+	Mpcplx *c;
+
+	r = toflt(r);
+	i = toflt(i);
+
+	c = mal(sizeof(*c));
+	n = nod(OLITERAL, N, N);
+	n->type = types[TIDEAL];
+	n->val.u.cval = c;
+	n->val.ctype = CTCPLX;
+
+	if(r.ctype != CTFLT || i.ctype != CTFLT)
+		fatal("nodcplxlit ctype %d/%d", r.ctype, i.ctype);
+
+	mpmovefltflt(&c->real, r.u.fval);
+	mpmovefltflt(&c->imag, i.u.fval);
+	return n;
+}
+
+// idealkind returns a constant kind like consttype
+// but for an arbitrary "ideal" (untyped constant) expression.
+static int
+idealkind(Node *n)
+{
+	int k1, k2;
+
+	if(n == N || !isideal(n->type))
+		return CTxxx;
+
+	switch(n->op) {
+	default:
+		return CTxxx;
+	case OLITERAL:
+		return n->val.ctype;
+	case OADD:
+	case OAND:
+	case OANDNOT:
+	case OCOM:
+	case ODIV:
+	case OMINUS:
+	case OMOD:
+	case OMUL:
+	case OSUB:
+	case OXOR:
+	case OOR:
+	case OPLUS:
+		// numeric kinds.
+		k1 = idealkind(n->left);
+		k2 = idealkind(n->right);
+		if(k1 > k2)
+			return k1;
+		else
+			return k2;
+	case OREAL:
+	case OIMAG:
+		return CTFLT;
+	case OCOMPLEX:
+		return CTCPLX;
+	case OADDSTR:
+		return CTSTR;
+	case OANDAND:
+	case OEQ:
+	case OGE:
+	case OGT:
+	case OLE:
+	case OLT:
+	case ONE:
+	case ONOT:
+	case OOROR:
+	case OCMPSTR:
+	case OCMPIFACE:
+		return CTBOOL;
+	case OLSH:
+	case ORSH:
+		// shifts (beware!).
+		return idealkind(n->left);
+	}
+}
+
+void
+defaultlit(Node **np, Type *t)
+{
+	int lno;
+	int ctype;
+	Node *n, *nn;
+	Type *t1;
+
+	n = *np;
+	if(n == N || !isideal(n->type))
+		return;
+
+	if(n->op == OLITERAL) {
+		nn = nod(OXXX, N, N);
+		*nn = *n;
+		n = nn;
+		*np = n;
+	}
+
+	lno = setlineno(n);
+	ctype = idealkind(n);
+	switch(ctype) {
+	default:
+		if(t != T) {
+			convlit(np, t);
+			return;
+		}
+		if(n->val.ctype == CTNIL) {
+			lineno = lno;
+			if(!n->diag) {
+				yyerror("use of untyped nil");
+				n->diag = 1;
+			}
+			n->type = T;
+			break;
+		}
+		if(n->val.ctype == CTSTR) {
+			t1 = types[TSTRING];
+			convlit(np, t1);
+			break;
+		}
+		yyerror("defaultlit: unknown literal: %N", n);
+		break;
+	case CTxxx:
+		fatal("defaultlit: idealkind is CTxxx: %+N", n);
+		break;
+	case CTBOOL:
+		t1 = types[TBOOL];
+		if(t != T && t->etype == TBOOL)
+			t1 = t;
+		convlit(np, t1);
+		break;
+	case CTINT:
+		t1 = types[TINT];
+		goto num;
+	case CTRUNE:
+		t1 = runetype;
+		goto num;
+	case CTFLT:
+		t1 = types[TFLOAT64];
+		goto num;
+	case CTCPLX:
+		t1 = types[TCOMPLEX128];
+		goto num;
+	num:
+		if(t != T) {
+			if(isint[t->etype]) {
+				t1 = t;
+				n->val = toint(n->val);
+			}
+			else
+			if(isfloat[t->etype]) {
+				t1 = t;
+				n->val = toflt(n->val);
+			}
+			else
+			if(iscomplex[t->etype]) {
+				t1 = t;
+				n->val = tocplx(n->val);
+			}
+		}
+		overflow(n->val, t1);
+		convlit(np, t1);
+		break;
+	}
+	lineno = lno;
+}
+
+/*
+ * defaultlit on both nodes simultaneously;
+ * if they're both ideal going in they better
+ * get the same type going out.
+ * force means must assign concrete (non-ideal) type.
+ */
+void
+defaultlit2(Node **lp, Node **rp, int force)
+{
+	Node *l, *r;
+	int lkind, rkind;
+
+	l = *lp;
+	r = *rp;
+	if(l->type == T || r->type == T)
+		return;
+	if(!isideal(l->type)) {
+		convlit(rp, l->type);
+		return;
+	}
+	if(!isideal(r->type)) {
+		convlit(lp, r->type);
+		return;
+	}
+	if(!force)
+		return;
+	if(l->type->etype == TBOOL) {
+		convlit(lp, types[TBOOL]);
+		convlit(rp, types[TBOOL]);
+	}
+	lkind = idealkind(l);
+	rkind = idealkind(r);
+	if(lkind == CTCPLX || rkind == CTCPLX) {
+		convlit(lp, types[TCOMPLEX128]);
+		convlit(rp, types[TCOMPLEX128]);
+		return;
+	}
+	if(lkind == CTFLT || rkind == CTFLT) {
+		convlit(lp, types[TFLOAT64]);
+		convlit(rp, types[TFLOAT64]);
+		return;
+	}
+
+	if(lkind == CTRUNE || rkind == CTRUNE) {
+		convlit(lp, runetype);
+		convlit(rp, runetype);
+		return;
+	}
+
+	convlit(lp, types[TINT]);
+	convlit(rp, types[TINT]);
+}
+
+int
+cmpslit(Node *l, Node *r)
+{
+	int32 l1, l2, i, m;
+	uchar *s1, *s2;
+
+	l1 = l->val.u.sval->len;
+	l2 = r->val.u.sval->len;
+	s1 = (uchar*)l->val.u.sval->s;
+	s2 = (uchar*)r->val.u.sval->s;
+
+	m = l1;
+	if(l2 < m)
+		m = l2;
+
+	for(i=0; i<m; i++) {
+		if(s1[i] == s2[i])
+			continue;
+		if(s1[i] > s2[i])
+			return +1;
+		return -1;
+	}
+	if(l1 == l2)
+		return 0;
+	if(l1 > l2)
+		return +1;
+	return -1;
+}
+
+int
+smallintconst(Node *n)
+{
+	if(n->op == OLITERAL && isconst(n, CTINT) && n->type != T)
+	switch(simtype[n->type->etype]) {
+	case TINT8:
+	case TUINT8:
+	case TINT16:
+	case TUINT16:
+	case TINT32:
+	case TUINT32:
+	case TBOOL:
+	case TPTR32:
+		return 1;
+	case TIDEAL:
+	case TINT64:
+	case TUINT64:
+	case TPTR64:
+		if(mpcmpfixfix(n->val.u.xval, minintval[TINT32]) < 0
+		|| mpcmpfixfix(n->val.u.xval, maxintval[TINT32]) > 0)
+			break;
+		return 1;
+	}
+	return 0;
+}
+
+long
+nonnegconst(Node *n)
+{
+	if(n->op == OLITERAL && n->type != T)
+	switch(simtype[n->type->etype]) {
+	case TINT8:
+	case TUINT8:
+	case TINT16:
+	case TUINT16:
+	case TINT32:
+	case TUINT32:
+	case TINT64:
+	case TUINT64:
+	case TIDEAL:
+		// check negative and 2^31
+		if(mpcmpfixfix(n->val.u.xval, minintval[TUINT32]) < 0
+		|| mpcmpfixfix(n->val.u.xval, maxintval[TINT32]) > 0)
+			break;
+		return mpgetfix(n->val.u.xval);
+	}
+	return -1;
+}
+
+/*
+ * convert x to type et and back to int64
+ * for sign extension and truncation.
+ */
+static int64
+iconv(int64 x, int et)
+{
+	switch(et) {
+	case TINT8:
+		x = (int8)x;
+		break;
+	case TUINT8:
+		x = (uint8)x;
+		break;
+	case TINT16:
+		x = (int16)x;
+		break;
+	case TUINT16:
+		x = (uint64)x;
+		break;
+	case TINT32:
+		x = (int32)x;
+		break;
+	case TUINT32:
+		x = (uint32)x;
+		break;
+	case TINT64:
+	case TUINT64:
+		break;
+	}
+	return x;
+}
+
+/*
+ * convert constant val to type t; leave in con.
+ * for back end.
+ */
+void
+convconst(Node *con, Type *t, Val *val)
+{
+	int64 i;
+	int tt;
+
+	tt = simsimtype(t);
+
+	// copy the constant for conversion
+	nodconst(con, types[TINT8], 0);
+	con->type = t;
+	con->val = *val;
+
+	if(isint[tt]) {
+		con->val.ctype = CTINT;
+		con->val.u.xval = mal(sizeof *con->val.u.xval);
+		switch(val->ctype) {
+		default:
+			fatal("convconst ctype=%d %lT", val->ctype, t);
+		case CTINT:
+		case CTRUNE:
+			i = mpgetfix(val->u.xval);
+			break;
+		case CTBOOL:
+			i = val->u.bval;
+			break;
+		case CTNIL:
+			i = 0;
+			break;
+		}
+		i = iconv(i, tt);
+		mpmovecfix(con->val.u.xval, i);
+		return;
+	}
+
+	if(isfloat[tt]) {
+		con->val = toflt(con->val);
+		if(con->val.ctype != CTFLT)
+			fatal("convconst ctype=%d %T", con->val.ctype, t);
+		if(tt == TFLOAT32)
+			con->val.u.fval = truncfltlit(con->val.u.fval, t);
+		return;
+	}
+
+	if(iscomplex[tt]) {
+		con->val = tocplx(con->val);
+		if(tt == TCOMPLEX64) {
+			con->val.u.cval->real = *truncfltlit(&con->val.u.cval->real, types[TFLOAT32]);
+			con->val.u.cval->imag = *truncfltlit(&con->val.u.cval->imag, types[TFLOAT32]);
+		}
+		return;
+	}
+
+	fatal("convconst %lT constant", t);
+
+}
+
+// complex multiply v *= rv
+//	(a, b) * (c, d) = (a*c - b*d, b*c + a*d)
+static void
+cmplxmpy(Mpcplx *v, Mpcplx *rv)
+{
+	Mpflt ac, bd, bc, ad;
+
+	mpmovefltflt(&ac, &v->real);
+	mpmulfltflt(&ac, &rv->real);	// ac
+
+	mpmovefltflt(&bd, &v->imag);
+	mpmulfltflt(&bd, &rv->imag);	// bd
+
+	mpmovefltflt(&bc, &v->imag);
+	mpmulfltflt(&bc, &rv->real);	// bc
+
+	mpmovefltflt(&ad, &v->real);
+	mpmulfltflt(&ad, &rv->imag);	// ad
+
+	mpmovefltflt(&v->real, &ac);
+	mpsubfltflt(&v->real, &bd);	// ac-bd
+
+	mpmovefltflt(&v->imag, &bc);
+	mpaddfltflt(&v->imag, &ad);	// bc+ad
+}
+
+// complex divide v /= rv
+//	(a, b) / (c, d) = ((a*c + b*d), (b*c - a*d))/(c*c + d*d)
+static void
+cmplxdiv(Mpcplx *v, Mpcplx *rv)
+{
+	Mpflt ac, bd, bc, ad, cc_plus_dd;
+
+	mpmovefltflt(&cc_plus_dd, &rv->real);
+	mpmulfltflt(&cc_plus_dd, &rv->real);	// cc
+
+	mpmovefltflt(&ac, &rv->imag);
+	mpmulfltflt(&ac, &rv->imag);		// dd
+
+	mpaddfltflt(&cc_plus_dd, &ac);		// cc+dd
+
+	mpmovefltflt(&ac, &v->real);
+	mpmulfltflt(&ac, &rv->real);		// ac
+
+	mpmovefltflt(&bd, &v->imag);
+	mpmulfltflt(&bd, &rv->imag);		// bd
+
+	mpmovefltflt(&bc, &v->imag);
+	mpmulfltflt(&bc, &rv->real);		// bc
+
+	mpmovefltflt(&ad, &v->real);
+	mpmulfltflt(&ad, &rv->imag);		// ad
+
+	mpmovefltflt(&v->real, &ac);
+	mpaddfltflt(&v->real, &bd);		// ac+bd
+	mpdivfltflt(&v->real, &cc_plus_dd);	// (ac+bd)/(cc+dd)
+
+	mpmovefltflt(&v->imag, &bc);
+	mpsubfltflt(&v->imag, &ad);		// bc-ad
+	mpdivfltflt(&v->imag, &cc_plus_dd);	// (bc+ad)/(cc+dd)
+}
+
+static int hascallchan(Node*);
+
+// Is n a Go language constant (as opposed to a compile-time constant)?
+// Expressions derived from nil, like string([]byte(nil)), while they
+// may be known at compile time, are not Go language constants.
+// Only called for expressions known to evaluated to compile-time
+// constants.
+int
+isgoconst(Node *n)
+{
+	Node *l;
+	Type *t;
+
+	if(n->orig != N)
+		n = n->orig;
+
+	switch(n->op) {
+	case OADD:
+	case OADDSTR:
+	case OAND:
+	case OANDAND:
+	case OANDNOT:
+	case OCOM:
+	case ODIV:
+	case OEQ:
+	case OGE:
+	case OGT:
+	case OLE:
+	case OLSH:
+	case OLT:
+	case OMINUS:
+	case OMOD:
+	case OMUL:
+	case ONE:
+	case ONOT:
+	case OOR:
+	case OOROR:
+	case OPLUS:
+	case ORSH:
+	case OSUB:
+	case OXOR:
+	case OIOTA:
+	case OCOMPLEX:
+	case OREAL:
+	case OIMAG:
+		if(isgoconst(n->left) && (n->right == N || isgoconst(n->right)))
+			return 1;
+		break;
+
+	case OCONV:
+		if(okforconst[n->type->etype] && isgoconst(n->left))
+			return 1;
+		break;
+
+	case OLEN:
+	case OCAP:
+		l = n->left;
+		if(isgoconst(l))
+			return 1;
+		// Special case: len/cap is constant when applied to array or
+		// pointer to array when the expression does not contain
+		// function calls or channel receive operations.
+		t = l->type;
+		if(t != T && isptr[t->etype])
+			t = t->type;
+		if(isfixedarray(t) && !hascallchan(l))
+			return 1;
+		break;
+
+	case OLITERAL:
+		if(n->val.ctype != CTNIL)
+			return 1;
+		break;
+
+	case ONAME:
+		l = n->sym->def;
+		if(l && l->op == OLITERAL && n->val.ctype != CTNIL)
+			return 1;
+		break;
+	
+	case ONONAME:
+		if(n->sym->def != N && n->sym->def->op == OIOTA)
+			return 1;
+		break;
+	
+	case OCALL:
+		// Only constant calls are unsafe.Alignof, Offsetof, and Sizeof.
+		l = n->left;
+		while(l->op == OPAREN)
+			l = l->left;
+		if(l->op != ONAME || l->sym->pkg != unsafepkg)
+			break;
+		if(strcmp(l->sym->name, "Alignof") == 0 ||
+		   strcmp(l->sym->name, "Offsetof") == 0 ||
+		   strcmp(l->sym->name, "Sizeof") == 0)
+			return 1;
+		break;		
+	}
+
+	//dump("nonconst", n);
+	return 0;
+}
+
+static int
+hascallchan(Node *n)
+{
+	NodeList *l;
+
+	if(n == N)
+		return 0;
+	switch(n->op) {
+	case OAPPEND:
+	case OCALL:
+	case OCALLFUNC:
+	case OCALLINTER:
+	case OCALLMETH:
+	case OCAP:
+	case OCLOSE:
+	case OCOMPLEX:
+	case OCOPY:
+	case ODELETE:
+	case OIMAG:
+	case OLEN:
+	case OMAKE:
+	case ONEW:
+	case OPANIC:
+	case OPRINT:
+	case OPRINTN:
+	case OREAL:
+	case ORECOVER:
+	case ORECV:
+		return 1;
+	}
+	
+	if(hascallchan(n->left) ||
+	   hascallchan(n->right))
+		return 1;
+	
+	for(l=n->list; l; l=l->next)
+		if(hascallchan(l->n))
+			return 1;
+	for(l=n->rlist; l; l=l->next)
+		if(hascallchan(l->n))
+			return 1;
+
+	return 0;
+}
diff --git a/src/cmd/gc/cplx.c b/src/cmd/gc/cplx.c
new file mode 100644
index 0000000..c9bab7a
--- /dev/null
+++ b/src/cmd/gc/cplx.c
@@ -0,0 +1,486 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <u.h>
+#include <libc.h>
+#include "gg.h"
+
+static	void	subnode(Node *nr, Node *ni, Node *nc);
+static	void	minus(Node *nl, Node *res);
+	void	complexminus(Node*, Node*);
+	void	complexadd(int op, Node*, Node*, Node*);
+	void	complexmul(Node*, Node*, Node*);
+
+#define	CASE(a,b)	(((a)<<16)|((b)<<0))
+
+static int
+overlap(Node *f, Node *t)
+{
+	// check whether f and t could be overlapping stack references.
+	// not exact, because it's hard to check for the stack register
+	// in portable code.  close enough: worst case we will allocate
+	// an extra temporary and the registerizer will clean it up.
+	return f->op == OINDREG &&
+		t->op == OINDREG &&
+		f->xoffset+f->type->width >= t->xoffset &&
+		t->xoffset+t->type->width >= f->xoffset;
+}
+
+/*
+ * generate:
+ *	res = n;
+ * simplifies and calls gmove.
+ */
+void
+complexmove(Node *f, Node *t)
+{
+	int ft, tt;
+	Node n1, n2, n3, n4, tmp;
+
+	if(debug['g']) {
+		dump("\ncomplexmove-f", f);
+		dump("complexmove-t", t);
+	}
+
+	if(!t->addable)
+		fatal("complexmove: to not addable");
+
+	ft = simsimtype(f->type);
+	tt = simsimtype(t->type);
+	switch(CASE(ft,tt)) {
+
+	default:
+		fatal("complexmove: unknown conversion: %T -> %T\n",
+			f->type, t->type);
+
+	case CASE(TCOMPLEX64,TCOMPLEX64):
+	case CASE(TCOMPLEX64,TCOMPLEX128):
+	case CASE(TCOMPLEX128,TCOMPLEX64):
+	case CASE(TCOMPLEX128,TCOMPLEX128):
+		// complex to complex move/convert.
+		// make f addable.
+		// also use temporary if possible stack overlap.
+		if(!f->addable || overlap(f, t)) {
+			tempname(&tmp, f->type);
+			complexmove(f, &tmp);
+			f = &tmp;
+		}
+
+		subnode(&n1, &n2, f);
+		subnode(&n3, &n4, t);
+
+		cgen(&n1, &n3);
+		cgen(&n2, &n4);
+		break;
+	}
+}
+
+int
+complexop(Node *n, Node *res)
+{
+	if(n != N && n->type != T)
+	if(iscomplex[n->type->etype]) {
+		goto maybe;
+	}
+	if(res != N && res->type != T)
+	if(iscomplex[res->type->etype]) {
+		goto maybe;
+	}
+
+	if(n->op == OREAL || n->op == OIMAG)
+		goto yes;
+
+	goto no;
+
+maybe:
+	switch(n->op) {
+	case OCONV:	// implemented ops
+	case OADD:
+	case OSUB:
+	case OMUL:
+	case OMINUS:
+	case OCOMPLEX:
+	case OREAL:
+	case OIMAG:
+		goto yes;
+
+	case ODOT:
+	case ODOTPTR:
+	case OINDEX:
+	case OIND:
+	case ONAME:
+		goto yes;
+	}
+
+no:
+//dump("\ncomplex-no", n);
+	return 0;
+yes:
+//dump("\ncomplex-yes", n);
+	return 1;
+}
+
+void
+complexgen(Node *n, Node *res)
+{
+	Node *nl, *nr;
+	Node tnl, tnr;
+	Node n1, n2, tmp;
+	int tl, tr;
+
+	if(debug['g']) {
+		dump("\ncomplexgen-n", n);
+		dump("complexgen-res", res);
+	}
+	
+	while(n->op == OCONVNOP)
+		n = n->left;
+
+	// pick off float/complex opcodes
+	switch(n->op) {
+	case OCOMPLEX:
+		if(res->addable) {
+			subnode(&n1, &n2, res);
+			tempname(&tmp, n1.type);
+			cgen(n->left, &tmp);
+			cgen(n->right, &n2);
+			cgen(&tmp, &n1);
+			return;
+		}
+		break;
+
+	case OREAL:
+	case OIMAG:
+		nl = n->left;
+		if(!nl->addable) {
+			tempname(&tmp, nl->type);
+			complexgen(nl, &tmp);
+			nl = &tmp;
+		}
+		subnode(&n1, &n2, nl);
+		if(n->op == OREAL) {
+			cgen(&n1, res);
+			return;
+		}
+		cgen(&n2, res);
+		return;
+	}
+
+	// perform conversion from n to res
+	tl = simsimtype(res->type);
+	tl = cplxsubtype(tl);
+	tr = simsimtype(n->type);
+	tr = cplxsubtype(tr);
+	if(tl != tr) {
+		if(!n->addable) {
+			tempname(&n1, n->type);
+			complexmove(n, &n1);
+			n = &n1;
+		}
+		complexmove(n, res);
+		return;
+	}
+
+	if(!res->addable) {
+		igen(res, &n1, N);
+		cgen(n, &n1);
+		regfree(&n1);
+		return;
+	}
+	if(n->addable) {
+		complexmove(n, res);
+		return;
+	}
+
+	switch(n->op) {
+	default:
+		dump("complexgen: unknown op", n);
+		fatal("complexgen: unknown op %O", n->op);
+
+	case ODOT:
+	case ODOTPTR:
+	case OINDEX:
+	case OIND:
+	case ONAME:	// PHEAP or PPARAMREF var
+	case OCALLFUNC:
+	case OCALLMETH:
+	case OCALLINTER:
+		igen(n, &n1, res);
+		complexmove(&n1, res);
+		regfree(&n1);
+		return;
+
+	case OCONV:
+	case OADD:
+	case OSUB:
+	case OMUL:
+	case OMINUS:
+	case OCOMPLEX:
+	case OREAL:
+	case OIMAG:
+		break;
+	}
+
+	nl = n->left;
+	if(nl == N)
+		return;
+	nr = n->right;
+
+	// make both sides addable in ullman order
+	if(nr != N) {
+		if(nl->ullman > nr->ullman && !nl->addable) {
+			tempname(&tnl, nl->type);
+			cgen(nl, &tnl);
+			nl = &tnl;
+		}
+		if(!nr->addable) {
+			tempname(&tnr, nr->type);
+			cgen(nr, &tnr);
+			nr = &tnr;
+		}
+	}
+	if(!nl->addable) {
+		tempname(&tnl, nl->type);
+		cgen(nl, &tnl);
+		nl = &tnl;
+	}
+
+	switch(n->op) {
+	default:
+		fatal("complexgen: unknown op %O", n->op);
+		break;
+
+	case OCONV:
+		complexmove(nl, res);
+		break;
+
+	case OMINUS:
+		complexminus(nl, res);
+		break;
+
+	case OADD:
+	case OSUB:
+		complexadd(n->op, nl, nr, res);
+		break;
+
+	case OMUL:
+		complexmul(nl, nr, res);
+		break;
+	}
+}
+
+void
+complexbool(int op, Node *nl, Node *nr, int true, int likely, Prog *to)
+{
+	Node tnl, tnr;
+	Node n1, n2, n3, n4;
+	Node na, nb, nc;
+
+	// make both sides addable in ullman order
+	if(nr != N) {
+		if(nl->ullman > nr->ullman && !nl->addable) {
+			tempname(&tnl, nl->type);
+			cgen(nl, &tnl);
+			nl = &tnl;
+		}
+		if(!nr->addable) {
+			tempname(&tnr, nr->type);
+			cgen(nr, &tnr);
+			nr = &tnr;
+		}
+	}
+	if(!nl->addable) {
+		tempname(&tnl, nl->type);
+		cgen(nl, &tnl);
+		nl = &tnl;
+	}
+
+	// build tree
+	// real(l) == real(r) && imag(l) == imag(r)
+
+	subnode(&n1, &n2, nl);
+	subnode(&n3, &n4, nr);
+
+	memset(&na, 0, sizeof(na));
+	na.op = OANDAND;
+	na.left = &nb;
+	na.right = &nc;
+	na.type = types[TBOOL];
+
+	memset(&nb, 0, sizeof(na));
+	nb.op = OEQ;
+	nb.left = &n1;
+	nb.right = &n3;
+	nb.type = types[TBOOL];
+
+	memset(&nc, 0, sizeof(na));
+	nc.op = OEQ;
+	nc.left = &n2;
+	nc.right = &n4;
+	nc.type = types[TBOOL];
+
+	if(op == ONE)
+		true = !true;
+
+	bgen(&na, true, likely, to);
+}
+
+void
+nodfconst(Node *n, Type *t, Mpflt* fval)
+{
+	memset(n, 0, sizeof(*n));
+	n->op = OLITERAL;
+	n->addable = 1;
+	ullmancalc(n);
+	n->val.u.fval = fval;
+	n->val.ctype = CTFLT;
+	n->type = t;
+
+	if(!isfloat[t->etype])
+		fatal("nodfconst: bad type %T", t);
+}
+
+// break addable nc-complex into nr-real and ni-imaginary
+static void
+subnode(Node *nr, Node *ni, Node *nc)
+{
+	int tc;
+	Type *t;
+
+	if(!nc->addable)
+		fatal("subnode not addable");
+
+	tc = simsimtype(nc->type);
+	tc = cplxsubtype(tc);
+	t = types[tc];
+
+	if(nc->op == OLITERAL) {
+		nodfconst(nr, t, &nc->val.u.cval->real);
+		nodfconst(ni, t, &nc->val.u.cval->imag);
+		return;
+	}
+
+	*nr = *nc;
+	nr->type = t;
+
+	*ni = *nc;
+	ni->type = t;
+	ni->xoffset += t->width;
+}
+
+// generate code res = -nl
+static void
+minus(Node *nl, Node *res)
+{
+	Node ra;
+
+	memset(&ra, 0, sizeof(ra));
+	ra.op = OMINUS;
+	ra.left = nl;
+	ra.type = nl->type;
+	cgen(&ra, res);
+}
+
+// build and execute tree
+//	real(res) = -real(nl)
+//	imag(res) = -imag(nl)
+void
+complexminus(Node *nl, Node *res)
+{
+	Node n1, n2, n5, n6;
+
+	subnode(&n1, &n2, nl);
+	subnode(&n5, &n6, res);
+
+	minus(&n1, &n5);
+	minus(&n2, &n6);
+}
+
+
+// build and execute tree
+//	real(res) = real(nl) op real(nr)
+//	imag(res) = imag(nl) op imag(nr)
+void
+complexadd(int op, Node *nl, Node *nr, Node *res)
+{
+	Node n1, n2, n3, n4, n5, n6;
+	Node ra;
+
+	subnode(&n1, &n2, nl);
+	subnode(&n3, &n4, nr);
+	subnode(&n5, &n6, res);
+
+	memset(&ra, 0, sizeof(ra));
+	ra.op = op;
+	ra.left = &n1;
+	ra.right = &n3;
+	ra.type = n1.type;
+	cgen(&ra, &n5);
+
+	memset(&ra, 0, sizeof(ra));
+	ra.op = op;
+	ra.left = &n2;
+	ra.right = &n4;
+	ra.type = n2.type;
+	cgen(&ra, &n6);
+}
+
+// build and execute tree
+//	tmp       = real(nl)*real(nr) - imag(nl)*imag(nr)
+//	imag(res) = real(nl)*imag(nr) + imag(nl)*real(nr)
+//	real(res) = tmp
+void
+complexmul(Node *nl, Node *nr, Node *res)
+{
+	Node n1, n2, n3, n4, n5, n6;
+	Node rm1, rm2, ra, tmp;
+
+	subnode(&n1, &n2, nl);
+	subnode(&n3, &n4, nr);
+	subnode(&n5, &n6, res);
+	tempname(&tmp, n5.type);
+
+	// real part -> tmp
+	memset(&rm1, 0, sizeof(ra));
+	rm1.op = OMUL;
+	rm1.left = &n1;
+	rm1.right = &n3;
+	rm1.type = n1.type;
+
+	memset(&rm2, 0, sizeof(ra));
+	rm2.op = OMUL;
+	rm2.left = &n2;
+	rm2.right = &n4;
+	rm2.type = n2.type;
+
+	memset(&ra, 0, sizeof(ra));
+	ra.op = OSUB;
+	ra.left = &rm1;
+	ra.right = &rm2;
+	ra.type = rm1.type;
+	cgen(&ra, &tmp);
+
+	// imag part
+	memset(&rm1, 0, sizeof(ra));
+	rm1.op = OMUL;
+	rm1.left = &n1;
+	rm1.right = &n4;
+	rm1.type = n1.type;
+
+	memset(&rm2, 0, sizeof(ra));
+	rm2.op = OMUL;
+	rm2.left = &n2;
+	rm2.right = &n3;
+	rm2.type = n2.type;
+
+	memset(&ra, 0, sizeof(ra));
+	ra.op = OADD;
+	ra.left = &rm1;
+	ra.right = &rm2;
+	ra.type = rm1.type;
+	cgen(&ra, &n6);
+
+	// tmp ->real part
+	cgen(&tmp, &n5);
+}
diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c
new file mode 100644
index 0000000..dfcf475
--- /dev/null
+++ b/src/cmd/gc/dcl.c
@@ -0,0 +1,1494 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include	<u.h>
+#include	<libc.h>
+#include	"go.h"
+#include	"y.tab.h"
+
+static	void	funcargs(Node*);
+static	void	funcargs2(Type*);
+
+static int
+dflag(void)
+{
+	if(!debug['d'])
+		return 0;
+	if(debug['y'])
+		return 1;
+	if(incannedimport)
+		return 0;
+	return 1;
+}
+
+/*
+ * declaration stack & operations
+ */
+
+static void
+dcopy(Sym *a, Sym *b)
+{
+	a->pkg = b->pkg;
+	a->name = b->name;
+	a->def = b->def;
+	a->block = b->block;
+	a->lastlineno = b->lastlineno;
+}
+
+static Sym*
+push(void)
+{
+	Sym *d;
+
+	d = mal(sizeof(*d));
+	d->lastlineno = lineno;
+	d->link = dclstack;
+	dclstack = d;
+	return d;
+}
+
+static Sym*
+pushdcl(Sym *s)
+{
+	Sym *d;
+
+	d = push();
+	dcopy(d, s);
+	if(dflag())
+		print("\t%L push %S %p\n", lineno, s, s->def);
+	return d;
+}
+
+void
+popdcl(void)
+{
+	Sym *d, *s;
+	int lno;
+
+//	if(dflag())
+//		print("revert\n");
+
+	for(d=dclstack; d!=S; d=d->link) {
+		if(d->name == nil)
+			break;
+		s = pkglookup(d->name, d->pkg);
+		lno = s->lastlineno;
+		dcopy(s, d);
+		d->lastlineno = lno;
+		if(dflag())
+			print("\t%L pop %S %p\n", lineno, s, s->def);
+	}
+	if(d == S)
+		fatal("popdcl: no mark");
+	dclstack = d->link;
+	block = d->block;
+}
+
+void
+poptodcl(void)
+{
+	// pop the old marker and push a new one
+	// (cannot reuse the existing one)
+	// because we use the markers to identify blocks
+	// for the goto restriction checks.
+	popdcl();
+	markdcl();
+}
+
+void
+markdcl(void)
+{
+	Sym *d;
+
+	d = push();
+	d->name = nil;		// used as a mark in fifo
+	d->block = block;
+
+	blockgen++;
+	block = blockgen;
+
+//	if(dflag())
+//		print("markdcl\n");
+}
+
+void
+dumpdcl(char *st)
+{
+	Sym *s, *d;
+	int i;
+
+	USED(st);
+
+	i = 0;
+	for(d=dclstack; d!=S; d=d->link) {
+		i++;
+		print("    %.2d %p", i, d);
+		if(d->name == nil) {
+			print("\n");
+			continue;
+		}
+		print(" '%s'", d->name);
+		s = pkglookup(d->name, d->pkg);
+		print(" %S\n", s);
+	}
+}
+
+void
+testdclstack(void)
+{
+	Sym *d;
+
+	for(d=dclstack; d!=S; d=d->link) {
+		if(d->name == nil) {
+			if(nerrors != 0)
+				errorexit();
+			yyerror("mark left on the stack");
+			continue;
+		}
+	}
+}
+
+void
+redeclare(Sym *s, char *where)
+{
+	Strlit *pkgstr;
+	int line1, line2;
+
+	if(s->lastlineno == 0) {
+		pkgstr = s->origpkg ? s->origpkg->path : s->pkg->path;
+		yyerror("%S redeclared %s\n"
+			"\tprevious declaration during import \"%Z\"",
+			s, where, pkgstr);
+	} else {
+		line1 = parserline();
+		line2 = s->lastlineno;
+		
+		// When an import and a declaration collide in separate files,
+		// present the import as the "redeclared", because the declaration
+		// is visible where the import is, but not vice versa.
+		// See issue 4510.
+		if(s->def == N) {
+			line2 = line1;
+			line1 = s->lastlineno;
+		}
+
+		yyerrorl(line1, "%S redeclared %s\n"
+			"\tprevious declaration at %L",
+			s, where, line2);
+	}
+}
+
+static int vargen;
+
+/*
+ * declare individual names - var, typ, const
+ */
+void
+declare(Node *n, int ctxt)
+{
+	Sym *s;
+	int gen;
+	static int typegen;
+	
+	if(ctxt == PDISCARD)
+		return;
+
+	if(isblank(n))
+		return;
+
+	n->lineno = parserline();
+	s = n->sym;
+
+	// kludgy: typecheckok means we're past parsing.  Eg genwrapper may declare out of package names later.
+	if(importpkg == nil && !typecheckok && s->pkg != localpkg)
+		yyerror("cannot declare name %S", s);
+
+	if(ctxt == PEXTERN && strcmp(s->name, "init") == 0)
+		yyerror("cannot declare init - must be func", s);
+
+	gen = 0;
+	if(ctxt == PEXTERN) {
+		externdcl = list(externdcl, n);
+		if(dflag())
+			print("\t%L global decl %S %p\n", lineno, s, n);
+	} else {
+		if(curfn == nil && ctxt == PAUTO)
+			fatal("automatic outside function");
+		if(curfn != nil)
+			curfn->dcl = list(curfn->dcl, n);
+		if(n->op == OTYPE)
+			gen = ++typegen;
+		else if(n->op == ONAME && ctxt == PAUTO && strstr(s->name, "·") == nil)
+			gen = ++vargen;
+		pushdcl(s);
+		n->curfn = curfn;
+	}
+	if(ctxt == PAUTO)
+		n->xoffset = 0;
+
+	if(s->block == block) {
+		// functype will print errors about duplicate function arguments.
+		// Don't repeat the error here.
+		if(ctxt != PPARAM && ctxt != PPARAMOUT)
+			redeclare(s, "in this block");
+	}
+
+	s->block = block;
+	s->lastlineno = parserline();
+	s->def = n;
+	n->vargen = gen;
+	n->funcdepth = funcdepth;
+	n->class = ctxt;
+
+	autoexport(n, ctxt);
+}
+
+void
+addvar(Node *n, Type *t, int ctxt)
+{
+	if(n==N || n->sym == S || (n->op != ONAME && n->op != ONONAME) || t == T)
+		fatal("addvar: n=%N t=%T nil", n, t);
+
+	n->op = ONAME;
+	declare(n, ctxt);
+	n->type = t;
+}
+
+/*
+ * declare variables from grammar
+ * new_name_list (type | [type] = expr_list)
+ */
+NodeList*
+variter(NodeList *vl, Node *t, NodeList *el)
+{
+	int doexpr;
+	Node *v, *e, *as2;
+	NodeList *init;
+
+	init = nil;
+	doexpr = el != nil;
+
+	if(count(el) == 1 && count(vl) > 1) {
+		e = el->n;
+		as2 = nod(OAS2, N, N);
+		as2->list = vl;
+		as2->rlist = list1(e);
+		for(; vl; vl=vl->next) {
+			v = vl->n;
+			v->op = ONAME;
+			declare(v, dclcontext);
+			v->ntype = t;
+			v->defn = as2;
+			if(funcdepth > 0)
+				init = list(init, nod(ODCL, v, N));
+		}
+		return list(init, as2);
+	}
+	
+	for(; vl; vl=vl->next) {
+		if(doexpr) {
+			if(el == nil) {
+				yyerror("missing expression in var declaration");
+				break;
+			}
+			e = el->n;
+			el = el->next;
+		} else
+			e = N;
+
+		v = vl->n;
+		v->op = ONAME;
+		declare(v, dclcontext);
+		v->ntype = t;
+
+		if(e != N || funcdepth > 0 || isblank(v)) {
+			if(funcdepth > 0)
+				init = list(init, nod(ODCL, v, N));
+			e = nod(OAS, v, e);
+			init = list(init, e);
+			if(e->right != N)
+				v->defn = e;
+		}
+	}
+	if(el != nil)
+		yyerror("extra expression in var declaration");
+	return init;
+}
+
+/*
+ * declare constants from grammar
+ * new_name_list [[type] = expr_list]
+ */
+NodeList*
+constiter(NodeList *vl, Node *t, NodeList *cl)
+{
+	Node *v, *c;
+	NodeList *vv;
+
+	vv = nil;
+	if(cl == nil) {
+		if(t != N)
+			yyerror("const declaration cannot have type without expression");
+		cl = lastconst;
+		t = lasttype;
+	} else {
+		lastconst = cl;
+		lasttype = t;
+	}
+	cl = listtreecopy(cl);
+
+	for(; vl; vl=vl->next) {
+		if(cl == nil) {
+			yyerror("missing value in const declaration");
+			break;
+		}
+		c = cl->n;
+		cl = cl->next;
+
+		v = vl->n;
+		v->op = OLITERAL;
+		declare(v, dclcontext);
+
+		v->ntype = t;
+		v->defn = c;
+
+		vv = list(vv, nod(ODCLCONST, v, N));
+	}
+	if(cl != nil)
+		yyerror("extra expression in const declaration");
+	iota += 1;
+	return vv;
+}
+
+/*
+ * this generates a new name node,
+ * typically for labels or other one-off names.
+ */
+Node*
+newname(Sym *s)
+{
+	Node *n;
+
+	if(s == S)
+		fatal("newname nil");
+
+	n = nod(ONAME, N, N);
+	n->sym = s;
+	n->type = T;
+	n->addable = 1;
+	n->ullman = 1;
+	n->xoffset = 0;
+	return n;
+}
+
+/*
+ * this generates a new name node for a name
+ * being declared.
+ */
+Node*
+dclname(Sym *s)
+{
+	Node *n;
+
+	n = newname(s);
+	n->op = ONONAME;	// caller will correct it
+	return n;
+}
+
+Node*
+typenod(Type *t)
+{
+	// if we copied another type with *t = *u
+	// then t->nod might be out of date, so
+	// check t->nod->type too
+	if(t->nod == N || t->nod->type != t) {
+		t->nod = nod(OTYPE, N, N);
+		t->nod->type = t;
+		t->nod->sym = t->sym;
+	}
+	return t->nod;
+}
+
+
+/*
+ * this will return an old name
+ * that has already been pushed on the
+ * declaration list. a diagnostic is
+ * generated if no name has been defined.
+ */
+Node*
+oldname(Sym *s)
+{
+	Node *n;
+	Node *c;
+
+	n = s->def;
+	if(n == N) {
+		// maybe a top-level name will come along
+		// to give this a definition later.
+		// walkdef will check s->def again once
+		// all the input source has been processed.
+		n = newname(s);
+		n->op = ONONAME;
+		n->iota = iota;	// save current iota value in const declarations
+	}
+	if(curfn != nil && n->funcdepth > 0 && n->funcdepth != funcdepth && n->op == ONAME) {
+		// inner func is referring to var in outer func.
+		//
+		// TODO(rsc): If there is an outer variable x and we
+		// are parsing x := 5 inside the closure, until we get to
+		// the := it looks like a reference to the outer x so we'll
+		// make x a closure variable unnecessarily.
+		if(n->closure == N || n->closure->funcdepth != funcdepth) {
+			// create new closure var.
+			c = nod(ONAME, N, N);
+			c->sym = s;
+			c->class = PPARAMREF;
+			c->isddd = n->isddd;
+			c->defn = n;
+			c->addable = 0;
+			c->ullman = 2;
+			c->funcdepth = funcdepth;
+			c->outer = n->closure;
+			n->closure = c;
+			n->addrtaken = 1;
+			c->closure = n;
+			c->xoffset = 0;
+			curfn->cvars = list(curfn->cvars, c);
+		}
+		// return ref to closure var, not original
+		return n->closure;
+	}
+	return n;
+}
+
+/*
+ * := declarations
+ */
+
+static int
+colasname(Node *n)
+{
+	switch(n->op) {
+	case ONAME:
+	case ONONAME:
+	case OPACK:
+	case OTYPE:
+	case OLITERAL:
+		return n->sym != S;
+	}
+	return 0;
+}
+
+void
+colasdefn(NodeList *left, Node *defn)
+{
+	int nnew, nerr;
+	NodeList *l;
+	Node *n;
+
+	for(l=left; l; l=l->next)
+		if(l->n->sym != S)
+			l->n->sym->flags |= SymUniq;
+
+	nnew = 0;
+	nerr = 0;
+	for(l=left; l; l=l->next) {
+		n = l->n;
+		if(isblank(n))
+			continue;
+		if(!colasname(n)) {
+			yyerrorl(defn->lineno, "non-name %N on left side of :=", n);
+			nerr++;
+			continue;
+		}
+		if((n->sym->flags & SymUniq) == 0) {
+			yyerrorl(defn->lineno, "%S repeated on left side of :=", n->sym);
+			n->diag++;
+			nerr++;
+			continue;
+		}
+		n->sym->flags &= ~SymUniq;
+		if(n->sym->block == block)
+			continue;
+
+		nnew++;
+		n = newname(n->sym);
+		declare(n, dclcontext);
+		n->defn = defn;
+		defn->ninit = list(defn->ninit, nod(ODCL, n, N));
+		l->n = n;
+	}
+	if(nnew == 0 && nerr == 0)
+		yyerrorl(defn->lineno, "no new variables on left side of :=");
+}
+
+Node*
+colas(NodeList *left, NodeList *right, int32 lno)
+{
+	Node *as;
+
+	as = nod(OAS2, N, N);
+	as->list = left;
+	as->rlist = right;
+	as->colas = 1;
+	as->lineno = lno;
+	colasdefn(left, as);
+
+	// make the tree prettier; not necessary
+	if(count(left) == 1 && count(right) == 1) {
+		as->left = as->list->n;
+		as->right = as->rlist->n;
+		as->list = nil;
+		as->rlist = nil;
+		as->op = OAS;
+	}
+
+	return as;
+}
+
+/*
+ * declare the arguments in an
+ * interface field declaration.
+ */
+void
+ifacedcl(Node *n)
+{
+	if(n->op != ODCLFIELD || n->right == N)
+		fatal("ifacedcl");
+
+	if(isblank(n->left))
+		yyerror("methods must have a unique non-blank name");
+
+	dclcontext = PPARAM;
+	markdcl();
+	funcdepth++;
+	n->outer = curfn;
+	curfn = n;
+	funcargs(n->right);
+
+	// funcbody is normally called after the parser has
+	// seen the body of a function but since an interface
+	// field declaration does not have a body, we must
+	// call it now to pop the current declaration context.
+	dclcontext = PAUTO;
+	funcbody(n);
+}
+
+/*
+ * declare the function proper
+ * and declare the arguments.
+ * called in extern-declaration context
+ * returns in auto-declaration context.
+ */
+void
+funchdr(Node *n)
+{
+	// change the declaration context from extern to auto
+	if(funcdepth == 0 && dclcontext != PEXTERN)
+		fatal("funchdr: dclcontext");
+
+	dclcontext = PAUTO;
+	markdcl();
+	funcdepth++;
+
+	n->outer = curfn;
+	curfn = n;
+
+	if(n->nname)
+		funcargs(n->nname->ntype);
+	else if (n->ntype)
+		funcargs(n->ntype);
+	else
+		funcargs2(n->type);
+}
+
+static void
+funcargs(Node *nt)
+{
+	Node *n, *nn;
+	NodeList *l;
+	int gen;
+
+	if(nt->op != OTFUNC)
+		fatal("funcargs %O", nt->op);
+
+	// re-start the variable generation number
+	// we want to use small numbers for the return variables,
+	// so let them have the chunk starting at 1.
+	vargen = count(nt->rlist);
+
+	// declare the receiver and in arguments.
+	// no n->defn because type checking of func header
+	// will not fill in the types until later
+	if(nt->left != N) {
+		n = nt->left;
+		if(n->op != ODCLFIELD)
+			fatal("funcargs receiver %O", n->op);
+		if(n->left != N) {
+			n->left->op = ONAME;
+			n->left->ntype = n->right;
+			declare(n->left, PPARAM);
+			if(dclcontext == PAUTO)
+				n->left->vargen = ++vargen;
+		}
+	}
+	for(l=nt->list; l; l=l->next) {
+		n = l->n;
+		if(n->op != ODCLFIELD)
+			fatal("funcargs in %O", n->op);
+		if(n->left != N) {
+			n->left->op = ONAME;
+			n->left->ntype = n->right;
+			declare(n->left, PPARAM);
+			if(dclcontext == PAUTO)
+				n->left->vargen = ++vargen;
+		}
+	}
+
+	// declare the out arguments.
+	gen = count(nt->list);
+	int i = 0;
+	for(l=nt->rlist; l; l=l->next) {
+		n = l->n;
+
+		if(n->op != ODCLFIELD)
+			fatal("funcargs out %O", n->op);
+
+		if(n->left == N) {
+			// Name so that escape analysis can track it. ~r stands for 'result'.
+			snprint(namebuf, sizeof(namebuf), "~r%d", gen++);
+			n->left = newname(lookup(namebuf));
+			// TODO: n->left->missing = 1;
+		} 
+
+		n->left->op = ONAME;
+
+		if(isblank(n->left)) {
+			// Give it a name so we can assign to it during return. ~b stands for 'blank'.
+			// The name must be different from ~r above because if you have
+			//	func f() (_ int)
+			//	func g() int
+			// f is allowed to use a plain 'return' with no arguments, while g is not.
+			// So the two cases must be distinguished.
+			// We do not record a pointer to the original node (n->orig).
+			// Having multiple names causes too much confusion in later passes.
+			nn = nod(OXXX, N, N);
+			*nn = *n->left;
+			nn->orig = nn;
+			snprint(namebuf, sizeof(namebuf), "~b%d", gen++);
+			nn->sym = lookup(namebuf);
+			n->left = nn;
+		}
+
+		n->left->ntype = n->right;
+		declare(n->left, PPARAMOUT);
+		if(dclcontext == PAUTO)
+			n->left->vargen = ++i;
+	}
+}
+
+/*
+ * Same as funcargs, except run over an already constructed TFUNC.
+ * This happens during import, where the hidden_fndcl rule has
+ * used functype directly to parse the function's type.
+ */
+static void
+funcargs2(Type *t)
+{
+	Type *ft;
+	Node *n;
+
+	if(t->etype != TFUNC)
+		fatal("funcargs2 %T", t);
+	
+	if(t->thistuple)
+		for(ft=getthisx(t)->type; ft; ft=ft->down) {
+			if(!ft->nname || !ft->nname->sym)
+				continue;
+			n = ft->nname;  // no need for newname(ft->nname->sym)
+			n->type = ft->type;
+			declare(n, PPARAM);
+		}
+
+	if(t->intuple)
+		for(ft=getinargx(t)->type; ft; ft=ft->down) {
+			if(!ft->nname || !ft->nname->sym)
+				continue;
+			n = ft->nname;
+			n->type = ft->type;
+			declare(n, PPARAM);
+		}
+
+	if(t->outtuple)
+		for(ft=getoutargx(t)->type; ft; ft=ft->down) {
+			if(!ft->nname || !ft->nname->sym)
+				continue;
+			n = ft->nname;
+			n->type = ft->type;
+			declare(n, PPARAMOUT);
+		}
+}
+
+/*
+ * finish the body.
+ * called in auto-declaration context.
+ * returns in extern-declaration context.
+ */
+void
+funcbody(Node *n)
+{
+	// change the declaration context from auto to extern
+	if(dclcontext != PAUTO)
+		fatal("funcbody: dclcontext");
+	popdcl();
+	funcdepth--;
+	curfn = n->outer;
+	n->outer = N;
+	if(funcdepth == 0)
+		dclcontext = PEXTERN;
+}
+
+/*
+ * new type being defined with name s.
+ */
+Node*
+typedcl0(Sym *s)
+{
+	Node *n;
+
+	n = newname(s);
+	n->op = OTYPE;
+	declare(n, dclcontext);
+	return n;
+}
+
+/*
+ * node n, which was returned by typedcl0
+ * is being declared to have uncompiled type t.
+ * return the ODCLTYPE node to use.
+ */
+Node*
+typedcl1(Node *n, Node *t, int local)
+{
+	n->ntype = t;
+	n->local = local;
+	return nod(ODCLTYPE, n, N);
+}
+
+/*
+ * structs, functions, and methods.
+ * they don't belong here, but where do they belong?
+ */
+
+static void
+checkembeddedtype(Type *t)
+{
+	if (t == T)
+		return;
+
+	if(t->sym == S && isptr[t->etype]) {
+		t = t->type;
+		if(t->etype == TINTER)
+			yyerror("embedded type cannot be a pointer to interface");
+	}
+	if(isptr[t->etype])
+		yyerror("embedded type cannot be a pointer");
+	else if(t->etype == TFORW && t->embedlineno == 0)
+		t->embedlineno = lineno;
+}
+
+static Type*
+structfield(Node *n)
+{
+	Type *f;
+	int lno;
+
+	lno = lineno;
+	lineno = n->lineno;
+
+	if(n->op != ODCLFIELD)
+		fatal("structfield: oops %N\n", n);
+
+	f = typ(TFIELD);
+	f->isddd = n->isddd;
+
+	if(n->right != N) {
+		typecheck(&n->right, Etype);
+		n->type = n->right->type;
+		if(n->left != N)
+			n->left->type = n->type;
+		if(n->embedded)
+			checkembeddedtype(n->type);
+	}
+	n->right = N;
+		
+	f->type = n->type;
+	if(f->type == T)
+		f->broke = 1;
+
+	switch(n->val.ctype) {
+	case CTSTR:
+		f->note = n->val.u.sval;
+		break;
+	default:
+		yyerror("field annotation must be string");
+		// fallthrough
+	case CTxxx:
+		f->note = nil;
+		break;
+	}
+
+	if(n->left && n->left->op == ONAME) {
+		f->nname = n->left;
+		f->embedded = n->embedded;
+		f->sym = f->nname->sym;
+	}
+
+	lineno = lno;
+	return f;
+}
+
+static uint32 uniqgen;
+
+static void
+checkdupfields(Type *t, char* what)
+{
+	int lno;
+
+	lno = lineno;
+
+	for( ; t; t=t->down) {
+		if(t->sym && t->nname && !isblank(t->nname)) {
+			if(t->sym->uniqgen == uniqgen) {
+				lineno = t->nname->lineno;
+				yyerror("duplicate %s %s", what, t->sym->name);
+			} else
+				t->sym->uniqgen = uniqgen;
+		}
+	}
+
+	lineno = lno;
+}
+
+/*
+ * convert a parsed id/type list into
+ * a type for struct/interface/arglist
+ */
+Type*
+tostruct(NodeList *l)
+{
+	Type *t, *f, **tp;
+	t = typ(TSTRUCT);
+
+	for(tp = &t->type; l; l=l->next) {
+		f = structfield(l->n);
+
+		*tp = f;
+		tp = &f->down;
+	}
+
+	for(f=t->type; f && !t->broke; f=f->down)
+		if(f->broke)
+			t->broke = 1;
+
+	uniqgen++;
+	checkdupfields(t->type, "field");
+
+	if (!t->broke)
+		checkwidth(t);
+
+	return t;
+}
+
+static Type*
+tofunargs(NodeList *l)
+{
+	Type *t, *f, **tp;
+
+	t = typ(TSTRUCT);
+	t->funarg = 1;
+
+	for(tp = &t->type; l; l=l->next) {
+		f = structfield(l->n);
+		f->funarg = 1;
+
+		// esc.c needs to find f given a PPARAM to add the tag.
+		if(l->n->left && l->n->left->class == PPARAM)
+			l->n->left->paramfld = f;
+
+		*tp = f;
+		tp = &f->down;
+	}
+
+	for(f=t->type; f && !t->broke; f=f->down)
+		if(f->broke)
+			t->broke = 1;
+
+	return t;
+}
+
+static Type*
+interfacefield(Node *n)
+{
+	Type *f;
+	int lno;
+
+	lno = lineno;
+	lineno = n->lineno;
+
+	if(n->op != ODCLFIELD)
+		fatal("interfacefield: oops %N\n", n);
+
+	if (n->val.ctype != CTxxx)
+		yyerror("interface method cannot have annotation");
+
+	f = typ(TFIELD);
+	f->isddd = n->isddd;
+	
+	if(n->right != N) {
+		if(n->left != N) {
+			// queue resolution of method type for later.
+			// right now all we need is the name list.
+			// avoids cycles for recursive interface types.
+			n->type = typ(TINTERMETH);
+			n->type->nname = n->right;
+			n->left->type = n->type;
+			queuemethod(n);
+
+			if(n->left->op == ONAME) {
+				f->nname = n->left;
+				f->embedded = n->embedded;
+				f->sym = f->nname->sym;
+			}
+
+		} else {
+
+			typecheck(&n->right, Etype);
+			n->type = n->right->type;
+
+			if(n->embedded)
+				checkembeddedtype(n->type);
+
+			if(n->type)
+				switch(n->type->etype) {
+				case TINTER:
+					break;
+				case TFORW:
+					yyerror("interface type loop involving %T", n->type);
+					f->broke = 1;
+					break;
+				default:
+					yyerror("interface contains embedded non-interface %T", n->type);
+					f->broke = 1;
+					break;
+				}
+		}
+	}
+
+	n->right = N;
+	
+	f->type = n->type;
+	if(f->type == T)
+		f->broke = 1;
+	
+	lineno = lno;
+	return f;
+}
+
+Type*
+tointerface(NodeList *l)
+{
+	Type *t, *f, **tp, *t1;
+
+	t = typ(TINTER);
+
+	tp = &t->type;
+	for(; l; l=l->next) {
+		f = interfacefield(l->n);
+
+		if (l->n->left == N && f->type->etype == TINTER) {
+			// embedded interface, inline methods
+			for(t1=f->type->type; t1; t1=t1->down) {
+				f = typ(TFIELD);
+				f->type = t1->type;
+				f->broke = t1->broke;
+				f->sym = t1->sym;
+				if(f->sym)
+					f->nname = newname(f->sym);
+				*tp = f;
+				tp = &f->down;
+			}
+		} else {
+			*tp = f;
+			tp = &f->down;
+		}
+	}
+
+	for(f=t->type; f && !t->broke; f=f->down)
+		if(f->broke)
+			t->broke = 1;
+
+	uniqgen++;
+	checkdupfields(t->type, "method");
+	t = sortinter(t);
+	checkwidth(t);
+
+	return t;
+}
+
+Node*
+embedded(Sym *s, Pkg *pkg)
+{
+	Node *n;
+	char *name;
+
+	// Names sometimes have disambiguation junk
+	// appended after a center dot.  Discard it when
+	// making the name for the embedded struct field.
+	enum { CenterDot = 0xB7 };
+	name = s->name;
+	if(utfrune(s->name, CenterDot)) {
+		name = strdup(s->name);
+		*utfrune(name, CenterDot) = 0;
+	}
+
+	if(exportname(name))
+		n = newname(lookup(name));
+	else if(s->pkg == builtinpkg)
+		// The name of embedded builtins belongs to pkg.
+		n = newname(pkglookup(name, pkg));
+	else
+		n = newname(pkglookup(name, s->pkg));
+	n = nod(ODCLFIELD, n, oldname(s));
+	n->embedded = 1;
+	return n;
+}
+
+/*
+ * check that the list of declarations is either all anonymous or all named
+ */
+
+static Node*
+findtype(NodeList *l)
+{
+	for(; l; l=l->next)
+		if(l->n->op == OKEY)
+			return l->n->right;
+	return N;
+}
+
+NodeList*
+checkarglist(NodeList *all, int input)
+{
+	int named;
+	Node *n, *t, *nextt;
+	NodeList *l;
+
+	named = 0;
+	for(l=all; l; l=l->next) {
+		if(l->n->op == OKEY) {
+			named = 1;
+			break;
+		}
+	}
+	if(named) {
+		n = N;
+		for(l=all; l; l=l->next) {
+			n = l->n;
+			if(n->op != OKEY && n->sym == S) {
+				yyerror("mixed named and unnamed function parameters");
+				break;
+			}
+		}
+		if(l == nil && n != N && n->op != OKEY)
+			yyerror("final function parameter must have type");
+	}
+
+	nextt = nil;
+	for(l=all; l; l=l->next) {
+		// can cache result from findtype to avoid
+		// quadratic behavior here, but unlikely to matter.
+		n = l->n;
+		if(named) {
+			if(n->op == OKEY) {
+				t = n->right;
+				n = n->left;
+				nextt = nil;
+			} else {
+				if(nextt == nil)
+					nextt = findtype(l);
+				t = nextt;
+			}
+		} else {
+			t = n;
+			n = N;
+		}
+
+		// during import l->n->op is OKEY, but l->n->left->sym == S
+		// means it was a '?', not that it was
+		// a lone type This doesn't matter for the exported
+		// declarations, which are parsed by rules that don't
+		// use checkargs, but can happen for func literals in
+		// the inline bodies.
+		// TODO(rsc) this can go when typefmt case TFIELD in exportmode fmt.c prints _ instead of ?
+		if(importpkg && n->sym == S)
+			n = N;
+
+		if(n != N && n->sym == S) {
+			t = n;
+			n = N;
+		}
+		if(n != N)
+			n = newname(n->sym);
+		n = nod(ODCLFIELD, n, t);
+		if(n->right != N && n->right->op == ODDD) {
+			if(!input)
+				yyerror("cannot use ... in output argument list");
+			else if(l->next != nil)
+				yyerror("can only use ... as final argument in list");
+			n->right->op = OTARRAY;
+			n->right->right = n->right->left;
+			n->right->left = N;
+			n->isddd = 1;
+			if(n->left != N)
+				n->left->isddd = 1;
+		}
+		l->n = n;
+	}
+	return all;
+}
+
+
+Node*
+fakethis(void)
+{
+	Node *n;
+
+	n = nod(ODCLFIELD, N, typenod(ptrto(typ(TSTRUCT))));
+	return n;
+}
+
+/*
+ * Is this field a method on an interface?
+ * Those methods have an anonymous
+ * *struct{} as the receiver.
+ * (See fakethis above.)
+ */
+int
+isifacemethod(Type *f)
+{
+	Type *rcvr;
+	Type *t;
+
+	rcvr = getthisx(f)->type;
+	if(rcvr->sym != S)
+		return 0;
+	t = rcvr->type;
+	if(!isptr[t->etype])
+		return 0;
+	t = t->type;
+	if(t->sym != S || t->etype != TSTRUCT || t->type != T)
+		return 0;
+	return 1;
+}
+
+/*
+ * turn a parsed function declaration
+ * into a type
+ */
+Type*
+functype(Node *this, NodeList *in, NodeList *out)
+{
+	Type *t;
+	NodeList *rcvr;
+	Sym *s;
+
+	t = typ(TFUNC);
+
+	rcvr = nil;
+	if(this)
+		rcvr = list1(this);
+	t->type = tofunargs(rcvr);
+	t->type->down = tofunargs(out);
+	t->type->down->down = tofunargs(in);
+
+	uniqgen++;
+	checkdupfields(t->type->type, "argument");
+	checkdupfields(t->type->down->type, "argument");
+	checkdupfields(t->type->down->down->type, "argument");
+
+	if (t->type->broke || t->type->down->broke || t->type->down->down->broke)
+		t->broke = 1;
+
+	if(this)
+		t->thistuple = 1;
+	t->outtuple = count(out);
+	t->intuple = count(in);
+	t->outnamed = 0;
+	if(t->outtuple > 0 && out->n->left != N && out->n->left->orig != N) {
+		s = out->n->left->orig->sym;
+		if(s != S && (s->name[0] != '~' || s->name[1] != 'r')) // ~r%d is the name invented for an unnamed result
+			t->outnamed = 1;
+	}
+
+	return t;
+}
+
+Sym*
+methodsym(Sym *nsym, Type *t0, int iface)
+{
+	Sym *s;
+	char *p;
+	Type *t;
+	char *suffix;
+	Pkg *spkg;
+	static Pkg *toppkg;
+
+	t = t0;
+	if(t == T)
+		goto bad;
+	s = t->sym;
+	if(s == S && isptr[t->etype]) {
+		t = t->type;
+		if(t == T)
+			goto bad;
+		s = t->sym;
+	}
+	spkg = nil;
+	if(s != S)
+		spkg = s->pkg;
+
+	// if t0 == *t and t0 has a sym,
+	// we want to see *t, not t0, in the method name.
+	if(t != t0 && t0->sym)
+		t0 = ptrto(t);
+
+	suffix = "";
+	if(iface) {
+		dowidth(t0);
+		if(t0->width < types[tptr]->width)
+			suffix = "·i";
+	}
+	if((spkg == nil || nsym->pkg != spkg) && !exportname(nsym->name)) {
+		if(t0->sym == S && isptr[t0->etype])
+			p = smprint("(%-hT).%s.%s%s", t0, nsym->pkg->prefix, nsym->name, suffix);
+		else
+			p = smprint("%-hT.%s.%s%s", t0, nsym->pkg->prefix, nsym->name, suffix);
+	} else {
+		if(t0->sym == S && isptr[t0->etype])
+			p = smprint("(%-hT).%s%s", t0, nsym->name, suffix);
+		else
+			p = smprint("%-hT.%s%s", t0, nsym->name, suffix);
+	}
+	if(spkg == nil) {
+		if(toppkg == nil)
+			toppkg = mkpkg(strlit("go"));
+		spkg = toppkg;
+	}
+	s = pkglookup(p, spkg);
+	free(p);
+	return s;
+
+bad:
+	yyerror("illegal receiver type: %T", t0);
+	return S;
+}
+
+Node*
+methodname(Node *n, Type *t)
+{
+	Sym *s;
+
+	s = methodsym(n->sym, t, 0);
+	if(s == S)
+		return n;
+	return newname(s);
+}
+
+Node*
+methodname1(Node *n, Node *t)
+{
+	char *star;
+	char *p;
+
+	star = nil;
+	if(t->op == OIND) {
+		star = "*";
+		t = t->left;
+	}
+	if(t->sym == S || isblank(n))
+		return newname(n->sym);
+
+	if(star)
+		p = smprint("(%s%S).%S", star, t->sym, n->sym);
+	else
+		p = smprint("%S.%S", t->sym, n->sym);
+
+	if(exportname(t->sym->name))
+		n = newname(lookup(p));
+	else
+		n = newname(pkglookup(p, t->sym->pkg));
+	free(p);
+	return n;
+}
+
+/*
+ * add a method, declared as a function,
+ * n is fieldname, pa is base type, t is function type
+ */
+void
+addmethod(Sym *sf, Type *t, int local, int nointerface)
+{
+	Type *f, *d, *pa;
+	Node *n;
+
+	// get field sym
+	if(sf == S)
+		fatal("no method symbol");
+
+	// get parent type sym
+	pa = getthisx(t)->type;	// ptr to this structure
+	if(pa == T) {
+		yyerror("missing receiver");
+		return;
+	}
+
+	pa = pa->type;
+	f = methtype(pa, 1);
+	if(f == T) {
+		t = pa;
+		if(t == T) // rely on typecheck having complained before
+			return;
+		if(t != T) {
+			if(isptr[t->etype]) {
+				if(t->sym != S) {
+					yyerror("invalid receiver type %T (%T is a pointer type)", pa, t);
+					return;
+				}
+				t = t->type;
+			}
+			if(t->broke) // rely on typecheck having complained before
+				return;
+			if(t->sym == S) {
+				yyerror("invalid receiver type %T (%T is an unnamed type)", pa, t);
+				return;
+			}
+			if(isptr[t->etype]) {
+				yyerror("invalid receiver type %T (%T is a pointer type)", pa, t);
+				return;
+			}
+			if(t->etype == TINTER) {
+				yyerror("invalid receiver type %T (%T is an interface type)", pa, t);
+				return;
+			}
+		}
+		// Should have picked off all the reasons above,
+		// but just in case, fall back to generic error.
+		yyerror("invalid receiver type %T (%lT / %lT)", pa, pa, t);
+		return;
+	}
+
+	pa = f;
+	if(pa->etype == TSTRUCT) {
+		for(f=pa->type; f; f=f->down) {
+			if(f->sym == sf) {
+				yyerror("type %T has both field and method named %S", pa, sf);
+				return;
+			}
+		}
+	}
+
+	if(local && !pa->local) {
+		// defining method on non-local type.
+		yyerror("cannot define new methods on non-local type %T", pa);
+		return;
+	}
+
+	n = nod(ODCLFIELD, newname(sf), N);
+	n->type = t;
+
+	d = T;	// last found
+	for(f=pa->method; f!=T; f=f->down) {
+		d = f;
+		if(f->etype != TFIELD)
+			fatal("addmethod: not TFIELD: %N", f);
+		if(strcmp(sf->name, f->sym->name) != 0)
+			continue;
+		if(!eqtype(t, f->type))
+			yyerror("method redeclared: %T.%S\n\t%T\n\t%T", pa, sf, f->type, t);
+		return;
+	}
+
+	f = structfield(n);
+	f->nointerface = nointerface;
+
+	// during import unexported method names should be in the type's package
+	if(importpkg && f->sym && !exportname(f->sym->name) && f->sym->pkg != structpkg)
+		fatal("imported method name %+S in wrong package %s\n", f->sym, structpkg->name);
+
+	if(d == T)
+		pa->method = f;
+	else
+		d->down = f;
+	return;
+}
+
+void
+funccompile(Node *n, int isclosure)
+{
+	stksize = BADWIDTH;
+	maxarg = 0;
+
+	if(n->type == T) {
+		if(nerrors == 0)
+			fatal("funccompile missing type");
+		return;
+	}
+
+	// assign parameter offsets
+	checkwidth(n->type);
+	
+	// record offset to actual frame pointer.
+	// for closure, have to skip over leading pointers and PC slot.
+	// TODO(rsc): this is the old jit closure handling code.
+	// with the new closures, isclosure is always 0; delete this block.
+	nodfp->xoffset = 0;
+	if(isclosure) {
+		NodeList *l;
+		for(l=n->nname->ntype->list; l; l=l->next) {
+			nodfp->xoffset += widthptr;
+			if(l->n->left == N)	// found slot for PC
+				break;
+		}
+	}
+
+	if(curfn)
+		fatal("funccompile %S inside %S", n->nname->sym, curfn->nname->sym);
+
+	stksize = 0;
+	dclcontext = PAUTO;
+	funcdepth = n->funcdepth + 1;
+	compile(n);
+	curfn = nil;
+	funcdepth = 0;
+	dclcontext = PEXTERN;
+}
+
+Sym*
+funcsym(Sym *s)
+{
+	char *p;
+	Sym *s1;
+	
+	p = smprint("%s·f", s->name);
+	s1 = pkglookup(p, s->pkg);
+	free(p);
+	if(s1->def == N) {
+		s1->def = newname(s1);
+		s1->def->shortname = newname(s);
+		funcsyms = list(funcsyms, s1->def);
+	}
+	return s1;
+}
diff --git a/src/cmd/gc/doc.go b/src/cmd/gc/doc.go
new file mode 100644
index 0000000..03df93a
--- /dev/null
+++ b/src/cmd/gc/doc.go
@@ -0,0 +1,95 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+/*
+
+Gc is the generic label for the family of Go compilers
+that function as part of the (modified) Plan 9 tool chain.  The C compiler
+documentation at
+
+	http://plan9.bell-labs.com/sys/doc/comp.pdf     (Tools overview)
+	http://plan9.bell-labs.com/sys/doc/compiler.pdf (C compiler architecture)
+
+gives the overall design of the tool chain.  Aside from a few adapted pieces,
+such as the optimizer, the Go compilers are wholly new programs.
+
+The compiler reads in a set of Go files, typically suffixed ".go".  They
+must all be part of one package.  The output is a single intermediate file
+representing the "binary assembly" of the compiled package, ready as input
+for the linker (6l, etc.).
+
+The generated files contain type information about the symbols exported by
+the package and about types used by symbols imported by the package from
+other packages. It is therefore not necessary when compiling client C of
+package P to read the files of P's dependencies, only the compiled output
+of P.
+
+Command Line
+
+Usage:
+	go tool 6g [flags] file...
+The specified files must be Go source files and all part of the same package.
+Substitute 6g with 8g or 5g where appropriate.
+
+Flags:
+	-o file
+		output file, default file.6 for 6g, etc.
+	-pack
+		write an archive file rather than an object file
+	-e
+		normally the compiler quits after 10 errors; -e prints all errors
+	-p path
+		assume that path is the eventual import path for this code,
+		and diagnose any attempt to import a package that depends on it.
+	-D path
+		treat a relative import as relative to path
+	-L
+		show entire file path when printing line numbers in errors
+	-I dir1 -I dir2
+		add dir1 and dir2 to the list of paths to check for imported packages
+	-N
+		disable optimizations
+	-nolocalimports
+		disallow local (relative) imports
+	-S
+		write assembly language text to standard output (code only)
+	-S -S
+		write assembly language text to standard output (code and data)
+	-u
+		disallow importing packages not marked as safe; implies -nolocalimports
+	-V
+		print the compiler version
+	-race
+		compile with race detection enabled
+
+There are also a number of debugging flags; run the command with no arguments
+to get a usage message.
+
+Compiler Directives
+
+The compiler accepts two compiler directives in the form of // comments at the
+beginning of a line. To distinguish them from non-directive comments, the directives
+require no space between the slashes and the name of the directive. However, since
+they are comments, tools unaware of the directive convention or of a particular
+directive can skip over a directive like any other comment.
+
+    //line path/to/file:linenumber
+
+The //line directive specifies that the source line that follows should be recorded
+as having come from the given file path and line number. Successive lines are
+recorded using increasing line numbers, until the next directive. This directive
+typically appears in machine-generated code, so that compilers and debuggers
+will show lines in the original input to the generator.
+
+    //go:noescape
+
+The //go:noescape directive specifies that the next declaration in the file, which
+must be a func without a body (meaning that it has an implementation not written
+in Go) does not allow any of the pointers passed as arguments to escape into the
+heap or into the values returned from the function. This information can be used as
+during the compiler's escape analysis of Go code calling the function.
+*/
+package main
diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c
new file mode 100644
index 0000000..324f24f
--- /dev/null
+++ b/src/cmd/gc/esc.c
@@ -0,0 +1,1281 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Escape analysis.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+// Run analysis on minimal sets of mutually recursive functions
+// or single non-recursive functions, bottom up.
+//
+// Finding these sets is finding strongly connected components
+// in the static call graph.  The algorithm for doing that is taken
+// from Sedgewick, Algorithms, Second Edition, p. 482, with two
+// adaptations.
+//
+// First, a hidden closure function (n->curfn != N) cannot be the
+// root of a connected component. Refusing to use it as a root
+// forces it into the component of the function in which it appears.
+// The analysis assumes that closures and the functions in which they
+// appear are analyzed together, so that the aliasing between their
+// variables can be modeled more precisely.
+//
+// Second, each function becomes two virtual nodes in the graph,
+// with numbers n and n+1. We record the function's node number as n
+// but search from node n+1. If the search tells us that the component
+// number (min) is n+1, we know that this is a trivial component: one function
+// plus its closures. If the search tells us that the component number is
+// n, then there was a path from node n+1 back to node n, meaning that
+// the function set is mutually recursive. The escape analysis can be
+// more precise when analyzing a single non-recursive function than
+// when analyzing a set of mutually recursive functions.
+
+static NodeList *stack;
+static uint32 visitgen;
+static uint32 visit(Node*);
+static uint32 visitcode(Node*, uint32);
+static uint32 visitcodelist(NodeList*, uint32);
+
+static void analyze(NodeList*, int);
+
+enum
+{
+	EscFuncUnknown = 0,
+	EscFuncPlanned,
+	EscFuncStarted,
+	EscFuncTagged,
+};
+
+void
+escapes(NodeList *all)
+{
+	NodeList *l;
+
+	for(l=all; l; l=l->next)
+		l->n->walkgen = 0;
+
+	visitgen = 0;
+	for(l=all; l; l=l->next)
+		if(l->n->op == ODCLFUNC && l->n->curfn == N)
+			visit(l->n);
+
+	for(l=all; l; l=l->next)
+		l->n->walkgen = 0;
+}
+
+static uint32
+visit(Node *n)
+{
+	uint32 min, recursive;
+	NodeList *l, *block;
+
+	if(n->walkgen > 0) {
+		// already visited
+		return n->walkgen;
+	}
+	
+	visitgen++;
+	n->walkgen = visitgen;
+	visitgen++;
+	min = visitgen;
+
+	l = mal(sizeof *l);
+	l->next = stack;
+	l->n = n;
+	stack = l;
+	min = visitcodelist(n->nbody, min);
+	if((min == n->walkgen || min == n->walkgen+1) && n->curfn == N) {
+		// This node is the root of a strongly connected component.
+
+		// The original min passed to visitcodelist was n->walkgen+1.
+		// If visitcodelist found its way back to n->walkgen, then this
+		// block is a set of mutually recursive functions.
+		// Otherwise it's just a lone function that does not recurse.
+		recursive = min == n->walkgen;
+
+		// Remove connected component from stack.
+		// Mark walkgen so that future visits return a large number
+		// so as not to affect the caller's min.
+		block = stack;
+		for(l=stack; l->n != n; l=l->next)
+			l->n->walkgen = (uint32)~0U;
+		n->walkgen = (uint32)~0U;
+		stack = l->next;
+		l->next = nil;
+
+		// Run escape analysis on this set of functions.
+		analyze(block, recursive);
+	}
+
+	return min;
+}
+
+static uint32
+visitcodelist(NodeList *l, uint32 min)
+{
+	for(; l; l=l->next)
+		min = visitcode(l->n, min);
+	return min;
+}
+
+static uint32
+visitcode(Node *n, uint32 min)
+{
+	Node *fn;
+	uint32 m;
+
+	if(n == N)
+		return min;
+
+	min = visitcodelist(n->ninit, min);
+	min = visitcode(n->left, min);
+	min = visitcode(n->right, min);
+	min = visitcodelist(n->list, min);
+	min = visitcode(n->ntest, min);
+	min = visitcode(n->nincr, min);
+	min = visitcodelist(n->nbody, min);
+	min = visitcodelist(n->nelse, min);
+	min = visitcodelist(n->rlist, min);
+	
+	if(n->op == OCALLFUNC || n->op == OCALLMETH) {
+		fn = n->left;
+		if(n->op == OCALLMETH)
+			fn = n->left->right->sym->def;
+		if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn)
+			if((m = visit(fn->defn)) < min)
+				min = m;
+	}
+	
+	if(n->op == OCLOSURE)
+		if((m = visit(n->closure)) < min)
+			min = m;
+
+	return min;
+}
+
+// An escape analysis pass for a set of functions.
+//
+// First escfunc, esc and escassign recurse over the ast of each
+// function to dig out flow(dst,src) edges between any
+// pointer-containing nodes and store them in dst->escflowsrc.  For
+// variables assigned to a variable in an outer scope or used as a
+// return value, they store a flow(theSink, src) edge to a fake node
+// 'the Sink'.  For variables referenced in closures, an edge
+// flow(closure, &var) is recorded and the flow of a closure itself to
+// an outer scope is tracked the same way as other variables.
+//
+// Then escflood walks the graph starting at theSink and tags all
+// variables of it can reach an & node as escaping and all function
+// parameters it can reach as leaking.
+//
+// If a value's address is taken but the address does not escape,
+// then the value can stay on the stack.  If the value new(T) does
+// not escape, then new(T) can be rewritten into a stack allocation.
+// The same is true of slice literals.
+//
+// If optimizations are disabled (-N), this code is not used.
+// Instead, the compiler assumes that any value whose address
+// is taken without being immediately dereferenced
+// needs to be moved to the heap, and new(T) and slice
+// literals are always real allocations.
+
+typedef struct EscState EscState;
+
+static void escfunc(EscState*, Node *func);
+static void esclist(EscState*, NodeList *l, Node *up);
+static void esc(EscState*, Node *n, Node *up);
+static void escloopdepthlist(EscState*, NodeList *l);
+static void escloopdepth(EscState*, Node *n);
+static void escassign(EscState*, Node *dst, Node *src);
+static void esccall(EscState*, Node*, Node *up);
+static void escflows(EscState*, Node *dst, Node *src);
+static void escflood(EscState*, Node *dst);
+static void escwalk(EscState*, int level, Node *dst, Node *src);
+static void esctag(EscState*, Node *func);
+
+struct EscState {
+	// Fake node that all
+	//   - return values and output variables
+	//   - parameters on imported functions not marked 'safe'
+	//   - assignments to global variables
+	// flow to.
+	Node	theSink;
+	
+	// If an analyzed function is recorded to return
+	// pieces obtained via indirection from a parameter,
+	// and later there is a call f(x) to that function,
+	// we create a link funcParam <- x to record that fact.
+	// The funcParam node is handled specially in escflood.
+	Node	funcParam;	
+	
+	NodeList*	dsts;		// all dst nodes
+	int	loopdepth;	// for detecting nested loop scopes
+	int	pdepth;		// for debug printing in recursions.
+	int	dstcount, edgecount;	// diagnostic
+	NodeList*	noesc;	// list of possible non-escaping nodes, for printing
+	int	recursive;	// recursive function or group of mutually recursive functions.
+};
+
+static Strlit *tags[16];
+
+static Strlit*
+mktag(int mask)
+{
+	Strlit *s;
+	char buf[40];
+
+	switch(mask&EscMask) {
+	case EscNone:
+	case EscReturn:
+		break;
+	default:
+		fatal("escape mktag");
+	}
+
+	mask >>= EscBits;
+
+	if(mask < nelem(tags) && tags[mask] != nil)
+		return tags[mask];
+
+	snprint(buf, sizeof buf, "esc:0x%x", mask);
+	s = strlit(buf);
+	if(mask < nelem(tags))
+		tags[mask] = s;
+	return s;
+}
+
+static int
+parsetag(Strlit *note)
+{
+	int em;
+
+	if(note == nil)
+		return EscUnknown;
+	if(strncmp(note->s, "esc:", 4) != 0)
+		return EscUnknown;
+	em = atoi(note->s + 4);
+	if (em == 0)
+		return EscNone;
+	return EscReturn | (em << EscBits);
+}
+
+static void
+analyze(NodeList *all, int recursive)
+{
+	NodeList *l;
+	EscState es, *e;
+	
+	memset(&es, 0, sizeof es);
+	e = &es;
+	e->theSink.op = ONAME;
+	e->theSink.orig = &e->theSink;
+	e->theSink.class = PEXTERN;
+	e->theSink.sym = lookup(".sink");
+	e->theSink.escloopdepth = -1;
+	e->recursive = recursive;
+	
+	e->funcParam.op = ONAME;
+	e->funcParam.orig = &e->funcParam;
+	e->funcParam.class = PAUTO;
+	e->funcParam.sym = lookup(".param");
+	e->funcParam.escloopdepth = 10000000;
+	
+	for(l=all; l; l=l->next)
+		if(l->n->op == ODCLFUNC)
+			l->n->esc = EscFuncPlanned;
+
+	// flow-analyze functions
+	for(l=all; l; l=l->next)
+		if(l->n->op == ODCLFUNC)
+			escfunc(e, l->n);
+
+	// print("escapes: %d e->dsts, %d edges\n", e->dstcount, e->edgecount);
+
+	// visit the upstream of each dst, mark address nodes with
+	// addrescapes, mark parameters unsafe
+	for(l = e->dsts; l; l=l->next)
+		escflood(e, l->n);
+
+	// for all top level functions, tag the typenodes corresponding to the param nodes
+	for(l=all; l; l=l->next)
+		if(l->n->op == ODCLFUNC)
+			esctag(e, l->n);
+
+	if(debug['m']) {
+		for(l=e->noesc; l; l=l->next)
+			if(l->n->esc == EscNone)
+				warnl(l->n->lineno, "%S %hN does not escape",
+					(l->n->curfn && l->n->curfn->nname) ? l->n->curfn->nname->sym : S,
+					l->n);
+	}
+}
+
+
+static void
+escfunc(EscState *e, Node *func)
+{
+	Node *savefn;
+	NodeList *ll;
+	int saveld;
+
+//	print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":"");
+
+	if(func->esc != 1)
+		fatal("repeat escfunc %N", func->nname);
+	func->esc = EscFuncStarted;
+
+	saveld = e->loopdepth;
+	e->loopdepth = 1;
+	savefn = curfn;
+	curfn = func;
+
+	for(ll=curfn->dcl; ll; ll=ll->next) {
+		if(ll->n->op != ONAME)
+			continue;
+		switch (ll->n->class) {
+		case PPARAMOUT:
+			// out params are in a loopdepth between the sink and all local variables
+			ll->n->escloopdepth = 0;
+			break;
+		case PPARAM:
+			ll->n->escloopdepth = 1; 
+			if(ll->n->type && !haspointers(ll->n->type))
+				break;
+			if(curfn->nbody == nil && !curfn->noescape)
+				ll->n->esc = EscHeap;
+			else
+				ll->n->esc = EscNone;	// prime for escflood later
+			e->noesc = list(e->noesc, ll->n);
+			break;
+		}
+	}
+
+	// in a mutually recursive group we lose track of the return values
+	if(e->recursive)
+		for(ll=curfn->dcl; ll; ll=ll->next)
+			if(ll->n->op == ONAME && ll->n->class == PPARAMOUT)
+				escflows(e, &e->theSink, ll->n);
+
+	escloopdepthlist(e, curfn->nbody);
+	esclist(e, curfn->nbody, curfn);
+	curfn = savefn;
+	e->loopdepth = saveld;
+}
+
+// Mark labels that have no backjumps to them as not increasing e->loopdepth.
+// Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat
+// and set it to one of the following two.  Then in esc we'll clear it again.
+static Label looping;
+static Label nonlooping;
+
+static void
+escloopdepthlist(EscState *e, NodeList *l)
+{
+	for(; l; l=l->next)
+		escloopdepth(e, l->n);
+}
+
+static void
+escloopdepth(EscState *e, Node *n)
+{
+	if(n == N)
+		return;
+
+	escloopdepthlist(e, n->ninit);
+
+	switch(n->op) {
+	case OLABEL:
+		if(!n->left || !n->left->sym)
+			fatal("esc:label without label: %+N", n);
+		// Walk will complain about this label being already defined, but that's not until
+		// after escape analysis. in the future, maybe pull label & goto analysis out of walk and put before esc
+		// if(n->left->sym->label != nil)
+		//	fatal("escape analysis messed up analyzing label: %+N", n);
+		n->left->sym->label = &nonlooping;
+		break;
+	case OGOTO:
+		if(!n->left || !n->left->sym)
+			fatal("esc:goto without label: %+N", n);
+		// If we come past one that's uninitialized, this must be a (harmless) forward jump
+		// but if it's set to nonlooping the label must have preceded this goto.
+		if(n->left->sym->label == &nonlooping)
+			n->left->sym->label = &looping;
+		break;
+	}
+
+	escloopdepth(e, n->left);
+	escloopdepth(e, n->right);
+	escloopdepthlist(e, n->list);
+	escloopdepth(e, n->ntest);
+	escloopdepth(e, n->nincr);
+	escloopdepthlist(e, n->nbody);
+	escloopdepthlist(e, n->nelse);
+	escloopdepthlist(e, n->rlist);
+
+}
+
+static void
+esclist(EscState *e, NodeList *l, Node *up)
+{
+	for(; l; l=l->next)
+		esc(e, l->n, up);
+}
+
+static void
+esc(EscState *e, Node *n, Node *up)
+{
+	int lno;
+	NodeList *ll, *lr;
+	Node *a;
+
+	if(n == N)
+		return;
+
+	lno = setlineno(n);
+
+	// ninit logically runs at a different loopdepth than the rest of the for loop.
+	esclist(e, n->ninit, n);
+
+	if(n->op == OFOR || n->op == ORANGE)
+		e->loopdepth++;
+
+	// type switch variables have no ODCL.
+	// process type switch as declaration.
+	// must happen before processing of switch body,
+	// so before recursion.
+	if(n->op == OSWITCH && n->ntest && n->ntest->op == OTYPESW) {
+		for(ll=n->list; ll; ll=ll->next) {  // cases
+			// ll->n->nname is the variable per case
+			if(ll->n->nname)
+				ll->n->nname->escloopdepth = e->loopdepth;
+		}
+	}
+
+	esc(e, n->left, n);
+	esc(e, n->right, n);
+	esc(e, n->ntest, n);
+	esc(e, n->nincr, n);
+	esclist(e, n->nbody, n);
+	esclist(e, n->nelse, n);
+	esclist(e, n->list, n);
+	esclist(e, n->rlist, n);
+
+	if(n->op == OFOR || n->op == ORANGE)
+		e->loopdepth--;
+
+	if(debug['m'] > 1)
+		print("%L:[%d] %S esc: %N\n", lineno, e->loopdepth,
+		      (curfn && curfn->nname) ? curfn->nname->sym : S, n);
+
+	switch(n->op) {
+	case ODCL:
+		// Record loop depth at declaration.
+		if(n->left)
+			n->left->escloopdepth = e->loopdepth;
+		break;
+
+	case OLABEL:
+		if(n->left->sym->label == &nonlooping) {
+			if(debug['m'] > 1)
+				print("%L:%N non-looping label\n", lineno, n);
+		} else if(n->left->sym->label == &looping) {
+			if(debug['m'] > 1)
+				print("%L: %N looping label\n", lineno, n);
+			e->loopdepth++;
+		}
+		// See case OLABEL in escloopdepth above
+		// else if(n->left->sym->label == nil)
+		//	fatal("escape analysis missed or messed up a label: %+N", n);
+
+		n->left->sym->label = nil;
+		break;
+
+	case ORANGE:
+		// Everything but fixed array is a dereference.
+		if(isfixedarray(n->type) && n->list && n->list->next)
+			escassign(e, n->list->next->n, n->right);
+		break;
+
+	case OSWITCH:
+		if(n->ntest && n->ntest->op == OTYPESW) {
+			for(ll=n->list; ll; ll=ll->next) {  // cases
+				// ntest->right is the argument of the .(type),
+				// ll->n->nname is the variable per case
+				escassign(e, ll->n->nname, n->ntest->right);
+			}
+		}
+		break;
+
+	case OAS:
+	case OASOP:
+		escassign(e, n->left, n->right);
+		break;
+
+	case OAS2:	// x,y = a,b
+		if(count(n->list) == count(n->rlist))
+			for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next)
+				escassign(e, ll->n, lr->n);
+		break;
+
+	case OAS2RECV:		// v, ok = <-ch
+	case OAS2MAPR:		// v, ok = m[k]
+	case OAS2DOTTYPE:	// v, ok = x.(type)
+		escassign(e, n->list->n, n->rlist->n);
+		break;
+
+	case OSEND:		// ch <- x
+		escassign(e, &e->theSink, n->right);
+		break;
+
+	case ODEFER:
+		if(e->loopdepth == 1)  // top level
+			break;
+		// arguments leak out of scope
+		// TODO: leak to a dummy node instead
+		// fallthrough
+	case OPROC:
+		// go f(x) - f and x escape
+		escassign(e, &e->theSink, n->left->left);
+		escassign(e, &e->theSink, n->left->right);  // ODDDARG for call
+		for(ll=n->left->list; ll; ll=ll->next)
+			escassign(e, &e->theSink, ll->n);
+		break;
+
+	case OCALLMETH:
+	case OCALLFUNC:
+	case OCALLINTER:
+		esccall(e, n, up);
+		break;
+
+	case OAS2FUNC:	// x,y = f()
+		// esccall already done on n->rlist->n. tie it's escretval to n->list
+		lr=n->rlist->n->escretval;
+		for(ll=n->list; lr && ll; lr=lr->next, ll=ll->next)
+			escassign(e, ll->n, lr->n);
+		if(lr || ll)
+			fatal("esc oas2func");
+		break;
+
+	case ORETURN:
+		ll=n->list;
+		if(count(n->list) == 1 && curfn->type->outtuple > 1) {
+			// OAS2FUNC in disguise
+			// esccall already done on n->list->n
+			// tie n->list->n->escretval to curfn->dcl PPARAMOUT's
+			ll = n->list->n->escretval;
+		}
+
+		for(lr = curfn->dcl; lr && ll; lr=lr->next) {
+			if (lr->n->op != ONAME || lr->n->class != PPARAMOUT)
+				continue;
+			escassign(e, lr->n, ll->n);
+			ll = ll->next;
+		}
+		if (ll != nil)
+			fatal("esc return list");
+		break;
+
+	case OPANIC:
+		// Argument could leak through recover.
+		escassign(e, &e->theSink, n->left);
+		break;
+
+	case OAPPEND:
+		if(!n->isddd)
+			for(ll=n->list->next; ll; ll=ll->next)
+				escassign(e, &e->theSink, ll->n);  // lose track of assign to dereference
+		break;
+
+	case OCONV:
+	case OCONVNOP:
+	case OCONVIFACE:
+		escassign(e, n, n->left);
+		break;
+
+	case OARRAYLIT:
+		if(isslice(n->type)) {
+			n->esc = EscNone;  // until proven otherwise
+			e->noesc = list(e->noesc, n);
+			n->escloopdepth = e->loopdepth;
+			// Values make it to memory, lose track.
+			for(ll=n->list; ll; ll=ll->next)
+				escassign(e, &e->theSink, ll->n->right);
+		} else {
+			// Link values to array.
+			for(ll=n->list; ll; ll=ll->next)
+				escassign(e, n, ll->n->right);
+		}
+		break;
+
+	case OSTRUCTLIT:
+		// Link values to struct.
+		for(ll=n->list; ll; ll=ll->next)
+			escassign(e, n, ll->n->right);
+		break;
+	
+	case OPTRLIT:
+		n->esc = EscNone;  // until proven otherwise
+		e->noesc = list(e->noesc, n);
+		n->escloopdepth = e->loopdepth;
+		// Contents make it to memory, lose track.
+		escassign(e, &e->theSink, n->left);
+		break;
+	
+	case OCALLPART:
+		n->esc = EscNone; // until proven otherwise
+		e->noesc = list(e->noesc, n);
+		n->escloopdepth = e->loopdepth;
+		// Contents make it to memory, lose track.
+		escassign(e, &e->theSink, n->left);
+		break;
+
+	case OMAPLIT:
+		n->esc = EscNone;  // until proven otherwise
+		e->noesc = list(e->noesc, n);
+		n->escloopdepth = e->loopdepth;
+		// Keys and values make it to memory, lose track.
+		for(ll=n->list; ll; ll=ll->next) {
+			escassign(e, &e->theSink, ll->n->left);
+			escassign(e, &e->theSink, ll->n->right);
+		}
+		break;
+	
+	case OCLOSURE:
+		// Link addresses of captured variables to closure.
+		for(ll=n->cvars; ll; ll=ll->next) {
+			if(ll->n->op == OXXX)  // unnamed out argument; see dcl.c:/^funcargs
+				continue;
+			a = nod(OADDR, ll->n->closure, N);
+			a->lineno = ll->n->lineno;
+			a->escloopdepth = e->loopdepth;
+			typecheck(&a, Erv);
+			escassign(e, n, a);
+		}
+		// fallthrough
+	case OMAKECHAN:
+	case OMAKEMAP:
+	case OMAKESLICE:
+	case ONEW:
+		n->escloopdepth = e->loopdepth;
+		n->esc = EscNone;  // until proven otherwise
+		e->noesc = list(e->noesc, n);
+		break;
+
+	case OADDR:
+		n->esc = EscNone;  // until proven otherwise
+		e->noesc = list(e->noesc, n);
+		// current loop depth is an upper bound on actual loop depth
+		// of addressed value.
+		n->escloopdepth = e->loopdepth;
+		// for &x, use loop depth of x if known.
+		// it should always be known, but if not, be conservative
+		// and keep the current loop depth.
+		if(n->left->op == ONAME) {
+			switch(n->left->class) {
+			case PAUTO:
+				if(n->left->escloopdepth != 0)
+					n->escloopdepth = n->left->escloopdepth;
+				break;
+			case PPARAM:
+			case PPARAMOUT:
+				// PPARAM is loop depth 1 always.
+				// PPARAMOUT is loop depth 0 for writes
+				// but considered loop depth 1 for address-of,
+				// so that writing the address of one result
+				// to another (or the same) result makes the
+				// first result move to the heap.
+				n->escloopdepth = 1;
+				break;
+			}
+		}
+		break;
+	}
+
+	lineno = lno;
+}
+
+// Assert that expr somehow gets assigned to dst, if non nil.  for
+// dst==nil, any name node expr still must be marked as being
+// evaluated in curfn.	For expr==nil, dst must still be examined for
+// evaluations inside it (e.g *f(x) = y)
+static void
+escassign(EscState *e, Node *dst, Node *src)
+{
+	int lno;
+	NodeList *ll;
+
+	if(isblank(dst) || dst == N || src == N || src->op == ONONAME || src->op == OXXX)
+		return;
+
+	if(debug['m'] > 1)
+		print("%L:[%d] %S escassign: %hN(%hJ) = %hN(%hJ)\n", lineno, e->loopdepth,
+		      (curfn && curfn->nname) ? curfn->nname->sym : S, dst, dst, src, src);
+
+	setlineno(dst);
+	
+	// Analyze lhs of assignment.
+	// Replace dst with e->theSink if we can't track it.
+	switch(dst->op) {
+	default:
+		dump("dst", dst);
+		fatal("escassign: unexpected dst");
+
+	case OARRAYLIT:
+	case OCLOSURE:
+	case OCONV:
+	case OCONVIFACE:
+	case OCONVNOP:
+	case OMAPLIT:
+	case OSTRUCTLIT:
+	case OCALLPART:
+		break;
+
+	case ONAME:
+		if(dst->class == PEXTERN)
+			dst = &e->theSink;
+		break;
+	case ODOT:	      // treat "dst.x  = src" as "dst = src"
+		escassign(e, dst->left, src);
+		return;
+	case OINDEX:
+		if(isfixedarray(dst->left->type)) {
+			escassign(e, dst->left, src);
+			return;
+		}
+		dst = &e->theSink;  // lose track of dereference
+		break;
+	case OIND:
+	case ODOTPTR:
+		dst = &e->theSink;  // lose track of dereference
+		break;
+	case OINDEXMAP:
+		// lose track of key and value
+		escassign(e, &e->theSink, dst->right);
+		dst = &e->theSink;
+		break;
+	}
+
+	lno = setlineno(src);
+	e->pdepth++;
+
+	switch(src->op) {
+	case OADDR:	// dst = &x
+	case OIND:	// dst = *x
+	case ODOTPTR:	// dst = (*x).f
+	case ONAME:
+	case OPARAM:
+	case ODDDARG:
+	case OPTRLIT:
+	case OARRAYLIT:
+	case OMAPLIT:
+	case OSTRUCTLIT:
+	case OMAKECHAN:
+	case OMAKEMAP:
+	case OMAKESLICE:
+	case ONEW:
+	case OCLOSURE:
+	case OCALLPART:
+		escflows(e, dst, src);
+		break;
+
+	case OCALLMETH:
+	case OCALLFUNC:
+	case OCALLINTER:
+		// Flowing multiple returns to a single dst happens when
+		// analyzing "go f(g())": here g() flows to sink (issue 4529).
+		for(ll=src->escretval; ll; ll=ll->next)
+			escflows(e, dst, ll->n);
+		break;
+
+	case ODOT:
+		// A non-pointer escaping from a struct does not concern us.
+		if(src->type && !haspointers(src->type))
+			break;
+		// fallthrough
+	case OCONV:
+	case OCONVIFACE:
+	case OCONVNOP:
+	case ODOTMETH:	// treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC
+			// iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here
+	case ODOTTYPE:
+	case ODOTTYPE2:
+	case OSLICE:
+	case OSLICE3:
+	case OSLICEARR:
+	case OSLICE3ARR:
+		// Conversions, field access, slice all preserve the input value.
+		escassign(e, dst, src->left);
+		break;
+
+	case OAPPEND:
+		// Append returns first argument.
+		escassign(e, dst, src->list->n);
+		break;
+	
+	case OINDEX:
+		// Index of array preserves input value.
+		if(isfixedarray(src->left->type))
+			escassign(e, dst, src->left);
+		break;
+
+	case OADD:
+	case OSUB:
+	case OOR:
+	case OXOR:
+	case OMUL:
+	case ODIV:
+	case OMOD:
+	case OLSH:
+	case ORSH:
+	case OAND:
+	case OANDNOT:
+	case OPLUS:
+	case OMINUS:
+	case OCOM:
+		// Might be pointer arithmetic, in which case
+		// the operands flow into the result.
+		// TODO(rsc): Decide what the story is here.  This is unsettling.
+		escassign(e, dst, src->left);
+		escassign(e, dst, src->right);
+		break;
+	}
+
+	e->pdepth--;
+	lineno = lno;
+}
+
+static int
+escassignfromtag(EscState *e, Strlit *note, NodeList *dsts, Node *src)
+{
+	int em, em0;
+	
+	em = parsetag(note);
+
+	if(em == EscUnknown) {
+		escassign(e, &e->theSink, src);
+		return em;
+	}
+
+	if(em == EscNone)
+		return em;
+	
+	// If content inside parameter (reached via indirection)
+	// escapes back to results, mark as such.
+	if(em & EscContentEscapes)
+		escassign(e, &e->funcParam, src);
+
+	em0 = em;
+	for(em >>= EscReturnBits; em && dsts; em >>= 1, dsts=dsts->next)
+		if(em & 1)
+			escassign(e, dsts->n, src);
+
+	if (em != 0 && dsts == nil)
+		fatal("corrupt esc tag %Z or messed up escretval list\n", note);
+	return em0;
+}
+
+// This is a bit messier than fortunate, pulled out of esc's big
+// switch for clarity.	We either have the paramnodes, which may be
+// connected to other things through flows or we have the parameter type
+// nodes, which may be marked "noescape". Navigating the ast is slightly
+// different for methods vs plain functions and for imported vs
+// this-package
+static void
+esccall(EscState *e, Node *n, Node *up)
+{
+	NodeList *ll, *lr;
+	Node *a, *fn, *src;
+	Type *t, *fntype;
+	char buf[40];
+	int i;
+
+	fn = N;
+	switch(n->op) {
+	default:
+		fatal("esccall");
+
+	case OCALLFUNC:
+		fn = n->left;
+		fntype = fn->type;
+		break;
+
+	case OCALLMETH:
+		fn = n->left->right->sym->def;
+		if(fn)
+			fntype = fn->type;
+		else
+			fntype = n->left->type;
+		break;
+
+	case OCALLINTER:
+		fntype = n->left->type;
+		break;
+	}
+
+	ll = n->list;
+	if(n->list != nil && n->list->next == nil) {
+		a = n->list->n;
+		if(a->type->etype == TSTRUCT && a->type->funarg) // f(g()).
+			ll = a->escretval;
+	}
+
+	if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype && fn->defn->esc < EscFuncTagged) {
+		// function in same mutually recursive group.  Incorporate into flow graph.
+//		print("esc local fn: %N\n", fn->ntype);
+		if(fn->defn->esc == EscFuncUnknown || n->escretval != nil)
+			fatal("graph inconsistency");
+
+		// set up out list on this call node
+		for(lr=fn->ntype->rlist; lr; lr=lr->next)
+			n->escretval = list(n->escretval, lr->n->left);  // type.rlist ->  dclfield -> ONAME (PPARAMOUT)
+
+		// Receiver.
+		if(n->op != OCALLFUNC)
+			escassign(e, fn->ntype->left->left, n->left->left);
+
+		for(lr=fn->ntype->list; ll && lr; ll=ll->next, lr=lr->next) {
+			src = ll->n;
+			if(lr->n->isddd && !n->isddd) {
+				// Introduce ODDDARG node to represent ... allocation.
+				src = nod(ODDDARG, N, N);
+				src->type = typ(TARRAY);
+				src->type->type = lr->n->type->type;
+				src->type->bound = count(ll);
+				src->type = ptrto(src->type); // make pointer so it will be tracked
+				src->escloopdepth = e->loopdepth;
+				src->lineno = n->lineno;
+				src->esc = EscNone;  // until we find otherwise
+				e->noesc = list(e->noesc, src);
+				n->right = src;
+			}
+			if(lr->n->left != N)
+				escassign(e, lr->n->left, src);
+			if(src != ll->n)
+				break;
+		}
+		// "..." arguments are untracked
+		for(; ll; ll=ll->next)
+			escassign(e, &e->theSink, ll->n);
+
+		return;
+	}
+
+	// Imported or completely analyzed function.  Use the escape tags.
+	if(n->escretval != nil)
+		fatal("esc already decorated call %+N\n", n);
+
+	// set up out list on this call node with dummy auto ONAMES in the current (calling) function.
+	i = 0;
+	for(t=getoutargx(fntype)->type; t; t=t->down) {
+		src = nod(ONAME, N, N);
+		snprint(buf, sizeof buf, ".dum%d", i++);
+		src->sym = lookup(buf);
+		src->type = t->type;
+		src->class = PAUTO;
+		src->curfn = curfn;
+		src->escloopdepth = e->loopdepth;
+		src->used = 1;
+		src->lineno = n->lineno;
+		n->escretval = list(n->escretval, src); 
+	}
+
+//	print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval);
+
+	// Receiver.
+	if(n->op != OCALLFUNC) {
+		t = getthisx(fntype)->type;
+		src = n->left->left;
+		if(haspointers(t->type))
+			escassignfromtag(e, t->note, n->escretval, src);
+	}
+	
+	for(t=getinargx(fntype)->type; ll; ll=ll->next) {
+		src = ll->n;
+		if(t->isddd && !n->isddd) {
+			// Introduce ODDDARG node to represent ... allocation.
+			src = nod(ODDDARG, N, N);
+			src->escloopdepth = e->loopdepth;
+			src->lineno = n->lineno;
+			src->type = typ(TARRAY);
+			src->type->type = t->type->type;
+			src->type->bound = count(ll);
+			src->type = ptrto(src->type); // make pointer so it will be tracked
+			src->esc = EscNone;  // until we find otherwise
+			e->noesc = list(e->noesc, src);
+			n->right = src;
+		}
+		if(haspointers(t->type)) {
+			if(escassignfromtag(e, t->note, n->escretval, src) == EscNone && up->op != ODEFER && up->op != OPROC) {
+				a = src;
+				while(a->op == OCONVNOP)
+					a = a->left;
+				switch(a->op) {
+				case OCALLPART:
+				case OCLOSURE:
+				case ODDDARG:
+				case OARRAYLIT:
+				case OPTRLIT:
+				case OSTRUCTLIT:
+					// The callee has already been analyzed, so its arguments have esc tags.
+					// The argument is marked as not escaping at all.
+					// Record that fact so that any temporary used for
+					// synthesizing this expression can be reclaimed when
+					// the function returns.
+					// This 'noescape' is even stronger than the usual esc == EscNone.
+					// src->esc == EscNone means that src does not escape the current function.
+					// src->noescape = 1 here means that src does not escape this statement
+					// in the current function.
+					a->noescape = 1;
+					break;
+				}
+			}
+		}
+		if(src != ll->n)
+			break;
+		t = t->down;
+	}
+	// "..." arguments are untracked
+	for(; ll; ll=ll->next)
+		escassign(e, &e->theSink, ll->n);
+}
+
+// Store the link src->dst in dst, throwing out some quick wins.
+static void
+escflows(EscState *e, Node *dst, Node *src)
+{
+	if(dst == nil || src == nil || dst == src)
+		return;
+
+	// Don't bother building a graph for scalars.
+	if(src->type && !haspointers(src->type))
+		return;
+
+	if(debug['m']>2)
+		print("%L::flows:: %hN <- %hN\n", lineno, dst, src);
+
+	if(dst->escflowsrc == nil) {
+		e->dsts = list(e->dsts, dst);
+		e->dstcount++;
+	}
+	e->edgecount++;
+
+	dst->escflowsrc = list(dst->escflowsrc, src);
+}
+
+// Whenever we hit a reference node, the level goes up by one, and whenever
+// we hit an OADDR, the level goes down by one. as long as we're on a level > 0
+// finding an OADDR just means we're following the upstream of a dereference,
+// so this address doesn't leak (yet).
+// If level == 0, it means the /value/ of this node can reach the root of this flood.
+// so if this node is an OADDR, it's argument should be marked as escaping iff
+// it's currfn/e->loopdepth are different from the flood's root.
+// Once an object has been moved to the heap, all of it's upstream should be considered
+// escaping to the global scope.
+static void
+escflood(EscState *e, Node *dst)
+{
+	NodeList *l;
+
+	switch(dst->op) {
+	case ONAME:
+	case OCLOSURE:
+		break;
+	default:
+		return;
+	}
+
+	if(debug['m']>1)
+		print("\nescflood:%d: dst %hN scope:%S[%d]\n", walkgen, dst,
+		      (dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S,
+		      dst->escloopdepth);
+
+	for(l = dst->escflowsrc; l; l=l->next) {
+		walkgen++;
+		escwalk(e, 0, dst, l->n);
+	}
+}
+
+// There appear to be some loops in the escape graph, causing
+// arbitrary recursion into deeper and deeper levels.
+// Cut this off safely by making minLevel sticky: once you
+// get that deep, you cannot go down any further but you also
+// cannot go up any further. This is a conservative fix.
+// Making minLevel smaller (more negative) would handle more
+// complex chains of indirections followed by address-of operations,
+// at the cost of repeating the traversal once for each additional
+// allowed level when a loop is encountered. Using -2 suffices to
+// pass all the tests we have written so far, which we assume matches
+// the level of complexity we want the escape analysis code to handle.
+#define MinLevel (-2)
+/*c2go enum { MinLevel = -2 };*/
+
+static void
+escwalk(EscState *e, int level, Node *dst, Node *src)
+{
+	NodeList *ll;
+	int leaks, newlevel;
+
+	if(src->walkgen == walkgen && src->esclevel <= level)
+		return;
+	src->walkgen = walkgen;
+	src->esclevel = level;
+
+	if(debug['m']>1)
+		print("escwalk: level:%d depth:%d %.*s %hN(%hJ) scope:%S[%d]\n",
+		      level, e->pdepth, e->pdepth, "\t\t\t\t\t\t\t\t\t\t", src, src,
+		      (src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->escloopdepth);
+
+	e->pdepth++;
+
+	// Input parameter flowing to output parameter?
+	if(dst->op == ONAME && dst->class == PPARAMOUT && dst->vargen <= 20) {
+		if(src->op == ONAME && src->class == PPARAM && src->curfn == dst->curfn && src->esc != EscScope && src->esc != EscHeap) {
+			if(level == 0) {
+				if(debug['m'])
+					warnl(src->lineno, "leaking param: %hN to result %S", src, dst->sym);
+				if((src->esc&EscMask) != EscReturn)
+					src->esc = EscReturn;
+				src->esc |= 1<<((dst->vargen-1) + EscReturnBits);
+				goto recurse;
+			} else if(level > 0) {
+				if(debug['m'])
+					warnl(src->lineno, "%N leaking param %hN content to result %S", src->curfn->nname, src, dst->sym);
+				if((src->esc&EscMask) != EscReturn)
+					src->esc = EscReturn;
+				src->esc |= EscContentEscapes;
+				goto recurse;
+			}
+		}
+	}
+
+	// The second clause is for values pointed at by an object passed to a call
+	// that returns something reached via indirect from the object.
+	// We don't know which result it is or how many indirects, so we treat it as leaking.
+	leaks = level <= 0 && dst->escloopdepth < src->escloopdepth ||
+		level < 0 && dst == &e->funcParam && haspointers(src->type);
+
+	switch(src->op) {
+	case ONAME:
+		if(src->class == PPARAM && (leaks || dst->escloopdepth < 0) && src->esc != EscHeap) {
+			src->esc = EscScope;
+			if(debug['m'])
+				warnl(src->lineno, "leaking param: %hN", src);
+		}
+
+		// Treat a PPARAMREF closure variable as equivalent to the
+		// original variable.
+		if(src->class == PPARAMREF) {
+			if(leaks && debug['m'])
+				warnl(src->lineno, "leaking closure reference %hN", src);
+			escwalk(e, level, dst, src->closure);
+		}
+		break;
+
+	case OPTRLIT:
+	case OADDR:
+		if(leaks) {
+			src->esc = EscHeap;
+			addrescapes(src->left);
+			if(debug['m'])
+				warnl(src->lineno, "%hN escapes to heap", src);
+		}
+		newlevel = level;
+		if(level > MinLevel)
+			newlevel--;
+		escwalk(e, newlevel, dst, src->left);
+		break;
+
+	case OARRAYLIT:
+		if(isfixedarray(src->type))
+			break;
+		// fall through
+	case ODDDARG:
+	case OMAKECHAN:
+	case OMAKEMAP:
+	case OMAKESLICE:
+	case OMAPLIT:
+	case ONEW:
+	case OCLOSURE:
+	case OCALLPART:
+		if(leaks) {
+			src->esc = EscHeap;
+			if(debug['m'])
+				warnl(src->lineno, "%hN escapes to heap", src);
+		}
+		break;
+
+	case ODOT:
+	case OSLICE:
+	case OSLICEARR:
+	case OSLICE3:
+	case OSLICE3ARR:
+		escwalk(e, level, dst, src->left);
+		break;
+
+	case OINDEX:
+		if(isfixedarray(src->left->type)) {
+			escwalk(e, level, dst, src->left);
+			break;
+		}
+		// fall through
+	case ODOTPTR:
+	case OINDEXMAP:
+	case OIND:
+		newlevel = level;
+		if(level > MinLevel)
+			newlevel++;
+		escwalk(e, newlevel, dst, src->left);
+	}
+
+recurse:
+	for(ll=src->escflowsrc; ll; ll=ll->next)
+		escwalk(e, level, dst, ll->n);
+
+	e->pdepth--;
+}
+
+static void
+esctag(EscState *e, Node *func)
+{
+	Node *savefn;
+	NodeList *ll;
+	Type *t;
+
+	USED(e);
+	func->esc = EscFuncTagged;
+	
+	// External functions are assumed unsafe,
+	// unless //go:noescape is given before the declaration.
+	if(func->nbody == nil) {
+		if(func->noescape) {
+			for(t=getinargx(func->type)->type; t; t=t->down)
+				if(haspointers(t->type))
+					t->note = mktag(EscNone);
+		}
+		return;
+	}
+
+	savefn = curfn;
+	curfn = func;
+
+	for(ll=curfn->dcl; ll; ll=ll->next) {
+		if(ll->n->op != ONAME || ll->n->class != PPARAM)
+			continue;
+
+		switch (ll->n->esc&EscMask) {
+		case EscNone:	// not touched by escflood
+		case EscReturn:	
+			if(haspointers(ll->n->type)) // don't bother tagging for scalars
+				ll->n->paramfld->note = mktag(ll->n->esc);
+			break;
+		case EscHeap:	// touched by escflood, moved to heap
+		case EscScope:	// touched by escflood, value leaves scope
+			break;
+		}
+	}
+
+	curfn = savefn;
+}
diff --git a/src/cmd/gc/export.c b/src/cmd/gc/export.c
new file mode 100644
index 0000000..da5984c
--- /dev/null
+++ b/src/cmd/gc/export.c
@@ -0,0 +1,521 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include	<u.h>
+#include	<libc.h>
+#include	"go.h"
+#include	"y.tab.h"
+
+static void	dumpexporttype(Type *t);
+
+// Mark n's symbol as exported
+void
+exportsym(Node *n)
+{
+	if(n == N || n->sym == S)
+		return;
+	if(n->sym->flags & (SymExport|SymPackage)) {
+		if(n->sym->flags & SymPackage)
+			yyerror("export/package mismatch: %S", n->sym);
+		return;
+	}
+	n->sym->flags |= SymExport;
+
+	if(debug['E'])
+		print("export symbol %S\n", n->sym);
+	exportlist = list(exportlist, n);
+}
+
+int
+exportname(char *s)
+{
+	Rune r;
+
+	if((uchar)s[0] < Runeself)
+		return 'A' <= s[0] && s[0] <= 'Z';
+	chartorune(&r, s);
+	return isupperrune(r);
+}
+
+static int
+initname(char *s)
+{
+	return strcmp(s, "init") == 0;
+}
+
+// exportedsym reports whether a symbol will be visible
+// to files that import our package.
+static int
+exportedsym(Sym *sym)
+{
+	// Builtins are visible everywhere.
+	if(sym->pkg == builtinpkg || sym->origpkg == builtinpkg)
+		return 1;
+
+	return sym->pkg == localpkg && exportname(sym->name);
+}
+
+void
+autoexport(Node *n, int ctxt)
+{
+	if(n == N || n->sym == S)
+		return;
+	if((ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN)
+		return;
+	if(n->ntype && n->ntype->op == OTFUNC && n->ntype->left)	// method
+		return;
+	// -A is for cmd/gc/mkbuiltin script, so export everything
+	if(debug['A'] || exportname(n->sym->name) || initname(n->sym->name))
+		exportsym(n);
+}
+
+static void
+dumppkg(Pkg *p)
+{
+	char *suffix;
+
+	if(p == nil || p == localpkg || p->exported || p == builtinpkg)
+		return;
+	p->exported = 1;
+	suffix = "";
+	if(!p->direct)
+		suffix = " // indirect";
+	Bprint(bout, "\timport %s \"%Z\"%s\n", p->name, p->path, suffix);
+}
+
+// Look for anything we need for the inline body
+static void reexportdep(Node *n);
+static void
+reexportdeplist(NodeList *ll)
+{
+	for(; ll ;ll=ll->next)
+		reexportdep(ll->n);
+}
+
+static void
+reexportdep(Node *n)
+{
+	Type *t;
+
+	if(!n)
+		return;
+
+	//print("reexportdep %+hN\n", n);
+	switch(n->op) {
+	case ONAME:
+		switch(n->class&~PHEAP) {
+		case PFUNC:
+			// methods will be printed along with their type
+			// nodes for T.Method expressions
+			if(n->left && n->left->op == OTYPE)
+				break;
+			// nodes for method calls.
+			if(!n->type || n->type->thistuple > 0)
+				break;
+			// fallthrough
+		case PEXTERN:
+			if(n->sym && !exportedsym(n->sym)) {
+				if(debug['E'])
+					print("reexport name %S\n", n->sym);
+				exportlist = list(exportlist, n);
+			}
+		}
+		break;
+
+	case ODCL:
+		// Local variables in the bodies need their type.
+		t = n->left->type;
+		if(t != types[t->etype] && t != idealbool && t != idealstring) {
+			if(isptr[t->etype])
+				t = t->type;
+			if(t && t->sym && t->sym->def && !exportedsym(t->sym)) {
+				if(debug['E'])
+					print("reexport type %S from declaration\n", t->sym);
+				exportlist = list(exportlist, t->sym->def);
+			}
+		}
+		break;
+
+	case OLITERAL:
+		t = n->type;
+		if(t != types[n->type->etype] && t != idealbool && t != idealstring) {
+			if(isptr[t->etype])
+				t = t->type;
+			if(t && t->sym && t->sym->def && !exportedsym(t->sym)) {
+				if(debug['E'])
+					print("reexport literal type %S\n", t->sym);
+				exportlist = list(exportlist, t->sym->def);
+			}
+		}
+		// fallthrough
+	case OTYPE:
+		if(n->sym && !exportedsym(n->sym)) {
+			if(debug['E'])
+				print("reexport literal/type %S\n", n->sym);
+			exportlist = list(exportlist, n);
+		}
+		break;
+
+	// for operations that need a type when rendered, put the type on the export list.
+	case OCONV:
+	case OCONVIFACE:
+	case OCONVNOP:
+	case ORUNESTR:
+	case OARRAYBYTESTR:
+	case OARRAYRUNESTR:
+	case OSTRARRAYBYTE:
+	case OSTRARRAYRUNE:
+	case ODOTTYPE:
+	case ODOTTYPE2:
+	case OSTRUCTLIT:
+	case OARRAYLIT:
+	case OPTRLIT:
+	case OMAKEMAP:
+	case OMAKESLICE:
+	case OMAKECHAN:
+		t = n->type;
+		if(!t->sym && t->type)
+			t = t->type;
+		if(t && t->sym && t->sym->def && !exportedsym(t->sym)) {
+			if(debug['E'])
+				print("reexport type for expression %S\n", t->sym);
+			exportlist = list(exportlist, t->sym->def);
+		}
+		break;
+	}
+
+	reexportdep(n->left);
+	reexportdep(n->right);
+	reexportdeplist(n->list);
+	reexportdeplist(n->rlist);
+	reexportdeplist(n->ninit);
+	reexportdep(n->ntest);
+	reexportdep(n->nincr);
+	reexportdeplist(n->nbody);
+	reexportdeplist(n->nelse);
+}
+
+
+static void
+dumpexportconst(Sym *s)
+{
+	Node *n;
+	Type *t;
+
+	n = s->def;
+	typecheck(&n, Erv);
+	if(n == N || n->op != OLITERAL)
+		fatal("dumpexportconst: oconst nil: %S", s);
+
+	t = n->type;	// may or may not be specified
+	dumpexporttype(t);
+
+	if(t != T && !isideal(t))
+		Bprint(bout, "\tconst %#S %#T = %#V\n", s, t, &n->val);
+	else
+		Bprint(bout, "\tconst %#S = %#V\n", s, &n->val);
+}
+
+static void
+dumpexportvar(Sym *s)
+{
+	Node *n;
+	Type *t;
+
+	n = s->def;
+	typecheck(&n, Erv|Ecall);
+	if(n == N || n->type == T) {
+		yyerror("variable exported but not defined: %S", s);
+		return;
+	}
+
+	t = n->type;
+	dumpexporttype(t);
+
+	if(t->etype == TFUNC && n->class == PFUNC) {
+		if (n->inl) {
+			// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
+			// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
+			if(debug['l'] < 2)
+				typecheckinl(n);
+			// NOTE: The space after %#S here is necessary for ld's export data parser.
+			Bprint(bout, "\tfunc %#S %#hT { %#H }\n", s, t, n->inl);
+			reexportdeplist(n->inl);
+		} else
+			Bprint(bout, "\tfunc %#S %#hT\n", s, t);
+	} else
+		Bprint(bout, "\tvar %#S %#T\n", s, t);
+}
+
+static int
+methcmp(const void *va, const void *vb)
+{
+	Type *a, *b;
+	
+	a = *(Type**)va;
+	b = *(Type**)vb;
+	return strcmp(a->sym->name, b->sym->name);
+}
+
+static void
+dumpexporttype(Type *t)
+{
+	Type *f;
+	Type **m;
+	int i, n;
+
+	if(t == T)
+		return;
+	if(t->printed || t == types[t->etype] || t == bytetype || t == runetype || t == errortype)
+		return;
+	t->printed = 1;
+
+	if(t->sym != S && t->etype != TFIELD)
+		dumppkg(t->sym->pkg);
+
+	dumpexporttype(t->type);
+	dumpexporttype(t->down);
+
+	if (t->sym == S || t->etype == TFIELD)
+		return;
+
+	n = 0;
+	for(f=t->method; f!=T; f=f->down) {	
+		dumpexporttype(f);
+		n++;
+	}
+
+	m = mal(n*sizeof m[0]);
+	i = 0;
+	for(f=t->method; f!=T; f=f->down)
+		m[i++] = f;
+	qsort(m, n, sizeof m[0], methcmp);
+
+	Bprint(bout, "\ttype %#S %#lT\n", t->sym, t);
+	for(i=0; i<n; i++) {
+		f = m[i];
+		if(f->nointerface)
+			Bprint(bout, "\t//go:nointerface\n");
+		if (f->type->nname && f->type->nname->inl) { // nname was set by caninl
+			// when lazily typechecking inlined bodies, some re-exported ones may not have been typechecked yet.
+			// currently that can leave unresolved ONONAMEs in import-dot-ed packages in the wrong package
+			if(debug['l'] < 2)
+				typecheckinl(f->type->nname);
+			Bprint(bout, "\tfunc (%#T) %#hhS %#hT { %#H }\n", getthisx(f->type)->type, f->sym, f->type, f->type->nname->inl);
+			reexportdeplist(f->type->nname->inl);
+		} else
+			Bprint(bout, "\tfunc (%#T) %#hhS %#hT\n", getthisx(f->type)->type, f->sym, f->type);
+	}
+}
+
+static void
+dumpsym(Sym *s)
+{
+	if(s->flags & SymExported)
+		return;
+	s->flags |= SymExported;
+
+	if(s->def == N) {
+		yyerror("unknown export symbol: %S", s);
+		return;
+	}
+//	print("dumpsym %O %+S\n", s->def->op, s);
+	dumppkg(s->pkg);
+
+	switch(s->def->op) {
+	default:
+		yyerror("unexpected export symbol: %O %S", s->def->op, s);
+		break;
+
+	case OLITERAL:
+		dumpexportconst(s);
+		break;
+
+	case OTYPE:
+		if(s->def->type->etype == TFORW)
+			yyerror("export of incomplete type %S", s);
+		else
+			dumpexporttype(s->def->type);
+		break;
+
+	case ONAME:
+		dumpexportvar(s);
+		break;
+	}
+}
+
+void
+dumpexport(void)
+{
+	NodeList *l;
+	int32 i, lno;
+	Pkg *p;
+
+	lno = lineno;
+
+	Bprint(bout, "\n$$\npackage %s", localpkg->name);
+	if(safemode)
+		Bprint(bout, " safe");
+	Bprint(bout, "\n");
+
+	for(i=0; i<nelem(phash); i++)
+		for(p=phash[i]; p; p=p->link)
+			if(p->direct)
+				dumppkg(p);
+
+	for(l=exportlist; l; l=l->next) {
+		lineno = l->n->lineno;
+		dumpsym(l->n->sym);
+	}
+
+	Bprint(bout, "\n$$\n");
+	lineno = lno;
+}
+
+/*
+ * import
+ */
+
+/*
+ * return the sym for ss, which should match lexical
+ */
+Sym*
+importsym(Sym *s, int op)
+{
+	char *pkgstr;
+
+	if(s->def != N && s->def->op != op) {
+		pkgstr = smprint("during import \"%Z\"", importpkg->path);
+		redeclare(s, pkgstr);
+	}
+
+	// mark the symbol so it is not reexported
+	if(s->def == N) {
+		if(exportname(s->name) || initname(s->name))
+			s->flags |= SymExport;
+		else
+			s->flags |= SymPackage;	// package scope
+	}
+	return s;
+}
+
+/*
+ * return the type pkg.name, forward declaring if needed
+ */
+Type*
+pkgtype(Sym *s)
+{
+	Type *t;
+
+	importsym(s, OTYPE);
+	if(s->def == N || s->def->op != OTYPE) {
+		t = typ(TFORW);
+		t->sym = s;
+		s->def = typenod(t);
+	}
+	if(s->def->type == T)
+		yyerror("pkgtype %S", s);
+	return s->def->type;
+}
+
+void
+importimport(Sym *s, Strlit *z)
+{
+	// Informational: record package name
+	// associated with import path, for use in
+	// human-readable messages.
+	Pkg *p;
+
+	if(isbadimport(z))
+		errorexit();
+	p = mkpkg(z);
+	if(p->name == nil) {
+		p->name = s->name;
+		pkglookup(s->name, nil)->npkg++;
+	} else if(strcmp(p->name, s->name) != 0)
+		yyerror("conflicting names %s and %s for package \"%Z\"", p->name, s->name, p->path);
+	
+	if(!incannedimport && myimportpath != nil && strcmp(z->s, myimportpath) == 0) {
+		yyerror("import \"%Z\": package depends on \"%Z\" (import cycle)", importpkg->path, z);
+		errorexit();
+	}
+}
+
+void
+importconst(Sym *s, Type *t, Node *n)
+{
+	Node *n1;
+
+	importsym(s, OLITERAL);
+	convlit(&n, t);
+
+	if(s->def != N)	 // TODO: check if already the same.
+		return;
+
+	if(n->op != OLITERAL) {
+		yyerror("expression must be a constant");
+		return;
+	}
+
+	if(n->sym != S) {
+		n1 = nod(OXXX, N, N);
+		*n1 = *n;
+		n = n1;
+	}
+	n->orig = newname(s);
+	n->sym = s;
+	declare(n, PEXTERN);
+
+	if(debug['E'])
+		print("import const %S\n", s);
+}
+
+void
+importvar(Sym *s, Type *t)
+{
+	Node *n;
+
+	importsym(s, ONAME);
+	if(s->def != N && s->def->op == ONAME) {
+		if(eqtype(t, s->def->type))
+			return;
+		yyerror("inconsistent definition for var %S during import\n\t%T (in \"%Z\")\n\t%T (in \"%Z\")", s, s->def->type, s->importdef->path, t, importpkg->path);
+	}
+	n = newname(s);
+	s->importdef = importpkg;
+	n->type = t;
+	declare(n, PEXTERN);
+
+	if(debug['E'])
+		print("import var %S %lT\n", s, t);
+}
+
+void
+importtype(Type *pt, Type *t)
+{
+	Node *n;
+
+	// override declaration in unsafe.go for Pointer.
+	// there is no way in Go code to define unsafe.Pointer
+	// so we have to supply it.
+	if(incannedimport &&
+	   strcmp(importpkg->name, "unsafe") == 0 &&
+	   strcmp(pt->nod->sym->name, "Pointer") == 0) {
+		t = types[TUNSAFEPTR];
+	}
+
+	if(pt->etype == TFORW) {
+		n = pt->nod;
+		copytype(pt->nod, t);
+		pt->nod = n;		// unzero nod
+		pt->sym->importdef = importpkg;
+		pt->sym->lastlineno = parserline();
+		declare(n, PEXTERN);
+		checkwidth(pt);
+	} else if(!eqtype(pt->orig, t))
+		yyerror("inconsistent definition for type %S during import\n\t%lT (in \"%Z\")\n\t%lT (in \"%Z\")", pt->sym, pt, pt->sym->importdef->path, t, importpkg->path);
+
+	if(debug['E'])
+		print("import type %T %lT\n", pt, t);
+}
diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c
new file mode 100644
index 0000000..89d2a14
--- /dev/null
+++ b/src/cmd/gc/fmt.c
@@ -0,0 +1,1691 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include	<u.h>
+#include	<libc.h>
+#include	"go.h"
+#include	"opnames.h"
+
+//
+// Format conversions
+//	%L int		Line numbers
+//
+//	%E int		etype values (aka 'Kind')
+//
+//	%O int		Node Opcodes
+//		Flags: "%#O": print go syntax. (automatic unless fmtmode == FDbg)
+//
+//	%J Node*	Node details
+//		Flags: "%hJ" suppresses things not relevant until walk.
+//
+//	%V Val*		Constant values
+//
+//	%S Sym*		Symbols
+//		Flags: +,- #: mode (see below)
+//			"%hS"	unqualified identifier in any mode
+//			"%hhS"  in export mode: unqualified identifier if exported, qualified if not
+//
+//	%T Type*	Types
+//		Flags: +,- #: mode (see below)
+//			'l' definition instead of name.
+//			'h' omit "func" and receiver in function types
+//			'u' (only in -/Sym mode) print type identifiers wit package name instead of prefix.
+//
+//	%N Node*	Nodes
+//		Flags: +,- #: mode (see below)
+//			'h' (only in +/debug mode) suppress recursion
+//			'l' (only in Error mode) print "foo (type Bar)"
+//
+//	%H NodeList*	NodeLists
+//		Flags: those of %N
+//			','  separate items with ',' instead of ';'
+//
+//	%Z Strlit*	String literals
+//
+//   In mparith1.c:
+//      %B Mpint*	Big integers
+//	%F Mpflt*	Big floats
+//
+//   %S, %T and %N obey use the following flags to set the format mode:
+enum {
+	FErr,	//     error mode (default)
+	FDbg,	//     "%+N" debug mode
+	FExp,	//     "%#N" export mode
+	FTypeId,  //   "%-N" turning-types-into-symbols-mode: identical types give identical strings
+};
+static int fmtmode;
+static int fmtpkgpfx;	// %uT stickyness
+//
+// E.g. for %S:	%+S %#S %-S	print an identifier properly qualified for debug/export/internal mode.
+//
+// The mode flags  +, - and # are sticky, meaning they persist through
+// recursions of %N, %T and %S, but not the h and l flags.  The u flag is
+// sticky only on %T recursions and only used in %-/Sym mode.
+
+//
+// Useful format combinations:
+//
+//	%+N   %+H	multiline recursive debug dump of node/nodelist
+//	%+hN  %+hH	non recursive debug dump
+//
+//	%#N   %#T	export format
+//	%#lT		type definition instead of name
+//	%#hT		omit"func" and receiver in function signature
+//
+//	%lN		"foo (type Bar)" for error messages
+//
+//	%-T		type identifiers
+//	%-hT		type identifiers without "func" and arg names in type signatures (methodsym)
+//	%-uT		type identifiers with package name instead of prefix (typesym, dcommontype, typehash)
+//
+
+
+static int
+setfmode(unsigned long *flags)
+{
+	int fm;
+
+	fm = fmtmode;
+	if(*flags & FmtSign)
+		fmtmode = FDbg;
+	else if(*flags & FmtSharp)
+		fmtmode = FExp;
+	else if(*flags & FmtLeft)
+		fmtmode = FTypeId;
+
+	*flags &= ~(FmtSharp|FmtLeft|FmtSign);
+	return fm;
+}
+
+// Fmt "%L": Linenumbers
+static int
+Lconv(Fmt *fp)
+{
+	return linklinefmt(ctxt, fp);
+}
+
+static char*
+goopnames[] =
+{
+	[OADDR]		= "&",
+	[OADD]		= "+",
+	[OADDSTR]	= "+",
+	[OANDAND]	= "&&",
+	[OANDNOT]	= "&^",
+	[OAND]		= "&",
+	[OAPPEND]	= "append",
+	[OAS]		= "=",
+	[OAS2]		= "=",
+	[OBREAK]	= "break",
+	[OCALL]		= "function call",	// not actual syntax
+	[OCAP]		= "cap",
+	[OCASE]		= "case",
+	[OCLOSE]	= "close",
+	[OCOMPLEX]	= "complex",
+	[OCOM]		= "^",
+	[OCONTINUE]	= "continue",
+	[OCOPY]		= "copy",
+	[ODEC]		= "--",
+	[ODELETE]	= "delete",
+	[ODEFER]	= "defer",
+	[ODIV]		= "/",
+	[OEQ]		= "==",
+	[OFALL]		= "fallthrough",
+	[OFOR]		= "for",
+	[OGE]		= ">=",
+	[OGOTO]		= "goto",
+	[OGT]		= ">",
+	[OIF]		= "if",
+	[OIMAG]		= "imag",
+	[OINC]		= "++",
+	[OIND]		= "*",
+	[OLEN]		= "len",
+	[OLE]		= "<=",
+	[OLSH]		= "<<",
+	[OLT]		= "<",
+	[OMAKE]		= "make",
+	[OMINUS]	= "-",
+	[OMOD]		= "%",
+	[OMUL]		= "*",
+	[ONEW]		= "new",
+	[ONE]		= "!=",
+	[ONOT]		= "!",
+	[OOROR]		= "||",
+	[OOR]		= "|",
+	[OPANIC]	= "panic",
+	[OPLUS]		= "+",
+	[OPRINTN]	= "println",
+	[OPRINT]	= "print",
+	[ORANGE]	= "range",
+	[OREAL]		= "real",
+	[ORECV]		= "<-",
+	[ORECOVER]	= "recover",
+	[ORETURN]	= "return",
+	[ORSH]		= ">>",
+	[OSELECT]	= "select",
+	[OSEND]		= "<-",
+	[OSUB]		= "-",
+	[OSWITCH]	= "switch",
+	[OXOR]		= "^",
+};
+
+// Fmt "%O":  Node opcodes
+static int
+Oconv(Fmt *fp)
+{
+	int o;
+
+	o = va_arg(fp->args, int);
+	if((fp->flags & FmtSharp) || fmtmode != FDbg)
+		if(o >= 0 && o < nelem(goopnames) && goopnames[o] != nil)
+			return fmtstrcpy(fp, goopnames[o]);
+
+	if(o >= 0 && o < nelem(opnames) && opnames[o] != nil)
+		return fmtstrcpy(fp, opnames[o]);
+
+	return fmtprint(fp, "O-%d", o);
+}
+
+static const char* classnames[] = {
+	"Pxxx",
+	"PEXTERN",
+	"PAUTO",
+	"PPARAM",
+	"PPARAMOUT",
+	"PPARAMREF",
+	"PFUNC",
+};
+
+// Fmt "%J": Node details.
+static int
+Jconv(Fmt *fp)
+{
+	Node *n;
+	char *s;
+	int c;
+
+	n = va_arg(fp->args, Node*);
+
+	c = fp->flags&FmtShort;
+
+	if(!c && n->ullman != 0)
+		fmtprint(fp, " u(%d)", n->ullman);
+
+	if(!c && n->addable != 0)
+		fmtprint(fp, " a(%d)", n->addable);
+
+	if(!c && n->vargen != 0)
+		fmtprint(fp, " g(%d)", n->vargen);
+
+	if(n->lineno != 0)
+		fmtprint(fp, " l(%d)", n->lineno);
+
+	if(!c && n->xoffset != BADWIDTH)
+		fmtprint(fp, " x(%lld%+lld)", n->xoffset, n->stkdelta);
+
+	if(n->class != 0) {
+		s = "";
+		if(n->class & PHEAP) s = ",heap";
+		if((n->class & ~PHEAP) < nelem(classnames))
+			fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s);
+		else
+			fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s);
+	}
+
+	if(n->colas != 0)
+		fmtprint(fp, " colas(%d)", n->colas);
+
+	if(n->funcdepth != 0)
+		fmtprint(fp, " f(%d)", n->funcdepth);
+
+	switch(n->esc) {
+	case EscUnknown:
+		break;
+	case EscHeap:
+		fmtprint(fp, " esc(h)");
+		break;
+	case EscScope:
+		fmtprint(fp, " esc(s)");
+		break;
+	case EscNone:
+		fmtprint(fp, " esc(no)");
+		break;
+	case EscNever:
+		if(!c)
+			fmtprint(fp, " esc(N)");
+		break;
+	default:
+		fmtprint(fp, " esc(%d)", n->esc);
+		break;
+	}
+
+	if(n->escloopdepth)
+		fmtprint(fp, " ld(%d)", n->escloopdepth);
+
+	if(!c && n->typecheck != 0)
+		fmtprint(fp, " tc(%d)", n->typecheck);
+
+	if(!c && n->dodata != 0)
+		fmtprint(fp, " dd(%d)", n->dodata);
+
+	if(n->isddd != 0)
+		fmtprint(fp, " isddd(%d)", n->isddd);
+
+	if(n->implicit != 0)
+		fmtprint(fp, " implicit(%d)", n->implicit);
+
+	if(n->embedded != 0)
+		fmtprint(fp, " embedded(%d)", n->embedded);
+
+	if(!c && n->used != 0)
+		fmtprint(fp, " used(%d)", n->used);
+	return 0;
+}
+
+// Fmt "%V": Values
+static int
+Vconv(Fmt *fp)
+{
+	Val *v;
+	vlong x;
+
+	v = va_arg(fp->args, Val*);
+
+	switch(v->ctype) {
+	case CTINT:
+		if((fp->flags & FmtSharp) || fmtmode == FExp)
+			return fmtprint(fp, "%#B", v->u.xval);
+		return fmtprint(fp, "%B", v->u.xval);
+	case CTRUNE:
+		x = mpgetfix(v->u.xval);
+		if(' ' <= x && x < 0x80 && x != '\\' && x != '\'')
+			return fmtprint(fp, "'%c'", (int)x);
+		if(0 <= x && x < (1<<16))
+			return fmtprint(fp, "'\\u%04ux'", (int)x);
+		if(0 <= x && x <= Runemax)
+			return fmtprint(fp, "'\\U%08llux'", x);
+		return fmtprint(fp, "('\\x00' + %B)", v->u.xval);
+	case CTFLT:
+		if((fp->flags & FmtSharp) || fmtmode == FExp)
+			return fmtprint(fp, "%F", v->u.fval);
+		return fmtprint(fp, "%#F", v->u.fval);
+	case CTCPLX:
+		if((fp->flags & FmtSharp) || fmtmode == FExp)
+			return fmtprint(fp, "(%F+%Fi)", &v->u.cval->real, &v->u.cval->imag);
+		if(mpcmpfltc(&v->u.cval->real, 0) == 0)
+			return fmtprint(fp, "%#Fi", &v->u.cval->imag);
+		if(mpcmpfltc(&v->u.cval->imag, 0) == 0)
+			return fmtprint(fp, "%#F", &v->u.cval->real);
+		if(mpcmpfltc(&v->u.cval->imag, 0) < 0)
+			return fmtprint(fp, "(%#F%#Fi)", &v->u.cval->real, &v->u.cval->imag);
+		return fmtprint(fp, "(%#F+%#Fi)", &v->u.cval->real, &v->u.cval->imag);
+	case CTSTR:
+		return fmtprint(fp, "\"%Z\"", v->u.sval);
+	case CTBOOL:
+		if( v->u.bval)
+			return fmtstrcpy(fp, "true");
+		return fmtstrcpy(fp, "false");
+	case CTNIL:
+		return fmtstrcpy(fp, "nil");
+	}
+	return fmtprint(fp, "<ctype=%d>", v->ctype);
+}
+
+// Fmt "%Z": escaped string literals
+static int
+Zconv(Fmt *fp)
+{
+	Rune r;
+	Strlit *sp;
+	char *s, *se;
+	int n;
+
+	sp = va_arg(fp->args, Strlit*);
+	if(sp == nil)
+		return fmtstrcpy(fp, "<nil>");
+
+	s = sp->s;
+	se = s + sp->len;
+
+	// NOTE: Keep in sync with ../ld/go.c:/^Zconv.
+	while(s < se) {
+		n = chartorune(&r, s);
+		s += n;
+		switch(r) {
+		case Runeerror:
+			if(n == 1) {
+				fmtprint(fp, "\\x%02x", (uchar)*(s-1));
+				break;
+			}
+			// fall through
+		default:
+			if(r < ' ') {
+				fmtprint(fp, "\\x%02x", r);
+				break;
+			}
+			fmtrune(fp, r);
+			break;
+		case '\t':
+			fmtstrcpy(fp, "\\t");
+			break;
+		case '\n':
+			fmtstrcpy(fp, "\\n");
+			break;
+		case '\"':
+		case '\\':
+			fmtrune(fp, '\\');
+			fmtrune(fp, r);
+			break;
+		case 0xFEFF: // BOM, basically disallowed in source code
+			fmtstrcpy(fp, "\\uFEFF");
+			break;
+		}
+	}
+	return 0;
+}
+
+/*
+s%,%,\n%g
+s%\n+%\n%g
+s%^[	]*T%%g
+s%,.*%%g
+s%.+%	[T&]		= "&",%g
+s%^	........*\]%&~%g
+s%~	%%g
+*/
+
+static char*
+etnames[] =
+{
+	[TINT]		= "INT",
+	[TUINT]		= "UINT",
+	[TINT8]		= "INT8",
+	[TUINT8]	= "UINT8",
+	[TINT16]	= "INT16",
+	[TUINT16]	= "UINT16",
+	[TINT32]	= "INT32",
+	[TUINT32]	= "UINT32",
+	[TINT64]	= "INT64",
+	[TUINT64]	= "UINT64",
+	[TUINTPTR]	= "UINTPTR",
+	[TFLOAT32]	= "FLOAT32",
+	[TFLOAT64]	= "FLOAT64",
+	[TCOMPLEX64]	= "COMPLEX64",
+	[TCOMPLEX128]	= "COMPLEX128",
+	[TBOOL]		= "BOOL",
+	[TPTR32]	= "PTR32",
+	[TPTR64]	= "PTR64",
+	[TFUNC]		= "FUNC",
+	[TARRAY]	= "ARRAY",
+	[TSTRUCT]	= "STRUCT",
+	[TCHAN]		= "CHAN",
+	[TMAP]		= "MAP",
+	[TINTER]	= "INTER",
+	[TFORW]		= "FORW",
+	[TFIELD]	= "FIELD",
+	[TSTRING]	= "STRING",
+	[TANY]		= "ANY",
+};
+
+// Fmt "%E": etype
+static int
+Econv(Fmt *fp)
+{
+	int et;
+
+	et = va_arg(fp->args, int);
+	if(et >= 0 && et < nelem(etnames) && etnames[et] != nil)
+		return fmtstrcpy(fp, etnames[et]);
+	return fmtprint(fp, "E-%d", et);
+}
+
+// Fmt "%S": syms
+static int
+symfmt(Fmt *fp, Sym *s)
+{
+	char *p;
+
+	if(s->pkg && !(fp->flags&FmtShort)) {
+		switch(fmtmode) {
+		case FErr:	// This is for the user
+			if(s->pkg == localpkg)
+				return fmtstrcpy(fp, s->name);
+			// If the name was used by multiple packages, display the full path,
+			if(s->pkg->name && pkglookup(s->pkg->name, nil)->npkg > 1)
+				return fmtprint(fp, "\"%Z\".%s", s->pkg->path, s->name);
+			return fmtprint(fp, "%s.%s", s->pkg->name, s->name);
+		case FDbg:
+			return fmtprint(fp, "%s.%s", s->pkg->name, s->name);
+		case FTypeId:
+			if(fp->flags&FmtUnsigned)
+				return fmtprint(fp, "%s.%s", s->pkg->name, s->name);	// dcommontype, typehash
+			return fmtprint(fp, "%s.%s", s->pkg->prefix, s->name);	// (methodsym), typesym, weaksym
+		case FExp:
+			if(s->name && s->name[0] == '.')
+				fatal("exporting synthetic symbol %s", s->name);
+			if(s->pkg != builtinpkg)
+				return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, s->name);
+		}
+	}
+
+	if(fp->flags&FmtByte) {  // FmtByte (hh) implies FmtShort (h)
+		// skip leading "type." in method name
+		p = utfrrune(s->name, '.');
+		if(p)
+			p++;
+		else
+			p = s->name;
+
+		// exportname needs to see the name without the prefix too.
+		if((fmtmode == FExp && !exportname(p)) || fmtmode == FDbg)
+			return fmtprint(fp, "@\"%Z\".%s", s->pkg->path, p);
+
+		return fmtstrcpy(fp, p);
+	}
+
+	return fmtstrcpy(fp, s->name);
+}
+
+static char*
+basicnames[] =
+{
+	[TINT]		= "int",
+	[TUINT]		= "uint",
+	[TINT8]		= "int8",
+	[TUINT8]	= "uint8",
+	[TINT16]	= "int16",
+	[TUINT16]	= "uint16",
+	[TINT32]	= "int32",
+	[TUINT32]	= "uint32",
+	[TINT64]	= "int64",
+	[TUINT64]	= "uint64",
+	[TUINTPTR]	= "uintptr",
+	[TFLOAT32]	= "float32",
+	[TFLOAT64]	= "float64",
+	[TCOMPLEX64]	= "complex64",
+	[TCOMPLEX128]	= "complex128",
+	[TBOOL]		= "bool",
+	[TANY]		= "any",
+	[TSTRING]	= "string",
+	[TNIL]		= "nil",
+	[TIDEAL]	= "untyped number",
+	[TBLANK]	= "blank",
+};
+
+static int
+typefmt(Fmt *fp, Type *t)
+{
+	Type *t1;
+	Sym *s;
+
+	if(t == T)
+		return fmtstrcpy(fp, "<T>");
+
+	if (t == bytetype || t == runetype) {
+		// in %-T mode collapse rune and byte with their originals.
+		if(fmtmode != FTypeId)
+			return fmtprint(fp, "%hS", t->sym);
+		t = types[t->etype];
+	}
+
+	if(t == errortype)
+		return fmtstrcpy(fp, "error");
+
+	// Unless the 'l' flag was specified, if the type has a name, just print that name.
+	if(!(fp->flags&FmtLong) && t->sym && t->etype != TFIELD && t != types[t->etype]) {
+		switch(fmtmode) {
+		case FTypeId:
+			if(fp->flags&FmtShort) {
+				if(t->vargen)
+					return fmtprint(fp, "%hS·%d", t->sym, t->vargen);
+				return fmtprint(fp, "%hS", t->sym);
+			}
+			if(fp->flags&FmtUnsigned)
+				return fmtprint(fp, "%uS", t->sym);
+			// fallthrough
+		case FExp:
+			if(t->sym->pkg == localpkg && t->vargen)
+				return fmtprint(fp, "%S·%d", t->sym, t->vargen);
+			break;
+		}
+		return fmtprint(fp, "%S", t->sym);
+	}
+
+	if(t->etype < nelem(basicnames) && basicnames[t->etype] != nil) {
+		if(fmtmode == FErr && (t == idealbool || t == idealstring))
+			fmtstrcpy(fp, "untyped ");
+		return fmtstrcpy(fp, basicnames[t->etype]);
+	}
+
+	if(fmtmode == FDbg)
+		fmtprint(fp, "%E-", t->etype);
+
+	switch(t->etype) {
+	case TPTR32:
+	case TPTR64:
+		if(fmtmode == FTypeId && (fp->flags&FmtShort))
+			return fmtprint(fp, "*%hT", t->type);
+		return fmtprint(fp, "*%T", t->type);
+
+	case TARRAY:
+		if(t->bound >= 0)
+			return fmtprint(fp, "[%lld]%T", t->bound, t->type);
+		if(t->bound == -100)
+			return fmtprint(fp, "[...]%T", t->type);
+		return fmtprint(fp, "[]%T", t->type);
+
+	case TCHAN:
+		switch(t->chan) {
+		case Crecv:
+			return fmtprint(fp, "<-chan %T", t->type);
+		case Csend:
+			return fmtprint(fp, "chan<- %T", t->type);
+		}
+
+		if(t->type != T && t->type->etype == TCHAN && t->type->sym == S && t->type->chan == Crecv)
+			return fmtprint(fp, "chan (%T)", t->type);
+		return fmtprint(fp, "chan %T", t->type);
+
+	case TMAP:
+		return fmtprint(fp, "map[%T]%T", t->down, t->type);
+
+	case TINTER:
+		fmtstrcpy(fp, "interface {");
+		for(t1=t->type; t1!=T; t1=t1->down)
+			if(exportname(t1->sym->name)) {
+				if(t1->down)
+					fmtprint(fp, " %hS%hT;", t1->sym, t1->type);
+				else
+					fmtprint(fp, " %hS%hT ", t1->sym, t1->type);
+			} else {
+				// non-exported method names must be qualified
+				if(t1->down)
+					fmtprint(fp, " %uS%hT;", t1->sym, t1->type);
+				else
+					fmtprint(fp, " %uS%hT ", t1->sym, t1->type);
+			}
+		fmtstrcpy(fp, "}");
+		return 0;
+
+	case TFUNC:
+		if(fp->flags & FmtShort) {
+			fmtprint(fp, "%T", getinargx(t));
+		} else {
+			if(t->thistuple)
+				fmtprint(fp, "method%T func%T", getthisx(t), getinargx(t));
+			else
+				fmtprint(fp, "func%T", getinargx(t));
+		}
+		switch(t->outtuple) {
+		case 0:
+			break;
+		case 1:
+			if(fmtmode != FExp) {
+				fmtprint(fp, " %T", getoutargx(t)->type->type);	 // struct->field->field's type
+				break;
+			}
+		default:
+			fmtprint(fp, " %T", getoutargx(t));
+			break;
+		}
+		return 0;
+
+	case TSTRUCT:
+		// Format the bucket struct for map[x]y as map.bucket[x]y.
+		// This avoids a recursive print that generates very long names.
+		if(t->map != T) {
+			if(t->map->bucket == t) {
+				return fmtprint(fp, "map.bucket[%T]%T", t->map->down, t->map->type);
+			}
+			if(t->map->hmap == t) {
+				return fmtprint(fp, "map.hdr[%T]%T", t->map->down, t->map->type);
+			}
+			if(t->map->hiter == t) {
+				return fmtprint(fp, "map.iter[%T]%T", t->map->down, t->map->type);
+			}
+			yyerror("unknown internal map type");
+		}
+
+		if(t->funarg) {
+			fmtstrcpy(fp, "(");
+			if(fmtmode == FTypeId || fmtmode == FErr) {	// no argument names on function signature, and no "noescape"/"nosplit" tags
+				for(t1=t->type; t1!=T; t1=t1->down)
+					if(t1->down)
+						fmtprint(fp, "%hT, ", t1);
+					else
+						fmtprint(fp, "%hT", t1);
+			} else {
+				for(t1=t->type; t1!=T; t1=t1->down)
+					if(t1->down)
+						fmtprint(fp, "%T, ", t1);
+					else
+						fmtprint(fp, "%T", t1);
+			}
+			fmtstrcpy(fp, ")");
+		} else {
+			fmtstrcpy(fp, "struct {");
+			for(t1=t->type; t1!=T; t1=t1->down)
+				if(t1->down)
+					fmtprint(fp, " %lT;", t1);
+				else
+					fmtprint(fp, " %lT ", t1);
+			fmtstrcpy(fp, "}");
+		}
+		return 0;
+
+	case TFIELD:
+		if(!(fp->flags&FmtShort)) {
+			s = t->sym;
+
+			// Take the name from the original, lest we substituted it with ~r%d or ~b%d.
+			// ~r%d is a (formerly) unnamed result.
+			if ((fmtmode == FErr || fmtmode == FExp) && t->nname != N) {
+				if(t->nname->orig != N) {
+					s = t->nname->orig->sym;
+					if(s != S && s->name[0] == '~') {
+						if(s->name[1] == 'r') // originally an unnamed result
+							s = S;
+						else if(s->name[1] == 'b') // originally the blank identifier _
+							s = lookup("_");
+					}
+				} else 
+					s = S;
+			}
+			
+			if(s != S && !t->embedded) {
+				if(t->funarg)
+					fmtprint(fp, "%N ", t->nname);
+				else if(fp->flags&FmtLong)
+					fmtprint(fp, "%hhS ", s);  // qualify non-exported names (used on structs, not on funarg)
+				else 
+					fmtprint(fp, "%S ", s);
+			} else if(fmtmode == FExp) {
+				// TODO(rsc) this breaks on the eliding of unused arguments in the backend
+				// when this is fixed, the special case in dcl.c checkarglist can go.
+				//if(t->funarg)
+				//	fmtstrcpy(fp, "_ ");
+				//else
+				if(t->embedded && s->pkg != nil && s->pkg->path->len > 0)
+					fmtprint(fp, "@\"%Z\".? ", s->pkg->path);
+				else
+					fmtstrcpy(fp, "? ");
+			}
+		}
+
+		if(t->isddd)
+			fmtprint(fp, "...%T", t->type->type);
+		else
+			fmtprint(fp, "%T", t->type);
+
+		if(!(fp->flags&FmtShort) && t->note)
+			fmtprint(fp, " \"%Z\"", t->note);
+		return 0;
+
+	case TFORW:
+		if(t->sym)
+			return fmtprint(fp, "undefined %S", t->sym);
+		return fmtstrcpy(fp, "undefined");
+
+	case TUNSAFEPTR:
+		if(fmtmode == FExp)
+			return fmtprint(fp, "@\"unsafe\".Pointer");
+		return fmtprint(fp, "unsafe.Pointer");
+	}
+
+	if(fmtmode == FExp)
+		fatal("missing %E case during export", t->etype);
+	// Don't know how to handle - fall back to detailed prints.
+	return fmtprint(fp, "%E <%S> %T", t->etype, t->sym, t->type);
+}
+
+// Statements which may be rendered with a simplestmt as init.
+static int
+stmtwithinit(int op)
+{
+	switch(op) {
+	case OIF:
+	case OFOR:
+	case OSWITCH:
+		return 1;
+	}
+	return 0;
+}
+
+static int
+stmtfmt(Fmt *f, Node *n)
+{
+	int complexinit, simpleinit, extrablock;
+
+	// some statements allow for an init, but at most one,
+	// but we may have an arbitrary number added, eg by typecheck
+	// and inlining.  If it doesn't fit the syntax, emit an enclosing
+	// block starting with the init statements.
+
+	// if we can just say "for" n->ninit; ... then do so
+	simpleinit = n->ninit && !n->ninit->next && !n->ninit->n->ninit && stmtwithinit(n->op);
+	// otherwise, print the inits as separate statements
+	complexinit = n->ninit && !simpleinit && (fmtmode != FErr);
+	// but if it was for if/for/switch, put in an extra surrounding block to limit the scope
+	extrablock = complexinit && stmtwithinit(n->op);
+
+	if(extrablock)
+		fmtstrcpy(f, "{");
+
+	if(complexinit)
+		fmtprint(f, " %H; ", n->ninit);
+
+	switch(n->op){
+	case ODCL:
+		if(fmtmode == FExp) {
+			switch(n->left->class&~PHEAP) {
+			case PPARAM:
+			case PPARAMOUT:
+			case PAUTO:
+				fmtprint(f, "var %N %T", n->left, n->left->type);
+				goto ret;
+			}
+		}			
+		fmtprint(f, "var %S %T", n->left->sym, n->left->type);
+		break;
+
+	case ODCLFIELD:
+		if(n->left)
+			fmtprint(f, "%N %N", n->left, n->right);
+		else
+			fmtprint(f, "%N", n->right);
+		break;
+
+	case OAS:
+		// Don't export "v = <N>" initializing statements, hope they're always 
+		// preceded by the DCL which will be re-parsed and typecheck to reproduce
+		// the "v = <N>" again.
+		if(fmtmode == FExp && n->right == N)
+			break;
+
+		if(n->colas && !complexinit)
+			fmtprint(f, "%N := %N", n->left, n->right);
+		else
+			fmtprint(f, "%N = %N", n->left, n->right);
+		break;
+
+	case OASOP:
+		if(n->implicit) {
+			if(n->etype == OADD)
+				fmtprint(f, "%N++", n->left);
+			else
+				fmtprint(f, "%N--", n->left);
+			break;
+		}
+		fmtprint(f, "%N %#O= %N", n->left, n->etype, n->right);
+		break;
+
+	case OAS2:
+		if(n->colas && !complexinit) {
+			fmtprint(f, "%,H := %,H", n->list, n->rlist);
+			break;
+		}
+		// fallthrough
+	case OAS2DOTTYPE:
+	case OAS2FUNC:
+	case OAS2MAPR:
+	case OAS2RECV:
+		fmtprint(f, "%,H = %,H", n->list, n->rlist);
+		break;
+
+	case ORETURN:
+		fmtprint(f, "return %,H", n->list);
+		break;
+
+	case ORETJMP:
+		fmtprint(f, "retjmp %S", n->sym);
+		break;
+	
+	case OPROC:
+		fmtprint(f, "go %N", n->left);
+		break;
+
+	case ODEFER:
+		fmtprint(f, "defer %N", n->left);
+		break;
+
+	case OIF:
+		if(simpleinit)
+			fmtprint(f, "if %N; %N { %H }", n->ninit->n, n->ntest, n->nbody);
+		else
+			fmtprint(f, "if %N { %H }", n->ntest, n->nbody);
+		if(n->nelse)
+			fmtprint(f, " else { %H }", n->nelse);
+		break;
+
+	case OFOR:
+		if(fmtmode == FErr) {	// TODO maybe only if FmtShort, same below
+			fmtstrcpy(f, "for loop");
+			break;
+		}
+
+		fmtstrcpy(f, "for");
+		if(simpleinit)
+			fmtprint(f, " %N;", n->ninit->n);
+		else if(n->nincr)
+			fmtstrcpy(f, " ;");
+
+		if(n->ntest)
+			fmtprint(f, " %N", n->ntest);
+
+		if(n->nincr)
+			fmtprint(f, "; %N", n->nincr);
+		else if(simpleinit)
+			fmtstrcpy(f, ";");
+
+
+		fmtprint(f, " { %H }", n->nbody);
+		break;
+
+	case ORANGE:
+		if(fmtmode == FErr) {
+			fmtstrcpy(f, "for loop");
+			break;
+		}
+		
+		if(n->list == nil) {
+			fmtprint(f, "for range %N { %H }", n->right, n->nbody);
+			break;
+		}
+		fmtprint(f, "for %,H = range %N { %H }", n->list, n->right, n->nbody);
+		break;
+
+	case OSELECT:
+	case OSWITCH:
+		if(fmtmode == FErr) {
+			fmtprint(f, "%O statement", n->op);
+			break;
+		}
+
+		fmtprint(f, "%#O", n->op);
+		if(simpleinit)
+			fmtprint(f, " %N;", n->ninit->n);
+		if(n->ntest)
+			fmtprint(f, "%N", n->ntest);
+
+		fmtprint(f, " { %H }", n->list);
+		break;
+
+	case OCASE:
+	case OXCASE:
+		if(n->list)
+			fmtprint(f, "case %,H: %H", n->list, n->nbody);
+		else
+			fmtprint(f, "default: %H", n->nbody);
+		break;
+
+	case OBREAK:
+	case OCONTINUE:
+	case OGOTO:
+	case OFALL:
+	case OXFALL:
+		if(n->left)
+			fmtprint(f, "%#O %N", n->op, n->left);
+		else
+			fmtprint(f, "%#O", n->op);
+		break;
+
+	case OEMPTY:
+		break;
+
+	case OLABEL:
+		fmtprint(f, "%N: ", n->left);
+		break;
+	  
+	}
+ret:
+
+	if(extrablock)
+		fmtstrcpy(f, "}");
+
+	return 0;
+}
+
+
+static int opprec[] = {
+	[OAPPEND] = 8,
+	[OARRAYBYTESTR] = 8,
+	[OARRAYLIT] = 8,
+	[OARRAYRUNESTR] = 8,
+	[OCALLFUNC] = 8,
+	[OCALLINTER] = 8,
+	[OCALLMETH] = 8,
+	[OCALL] = 8,
+	[OCAP] = 8,
+	[OCLOSE] = 8,
+	[OCONVIFACE] = 8,
+	[OCONVNOP] = 8,
+	[OCONV] = 8,
+	[OCOPY] = 8,
+	[ODELETE] = 8,
+	[OLEN] = 8,
+	[OLITERAL] = 8,
+	[OMAKESLICE] = 8,
+	[OMAKE] = 8,
+	[OMAPLIT] = 8,
+	[ONAME] = 8,
+	[ONEW] = 8,
+	[ONONAME] = 8,
+	[OPACK] = 8,
+	[OPANIC] = 8,
+	[OPAREN] = 8,
+	[OPRINTN] = 8,
+	[OPRINT] = 8,
+	[ORUNESTR] = 8,
+	[OSTRARRAYBYTE] = 8,
+	[OSTRARRAYRUNE] = 8,
+	[OSTRUCTLIT] = 8,
+	[OTARRAY] = 8,
+	[OTCHAN] = 8,
+	[OTFUNC] = 8,
+	[OTINTER] = 8,
+	[OTMAP] = 8,
+	[OTSTRUCT] = 8,
+
+	[OINDEXMAP] = 8,
+	[OINDEX] = 8,
+	[OSLICE] = 8,
+	[OSLICESTR] = 8,
+	[OSLICEARR] = 8,
+	[OSLICE3] = 8,
+	[OSLICE3ARR] = 8,
+	[ODOTINTER] = 8,
+	[ODOTMETH] = 8,
+	[ODOTPTR] = 8,
+	[ODOTTYPE2] = 8,
+	[ODOTTYPE] = 8,
+	[ODOT] = 8,
+	[OXDOT] = 8,
+	[OCALLPART] = 8,
+
+	[OPLUS] = 7,
+	[ONOT] = 7,
+	[OCOM] = 7,
+	[OMINUS] = 7,
+	[OADDR] = 7,
+	[OIND] = 7,
+	[ORECV] = 7,
+
+	[OMUL] = 6,
+	[ODIV] = 6,
+	[OMOD] = 6,
+	[OLSH] = 6,
+	[ORSH] = 6,
+	[OAND] = 6,
+	[OANDNOT] = 6,
+
+	[OADD] = 5,
+	[OSUB] = 5,
+	[OOR] = 5,
+	[OXOR] = 5,
+
+	[OEQ] = 4,
+	[OLT] = 4,
+	[OLE] = 4,
+	[OGE] = 4,
+	[OGT] = 4,
+	[ONE] = 4,
+	[OCMPSTR] = 4,
+	[OCMPIFACE] = 4,
+
+	[OSEND] = 3,
+	[OANDAND] = 2,
+	[OOROR] = 1,
+
+	// Statements handled by stmtfmt
+	[OAS] = -1,
+	[OAS2] = -1,
+	[OAS2DOTTYPE] = -1,
+	[OAS2FUNC] = -1,
+	[OAS2MAPR] = -1,
+	[OAS2RECV] = -1,
+	[OASOP] = -1,
+	[OBREAK] = -1,
+	[OCASE] = -1,
+	[OCONTINUE] = -1,
+	[ODCL] = -1,
+	[ODCLFIELD] = -1,
+	[ODEFER] = -1,
+	[OEMPTY] = -1,
+	[OFALL] = -1,
+	[OFOR] = -1,
+	[OGOTO] = -1,
+	[OIF] = -1,
+	[OLABEL] = -1,
+	[OPROC] = -1,
+	[ORANGE] = -1,
+	[ORETURN] = -1,
+	[OSELECT] = -1,
+	[OSWITCH] = -1,
+	[OXCASE] = -1,
+	[OXFALL] = -1,
+
+	[OEND] = 0
+};
+
+static int
+exprfmt(Fmt *f, Node *n, int prec)
+{
+	int nprec;
+	int ptrlit;
+	NodeList *l;
+
+	while(n && n->implicit && (n->op == OIND || n->op == OADDR))
+		n = n->left;
+
+	if(n == N)
+		return fmtstrcpy(f, "<N>");
+
+	nprec = opprec[n->op];
+	if(n->op == OTYPE && n->sym != S)
+		nprec = 8;
+
+	if(prec > nprec)
+		return fmtprint(f, "(%N)", n);
+
+	switch(n->op) {
+	case OPAREN:
+		return fmtprint(f, "(%N)", n->left);
+
+	case ODDDARG:
+		return fmtprint(f, "... argument");
+
+	case OREGISTER:
+		return fmtprint(f, "%R", n->val.u.reg);
+
+	case OLITERAL:  // this is a bit of a mess
+		if(fmtmode == FErr && n->sym != S)
+			return fmtprint(f, "%S", n->sym);
+		if(n->val.ctype == CTNIL && n->orig != N && n->orig != n)
+			return exprfmt(f, n->orig, prec);
+		if(n->type != T && n->type != types[n->type->etype] && n->type != idealbool && n->type != idealstring) {
+			// Need parens when type begins with what might
+			// be misinterpreted as a unary operator: * or <-.
+			if(isptr[n->type->etype] || (n->type->etype == TCHAN && n->type->chan == Crecv))
+				return fmtprint(f, "(%T)(%V)", n->type, &n->val);
+			else 
+				return fmtprint(f, "%T(%V)", n->type, &n->val);
+		}
+		return fmtprint(f, "%V", &n->val);
+
+	case ONAME:
+		// Special case: name used as local variable in export.
+		// _ becomes ~b%d internally; print as _ for export
+		if(fmtmode == FExp && n->sym && n->sym->name[0] == '~' && n->sym->name[1] == 'b')
+			return fmtprint(f, "_");
+		if(fmtmode == FExp && n->sym && !isblank(n) && n->vargen > 0)
+			return fmtprint(f, "%S·%d", n->sym, n->vargen);
+
+		// Special case: explicit name of func (*T) method(...) is turned into pkg.(*T).method,
+		// but for export, this should be rendered as (*pkg.T).meth.
+		// These nodes have the special property that they are names with a left OTYPE and a right ONAME.
+		if(fmtmode == FExp && n->left && n->left->op == OTYPE && n->right && n->right->op == ONAME) {
+			if(isptr[n->left->type->etype])
+				return fmtprint(f, "(%T).%hhS", n->left->type, n->right->sym);
+			else
+				return fmtprint(f, "%T.%hhS", n->left->type, n->right->sym);
+		}
+		//fallthrough
+	case OPACK:
+	case ONONAME:
+		return fmtprint(f, "%S", n->sym);
+
+	case OTYPE:
+		if(n->type == T && n->sym != S)
+			return fmtprint(f, "%S", n->sym);
+		return fmtprint(f, "%T", n->type);
+
+	case OTARRAY:
+		if(n->left)
+			return fmtprint(f, "[]%N", n->left);
+		return fmtprint(f, "[]%N", n->right);  // happens before typecheck
+
+	case OTMAP:
+		return fmtprint(f, "map[%N]%N", n->left, n->right);
+
+	case OTCHAN:
+		switch(n->etype) {
+		case Crecv:
+			return fmtprint(f, "<-chan %N", n->left);
+		case Csend:
+			return fmtprint(f, "chan<- %N", n->left);
+		default:
+			if(n->left != N && n->left->op == OTCHAN && n->left->sym == S && n->left->etype == Crecv)
+				return fmtprint(f, "chan (%N)", n->left);
+			else
+				return fmtprint(f, "chan %N", n->left);
+		}
+
+	case OTSTRUCT:
+		return fmtprint(f, "<struct>");
+
+	case OTINTER:
+		return fmtprint(f, "<inter>");
+
+	case OTFUNC:
+		return fmtprint(f, "<func>");
+
+	case OCLOSURE:
+		if(fmtmode == FErr)
+			return fmtstrcpy(f, "func literal");
+		if(n->nbody)
+			return fmtprint(f, "%T { %H }", n->type, n->nbody);
+		return fmtprint(f, "%T { %H }", n->type, n->closure->nbody);
+
+	case OCOMPLIT:
+		ptrlit = n->right != N && n->right->implicit && n->right->type && isptr[n->right->type->etype];
+		if(fmtmode == FErr) {
+			if(n->right != N && n->right->type != T && !n->implicit) {
+				if(ptrlit)
+					return fmtprint(f, "&%T literal", n->right->type->type);
+				else
+					return fmtprint(f, "%T literal", n->right->type);
+			}
+			return fmtstrcpy(f, "composite literal");
+		}
+		if(fmtmode == FExp && ptrlit)
+			// typecheck has overwritten OIND by OTYPE with pointer type.
+			return fmtprint(f, "(&%T{ %,H })", n->right->type->type, n->list);
+		return fmtprint(f, "(%N{ %,H })", n->right, n->list);
+
+	case OPTRLIT:
+		if(fmtmode == FExp && n->left->implicit)
+			return fmtprint(f, "%N", n->left);
+		return fmtprint(f, "&%N", n->left);
+
+	case OSTRUCTLIT:
+		if(fmtmode == FExp) {   // requires special handling of field names
+			if(n->implicit)
+				fmtstrcpy(f, "{");
+			else
+				fmtprint(f, "(%T{", n->type);
+			for(l=n->list; l; l=l->next) {
+				fmtprint(f, " %hhS:%N", l->n->left->sym, l->n->right);
+
+				if(l->next)
+					fmtstrcpy(f, ",");
+				else
+					fmtstrcpy(f, " ");
+			}
+			if(!n->implicit)
+				return fmtstrcpy(f, "})");
+			return fmtstrcpy(f, "}");
+		}
+		// fallthrough
+
+	case OARRAYLIT:
+	case OMAPLIT:
+		if(fmtmode == FErr)
+			return fmtprint(f, "%T literal", n->type);
+		if(fmtmode == FExp && n->implicit)
+			return fmtprint(f, "{ %,H }", n->list);
+		return fmtprint(f, "(%T{ %,H })", n->type, n->list);
+
+	case OKEY:
+		if(n->left && n->right) {
+			if(fmtmode == FExp && n->left->type && n->left->type->etype == TFIELD) {
+				// requires special handling of field names
+				return fmtprint(f, "%hhS:%N", n->left->sym, n->right);
+			} else
+				return fmtprint(f, "%N:%N", n->left, n->right);
+		}
+		if(!n->left && n->right)
+			return fmtprint(f, ":%N", n->right);
+		if(n->left && !n->right)
+			return fmtprint(f, "%N:", n->left);
+		return fmtstrcpy(f, ":");
+
+	case OXDOT:
+	case ODOT:
+	case ODOTPTR:
+	case ODOTINTER:
+	case ODOTMETH:
+	case OCALLPART:
+		exprfmt(f, n->left, nprec);
+		if(n->right == N || n->right->sym == S)
+			return fmtstrcpy(f, ".<nil>");
+		return fmtprint(f, ".%hhS", n->right->sym);
+
+	case ODOTTYPE:
+	case ODOTTYPE2:
+		exprfmt(f, n->left, nprec);
+		if(n->right != N)
+			return fmtprint(f, ".(%N)", n->right);
+		return fmtprint(f, ".(%T)", n->type);
+
+	case OINDEX:
+	case OINDEXMAP:
+	case OSLICE:
+	case OSLICESTR:
+	case OSLICEARR:
+	case OSLICE3:
+	case OSLICE3ARR:
+		exprfmt(f, n->left, nprec);
+		return fmtprint(f, "[%N]", n->right);
+
+	case OCOPY:
+	case OCOMPLEX:
+		return fmtprint(f, "%#O(%N, %N)", n->op, n->left, n->right);
+
+	case OCONV:
+	case OCONVIFACE:
+	case OCONVNOP:
+	case OARRAYBYTESTR:
+	case OARRAYRUNESTR:
+	case OSTRARRAYBYTE:
+	case OSTRARRAYRUNE:
+	case ORUNESTR:
+		if(n->type == T || n->type->sym == S)
+			return fmtprint(f, "(%T)(%N)", n->type, n->left);
+		if(n->left)
+			return fmtprint(f, "%T(%N)", n->type, n->left);
+		return fmtprint(f, "%T(%,H)", n->type, n->list);
+
+	case OREAL:
+	case OIMAG:
+	case OAPPEND:
+	case OCAP:
+	case OCLOSE:
+	case ODELETE:
+	case OLEN:
+	case OMAKE:
+	case ONEW:
+	case OPANIC:
+	case ORECOVER:
+	case OPRINT:
+	case OPRINTN:
+		if(n->left)
+			return fmtprint(f, "%#O(%N)", n->op, n->left);
+		if(n->isddd)
+			return fmtprint(f, "%#O(%,H...)", n->op, n->list);
+		return fmtprint(f, "%#O(%,H)", n->op, n->list);
+
+	case OCALL:
+	case OCALLFUNC:
+	case OCALLINTER:
+	case OCALLMETH:
+		exprfmt(f, n->left, nprec);
+		if(n->isddd)
+			return fmtprint(f, "(%,H...)", n->list);
+		return fmtprint(f, "(%,H)", n->list);
+
+	case OMAKEMAP:
+	case OMAKECHAN:
+	case OMAKESLICE:
+		if(n->list) // pre-typecheck
+			return fmtprint(f, "make(%T, %,H)", n->type, n->list);
+		if(n->right)
+			return fmtprint(f, "make(%T, %N, %N)", n->type, n->left, n->right);
+		if(n->left)
+			return fmtprint(f, "make(%T, %N)", n->type, n->left);
+		return fmtprint(f, "make(%T)", n->type);
+
+	// Unary
+	case OPLUS:
+	case OMINUS:
+	case OADDR:
+	case OCOM:
+	case OIND:
+	case ONOT:
+	case ORECV:
+		if(n->left->op == n->op)
+			fmtprint(f, "%#O ", n->op);
+		else
+			fmtprint(f, "%#O", n->op);
+		return exprfmt(f, n->left, nprec+1);
+
+	// Binary
+	case OADD:
+	case OAND:
+	case OANDAND:
+	case OANDNOT:
+	case ODIV:
+	case OEQ:
+	case OGE:
+	case OGT:
+	case OLE:
+	case OLT:
+	case OLSH:
+	case OMOD:
+	case OMUL:
+	case ONE:
+	case OOR:
+	case OOROR:
+	case ORSH:
+	case OSEND:
+	case OSUB:
+	case OXOR:
+		exprfmt(f, n->left, nprec);
+		fmtprint(f, " %#O ", n->op);
+		exprfmt(f, n->right, nprec+1);
+		return 0;
+
+	case OADDSTR:
+		for(l=n->list; l; l=l->next) {
+			if(l != n->list)
+				fmtprint(f, " + ");
+			exprfmt(f, l->n, nprec);
+		}
+		return 0;
+
+	case OCMPSTR:
+	case OCMPIFACE:
+		exprfmt(f, n->left, nprec);
+		fmtprint(f, " %#O ", n->etype);
+		exprfmt(f, n->right, nprec+1);
+		return 0;
+	}
+
+	return fmtprint(f, "<node %O>", n->op);
+}
+
+static int
+nodefmt(Fmt *f, Node *n)
+{
+	Type *t;
+
+	t = n->type;
+
+	// we almost always want the original, except in export mode for literals
+	// this saves the importer some work, and avoids us having to redo some
+	// special casing for package unsafe
+	if((fmtmode != FExp || n->op != OLITERAL) && n->orig != N)
+		n = n->orig;
+
+	if(f->flags&FmtLong && t != T) {
+		if(t->etype == TNIL)
+			return fmtprint(f, "nil");
+		else
+			return fmtprint(f, "%N (type %T)", n, t);
+	}
+
+	// TODO inlining produces expressions with ninits. we can't print these yet.
+
+	if(opprec[n->op] < 0)
+		return stmtfmt(f, n);
+
+	return exprfmt(f, n, 0);
+}
+
+static int dumpdepth;
+
+static void
+indent(Fmt *fp)
+{
+	int i;
+
+	fmtstrcpy(fp, "\n");
+	for(i = 0; i < dumpdepth; ++i)
+		fmtstrcpy(fp, ".   ");
+}
+
+static int
+nodedump(Fmt *fp, Node *n)
+{
+	int recur;
+
+	if(n == N)
+		return 0;
+
+	recur = !(fp->flags&FmtShort);
+
+	if(recur) {
+		indent(fp);
+		if(dumpdepth > 10)
+			return fmtstrcpy(fp, "...");
+
+		if(n->ninit != nil) {
+			fmtprint(fp, "%O-init%H", n->op, n->ninit);
+			indent(fp);
+		}
+	}
+
+//	fmtprint(fp, "[%p]", n);
+
+	switch(n->op) {
+	default:
+		fmtprint(fp, "%O%J", n->op, n);
+		break;
+	case OREGISTER:
+	case OINDREG:
+		fmtprint(fp, "%O-%R%J", n->op, n->val.u.reg, n);
+		break;
+	case OLITERAL:
+		fmtprint(fp, "%O-%V%J", n->op, &n->val, n);
+		break;
+	case ONAME:
+	case ONONAME:
+		if(n->sym != S)
+			fmtprint(fp, "%O-%S%J", n->op, n->sym, n);
+		else
+			fmtprint(fp, "%O%J", n->op, n);
+		if(recur && n->type == T && n->ntype) {
+			indent(fp);
+			fmtprint(fp, "%O-ntype%N", n->op, n->ntype);
+		}
+		break;
+	case OASOP:
+		fmtprint(fp, "%O-%O%J", n->op, n->etype, n);
+		break;
+	case OTYPE:
+		fmtprint(fp, "%O %S%J type=%T", n->op, n->sym, n, n->type);
+		if(recur && n->type == T && n->ntype) {
+			indent(fp);
+			fmtprint(fp, "%O-ntype%N", n->op, n->ntype);
+		}
+		break;
+	}
+
+	if(n->sym != S && n->op != ONAME)
+		fmtprint(fp, " %S G%d", n->sym, n->vargen);
+
+	if(n->type != T)
+		fmtprint(fp, " %T", n->type);
+
+	if(recur) {
+		if(n->left)
+			fmtprint(fp, "%N", n->left);
+		if(n->right)
+			fmtprint(fp, "%N", n->right);
+		if(n->list) {
+			indent(fp);
+			fmtprint(fp, "%O-list%H", n->op, n->list);
+		}
+		if(n->rlist) {
+			indent(fp);
+			fmtprint(fp, "%O-rlist%H", n->op, n->rlist);
+		}
+		if(n->ntest) {
+			indent(fp);
+			fmtprint(fp, "%O-test%N", n->op, n->ntest);
+		}
+		if(n->nbody) {
+			indent(fp);
+			fmtprint(fp, "%O-body%H", n->op, n->nbody);
+		}
+		if(n->nelse) {
+			indent(fp);
+			fmtprint(fp, "%O-else%H", n->op, n->nelse);
+		}
+		if(n->nincr) {
+			indent(fp);
+			fmtprint(fp, "%O-incr%N", n->op, n->nincr);
+		}
+	}
+
+	return 0;
+}
+
+// Fmt "%S": syms
+// Flags:  "%hS" suppresses qualifying with package
+static int
+Sconv(Fmt *fp)
+{
+	Sym *s;
+	int r, sm;
+	unsigned long sf;
+
+	if(fp->flags&FmtLong)
+		return linksymfmt(fp);
+
+	s = va_arg(fp->args, Sym*);
+	if(s == S)
+		return fmtstrcpy(fp, "<S>");
+
+	if(s->name && s->name[0] == '_' && s->name[1] == '\0')
+		return fmtstrcpy(fp, "_");
+
+	sf = fp->flags;
+	sm = setfmode(&fp->flags);
+	r = symfmt(fp, s);
+	fp->flags = sf;
+	fmtmode = sm;
+	return r;
+}
+
+// Fmt "%T": types.
+// Flags: 'l' print definition, not name
+//	  'h' omit 'func' and receiver from function types, short type names
+//	  'u' package name, not prefix (FTypeId mode, sticky)
+static int
+Tconv(Fmt *fp)
+{
+	Type *t;
+	int r, sm;
+	unsigned long sf;
+
+	t = va_arg(fp->args, Type*);
+	if(t == T)
+		return fmtstrcpy(fp, "<T>");
+
+	if(t->trecur > 4)
+		return fmtstrcpy(fp, "<...>");
+
+	t->trecur++;
+	sf = fp->flags;
+	sm = setfmode(&fp->flags);
+
+	if(fmtmode == FTypeId && (sf&FmtUnsigned))
+		fmtpkgpfx++;
+	if(fmtpkgpfx)
+		fp->flags |= FmtUnsigned;
+
+	r = typefmt(fp, t);
+
+	if(fmtmode == FTypeId && (sf&FmtUnsigned))
+		fmtpkgpfx--;
+
+	fp->flags = sf;
+	fmtmode = sm;
+	t->trecur--;
+	return r;
+}
+
+// Fmt '%N': Nodes.
+// Flags: 'l' suffix with "(type %T)" where possible
+//	  '+h' in debug mode, don't recurse, no multiline output
+static int
+Nconv(Fmt *fp)
+{
+	Node *n;
+	int r, sm;
+	unsigned long sf;
+
+	n = va_arg(fp->args, Node*);
+	if(n == N)
+		return fmtstrcpy(fp, "<N>");
+	sf = fp->flags;
+	sm = setfmode(&fp->flags);
+
+	r = -1;
+	switch(fmtmode) {
+	case FErr:
+	case FExp:
+		r = nodefmt(fp, n);
+		break;
+	case FDbg:
+		dumpdepth++;
+		r = nodedump(fp, n);
+		dumpdepth--;
+		break;
+	default:
+		fatal("unhandled %%N mode");
+	}
+
+	fp->flags = sf;
+	fmtmode = sm;
+	return r;
+}
+
+// Fmt '%H': NodeList.
+// Flags: all those of %N plus ',': separate with comma's instead of semicolons.
+static int
+Hconv(Fmt *fp)
+{
+	NodeList *l;
+	int r, sm;
+	unsigned long sf;
+	char *sep;
+
+	l = va_arg(fp->args, NodeList*);
+
+	if(l == nil && fmtmode == FDbg)
+		return fmtstrcpy(fp, "<nil>");
+
+	sf = fp->flags;
+	sm = setfmode(&fp->flags);
+	r = 0;
+	sep = "; ";
+	if(fmtmode == FDbg)
+		sep = "\n";
+	else if(fp->flags & FmtComma)
+		sep = ", ";
+
+	for(;l; l=l->next) {
+		r += fmtprint(fp, "%N", l->n);
+		if(l->next)
+			r += fmtstrcpy(fp, sep);
+	}
+
+	fp->flags = sf;
+	fmtmode = sm;
+	return r;
+}
+
+void
+fmtinstallgo(void)
+{
+	fmtmode = FErr;
+	fmtinstall('E', Econv);		// etype opcodes
+	fmtinstall('J', Jconv);		// all the node flags
+	fmtinstall('H', Hconv);		// node lists
+	fmtinstall('L', Lconv);		// line number
+	fmtinstall('N', Nconv);		// node pointer
+	fmtinstall('O', Oconv);		// node opcodes
+	fmtinstall('S', Sconv);		// sym pointer
+	fmtinstall('T', Tconv);		// type pointer
+	fmtinstall('V', Vconv);		// val pointer
+	fmtinstall('Z', Zconv);		// escaped string
+
+	// These are in mparith1.c
+	fmtinstall('B', Bconv);	// big numbers
+	fmtinstall('F', Fconv);	// big float numbers
+
+}
+
+void
+dumplist(char *s, NodeList *l)
+{
+	print("%s%+H\n", s, l);
+}
+
+void
+dump(char *s, Node *n)
+{
+	print("%s [%p]%+N\n", s, n, n);
+}
diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c
new file mode 100644
index 0000000..c7c9fcd
--- /dev/null
+++ b/src/cmd/gc/gen.c
@@ -0,0 +1,991 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * portable half of code generator.
+ * mainly statements and control flow.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+static void	cgen_dcl(Node *n);
+static void	cgen_proc(Node *n, int proc);
+static void	checkgoto(Node*, Node*);
+
+static Label *labellist;
+static Label *lastlabel;
+
+Node*
+sysfunc(char *name)
+{
+	Node *n;
+
+	n = newname(pkglookup(name, runtimepkg));
+	n->class = PFUNC;
+	return n;
+}
+
+/*
+ * the address of n has been taken and might be used after
+ * the current function returns.  mark any local vars
+ * as needing to move to the heap.
+ */
+void
+addrescapes(Node *n)
+{
+	char buf[100];
+	Node *oldfn;
+
+	switch(n->op) {
+	default:
+		// probably a type error already.
+		// dump("addrescapes", n);
+		break;
+
+	case ONAME:
+		if(n == nodfp)
+			break;
+
+		// if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping.
+		// on PPARAM it means something different.
+		if(n->class == PAUTO && n->esc == EscNever)
+			break;
+
+		switch(n->class) {
+		case PPARAMREF:
+			addrescapes(n->defn);
+			break;
+		case PPARAM:
+		case PPARAMOUT:
+			// if func param, need separate temporary
+			// to hold heap pointer.
+			// the function type has already been checked
+			// (we're in the function body)
+			// so the param already has a valid xoffset.
+
+			// expression to refer to stack copy
+			n->stackparam = nod(OPARAM, n, N);
+			n->stackparam->type = n->type;
+			n->stackparam->addable = 1;
+			if(n->xoffset == BADWIDTH)
+				fatal("addrescapes before param assignment");
+			n->stackparam->xoffset = n->xoffset;
+			// fallthrough
+
+		case PAUTO:
+			n->class |= PHEAP;
+			n->addable = 0;
+			n->ullman = 2;
+			n->xoffset = 0;
+
+			// create stack variable to hold pointer to heap
+			oldfn = curfn;
+			curfn = n->curfn;
+			n->heapaddr = temp(ptrto(n->type));
+			snprint(buf, sizeof buf, "&%S", n->sym);
+			n->heapaddr->sym = lookup(buf);
+			n->heapaddr->orig->sym = n->heapaddr->sym;
+			n->esc = EscHeap;
+			if(debug['m'])
+				print("%L: moved to heap: %N\n", n->lineno, n);
+			curfn = oldfn;
+			break;
+		}
+		break;
+
+	case OIND:
+	case ODOTPTR:
+		break;
+
+	case ODOT:
+	case OINDEX:
+		// ODOTPTR has already been introduced,
+		// so these are the non-pointer ODOT and OINDEX.
+		// In &x[0], if x is a slice, then x does not
+		// escape--the pointer inside x does, but that
+		// is always a heap pointer anyway.
+		if(!isslice(n->left->type))
+			addrescapes(n->left);
+		break;
+	}
+}
+
+void
+clearlabels(void)
+{
+	Label *l;
+
+	for(l=labellist; l!=L; l=l->link)
+		l->sym->label = L;
+	
+	labellist = L;
+	lastlabel = L;
+}
+
+static Label*
+newlab(Node *n)
+{
+	Sym *s;
+	Label *lab;
+	
+	s = n->left->sym;
+	if((lab = s->label) == L) {
+		lab = mal(sizeof(*lab));
+		if(lastlabel == nil)
+			labellist = lab;
+		else
+			lastlabel->link = lab;
+		lastlabel = lab;
+		lab->sym = s;
+		s->label = lab;
+	}
+	
+	if(n->op == OLABEL) {
+		if(lab->def != N)
+			yyerror("label %S already defined at %L", s, lab->def->lineno);
+		else
+			lab->def = n;
+	} else
+		lab->use = list(lab->use, n);
+
+	return lab;
+}
+
+void
+checklabels(void)
+{
+	Label *lab;
+	NodeList *l;
+
+	for(lab=labellist; lab!=L; lab=lab->link) {
+		if(lab->def == N) {
+			for(l=lab->use; l; l=l->next)
+				yyerrorl(l->n->lineno, "label %S not defined", lab->sym);
+			continue;
+		}
+		if(lab->use == nil && !lab->used) {
+			yyerrorl(lab->def->lineno, "label %S defined and not used", lab->sym);
+			continue;
+		}
+		if(lab->gotopc != P)
+			fatal("label %S never resolved", lab->sym);
+		for(l=lab->use; l; l=l->next)
+			checkgoto(l->n, lab->def);
+	}
+}
+
+static void
+checkgoto(Node *from, Node *to)
+{
+	int nf, nt;
+	Sym *block, *dcl, *fs, *ts;
+	int lno;
+
+	if(from->sym == to->sym)
+		return;
+
+	nf = 0;
+	for(fs=from->sym; fs; fs=fs->link)
+		nf++;
+	nt = 0;
+	for(fs=to->sym; fs; fs=fs->link)
+		nt++;
+	fs = from->sym;
+	for(; nf > nt; nf--)
+		fs = fs->link;
+	if(fs != to->sym) {
+		lno = lineno;
+		setlineno(from);
+
+		// decide what to complain about.
+		// prefer to complain about 'into block' over declarations,
+		// so scan backward to find most recent block or else dcl.
+		block = S;
+		dcl = S;
+		ts = to->sym;
+		for(; nt > nf; nt--) {
+			if(ts->pkg == nil)
+				block = ts;
+			else
+				dcl = ts;
+			ts = ts->link;
+		}
+		while(ts != fs) {
+			if(ts->pkg == nil)
+				block = ts;
+			else
+				dcl = ts;
+			ts = ts->link;
+			fs = fs->link;
+		}
+
+		if(block)
+			yyerror("goto %S jumps into block starting at %L", from->left->sym, block->lastlineno);
+		else
+			yyerror("goto %S jumps over declaration of %S at %L", from->left->sym, dcl, dcl->lastlineno);
+		lineno = lno;
+	}
+}
+
+static Label*
+stmtlabel(Node *n)
+{
+	Label *lab;
+
+	if(n->sym != S)
+	if((lab = n->sym->label) != L)
+	if(lab->def != N)
+	if(lab->def->defn == n)
+		return lab;
+	return L;
+}
+
+/*
+ * compile statements
+ */
+void
+genlist(NodeList *l)
+{
+	for(; l; l=l->next)
+		gen(l->n);
+}
+
+void
+gen(Node *n)
+{
+	int32 lno;
+	Prog *scontin, *sbreak;
+	Prog *p1, *p2, *p3;
+	Label *lab;
+	int32 wasregalloc;
+
+//dump("gen", n);
+
+	lno = setlineno(n);
+	wasregalloc = anyregalloc();
+
+	if(n == N)
+		goto ret;
+
+	if(n->ninit)
+		genlist(n->ninit);
+
+	setlineno(n);
+
+	switch(n->op) {
+	default:
+		fatal("gen: unknown op %+hN", n);
+		break;
+
+	case OCASE:
+	case OFALL:
+	case OXCASE:
+	case OXFALL:
+	case ODCLCONST:
+	case ODCLFUNC:
+	case ODCLTYPE:
+		break;
+
+	case OEMPTY:
+		break;
+
+	case OBLOCK:
+		genlist(n->list);
+		break;
+
+	case OLABEL:
+		if(isblanksym(n->left->sym))
+			break;
+		
+		lab = newlab(n);
+
+		// if there are pending gotos, resolve them all to the current pc.
+		for(p1=lab->gotopc; p1; p1=p2) {
+			p2 = unpatch(p1);
+			patch(p1, pc);
+		}
+		lab->gotopc = P;
+		if(lab->labelpc == P)
+			lab->labelpc = pc;
+
+		if(n->defn) {
+			switch(n->defn->op) {
+			case OFOR:
+			case OSWITCH:
+			case OSELECT:
+				// so stmtlabel can find the label
+				n->defn->sym = lab->sym;
+			}
+		}
+		break;
+
+	case OGOTO:
+		// if label is defined, emit jump to it.
+		// otherwise save list of pending gotos in lab->gotopc.
+		// the list is linked through the normal jump target field
+		// to avoid a second list.  (the jumps are actually still
+		// valid code, since they're just going to another goto
+		// to the same label.  we'll unwind it when we learn the pc
+		// of the label in the OLABEL case above.)
+		lab = newlab(n);
+		if(lab->labelpc != P)
+			gjmp(lab->labelpc);
+		else
+			lab->gotopc = gjmp(lab->gotopc);
+		break;
+
+	case OBREAK:
+		if(n->left != N) {
+			lab = n->left->sym->label;
+			if(lab == L) {
+				yyerror("break label not defined: %S", n->left->sym);
+				break;
+			}
+			lab->used = 1;
+			if(lab->breakpc == P) {
+				yyerror("invalid break label %S", n->left->sym);
+				break;
+			}
+			gjmp(lab->breakpc);
+			break;
+		}
+		if(breakpc == P) {
+			yyerror("break is not in a loop");
+			break;
+		}
+		gjmp(breakpc);
+		break;
+
+	case OCONTINUE:
+		if(n->left != N) {
+			lab = n->left->sym->label;
+			if(lab == L) {
+				yyerror("continue label not defined: %S", n->left->sym);
+				break;
+			}
+			lab->used = 1;
+			if(lab->continpc == P) {
+				yyerror("invalid continue label %S", n->left->sym);
+				break;
+			}
+			gjmp(lab->continpc);
+			break;
+		}
+		if(continpc == P) {
+			yyerror("continue is not in a loop");
+			break;
+		}
+		gjmp(continpc);
+		break;
+
+	case OFOR:
+		sbreak = breakpc;
+		p1 = gjmp(P);			//		goto test
+		breakpc = gjmp(P);		// break:	goto done
+		scontin = continpc;
+		continpc = pc;
+
+		// define break and continue labels
+		if((lab = stmtlabel(n)) != L) {
+			lab->breakpc = breakpc;
+			lab->continpc = continpc;
+		}
+		gen(n->nincr);				// contin:	incr
+		patch(p1, pc);				// test:
+		bgen(n->ntest, 0, -1, breakpc);		//		if(!test) goto break
+		genlist(n->nbody);				//		body
+		gjmp(continpc);
+		patch(breakpc, pc);			// done:
+		continpc = scontin;
+		breakpc = sbreak;
+		if(lab) {
+			lab->breakpc = P;
+			lab->continpc = P;
+		}
+		break;
+
+	case OIF:
+		p1 = gjmp(P);			//		goto test
+		p2 = gjmp(P);			// p2:		goto else
+		patch(p1, pc);				// test:
+		bgen(n->ntest, 0, -n->likely, p2);		//		if(!test) goto p2
+		genlist(n->nbody);				//		then
+		p3 = gjmp(P);			//		goto done
+		patch(p2, pc);				// else:
+		genlist(n->nelse);				//		else
+		patch(p3, pc);				// done:
+		break;
+
+	case OSWITCH:
+		sbreak = breakpc;
+		p1 = gjmp(P);			//		goto test
+		breakpc = gjmp(P);		// break:	goto done
+
+		// define break label
+		if((lab = stmtlabel(n)) != L)
+			lab->breakpc = breakpc;
+
+		patch(p1, pc);				// test:
+		genlist(n->nbody);				//		switch(test) body
+		patch(breakpc, pc);			// done:
+		breakpc = sbreak;
+		if(lab != L)
+			lab->breakpc = P;
+		break;
+
+	case OSELECT:
+		sbreak = breakpc;
+		p1 = gjmp(P);			//		goto test
+		breakpc = gjmp(P);		// break:	goto done
+
+		// define break label
+		if((lab = stmtlabel(n)) != L)
+			lab->breakpc = breakpc;
+
+		patch(p1, pc);				// test:
+		genlist(n->nbody);				//		select() body
+		patch(breakpc, pc);			// done:
+		breakpc = sbreak;
+		if(lab != L)
+			lab->breakpc = P;
+		break;
+
+	case OASOP:
+		cgen_asop(n);
+		break;
+
+	case ODCL:
+		cgen_dcl(n->left);
+		break;
+
+	case OAS:
+		if(gen_as_init(n))
+			break;
+		cgen_as(n->left, n->right);
+		break;
+
+	case OCALLMETH:
+		cgen_callmeth(n, 0);
+		break;
+
+	case OCALLINTER:
+		cgen_callinter(n, N, 0);
+		break;
+
+	case OCALLFUNC:
+		cgen_call(n, 0);
+		break;
+
+	case OPROC:
+		cgen_proc(n, 1);
+		break;
+
+	case ODEFER:
+		cgen_proc(n, 2);
+		break;
+
+	case ORETURN:
+	case ORETJMP:
+		cgen_ret(n);
+		break;
+	
+	case OCHECKNIL:
+		cgen_checknil(n->left);
+		break;
+	
+	case OVARKILL:
+		gvarkill(n->left);
+		break;
+	}
+
+ret:
+	if(anyregalloc() != wasregalloc) {
+		dump("node", n);
+		fatal("registers left allocated");
+	}
+
+	lineno = lno;
+}
+
+/*
+ * generate call to non-interface method
+ *	proc=0	normal call
+ *	proc=1	goroutine run in new proc
+ *	proc=2	defer call save away stack
+ */
+void
+cgen_callmeth(Node *n, int proc)
+{
+	Node n2;
+	Node *l;
+
+	// generate a rewrite in n2 for the method call
+	// (p.f)(...) goes to (f)(p,...)
+
+	l = n->left;
+	if(l->op != ODOTMETH)
+		fatal("cgen_callmeth: not dotmethod: %N");
+
+	n2 = *n;
+	n2.op = OCALLFUNC;
+	n2.left = l->right;
+	n2.left->type = l->type;
+
+	if(n2.left->op == ONAME)
+		n2.left->class = PFUNC;
+	cgen_call(&n2, proc);
+}
+
+/*
+ * generate code to start new proc running call n.
+ */
+static void
+cgen_proc(Node *n, int proc)
+{
+	switch(n->left->op) {
+	default:
+		fatal("cgen_proc: unknown call %O", n->left->op);
+
+	case OCALLMETH:
+		cgen_callmeth(n->left, proc);
+		break;
+
+	case OCALLINTER:
+		cgen_callinter(n->left, N, proc);
+		break;
+
+	case OCALLFUNC:
+		cgen_call(n->left, proc);
+		break;
+	}
+
+}
+
+/*
+ * generate declaration.
+ * have to allocate heap copy
+ * for escaped variables.
+ */
+static void
+cgen_dcl(Node *n)
+{
+	if(debug['g'])
+		dump("\ncgen-dcl", n);
+	if(n->op != ONAME) {
+		dump("cgen_dcl", n);
+		fatal("cgen_dcl");
+	}
+	if(!(n->class & PHEAP))
+		return;
+	if(compiling_runtime)
+		yyerror("%N escapes to heap, not allowed in runtime.", n);
+	if(n->alloc == nil)
+		n->alloc = callnew(n->type);
+	cgen_as(n->heapaddr, n->alloc);
+}
+
+/*
+ * generate discard of value
+ */
+static void
+cgen_discard(Node *nr)
+{
+	Node tmp;
+
+	if(nr == N)
+		return;
+
+	switch(nr->op) {
+	case ONAME:
+		if(!(nr->class & PHEAP) && nr->class != PEXTERN && nr->class != PFUNC && nr->class != PPARAMREF)
+			gused(nr);
+		break;
+
+	// unary
+	case OADD:
+	case OAND:
+	case ODIV:
+	case OEQ:
+	case OGE:
+	case OGT:
+	case OLE:
+	case OLSH:
+	case OLT:
+	case OMOD:
+	case OMUL:
+	case ONE:
+	case OOR:
+	case ORSH:
+	case OSUB:
+	case OXOR:
+		cgen_discard(nr->left);
+		cgen_discard(nr->right);
+		break;
+
+	// binary
+	case OCAP:
+	case OCOM:
+	case OLEN:
+	case OMINUS:
+	case ONOT:
+	case OPLUS:
+		cgen_discard(nr->left);
+		break;
+	
+	case OIND:
+		cgen_checknil(nr->left);
+		break;
+
+	// special enough to just evaluate
+	default:
+		tempname(&tmp, nr->type);
+		cgen_as(&tmp, nr);
+		gused(&tmp);
+	}
+}
+
+/*
+ * clearslim generates code to zero a slim node.
+ */
+void
+clearslim(Node *n)
+{
+	Node z;
+	Mpflt zero;
+
+	memset(&z, 0, sizeof(z));
+	z.op = OLITERAL;
+	z.type = n->type;
+	z.addable = 1;
+
+	switch(simtype[n->type->etype]) {
+	case TCOMPLEX64:
+	case TCOMPLEX128:
+		z.val.u.cval = mal(sizeof(*z.val.u.cval));
+		mpmovecflt(&z.val.u.cval->real, 0.0);
+		mpmovecflt(&z.val.u.cval->imag, 0.0);
+		break;
+
+	case TFLOAT32:
+	case TFLOAT64:
+		mpmovecflt(&zero, 0.0);
+		z.val.ctype = CTFLT;
+		z.val.u.fval = &zero;
+		break;
+
+	case TPTR32:
+	case TPTR64:
+	case TCHAN:
+	case TMAP:
+		z.val.ctype = CTNIL;
+		break;
+
+	case TBOOL:
+		z.val.ctype = CTBOOL;
+		break;
+
+	case TINT8:
+	case TINT16:
+	case TINT32:
+	case TINT64:
+	case TUINT8:
+	case TUINT16:
+	case TUINT32:
+	case TUINT64:
+		z.val.ctype = CTINT;
+		z.val.u.xval = mal(sizeof(*z.val.u.xval));
+		mpmovecfix(z.val.u.xval, 0);
+		break;
+
+	default:
+		fatal("clearslim called on type %T", n->type);
+	}
+
+	ullmancalc(&z);
+	cgen(&z, n);
+}
+
+/*
+ * generate assignment:
+ *	nl = nr
+ * nr == N means zero nl.
+ */
+void
+cgen_as(Node *nl, Node *nr)
+{
+	Type *tl;
+
+	if(debug['g']) {
+		dump("cgen_as", nl);
+		dump("cgen_as = ", nr);
+	}
+
+	while(nr != N && nr->op == OCONVNOP)
+		nr = nr->left;
+
+	if(nl == N || isblank(nl)) {
+		cgen_discard(nr);
+		return;
+	}
+
+	if(nr == N || iszero(nr)) {
+		// heaps should already be clear
+		if(nr == N && (nl->class & PHEAP))
+			return;
+
+		tl = nl->type;
+		if(tl == T)
+			return;
+		if(isfat(tl)) {
+			if(nl->op == ONAME)
+				gvardef(nl);
+			clearfat(nl);
+			return;
+		}
+		clearslim(nl);
+		return;
+	}
+
+	tl = nl->type;
+	if(tl == T)
+		return;
+
+	cgen(nr, nl);
+}
+
+/*
+ * generate:
+ *	res = iface{typ, data}
+ * n->left is typ
+ * n->right is data
+ */
+void
+cgen_eface(Node *n, Node *res)
+{
+	/* 
+	 * the right node of an eface may contain function calls that uses res as an argument,
+	 * so it's important that it is done first
+	 */
+	Node dst;
+	Node *tmp;
+
+	tmp = temp(types[tptr]);
+	cgen(n->right, tmp);
+
+	gvardef(res);
+
+	dst = *res;
+	dst.type = types[tptr];
+	dst.xoffset += widthptr;
+	cgen(tmp, &dst);
+
+	dst.xoffset -= widthptr;
+	cgen(n->left, &dst);
+}
+
+/*
+ * generate:
+ *	res = s[lo, hi];
+ * n->left is s
+ * n->list is (cap(s)-lo(TUINT), hi-lo(TUINT)[, lo*width(TUINTPTR)])
+ * caller (cgen) guarantees res is an addable ONAME.
+ *
+ * called for OSLICE, OSLICE3, OSLICEARR, OSLICE3ARR, OSLICESTR.
+ */
+void
+cgen_slice(Node *n, Node *res)
+{
+	Node src, dst, *cap, *len, *offs, *add, *base, *tmpcap, *tmplen, *cmp, con;
+	Prog *p1, *p2;
+
+	cap = n->list->n;
+	len = n->list->next->n;
+	offs = N;
+	if(n->list->next->next)
+		offs = n->list->next->next->n;
+
+	// evaluate base pointer first, because it is the only
+	// possibly complex expression. once that is evaluated
+	// and stored, updating the len and cap can be done
+	// without making any calls, so without doing anything that
+	// might cause preemption or garbage collection.
+	// this makes the whole slice update atomic as far as the
+	// garbage collector can see.
+	
+	base = temp(types[TUINTPTR]);
+	tmplen = temp(types[TINT]);
+	if(n->op != OSLICESTR)
+		tmpcap = temp(types[TINT]);
+	else
+		tmpcap = tmplen;
+
+	if(isnil(n->left)) {
+		tempname(&src, n->left->type);
+		cgen(n->left, &src);
+	} else
+		src = *n->left;
+	if(n->op == OSLICE || n->op == OSLICE3 || n->op == OSLICESTR)
+		src.xoffset += Array_array;
+
+	if(n->op == OSLICEARR || n->op == OSLICE3ARR) {
+		if(!isptr[n->left->type->etype])
+			fatal("slicearr is supposed to work on pointer: %+N\n", n);
+		cgen(&src, base);
+		cgen_checknil(base);
+	} else {
+		src.type = types[tptr];
+		cgen(&src, base);
+	}
+	
+	// committed to the update
+	gvardef(res);
+
+	// compute len and cap.
+	// len = n-i, cap = m-i, and offs = i*width.
+	// computing offs last lets the multiply overwrite i.
+	cgen(len, tmplen);
+	if(n->op != OSLICESTR)
+		cgen(cap, tmpcap);
+
+	// if new cap != 0 { base += add }
+	// This avoids advancing base past the end of the underlying array/string,
+	// so that it cannot point at the next object in memory.
+	// If cap == 0, the base doesn't matter except insofar as it is 0 or non-zero.
+	// In essence we are replacing x[i:j:k] where i == j == k
+	// or x[i:j] where i == j == cap(x) with x[0:0:0].
+	if(offs != N) {
+		p1 = gjmp(P);
+		p2 = gjmp(P);
+		patch(p1, pc);
+
+		nodconst(&con, tmpcap->type, 0);
+		cmp = nod(OEQ, tmpcap, &con);
+		typecheck(&cmp, Erv);
+		bgen(cmp, 1, -1, p2);
+
+		add = nod(OADD, base, offs);
+		typecheck(&add, Erv);
+		cgen(add, base);
+
+		patch(p2, pc);
+	}
+
+	// dst.array = src.array  [ + lo *width ]
+	dst = *res;
+	dst.xoffset += Array_array;
+	dst.type = types[tptr];
+	cgen(base, &dst);
+
+	// dst.len = hi [ - lo ]
+	dst = *res;
+	dst.xoffset += Array_nel;
+	dst.type = types[simtype[TUINT]];
+	cgen(tmplen, &dst);
+
+	if(n->op != OSLICESTR) {
+		// dst.cap = cap [ - lo ]
+		dst = *res;
+		dst.xoffset += Array_cap;
+		dst.type = types[simtype[TUINT]];
+		cgen(tmpcap, &dst);
+	}
+}
+
+/*
+ * gather series of offsets
+ * >=0 is direct addressed field
+ * <0 is pointer to next field (+1)
+ */
+int
+dotoffset(Node *n, int64 *oary, Node **nn)
+{
+	int i;
+
+	switch(n->op) {
+	case ODOT:
+		if(n->xoffset == BADWIDTH) {
+			dump("bad width in dotoffset", n);
+			fatal("bad width in dotoffset");
+		}
+		i = dotoffset(n->left, oary, nn);
+		if(i > 0) {
+			if(oary[i-1] >= 0)
+				oary[i-1] += n->xoffset;
+			else
+				oary[i-1] -= n->xoffset;
+			break;
+		}
+		if(i < 10)
+			oary[i++] = n->xoffset;
+		break;
+
+	case ODOTPTR:
+		if(n->xoffset == BADWIDTH) {
+			dump("bad width in dotoffset", n);
+			fatal("bad width in dotoffset");
+		}
+		i = dotoffset(n->left, oary, nn);
+		if(i < 10)
+			oary[i++] = -(n->xoffset+1);
+		break;
+
+	default:
+		*nn = n;
+		return 0;
+	}
+	if(i >= 10)
+		*nn = N;
+	return i;
+}
+
+/*
+ * make a new off the books
+ */
+void
+tempname(Node *nn, Type *t)
+{
+	Node *n;
+	Sym *s;
+
+	if(curfn == N)
+		fatal("no curfn for tempname");
+
+	if(t == T) {
+		yyerror("tempname called with nil type");
+		t = types[TINT32];
+	}
+
+	// give each tmp a different name so that there
+	// a chance to registerizer them
+	snprint(namebuf, sizeof(namebuf), "autotmp_%.4d", statuniqgen);
+	statuniqgen++;
+	s = lookup(namebuf);
+	n = nod(ONAME, N, N);
+	n->sym = s;
+	s->def = n;
+	n->type = t;
+	n->class = PAUTO;
+	n->addable = 1;
+	n->ullman = 1;
+	n->esc = EscNever;
+	n->curfn = curfn;
+	curfn->dcl = list(curfn->dcl, n);
+
+	dowidth(t);
+	n->xoffset = 0;
+	*nn = *n;
+}
+
+Node*
+temp(Type *t)
+{
+	Node *n;
+	
+	n = nod(OXXX, N, N);
+	tempname(n, t);
+	n->sym->def->used = 1;
+	return n->orig;
+}
diff --git a/src/cmd/gc/go.errors b/src/cmd/gc/go.errors
new file mode 100644
index 0000000..f90d619
--- /dev/null
+++ b/src/cmd/gc/go.errors
@@ -0,0 +1,79 @@
+// Copyright 2010 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Example-based syntax error messages.
+// See bisonerrors, Makefile, go.y.
+
+static struct {
+	int yystate;
+	int yychar;
+	char *msg;
+} yymsg[] = {
+	// Each line of the form % token list
+	// is converted by bisonerrors into the yystate and yychar caused
+	// by that token list.
+
+	% loadsys package LIMPORT '(' LLITERAL import_package import_there ','
+	"unexpected comma during import block",
+
+	% loadsys package LIMPORT LNAME ';'
+	"missing import path; require quoted string",
+
+	% loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header ';'
+	"missing { after if clause",
+
+	% loadsys package imports LFUNC LNAME '(' ')' '{' LSWITCH if_header ';'
+	"missing { after switch clause",
+
+	% loadsys package imports LFUNC LNAME '(' ')' '{' LFOR for_header ';'
+	"missing { after for clause",
+
+	% loadsys package imports LFUNC LNAME '(' ')' '{' LFOR ';' LBODY
+	"missing { after for clause",
+
+	% loadsys package imports LFUNC LNAME '(' ')' ';' '{'
+	"unexpected semicolon or newline before {",
+
+	% loadsys package imports LTYPE LNAME ';'
+	"unexpected semicolon or newline in type declaration",
+
+	% loadsys package imports LCHAN '}'
+	"unexpected } in channel type",
+	
+	% loadsys package imports LCHAN ')'
+	"unexpected ) in channel type",
+	
+	% loadsys package imports LCHAN ','
+	"unexpected comma in channel type",
+
+	% loadsys package imports LFUNC LNAME '(' ')' '{' if_stmt ';' LELSE
+	"unexpected semicolon or newline before else",
+
+	% loadsys package imports LTYPE LNAME LINTERFACE '{' LNAME ',' LNAME
+	"name list not allowed in interface type",
+
+	% loadsys package imports LFUNC LNAME '(' ')' '{' LFOR LVAR LNAME '=' LNAME
+	"var declaration not allowed in for initializer",
+
+	% loadsys package imports LVAR LNAME '[' ']' LNAME '{'
+	"unexpected { at end of statement",
+
+	% loadsys package imports LFUNC LNAME '(' ')' '{' LVAR LNAME '[' ']' LNAME '{'
+	"unexpected { at end of statement",
+	
+	% loadsys package imports LFUNC LNAME '(' ')' '{' LDEFER LNAME ';'
+	"argument to go/defer must be function call",
+	
+	% loadsys package imports LVAR LNAME '=' LNAME '{' LNAME ';'
+	"need trailing comma before newline in composite literal",
+	
+	% loadsys package imports LVAR LNAME '=' comptype '{' LNAME ';'
+	"need trailing comma before newline in composite literal",
+	
+	% loadsys package imports LFUNC LNAME '(' ')' '{' LFUNC LNAME
+	"nested func not allowed",
+
+	% loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header loop_body LELSE ';'
+	"else must be followed by if or statement block"
+};
diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h
new file mode 100644
index 0000000..bbb8835
--- /dev/null
+++ b/src/cmd/gc/go.h
@@ -0,0 +1,1555 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include	<bio.h>
+#include	<link.h>
+
+#undef OAPPEND
+
+// avoid <ctype.h>
+#undef isblank
+#define isblank goisblank
+
+#ifndef	EXTERN
+#define	EXTERN	extern
+#endif
+
+#undef	BUFSIZ
+
+// The parser's maximum stack size.
+// We have to use a #define macro here since yacc
+// or bison will check for its definition and use
+// a potentially smaller value if it is undefined.
+#define YYMAXDEPTH 500
+
+enum
+{
+	NHUNK		= 50000,
+	BUFSIZ		= 8192,
+	NSYMB		= 500,
+	NHASH		= 1024,
+	STRINGSZ	= 200,
+	MAXALIGN	= 7,
+	UINF		= 100,
+
+	PRIME1		= 3,
+
+	AUNK		= 100,
+
+	// These values are known by runtime.
+	// The MEMx and NOEQx values must run in parallel.  See algtype.
+	AMEM		= 0,
+	AMEM0,
+	AMEM8,
+	AMEM16,
+	AMEM32,
+	AMEM64,
+	AMEM128,
+	ANOEQ,
+	ANOEQ0,
+	ANOEQ8,
+	ANOEQ16,
+	ANOEQ32,
+	ANOEQ64,
+	ANOEQ128,
+	ASTRING,
+	AINTER,
+	ANILINTER,
+	ASLICE,
+	AFLOAT32,
+	AFLOAT64,
+	ACPLX64,
+	ACPLX128,
+
+	BADWIDTH	= -1000000000,
+	
+	MaxStackVarSize = 10*1024*1024,
+};
+
+extern vlong	MAXWIDTH;
+
+/*
+ * note this is the representation
+ * of the compilers string literals,
+ * it is not the runtime representation
+ */
+typedef	struct	Strlit	Strlit;
+struct	Strlit
+{
+	int32	len;
+	char	s[1]; // variable
+};
+
+enum
+{
+	Mpscale	= 29,		// safely smaller than bits in a long
+	Mpprec	= 16,		// Mpscale*Mpprec is max number of bits
+	Mpnorm	= Mpprec - 1,	// significant words in a normalized float
+	Mpbase	= 1L << Mpscale,
+	Mpsign	= Mpbase >> 1,
+	Mpmask	= Mpbase - 1,
+	Mpdebug	= 0,
+};
+
+typedef	struct	Mpint	Mpint;
+struct	Mpint
+{
+	long	a[Mpprec];
+	uchar	neg;
+	uchar	ovf;
+};
+
+typedef	struct	Mpflt	Mpflt;
+struct	Mpflt
+{
+	Mpint	val;
+	short	exp;
+};
+
+typedef	struct	Mpcplx	Mpcplx;
+struct	Mpcplx
+{
+	Mpflt	real;
+	Mpflt	imag;
+};
+
+typedef	struct	Val	Val;
+struct	Val
+{
+	short	ctype;
+	union
+	{
+		short	reg;		// OREGISTER
+		short	bval;		// bool value CTBOOL
+		Mpint*	xval;		// int CTINT, rune CTRUNE
+		Mpflt*	fval;		// float CTFLT
+		Mpcplx*	cval;		// float CTCPLX
+		Strlit*	sval;		// string CTSTR
+	} u;
+};
+
+// prevent incompatible type signatures between libgc and 8g on Plan 9
+#pragma incomplete struct Array
+
+typedef	struct	Array	Array;
+typedef	struct	Bvec	Bvec;
+typedef	struct	Pkg Pkg;
+typedef	struct	Sym	Sym;
+typedef	struct	Node	Node;
+typedef	struct	NodeList	NodeList;
+typedef	struct	Type	Type;
+typedef	struct	Label	Label;
+
+struct	Type
+{
+	uchar	etype;
+	uchar	nointerface;
+	uchar	noalg;
+	uchar	chan;
+	uchar	trecur;		// to detect loops
+	uchar	printed;
+	uchar	embedded;	// TFIELD embedded type
+	uchar	siggen;
+	uchar	funarg;		// on TSTRUCT and TFIELD
+	uchar	copyany;
+	uchar	local;		// created in this file
+	uchar	deferwidth;
+	uchar	broke;  	// broken type definition.
+	uchar	isddd;		// TFIELD is ... argument
+	uchar	align;
+	uchar	haspointers;	// 0 unknown, 1 no, 2 yes
+
+	Node*	nod;		// canonical OTYPE node
+	Type*	orig;		// original type (type literal or predefined type)
+	int		lineno;
+
+	// TFUNC
+	int	thistuple;
+	int	outtuple;
+	int	intuple;
+	uchar	outnamed;
+
+	Type*	method;
+	Type*	xmethod;
+
+	Sym*	sym;
+	int32	vargen;		// unique name for OTYPE/ONAME
+
+	Node*	nname;
+	vlong	argwid;
+
+	// most nodes
+	Type*	type;   	// actual type for TFIELD, element type for TARRAY, TCHAN, TMAP, TPTRxx
+	vlong	width;  	// offset in TFIELD, width in all others
+
+	// TFIELD
+	Type*	down;		// next struct field, also key type in TMAP
+	Type*	outer;		// outer struct
+	Strlit*	note;		// literal string annotation
+
+	// TARRAY
+	vlong	bound;		// negative is dynamic array
+
+	// TMAP
+	Type*	bucket;		// internal type representing a hash bucket
+	Type*	hmap;		// internal type representing a Hmap (map header object)
+	Type*	hiter;		// internal type representing hash iterator state
+	Type*	map;		// link from the above 3 internal types back to the map type.
+
+	int32	maplineno;	// first use of TFORW as map key
+	int32	embedlineno;	// first use of TFORW as embedded type
+	
+	// for TFORW, where to copy the eventual value to
+	NodeList	*copyto;
+	
+	Node	*lastfn;	// for usefield
+};
+#define	T	((Type*)0)
+
+typedef struct InitEntry InitEntry;
+typedef struct InitPlan InitPlan;
+
+struct InitEntry
+{
+	vlong xoffset;  // struct, array only
+	Node *key;  // map only
+	Node *expr;
+};
+
+struct InitPlan
+{
+	vlong lit;  // bytes of initialized non-zero literals
+	vlong zero;  // bytes of zeros
+	vlong expr;  // bytes of run-time computed expressions
+
+	InitEntry *e;
+	int len;
+	int cap;
+};
+
+enum
+{
+	EscUnknown,
+	EscHeap,
+	EscScope,
+	EscNone,
+	EscReturn,
+	EscNever,
+	EscBits = 3,
+	EscMask = (1<<EscBits) - 1,
+	EscContentEscapes = 1<<EscBits, // value obtained by indirect of parameter escapes to some returned result
+	EscReturnBits = EscBits+1,
+};
+
+struct	Node
+{
+	// Tree structure.
+	// Generic recursive walks should follow these fields.
+	Node*	left;
+	Node*	right;
+	Node*	ntest;
+	Node*	nincr;
+	NodeList*	ninit;
+	NodeList*	nbody;
+	NodeList*	nelse;
+	NodeList*	list;
+	NodeList*	rlist;
+
+	uchar	op;
+	uchar	nointerface;
+	uchar	ullman;		// sethi/ullman number
+	uchar	addable;	// type of addressability - 0 is not addressable
+	uchar	trecur;		// to detect loops
+	uchar	etype;		// op for OASOP, etype for OTYPE, exclam for export
+	uchar	bounded;	// bounds check unnecessary
+	uchar	class;		// PPARAM, PAUTO, PEXTERN, etc
+	uchar	method;		// OCALLMETH name
+	uchar	embedded;	// ODCLFIELD embedded type
+	uchar	colas;		// OAS resulting from :=
+	uchar	diag;		// already printed error about this
+	uchar	noescape;	// func arguments do not escape
+	uchar	nosplit;	// func should not execute on separate stack
+	uchar	builtin;	// built-in name, like len or close
+	uchar	walkdef;
+	uchar	typecheck;
+	uchar	local;
+	uchar	dodata;
+	uchar	initorder;
+	uchar	used;
+	uchar	isddd;
+	uchar	readonly;
+	uchar	implicit;
+	uchar	addrtaken;	// address taken, even if not moved to heap
+	uchar	dupok;	// duplicate definitions ok (for func)
+	uchar	wrapper;	// is method wrapper (for func)
+	uchar	reslice;	// this is a reslice x = x[0:y] or x = append(x, ...)
+	schar	likely; // likeliness of if statement
+	uchar	hasbreak;	// has break statement
+	uchar	needzero; // if it contains pointers, needs to be zeroed on function entry
+	uchar	needctxt;	// function uses context register (has closure variables)
+	uint	esc;		// EscXXX
+	int	funcdepth;
+
+	// most nodes
+	Type*	type;
+	Node*	orig;		// original form, for printing, and tracking copies of ONAMEs
+
+	// func
+	Node*	nname;
+	Node*	shortname;
+	NodeList*	enter;
+	NodeList*	exit;
+	NodeList*	cvars;	// closure params
+	NodeList*	dcl;	// autodcl for this func/closure
+	NodeList*	inl;	// copy of the body for use in inlining
+	NodeList*	inldcl;	// copy of dcl for use in inlining
+
+	// OLITERAL/OREGISTER
+	Val	val;
+
+	// ONAME
+	Node*	ntype;
+	Node*	defn;	// ONAME: initializing assignment; OLABEL: labeled statement
+	Node*	pack;	// real package for import . names
+	Node*	curfn;	// function for local variables
+	Type*	paramfld; // TFIELD for this PPARAM; also for ODOT, curfn
+
+	// ONAME func param with PHEAP
+	Node*	heapaddr;	// temp holding heap address of param
+	Node*	stackparam;	// OPARAM node referring to stack copy of param
+	Node*	alloc;	// allocation call
+
+	// ONAME closure param with PPARAMREF
+	Node*	outer;	// outer PPARAMREF in nested closure
+	Node*	closure;	// ONAME/PHEAP <-> ONAME/PPARAMREF
+
+	// ONAME substitute while inlining
+	Node* inlvar;
+
+	// OPACK
+	Pkg*	pkg;
+	
+	// OARRAYLIT, OMAPLIT, OSTRUCTLIT.
+	InitPlan*	initplan;
+
+	// Escape analysis.
+	NodeList* escflowsrc;	// flow(this, src)
+	NodeList* escretval;	// on OCALLxxx, list of dummy return values
+	int	escloopdepth;	// -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
+
+	Sym*	sym;		// various
+	int32	vargen;		// unique name for OTYPE/ONAME
+	int32	lineno;
+	int32	endlineno;
+	vlong	xoffset;
+	vlong	stkdelta;	// offset added by stack frame compaction phase.
+	int32	ostk;
+	int32	iota;
+	uint32	walkgen;
+	int32	esclevel;
+	void*	opt;	// for optimization passes
+};
+#define	N	((Node*)0)
+
+/*
+ * Every node has a walkgen field.
+ * If you want to do a traversal of a node graph that
+ * might contain duplicates and want to avoid
+ * visiting the same nodes twice, increment walkgen
+ * before starting.  Then before processing a node, do
+ *
+ *	if(n->walkgen == walkgen)
+ *		return;
+ *	n->walkgen = walkgen;
+ *
+ * Such a walk cannot call another such walk recursively,
+ * because of the use of the global walkgen.
+ */
+EXTERN	uint32	walkgen;
+
+struct	NodeList
+{
+	Node*	n;
+	NodeList*	next;
+	NodeList*	end;
+};
+
+enum
+{
+	SymExport	= 1<<0,	// to be exported
+	SymPackage	= 1<<1,
+	SymExported	= 1<<2,	// already written out by export
+	SymUniq		= 1<<3,
+	SymSiggen	= 1<<4,
+};
+
+struct	Sym
+{
+	ushort	lexical;
+	uchar	flags;
+	uchar	sym;		// huffman encoding in object file
+	Sym*	link;
+	int32	npkg;	// number of imported packages with this name
+	uint32	uniqgen;
+	Pkg*	importdef;	// where imported definition was found
+
+	// saved and restored by dcopy
+	Pkg*	pkg;
+	char*	name;		// variable name
+	Node*	def;		// definition: ONAME OTYPE OPACK or OLITERAL
+	Label*	label;	// corresponding label (ephemeral)
+	int32	block;		// blocknumber to catch redeclaration
+	int32	lastlineno;	// last declaration for diagnostic
+	Pkg*	origpkg;	// original package for . import
+	LSym*	lsym;
+};
+#define	S	((Sym*)0)
+
+EXTERN	Sym*	dclstack;
+
+struct	Pkg
+{
+	char*	name;		// package name
+	Strlit*	path;		// string literal used in import statement
+	Sym*	pathsym;
+	char*	prefix;		// escaped path for use in symbol table
+	Pkg*	link;
+	uchar	imported;	// export data of this package was parsed
+	char	exported;	// import line written in export data
+	char	direct;	// imported directly
+	char	safe;	// whether the package is marked as safe
+};
+
+typedef	struct	Iter	Iter;
+struct	Iter
+{
+	int	done;
+	Type*	tfunc;
+	Type*	t;
+	Node**	an;
+	Node*	n;
+};
+
+// Node ops.
+enum
+{
+	OXXX,
+
+	// names
+	ONAME,	// var, const or func name
+	ONONAME,	// unnamed arg or return value: f(int, string) (int, error) { etc }
+	OTYPE,	// type name
+	OPACK,	// import
+	OLITERAL, // literal
+
+	// expressions
+	OADD,	// x + y
+	OSUB,	// x - y
+	OOR,	// x | y
+	OXOR,	// x ^ y
+	OADDSTR,	// s + "foo"
+	OADDR,	// &x
+	OANDAND,	// b0 && b1
+	OAPPEND,	// append
+	OARRAYBYTESTR,	// string(bytes)
+	OARRAYBYTESTRTMP, // string(bytes) ephemeral
+	OARRAYRUNESTR,	// string(runes)
+	OSTRARRAYBYTE,	// []byte(s)
+	OSTRARRAYRUNE,	// []rune(s)
+	OAS,	// x = y or x := y
+	OAS2,	// x, y, z = xx, yy, zz
+	OAS2FUNC,	// x, y = f()
+	OAS2RECV,	// x, ok = <-c
+	OAS2MAPR,	// x, ok = m["foo"]
+	OAS2DOTTYPE,	// x, ok = I.(int)
+	OASOP,	// x += y
+	OCALL,	// function call, method call or type conversion, possibly preceded by defer or go.
+	OCALLFUNC,	// f()
+	OCALLMETH,	// t.Method()
+	OCALLINTER,	// err.Error()
+	OCALLPART,	// t.Method (without ())
+	OCAP,	// cap
+	OCLOSE,	// close
+	OCLOSURE,	// f = func() { etc }
+	OCMPIFACE,	// err1 == err2
+	OCMPSTR,	// s1 == s2
+	OCOMPLIT,	// composite literal, typechecking may convert to a more specific OXXXLIT.
+	OMAPLIT,	// M{"foo":3, "bar":4}
+	OSTRUCTLIT,	// T{x:3, y:4}
+	OARRAYLIT,	// [2]int{3, 4}
+	OPTRLIT,	// &T{x:3, y:4}
+	OCONV,	// var i int; var u uint; i = int(u)
+	OCONVIFACE,	// I(t)
+	OCONVNOP,	// type Int int; var i int; var j Int; i = int(j)
+	OCOPY,	// copy
+	ODCL,	// var x int
+	ODCLFUNC,	// func f() or func (r) f()
+	ODCLFIELD,	// struct field, interface field, or func/method argument/return value.
+	ODCLCONST,	// const pi = 3.14
+	ODCLTYPE,	// type Int int
+	ODELETE,	// delete
+	ODOT,	// t.x
+	ODOTPTR,	// p.x that is implicitly (*p).x
+	ODOTMETH,	// t.Method
+	ODOTINTER,	// err.Error
+	OXDOT,	// t.x, typechecking may convert to a more specific ODOTXXX.
+	ODOTTYPE,	// e = err.(MyErr)
+	ODOTTYPE2,	// e, ok = err.(MyErr)
+	OEQ,	// x == y
+	ONE,	// x != y
+	OLT,	// x < y
+	OLE,	// x <= y
+	OGE,	// x >= y
+	OGT,	// x > y
+	OIND,	// *p
+	OINDEX,	// a[i]
+	OINDEXMAP,	// m[s]
+	OKEY,	// The x:3 in t{x:3, y:4}, the 1:2 in a[1:2], the 2:20 in [3]int{2:20}, etc.
+	OPARAM,	// The on-stack copy of a parameter or return value that escapes.
+	OLEN,	// len
+	OMAKE,	// make, typechecking may convert to a more specific OMAKEXXX.
+	OMAKECHAN,	// make(chan int)
+	OMAKEMAP,	// make(map[string]int)
+	OMAKESLICE,	// make([]int, 0)
+	OMUL,	// x * y
+	ODIV,	// x / y
+	OMOD,	// x % y
+	OLSH,	// x << u
+	ORSH,	// x >> u
+	OAND,	// x & y
+	OANDNOT,	// x &^ y
+	ONEW,	// new
+	ONOT,	// !b
+	OCOM,	// ^x
+	OPLUS,	// +x
+	OMINUS,	// -y
+	OOROR,	// b1 || b2
+	OPANIC,	// panic
+	OPRINT,	// print
+	OPRINTN,	// println
+	OPAREN,	// (x)
+	OSEND,	// c <- x
+	OSLICE,	// v[1:2], typechecking may convert to a more specific OSLICEXXX.
+	OSLICEARR,	// a[1:2]
+	OSLICESTR,	// s[1:2]
+	OSLICE3,	// v[1:2:3], typechecking may convert to OSLICE3ARR.
+	OSLICE3ARR,	// a[1:2:3]
+	ORECOVER,	// recover
+	ORECV,	// <-c
+	ORUNESTR,	// string(i)
+	OSELRECV,	// case x = <-c:
+	OSELRECV2,	// case x, ok = <-c:
+	OIOTA,	// iota
+	OREAL,	// real
+	OIMAG,	// imag
+	OCOMPLEX,	// complex
+
+	// statements
+	OBLOCK,	// block of code
+	OBREAK,	// break
+	OCASE,	// case, after being verified by swt.c's casebody.
+	OXCASE,	// case, before verification.
+	OCONTINUE,	// continue
+	ODEFER,	// defer
+	OEMPTY,	// no-op
+	OFALL,	// fallthrough, after being verified by swt.c's casebody.
+	OXFALL,	// fallthrough, before verification.
+	OFOR,	// for
+	OGOTO,	// goto
+	OIF,	// if
+	OLABEL,	// label:
+	OPROC,	// go
+	ORANGE,	// range
+	ORETURN,	// return
+	OSELECT,	// select
+	OSWITCH,	// switch x
+	OTYPESW,	// switch err.(type)
+
+	// types
+	OTCHAN,	// chan int
+	OTMAP,	// map[string]int
+	OTSTRUCT,	// struct{}
+	OTINTER,	// interface{}
+	OTFUNC,	// func()
+	OTARRAY,	// []int, [8]int, [N]int or [...]int
+
+	// misc
+	ODDD,	// func f(args ...int) or f(l...) or var a = [...]int{0, 1, 2}.
+	ODDDARG,	// func f(args ...int), introduced by escape analysis.
+	OINLCALL,	// intermediary representation of an inlined call.
+	OEFACE,	// itable and data words of an empty-interface value.
+	OITAB,	// itable word of an interface value.
+	OSPTR,  // base pointer of a slice or string.
+	OCLOSUREVAR, // variable reference at beginning of closure function
+	OCFUNC,	// reference to c function pointer (not go func value)
+	OCHECKNIL, // emit code to ensure pointer/interface not nil
+	OVARKILL, // variable is dead
+
+	// arch-specific registers
+	OREGISTER,	// a register, such as AX.
+	OINDREG,	// offset plus indirect of a register, such as 8(SP).
+
+	// 386/amd64-specific opcodes
+	OCMP,	// compare: ACMP.
+	ODEC,	// decrement: ADEC.
+	OINC,	// increment: AINC.
+	OEXTEND,	// extend: ACWD/ACDQ/ACQO.
+	OHMUL, // high mul: AMUL/AIMUL for unsigned/signed (OMUL uses AIMUL for both).
+	OLROT,	// left rotate: AROL.
+	ORROTC, // right rotate-carry: ARCR.
+	ORETJMP,	// return to other function
+
+	OEND,
+};
+
+enum
+{
+	Txxx,			// 0
+
+	TINT8,	TUINT8,		// 1
+	TINT16,	TUINT16,
+	TINT32,	TUINT32,
+	TINT64,	TUINT64,
+	TINT, TUINT, TUINTPTR,
+
+	TCOMPLEX64,		// 12
+	TCOMPLEX128,
+
+	TFLOAT32,		// 14
+	TFLOAT64,
+
+	TBOOL,			// 16
+
+	TPTR32, TPTR64,		// 17
+
+	TFUNC,			// 19
+	TARRAY,
+	T_old_DARRAY,
+	TSTRUCT,		// 22
+	TCHAN,
+	TMAP,
+	TINTER,			// 25
+	TFORW,
+	TFIELD,
+	TANY,
+	TSTRING,
+	TUNSAFEPTR,
+
+	// pseudo-types for literals
+	TIDEAL,			// 31
+	TNIL,
+	TBLANK,
+
+	// pseudo-type for frame layout
+	TFUNCARGS,
+	TCHANARGS,
+	TINTERMETH,
+
+	NTYPE,
+};
+
+enum
+{
+	CTxxx,
+
+	CTINT,
+	CTRUNE,
+	CTFLT,
+	CTCPLX,
+	CTSTR,
+	CTBOOL,
+	CTNIL,
+};
+
+enum
+{
+	/* types of channel */
+	/* must match ../../pkg/nreflect/type.go:/Chandir */
+	Cxxx,
+	Crecv = 1<<0,
+	Csend = 1<<1,
+	Cboth = Crecv | Csend,
+};
+
+// declaration context
+enum
+{
+	Pxxx,
+
+	PEXTERN,	// global variable
+	PAUTO,		// local variables
+	PPARAM,		// input arguments
+	PPARAMOUT,	// output results
+	PPARAMREF,	// closure variable reference
+	PFUNC,		// global function
+
+	PDISCARD,	// discard during parse of duplicate import
+
+	PHEAP = 1<<7,	// an extra bit to identify an escaped variable
+};
+
+enum
+{
+	Etop = 1<<1,		// evaluated at statement level
+	Erv = 1<<2,		// evaluated in value context
+	Etype = 1<<3,
+	Ecall = 1<<4,		// call-only expressions are ok
+	Efnstruct = 1<<5,	// multivalue function returns are ok
+	Eiota = 1<<6,		// iota is ok
+	Easgn = 1<<7,		// assigning to expression
+	Eindir = 1<<8,		// indirecting through expression
+	Eaddr = 1<<9,		// taking address of expression
+	Eproc = 1<<10,		// inside a go statement
+	Ecomplit = 1<<11,	// type in composite literal
+};
+
+#define	BITS	5
+#define	NVAR	(BITS*sizeof(uint32)*8)
+
+typedef	struct	Bits	Bits;
+struct	Bits
+{
+	uint32	b[BITS];
+};
+
+EXTERN	Bits	zbits;
+
+struct Bvec
+{
+	int32	n;	// number of bits
+	uint32	b[];
+};
+
+typedef	struct	Var	Var;
+struct	Var
+{
+	vlong	offset;
+	Node*	node;
+	Var*	nextinnode;
+	int	width;
+	char	name;
+	char	etype;
+	char	addr;
+};
+
+EXTERN	Var	var[NVAR];
+
+typedef	struct	Typedef	Typedef;
+struct	Typedef
+{
+	char*	name;
+	int	etype;
+	int	sameas;
+};
+
+extern	Typedef	typedefs[];
+
+typedef	struct	Sig	Sig;
+struct	Sig
+{
+	char*	name;
+	Pkg*	pkg;
+	Sym*	isym;
+	Sym*	tsym;
+	Type*	type;
+	Type*	mtype;
+	int32	offset;
+	Sig*	link;
+};
+
+typedef	struct	Io	Io;
+struct	Io
+{
+	char*	infile;
+	Biobuf*	bin;
+	int32	ilineno;
+	int	nlsemi;
+	int	eofnl;
+	int	last;
+	int	peekc;
+	int	peekc1;	// second peekc for ...
+	char*	cp;	// used for content when bin==nil
+	int	importsafe;
+};
+
+typedef	struct	Dlist	Dlist;
+struct	Dlist
+{
+	Type*	field;
+};
+
+typedef	struct	Idir	Idir;
+struct Idir
+{
+	Idir*	link;
+	char*	dir;
+};
+
+/*
+ * argument passing to/from
+ * smagic and umagic
+ */
+typedef	struct	Magic Magic;
+struct	Magic
+{
+	int	w;	// input for both - width
+	int	s;	// output for both - shift
+	int	bad;	// output for both - unexpected failure
+
+	// magic multiplier for signed literal divisors
+	int64	sd;	// input - literal divisor
+	int64	sm;	// output - multiplier
+
+	// magic multiplier for unsigned literal divisors
+	uint64	ud;	// input - literal divisor
+	uint64	um;	// output - multiplier
+	int	ua;	// output - adder
+};
+
+struct	Label
+{
+	uchar	used;
+	Sym*	sym;
+	Node*	def;
+	NodeList*	use;
+	Label*	link;
+	
+	// for use during gen
+	Prog*	gotopc;	// pointer to unresolved gotos
+	Prog*	labelpc;	// pointer to code
+	Prog*	breakpc;	// pointer to code
+	Prog*	continpc;	// pointer to code
+};
+#define	L	((Label*)0)
+
+/*
+ * note this is the runtime representation
+ * of the compilers arrays.
+ *
+ * typedef	struct
+ * {				// must not move anything
+ *	uchar	array[8];	// pointer to data
+ *	uchar	nel[4];		// number of elements
+ *	uchar	cap[4];		// allocated number of elements
+ * } Array;
+ */
+EXTERN	int	Array_array;	// runtime offsetof(Array,array) - same for String
+EXTERN	int	Array_nel;	// runtime offsetof(Array,nel) - same for String
+EXTERN	int	Array_cap;	// runtime offsetof(Array,cap)
+EXTERN	int	sizeof_Array;	// runtime sizeof(Array)
+
+
+/*
+ * note this is the runtime representation
+ * of the compilers strings.
+ *
+ * typedef	struct
+ * {				// must not move anything
+ *	uchar	array[8];	// pointer to data
+ *	uchar	nel[4];		// number of elements
+ * } String;
+ */
+EXTERN	int	sizeof_String;	// runtime sizeof(String)
+
+EXTERN	Dlist	dotlist[10];	// size is max depth of embeddeds
+
+EXTERN	Io	curio;
+EXTERN	Io	pushedio;
+EXTERN	int32	lexlineno;
+EXTERN	int32	lineno;
+EXTERN	int32	prevlineno;
+
+EXTERN	char*	infile;
+EXTERN	char*	outfile;
+EXTERN	Biobuf*	bout;
+EXTERN	int	nerrors;
+EXTERN	int	nsavederrors;
+EXTERN	int	nsyntaxerrors;
+EXTERN	int	safemode;
+EXTERN	int	nolocalimports;
+EXTERN	char	namebuf[NSYMB];
+EXTERN	char	lexbuf[NSYMB];
+EXTERN	char	litbuf[NSYMB];
+EXTERN	int	debug[256];
+EXTERN	char*	debugstr;
+EXTERN	int	debug_checknil;
+EXTERN	Sym*	hash[NHASH];
+EXTERN	Sym*	importmyname;	// my name for package
+EXTERN	Pkg*	localpkg;	// package being compiled
+EXTERN	Pkg*	importpkg;	// package being imported
+EXTERN	Pkg*	structpkg;	// package that declared struct, during import
+EXTERN	Pkg*	builtinpkg;	// fake package for builtins
+EXTERN	Pkg*	gostringpkg;	// fake pkg for Go strings
+EXTERN	Pkg*	itabpkg;	// fake pkg for itab cache
+EXTERN	Pkg*	runtimepkg;	// package runtime
+EXTERN	Pkg*	racepkg;	// package runtime/race
+EXTERN	Pkg*	stringpkg;	// fake package for C strings
+EXTERN	Pkg*	typepkg;	// fake package for runtime type info (headers)
+EXTERN	Pkg*	typelinkpkg;	// fake package for runtime type info (data)
+EXTERN	Pkg*	weaktypepkg;	// weak references to runtime type info
+EXTERN	Pkg*	unsafepkg;	// package unsafe
+EXTERN	Pkg*	trackpkg;	// fake package for field tracking
+EXTERN	Pkg*	phash[128];
+EXTERN	int	tptr;		// either TPTR32 or TPTR64
+extern	char*	runtimeimport;
+extern	char*	unsafeimport;
+EXTERN	char*	myimportpath;
+EXTERN	Idir*	idirs;
+EXTERN	char*	localimport;
+
+EXTERN	Type*	types[NTYPE];
+EXTERN	Type*	idealstring;
+EXTERN	Type*	idealbool;
+EXTERN	Type*	bytetype;
+EXTERN	Type*	runetype;
+EXTERN	Type*	errortype;
+EXTERN	uchar	simtype[NTYPE];
+EXTERN	uchar	isptr[NTYPE];
+EXTERN	uchar	isforw[NTYPE];
+EXTERN	uchar	isint[NTYPE];
+EXTERN	uchar	isfloat[NTYPE];
+EXTERN	uchar	iscomplex[NTYPE];
+EXTERN	uchar	issigned[NTYPE];
+EXTERN	uchar	issimple[NTYPE];
+
+EXTERN	uchar	okforeq[NTYPE];
+EXTERN	uchar	okforadd[NTYPE];
+EXTERN	uchar	okforand[NTYPE];
+EXTERN	uchar	okfornone[NTYPE];
+EXTERN	uchar	okforcmp[NTYPE];
+EXTERN	uchar	okforbool[NTYPE];
+EXTERN	uchar	okforcap[NTYPE];
+EXTERN	uchar	okforlen[NTYPE];
+EXTERN	uchar	okforarith[NTYPE];
+EXTERN	uchar	okforconst[NTYPE];
+EXTERN	uchar*	okfor[OEND];
+EXTERN	uchar	iscmp[OEND];
+
+EXTERN	Mpint*	minintval[NTYPE];
+EXTERN	Mpint*	maxintval[NTYPE];
+EXTERN	Mpflt*	minfltval[NTYPE];
+EXTERN	Mpflt*	maxfltval[NTYPE];
+
+EXTERN	NodeList*	xtop;
+EXTERN	NodeList*	externdcl;
+EXTERN	NodeList*	closures;
+EXTERN	NodeList*	exportlist;
+EXTERN	NodeList*	importlist;	// imported functions and methods with inlinable bodies
+EXTERN	NodeList*	funcsyms;
+EXTERN	int	dclcontext;		// PEXTERN/PAUTO
+EXTERN	int	incannedimport;
+EXTERN	int	statuniqgen;		// name generator for static temps
+EXTERN	int	loophack;
+
+EXTERN	int32	iota;
+EXTERN	NodeList*	lastconst;
+EXTERN	Node*	lasttype;
+EXTERN	vlong	maxarg;
+EXTERN	vlong	stksize;		// stack size for current frame
+EXTERN	vlong	stkptrsize;		// prefix of stack containing pointers
+EXTERN	int32	blockgen;		// max block number
+EXTERN	int32	block;			// current block number
+EXTERN	int	hasdefer;		// flag that curfn has defer statetment
+
+EXTERN	Node*	curfn;
+
+EXTERN	int	widthptr;
+EXTERN	int	widthint;
+EXTERN	int	widthreg;
+
+EXTERN	Node*	typesw;
+EXTERN	Node*	nblank;
+
+extern	int	thechar;
+extern	char*	thestring;
+extern	LinkArch*	thelinkarch;
+EXTERN	int  	use_sse;
+
+EXTERN	char*	hunk;
+EXTERN	int32	nhunk;
+EXTERN	int32	thunk;
+
+EXTERN	int	funcdepth;
+EXTERN	int	typecheckok;
+EXTERN	int	compiling_runtime;
+EXTERN	int	compiling_wrappers;
+EXTERN	int	inl_nonlocal;
+EXTERN	int	use_writebarrier;
+EXTERN	int	pure_go;
+EXTERN	char*	flag_installsuffix;
+EXTERN	int	flag_race;
+EXTERN	int	flag_largemodel;
+EXTERN	int	noescape;
+EXTERN	int	nosplit;
+EXTERN	int	debuglive;
+EXTERN	Link*	ctxt;
+
+EXTERN	int	nointerface;
+EXTERN	int	fieldtrack_enabled;
+EXTERN	int	precisestack_enabled;
+EXTERN	int	writearchive;
+
+EXTERN	Biobuf	bstdout;
+
+EXTERN	int	nacl;
+
+/*
+ *	y.tab.c
+ */
+int	yyparse(void);
+
+/*
+ *	align.c
+ */
+int	argsize(Type *t);
+void	checkwidth(Type *t);
+void	defercheckwidth(void);
+void	dowidth(Type *t);
+void	resumecheckwidth(void);
+vlong	rnd(vlong o, vlong r);
+void	typeinit(void);
+
+/*
+ *	array.c
+ */
+Array*	arraynew(int32 capacity, int32 size);
+void	arrayfree(Array *array);
+int32	arraylength(Array *array);
+void*	arrayget(Array *array, int32 index);
+void	arrayset(Array *array, int32 index, void *element);
+void	arrayadd(Array *array, void *element);
+void	arraysort(Array* array, int (*cmp)(const void*, const void*));
+
+/*
+ *	bits.c
+ */
+int	Qconv(Fmt *fp);
+Bits	band(Bits a, Bits b);
+int	bany(Bits *a);
+int	beq(Bits a, Bits b);
+int	bitno(int32 b);
+Bits	blsh(uint n);
+Bits	bnot(Bits a);
+int	bnum(Bits a);
+Bits	bor(Bits a, Bits b);
+int	bset(Bits a, uint n);
+
+/*
+ *	bv.c
+ */
+Bvec*	bvalloc(int32 n);
+void	bvandnot(Bvec *dst, Bvec *src1, Bvec *src2);
+int	bvcmp(Bvec *bv1, Bvec *bv2);
+void	bvcopy(Bvec *dst, Bvec *src);
+Bvec*	bvconcat(Bvec *src1, Bvec *src2);
+int	bvget(Bvec *bv, int32 i);
+int32	bvnext(Bvec *bv, int32 i);
+int	bvisempty(Bvec *bv);
+void	bvnot(Bvec *bv);
+void	bvor(Bvec *dst, Bvec *src1, Bvec *src2);
+void	bvand(Bvec *dst, Bvec *src1, Bvec *src2);
+void	bvprint(Bvec *bv);
+void	bvreset(Bvec *bv, int32 i);
+void	bvresetall(Bvec *bv);
+void	bvset(Bvec *bv, int32 i);
+
+/*
+ *	closure.c
+ */
+Node*	closurebody(NodeList *body);
+void	closurehdr(Node *ntype);
+void	typecheckclosure(Node *func, int top);
+Node*	walkclosure(Node *func, NodeList **init);
+void	typecheckpartialcall(Node*, Node*);
+Node*	walkpartialcall(Node*, NodeList**);
+
+/*
+ *	const.c
+ */
+int	cmpslit(Node *l, Node *r);
+int	consttype(Node *n);
+void	convconst(Node *con, Type *t, Val *val);
+void	convlit(Node **np, Type *t);
+void	convlit1(Node **np, Type *t, int explicit);
+void	defaultlit(Node **np, Type *t);
+void	defaultlit2(Node **lp, Node **rp, int force);
+void	evconst(Node *n);
+int	isconst(Node *n, int ct);
+int	isgoconst(Node *n);
+Node*	nodcplxlit(Val r, Val i);
+Node*	nodlit(Val v);
+long	nonnegconst(Node *n);
+int	doesoverflow(Val v, Type *t);
+void	overflow(Val v, Type *t);
+int	smallintconst(Node *n);
+Val	toint(Val v);
+Mpflt*	truncfltlit(Mpflt *oldv, Type *t);
+
+/*
+ *	cplx.c
+ */
+void	complexadd(int op, Node *nl, Node *nr, Node *res);
+void	complexbool(int op, Node *nl, Node *nr, int true, int likely, Prog *to);
+void	complexgen(Node *n, Node *res);
+void	complexminus(Node *nl, Node *res);
+void	complexmove(Node *f, Node *t);
+void	complexmul(Node *nl, Node *nr, Node *res);
+int	complexop(Node *n, Node *res);
+void	nodfconst(Node *n, Type *t, Mpflt* fval);
+
+/*
+ *	dcl.c
+ */
+void	addmethod(Sym *sf, Type *t, int local, int nointerface);
+void	addvar(Node *n, Type *t, int ctxt);
+NodeList*	checkarglist(NodeList *all, int input);
+Node*	colas(NodeList *left, NodeList *right, int32 lno);
+void	colasdefn(NodeList *left, Node *defn);
+NodeList*	constiter(NodeList *vl, Node *t, NodeList *cl);
+Node*	dclname(Sym *s);
+void	declare(Node *n, int ctxt);
+void	dumpdcl(char *st);
+Node*	embedded(Sym *s, Pkg *pkg);
+Node*	fakethis(void);
+void	funcbody(Node *n);
+void	funccompile(Node *n, int isclosure);
+void	funchdr(Node *n);
+Type*	functype(Node *this, NodeList *in, NodeList *out);
+void	ifacedcl(Node *n);
+int	isifacemethod(Type *f);
+void	markdcl(void);
+Node*	methodname(Node *n, Type *t);
+Node*	methodname1(Node *n, Node *t);
+Sym*	methodsym(Sym *nsym, Type *t0, int iface);
+Node*	newname(Sym *s);
+Node*	oldname(Sym *s);
+void	popdcl(void);
+void	poptodcl(void);
+void	redeclare(Sym *s, char *where);
+void	testdclstack(void);
+Type*	tointerface(NodeList *l);
+Type*	tostruct(NodeList *l);
+Node*	typedcl0(Sym *s);
+Node*	typedcl1(Node *n, Node *t, int local);
+Node*	typenod(Type *t);
+NodeList*	variter(NodeList *vl, Node *t, NodeList *el);
+Sym*	funcsym(Sym*);
+
+/*
+ *	esc.c
+ */
+void	escapes(NodeList*);
+
+/*
+ *	export.c
+ */
+void	autoexport(Node *n, int ctxt);
+void	dumpexport(void);
+int	exportname(char *s);
+void	exportsym(Node *n);
+void    importconst(Sym *s, Type *t, Node *n);
+void	importimport(Sym *s, Strlit *z);
+Sym*    importsym(Sym *s, int op);
+void    importtype(Type *pt, Type *t);
+void    importvar(Sym *s, Type *t);
+Type*	pkgtype(Sym *s);
+
+/*
+ *	fmt.c
+ */
+void	fmtinstallgo(void);
+void	dump(char *s, Node *n);
+void	dumplist(char *s, NodeList *l);
+
+/*
+ *	gen.c
+ */
+void	addrescapes(Node *n);
+void	cgen_as(Node *nl, Node *nr);
+void	cgen_callmeth(Node *n, int proc);
+void	cgen_eface(Node* n, Node* res);
+void	cgen_slice(Node* n, Node* res);
+void	clearlabels(void);
+void	clearslim(Node*);
+void	checklabels(void);
+int	dotoffset(Node *n, int64 *oary, Node **nn);
+void	gen(Node *n);
+void	genlist(NodeList *l);
+Node*	sysfunc(char *name);
+void	tempname(Node *n, Type *t);
+Node*	temp(Type*);
+
+/*
+ *	init.c
+ */
+void	fninit(NodeList *n);
+Sym*	renameinit(void);
+
+/*
+ *	inl.c
+ */
+void	caninl(Node *fn);
+void	inlcalls(Node *fn);
+void	typecheckinl(Node *fn);
+
+/*
+ *	lex.c
+ */
+void	cannedimports(char *file, char *cp);
+void	importfile(Val *f, int line);
+char*	lexname(int lex);
+char*	expstring(void);
+void	mkpackage(char* pkgname);
+void	unimportfile(void);
+int32	yylex(void);
+extern	int	yylast;
+extern	int	yyprev;
+
+/*
+ *	mparith1.c
+ */
+int	Bconv(Fmt *fp);
+int	Fconv(Fmt *fp);
+void	mpaddcfix(Mpint *a, vlong c);
+void	mpaddcflt(Mpflt *a, double c);
+void	mpatofix(Mpint *a, char *as);
+void	mpatoflt(Mpflt *a, char *as);
+int	mpcmpfixc(Mpint *b, vlong c);
+int	mpcmpfixfix(Mpint *a, Mpint *b);
+int	mpcmpfixflt(Mpint *a, Mpflt *b);
+int	mpcmpfltc(Mpflt *b, double c);
+int	mpcmpfltfix(Mpflt *a, Mpint *b);
+int	mpcmpfltflt(Mpflt *a, Mpflt *b);
+void	mpcomfix(Mpint *a);
+void	mpdivfixfix(Mpint *a, Mpint *b);
+void	mpmodfixfix(Mpint *a, Mpint *b);
+void	mpmovefixfix(Mpint *a, Mpint *b);
+void	mpmovefixflt(Mpflt *a, Mpint *b);
+int	mpmovefltfix(Mpint *a, Mpflt *b);
+void	mpmovefltflt(Mpflt *a, Mpflt *b);
+void	mpmulcfix(Mpint *a, vlong c);
+void	mpmulcflt(Mpflt *a, double c);
+void	mpsubfixfix(Mpint *a, Mpint *b);
+void	mpsubfltflt(Mpflt *a, Mpflt *b);
+
+/*
+ *	mparith2.c
+ */
+void	mpaddfixfix(Mpint *a, Mpint *b, int);
+void	mpandfixfix(Mpint *a, Mpint *b);
+void	mpandnotfixfix(Mpint *a, Mpint *b);
+void	mpdivfract(Mpint *a, Mpint *b);
+void	mpdivmodfixfix(Mpint *q, Mpint *r, Mpint *n, Mpint *d);
+vlong	mpgetfix(Mpint *a);
+void	mplshfixfix(Mpint *a, Mpint *b);
+void	mpmovecfix(Mpint *a, vlong c);
+void	mpmulfixfix(Mpint *a, Mpint *b);
+void	mpmulfract(Mpint *a, Mpint *b);
+void	mpnegfix(Mpint *a);
+void	mporfixfix(Mpint *a, Mpint *b);
+void	mprshfixfix(Mpint *a, Mpint *b);
+void	mpshiftfix(Mpint *a, int s);
+int	mptestfix(Mpint *a);
+void	mpxorfixfix(Mpint *a, Mpint *b);
+
+/*
+ *	mparith3.c
+ */
+void	mpaddfltflt(Mpflt *a, Mpflt *b);
+void	mpdivfltflt(Mpflt *a, Mpflt *b);
+double	mpgetflt(Mpflt *a);
+double	mpgetflt32(Mpflt *a);
+void	mpmovecflt(Mpflt *a, double c);
+void	mpmulfltflt(Mpflt *a, Mpflt *b);
+void	mpnegflt(Mpflt *a);
+void	mpnorm(Mpflt *a);
+void	mpsetexp(Mpflt *a, int exp);
+int	mptestflt(Mpflt *a);
+int	sigfig(Mpflt *a);
+
+/*
+ *	obj.c
+ */
+void	Bputname(Biobuf *b, LSym *s);
+int	duint16(Sym *s, int off, uint16 v);
+int	duint32(Sym *s, int off, uint32 v);
+int	duint64(Sym *s, int off, uint64 v);
+int	duint8(Sym *s, int off, uint8 v);
+int	duintptr(Sym *s, int off, uint64 v);
+int	dsname(Sym *s, int off, char *dat, int ndat);
+void	dumpobj(void);
+Sym*	stringsym(char*, int);
+void	slicebytes(Node*, char*, int);
+LSym*	linksym(Sym*);
+
+/*
+ *	order.c
+ */
+void	order(Node *fn);
+void	orderstmtinplace(Node **stmt);
+
+/*
+ *	range.c
+ */
+void	typecheckrange(Node *n);
+void	walkrange(Node *n);
+
+/*
+ *	reflect.c
+ */
+void	dumptypestructs(void);
+Type*	methodfunc(Type *f, Type*);
+Node*	typename(Type *t);
+Sym*	typesym(Type *t);
+Sym*	typenamesym(Type *t);
+Sym*	tracksym(Type *t);
+Sym*	typesymprefix(char *prefix, Type *t);
+int	haspointers(Type *t);
+Type*	hiter(Type* t);
+
+/*
+ *	select.c
+ */
+void	typecheckselect(Node *sel);
+void	walkselect(Node *sel);
+
+/*
+ *	sinit.c
+ */
+void	anylit(int, Node *n, Node *var, NodeList **init);
+int	gen_as_init(Node *n);
+NodeList*	initfix(NodeList *l);
+int	oaslit(Node *n, NodeList **init);
+int	stataddr(Node *nam, Node *n);
+
+/*
+ *	subr.c
+ */
+Node*	adddot(Node *n);
+int	adddot1(Sym *s, Type *t, int d, Type **save, int ignorecase);
+void	addinit(Node**, NodeList*);
+Type*	aindex(Node *b, Type *t);
+int	algtype(Type *t);
+int	algtype1(Type *t, Type **bad);
+void	argtype(Node *on, Type *t);
+Node*	assignconv(Node *n, Type *t, char *context);
+int	assignop(Type *src, Type *dst, char **why);
+void	badtype(int o, Type *tl, Type *tr);
+int	brcom(int a);
+int	brrev(int a);
+NodeList*	concat(NodeList *a, NodeList *b);
+int	convertop(Type *src, Type *dst, char **why);
+Node*	copyexpr(Node*, Type*, NodeList**);
+int	count(NodeList *l);
+int	cplxsubtype(int et);
+int	eqtype(Type *t1, Type *t2);
+int	eqtypenoname(Type *t1, Type *t2);
+void	errorexit(void);
+void	expandmeth(Type *t);
+void	fatal(char *fmt, ...);
+void	flusherrors(void);
+void	frame(int context);
+Type*	funcfirst(Iter *s, Type *t);
+Type*	funcnext(Iter *s);
+void	genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface);
+void	genhash(Sym *sym, Type *t);
+void	geneq(Sym *sym, Type *t);
+Type**	getinarg(Type *t);
+Type*	getinargx(Type *t);
+Type**	getoutarg(Type *t);
+Type*	getoutargx(Type *t);
+Type**	getthis(Type *t);
+Type*	getthisx(Type *t);
+int	implements(Type *t, Type *iface, Type **missing, Type **have, int *ptr);
+void	importdot(Pkg *opkg, Node *pack);
+int	is64(Type *t);
+int	isbadimport(Strlit *s);
+int	isblank(Node *n);
+int	isblanksym(Sym *s);
+int	isdirectiface(Type*);
+int	isfixedarray(Type *t);
+int	isideal(Type *t);
+int	isinter(Type *t);
+int	isnil(Node *n);
+int	isnilinter(Type *t);
+int	isptrto(Type *t, int et);
+int	isslice(Type *t);
+int	istype(Type *t, int et);
+int	iszero(Node *n);
+void	linehist(char *file, int32 off, int relative);
+NodeList*	list(NodeList *l, Node *n);
+NodeList*	list1(Node *n);
+void	listsort(NodeList**, int(*f)(Node*, Node*));
+Node*	liststmt(NodeList *l);
+NodeList*	listtreecopy(NodeList *l);
+Sym*	lookup(char *name);
+void*	mal(int32 n);
+Type*	maptype(Type *key, Type *val);
+Type*	methtype(Type *t, int mustname);
+Pkg*	mkpkg(Strlit *path);
+Sym*	ngotype(Node *n);
+int	noconv(Type *t1, Type *t2);
+Node*	nod(int op, Node *nleft, Node *nright);
+Node*	nodbool(int b);
+void	nodconst(Node *n, Type *t, int64 v);
+Node*	nodintconst(int64 v);
+Node*	nodfltconst(Mpflt *v);
+Node*	nodnil(void);
+int	parserline(void);
+Sym*	pkglookup(char *name, Pkg *pkg);
+int	powtwo(Node *n);
+Type*	ptrto(Type *t);
+void*	remal(void *p, int32 on, int32 n);
+Sym*	restrictlookup(char *name, Pkg *pkg);
+Node*	safeexpr(Node *n, NodeList **init);
+void	saveerrors(void);
+Node*	cheapexpr(Node *n, NodeList **init);
+Node*	localexpr(Node *n, Type *t, NodeList **init);
+void	saveorignode(Node *n);
+int32	setlineno(Node *n);
+void	setmaxarg(Type *t);
+Type*	shallow(Type *t);
+int	simsimtype(Type *t);
+void	smagic(Magic *m);
+Type*	sortinter(Type *t);
+uint32	stringhash(char *p);
+Strlit*	strlit(char *s);
+int	structcount(Type *t);
+Type*	structfirst(Iter *s, Type **nn);
+Type*	structnext(Iter *s);
+Node*	syslook(char *name, int copy);
+Type*	tounsigned(Type *t);
+Node*	treecopy(Node *n);
+Type*	typ(int et);
+uint32	typehash(Type *t);
+void	ullmancalc(Node *n);
+void	umagic(Magic *m);
+void	warn(char *fmt, ...);
+void	warnl(int line, char *fmt, ...);
+void	yyerror(char *fmt, ...);
+void	yyerrorl(int line, char *fmt, ...);
+
+/*
+ *	swt.c
+ */
+void	typecheckswitch(Node *n);
+void	walkswitch(Node *sw);
+
+/*
+ *	typecheck.c
+ */
+int	islvalue(Node *n);
+Node*	typecheck(Node **np, int top);
+void	typechecklist(NodeList *l, int top);
+Node*	typecheckdef(Node *n);
+void	copytype(Node *n, Type *t);
+void	checkreturn(Node*);
+void	queuemethod(Node *n);
+
+/*
+ *	unsafe.c
+ */
+int	isunsafebuiltin(Node *n);
+Node*	unsafenmagic(Node *n);
+
+/*
+ *	walk.c
+ */
+Node*	callnew(Type *t);
+Node*	chanfn(char *name, int n, Type *t);
+Node*	mkcall(char *name, Type *t, NodeList **init, ...);
+Node*	mkcall1(Node *fn, Type *t, NodeList **init, ...);
+int	vmatch1(Node *l, Node *r);
+void	walk(Node *fn);
+void	walkexpr(Node **np, NodeList **init);
+void	walkexprlist(NodeList *l, NodeList **init);
+void	walkexprlistsafe(NodeList *l, NodeList **init);
+void	walkstmt(Node **np);
+void	walkstmtlist(NodeList *l);
+Node*	conv(Node*, Type*);
+int	candiscard(Node*);
+int	needwritebarrier(Node*, Node*);
+Node*	outervalue(Node*);
+void	usefield(Node*);
+
+/*
+ *	arch-specific ggen.c/gsubr.c/gobj.c/pgen.c/plive.c
+ */
+#define	P	((Prog*)0)
+
+EXTERN	Prog*	continpc;
+EXTERN	Prog*	breakpc;
+EXTERN	Prog*	pc;
+EXTERN	Prog*	firstpc;
+
+EXTERN	Node*	nodfp;
+EXTERN	int	disable_checknil;
+EXTERN	vlong	zerosize;
+
+int	anyregalloc(void);
+void	betypeinit(void);
+void	bgen(Node *n, int true, int likely, Prog *to);
+void	checknil(Node*, NodeList**);
+void	expandchecks(Prog*);
+void	cgen(Node*, Node*);
+void	cgen_asop(Node *n);
+void	cgen_call(Node *n, int proc);
+void	cgen_callinter(Node *n, Node *res, int proc);
+void	cgen_checknil(Node*);
+void	cgen_ret(Node *n);
+void	clearfat(Node *n);
+void	compile(Node*);
+void	defframe(Prog*);
+int	dgostringptr(Sym*, int off, char *str);
+int	dgostrlitptr(Sym*, int off, Strlit*);
+int	dstringptr(Sym *s, int off, char *str);
+int	dsymptr(Sym *s, int off, Sym *x, int xoff);
+int	duintxx(Sym *s, int off, uint64 v, int wid);
+void	dumpdata(void);
+void	fixautoused(Prog*);
+void	gdata(Node*, Node*, int);
+void	gdatacomplex(Node*, Mpcplx*);
+void	gdatastring(Node*, Strlit*);
+void	ggloblnod(Node *nam);
+void	ggloblsym(Sym *s, int32 width, int8 flags);
+void	gvardef(Node*);
+void	gvarkill(Node*);
+Prog*	gjmp(Prog*);
+void	gused(Node*);
+void	movelarge(NodeList*);
+int	isfat(Type*);
+void	linkarchinit(void);
+void	liveness(Node*, Prog*, Sym*, Sym*);
+void	twobitwalktype1(Type*, vlong*, Bvec*);
+void	markautoused(Prog*);
+Plist*	newplist(void);
+Node*	nodarg(Type*, int);
+void	nopout(Prog*);
+void	patch(Prog*, Prog*);
+Prog*	unpatch(Prog*);
+
+#pragma	varargck	type	"B"	Mpint*
+#pragma	varargck	type	"E"	int
+#pragma	varargck	type	"E"	uint
+#pragma	varargck	type	"F"	Mpflt*
+#pragma	varargck	type	"H"	NodeList*
+#pragma	varargck	type	"J"	Node*
+#pragma	varargck	type	"lL"	int32
+#pragma	varargck	type	"L"	int32
+#pragma	varargck	type	"N"	Node*
+#pragma	varargck	type	"lN"	Node*
+#pragma	varargck	type	"O"	int
+#pragma	varargck	type	"O"	uint
+#pragma	varargck	type	"Q"	Bits
+#pragma	varargck	type	"S"	Sym*
+#pragma	varargck	type	"lS"	LSym*
+#pragma	varargck	type	"T"	Type*
+#pragma	varargck	type	"lT"	Type*
+#pragma	varargck	type	"V"	Val*
+#pragma	varargck	type	"Z"	Strlit*
+
+/*
+ *	racewalk.c
+ */
+void	racewalk(Node *fn);
diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y
new file mode 100644
index 0000000..68fccc1
--- /dev/null
+++ b/src/cmd/gc/go.y
@@ -0,0 +1,2223 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * Go language grammar.
+ *
+ * The Go semicolon rules are:
+ *
+ *  1. all statements and declarations are terminated by semicolons.
+ *  2. semicolons can be omitted before a closing ) or }.
+ *  3. semicolons are inserted by the lexer before a newline
+ *      following a specific list of tokens.
+ *
+ * Rules #1 and #2 are accomplished by writing the lists as
+ * semicolon-separated lists with an optional trailing semicolon.
+ * Rule #3 is implemented in yylex.
+ */
+
+%{
+#include <u.h>
+#include <stdio.h>	/* if we don't, bison will, and go.h re-#defines getc */
+#include <libc.h>
+#include "go.h"
+
+static void fixlbrace(int);
+%}
+%union	{
+	Node*		node;
+	NodeList*		list;
+	Type*		type;
+	Sym*		sym;
+	struct	Val	val;
+	int		i;
+}
+
+// |sed 's/.*	//' |9 fmt -l1 |sort |9 fmt -l50 | sed 's/^/%xxx		/'
+
+%token	<val>	LLITERAL
+%token	<i>	LASOP LCOLAS
+%token	<sym>	LBREAK LCASE LCHAN LCONST LCONTINUE LDDD
+%token	<sym>	LDEFAULT LDEFER LELSE LFALL LFOR LFUNC LGO LGOTO
+%token	<sym>	LIF LIMPORT LINTERFACE LMAP LNAME
+%token	<sym>	LPACKAGE LRANGE LRETURN LSELECT LSTRUCT LSWITCH
+%token	<sym>	LTYPE LVAR
+
+%token		LANDAND LANDNOT LBODY LCOMM LDEC LEQ LGE LGT
+%token		LIGNORE LINC LLE LLSH LLT LNE LOROR LRSH
+
+%type	<i>	lbrace import_here
+%type	<sym>	sym packname
+%type	<val>	oliteral
+
+%type	<node>	stmt ntype
+%type	<node>	arg_type
+%type	<node>	case caseblock
+%type	<node>	compound_stmt dotname embed expr complitexpr bare_complitexpr
+%type	<node>	expr_or_type
+%type	<node>	fndcl hidden_fndcl fnliteral
+%type	<node>	for_body for_header for_stmt if_header if_stmt non_dcl_stmt
+%type	<node>	interfacedcl keyval labelname name
+%type	<node>	name_or_type non_expr_type
+%type	<node>	new_name dcl_name oexpr typedclname
+%type	<node>	onew_name
+%type	<node>	osimple_stmt pexpr pexpr_no_paren
+%type	<node>	pseudocall range_stmt select_stmt
+%type	<node>	simple_stmt
+%type	<node>	switch_stmt uexpr
+%type	<node>	xfndcl typedcl start_complit
+
+%type	<list>	xdcl fnbody fnres loop_body dcl_name_list
+%type	<list>	new_name_list expr_list keyval_list braced_keyval_list expr_or_type_list xdcl_list
+%type	<list>	oexpr_list caseblock_list elseif elseif_list else stmt_list oarg_type_list_ocomma arg_type_list
+%type	<list>	interfacedcl_list vardcl vardcl_list structdcl structdcl_list
+%type	<list>	common_dcl constdcl constdcl1 constdcl_list typedcl_list
+
+%type	<node>	convtype comptype dotdotdot
+%type	<node>	indcl interfacetype structtype ptrtype
+%type	<node>	recvchantype non_recvchantype othertype fnret_type fntype
+
+%type	<sym>	hidden_importsym hidden_pkg_importsym
+
+%type	<node>	hidden_constant hidden_literal hidden_funarg
+%type	<node>	hidden_interfacedcl hidden_structdcl
+
+%type	<list>	hidden_funres
+%type	<list>	ohidden_funres
+%type	<list>	hidden_funarg_list ohidden_funarg_list
+%type	<list>	hidden_interfacedcl_list ohidden_interfacedcl_list
+%type	<list>	hidden_structdcl_list ohidden_structdcl_list
+
+%type	<type>	hidden_type hidden_type_misc hidden_pkgtype
+%type	<type>	hidden_type_func
+%type	<type>	hidden_type_recv_chan hidden_type_non_recv_chan
+
+%left		LCOMM	/* outside the usual hierarchy; here for good error messages */
+
+%left		LOROR
+%left		LANDAND
+%left		LEQ LNE LLE LGE LLT LGT
+%left		'+' '-' '|' '^'
+%left		'*' '/' '%' '&' LLSH LRSH LANDNOT
+
+/*
+ * manual override of shift/reduce conflicts.
+ * the general form is that we assign a precedence
+ * to the token being shifted and then introduce
+ * NotToken with lower precedence or PreferToToken with higher
+ * and annotate the reducing rule accordingly.
+ */
+%left		NotPackage
+%left		LPACKAGE
+
+%left		NotParen
+%left		'('
+
+%left		')'
+%left		PreferToRightParen
+
+%error-verbose
+
+%%
+file:
+	loadsys
+	package
+	imports
+	xdcl_list
+	{
+		xtop = concat(xtop, $4);
+	}
+
+package:
+	%prec NotPackage
+	{
+		prevlineno = lineno;
+		yyerror("package statement must be first");
+		errorexit();
+	}
+|	LPACKAGE sym ';'
+	{
+		mkpackage($2->name);
+	}
+
+/*
+ * this loads the definitions for the low-level runtime functions,
+ * so that the compiler can generate calls to them,
+ * but does not make the name "runtime" visible as a package.
+ */
+loadsys:
+	{
+		importpkg = runtimepkg;
+
+		if(debug['A'])
+			cannedimports("runtime.builtin", "package runtime\n\n$$\n\n");
+		else
+			cannedimports("runtime.builtin", runtimeimport);
+		curio.importsafe = 1;
+	}
+	import_package
+	import_there
+	{
+		importpkg = nil;
+	}
+
+imports:
+|	imports import ';'
+
+import:
+	LIMPORT import_stmt
+|	LIMPORT '(' import_stmt_list osemi ')'
+|	LIMPORT '(' ')'
+
+import_stmt:
+	import_here import_package import_there
+	{
+		Pkg *ipkg;
+		Sym *my;
+		Node *pack;
+		
+		ipkg = importpkg;
+		my = importmyname;
+		importpkg = nil;
+		importmyname = S;
+
+		if(my == nil)
+			my = lookup(ipkg->name);
+
+		pack = nod(OPACK, N, N);
+		pack->sym = my;
+		pack->pkg = ipkg;
+		pack->lineno = $1;
+
+		if(my->name[0] == '.') {
+			importdot(ipkg, pack);
+			break;
+		}
+		if(strcmp(my->name, "init") == 0) {
+			yyerror("cannot import package as init - init must be a func");
+			break;
+		}
+		if(my->name[0] == '_' && my->name[1] == '\0')
+			break;
+		if(my->def) {
+			lineno = $1;
+			redeclare(my, "as imported package name");
+		}
+		my->def = pack;
+		my->lastlineno = $1;
+		my->block = 1;	// at top level
+	}
+|	import_here import_there
+	{
+		// When an invalid import path is passed to importfile,
+		// it calls yyerror and then sets up a fake import with
+		// no package statement. This allows us to test more
+		// than one invalid import statement in a single file.
+		if(nerrors == 0)
+			fatal("phase error in import");
+	}
+
+import_stmt_list:
+	import_stmt
+|	import_stmt_list ';' import_stmt
+
+import_here:
+	LLITERAL
+	{
+		// import with original name
+		$$ = parserline();
+		importmyname = S;
+		importfile(&$1, $$);
+	}
+|	sym LLITERAL
+	{
+		// import with given name
+		$$ = parserline();
+		importmyname = $1;
+		importfile(&$2, $$);
+	}
+|	'.' LLITERAL
+	{
+		// import into my name space
+		$$ = parserline();
+		importmyname = lookup(".");
+		importfile(&$2, $$);
+	}
+
+import_package:
+	LPACKAGE LNAME import_safety ';'
+	{
+		if(importpkg->name == nil) {
+			importpkg->name = $2->name;
+			pkglookup($2->name, nil)->npkg++;
+		} else if(strcmp(importpkg->name, $2->name) != 0)
+			yyerror("conflicting names %s and %s for package \"%Z\"", importpkg->name, $2->name, importpkg->path);
+		importpkg->direct = 1;
+		importpkg->safe = curio.importsafe;
+
+		if(safemode && !curio.importsafe)
+			yyerror("cannot import unsafe package \"%Z\"", importpkg->path);
+	}
+
+import_safety:
+|	LNAME
+	{
+		if(strcmp($1->name, "safe") == 0)
+			curio.importsafe = 1;
+	}
+
+import_there:
+	{
+		defercheckwidth();
+	}
+	hidden_import_list '$' '$'
+	{
+		resumecheckwidth();
+		unimportfile();
+	}
+
+/*
+ * declarations
+ */
+xdcl:
+	{
+		yyerror("empty top-level declaration");
+		$$ = nil;
+	}
+|	common_dcl
+|	xfndcl
+	{
+		$$ = list1($1);
+	}
+|	non_dcl_stmt
+	{
+		yyerror("non-declaration statement outside function body");
+		$$ = nil;
+	}
+|	error
+	{
+		$$ = nil;
+	}
+
+common_dcl:
+	LVAR vardcl
+	{
+		$$ = $2;
+	}
+|	LVAR '(' vardcl_list osemi ')'
+	{
+		$$ = $3;
+	}
+|	LVAR '(' ')'
+	{
+		$$ = nil;
+	}
+|	lconst constdcl
+	{
+		$$ = $2;
+		iota = -100000;
+		lastconst = nil;
+	}
+|	lconst '(' constdcl osemi ')'
+	{
+		$$ = $3;
+		iota = -100000;
+		lastconst = nil;
+	}
+|	lconst '(' constdcl ';' constdcl_list osemi ')'
+	{
+		$$ = concat($3, $5);
+		iota = -100000;
+		lastconst = nil;
+	}
+|	lconst '(' ')'
+	{
+		$$ = nil;
+		iota = -100000;
+	}
+|	LTYPE typedcl
+	{
+		$$ = list1($2);
+	}
+|	LTYPE '(' typedcl_list osemi ')'
+	{
+		$$ = $3;
+	}
+|	LTYPE '(' ')'
+	{
+		$$ = nil;
+	}
+
+lconst:
+	LCONST
+	{
+		iota = 0;
+	}
+
+vardcl:
+	dcl_name_list ntype
+	{
+		$$ = variter($1, $2, nil);
+	}
+|	dcl_name_list ntype '=' expr_list
+	{
+		$$ = variter($1, $2, $4);
+	}
+|	dcl_name_list '=' expr_list
+	{
+		$$ = variter($1, nil, $3);
+	}
+
+constdcl:
+	dcl_name_list ntype '=' expr_list
+	{
+		$$ = constiter($1, $2, $4);
+	}
+|	dcl_name_list '=' expr_list
+	{
+		$$ = constiter($1, N, $3);
+	}
+
+constdcl1:
+	constdcl
+|	dcl_name_list ntype
+	{
+		$$ = constiter($1, $2, nil);
+	}
+|	dcl_name_list
+	{
+		$$ = constiter($1, N, nil);
+	}
+
+typedclname:
+	sym
+	{
+		// different from dclname because the name
+		// becomes visible right here, not at the end
+		// of the declaration.
+		$$ = typedcl0($1);
+	}
+
+typedcl:
+	typedclname ntype
+	{
+		$$ = typedcl1($1, $2, 1);
+	}
+
+simple_stmt:
+	expr
+	{
+		$$ = $1;
+
+		// These nodes do not carry line numbers.
+		// Since a bare name used as an expression is an error,
+		// introduce a wrapper node to give the correct line.
+		switch($$->op) {
+		case ONAME:
+		case ONONAME:
+		case OTYPE:
+		case OPACK:
+		case OLITERAL:
+			$$ = nod(OPAREN, $$, N);
+			$$->implicit = 1;
+			break;
+		}
+	}
+|	expr LASOP expr
+	{
+		$$ = nod(OASOP, $1, $3);
+		$$->etype = $2;			// rathole to pass opcode
+	}
+|	expr_list '=' expr_list
+	{
+		if($1->next == nil && $3->next == nil) {
+			// simple
+			$$ = nod(OAS, $1->n, $3->n);
+			break;
+		}
+		// multiple
+		$$ = nod(OAS2, N, N);
+		$$->list = $1;
+		$$->rlist = $3;
+	}
+|	expr_list LCOLAS expr_list
+	{
+		if($3->n->op == OTYPESW) {
+			$$ = nod(OTYPESW, N, $3->n->right);
+			if($3->next != nil)
+				yyerror("expr.(type) must be alone in list");
+			if($1->next != nil)
+				yyerror("argument count mismatch: %d = %d", count($1), 1);
+			else if(($1->n->op != ONAME && $1->n->op != OTYPE && $1->n->op != ONONAME) || isblank($1->n))
+				yyerror("invalid variable name %N in type switch", $1->n);
+			else
+				$$->left = dclname($1->n->sym);  // it's a colas, so must not re-use an oldname.
+			break;
+		}
+		$$ = colas($1, $3, $2);
+	}
+|	expr LINC
+	{
+		$$ = nod(OASOP, $1, nodintconst(1));
+		$$->implicit = 1;
+		$$->etype = OADD;
+	}
+|	expr LDEC
+	{
+		$$ = nod(OASOP, $1, nodintconst(1));
+		$$->implicit = 1;
+		$$->etype = OSUB;
+	}
+
+case:
+	LCASE expr_or_type_list ':'
+	{
+		Node *n, *nn;
+
+		// will be converted to OCASE
+		// right will point to next case
+		// done in casebody()
+		markdcl();
+		$$ = nod(OXCASE, N, N);
+		$$->list = $2;
+		if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
+			// type switch - declare variable
+			nn = newname(n->sym);
+			declare(nn, dclcontext);
+			$$->nname = nn;
+
+			// keep track of the instances for reporting unused
+			nn->defn = typesw->right;
+		}
+	}
+|	LCASE expr_or_type_list '=' expr ':'
+	{
+		Node *n;
+
+		// will be converted to OCASE
+		// right will point to next case
+		// done in casebody()
+		markdcl();
+		$$ = nod(OXCASE, N, N);
+		if($2->next == nil)
+			n = nod(OAS, $2->n, $4);
+		else {
+			n = nod(OAS2, N, N);
+			n->list = $2;
+			n->rlist = list1($4);
+		}
+		$$->list = list1(n);
+	}
+|	LCASE expr_or_type_list LCOLAS expr ':'
+	{
+		// will be converted to OCASE
+		// right will point to next case
+		// done in casebody()
+		markdcl();
+		$$ = nod(OXCASE, N, N);
+		$$->list = list1(colas($2, list1($4), $3));
+	}
+|	LDEFAULT ':'
+	{
+		Node *n, *nn;
+
+		markdcl();
+		$$ = nod(OXCASE, N, N);
+		if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
+			// type switch - declare variable
+			nn = newname(n->sym);
+			declare(nn, dclcontext);
+			$$->nname = nn;
+
+			// keep track of the instances for reporting unused
+			nn->defn = typesw->right;
+		}
+	}
+
+compound_stmt:
+	'{'
+	{
+		markdcl();
+	}
+	stmt_list '}'
+	{
+		if($3 == nil)
+			$$ = nod(OEMPTY, N, N);
+		else
+			$$ = liststmt($3);
+		popdcl();
+	}
+
+caseblock:
+	case
+	{
+		// If the last token read by the lexer was consumed
+		// as part of the case, clear it (parser has cleared yychar).
+		// If the last token read by the lexer was the lookahead
+		// leave it alone (parser has it cached in yychar).
+		// This is so that the stmt_list action doesn't look at
+		// the case tokens if the stmt_list is empty.
+		yylast = yychar;
+		$1->xoffset = block;
+	}
+	stmt_list
+	{
+		int last;
+
+		// This is the only place in the language where a statement
+		// list is not allowed to drop the final semicolon, because
+		// it's the only place where a statement list is not followed 
+		// by a closing brace.  Handle the error for pedantry.
+
+		// Find the final token of the statement list.
+		// yylast is lookahead; yyprev is last of stmt_list
+		last = yyprev;
+
+		if(last > 0 && last != ';' && yychar != '}')
+			yyerror("missing statement after label");
+		$$ = $1;
+		$$->nbody = $3;
+		popdcl();
+	}
+
+caseblock_list:
+	{
+		$$ = nil;
+	}
+|	caseblock_list caseblock
+	{
+		$$ = list($1, $2);
+	}
+
+loop_body:
+	LBODY
+	{
+		markdcl();
+	}
+	stmt_list '}'
+	{
+		$$ = $3;
+		popdcl();
+	}
+
+range_stmt:
+	expr_list '=' LRANGE expr
+	{
+		$$ = nod(ORANGE, N, $4);
+		$$->list = $1;
+		$$->etype = 0;	// := flag
+	}
+|	expr_list LCOLAS LRANGE expr
+	{
+		$$ = nod(ORANGE, N, $4);
+		$$->list = $1;
+		$$->colas = 1;
+		colasdefn($1, $$);
+	}
+|	LRANGE expr
+	{
+		$$ = nod(ORANGE, N, $2);
+		$$->etype = 0; // := flag
+	}
+
+for_header:
+	osimple_stmt ';' osimple_stmt ';' osimple_stmt
+	{
+		// init ; test ; incr
+		if($5 != N && $5->colas != 0)
+			yyerror("cannot declare in the for-increment");
+		$$ = nod(OFOR, N, N);
+		if($1 != N)
+			$$->ninit = list1($1);
+		$$->ntest = $3;
+		$$->nincr = $5;
+	}
+|	osimple_stmt
+	{
+		// normal test
+		$$ = nod(OFOR, N, N);
+		$$->ntest = $1;
+	}
+|	range_stmt
+
+for_body:
+	for_header loop_body
+	{
+		$$ = $1;
+		$$->nbody = concat($$->nbody, $2);
+	}
+
+for_stmt:
+	LFOR
+	{
+		markdcl();
+	}
+	for_body
+	{
+		$$ = $3;
+		popdcl();
+	}
+
+if_header:
+	osimple_stmt
+	{
+		// test
+		$$ = nod(OIF, N, N);
+		$$->ntest = $1;
+	}
+|	osimple_stmt ';' osimple_stmt
+	{
+		// init ; test
+		$$ = nod(OIF, N, N);
+		if($1 != N)
+			$$->ninit = list1($1);
+		$$->ntest = $3;
+	}
+
+/* IF cond body (ELSE IF cond body)* (ELSE block)? */
+if_stmt:
+	LIF
+	{
+		markdcl();
+	}
+	if_header
+	{
+		if($3->ntest == N)
+			yyerror("missing condition in if statement");
+	}
+	loop_body
+	{
+		$3->nbody = $5;
+	}
+	elseif_list else
+	{
+		Node *n;
+		NodeList *nn;
+
+		$$ = $3;
+		n = $3;
+		popdcl();
+		for(nn = concat($7, $8); nn; nn = nn->next) {
+			if(nn->n->op == OIF)
+				popdcl();
+			n->nelse = list1(nn->n);
+			n = nn->n;
+		}
+	}
+
+elseif:
+	LELSE LIF 
+	{
+		markdcl();
+	}
+	if_header loop_body
+	{
+		if($4->ntest == N)
+			yyerror("missing condition in if statement");
+		$4->nbody = $5;
+		$$ = list1($4);
+	}
+
+elseif_list:
+	{
+		$$ = nil;
+	}
+|	elseif_list elseif
+	{
+		$$ = concat($1, $2);
+	}
+
+else:
+	{
+		$$ = nil;
+	}
+|	LELSE compound_stmt
+	{
+		NodeList *node;
+		
+		node = mal(sizeof *node);
+		node->n = $2;
+		node->end = node;
+		$$ = node;
+	}
+
+switch_stmt:
+	LSWITCH
+	{
+		markdcl();
+	}
+	if_header
+	{
+		Node *n;
+		n = $3->ntest;
+		if(n != N && n->op != OTYPESW)
+			n = N;
+		typesw = nod(OXXX, typesw, n);
+	}
+	LBODY caseblock_list '}'
+	{
+		$$ = $3;
+		$$->op = OSWITCH;
+		$$->list = $6;
+		typesw = typesw->left;
+		popdcl();
+	}
+
+select_stmt:
+	LSELECT
+	{
+		typesw = nod(OXXX, typesw, N);
+	}
+	LBODY caseblock_list '}'
+	{
+		$$ = nod(OSELECT, N, N);
+		$$->lineno = typesw->lineno;
+		$$->list = $4;
+		typesw = typesw->left;
+	}
+
+/*
+ * expressions
+ */
+expr:
+	uexpr
+|	expr LOROR expr
+	{
+		$$ = nod(OOROR, $1, $3);
+	}
+|	expr LANDAND expr
+	{
+		$$ = nod(OANDAND, $1, $3);
+	}
+|	expr LEQ expr
+	{
+		$$ = nod(OEQ, $1, $3);
+	}
+|	expr LNE expr
+	{
+		$$ = nod(ONE, $1, $3);
+	}
+|	expr LLT expr
+	{
+		$$ = nod(OLT, $1, $3);
+	}
+|	expr LLE expr
+	{
+		$$ = nod(OLE, $1, $3);
+	}
+|	expr LGE expr
+	{
+		$$ = nod(OGE, $1, $3);
+	}
+|	expr LGT expr
+	{
+		$$ = nod(OGT, $1, $3);
+	}
+|	expr '+' expr
+	{
+		$$ = nod(OADD, $1, $3);
+	}
+|	expr '-' expr
+	{
+		$$ = nod(OSUB, $1, $3);
+	}
+|	expr '|' expr
+	{
+		$$ = nod(OOR, $1, $3);
+	}
+|	expr '^' expr
+	{
+		$$ = nod(OXOR, $1, $3);
+	}
+|	expr '*' expr
+	{
+		$$ = nod(OMUL, $1, $3);
+	}
+|	expr '/' expr
+	{
+		$$ = nod(ODIV, $1, $3);
+	}
+|	expr '%' expr
+	{
+		$$ = nod(OMOD, $1, $3);
+	}
+|	expr '&' expr
+	{
+		$$ = nod(OAND, $1, $3);
+	}
+|	expr LANDNOT expr
+	{
+		$$ = nod(OANDNOT, $1, $3);
+	}
+|	expr LLSH expr
+	{
+		$$ = nod(OLSH, $1, $3);
+	}
+|	expr LRSH expr
+	{
+		$$ = nod(ORSH, $1, $3);
+	}
+	/* not an expression anymore, but left in so we can give a good error */
+|	expr LCOMM expr
+	{
+		$$ = nod(OSEND, $1, $3);
+	}
+
+uexpr:
+	pexpr
+|	'*' uexpr
+	{
+		$$ = nod(OIND, $2, N);
+	}
+|	'&' uexpr
+	{
+		if($2->op == OCOMPLIT) {
+			// Special case for &T{...}: turn into (*T){...}.
+			$$ = $2;
+			$$->right = nod(OIND, $$->right, N);
+			$$->right->implicit = 1;
+		} else {
+			$$ = nod(OADDR, $2, N);
+		}
+	}
+|	'+' uexpr
+	{
+		$$ = nod(OPLUS, $2, N);
+	}
+|	'-' uexpr
+	{
+		$$ = nod(OMINUS, $2, N);
+	}
+|	'!' uexpr
+	{
+		$$ = nod(ONOT, $2, N);
+	}
+|	'~' uexpr
+	{
+		yyerror("the bitwise complement operator is ^");
+		$$ = nod(OCOM, $2, N);
+	}
+|	'^' uexpr
+	{
+		$$ = nod(OCOM, $2, N);
+	}
+|	LCOMM uexpr
+	{
+		$$ = nod(ORECV, $2, N);
+	}
+
+/*
+ * call-like statements that
+ * can be preceded by 'defer' and 'go'
+ */
+pseudocall:
+	pexpr '(' ')'
+	{
+		$$ = nod(OCALL, $1, N);
+	}
+|	pexpr '(' expr_or_type_list ocomma ')'
+	{
+		$$ = nod(OCALL, $1, N);
+		$$->list = $3;
+	}
+|	pexpr '(' expr_or_type_list LDDD ocomma ')'
+	{
+		$$ = nod(OCALL, $1, N);
+		$$->list = $3;
+		$$->isddd = 1;
+	}
+
+pexpr_no_paren:
+	LLITERAL
+	{
+		$$ = nodlit($1);
+	}
+|	name
+|	pexpr '.' sym
+	{
+		if($1->op == OPACK) {
+			Sym *s;
+			s = restrictlookup($3->name, $1->pkg);
+			$1->used = 1;
+			$$ = oldname(s);
+			break;
+		}
+		$$ = nod(OXDOT, $1, newname($3));
+	}
+|	pexpr '.' '(' expr_or_type ')'
+	{
+		$$ = nod(ODOTTYPE, $1, $4);
+	}
+|	pexpr '.' '(' LTYPE ')'
+	{
+		$$ = nod(OTYPESW, N, $1);
+	}
+|	pexpr '[' expr ']'
+	{
+		$$ = nod(OINDEX, $1, $3);
+	}
+|	pexpr '[' oexpr ':' oexpr ']'
+	{
+		$$ = nod(OSLICE, $1, nod(OKEY, $3, $5));
+	}
+|	pexpr '[' oexpr ':' oexpr ':' oexpr ']'
+	{
+		if($5 == N)
+			yyerror("middle index required in 3-index slice");
+		if($7 == N)
+			yyerror("final index required in 3-index slice");
+		$$ = nod(OSLICE3, $1, nod(OKEY, $3, nod(OKEY, $5, $7)));
+	}
+|	pseudocall
+|	convtype '(' expr ocomma ')'
+	{
+		// conversion
+		$$ = nod(OCALL, $1, N);
+		$$->list = list1($3);
+	}
+|	comptype lbrace start_complit braced_keyval_list '}'
+	{
+		$$ = $3;
+		$$->right = $1;
+		$$->list = $4;
+		fixlbrace($2);
+	}
+|	pexpr_no_paren '{' start_complit braced_keyval_list '}'
+	{
+		$$ = $3;
+		$$->right = $1;
+		$$->list = $4;
+	}
+|	'(' expr_or_type ')' '{' start_complit braced_keyval_list '}'
+	{
+		yyerror("cannot parenthesize type in composite literal");
+		$$ = $5;
+		$$->right = $2;
+		$$->list = $6;
+	}
+|	fnliteral
+
+start_complit:
+	{
+		// composite expression.
+		// make node early so we get the right line number.
+		$$ = nod(OCOMPLIT, N, N);
+	}
+
+keyval:
+	expr ':' complitexpr
+	{
+		$$ = nod(OKEY, $1, $3);
+	}
+
+bare_complitexpr:
+	expr
+	{
+		// These nodes do not carry line numbers.
+		// Since a composite literal commonly spans several lines,
+		// the line number on errors may be misleading.
+		// Introduce a wrapper node to give the correct line.
+		$$ = $1;
+		switch($$->op) {
+		case ONAME:
+		case ONONAME:
+		case OTYPE:
+		case OPACK:
+		case OLITERAL:
+			$$ = nod(OPAREN, $$, N);
+			$$->implicit = 1;
+		}
+	}
+|	'{' start_complit braced_keyval_list '}'
+	{
+		$$ = $2;
+		$$->list = $3;
+	}
+
+complitexpr:
+	expr
+|	'{' start_complit braced_keyval_list '}'
+	{
+		$$ = $2;
+		$$->list = $3;
+	}
+
+pexpr:
+	pexpr_no_paren
+|	'(' expr_or_type ')'
+	{
+		$$ = $2;
+		
+		// Need to know on lhs of := whether there are ( ).
+		// Don't bother with the OPAREN in other cases:
+		// it's just a waste of memory and time.
+		switch($$->op) {
+		case ONAME:
+		case ONONAME:
+		case OPACK:
+		case OTYPE:
+		case OLITERAL:
+		case OTYPESW:
+			$$ = nod(OPAREN, $$, N);
+		}
+	}
+
+expr_or_type:
+	expr
+|	non_expr_type	%prec PreferToRightParen
+
+name_or_type:
+	ntype
+
+lbrace:
+	LBODY
+	{
+		$$ = LBODY;
+	}
+|	'{'
+	{
+		$$ = '{';
+	}
+
+/*
+ * names and types
+ *	newname is used before declared
+ *	oldname is used after declared
+ */
+new_name:
+	sym
+	{
+		if($1 == S)
+			$$ = N;
+		else
+			$$ = newname($1);
+	}
+
+dcl_name:
+	sym
+	{
+		$$ = dclname($1);
+	}
+
+onew_name:
+	{
+		$$ = N;
+	}
+|	new_name
+
+sym:
+	LNAME
+	{
+		$$ = $1;
+		// during imports, unqualified non-exported identifiers are from builtinpkg
+		if(importpkg != nil && !exportname($1->name))
+			$$ = pkglookup($1->name, builtinpkg);
+	}
+|	hidden_importsym
+|	'?'
+	{
+		$$ = S;
+	}
+
+hidden_importsym:
+	'@' LLITERAL '.' LNAME
+	{
+		Pkg *p;
+
+		if($2.u.sval->len == 0)
+			p = importpkg;
+		else {
+			if(isbadimport($2.u.sval))
+				errorexit();
+			p = mkpkg($2.u.sval);
+		}
+		$$ = pkglookup($4->name, p);
+	}
+|	'@' LLITERAL '.' '?'
+	{
+		Pkg *p;
+
+		if($2.u.sval->len == 0)
+			p = importpkg;
+		else {
+			if(isbadimport($2.u.sval))
+				errorexit();
+			p = mkpkg($2.u.sval);
+		}
+		$$ = pkglookup("?", p);
+	}
+
+name:
+	sym	%prec NotParen
+	{
+		$$ = oldname($1);
+		if($$->pack != N)
+			$$->pack->used = 1;
+	}
+
+labelname:
+	new_name
+
+/*
+ * to avoid parsing conflicts, type is split into
+ *	channel types
+ *	function types
+ *	parenthesized types
+ *	any other type
+ * the type system makes additional restrictions,
+ * but those are not implemented in the grammar.
+ */
+dotdotdot:
+	LDDD
+	{
+		yyerror("final argument in variadic function missing type");
+		$$ = nod(ODDD, typenod(typ(TINTER)), N);
+	}
+|	LDDD ntype
+	{
+		$$ = nod(ODDD, $2, N);
+	}
+
+ntype:
+	recvchantype
+|	fntype
+|	othertype
+|	ptrtype
+|	dotname
+|	'(' ntype ')'
+	{
+		$$ = $2;
+	}
+
+non_expr_type:
+	recvchantype
+|	fntype
+|	othertype
+|	'*' non_expr_type
+	{
+		$$ = nod(OIND, $2, N);
+	}
+
+non_recvchantype:
+	fntype
+|	othertype
+|	ptrtype
+|	dotname
+|	'(' ntype ')'
+	{
+		$$ = $2;
+	}
+
+convtype:
+	fntype
+|	othertype
+
+comptype:
+	othertype
+
+fnret_type:
+	recvchantype
+|	fntype
+|	othertype
+|	ptrtype
+|	dotname
+
+dotname:
+	name
+|	name '.' sym
+	{
+		if($1->op == OPACK) {
+			Sym *s;
+			s = restrictlookup($3->name, $1->pkg);
+			$1->used = 1;
+			$$ = oldname(s);
+			break;
+		}
+		$$ = nod(OXDOT, $1, newname($3));
+	}
+
+othertype:
+	'[' oexpr ']' ntype
+	{
+		$$ = nod(OTARRAY, $2, $4);
+	}
+|	'[' LDDD ']' ntype
+	{
+		// array literal of nelem
+		$$ = nod(OTARRAY, nod(ODDD, N, N), $4);
+	}
+|	LCHAN non_recvchantype
+	{
+		$$ = nod(OTCHAN, $2, N);
+		$$->etype = Cboth;
+	}
+|	LCHAN LCOMM ntype
+	{
+		$$ = nod(OTCHAN, $3, N);
+		$$->etype = Csend;
+	}
+|	LMAP '[' ntype ']' ntype
+	{
+		$$ = nod(OTMAP, $3, $5);
+	}
+|	structtype
+|	interfacetype
+
+ptrtype:
+	'*' ntype
+	{
+		$$ = nod(OIND, $2, N);
+	}
+
+recvchantype:
+	LCOMM LCHAN ntype
+	{
+		$$ = nod(OTCHAN, $3, N);
+		$$->etype = Crecv;
+	}
+
+structtype:
+	LSTRUCT lbrace structdcl_list osemi '}'
+	{
+		$$ = nod(OTSTRUCT, N, N);
+		$$->list = $3;
+		fixlbrace($2);
+	}
+|	LSTRUCT lbrace '}'
+	{
+		$$ = nod(OTSTRUCT, N, N);
+		fixlbrace($2);
+	}
+
+interfacetype:
+	LINTERFACE lbrace interfacedcl_list osemi '}'
+	{
+		$$ = nod(OTINTER, N, N);
+		$$->list = $3;
+		fixlbrace($2);
+	}
+|	LINTERFACE lbrace '}'
+	{
+		$$ = nod(OTINTER, N, N);
+		fixlbrace($2);
+	}
+
+/*
+ * function stuff
+ * all in one place to show how crappy it all is
+ */
+xfndcl:
+	LFUNC fndcl fnbody
+	{
+		$$ = $2;
+		if($$ == N)
+			break;
+		if(noescape && $3 != nil)
+			yyerror("can only use //go:noescape with external func implementations");
+		$$->nbody = $3;
+		$$->endlineno = lineno;
+		$$->noescape = noescape;
+		$$->nosplit = nosplit;
+		funcbody($$);
+	}
+
+fndcl:
+	sym '(' oarg_type_list_ocomma ')' fnres
+	{
+		Node *t;
+
+		$$ = N;
+		$3 = checkarglist($3, 1);
+
+		if(strcmp($1->name, "init") == 0) {
+			$1 = renameinit();
+			if($3 != nil || $5 != nil)
+				yyerror("func init must have no arguments and no return values");
+		}
+		if(strcmp(localpkg->name, "main") == 0 && strcmp($1->name, "main") == 0) {
+			if($3 != nil || $5 != nil)
+				yyerror("func main must have no arguments and no return values");
+		}
+
+		t = nod(OTFUNC, N, N);
+		t->list = $3;
+		t->rlist = $5;
+
+		$$ = nod(ODCLFUNC, N, N);
+		$$->nname = newname($1);
+		$$->nname->defn = $$;
+		$$->nname->ntype = t;		// TODO: check if nname already has an ntype
+		declare($$->nname, PFUNC);
+
+		funchdr($$);
+	}
+|	'(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres
+	{
+		Node *rcvr, *t;
+
+		$$ = N;
+		$2 = checkarglist($2, 0);
+		$6 = checkarglist($6, 1);
+
+		if($2 == nil) {
+			yyerror("method has no receiver");
+			break;
+		}
+		if($2->next != nil) {
+			yyerror("method has multiple receivers");
+			break;
+		}
+		rcvr = $2->n;
+		if(rcvr->op != ODCLFIELD) {
+			yyerror("bad receiver in method");
+			break;
+		}
+
+		t = nod(OTFUNC, rcvr, N);
+		t->list = $6;
+		t->rlist = $8;
+
+		$$ = nod(ODCLFUNC, N, N);
+		$$->shortname = newname($4);
+		$$->nname = methodname1($$->shortname, rcvr->right);
+		$$->nname->defn = $$;
+		$$->nname->ntype = t;
+		$$->nname->nointerface = nointerface;
+		declare($$->nname, PFUNC);
+
+		funchdr($$);
+	}
+
+hidden_fndcl:
+	hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres
+	{
+		Sym *s;
+		Type *t;
+
+		$$ = N;
+
+		s = $1;
+		t = functype(N, $3, $5);
+
+		importsym(s, ONAME);
+		if(s->def != N && s->def->op == ONAME) {
+			if(eqtype(t, s->def->type)) {
+				dclcontext = PDISCARD;  // since we skip funchdr below
+				break;
+			}
+			yyerror("inconsistent definition for func %S during import\n\t%T\n\t%T", s, s->def->type, t);
+		}
+
+		$$ = newname(s);
+		$$->type = t;
+		declare($$, PFUNC);
+
+		funchdr($$);
+	}
+|	'(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres
+	{
+		$$ = methodname1(newname($4), $2->n->right); 
+		$$->type = functype($2->n, $6, $8);
+
+		checkwidth($$->type);
+		addmethod($4, $$->type, 0, nointerface);
+		nointerface = 0;
+		funchdr($$);
+		
+		// inl.c's inlnode in on a dotmeth node expects to find the inlineable body as
+		// (dotmeth's type)->nname->inl, and dotmeth's type has been pulled
+		// out by typecheck's lookdot as this $$->ttype.  So by providing
+		// this back link here we avoid special casing there.
+		$$->type->nname = $$;
+	}
+
+fntype:
+	LFUNC '(' oarg_type_list_ocomma ')' fnres
+	{
+		$3 = checkarglist($3, 1);
+		$$ = nod(OTFUNC, N, N);
+		$$->list = $3;
+		$$->rlist = $5;
+	}
+
+fnbody:
+	{
+		$$ = nil;
+	}
+|	'{' stmt_list '}'
+	{
+		$$ = $2;
+		if($$ == nil)
+			$$ = list1(nod(OEMPTY, N, N));
+	}
+
+fnres:
+	%prec NotParen
+	{
+		$$ = nil;
+	}
+|	fnret_type
+	{
+		$$ = list1(nod(ODCLFIELD, N, $1));
+	}
+|	'(' oarg_type_list_ocomma ')'
+	{
+		$2 = checkarglist($2, 0);
+		$$ = $2;
+	}
+
+fnlitdcl:
+	fntype
+	{
+		closurehdr($1);
+	}
+
+fnliteral:
+	fnlitdcl lbrace stmt_list '}'
+	{
+		$$ = closurebody($3);
+		fixlbrace($2);
+	}
+|	fnlitdcl error
+	{
+		$$ = closurebody(nil);
+	}
+
+/*
+ * lists of things
+ * note that they are left recursive
+ * to conserve yacc stack. they need to
+ * be reversed to interpret correctly
+ */
+xdcl_list:
+	{
+		$$ = nil;
+	}
+|	xdcl_list xdcl ';'
+	{
+		$$ = concat($1, $2);
+		if(nsyntaxerrors == 0)
+			testdclstack();
+		nointerface = 0;
+		noescape = 0;
+		nosplit = 0;
+	}
+
+vardcl_list:
+	vardcl
+|	vardcl_list ';' vardcl
+	{
+		$$ = concat($1, $3);
+	}
+
+constdcl_list:
+	constdcl1
+|	constdcl_list ';' constdcl1
+	{
+		$$ = concat($1, $3);
+	}
+
+typedcl_list:
+	typedcl
+	{
+		$$ = list1($1);
+	}
+|	typedcl_list ';' typedcl
+	{
+		$$ = list($1, $3);
+	}
+
+structdcl_list:
+	structdcl
+|	structdcl_list ';' structdcl
+	{
+		$$ = concat($1, $3);
+	}
+
+interfacedcl_list:
+	interfacedcl
+	{
+		$$ = list1($1);
+	}
+|	interfacedcl_list ';' interfacedcl
+	{
+		$$ = list($1, $3);
+	}
+
+structdcl:
+	new_name_list ntype oliteral
+	{
+		NodeList *l;
+
+		Node *n;
+		l = $1;
+		if(l == nil) {
+			// ? symbol, during import (list1(N) == nil)
+			n = $2;
+			if(n->op == OIND)
+				n = n->left;
+			n = embedded(n->sym, importpkg);
+			n->right = $2;
+			n->val = $3;
+			$$ = list1(n);
+			break;
+		}
+
+		for(l=$1; l; l=l->next) {
+			l->n = nod(ODCLFIELD, l->n, $2);
+			l->n->val = $3;
+		}
+	}
+|	embed oliteral
+	{
+		$1->val = $2;
+		$$ = list1($1);
+	}
+|	'(' embed ')' oliteral
+	{
+		$2->val = $4;
+		$$ = list1($2);
+		yyerror("cannot parenthesize embedded type");
+	}
+|	'*' embed oliteral
+	{
+		$2->right = nod(OIND, $2->right, N);
+		$2->val = $3;
+		$$ = list1($2);
+	}
+|	'(' '*' embed ')' oliteral
+	{
+		$3->right = nod(OIND, $3->right, N);
+		$3->val = $5;
+		$$ = list1($3);
+		yyerror("cannot parenthesize embedded type");
+	}
+|	'*' '(' embed ')' oliteral
+	{
+		$3->right = nod(OIND, $3->right, N);
+		$3->val = $5;
+		$$ = list1($3);
+		yyerror("cannot parenthesize embedded type");
+	}
+
+packname:
+	LNAME
+	{
+		Node *n;
+
+		$$ = $1;
+		n = oldname($1);
+		if(n->pack != N)
+			n->pack->used = 1;
+	}
+|	LNAME '.' sym
+	{
+		Pkg *pkg;
+
+		if($1->def == N || $1->def->op != OPACK) {
+			yyerror("%S is not a package", $1);
+			pkg = localpkg;
+		} else {
+			$1->def->used = 1;
+			pkg = $1->def->pkg;
+		}
+		$$ = restrictlookup($3->name, pkg);
+	}
+
+embed:
+	packname
+	{
+		$$ = embedded($1, localpkg);
+	}
+
+interfacedcl:
+	new_name indcl
+	{
+		$$ = nod(ODCLFIELD, $1, $2);
+		ifacedcl($$);
+	}
+|	packname
+	{
+		$$ = nod(ODCLFIELD, N, oldname($1));
+	}
+|	'(' packname ')'
+	{
+		$$ = nod(ODCLFIELD, N, oldname($2));
+		yyerror("cannot parenthesize embedded type");
+	}
+
+indcl:
+	'(' oarg_type_list_ocomma ')' fnres
+	{
+		// without func keyword
+		$2 = checkarglist($2, 1);
+		$$ = nod(OTFUNC, fakethis(), N);
+		$$->list = $2;
+		$$->rlist = $4;
+	}
+
+/*
+ * function arguments.
+ */
+arg_type:
+	name_or_type
+|	sym name_or_type
+	{
+		$$ = nod(ONONAME, N, N);
+		$$->sym = $1;
+		$$ = nod(OKEY, $$, $2);
+	}
+|	sym dotdotdot
+	{
+		$$ = nod(ONONAME, N, N);
+		$$->sym = $1;
+		$$ = nod(OKEY, $$, $2);
+	}
+|	dotdotdot
+
+arg_type_list:
+	arg_type
+	{
+		$$ = list1($1);
+	}
+|	arg_type_list ',' arg_type
+	{
+		$$ = list($1, $3);
+	}
+
+oarg_type_list_ocomma:
+	{
+		$$ = nil;
+	}
+|	arg_type_list ocomma
+	{
+		$$ = $1;
+	}
+
+/*
+ * statement
+ */
+stmt:
+	{
+		$$ = N;
+	}
+|	compound_stmt
+|	common_dcl
+	{
+		$$ = liststmt($1);
+	}
+|	non_dcl_stmt
+|	error
+	{
+		$$ = N;
+	}
+
+non_dcl_stmt:
+	simple_stmt
+|	for_stmt
+|	switch_stmt
+|	select_stmt
+|	if_stmt
+|	labelname ':'
+	{
+		$1 = nod(OLABEL, $1, N);
+		$1->sym = dclstack;  // context, for goto restrictions
+	}
+	stmt
+	{
+		NodeList *l;
+
+		$1->defn = $4;
+		l = list1($1);
+		if($4)
+			l = list(l, $4);
+		$$ = liststmt(l);
+	}
+|	LFALL
+	{
+		// will be converted to OFALL
+		$$ = nod(OXFALL, N, N);
+		$$->xoffset = block;
+	}
+|	LBREAK onew_name
+	{
+		$$ = nod(OBREAK, $2, N);
+	}
+|	LCONTINUE onew_name
+	{
+		$$ = nod(OCONTINUE, $2, N);
+	}
+|	LGO pseudocall
+	{
+		$$ = nod(OPROC, $2, N);
+	}
+|	LDEFER pseudocall
+	{
+		$$ = nod(ODEFER, $2, N);
+	}
+|	LGOTO new_name
+	{
+		$$ = nod(OGOTO, $2, N);
+		$$->sym = dclstack;  // context, for goto restrictions
+	}
+|	LRETURN oexpr_list
+	{
+		$$ = nod(ORETURN, N, N);
+		$$->list = $2;
+		if($$->list == nil && curfn != N) {
+			NodeList *l;
+
+			for(l=curfn->dcl; l; l=l->next) {
+				if(l->n->class == PPARAM)
+					continue;
+				if(l->n->class != PPARAMOUT)
+					break;
+				if(l->n->sym->def != l->n)
+					yyerror("%s is shadowed during return", l->n->sym->name);
+			}
+		}
+	}
+
+stmt_list:
+	stmt
+	{
+		$$ = nil;
+		if($1 != N)
+			$$ = list1($1);
+	}
+|	stmt_list ';' stmt
+	{
+		$$ = $1;
+		if($3 != N)
+			$$ = list($$, $3);
+	}
+
+new_name_list:
+	new_name
+	{
+		$$ = list1($1);
+	}
+|	new_name_list ',' new_name
+	{
+		$$ = list($1, $3);
+	}
+
+dcl_name_list:
+	dcl_name
+	{
+		$$ = list1($1);
+	}
+|	dcl_name_list ',' dcl_name
+	{
+		$$ = list($1, $3);
+	}
+
+expr_list:
+	expr
+	{
+		$$ = list1($1);
+	}
+|	expr_list ',' expr
+	{
+		$$ = list($1, $3);
+	}
+
+expr_or_type_list:
+	expr_or_type
+	{
+		$$ = list1($1);
+	}
+|	expr_or_type_list ',' expr_or_type
+	{
+		$$ = list($1, $3);
+	}
+
+/*
+ * list of combo of keyval and val
+ */
+keyval_list:
+	keyval
+	{
+		$$ = list1($1);
+	}
+|	bare_complitexpr
+	{
+		$$ = list1($1);
+	}
+|	keyval_list ',' keyval
+	{
+		$$ = list($1, $3);
+	}
+|	keyval_list ',' bare_complitexpr
+	{
+		$$ = list($1, $3);
+	}
+
+braced_keyval_list:
+	{
+		$$ = nil;
+	}
+|	keyval_list ocomma
+	{
+		$$ = $1;
+	}
+
+/*
+ * optional things
+ */
+osemi:
+|	';'
+
+ocomma:
+|	','
+
+oexpr:
+	{
+		$$ = N;
+	}
+|	expr
+
+oexpr_list:
+	{
+		$$ = nil;
+	}
+|	expr_list
+
+osimple_stmt:
+	{
+		$$ = N;
+	}
+|	simple_stmt
+
+ohidden_funarg_list:
+	{
+		$$ = nil;
+	}
+|	hidden_funarg_list
+
+ohidden_structdcl_list:
+	{
+		$$ = nil;
+	}
+|	hidden_structdcl_list
+
+ohidden_interfacedcl_list:
+	{
+		$$ = nil;
+	}
+|	hidden_interfacedcl_list
+
+oliteral:
+	{
+		$$.ctype = CTxxx;
+	}
+|	LLITERAL
+
+/*
+ * import syntax from package header
+ */
+hidden_import:
+	LIMPORT LNAME LLITERAL ';'
+	{
+		importimport($2, $3.u.sval);
+	}
+|	LVAR hidden_pkg_importsym hidden_type ';'
+	{
+		importvar($2, $3);
+	}
+|	LCONST hidden_pkg_importsym '=' hidden_constant ';'
+	{
+		importconst($2, types[TIDEAL], $4);
+	}
+|	LCONST hidden_pkg_importsym hidden_type '=' hidden_constant ';'
+	{
+		importconst($2, $3, $5);
+	}
+|	LTYPE hidden_pkgtype hidden_type ';'
+	{
+		importtype($2, $3);
+	}
+|	LFUNC hidden_fndcl fnbody ';'
+	{
+		if($2 == N) {
+			dclcontext = PEXTERN;  // since we skip the funcbody below
+			break;
+		}
+
+		$2->inl = $3;
+
+		funcbody($2);
+		importlist = list(importlist, $2);
+
+		if(debug['E']) {
+			print("import [%Z] func %lN \n", importpkg->path, $2);
+			if(debug['m'] > 2 && $2->inl)
+				print("inl body:%+H\n", $2->inl);
+		}
+	}
+
+hidden_pkg_importsym:
+	hidden_importsym
+	{
+		$$ = $1;
+		structpkg = $$->pkg;
+	}
+
+hidden_pkgtype:
+	hidden_pkg_importsym
+	{
+		$$ = pkgtype($1);
+		importsym($1, OTYPE);
+	}
+
+/*
+ *  importing types
+ */
+
+hidden_type:
+	hidden_type_misc
+|	hidden_type_recv_chan
+|	hidden_type_func
+
+hidden_type_non_recv_chan:
+	hidden_type_misc
+|	hidden_type_func
+
+hidden_type_misc:
+	hidden_importsym
+	{
+		$$ = pkgtype($1);
+	}
+|	LNAME
+	{
+		// predefined name like uint8
+		$1 = pkglookup($1->name, builtinpkg);
+		if($1->def == N || $1->def->op != OTYPE) {
+			yyerror("%s is not a type", $1->name);
+			$$ = T;
+		} else
+			$$ = $1->def->type;
+	}
+|	'[' ']' hidden_type
+	{
+		$$ = aindex(N, $3);
+	}
+|	'[' LLITERAL ']' hidden_type
+	{
+		$$ = aindex(nodlit($2), $4);
+	}
+|	LMAP '[' hidden_type ']' hidden_type
+	{
+		$$ = maptype($3, $5);
+	}
+|	LSTRUCT '{' ohidden_structdcl_list '}'
+	{
+		$$ = tostruct($3);
+	}
+|	LINTERFACE '{' ohidden_interfacedcl_list '}'
+	{
+		$$ = tointerface($3);
+	}
+|	'*' hidden_type
+	{
+		$$ = ptrto($2);
+	}
+|	LCHAN hidden_type_non_recv_chan
+	{
+		$$ = typ(TCHAN);
+		$$->type = $2;
+		$$->chan = Cboth;
+	}
+|	LCHAN '(' hidden_type_recv_chan ')'
+	{
+		$$ = typ(TCHAN);
+		$$->type = $3;
+		$$->chan = Cboth;
+	}
+|	LCHAN LCOMM hidden_type
+	{
+		$$ = typ(TCHAN);
+		$$->type = $3;
+		$$->chan = Csend;
+	}
+
+hidden_type_recv_chan:
+	LCOMM LCHAN hidden_type
+	{
+		$$ = typ(TCHAN);
+		$$->type = $3;
+		$$->chan = Crecv;
+	}
+
+hidden_type_func:
+	LFUNC '(' ohidden_funarg_list ')' ohidden_funres
+	{
+		$$ = functype(nil, $3, $5);
+	}
+
+hidden_funarg:
+	sym hidden_type oliteral
+	{
+		$$ = nod(ODCLFIELD, N, typenod($2));
+		if($1)
+			$$->left = newname($1);
+		$$->val = $3;
+	}
+|	sym LDDD hidden_type oliteral
+	{
+		Type *t;
+	
+		t = typ(TARRAY);
+		t->bound = -1;
+		t->type = $3;
+
+		$$ = nod(ODCLFIELD, N, typenod(t));
+		if($1)
+			$$->left = newname($1);
+		$$->isddd = 1;
+		$$->val = $4;
+	}
+
+hidden_structdcl:
+	sym hidden_type oliteral
+	{
+		Sym *s;
+		Pkg *p;
+
+		if($1 != S && strcmp($1->name, "?") != 0) {
+			$$ = nod(ODCLFIELD, newname($1), typenod($2));
+			$$->val = $3;
+		} else {
+			s = $2->sym;
+			if(s == S && isptr[$2->etype])
+				s = $2->type->sym;
+			p = importpkg;
+			if($1 != S)
+				p = $1->pkg;
+			$$ = embedded(s, p);
+			$$->right = typenod($2);
+			$$->val = $3;
+		}
+	}
+
+hidden_interfacedcl:
+	sym '(' ohidden_funarg_list ')' ohidden_funres
+	{
+		$$ = nod(ODCLFIELD, newname($1), typenod(functype(fakethis(), $3, $5)));
+	}
+|	hidden_type
+	{
+		$$ = nod(ODCLFIELD, N, typenod($1));
+	}
+
+ohidden_funres:
+	{
+		$$ = nil;
+	}
+|	hidden_funres
+
+hidden_funres:
+	'(' ohidden_funarg_list ')'
+	{
+		$$ = $2;
+	}
+|	hidden_type
+	{
+		$$ = list1(nod(ODCLFIELD, N, typenod($1)));
+	}
+
+/*
+ *  importing constants
+ */
+
+hidden_literal:
+	LLITERAL
+	{
+		$$ = nodlit($1);
+	}
+|	'-' LLITERAL
+	{
+		$$ = nodlit($2);
+		switch($$->val.ctype){
+		case CTINT:
+		case CTRUNE:
+			mpnegfix($$->val.u.xval);
+			break;
+		case CTFLT:
+			mpnegflt($$->val.u.fval);
+			break;
+		case CTCPLX:
+			mpnegflt(&$$->val.u.cval->real);
+			mpnegflt(&$$->val.u.cval->imag);
+			break;
+		default:
+			yyerror("bad negated constant");
+		}
+	}
+|	sym
+	{
+		$$ = oldname(pkglookup($1->name, builtinpkg));
+		if($$->op != OLITERAL)
+			yyerror("bad constant %S", $$->sym);
+	}
+
+hidden_constant:
+	hidden_literal
+|	'(' hidden_literal '+' hidden_literal ')'
+	{
+		if($2->val.ctype == CTRUNE && $4->val.ctype == CTINT) {
+			$$ = $2;
+			mpaddfixfix($2->val.u.xval, $4->val.u.xval, 0);
+			break;
+		}
+		$4->val.u.cval->real = $4->val.u.cval->imag;
+		mpmovecflt(&$4->val.u.cval->imag, 0.0);
+		$$ = nodcplxlit($2->val, $4->val);
+	}
+
+hidden_import_list:
+|	hidden_import_list hidden_import
+
+hidden_funarg_list:
+	hidden_funarg
+	{
+		$$ = list1($1);
+	}
+|	hidden_funarg_list ',' hidden_funarg
+	{
+		$$ = list($1, $3);
+	}
+
+hidden_structdcl_list:
+	hidden_structdcl
+	{
+		$$ = list1($1);
+	}
+|	hidden_structdcl_list ';' hidden_structdcl
+	{
+		$$ = list($1, $3);
+	}
+
+hidden_interfacedcl_list:
+	hidden_interfacedcl
+	{
+		$$ = list1($1);
+	}
+|	hidden_interfacedcl_list ';' hidden_interfacedcl
+	{
+		$$ = list($1, $3);
+	}
+
+%%
+
+static void
+fixlbrace(int lbr)
+{
+	// If the opening brace was an LBODY,
+	// set up for another one now that we're done.
+	// See comment in lex.c about loophack.
+	if(lbr == LBODY)
+		loophack = 1;
+}
+
diff --git a/src/cmd/gc/init.c b/src/cmd/gc/init.c
new file mode 100644
index 0000000..918d371
--- /dev/null
+++ b/src/cmd/gc/init.c
@@ -0,0 +1,195 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+/*
+ * a function named init is a special case.
+ * it is called by the initialization before
+ * main is run. to make it unique within a
+ * package and also uncallable, the name,
+ * normally "pkg.init", is altered to "pkg.init·1".
+ */
+Sym*
+renameinit(void)
+{
+	static int initgen;
+
+	snprint(namebuf, sizeof(namebuf), "init·%d", ++initgen);
+	return lookup(namebuf);
+}
+
+/*
+ * hand-craft the following initialization code
+ *	var initdone· uint8 				(1)
+ *	func init()					(2)
+ *		if initdone· != 0 {			(3)
+ *			if initdone· == 2		(4)
+ *				return
+ *			throw();			(5)
+ *		}
+ *		initdone· = 1;				(6)
+ *		// over all matching imported symbols
+ *			<pkg>.init()			(7)
+ *		{ <init stmts> }			(8)
+ *		init·<n>() // if any			(9)
+ *		initdone· = 2;				(10)
+ *		return					(11)
+ *	}
+ */
+static int
+anyinit(NodeList *n)
+{
+	uint32 h;
+	Sym *s;
+	NodeList *l;
+
+	// are there any interesting init statements
+	for(l=n; l; l=l->next) {
+		switch(l->n->op) {
+		case ODCLFUNC:
+		case ODCLCONST:
+		case ODCLTYPE:
+		case OEMPTY:
+			break;
+		case OAS:
+			if(isblank(l->n->left) && candiscard(l->n->right))
+				break;
+			// fall through
+		default:
+			return 1;
+		}
+	}
+
+	// is this main
+	if(strcmp(localpkg->name, "main") == 0)
+		return 1;
+
+	// is there an explicit init function
+	snprint(namebuf, sizeof(namebuf), "init·1");
+	s = lookup(namebuf);
+	if(s->def != N)
+		return 1;
+
+	// are there any imported init functions
+	for(h=0; h<NHASH; h++)
+	for(s = hash[h]; s != S; s = s->link) {
+		if(s->name[0] != 'i' || strcmp(s->name, "init") != 0)
+			continue;
+		if(s->def == N)
+			continue;
+		return 1;
+	}
+
+	// then none
+	return 0;
+}
+
+void
+fninit(NodeList *n)
+{
+	int i;
+	Node *gatevar;
+	Node *a, *b, *fn;
+	NodeList *r;
+	uint32 h;
+	Sym *s, *initsym;
+
+	if(debug['A']) {
+		// sys.go or unsafe.go during compiler build
+		return;
+	}
+
+	n = initfix(n);
+	if(!anyinit(n))
+		return;
+
+	r = nil;
+
+	// (1)
+	snprint(namebuf, sizeof(namebuf), "initdone·");
+	gatevar = newname(lookup(namebuf));
+	addvar(gatevar, types[TUINT8], PEXTERN);
+
+	// (2)
+	maxarg = 0;
+	snprint(namebuf, sizeof(namebuf), "init");
+
+	fn = nod(ODCLFUNC, N, N);
+	initsym = lookup(namebuf);
+	fn->nname = newname(initsym);
+	fn->nname->defn = fn;
+	fn->nname->ntype = nod(OTFUNC, N, N);
+	declare(fn->nname, PFUNC);
+	funchdr(fn);
+
+	// (3)
+	a = nod(OIF, N, N);
+	a->ntest = nod(ONE, gatevar, nodintconst(0));
+	r = list(r, a);
+
+	// (4)
+	b = nod(OIF, N, N);
+	b->ntest = nod(OEQ, gatevar, nodintconst(2));
+	b->nbody = list1(nod(ORETURN, N, N));
+	a->nbody = list1(b);
+
+	// (5)
+	b = syslook("throwinit", 0);
+	b = nod(OCALL, b, N);
+	a->nbody = list(a->nbody, b);
+
+	// (6)
+	a = nod(OAS, gatevar, nodintconst(1));
+	r = list(r, a);
+
+	// (7)
+	for(h=0; h<NHASH; h++)
+	for(s = hash[h]; s != S; s = s->link) {
+		if(s->name[0] != 'i' || strcmp(s->name, "init") != 0)
+			continue;
+		if(s->def == N)
+			continue;
+		if(s == initsym)
+			continue;
+
+		// could check that it is fn of no args/returns
+		a = nod(OCALL, s->def, N);
+		r = list(r, a);
+	}
+
+	// (8)
+	r = concat(r, n);
+
+	// (9)
+	// could check that it is fn of no args/returns
+	for(i=1;; i++) {
+		snprint(namebuf, sizeof(namebuf), "init·%d", i);
+		s = lookup(namebuf);
+		if(s->def == N)
+			break;
+		a = nod(OCALL, s->def, N);
+		r = list(r, a);
+	}
+
+	// (10)
+	a = nod(OAS, gatevar, nodintconst(2));
+	r = list(r, a);
+
+	// (11)
+	a = nod(ORETURN, N, N);
+	r = list(r, a);
+	exportsym(fn->nname);
+
+	fn->nbody = r;
+	funcbody(fn);
+
+	curfn = fn;
+	typecheck(&fn, Etop);
+	typechecklist(r, Etop);
+	curfn = nil;
+	funccompile(fn, 0);
+}
diff --git a/src/cmd/gc/inl.c b/src/cmd/gc/inl.c
new file mode 100644
index 0000000..45e15bb
--- /dev/null
+++ b/src/cmd/gc/inl.c
@@ -0,0 +1,986 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// The inlining facility makes 2 passes: first caninl determines which
+// functions are suitable for inlining, and for those that are it
+// saves a copy of the body. Then inlcalls walks each function body to
+// expand calls to inlinable functions.
+//
+// The debug['l'] flag controls the agressiveness. Note that main() swaps level 0 and 1,
+// making 1 the default and -l disable.  -ll and more is useful to flush out bugs.
+// These additional levels (beyond -l) may be buggy and are not supported.
+//      0: disabled
+//      1: 40-nodes leaf functions, oneliners, lazy typechecking (default)
+//      2: early typechecking of all imported bodies 
+//      3: allow variadic functions
+//      4: allow non-leaf functions , (breaks runtime.Caller)
+//      5: transitive inlining
+//
+//  At some point this may get another default and become switch-offable with -N.
+//
+//  The debug['m'] flag enables diagnostic output.  a single -m is useful for verifying
+//  which calls get inlined or not, more is for debugging, and may go away at any point.
+//
+// TODO:
+//   - inline functions with ... args
+//   - handle T.meth(f()) with func f() (t T, arg, arg, )
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+// Used by caninl.
+static Node*	inlcopy(Node *n);
+static NodeList* inlcopylist(NodeList *ll);
+static int	ishairy(Node *n, int *budget);
+static int	ishairylist(NodeList *ll, int *budget); 
+
+// Used by inlcalls
+static void	inlnodelist(NodeList *l);
+static void	inlnode(Node **np);
+static void	mkinlcall(Node **np, Node *fn, int isddd);
+static Node*	inlvar(Node *n);
+static Node*	retvar(Type *n, int i);
+static Node*	argvar(Type *n, int i);
+static Node*	newlabel(void);
+static Node*	inlsubst(Node *n);
+static NodeList* inlsubstlist(NodeList *l);
+
+static void	setlno(Node*, int);
+
+// Used during inlsubst[list]
+static Node *inlfn;		// function currently being inlined
+static Node *inlretlabel;	// target of the goto substituted in place of a return
+static NodeList *inlretvars;	// temp out variables
+
+// Get the function's package.  For ordinary functions it's on the ->sym, but for imported methods
+// the ->sym can be re-used in the local package, so peel it off the receiver's type.
+static Pkg*
+fnpkg(Node *fn)
+{
+	Type *rcvr;
+	
+	if(fn->type->thistuple) {
+		// method
+		rcvr = getthisx(fn->type)->type->type;
+		if(isptr[rcvr->etype])
+			rcvr = rcvr->type;
+		if(!rcvr->sym)
+			fatal("receiver with no sym: [%S] %lN  (%T)", fn->sym, fn, rcvr);
+		return rcvr->sym->pkg;
+	}
+	// non-method
+	return fn->sym->pkg;
+}
+
+// Lazy typechecking of imported bodies.  For local functions, caninl will set ->typecheck
+// because they're a copy of an already checked body. 
+void
+typecheckinl(Node *fn)
+{
+	Node *savefn;
+	Pkg *pkg;
+	int save_safemode, lno;
+
+	lno = setlineno(fn);
+
+	// typecheckinl is only for imported functions;
+	// their bodies may refer to unsafe as long as the package
+	// was marked safe during import (which was checked then).
+	// the ->inl of a local function has been typechecked before caninl copied it.
+	pkg = fnpkg(fn);
+	if (pkg == localpkg || pkg == nil)
+		return; // typecheckinl on local function
+
+	if (debug['m']>2)
+		print("typecheck import [%S] %lN { %#H }\n", fn->sym, fn, fn->inl);
+
+	save_safemode = safemode;
+	safemode = 0;
+
+	savefn = curfn;
+	curfn = fn;
+	typechecklist(fn->inl, Etop);
+	curfn = savefn;
+
+	safemode = save_safemode;
+
+	lineno = lno;
+}
+
+// Caninl determines whether fn is inlineable.
+// If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy.
+// fn and ->nbody will already have been typechecked.
+void
+caninl(Node *fn)
+{
+	Node *savefn;
+	Type *t;
+	int budget;
+
+	if(fn->op != ODCLFUNC)
+		fatal("caninl %N", fn);
+	if(!fn->nname)
+		fatal("caninl no nname %+N", fn);
+
+	// If fn has no body (is defined outside of Go), cannot inline it.
+	if(fn->nbody == nil)
+		return;
+
+	if(fn->typecheck == 0)
+		fatal("caninl on non-typechecked function %N", fn);
+
+	// can't handle ... args yet
+	if(debug['l'] < 3)
+		for(t=fn->type->type->down->down->type; t; t=t->down)
+			if(t->isddd)
+				return;
+
+	budget = 40;  // allowed hairyness
+	if(ishairylist(fn->nbody, &budget))
+		return;
+
+	savefn = curfn;
+	curfn = fn;
+
+	fn->nname->inl = fn->nbody;
+	fn->nbody = inlcopylist(fn->nname->inl);
+	fn->nname->inldcl = inlcopylist(fn->nname->defn->dcl);
+
+	// hack, TODO, check for better way to link method nodes back to the thing with the ->inl
+	// this is so export can find the body of a method
+	fn->type->nname = fn->nname;
+
+	if(debug['m'] > 1)
+		print("%L: can inline %#N as: %#T { %#H }\n", fn->lineno, fn->nname, fn->type, fn->nname->inl);
+	else if(debug['m'])
+		print("%L: can inline %N\n", fn->lineno, fn->nname);
+
+	curfn = savefn;
+}
+
+// Look for anything we want to punt on.
+static int
+ishairylist(NodeList *ll, int* budget)
+{
+	for(;ll;ll=ll->next)
+		if(ishairy(ll->n, budget))
+			return 1;
+	return 0;
+}
+
+static int
+ishairy(Node *n, int *budget)
+{
+	if(!n)
+		return 0;
+
+	// Things that are too hairy, irrespective of the budget
+	switch(n->op) {
+	case OCALL:
+	case OCALLFUNC:
+	case OCALLINTER:
+	case OCALLMETH:
+	case OPANIC:
+	case ORECOVER:
+		if(debug['l'] < 4)
+			return 1;
+		break;
+
+	case OCLOSURE:
+	case OCALLPART:
+	case ORANGE:
+	case OFOR:
+	case OSELECT:
+	case OSWITCH:
+	case OPROC:
+	case ODEFER:
+	case ODCLTYPE:  // can't print yet
+	case ODCLCONST:  // can't print yet
+	case ORETJMP:
+		return 1;
+
+		break;
+	}
+
+	(*budget)--;
+
+	return  *budget < 0 ||
+		ishairy(n->left, budget) ||
+		ishairy(n->right, budget) ||
+		ishairylist(n->list, budget) ||
+		ishairylist(n->rlist, budget) ||
+		ishairylist(n->ninit, budget) ||
+		ishairy(n->ntest, budget) ||
+		ishairy(n->nincr, budget) ||
+		ishairylist(n->nbody, budget) ||
+		ishairylist(n->nelse, budget);
+}
+
+// Inlcopy and inlcopylist recursively copy the body of a function.
+// Any name-like node of non-local class is marked for re-export by adding it to
+// the exportlist.
+static NodeList*
+inlcopylist(NodeList *ll)
+{
+	NodeList *l;
+
+	l = nil;
+	for(; ll; ll=ll->next)
+		l = list(l, inlcopy(ll->n));
+	return l;
+}
+
+static Node*
+inlcopy(Node *n)
+{
+	Node *m;
+
+	if(n == N)
+		return N;
+
+	switch(n->op) {
+	case ONAME:
+	case OTYPE:
+	case OLITERAL:
+		return n;
+	}
+
+	m = nod(OXXX, N, N);
+	*m = *n;
+	m->inl = nil;
+	m->left	  = inlcopy(n->left);
+	m->right  = inlcopy(n->right);
+	m->list   = inlcopylist(n->list);
+	m->rlist  = inlcopylist(n->rlist);
+	m->ninit  = inlcopylist(n->ninit);
+	m->ntest  = inlcopy(n->ntest);
+	m->nincr  = inlcopy(n->nincr);
+	m->nbody  = inlcopylist(n->nbody);
+	m->nelse  = inlcopylist(n->nelse);
+
+	return m;
+}
+
+
+// Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
+// calls made to inlineable functions.  This is the external entry point.
+void
+inlcalls(Node *fn)
+{
+	Node *savefn;
+
+	savefn = curfn;
+	curfn = fn;
+	inlnode(&fn);
+	if(fn != curfn)
+		fatal("inlnode replaced curfn");
+	curfn = savefn;
+}
+
+// Turn an OINLCALL into a statement.
+static void
+inlconv2stmt(Node *n)
+{
+	n->op = OBLOCK;
+	// n->ninit stays
+	n->list = n->nbody;
+	n->nbody = nil;
+	n->rlist = nil;
+}
+
+// Turn an OINLCALL into a single valued expression.
+static void
+inlconv2expr(Node **np)
+{
+	Node *n, *r;
+	n = *np;
+	r = n->rlist->n;
+	addinit(&r, concat(n->ninit, n->nbody));
+	*np = r;
+}
+
+// Turn the rlist (with the return values) of the OINLCALL in
+// n into an expression list lumping the ninit and body
+// containing the inlined statements on the first list element so
+// order will be preserved Used in return, oas2func and call
+// statements.
+static NodeList*
+inlconv2list(Node *n)
+{
+	NodeList *l;
+
+	if(n->op != OINLCALL || n->rlist == nil)
+		fatal("inlconv2list %+N\n", n);
+	
+	l = n->rlist;
+	addinit(&l->n, concat(n->ninit, n->nbody));
+	return l;
+} 
+ 
+static void
+inlnodelist(NodeList *l)
+{
+	for(; l; l=l->next)
+		inlnode(&l->n);
+}
+
+// inlnode recurses over the tree to find inlineable calls, which will
+// be turned into OINLCALLs by mkinlcall.  When the recursion comes
+// back up will examine left, right, list, rlist, ninit, ntest, nincr,
+// nbody and nelse and use one of the 4 inlconv/glue functions above
+// to turn the OINLCALL into an expression, a statement, or patch it
+// in to this nodes list or rlist as appropriate.
+// NOTE it makes no sense to pass the glue functions down the
+// recursion to the level where the OINLCALL gets created because they
+// have to edit /this/ n, so you'd have to push that one down as well,
+// but then you may as well do it here.  so this is cleaner and
+// shorter and less complicated.
+static void
+inlnode(Node **np)
+{
+	Node *n;
+	NodeList *l;
+	int lno;
+
+	if(*np == nil)
+		return;
+
+	n = *np;
+	
+	switch(n->op) {
+	case ODEFER:
+	case OPROC:
+		// inhibit inlining of their argument
+		switch(n->left->op) {
+		case OCALLFUNC:
+		case OCALLMETH:
+			n->left->etype = n->op;
+		}
+
+	case OCLOSURE:
+		// TODO do them here (or earlier),
+		// so escape analysis can avoid more heapmoves.
+		return;
+	}
+
+	lno = setlineno(n);
+
+	inlnodelist(n->ninit);
+	for(l=n->ninit; l; l=l->next)
+		if(l->n->op == OINLCALL)
+			inlconv2stmt(l->n);
+
+	inlnode(&n->left);
+	if(n->left && n->left->op == OINLCALL)
+		inlconv2expr(&n->left);
+
+	inlnode(&n->right);
+	if(n->right && n->right->op == OINLCALL)
+		inlconv2expr(&n->right);
+
+	inlnodelist(n->list);
+	switch(n->op) {
+	case OBLOCK:
+		for(l=n->list; l; l=l->next)
+			if(l->n->op == OINLCALL)
+				inlconv2stmt(l->n);
+		break;
+
+	case ORETURN:
+	case OCALLFUNC:
+	case OCALLMETH:
+	case OCALLINTER:
+	case OAPPEND:
+	case OCOMPLEX:
+		// if we just replaced arg in f(arg()) or return arg with an inlined call
+		// and arg returns multiple values, glue as list
+		if(count(n->list) == 1 && n->list->n->op == OINLCALL && count(n->list->n->rlist) > 1) {
+			n->list = inlconv2list(n->list->n);
+			break;
+		}
+
+		// fallthrough
+	default:
+		for(l=n->list; l; l=l->next)
+			if(l->n->op == OINLCALL)
+				inlconv2expr(&l->n);
+	}
+
+	inlnodelist(n->rlist);
+	switch(n->op) {
+	case OAS2FUNC:
+		if(n->rlist->n->op == OINLCALL) {
+			n->rlist = inlconv2list(n->rlist->n);
+			n->op = OAS2;
+			n->typecheck = 0;
+			typecheck(np, Etop);
+			break;
+		}
+
+		// fallthrough
+	default:
+		for(l=n->rlist; l; l=l->next)
+			if(l->n->op == OINLCALL)
+				inlconv2expr(&l->n);
+
+	}
+
+	inlnode(&n->ntest);
+	if(n->ntest && n->ntest->op == OINLCALL)
+		inlconv2expr(&n->ntest);
+
+	inlnode(&n->nincr);
+	if(n->nincr && n->nincr->op == OINLCALL)
+		inlconv2stmt(n->nincr);
+
+	inlnodelist(n->nbody);
+	for(l=n->nbody; l; l=l->next)
+		if(l->n->op == OINLCALL)
+			inlconv2stmt(l->n);
+
+	inlnodelist(n->nelse);
+	for(l=n->nelse; l; l=l->next)
+		if(l->n->op == OINLCALL)
+			inlconv2stmt(l->n);
+
+	// with all the branches out of the way, it is now time to
+	// transmogrify this node itself unless inhibited by the
+	// switch at the top of this function.
+	switch(n->op) {
+	case OCALLFUNC:
+	case OCALLMETH:
+		if (n->etype == OPROC || n->etype == ODEFER)
+			return;
+	}
+
+	switch(n->op) {
+	case OCALLFUNC:
+		if(debug['m']>3)
+			print("%L:call to func %+N\n", n->lineno, n->left);
+		if(n->left->inl)	// normal case
+			mkinlcall(np, n->left, n->isddd);
+		else if(n->left->op == ONAME && n->left->left && n->left->left->op == OTYPE && n->left->right &&  n->left->right->op == ONAME)  // methods called as functions
+			if(n->left->sym->def)
+				mkinlcall(np, n->left->sym->def, n->isddd);
+		break;
+
+	case OCALLMETH:
+		if(debug['m']>3)
+			print("%L:call to meth %lN\n", n->lineno, n->left->right);
+		// typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
+		if(n->left->type == T) 
+			fatal("no function type for [%p] %+N\n", n->left, n->left);
+
+		if(n->left->type->nname == N) 
+			fatal("no function definition for [%p] %+T\n", n->left->type, n->left->type);
+
+		mkinlcall(np, n->left->type->nname, n->isddd);
+
+		break;
+	}
+	
+	lineno = lno;
+}
+
+static void	mkinlcall1(Node **np, Node *fn, int isddd);
+
+static void
+mkinlcall(Node **np, Node *fn, int isddd)
+{
+	int save_safemode;
+	Pkg *pkg;
+
+	save_safemode = safemode;
+
+	// imported functions may refer to unsafe as long as the
+	// package was marked safe during import (already checked).
+	pkg = fnpkg(fn);
+	if(pkg != localpkg && pkg != nil)
+		safemode = 0;
+	mkinlcall1(np, fn, isddd);
+	safemode = save_safemode;
+}
+
+static Node*
+tinlvar(Type *t)
+{
+	if(t->nname && !isblank(t->nname)) {
+		if(!t->nname->inlvar)
+			fatal("missing inlvar for %N\n", t->nname);
+		return t->nname->inlvar;
+	}
+	typecheck(&nblank, Erv | Easgn);
+	return nblank;
+}
+
+static int inlgen;
+
+// if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL.
+// On return ninit has the parameter assignments, the nbody is the
+// inlined function body and list, rlist contain the input, output
+// parameters.
+static void
+mkinlcall1(Node **np, Node *fn, int isddd)
+{
+	int i;
+	int chkargcount;
+	Node *n, *call, *saveinlfn, *as, *m;
+	NodeList *dcl, *ll, *ninit, *body;
+	Type *t;
+	// For variadic fn.
+	int variadic, varargcount, multiret;
+	Node *vararg;
+	NodeList *varargs;
+	Type *varargtype, *vararrtype;
+
+	if (fn->inl == nil)
+		return;
+
+	if (fn == curfn || fn->defn == curfn)
+		return;
+
+	if(debug['l']<2)
+		typecheckinl(fn);
+
+	n = *np;
+
+	// Bingo, we have a function node, and it has an inlineable body
+	if(debug['m']>1)
+		print("%L: inlining call to %S %#T { %#H }\n", n->lineno, fn->sym, fn->type, fn->inl);
+	else if(debug['m'])
+		print("%L: inlining call to %N\n", n->lineno, fn);
+
+	if(debug['m']>2)
+		print("%L: Before inlining: %+N\n", n->lineno, n);
+
+	saveinlfn = inlfn;
+	inlfn = fn;
+
+	ninit = n->ninit;
+
+//dumplist("ninit pre", ninit);
+
+	if(fn->defn) // local function
+		dcl = fn->inldcl;
+	else // imported function
+		dcl = fn->dcl;
+
+	inlretvars = nil;
+	i = 0;
+	// Make temp names to use instead of the originals
+	for(ll = dcl; ll; ll=ll->next) {
+		if(ll->n->class == PPARAMOUT)  // return values handled below.
+			continue;
+		if(ll->n->op == ONAME) {
+			ll->n->inlvar = inlvar(ll->n);
+			// Typecheck because inlvar is not necessarily a function parameter.
+			typecheck(&ll->n->inlvar, Erv);
+			if ((ll->n->class&~PHEAP) != PAUTO)
+				ninit = list(ninit, nod(ODCL, ll->n->inlvar, N));  // otherwise gen won't emit the allocations for heapallocs
+		}
+	}
+
+	// temporaries for return values.
+	for(t = getoutargx(fn->type)->type; t; t = t->down) {
+		if(t != T && t->nname != N && !isblank(t->nname)) {
+			m = inlvar(t->nname);
+			typecheck(&m, Erv);
+			t->nname->inlvar = m;
+		} else {
+			// anonymous return values, synthesize names for use in assignment that replaces return
+			m = retvar(t, i++);
+		}
+		ninit = list(ninit, nod(ODCL, m, N));
+		inlretvars = list(inlretvars, m);
+	}
+
+	// assign receiver.
+	if(fn->type->thistuple && n->left->op == ODOTMETH) {
+		// method call with a receiver.
+		t = getthisx(fn->type)->type;
+		if(t != T && t->nname != N && !isblank(t->nname) && !t->nname->inlvar)
+			fatal("missing inlvar for %N\n", t->nname);
+		if(!n->left->left)
+			fatal("method call without receiver: %+N", n);
+		if(t == T)
+			fatal("method call unknown receiver type: %+N", n);
+		as = nod(OAS, tinlvar(t), n->left->left);
+		if(as != N) {
+			typecheck(&as, Etop);
+			ninit = list(ninit, as);
+		}
+	}
+
+	// check if inlined function is variadic.
+	variadic = 0;
+	varargtype = T;
+	varargcount = 0;
+	for(t=fn->type->type->down->down->type; t; t=t->down) {
+		if(t->isddd) {
+			variadic = 1;
+			varargtype = t->type;
+		}
+	}
+	// but if argument is dotted too forget about variadicity.
+	if(variadic && isddd)
+		variadic = 0;
+
+	// check if argument is actually a returned tuple from call.
+	multiret = 0;
+	if(n->list && !n->list->next) {
+		switch(n->list->n->op) {
+		case OCALL:
+		case OCALLFUNC:
+		case OCALLINTER:
+		case OCALLMETH:
+			if(n->list->n->left->type->outtuple > 1)
+				multiret = n->list->n->left->type->outtuple-1;
+		}
+	}
+
+	if(variadic) {
+		varargcount = count(n->list) + multiret;
+		if(n->left->op != ODOTMETH)
+			varargcount -= fn->type->thistuple;
+		varargcount -= fn->type->intuple - 1;
+	}
+
+	// assign arguments to the parameters' temp names
+	as = nod(OAS2, N, N);
+	as->rlist = n->list;
+	ll = n->list;
+
+	// TODO: if len(nlist) == 1 but multiple args, check that n->list->n is a call?
+	if(fn->type->thistuple && n->left->op != ODOTMETH) {
+		// non-method call to method
+		if(!n->list)
+			fatal("non-method call to method without first arg: %+N", n);
+		// append receiver inlvar to LHS.
+		t = getthisx(fn->type)->type;
+		if(t != T && t->nname != N && !isblank(t->nname) && !t->nname->inlvar)
+			fatal("missing inlvar for %N\n", t->nname);
+		if(t == T)
+			fatal("method call unknown receiver type: %+N", n);
+		as->list = list(as->list, tinlvar(t));
+		ll = ll->next; // track argument count.
+	}
+
+	// append ordinary arguments to LHS.
+	chkargcount = n->list && n->list->next;
+	vararg = N;    // the slice argument to a variadic call
+	varargs = nil; // the list of LHS names to put in vararg.
+	if(!chkargcount) {
+		// 0 or 1 expression on RHS.
+		for(t = getinargx(fn->type)->type; t; t=t->down) {
+			if(variadic && t->isddd) {
+				vararg = tinlvar(t);
+				for(i=0; i<varargcount && ll; i++) {
+					m = argvar(varargtype, i);
+					varargs = list(varargs, m);
+					as->list = list(as->list, m);
+				}
+				break;
+			}
+			as->list = list(as->list, tinlvar(t));
+		}
+	} else {
+		// match arguments except final variadic (unless the call is dotted itself)
+		for(t = getinargx(fn->type)->type; t;) {
+			if(!ll)
+				break;
+			if(variadic && t->isddd)
+				break;
+			as->list = list(as->list, tinlvar(t));
+			t=t->down;
+			ll=ll->next;
+		}
+		// match varargcount arguments with variadic parameters.
+		if(variadic && t && t->isddd) {
+			vararg = tinlvar(t);
+			for(i=0; i<varargcount && ll; i++) {
+				m = argvar(varargtype, i);
+				varargs = list(varargs, m);
+				as->list = list(as->list, m);
+				ll=ll->next;
+			}
+			if(i==varargcount)
+				t=t->down;
+		}
+		if(ll || t)
+			fatal("arg count mismatch: %#T  vs %,H\n",  getinargx(fn->type), n->list);
+	}
+
+	if (as->rlist) {
+		typecheck(&as, Etop);
+		ninit = list(ninit, as);
+	}
+
+	// turn the variadic args into a slice.
+	if(variadic) {
+		as = nod(OAS, vararg, N);
+		if(!varargcount) {
+			as->right = nodnil();
+			as->right->type = varargtype;
+		} else {
+			vararrtype = typ(TARRAY);
+			vararrtype->type = varargtype->type;
+			vararrtype->bound = varargcount;
+
+			as->right = nod(OCOMPLIT, N, typenod(varargtype));
+			as->right->list = varargs;
+			as->right = nod(OSLICE, as->right, nod(OKEY, N, N));
+		}
+		typecheck(&as, Etop);
+		ninit = list(ninit, as);
+	}
+
+	// zero the outparams
+	for(ll = inlretvars; ll; ll=ll->next) {
+		as = nod(OAS, ll->n, N);
+		typecheck(&as, Etop);
+		ninit = list(ninit, as);
+	}
+
+	inlretlabel = newlabel();
+	inlgen++;
+	body = inlsubstlist(fn->inl);
+
+	body = list(body, nod(OGOTO, inlretlabel, N));	// avoid 'not used' when function doesnt have return
+	body = list(body, nod(OLABEL, inlretlabel, N));
+
+	typechecklist(body, Etop);
+//dumplist("ninit post", ninit);
+
+	call = nod(OINLCALL, N, N);
+	call->ninit = ninit;
+	call->nbody = body;
+	call->rlist = inlretvars;
+	call->type = n->type;
+	call->typecheck = 1;
+
+	setlno(call, n->lineno);
+//dumplist("call body", body);
+
+	*np = call;
+
+	inlfn =	saveinlfn;
+
+	// transitive inlining
+	// TODO do this pre-expansion on fn->inl directly.  requires
+	// either supporting exporting statemetns with complex ninits
+	// or saving inl and making inlinl
+	if(debug['l'] >= 5) {
+		body = fn->inl;
+		fn->inl = nil;	// prevent infinite recursion
+		inlnodelist(call->nbody);
+		for(ll=call->nbody; ll; ll=ll->next)
+			if(ll->n->op == OINLCALL)
+				inlconv2stmt(ll->n);
+		fn->inl = body;
+	}
+
+	if(debug['m']>2)
+		print("%L: After inlining %+N\n\n", n->lineno, *np);
+
+}
+
+// Every time we expand a function we generate a new set of tmpnames,
+// PAUTO's in the calling functions, and link them off of the
+// PPARAM's, PAUTOS and PPARAMOUTs of the called function. 
+static Node*
+inlvar(Node *var)
+{
+	Node *n;
+
+	if(debug['m']>3)
+		print("inlvar %+N\n", var);
+
+	n = newname(var->sym);
+	n->type = var->type;
+	n->class = PAUTO;
+	n->used = 1;
+	n->curfn = curfn;   // the calling function, not the called one
+	n->addrtaken = var->addrtaken;
+
+	// Esc pass wont run if we're inlining into a iface wrapper.
+	// Luckily, we can steal the results from the target func.
+	// If inlining a function defined in another package after
+	// escape analysis is done, treat all local vars as escaping.
+	// See issue 9537.
+	if(var->esc == EscHeap || (inl_nonlocal && var->op == ONAME))
+		addrescapes(n);
+
+	curfn->dcl = list(curfn->dcl, n);
+	return n;
+}
+
+// Synthesize a variable to store the inlined function's results in.
+static Node*
+retvar(Type *t, int i)
+{
+	Node *n;
+
+	snprint(namebuf, sizeof(namebuf), "~r%d", i);
+	n = newname(lookup(namebuf));
+	n->type = t->type;
+	n->class = PAUTO;
+	n->used = 1;
+	n->curfn = curfn;   // the calling function, not the called one
+	curfn->dcl = list(curfn->dcl, n);
+	return n;
+}
+
+// Synthesize a variable to store the inlined function's arguments
+// when they come from a multiple return call.
+static Node*
+argvar(Type *t, int i)
+{
+	Node *n;
+
+	snprint(namebuf, sizeof(namebuf), "~arg%d", i);
+	n = newname(lookup(namebuf));
+	n->type = t->type;
+	n->class = PAUTO;
+	n->used = 1;
+	n->curfn = curfn;   // the calling function, not the called one
+	curfn->dcl = list(curfn->dcl, n);
+	return n;
+}
+
+static Node*
+newlabel(void)
+{
+	Node *n;
+	static int label;
+	
+	label++;
+	snprint(namebuf, sizeof(namebuf), ".inlret%.6d", label);
+	n = newname(lookup(namebuf));
+	n->etype = 1;  // flag 'safe' for escape analysis (no backjumps)
+	return n;
+}
+
+// inlsubst and inlsubstlist recursively copy the body of the saved
+// pristine ->inl body of the function while substituting references
+// to input/output parameters with ones to the tmpnames, and
+// substituting returns with assignments to the output.
+static NodeList*
+inlsubstlist(NodeList *ll)
+{
+	NodeList *l;
+
+	l = nil;
+	for(; ll; ll=ll->next)
+		l = list(l, inlsubst(ll->n));
+	return l;
+}
+
+static Node*
+inlsubst(Node *n)
+{
+	char *p;
+	Node *m, *as;
+	NodeList *ll;
+
+	if(n == N)
+		return N;
+
+	switch(n->op) {
+	case ONAME:
+		if(n->inlvar) { // These will be set during inlnode
+			if (debug['m']>2)
+				print ("substituting name %+N  ->  %+N\n", n, n->inlvar);
+			return n->inlvar;
+		}
+		if (debug['m']>2)
+			print ("not substituting name %+N\n", n);
+		return n;
+
+	case OLITERAL:
+	case OTYPE:
+		return n;
+
+	case ORETURN:
+		// Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function.
+
+//		dump("Return before substitution", n);
+		m = nod(OGOTO, inlretlabel, N);
+		m->ninit  = inlsubstlist(n->ninit);
+
+		if(inlretvars && n->list) {
+			as = nod(OAS2, N, N);
+			// shallow copy or OINLCALL->rlist will be the same list, and later walk and typecheck may clobber that.
+			for(ll=inlretvars; ll; ll=ll->next)
+				as->list = list(as->list, ll->n);
+			as->rlist = inlsubstlist(n->list);
+			typecheck(&as, Etop);
+			m->ninit = list(m->ninit, as);
+		}
+
+		typechecklist(m->ninit, Etop);
+		typecheck(&m, Etop);
+//		dump("Return after substitution", m);
+		return m;
+	
+	case OGOTO:
+	case OLABEL:
+		m = nod(OXXX, N, N);
+		*m = *n;
+		m->ninit = nil;
+		p = smprint("%s·%d", n->left->sym->name, inlgen);	
+		m->left = newname(lookup(p));
+		free(p);
+		return m;	
+	}
+
+
+	m = nod(OXXX, N, N);
+	*m = *n;
+	m->ninit = nil;
+	
+	if(n->op == OCLOSURE)
+		fatal("cannot inline function containing closure: %+N", n);
+
+	m->left	  = inlsubst(n->left);
+	m->right  = inlsubst(n->right);
+	m->list	  = inlsubstlist(n->list);
+	m->rlist  = inlsubstlist(n->rlist);
+	m->ninit  = concat(m->ninit, inlsubstlist(n->ninit));
+	m->ntest  = inlsubst(n->ntest);
+	m->nincr  = inlsubst(n->nincr);
+	m->nbody  = inlsubstlist(n->nbody);
+	m->nelse  = inlsubstlist(n->nelse);
+
+	return m;
+}
+
+// Plaster over linenumbers
+static void
+setlnolist(NodeList *ll, int lno)
+{
+	for(;ll;ll=ll->next)
+		setlno(ll->n, lno);
+}
+
+static void
+setlno(Node *n, int lno)
+{
+	if(!n)
+		return;
+
+	// don't clobber names, unless they're freshly synthesized
+	if(n->op != ONAME || n->lineno == 0)
+		n->lineno = lno;
+	
+	setlno(n->left, lno);
+	setlno(n->right, lno);
+	setlnolist(n->list, lno);
+	setlnolist(n->rlist, lno);
+	setlnolist(n->ninit, lno);
+	setlno(n->ntest, lno);
+	setlno(n->nincr, lno);
+	setlnolist(n->nbody, lno);
+	setlnolist(n->nelse, lno);
+}
diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c
new file mode 100644
index 0000000..523ba37
--- /dev/null
+++ b/src/cmd/gc/lex.c
@@ -0,0 +1,2414 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include	<u.h>
+#include	<libc.h>
+#include	"go.h"
+#include	"y.tab.h"
+#include	<ar.h>
+
+#undef	getc
+#undef	ungetc
+#define	getc	ccgetc
+#define	ungetc	ccungetc
+
+extern int yychar;
+int yyprev;
+int yylast;
+
+static void	lexinit(void);
+static void	lexinit1(void);
+static void	lexfini(void);
+static void	yytinit(void);
+static int	getc(void);
+static void	ungetc(int);
+static int32	getr(void);
+static int	escchar(int, int*, vlong*);
+static void	addidir(char*);
+static int	getlinepragma(void);
+static char *goos, *goarch, *goroot;
+
+#define	BOM	0xFEFF
+
+// Compiler experiments.
+// These are controlled by the GOEXPERIMENT environment
+// variable recorded when the compiler is built.
+static struct {
+	char *name;
+	int *val;
+} exper[] = {
+//	{"rune32", &rune32},
+	{"fieldtrack", &fieldtrack_enabled},
+	{"precisestack", &precisestack_enabled},
+	{nil, nil},
+};
+
+// Debug arguments.
+// These can be specified with the -d flag, as in "-d nil"
+// to set the debug_checknil variable. In general the list passed
+// to -d can be comma-separated.
+static struct {
+	char *name;
+	int *val;
+} debugtab[] = {
+	{"nil", &debug_checknil},
+	{nil, nil},
+};
+
+static void
+addexp(char *s)
+{
+	int i;
+
+	for(i=0; exper[i].name != nil; i++) {
+		if(strcmp(exper[i].name, s) == 0) {
+			*exper[i].val = 1;
+			return;
+		}
+	}
+	
+	print("unknown experiment %s\n", s);
+	exits("unknown experiment");
+}
+
+static void
+setexp(void)
+{
+	char *f[20];
+	int i, nf;
+
+	precisestack_enabled = 1; // on by default
+
+	// cmd/dist #defines GOEXPERIMENT for us.
+	nf = getfields(GOEXPERIMENT, f, nelem(f), 1, ",");
+	for(i=0; i<nf; i++)
+		addexp(f[i]);
+}
+
+char*
+expstring(void)
+{
+	int i;
+	static char buf[512];
+
+	strcpy(buf, "X");
+	for(i=0; exper[i].name != nil; i++)
+		if(*exper[i].val)
+			seprint(buf+strlen(buf), buf+sizeof buf, ",%s", exper[i].name);
+	if(strlen(buf) == 1)
+		strcpy(buf, "X,none");
+	buf[1] = ':';
+	return buf;
+}
+
+// Our own isdigit, isspace, isalpha, isalnum that take care 
+// of EOF and other out of range arguments.
+static int
+yy_isdigit(int c)
+{
+	return c >= 0 && c <= 0xFF && isdigit(c);
+}
+
+static int
+yy_isspace(int c)
+{
+	return c == ' ' || c == '\t' || c == '\n' || c == '\r';
+}
+
+static int
+yy_isalpha(int c)
+{
+	return c >= 0 && c <= 0xFF && isalpha(c);
+}
+
+static int
+yy_isalnum(int c)
+{
+	return c >= 0 && c <= 0xFF && isalnum(c);
+}
+
+// Disallow use of isdigit etc.
+#undef isdigit
+#undef isspace
+#undef isalpha
+#undef isalnum
+#define isdigit use_yy_isdigit_instead_of_isdigit
+#define isspace use_yy_isspace_instead_of_isspace
+#define isalpha use_yy_isalpha_instead_of_isalpha
+#define isalnum use_yy_isalnum_instead_of_isalnum
+
+#define	DBG	if(!debug['x']){}else print
+/*c2go void DBG(char*, ...); */
+
+enum
+{
+	EOF		= -1,
+};
+
+void
+usage(void)
+{
+	print("usage: %cg [options] file.go...\n", thechar);
+	flagprint(1);
+	exits("usage");
+}
+
+void
+fault(int s)
+{
+	USED(s);
+
+	// If we've already complained about things
+	// in the program, don't bother complaining
+	// about the seg fault too; let the user clean up
+	// the code and try again.
+	if(nsavederrors + nerrors > 0)
+		errorexit();
+	fatal("fault");
+}
+
+#ifdef	PLAN9
+void
+catcher(void *v, char *s)
+{
+	USED(v);
+
+	if(strncmp(s, "sys: trap: fault read", 21) == 0) {
+		if(nsavederrors + nerrors > 0)
+			errorexit();
+		fatal("fault");
+	}
+	noted(NDFLT);
+}
+#endif
+
+void
+doversion(void)
+{
+	char *p;
+
+	p = expstring();
+	if(strcmp(p, "X:none") == 0)
+		p = "";
+	print("%cg version %s%s%s\n", thechar, getgoversion(), *p ? " " : "", p);
+	exits(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int i;
+	NodeList *l;
+	char *p;
+
+#ifdef	SIGBUS	
+	signal(SIGBUS, fault);
+	signal(SIGSEGV, fault);
+#endif
+
+#ifdef	PLAN9
+	notify(catcher);
+	// Tell the FPU to handle all exceptions.
+	setfcr(FPPDBL|FPRNR);
+#endif
+	// Allow GOARCH=thestring or GOARCH=thestringsuffix,
+	// but not other values.	
+	p = getgoarch();
+	if(strncmp(p, thestring, strlen(thestring)) != 0)
+		sysfatal("cannot use %cg with GOARCH=%s", thechar, p);
+	goarch = p;
+
+	linkarchinit();
+	ctxt = linknew(thelinkarch);
+	ctxt->diag = yyerror;
+	ctxt->bso = &bstdout;
+	Binit(&bstdout, 1, OWRITE);
+
+	localpkg = mkpkg(strlit(""));
+	localpkg->prefix = "\"\"";
+	
+	// pseudo-package, for scoping
+	builtinpkg = mkpkg(strlit("go.builtin"));
+
+	// pseudo-package, accessed by import "unsafe"
+	unsafepkg = mkpkg(strlit("unsafe"));
+	unsafepkg->name = "unsafe";
+
+	// real package, referred to by generated runtime calls
+	runtimepkg = mkpkg(strlit("runtime"));
+	runtimepkg->name = "runtime";
+
+	// pseudo-packages used in symbol tables
+	gostringpkg = mkpkg(strlit("go.string"));
+	gostringpkg->name = "go.string";
+	gostringpkg->prefix = "go.string";	// not go%2estring
+
+	itabpkg = mkpkg(strlit("go.itab"));
+	itabpkg->name = "go.itab";
+	itabpkg->prefix = "go.itab";	// not go%2eitab
+
+	weaktypepkg = mkpkg(strlit("go.weak.type"));
+	weaktypepkg->name = "go.weak.type";
+	weaktypepkg->prefix = "go.weak.type";  // not go%2eweak%2etype
+	
+	typelinkpkg = mkpkg(strlit("go.typelink"));
+	typelinkpkg->name = "go.typelink";
+	typelinkpkg->prefix = "go.typelink"; // not go%2etypelink
+
+	trackpkg = mkpkg(strlit("go.track"));
+	trackpkg->name = "go.track";
+	trackpkg->prefix = "go.track";  // not go%2etrack
+
+	typepkg = mkpkg(strlit("type"));
+	typepkg->name = "type";
+
+	goroot = getgoroot();
+	goos = getgoos();
+
+	nacl = strcmp(goos, "nacl") == 0;
+	if(nacl)
+		flag_largemodel = 1;
+
+	setexp();
+
+	outfile = nil;
+	flagcount("+", "compiling runtime", &compiling_runtime);
+	flagcount("%", "debug non-static initializers", &debug['%']);
+	flagcount("A", "for bootstrapping, allow 'any' type", &debug['A']);
+	flagcount("B", "disable bounds checking", &debug['B']);
+	flagstr("D", "path: set relative path for local imports", &localimport);
+	flagcount("E", "debug symbol export", &debug['E']);
+	flagfn1("I", "dir: add dir to import search path", addidir);
+	flagcount("K", "debug missing line numbers", &debug['K']);
+	flagcount("L", "use full (long) path in error messages", &debug['L']);
+	flagcount("M", "debug move generation", &debug['M']);
+	flagcount("N", "disable optimizations", &debug['N']);
+	flagcount("P", "debug peephole optimizer", &debug['P']);
+	flagcount("R", "debug register optimizer", &debug['R']);
+	flagcount("S", "print assembly listing", &debug['S']);
+	flagfn0("V", "print compiler version", doversion);
+	flagcount("W", "debug parse tree after type checking", &debug['W']);
+	flagcount("complete", "compiling complete package (no C or assembly)", &pure_go);
+	flagstr("d", "list: print debug information about items in list", &debugstr);
+	flagcount("e", "no limit on number of errors reported", &debug['e']);
+	flagcount("f", "debug stack frames", &debug['f']);
+	flagcount("g", "debug code generation", &debug['g']);
+	flagcount("h", "halt on error", &debug['h']);
+	flagcount("i", "debug line number stack", &debug['i']);
+	flagstr("installsuffix", "pkg directory suffix", &flag_installsuffix);
+	flagcount("j", "debug runtime-initialized variables", &debug['j']);
+	flagcount("l", "disable inlining", &debug['l']);
+	flagcount("live", "debug liveness analysis", &debuglive);
+	flagcount("m", "print optimization decisions", &debug['m']);
+	flagcount("nolocalimports", "reject local (relative) imports", &nolocalimports);
+	flagstr("o", "obj: set output file", &outfile);
+	flagstr("p", "path: set expected package import path", &myimportpath);
+	flagcount("pack", "write package file instead of object file", &writearchive);
+	flagcount("r", "debug generated wrappers", &debug['r']);
+	flagcount("race", "enable race detector", &flag_race);
+	flagcount("s", "warn about composite literals that can be simplified", &debug['s']);
+	flagstr("trimpath", "prefix: remove prefix from recorded source file paths", &ctxt->trimpath);
+	flagcount("u", "reject unsafe code", &safemode);
+	flagcount("v", "increase debug verbosity", &debug['v']);
+	flagcount("w", "debug type checking", &debug['w']);
+	use_writebarrier = 1;
+	flagcount("wb", "enable write barrier", &use_writebarrier);
+	flagcount("x", "debug lexer", &debug['x']);
+	flagcount("y", "debug declarations in canned imports (with -d)", &debug['y']);
+	if(thechar == '6')
+		flagcount("largemodel", "generate code that assumes a large memory model", &flag_largemodel);
+
+	flagparse(&argc, &argv, usage);
+	ctxt->debugasm = debug['S'];
+	ctxt->debugvlog = debug['v'];
+
+	if(argc < 1)
+		usage();
+
+	if(flag_race) {
+		racepkg = mkpkg(strlit("runtime/race"));
+		racepkg->name = "race";
+	}
+	
+	// parse -d argument
+	if(debugstr) {
+		char *f[100];
+		int i, j, nf;
+		
+		nf = getfields(debugstr, f, nelem(f), 1, ",");
+		for(i=0; i<nf; i++) {
+			for(j=0; debugtab[j].name != nil; j++) {
+				if(strcmp(debugtab[j].name, f[i]) == 0) {
+					*debugtab[j].val = 1;
+					break;
+				}
+			}
+			if(debugtab[j].name == nil)
+				sysfatal("unknown debug information -d '%s'\n", f[i]);
+		}
+	}
+
+	// enable inlining.  for now:
+	//	default: inlining on.  (debug['l'] == 1)
+	//	-l: inlining off  (debug['l'] == 0)
+	//	-ll, -lll: inlining on again, with extra debugging (debug['l'] > 1)
+	if(debug['l'] <= 1)
+		debug['l'] = 1 - debug['l'];
+
+	if(thechar == '8') {
+		p = getgo386();
+		if(strcmp(p, "387") == 0)
+			use_sse = 0;
+		else if(strcmp(p, "sse2") == 0)
+			use_sse = 1;
+		else
+			sysfatal("unsupported setting GO386=%s", p);
+	}
+
+	fmtinstallgo();
+	betypeinit();
+	if(widthptr == 0)
+		fatal("betypeinit failed");
+
+	lexinit();
+	typeinit();
+	lexinit1();
+	yytinit();
+
+	blockgen = 1;
+	dclcontext = PEXTERN;
+	nerrors = 0;
+	lexlineno = 1;
+
+	for(i=0; i<argc; i++) {
+		infile = argv[i];
+		linehist(infile, 0, 0);
+
+		curio.infile = infile;
+		curio.bin = Bopen(infile, OREAD);
+		if(curio.bin == nil) {
+			print("open %s: %r\n", infile);
+			errorexit();
+		}
+		curio.peekc = 0;
+		curio.peekc1 = 0;
+		curio.nlsemi = 0;
+		curio.eofnl = 0;
+		curio.last = 0;
+
+		// Skip initial BOM if present.
+		if(Bgetrune(curio.bin) != BOM)
+			Bungetrune(curio.bin);
+
+		block = 1;
+		iota = -1000000;
+
+		yyparse();
+		if(nsyntaxerrors != 0)
+			errorexit();
+
+		linehist(nil, 0, 0);
+		if(curio.bin != nil)
+			Bterm(curio.bin);
+	}
+	testdclstack();
+	mkpackage(localpkg->name);	// final import not used checks
+	lexfini();
+
+	typecheckok = 1;
+	if(debug['f'])
+		frame(1);
+
+	// Process top-level declarations in phases.
+
+	// Phase 1: const, type, and names and types of funcs.
+	//   This will gather all the information about types
+	//   and methods but doesn't depend on any of it.
+	defercheckwidth();
+	for(l=xtop; l; l=l->next)
+		if(l->n->op != ODCL && l->n->op != OAS)
+			typecheck(&l->n, Etop);
+
+	// Phase 2: Variable assignments.
+	//   To check interface assignments, depends on phase 1.
+	for(l=xtop; l; l=l->next)
+		if(l->n->op == ODCL || l->n->op == OAS)
+			typecheck(&l->n, Etop);
+	resumecheckwidth();
+
+	// Phase 3: Type check function bodies.
+	for(l=xtop; l; l=l->next) {
+		if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) {
+			curfn = l->n;
+			saveerrors();
+			typechecklist(l->n->nbody, Etop);
+			checkreturn(l->n);
+			if(nerrors != 0)
+				l->n->nbody = nil;  // type errors; do not compile
+		}
+	}
+
+	curfn = nil;
+	
+	if(nsavederrors+nerrors)
+		errorexit();
+
+	// Phase 4: Inlining
+	if(debug['l'] > 1) {
+		// Typecheck imported function bodies if debug['l'] > 1,
+		// otherwise lazily when used or re-exported.
+		for(l=importlist; l; l=l->next)
+			if (l->n->inl) {
+				saveerrors();
+				typecheckinl(l->n);
+			}
+		
+		if(nsavederrors+nerrors)
+			errorexit();
+	}
+
+	if(debug['l']) {
+		// Find functions that can be inlined and clone them before walk expands them.
+		for(l=xtop; l; l=l->next)
+			if(l->n->op == ODCLFUNC)
+				caninl(l->n);
+		
+		// Expand inlineable calls in all functions
+		for(l=xtop; l; l=l->next)
+			if(l->n->op == ODCLFUNC)
+				inlcalls(l->n);
+	}
+
+	// Phase 5: Escape analysis.
+	// Required for moving heap allocations onto stack,
+	// which in turn is required by the closure implementation,
+	// which stores the addresses of stack variables into the closure.
+	// If the closure does not escape, it needs to be on the stack
+	// or else the stack copier will not update it.
+	escapes(xtop);
+	
+	// Escape analysis moved escaped values off stack.
+	// Move large values off stack too.
+	movelarge(xtop);
+
+	// Phase 6: Compile top level functions.
+	for(l=xtop; l; l=l->next)
+		if(l->n->op == ODCLFUNC)
+			funccompile(l->n, 0);
+
+	if(nsavederrors+nerrors == 0)
+		fninit(xtop);
+
+	// Phase 7: Check external declarations.
+	for(l=externdcl; l; l=l->next)
+		if(l->n->op == ONAME)
+			typecheck(&l->n, Erv);
+
+	if(nerrors+nsavederrors)
+		errorexit();
+
+	dumpobj();
+
+	if(nerrors+nsavederrors)
+		errorexit();
+
+	flusherrors();
+	exits(0);
+	return 0;
+}
+
+void
+saveerrors(void)
+{
+	nsavederrors += nerrors;
+	nerrors = 0;
+}
+
+static int
+arsize(Biobuf *b, char *name)
+{
+	struct ar_hdr a;
+
+	if(Bread(b, a.name, sizeof(a.name)) != sizeof(a.name) ||
+	   Bread(b, a.date, sizeof(a.date)) != sizeof(a.date) ||
+	   Bread(b, a.uid, sizeof(a.uid)) != sizeof(a.uid) ||
+	   Bread(b, a.gid, sizeof(a.gid)) != sizeof(a.gid) ||
+	   Bread(b, a.mode, sizeof(a.mode)) != sizeof(a.mode) ||
+	   Bread(b, a.size, sizeof(a.size)) != sizeof(a.size) ||
+	   Bread(b, a.fmag, sizeof(a.fmag)) != sizeof(a.fmag))
+		return -1;
+
+	if(strncmp(a.name, name, strlen(name)) != 0)
+		return -1;
+
+	return atoi(a.size);
+}
+
+static int
+skiptopkgdef(Biobuf *b)
+{
+	char *p;
+	int sz;
+
+	/* archive header */
+	if((p = Brdline(b, '\n')) == nil)
+		return 0;
+	if(Blinelen(b) != 8)
+		return 0;
+	if(memcmp(p, "!<arch>\n", 8) != 0)
+		return 0;
+	/* symbol table may be first; skip it */
+	sz = arsize(b, "__.GOSYMDEF");
+	if(sz >= 0)
+		Bseek(b, sz, 1);
+	else
+		Bseek(b, 8, 0);
+	/* package export block is next */
+	sz = arsize(b, "__.PKGDEF");
+	if(sz <= 0)
+		return 0;
+	return 1;
+}
+
+static void
+addidir(char* dir)
+{
+	Idir** pp;
+
+	if(dir == nil)
+		return;
+
+	for(pp = &idirs; *pp != nil; pp = &(*pp)->link)
+		;
+	*pp = mal(sizeof(Idir));
+	(*pp)->link = nil;
+	(*pp)->dir = dir;
+}
+
+// is this path a local name?  begins with ./ or ../ or /
+static int
+islocalname(Strlit *name)
+{
+	if(name->len >= 1 && name->s[0] == '/')
+		return 1;
+	if(ctxt->windows && name->len >= 3 &&
+	   yy_isalpha(name->s[0]) && name->s[1] == ':' && name->s[2] == '/')
+	   	return 1;
+	if(name->len >= 2 && strncmp(name->s, "./", 2) == 0)
+		return 1;
+	if(name->len == 1 && strncmp(name->s, ".", 1) == 0)
+		return 1;
+	if(name->len >= 3 && strncmp(name->s, "../", 3) == 0)
+		return 1;
+	if(name->len == 2 && strncmp(name->s, "..", 2) == 0)
+		return 1;
+	return 0;
+}
+
+static int
+findpkg(Strlit *name)
+{
+	Idir *p;
+	char *q, *suffix, *suffixsep;
+
+	if(islocalname(name)) {
+		if(safemode || nolocalimports)
+			return 0;
+		// try .a before .6.  important for building libraries:
+		// if there is an array.6 in the array.a library,
+		// want to find all of array.a, not just array.6.
+		snprint(namebuf, sizeof(namebuf), "%Z.a", name);
+		if(access(namebuf, 0) >= 0)
+			return 1;
+		snprint(namebuf, sizeof(namebuf), "%Z.%c", name, thechar);
+		if(access(namebuf, 0) >= 0)
+			return 1;
+		return 0;
+	}
+
+	// local imports should be canonicalized already.
+	// don't want to see "encoding/../encoding/base64"
+	// as different from "encoding/base64".
+	q = mal(name->len+1);
+	memmove(q, name->s, name->len);
+	q[name->len] = '\0';
+	cleanname(q);
+	if(strlen(q) != name->len || memcmp(q, name->s, name->len) != 0) {
+		yyerror("non-canonical import path %Z (should be %s)", name, q);
+		return 0;
+	}
+
+	for(p = idirs; p != nil; p = p->link) {
+		snprint(namebuf, sizeof(namebuf), "%s/%Z.a", p->dir, name);
+		if(access(namebuf, 0) >= 0)
+			return 1;
+		snprint(namebuf, sizeof(namebuf), "%s/%Z.%c", p->dir, name, thechar);
+		if(access(namebuf, 0) >= 0)
+			return 1;
+	}
+	if(goroot != nil) {
+		suffix = "";
+		suffixsep = "";
+		if(flag_installsuffix != nil) {
+			suffixsep = "_";
+			suffix = flag_installsuffix;
+		} else if(flag_race) {
+			suffixsep = "_";
+			suffix = "race";
+		}
+		snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s%s/%Z.a", goroot, goos, goarch, suffixsep, suffix, name);
+		if(access(namebuf, 0) >= 0)
+			return 1;
+		snprint(namebuf, sizeof(namebuf), "%s/pkg/%s_%s%s%s/%Z.%c", goroot, goos, goarch, suffixsep, suffix, name, thechar);
+		if(access(namebuf, 0) >= 0)
+			return 1;
+	}
+	return 0;
+}
+
+static void
+fakeimport(void)
+{
+	importpkg = mkpkg(strlit("fake"));
+	cannedimports("fake.6", "$$\n");
+}
+
+void
+importfile(Val *f, int line)
+{
+	Biobuf *imp;
+	char *file, *p, *q, *tag;
+	int32 c;
+	int len;
+	Strlit *path;
+	char *cleanbuf, *prefix;
+
+	USED(line);
+
+	if(f->ctype != CTSTR) {
+		yyerror("import statement not a string");
+		fakeimport();
+		return;
+	}
+
+	if(f->u.sval->len == 0) {
+		yyerror("import path is empty");
+		fakeimport();
+		return;
+	}
+
+	if(isbadimport(f->u.sval)) {
+		fakeimport();
+		return;
+	}
+
+	// The package name main is no longer reserved,
+	// but we reserve the import path "main" to identify
+	// the main package, just as we reserve the import 
+	// path "math" to identify the standard math package.
+	if(strcmp(f->u.sval->s, "main") == 0) {
+		yyerror("cannot import \"main\"");
+		errorexit();
+	}
+
+	if(myimportpath != nil && strcmp(f->u.sval->s, myimportpath) == 0) {
+		yyerror("import \"%Z\" while compiling that package (import cycle)", f->u.sval);
+		errorexit();
+	}
+
+	if(strcmp(f->u.sval->s, "unsafe") == 0) {
+		if(safemode) {
+			yyerror("cannot import package unsafe");
+			errorexit();
+		}
+		importpkg = mkpkg(f->u.sval);
+		cannedimports("unsafe.6", unsafeimport);
+		return;
+	}
+	
+	path = f->u.sval;
+	if(islocalname(path)) {
+		if(path->s[0] == '/') {
+			yyerror("import path cannot be absolute path");
+			fakeimport();
+			return;
+		}
+		prefix = ctxt->pathname;
+		if(localimport != nil)
+			prefix = localimport;
+		cleanbuf = mal(strlen(prefix) + strlen(path->s) + 2);
+		strcpy(cleanbuf, prefix);
+		strcat(cleanbuf, "/");
+		strcat(cleanbuf, path->s);
+		cleanname(cleanbuf);
+		path = strlit(cleanbuf);
+		
+		if(isbadimport(path)) {
+			fakeimport();
+			return;
+		}
+	}
+
+	if(!findpkg(path)) {
+		yyerror("can't find import: \"%Z\"", f->u.sval);
+		errorexit();
+	}
+	importpkg = mkpkg(path);
+
+	// If we already saw that package, feed a dummy statement
+	// to the lexer to avoid parsing export data twice.
+	if(importpkg->imported) {
+		file = strdup(namebuf);
+		tag = "";
+		if(importpkg->safe) {
+			tag = "safe";
+		}
+		p = smprint("package %s %s\n$$\n", importpkg->name, tag);
+		cannedimports(file, p);
+		return;
+	}
+	importpkg->imported = 1;
+
+	imp = Bopen(namebuf, OREAD);
+	if(imp == nil) {
+		yyerror("can't open import: \"%Z\": %r", f->u.sval);
+		errorexit();
+	}
+	file = strdup(namebuf);
+
+	len = strlen(namebuf);
+	if(len > 2 && namebuf[len-2] == '.' && namebuf[len-1] == 'a') {
+		if(!skiptopkgdef(imp)) {
+			yyerror("import %s: not a package file", file);
+			errorexit();
+		}
+	}
+	
+	// check object header
+	p = Brdstr(imp, '\n', 1);
+	if(strcmp(p, "empty archive") != 0) {
+		if(strncmp(p, "go object ", 10) != 0) {
+			yyerror("import %s: not a go object file", file);
+			errorexit();
+		}
+		q = smprint("%s %s %s %s", getgoos(), getgoarch(), getgoversion(), expstring());
+		if(strcmp(p+10, q) != 0) {
+			yyerror("import %s: object is [%s] expected [%s]", file, p+10, q);
+			errorexit();
+		}
+		free(q);
+	}
+
+	// assume files move (get installed)
+	// so don't record the full path.
+	linehist(file + len - path->len - 2, -1, 1);	// acts as #pragma lib
+
+	/*
+	 * position the input right
+	 * after $$ and return
+	 */
+	pushedio = curio;
+	curio.bin = imp;
+	curio.peekc = 0;
+	curio.peekc1 = 0;
+	curio.infile = file;
+	curio.nlsemi = 0;
+	typecheckok = 1;
+
+	for(;;) {
+		c = getc();
+		if(c == EOF)
+			break;
+		if(c != '$')
+			continue;
+		c = getc();
+		if(c == EOF)
+			break;
+		if(c != '$')
+			continue;
+		return;
+	}
+	yyerror("no import in \"%Z\"", f->u.sval);
+	unimportfile();
+}
+
+void
+unimportfile(void)
+{
+	if(curio.bin != nil) {
+		Bterm(curio.bin);
+		curio.bin = nil;
+	} else
+		lexlineno--;	// re correct sys.6 line number
+
+	curio = pushedio;
+	pushedio.bin = nil;
+	incannedimport = 0;
+	typecheckok = 0;
+}
+
+void
+cannedimports(char *file, char *cp)
+{
+	lexlineno++;		// if sys.6 is included on line 1,
+
+	pushedio = curio;
+	curio.bin = nil;
+	curio.peekc = 0;
+	curio.peekc1 = 0;
+	curio.infile = file;
+	curio.cp = cp;
+	curio.nlsemi = 0;
+	curio.importsafe = 0;
+
+	typecheckok = 1;
+	incannedimport = 1;
+}
+
+static int
+isfrog(int c)
+{
+	// complain about possibly invisible control characters
+	if(c < ' ') {
+		return !yy_isspace(c);	// exclude good white space
+	}
+	if(0x7f <= c && c <= 0xa0)	// DEL, unicode block including unbreakable space.
+		return 1;
+	return 0;
+}
+
+typedef struct Loophack Loophack;
+struct Loophack {
+	int v;
+	Loophack *next;
+};
+
+static int32
+_yylex(void)
+{
+	int c, c1, clen, escflag, ncp;
+	vlong v;
+	char *cp, *ep;
+	Rune rune;
+	Sym *s;
+	static Loophack *lstk;
+	Loophack *h;
+
+	prevlineno = lineno;
+
+l0:
+	c = getc();
+	if(yy_isspace(c)) {
+		if(c == '\n' && curio.nlsemi) {
+			ungetc(c);
+			DBG("lex: implicit semi\n");
+			return ';';
+		}
+		goto l0;
+	}
+
+	lineno = lexlineno;	/* start of token */
+
+	if(c >= Runeself) {
+		/* all multibyte runes are alpha */
+		cp = lexbuf;
+		ep = lexbuf+sizeof lexbuf;
+		goto talph;
+	}
+
+	if(yy_isalpha(c)) {
+		cp = lexbuf;
+		ep = lexbuf+sizeof lexbuf;
+		goto talph;
+	}
+
+	if(yy_isdigit(c))
+		goto tnum;
+
+	switch(c) {
+	case EOF:
+		lineno = prevlineno;
+		ungetc(EOF);
+		return -1;
+
+	case '_':
+		cp = lexbuf;
+		ep = lexbuf+sizeof lexbuf;
+		goto talph;
+
+	case '.':
+		c1 = getc();
+		if(yy_isdigit(c1)) {
+			cp = lexbuf;
+			ep = lexbuf+sizeof lexbuf;
+			*cp++ = c;
+			c = c1;
+			goto casedot;
+		}
+		if(c1 == '.') {
+			c1 = getc();
+			if(c1 == '.') {
+				c = LDDD;
+				goto lx;
+			}
+			ungetc(c1);
+			c1 = '.';
+		}
+		break;
+
+	case '"':
+		/* "..." */
+		strcpy(lexbuf, "\"<string>\"");
+		cp = mal(8);
+		clen = sizeof(int32);
+		ncp = 8;
+
+		for(;;) {
+			if(clen+UTFmax > ncp) {
+				cp = remal(cp, ncp, ncp);
+				ncp += ncp;
+			}
+			if(escchar('"', &escflag, &v))
+				break;
+			if(v < Runeself || escflag) {
+				cp[clen++] = v;
+			} else {
+				rune = v;
+				c = runelen(rune);
+				runetochar(cp+clen, &rune);
+				clen += c;
+			}
+		}
+		goto strlit;
+	
+	case '`':
+		/* `...` */
+		strcpy(lexbuf, "`<string>`");
+		cp = mal(8);
+		clen = sizeof(int32);
+		ncp = 8;
+
+		for(;;) {
+			if(clen+UTFmax > ncp) {
+				cp = remal(cp, ncp, ncp);
+				ncp += ncp;
+			}
+			c = getr();
+			if(c == '\r')
+				continue;
+			if(c == EOF) {
+				yyerror("eof in string");
+				break;
+			}
+			if(c == '`')
+				break;
+			rune = c;
+			clen += runetochar(cp+clen, &rune);
+		}
+
+	strlit:
+		*(int32*)cp = clen-sizeof(int32);	// length
+		do {
+			cp[clen++] = 0;
+		} while(clen & MAXALIGN);
+		yylval.val.u.sval = (Strlit*)cp;
+		yylval.val.ctype = CTSTR;
+		DBG("lex: string literal\n");
+		strcpy(litbuf, "string literal");
+		return LLITERAL;
+
+	case '\'':
+		/* '.' */
+		if(escchar('\'', &escflag, &v)) {
+			yyerror("empty character literal or unescaped ' in character literal");
+			v = '\'';
+		}
+		if(!escchar('\'', &escflag, &v)) {
+			yyerror("missing '");
+			ungetc(v);
+		}
+		yylval.val.u.xval = mal(sizeof(*yylval.val.u.xval));
+		mpmovecfix(yylval.val.u.xval, v);
+		yylval.val.ctype = CTRUNE;
+		DBG("lex: codepoint literal\n");
+		strcpy(litbuf, "string literal");
+		return LLITERAL;
+
+	case '/':
+		c1 = getc();
+		if(c1 == '*') {
+			int nl;
+			
+			nl = 0;
+			for(;;) {
+				c = getr();
+				if(c == '\n')
+					nl = 1;
+				while(c == '*') {
+					c = getr();
+					if(c == '/') {
+						if(nl)
+							ungetc('\n');
+						goto l0;
+					}
+					if(c == '\n')
+						nl = 1;
+				}
+				if(c == EOF) {
+					yyerror("eof in comment");
+					errorexit();
+				}
+			}
+		}
+		if(c1 == '/') {
+			c = getlinepragma();
+			for(;;) {
+				if(c == '\n' || c == EOF) {
+					ungetc(c);
+					goto l0;
+				}
+				c = getr();
+			}
+		}
+		if(c1 == '=') {
+			c = ODIV;
+			goto asop;
+		}
+		break;
+
+	case ':':
+		c1 = getc();
+		if(c1 == '=') {
+			c = LCOLAS;
+			yylval.i = lexlineno;
+			goto lx;
+		}
+		break;
+
+	case '*':
+		c1 = getc();
+		if(c1 == '=') {
+			c = OMUL;
+			goto asop;
+		}
+		break;
+
+	case '%':
+		c1 = getc();
+		if(c1 == '=') {
+			c = OMOD;
+			goto asop;
+		}
+		break;
+
+	case '+':
+		c1 = getc();
+		if(c1 == '+') {
+			c = LINC;
+			goto lx;
+		}
+		if(c1 == '=') {
+			c = OADD;
+			goto asop;
+		}
+		break;
+
+	case '-':
+		c1 = getc();
+		if(c1 == '-') {
+			c = LDEC;
+			goto lx;
+		}
+		if(c1 == '=') {
+			c = OSUB;
+			goto asop;
+		}
+		break;
+
+	case '>':
+		c1 = getc();
+		if(c1 == '>') {
+			c = LRSH;
+			c1 = getc();
+			if(c1 == '=') {
+				c = ORSH;
+				goto asop;
+			}
+			break;
+		}
+		if(c1 == '=') {
+			c = LGE;
+			goto lx;
+		}
+		c = LGT;
+		break;
+
+	case '<':
+		c1 = getc();
+		if(c1 == '<') {
+			c = LLSH;
+			c1 = getc();
+			if(c1 == '=') {
+				c = OLSH;
+				goto asop;
+			}
+			break;
+		}
+		if(c1 == '=') {
+			c = LLE;
+			goto lx;
+		}
+		if(c1 == '-') {
+			c = LCOMM;
+			goto lx;
+		}
+		c = LLT;
+		break;
+
+	case '=':
+		c1 = getc();
+		if(c1 == '=') {
+			c = LEQ;
+			goto lx;
+		}
+		break;
+
+	case '!':
+		c1 = getc();
+		if(c1 == '=') {
+			c = LNE;
+			goto lx;
+		}
+		break;
+
+	case '&':
+		c1 = getc();
+		if(c1 == '&') {
+			c = LANDAND;
+			goto lx;
+		}
+		if(c1 == '^') {
+			c = LANDNOT;
+			c1 = getc();
+			if(c1 == '=') {
+				c = OANDNOT;
+				goto asop;
+			}
+			break;
+		}
+		if(c1 == '=') {
+			c = OAND;
+			goto asop;
+		}
+		break;
+
+	case '|':
+		c1 = getc();
+		if(c1 == '|') {
+			c = LOROR;
+			goto lx;
+		}
+		if(c1 == '=') {
+			c = OOR;
+			goto asop;
+		}
+		break;
+
+	case '^':
+		c1 = getc();
+		if(c1 == '=') {
+			c = OXOR;
+			goto asop;
+		}
+		break;
+
+	/*
+	 * clumsy dance:
+	 * to implement rule that disallows
+	 *	if T{1}[0] { ... }
+	 * but allows
+	 * 	if (T{1}[0]) { ... }
+	 * the block bodies for if/for/switch/select
+	 * begin with an LBODY token, not '{'.
+	 *
+	 * when we see the keyword, the next
+	 * non-parenthesized '{' becomes an LBODY.
+	 * loophack is normally 0.
+	 * a keyword makes it go up to 1.
+	 * parens push loophack onto a stack and go back to 0.
+	 * a '{' with loophack == 1 becomes LBODY and disables loophack.
+	 *
+	 * i said it was clumsy.
+	 */
+	case '(':
+	case '[':
+		if(loophack || lstk != nil) {
+			h = malloc(sizeof *h);
+			if(h == nil) {
+				flusherrors();
+				yyerror("out of memory");
+				errorexit();
+			}
+			h->v = loophack;
+			h->next = lstk;
+			lstk = h;
+			loophack = 0;
+		}
+		goto lx;
+	case ')':
+	case ']':
+		if(lstk != nil) {
+			h = lstk;
+			loophack = h->v;
+			lstk = h->next;
+			free(h);
+		}
+		goto lx;
+	case '{':
+		if(loophack == 1) {
+			DBG("%L lex: LBODY\n", lexlineno);
+			loophack = 0;
+			return LBODY;
+		}
+		goto lx;
+
+	default:
+		goto lx;
+	}
+	ungetc(c1);
+
+lx:
+	if(c > 0xff)
+		DBG("%L lex: TOKEN %s\n", lexlineno, lexname(c));
+	else
+		DBG("%L lex: TOKEN '%c'\n", lexlineno, c);
+	if(isfrog(c)) {
+		yyerror("illegal character 0x%ux", c);
+		goto l0;
+	}
+	if(importpkg == nil && (c == '#' || c == '$' || c == '?' || c == '@' || c == '\\')) {
+		yyerror("%s: unexpected %c", "syntax error", c);
+		goto l0;
+	}
+	return c;
+
+asop:
+	yylval.i = c;	// rathole to hold which asop
+	DBG("lex: TOKEN ASOP %c\n", c);
+	return LASOP;
+
+talph:
+	/*
+	 * cp is set to lexbuf and some
+	 * prefix has been stored
+	 */
+	for(;;) {
+		if(cp+10 >= ep) {
+			yyerror("identifier too long");
+			errorexit();
+		}
+		if(c >= Runeself) {
+			ungetc(c);
+			rune = getr();
+			// 0xb7 · is used for internal names
+			if(!isalpharune(rune) && !isdigitrune(rune) && (importpkg == nil || rune != 0xb7))
+				yyerror("invalid identifier character U+%04x", rune);
+			cp += runetochar(cp, &rune);
+		} else if(!yy_isalnum(c) && c != '_')
+			break;
+		else
+			*cp++ = c;
+		c = getc();
+	}
+	*cp = 0;
+	ungetc(c);
+
+	s = lookup(lexbuf);
+	switch(s->lexical) {
+	case LIGNORE:
+		goto l0;
+
+	case LFOR:
+	case LIF:
+	case LSWITCH:
+	case LSELECT:
+		loophack = 1;	// see comment about loophack above
+		break;
+	}
+
+	DBG("lex: %S %s\n", s, lexname(s->lexical));
+	yylval.sym = s;
+	return s->lexical;
+
+tnum:
+	cp = lexbuf;
+	ep = lexbuf+sizeof lexbuf;
+	if(c != '0') {
+		for(;;) {
+			if(cp+10 >= ep) {
+				yyerror("identifier too long");
+				errorexit();
+			}
+			*cp++ = c;
+			c = getc();
+			if(yy_isdigit(c))
+				continue;
+			goto dc;
+		}
+	}
+	*cp++ = c;
+	c = getc();
+	if(c == 'x' || c == 'X') {
+		for(;;) {
+			if(cp+10 >= ep) {
+				yyerror("identifier too long");
+				errorexit();
+			}
+			*cp++ = c;
+			c = getc();
+			if(yy_isdigit(c))
+				continue;
+			if(c >= 'a' && c <= 'f')
+				continue;
+			if(c >= 'A' && c <= 'F')
+				continue;
+			if(cp == lexbuf+2)
+				yyerror("malformed hex constant");
+			if(c == 'p')
+				goto caseep;
+			goto ncu;
+		}
+	}
+
+	if(c == 'p')	// 0p begins floating point zero
+		goto caseep;
+
+	c1 = 0;
+	for(;;) {
+		if(cp+10 >= ep) {
+			yyerror("identifier too long");
+			errorexit();
+		}
+		if(!yy_isdigit(c))
+			break;
+		if(c < '0' || c > '7')
+			c1 = 1;		// not octal
+		*cp++ = c;
+		c = getc();
+	}
+	if(c == '.')
+		goto casedot;
+	if(c == 'e' || c == 'E')
+		goto caseep;
+	if(c == 'i')
+		goto casei;
+	if(c1)
+		yyerror("malformed octal constant");
+	goto ncu;
+
+dc:
+	if(c == '.')
+		goto casedot;
+	if(c == 'e' || c == 'E' || c == 'p' || c == 'P')
+		goto caseep;
+	if(c == 'i')
+		goto casei;
+
+ncu:
+	*cp = 0;
+	ungetc(c);
+
+	yylval.val.u.xval = mal(sizeof(*yylval.val.u.xval));
+	mpatofix(yylval.val.u.xval, lexbuf);
+	if(yylval.val.u.xval->ovf) {
+		yyerror("overflow in constant");
+		mpmovecfix(yylval.val.u.xval, 0);
+	}
+	yylval.val.ctype = CTINT;
+	DBG("lex: integer literal\n");
+	strcpy(litbuf, "literal ");
+	strcat(litbuf, lexbuf);
+	return LLITERAL;
+
+casedot:
+	for(;;) {
+		if(cp+10 >= ep) {
+			yyerror("identifier too long");
+			errorexit();
+		}
+		*cp++ = c;
+		c = getc();
+		if(!yy_isdigit(c))
+			break;
+	}
+	if(c == 'i')
+		goto casei;
+	if(c != 'e' && c != 'E')
+		goto caseout;
+
+caseep:
+	*cp++ = c;
+	c = getc();
+	if(c == '+' || c == '-') {
+		*cp++ = c;
+		c = getc();
+	}
+	if(!yy_isdigit(c))
+		yyerror("malformed fp constant exponent");
+	while(yy_isdigit(c)) {
+		if(cp+10 >= ep) {
+			yyerror("identifier too long");
+			errorexit();
+		}
+		*cp++ = c;
+		c = getc();
+	}
+	if(c == 'i')
+		goto casei;
+	goto caseout;
+
+casei:
+	// imaginary constant
+	*cp = 0;
+	yylval.val.u.cval = mal(sizeof(*yylval.val.u.cval));
+	mpmovecflt(&yylval.val.u.cval->real, 0.0);
+	mpatoflt(&yylval.val.u.cval->imag, lexbuf);
+	if(yylval.val.u.cval->imag.val.ovf) {
+		yyerror("overflow in imaginary constant");
+		mpmovecflt(&yylval.val.u.cval->real, 0.0);
+	}
+	yylval.val.ctype = CTCPLX;
+	DBG("lex: imaginary literal\n");
+	strcpy(litbuf, "literal ");
+	strcat(litbuf, lexbuf);
+	return LLITERAL;
+
+caseout:
+	*cp = 0;
+	ungetc(c);
+
+	yylval.val.u.fval = mal(sizeof(*yylval.val.u.fval));
+	mpatoflt(yylval.val.u.fval, lexbuf);
+	if(yylval.val.u.fval->val.ovf) {
+		yyerror("overflow in float constant");
+		mpmovecflt(yylval.val.u.fval, 0.0);
+	}
+	yylval.val.ctype = CTFLT;
+	DBG("lex: floating literal\n");
+	strcpy(litbuf, "literal ");
+	strcat(litbuf, lexbuf);
+	return LLITERAL;
+}
+
+/*
+ * read and interpret syntax that looks like
+ * //line parse.y:15
+ * as a discontinuity in sequential line numbers.
+ * the next line of input comes from parse.y:15
+ */
+static int
+getlinepragma(void)
+{
+	int i, c, n;
+	char *cp, *ep, *linep;
+	Hist *h;
+
+	c = getr();
+	if(c == 'g')
+		goto go;
+	if(c != 'l')	
+		goto out;
+	for(i=1; i<5; i++) {
+		c = getr();
+		if(c != "line "[i])
+			goto out;
+	}
+
+	cp = lexbuf;
+	ep = lexbuf+sizeof(lexbuf)-5;
+	linep = nil;
+	for(;;) {
+		c = getr();
+		if(c == EOF)
+			goto out;
+		if(c == '\n')
+			break;
+		if(c == ' ')
+			continue;
+		if(c == ':')
+			linep = cp;
+		if(cp < ep)
+			*cp++ = c;
+	}
+	*cp = 0;
+
+	if(linep == nil || linep >= ep)
+		goto out;
+	*linep++ = '\0';
+	n = 0;
+	for(cp=linep; *cp; cp++) {
+		if(*cp < '0' || *cp > '9')
+			goto out;
+		n = n*10 + *cp - '0';
+		if(n > 1e8) {
+			yyerror("line number out of range");
+			errorexit();
+		}
+	}
+	if(n <= 0)
+		goto out;
+
+	// try to avoid allocating file name over and over
+	for(h=ctxt->hist; h!=nil; h=h->link) {
+		if(h->name != nil && strcmp(h->name, lexbuf) == 0) {
+			linehist(h->name, n, 0);
+			goto out;
+		}
+	}
+	linehist(strdup(lexbuf), n, 0);
+	goto out;
+
+go:
+	cp = lexbuf;
+	ep = lexbuf+sizeof(lexbuf)-5;
+	*cp++ = 'g'; // already read
+	for(;;) {
+		c = getr();
+		if(c == EOF || c >= Runeself)
+			goto out;
+		if(c == '\n')
+			break;
+		if(cp < ep)
+			*cp++ = c;
+	}
+	*cp = 0;
+	ep = strchr(lexbuf, ' ');
+	if(ep != nil)
+		*ep = 0;
+
+	if(strcmp(lexbuf, "go:nointerface") == 0 && fieldtrack_enabled) {
+		nointerface = 1;
+		goto out;
+	}
+	if(strcmp(lexbuf, "go:noescape") == 0) {
+		noescape = 1;
+		goto out;
+	}
+	if(strcmp(lexbuf, "go:nosplit") == 0) {
+		nosplit = 1;
+		goto out;
+	}
+	
+out:
+	return c;
+}
+
+int32
+yylex(void)
+{
+	int lx;
+	
+	lx = _yylex();
+	
+	if(curio.nlsemi && lx == EOF) {
+		// Treat EOF as "end of line" for the purposes
+		// of inserting a semicolon.
+		lx = ';';
+	}
+
+	switch(lx) {
+	case LNAME:
+	case LLITERAL:
+	case LBREAK:
+	case LCONTINUE:
+	case LFALL:
+	case LRETURN:
+	case LINC:
+	case LDEC:
+	case ')':
+	case '}':
+	case ']':
+		curio.nlsemi = 1;
+		break;
+	default:
+		curio.nlsemi = 0;
+		break;
+	}
+
+	// Track last two tokens returned by yylex.
+	yyprev = yylast;
+	yylast = lx;
+	return lx;
+}
+
+static int
+getc(void)
+{
+	int c, c1, c2;
+
+	c = curio.peekc;
+	if(c != 0) {
+		curio.peekc = curio.peekc1;
+		curio.peekc1 = 0;
+		goto check;
+	}
+	
+	if(curio.bin == nil) {
+		c = *curio.cp & 0xff;
+		if(c != 0)
+			curio.cp++;
+	} else {
+	loop:
+		c = BGETC(curio.bin);
+		if(c == 0xef) {
+			c1 = BGETC(curio.bin);
+			c2 = BGETC(curio.bin);
+			if(c1 == 0xbb && c2 == 0xbf) {
+				yyerrorl(lexlineno, "Unicode (UTF-8) BOM in middle of file");
+				goto loop;
+			}
+			Bungetc(curio.bin);
+			Bungetc(curio.bin);
+		}
+	}
+
+check:
+	switch(c) {
+	case 0:
+		if(curio.bin != nil) {
+			yyerror("illegal NUL byte");
+			break;
+		}
+	case EOF:
+		// insert \n at EOF
+		if(curio.eofnl || curio.last == '\n')
+			return EOF;
+		curio.eofnl = 1;
+		c = '\n';
+	case '\n':
+		if(pushedio.bin == nil)
+			lexlineno++;
+		break;
+	}
+	curio.last = c;
+	return c;
+}
+
+static void
+ungetc(int c)
+{
+	curio.peekc1 = curio.peekc;
+	curio.peekc = c;
+	if(c == '\n' && pushedio.bin == nil)
+		lexlineno--;
+}
+
+static int32
+getr(void)
+{
+	int c, i;
+	char str[UTFmax+1];
+	Rune rune;
+
+	c = getc();
+	if(c < Runeself)
+		return c;
+	i = 0;
+	str[i++] = c;
+
+loop:
+	c = getc();
+	str[i++] = c;
+	if(!fullrune(str, i))
+		goto loop;
+	c = chartorune(&rune, str);
+	if(rune == Runeerror && c == 1) {
+		lineno = lexlineno;
+		yyerror("illegal UTF-8 sequence");
+		flusherrors();
+		print("\t");
+		for(c=0; c<i; c++)
+			print("%s%.2x", c > 0 ? " " : "", *(uchar*)(str+c));
+		print("\n");
+	}
+	return rune;
+}
+
+static int
+escchar(int e, int *escflg, vlong *val)
+{
+	int i, u, c;
+	vlong l;
+
+	*escflg = 0;
+
+	c = getr();
+	switch(c) {
+	case EOF:
+		yyerror("eof in string");
+		return 1;
+	case '\n':
+		yyerror("newline in string");
+		return 1;
+	case '\\':
+		break;
+	default:
+		if(c == e)
+			return 1;
+		*val = c;
+		return 0;
+	}
+
+	u = 0;
+	c = getr();
+	switch(c) {
+	case 'x':
+		*escflg = 1;	// it's a byte
+		i = 2;
+		goto hex;
+
+	case 'u':
+		i = 4;
+		u = 1;
+		goto hex;
+
+	case 'U':
+		i = 8;
+		u = 1;
+		goto hex;
+
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+		*escflg = 1;	// it's a byte
+		goto oct;
+
+	case 'a': c = '\a'; break;
+	case 'b': c = '\b'; break;
+	case 'f': c = '\f'; break;
+	case 'n': c = '\n'; break;
+	case 'r': c = '\r'; break;
+	case 't': c = '\t'; break;
+	case 'v': c = '\v'; break;
+	case '\\': c = '\\'; break;
+
+	default:
+		if(c != e)
+			yyerror("unknown escape sequence: %c", c);
+	}
+	*val = c;
+	return 0;
+
+hex:
+	l = 0;
+	for(; i>0; i--) {
+		c = getc();
+		if(c >= '0' && c <= '9') {
+			l = l*16 + c-'0';
+			continue;
+		}
+		if(c >= 'a' && c <= 'f') {
+			l = l*16 + c-'a' + 10;
+			continue;
+		}
+		if(c >= 'A' && c <= 'F') {
+			l = l*16 + c-'A' + 10;
+			continue;
+		}
+		yyerror("non-hex character in escape sequence: %c", c);
+		ungetc(c);
+		break;
+	}
+	if(u && (l > Runemax || (0xd800 <= l && l < 0xe000))) {
+		yyerror("invalid Unicode code point in escape sequence: %#llx", l);
+		l = Runeerror;
+	}
+	*val = l;
+	return 0;
+
+oct:
+	l = c - '0';
+	for(i=2; i>0; i--) {
+		c = getc();
+		if(c >= '0' && c <= '7') {
+			l = l*8 + c-'0';
+			continue;
+		}
+		yyerror("non-octal character in escape sequence: %c", c);
+		ungetc(c);
+	}
+	if(l > 255)
+		yyerror("octal escape value > 255: %d", l);
+
+	*val = l;
+	return 0;
+}
+
+static	struct
+{
+	char*	name;
+	int	lexical;
+	int	etype;
+	int	op;
+} syms[] =
+{
+/*	name		lexical		etype		op
+ */
+/* basic types */
+	{"int8",		LNAME,		TINT8,		OXXX},
+	{"int16",	LNAME,		TINT16,		OXXX},
+	{"int32",	LNAME,		TINT32,		OXXX},
+	{"int64",	LNAME,		TINT64,		OXXX},
+
+	{"uint8",	LNAME,		TUINT8,		OXXX},
+	{"uint16",	LNAME,		TUINT16,	OXXX},
+	{"uint32",	LNAME,		TUINT32,	OXXX},
+	{"uint64",	LNAME,		TUINT64,	OXXX},
+
+	{"float32",	LNAME,		TFLOAT32,	OXXX},
+	{"float64",	LNAME,		TFLOAT64,	OXXX},
+
+	{"complex64",	LNAME,		TCOMPLEX64,	OXXX},
+	{"complex128",	LNAME,		TCOMPLEX128,	OXXX},
+
+	{"bool",		LNAME,		TBOOL,		OXXX},
+	{"string",	LNAME,		TSTRING,	OXXX},
+
+	{"any",		LNAME,		TANY,		OXXX},
+
+	{"break",	LBREAK,		Txxx,		OXXX},
+	{"case",		LCASE,		Txxx,		OXXX},
+	{"chan",		LCHAN,		Txxx,		OXXX},
+	{"const",	LCONST,		Txxx,		OXXX},
+	{"continue",	LCONTINUE,	Txxx,		OXXX},
+	{"default",	LDEFAULT,	Txxx,		OXXX},
+	{"else",		LELSE,		Txxx,		OXXX},
+	{"defer",	LDEFER,		Txxx,		OXXX},
+	{"fallthrough",	LFALL,		Txxx,		OXXX},
+	{"for",		LFOR,		Txxx,		OXXX},
+	{"func",		LFUNC,		Txxx,		OXXX},
+	{"go",		LGO,		Txxx,		OXXX},
+	{"goto",		LGOTO,		Txxx,		OXXX},
+	{"if",		LIF,		Txxx,		OXXX},
+	{"import",	LIMPORT,	Txxx,		OXXX},
+	{"interface",	LINTERFACE,	Txxx,		OXXX},
+	{"map",		LMAP,		Txxx,		OXXX},
+	{"package",	LPACKAGE,	Txxx,		OXXX},
+	{"range",	LRANGE,		Txxx,		OXXX},
+	{"return",	LRETURN,	Txxx,		OXXX},
+	{"select",	LSELECT,	Txxx,		OXXX},
+	{"struct",	LSTRUCT,	Txxx,		OXXX},
+	{"switch",	LSWITCH,	Txxx,		OXXX},
+	{"type",		LTYPE,		Txxx,		OXXX},
+	{"var",		LVAR,		Txxx,		OXXX},
+
+	{"append",	LNAME,		Txxx,		OAPPEND},
+	{"cap",		LNAME,		Txxx,		OCAP},
+	{"close",	LNAME,		Txxx,		OCLOSE},
+	{"complex",	LNAME,		Txxx,		OCOMPLEX},
+	{"copy",		LNAME,		Txxx,		OCOPY},
+	{"delete",	LNAME,		Txxx,		ODELETE},
+	{"imag",		LNAME,		Txxx,		OIMAG},
+	{"len",		LNAME,		Txxx,		OLEN},
+	{"make",		LNAME,		Txxx,		OMAKE},
+	{"new",		LNAME,		Txxx,		ONEW},
+	{"panic",	LNAME,		Txxx,		OPANIC},
+	{"print",	LNAME,		Txxx,		OPRINT},
+	{"println",	LNAME,		Txxx,		OPRINTN},
+	{"real",		LNAME,		Txxx,		OREAL},
+	{"recover",	LNAME,		Txxx,		ORECOVER},
+
+	{"notwithstanding",		LIGNORE,	Txxx,		OXXX},
+	{"thetruthofthematter",		LIGNORE,	Txxx,		OXXX},
+	{"despiteallobjections",		LIGNORE,	Txxx,		OXXX},
+	{"whereas",			LIGNORE,	Txxx,		OXXX},
+	{"insofaras",			LIGNORE,	Txxx,		OXXX},
+};
+
+static void
+lexinit(void)
+{
+	int i, lex;
+	Sym *s, *s1;
+	Type *t;
+	int etype;
+	Val v;
+
+	/*
+	 * initialize basic types array
+	 * initialize known symbols
+	 */
+	for(i=0; i<nelem(syms); i++) {
+		lex = syms[i].lexical;
+		s = lookup(syms[i].name);
+		s->lexical = lex;
+
+		etype = syms[i].etype;
+		if(etype != Txxx) {
+			if(etype < 0 || etype >= nelem(types))
+				fatal("lexinit: %s bad etype", s->name);
+			s1 = pkglookup(syms[i].name, builtinpkg);
+			t = types[etype];
+			if(t == T) {
+				t = typ(etype);
+				t->sym = s1;
+
+				if(etype != TANY && etype != TSTRING)
+					dowidth(t);
+				types[etype] = t;
+			}
+			s1->lexical = LNAME;
+			s1->def = typenod(t);
+			continue;
+		}
+
+		etype = syms[i].op;
+		if(etype != OXXX) {
+			s1 = pkglookup(syms[i].name, builtinpkg);
+			s1->lexical = LNAME;
+			s1->def = nod(ONAME, N, N);
+			s1->def->sym = s1;
+			s1->def->etype = etype;
+			s1->def->builtin = 1;
+		}
+	}
+
+	// logically, the type of a string literal.
+	// types[TSTRING] is the named type string
+	// (the type of x in var x string or var x = "hello").
+	// this is the ideal form
+	// (the type of x in const x = "hello").
+	idealstring = typ(TSTRING);
+	idealbool = typ(TBOOL);
+
+	s = pkglookup("true", builtinpkg);
+	s->def = nodbool(1);
+	s->def->sym = lookup("true");
+	s->def->type = idealbool;
+
+	s = pkglookup("false", builtinpkg);
+	s->def = nodbool(0);
+	s->def->sym = lookup("false");
+	s->def->type = idealbool;
+
+	s = lookup("_");
+	s->block = -100;
+	s->def = nod(ONAME, N, N);
+	s->def->sym = s;
+	types[TBLANK] = typ(TBLANK);
+	s->def->type = types[TBLANK];
+	nblank = s->def;
+
+	s = pkglookup("_", builtinpkg);
+	s->block = -100;
+	s->def = nod(ONAME, N, N);
+	s->def->sym = s;
+	types[TBLANK] = typ(TBLANK);
+	s->def->type = types[TBLANK];
+
+	types[TNIL] = typ(TNIL);
+	s = pkglookup("nil", builtinpkg);
+	v.ctype = CTNIL;
+	s->def = nodlit(v);
+	s->def->sym = s;
+}
+
+static void
+lexinit1(void)
+{
+	Sym *s, *s1;
+	Type *t, *f, *rcvr, *in, *out;
+
+	// t = interface { Error() string }
+	rcvr = typ(TSTRUCT);
+	rcvr->type = typ(TFIELD);
+	rcvr->type->type = ptrto(typ(TSTRUCT));
+	rcvr->funarg = 1;
+	in = typ(TSTRUCT);
+	in->funarg = 1;
+	out = typ(TSTRUCT);
+	out->type = typ(TFIELD);
+	out->type->type = types[TSTRING];
+	out->funarg = 1;
+	f = typ(TFUNC);
+	*getthis(f) = rcvr;
+	*getoutarg(f) = out;
+	*getinarg(f) = in;
+	f->thistuple = 1;
+	f->intuple = 0;
+	f->outnamed = 0;
+	f->outtuple = 1;
+	t = typ(TINTER);
+	t->type = typ(TFIELD);
+	t->type->sym = lookup("Error");
+	t->type->type = f;
+
+	// error type
+	s = lookup("error");
+	s->lexical = LNAME;
+	s1 = pkglookup("error", builtinpkg);
+	errortype = t;
+	errortype->sym = s1;
+	s1->lexical = LNAME;
+	s1->def = typenod(errortype);
+
+	// byte alias
+	s = lookup("byte");
+	s->lexical = LNAME;
+	s1 = pkglookup("byte", builtinpkg);
+	bytetype = typ(TUINT8);
+	bytetype->sym = s1;
+	s1->lexical = LNAME;
+	s1->def = typenod(bytetype);
+
+	// rune alias
+	s = lookup("rune");
+	s->lexical = LNAME;
+	s1 = pkglookup("rune", builtinpkg);
+	runetype = typ(TINT32);
+	runetype->sym = s1;
+	s1->lexical = LNAME;
+	s1->def = typenod(runetype);
+}
+
+static void
+lexfini(void)
+{
+	Sym *s;
+	int lex, etype, i;
+	Val v;
+
+	for(i=0; i<nelem(syms); i++) {
+		lex = syms[i].lexical;
+		if(lex != LNAME)
+			continue;
+		s = lookup(syms[i].name);
+		s->lexical = lex;
+
+		etype = syms[i].etype;
+		if(etype != Txxx && (etype != TANY || debug['A']) && s->def == N) {
+			s->def = typenod(types[etype]);
+			s->origpkg = builtinpkg;
+		}
+
+		etype = syms[i].op;
+		if(etype != OXXX && s->def == N) {
+			s->def = nod(ONAME, N, N);
+			s->def->sym = s;
+			s->def->etype = etype;
+			s->def->builtin = 1;
+			s->origpkg = builtinpkg;
+		}
+	}
+
+	// backend-specific builtin types (e.g. int).
+	for(i=0; typedefs[i].name; i++) {
+		s = lookup(typedefs[i].name);
+		if(s->def == N) {
+			s->def = typenod(types[typedefs[i].etype]);
+			s->origpkg = builtinpkg;
+		}
+	}
+
+	// there's only so much table-driven we can handle.
+	// these are special cases.
+	s = lookup("byte");
+	if(s->def == N) {
+		s->def = typenod(bytetype);
+		s->origpkg = builtinpkg;
+	}
+
+	s = lookup("error");
+	if(s->def == N) {
+		s->def = typenod(errortype);
+		s->origpkg = builtinpkg;
+	}
+
+	s = lookup("rune");
+	if(s->def == N) {
+		s->def = typenod(runetype);
+		s->origpkg = builtinpkg;
+	}
+
+	s = lookup("nil");
+	if(s->def == N) {
+		v.ctype = CTNIL;
+		s->def = nodlit(v);
+		s->def->sym = s;
+		s->origpkg = builtinpkg;
+	}
+
+	s = lookup("iota");
+	if(s->def == N) {
+		s->def = nod(OIOTA, N, N);
+		s->def->sym = s;
+		s->origpkg = builtinpkg;
+	}
+
+	s = lookup("true");
+	if(s->def == N) {
+		s->def = nodbool(1);
+		s->def->sym = s;
+		s->origpkg = builtinpkg;
+	}
+
+	s = lookup("false");
+	if(s->def == N) {
+		s->def = nodbool(0);
+		s->def->sym = s;
+		s->origpkg = builtinpkg;
+	}
+
+	nodfp = nod(ONAME, N, N);
+	nodfp->type = types[TINT32];
+	nodfp->xoffset = 0;
+	nodfp->class = PPARAM;
+	nodfp->sym = lookup(".fp");
+}
+
+struct
+{
+	int	lex;
+	char*	name;
+} lexn[] =
+{
+	{LANDAND,	"ANDAND"},
+	{LANDNOT,	"ANDNOT"},
+	{LASOP,		"ASOP"},
+	{LBREAK,		"BREAK"},
+	{LCASE,		"CASE"},
+	{LCHAN,		"CHAN"},
+	{LCOLAS,		"COLAS"},
+	{LCOMM,		"<-"},
+	{LCONST,		"CONST"},
+	{LCONTINUE,	"CONTINUE"},
+	{LDDD,		"..."},
+	{LDEC,		"DEC"},
+	{LDEFAULT,	"DEFAULT"},
+	{LDEFER,		"DEFER"},
+	{LELSE,		"ELSE"},
+	{LEQ,		"EQ"},
+	{LFALL,		"FALL"},
+	{LFOR,		"FOR"},
+	{LFUNC,		"FUNC"},
+	{LGE,		"GE"},
+	{LGO,		"GO"},
+	{LGOTO,		"GOTO"},
+	{LGT,		"GT"},
+	{LIF,		"IF"},
+	{LIMPORT,	"IMPORT"},
+	{LINC,		"INC"},
+	{LINTERFACE,	"INTERFACE"},
+	{LLE,		"LE"},
+	{LLITERAL,	"LITERAL"},
+	{LLSH,		"LSH"},
+	{LLT,		"LT"},
+	{LMAP,		"MAP"},
+	{LNAME,		"NAME"},
+	{LNE,		"NE"},
+	{LOROR,		"OROR"},
+	{LPACKAGE,	"PACKAGE"},
+	{LRANGE,		"RANGE"},
+	{LRETURN,	"RETURN"},
+	{LRSH,		"RSH"},
+	{LSELECT,	"SELECT"},
+	{LSTRUCT,	"STRUCT"},
+	{LSWITCH,	"SWITCH"},
+	{LTYPE,		"TYPE"},
+	{LVAR,		"VAR"},
+};
+
+char*
+lexname(int lex)
+{
+	int i;
+	static char buf[100];
+
+	for(i=0; i<nelem(lexn); i++)
+		if(lexn[i].lex == lex)
+			return lexn[i].name;
+	snprint(buf, sizeof(buf), "LEX-%d", lex);
+	return buf;
+}
+
+struct
+{
+	char *have;
+	char *want;
+} yytfix[] =
+{
+	{"$end",	"EOF"},
+	{"LLITERAL",	"literal"},
+	{"LASOP",	"op="},
+	{"LBREAK",	"break"},
+	{"LCASE",	"case"},
+	{"LCHAN",	"chan"},
+	{"LCOLAS",	":="},
+	{"LCONST",	"const"},
+	{"LCONTINUE",	"continue"},
+	{"LDDD",	"..."},
+	{"LDEFAULT",	"default"},
+	{"LDEFER",	"defer"},
+	{"LELSE",	"else"},
+	{"LFALL",	"fallthrough"},
+	{"LFOR",	"for"},
+	{"LFUNC",	"func"},
+	{"LGO",	"go"},
+	{"LGOTO",	"goto"},
+	{"LIF",	"if"},
+	{"LIMPORT",	"import"},
+	{"LINTERFACE",	"interface"},
+	{"LMAP",	"map"},
+	{"LNAME",	"name"},
+	{"LPACKAGE",	"package"},
+	{"LRANGE",	"range"},
+	{"LRETURN",	"return"},
+	{"LSELECT",	"select"},
+	{"LSTRUCT",	"struct"},
+	{"LSWITCH",	"switch"},
+	{"LTYPE",	"type"},
+	{"LVAR",	"var"},
+	{"LANDAND",	"&&"},
+	{"LANDNOT",	"&^"},
+	{"LBODY",	"{"},
+	{"LCOMM",	"<-"},
+	{"LDEC",	"--"},
+	{"LINC",	"++"},
+	{"LEQ",	"=="},
+	{"LGE",	">="},
+	{"LGT",	">"},
+	{"LLE",	"<="},
+	{"LLT",	"<"},
+	{"LLSH",	"<<"},
+	{"LRSH",	">>"},
+	{"LOROR",	"||"},
+	{"LNE",	"!="},
+	
+	// spell out to avoid confusion with punctuation in error messages
+	{"';'",	"semicolon or newline"},
+	{"','",	"comma"},
+};
+
+static void
+yytinit(void)
+{
+	int i, j;
+	extern char *yytname[];
+	char *s, *t;
+
+	for(i=0; yytname[i] != nil; i++) {
+		s = yytname[i];
+		
+		if(strcmp(s, "LLITERAL") == 0) {
+			strcpy(litbuf, "literal");
+			yytname[i] = litbuf;
+			goto loop;
+		}
+		
+		// apply yytfix if possible
+		for(j=0; j<nelem(yytfix); j++) {
+			if(strcmp(s, yytfix[j].have) == 0) {
+				yytname[i] = yytfix[j].want;
+				goto loop;
+			}
+		}
+
+		// turn 'x' into x.
+		if(s[0] == '\'') {
+			t = strdup(s+1);
+			t[strlen(t)-1] = '\0';
+			yytname[i] = t;
+		}
+	loop:;
+	}		
+}
+
+static void
+pkgnotused(int lineno, Strlit *path, char *name)
+{
+	char *elem;
+	
+	// If the package was imported with a name other than the final
+	// import path element, show it explicitly in the error message.
+	// Note that this handles both renamed imports and imports of
+	// packages containing unconventional package declarations.
+	// Note that this uses / always, even on Windows, because Go import
+	// paths always use forward slashes.
+	elem = strrchr(path->s, '/');
+	if(elem != nil)
+		elem++;
+	else
+		elem = path->s;
+	if(name == nil || strcmp(elem, name) == 0)
+		yyerrorl(lineno, "imported and not used: \"%Z\"", path);
+	else
+		yyerrorl(lineno, "imported and not used: \"%Z\" as %s", path, name);
+}
+
+void
+mkpackage(char* pkgname)
+{
+	Sym *s;
+	int32 h;
+	char *p, *q;
+
+	if(localpkg->name == nil) {
+		if(strcmp(pkgname, "_") == 0)
+			yyerror("invalid package name _");
+		localpkg->name = pkgname;
+	} else {
+		if(strcmp(pkgname, localpkg->name) != 0)
+			yyerror("package %s; expected %s", pkgname, localpkg->name);
+		for(h=0; h<NHASH; h++) {
+			for(s = hash[h]; s != S; s = s->link) {
+				if(s->def == N || s->pkg != localpkg)
+					continue;
+				if(s->def->op == OPACK) {
+					// throw away top-level package name leftover
+					// from previous file.
+					// leave s->block set to cause redeclaration
+					// errors if a conflicting top-level name is
+					// introduced by a different file.
+					if(!s->def->used && !nsyntaxerrors)
+						pkgnotused(s->def->lineno, s->def->pkg->path, s->name);
+					s->def = N;
+					continue;
+				}
+				if(s->def->sym != s) {
+					// throw away top-level name left over
+					// from previous import . "x"
+					if(s->def->pack != N && !s->def->pack->used && !nsyntaxerrors) {
+						pkgnotused(s->def->pack->lineno, s->def->pack->pkg->path, nil);
+						s->def->pack->used = 1;
+					}
+					s->def = N;
+					continue;
+				}
+			}
+		}
+	}
+
+	if(outfile == nil) {
+		p = strrchr(infile, '/');
+		if(ctxt->windows) {
+			q = strrchr(infile, '\\');
+			if(q > p)
+				p = q;
+		}
+		if(p == nil)
+			p = infile;
+		else
+			p = p+1;
+		snprint(namebuf, sizeof(namebuf), "%s", p);
+		p = strrchr(namebuf, '.');
+		if(p != nil)
+			*p = 0;
+		outfile = smprint("%s.%c", namebuf, thechar);
+	}
+}
diff --git a/src/cmd/gc/md5.c b/src/cmd/gc/md5.c
new file mode 100644
index 0000000..46cb6b7
--- /dev/null
+++ b/src/cmd/gc/md5.c
@@ -0,0 +1,302 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// 64-bit MD5 (does full MD5 but returns 64 bits only).
+// Translation of ../../crypto/md5/md5*.go.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+#include "md5.h"
+
+static int md5block(MD5 *dig, uchar *p, int nn);
+
+enum {
+	_Chunk = 64
+};
+
+#define _Init0 0x67452301
+#define _Init1 0xEFCDAB89
+#define _Init2 0x98BADCFE
+#define _Init3 0x10325476
+/*c2go
+enum {
+	_Init0 = 0x67452301,
+	_Init1 = 0xEFCDAB89,
+	_Init2 = 0x98BADCFE,
+	_Init3 = 0x10325476
+};
+*/
+	
+void
+md5reset(MD5 *d)
+{
+	d->s[0] = _Init0;
+	d->s[1] = _Init1;
+	d->s[2] = _Init2;
+	d->s[3] = _Init3;
+	d->nx = 0;
+	d->len = 0;
+}
+
+void
+md5write(MD5 *d, uchar *p, int nn)
+{
+	int i, n;
+
+	d->len += nn;
+	if(d->nx > 0) {
+		n = nn;
+		if(n > _Chunk - d->nx)
+			n = _Chunk - d->nx;
+		for(i=0; i<n; i++)
+			d->x[d->nx+i] = p[i];
+		d->nx += n;
+		if(d->nx == _Chunk) {
+			md5block(d, d->x, _Chunk);
+			d->nx = 0;
+		}
+		p += n;
+		nn -= n;
+	}
+	n = md5block(d, p, nn);
+	p += n;
+	nn -= n;
+	if(nn > 0) {
+		for(i=0; i<nn; i++)
+			d->x[i] = p[i];
+		d->nx = nn;
+	}
+}
+
+uint64
+md5sum(MD5 *d, uint64 *hi)
+{
+	uchar tmp[64];
+	int i;
+	uint64 len;
+
+	// Padding.  Add a 1 bit and 0 bits until 56 bytes mod 64.
+	len = d->len;
+	memset(tmp, 0, sizeof tmp);
+	tmp[0] = 0x80;
+	if(len%64 < 56)
+		md5write(d, tmp, 56-len%64);
+	else
+		md5write(d, tmp, 64+56-len%64);
+
+	// Length in bits.
+	len <<= 3;
+	for(i=0; i<8; i++)
+		tmp[i] = len>>(8*i);
+	md5write(d, tmp, 8);
+
+	if(d->nx != 0)
+		fatal("md5sum");
+
+	if(hi != nil)
+		*hi = d->s[2] | ((uint64)d->s[3]<<32);
+	return d->s[0] | ((uint64)d->s[1]<<32);
+}
+
+
+// MD5 block step.
+// In its own file so that a faster assembly or C version
+// can be substituted easily.
+
+// table[i] = int((1<<32) * abs(sin(i+1 radians))).
+static uint32 table[64] = {
+	// round 1
+	0xd76aa478,
+	0xe8c7b756,
+	0x242070db,
+	0xc1bdceee,
+	0xf57c0faf,
+	0x4787c62a,
+	0xa8304613,
+	0xfd469501,
+	0x698098d8,
+	0x8b44f7af,
+	0xffff5bb1,
+	0x895cd7be,
+	0x6b901122,
+	0xfd987193,
+	0xa679438e,
+	0x49b40821,
+
+	// round 2
+	0xf61e2562,
+	0xc040b340,
+	0x265e5a51,
+	0xe9b6c7aa,
+	0xd62f105d,
+	0x2441453,
+	0xd8a1e681,
+	0xe7d3fbc8,
+	0x21e1cde6,
+	0xc33707d6,
+	0xf4d50d87,
+	0x455a14ed,
+	0xa9e3e905,
+	0xfcefa3f8,
+	0x676f02d9,
+	0x8d2a4c8a,
+
+	// round3
+	0xfffa3942,
+	0x8771f681,
+	0x6d9d6122,
+	0xfde5380c,
+	0xa4beea44,
+	0x4bdecfa9,
+	0xf6bb4b60,
+	0xbebfbc70,
+	0x289b7ec6,
+	0xeaa127fa,
+	0xd4ef3085,
+	0x4881d05,
+	0xd9d4d039,
+	0xe6db99e5,
+	0x1fa27cf8,
+	0xc4ac5665,
+
+	// round 4
+	0xf4292244,
+	0x432aff97,
+	0xab9423a7,
+	0xfc93a039,
+	0x655b59c3,
+	0x8f0ccc92,
+	0xffeff47d,
+	0x85845dd1,
+	0x6fa87e4f,
+	0xfe2ce6e0,
+	0xa3014314,
+	0x4e0811a1,
+	0xf7537e82,
+	0xbd3af235,
+	0x2ad7d2bb,
+	0xeb86d391,
+};
+
+static uint32 shift1[] = { 7, 12, 17, 22 };
+static uint32 shift2[] = { 5, 9, 14, 20 };
+static uint32 shift3[] = { 4, 11, 16, 23 };
+static uint32 shift4[] = { 6, 10, 15, 21 };
+
+static int
+md5block(MD5 *dig, uchar *p, int nn)
+{
+	uint32 a, b, c, d, aa, bb, cc, dd;
+	int i, j, n;
+	uint32 X[16];
+
+	a = dig->s[0];
+	b = dig->s[1];
+	c = dig->s[2];
+	d = dig->s[3];
+	n = 0;
+
+	while(nn >= _Chunk) {
+		aa = a;
+		bb = b;
+		cc = c;
+		dd = d;
+
+		for(i=0; i<16; i++) {
+			j = i*4;
+			X[i] = p[j] | (p[j+1]<<8) | (p[j+2]<<16) | ((uint32)p[j+3]<<24);
+		}
+
+		// Round 1.
+		for(i=0; i<16; i++) {
+			uint32 x, t, s, f;
+			x = i;
+			t = i;
+			s = shift1[i%4];
+			f = ((c ^ d) & b) ^ d;
+			a += f + X[x] + table[t];
+			a = a<<s | a>>(32-s);
+			a += b;
+
+			t = d;
+			d = c;
+			c = b;
+			b = a;
+			a = t;
+		}
+
+		// Round 2.
+		for(i=0; i<16; i++) {
+			uint32 x, t, s, g;
+
+			x = (1+5*i)%16;
+			t = 16+i;
+			s = shift2[i%4];
+			g = ((b ^ c) & d) ^ c;
+			a += g + X[x] + table[t];
+			a = a<<s | a>>(32-s);
+			a += b;
+
+			t = d;
+			d = c;
+			c = b;
+			b = a;
+			a = t;
+		}
+
+		// Round 3.
+		for(i=0; i<16; i++) {
+			uint32 x, t, s, h;
+
+			x = (5+3*i)%16;
+			t = 32+i;
+			s = shift3[i%4];
+			h = b ^ c ^ d;
+			a += h + X[x] + table[t];
+			a = a<<s | a>>(32-s);
+			a += b;
+
+			t = d;
+			d = c;
+			c = b;
+			b = a;
+			a = t;
+		}
+
+		// Round 4.
+		for(i=0; i<16; i++) {
+			uint32 x, s, t, ii;
+
+			x = (7*i)%16;
+			s = shift4[i%4];
+			t = 48+i;
+			ii = c ^ (b | ~d);
+			a += ii + X[x] + table[t];
+			a = a<<s | a>>(32-s);
+			a += b;
+
+			t = d;
+			d = c;
+			c = b;
+			b = a;
+			a = t;
+		}
+
+		a += aa;
+		b += bb;
+		c += cc;
+		d += dd;
+
+		p += _Chunk;
+		n += _Chunk;
+		nn -= _Chunk;
+	}
+
+	dig->s[0] = a;
+	dig->s[1] = b;
+	dig->s[2] = c;
+	dig->s[3] = d;
+	return n;
+}
diff --git a/src/cmd/gc/md5.h b/src/cmd/gc/md5.h
new file mode 100644
index 0000000..5a60106
--- /dev/null
+++ b/src/cmd/gc/md5.h
@@ -0,0 +1,16 @@
+// Copyright 2009 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+typedef struct MD5 MD5;
+struct MD5
+{
+	uint32 s[4];
+	uchar x[64];
+	int nx;
+	uint64 len;
+};
+
+void md5reset(MD5*);
+void md5write(MD5*, uchar*, int);
+uint64 md5sum(MD5*, uint64*);
diff --git a/src/cmd/gc/mkbuiltin b/src/cmd/gc/mkbuiltin
new file mode 100755
index 0000000..1dab1c9
--- /dev/null
+++ b/src/cmd/gc/mkbuiltin
@@ -0,0 +1,32 @@
+#!/bin/sh
+# Copyright 2009 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# Generate builtin.c from $* (runtime.go and unsafe.go).
+# Run this after changing runtime.go and unsafe.go
+# or after changing the export metadata format in the compiler.
+# Either way, you need to have a working compiler binary first.
+
+set -e
+
+eval $(go tool dist env)
+if [ -z "$GOCHAR" ]; then
+	echo 'missing $GOCHAR - go tool dist failed?' 1>&2
+	exit 1
+fi
+
+GC=${GOCHAR}g
+gcc -o mkbuiltin1 mkbuiltin1.c
+rm -f _builtin.c
+echo "// AUTO-GENERATED by mkbuiltin; DO NOT EDIT" >>_builtin.c
+for i in runtime unsafe
+do
+	go tool $GC -A $i.go
+	O=$GOCHAR ./mkbuiltin1 $i >>_builtin.c
+done
+
+# If _builtin.c has changed vs builtin.c,
+# check in the new change.
+cmp -s _builtin.c builtin.c || cp _builtin.c builtin.c
+rm _builtin.c mkbuiltin1 unsafe.$GOCHAR runtime.$GOCHAR
diff --git a/src/cmd/gc/mkbuiltin1.c b/src/cmd/gc/mkbuiltin1.c
new file mode 100644
index 0000000..69027fd
--- /dev/null
+++ b/src/cmd/gc/mkbuiltin1.c
@@ -0,0 +1,102 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build ignore
+
+// Compile .go file, import data from .6 file, and generate C string version.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+void esc(char*);
+void fatal(char*, ...);
+
+int
+main(int argc, char **argv)
+{
+	char *name;
+	FILE *fin;
+	char buf[1024], initfunc[1024], *p, *q;
+
+	if(argc != 2) {
+		fprintf(stderr, "usage: mkbuiltin1 sys\n");
+		fatal("in file $1.6 s/PACKAGE/$1/");
+	}
+
+	name = argv[1];
+	snprintf(initfunc, sizeof(initfunc), "init_%s_function", name);
+
+	snprintf(buf, sizeof(buf), "%s.%s", name, getenv("O"));
+	if((fin = fopen(buf, "r")) == NULL) {
+		fatal("open %s: %s", buf, strerror(errno));
+	}
+
+	// look for $$ that introduces imports
+	while(fgets(buf, sizeof buf, fin) != NULL)
+		if(strstr(buf, "$$"))
+			goto begin;
+	fatal("did not find beginning of imports");
+
+begin:
+	printf("char *%simport =\n", name);
+
+	// process imports, stopping at $$ that closes them
+	while(fgets(buf, sizeof buf, fin) != NULL) {
+		buf[strlen(buf)-1] = 0;	// chop \n
+		if(strstr(buf, "$$"))
+			goto end;
+
+		// chop leading white space
+		for(p=buf; *p==' ' || *p == '\t'; p++)
+			;
+
+		// cut out decl of init_$1_function - it doesn't exist
+		if(strstr(buf, initfunc))
+			continue;
+
+		// sys.go claims to be in package PACKAGE to avoid
+		// conflicts during "6g sys.go".  rename PACKAGE to $2.
+		printf("\t\"");
+		while((q = strstr(p, "PACKAGE")) != NULL) {
+			*q = 0;
+			esc(p);	// up to the substitution
+			printf("%s", name);	// the sub name
+			p = q+7;		// continue with rest
+		}
+
+		esc(p);
+		printf("\\n\"\n");
+	}
+	fatal("did not find end of imports");
+
+end:
+	printf("\t\"$$\\n\";\n");
+	return 0;
+}
+
+void
+esc(char *p)
+{
+	for(; *p; p++) {
+		if(*p == '\\' || *p == '\"')
+			printf("\\");
+		putchar(*p);
+	}
+}
+
+void
+fatal(char *msg, ...)
+{
+	va_list arg;
+	
+	va_start(arg, msg);
+	fprintf(stderr, "fatal: ");
+	vfprintf(stderr, msg, arg);
+	fprintf(stderr, "\n");
+	exit(2);
+}
diff --git a/src/cmd/gc/mkopnames b/src/cmd/gc/mkopnames
new file mode 100755
index 0000000..d3f27e8
--- /dev/null
+++ b/src/cmd/gc/mkopnames
@@ -0,0 +1,24 @@
+#!/bin/sh
+# Copyright 2009 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# Disable colored grep if user has it set to --color=always.
+# (Arguably user error.)
+export GREP_OPTIONS=""
+
+echo '// auto generated by mkopnames'
+echo 'static char*'
+echo 'opnames[] = '
+echo '{'
+sed -n '/OXXX/,/OEND/p' go.h |
+	cpp |
+	sed 's!//.*!!; /^#/d'  |
+	tr ' ' '\012' |
+	tr -d ' \011,' |
+	grep . |
+	sort |
+	grep -v '^OEND$' |
+	sed 's/O//; s/.*/	[O&] =	"&",/'
+echo '};'
+
diff --git a/src/cmd/gc/mparith1.c b/src/cmd/gc/mparith1.c
new file mode 100644
index 0000000..d33a81e
--- /dev/null
+++ b/src/cmd/gc/mparith1.c
@@ -0,0 +1,641 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include	<u.h>
+#include	<libc.h>
+#include	"go.h"
+
+/// uses arithmetic
+
+int
+mpcmpfixflt(Mpint *a, Mpflt *b)
+{
+	char buf[500];
+	Mpflt c;
+
+	snprint(buf, sizeof(buf), "%B", a);
+	mpatoflt(&c, buf);
+	return mpcmpfltflt(&c, b);
+}
+
+int
+mpcmpfltfix(Mpflt *a, Mpint *b)
+{
+	char buf[500];
+	Mpflt c;
+
+	snprint(buf, sizeof(buf), "%B", b);
+	mpatoflt(&c, buf);
+	return mpcmpfltflt(a, &c);
+}
+
+int
+mpcmpfixfix(Mpint *a, Mpint *b)
+{
+	Mpint c;
+
+	mpmovefixfix(&c, a);
+	mpsubfixfix(&c, b);
+	return mptestfix(&c);
+}
+
+int
+mpcmpfixc(Mpint *b, vlong c)
+{
+	Mpint c1;
+
+	mpmovecfix(&c1, c);
+	return mpcmpfixfix(b, &c1);
+}
+
+int
+mpcmpfltflt(Mpflt *a, Mpflt *b)
+{
+	Mpflt c;
+
+	mpmovefltflt(&c, a);
+	mpsubfltflt(&c, b);
+	return mptestflt(&c);
+}
+
+int
+mpcmpfltc(Mpflt *b, double c)
+{
+	Mpflt a;
+
+	mpmovecflt(&a, c);
+	return mpcmpfltflt(b, &a);
+}
+
+void
+mpsubfixfix(Mpint *a, Mpint *b)
+{
+	mpnegfix(a);
+	mpaddfixfix(a, b, 0);
+	mpnegfix(a);
+}
+
+void
+mpsubfltflt(Mpflt *a, Mpflt *b)
+{
+	mpnegflt(a);
+	mpaddfltflt(a, b);
+	mpnegflt(a);
+}
+
+void
+mpaddcfix(Mpint *a, vlong c)
+{
+	Mpint b;
+
+	mpmovecfix(&b, c);
+	mpaddfixfix(a, &b, 0);
+}
+
+void
+mpaddcflt(Mpflt *a, double c)
+{
+	Mpflt b;
+
+	mpmovecflt(&b, c);
+	mpaddfltflt(a, &b);
+}
+
+void
+mpmulcfix(Mpint *a, vlong c)
+{
+	Mpint b;
+
+	mpmovecfix(&b, c);
+	mpmulfixfix(a, &b);
+}
+
+void
+mpmulcflt(Mpflt *a, double c)
+{
+	Mpflt b;
+
+	mpmovecflt(&b, c);
+	mpmulfltflt(a, &b);
+}
+
+void
+mpdivfixfix(Mpint *a, Mpint *b)
+{
+	Mpint q, r;
+
+	mpdivmodfixfix(&q, &r, a, b);
+	mpmovefixfix(a, &q);
+}
+
+void
+mpmodfixfix(Mpint *a, Mpint *b)
+{
+	Mpint q, r;
+
+	mpdivmodfixfix(&q, &r, a, b);
+	mpmovefixfix(a, &r);
+}
+
+void
+mpcomfix(Mpint *a)
+{
+	Mpint b;
+
+	mpmovecfix(&b, 1);
+	mpnegfix(a);
+	mpsubfixfix(a, &b);
+}
+
+void
+mpmovefixflt(Mpflt *a, Mpint *b)
+{
+	a->val = *b;
+	a->exp = 0;
+	mpnorm(a);
+}
+
+// convert (truncate) b to a.
+// return -1 (but still convert) if b was non-integer.
+static int
+mpexactfltfix(Mpint *a, Mpflt *b)
+{
+	Mpflt f;
+
+	*a = b->val;
+	mpshiftfix(a, b->exp);
+	if(b->exp < 0) {
+		f.val = *a;
+		f.exp = 0;
+		mpnorm(&f);
+		if(mpcmpfltflt(b, &f) != 0)
+			return -1;
+	}
+	return 0;
+}
+
+int
+mpmovefltfix(Mpint *a, Mpflt *b)
+{
+	Mpflt f;
+	int i;
+
+	if(mpexactfltfix(a, b) == 0)
+		return 0;
+
+	// try rounding down a little
+	f = *b;
+	f.val.a[0] = 0;
+	if(mpexactfltfix(a, &f) == 0)
+		return 0;
+
+	// try rounding up a little
+	for(i=1; i<Mpprec; i++) {
+		f.val.a[i]++;
+		if(f.val.a[i] != Mpbase)
+			break;
+		f.val.a[i] = 0;
+	}
+	mpnorm(&f);
+	if(mpexactfltfix(a, &f) == 0)
+		return 0;
+
+	return -1;
+}
+
+void
+mpmovefixfix(Mpint *a, Mpint *b)
+{
+	*a = *b;
+}
+
+void
+mpmovefltflt(Mpflt *a, Mpflt *b)
+{
+	*a = *b;
+}
+
+static	double	tab[] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7 };
+static void
+mppow10flt(Mpflt *a, int p)
+{
+	if(p < 0)
+		abort();
+	if(p < nelem(tab)) {
+		mpmovecflt(a, tab[p]);
+		return;
+	}
+	mppow10flt(a, p>>1);
+	mpmulfltflt(a, a);
+	if(p & 1)
+		mpmulcflt(a, 10);
+}
+
+static void
+mphextofix(Mpint *a, char *s, int n)
+{
+	char *hexdigitp, *end, c;
+	long d;
+	int bit;
+
+	while(*s == '0') {
+		s++;
+		n--;
+	}
+
+	// overflow
+	if(4*n > Mpscale*Mpprec) {
+		a->ovf = 1;
+		return;
+	}
+
+	end = s+n-1;
+	for(hexdigitp=end; hexdigitp>=s; hexdigitp--) {
+		c = *hexdigitp;
+		if(c >= '0' && c <= '9')
+			d = c-'0';
+		else if(c >= 'A' && c <= 'F')
+			d = c-'A'+10;
+		else
+			d = c-'a'+10;
+
+		bit = 4*(end - hexdigitp);
+		while(d > 0) {
+			if(d & 1)
+				a->a[bit/Mpscale] |= (long)1 << (bit%Mpscale);
+			bit++;
+			d = d >> 1;
+		}
+	}
+}
+
+//
+// floating point input
+// required syntax is [+-]d*[.]d*[e[+-]d*] or [+-]0xH*[e[+-]d*]
+//
+void
+mpatoflt(Mpflt *a, char *as)
+{
+	Mpflt b;
+	int dp, c, f, ef, ex, eb, base;
+	char *s, *start;
+
+	while(*as == ' ' || *as == '\t')
+		as++;
+
+	/* determine base */
+	s = as;
+	base = -1;
+	while(base == -1) {
+		switch(*s++) {
+		case '-':
+		case '+':
+			break;
+
+		case '0':
+			if(*s == 'x')
+				base = 16;
+			else
+				base = 10;
+			break;
+
+		default:
+			base = 10;
+		}
+	}
+
+	s = as;
+	dp = 0;		/* digits after decimal point */
+	f = 0;		/* sign */
+	ex = 0;		/* exponent */
+	eb = 0;		/* binary point */
+
+	mpmovecflt(a, 0.0);
+	if(base == 16) {
+		start = nil;
+		for(;;) {
+			c = *s;
+			if(c == '-') {
+				f = 1;
+				s++;
+			}
+			else if(c == '+') {
+				s++;
+			}
+			else if(c == '0' && s[1] == 'x') {
+				s += 2;
+				start = s;
+			}
+			else if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
+				s++;
+			}
+			else {
+				break;
+			}
+		}
+		if(start == nil)
+			goto bad;
+
+		mphextofix(&a->val, start, s-start);
+		if(a->val.ovf)
+			goto bad;
+		a->exp = 0;
+		mpnorm(a);
+	}
+	for(;;) {
+		switch(c = *s++) {
+		default:
+			goto bad;
+
+		case '-':
+			f = 1;
+
+		case ' ':
+		case '\t':
+		case '+':
+			continue;
+
+		case '.':
+			if(base == 16)
+				goto bad;
+			dp = 1;
+			continue;
+
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+		case '0':
+			mpmulcflt(a, 10);
+			mpaddcflt(a, c-'0');
+			if(dp)
+				dp++;
+			continue;
+
+		case 'P':
+		case 'p':
+			eb = 1;
+
+		case 'E':
+		case 'e':
+			ex = 0;
+			ef = 0;
+			for(;;) {
+				c = *s++;
+				if(c == '+' || c == ' ' || c == '\t')
+					continue;
+				if(c == '-') {
+					ef = 1;
+					continue;
+				}
+				if(c >= '0' && c <= '9') {
+					ex = ex*10 + (c-'0');
+					if(ex > 1e8) {
+						yyerror("constant exponent out of range: %s", as);
+						errorexit();
+					}
+					continue;
+				}
+				break;
+			}
+			if(ef)
+				ex = -ex;
+
+		case 0:
+			break;
+		}
+		break;
+	}
+
+	if(eb) {
+		if(dp)
+			goto bad;
+		mpsetexp(a, a->exp+ex);
+		goto out;
+	}
+
+	if(dp)
+		dp--;
+	if(mpcmpfltc(a, 0.0) != 0) {
+		if(ex >= dp) {
+			mppow10flt(&b, ex-dp);
+			mpmulfltflt(a, &b);
+		} else {
+			// 4 approximates least_upper_bound(log2(10)).
+			if(dp-ex >= (1<<(8*sizeof(dp)-3)) || (short)(4*(dp-ex)) != 4*(dp-ex)) {
+				mpmovecflt(a, 0.0);
+			}
+			else {
+				mppow10flt(&b, dp-ex);
+				mpdivfltflt(a, &b);
+			}
+		}
+	}
+
+out:
+	if(f)
+		mpnegflt(a);
+	return;
+
+bad:
+	yyerror("constant too large: %s", as);
+	mpmovecflt(a, 0.0);
+}
+
+//
+// fixed point input
+// required syntax is [+-][0[x]]d*
+//
+void
+mpatofix(Mpint *a, char *as)
+{
+	int c, f;
+	char *s, *s0;
+
+	s = as;
+	f = 0;
+	mpmovecfix(a, 0);
+
+	c = *s++;
+	switch(c) {
+	case '-':
+		f = 1;
+
+	case '+':
+		c = *s++;
+		if(c != '0')
+			break;
+
+	case '0':
+		goto oct;
+	}
+
+	while(c) {
+		if(c >= '0' && c <= '9') {
+			mpmulcfix(a, 10);
+			mpaddcfix(a, c-'0');
+			c = *s++;
+			continue;
+		}
+		goto bad;
+	}
+	goto out;
+
+oct:
+	c = *s++;
+	if(c == 'x' || c == 'X')
+		goto hex;
+	while(c) {
+		if(c >= '0' && c <= '7') {
+			mpmulcfix(a, 8);
+			mpaddcfix(a, c-'0');
+			c = *s++;
+			continue;
+		}
+		goto bad;
+	}
+	goto out;
+
+hex:
+	s0 = s;
+	c = *s;
+	while(c) {
+		if((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) {
+			s++;
+			c = *s;
+			continue;
+		}
+		goto bad;
+	}
+	mphextofix(a, s0, s-s0);
+	if(a->ovf)
+		goto bad;
+
+out:
+	if(f)
+		mpnegfix(a);
+	return;
+
+bad:
+	yyerror("constant too large: %s", as);
+	mpmovecfix(a, 0);
+}
+
+int
+Bconv(Fmt *fp)
+{
+	char buf[500], *p;
+	Mpint *xval, q, r, ten, sixteen;
+	int f, digit;
+
+	xval = va_arg(fp->args, Mpint*);
+	mpmovefixfix(&q, xval);
+	f = 0;
+	if(mptestfix(&q) < 0) {
+		f = 1;
+		mpnegfix(&q);
+	}
+
+	p = &buf[sizeof(buf)];
+	*--p = 0;
+	if(fp->flags & FmtSharp) {
+		// Hexadecimal
+		mpmovecfix(&sixteen, 16);
+		for(;;) {
+			mpdivmodfixfix(&q, &r, &q, &sixteen);
+			digit = mpgetfix(&r);
+			if(digit < 10)
+				*--p = digit + '0';
+			else
+				*--p = digit - 10 + 'A';
+			if(mptestfix(&q) <= 0)
+				break;
+		}
+		*--p = 'x';
+		*--p = '0';
+	} else {
+		// Decimal
+		mpmovecfix(&ten, 10);
+		for(;;) {
+			mpdivmodfixfix(&q, &r, &q, &ten);
+			*--p = mpgetfix(&r) + '0';
+			if(mptestfix(&q) <= 0)
+				break;
+		}
+	}
+	if(f)
+		*--p = '-';
+	return fmtstrcpy(fp, p);
+}
+
+int
+Fconv(Fmt *fp)
+{
+	char buf[500];
+	Mpflt *fvp, fv;
+	double d, dexp;
+	int exp;
+
+	fvp = va_arg(fp->args, Mpflt*);
+	if(fp->flags & FmtSharp) {
+		// alternate form - decimal for error messages.
+		// for well in range, convert to double and use print's %g
+		exp = fvp->exp + sigfig(fvp)*Mpscale;
+		if(-900 < exp && exp < 900) {
+			d = mpgetflt(fvp);
+			if(d >= 0 && (fp->flags & FmtSign))
+				fmtprint(fp, "+");
+			return fmtprint(fp, "%g", d);
+		}
+		
+		// very out of range. compute decimal approximation by hand.
+		// decimal exponent
+		dexp = fvp->exp * 0.301029995663981195; // log_10(2)
+		exp = (int)dexp;
+		// decimal mantissa
+		fv = *fvp;
+		fv.val.neg = 0;
+		fv.exp = 0;
+		d = mpgetflt(&fv);
+		d *= pow(10, dexp-exp);
+		while(d >= 9.99995) {
+			d /= 10;
+			exp++;
+		}
+		if(fvp->val.neg)
+			fmtprint(fp, "-");
+		else if(fp->flags & FmtSign)
+			fmtprint(fp, "+");
+		return fmtprint(fp, "%.5fe+%d", d, exp);
+	}
+
+	if(sigfig(fvp) == 0) {
+		snprint(buf, sizeof(buf), "0p+0");
+		goto out;
+	}
+	fv = *fvp;
+
+	while(fv.val.a[0] == 0) {
+		mpshiftfix(&fv.val, -Mpscale);
+		fv.exp += Mpscale;
+	}
+	while((fv.val.a[0]&1) == 0) {
+		mpshiftfix(&fv.val, -1);
+		fv.exp += 1;
+	}
+
+	if(fv.exp >= 0) {
+		snprint(buf, sizeof(buf), "%#Bp+%d", &fv.val, fv.exp);
+		goto out;
+	}
+	snprint(buf, sizeof(buf), "%#Bp-%d", &fv.val, -fv.exp);
+
+out:
+	return fmtstrcpy(fp, buf);
+}
diff --git a/src/cmd/gc/mparith2.c b/src/cmd/gc/mparith2.c
new file mode 100644
index 0000000..fd9f591
--- /dev/null
+++ b/src/cmd/gc/mparith2.c
@@ -0,0 +1,716 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include	<u.h>
+#include	<libc.h>
+#include	"go.h"
+
+//
+// return the significant
+// words of the argument
+//
+static int
+mplen(Mpint *a)
+{
+	int i, n;
+	long *a1;
+
+	n = -1;
+	a1 = &a->a[0];
+	for(i=0; i<Mpprec; i++) {
+		if(*a1++ != 0)
+			n = i;
+	}
+	return n+1;
+}
+
+//
+// left shift mpint by one
+// ignores sign
+//
+static void
+mplsh(Mpint *a, int quiet)
+{
+	long *a1, x;
+	int i, c;
+
+	c = 0;
+	a1 = &a->a[0];
+	for(i=0; i<Mpprec; i++) {
+		x = (*a1 << 1) + c;
+		c = 0;
+		if(x >= Mpbase) {
+			x -= Mpbase;
+			c = 1;
+		}
+		*a1++ = x;
+	}
+	a->ovf = c;
+	if(a->ovf && !quiet)
+		yyerror("constant shift overflow");
+}
+
+//
+// left shift mpint by Mpscale
+// ignores sign
+//
+static void
+mplshw(Mpint *a, int quiet)
+{
+	long *a1;
+	int i;
+
+	a1 = &a->a[Mpprec-1];
+	if(*a1) {
+		a->ovf = 1;
+		if(!quiet)
+			yyerror("constant shift overflow");
+	}
+	for(i=1; i<Mpprec; i++) {
+		a1[0] = a1[-1];
+		a1--;
+	}
+	a1[0] = 0;
+}
+
+//
+// right shift mpint by one
+// ignores sign and overflow
+//
+static void
+mprsh(Mpint *a)
+{
+	long *a1, x, lo;
+	int i, c;
+
+	c = 0;
+	lo = a->a[0] & 1;
+	a1 = &a->a[Mpprec];
+	for(i=0; i<Mpprec; i++) {
+		x = *--a1;
+		*a1 = (x + c) >> 1;
+		c = 0;
+		if(x & 1)
+			c = Mpbase;
+	}
+	if(a->neg && lo != 0)
+		mpaddcfix(a, -1);
+}
+
+//
+// right shift mpint by Mpscale
+// ignores sign and overflow
+//
+static void
+mprshw(Mpint *a)
+{
+	long *a1, lo;
+	int i;
+
+	lo = a->a[0];
+	a1 = &a->a[0];
+	for(i=1; i<Mpprec; i++) {
+		a1[0] = a1[1];
+		a1++;
+	}
+	a1[0] = 0;
+	if(a->neg && lo != 0)
+		mpaddcfix(a, -1);
+}
+
+//
+// return the sign of (abs(a)-abs(b))
+//
+static int
+mpcmp(Mpint *a, Mpint *b)
+{
+	long x, *a1, *b1;
+	int i;
+
+	if(a->ovf || b->ovf) {
+		if(nsavederrors+nerrors == 0)
+			yyerror("ovf in cmp");
+		return 0;
+	}
+
+	a1 = &a->a[0] + Mpprec;
+	b1 = &b->a[0] + Mpprec;
+
+	for(i=0; i<Mpprec; i++) {
+		x = *--a1 - *--b1;
+		if(x > 0)
+			return +1;
+		if(x < 0)
+			return -1;
+	}
+	return 0;
+}
+
+//
+// negate a
+// ignore sign and ovf
+//
+static void
+mpneg(Mpint *a)
+{
+	long x, *a1;
+	int i, c;
+
+	a1 = &a->a[0];
+	c = 0;
+	for(i=0; i<Mpprec; i++) {
+		x = -*a1 -c;
+		c = 0;
+		if(x < 0) {
+			x += Mpbase;
+			c = 1;
+		}
+		*a1++ = x;
+	}
+}
+
+// shift left by s (or right by -s)
+void
+mpshiftfix(Mpint *a, int s)
+{
+	if(s >= 0) {
+		while(s >= Mpscale) {
+			mplshw(a, 0);
+			s -= Mpscale;
+		}
+		while(s > 0) {
+			mplsh(a, 0);
+			s--;
+		}
+	} else {
+		s = -s;
+		while(s >= Mpscale) {
+			mprshw(a);
+			s -= Mpscale;
+		}
+		while(s > 0) {
+			mprsh(a);
+			s--;
+		}
+	}
+}
+
+/// implements fix arihmetic
+
+void
+mpaddfixfix(Mpint *a, Mpint *b, int quiet)
+{
+	int i, c;
+	long x, *a1, *b1;
+
+	if(a->ovf || b->ovf) {
+		if(nsavederrors+nerrors == 0)
+			yyerror("ovf in mpaddxx");
+		a->ovf = 1;
+		return;
+	}
+
+	c = 0;
+	a1 = &a->a[0];
+	b1 = &b->a[0];
+	if(a->neg != b->neg)
+		goto sub;
+
+	// perform a+b
+	for(i=0; i<Mpprec; i++) {
+		x = *a1 + *b1++ + c;
+		c = 0;
+		if(x >= Mpbase) {
+			x -= Mpbase;
+			c = 1;
+		}
+		*a1++ = x;
+	}
+	a->ovf = c;
+	if(a->ovf && !quiet)
+		yyerror("constant addition overflow");
+
+	return;
+
+sub:
+	// perform a-b
+	switch(mpcmp(a, b)) {
+	case 0:
+		mpmovecfix(a, 0);
+		break;
+
+	case 1:
+		for(i=0; i<Mpprec; i++) {
+			x = *a1 - *b1++ - c;
+			c = 0;
+			if(x < 0) {
+				x += Mpbase;
+				c = 1;
+			}
+			*a1++ = x;
+		}
+		break;
+
+	case -1:
+		a->neg ^= 1;
+		for(i=0; i<Mpprec; i++) {
+			x = *b1++ - *a1 - c;
+			c = 0;
+			if(x < 0) {
+				x += Mpbase;
+				c = 1;
+			}
+			*a1++ = x;
+		}
+		break;
+	}
+}
+
+void
+mpmulfixfix(Mpint *a, Mpint *b)
+{
+
+	int i, j, na, nb;
+	long *a1, x;
+	Mpint s, q;
+
+	if(a->ovf || b->ovf) {
+		if(nsavederrors+nerrors == 0)
+			yyerror("ovf in mpmulfixfix");
+		a->ovf = 1;
+		return;
+	}
+
+	// pick the smaller
+	// to test for bits
+	na = mplen(a);
+	nb = mplen(b);
+	if(na > nb) {
+		mpmovefixfix(&s, a);
+		a1 = &b->a[0];
+		na = nb;
+	} else {
+		mpmovefixfix(&s, b);
+		a1 = &a->a[0];
+	}
+	s.neg = 0;
+
+	mpmovecfix(&q, 0);
+	for(i=0; i<na; i++) {
+		x = *a1++;
+		for(j=0; j<Mpscale; j++) {
+			if(x & 1) {
+				if(s.ovf) {
+					q.ovf = 1;
+					goto out;
+				}
+				mpaddfixfix(&q, &s, 1);
+				if(q.ovf)
+					goto out;
+			}
+			mplsh(&s, 1);
+			x >>= 1;
+		}
+	}
+
+out:
+	q.neg = a->neg ^ b->neg;
+	mpmovefixfix(a, &q);
+	if(a->ovf)
+		yyerror("constant multiplication overflow");
+}
+
+void
+mpmulfract(Mpint *a, Mpint *b)
+{
+
+	int i, j;
+	long *a1, x;
+	Mpint s, q;
+
+	if(a->ovf || b->ovf) {
+		if(nsavederrors+nerrors == 0)
+			yyerror("ovf in mpmulflt");
+		a->ovf = 1;
+		return;
+	}
+
+	mpmovefixfix(&s, b);
+	a1 = &a->a[Mpprec];
+	s.neg = 0;
+	mpmovecfix(&q, 0);
+
+	x = *--a1;
+	if(x != 0)
+		yyerror("mpmulfract not normal");
+
+	for(i=0; i<Mpprec-1; i++) {
+		x = *--a1;
+		if(x == 0) {
+			mprshw(&s);
+			continue;
+		}
+		for(j=0; j<Mpscale; j++) {
+			x <<= 1;
+			if(x & Mpbase)
+				mpaddfixfix(&q, &s, 1);
+			mprsh(&s);
+		}
+	}
+
+	q.neg = a->neg ^ b->neg;
+	mpmovefixfix(a, &q);
+	if(a->ovf)
+		yyerror("constant multiplication overflow");
+}
+
+void
+mporfixfix(Mpint *a, Mpint *b)
+{
+	int i;
+	long x, *a1, *b1;
+
+	x = 0;
+	if(a->ovf || b->ovf) {
+		if(nsavederrors+nerrors == 0)
+			yyerror("ovf in mporfixfix");
+		mpmovecfix(a, 0);
+		a->ovf = 1;
+		return;
+	}
+	if(a->neg) {
+		a->neg = 0;
+		mpneg(a);
+	}
+	if(b->neg)
+		mpneg(b);
+
+	a1 = &a->a[0];
+	b1 = &b->a[0];
+	for(i=0; i<Mpprec; i++) {
+		x = *a1 | *b1++;
+		*a1++ = x;
+	}
+
+	if(b->neg)
+		mpneg(b);
+	if(x & Mpsign) {
+		a->neg = 1;
+		mpneg(a);
+	}
+}
+
+void
+mpandfixfix(Mpint *a, Mpint *b)
+{
+	int i;
+	long x, *a1, *b1;
+
+	x = 0;
+	if(a->ovf || b->ovf) {
+		if(nsavederrors+nerrors == 0)
+			yyerror("ovf in mpandfixfix");
+		mpmovecfix(a, 0);
+		a->ovf = 1;
+		return;
+	}
+	if(a->neg) {
+		a->neg = 0;
+		mpneg(a);
+	}
+	if(b->neg)
+		mpneg(b);
+
+	a1 = &a->a[0];
+	b1 = &b->a[0];
+	for(i=0; i<Mpprec; i++) {
+		x = *a1 & *b1++;
+		*a1++ = x;
+	}
+
+	if(b->neg)
+		mpneg(b);
+	if(x & Mpsign) {
+		a->neg = 1;
+		mpneg(a);
+	}
+}
+
+void
+mpandnotfixfix(Mpint *a, Mpint *b)
+{
+	int i;
+	long x, *a1, *b1;
+
+	x = 0;
+	if(a->ovf || b->ovf) {
+		if(nsavederrors+nerrors == 0)
+			yyerror("ovf in mpandnotfixfix");
+		mpmovecfix(a, 0);
+		a->ovf = 1;
+		return;
+	}
+	if(a->neg) {
+		a->neg = 0;
+		mpneg(a);
+	}
+	if(b->neg)
+		mpneg(b);
+
+	a1 = &a->a[0];
+	b1 = &b->a[0];
+	for(i=0; i<Mpprec; i++) {
+		x = *a1 & ~*b1++;
+		*a1++ = x;
+	}
+
+	if(b->neg)
+		mpneg(b);
+	if(x & Mpsign) {
+		a->neg = 1;
+		mpneg(a);
+	}
+}
+
+void
+mpxorfixfix(Mpint *a, Mpint *b)
+{
+	int i;
+	long x, *a1, *b1;
+
+	x = 0;
+	if(a->ovf || b->ovf) {
+		if(nsavederrors+nerrors == 0)
+			yyerror("ovf in mporfixfix");
+		mpmovecfix(a, 0);
+		a->ovf = 1;
+		return;
+	}
+	if(a->neg) {
+		a->neg = 0;
+		mpneg(a);
+	}
+	if(b->neg)
+		mpneg(b);
+
+	a1 = &a->a[0];
+	b1 = &b->a[0];
+	for(i=0; i<Mpprec; i++) {
+		x = *a1 ^ *b1++;
+		*a1++ = x;
+	}
+
+	if(b->neg)
+		mpneg(b);
+	if(x & Mpsign) {
+		a->neg = 1;
+		mpneg(a);
+	}
+}
+
+void
+mplshfixfix(Mpint *a, Mpint *b)
+{
+	vlong s;
+
+	if(a->ovf || b->ovf) {
+		if(nsavederrors+nerrors == 0)
+			yyerror("ovf in mporfixfix");
+		mpmovecfix(a, 0);
+		a->ovf = 1;
+		return;
+	}
+	s = mpgetfix(b);
+	if(s < 0 || s >= Mpprec*Mpscale) {
+		yyerror("stupid shift: %lld", s);
+		mpmovecfix(a, 0);
+		return;
+	}
+
+	mpshiftfix(a, s);
+}
+
+void
+mprshfixfix(Mpint *a, Mpint *b)
+{
+	vlong s;
+
+	if(a->ovf || b->ovf) {
+		if(nsavederrors+nerrors == 0)
+			yyerror("ovf in mprshfixfix");
+		mpmovecfix(a, 0);
+		a->ovf = 1;
+		return;
+	}
+	s = mpgetfix(b);
+	if(s < 0 || s >= Mpprec*Mpscale) {
+		yyerror("stupid shift: %lld", s);
+		if(a->neg)
+			mpmovecfix(a, -1);
+		else
+			mpmovecfix(a, 0);
+		return;
+	}
+
+	mpshiftfix(a, -s);
+}
+
+void
+mpnegfix(Mpint *a)
+{
+	a->neg ^= 1;
+}
+
+vlong
+mpgetfix(Mpint *a)
+{
+	vlong v;
+
+	if(a->ovf) {
+		if(nsavederrors+nerrors == 0)
+			yyerror("constant overflow");
+		return 0;
+	}
+
+	v = (uvlong)a->a[0];
+	v |= (uvlong)a->a[1] << Mpscale;
+	v |= (uvlong)a->a[2] << (Mpscale+Mpscale);
+	if(a->neg)
+		v = -(uvlong)v;
+	return v;
+}
+
+void
+mpmovecfix(Mpint *a, vlong c)
+{
+	int i;
+	long *a1;
+	vlong x;
+
+	a->neg = 0;
+	a->ovf = 0;
+
+	x = c;
+	if(x < 0) {
+		a->neg = 1;
+		x = -(uvlong)x;
+	}
+
+	a1 = &a->a[0];
+	for(i=0; i<Mpprec; i++) {
+		*a1++ = x&Mpmask;
+		x >>= Mpscale;
+	}
+}
+
+void
+mpdivmodfixfix(Mpint *q, Mpint *r, Mpint *n, Mpint *d)
+{
+	int i, ns, ds;
+
+	ns = n->neg;
+	ds = d->neg;
+	n->neg = 0;
+	d->neg = 0;
+
+	mpmovefixfix(r, n);
+	mpmovecfix(q, 0);
+
+	// shift denominator until it
+	// is larger than numerator
+	for(i=0; i<Mpprec*Mpscale; i++) {
+		if(mpcmp(d, r) > 0)
+			break;
+		mplsh(d, 1);
+	}
+
+	// if it never happens
+	// denominator is probably zero
+	if(i >= Mpprec*Mpscale) {
+		q->ovf = 1;
+		r->ovf = 1;
+		n->neg = ns;
+		d->neg = ds;
+		yyerror("constant division overflow");
+		return;
+	}
+
+	// shift denominator back creating
+	// quotient a bit at a time
+	// when done the remaining numerator
+	// will be the remainder
+	for(; i>0; i--) {
+		mplsh(q, 1);
+		mprsh(d);
+		if(mpcmp(d, r) <= 0) {
+			mpaddcfix(q, 1);
+			mpsubfixfix(r, d);
+		}
+	}
+
+	n->neg = ns;
+	d->neg = ds;
+	r->neg = ns;
+	q->neg = ns^ds;
+}
+
+static int
+mpiszero(Mpint *a)
+{
+	long *a1;
+	int i;
+	a1 = &a->a[0] + Mpprec;
+	for(i=0; i<Mpprec; i++) {
+		if(*--a1 != 0)
+			return 0;
+	}
+	return 1;
+}
+
+void
+mpdivfract(Mpint *a, Mpint *b)
+{
+	Mpint n, d;
+	int i, j, neg;
+	long *a1, x;
+
+	mpmovefixfix(&n, a);	// numerator
+	mpmovefixfix(&d, b);	// denominator
+	a1 = &a->a[Mpprec];	// quotient
+
+	neg = n.neg ^ d.neg;
+	n.neg = 0;
+	d.neg = 0;
+	for(i=0; i<Mpprec; i++) {
+		x = 0;
+		for(j=0; j<Mpscale; j++) {
+			x <<= 1;
+			if(mpcmp(&d, &n) <= 0) {
+				if(!mpiszero(&d))
+					x |= 1;
+				mpsubfixfix(&n, &d);
+			}
+			mprsh(&d);
+		}
+		*--a1 = x;
+	}
+	a->neg = neg;
+}
+
+int
+mptestfix(Mpint *a)
+{
+	Mpint b;
+	int r;
+
+	mpmovecfix(&b, 0);
+	r = mpcmp(a, &b);
+	if(a->neg) {
+		if(r > 0)
+			return -1;
+		if(r < 0)
+			return +1;
+	}
+	return r;
+}
diff --git a/src/cmd/gc/mparith3.c b/src/cmd/gc/mparith3.c
new file mode 100644
index 0000000..6afd75c
--- /dev/null
+++ b/src/cmd/gc/mparith3.c
@@ -0,0 +1,346 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include	<u.h>
+#include	<libc.h>
+#include	"go.h"
+
+/*
+ * returns the leading non-zero
+ * word of the number
+ */
+int
+sigfig(Mpflt *a)
+{
+	int i;
+
+	for(i=Mpprec-1; i>=0; i--)
+		if(a->val.a[i] != 0)
+			break;
+//print("sigfig %d %d\n", i-z+1, z);
+	return i+1;
+}
+
+/*
+ * sets the exponent.
+ * a too large exponent is an error.
+ * a too small exponent rounds the number to zero.
+ */
+void
+mpsetexp(Mpflt *a, int exp) {
+	if((short)exp != exp) {
+		if(exp > 0) {
+			yyerror("float constant is too large");
+			a->exp = 0x7fff;
+		}
+		else {
+			mpmovecflt(a, 0);
+		}
+	}
+	else {
+		a->exp = exp;
+	}
+}
+
+/*
+ * shifts the leading non-zero
+ * word of the number to Mpnorm
+ */
+void
+mpnorm(Mpflt *a)
+{
+	int s, os;
+	long x;
+
+	os = sigfig(a);
+	if(os == 0) {
+		// zero
+		a->exp = 0;
+		a->val.neg = 0;
+		return;
+	}
+
+	// this will normalize to the nearest word
+	x = a->val.a[os-1];
+	s = (Mpnorm-os) * Mpscale;
+
+	// further normalize to the nearest bit
+	for(;;) {
+		x <<= 1;
+		if(x & Mpbase)
+			break;
+		s++;
+		if(x == 0) {
+			// this error comes from trying to
+			// convert an Inf or something
+			// where the initial x=0x80000000
+			s = (Mpnorm-os) * Mpscale;
+			break;
+		}
+	}
+
+	mpshiftfix(&a->val, s);
+	mpsetexp(a, a->exp-s);
+}
+
+/// implements float arihmetic
+
+void
+mpaddfltflt(Mpflt *a, Mpflt *b)
+{
+	int sa, sb, s;
+	Mpflt c;
+
+	if(Mpdebug)
+		print("\n%F + %F", a, b);
+
+	sa = sigfig(a);
+	if(sa == 0) {
+		mpmovefltflt(a, b);
+		goto out;
+	}
+
+	sb = sigfig(b);
+	if(sb == 0)
+		goto out;
+
+	s = a->exp - b->exp;
+	if(s > 0) {
+		// a is larger, shift b right
+		mpmovefltflt(&c, b);
+		mpshiftfix(&c.val, -s);
+		mpaddfixfix(&a->val, &c.val, 0);
+		goto out;
+	}
+	if(s < 0) {
+		// b is larger, shift a right
+		mpshiftfix(&a->val, s);
+		mpsetexp(a, a->exp-s);
+		mpaddfixfix(&a->val, &b->val, 0);
+		goto out;
+	}
+	mpaddfixfix(&a->val, &b->val, 0);
+
+out:
+	mpnorm(a);
+	if(Mpdebug)
+		print(" = %F\n\n", a);
+}
+
+void
+mpmulfltflt(Mpflt *a, Mpflt *b)
+{
+	int sa, sb;
+
+	if(Mpdebug)
+		print("%F\n * %F\n", a, b);
+
+	sa = sigfig(a);
+	if(sa == 0) {
+		// zero
+		a->exp = 0;
+		a->val.neg = 0;
+		return;
+	}
+
+	sb = sigfig(b);
+	if(sb == 0) {
+		// zero
+		mpmovefltflt(a, b);
+		return;
+	}
+
+	mpmulfract(&a->val, &b->val);
+	mpsetexp(a, (a->exp + b->exp) + Mpscale*Mpprec - Mpscale - 1);
+
+	mpnorm(a);
+	if(Mpdebug)
+		print(" = %F\n\n", a);
+}
+
+void
+mpdivfltflt(Mpflt *a, Mpflt *b)
+{
+	int sa, sb;
+	Mpflt c;
+
+	if(Mpdebug)
+		print("%F\n / %F\n", a, b);
+
+	sb = sigfig(b);
+	if(sb == 0) {
+		// zero and ovfl
+		a->exp = 0;
+		a->val.neg = 0;
+		a->val.ovf = 1;
+		yyerror("constant division by zero");
+		return;
+	}
+
+	sa = sigfig(a);
+	if(sa == 0) {
+		// zero
+		a->exp = 0;
+		a->val.neg = 0;
+		return;
+	}
+
+	// adjust b to top
+	mpmovefltflt(&c, b);
+	mpshiftfix(&c.val, Mpscale);
+
+	// divide
+	mpdivfract(&a->val, &c.val);
+	mpsetexp(a, (a->exp-c.exp) - Mpscale*(Mpprec-1) + 1);
+
+	mpnorm(a);
+	if(Mpdebug)
+		print(" = %F\n\n", a);
+}
+
+static double
+mpgetfltN(Mpflt *a, int prec, int bias)
+{
+	int s, i, e, minexp;
+	uvlong v;
+	double f;
+
+	if(a->val.ovf && nsavederrors+nerrors == 0)
+		yyerror("mpgetflt ovf");
+
+	s = sigfig(a);
+	if(s == 0)
+		return 0;
+
+	if(s != Mpnorm) {
+		yyerror("mpgetflt norm");
+		mpnorm(a);
+	}
+
+	while((a->val.a[Mpnorm-1] & Mpsign) == 0) {
+		mpshiftfix(&a->val, 1);
+		mpsetexp(a, a->exp-1);	// can set 'a' to zero
+		s = sigfig(a);
+		if(s == 0)
+			return 0;
+	}
+
+	// pick up the mantissa, a rounding bit, and a tie-breaking bit in a uvlong
+	s = prec+2;
+	v = 0;
+	for(i=Mpnorm-1; s>=Mpscale; i--) {
+		v = (v<<Mpscale) | a->val.a[i];
+		s -= Mpscale;
+	}
+	if(s > 0) {
+		v = (v<<s) | (a->val.a[i]>>(Mpscale-s));
+		if((a->val.a[i]&((1<<(Mpscale-s))-1)) != 0)
+			v |= 1;
+		i--;
+	}
+	for(; i >= 0; i--) {
+		if(a->val.a[i] != 0)
+			v |= 1;
+	}
+
+	// gradual underflow
+	e = Mpnorm*Mpscale + a->exp - prec;
+	minexp = bias+1-prec+1;
+	if(e < minexp) {
+		s = minexp - e;
+		if(s > prec+1)
+			s = prec+1;
+		if((v & ((1ULL<<s)-1)) != 0)
+			v |= 1ULL<<s;
+		v >>= s;
+		e = minexp;
+	}
+	
+	// round to even
+	v |= (v&4)>>2;
+	v += v&1;
+	v >>= 2;
+
+	f = (double)(v);
+	f = ldexp(f, e);
+
+	if(a->val.neg)
+		f = -f;
+
+	return f;
+}
+
+double
+mpgetflt(Mpflt *a)
+{
+	return mpgetfltN(a, 53, -1023);
+}
+
+double
+mpgetflt32(Mpflt *a)
+{
+	return mpgetfltN(a, 24, -127);
+}
+
+void
+mpmovecflt(Mpflt *a, double c)
+{
+	int i;
+	double f;
+	long l;
+
+	if(Mpdebug)
+		print("\nconst %g", c);
+	mpmovecfix(&a->val, 0);
+	a->exp = 0;
+	if(c == 0)
+		goto out;
+	if(c < 0) {
+		a->val.neg = 1;
+		c = -c;
+	}
+
+	f = frexp(c, &i);
+	a->exp = i;
+
+	for(i=0; i<10; i++) {
+		f = f*Mpbase;
+		l = floor(f);
+		f = f - l;
+		a->exp -= Mpscale;
+		a->val.a[0] = l;
+		if(f == 0)
+			break;
+		mpshiftfix(&a->val, Mpscale);
+	}
+
+out:
+	mpnorm(a);
+	if(Mpdebug)
+		print(" = %F\n", a);
+}
+
+void
+mpnegflt(Mpflt *a)
+{
+	a->val.neg ^= 1;
+}
+
+int
+mptestflt(Mpflt *a)
+{
+	int s;
+
+	if(Mpdebug)
+		print("\n%F?", a);
+	s = sigfig(a);
+	if(s != 0) {
+		s = +1;
+		if(a->val.neg)
+			s = -1;
+	}
+	if(Mpdebug)
+		print(" = %d\n", s);
+	return s;
+}
diff --git a/src/cmd/gc/obj.c b/src/cmd/gc/obj.c
new file mode 100644
index 0000000..b752a13
--- /dev/null
+++ b/src/cmd/gc/obj.c
@@ -0,0 +1,284 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+#include "../ld/textflag.h"
+
+/*
+ * architecture-independent object file output
+ */
+
+static	void	dumpglobls(void);
+
+enum
+{
+	ArhdrSize = 60
+};
+
+static void
+formathdr(char *arhdr, char *name, vlong size)
+{
+	snprint(arhdr, ArhdrSize, "%-16s%-12d%-6d%-6d%-8o%-10lld`",
+		name, 0, 0, 0, 0644, size);
+	arhdr[ArhdrSize-1] = '\n'; // overwrite \0 written by snprint
+}
+
+void
+dumpobj(void)
+{
+	NodeList *externs, *tmp;
+	char arhdr[ArhdrSize];
+	vlong startobj, size;
+	Sym *zero;
+
+	bout = Bopen(outfile, OWRITE);
+	if(bout == nil) {
+		flusherrors();
+		print("can't create %s: %r\n", outfile);
+		errorexit();
+	}
+
+	startobj = 0;
+	if(writearchive) {
+		Bwrite(bout, "!<arch>\n", 8);
+		memset(arhdr, 0, sizeof arhdr);
+		Bwrite(bout, arhdr, sizeof arhdr);
+		startobj = Boffset(bout);
+	}
+	Bprint(bout, "go object %s %s %s %s\n", getgoos(), getgoarch(), getgoversion(), expstring());
+	dumpexport();
+	
+	if(writearchive) {
+		Bflush(bout);
+		size = Boffset(bout) - startobj;
+		if(size&1)
+			Bputc(bout, 0);
+		Bseek(bout, startobj - ArhdrSize, 0);
+		formathdr(arhdr, "__.PKGDEF", size);
+		Bwrite(bout, arhdr, ArhdrSize);
+		Bflush(bout);
+
+		Bseek(bout, startobj + size + (size&1), 0);
+		memset(arhdr, 0, ArhdrSize);
+		Bwrite(bout, arhdr, ArhdrSize);
+		startobj = Boffset(bout);
+		Bprint(bout, "go object %s %s %s %s\n", getgoos(), getgoarch(), getgoversion(), expstring());
+	}
+
+	Bprint(bout, "\n!\n");
+
+	externs = nil;
+	if(externdcl != nil)
+		externs = externdcl->end;
+
+	dumpglobls();
+	dumptypestructs();
+
+	// Dump extra globals.
+	tmp = externdcl;
+	if(externs != nil)
+		externdcl = externs->next;
+	dumpglobls();
+	externdcl = tmp;
+
+	zero = pkglookup("zerovalue", runtimepkg);
+	ggloblsym(zero, zerosize, DUPOK|RODATA);
+
+	dumpdata();
+	writeobj(ctxt, bout);
+
+	if(writearchive) {
+		Bflush(bout);
+		size = Boffset(bout) - startobj;
+		if(size&1)
+			Bputc(bout, 0);
+		Bseek(bout, startobj - ArhdrSize, 0);
+		snprint(namebuf, sizeof namebuf, "_go_.%c", thechar);
+		formathdr(arhdr, namebuf, size);
+		Bwrite(bout, arhdr, ArhdrSize);
+	}
+	Bterm(bout);
+}
+
+static void
+dumpglobls(void)
+{
+	Node *n;
+	NodeList *l;
+
+	// add globals
+	for(l=externdcl; l; l=l->next) {
+		n = l->n;
+		if(n->op != ONAME)
+			continue;
+
+		if(n->type == T)
+			fatal("external %N nil type\n", n);
+		if(n->class == PFUNC)
+			continue;
+		if(n->sym->pkg != localpkg)
+			continue;
+		dowidth(n->type);
+
+		ggloblnod(n);
+	}
+	
+	for(l=funcsyms; l; l=l->next) {
+		n = l->n;
+		dsymptr(n->sym, 0, n->sym->def->shortname->sym, 0);
+		ggloblsym(n->sym, widthptr, DUPOK|RODATA);
+	}
+	
+	// Do not reprocess funcsyms on next dumpglobls call.
+	funcsyms = nil;
+}
+
+void
+Bputname(Biobuf *b, LSym *s)
+{
+	Bwrite(b, s->name, strlen(s->name)+1);
+}
+
+LSym*
+linksym(Sym *s)
+{
+	char *p;
+
+	if(s == nil)
+		return nil;
+	if(s->lsym != nil)
+		return s->lsym;
+	if(isblanksym(s))
+		s->lsym = linklookup(ctxt, "_", 0);
+	else {
+		p = smprint("%s.%s", s->pkg->prefix, s->name);
+		s->lsym = linklookup(ctxt, p, 0);
+		free(p);
+	}
+	return s->lsym;	
+}
+
+int
+duintxx(Sym *s, int off, uint64 v, int wid)
+{
+	// Update symbol data directly instead of generating a
+	// DATA instruction that liblink will have to interpret later.
+	// This reduces compilation time and memory usage.
+	off = rnd(off, wid);
+	return setuintxx(ctxt, linksym(s), off, v, wid);
+}
+
+int
+duint8(Sym *s, int off, uint8 v)
+{
+	return duintxx(s, off, v, 1);
+}
+
+int
+duint16(Sym *s, int off, uint16 v)
+{
+	return duintxx(s, off, v, 2);
+}
+
+int
+duint32(Sym *s, int off, uint32 v)
+{
+	return duintxx(s, off, v, 4);
+}
+
+int
+duint64(Sym *s, int off, uint64 v)
+{
+	return duintxx(s, off, v, 8);
+}
+
+int
+duintptr(Sym *s, int off, uint64 v)
+{
+	return duintxx(s, off, v, widthptr);
+}
+
+Sym*
+stringsym(char *s, int len)
+{
+	static int gen;
+	Sym *sym;
+	int off, n, m;
+	struct {
+		Strlit lit;
+		char buf[110];
+	} tmp;
+	Pkg *pkg;
+
+	if(len > 100) {
+		// huge strings are made static to avoid long names
+		snprint(namebuf, sizeof(namebuf), ".gostring.%d", ++gen);
+		pkg = localpkg;
+	} else {
+		// small strings get named by their contents,
+		// so that multiple modules using the same string
+		// can share it.
+		tmp.lit.len = len;
+		memmove(tmp.lit.s, s, len);
+		tmp.lit.s[len] = '\0';
+		snprint(namebuf, sizeof(namebuf), "\"%Z\"", &tmp.lit);
+		pkg = gostringpkg;
+	}
+	sym = pkglookup(namebuf, pkg);
+	
+	// SymUniq flag indicates that data is generated already
+	if(sym->flags & SymUniq)
+		return sym;
+	sym->flags |= SymUniq;
+	sym->def = newname(sym);
+
+	off = 0;
+	
+	// string header
+	off = dsymptr(sym, off, sym, widthptr+widthint);
+	off = duintxx(sym, off, len, widthint);
+	
+	// string data
+	for(n=0; n<len; n+=m) {
+		m = 8;
+		if(m > len-n)
+			m = len-n;
+		off = dsname(sym, off, s+n, m);
+	}
+	off = duint8(sym, off, 0);  // terminating NUL for runtime
+	off = (off+widthptr-1)&~(widthptr-1);  // round to pointer alignment
+	ggloblsym(sym, off, DUPOK|RODATA);
+
+	return sym;	
+}
+
+void
+slicebytes(Node *nam, char *s, int len)
+{
+	int off, n, m;
+	static int gen;
+	Sym *sym;
+
+	snprint(namebuf, sizeof(namebuf), ".gobytes.%d", ++gen);
+	sym = pkglookup(namebuf, localpkg);
+	sym->def = newname(sym);
+
+	off = 0;
+	for(n=0; n<len; n+=m) {
+		m = 8;
+		if(m > len-n)
+			m = len-n;
+		off = dsname(sym, off, s+n, m);
+	}
+	ggloblsym(sym, off, NOPTR);
+	
+	if(nam->op != ONAME)
+		fatal("slicebytes %N", nam);
+	off = nam->xoffset;
+	off = dsymptr(nam->sym, off, sym, 0);
+	off = duintxx(nam->sym, off, len, widthint);
+	duintxx(nam->sym, off, len, widthint);
+}
diff --git a/src/cmd/gc/opnames.h b/src/cmd/gc/opnames.h
new file mode 100644
index 0000000..be93036
--- /dev/null
+++ b/src/cmd/gc/opnames.h
@@ -0,0 +1,155 @@
+// auto generated by go tool dist
+static char *opnames[] = {
+	[OXXX] = "XXX",
+	[ONAME] = "NAME",
+	[ONONAME] = "NONAME",
+	[OTYPE] = "TYPE",
+	[OPACK] = "PACK",
+	[OLITERAL] = "LITERAL",
+	[OADD] = "ADD",
+	[OSUB] = "SUB",
+	[OOR] = "OR",
+	[OXOR] = "XOR",
+	[OADDSTR] = "ADDSTR",
+	[OADDR] = "ADDR",
+	[OANDAND] = "ANDAND",
+	[OAPPEND] = "APPEND",
+	[OARRAYBYTESTR] = "ARRAYBYTESTR",
+	[OARRAYBYTESTRTMP] = "ARRAYBYTESTRTMP",
+	[OARRAYRUNESTR] = "ARRAYRUNESTR",
+	[OSTRARRAYBYTE] = "STRARRAYBYTE",
+	[OSTRARRAYRUNE] = "STRARRAYRUNE",
+	[OAS] = "AS",
+	[OAS2] = "AS2",
+	[OAS2FUNC] = "AS2FUNC",
+	[OAS2RECV] = "AS2RECV",
+	[OAS2MAPR] = "AS2MAPR",
+	[OAS2DOTTYPE] = "AS2DOTTYPE",
+	[OASOP] = "ASOP",
+	[OCALL] = "CALL",
+	[OCALLFUNC] = "CALLFUNC",
+	[OCALLMETH] = "CALLMETH",
+	[OCALLINTER] = "CALLINTER",
+	[OCALLPART] = "CALLPART",
+	[OCAP] = "CAP",
+	[OCLOSE] = "CLOSE",
+	[OCLOSURE] = "CLOSURE",
+	[OCMPIFACE] = "CMPIFACE",
+	[OCMPSTR] = "CMPSTR",
+	[OCOMPLIT] = "COMPLIT",
+	[OMAPLIT] = "MAPLIT",
+	[OSTRUCTLIT] = "STRUCTLIT",
+	[OARRAYLIT] = "ARRAYLIT",
+	[OPTRLIT] = "PTRLIT",
+	[OCONV] = "CONV",
+	[OCONVIFACE] = "CONVIFACE",
+	[OCONVNOP] = "CONVNOP",
+	[OCOPY] = "COPY",
+	[ODCL] = "DCL",
+	[ODCLFUNC] = "DCLFUNC",
+	[ODCLFIELD] = "DCLFIELD",
+	[ODCLCONST] = "DCLCONST",
+	[ODCLTYPE] = "DCLTYPE",
+	[ODELETE] = "DELETE",
+	[ODOT] = "DOT",
+	[ODOTPTR] = "DOTPTR",
+	[ODOTMETH] = "DOTMETH",
+	[ODOTINTER] = "DOTINTER",
+	[OXDOT] = "XDOT",
+	[ODOTTYPE] = "DOTTYPE",
+	[ODOTTYPE2] = "DOTTYPE2",
+	[OEQ] = "EQ",
+	[ONE] = "NE",
+	[OLT] = "LT",
+	[OLE] = "LE",
+	[OGE] = "GE",
+	[OGT] = "GT",
+	[OIND] = "IND",
+	[OINDEX] = "INDEX",
+	[OINDEXMAP] = "INDEXMAP",
+	[OKEY] = "KEY",
+	[OPARAM] = "PARAM",
+	[OLEN] = "LEN",
+	[OMAKE] = "MAKE",
+	[OMAKECHAN] = "MAKECHAN",
+	[OMAKEMAP] = "MAKEMAP",
+	[OMAKESLICE] = "MAKESLICE",
+	[OMUL] = "MUL",
+	[ODIV] = "DIV",
+	[OMOD] = "MOD",
+	[OLSH] = "LSH",
+	[ORSH] = "RSH",
+	[OAND] = "AND",
+	[OANDNOT] = "ANDNOT",
+	[ONEW] = "NEW",
+	[ONOT] = "NOT",
+	[OCOM] = "COM",
+	[OPLUS] = "PLUS",
+	[OMINUS] = "MINUS",
+	[OOROR] = "OROR",
+	[OPANIC] = "PANIC",
+	[OPRINT] = "PRINT",
+	[OPRINTN] = "PRINTN",
+	[OPAREN] = "PAREN",
+	[OSEND] = "SEND",
+	[OSLICE] = "SLICE",
+	[OSLICEARR] = "SLICEARR",
+	[OSLICESTR] = "SLICESTR",
+	[OSLICE3] = "SLICE3",
+	[OSLICE3ARR] = "SLICE3ARR",
+	[ORECOVER] = "RECOVER",
+	[ORECV] = "RECV",
+	[ORUNESTR] = "RUNESTR",
+	[OSELRECV] = "SELRECV",
+	[OSELRECV2] = "SELRECV2",
+	[OIOTA] = "IOTA",
+	[OREAL] = "REAL",
+	[OIMAG] = "IMAG",
+	[OCOMPLEX] = "COMPLEX",
+	[OBLOCK] = "BLOCK",
+	[OBREAK] = "BREAK",
+	[OCASE] = "CASE",
+	[OXCASE] = "XCASE",
+	[OCONTINUE] = "CONTINUE",
+	[ODEFER] = "DEFER",
+	[OEMPTY] = "EMPTY",
+	[OFALL] = "FALL",
+	[OXFALL] = "XFALL",
+	[OFOR] = "FOR",
+	[OGOTO] = "GOTO",
+	[OIF] = "IF",
+	[OLABEL] = "LABEL",
+	[OPROC] = "PROC",
+	[ORANGE] = "RANGE",
+	[ORETURN] = "RETURN",
+	[OSELECT] = "SELECT",
+	[OSWITCH] = "SWITCH",
+	[OTYPESW] = "TYPESW",
+	[OTCHAN] = "TCHAN",
+	[OTMAP] = "TMAP",
+	[OTSTRUCT] = "TSTRUCT",
+	[OTINTER] = "TINTER",
+	[OTFUNC] = "TFUNC",
+	[OTARRAY] = "TARRAY",
+	[ODDD] = "DDD",
+	[ODDDARG] = "DDDARG",
+	[OINLCALL] = "INLCALL",
+	[OEFACE] = "EFACE",
+	[OITAB] = "ITAB",
+	[OSPTR] = "SPTR",
+	[OCLOSUREVAR] = "CLOSUREVAR",
+	[OCFUNC] = "CFUNC",
+	[OCHECKNIL] = "CHECKNIL",
+	[OVARKILL] = "VARKILL",
+	[OREGISTER] = "REGISTER",
+	[OINDREG] = "INDREG",
+	[OCMP] = "CMP",
+	[ODEC] = "DEC",
+	[OINC] = "INC",
+	[OEXTEND] = "EXTEND",
+	[OHMUL] = "HMUL",
+	[OLROT] = "LROT",
+	[ORROTC] = "RROTC",
+	[ORETJMP] = "RETJMP",
+	[OEND] = "END",
+};
diff --git a/src/cmd/gc/order.c b/src/cmd/gc/order.c
new file mode 100644
index 0000000..76820fd
--- /dev/null
+++ b/src/cmd/gc/order.c
@@ -0,0 +1,1101 @@
+// Copyright 2012 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Rewrite tree to use separate statements to enforce
+// order of evaluation.  Makes walk easier, because it
+// can (after this runs) reorder at will within an expression.
+//
+// Rewrite x op= y into x = x op y.
+//
+// Introduce temporaries as needed by runtime routines.
+// For example, the map runtime routines take the map key
+// by reference, so make sure all map keys are addressable
+// by copying them to temporaries as needed.
+// The same is true for channel operations.
+//
+// Arrange that map index expressions only appear in direct
+// assignments x = m[k] or m[k] = x, never in larger expressions.
+//
+// Arrange that receive expressions only appear in direct assignments
+// x = <-c or as standalone statements <-c, never in larger expressions.
+
+// TODO(rsc): The temporary introduction during multiple assignments
+// should be moved into this file, so that the temporaries can be cleaned
+// and so that conversions implicit in the OAS2FUNC and OAS2RECV
+// nodes can be made explicit and then have their temporaries cleaned.
+
+// TODO(rsc): Goto and multilevel break/continue can jump over
+// inserted VARKILL annotations. Work out a way to handle these.
+// The current implementation is safe, in that it will execute correctly.
+// But it won't reuse temporaries as aggressively as it might, and
+// it can result in unnecessary zeroing of those variables in the function
+// prologue.
+
+#include	<u.h>
+#include	<libc.h>
+#include	"go.h"
+
+// Order holds state during the ordering process.
+typedef struct Order Order;
+struct Order
+{
+	NodeList *out; // list of generated statements
+	NodeList *temp; // head of stack of temporary variables
+	NodeList *free; // free list of NodeList* structs (for use in temp)
+};
+
+static void	orderstmt(Node*, Order*);
+static void	orderstmtlist(NodeList*, Order*);
+static void	orderblock(NodeList **l);
+static void	orderexpr(Node**, Order*);
+static void orderexprinplace(Node**, Order*);
+static void	orderexprlist(NodeList*, Order*);
+static void	orderexprlistinplace(NodeList*, Order*);
+
+// Order rewrites fn->nbody to apply the ordering constraints
+// described in the comment at the top of the file.
+void
+order(Node *fn)
+{
+	orderblock(&fn->nbody);
+}
+
+// Ordertemp allocates a new temporary with the given type,
+// pushes it onto the temp stack, and returns it.
+// If clear is true, ordertemp emits code to zero the temporary.
+static Node*
+ordertemp(Type *t, Order *order, int clear)
+{
+	Node *var, *a;
+	NodeList *l;
+
+	var = temp(t);
+	if(clear) {
+		a = nod(OAS, var, N);
+		typecheck(&a, Etop);
+		order->out = list(order->out, a);
+	}
+	if((l = order->free) == nil)
+		l = mal(sizeof *l);
+	order->free = l->next;
+	l->next = order->temp;
+	l->n = var;
+	order->temp = l;
+	return var;
+}
+
+// Ordercopyexpr behaves like ordertemp but also emits
+// code to initialize the temporary to the value n.
+//
+// The clear argument is provided for use when the evaluation
+// of tmp = n turns into a function call that is passed a pointer
+// to the temporary as the output space. If the call blocks before
+// tmp has been written, the garbage collector will still treat the
+// temporary as live, so we must zero it before entering that call.
+// Today, this only happens for channel receive operations.
+// (The other candidate would be map access, but map access
+// returns a pointer to the result data instead of taking a pointer
+// to be filled in.)
+static Node*
+ordercopyexpr(Node *n, Type *t, Order *order, int clear)
+{
+	Node *a, *var;
+
+	var = ordertemp(t, order, clear);
+	a = nod(OAS, var, n);
+	typecheck(&a, Etop);
+	order->out = list(order->out, a);
+	return var;
+}
+
+// Ordercheapexpr returns a cheap version of n.
+// The definition of cheap is that n is a variable or constant.
+// If not, ordercheapexpr allocates a new tmp, emits tmp = n,
+// and then returns tmp.
+static Node*
+ordercheapexpr(Node *n, Order *order)
+{
+	switch(n->op) {
+	case ONAME:
+	case OLITERAL:
+		return n;
+	}
+	return ordercopyexpr(n, n->type, order, 0);
+}
+
+// Ordersafeexpr returns a safe version of n.
+// The definition of safe is that n can appear multiple times
+// without violating the semantics of the original program,
+// and that assigning to the safe version has the same effect
+// as assigning to the original n.
+//
+// The intended use is to apply to x when rewriting x += y into x = x + y.
+static Node*
+ordersafeexpr(Node *n, Order *order)
+{
+	Node *l, *r, *a;
+	
+	switch(n->op) {
+	default:
+		fatal("ordersafeexpr %O", n->op);
+
+	case ONAME:
+	case OLITERAL:
+		return n;
+
+	case ODOT:
+		l = ordersafeexpr(n->left, order);
+		if(l == n->left)
+			return n;
+		a = nod(OXXX, N, N);
+		*a = *n;
+		a->orig = a;
+		a->left = l;
+		typecheck(&a, Erv);
+		return a;
+
+	case ODOTPTR:
+	case OIND:
+		l = ordercheapexpr(n->left, order);
+		if(l == n->left)
+			return n;
+		a = nod(OXXX, N, N);
+		*a = *n;
+		a->orig = a;
+		a->left = l;
+		typecheck(&a, Erv);
+		return a;
+		
+	case OINDEX:
+	case OINDEXMAP:
+		if(isfixedarray(n->left->type))
+			l = ordersafeexpr(n->left, order);
+		else
+			l = ordercheapexpr(n->left, order);
+		r = ordercheapexpr(n->right, order);
+		if(l == n->left && r == n->right)
+			return n;
+		a = nod(OXXX, N, N);
+		*a = *n;
+		a->orig = a;
+		a->left = l;
+		a->right = r;
+		typecheck(&a, Erv);
+		return a;
+	}
+}		
+
+// Istemp reports whether n is a temporary variable.
+static int
+istemp(Node *n)
+{
+	if(n->op != ONAME)
+		return 0;
+	return strncmp(n->sym->name, "autotmp_", 8) == 0;
+}
+
+// Isaddrokay reports whether it is okay to pass n's address to runtime routines.
+// Taking the address of a variable makes the liveness and optimization analyses
+// lose track of where the variable's lifetime ends. To avoid hurting the analyses
+// of ordinary stack variables, those are not 'isaddrokay'. Temporaries are okay,
+// because we emit explicit VARKILL instructions marking the end of those
+// temporaries' lifetimes.
+static int
+isaddrokay(Node *n)
+{
+	return islvalue(n) && (n->op != ONAME || n->class == PEXTERN || istemp(n));
+}
+
+// Orderaddrtemp ensures that *np is okay to pass by address to runtime routines.
+// If the original argument *np is not okay, orderaddrtemp creates a tmp, emits
+// tmp = *np, and then sets *np to the tmp variable.
+static void
+orderaddrtemp(Node **np, Order *order)
+{
+	Node *n;
+	
+	n = *np;
+	if(isaddrokay(n))
+		return;
+	*np = ordercopyexpr(n, n->type, order, 0);
+}
+
+// Marktemp returns the top of the temporary variable stack.
+static NodeList*
+marktemp(Order *order)
+{
+	return order->temp;
+}
+
+// Poptemp pops temporaries off the stack until reaching the mark,
+// which must have been returned by marktemp.
+static void
+poptemp(NodeList *mark, Order *order)
+{
+	NodeList *l;
+
+	while((l = order->temp) != mark) {
+		order->temp = l->next;
+		l->next = order->free;
+		order->free = l;
+	}
+}
+
+// Cleantempnopop emits to *out VARKILL instructions for each temporary
+// above the mark on the temporary stack, but it does not pop them
+// from the stack.
+static void
+cleantempnopop(NodeList *mark, Order *order, NodeList **out)
+{
+	NodeList *l;
+	Node *kill;
+
+	for(l=order->temp; l != mark; l=l->next) {
+		kill = nod(OVARKILL, l->n, N);
+		typecheck(&kill, Etop);
+		*out = list(*out, kill);
+	}
+}
+
+// Cleantemp emits VARKILL instructions for each temporary above the
+// mark on the temporary stack and removes them from the stack.
+static void
+cleantemp(NodeList *top, Order *order)
+{
+	cleantempnopop(top, order, &order->out);
+	poptemp(top, order);
+}
+
+// Orderstmtlist orders each of the statements in the list.
+static void
+orderstmtlist(NodeList *l, Order *order)
+{
+	for(; l; l=l->next)
+		orderstmt(l->n, order);
+}
+
+// Orderblock orders the block of statements *l onto a new list,
+// and then replaces *l with that list.
+static void
+orderblock(NodeList **l)
+{
+	Order order;
+	NodeList *mark;
+	
+	memset(&order, 0, sizeof order);
+	mark = marktemp(&order);
+	orderstmtlist(*l, &order);
+	cleantemp(mark, &order);
+	*l = order.out;
+}
+
+// Orderexprinplace orders the side effects in *np and
+// leaves them as the init list of the final *np.
+static void
+orderexprinplace(Node **np, Order *outer)
+{
+	Node *n;
+	NodeList **lp;
+	Order order;
+	
+	n = *np;
+	memset(&order, 0, sizeof order);
+	orderexpr(&n, &order);
+	addinit(&n, order.out);
+	
+	// insert new temporaries from order
+	// at head of outer list.
+	lp = &order.temp;
+	while(*lp != nil)
+		lp = &(*lp)->next;
+	*lp = outer->temp;
+	outer->temp = order.temp;
+
+	*np = n;
+}
+
+// Orderstmtinplace orders the side effects of the single statement *np
+// and replaces it with the resulting statement list.
+void
+orderstmtinplace(Node **np)
+{
+	Node *n;
+	Order order;
+	NodeList *mark;
+	
+	n = *np;
+	memset(&order, 0, sizeof order);
+	mark = marktemp(&order);
+	orderstmt(n, &order);
+	cleantemp(mark, &order);
+	*np = liststmt(order.out);
+}
+
+// Orderinit moves n's init list to order->out.
+static void
+orderinit(Node *n, Order *order)
+{
+	orderstmtlist(n->ninit, order);
+	n->ninit = nil;
+}
+
+// Ismulticall reports whether the list l is f() for a multi-value function.
+// Such an f() could appear as the lone argument to a multi-arg function.
+static int
+ismulticall(NodeList *l)
+{
+	Node *n;
+	
+	// one arg only
+	if(l == nil || l->next != nil)
+		return 0;
+	n = l->n;
+	
+	// must be call
+	switch(n->op) {
+	default:
+		return 0;
+	case OCALLFUNC:
+	case OCALLMETH:
+	case OCALLINTER:
+		break;
+	}
+	
+	// call must return multiple values
+	return n->left->type->outtuple > 1;
+}
+
+// Copyret emits t1, t2, ... = n, where n is a function call,
+// and then returns the list t1, t2, ....
+static NodeList*
+copyret(Node *n, Order *order)
+{
+	Type *t;
+	Node *tmp, *as;
+	NodeList *l1, *l2;
+	Iter tl;
+	
+	if(n->type->etype != TSTRUCT || !n->type->funarg)
+		fatal("copyret %T %d", n->type, n->left->type->outtuple);
+
+	l1 = nil;
+	l2 = nil;
+	for(t=structfirst(&tl, &n->type); t; t=structnext(&tl)) {
+		tmp = temp(t->type);
+		l1 = list(l1, tmp);
+		l2 = list(l2, tmp);
+	}
+	
+	as = nod(OAS2, N, N);
+	as->list = l1;
+	as->rlist = list1(n);
+	typecheck(&as, Etop);
+	orderstmt(as, order);
+
+	return l2;
+}
+
+// Ordercallargs orders the list of call arguments *l.
+static void
+ordercallargs(NodeList **l, Order *order)
+{
+	if(ismulticall(*l)) {
+		// return f() where f() is multiple values.
+		*l = copyret((*l)->n, order);
+	} else {
+		orderexprlist(*l, order);
+	}
+}
+
+// Ordercall orders the call expression n.
+// n->op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY.
+static void
+ordercall(Node *n, Order *order)
+{
+	orderexpr(&n->left, order);
+	orderexpr(&n->right, order); // ODDDARG temp
+	ordercallargs(&n->list, order);
+}
+
+// Ordermapassign appends n to order->out, introducing temporaries
+// to make sure that all map assignments have the form m[k] = x,
+// where x is adressable.
+// (Orderexpr has already been called on n, so we know k is addressable.)
+//
+// If n is m[k] = x where x is not addressable, the rewrite is:
+//	tmp = x
+//	m[k] = tmp
+//
+// If n is the multiple assignment form ..., m[k], ... = ..., the rewrite is
+//	t1 = m
+//	t2 = k
+//	...., t3, ... = x
+//	t1[t2] = t3
+//
+// The temporaries t1, t2 are needed in case the ... being assigned
+// contain m or k. They are usually unnecessary, but in the unnecessary
+// cases they are also typically registerizable, so not much harm done.
+// And this only applies to the multiple-assignment form.
+// We could do a more precise analysis if needed, like in walk.c.
+//
+// Ordermapassign also inserts these temporaries if needed for
+// calling writebarrierfat with a pointer to n->right.
+static void
+ordermapassign(Node *n, Order *order)
+{
+	Node *m, *a;
+	NodeList *l;
+	NodeList *post;
+
+	switch(n->op) {
+	default:
+		fatal("ordermapassign %O", n->op);
+
+	case OAS:
+		order->out = list(order->out, n);
+		// We call writebarrierfat only for values > 4 pointers long. See walk.c.
+		if((n->left->op == OINDEXMAP || (needwritebarrier(n->left, n->right) && n->left->type->width > 4*widthptr)) && !isaddrokay(n->right)) {
+			m = n->left;
+			n->left = ordertemp(m->type, order, 0);
+			a = nod(OAS, m, n->left);
+			typecheck(&a, Etop);
+			order->out = list(order->out, a);
+		}
+		break;
+
+	case OAS2:
+	case OAS2DOTTYPE:
+	case OAS2MAPR:
+	case OAS2FUNC:
+		post = nil;
+		for(l=n->list; l != nil; l=l->next) {
+			if(l->n->op == OINDEXMAP) {
+				m = l->n;
+				if(!istemp(m->left))
+					m->left = ordercopyexpr(m->left, m->left->type, order, 0);
+				if(!istemp(m->right))
+					m->right = ordercopyexpr(m->right, m->right->type, order, 0);
+				l->n = ordertemp(m->type, order, 0);
+				a = nod(OAS, m, l->n);
+				typecheck(&a, Etop);
+				post = list(post, a);
+			}
+		}
+		order->out = list(order->out, n);
+		order->out = concat(order->out, post);
+		break;
+	}
+}
+
+// Orderstmt orders the statement n, appending to order->out.
+// Temporaries created during the statement are cleaned
+// up using VARKILL instructions as possible.
+static void
+orderstmt(Node *n, Order *order)
+{
+	int lno;
+	NodeList *l, *t, *t1;
+	Node *r, *tmp1, *tmp2, **np;
+	Type *ch;
+
+	if(n == N)
+		return;
+
+	lno = setlineno(n);
+
+	orderinit(n, order);
+
+	switch(n->op) {
+	default:
+		fatal("orderstmt %O", n->op);
+
+	case OVARKILL:
+		order->out = list(order->out, n);
+		break;
+
+	case OAS:
+	case OAS2:
+	case OAS2DOTTYPE:
+	case OCLOSE:
+	case OCOPY:
+	case OPRINT:
+	case OPRINTN:
+	case ORECOVER:
+	case ORECV:
+		t = marktemp(order);
+		orderexpr(&n->left, order);
+		orderexpr(&n->right, order);
+		orderexprlist(n->list, order);
+		orderexprlist(n->rlist, order);
+		switch(n->op) {
+		case OAS:
+		case OAS2:
+		case OAS2DOTTYPE:
+			ordermapassign(n, order);
+			break;
+		default:
+			order->out = list(order->out, n);
+			break;
+		}
+		cleantemp(t, order);
+		break;
+
+	case OASOP:
+		// Special: rewrite l op= r into l = l op r.
+		// This simplies quite a few operations;
+		// most important is that it lets us separate
+		// out map read from map write when l is
+		// a map index expression.
+		t = marktemp(order);
+		orderexpr(&n->left, order);
+		n->left = ordersafeexpr(n->left, order);
+		tmp1 = treecopy(n->left);
+		if(tmp1->op == OINDEXMAP)
+			tmp1->etype = 0; // now an rvalue not an lvalue
+		tmp1 = ordercopyexpr(tmp1, n->left->type, order, 0);
+		n->right = nod(n->etype, tmp1, n->right);
+		typecheck(&n->right, Erv);
+		orderexpr(&n->right, order);
+		n->etype = 0;
+		n->op = OAS;
+		ordermapassign(n, order);
+		cleantemp(t, order);
+		break;
+
+	case OAS2MAPR:
+		// Special: make sure key is addressable,
+		// and make sure OINDEXMAP is not copied out.
+		t = marktemp(order);
+		orderexprlist(n->list, order);
+		r = n->rlist->n;
+		orderexpr(&r->left, order);
+		orderexpr(&r->right, order);
+		// See case OINDEXMAP below.
+		if(r->right->op == OARRAYBYTESTR)
+			r->right->op = OARRAYBYTESTRTMP;
+		orderaddrtemp(&r->right, order);
+		ordermapassign(n, order);
+		cleantemp(t, order);
+		break;
+
+	case OAS2FUNC:
+		// Special: avoid copy of func call n->rlist->n.
+		t = marktemp(order);
+		orderexprlist(n->list, order);
+		ordercall(n->rlist->n, order);
+		ordermapassign(n, order);
+		cleantemp(t, order);
+		break;
+
+	case OAS2RECV:
+		// Special: avoid copy of receive.
+		// Use temporary variables to hold result,
+		// so that chanrecv can take address of temporary.
+		t = marktemp(order);
+		orderexprlist(n->list, order);
+		orderexpr(&n->rlist->n->left, order);  // arg to recv
+		ch = n->rlist->n->left->type;
+		tmp1 = ordertemp(ch->type, order, haspointers(ch->type));
+		if(!isblank(n->list->next->n))
+			tmp2 = ordertemp(n->list->next->n->type, order, 0);
+		else
+			tmp2 = ordertemp(types[TBOOL], order, 0);
+		order->out = list(order->out, n);
+		r = nod(OAS, n->list->n, tmp1);
+		typecheck(&r, Etop);
+		ordermapassign(r, order);
+		r = nod(OAS, n->list->next->n, tmp2);
+		typecheck(&r, Etop);
+		ordermapassign(r, order);
+		n->list = list(list1(tmp1), tmp2);
+		cleantemp(t, order);
+		break;
+
+	case OBLOCK:
+	case OEMPTY:
+		// Special: does not save n onto out.
+		orderstmtlist(n->list, order);
+		break;
+
+	case OBREAK:
+	case OCONTINUE:
+	case ODCL:
+	case ODCLCONST:
+	case ODCLTYPE:
+	case OFALL:
+	case OXFALL:
+	case OGOTO:
+	case OLABEL:
+	case ORETJMP:
+		// Special: n->left is not an expression; save as is.
+		order->out = list(order->out, n);
+		break;
+
+	case OCALLFUNC:
+	case OCALLINTER:
+	case OCALLMETH:
+		// Special: handle call arguments.
+		t = marktemp(order);
+		ordercall(n, order);
+		order->out = list(order->out, n);
+		cleantemp(t, order);
+		break;
+
+	case ODEFER:
+	case OPROC:
+		// Special: order arguments to inner call but not call itself.
+		t = marktemp(order);
+		switch(n->left->op) {
+		case ODELETE:
+			// Delete will take the address of the key.
+			// Copy key into new temp and do not clean it
+			// (it persists beyond the statement).
+			orderexprlist(n->left->list, order);
+			t1 = marktemp(order);
+			np = &n->left->list->next->n; // map key
+			*np = ordercopyexpr(*np, (*np)->type, order, 0);
+			poptemp(t1, order);
+			break;
+		default:
+			ordercall(n->left, order);
+			break;
+		}
+		order->out = list(order->out, n);
+		cleantemp(t, order);
+		break;
+
+	case ODELETE:
+		t = marktemp(order);
+		orderexpr(&n->list->n, order);
+		orderexpr(&n->list->next->n, order);
+		orderaddrtemp(&n->list->next->n, order); // map key
+		order->out = list(order->out, n);
+		cleantemp(t, order);
+		break;
+
+	case OFOR:
+		// Clean temporaries from condition evaluation at
+		// beginning of loop body and after for statement.
+		t = marktemp(order);
+		orderexprinplace(&n->ntest, order);
+		l = nil;
+		cleantempnopop(t, order, &l);
+		n->nbody = concat(l, n->nbody);
+		orderblock(&n->nbody);
+		orderstmtinplace(&n->nincr);
+		order->out = list(order->out, n);
+		cleantemp(t, order);
+		break;
+		
+	case OIF:
+		// Clean temporaries from condition at
+		// beginning of both branches.
+		t = marktemp(order);
+		orderexprinplace(&n->ntest, order);
+		l = nil;
+		cleantempnopop(t, order, &l);
+		n->nbody = concat(l, n->nbody);
+		l = nil;
+		cleantempnopop(t, order, &l);
+		n->nelse = concat(l, n->nelse);
+		poptemp(t, order);
+		orderblock(&n->nbody);
+		orderblock(&n->nelse);
+		order->out = list(order->out, n);
+		break;
+
+	case OPANIC:
+		// Special: argument will be converted to interface using convT2E
+		// so make sure it is an addressable temporary.
+		t = marktemp(order);
+		orderexpr(&n->left, order);
+		if(!isinter(n->left->type))
+			orderaddrtemp(&n->left, order);
+		order->out = list(order->out, n);
+		cleantemp(t, order);
+		break;
+
+	case ORANGE:
+		// n->right is the expression being ranged over.
+		// order it, and then make a copy if we need one.
+		// We almost always do, to ensure that we don't
+		// see any value changes made during the loop.
+		// Usually the copy is cheap (e.g., array pointer, chan, slice, string are all tiny).
+		// The exception is ranging over an array value (not a slice, not a pointer to array),
+		// which must make a copy to avoid seeing updates made during
+		// the range body. Ranging over an array value is uncommon though.
+		t = marktemp(order);
+		orderexpr(&n->right, order);
+		switch(n->type->etype) {
+		default:
+			fatal("orderstmt range %T", n->type);
+		case TARRAY:
+			if(count(n->list) < 2 || isblank(n->list->next->n)) {
+				// for i := range x will only use x once, to compute len(x).
+				// No need to copy it.
+				break;
+			}
+			// fall through
+		case TCHAN:
+		case TSTRING:
+			// chan, string, slice, array ranges use value multiple times.
+			// make copy.
+			r = n->right;
+			if(r->type->etype == TSTRING && r->type != types[TSTRING]) {
+				r = nod(OCONV, r, N);
+				r->type = types[TSTRING];
+				typecheck(&r, Erv);
+			}
+			n->right = ordercopyexpr(r, r->type, order, 0);
+			break;
+		case TMAP:
+			// copy the map value in case it is a map literal.
+			// TODO(rsc): Make tmp = literal expressions reuse tmp.
+			// For maps tmp is just one word so it hardly matters.
+			r = n->right;
+			n->right = ordercopyexpr(r, r->type, order, 0);
+			// n->alloc is the temp for the iterator.
+			n->alloc = ordertemp(types[TUINT8], order, 1);
+			break;
+		}
+		for(l=n->list; l; l=l->next)
+			orderexprinplace(&l->n, order);
+		orderblock(&n->nbody);
+		order->out = list(order->out, n);
+		cleantemp(t, order);
+		break;
+
+	case ORETURN:
+		ordercallargs(&n->list, order);
+		order->out = list(order->out, n);
+		break;
+	
+	case OSELECT:
+		// Special: clean case temporaries in each block entry.
+		// Select must enter one of its blocks, so there is no
+		// need for a cleaning at the end.
+		// Doubly special: evaluation order for select is stricter
+		// than ordinary expressions. Even something like p.c
+		// has to be hoisted into a temporary, so that it cannot be
+		// reordered after the channel evaluation for a different
+		// case (if p were nil, then the timing of the fault would
+		// give this away).
+		t = marktemp(order);
+		for(l=n->list; l; l=l->next) {
+			if(l->n->op != OXCASE)
+				fatal("order select case %O", l->n->op);
+			r = l->n->left;
+			setlineno(l->n);
+			// Append any new body prologue to ninit.
+			// The next loop will insert ninit into nbody.
+			if(l->n->ninit != nil)
+				fatal("order select ninit");
+			if(r != nil) {
+				switch(r->op) {
+				default:
+					yyerror("unknown op in select %O", r->op);
+					dump("select case", r);
+					break;
+
+				case OSELRECV:
+				case OSELRECV2:
+					// If this is case x := <-ch or case x, y := <-ch, the case has
+					// the ODCL nodes to declare x and y. We want to delay that
+					// declaration (and possible allocation) until inside the case body.
+					// Delete the ODCL nodes here and recreate them inside the body below.
+					if(r->colas) {
+						t = r->ninit;
+						if(t != nil && t->n->op == ODCL && t->n->left == r->left)
+							t = t->next;
+						if(t != nil && t->n->op == ODCL && t->n->left == r->ntest)
+							t = t->next;
+						if(t == nil)
+							r->ninit = nil;
+					}
+					if(r->ninit != nil) {
+						yyerror("ninit on select recv");
+						dumplist("ninit", r->ninit);
+					}
+					// case x = <-c
+					// case x, ok = <-c
+					// r->left is x, r->ntest is ok, r->right is ORECV, r->right->left is c.
+					// r->left == N means 'case <-c'.
+					// c is always evaluated; x and ok are only evaluated when assigned.
+					orderexpr(&r->right->left, order);
+					if(r->right->left->op != ONAME)
+						r->right->left = ordercopyexpr(r->right->left, r->right->left->type, order, 0);
+
+					// Introduce temporary for receive and move actual copy into case body.
+					// avoids problems with target being addressed, as usual.
+					// NOTE: If we wanted to be clever, we could arrange for just one
+					// temporary per distinct type, sharing the temp among all receives
+					// with that temp. Similarly one ok bool could be shared among all
+					// the x,ok receives. Not worth doing until there's a clear need.
+					if(r->left != N && isblank(r->left))
+						r->left = N;
+					if(r->left != N) {
+						// use channel element type for temporary to avoid conversions,
+						// such as in case interfacevalue = <-intchan.
+						// the conversion happens in the OAS instead.
+						tmp1 = r->left;
+						if(r->colas) {
+							tmp2 = nod(ODCL, tmp1, N);
+							typecheck(&tmp2, Etop);
+							l->n->ninit = list(l->n->ninit, tmp2);
+						}
+						r->left = ordertemp(r->right->left->type->type, order, haspointers(r->right->left->type->type));
+						tmp2 = nod(OAS, tmp1, r->left);
+						typecheck(&tmp2, Etop);
+						l->n->ninit = list(l->n->ninit, tmp2);
+					}
+					if(r->ntest != N && isblank(r->ntest))
+						r->ntest = N;
+					if(r->ntest != N) {
+						tmp1 = r->ntest;
+						if(r->colas) {
+							tmp2 = nod(ODCL, tmp1, N);
+							typecheck(&tmp2, Etop);
+							l->n->ninit = list(l->n->ninit, tmp2);
+						}
+						r->ntest = ordertemp(tmp1->type, order, 0);
+						tmp2 = nod(OAS, tmp1, r->ntest);
+						typecheck(&tmp2, Etop);
+						l->n->ninit = list(l->n->ninit, tmp2);
+					}
+					orderblock(&l->n->ninit);
+					break;
+
+				case OSEND:
+					if(r->ninit != nil) {
+						yyerror("ninit on select send");
+						dumplist("ninit", r->ninit);
+					}
+					// case c <- x
+					// r->left is c, r->right is x, both are always evaluated.
+					orderexpr(&r->left, order);
+					if(!istemp(r->left))
+						r->left = ordercopyexpr(r->left, r->left->type, order, 0);
+					orderexpr(&r->right, order);
+					if(!istemp(r->right))
+						r->right = ordercopyexpr(r->right, r->right->type, order, 0);
+					break;
+				}
+			}
+			orderblock(&l->n->nbody);
+		}
+		// Now that we have accumulated all the temporaries, clean them.
+		// Also insert any ninit queued during the previous loop.
+		// (The temporary cleaning must follow that ninit work.)
+		for(l=n->list; l; l=l->next) {
+			cleantempnopop(t, order, &l->n->ninit);
+			l->n->nbody = concat(l->n->ninit, l->n->nbody);
+			l->n->ninit = nil;
+		}
+		order->out = list(order->out, n);
+		poptemp(t, order);
+		break;
+
+	case OSEND:
+		// Special: value being sent is passed as a pointer; make it addressable.
+		t = marktemp(order);
+		orderexpr(&n->left, order);
+		orderexpr(&n->right, order);
+		orderaddrtemp(&n->right, order);
+		order->out = list(order->out, n);
+		cleantemp(t, order);
+		break;
+
+	case OSWITCH:
+		// TODO(rsc): Clean temporaries more aggressively.
+		// Note that because walkswitch will rewrite some of the
+		// switch into a binary search, this is not as easy as it looks.
+		// (If we ran that code here we could invoke orderstmt on
+		// the if-else chain instead.)
+		// For now just clean all the temporaries at the end.
+		// In practice that's fine.
+		t = marktemp(order);
+		orderexpr(&n->ntest, order);
+		for(l=n->list; l; l=l->next) {
+			if(l->n->op != OXCASE)
+				fatal("order switch case %O", l->n->op);
+			orderexprlistinplace(l->n->list, order);
+			orderblock(&l->n->nbody);
+		}
+		order->out = list(order->out, n);
+		cleantemp(t, order);
+		break;
+	}
+	
+	lineno = lno;
+}
+
+// Orderexprlist orders the expression list l into order.
+static void
+orderexprlist(NodeList *l, Order *order)
+{
+	for(; l; l=l->next)
+		orderexpr(&l->n, order);
+}
+
+// Orderexprlist orders the expression list l but saves
+// the side effects on the individual expression ninit lists.
+static void
+orderexprlistinplace(NodeList *l, Order *order)
+{
+	for(; l; l=l->next)
+		orderexprinplace(&l->n, order);
+}
+
+// Orderexpr orders a single expression, appending side
+// effects to order->out as needed.
+static void
+orderexpr(Node **np, Order *order)
+{
+	Node *n;
+	NodeList *mark, *l;
+	Type *t;
+	int lno;
+
+	n = *np;
+	if(n == N)
+		return;
+
+	lno = setlineno(n);
+	orderinit(n, order);
+
+	switch(n->op) {
+	default:
+		orderexpr(&n->left, order);
+		orderexpr(&n->right, order);
+		orderexprlist(n->list, order);
+		orderexprlist(n->rlist, order);
+		break;
+	
+	case OADDSTR:
+		// Addition of strings turns into a function call.
+		// Allocate a temporary to hold the strings.
+		// Fewer than 5 strings use direct runtime helpers.
+		orderexprlist(n->list, order);
+		if(count(n->list) > 5) {
+			t = typ(TARRAY);
+			t->bound = count(n->list);
+			t->type = types[TSTRING];
+			n->alloc = ordertemp(t, order, 0);
+		}
+		break;
+
+	case OINDEXMAP:
+		// key must be addressable
+		orderexpr(&n->left, order);
+		orderexpr(&n->right, order);
+
+		// For x = m[string(k)] where k is []byte, the allocation of
+		// backing bytes for the string can be avoided by reusing
+		// the []byte backing array. This is a special case that it
+		// would be nice to handle more generally, but because
+		// there are no []byte-keyed maps, this specific case comes
+		// up in important cases in practice. See issue 3512.
+		// Nothing can change the []byte we are not copying before
+		// the map index, because the map access is going to
+		// be forced to happen immediately following this
+		// conversion (by the ordercopyexpr a few lines below).
+		if(n->etype == 0 && n->right->op == OARRAYBYTESTR)
+			n->right->op = OARRAYBYTESTRTMP;
+
+		orderaddrtemp(&n->right, order);
+		if(n->etype == 0) {
+			// use of value (not being assigned);
+			// make copy in temporary.
+			n = ordercopyexpr(n, n->type, order, 0);
+		}
+		break;
+	
+	case OCONVIFACE:
+		// concrete type (not interface) argument must be addressable
+		// temporary to pass to runtime.
+		orderexpr(&n->left, order);
+		if(!isinter(n->left->type))
+			orderaddrtemp(&n->left, order);
+		break;
+	
+	case OANDAND:
+	case OOROR:
+		mark = marktemp(order);
+		orderexpr(&n->left, order);
+		// Clean temporaries from first branch at beginning of second.
+		// Leave them on the stack so that they can be killed in the outer
+		// context in case the short circuit is taken.
+		l = nil;
+		cleantempnopop(mark, order, &l);
+		n->right->ninit = concat(l, n->right->ninit);
+		orderexprinplace(&n->right, order);
+		break;
+	
+	case OAPPEND:
+	case OCALLFUNC:
+	case OCALLINTER:
+	case OCALLMETH:
+	case OCAP:
+	case OCOMPLEX:
+	case OCOPY:
+	case OIMAG:
+	case OLEN:
+	case OMAKECHAN:
+	case OMAKEMAP:
+	case OMAKESLICE:
+	case ONEW:
+	case OREAL:
+	case ORECOVER:
+		ordercall(n, order);
+		n = ordercopyexpr(n, n->type, order, 0);
+		break;
+
+	case OCLOSURE:
+		if(n->noescape && n->cvars != nil)
+			n->alloc = ordertemp(types[TUINT8], order, 0); // walk will fill in correct type
+		break;
+
+	case OARRAYLIT:
+	case OCALLPART:
+		orderexpr(&n->left, order);
+		orderexpr(&n->right, order);
+		orderexprlist(n->list, order);
+		orderexprlist(n->rlist, order);
+		if(n->noescape)
+			n->alloc = ordertemp(types[TUINT8], order, 0); // walk will fill in correct type
+		break;
+
+	case ODDDARG:
+		if(n->noescape) {
+			// The ddd argument does not live beyond the call it is created for.
+			// Allocate a temporary that will be cleaned up when this statement
+			// completes. We could be more aggressive and try to arrange for it
+			// to be cleaned up when the call completes.
+			n->alloc = ordertemp(n->type->type, order, 0);
+		}
+		break;
+
+	case ORECV:
+		orderexpr(&n->left, order);
+		n = ordercopyexpr(n, n->type, order, 1);
+		break;
+
+	case OEQ:
+	case ONE:
+		orderexpr(&n->left, order);
+		orderexpr(&n->right, order);
+		t = n->left->type;
+		if(t->etype == TSTRUCT || isfixedarray(t)) {
+			// for complex comparisons, we need both args to be
+			// addressable so we can pass them to the runtime.
+			orderaddrtemp(&n->left, order);
+			orderaddrtemp(&n->right, order);
+		}
+		break;
+	}
+	
+	lineno = lno;
+
+	*np = n;
+}
diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c
new file mode 100644
index 0000000..39028e3
--- /dev/null
+++ b/src/cmd/gc/pgen.c
@@ -0,0 +1,539 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// "Portable" code generation.
+// Compiled separately for 5g, 6g, and 8g, so allowed to use gg.h, opt.h.
+// Must code to the intersection of the three back ends.
+
+#include	<u.h>
+#include	<libc.h>
+#include	"md5.h"
+#include	"gg.h"
+#include	"opt.h"
+#include	"../../runtime/funcdata.h"
+
+static void allocauto(Prog* p);
+static void emitptrargsmap(void);
+
+static Sym*
+makefuncdatasym(char *namefmt, int64 funcdatakind)
+{
+	Node nod;
+	Node *pnod;
+	Sym *sym;
+	static int32 nsym;
+
+	snprint(namebuf, sizeof(namebuf), namefmt, nsym++);
+	sym = lookup(namebuf);
+	pnod = newname(sym);
+	pnod->class = PEXTERN;
+	nodconst(&nod, types[TINT32], funcdatakind);
+	gins(AFUNCDATA, &nod, pnod);
+	return sym;
+}
+
+// gvardef inserts a VARDEF for n into the instruction stream.
+// VARDEF is an annotation for the liveness analysis, marking a place
+// where a complete initialization (definition) of a variable begins.
+// Since the liveness analysis can see initialization of single-word
+// variables quite easy, gvardef is usually only called for multi-word
+// or 'fat' variables, those satisfying isfat(n->type).
+// However, gvardef is also called when a non-fat variable is initialized
+// via a block move; the only time this happens is when you have
+//	return f()
+// for a function with multiple return values exactly matching the return
+// types of the current function.
+//
+// A 'VARDEF x' annotation in the instruction stream tells the liveness
+// analysis to behave as though the variable x is being initialized at that
+// point in the instruction stream. The VARDEF must appear before the
+// actual (multi-instruction) initialization, and it must also appear after
+// any uses of the previous value, if any. For example, if compiling:
+//
+//	x = x[1:]
+//
+// it is important to generate code like:
+//
+//	base, len, cap = pieces of x[1:]
+//	VARDEF x
+//	x = {base, len, cap}
+//
+// If instead the generated code looked like:
+//
+//	VARDEF x
+//	base, len, cap = pieces of x[1:]
+//	x = {base, len, cap}
+//
+// then the liveness analysis would decide the previous value of x was
+// unnecessary even though it is about to be used by the x[1:] computation.
+// Similarly, if the generated code looked like:
+//
+//	base, len, cap = pieces of x[1:]
+//	x = {base, len, cap}
+//	VARDEF x
+//
+// then the liveness analysis will not preserve the new value of x, because
+// the VARDEF appears to have "overwritten" it.
+//
+// VARDEF is a bit of a kludge to work around the fact that the instruction
+// stream is working on single-word values but the liveness analysis
+// wants to work on individual variables, which might be multi-word
+// aggregates. It might make sense at some point to look into letting
+// the liveness analysis work on single-word values as well, although
+// there are complications around interface values, slices, and strings,
+// all of which cannot be treated as individual words.
+//
+// VARKILL is the opposite of VARDEF: it marks a value as no longer needed,
+// even if its address has been taken. That is, a VARKILL annotation asserts
+// that its argument is certainly dead, for use when the liveness analysis
+// would not otherwise be able to deduce that fact.
+
+static void
+gvardefx(Node *n, int as)
+{
+	if(n == N)
+		fatal("gvardef nil");
+	if(n->op != ONAME) {
+		yyerror("gvardef %#O; %N", n->op, n);
+		return;
+	}
+	switch(n->class) {
+	case PAUTO:
+	case PPARAM:
+	case PPARAMOUT:
+		gins(as, N, n);
+	}
+}
+
+void
+gvardef(Node *n)
+{
+	gvardefx(n, AVARDEF);
+}
+
+void
+gvarkill(Node *n)
+{
+	gvardefx(n, AVARKILL);
+}
+
+static void
+removevardef(Prog *firstp)
+{
+	Prog *p;
+
+	for(p = firstp; p != P; p = p->link) {
+		while(p->link != P && (p->link->as == AVARDEF || p->link->as == AVARKILL))
+			p->link = p->link->link;
+		if(p->to.type == D_BRANCH)
+			while(p->to.u.branch != P && (p->to.u.branch->as == AVARDEF || p->to.u.branch->as == AVARKILL))
+				p->to.u.branch = p->to.u.branch->link;
+	}
+}
+
+static void
+gcsymdup(Sym *s)
+{
+	LSym *ls;
+	uint64 lo, hi;
+	
+	ls = linksym(s);
+	if(ls->nr > 0)
+		fatal("cannot rosymdup %s with relocations", ls->name);
+	MD5 d;
+	md5reset(&d);
+	md5write(&d, ls->p, ls->np);
+	lo = md5sum(&d, &hi);
+	ls->name = smprint("gclocals·%016llux%016llux", lo, hi);
+	ls->dupok = 1;
+}
+
+void
+compile(Node *fn)
+{
+	Plist *pl;
+	Node nod1, *n;
+	Prog *ptxt, *p;
+	int32 lno;
+	Type *t;
+	Iter save;
+	vlong oldstksize;
+	NodeList *l;
+	Sym *gcargs;
+	Sym *gclocals;
+
+	if(newproc == N) {
+		newproc = sysfunc("newproc");
+		deferproc = sysfunc("deferproc");
+		deferreturn = sysfunc("deferreturn");
+		panicindex = sysfunc("panicindex");
+		panicslice = sysfunc("panicslice");
+		throwreturn = sysfunc("throwreturn");
+	}
+
+	lno = setlineno(fn);
+
+	curfn = fn;
+	dowidth(curfn->type);
+
+	if(fn->nbody == nil) {
+		if(pure_go || strncmp(fn->nname->sym->name, "init·", 6) == 0) {
+			yyerror("missing function body", fn);
+			goto ret;
+		}
+		if(debug['A'])
+			goto ret;
+		emitptrargsmap();
+		goto ret;
+	}
+
+	saveerrors();
+
+	// set up domain for labels
+	clearlabels();
+
+	if(curfn->type->outnamed) {
+		// add clearing of the output parameters
+		t = structfirst(&save, getoutarg(curfn->type));
+		while(t != T) {
+			if(t->nname != N) {
+				n = nod(OAS, t->nname, N);
+				typecheck(&n, Etop);
+				curfn->nbody = concat(list1(n), curfn->nbody);
+			}
+			t = structnext(&save);
+		}
+	}
+	
+	order(curfn);
+	if(nerrors != 0)
+		goto ret;
+	
+	hasdefer = 0;
+	walk(curfn);
+	if(nerrors != 0)
+		goto ret;
+	if(flag_race)
+		racewalk(curfn);
+	if(nerrors != 0)
+		goto ret;
+
+	continpc = P;
+	breakpc = P;
+
+	pl = newplist();
+	pl->name = linksym(curfn->nname->sym);
+
+	setlineno(curfn);
+
+	nodconst(&nod1, types[TINT32], 0);
+	ptxt = gins(ATEXT, isblank(curfn->nname) ? N : curfn->nname, &nod1);
+	if(fn->dupok)
+		ptxt->TEXTFLAG |= DUPOK;
+	if(fn->wrapper)
+		ptxt->TEXTFLAG |= WRAPPER;
+	if(fn->needctxt)
+		ptxt->TEXTFLAG |= NEEDCTXT;
+	if(fn->nosplit)
+		ptxt->TEXTFLAG |= NOSPLIT;
+
+	// Clumsy but important.
+	// See test/recover.go for test cases and src/reflect/value.go
+	// for the actual functions being considered.
+	if(myimportpath != nil && strcmp(myimportpath, "reflect") == 0) {
+		if(strcmp(curfn->nname->sym->name, "callReflect") == 0 || strcmp(curfn->nname->sym->name, "callMethod") == 0)
+			ptxt->TEXTFLAG |= WRAPPER;
+	}	
+	
+	afunclit(&ptxt->from, curfn->nname);
+
+	ginit();
+
+	gcargs = makefuncdatasym("gcargs·%d", FUNCDATA_ArgsPointerMaps);
+	gclocals = makefuncdatasym("gclocals·%d", FUNCDATA_LocalsPointerMaps);
+
+	for(t=curfn->paramfld; t; t=t->down)
+		gtrack(tracksym(t->type));
+
+	for(l=fn->dcl; l; l=l->next) {
+		n = l->n;
+		if(n->op != ONAME) // might be OTYPE or OLITERAL
+			continue;
+		switch(n->class) {
+		case PAUTO:
+		case PPARAM:
+		case PPARAMOUT:
+			nodconst(&nod1, types[TUINTPTR], l->n->type->width);
+			p = gins(ATYPE, l->n, &nod1);
+			p->from.gotype = linksym(ngotype(l->n));
+			break;
+		}
+	}
+
+	genlist(curfn->enter);
+	genlist(curfn->nbody);
+	gclean();
+	checklabels();
+	if(nerrors != 0)
+		goto ret;
+	if(curfn->endlineno)
+		lineno = curfn->endlineno;
+
+	if(curfn->type->outtuple != 0)
+		ginscall(throwreturn, 0);
+
+	ginit();
+	// TODO: Determine when the final cgen_ret can be omitted. Perhaps always?
+	cgen_ret(nil);
+	if(hasdefer) {
+		// deferreturn pretends to have one uintptr argument.
+		// Reserve space for it so stack scanner is happy.
+		if(maxarg < widthptr)
+			maxarg = widthptr;
+	}
+	gclean();
+	if(nerrors != 0)
+		goto ret;
+
+	pc->as = ARET;	// overwrite AEND
+	pc->lineno = lineno;
+
+	fixjmp(ptxt);
+	if(!debug['N'] || debug['R'] || debug['P']) {
+		regopt(ptxt);
+		nilopt(ptxt);
+	}
+	expandchecks(ptxt);
+
+	oldstksize = stksize;
+	allocauto(ptxt);
+
+	if(0)
+		print("allocauto: %lld to %lld\n", oldstksize, (vlong)stksize);
+	USED(oldstksize);
+
+	setlineno(curfn);
+	if((int64)stksize+maxarg > (1ULL<<31)) {
+		yyerror("stack frame too large (>2GB)");
+		goto ret;
+	}
+
+	// Emit garbage collection symbols.
+	liveness(curfn, ptxt, gcargs, gclocals);
+	gcsymdup(gcargs);
+	gcsymdup(gclocals);
+
+	defframe(ptxt);
+
+	if(0)
+		frame(0);
+
+	// Remove leftover instrumentation from the instruction stream.
+	removevardef(ptxt);
+ret:
+	lineno = lno;
+}
+
+static void
+emitptrargsmap(void)
+{
+	int nptr, nbitmap, j, off;
+	vlong xoffset;
+	Bvec *bv;
+	Sym *sym;
+	
+	sym = lookup(smprint("%s.args_stackmap", curfn->nname->sym->name));
+
+	nptr = curfn->type->argwid / widthptr;
+	bv = bvalloc(nptr*2);
+	nbitmap = 1;
+	if(curfn->type->outtuple > 0)
+		nbitmap = 2;
+	off = duint32(sym, 0, nbitmap);
+	off = duint32(sym, off, bv->n);
+	if(curfn->type->thistuple > 0) {
+		xoffset = 0;
+		twobitwalktype1(getthisx(curfn->type), &xoffset, bv);
+	}
+	if(curfn->type->intuple > 0) {
+		xoffset = 0;
+		twobitwalktype1(getinargx(curfn->type), &xoffset, bv);
+	}
+	for(j = 0; j < bv->n; j += 32)
+		off = duint32(sym, off, bv->b[j/32]);
+	if(curfn->type->outtuple > 0) {
+		xoffset = 0;
+		twobitwalktype1(getoutargx(curfn->type), &xoffset, bv);
+		for(j = 0; j < bv->n; j += 32)
+			off = duint32(sym, off, bv->b[j/32]);
+	}
+	ggloblsym(sym, off, RODATA);
+	free(bv);
+}
+
+// Sort the list of stack variables. Autos after anything else,
+// within autos, unused after used, within used, things with
+// pointers first, zeroed things first, and then decreasing size.
+// Because autos are laid out in decreasing addresses
+// on the stack, pointers first, zeroed things first and decreasing size
+// really means, in memory, things with pointers needing zeroing at
+// the top of the stack and increasing in size.
+// Non-autos sort on offset.
+static int
+cmpstackvar(Node *a, Node *b)
+{
+	int ap, bp;
+
+	if (a->class != b->class)
+		return (a->class == PAUTO) ? +1 : -1;
+	if (a->class != PAUTO) {
+		if (a->xoffset < b->xoffset)
+			return -1;
+		if (a->xoffset > b->xoffset)
+			return +1;
+		return 0;
+	}
+	if ((a->used == 0) != (b->used == 0))
+		return b->used - a->used;
+
+	ap = haspointers(a->type);
+	bp = haspointers(b->type);
+	if(ap != bp)
+		return bp - ap;
+
+	ap = a->needzero;
+	bp = b->needzero;
+	if(ap != bp)
+		return bp - ap;
+
+	if(a->type->width < b->type->width)
+		return +1;
+	if(a->type->width > b->type->width)
+		return -1;
+
+	return strcmp(a->sym->name, b->sym->name);
+}
+
+// TODO(lvd) find out where the PAUTO/OLITERAL nodes come from.
+static void
+allocauto(Prog* ptxt)
+{
+	NodeList *ll;
+	Node* n;
+	vlong w;
+
+	stksize = 0;
+	stkptrsize = 0;
+
+	if(curfn->dcl == nil)
+		return;
+
+	// Mark the PAUTO's unused.
+	for(ll=curfn->dcl; ll != nil; ll=ll->next)
+		if (ll->n->class == PAUTO)
+			ll->n->used = 0;
+
+	markautoused(ptxt);
+
+	listsort(&curfn->dcl, cmpstackvar);
+
+	// Unused autos are at the end, chop 'em off.
+	ll = curfn->dcl;
+	n = ll->n;
+	if (n->class == PAUTO && n->op == ONAME && !n->used) {
+		// No locals used at all
+		curfn->dcl = nil;
+		fixautoused(ptxt);
+		return;
+	}
+
+	for(ll = curfn->dcl; ll->next != nil; ll=ll->next) {
+		n = ll->next->n;
+		if (n->class == PAUTO && n->op == ONAME && !n->used) {
+			ll->next = nil;
+			curfn->dcl->end = ll;
+			break;
+		}
+	}
+
+	// Reassign stack offsets of the locals that are still there.
+	for(ll = curfn->dcl; ll != nil; ll=ll->next) {
+		n = ll->n;
+		if (n->class != PAUTO || n->op != ONAME)
+			continue;
+
+		dowidth(n->type);
+		w = n->type->width;
+		if(w >= MAXWIDTH || w < 0)
+			fatal("bad width");
+		stksize += w;
+		stksize = rnd(stksize, n->type->align);
+		if(haspointers(n->type))
+			stkptrsize = stksize;
+		if(thechar == '5')
+			stksize = rnd(stksize, widthptr);
+		if(stksize >= (1ULL<<31)) {
+			setlineno(curfn);
+			yyerror("stack frame too large (>2GB)");
+		}
+		n->stkdelta = -stksize - n->xoffset;
+	}
+	stksize = rnd(stksize, widthreg);
+	stkptrsize = rnd(stkptrsize, widthreg);
+
+	fixautoused(ptxt);
+
+	// The debug information needs accurate offsets on the symbols.
+	for(ll = curfn->dcl; ll != nil; ll=ll->next) {
+		if (ll->n->class != PAUTO || ll->n->op != ONAME)
+			continue;
+		ll->n->xoffset += ll->n->stkdelta;
+		ll->n->stkdelta = 0;
+	}
+}
+
+static void movelargefn(Node*);
+
+void
+movelarge(NodeList *l)
+{
+	for(; l; l=l->next)
+		if(l->n->op == ODCLFUNC)
+			movelargefn(l->n);
+}
+
+static void
+movelargefn(Node *fn)
+{
+	NodeList *l;
+	Node *n;
+
+	for(l=fn->dcl; l != nil; l=l->next) {
+		n = l->n;
+		if(n->class == PAUTO && n->type != T && n->type->width > MaxStackVarSize)
+			addrescapes(n);
+	}
+}
+
+void
+cgen_checknil(Node *n)
+{
+	Node reg;
+
+	if(disable_checknil)
+		return;
+	// Ideally we wouldn't see any integer types here, but we do.
+	if(n->type == T || (!isptr[n->type->etype] && !isint[n->type->etype] && n->type->etype != TUNSAFEPTR)) {
+		dump("checknil", n);
+		fatal("bad checknil");
+	}
+	if((thechar == '5' && n->op != OREGISTER) || !n->addable || n->op == OLITERAL) {
+		regalloc(&reg, types[tptr], n);
+		cgen(n, &reg);
+		gins(ACHECKNIL, &reg, N);
+		regfree(&reg);
+		return;
+	}
+	gins(ACHECKNIL, n, N);
+}
diff --git a/src/cmd/gc/plive.c b/src/cmd/gc/plive.c
new file mode 100644
index 0000000..0feb2c7
--- /dev/null
+++ b/src/cmd/gc/plive.c
@@ -0,0 +1,2017 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Garbage collector liveness bitmap generation.
+
+// The command line flag -live causes this code to print debug information.
+// The levels are:
+//
+//	-live (aka -live=1): print liveness lists as code warnings at safe points
+//	-live=2: print an assembly listing with liveness annotations
+//	-live=3: print information during each computation phase (much chattier)
+//
+// Each level includes the earlier output as well.
+
+#include <u.h>
+#include <libc.h>
+#include "gg.h"
+#include "opt.h"
+#include "../ld/textflag.h"
+#include "../../runtime/funcdata.h"
+#include "../../runtime/mgc0.h"
+
+enum {
+	UNVISITED = 0,
+	VISITED = 1,
+};
+
+// An ordinary basic block.
+//
+// Instructions are threaded together in a doubly-linked list.  To iterate in
+// program order follow the link pointer from the first node and stop after the
+// last node has been visited
+//
+//   for(p = bb->first;; p = p->link) {
+//     ...
+//     if(p == bb->last)
+//       break;
+//   }
+//
+// To iterate in reverse program order by following the opt pointer from the
+// last node
+//
+//   for(p = bb->last; p != nil; p = p->opt) {
+//     ...
+//   }
+typedef struct BasicBlock BasicBlock;
+struct BasicBlock {
+	// An array of preceding blocks.  If the length of this array is 0 the
+	// block is probably the start block of the CFG.
+	Array *pred;
+
+	// An array out succeeding blocks.  If the length of this array is zero,
+	// the block probably ends in a return instruction.
+	Array *succ;
+
+	// First instruction in the block.  When part of a fully initialized
+	// control flow graph, the opt member will be nil.
+	Prog *first;
+
+	// Last instruction in the basic block.
+	Prog *last;
+
+	// The reverse post order number.  This value is initialized to -1 and
+	// will be replaced by a non-negative value when the CFG is constructed.
+	// After CFG construction, if rpo is -1 this block is unreachable.
+	int rpo;
+
+	// State to denote whether the block has been visited during a
+	// traversal.
+	int mark;
+	
+	// For use during livenessepilogue.
+	int lastbitmapindex;
+};
+
+// A collection of global state used by liveness analysis.
+typedef struct Liveness Liveness;
+struct Liveness {
+	// A pointer to the node corresponding to the function being analyzed.
+	Node *fn;
+
+	// A linked list of instructions for this function.
+	Prog *ptxt;
+
+	// A list of arguments and local variables in this function.
+	Array *vars;
+
+	// A list of basic blocks that are overlayed on the instruction list.
+	// The blocks are roughly in the same order as the instructions
+	// in the function (first block has TEXT instruction, and so on).
+	Array *cfg;
+
+	// Summary sets of block effects.
+	// The Bvec** is indexed by bb->rpo to yield a single Bvec*.
+	// That bit vector is indexed by variable number (same as lv->vars).
+	//
+	// Computed during livenessprologue using only the content of
+	// individual blocks:
+	//
+	//	uevar: upward exposed variables (used before set in block)
+	//	varkill: killed variables (set in block)
+	//	avarinit: addrtaken variables set or used (proof of initialization)
+	//
+	// Computed during livenesssolve using control flow information:
+	//
+	//	livein: variables live at block entry
+	//	liveout: variables live at block exit
+	//	avarinitany: addrtaken variables possibly initialized at block exit
+	//		(initialized in block or at exit from any predecessor block)
+	//	avarinitall: addrtaken variables certainly initialized at block exit
+	//		(initialized in block or at exit from all predecessor blocks)
+	Bvec **uevar;
+	Bvec **varkill;
+	Bvec **livein;
+	Bvec **liveout;
+	Bvec **avarinit;
+	Bvec **avarinitany;
+	Bvec **avarinitall;
+
+	// An array with a bit vector for each safe point tracking live pointers
+	// in the arguments and locals area, indexed by bb->rpo.
+	Array *argslivepointers;
+	Array *livepointers;
+};
+
+static void*
+xmalloc(uintptr size)
+{
+	void *result;
+
+	result = malloc(size);
+	if(result == nil)
+		fatal("malloc failed");
+	return result;
+}
+
+// Constructs a new basic block containing a single instruction.
+static BasicBlock*
+newblock(Prog *prog)
+{
+	BasicBlock *result;
+
+	if(prog == nil)
+		fatal("newblock: prog cannot be nil");
+	result = xmalloc(sizeof(*result));
+	result->rpo = -1;
+	result->mark = UNVISITED;
+	result->first = prog;
+	result->last = prog;
+	result->pred = arraynew(2, sizeof(BasicBlock*));
+	result->succ = arraynew(2, sizeof(BasicBlock*));
+	return result;
+}
+
+// Frees a basic block and all of its leaf data structures.
+static void
+freeblock(BasicBlock *bb)
+{
+	if(bb == nil)
+		fatal("freeblock: cannot free nil");
+	arrayfree(bb->pred);
+	arrayfree(bb->succ);
+	free(bb);
+}
+
+// Adds an edge between two basic blocks by making from a predecessor of to and
+// to a successor of from.
+static void
+addedge(BasicBlock *from, BasicBlock *to)
+{
+	if(from == nil)
+		fatal("addedge: from is nil");
+	if(to == nil)
+		fatal("addedge: to is nil");
+	arrayadd(from->succ, &to);
+	arrayadd(to->pred, &from);
+}
+
+// Inserts prev before curr in the instruction
+// stream.  Any control flow, such as branches or fall throughs, that target the
+// existing instruction are adjusted to target the new instruction.
+static void
+splicebefore(Liveness *lv, BasicBlock *bb, Prog *prev, Prog *curr)
+{
+	Prog *next, tmp;
+
+	USED(lv);
+
+	// There may be other instructions pointing at curr,
+	// and we want them to now point at prev. Instead of
+	// trying to find all such instructions, swap the contents
+	// so that the problem becomes inserting next after curr.
+	// The "opt" field is the backward link in the linked list.
+
+	// Overwrite curr's data with prev, but keep the list links.
+	tmp = *curr;
+	*curr = *prev;
+	curr->opt = tmp.opt;
+	curr->link = tmp.link;
+	
+	// Overwrite prev (now next) with curr's old data.
+	next = prev;
+	*next = tmp;
+	next->opt = nil;
+	next->link = nil;
+
+	// Now insert next after curr.
+	next->link = curr->link;
+	next->opt = curr;
+	curr->link = next;
+	if(next->link && next->link->opt == curr)
+		next->link->opt = next;
+
+	if(bb->last == curr)
+		bb->last = next;
+}
+
+// A pretty printer for basic blocks.
+static void
+printblock(BasicBlock *bb)
+{
+	BasicBlock *pred;
+	BasicBlock *succ;
+	Prog *prog;
+	int i;
+
+	print("basic block %d\n", bb->rpo);
+	print("\tpred:");
+	for(i = 0; i < arraylength(bb->pred); i++) {
+		pred = *(BasicBlock**)arrayget(bb->pred, i);
+		print(" %d", pred->rpo);
+	}
+	print("\n");
+	print("\tsucc:");
+	for(i = 0; i < arraylength(bb->succ); i++) {
+		succ = *(BasicBlock**)arrayget(bb->succ, i);
+		print(" %d", succ->rpo);
+	}
+	print("\n");
+	print("\tprog:\n");
+	for(prog = bb->first;; prog=prog->link) {
+		print("\t\t%P\n", prog);
+		if(prog == bb->last)
+			break;
+	}
+}
+
+
+// Iterates over a basic block applying a callback to each instruction.  There
+// are two criteria for termination.  If the end of basic block is reached a
+// value of zero is returned.  If the callback returns a non-zero value, the
+// iteration is stopped and the value of the callback is returned.
+static int
+blockany(BasicBlock *bb, int (*callback)(Prog*))
+{
+	Prog *p;
+	int result;
+
+	for(p = bb->last; p != nil; p = p->opt) {
+		result = (*callback)(p);
+		if(result != 0)
+			return result;
+	}
+	return 0;
+}
+
+// Collects and returns and array of Node*s for functions arguments and local
+// variables.
+static Array*
+getvariables(Node *fn)
+{
+	Array *result;
+	NodeList *ll;
+
+	result = arraynew(0, sizeof(Node*));
+	for(ll = fn->dcl; ll != nil; ll = ll->next) {
+		if(ll->n->op == ONAME) {
+			// In order for GODEBUG=gcdead=1 to work, each bitmap needs
+			// to contain information about all variables covered by the bitmap.
+			// For local variables, the bitmap only covers the stkptrsize
+			// bytes in the frame where variables containing pointers live.
+			// For arguments and results, the bitmap covers all variables,
+			// so we must include all the variables, even the ones without
+			// pointers.
+			//
+			// The Node.opt field is available for use by optimization passes.
+			// We use it to hold the index of the node in the variables array, plus 1
+			// (so that 0 means the Node is not in the variables array).
+			// Each pass should clear opt when done, but you never know,
+			// so clear them all ourselves too.
+			// The Node.curfn field is supposed to be set to the current function
+			// already, but for some compiler-introduced names it seems not to be,
+			// so fix that here.
+			// Later, when we want to find the index of a node in the variables list,
+			// we will check that n->curfn == curfn and n->opt > 0. Then n->opt - 1
+			// is the index in the variables list.
+			ll->n->opt = nil;
+			ll->n->curfn = curfn;
+			switch(ll->n->class) {
+			case PAUTO:
+				if(haspointers(ll->n->type)) {
+					ll->n->opt = (void*)(uintptr)(arraylength(result)+1);
+					arrayadd(result, &ll->n);
+				}
+				break;
+			case PPARAM:
+			case PPARAMOUT:
+				ll->n->opt = (void*)(uintptr)(arraylength(result)+1);
+				arrayadd(result, &ll->n);
+				break;
+			}
+		}
+	}
+	return result;
+}
+
+// A pretty printer for control flow graphs.  Takes an array of BasicBlock*s.
+static void
+printcfg(Array *cfg)
+{
+	BasicBlock *bb;
+	int32 i;
+
+	for(i = 0; i < arraylength(cfg); i++) {
+		bb = *(BasicBlock**)arrayget(cfg, i);
+		printblock(bb);
+	}
+}
+
+// Assigns a reverse post order number to each connected basic block using the
+// standard algorithm.  Unconnected blocks will not be affected.
+static void
+reversepostorder(BasicBlock *root, int32 *rpo)
+{
+	BasicBlock *bb;
+	int i;
+
+	root->mark = VISITED;
+	for(i = 0; i < arraylength(root->succ); i++) {
+		bb = *(BasicBlock**)arrayget(root->succ, i);
+		if(bb->mark == UNVISITED)
+			reversepostorder(bb, rpo);
+	}
+	*rpo -= 1;
+	root->rpo = *rpo;
+}
+
+// Comparison predicate used for sorting basic blocks by their rpo in ascending
+// order.
+static int
+blockrpocmp(const void *p1, const void *p2)
+{
+	BasicBlock *bb1;
+	BasicBlock *bb2;
+
+	bb1 = *(BasicBlock**)p1;
+	bb2 = *(BasicBlock**)p2;
+	if(bb1->rpo < bb2->rpo)
+		return -1;
+	if(bb1->rpo > bb2->rpo)
+		return 1;
+	return 0;
+}
+
+// A pattern matcher for call instructions.  Returns true when the instruction
+// is a call to a specific package qualified function name.
+static int
+iscall(Prog *prog, LSym *name)
+{
+	if(prog == nil)
+		fatal("iscall: prog is nil");
+	if(name == nil)
+		fatal("iscall: function name is nil");
+	if(prog->as != ACALL)
+		return 0;
+	return name == prog->to.sym;
+}
+
+// Returns true for instructions that call a runtime function implementing a
+// select communication clause.
+static int
+isselectcommcasecall(Prog *prog)
+{
+	static LSym* names[5];
+	int32 i;
+
+	if(names[0] == nil) {
+		names[0] = linksym(pkglookup("selectsend", runtimepkg));
+		names[1] = linksym(pkglookup("selectrecv", runtimepkg));
+		names[2] = linksym(pkglookup("selectrecv2", runtimepkg));
+		names[3] = linksym(pkglookup("selectdefault", runtimepkg));
+	}
+	for(i = 0; names[i] != nil; i++)
+		if(iscall(prog, names[i]))
+			return 1;
+	return 0;
+}
+
+// Returns true for call instructions that target runtime·newselect.
+static int
+isnewselect(Prog *prog)
+{
+	static LSym *sym;
+
+	if(sym == nil)
+		sym = linksym(pkglookup("newselect", runtimepkg));
+	return iscall(prog, sym);
+}
+
+// Returns true for call instructions that target runtime·selectgo.
+static int
+isselectgocall(Prog *prog)
+{
+	static LSym *sym;
+
+	if(sym == nil)
+		sym = linksym(pkglookup("selectgo", runtimepkg));
+	return iscall(prog, sym);
+}
+
+static int
+isdeferreturn(Prog *prog)
+{
+	static LSym *sym;
+
+	if(sym == nil)
+		sym = linksym(pkglookup("deferreturn", runtimepkg));
+	return iscall(prog, sym);
+}
+
+// Walk backwards from a runtime·selectgo call up to its immediately dominating
+// runtime·newselect call.  Any successor nodes of communication clause nodes
+// are implicit successors of the runtime·selectgo call node.  The goal of this
+// analysis is to add these missing edges to complete the control flow graph.
+static void
+addselectgosucc(BasicBlock *selectgo)
+{
+	BasicBlock *pred;
+	BasicBlock *succ;
+
+	pred = selectgo;
+	for(;;) {
+		if(arraylength(pred->pred) == 0)
+			fatal("selectgo does not have a newselect");
+		pred = *(BasicBlock**)arrayget(pred->pred, 0);
+		if(blockany(pred, isselectcommcasecall)) {
+			// A select comm case block should have exactly one
+			// successor.
+			if(arraylength(pred->succ) != 1)
+				fatal("select comm case has too many successors");
+			succ = *(BasicBlock**)arrayget(pred->succ, 0);
+			// Its successor should have exactly two successors.
+			// The drop through should flow to the selectgo block
+			// and the branch should lead to the select case
+			// statements block.
+			if(arraylength(succ->succ) != 2)
+				fatal("select comm case successor has too many successors");
+			// Add the block as a successor of the selectgo block.
+			addedge(selectgo, succ);
+		}
+		if(blockany(pred, isnewselect)) {
+			// Reached the matching newselect.
+			break;
+		}
+	}
+}
+
+// The entry point for the missing selectgo control flow algorithm.  Takes an
+// array of BasicBlock*s containing selectgo calls.
+static void
+fixselectgo(Array *selectgo)
+{
+	BasicBlock *bb;
+	int32 i;
+
+	for(i = 0; i < arraylength(selectgo); i++) {
+		bb = *(BasicBlock**)arrayget(selectgo, i);
+		addselectgosucc(bb);
+	}
+}
+
+// Constructs a control flow graph from a sequence of instructions.  This
+// procedure is complicated by various sources of implicit control flow that are
+// not accounted for using the standard cfg construction algorithm.  Returns an
+// array of BasicBlock*s in control flow graph form (basic blocks ordered by
+// their RPO number).
+static Array*
+newcfg(Prog *firstp)
+{
+	Prog *p;
+	Prog *prev;
+	BasicBlock *bb;
+	Array *cfg;
+	Array *selectgo;
+	int32 i;
+	int32 rpo;
+
+	// Reset the opt field of each prog to nil.  In the first and second
+	// passes, instructions that are labels temporarily use the opt field to
+	// point to their basic block.  In the third pass, the opt field reset
+	// to point to the predecessor of an instruction in its basic block.
+	for(p = firstp; p != P; p = p->link)
+		p->opt = nil;
+
+	// Allocate an array to remember where we have seen selectgo calls.
+	// These blocks will be revisited to add successor control flow edges.
+	selectgo = arraynew(0, sizeof(BasicBlock*));
+
+	// Loop through all instructions identifying branch targets
+	// and fall-throughs and allocate basic blocks.
+	cfg = arraynew(0, sizeof(BasicBlock*));
+	bb = newblock(firstp);
+	arrayadd(cfg, &bb);
+	for(p = firstp; p != P; p = p->link) {
+		if(p->to.type == D_BRANCH) {
+			if(p->to.u.branch == nil)
+				fatal("prog branch to nil");
+			if(p->to.u.branch->opt == nil) {
+				p->to.u.branch->opt = newblock(p->to.u.branch);
+				arrayadd(cfg, &p->to.u.branch->opt);
+			}
+			if(p->as != AJMP && p->link != nil && p->link->opt == nil) {
+				p->link->opt = newblock(p->link);
+				arrayadd(cfg, &p->link->opt);
+			}
+		} else if(isselectcommcasecall(p) || isselectgocall(p)) {
+			// Accommodate implicit selectgo control flow.
+			if(p->link->opt == nil) {
+				p->link->opt = newblock(p->link);
+				arrayadd(cfg, &p->link->opt);
+			}
+		}
+	}
+
+	// Loop through all basic blocks maximally growing the list of
+	// contained instructions until a label is reached.  Add edges
+	// for branches and fall-through instructions.
+	for(i = 0; i < arraylength(cfg); i++) {
+		bb = *(BasicBlock**)arrayget(cfg, i);
+		for(p = bb->last; p != nil; p = p->link) {
+			if(p->opt != nil && p != bb->last)
+				break;
+			bb->last = p;
+
+			// Stop before an unreachable RET, to avoid creating
+			// unreachable control flow nodes.
+			if(p->link != nil && p->link->as == ARET && p->link->mode == 1)
+				break;
+
+			// Collect basic blocks with selectgo calls.
+			if(isselectgocall(p))
+				arrayadd(selectgo, &bb);
+		}
+		if(bb->last->to.type == D_BRANCH)
+			addedge(bb, bb->last->to.u.branch->opt);
+		if(bb->last->link != nil) {
+			// Add a fall-through when the instruction is
+			// not an unconditional control transfer.
+			switch(bb->last->as) {
+			case AJMP:
+			case ARET:
+			case AUNDEF:
+				break;
+			default:
+				addedge(bb, bb->last->link->opt);
+			}
+		}
+	}
+
+	// Add back links so the instructions in a basic block can be traversed
+	// backward.  This is the final state of the instruction opt field.
+	for(i = 0; i < arraylength(cfg); i++) {
+		bb = *(BasicBlock**)arrayget(cfg, i);
+		p = bb->first;
+		prev = nil;
+		for(;;) {
+			p->opt = prev;
+			if(p == bb->last)
+				break;
+			prev = p;
+			p = p->link;
+		}
+	}
+
+	// Add missing successor edges to the selectgo blocks.
+	if(arraylength(selectgo))
+		fixselectgo(selectgo);
+	arrayfree(selectgo);
+
+	// Find a depth-first order and assign a depth-first number to
+	// all basic blocks.
+	for(i = 0; i < arraylength(cfg); i++) {
+		bb = *(BasicBlock**)arrayget(cfg, i);
+		bb->mark = UNVISITED;
+	}
+	bb = *(BasicBlock**)arrayget(cfg, 0);
+	rpo = arraylength(cfg);
+	reversepostorder(bb, &rpo);
+
+	// Sort the basic blocks by their depth first number.  The
+	// array is now a depth-first spanning tree with the first
+	// node being the root.
+	arraysort(cfg, blockrpocmp);
+	bb = *(BasicBlock**)arrayget(cfg, 0);
+
+	// Unreachable control flow nodes are indicated by a -1 in the rpo
+	// field.  If we see these nodes something must have gone wrong in an
+	// upstream compilation phase.
+	if(bb->rpo == -1) {
+		print("newcfg: unreachable basic block for %P\n", bb->last);
+		printcfg(cfg);
+		fatal("newcfg: invalid control flow graph");
+	}
+
+	return cfg;
+}
+
+// Frees a control flow graph (an array of BasicBlock*s) and all of its leaf
+// data structures.
+static void
+freecfg(Array *cfg)
+{
+	BasicBlock *bb;
+	BasicBlock *bb0;
+	Prog *p;
+	int32 i;
+	int32 len;
+
+	len = arraylength(cfg);
+	if(len > 0) {
+		bb0 = *(BasicBlock**)arrayget(cfg, 0);
+		for(p = bb0->first; p != P; p = p->link) {
+			p->opt = nil;
+		}
+		for(i = 0; i < len; i++) {
+			bb = *(BasicBlock**)arrayget(cfg, i);
+			freeblock(bb);
+		}
+	}
+	arrayfree(cfg);
+}
+
+// Returns true if the node names a variable that is otherwise uninteresting to
+// the liveness computation.
+static int
+isfunny(Node *node)
+{
+	char *names[] = { ".fp", ".args", nil };
+	int i;
+
+	if(node->sym != nil && node->sym->name != nil)
+		for(i = 0; names[i] != nil; i++)
+			if(strcmp(node->sym->name, names[i]) == 0)
+				return 1;
+	return 0;
+}
+
+// Computes the effects of an instruction on a set of
+// variables.  The vars argument is an array of Node*s.
+//
+// The output vectors give bits for variables:
+//	uevar - used by this instruction
+//	varkill - killed by this instruction
+//		for variables without address taken, means variable was set
+//		for variables with address taken, means variable was marked dead
+//	avarinit - initialized or referred to by this instruction,
+//		only for variables with address taken but not escaping to heap
+//
+// The avarinit output serves as a signal that the data has been
+// initialized, because any use of a variable must come after its
+// initialization.
+static void
+progeffects(Prog *prog, Array *vars, Bvec *uevar, Bvec *varkill, Bvec *avarinit)
+{
+	ProgInfo info;
+	Addr *from;
+	Addr *to;
+	Node *node;
+	int32 i;
+	int32 pos;
+
+	bvresetall(uevar);
+	bvresetall(varkill);
+	bvresetall(avarinit);
+
+	proginfo(&info, prog);
+	if(prog->as == ARET) {
+		// Return instructions implicitly read all the arguments.  For
+		// the sake of correctness, out arguments must be read.  For the
+		// sake of backtrace quality, we read in arguments as well.
+		//
+		// A return instruction with a p->to is a tail return, which brings
+		// the stack pointer back up (if it ever went down) and then jumps
+		// to a new function entirely. That form of instruction must read
+		// all the parameters for correctness, and similarly it must not
+		// read the out arguments - they won't be set until the new
+		// function runs.
+		for(i = 0; i < arraylength(vars); i++) {
+			node = *(Node**)arrayget(vars, i);
+			switch(node->class & ~PHEAP) {
+			case PPARAM:
+				bvset(uevar, i);
+				break;
+			case PPARAMOUT:
+				// If the result had its address taken, it is being tracked
+				// by the avarinit code, which does not use uevar.
+				// If we added it to uevar too, we'd not see any kill
+				// and decide that the varible was live entry, which it is not.
+				// So only use uevar in the non-addrtaken case.
+				// The p->to.type == D_NONE limits the bvset to
+				// non-tail-call return instructions; see note above
+				// the for loop for details.
+				if(!node->addrtaken && prog->to.type == D_NONE)
+					bvset(uevar, i);
+				break;
+			}
+		}
+		return;
+	}
+	if(prog->as == ATEXT) {
+		// A text instruction marks the entry point to a function and
+		// the definition point of all in arguments.
+		for(i = 0; i < arraylength(vars); i++) {
+			node = *(Node**)arrayget(vars, i);
+			switch(node->class & ~PHEAP) {
+			case PPARAM:
+				if(node->addrtaken)
+					bvset(avarinit, i);
+				bvset(varkill, i);
+				break;
+			}
+		}
+		return;
+	}
+	if(info.flags & (LeftRead | LeftWrite | LeftAddr)) {
+		from = &prog->from;
+		if (from->node != nil && from->sym != nil && from->node->curfn == curfn) {
+			switch(from->node->class & ~PHEAP) {
+			case PAUTO:
+			case PPARAM:
+			case PPARAMOUT:
+				pos = (int)(uintptr)from->node->opt - 1; // index in vars
+				if(pos == -1)
+					goto Next;
+				if(pos >= arraylength(vars) || *(Node**)arrayget(vars, pos) != from->node)
+					fatal("bad bookkeeping in liveness %N %d", from->node, pos);
+				if(from->node->addrtaken) {
+					bvset(avarinit, pos);
+				} else {
+					if(info.flags & (LeftRead | LeftAddr))
+						bvset(uevar, pos);
+					if(info.flags & LeftWrite)
+						if(from->node != nil && !isfat(from->node->type))
+							bvset(varkill, pos);
+				}
+			}
+		}
+	}
+Next:
+	if(info.flags & (RightRead | RightWrite | RightAddr)) {
+		to = &prog->to;
+		if (to->node != nil && to->sym != nil && to->node->curfn == curfn) {
+			switch(to->node->class & ~PHEAP) {
+			case PAUTO:
+			case PPARAM:
+			case PPARAMOUT:
+				pos = (int)(uintptr)to->node->opt - 1; // index in vars
+				if(pos == -1)
+					goto Next1;
+				if(pos >= arraylength(vars) || *(Node**)arrayget(vars, pos) != to->node)
+					fatal("bad bookkeeping in liveness %N %d", to->node, pos);
+				if(to->node->addrtaken) {
+					if(prog->as != AVARKILL)
+						bvset(avarinit, pos);
+					if(prog->as == AVARDEF || prog->as == AVARKILL)
+						bvset(varkill, pos);
+				} else {
+					// RightRead is a read, obviously.
+					// RightAddr by itself is also implicitly a read.
+					//
+					// RightAddr|RightWrite means that the address is being taken
+					// but only so that the instruction can write to the value.
+					// It is not a read. It is equivalent to RightWrite except that
+					// having the RightAddr bit set keeps the registerizer from
+					// trying to substitute a register for the memory location.
+					if((info.flags & RightRead) || (info.flags & (RightAddr|RightWrite)) == RightAddr)
+						bvset(uevar, pos);
+					if(info.flags & RightWrite)
+						if(to->node != nil && (!isfat(to->node->type) || prog->as == AVARDEF))
+							bvset(varkill, pos);
+				}
+			}
+		}
+	}
+Next1:;
+}
+
+// Constructs a new liveness structure used to hold the global state of the
+// liveness computation.  The cfg argument is an array of BasicBlock*s and the
+// vars argument is an array of Node*s.
+static Liveness*
+newliveness(Node *fn, Prog *ptxt, Array *cfg, Array *vars)
+{
+	Liveness *result;
+	int32 i;
+	int32 nblocks;
+	int32 nvars;
+
+	result = xmalloc(sizeof(*result));
+	result->fn = fn;
+	result->ptxt = ptxt;
+	result->cfg = cfg;
+	result->vars = vars;
+
+	nblocks = arraylength(cfg);
+	result->uevar = xmalloc(sizeof(Bvec*) * nblocks);
+	result->varkill = xmalloc(sizeof(Bvec*) * nblocks);
+	result->livein = xmalloc(sizeof(Bvec*) * nblocks);
+	result->liveout = xmalloc(sizeof(Bvec*) * nblocks);
+	result->avarinit = xmalloc(sizeof(Bvec*) * nblocks);
+	result->avarinitany = xmalloc(sizeof(Bvec*) * nblocks);
+	result->avarinitall = xmalloc(sizeof(Bvec*) * nblocks);
+
+	nvars = arraylength(vars);
+	for(i = 0; i < nblocks; i++) {
+		result->uevar[i] = bvalloc(nvars);
+		result->varkill[i] = bvalloc(nvars);
+		result->livein[i] = bvalloc(nvars);
+		result->liveout[i] = bvalloc(nvars);
+		result->avarinit[i] = bvalloc(nvars);
+		result->avarinitany[i] = bvalloc(nvars);
+		result->avarinitall[i] = bvalloc(nvars);
+	}
+
+	result->livepointers = arraynew(0, sizeof(Bvec*));
+	result->argslivepointers = arraynew(0, sizeof(Bvec*));
+	return result;
+}
+
+// Frees the liveness structure and all of its leaf data structures.
+static void
+freeliveness(Liveness *lv)
+{
+	int32 i;
+
+	if(lv == nil)
+		fatal("freeliveness: cannot free nil");
+
+	for(i = 0; i < arraylength(lv->livepointers); i++)
+		free(*(Bvec**)arrayget(lv->livepointers, i));
+	arrayfree(lv->livepointers);
+
+	for(i = 0; i < arraylength(lv->argslivepointers); i++)
+		free(*(Bvec**)arrayget(lv->argslivepointers, i));
+	arrayfree(lv->argslivepointers);
+
+	for(i = 0; i < arraylength(lv->cfg); i++) {
+		free(lv->uevar[i]);
+		free(lv->varkill[i]);
+		free(lv->livein[i]);
+		free(lv->liveout[i]);
+		free(lv->avarinit[i]);
+		free(lv->avarinitany[i]);
+		free(lv->avarinitall[i]);
+	}
+
+	free(lv->uevar);
+	free(lv->varkill);
+	free(lv->livein);
+	free(lv->liveout);
+	free(lv->avarinit);
+	free(lv->avarinitany);
+	free(lv->avarinitall);
+
+	free(lv);
+}
+
+static void
+printeffects(Prog *p, Bvec *uevar, Bvec *varkill, Bvec *avarinit)
+{
+	print("effects of %P", p);
+	print("\nuevar: ");
+	bvprint(uevar);
+	print("\nvarkill: ");
+	bvprint(varkill);
+	print("\navarinit: ");
+	bvprint(avarinit);
+	print("\n");
+}
+
+// Pretty print a variable node.  Uses Pascal like conventions for pointers and
+// addresses to avoid confusing the C like conventions used in the node variable
+// names.
+static void
+printnode(Node *node)
+{
+	char *p;
+	char *a;
+
+	p = haspointers(node->type) ? "^" : "";
+	a = node->addrtaken ? "@" : "";
+	print(" %N%s%s", node, p, a);
+}
+
+// Pretty print a list of variables.  The vars argument is an array of Node*s.
+static void
+printvars(char *name, Bvec *bv, Array *vars)
+{
+	int32 i;
+
+	print("%s:", name);
+	for(i = 0; i < arraylength(vars); i++)
+		if(bvget(bv, i))
+			printnode(*(Node**)arrayget(vars, i));
+	print("\n");
+}
+
+// Prints a basic block annotated with the information computed by liveness
+// analysis.
+static void
+livenessprintblock(Liveness *lv, BasicBlock *bb)
+{
+	BasicBlock *pred;
+	BasicBlock *succ;
+	Prog *prog;
+	Bvec *live;
+	int i;
+	int32 pos;
+
+	print("basic block %d\n", bb->rpo);
+
+	print("\tpred:");
+	for(i = 0; i < arraylength(bb->pred); i++) {
+		pred = *(BasicBlock**)arrayget(bb->pred, i);
+		print(" %d", pred->rpo);
+	}
+	print("\n");
+
+	print("\tsucc:");
+	for(i = 0; i < arraylength(bb->succ); i++) {
+		succ = *(BasicBlock**)arrayget(bb->succ, i);
+		print(" %d", succ->rpo);
+	}
+	print("\n");
+
+	printvars("\tuevar", lv->uevar[bb->rpo], lv->vars);
+	printvars("\tvarkill", lv->varkill[bb->rpo], lv->vars);
+	printvars("\tlivein", lv->livein[bb->rpo], lv->vars);
+	printvars("\tliveout", lv->liveout[bb->rpo], lv->vars);
+	printvars("\tavarinit", lv->avarinit[bb->rpo], lv->vars);
+	printvars("\tavarinitany", lv->avarinitany[bb->rpo], lv->vars);
+	printvars("\tavarinitall", lv->avarinitall[bb->rpo], lv->vars);
+
+	print("\tprog:\n");
+	for(prog = bb->first;; prog = prog->link) {
+		print("\t\t%P", prog);
+		if(prog->as == APCDATA && prog->from.offset == PCDATA_StackMapIndex) {
+			pos = prog->to.offset;
+			live = *(Bvec**)arrayget(lv->livepointers, pos);
+			print(" ");
+			bvprint(live);
+		}
+		print("\n");
+		if(prog == bb->last)
+			break;
+	}
+}
+
+// Prints a control flow graph annotated with any information computed by
+// liveness analysis.
+static void
+livenessprintcfg(Liveness *lv)
+{
+	BasicBlock *bb;
+	int32 i;
+
+	for(i = 0; i < arraylength(lv->cfg); i++) {
+		bb = *(BasicBlock**)arrayget(lv->cfg, i);
+		livenessprintblock(lv, bb);
+	}
+}
+
+static void
+checkauto(Node *fn, Prog *p, Node *n)
+{
+	NodeList *l;
+
+	for(l = fn->dcl; l != nil; l = l->next)
+		if(l->n->op == ONAME && l->n->class == PAUTO && l->n == n)
+			return;
+
+	print("checkauto %N: %N (%p; class=%d) not found in %P\n", curfn, n, n, n->class, p);
+	for(l = fn->dcl; l != nil; l = l->next)
+		print("\t%N (%p; class=%d)\n", l->n, l->n, l->n->class);
+	yyerror("checkauto: invariant lost");
+}
+
+static void
+checkparam(Node *fn, Prog *p, Node *n)
+{
+	NodeList *l;
+	Node *a;
+	int class;
+
+	if(isfunny(n))
+		return;
+	for(l = fn->dcl; l != nil; l = l->next) {
+		a = l->n;
+		class = a->class & ~PHEAP;
+		if(a->op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n)
+			return;
+	}
+
+	print("checkparam %N: %N (%p; class=%d) not found in %P\n", curfn, n, n, n->class, p);
+	for(l = fn->dcl; l != nil; l = l->next)
+		print("\t%N (%p; class=%d)\n", l->n, l->n, l->n->class);
+	yyerror("checkparam: invariant lost");
+}
+
+static void
+checkprog(Node *fn, Prog *p)
+{
+	if(p->from.type == D_AUTO)
+		checkauto(fn, p, p->from.node);
+	if(p->from.type == D_PARAM)
+		checkparam(fn, p, p->from.node);
+	if(p->to.type == D_AUTO)
+		checkauto(fn, p, p->to.node);
+	if(p->to.type == D_PARAM)
+		checkparam(fn, p, p->to.node);
+}
+
+// Check instruction invariants.  We assume that the nodes corresponding to the
+// sources and destinations of memory operations will be declared in the
+// function.  This is not strictly true, as is the case for the so-called funny
+// nodes and there are special cases to skip over that stuff.  The analysis will
+// fail if this invariant blindly changes.
+static void
+checkptxt(Node *fn, Prog *firstp)
+{
+	Prog *p;
+
+	if(debuglive == 0)
+		return;
+
+	for(p = firstp; p != P; p = p->link) {
+		if(0)
+			print("analyzing '%P'\n", p);
+		switch(p->as) {
+		case ADATA:
+		case AGLOBL:
+		case ANAME:
+		case ASIGNAME:
+		case ATYPE:
+			continue;
+		}
+		checkprog(fn, p);
+	}
+}
+
+// NOTE: The bitmap for a specific type t should be cached in t after the first run
+// and then simply copied into bv at the correct offset on future calls with
+// the same type t. On https://rsc.googlecode.com/hg/testdata/slow.go, twobitwalktype1
+// accounts for 40% of the 6g execution time.
+void
+twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv)
+{
+	vlong fieldoffset;
+	vlong i;
+	vlong o;
+	Type *t1;
+
+	if(t->align > 0 && (*xoffset & (t->align - 1)) != 0)
+		fatal("twobitwalktype1: invalid initial alignment, %T", t);
+
+	switch(t->etype) {
+	case TINT8:
+	case TUINT8:
+	case TINT16:
+	case TUINT16:
+	case TINT32:
+	case TUINT32:
+	case TINT64:
+	case TUINT64:
+	case TINT:
+	case TUINT:
+	case TUINTPTR:
+	case TBOOL:
+	case TFLOAT32:
+	case TFLOAT64:
+	case TCOMPLEX64:
+	case TCOMPLEX128:
+		for(i = 0; i < t->width; i++) {
+			bvset(bv, ((*xoffset + i) / widthptr) * BitsPerPointer); // 1 = live scalar
+		}
+		*xoffset += t->width;
+		break;
+
+	case TPTR32:
+	case TPTR64:
+	case TUNSAFEPTR:
+	case TFUNC:
+	case TCHAN:
+	case TMAP:
+		if((*xoffset & (widthptr-1)) != 0)
+			fatal("twobitwalktype1: invalid alignment, %T", t);
+		bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr
+		*xoffset += t->width;
+		break;
+
+	case TSTRING:
+		// struct { byte *str; intgo len; }
+		if((*xoffset & (widthptr-1)) != 0)
+			fatal("twobitwalktype1: invalid alignment, %T", t);
+		bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot
+		*xoffset += t->width;
+		break;
+
+	case TINTER:
+		// struct { Itab *tab;	union { void *ptr, uintptr val } data; }
+		// or, when isnilinter(t)==true:
+		// struct { Type *type; union { void *ptr, uintptr val } data; }
+		if((*xoffset & (widthptr-1)) != 0)
+			fatal("twobitwalktype1: invalid alignment, %T", t);
+		bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 0);
+		bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 1); // 3 = multiword
+		// next word contains 2 = Iface, 3 = Eface
+		if(isnilinter(t)) {
+			bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 2);
+			bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 3);
+		} else {
+			bvset(bv, ((*xoffset / widthptr) * BitsPerPointer) + 3);
+		}
+		*xoffset += t->width;
+		break;
+
+	case TARRAY:
+		// The value of t->bound is -1 for slices types and >0 for
+		// for fixed array types.  All other values are invalid.
+		if(t->bound < -1)
+			fatal("twobitwalktype1: invalid bound, %T", t);
+		if(isslice(t)) {
+			// struct { byte *array; uintgo len; uintgo cap; }
+			if((*xoffset & (widthptr-1)) != 0)
+				fatal("twobitwalktype1: invalid TARRAY alignment, %T", t);
+			bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot
+			*xoffset += t->width;
+		} else
+			for(i = 0; i < t->bound; i++)
+				twobitwalktype1(t->type, xoffset, bv);
+		break;
+
+	case TSTRUCT:
+		o = 0;
+		for(t1 = t->type; t1 != T; t1 = t1->down) {
+			fieldoffset = t1->width;
+			*xoffset += fieldoffset - o;
+			twobitwalktype1(t1->type, xoffset, bv);
+			o = fieldoffset + t1->type->width;
+		}
+		*xoffset += t->width - o;
+		break;
+
+	default:
+		fatal("twobitwalktype1: unexpected type, %T", t);
+	}
+}
+
+// Returns the number of words of local variables.
+static int32
+localswords(void)
+{
+	return stkptrsize / widthptr;
+}
+
+// Returns the number of words of in and out arguments.
+static int32
+argswords(void)
+{
+	return curfn->type->argwid / widthptr;
+}
+
+// Generates live pointer value maps for arguments and local variables.  The
+// this argument and the in arguments are always assumed live.  The vars
+// argument is an array of Node*s.
+static void
+twobitlivepointermap(Liveness *lv, Bvec *liveout, Array *vars, Bvec *args, Bvec *locals)
+{
+	Node *node;
+	Type *thisargtype;
+	Type *inargtype;
+	vlong xoffset;
+	int32 i;
+
+	for(i = 0; (i = bvnext(liveout, i)) >= 0; i++) {
+		node = *(Node**)arrayget(vars, i);
+		switch(node->class) {
+		case PAUTO:
+			xoffset = node->xoffset + stkptrsize;
+			twobitwalktype1(node->type, &xoffset, locals);
+			break;
+		case PPARAM:
+		case PPARAMOUT:
+			xoffset = node->xoffset;
+			twobitwalktype1(node->type, &xoffset, args);
+			break;
+		}
+	}
+	
+	// The node list only contains declared names.
+	// If the receiver or arguments are unnamed, they will be omitted
+	// from the list above. Preserve those values - even though they are unused -
+	// in order to keep their addresses live for use in stack traces.
+	thisargtype = getthisx(lv->fn->type);
+	if(thisargtype != nil) {
+		xoffset = 0;
+		twobitwalktype1(thisargtype, &xoffset, args);
+	}
+	inargtype = getinargx(lv->fn->type);
+	if(inargtype != nil) {
+		xoffset = 0;
+		twobitwalktype1(inargtype, &xoffset, args);
+	}
+}
+
+// Construct a disembodied instruction.
+static Prog*
+unlinkedprog(int as)
+{
+	Prog *p;
+
+	p = mal(sizeof(*p));
+	clearp(p);
+	p->as = as;
+	return p;
+}
+
+// Construct a new PCDATA instruction associated with and for the purposes of
+// covering an existing instruction.
+static Prog*
+newpcdataprog(Prog *prog, int32 index)
+{
+	Node from, to;
+	Prog *pcdata;
+
+	nodconst(&from, types[TINT32], PCDATA_StackMapIndex);
+	nodconst(&to, types[TINT32], index);
+	pcdata = unlinkedprog(APCDATA);
+	pcdata->lineno = prog->lineno;
+	naddr(&from, &pcdata->from, 0);
+	naddr(&to, &pcdata->to, 0);
+	return pcdata;
+}
+
+// Returns true for instructions that are safe points that must be annotated
+// with liveness information.
+static int
+issafepoint(Prog *prog)
+{
+	return prog->as == ATEXT || prog->as == ACALL;
+}
+
+// Initializes the sets for solving the live variables.  Visits all the
+// instructions in each basic block to summarizes the information at each basic
+// block
+static void
+livenessprologue(Liveness *lv)
+{
+	BasicBlock *bb;
+	Bvec *uevar, *varkill, *avarinit;
+	Prog *p;
+	int32 i;
+	int32 nvars;
+
+	nvars = arraylength(lv->vars);
+	uevar = bvalloc(nvars);
+	varkill = bvalloc(nvars);
+	avarinit = bvalloc(nvars);
+	for(i = 0; i < arraylength(lv->cfg); i++) {
+		bb = *(BasicBlock**)arrayget(lv->cfg, i);
+		// Walk the block instructions backward and update the block
+		// effects with the each prog effects.
+		for(p = bb->last; p != nil; p = p->opt) {
+			progeffects(p, lv->vars, uevar, varkill, avarinit);
+			if(debuglive >= 3)
+				printeffects(p, uevar, varkill, avarinit);
+			bvor(lv->varkill[i], lv->varkill[i], varkill);
+			bvandnot(lv->uevar[i], lv->uevar[i], varkill);
+			bvor(lv->uevar[i], lv->uevar[i], uevar);			
+		}
+		// Walk the block instructions forward to update avarinit bits.
+		// avarinit describes the effect at the end of the block, not the beginning.
+		bvresetall(varkill);
+		for(p = bb->first;; p = p->link) {
+			progeffects(p, lv->vars, uevar, varkill, avarinit);
+			if(debuglive >= 3)
+				printeffects(p, uevar, varkill, avarinit);
+			bvandnot(lv->avarinit[i], lv->avarinit[i], varkill);
+			bvor(lv->avarinit[i], lv->avarinit[i], avarinit);
+			if(p == bb->last)
+				break;
+		}
+	}
+	free(uevar);
+	free(varkill);
+	free(avarinit);
+}
+
+// Solve the liveness dataflow equations.
+static void
+livenesssolve(Liveness *lv)
+{
+	BasicBlock *bb, *succ, *pred;
+	Bvec *newlivein, *newliveout, *any, *all;
+	int32 rpo, i, j, change;
+
+	// These temporary bitvectors exist to avoid successive allocations and
+	// frees within the loop.
+	newlivein = bvalloc(arraylength(lv->vars));
+	newliveout = bvalloc(arraylength(lv->vars));
+	any = bvalloc(arraylength(lv->vars));
+	all = bvalloc(arraylength(lv->vars));
+
+	// Push avarinitall, avarinitany forward.
+	// avarinitall says the addressed var is initialized along all paths reaching the block exit.
+	// avarinitany says the addressed var is initialized along some path reaching the block exit.
+	for(i = 0; i < arraylength(lv->cfg); i++) {
+		bb = *(BasicBlock**)arrayget(lv->cfg, i);
+		rpo = bb->rpo;
+		if(i == 0)
+			bvcopy(lv->avarinitall[rpo], lv->avarinit[rpo]);
+		else {
+			bvresetall(lv->avarinitall[rpo]);
+			bvnot(lv->avarinitall[rpo]);
+		}
+		bvcopy(lv->avarinitany[rpo], lv->avarinit[rpo]);
+	}
+
+	change = 1;
+	while(change != 0) {
+		change = 0;
+		for(i = 0; i < arraylength(lv->cfg); i++) {
+			bb = *(BasicBlock**)arrayget(lv->cfg, i);
+			rpo = bb->rpo;
+			bvresetall(any);
+			bvresetall(all);
+			for(j = 0; j < arraylength(bb->pred); j++) {
+				pred = *(BasicBlock**)arrayget(bb->pred, j);
+				if(j == 0) {
+					bvcopy(any, lv->avarinitany[pred->rpo]);
+					bvcopy(all, lv->avarinitall[pred->rpo]);
+				} else {
+					bvor(any, any, lv->avarinitany[pred->rpo]);
+					bvand(all, all, lv->avarinitall[pred->rpo]);
+				}
+			}
+			bvandnot(any, any, lv->varkill[rpo]);
+			bvandnot(all, all, lv->varkill[rpo]);
+			bvor(any, any, lv->avarinit[rpo]);
+			bvor(all, all, lv->avarinit[rpo]);
+			if(bvcmp(any, lv->avarinitany[rpo])) {
+				change = 1;
+				bvcopy(lv->avarinitany[rpo], any);
+			}
+			if(bvcmp(all, lv->avarinitall[rpo])) {
+				change = 1;
+				bvcopy(lv->avarinitall[rpo], all);
+			}
+		}
+	}
+
+	// Iterate through the blocks in reverse round-robin fashion.  A work
+	// queue might be slightly faster.  As is, the number of iterations is
+	// so low that it hardly seems to be worth the complexity.
+	change = 1;
+	while(change != 0) {
+		change = 0;
+		// Walk blocks in the general direction of propagation.  This
+		// improves convergence.
+		for(i = arraylength(lv->cfg) - 1; i >= 0; i--) {
+			// A variable is live on output from this block
+			// if it is live on input to some successor.
+			//
+			// out[b] = \bigcup_{s \in succ[b]} in[s]
+			bb = *(BasicBlock**)arrayget(lv->cfg, i);
+			rpo = bb->rpo;
+			bvresetall(newliveout);
+			for(j = 0; j < arraylength(bb->succ); j++) {
+				succ = *(BasicBlock**)arrayget(bb->succ, j);
+				bvor(newliveout, newliveout, lv->livein[succ->rpo]);
+			}
+			if(bvcmp(lv->liveout[rpo], newliveout)) {
+				change = 1;
+				bvcopy(lv->liveout[rpo], newliveout);
+			}
+
+			// A variable is live on input to this block
+			// if it is live on output from this block and
+			// not set by the code in this block.
+			//
+			// in[b] = uevar[b] \cup (out[b] \setminus varkill[b])
+			bvandnot(newlivein, lv->liveout[rpo], lv->varkill[rpo]);
+			bvor(lv->livein[rpo], newlivein, lv->uevar[rpo]);
+		}
+	}
+
+	free(newlivein);
+	free(newliveout);
+	free(any);
+	free(all);
+}
+
+// This function is slow but it is only used for generating debug prints.
+// Check whether n is marked live in args/locals.
+static int
+islive(Node *n, Bvec *args, Bvec *locals)
+{
+	int i;
+
+	switch(n->class) {
+	case PPARAM:
+	case PPARAMOUT:
+		for(i = 0; i < n->type->width/widthptr*BitsPerPointer; i++)
+			if(bvget(args, n->xoffset/widthptr*BitsPerPointer + i))
+				return 1;
+		break;
+	case PAUTO:
+		for(i = 0; i < n->type->width/widthptr*BitsPerPointer; i++)
+			if(bvget(locals, (n->xoffset + stkptrsize)/widthptr*BitsPerPointer + i))
+				return 1;
+		break;
+	}
+	return 0;
+}
+
+// Visits all instructions in a basic block and computes a bit vector of live
+// variables at each safe point locations.
+static void
+livenessepilogue(Liveness *lv)
+{
+	BasicBlock *bb, *pred;
+	Bvec *ambig, *livein, *liveout, *uevar, *varkill, *args, *locals, *avarinit, *any, *all;
+	Node *n;
+	Prog *p, *next;
+	int32 i, j, numlive, startmsg, nmsg, nvars, pos;
+	vlong xoffset;
+	char **msg;
+	Fmt fmt;
+
+	nvars = arraylength(lv->vars);
+	livein = bvalloc(nvars);
+	liveout = bvalloc(nvars);
+	uevar = bvalloc(nvars);
+	varkill = bvalloc(nvars);
+	avarinit = bvalloc(nvars);
+	any = bvalloc(nvars);
+	all = bvalloc(nvars);
+	ambig = bvalloc(localswords() * BitsPerPointer);
+	msg = nil;
+	nmsg = 0;
+	startmsg = 0;
+
+	for(i = 0; i < arraylength(lv->cfg); i++) {
+		bb = *(BasicBlock**)arrayget(lv->cfg, i);
+		
+		// Compute avarinitany and avarinitall for entry to block.
+		// This duplicates information known during livenesssolve
+		// but avoids storing two more vectors for each block.
+		bvresetall(any);
+		bvresetall(all);
+		for(j = 0; j < arraylength(bb->pred); j++) {
+			pred = *(BasicBlock**)arrayget(bb->pred, j);
+			if(j == 0) {
+				bvcopy(any, lv->avarinitany[pred->rpo]);
+				bvcopy(all, lv->avarinitall[pred->rpo]);
+			} else {
+				bvor(any, any, lv->avarinitany[pred->rpo]);
+				bvand(all, all, lv->avarinitall[pred->rpo]);
+			}
+		}
+
+		// Walk forward through the basic block instructions and
+		// allocate liveness maps for those instructions that need them.
+		// Seed the maps with information about the addrtaken variables.
+		for(p = bb->first;; p = p->link) {
+			progeffects(p, lv->vars, uevar, varkill, avarinit);
+			bvandnot(any, any, varkill);
+			bvandnot(all, all, varkill);
+			bvor(any, any, avarinit);
+			bvor(all, all, avarinit);
+
+			if(issafepoint(p)) {
+				// Annotate ambiguously live variables so that they can
+				// be zeroed at function entry.
+				// livein and liveout are dead here and used as temporaries.
+				// For now, only enabled when using GOEXPERIMENT=precisestack
+				// during make.bash / all.bash.
+				if(precisestack_enabled) {
+					bvresetall(livein);
+					bvandnot(liveout, any, all);
+					if(!bvisempty(liveout)) {
+						for(pos = 0; pos < liveout->n; pos++) {
+							if(!bvget(liveout, pos))
+								continue;
+							bvset(all, pos); // silence future warnings in this block
+							n = *(Node**)arrayget(lv->vars, pos);
+							if(!n->needzero) {
+								n->needzero = 1;
+								if(debuglive >= 1)
+									warnl(p->lineno, "%N: %lN is ambiguously live", curfn->nname, n);
+								// Record in 'ambiguous' bitmap.
+								xoffset = n->xoffset + stkptrsize;
+								twobitwalktype1(n->type, &xoffset, ambig);
+							}
+						}
+					}
+				}
+	
+				// Allocate a bit vector for each class and facet of
+				// value we are tracking.
+	
+				// Live stuff first.
+				args = bvalloc(argswords() * BitsPerPointer);
+				arrayadd(lv->argslivepointers, &args);
+				locals = bvalloc(localswords() * BitsPerPointer);
+				arrayadd(lv->livepointers, &locals);
+
+				if(debuglive >= 3) {
+					print("%P\n", p);
+					printvars("avarinitany", any, lv->vars);
+				}
+
+				// Record any values with an "address taken" reaching
+				// this code position as live. Must do now instead of below
+				// because the any/all calculation requires walking forward
+				// over the block (as this loop does), while the liveout
+				// requires walking backward (as the next loop does).
+				twobitlivepointermap(lv, any, lv->vars, args, locals);
+			}
+			
+			if(p == bb->last)
+				break;
+		}
+		bb->lastbitmapindex = arraylength(lv->livepointers) - 1;
+	}
+	
+	for(i = 0; i < arraylength(lv->cfg); i++) {
+		bb = *(BasicBlock**)arrayget(lv->cfg, i);
+		
+		if(debuglive >= 1 && strcmp(curfn->nname->sym->name, "init") != 0 && curfn->nname->sym->name[0] != '.') {
+			nmsg = arraylength(lv->livepointers);
+			startmsg = nmsg;
+			msg = xmalloc(nmsg*sizeof msg[0]);
+			for(j=0; j<nmsg; j++)
+				msg[j] = nil;
+		}
+
+		// walk backward, emit pcdata and populate the maps
+		pos = bb->lastbitmapindex;
+		if(pos < 0) {
+			// the first block we encounter should have the ATEXT so
+			// at no point should pos ever be less than zero.
+			fatal("livenessepilogue");
+		}
+
+		bvcopy(livein, lv->liveout[bb->rpo]);
+		for(p = bb->last; p != nil; p = next) {
+			next = p->opt; // splicebefore modifies p->opt
+			// Propagate liveness information
+			progeffects(p, lv->vars, uevar, varkill, avarinit);
+			bvcopy(liveout, livein);
+			bvandnot(livein, liveout, varkill);
+			bvor(livein, livein, uevar);
+			if(debuglive >= 3 && issafepoint(p)){
+				print("%P\n", p);
+				printvars("uevar", uevar, lv->vars);
+				printvars("varkill", varkill, lv->vars);
+				printvars("livein", livein, lv->vars);
+				printvars("liveout", liveout, lv->vars);
+			}
+			if(issafepoint(p)) {
+				// Found an interesting instruction, record the
+				// corresponding liveness information.  
+				
+				// Useful sanity check: on entry to the function,
+				// the only things that can possibly be live are the
+				// input parameters.
+				if(p->as == ATEXT) {
+					for(j = 0; j < liveout->n; j++) {
+						if(!bvget(liveout, j))
+							continue;
+						n = *(Node**)arrayget(lv->vars, j);
+						if(n->class != PPARAM)
+							yyerrorl(p->lineno, "internal error: %N %lN recorded as live on entry", curfn->nname, n);
+					}
+				}
+
+				// Record live pointers.
+				args = *(Bvec**)arrayget(lv->argslivepointers, pos);
+				locals = *(Bvec**)arrayget(lv->livepointers, pos);
+				twobitlivepointermap(lv, liveout, lv->vars, args, locals);
+				
+				// Ambiguously live variables are zeroed immediately after
+				// function entry. Mark them live for all the non-entry bitmaps
+				// so that GODEBUG=gcdead=1 mode does not poison them.
+				if(p->as == ACALL)
+					bvor(locals, locals, ambig);
+
+				// Show live pointer bitmaps.
+				// We're interpreting the args and locals bitmap instead of liveout so that we
+				// include the bits added by the avarinit logic in the
+				// previous loop.
+				if(msg != nil) {
+					fmtstrinit(&fmt);
+					fmtprint(&fmt, "%L: live at ", p->lineno);
+					if(p->as == ACALL && p->to.node)
+						fmtprint(&fmt, "call to %s:", p->to.node->sym->name);
+					else if(p->as == ACALL)
+						fmtprint(&fmt, "indirect call:");
+					else
+						fmtprint(&fmt, "entry to %s:", p->from.node->sym->name);
+					numlive = 0;
+					for(j = 0; j < arraylength(lv->vars); j++) {
+						n = *(Node**)arrayget(lv->vars, j);
+						if(islive(n, args, locals)) {
+							fmtprint(&fmt, " %N", n);
+							numlive++;
+						}
+					}
+					fmtprint(&fmt, "\n");
+					if(numlive == 0) // squelch message
+						free(fmtstrflush(&fmt));
+					else
+						msg[--startmsg] = fmtstrflush(&fmt);
+				}
+
+				// Only CALL instructions need a PCDATA annotation.
+				// The TEXT instruction annotation is implicit.
+				if(p->as == ACALL) {
+					if(isdeferreturn(p)) {
+						// runtime.deferreturn modifies its return address to return
+						// back to the CALL, not to the subsequent instruction.
+						// Because the return comes back one instruction early,
+						// the PCDATA must begin one instruction early too.
+						// The instruction before a call to deferreturn is always a
+						// no-op, to keep PC-specific data unambiguous.
+						splicebefore(lv, bb, newpcdataprog(p->opt, pos), p->opt);
+					} else {
+						splicebefore(lv, bb, newpcdataprog(p, pos), p);
+					}
+				}
+
+				pos--;
+			}
+		}
+		if(msg != nil) {
+			for(j=startmsg; j<nmsg; j++) 
+				if(msg[j] != nil)
+					print("%s", msg[j]);
+			free(msg);
+			msg = nil;
+			nmsg = 0;
+			startmsg = 0;
+		}
+	}
+
+	free(livein);
+	free(liveout);
+	free(uevar);
+	free(varkill);
+	free(avarinit);
+	free(any);
+	free(all);
+	free(ambig);
+	
+	flusherrors();
+}
+
+// FNV-1 hash function constants.
+#define H0 2166136261UL
+#define Hp 16777619UL
+/*c2go
+enum
+{
+	H0 = 2166136261,
+	Hp = 16777619,
+};
+*/
+
+static uint32
+hashbitmap(uint32 h, Bvec *bv)
+{
+	uchar *p, *ep;
+	
+	p = (uchar*)bv->b;
+	ep = p + 4*((bv->n+31)/32);
+	while(p < ep)
+		h = (h*Hp) ^ *p++;
+	return h;
+}
+
+// Compact liveness information by coalescing identical per-call-site bitmaps.
+// The merging only happens for a single function, not across the entire binary.
+//
+// There are actually two lists of bitmaps, one list for the local variables and one
+// list for the function arguments. Both lists are indexed by the same PCDATA
+// index, so the corresponding pairs must be considered together when
+// merging duplicates. The argument bitmaps change much less often during
+// function execution than the local variable bitmaps, so it is possible that
+// we could introduce a separate PCDATA index for arguments vs locals and
+// then compact the set of argument bitmaps separately from the set of
+// local variable bitmaps. As of 2014-04-02, doing this to the godoc binary
+// is actually a net loss: we save about 50k of argument bitmaps but the new
+// PCDATA tables cost about 100k. So for now we keep using a single index for
+// both bitmap lists.
+static void
+livenesscompact(Liveness *lv)
+{
+	int *table, *remap, i, j, n, tablesize, uniq;
+	uint32 h;
+	Bvec *local, *arg, *jlocal, *jarg;
+	Prog *p;
+
+	// Linear probing hash table of bitmaps seen so far.
+	// The hash table has 4n entries to keep the linear
+	// scan short. An entry of -1 indicates an empty slot.
+	n = arraylength(lv->livepointers);
+	tablesize = 4*n;
+	table = xmalloc(tablesize*sizeof table[0]);
+	memset(table, 0xff, tablesize*sizeof table[0]);
+	
+	// remap[i] = the new index of the old bit vector #i.
+	remap = xmalloc(n*sizeof remap[0]);
+	memset(remap, 0xff, n*sizeof remap[0]);
+	uniq = 0; // unique tables found so far
+
+	// Consider bit vectors in turn.
+	// If new, assign next number using uniq,
+	// record in remap, record in lv->livepointers and lv->argslivepointers
+	// under the new index, and add entry to hash table.
+	// If already seen, record earlier index in remap and free bitmaps.
+	for(i=0; i<n; i++) {
+		local = *(Bvec**)arrayget(lv->livepointers, i);
+		arg = *(Bvec**)arrayget(lv->argslivepointers, i);
+		h = hashbitmap(hashbitmap(H0, local), arg) % tablesize;
+
+		for(;;) {
+			j = table[h];
+			if(j < 0)
+				break;
+			jlocal = *(Bvec**)arrayget(lv->livepointers, j);
+			jarg = *(Bvec**)arrayget(lv->argslivepointers, j);
+			if(bvcmp(local, jlocal) == 0 && bvcmp(arg, jarg) == 0) {
+				free(local);
+				free(arg);
+				remap[i] = j;
+				goto Next;
+			}
+			if(++h == tablesize)
+				h = 0;
+		}
+		table[h] = uniq;
+		remap[i] = uniq;
+		*(Bvec**)arrayget(lv->livepointers, uniq) = local;
+		*(Bvec**)arrayget(lv->argslivepointers, uniq) = arg;
+		uniq++;
+	Next:;
+	}
+
+	// We've already reordered lv->livepointers[0:uniq]
+	// and lv->argslivepointers[0:uniq] and freed the bitmaps
+	// we don't need anymore. Clear the pointers later in the
+	// array so that we can tell where the coalesced bitmaps stop
+	// and so that we don't double-free when cleaning up.
+	for(j=uniq; j<n; j++) {
+		*(Bvec**)arrayget(lv->livepointers, j) = nil;
+		*(Bvec**)arrayget(lv->argslivepointers, j) = nil;
+	}
+	
+	// Rewrite PCDATA instructions to use new numbering.
+	for(p=lv->ptxt; p != P; p=p->link) {
+		if(p->as == APCDATA && p->from.offset == PCDATA_StackMapIndex) {
+			i = p->to.offset;
+			if(i >= 0)
+				p->to.offset = remap[i];
+		}
+	}
+
+	free(table);
+	free(remap);
+}
+
+static int
+printbitset(int printed, char *name, Array *vars, Bvec *bits)
+{
+	int i, started;
+	Node *n;
+
+	started = 0;	
+	for(i=0; i<arraylength(vars); i++) {
+		if(!bvget(bits, i))
+			continue;
+		if(!started) {
+			if(!printed)
+				print("\t");
+			else
+				print(" ");
+			started = 1;
+			printed = 1;
+			print("%s=", name);
+		} else {
+			print(",");
+		}
+		n = *(Node**)arrayget(vars, i);
+		print("%s", n->sym->name);
+	}
+	return printed;
+}
+
+// Prints the computed liveness information and inputs, for debugging.
+// This format synthesizes the information used during the multiple passes
+// into a single presentation.
+static void
+livenessprintdebug(Liveness *lv)
+{
+	int i, j, pcdata, printed;
+	BasicBlock *bb;
+	Prog *p;
+	Bvec *uevar, *varkill, *avarinit, *args, *locals;
+	Node *n;
+
+	print("liveness: %s\n", curfn->nname->sym->name);
+
+	uevar = bvalloc(arraylength(lv->vars));
+	varkill = bvalloc(arraylength(lv->vars));
+	avarinit = bvalloc(arraylength(lv->vars));
+
+	pcdata = 0;
+	for(i = 0; i < arraylength(lv->cfg); i++) {
+		if(i > 0)
+			print("\n");
+		bb = *(BasicBlock**)arrayget(lv->cfg, i);
+
+		// bb#0 pred=1,2 succ=3,4
+		print("bb#%d pred=", i);
+		for(j = 0; j < arraylength(bb->pred); j++) {
+			if(j > 0)
+				print(",");
+			print("%d", (*(BasicBlock**)arrayget(bb->pred, j))->rpo);
+		}
+		print(" succ=");
+		for(j = 0; j < arraylength(bb->succ); j++) {
+			if(j > 0)
+				print(",");
+			print("%d", (*(BasicBlock**)arrayget(bb->succ, j))->rpo);
+		}
+		print("\n");
+		
+		// initial settings
+		printed = 0;
+		printed = printbitset(printed, "uevar", lv->vars, lv->uevar[bb->rpo]);
+		printed = printbitset(printed, "livein", lv->vars, lv->livein[bb->rpo]);
+		if(printed)
+			print("\n");
+		
+		// program listing, with individual effects listed
+		for(p = bb->first;; p = p->link) {
+			print("%P\n", p);
+			if(p->as == APCDATA && p->from.offset == PCDATA_StackMapIndex)
+				pcdata = p->to.offset;
+			progeffects(p, lv->vars, uevar, varkill, avarinit);
+			printed = 0;
+			printed = printbitset(printed, "uevar", lv->vars, uevar);
+			printed = printbitset(printed, "varkill", lv->vars, varkill);
+			printed = printbitset(printed, "avarinit", lv->vars, avarinit);
+			if(printed)
+				print("\n");
+			if(issafepoint(p)) {
+				args = *(Bvec**)arrayget(lv->argslivepointers, pcdata);
+				locals = *(Bvec**)arrayget(lv->livepointers, pcdata);
+				print("\tlive=");
+				printed = 0;
+				for(j = 0; j < arraylength(lv->vars); j++) {
+					n = *(Node**)arrayget(lv->vars, j);
+					if(islive(n, args, locals)) {
+						if(printed++)
+							print(",");
+						print("%N", n);
+					}
+				}
+				print("\n");
+			}
+			if(p == bb->last)
+				break;
+		}
+		
+		// bb bitsets
+		print("end\n");
+		printed = printbitset(printed, "varkill", lv->vars, lv->varkill[bb->rpo]);
+		printed = printbitset(printed, "liveout", lv->vars, lv->liveout[bb->rpo]);
+		printed = printbitset(printed, "avarinit", lv->vars, lv->avarinit[bb->rpo]);
+		printed = printbitset(printed, "avarinitany", lv->vars, lv->avarinitany[bb->rpo]);
+		printed = printbitset(printed, "avarinitall", lv->vars, lv->avarinitall[bb->rpo]);
+		if(printed)
+			print("\n");
+	}
+	print("\n");
+
+	free(uevar);
+	free(varkill);
+	free(avarinit);
+}
+
+// Dumps an array of bitmaps to a symbol as a sequence of uint32 values.  The
+// first word dumped is the total number of bitmaps.  The second word is the
+// length of the bitmaps.  All bitmaps are assumed to be of equal length.  The
+// words that are followed are the raw bitmap words.  The arr argument is an
+// array of Node*s.
+static void
+twobitwritesymbol(Array *arr, Sym *sym)
+{
+	Bvec *bv;
+	int off, i, j, len;
+	uint32 word;
+
+	len = arraylength(arr);
+	off = 0;
+	off += 4; // number of bitmaps, to fill in later
+	bv = *(Bvec**)arrayget(arr, 0);
+	off = duint32(sym, off, bv->n); // number of bits in each bitmap
+	for(i = 0; i < len; i++) {
+		// bitmap words
+		bv = *(Bvec**)arrayget(arr, i);
+		if(bv == nil)
+			break;
+		for(j = 0; j < bv->n; j += 32) {
+			word = bv->b[j/32];
+			// Runtime reads the bitmaps as byte arrays. Oblige.
+			off = duint8(sym, off, word);
+			off = duint8(sym, off, word>>8);
+			off = duint8(sym, off, word>>16);
+			off = duint8(sym, off, word>>24);
+		}
+	}
+	duint32(sym, 0, i); // number of bitmaps
+	ggloblsym(sym, off, RODATA);
+}
+
+static void
+printprog(Prog *p)
+{
+	while(p != nil) {
+		print("%P\n", p);
+		p = p->link;
+	}
+}
+
+// Entry pointer for liveness analysis.  Constructs a complete CFG, solves for
+// the liveness of pointer variables in the function, and emits a runtime data
+// structure read by the garbage collector.
+void
+liveness(Node *fn, Prog *firstp, Sym *argssym, Sym *livesym)
+{
+	Array *cfg, *vars;
+	Liveness *lv;
+	int debugdelta;
+	NodeList *l;
+
+	// Change name to dump debugging information only for a specific function.
+	debugdelta = 0;
+	if(strcmp(curfn->nname->sym->name, "!") == 0)
+		debugdelta = 2;
+	
+	debuglive += debugdelta;
+	if(debuglive >= 3) {
+		print("liveness: %s\n", curfn->nname->sym->name);
+		printprog(firstp);
+	}
+	checkptxt(fn, firstp);
+
+	// Construct the global liveness state.
+	cfg = newcfg(firstp);
+	if(debuglive >= 3)
+		printcfg(cfg);
+	vars = getvariables(fn);
+	lv = newliveness(fn, firstp, cfg, vars);
+
+	// Run the dataflow framework.
+	livenessprologue(lv);
+	if(debuglive >= 3)
+		livenessprintcfg(lv);
+	livenesssolve(lv);
+	if(debuglive >= 3)
+		livenessprintcfg(lv);
+	livenessepilogue(lv);
+	if(debuglive >= 3)
+		livenessprintcfg(lv);
+	livenesscompact(lv);
+
+	if(debuglive >= 2)
+		livenessprintdebug(lv);
+
+	// Emit the live pointer map data structures
+	twobitwritesymbol(lv->livepointers, livesym);
+	twobitwritesymbol(lv->argslivepointers, argssym);
+
+	// Free everything.
+	for(l=fn->dcl; l != nil; l = l->next)
+		if(l->n != N)
+			l->n->opt = nil;
+	freeliveness(lv);
+	arrayfree(vars);
+	freecfg(cfg);
+	
+	debuglive -= debugdelta;
+}
diff --git a/src/cmd/gc/popt.c b/src/cmd/gc/popt.c
new file mode 100644
index 0000000..993bb24
--- /dev/null
+++ b/src/cmd/gc/popt.c
@@ -0,0 +1,1005 @@
+// Derived from Inferno utils/6c/reg.c
+// http://code.google.com/p/inferno-os/source/browse/utils/6c/reg.c
+//
+//	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
+//	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
+//	Portions Copyright © 1997-1999 Vita Nuova Limited
+//	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
+//	Portions Copyright © 2004,2006 Bruce Ellis
+//	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
+//	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
+//	Portions Copyright © 2009 The Go Authors.  All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+// "Portable" optimizations.
+// Compiled separately for 5g, 6g, and 8g, so allowed to use gg.h, opt.h.
+// Must code to the intersection of the three back ends.
+
+#include	<u.h>
+#include	<libc.h>
+#include	"gg.h"
+#include	"opt.h"
+
+// p is a call instruction. Does the call fail to return?
+int
+noreturn(Prog *p)
+{
+	Sym *s;
+	int i;
+	static Sym*	symlist[10];
+
+	if(symlist[0] == S) {
+		symlist[0] = pkglookup("panicindex", runtimepkg);
+		symlist[1] = pkglookup("panicslice", runtimepkg);
+		symlist[2] = pkglookup("throwinit", runtimepkg);
+		symlist[3] = pkglookup("gopanic", runtimepkg);
+		symlist[4] = pkglookup("panicwrap", runtimepkg);
+		symlist[5] = pkglookup("throwreturn", runtimepkg);
+		symlist[6] = pkglookup("selectgo", runtimepkg);
+		symlist[7] = pkglookup("block", runtimepkg);
+	}
+
+	if(p->to.node == nil)
+		return 0;
+	s = p->to.node->sym;
+	if(s == S)
+		return 0;
+	for(i=0; symlist[i]!=S; i++)
+		if(s == symlist[i])
+			return 1;
+	return 0;
+}
+
+// JMP chasing and removal.
+//
+// The code generator depends on being able to write out jump
+// instructions that it can jump to now but fill in later.
+// the linker will resolve them nicely, but they make the code
+// longer and more difficult to follow during debugging.
+// Remove them.
+
+/* what instruction does a JMP to p eventually land on? */
+static Prog*
+chasejmp(Prog *p, int *jmploop)
+{
+	int n;
+
+	n = 0;
+	while(p != P && p->as == AJMP && p->to.type == D_BRANCH) {
+		if(++n > 10) {
+			*jmploop = 1;
+			break;
+		}
+		p = p->to.u.branch;
+	}
+	return p;
+}
+
+/*
+ * reuse reg pointer for mark/sweep state.
+ * leave reg==nil at end because alive==nil.
+ */
+#define alive ((void*)0)
+#define dead ((void*)1)
+/*c2go
+extern void *alive;
+extern void *dead;
+*/
+
+/* mark all code reachable from firstp as alive */
+static void
+mark(Prog *firstp)
+{
+	Prog *p;
+	
+	for(p=firstp; p; p=p->link) {
+		if(p->opt != dead)
+			break;
+		p->opt = alive;
+		if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch)
+			mark(p->to.u.branch);
+		if(p->as == AJMP || p->as == ARET || p->as == AUNDEF)
+			break;
+	}
+}
+
+void
+fixjmp(Prog *firstp)
+{
+	int jmploop;
+	Prog *p, *last;
+	
+	if(debug['R'] && debug['v'])
+		print("\nfixjmp\n");
+
+	// pass 1: resolve jump to jump, mark all code as dead.
+	jmploop = 0;
+	for(p=firstp; p; p=p->link) {
+		if(debug['R'] && debug['v'])
+			print("%P\n", p);
+		if(p->as != ACALL && p->to.type == D_BRANCH && p->to.u.branch && p->to.u.branch->as == AJMP) {
+			p->to.u.branch = chasejmp(p->to.u.branch, &jmploop);
+			if(debug['R'] && debug['v'])
+				print("->%P\n", p);
+		}
+		p->opt = dead;
+	}
+	if(debug['R'] && debug['v'])
+		print("\n");
+
+	// pass 2: mark all reachable code alive
+	mark(firstp);
+	
+	// pass 3: delete dead code (mostly JMPs).
+	last = nil;
+	for(p=firstp; p; p=p->link) {
+		if(p->opt == dead) {
+			if(p->link == P && p->as == ARET && last && last->as != ARET) {
+				// This is the final ARET, and the code so far doesn't have one.
+				// Let it stay. The register allocator assumes that all live code in
+				// the function can be traversed by starting at all the RET instructions
+				// and following predecessor links. If we remove the final RET,
+				// this assumption will not hold in the case of an infinite loop
+				// at the end of a function.
+				// Keep the RET but mark it dead for the liveness analysis.
+				p->mode = 1;
+			} else {
+				if(debug['R'] && debug['v'])
+					print("del %P\n", p);
+				continue;
+			}
+		}
+		if(last)
+			last->link = p;
+		last = p;
+	}
+	last->link = P;
+	
+	// pass 4: elide JMP to next instruction.
+	// only safe if there are no jumps to JMPs anymore.
+	if(!jmploop) {
+		last = nil;
+		for(p=firstp; p; p=p->link) {
+			if(p->as == AJMP && p->to.type == D_BRANCH && p->to.u.branch == p->link) {
+				if(debug['R'] && debug['v'])
+					print("del %P\n", p);
+				continue;
+			}
+			if(last)
+				last->link = p;
+			last = p;
+		}
+		last->link = P;
+	}
+	
+	if(debug['R'] && debug['v']) {
+		print("\n");
+		for(p=firstp; p; p=p->link)
+			print("%P\n", p);
+		print("\n");
+	}
+}
+
+#undef alive
+#undef dead
+
+// Control flow analysis. The Flow structures hold predecessor and successor
+// information as well as basic loop analysis.
+//
+//	graph = flowstart(firstp, sizeof(Flow));
+//	... use flow graph ...
+//	flowend(graph); // free graph
+//
+// Typical uses of the flow graph are to iterate over all the flow-relevant instructions:
+//
+//	for(f = graph->start; f != nil; f = f->link)
+//
+// or, given an instruction f, to iterate over all the predecessors, which is
+// f->p1 and this list:
+//
+//	for(f2 = f->p2; f2 != nil; f2 = f2->p2link)
+//	
+// Often the Flow struct is embedded as the first field inside a larger struct S.
+// In that case casts are needed to convert Flow* to S* in many places but the
+// idea is the same. Pass sizeof(S) instead of sizeof(Flow) to flowstart.
+
+Graph*
+flowstart(Prog *firstp, int size)
+{
+	int nf;
+	Flow *f, *f1, *start, *last;
+	Graph *graph;
+	Prog *p;
+	ProgInfo info;
+
+	// Count and mark instructions to annotate.
+	nf = 0;
+	for(p = firstp; p != P; p = p->link) {
+		p->opt = nil; // should be already, but just in case
+		proginfo(&info, p);
+		if(info.flags & Skip)
+			continue;
+		p->opt = (void*)1;
+		nf++;
+	}
+	
+	if(nf == 0)
+		return nil;
+
+	if(nf >= 20000) {
+		// fatal("%S is too big (%d instructions)", curfn->nname->sym, nf);
+		return nil;
+	}
+
+	// Allocate annotations and assign to instructions.
+	graph = calloc(sizeof *graph + size*nf, 1);
+	if(graph == nil)
+		fatal("out of memory");
+	start = (Flow*)(graph+1);
+	last = nil;
+	f = start;
+	for(p = firstp; p != P; p = p->link) {
+		if(p->opt == nil)
+			continue;
+		p->opt = f;
+		f->prog = p;
+		if(last)
+			last->link = f;
+		last = f;
+		
+		f = (Flow*)((uchar*)f + size);
+	}
+
+	// Fill in pred/succ information.
+	for(f = start; f != nil; f = f->link) {
+		p = f->prog;
+		proginfo(&info, p);
+		if(!(info.flags & Break)) {
+			f1 = f->link;
+			f->s1 = f1;
+			f1->p1 = f;
+		}
+		if(p->to.type == D_BRANCH) {
+			if(p->to.u.branch == P)
+				fatal("pnil %P", p);
+			f1 = p->to.u.branch->opt;
+			if(f1 == nil)
+				fatal("fnil %P / %P", p, p->to.u.branch);
+			if(f1 == f) {
+				//fatal("self loop %P", p);
+				continue;
+			}
+			f->s2 = f1;
+			f->p2link = f1->p2;
+			f1->p2 = f;
+		}
+	}
+	
+	graph->start = start;
+	graph->num = nf;
+	return graph;
+}
+
+void
+flowend(Graph *graph)
+{
+	Flow *f;
+	
+	for(f = graph->start; f != nil; f = f->link)
+		f->prog->opt = nil;
+	free(graph);
+}
+
+/*
+ * find looping structure
+ *
+ * 1) find reverse postordering
+ * 2) find approximate dominators,
+ *	the actual dominators if the flow graph is reducible
+ *	otherwise, dominators plus some other non-dominators.
+ *	See Matthew S. Hecht and Jeffrey D. Ullman,
+ *	"Analysis of a Simple Algorithm for Global Data Flow Problems",
+ *	Conf.  Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts,
+ *	Oct. 1-3, 1973, pp.  207-217.
+ * 3) find all nodes with a predecessor dominated by the current node.
+ *	such a node is a loop head.
+ *	recursively, all preds with a greater rpo number are in the loop
+ */
+static int32
+postorder(Flow *r, Flow **rpo2r, int32 n)
+{
+	Flow *r1;
+
+	r->rpo = 1;
+	r1 = r->s1;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	r1 = r->s2;
+	if(r1 && !r1->rpo)
+		n = postorder(r1, rpo2r, n);
+	rpo2r[n] = r;
+	n++;
+	return n;
+}
+
+static int32
+rpolca(int32 *idom, int32 rpo1, int32 rpo2)
+{
+	int32 t;
+
+	if(rpo1 == -1)
+		return rpo2;
+	while(rpo1 != rpo2){
+		if(rpo1 > rpo2){
+			t = rpo2;
+			rpo2 = rpo1;
+			rpo1 = t;
+		}
+		while(rpo1 < rpo2){
+			t = idom[rpo2];
+			if(t >= rpo2)
+				fatal("bad idom");
+			rpo2 = t;
+		}
+	}
+	return rpo1;
+}
+
+static int
+doms(int32 *idom, int32 r, int32 s)
+{
+	while(s > r)
+		s = idom[s];
+	return s == r;
+}
+
+static int
+loophead(int32 *idom, Flow *r)
+{
+	int32 src;
+
+	src = r->rpo;
+	if(r->p1 != nil && doms(idom, src, r->p1->rpo))
+		return 1;
+	for(r = r->p2; r != nil; r = r->p2link)
+		if(doms(idom, src, r->rpo))
+			return 1;
+	return 0;
+}
+
+static void
+loopmark(Flow **rpo2r, int32 head, Flow *r)
+{
+	if(r->rpo < head || r->active == head)
+		return;
+	r->active = head;
+	r->loop += LOOP;
+	if(r->p1 != nil)
+		loopmark(rpo2r, head, r->p1);
+	for(r = r->p2; r != nil; r = r->p2link)
+		loopmark(rpo2r, head, r);
+}
+
+void
+flowrpo(Graph *g)
+{
+	Flow *r1;
+	int32 i, d, me, nr, *idom;
+	Flow **rpo2r;
+
+	free(g->rpo);
+	g->rpo = calloc(g->num*sizeof g->rpo[0], 1);
+	idom = calloc(g->num*sizeof idom[0], 1);
+	if(g->rpo == nil || idom == nil)
+		fatal("out of memory");
+
+	for(r1 = g->start; r1 != nil; r1 = r1->link)
+		r1->active = 0;
+
+	rpo2r = g->rpo;
+	d = postorder(g->start, rpo2r, 0);
+	nr = g->num;
+	if(d > nr)
+		fatal("too many reg nodes %d %d", d, nr);
+	nr = d;
+	for(i = 0; i < nr / 2; i++) {
+		r1 = rpo2r[i];
+		rpo2r[i] = rpo2r[nr - 1 - i];
+		rpo2r[nr - 1 - i] = r1;
+	}
+	for(i = 0; i < nr; i++)
+		rpo2r[i]->rpo = i;
+
+	idom[0] = 0;
+	for(i = 0; i < nr; i++) {
+		r1 = rpo2r[i];
+		me = r1->rpo;
+		d = -1;
+		// rpo2r[r->rpo] == r protects against considering dead code,
+		// which has r->rpo == 0.
+		if(r1->p1 != nil && rpo2r[r1->p1->rpo] == r1->p1 && r1->p1->rpo < me)
+			d = r1->p1->rpo;
+		for(r1 = r1->p2; r1 != nil; r1 = r1->p2link)
+			if(rpo2r[r1->rpo] == r1 && r1->rpo < me)
+				d = rpolca(idom, d, r1->rpo);
+		idom[i] = d;
+	}
+
+	for(i = 0; i < nr; i++) {
+		r1 = rpo2r[i];
+		r1->loop++;
+		if(r1->p2 != nil && loophead(idom, r1))
+			loopmark(rpo2r, i, r1);
+	}
+	free(idom);
+
+	for(r1 = g->start; r1 != nil; r1 = r1->link)
+		r1->active = 0;
+}
+
+Flow*
+uniqp(Flow *r)
+{
+	Flow *r1;
+
+	r1 = r->p1;
+	if(r1 == nil) {
+		r1 = r->p2;
+		if(r1 == nil || r1->p2link != nil)
+			return nil;
+	} else
+		if(r->p2 != nil)
+			return nil;
+	return r1;
+}
+
+Flow*
+uniqs(Flow *r)
+{
+	Flow *r1;
+
+	r1 = r->s1;
+	if(r1 == nil) {
+		r1 = r->s2;
+		if(r1 == nil)
+			return nil;
+	} else
+		if(r->s2 != nil)
+			return nil;
+	return r1;
+}
+
+// The compilers assume they can generate temporary variables
+// as needed to preserve the right semantics or simplify code
+// generation and the back end will still generate good code.
+// This results in a large number of ephemeral temporary variables.
+// Merge temps with non-overlapping lifetimes and equal types using the
+// greedy algorithm in Poletto and Sarkar, "Linear Scan Register Allocation",
+// ACM TOPLAS 1999.
+
+typedef struct TempVar TempVar;
+typedef struct TempFlow TempFlow;
+
+struct TempVar
+{
+	Node *node;
+	TempFlow *def; // definition of temp var
+	TempFlow *use; // use list, chained through TempFlow.uselink
+	TempVar *freelink; // next free temp in Type.opt list
+	TempVar *merge; // merge var with this one
+	vlong start; // smallest Prog.pc in live range
+	vlong end; // largest Prog.pc in live range
+	uchar addr; // address taken - no accurate end
+	uchar removed; // removed from program
+};
+
+struct TempFlow
+{
+	Flow	f;
+	TempFlow *uselink;
+};
+
+static int
+startcmp(const void *va, const void *vb)
+{
+	TempVar *a, *b;
+	
+	a = *(TempVar**)va;
+	b = *(TempVar**)vb;
+
+	if(a->start < b->start)
+		return -1;
+	if(a->start > b->start)
+		return +1;
+	return 0;
+}
+
+// Is n available for merging?
+static int
+canmerge(Node *n)
+{
+	return n->class == PAUTO && strncmp(n->sym->name, "autotmp", 7) == 0;
+}
+
+static void mergewalk(TempVar*, TempFlow*, uint32);
+static void varkillwalk(TempVar*, TempFlow*, uint32);
+
+void
+mergetemp(Prog *firstp)
+{
+	int i, j, nvar, ninuse, nfree, nkill;
+	TempVar *var, *v, *v1, **bystart, **inuse;
+	TempFlow *r;
+	NodeList *l, **lp;
+	Node *n;
+	Prog *p, *p1;
+	Type *t;
+	ProgInfo info, info1;
+	int32 gen;
+	Graph *g;
+
+	enum { Debug = 0 };
+
+	g = flowstart(firstp, sizeof(TempFlow));
+	if(g == nil)
+		return;
+	
+	// Build list of all mergeable variables.
+	nvar = 0;
+	for(l = curfn->dcl; l != nil; l = l->next)
+		if(canmerge(l->n))
+			nvar++;
+	
+	var = calloc(nvar*sizeof var[0], 1);
+	nvar = 0;
+	for(l = curfn->dcl; l != nil; l = l->next) {
+		n = l->n;
+		if(canmerge(n)) {
+			v = &var[nvar++];
+			n->opt = v;
+			v->node = n;
+		}
+	}
+	
+	// Build list of uses.
+	// We assume that the earliest reference to a temporary is its definition.
+	// This is not true of variables in general but our temporaries are all
+	// single-use (that's why we have so many!).
+	for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) {
+		p = r->f.prog;
+		proginfo(&info, p);
+
+		if(p->from.node != N && p->from.node->opt && p->to.node != N && p->to.node->opt)
+			fatal("double node %P", p);
+		if((n = p->from.node) != N && (v = n->opt) != nil ||
+		   (n = p->to.node) != N && (v = n->opt) != nil) {
+		   	if(v->def == nil)
+		   		v->def = r;
+			r->uselink = v->use;
+			v->use = r;
+			if(n == p->from.node && (info.flags & LeftAddr))
+				v->addr = 1;
+		}
+	}
+	
+	if(Debug > 1)
+		dumpit("before", g->start, 0);
+	
+	nkill = 0;
+
+	// Special case.
+	for(v = var; v < var+nvar; v++) {
+		if(v->addr)
+			continue;
+		// Used in only one instruction, which had better be a write.
+		if((r = v->use) != nil && r->uselink == nil) {
+			p = r->f.prog;
+			proginfo(&info, p);
+			if(p->to.node == v->node && (info.flags & RightWrite) && !(info.flags & RightRead)) {
+				p->as = ANOP;
+				p->to = zprog.to;
+				v->removed = 1;
+				if(Debug)
+					print("drop write-only %S\n", v->node->sym);
+			} else
+				fatal("temp used and not set: %P", p);
+			nkill++;
+			continue;
+		}
+		
+		// Written in one instruction, read in the next, otherwise unused,
+		// no jumps to the next instruction. Happens mainly in 386 compiler.
+		if((r = v->use) != nil && r->f.link == &r->uselink->f && r->uselink->uselink == nil && uniqp(r->f.link) == &r->f) {
+			p = r->f.prog;
+			proginfo(&info, p);
+			p1 = r->f.link->prog;
+			proginfo(&info1, p1);
+			enum {
+				SizeAny = SizeB | SizeW | SizeL | SizeQ | SizeF | SizeD,
+			};
+			if(p->from.node == v->node && p1->to.node == v->node && (info.flags & Move) &&
+			   !((info.flags|info1.flags) & (LeftAddr|RightAddr)) &&
+			   (info.flags & SizeAny) == (info1.flags & SizeAny)) {
+				p1->from = p->from;
+				excise(&r->f);
+				v->removed = 1;
+				if(Debug)
+					print("drop immediate-use %S\n", v->node->sym);
+			}
+			nkill++;
+			continue;
+		}			   
+	}
+
+	// Traverse live range of each variable to set start, end.
+	// Each flood uses a new value of gen so that we don't have
+	// to clear all the r->f.active words after each variable.
+	gen = 0;
+	for(v = var; v < var+nvar; v++) {
+		gen++;
+		for(r = v->use; r != nil; r = r->uselink)
+			mergewalk(v, r, gen);
+		if(v->addr) {
+			gen++;
+			for(r = v->use; r != nil; r = r->uselink)
+				varkillwalk(v, r, gen);
+		}
+	}
+
+	// Sort variables by start.
+	bystart = malloc(nvar*sizeof bystart[0]);
+	for(i=0; i<nvar; i++)
+		bystart[i] = &var[i];
+	qsort(bystart, nvar, sizeof bystart[0], startcmp);
+
+	// List of in-use variables, sorted by end, so that the ones that
+	// will last the longest are the earliest ones in the array.
+	// The tail inuse[nfree:] holds no-longer-used variables.
+	// In theory we should use a sorted tree so that insertions are
+	// guaranteed O(log n) and then the loop is guaranteed O(n log n).
+	// In practice, it doesn't really matter.
+	inuse = malloc(nvar*sizeof inuse[0]);
+	ninuse = 0;
+	nfree = nvar;
+	for(i=0; i<nvar; i++) {
+		v = bystart[i];
+		if(v->removed)
+			continue;
+
+		// Expire no longer in use.
+		while(ninuse > 0 && inuse[ninuse-1]->end < v->start) {
+			v1 = inuse[--ninuse];
+			inuse[--nfree] = v1;
+		}
+
+		// Find old temp to reuse if possible.
+		t = v->node->type;
+		for(j=nfree; j<nvar; j++) {
+			v1 = inuse[j];
+			// Require the types to match but also require the addrtaken bits to match.
+			// If a variable's address is taken, that disables registerization for the individual
+			// words of the variable (for example, the base,len,cap of a slice).
+			// We don't want to merge a non-addressed var with an addressed one and
+			// inhibit registerization of the former.
+			if(eqtype(t, v1->node->type) && v->node->addrtaken == v1->node->addrtaken) {
+				inuse[j] = inuse[nfree++];
+				if(v1->merge)
+					v->merge = v1->merge;
+				else
+					v->merge = v1;
+				nkill++;
+				break;
+			}
+		}
+
+		// Sort v into inuse.
+		j = ninuse++;
+		while(j > 0 && inuse[j-1]->end < v->end) {
+			inuse[j] = inuse[j-1];
+			j--;
+		}
+		inuse[j] = v;
+	}
+
+	if(Debug) {
+		print("%S [%d - %d]\n", curfn->nname->sym, nvar, nkill);
+		for(v=var; v<var+nvar; v++) {
+			print("var %#N %T %lld-%lld", v->node, v->node->type, v->start, v->end);
+			if(v->addr)
+				print(" addr=1");
+			if(v->removed)
+				print(" dead=1");
+			if(v->merge)
+				print(" merge %#N", v->merge->node);
+			if(v->start == v->end)
+				print(" %P", v->def->f.prog);
+			print("\n");
+		}
+	
+		if(Debug > 1)
+			dumpit("after", g->start, 0);
+	}
+
+	// Update node references to use merged temporaries.
+	for(r = (TempFlow*)g->start; r != nil; r = (TempFlow*)r->f.link) {
+		p = r->f.prog;
+		if((n = p->from.node) != N && (v = n->opt) != nil && v->merge != nil)
+			p->from.node = v->merge->node;
+		if((n = p->to.node) != N && (v = n->opt) != nil && v->merge != nil)
+			p->to.node = v->merge->node;
+	}
+
+	// Delete merged nodes from declaration list.
+	for(lp = &curfn->dcl; (l = *lp); ) {
+		curfn->dcl->end = l;
+		n = l->n;
+		v = n->opt;
+		if(v && (v->merge || v->removed)) {
+			*lp = l->next;
+			continue;
+		}
+		lp = &l->next;
+	}
+
+	// Clear aux structures.
+	for(v=var; v<var+nvar; v++)
+		v->node->opt = nil;
+	free(var);
+	free(bystart);
+	free(inuse);
+	flowend(g);
+}
+
+static void
+mergewalk(TempVar *v, TempFlow *r0, uint32 gen)
+{
+	Prog *p;
+	TempFlow *r1, *r, *r2;
+	
+	for(r1 = r0; r1 != nil; r1 = (TempFlow*)r1->f.p1) {
+		if(r1->f.active == gen)
+			break;
+		r1->f.active = gen;
+		p = r1->f.prog;
+		if(v->end < p->pc)
+			v->end = p->pc;
+		if(r1 == v->def) {
+			v->start = p->pc;
+			break;
+		}
+	}
+	
+	for(r = r0; r != r1; r = (TempFlow*)r->f.p1)
+		for(r2 = (TempFlow*)r->f.p2; r2 != nil; r2 = (TempFlow*)r2->f.p2link)
+			mergewalk(v, r2, gen);
+}
+
+static void
+varkillwalk(TempVar *v, TempFlow *r0, uint32 gen)
+{
+	Prog *p;
+	TempFlow *r1, *r;
+	
+	for(r1 = r0; r1 != nil; r1 = (TempFlow*)r1->f.s1) {
+		if(r1->f.active == gen)
+			break;
+		r1->f.active = gen;
+		p = r1->f.prog;
+		if(v->end < p->pc)
+			v->end = p->pc;
+		if(v->start > p->pc)
+			v->start = p->pc;
+		if(p->as == ARET || (p->as == AVARKILL && p->to.node == v->node))
+			break;
+	}
+	
+	for(r = r0; r != r1; r = (TempFlow*)r->f.s1)
+		varkillwalk(v, (TempFlow*)r->f.s2, gen);
+}
+
+// Eliminate redundant nil pointer checks.
+//
+// The code generation pass emits a CHECKNIL for every possibly nil pointer.
+// This pass removes a CHECKNIL if every predecessor path has already
+// checked this value for nil.
+//
+// Simple backwards flood from check to definition.
+// Run prog loop backward from end of program to beginning to avoid quadratic
+// behavior removing a run of checks.
+//
+// Assume that stack variables with address not taken can be loaded multiple times
+// from memory without being rechecked. Other variables need to be checked on
+// each load.
+	
+typedef struct NilVar NilVar;
+typedef struct NilFlow NilFlow;
+
+struct NilFlow {
+	Flow f;
+	int kill;
+};
+
+static void nilwalkback(NilFlow *rcheck);
+static void nilwalkfwd(NilFlow *rcheck);
+
+void
+nilopt(Prog *firstp)
+{
+	NilFlow *r;
+	Prog *p;
+	Graph *g;
+	int ncheck, nkill;
+
+	g = flowstart(firstp, sizeof(NilFlow));
+	if(g == nil)
+		return;
+
+	if(debug_checknil > 1 /* || strcmp(curfn->nname->sym->name, "f1") == 0 */)
+		dumpit("nilopt", g->start, 0);
+
+	ncheck = 0;
+	nkill = 0;
+	for(r = (NilFlow*)g->start; r != nil; r = (NilFlow*)r->f.link) {
+		p = r->f.prog;
+		if(p->as != ACHECKNIL || !regtyp(&p->from))
+			continue;
+		ncheck++;
+		if(stackaddr(&p->from)) {
+			if(debug_checknil && p->lineno > 1)
+				warnl(p->lineno, "removed nil check of SP address");
+			r->kill = 1;
+			continue;
+		}
+		nilwalkfwd(r);
+		if(r->kill) {
+			if(debug_checknil && p->lineno > 1)
+				warnl(p->lineno, "removed nil check before indirect");
+			continue;
+		}
+		nilwalkback(r);
+		if(r->kill) {
+			if(debug_checknil && p->lineno > 1)
+				warnl(p->lineno, "removed repeated nil check");
+			continue;
+		}
+	}
+	
+	for(r = (NilFlow*)g->start; r != nil; r = (NilFlow*)r->f.link) {
+		if(r->kill) {
+			nkill++;
+			excise(&r->f);
+		}
+	}
+
+	flowend(g);
+	
+	if(debug_checknil > 1)
+		print("%S: removed %d of %d nil checks\n", curfn->nname->sym, nkill, ncheck);
+}
+
+static void
+nilwalkback(NilFlow *rcheck)
+{
+	Prog *p;
+	ProgInfo info;
+	NilFlow *r;
+	
+	for(r = rcheck; r != nil; r = (NilFlow*)uniqp(&r->f)) {
+		p = r->f.prog;
+		proginfo(&info, p);
+		if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from)) {
+			// Found initialization of value we're checking for nil.
+			// without first finding the check, so this one is unchecked.
+			return;
+		}
+		if(r != rcheck && p->as == ACHECKNIL && sameaddr(&p->from, &rcheck->f.prog->from)) {
+			rcheck->kill = 1;
+			return;
+		}
+	}
+
+	// Here is a more complex version that scans backward across branches.
+	// It assumes rcheck->kill = 1 has been set on entry, and its job is to find a reason
+	// to keep the check (setting rcheck->kill = 0).
+	// It doesn't handle copying of aggregates as well as I would like,
+	// nor variables with their address taken,
+	// and it's too subtle to turn on this late in Go 1.2. Perhaps for Go 1.3.
+	/*
+	for(r1 = r0; r1 != nil; r1 = (NilFlow*)r1->f.p1) {
+		if(r1->f.active == gen)
+			break;
+		r1->f.active = gen;
+		p = r1->f.prog;
+		
+		// If same check, stop this loop but still check
+		// alternate predecessors up to this point.
+		if(r1 != rcheck && p->as == ACHECKNIL && sameaddr(&p->from, &rcheck->f.prog->from))
+			break;
+
+		proginfo(&info, p);
+		if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from)) {
+			// Found initialization of value we're checking for nil.
+			// without first finding the check, so this one is unchecked.
+			rcheck->kill = 0;
+			return;
+		}
+		
+		if(r1->f.p1 == nil && r1->f.p2 == nil) {
+			print("lost pred for %P\n", rcheck->f.prog);
+			for(r1=r0; r1!=nil; r1=(NilFlow*)r1->f.p1) {
+				proginfo(&info, r1->f.prog);
+				print("\t%P %d %d %D %D\n", r1->f.prog, info.flags&RightWrite, sameaddr(&r1->f.prog->to, &rcheck->f.prog->from), &r1->f.prog->to, &rcheck->f.prog->from);
+			}
+			fatal("lost pred trail");
+		}
+	}
+
+	for(r = r0; r != r1; r = (NilFlow*)r->f.p1)
+		for(r2 = (NilFlow*)r->f.p2; r2 != nil; r2 = (NilFlow*)r2->f.p2link)
+			nilwalkback(rcheck, r2, gen);
+	*/
+}
+
+static void
+nilwalkfwd(NilFlow *rcheck)
+{
+	NilFlow *r, *last;
+	Prog *p;
+	ProgInfo info;
+	
+	// If the path down from rcheck dereferences the address
+	// (possibly with a small offset) before writing to memory
+	// and before any subsequent checks, it's okay to wait for
+	// that implicit check. Only consider this basic block to
+	// avoid problems like:
+	//	_ = *x // should panic
+	//	for {} // no writes but infinite loop may be considered visible
+	last = nil;
+	for(r = (NilFlow*)uniqs(&rcheck->f); r != nil; r = (NilFlow*)uniqs(&r->f)) {
+		p = r->f.prog;
+		proginfo(&info, p);
+		
+		if((info.flags & LeftRead) && smallindir(&p->from, &rcheck->f.prog->from)) {
+			rcheck->kill = 1;
+			return;
+		}
+		if((info.flags & (RightRead|RightWrite)) && smallindir(&p->to, &rcheck->f.prog->from)) {
+			rcheck->kill = 1;
+			return;
+		}
+		
+		// Stop if another nil check happens.
+		if(p->as == ACHECKNIL)
+			return;
+		// Stop if value is lost.
+		if((info.flags & RightWrite) && sameaddr(&p->to, &rcheck->f.prog->from))
+			return;
+		// Stop if memory write.
+		if((info.flags & RightWrite) && !regtyp(&p->to))
+			return;
+		// Stop if we jump backward.
+		// This test is valid because all the NilFlow* are pointers into
+		// a single contiguous array. We will need to add an explicit
+		// numbering when the code is converted to Go.
+		if(last != nil && r <= last)
+			return;
+		last = r;
+	}
+}
diff --git a/src/cmd/gc/popt.h b/src/cmd/gc/popt.h
new file mode 100644
index 0000000..8d5dfff
--- /dev/null
+++ b/src/cmd/gc/popt.h
@@ -0,0 +1,46 @@
+// Copyright 2013 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+typedef struct Flow Flow;
+typedef struct Graph Graph;
+
+struct Flow {
+	Prog*	prog;   	// actual instruction
+	Flow*	p1;     	// predecessors of this instruction: p1,
+	Flow*	p2;     	// and then p2 linked though p2link.
+	Flow*	p2link;
+	Flow*	s1;     	// successors of this instruction (at most two: s1 and s2).
+	Flow*	s2;
+	Flow*	link;   	// next instruction in function code
+	
+	int32	active;	// usable by client
+
+	int32	rpo;		// reverse post ordering
+	uint16	loop;		// x5 for every loop
+	uchar	refset;		// diagnostic generated
+};
+
+struct Graph
+{
+	Flow*	start;
+	int	num;
+	
+	// After calling flowrpo, rpo lists the flow nodes in reverse postorder,
+	// and each non-dead Flow node f has g->rpo[f->rpo] == f.
+	Flow**	rpo;
+};
+
+void	fixjmp(Prog*);
+Graph*	flowstart(Prog*, int);
+void	flowrpo(Graph*);
+void	flowend(Graph*);
+void	mergetemp(Prog*);
+void	nilopt(Prog*);
+int	noreturn(Prog*);
+int	regtyp(Addr*);
+int	sameaddr(Addr*, Addr*);
+int	smallindir(Addr*, Addr*);
+int	stackaddr(Addr*);
+Flow*	uniqp(Flow*);
+Flow*	uniqs(Flow*);
diff --git a/src/cmd/gc/racewalk.c b/src/cmd/gc/racewalk.c
new file mode 100644
index 0000000..c9e27fe
--- /dev/null
+++ b/src/cmd/gc/racewalk.c
@@ -0,0 +1,664 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// The racewalk pass modifies the code tree for the function as follows:
+//
+// 1. It inserts a call to racefuncenter at the beginning of each function.
+// 2. It inserts a call to racefuncexit at the end of each function.
+// 3. It inserts a call to raceread before each memory read.
+// 4. It inserts a call to racewrite before each memory write.
+//
+// The rewriting is not yet complete. Certain nodes are not rewritten
+// but should be.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+// TODO(dvyukov): do not instrument initialization as writes:
+// a := make([]int, 10)
+
+static void racewalklist(NodeList *l, NodeList **init);
+static void racewalknode(Node **np, NodeList **init, int wr, int skip);
+static int callinstr(Node **n, NodeList **init, int wr, int skip);
+static Node* uintptraddr(Node *n);
+static void makeaddable(Node *n);
+static Node* basenod(Node *n);
+static void foreach(Node *n, void(*f)(Node*, void*), void *c);
+static void hascallspred(Node *n, void *c);
+static void appendinit(Node **np, NodeList *init);
+static Node* detachexpr(Node *n, NodeList **init);
+
+// Do not instrument the following packages at all,
+// at best instrumentation would cause infinite recursion.
+static const char *omit_pkgs[] = {"runtime", "runtime/race"};
+// Only insert racefuncenter/racefuncexit into the following packages.
+// Memory accesses in the packages are either uninteresting or will cause false positives.
+static const char *noinst_pkgs[] = {"sync", "sync/atomic"};
+
+static int
+ispkgin(const char **pkgs, int n)
+{
+	int i;
+
+	if(myimportpath) {
+		for(i=0; i<n; i++) {
+			if(strcmp(myimportpath, pkgs[i]) == 0)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+static int
+isforkfunc(Node *fn)
+{
+	// Special case for syscall.forkAndExecInChild.
+	// In the child, this function must not acquire any locks, because
+	// they might have been locked at the time of the fork.  This means
+	// no rescheduling, no malloc calls, and no new stack segments.
+	// Race instrumentation does all of the above.
+	return myimportpath != nil && strcmp(myimportpath, "syscall") == 0 &&
+		strcmp(fn->nname->sym->name, "forkAndExecInChild") == 0;
+}
+
+void
+racewalk(Node *fn)
+{
+	Node *nd;
+	Node *nodpc;
+	char s[1024];
+
+	if(ispkgin(omit_pkgs, nelem(omit_pkgs)) || isforkfunc(fn))
+		return;
+
+	if(!ispkgin(noinst_pkgs, nelem(noinst_pkgs))) {
+		racewalklist(fn->nbody, nil);
+		// nothing interesting for race detector in fn->enter
+		racewalklist(fn->exit, nil);
+	}
+
+	// nodpc is the PC of the caller as extracted by
+	// getcallerpc. We use -widthptr(FP) for x86.
+	// BUG: this will not work on arm.
+	nodpc = nod(OXXX, nil, nil);
+	*nodpc = *nodfp;
+	nodpc->type = types[TUINTPTR];
+	nodpc->xoffset = -widthptr;
+	nd = mkcall("racefuncenter", T, nil, nodpc);
+	fn->enter = concat(list1(nd), fn->enter);
+	nd = mkcall("racefuncexit", T, nil);
+	fn->exit = list(fn->exit, nd);
+
+	if(debug['W']) {
+		snprint(s, sizeof(s), "after racewalk %S", fn->nname->sym);
+		dumplist(s, fn->nbody);
+		snprint(s, sizeof(s), "enter %S", fn->nname->sym);
+		dumplist(s, fn->enter);
+		snprint(s, sizeof(s), "exit %S", fn->nname->sym);
+		dumplist(s, fn->exit);
+	}
+}
+
+static void
+racewalklist(NodeList *l, NodeList **init)
+{
+	NodeList *instr;
+
+	for(; l; l = l->next) {
+		instr = nil;
+		racewalknode(&l->n, &instr, 0, 0);
+		if(init == nil)
+			l->n->ninit = concat(l->n->ninit, instr);
+		else
+			*init = concat(*init, instr);
+	}
+}
+
+// walkexpr and walkstmt combined
+// walks the tree and adds calls to the
+// instrumentation code to top-level (statement) nodes' init
+static void
+racewalknode(Node **np, NodeList **init, int wr, int skip)
+{
+	Node *n, *n1;
+	NodeList *l;
+	NodeList *fini;
+
+	n = *np;
+
+	if(n == N)
+		return;
+
+	if(debug['w'] > 1)
+		dump("racewalk-before", n);
+	setlineno(n);
+	if(init == nil)
+		fatal("racewalk: bad init list");
+	if(init == &n->ninit) {
+		// If init == &n->ninit and n->ninit is non-nil,
+		// racewalknode might append it to itself.
+		// nil it out and handle it separately before putting it back.
+		l = n->ninit;
+		n->ninit = nil;
+		racewalklist(l, nil);
+		racewalknode(&n, &l, wr, skip);  // recurse with nil n->ninit
+		appendinit(&n, l);
+		*np = n;
+		return;
+	}
+
+	racewalklist(n->ninit, nil);
+
+	switch(n->op) {
+	default:
+		fatal("racewalk: unknown node type %O", n->op);
+
+	case OASOP:
+	case OAS:
+	case OAS2:
+	case OAS2RECV:
+	case OAS2FUNC:
+	case OAS2MAPR:
+		racewalknode(&n->left, init, 1, 0);
+		racewalknode(&n->right, init, 0, 0);
+		goto ret;
+
+	case OCFUNC:
+	case OVARKILL:
+		// can't matter
+		goto ret;
+
+	case OBLOCK:
+		if(n->list == nil)
+			goto ret;
+
+		switch(n->list->n->op) {
+		case OCALLFUNC:
+		case OCALLMETH:
+		case OCALLINTER:
+			// Blocks are used for multiple return function calls.
+			// x, y := f() becomes BLOCK{CALL f, AS x [SP+0], AS y [SP+n]}
+			// We don't want to instrument between the statements because it will
+			// smash the results.
+			racewalknode(&n->list->n, &n->list->n->ninit, 0, 0);
+			fini = nil;
+			racewalklist(n->list->next, &fini);
+			n->list = concat(n->list, fini);
+			break;
+
+		default:
+			// Ordinary block, for loop initialization or inlined bodies.
+			racewalklist(n->list, nil);
+			break;
+		}
+		goto ret;
+
+	case ODEFER:
+		racewalknode(&n->left, init, 0, 0);
+		goto ret;
+
+	case OPROC:
+		racewalknode(&n->left, init, 0, 0);
+		goto ret;
+
+	case OCALLINTER:
+		racewalknode(&n->left, init, 0, 0);
+		goto ret;
+
+	case OCALLFUNC:
+		// Instrument dst argument of runtime.writebarrier* calls
+		// as we do not instrument runtime code.
+		if(n->left->sym != S && n->left->sym->pkg == runtimepkg && strncmp(n->left->sym->name, "writebarrier", 12) == 0) {
+			// Find the dst argument.
+			// The list can be reordered, so it's not necessary just the first or the second element.
+			for(l = n->list; l; l = l->next) {
+				if(strcmp(n->left->sym->name, "writebarrierfat") == 0) {
+					if(l->n->left->xoffset == widthptr)
+						break;
+				} else {
+					if(l->n->left->xoffset == 0)
+						break;
+				}
+			}
+			if(l == nil)
+				fatal("racewalk: writebarrier no arg");
+			if(l->n->right->op != OADDR)
+				fatal("racewalk: writebarrier bad arg");
+			callinstr(&l->n->right->left, init, 1, 0);
+		}
+		racewalknode(&n->left, init, 0, 0);
+		goto ret;
+
+	case ONOT:
+	case OMINUS:
+	case OPLUS:
+	case OREAL:
+	case OIMAG:
+	case OCOM:
+		racewalknode(&n->left, init, wr, 0);
+		goto ret;
+
+	case ODOTINTER:
+		racewalknode(&n->left, init, 0, 0);
+		goto ret;
+
+	case ODOT:
+		racewalknode(&n->left, init, 0, 1);
+		callinstr(&n, init, wr, skip);
+		goto ret;
+
+	case ODOTPTR: // dst = (*x).f with implicit *; otherwise it's ODOT+OIND
+		racewalknode(&n->left, init, 0, 0);
+		callinstr(&n, init, wr, skip);
+		goto ret;
+
+	case OIND: // *p
+		racewalknode(&n->left, init, 0, 0);
+		callinstr(&n, init, wr, skip);
+		goto ret;
+
+	case OSPTR:
+	case OLEN:
+	case OCAP:
+		racewalknode(&n->left, init, 0, 0);
+		if(istype(n->left->type, TMAP)) {
+			n1 = nod(OCONVNOP, n->left, N);
+			n1->type = ptrto(types[TUINT8]);
+			n1 = nod(OIND, n1, N);
+			typecheck(&n1, Erv);
+			callinstr(&n1, init, 0, skip);
+		}
+		goto ret;
+
+	case OLSH:
+	case ORSH:
+	case OLROT:
+	case OAND:
+	case OANDNOT:
+	case OOR:
+	case OXOR:
+	case OSUB:
+	case OMUL:
+	case OHMUL:
+	case OEQ:
+	case ONE:
+	case OLT:
+	case OLE:
+	case OGE:
+	case OGT:
+	case OADD:
+	case OCOMPLEX:
+		racewalknode(&n->left, init, wr, 0);
+		racewalknode(&n->right, init, wr, 0);
+		goto ret;
+
+	case OANDAND:
+	case OOROR:
+		racewalknode(&n->left, init, wr, 0);
+		// walk has ensured the node has moved to a location where
+		// side effects are safe.
+		// n->right may not be executed,
+		// so instrumentation goes to n->right->ninit, not init.
+		racewalknode(&n->right, &n->right->ninit, wr, 0);
+		goto ret;
+
+	case ONAME:
+		callinstr(&n, init, wr, skip);
+		goto ret;
+
+	case OCONV:
+		racewalknode(&n->left, init, wr, 0);
+		goto ret;
+
+	case OCONVNOP:
+		racewalknode(&n->left, init, wr, 0);
+		goto ret;
+
+	case ODIV:
+	case OMOD:
+		racewalknode(&n->left, init, wr, 0);
+		racewalknode(&n->right, init, wr, 0);
+		goto ret;
+
+	case OINDEX:
+		if(!isfixedarray(n->left->type))
+			racewalknode(&n->left, init, 0, 0);
+		else if(!islvalue(n->left)) {
+			// index of unaddressable array, like Map[k][i].
+			racewalknode(&n->left, init, wr, 0);
+			racewalknode(&n->right, init, 0, 0);
+			goto ret;
+		}
+		racewalknode(&n->right, init, 0, 0);
+		if(n->left->type->etype != TSTRING)
+			callinstr(&n, init, wr, skip);
+		goto ret;
+
+	case OSLICE:
+	case OSLICEARR:
+	case OSLICE3:
+	case OSLICE3ARR:
+		// Seems to only lead to double instrumentation.
+		//racewalknode(&n->left, init, 0, 0);
+		goto ret;
+
+	case OADDR:
+		racewalknode(&n->left, init, 0, 1);
+		goto ret;
+
+	case OEFACE:
+		racewalknode(&n->left, init, 0, 0);
+		racewalknode(&n->right, init, 0, 0);
+		goto ret;
+
+	case OITAB:
+		racewalknode(&n->left, init, 0, 0);
+		goto ret;
+
+	// should not appear in AST by now
+	case OSEND:
+	case ORECV:
+	case OCLOSE:
+	case ONEW:
+	case OXCASE:
+	case OXFALL:
+	case OCASE:
+	case OPANIC:
+	case ORECOVER:
+	case OCONVIFACE:
+	case OCMPIFACE:
+	case OMAKECHAN:
+	case OMAKEMAP:
+	case OMAKESLICE:
+	case OCALL:
+	case OCOPY:
+	case OAPPEND:
+	case ORUNESTR:
+	case OARRAYBYTESTR:
+	case OARRAYRUNESTR:
+	case OSTRARRAYBYTE:
+	case OSTRARRAYRUNE:
+	case OINDEXMAP:  // lowered to call
+	case OCMPSTR:
+	case OADDSTR:
+	case ODOTTYPE:
+	case ODOTTYPE2:
+	case OAS2DOTTYPE:
+	case OCALLPART: // lowered to PTRLIT
+	case OCLOSURE:  // lowered to PTRLIT
+	case ORANGE:    // lowered to ordinary for loop
+	case OARRAYLIT: // lowered to assignments
+	case OMAPLIT:
+	case OSTRUCTLIT:
+		yyerror("racewalk: %O must be lowered by now", n->op);
+		goto ret;
+
+	// impossible nodes: only appear in backend.
+	case ORROTC:
+	case OEXTEND:
+		yyerror("racewalk: %O cannot exist now", n->op);
+		goto ret;
+
+	// just do generic traversal
+	case OFOR:
+	case OIF:
+	case OCALLMETH:
+	case ORETURN:
+	case ORETJMP:
+	case OSWITCH:
+	case OSELECT:
+	case OEMPTY:
+	case OBREAK:
+	case OCONTINUE:
+	case OFALL:
+	case OGOTO:
+	case OLABEL:
+		goto ret;
+
+	// does not require instrumentation
+	case OPRINT:     // don't bother instrumenting it
+	case OPRINTN:    // don't bother instrumenting it
+	case OCHECKNIL: // always followed by a read.
+	case OPARAM:     // it appears only in fn->exit to copy heap params back
+	case OCLOSUREVAR:// immutable pointer to captured variable
+	case ODOTMETH:   // either part of CALLMETH or CALLPART (lowered to PTRLIT)
+	case OINDREG:    // at this stage, only n(SP) nodes from nodarg
+	case ODCL:       // declarations (without value) cannot be races
+	case ODCLCONST:
+	case ODCLTYPE:
+	case OTYPE:
+	case ONONAME:
+	case OLITERAL:
+	case OSLICESTR:  // always preceded by bounds checking, avoid double instrumentation.
+	case OTYPESW:    // ignored by code generation, do not instrument.
+		goto ret;
+	}
+
+ret:
+	if(n->op != OBLOCK)  // OBLOCK is handled above in a special way.
+		racewalklist(n->list, init);
+	if(n->ntest != N)
+		racewalknode(&n->ntest, &n->ntest->ninit, 0, 0);
+	if(n->nincr != N)
+		racewalknode(&n->nincr, &n->nincr->ninit, 0, 0);
+	racewalklist(n->nbody, nil);
+	racewalklist(n->nelse, nil);
+	racewalklist(n->rlist, nil);
+	*np = n;
+}
+
+static int
+isartificial(Node *n)
+{
+	// compiler-emitted artificial things that we do not want to instrument,
+	// cant' possibly participate in a data race.
+	if(n->op == ONAME && n->sym != S && n->sym->name != nil) {
+		if(strcmp(n->sym->name, "_") == 0)
+			return 1;
+		// autotmp's are always local
+		if(strncmp(n->sym->name, "autotmp_", sizeof("autotmp_")-1) == 0)
+			return 1;
+		// statictmp's are read-only
+		if(strncmp(n->sym->name, "statictmp_", sizeof("statictmp_")-1) == 0)
+			return 1;
+		// go.itab is accessed only by the compiler and runtime (assume safe)
+		if(n->sym->pkg && n->sym->pkg->name && strcmp(n->sym->pkg->name, "go.itab") == 0)
+			return 1;
+	}
+	return 0;
+}
+
+static int
+callinstr(Node **np, NodeList **init, int wr, int skip)
+{
+	Node *f, *b, *n;
+	Type *t;
+	int class, hascalls;
+
+	n = *np;
+	//print("callinstr for %+N [ %O ] etype=%E class=%d\n",
+	//	  n, n->op, n->type ? n->type->etype : -1, n->class);
+
+	if(skip || n->type == T || n->type->etype >= TIDEAL)
+		return 0;
+	t = n->type;
+	if(isartificial(n))
+		return 0;
+
+	b = basenod(n);
+	// it skips e.g. stores to ... parameter array
+	if(isartificial(b))
+		return 0;
+	class = b->class;
+	// BUG: we _may_ want to instrument PAUTO sometimes
+	// e.g. if we've got a local variable/method receiver
+	// that has got a pointer inside. Whether it points to
+	// the heap or not is impossible to know at compile time
+	if((class&PHEAP) || class == PPARAMREF || class == PEXTERN
+		|| b->op == OINDEX || b->op == ODOTPTR || b->op == OIND || b->op == OXDOT) {
+		hascalls = 0;
+		foreach(n, hascallspred, &hascalls);
+		if(hascalls) {
+			n = detachexpr(n, init);
+			*np = n;
+		}
+		n = treecopy(n);
+		makeaddable(n);
+		if(t->etype == TSTRUCT || isfixedarray(t)) {
+			f = mkcall(wr ? "racewriterange" : "racereadrange", T, init, uintptraddr(n),
+					nodintconst(t->width));
+		} else
+			f = mkcall(wr ? "racewrite" : "raceread", T, init, uintptraddr(n));
+		*init = list(*init, f);
+		return 1;
+	}
+	return 0;
+}
+
+// makeaddable returns a node whose memory location is the
+// same as n, but which is addressable in the Go language
+// sense.
+// This is different from functions like cheapexpr that may make
+// a copy of their argument.
+static void
+makeaddable(Node *n)
+{
+	// The arguments to uintptraddr technically have an address but
+	// may not be addressable in the Go sense: for example, in the case
+	// of T(v).Field where T is a struct type and v is
+	// an addressable value.
+	switch(n->op) {
+	case OINDEX:
+		if(isfixedarray(n->left->type))
+			makeaddable(n->left);
+		break;
+	case ODOT:
+	case OXDOT:
+		// Turn T(v).Field into v.Field
+		if(n->left->op == OCONVNOP)
+			n->left = n->left->left;
+		makeaddable(n->left);
+		break;
+	case ODOTPTR:
+	default:
+		// nothing to do
+		break;
+	}
+}
+
+static Node*
+uintptraddr(Node *n)
+{
+	Node *r;
+
+	r = nod(OADDR, n, N);
+	r->bounded = 1;
+	r = conv(r, types[TUNSAFEPTR]);
+	r = conv(r, types[TUINTPTR]);
+	return r;
+}
+
+// basenod returns the simplest child node of n pointing to the same
+// memory area.
+static Node*
+basenod(Node *n)
+{
+	for(;;) {
+		if(n->op == ODOT || n->op == OXDOT || n->op == OCONVNOP || n->op == OCONV || n->op == OPAREN) {
+			n = n->left;
+			continue;
+		}
+		if(n->op == OINDEX && isfixedarray(n->type)) {
+			n = n->left;
+			continue;
+		}
+		break;
+	}
+	return n;
+}
+
+static Node*
+detachexpr(Node *n, NodeList **init)
+{
+	Node *addr, *as, *ind, *l;
+
+	addr = nod(OADDR, n, N);
+	l = temp(ptrto(n->type));
+	as = nod(OAS, l, addr);
+	typecheck(&as, Etop);
+	walkexpr(&as, init);
+	*init = list(*init, as);
+	ind = nod(OIND, l, N);
+	typecheck(&ind, Erv);
+	walkexpr(&ind, init);
+	return ind;
+}
+
+static void
+foreachnode(Node *n, void(*f)(Node*, void*), void *c)
+{
+	if(n)
+		f(n, c);
+}
+
+static void
+foreachlist(NodeList *l, void(*f)(Node*, void*), void *c)
+{
+	for(; l; l = l->next)
+		foreachnode(l->n, f, c);
+}
+
+static void
+foreach(Node *n, void(*f)(Node*, void*), void *c)
+{
+	foreachlist(n->ninit, f, c);
+	foreachnode(n->left, f, c);
+	foreachnode(n->right, f, c);
+	foreachlist(n->list, f, c);
+	foreachnode(n->ntest, f, c);
+	foreachnode(n->nincr, f, c);
+	foreachlist(n->nbody, f, c);
+	foreachlist(n->nelse, f, c);
+	foreachlist(n->rlist, f, c);
+}
+
+static void
+hascallspred(Node *n, void *c)
+{
+	switch(n->op) {
+	case OCALL:
+	case OCALLFUNC:
+	case OCALLMETH:
+	case OCALLINTER:
+		(*(int*)c)++;
+	}
+}
+
+// appendinit is like addinit in subr.c
+// but appends rather than prepends.
+static void
+appendinit(Node **np, NodeList *init)
+{
+	Node *n;
+
+	if(init == nil)
+		return;
+
+	n = *np;
+	switch(n->op) {
+	case ONAME:
+	case OLITERAL:
+		// There may be multiple refs to this node;
+		// introduce OCONVNOP to hold init list.
+		n = nod(OCONVNOP, n, N);
+		n->type = n->left->type;
+		n->typecheck = 1;
+		*np = n;
+		break;
+	}
+	n->ninit = concat(n->ninit, init);
+	n->ullman = UINF;
+}
+
diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c
new file mode 100644
index 0000000..4ed4528
--- /dev/null
+++ b/src/cmd/gc/range.c
@@ -0,0 +1,296 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * range
+ */
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+void
+typecheckrange(Node *n)
+{
+	char *why;
+	Type *t, *t1, *t2;
+	Node *v1, *v2;
+	NodeList *ll;
+
+	// delicate little dance.  see typecheckas2
+	for(ll=n->list; ll; ll=ll->next)
+		if(ll->n->defn != n)
+			typecheck(&ll->n, Erv | Easgn);
+
+	typecheck(&n->right, Erv);
+	if((t = n->right->type) == T)
+		goto out;
+	if(isptr[t->etype] && isfixedarray(t->type))
+		t = t->type;
+	n->type = t;
+
+	switch(t->etype) {
+	default:
+		yyerror("cannot range over %lN", n->right);
+		goto out;
+
+	case TARRAY:
+		t1 = types[TINT];
+		t2 = t->type;
+		break;
+
+	case TMAP:
+		t1 = t->down;
+		t2 = t->type;
+		break;
+
+	case TCHAN:
+		if(!(t->chan & Crecv)) {
+			yyerror("invalid operation: range %N (receive from send-only type %T)", n->right, n->right->type);
+			goto out;
+		}
+		t1 = t->type;
+		t2 = nil;
+		if(count(n->list) == 2)
+			goto toomany;
+		break;
+
+	case TSTRING:
+		t1 = types[TINT];
+		t2 = runetype;
+		break;
+	}
+
+	if(count(n->list) > 2) {
+	toomany:
+		yyerror("too many variables in range");
+	}
+
+	v1 = N;
+	if(n->list)
+		v1 = n->list->n;
+	v2 = N;
+	if(n->list && n->list->next)
+		v2 = n->list->next->n;
+
+	// this is not only a optimization but also a requirement in the spec.
+	// "if the second iteration variable is the blank identifier, the range
+	// clause is equivalent to the same clause with only the first variable
+	// present."
+	if(isblank(v2)) {
+		if(v1 != N)
+			n->list = list1(v1);
+		v2 = N;
+	}
+
+	if(v1) {
+		if(v1->defn == n)
+			v1->type = t1;
+		else if(v1->type != T && assignop(t1, v1->type, &why) == 0)
+			yyerror("cannot assign type %T to %lN in range%s", t1, v1, why);
+	}
+	if(v2) {
+		if(v2->defn == n)
+			v2->type = t2;
+		else if(v2->type != T && assignop(t2, v2->type, &why) == 0)
+			yyerror("cannot assign type %T to %lN in range%s", t2, v2, why);
+	}
+
+out:
+	typechecklist(n->nbody, Etop);
+
+	// second half of dance
+	n->typecheck = 1;
+	for(ll=n->list; ll; ll=ll->next)
+		if(ll->n->typecheck == 0)
+			typecheck(&ll->n, Erv | Easgn);
+}
+
+void
+walkrange(Node *n)
+{
+	Node *ohv1, *hv1, *hv2;	// hidden (old) val 1, 2
+	Node *ha, *hit;	// hidden aggregate, iterator
+	Node *hn, *hp;	// hidden len, pointer
+	Node *hb;  // hidden bool
+	Node *a, *v1, *v2;	// not hidden aggregate, val 1, 2
+	Node *fn, *tmp;
+	Node *keyname, *valname;
+	Node *key, *val;
+	NodeList *body, *init;
+	Type *th, *t;
+	int lno;
+
+	t = n->type;
+	init = nil;
+
+	a = n->right;
+	lno = setlineno(a);
+
+	v1 = N;
+	if(n->list)
+		v1 = n->list->n;
+	v2 = N;
+	if(n->list && n->list->next && !isblank(n->list->next->n))
+		v2 = n->list->next->n;
+	// n->list has no meaning anymore, clear it
+	// to avoid erroneous processing by racewalk.
+	n->list = nil;
+	hv2 = N;
+
+	switch(t->etype) {
+	default:
+		fatal("walkrange");
+
+	case TARRAY:
+		// orderstmt arranged for a copy of the array/slice variable if needed.
+		ha = a;
+		hv1 = temp(types[TINT]);
+		hn = temp(types[TINT]);
+		hp = nil;
+
+		init = list(init, nod(OAS, hv1, N));
+		init = list(init, nod(OAS, hn, nod(OLEN, ha, N)));
+		if(v2) {
+			hp = temp(ptrto(n->type->type));
+			tmp = nod(OINDEX, ha, nodintconst(0));
+			tmp->bounded = 1;
+			init = list(init, nod(OAS, hp, nod(OADDR, tmp, N)));
+		}
+
+		n->ntest = nod(OLT, hv1, hn);
+		n->nincr = nod(OAS, hv1, nod(OADD, hv1, nodintconst(1)));
+		if(v1 == N)
+			body = nil;
+		else if(v2 == N)
+			body = list1(nod(OAS, v1, hv1));
+		else {
+			a = nod(OAS2, N, N);
+			a->list = list(list1(v1), v2);
+			a->rlist = list(list1(hv1), nod(OIND, hp, N));
+			body = list1(a);
+			
+			// Advance pointer as part of increment.
+			// We used to advance the pointer before executing the loop body,
+			// but doing so would make the pointer point past the end of the
+			// array during the final iteration, possibly causing another unrelated
+			// piece of memory not to be garbage collected until the loop finished.
+			// Advancing during the increment ensures that the pointer p only points
+			// pass the end of the array during the final "p++; i++; if(i >= len(x)) break;",
+			// after which p is dead, so it cannot confuse the collector.
+			tmp = nod(OADD, hp, nodintconst(t->type->width));
+			tmp->type = hp->type;
+			tmp->typecheck = 1;
+			tmp->right->type = types[tptr];
+			tmp->right->typecheck = 1;
+			a = nod(OAS, hp, tmp);
+			typecheck(&a, Etop);
+			n->nincr->ninit = list1(a);
+		}
+		break;
+
+	case TMAP:
+		// orderstmt allocated the iterator for us.
+		// we only use a once, so no copy needed.
+		ha = a;
+		th = hiter(t);
+		hit = n->alloc;
+		hit->type = th;
+		n->left = N;
+		keyname = newname(th->type->sym);  // depends on layout of iterator struct.  See reflect.c:hiter
+		valname = newname(th->type->down->sym); // ditto
+
+		fn = syslook("mapiterinit", 1);
+		argtype(fn, t->down);
+		argtype(fn, t->type);
+		argtype(fn, th);
+		init = list(init, mkcall1(fn, T, nil, typename(t), ha, nod(OADDR, hit, N)));
+		n->ntest = nod(ONE, nod(ODOT, hit, keyname), nodnil());
+
+		fn = syslook("mapiternext", 1);
+		argtype(fn, th);
+		n->nincr = mkcall1(fn, T, nil, nod(OADDR, hit, N));
+
+		key = nod(ODOT, hit, keyname);
+		key = nod(OIND, key, N);
+		if(v1 == N)
+			body = nil;
+		else if(v2 == N) {
+			body = list1(nod(OAS, v1, key));
+		} else {
+			val = nod(ODOT, hit, valname);
+			val = nod(OIND, val, N);
+			a = nod(OAS2, N, N);
+			a->list = list(list1(v1), v2);
+			a->rlist = list(list1(key), val);
+			body = list1(a);
+		}
+		break;
+
+	case TCHAN:
+		// orderstmt arranged for a copy of the channel variable.
+		ha = a;
+		n->ntest = N;
+		
+		hv1 = temp(t->type);
+		hv1->typecheck = 1;
+		if(haspointers(t->type))
+			init = list(init, nod(OAS, hv1, N));
+		hb = temp(types[TBOOL]);
+
+		n->ntest = nod(ONE, hb, nodbool(0));
+		a = nod(OAS2RECV, N, N);
+		a->typecheck = 1;
+		a->list = list(list1(hv1), hb);
+		a->rlist = list1(nod(ORECV, ha, N));
+		n->ntest->ninit = list1(a);
+		if(v1 == N)
+			body = nil;
+		else
+			body = list1(nod(OAS, v1, hv1));
+		break;
+
+	case TSTRING:
+		// orderstmt arranged for a copy of the string variable.
+		ha = a;
+
+		ohv1 = temp(types[TINT]);
+
+		hv1 = temp(types[TINT]);
+		init = list(init, nod(OAS, hv1, N));
+
+		if(v2 == N)
+			a = nod(OAS, hv1, mkcall("stringiter", types[TINT], nil, ha, hv1));
+		else {
+			hv2 = temp(runetype);
+			a = nod(OAS2, N, N);
+			a->list = list(list1(hv1), hv2);
+			fn = syslook("stringiter2", 0);
+			a->rlist = list1(mkcall1(fn, getoutargx(fn->type), nil, ha, hv1));
+		}
+		n->ntest = nod(ONE, hv1, nodintconst(0));
+		n->ntest->ninit = list(list1(nod(OAS, ohv1, hv1)), a);
+
+		
+		body = nil;
+		if(v1 != N)
+			body = list1(nod(OAS, v1, ohv1));
+		if(v2 != N)
+			body = list(body, nod(OAS, v2, hv2));
+		break;
+	}
+
+	n->op = OFOR;
+	typechecklist(init, Etop);
+	n->ninit = concat(n->ninit, init);
+	typechecklist(n->ntest->ninit, Etop);
+	typecheck(&n->ntest, Erv);
+	typecheck(&n->nincr, Etop);
+	typechecklist(body, Etop);
+	n->nbody = concat(body, n->nbody);
+	walkstmt(&n);
+	
+	lineno = lno;
+}
+
diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c
new file mode 100644
index 0000000..8788a67
--- /dev/null
+++ b/src/cmd/gc/reflect.c
@@ -0,0 +1,1577 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+#include "../ld/textflag.h"
+#include "../../runtime/mgc0.h"
+#include "../../runtime/typekind.h"
+
+/*
+ * runtime interface and reflection data structures
+ */
+
+static	NodeList*	signatlist;
+static	Sym*	dtypesym(Type*);
+static	Sym*	weaktypesym(Type*);
+static	Sym*	dalgsym(Type*);
+static	int	usegcprog(Type*);
+static	void	gengcprog(Type*, Sym**, Sym**);
+static	void	gengcmask(Type*, uint8[16]);
+
+static int
+sigcmp(Sig *a, Sig *b)
+{
+	int i;
+
+	i = strcmp(a->name, b->name);
+	if(i != 0)
+		return i;
+	if(a->pkg == b->pkg)
+		return 0;
+	if(a->pkg == nil)
+		return -1;
+	if(b->pkg == nil)
+		return +1;
+	return strcmp(a->pkg->path->s, b->pkg->path->s);
+}
+
+static Sig*
+lsort(Sig *l, int(*f)(Sig*, Sig*))
+{
+	Sig *l1, *l2, *le;
+
+	if(l == 0 || l->link == 0)
+		return l;
+
+	l1 = l;
+	l2 = l;
+	for(;;) {
+		l2 = l2->link;
+		if(l2 == 0)
+			break;
+		l2 = l2->link;
+		if(l2 == 0)
+			break;
+		l1 = l1->link;
+	}
+
+	l2 = l1->link;
+	l1->link = 0;
+	l1 = lsort(l, f);
+	l2 = lsort(l2, f);
+
+	/* set up lead element */
+	if((*f)(l1, l2) < 0) {
+		l = l1;
+		l1 = l1->link;
+	} else {
+		l = l2;
+		l2 = l2->link;
+	}
+	le = l;
+
+	for(;;) {
+		if(l1 == 0) {
+			while(l2) {
+				le->link = l2;
+				le = l2;
+				l2 = l2->link;
+			}
+			le->link = 0;
+			break;
+		}
+		if(l2 == 0) {
+			while(l1) {
+				le->link = l1;
+				le = l1;
+				l1 = l1->link;
+			}
+			break;
+		}
+		if((*f)(l1, l2) < 0) {
+			le->link = l1;
+			le = l1;
+			l1 = l1->link;
+		} else {
+			le->link = l2;
+			le = l2;
+			l2 = l2->link;
+		}
+	}
+	le->link = 0;
+	return l;
+}
+
+// Builds a type respresenting a Bucket structure for
+// the given map type.  This type is not visible to users -
+// we include only enough information to generate a correct GC
+// program for it.
+// Make sure this stays in sync with ../../runtime/hashmap.c!
+enum {
+	BUCKETSIZE = 8,
+	MAXKEYSIZE = 128,
+	MAXVALSIZE = 128,
+};
+
+static Type*
+mapbucket(Type *t)
+{
+	Type *keytype, *valtype;
+	Type *bucket;
+	Type *overflowfield, *keysfield, *valuesfield;
+	int32 offset;
+
+	if(t->bucket != T)
+		return t->bucket;
+
+	keytype = t->down;
+	valtype = t->type;
+	dowidth(keytype);
+	dowidth(valtype);
+	if(keytype->width > MAXKEYSIZE)
+		keytype = ptrto(keytype);
+	if(valtype->width > MAXVALSIZE)
+		valtype = ptrto(valtype);
+
+	bucket = typ(TSTRUCT);
+	bucket->noalg = 1;
+
+	// The first field is: uint8 topbits[BUCKETSIZE].
+	// We don't need to encode it as GC doesn't care about it.
+	offset = BUCKETSIZE * 1;
+
+	keysfield = typ(TFIELD);
+	keysfield->type = typ(TARRAY);
+	keysfield->type->type = keytype;
+	keysfield->type->bound = BUCKETSIZE;
+	keysfield->type->width = BUCKETSIZE * keytype->width;
+	keysfield->width = offset;
+	keysfield->sym = mal(sizeof(Sym));
+	keysfield->sym->name = "keys";
+	offset += BUCKETSIZE * keytype->width;
+
+	valuesfield = typ(TFIELD);
+	valuesfield->type = typ(TARRAY);
+	valuesfield->type->type = valtype;
+	valuesfield->type->bound = BUCKETSIZE;
+	valuesfield->type->width = BUCKETSIZE * valtype->width;
+	valuesfield->width = offset;
+	valuesfield->sym = mal(sizeof(Sym));
+	valuesfield->sym->name = "values";
+	offset += BUCKETSIZE * valtype->width;
+
+	overflowfield = typ(TFIELD);
+	overflowfield->type = ptrto(bucket);
+	overflowfield->width = offset;         // "width" is offset in structure
+	overflowfield->sym = mal(sizeof(Sym)); // not important but needs to be set to give this type a name
+	overflowfield->sym->name = "overflow";
+	offset += widthptr;
+	
+	// Pad to the native integer alignment.
+	// This is usually the same as widthptr; the exception (as usual) is nacl/amd64.
+	if(widthreg > widthptr)
+		offset += widthreg - widthptr;
+
+	// link up fields
+	bucket->type = keysfield;
+	keysfield->down = valuesfield;
+	valuesfield->down = overflowfield;
+	overflowfield->down = T;
+
+	bucket->width = offset;
+	bucket->local = t->local;
+	t->bucket = bucket;
+	bucket->map = t;
+	return bucket;
+}
+
+// Builds a type respresenting a Hmap structure for
+// the given map type.  This type is not visible to users -
+// we include only enough information to generate a correct GC
+// program for it.
+// Make sure this stays in sync with ../../runtime/hashmap.go!
+static Type*
+hmap(Type *t)
+{
+	Type *h, *bucket;
+	Type *bucketsfield, *oldbucketsfield;
+	int32 offset;
+
+	if(t->hmap != T)
+		return t->hmap;
+
+	bucket = mapbucket(t);
+	h = typ(TSTRUCT);
+	h->noalg = 1;
+
+	offset = widthint; // count
+	offset += 4;       // flags
+	offset += 4;       // hash0
+	offset += 1;       // B
+	offset = (offset + widthptr - 1) / widthptr * widthptr;
+	
+	bucketsfield = typ(TFIELD);
+	bucketsfield->type = ptrto(bucket);
+	bucketsfield->width = offset;
+	bucketsfield->sym = mal(sizeof(Sym));
+	bucketsfield->sym->name = "buckets";
+	offset += widthptr;
+
+	oldbucketsfield = typ(TFIELD);
+	oldbucketsfield->type = ptrto(bucket);
+	oldbucketsfield->width = offset;
+	oldbucketsfield->sym = mal(sizeof(Sym));
+	oldbucketsfield->sym->name = "oldbuckets";
+	offset += widthptr;
+
+	offset += widthptr; // nevacuate (last field in Hmap)
+
+	// link up fields
+	h->type = bucketsfield;
+	bucketsfield->down = oldbucketsfield;
+	oldbucketsfield->down = T;
+
+	h->width = offset;
+	h->local = t->local;
+	t->hmap = h;
+	h->map = t;
+	return h;
+}
+
+Type*
+hiter(Type *t)
+{
+	int32 n, off;
+	Type *field[7];
+	Type *i;
+
+	if(t->hiter != T)
+		return t->hiter;
+
+	// build a struct:
+	// hash_iter {
+	//    key *Key
+	//    val *Value
+	//    t *MapType
+	//    h *Hmap
+	//    buckets *Bucket
+	//    bptr *Bucket
+	//    other [4]uintptr
+	// }
+	// must match ../../runtime/hashmap.c:hash_iter.
+	field[0] = typ(TFIELD);
+	field[0]->type = ptrto(t->down);
+	field[0]->sym = mal(sizeof(Sym));
+	field[0]->sym->name = "key";
+	
+	field[1] = typ(TFIELD);
+	field[1]->type = ptrto(t->type);
+	field[1]->sym = mal(sizeof(Sym));
+	field[1]->sym->name = "val";
+	
+	field[2] = typ(TFIELD);
+	field[2]->type = ptrto(types[TUINT8]); // TODO: is there a Type type?
+	field[2]->sym = mal(sizeof(Sym));
+	field[2]->sym->name = "t";
+	
+	field[3] = typ(TFIELD);
+	field[3]->type = ptrto(hmap(t));
+	field[3]->sym = mal(sizeof(Sym));
+	field[3]->sym->name = "h";
+	
+	field[4] = typ(TFIELD);
+	field[4]->type = ptrto(mapbucket(t));
+	field[4]->sym = mal(sizeof(Sym));
+	field[4]->sym->name = "buckets";
+	
+	field[5] = typ(TFIELD);
+	field[5]->type = ptrto(mapbucket(t));
+	field[5]->sym = mal(sizeof(Sym));
+	field[5]->sym->name = "bptr";
+	
+	// all other non-pointer fields
+	field[6] = typ(TFIELD);
+	field[6]->type = typ(TARRAY);
+	field[6]->type->type = types[TUINTPTR];
+	field[6]->type->bound = 4;
+	field[6]->type->width = 4 * widthptr;
+	field[6]->sym = mal(sizeof(Sym));
+	field[6]->sym->name = "other";
+	
+	// build iterator struct holding the above fields
+	i = typ(TSTRUCT);
+	i->noalg = 1;
+	i->type = field[0];
+	off = 0;
+	for(n = 0; n < 6; n++) {
+		field[n]->down = field[n+1];
+		field[n]->width = off;
+		off += field[n]->type->width;
+	}
+	field[6]->down = T;
+	off += field[6]->type->width;
+	if(off != 10 * widthptr)
+		yyerror("hash_iter size not correct %d %d", off, 10 * widthptr);
+	t->hiter = i;
+	i->map = t;
+	return i;
+}
+
+/*
+ * f is method type, with receiver.
+ * return function type, receiver as first argument (or not).
+ */
+Type*
+methodfunc(Type *f, Type *receiver)
+{
+	NodeList *in, *out;
+	Node *d;
+	Type *t;
+
+	in = nil;
+	if(receiver) {
+		d = nod(ODCLFIELD, N, N);
+		d->type = receiver;
+		in = list(in, d);
+	}
+	for(t=getinargx(f)->type; t; t=t->down) {
+		d = nod(ODCLFIELD, N, N);
+		d->type = t->type;
+		d->isddd = t->isddd;
+		in = list(in, d);
+	}
+
+	out = nil;
+	for(t=getoutargx(f)->type; t; t=t->down) {
+		d = nod(ODCLFIELD, N, N);
+		d->type = t->type;
+		out = list(out, d);
+	}
+
+	t = functype(N, in, out);
+	if(f->nname) {
+		// Link to name of original method function.
+		t->nname = f->nname;
+	}
+	return t;
+}
+
+/*
+ * return methods of non-interface type t, sorted by name.
+ * generates stub functions as needed.
+ */
+static Sig*
+methods(Type *t)
+{
+	Type *f, *mt, *it, *this;
+	Sig *a, *b;
+	Sym *method;
+
+	// method type
+	mt = methtype(t, 0);
+	if(mt == T)
+		return nil;
+	expandmeth(mt);
+
+	// type stored in interface word
+	it = t;
+	if(!isdirectiface(it))
+		it = ptrto(t);
+
+	// make list of methods for t,
+	// generating code if necessary.
+	a = nil;
+	for(f=mt->xmethod; f; f=f->down) {
+		if(f->etype != TFIELD)
+			fatal("methods: not field %T", f);
+		if (f->type->etype != TFUNC || f->type->thistuple == 0)
+			fatal("non-method on %T method %S %T\n", mt, f->sym, f);
+		if (!getthisx(f->type)->type)
+			fatal("receiver with no type on %T method %S %T\n", mt, f->sym, f);
+		if(f->nointerface)
+			continue;
+
+		method = f->sym;
+		if(method == nil)
+			continue;
+
+		// get receiver type for this particular method.
+		// if pointer receiver but non-pointer t and
+		// this is not an embedded pointer inside a struct,
+		// method does not apply.
+		this = getthisx(f->type)->type->type;
+		if(isptr[this->etype] && this->type == t)
+			continue;
+		if(isptr[this->etype] && !isptr[t->etype]
+		&& f->embedded != 2 && !isifacemethod(f->type))
+			continue;
+
+		b = mal(sizeof(*b));
+		b->link = a;
+		a = b;
+
+		a->name = method->name;
+		if(!exportname(method->name)) {
+			if(method->pkg == nil)
+				fatal("methods: missing package");
+			a->pkg = method->pkg;
+		}
+		a->isym = methodsym(method, it, 1);
+		a->tsym = methodsym(method, t, 0);
+		a->type = methodfunc(f->type, t);
+		a->mtype = methodfunc(f->type, nil);
+
+		if(!(a->isym->flags & SymSiggen)) {
+			a->isym->flags |= SymSiggen;
+			if(!eqtype(this, it) || this->width < types[tptr]->width) {
+				compiling_wrappers = 1;
+				genwrapper(it, f, a->isym, 1);
+				compiling_wrappers = 0;
+			}
+		}
+
+		if(!(a->tsym->flags & SymSiggen)) {
+			a->tsym->flags |= SymSiggen;
+			if(!eqtype(this, t)) {
+				compiling_wrappers = 1;
+				genwrapper(t, f, a->tsym, 0);
+				compiling_wrappers = 0;
+			}
+		}
+	}
+
+	return lsort(a, sigcmp);
+}
+
+/*
+ * return methods of interface type t, sorted by name.
+ */
+static Sig*
+imethods(Type *t)
+{
+	Sig *a, *all, *last;
+	Type *f;
+	Sym *method, *isym;
+
+	all = nil;
+	last = nil;
+	for(f=t->type; f; f=f->down) {
+		if(f->etype != TFIELD)
+			fatal("imethods: not field");
+		if(f->type->etype != TFUNC || f->sym == nil)
+			continue;
+		method = f->sym;
+		a = mal(sizeof(*a));
+		a->name = method->name;
+		if(!exportname(method->name)) {
+			if(method->pkg == nil)
+				fatal("imethods: missing package");
+			a->pkg = method->pkg;
+		}
+		a->mtype = f->type;
+		a->offset = 0;
+		a->type = methodfunc(f->type, nil);
+
+		if(last && sigcmp(last, a) >= 0)
+			fatal("sigcmp vs sortinter %s %s", last->name, a->name);
+		if(last == nil)
+			all = a;
+		else
+			last->link = a;
+		last = a;
+
+		// Compiler can only refer to wrappers for non-blank methods.
+		if(isblanksym(method))
+			continue;
+
+		// NOTE(rsc): Perhaps an oversight that
+		// IfaceType.Method is not in the reflect data.
+		// Generate the method body, so that compiled
+		// code can refer to it.
+		isym = methodsym(method, t, 0);
+		if(!(isym->flags & SymSiggen)) {
+			isym->flags |= SymSiggen;
+			genwrapper(t, f, isym, 0);
+		}
+	}
+	return all;
+}
+
+static void
+dimportpath(Pkg *p)
+{
+	static Pkg *gopkg;
+	char *nam;
+	Node *n;
+
+	if(p->pathsym != S)
+		return;
+
+	if(gopkg == nil) {
+		gopkg = mkpkg(strlit("go"));
+		gopkg->name = "go";
+	}
+	nam = smprint("importpath.%s.", p->prefix);
+
+	n = nod(ONAME, N, N);
+	n->sym = pkglookup(nam, gopkg);
+	free(nam);
+	n->class = PEXTERN;
+	n->xoffset = 0;
+	p->pathsym = n->sym;
+
+	gdatastring(n, p->path);
+	ggloblsym(n->sym, types[TSTRING]->width, DUPOK|RODATA);
+}
+
+static int
+dgopkgpath(Sym *s, int ot, Pkg *pkg)
+{
+	if(pkg == nil)
+		return dgostringptr(s, ot, nil);
+
+	// Emit reference to go.importpath.""., which 6l will
+	// rewrite using the correct import path.  Every package
+	// that imports this one directly defines the symbol.
+	if(pkg == localpkg) {
+		static Sym *ns;
+
+		if(ns == nil)
+			ns = pkglookup("importpath.\"\".", mkpkg(strlit("go")));
+		return dsymptr(s, ot, ns, 0);
+	}
+
+	dimportpath(pkg);
+	return dsymptr(s, ot, pkg->pathsym, 0);
+}
+
+/*
+ * uncommonType
+ * ../../runtime/type.go:/uncommonType
+ */
+static int
+dextratype(Sym *sym, int off, Type *t, int ptroff)
+{
+	int ot, n;
+	Sym *s;
+	Sig *a, *m;
+
+	m = methods(t);
+	if(t->sym == nil && m == nil)
+		return off;
+
+	// fill in *extraType pointer in header
+	off = rnd(off, widthptr);
+	dsymptr(sym, ptroff, sym, off);
+
+	n = 0;
+	for(a=m; a; a=a->link) {
+		dtypesym(a->type);
+		n++;
+	}
+
+	ot = off;
+	s = sym;
+	if(t->sym) {
+		ot = dgostringptr(s, ot, t->sym->name);
+		if(t != types[t->etype] && t != errortype)
+			ot = dgopkgpath(s, ot, t->sym->pkg);
+		else
+			ot = dgostringptr(s, ot, nil);
+	} else {
+		ot = dgostringptr(s, ot, nil);
+		ot = dgostringptr(s, ot, nil);
+	}
+
+	// slice header
+	ot = dsymptr(s, ot, s, ot + widthptr + 2*widthint);
+	ot = duintxx(s, ot, n, widthint);
+	ot = duintxx(s, ot, n, widthint);
+
+	// methods
+	for(a=m; a; a=a->link) {
+		// method
+		// ../../runtime/type.go:/method
+		ot = dgostringptr(s, ot, a->name);
+		ot = dgopkgpath(s, ot, a->pkg);
+		ot = dsymptr(s, ot, dtypesym(a->mtype), 0);
+		ot = dsymptr(s, ot, dtypesym(a->type), 0);
+		if(a->isym)
+			ot = dsymptr(s, ot, a->isym, 0);
+		else
+			ot = duintptr(s, ot, 0);
+		if(a->tsym)
+			ot = dsymptr(s, ot, a->tsym, 0);
+		else
+			ot = duintptr(s, ot, 0);
+	}
+
+	return ot;
+}
+
+static int
+kinds[] =
+{
+	[TINT]		= KindInt,
+	[TUINT]		= KindUint,
+	[TINT8]		= KindInt8,
+	[TUINT8]	= KindUint8,
+	[TINT16]	= KindInt16,
+	[TUINT16]	= KindUint16,
+	[TINT32]	= KindInt32,
+	[TUINT32]	= KindUint32,
+	[TINT64]	= KindInt64,
+	[TUINT64]	= KindUint64,
+	[TUINTPTR]	= KindUintptr,
+	[TFLOAT32]	= KindFloat32,
+	[TFLOAT64]	= KindFloat64,
+	[TBOOL]		= KindBool,
+	[TSTRING]		= KindString,
+	[TPTR32]		= KindPtr,
+	[TPTR64]		= KindPtr,
+	[TSTRUCT]	= KindStruct,
+	[TINTER]		= KindInterface,
+	[TCHAN]		= KindChan,
+	[TMAP]		= KindMap,
+	[TARRAY]		= KindArray,
+	[TFUNC]		= KindFunc,
+	[TCOMPLEX64]	= KindComplex64,
+	[TCOMPLEX128]	= KindComplex128,
+	[TUNSAFEPTR]	= KindUnsafePointer,
+};
+
+int
+haspointers(Type *t)
+{
+	Type *t1;
+	int ret;
+
+	if(t->haspointers != 0)
+		return t->haspointers - 1;
+
+	switch(t->etype) {
+	case TINT:
+	case TUINT:
+	case TINT8:
+	case TUINT8:
+	case TINT16:
+	case TUINT16:
+	case TINT32:
+	case TUINT32:
+	case TINT64:
+	case TUINT64:
+	case TUINTPTR:
+	case TFLOAT32:
+	case TFLOAT64:
+	case TCOMPLEX64:
+	case TCOMPLEX128:
+	case TBOOL:
+		ret = 0;
+		break;
+	case TARRAY:
+		if(t->bound < 0) {	// slice
+			ret = 1;
+			break;
+		}
+		if(t->bound == 0) {	// empty array
+			ret = 0;
+			break;
+		}
+		ret = haspointers(t->type);
+		break;
+	case TSTRUCT:
+		ret = 0;
+		for(t1=t->type; t1!=T; t1=t1->down) {
+			if(haspointers(t1->type)) {
+				ret = 1;
+				break;
+			}
+		}
+		break;
+	case TSTRING:
+	case TPTR32:
+	case TPTR64:
+	case TUNSAFEPTR:
+	case TINTER:
+	case TCHAN:
+	case TMAP:
+	case TFUNC:
+	default:
+		ret = 1;
+		break;
+	}
+	
+	t->haspointers = 1+ret;
+	return ret;
+}
+
+/*
+ * commonType
+ * ../../runtime/type.go:/commonType
+ */
+static int
+dcommontype(Sym *s, int ot, Type *t)
+{
+	int i, alg, sizeofAlg, gcprog;
+	Sym *sptr, *algsym, *zero, *gcprog0, *gcprog1, *sbits;
+	uint8 gcmask[16];
+	static Sym *algarray;
+	uint64 x1, x2;
+	char *p;
+	
+	if(ot != 0)
+		fatal("dcommontype %d", ot);
+
+	sizeofAlg = 2*widthptr;
+	if(algarray == nil)
+		algarray = pkglookup("algarray", runtimepkg);
+	dowidth(t);
+	alg = algtype(t);
+	algsym = S;
+	if(alg < 0)
+		algsym = dalgsym(t);
+
+	if(t->sym != nil && !isptr[t->etype])
+		sptr = dtypesym(ptrto(t));
+	else
+		sptr = weaktypesym(ptrto(t));
+
+	// All (non-reflect-allocated) Types share the same zero object.
+	// Each place in the compiler where a pointer to the zero object
+	// might be returned by a runtime call (map access return value,
+	// 2-arg type cast) declares the size of the zerovalue it needs.
+	// The linker magically takes the max of all the sizes.
+	zero = pkglookup("zerovalue", runtimepkg);
+
+	// We use size 0 here so we get the pointer to the zero value,
+	// but don't allocate space for the zero value unless we need it.
+	// TODO: how do we get this symbol into bss?  We really want
+	// a read-only bss, but I don't think such a thing exists.
+
+	// ../../pkg/reflect/type.go:/^type.commonType
+	// actual type structure
+	//	type commonType struct {
+	//		size          uintptr
+	//		hash          uint32
+	//		_             uint8
+	//		align         uint8
+	//		fieldAlign    uint8
+	//		kind          uint8
+	//		alg           unsafe.Pointer
+	//		gc            unsafe.Pointer
+	//		string        *string
+	//		*extraType
+	//		ptrToThis     *Type
+	//		zero          unsafe.Pointer
+	//	}
+	ot = duintptr(s, ot, t->width);
+	ot = duint32(s, ot, typehash(t));
+	ot = duint8(s, ot, 0);	// unused
+
+	// runtime (and common sense) expects alignment to be a power of two.
+	i = t->align;
+	if(i == 0)
+		i = 1;
+	if((i&(i-1)) != 0)
+		fatal("invalid alignment %d for %T", t->align, t);
+	ot = duint8(s, ot, t->align);	// align
+	ot = duint8(s, ot, t->align);	// fieldAlign
+
+	gcprog = usegcprog(t);
+	i = kinds[t->etype];
+	if(t->etype == TARRAY && t->bound < 0)
+		i = KindSlice;
+	if(!haspointers(t))
+		i |= KindNoPointers;
+	if(isdirectiface(t))
+		i |= KindDirectIface;
+	if(gcprog)
+		i |= KindGCProg;
+	ot = duint8(s, ot, i);  // kind
+	if(alg >= 0)
+		ot = dsymptr(s, ot, algarray, alg*sizeofAlg);
+	else
+		ot = dsymptr(s, ot, algsym, 0);
+	// gc
+	if(gcprog) {
+		gengcprog(t, &gcprog0, &gcprog1);
+		if(gcprog0 != S)
+			ot = dsymptr(s, ot, gcprog0, 0);
+		else
+			ot = duintptr(s, ot, 0);
+		ot = dsymptr(s, ot, gcprog1, 0);
+	} else {
+		gengcmask(t, gcmask);
+		x1 = 0;
+		for(i=0; i<8; i++)
+			x1 = x1<<8 | gcmask[i];
+		if(widthptr == 4) {
+			p = smprint("gcbits.%#016llux", x1);
+		} else {
+			x2 = 0;
+			for(i=0; i<8; i++)
+				x2 = x2<<8 | gcmask[i+8];
+			p = smprint("gcbits.%#016llux%016llux", x1, x2);
+		}
+		sbits = pkglookup(p, runtimepkg);
+		if((sbits->flags & SymUniq) == 0) {
+			sbits->flags |= SymUniq;
+			for(i = 0; i < 2*widthptr; i++)
+				duint8(sbits, i, gcmask[i]);
+			ggloblsym(sbits, 2*widthptr, DUPOK|RODATA);
+		}
+		ot = dsymptr(s, ot, sbits, 0);
+		ot = duintptr(s, ot, 0);
+	}
+	p = smprint("%-uT", t);
+	//print("dcommontype: %s\n", p);
+	ot = dgostringptr(s, ot, p);	// string
+	free(p);
+
+	// skip pointer to extraType,
+	// which follows the rest of this type structure.
+	// caller will fill in if needed.
+	// otherwise linker will assume 0.
+	ot += widthptr;
+
+	ot = dsymptr(s, ot, sptr, 0);  // ptrto type
+	ot = dsymptr(s, ot, zero, 0);  // ptr to zero value
+	return ot;
+}
+
+Sym*
+typesym(Type *t)
+{
+	char *p;
+	Sym *s;
+
+	p = smprint("%-T", t);
+	s = pkglookup(p, typepkg);
+	//print("typesym: %s -> %+S\n", p, s);
+	free(p);
+	return s;
+}
+
+Sym*
+tracksym(Type *t)
+{
+	char *p;
+	Sym *s;
+
+	p = smprint("%-T.%s", t->outer, t->sym->name);
+	s = pkglookup(p, trackpkg);
+	free(p);
+	return s;
+}
+
+Sym*
+typelinksym(Type *t)
+{
+	char *p;
+	Sym *s;
+
+	// %-uT is what the generated Type's string field says.
+	// It uses (ambiguous) package names instead of import paths.
+	// %-T is the complete, unambiguous type name.
+	// We want the types to end up sorted by string field,
+	// so use that first in the name, and then add :%-T to
+	// disambiguate. The names are a little long but they are
+	// discarded by the linker and do not end up in the symbol
+	// table of the final binary.
+	p = smprint("%-uT/%-T", t, t);
+	s = pkglookup(p, typelinkpkg);
+	//print("typelinksym: %s -> %+S\n", p, s);
+	free(p);
+	return s;
+}
+
+Sym*
+typesymprefix(char *prefix, Type *t)
+{
+	char *p;
+	Sym *s;
+
+	p = smprint("%s.%-T", prefix, t);
+	s = pkglookup(p, typepkg);
+	//print("algsym: %s -> %+S\n", p, s);
+	free(p);
+	return s;
+}
+
+Sym*
+typenamesym(Type *t)
+{
+	Sym *s;
+	Node *n;
+
+	if(t == T || (isptr[t->etype] && t->type == T) || isideal(t))
+		fatal("typename %T", t);
+	s = typesym(t);
+	if(s->def == N) {
+		n = nod(ONAME, N, N);
+		n->sym = s;
+		n->type = types[TUINT8];
+		n->addable = 1;
+		n->ullman = 1;
+		n->class = PEXTERN;
+		n->xoffset = 0;
+		n->typecheck = 1;
+		s->def = n;
+
+		signatlist = list(signatlist, typenod(t));
+	}
+	return s->def->sym;
+}
+
+Node*
+typename(Type *t)
+{
+	Sym *s;
+	Node *n;
+
+	s = typenamesym(t);
+	n = nod(OADDR, s->def, N);
+	n->type = ptrto(s->def->type);
+	n->addable = 1;
+	n->ullman = 2;
+	n->typecheck = 1;
+	return n;
+}
+
+static Sym*
+weaktypesym(Type *t)
+{
+	char *p;
+	Sym *s;
+
+	p = smprint("%-T", t);
+	s = pkglookup(p, weaktypepkg);
+	//print("weaktypesym: %s -> %+S\n", p, s);
+	free(p);
+	return s;
+}
+
+static Sym*
+dtypesym(Type *t)
+{
+	int ot, xt, n, isddd, dupok;
+	Sym *s, *s1, *s2, *s3, *s4, *slink;
+	Sig *a, *m;
+	Type *t1, *tbase, *t2;
+
+	// Replace byte, rune aliases with real type.
+	// They've been separate internally to make error messages
+	// better, but we have to merge them in the reflect tables.
+	if(t == bytetype || t == runetype)
+		t = types[t->etype];
+
+	if(isideal(t))
+		fatal("dtypesym %T", t);
+
+	s = typesym(t);
+	if(s->flags & SymSiggen)
+		return s;
+	s->flags |= SymSiggen;
+
+	// special case (look for runtime below):
+	// when compiling package runtime,
+	// emit the type structures for int, float, etc.
+	tbase = t;
+	if(isptr[t->etype] && t->sym == S && t->type->sym != S)
+		tbase = t->type;
+	dupok = 0;
+	if(tbase->sym == S)
+		dupok = DUPOK;
+
+	if(compiling_runtime &&
+			(tbase == types[tbase->etype] ||
+			tbase == bytetype ||
+			tbase == runetype ||
+			tbase == errortype)) { // int, float, etc
+		goto ok;
+	}
+
+	// named types from other files are defined only by those files
+	if(tbase->sym && !tbase->local)
+		return s;
+	if(isforw[tbase->etype])
+		return s;
+
+ok:
+	ot = 0;
+	xt = 0;
+	switch(t->etype) {
+	default:
+		ot = dcommontype(s, ot, t);
+		xt = ot - 3*widthptr;
+		break;
+
+	case TARRAY:
+		if(t->bound >= 0) {
+			// ../../runtime/type.go:/ArrayType
+			s1 = dtypesym(t->type);
+			t2 = typ(TARRAY);
+			t2->type = t->type;
+			t2->bound = -1;  // slice
+			s2 = dtypesym(t2);
+			ot = dcommontype(s, ot, t);
+			xt = ot - 3*widthptr;
+			ot = dsymptr(s, ot, s1, 0);
+			ot = dsymptr(s, ot, s2, 0);
+			ot = duintptr(s, ot, t->bound);
+		} else {
+			// ../../runtime/type.go:/SliceType
+			s1 = dtypesym(t->type);
+			ot = dcommontype(s, ot, t);
+			xt = ot - 3*widthptr;
+			ot = dsymptr(s, ot, s1, 0);
+		}
+		break;
+
+	case TCHAN:
+		// ../../runtime/type.go:/ChanType
+		s1 = dtypesym(t->type);
+		ot = dcommontype(s, ot, t);
+		xt = ot - 3*widthptr;
+		ot = dsymptr(s, ot, s1, 0);
+		ot = duintptr(s, ot, t->chan);
+		break;
+
+	case TFUNC:
+		for(t1=getthisx(t)->type; t1; t1=t1->down)
+			dtypesym(t1->type);
+		isddd = 0;
+		for(t1=getinargx(t)->type; t1; t1=t1->down) {
+			isddd = t1->isddd;
+			dtypesym(t1->type);
+		}
+		for(t1=getoutargx(t)->type; t1; t1=t1->down)
+			dtypesym(t1->type);
+
+		ot = dcommontype(s, ot, t);
+		xt = ot - 3*widthptr;
+		ot = duint8(s, ot, isddd);
+
+		// two slice headers: in and out.
+		ot = rnd(ot, widthptr);
+		ot = dsymptr(s, ot, s, ot+2*(widthptr+2*widthint));
+		n = t->thistuple + t->intuple;
+		ot = duintxx(s, ot, n, widthint);
+		ot = duintxx(s, ot, n, widthint);
+		ot = dsymptr(s, ot, s, ot+1*(widthptr+2*widthint)+n*widthptr);
+		ot = duintxx(s, ot, t->outtuple, widthint);
+		ot = duintxx(s, ot, t->outtuple, widthint);
+
+		// slice data
+		for(t1=getthisx(t)->type; t1; t1=t1->down, n++)
+			ot = dsymptr(s, ot, dtypesym(t1->type), 0);
+		for(t1=getinargx(t)->type; t1; t1=t1->down, n++)
+			ot = dsymptr(s, ot, dtypesym(t1->type), 0);
+		for(t1=getoutargx(t)->type; t1; t1=t1->down, n++)
+			ot = dsymptr(s, ot, dtypesym(t1->type), 0);
+		break;
+
+	case TINTER:
+		m = imethods(t);
+		n = 0;
+		for(a=m; a; a=a->link) {
+			dtypesym(a->type);
+			n++;
+		}
+
+		// ../../runtime/type.go:/InterfaceType
+		ot = dcommontype(s, ot, t);
+		xt = ot - 3*widthptr;
+		ot = dsymptr(s, ot, s, ot+widthptr+2*widthint);
+		ot = duintxx(s, ot, n, widthint);
+		ot = duintxx(s, ot, n, widthint);
+		for(a=m; a; a=a->link) {
+			// ../../runtime/type.go:/imethod
+			ot = dgostringptr(s, ot, a->name);
+			ot = dgopkgpath(s, ot, a->pkg);
+			ot = dsymptr(s, ot, dtypesym(a->type), 0);
+		}
+		break;
+
+	case TMAP:
+		// ../../runtime/type.go:/MapType
+		s1 = dtypesym(t->down);
+		s2 = dtypesym(t->type);
+		s3 = dtypesym(mapbucket(t));
+		s4 = dtypesym(hmap(t));
+		ot = dcommontype(s, ot, t);
+		xt = ot - 3*widthptr;
+		ot = dsymptr(s, ot, s1, 0);
+		ot = dsymptr(s, ot, s2, 0);
+		ot = dsymptr(s, ot, s3, 0);
+		ot = dsymptr(s, ot, s4, 0);
+		if(t->down->width > MAXKEYSIZE) {
+			ot = duint8(s, ot, widthptr);
+			ot = duint8(s, ot, 1); // indirect
+		} else {
+			ot = duint8(s, ot, t->down->width);
+			ot = duint8(s, ot, 0); // not indirect
+		}
+		if(t->type->width > MAXVALSIZE) {
+			ot = duint8(s, ot, widthptr);
+			ot = duint8(s, ot, 1); // indirect
+		} else {
+			ot = duint8(s, ot, t->type->width);
+			ot = duint8(s, ot, 0); // not indirect
+		}
+		ot = duint16(s, ot, mapbucket(t)->width);
+		break;
+
+	case TPTR32:
+	case TPTR64:
+		if(t->type->etype == TANY) {
+			// ../../runtime/type.go:/UnsafePointerType
+			ot = dcommontype(s, ot, t);
+			break;
+		}
+		// ../../runtime/type.go:/PtrType
+		s1 = dtypesym(t->type);
+		ot = dcommontype(s, ot, t);
+		xt = ot - 3*widthptr;
+		ot = dsymptr(s, ot, s1, 0);
+		break;
+
+	case TSTRUCT:
+		// ../../runtime/type.go:/StructType
+		// for security, only the exported fields.
+		n = 0;
+		for(t1=t->type; t1!=T; t1=t1->down) {
+			dtypesym(t1->type);
+			n++;
+		}
+		ot = dcommontype(s, ot, t);
+		xt = ot - 3*widthptr;
+		ot = dsymptr(s, ot, s, ot+widthptr+2*widthint);
+		ot = duintxx(s, ot, n, widthint);
+		ot = duintxx(s, ot, n, widthint);
+		for(t1=t->type; t1!=T; t1=t1->down) {
+			// ../../runtime/type.go:/structField
+			if(t1->sym && !t1->embedded) {
+				ot = dgostringptr(s, ot, t1->sym->name);
+				if(exportname(t1->sym->name))
+					ot = dgostringptr(s, ot, nil);
+				else
+					ot = dgopkgpath(s, ot, t1->sym->pkg);
+			} else {
+				ot = dgostringptr(s, ot, nil);
+				if(t1->type->sym != S && t1->type->sym->pkg == builtinpkg)
+					ot = dgopkgpath(s, ot, localpkg);
+				else
+					ot = dgostringptr(s, ot, nil);
+			}
+			ot = dsymptr(s, ot, dtypesym(t1->type), 0);
+			ot = dgostrlitptr(s, ot, t1->note);
+			ot = duintptr(s, ot, t1->width);	// field offset
+		}
+		break;
+	}
+	ot = dextratype(s, ot, t, xt);
+	ggloblsym(s, ot, dupok|RODATA);
+
+	// generate typelink.foo pointing at s = type.foo.
+	// The linker will leave a table of all the typelinks for
+	// types in the binary, so reflect can find them.
+	// We only need the link for unnamed composites that
+	// we want be able to find.
+	if(t->sym == S) {
+		switch(t->etype) {
+		case TARRAY:
+		case TCHAN:
+		case TMAP:
+			slink = typelinksym(t);
+			dsymptr(slink, 0, s, 0);
+			ggloblsym(slink, widthptr, dupok|RODATA);
+		}
+	}
+
+	return s;
+}
+
+void
+dumptypestructs(void)
+{
+	int i;
+	NodeList *l;
+	Node *n;
+	Type *t;
+	Pkg *p;
+
+	// copy types from externdcl list to signatlist
+	for(l=externdcl; l; l=l->next) {
+		n = l->n;
+		if(n->op != OTYPE)
+			continue;
+		signatlist = list(signatlist, n);
+	}
+
+	// process signatlist
+	for(l=signatlist; l; l=l->next) {
+		n = l->n;
+		if(n->op != OTYPE)
+			continue;
+		t = n->type;
+		dtypesym(t);
+		if(t->sym)
+			dtypesym(ptrto(t));
+	}
+
+	// generate import strings for imported packages
+	for(i=0; i<nelem(phash); i++)
+		for(p=phash[i]; p; p=p->link)
+			if(p->direct)
+				dimportpath(p);
+
+	// do basic types if compiling package runtime.
+	// they have to be in at least one package,
+	// and runtime is always loaded implicitly,
+	// so this is as good as any.
+	// another possible choice would be package main,
+	// but using runtime means fewer copies in .6 files.
+	if(compiling_runtime) {
+		for(i=1; i<=TBOOL; i++)
+			dtypesym(ptrto(types[i]));
+		dtypesym(ptrto(types[TSTRING]));
+		dtypesym(ptrto(types[TUNSAFEPTR]));
+
+		// emit type structs for error and func(error) string.
+		// The latter is the type of an auto-generated wrapper.
+		dtypesym(ptrto(errortype));
+		dtypesym(functype(nil,
+			list1(nod(ODCLFIELD, N, typenod(errortype))),
+			list1(nod(ODCLFIELD, N, typenod(types[TSTRING])))));
+
+		// add paths for runtime and main, which 6l imports implicitly.
+		dimportpath(runtimepkg);
+		if(flag_race)
+			dimportpath(racepkg);
+		dimportpath(mkpkg(strlit("main")));
+	}
+}
+
+static Sym*
+dalgsym(Type *t)
+{
+	int ot;
+	Sym *s, *hash, *hashfunc, *eq, *eqfunc;
+
+	// dalgsym is only called for a type that needs an algorithm table,
+	// which implies that the type is comparable (or else it would use ANOEQ).
+
+	s = typesymprefix(".alg", t);
+	hash = typesymprefix(".hash", t);
+	genhash(hash, t);
+	eq = typesymprefix(".eq", t);
+	geneq(eq, t);
+
+	// make Go funcs (closures) for calling hash and equal from Go
+	hashfunc = typesymprefix(".hashfunc", t);
+	dsymptr(hashfunc, 0, hash, 0);
+	ggloblsym(hashfunc, widthptr, DUPOK|RODATA);
+	eqfunc = typesymprefix(".eqfunc", t);
+	dsymptr(eqfunc, 0, eq, 0);
+	ggloblsym(eqfunc, widthptr, DUPOK|RODATA);
+
+	// ../../runtime/alg.go:/typeAlg
+	ot = 0;
+	ot = dsymptr(s, ot, hashfunc, 0);
+	ot = dsymptr(s, ot, eqfunc, 0);
+
+	ggloblsym(s, ot, DUPOK|RODATA);
+	return s;
+}
+
+static int
+usegcprog(Type *t)
+{
+	vlong size, nptr;
+
+	if(!haspointers(t))
+		return 0;
+	if(t->width == BADWIDTH)
+		dowidth(t);
+	// Calculate size of the unrolled GC mask.
+	nptr = (t->width+widthptr-1)/widthptr;
+	size = nptr;
+	if(size%2)
+		size *= 2;	// repeated
+	size = size*gcBits/8;	// 4 bits per word
+	// Decide whether to use unrolled GC mask or GC program.
+	// We could use a more elaborate condition, but this seems to work well in practice.
+	// For small objects GC program can't give significant reduction.
+	// While large objects usually contain arrays; and even if it don't
+	// the program uses 2-bits per word while mask uses 4-bits per word,
+	// so the program is still smaller.
+	return size > 2*widthptr;
+}
+
+// Generates sparse GC bitmask (4 bits per word).
+static void
+gengcmask(Type *t, uint8 gcmask[16])
+{
+	Bvec *vec;
+	vlong xoffset, nptr, i, j;
+	int  half, mw;
+	uint8 bits, *pos;
+
+	memset(gcmask, 0, 16);
+	if(!haspointers(t))
+		return;
+
+	// Generate compact mask as stacks use.
+	xoffset = 0;
+	vec = bvalloc(2*widthptr*8);
+	twobitwalktype1(t, &xoffset, vec);
+
+	// Unfold the mask for the GC bitmap format:
+	// 4 bits per word, 2 high bits encode pointer info.
+	pos = (uint8*)gcmask;
+	nptr = (t->width+widthptr-1)/widthptr;
+	half = 0;
+	mw = 0;
+	// If number of words is odd, repeat the mask.
+	// This makes simpler handling of arrays in runtime.
+	for(j=0; j<=(nptr%2); j++) {
+		for(i=0; i<nptr; i++) {
+			bits = bvget(vec, i*BitsPerPointer) | bvget(vec, i*BitsPerPointer+1)<<1;
+			// Some fake types (e.g. Hmap) has missing fileds.
+			// twobitwalktype1 generates BitsDead for that holes,
+			// replace BitsDead with BitsScalar.
+			if(!mw && bits == BitsDead)
+				bits = BitsScalar;
+			mw = !mw && bits == BitsMultiWord;
+			bits <<= 2;
+			if(half)
+				bits <<= 4;
+			*pos |= bits;
+			half = !half;
+			if(!half)
+				pos++;
+		}
+	}
+}
+
+// Helper object for generation of GC programs.
+typedef struct ProgGen ProgGen;
+struct ProgGen
+{
+	Sym*	s;
+	int32	datasize;
+	uint8	data[256/PointersPerByte];
+	vlong	ot;
+};
+
+static void
+proggeninit(ProgGen *g, Sym *s)
+{
+	g->s = s;
+	g->datasize = 0;
+	g->ot = 0;
+	memset(g->data, 0, sizeof(g->data));
+}
+
+static void
+proggenemit(ProgGen *g, uint8 v)
+{
+	g->ot = duint8(g->s, g->ot, v);
+}
+
+// Emits insData block from g->data.
+static void
+proggendataflush(ProgGen *g)
+{
+	int32 i, s;
+
+	if(g->datasize == 0)
+		return;
+	proggenemit(g, insData);
+	proggenemit(g, g->datasize);
+	s = (g->datasize + PointersPerByte - 1)/PointersPerByte;
+	for(i = 0; i < s; i++)
+		proggenemit(g, g->data[i]);
+	g->datasize = 0;
+	memset(g->data, 0, sizeof(g->data));
+}
+
+static void
+proggendata(ProgGen *g, uint8 d)
+{
+	g->data[g->datasize/PointersPerByte] |= d << ((g->datasize%PointersPerByte)*BitsPerPointer);
+	g->datasize++;
+	if(g->datasize == 255)
+		proggendataflush(g);
+}
+
+// Skip v bytes due to alignment, etc.
+static void
+proggenskip(ProgGen *g, vlong off, vlong v)
+{
+	vlong i;
+
+	for(i = off; i < off+v; i++) {
+		if((i%widthptr) == 0)
+			proggendata(g, BitsScalar);
+	}
+}
+
+// Emit insArray instruction.
+static void
+proggenarray(ProgGen *g, vlong len)
+{
+	int32 i;
+
+	proggendataflush(g);
+	proggenemit(g, insArray);
+	for(i = 0; i < widthptr; i++, len >>= 8)
+		proggenemit(g, len);
+}
+
+static void
+proggenarrayend(ProgGen *g)
+{
+	proggendataflush(g);
+	proggenemit(g, insArrayEnd);
+}
+
+static vlong
+proggenfini(ProgGen *g)
+{
+	proggendataflush(g);
+	proggenemit(g, insEnd);
+	return g->ot;
+}
+
+static void gengcprog1(ProgGen *g, Type *t, vlong *xoffset);
+
+// Generates GC program for large types.
+static void
+gengcprog(Type *t, Sym **pgc0, Sym **pgc1)
+{
+	Sym *gc0, *gc1;
+	vlong nptr, size, ot, xoffset;
+	ProgGen g;
+
+	nptr = (t->width+widthptr-1)/widthptr;
+	size = nptr;
+	if(size%2)
+		size *= 2;	// repeated twice
+	size = size*PointersPerByte/8;	// 4 bits per word
+	size++;	// unroll flag in the beginning, used by runtime (see runtime.markallocated)
+	// emity space in BSS for unrolled program
+	*pgc0 = S;
+	// Don't generate it if it's too large, runtime will unroll directly into GC bitmap.
+	if(size <= MaxGCMask) {
+		gc0 = typesymprefix(".gc", t);
+		ggloblsym(gc0, size, DUPOK|NOPTR);
+		*pgc0 = gc0;
+	}
+
+	// program in RODATA
+	gc1 = typesymprefix(".gcprog", t);
+	proggeninit(&g, gc1);
+	xoffset = 0;
+	gengcprog1(&g, t, &xoffset);
+	ot = proggenfini(&g);
+	ggloblsym(gc1, ot, DUPOK|RODATA);
+	*pgc1 = gc1;
+}
+
+// Recursively walks type t and writes GC program into g.
+static void
+gengcprog1(ProgGen *g, Type *t, vlong *xoffset)
+{
+	vlong fieldoffset, i, o, n;
+	Type *t1;
+
+	switch(t->etype) {
+	case TINT8:
+	case TUINT8:
+	case TINT16:
+	case TUINT16:
+	case TINT32:
+	case TUINT32:
+	case TINT64:
+	case TUINT64:
+	case TINT:
+	case TUINT:
+	case TUINTPTR:
+	case TBOOL:
+	case TFLOAT32:
+	case TFLOAT64:
+	case TCOMPLEX64:
+	case TCOMPLEX128:
+		proggenskip(g, *xoffset, t->width);
+		*xoffset += t->width;
+		break;
+	case TPTR32:
+	case TPTR64:
+	case TUNSAFEPTR:
+	case TFUNC:
+	case TCHAN:
+	case TMAP:
+		proggendata(g, BitsPointer);
+		*xoffset += t->width;
+		break;
+	case TSTRING:
+		proggendata(g, BitsPointer);
+		proggendata(g, BitsScalar);
+		*xoffset += t->width;
+		break;
+	case TINTER:
+		proggendata(g, BitsMultiWord);
+		if(isnilinter(t))
+			proggendata(g, BitsEface);
+		else
+			proggendata(g, BitsIface);
+		*xoffset += t->width;
+		break;
+	case TARRAY:
+		if(isslice(t)) {
+			proggendata(g, BitsPointer);
+			proggendata(g, BitsScalar);
+			proggendata(g, BitsScalar);
+		} else {
+			t1 = t->type;
+			if(t1->width == 0) {
+				// ignore
+			} if(t->bound <= 1 || t->bound*t1->width < 32*widthptr) {
+				for(i = 0; i < t->bound; i++)
+					gengcprog1(g, t1, xoffset);
+			} else if(!haspointers(t1)) {
+				n = t->width;
+				n -= -*xoffset&(widthptr-1); // skip to next ptr boundary
+				proggenarray(g, (n+widthptr-1)/widthptr);
+				proggendata(g, BitsScalar);
+				proggenarrayend(g);
+				*xoffset -= (n+widthptr-1)/widthptr*widthptr - t->width;
+			} else {
+				proggenarray(g, t->bound);
+				gengcprog1(g, t1, xoffset);
+				*xoffset += (t->bound-1)*t1->width;
+				proggenarrayend(g);
+			}
+		}
+		break;
+	case TSTRUCT:
+		o = 0;
+		for(t1 = t->type; t1 != T; t1 = t1->down) {
+			fieldoffset = t1->width;
+			proggenskip(g, *xoffset, fieldoffset - o);
+			*xoffset += fieldoffset - o;
+			gengcprog1(g, t1->type, xoffset);
+			o = fieldoffset + t1->type->width;
+		}
+		proggenskip(g, *xoffset, t->width - o);
+		*xoffset += t->width - o;
+		break;
+	default:
+		fatal("gengcprog1: unexpected type, %T", t);
+	}
+}
diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go
new file mode 100644
index 0000000..0fb15c2
--- /dev/null
+++ b/src/cmd/gc/runtime.go
@@ -0,0 +1,164 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// NOTE: If you change this file you must run "./mkbuiltin"
+// to update builtin.c.  This is not done automatically
+// to avoid depending on having a working compiler binary.
+
+// +build ignore
+
+package PACKAGE
+
+// emitted by compiler, not referred to by go programs
+
+func newobject(typ *byte) *any
+func panicindex()
+func panicslice()
+func panicdivide()
+func throwreturn()
+func throwinit()
+func panicwrap(string, string, string)
+
+func gopanic(interface{})
+func gorecover(*int32) interface{}
+
+func printbool(bool)
+func printfloat(float64)
+func printint(int64)
+func printhex(uint64)
+func printuint(uint64)
+func printcomplex(complex128)
+func printstring(string)
+func printpointer(any)
+func printiface(any)
+func printeface(any)
+func printslice(any)
+func printnl()
+func printsp()
+
+func concatstring2(string, string) string
+func concatstring3(string, string, string) string
+func concatstring4(string, string, string, string) string
+func concatstring5(string, string, string, string, string) string
+func concatstrings([]string) string
+
+func cmpstring(string, string) int
+func eqstring(string, string) bool
+func intstring(int64) string
+func slicebytetostring([]byte) string
+func slicebytetostringtmp([]byte) string
+func slicerunetostring([]rune) string
+func stringtoslicebyte(string) []byte
+func stringtoslicerune(string) []rune
+func stringiter(string, int) int
+func stringiter2(string, int) (retk int, retv rune)
+func slicecopy(to any, fr any, wid uintptr) int
+func slicestringcopy(to any, fr any) int
+
+// interface conversions
+func typ2Itab(typ *byte, typ2 *byte, cache **byte) (ret *byte)
+func convI2E(elem any) (ret any)
+func convI2I(typ *byte, elem any) (ret any)
+func convT2E(typ *byte, elem *any) (ret any)
+func convT2I(typ *byte, typ2 *byte, cache **byte, elem *any) (ret any)
+
+// interface type assertions  x.(T)
+func assertE2E(typ *byte, iface any) (ret any)
+func assertE2E2(typ *byte, iface any) (ret any, ok bool)
+func assertE2I(typ *byte, iface any) (ret any)
+func assertE2I2(typ *byte, iface any) (ret any, ok bool)
+func assertE2T(typ *byte, iface any) (ret any)
+func assertE2T2(typ *byte, iface any) (ret any, ok bool)
+func assertI2E(typ *byte, iface any) (ret any)
+func assertI2E2(typ *byte, iface any) (ret any, ok bool)
+func assertI2I(typ *byte, iface any) (ret any)
+func assertI2I2(typ *byte, iface any) (ret any, ok bool)
+func assertI2T(typ *byte, iface any) (ret any)
+func assertI2T2(typ *byte, iface any) (ret any, ok bool)
+func assertI2TOK(typ *byte, iface any) (ok bool)
+func assertE2TOK(typ *byte, iface any) (ok bool)
+
+func ifaceeq(i1 any, i2 any) (ret bool)
+func efaceeq(i1 any, i2 any) (ret bool)
+func ifacethash(i1 any) (ret uint32)
+func efacethash(i1 any) (ret uint32)
+
+// *byte is really *runtime.Type
+func makemap(mapType *byte, hint int64) (hmap map[any]any)
+func mapaccess1(mapType *byte, hmap map[any]any, key *any) (val *any)
+func mapaccess1_fast32(mapType *byte, hmap map[any]any, key any) (val *any)
+func mapaccess1_fast64(mapType *byte, hmap map[any]any, key any) (val *any)
+func mapaccess1_faststr(mapType *byte, hmap map[any]any, key any) (val *any)
+func mapaccess2(mapType *byte, hmap map[any]any, key *any) (val *any, pres bool)
+func mapaccess2_fast32(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
+func mapaccess2_fast64(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
+func mapaccess2_faststr(mapType *byte, hmap map[any]any, key any) (val *any, pres bool)
+func mapassign1(mapType *byte, hmap map[any]any, key *any, val *any)
+func mapiterinit(mapType *byte, hmap map[any]any, hiter *any)
+func mapdelete(mapType *byte, hmap map[any]any, key *any)
+func mapiternext(hiter *any)
+
+// *byte is really *runtime.Type
+func makechan(chanType *byte, hint int64) (hchan chan any)
+func chanrecv1(chanType *byte, hchan <-chan any, elem *any)
+func chanrecv2(chanType *byte, hchan <-chan any, elem *any) bool
+func chansend1(chanType *byte, hchan chan<- any, elem *any)
+func closechan(hchan any)
+
+// *byte is really *runtime.Type
+func writebarrierptr(dst *any, src any)
+func writebarrierstring(dst *any, src any)
+func writebarrierslice(dst *any, src any)
+func writebarrieriface(dst *any, src any)
+
+// The unused *byte argument makes sure that src is 2-pointer-aligned,
+// which is the maximum alignment on NaCl amd64p32
+// (and possibly on 32-bit systems if we start 64-bit aligning uint64s).
+func writebarrierfat2(dst *any, _ *byte, src any)
+func writebarrierfat3(dst *any, _ *byte, src any)
+func writebarrierfat4(dst *any, _ *byte, src any)
+func writebarrierfat(typ *byte, dst *any, src *any)
+
+func selectnbsend(chanType *byte, hchan chan<- any, elem *any) bool
+func selectnbrecv(chanType *byte, elem *any, hchan <-chan any) bool
+func selectnbrecv2(chanType *byte, elem *any, received *bool, hchan <-chan any) bool
+
+func newselect(sel *byte, selsize int64, size int32)
+func selectsend(sel *byte, hchan chan<- any, elem *any) (selected bool)
+func selectrecv(sel *byte, hchan <-chan any, elem *any) (selected bool)
+func selectrecv2(sel *byte, hchan <-chan any, elem *any, received *bool) (selected bool)
+func selectdefault(sel *byte) (selected bool)
+func selectgo(sel *byte)
+func block()
+
+func makeslice(typ *byte, nel int64, cap int64) (ary []any)
+func growslice(typ *byte, old []any, n int64) (ary []any)
+func memmove(to *any, frm *any, length uintptr)
+
+func memequal(x, y *any, size uintptr) bool
+func memequal8(x, y *any, size uintptr) bool
+func memequal16(x, y *any, size uintptr) bool
+func memequal32(x, y *any, size uintptr) bool
+func memequal64(x, y *any, size uintptr) bool
+func memequal128(x, y *any, size uintptr) bool
+
+// only used on 32-bit
+func int64div(int64, int64) int64
+func uint64div(uint64, uint64) uint64
+func int64mod(int64, int64) int64
+func uint64mod(uint64, uint64) uint64
+func float64toint64(float64) int64
+func float64touint64(float64) uint64
+func int64tofloat64(int64) float64
+func uint64tofloat64(uint64) float64
+
+func complex128div(num complex128, den complex128) (quo complex128)
+
+// race detection
+func racefuncenter(uintptr)
+func racefuncexit()
+func raceread(uintptr)
+func racewrite(uintptr)
+func racereadrange(addr, size uintptr)
+func racewriterange(addr, size uintptr)
diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c
new file mode 100644
index 0000000..965ad27
--- /dev/null
+++ b/src/cmd/gc/select.c
@@ -0,0 +1,377 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * select
+ */
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+static Type* selecttype(int32 size);
+
+void
+typecheckselect(Node *sel)
+{
+	Node *ncase, *n, *def;
+	NodeList *l;
+	int lno, count;
+
+	def = nil;
+	lno = setlineno(sel);
+	count = 0;
+	typechecklist(sel->ninit, Etop);
+	for(l=sel->list; l; l=l->next) {
+		count++;
+		ncase = l->n;
+		setlineno(ncase);
+		if(ncase->op != OXCASE)
+			fatal("typecheckselect %O", ncase->op);
+
+		if(ncase->list == nil) {
+			// default
+			if(def != N)
+				yyerror("multiple defaults in select (first at %L)", def->lineno);
+			else
+				def = ncase;
+		} else if(ncase->list->next) {
+			yyerror("select cases cannot be lists");
+		} else {
+			n = typecheck(&ncase->list->n, Etop);
+			ncase->left = n;
+			ncase->list = nil;
+			setlineno(n);
+			switch(n->op) {
+			default:
+				yyerror("select case must be receive, send or assign recv");
+				break;
+
+			case OAS:
+				// convert x = <-c into OSELRECV(x, <-c).
+				// remove implicit conversions; the eventual assignment
+				// will reintroduce them.
+				if((n->right->op == OCONVNOP || n->right->op == OCONVIFACE) && n->right->implicit)
+					n->right = n->right->left;
+
+				if(n->right->op != ORECV) {
+					yyerror("select assignment must have receive on right hand side");
+					break;
+				}
+				n->op = OSELRECV;
+				break;
+
+			case OAS2RECV:
+				// convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
+				if(n->rlist->n->op != ORECV) {
+					yyerror("select assignment must have receive on right hand side");
+					break;
+				}
+				n->op = OSELRECV2;
+				n->left = n->list->n;
+				n->ntest = n->list->next->n;
+				n->list = nil;
+				n->right = n->rlist->n;
+				n->rlist = nil;
+				break;
+
+			case ORECV:
+				// convert <-c into OSELRECV(N, <-c)
+				n = nod(OSELRECV, N, n);
+				n->typecheck = 1;
+				ncase->left = n;
+				break;
+
+			case OSEND:
+				break;
+			}
+		}
+		typechecklist(ncase->nbody, Etop);
+	}
+	sel->xoffset = count;
+	lineno = lno;
+}
+
+void
+walkselect(Node *sel)
+{
+	int lno, i;
+	Node *n, *r, *a, *var, *selv, *cas, *dflt, *ch;
+	NodeList *l, *init;
+	
+	if(sel->list == nil && sel->xoffset != 0)
+		fatal("double walkselect");	// already rewrote
+	
+	lno = setlineno(sel);
+	i = count(sel->list);
+	
+	// optimization: zero-case select
+	if(i == 0) {
+		sel->nbody = list1(mkcall("block", nil, nil));
+		goto out;
+	}
+
+	// optimization: one-case select: single op.
+	// TODO(rsc): Reenable optimization once order.c can handle it.
+	// golang.org/issue/7672.
+	if(i == 1) {
+		cas = sel->list->n;
+		setlineno(cas);
+		l = cas->ninit;
+		if(cas->left != N) {  // not default:
+			n = cas->left;
+			l = concat(l, n->ninit);
+			n->ninit = nil;
+			switch(n->op) {
+			default:
+				fatal("select %O", n->op);
+
+			case OSEND:
+				// ok already
+				ch = n->left;
+				break;
+
+			case OSELRECV:
+				ch = n->right->left;
+			Selrecv1:
+				if(n->left == N)
+					n = n->right;
+				else
+					n->op = OAS;
+				break;
+			
+			case OSELRECV2:
+				ch = n->right->left;
+				if(n->ntest == N)
+					goto Selrecv1;
+				if(n->left == N) {
+					typecheck(&nblank, Erv | Easgn);
+					n->left = nblank;
+				}
+				n->op = OAS2;
+				n->list = list(list1(n->left), n->ntest);
+				n->rlist = list1(n->right);
+				n->right = N;
+				n->left = N;
+				n->ntest = N;
+				n->typecheck = 0;
+				typecheck(&n, Etop);
+				break;
+			}
+
+			// if ch == nil { block() }; n;
+			a = nod(OIF, N, N);
+			a->ntest = nod(OEQ, ch, nodnil());
+			a->nbody = list1(mkcall("block", nil, &l));
+			typecheck(&a, Etop);
+			l = list(l, a);
+			l = list(l, n);
+		}
+		l = concat(l, cas->nbody);
+		sel->nbody = l;
+		goto out;
+	}
+
+	// convert case value arguments to addresses.
+	// this rewrite is used by both the general code and the next optimization.
+	for(l=sel->list; l; l=l->next) {
+		cas = l->n;
+		setlineno(cas);
+		n = cas->left;
+		if(n == N)
+			continue;
+		switch(n->op) {
+		case OSEND:
+			n->right = nod(OADDR, n->right, N);
+			typecheck(&n->right, Erv);
+			break;
+		case OSELRECV:
+		case OSELRECV2:
+			if(n->op == OSELRECV2 && n->ntest == N)
+				n->op = OSELRECV;
+			if(n->op == OSELRECV2) {
+				n->ntest = nod(OADDR, n->ntest, N);
+				typecheck(&n->ntest, Erv);
+			}
+			if(n->left == N)
+				n->left = nodnil();
+			else {
+				n->left = nod(OADDR, n->left, N);
+				typecheck(&n->left, Erv);
+			}			
+			break;
+		}
+	}
+
+	// optimization: two-case select but one is default: single non-blocking op.
+	if(i == 2 && (sel->list->n->left == nil || sel->list->next->n->left == nil)) {
+		if(sel->list->n->left == nil) {
+			cas = sel->list->next->n;
+			dflt = sel->list->n;
+		} else {
+			dflt = sel->list->next->n;
+			cas = sel->list->n;
+		}
+		
+		n = cas->left;
+		setlineno(n);
+		r = nod(OIF, N, N);
+		r->ninit = cas->ninit;
+		switch(n->op) {
+		default:
+			fatal("select %O", n->op);
+
+		case OSEND:
+			// if selectnbsend(c, v) { body } else { default body }
+			ch = n->left;
+			r->ntest = mkcall1(chanfn("selectnbsend", 2, ch->type),
+					types[TBOOL], &r->ninit, typename(ch->type), ch, n->right);
+			break;
+			
+		case OSELRECV:
+			// if c != nil && selectnbrecv(&v, c) { body } else { default body }
+			r = nod(OIF, N, N);
+			r->ninit = cas->ninit;
+			ch = n->right->left;
+			r->ntest = mkcall1(chanfn("selectnbrecv", 2, ch->type),
+					types[TBOOL], &r->ninit, typename(ch->type), n->left, ch);
+			break;
+
+		case OSELRECV2:
+			// if c != nil && selectnbrecv2(&v, c) { body } else { default body }
+			r = nod(OIF, N, N);
+			r->ninit = cas->ninit;
+			ch = n->right->left;
+			r->ntest = mkcall1(chanfn("selectnbrecv2", 2, ch->type),
+					types[TBOOL], &r->ninit, typename(ch->type), n->left, n->ntest, ch);
+			break;
+		}
+		typecheck(&r->ntest, Erv);
+		r->nbody = cas->nbody;
+		r->nelse = concat(dflt->ninit, dflt->nbody);
+		sel->nbody = list1(r);
+		goto out;
+	}		
+
+	init = sel->ninit;
+	sel->ninit = nil;
+
+	// generate sel-struct
+	setlineno(sel);
+	selv = temp(selecttype(sel->xoffset));
+	r = nod(OAS, selv, N);
+	typecheck(&r, Etop);
+	init = list(init, r);
+	var = conv(conv(nod(OADDR, selv, N), types[TUNSAFEPTR]), ptrto(types[TUINT8]));
+	r = mkcall("newselect", T, nil, var, nodintconst(selv->type->width), nodintconst(sel->xoffset));
+	typecheck(&r, Etop);
+	init = list(init, r);
+
+	// register cases
+	for(l=sel->list; l; l=l->next) {
+		cas = l->n;
+		setlineno(cas);
+		n = cas->left;
+		r = nod(OIF, N, N);
+		r->ninit = cas->ninit;
+		cas->ninit = nil;
+		if(n != nil) {
+			r->ninit = concat(r->ninit, n->ninit);
+			n->ninit = nil;
+		}
+		if(n == nil) {
+			// selectdefault(sel *byte);
+			r->ntest = mkcall("selectdefault", types[TBOOL], &r->ninit, var);
+		} else {
+			switch(n->op) {
+			default:
+				fatal("select %O", n->op);
+	
+			case OSEND:
+				// selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
+				r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL],
+					&r->ninit, var, n->left, n->right);
+				break;
+
+			case OSELRECV:
+				// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
+				r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->left->type), types[TBOOL],
+					&r->ninit, var, n->right->left, n->left);
+				break;
+
+			case OSELRECV2:
+				// selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
+				r->ntest = mkcall1(chanfn("selectrecv2", 2, n->right->left->type), types[TBOOL],
+					&r->ninit, var, n->right->left, n->left, n->ntest);
+				break;
+			}
+		}
+		// selv is no longer alive after use.
+		r->nbody = list(r->nbody, nod(OVARKILL, selv, N));
+		r->nbody = concat(r->nbody, cas->nbody);
+		r->nbody = list(r->nbody, nod(OBREAK, N, N));
+		init = list(init, r);
+	}
+
+	// run the select
+	setlineno(sel);
+	init = list(init, mkcall("selectgo", T, nil, var));
+	sel->nbody = init;
+
+out:
+	sel->list = nil;
+	walkstmtlist(sel->nbody);
+	lineno = lno;
+}
+
+// Keep in sync with src/runtime/chan.h.
+static Type*
+selecttype(int32 size)
+{
+	Node *sel, *sudog, *scase, *arr;
+
+	// TODO(dvyukov): it's possible to generate SudoG and Scase only once
+	// and then cache; and also cache Select per size.
+	sudog = nod(OTSTRUCT, N, N);
+	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("g")), typenod(ptrto(types[TUINT8]))));
+	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("selectdone")), typenod(ptrto(types[TUINT8]))));
+	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("link")), typenod(ptrto(types[TUINT8]))));
+	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("prev")), typenod(ptrto(types[TUINT8]))));
+	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8]))));
+	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64])));
+	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("nrelease")), typenod(types[TINT32])));
+	sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("waitlink")), typenod(ptrto(types[TUINT8]))));
+	typecheck(&sudog, Etype);
+	sudog->type->noalg = 1;
+	sudog->type->local = 1;
+
+	scase = nod(OTSTRUCT, N, N);
+	scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8]))));
+	scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("chan")), typenod(ptrto(types[TUINT8]))));
+	scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("pc")), typenod(types[TUINTPTR])));
+	scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("kind")), typenod(types[TUINT16])));
+	scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("so")), typenod(types[TUINT16])));
+	scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("receivedp")), typenod(ptrto(types[TUINT8]))));
+	scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64])));
+	typecheck(&scase, Etype);
+	scase->type->noalg = 1;
+	scase->type->local = 1;
+
+	sel = nod(OTSTRUCT, N, N);
+	sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("tcase")), typenod(types[TUINT16])));
+	sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("ncase")), typenod(types[TUINT16])));
+	sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("pollorder")), typenod(ptrto(types[TUINT8]))));
+	sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("lockorder")), typenod(ptrto(types[TUINT8]))));
+	arr = nod(OTARRAY, nodintconst(size), scase);
+	sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("scase")), arr));
+	arr = nod(OTARRAY, nodintconst(size), typenod(ptrto(types[TUINT8])));
+	sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("lockorderarr")), arr));
+	arr = nod(OTARRAY, nodintconst(size), typenod(types[TUINT16]));
+	sel->list = list(sel->list, nod(ODCLFIELD, newname(lookup("pollorderarr")), arr));
+	typecheck(&sel, Etype);
+	sel->type->noalg = 1;
+	sel->type->local = 1;
+
+	return sel->type;
+}
diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c
new file mode 100644
index 0000000..8ad7ae7
--- /dev/null
+++ b/src/cmd/gc/sinit.c
@@ -0,0 +1,1505 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * static initialization
+ */
+
+#include	<u.h>
+#include	<libc.h>
+#include	"go.h"
+
+enum
+{
+	InitNotStarted = 0,
+	InitDone = 1,
+	InitPending = 2,
+};
+
+static void initplan(Node*);
+static NodeList *initlist;
+static void init2(Node*, NodeList**);
+static void init2list(NodeList*, NodeList**);
+static int staticinit(Node*, NodeList**);
+static Node *staticname(Type*, int);
+
+// init1 walks the AST starting at n, and accumulates in out
+// the list of definitions needing init code in dependency order.
+static void
+init1(Node *n, NodeList **out)
+{
+	NodeList *l;
+	Node *nv;
+
+	if(n == N)
+		return;
+	init1(n->left, out);
+	init1(n->right, out);
+	for(l=n->list; l; l=l->next)
+		init1(l->n, out);
+
+	if(n->left && n->type && n->left->op == OTYPE && n->class == PFUNC) {
+		// Methods called as Type.Method(receiver, ...).
+		// Definitions for method expressions are stored in type->nname.
+		init1(n->type->nname, out);
+	}
+
+	if(n->op != ONAME)
+		return;
+	switch(n->class) {
+	case PEXTERN:
+	case PFUNC:
+		break;
+	default:
+		if(isblank(n) && n->curfn == N && n->defn != N && n->defn->initorder == InitNotStarted) {
+			// blank names initialization is part of init() but not
+			// when they are inside a function.
+			break;
+		}
+		return;
+	}
+
+	if(n->initorder == InitDone)
+		return;
+	if(n->initorder == InitPending) {
+		// Since mutually recursive sets of functions are allowed,
+		// we don't necessarily raise an error if n depends on a node
+		// which is already waiting for its dependencies to be visited.
+		//
+		// initlist contains a cycle of identifiers referring to each other.
+		// If this cycle contains a variable, then this variable refers to itself.
+		// Conversely, if there exists an initialization cycle involving
+		// a variable in the program, the tree walk will reach a cycle
+		// involving that variable.
+		if(n->class != PFUNC) {
+			nv = n;
+			goto foundinitloop;
+		}
+		for(l=initlist; l->n!=n; l=l->next) {
+			if(l->n->class != PFUNC) {
+				nv = l->n;
+				goto foundinitloop;
+			}
+		}
+		// The loop involves only functions, ok.
+		return;
+
+	foundinitloop:
+		// if there have already been errors printed,
+		// those errors probably confused us and
+		// there might not be a loop.  let the user
+		// fix those first.
+		flusherrors();
+		if(nerrors > 0)
+			errorexit();
+
+		// There is a loop involving nv. We know about
+		// n and initlist = n1 <- ... <- nv <- ... <- n <- ...
+		print("%L: initialization loop:\n", nv->lineno);
+		// Build back pointers in initlist.
+		for(l=initlist; l; l=l->next)
+			if(l->next != nil)
+				l->next->end = l;
+		// Print nv -> ... -> n1 -> n.
+		for(l=initlist; l->n!=nv; l=l->next);
+		for(; l; l=l->end)
+			print("\t%L %S refers to\n", l->n->lineno, l->n->sym);
+		// Print n -> ... -> nv.
+		for(l=initlist; l->n!=n; l=l->next);
+		for(; l->n != nv; l=l->end)
+			print("\t%L %S refers to\n", l->n->lineno, l->n->sym);
+		print("\t%L %S\n", nv->lineno, nv->sym);
+		errorexit();
+	}
+
+	// reached a new unvisited node.
+	n->initorder = InitPending;
+	l = malloc(sizeof *l);
+	if(l == nil) {
+		flusherrors();
+		yyerror("out of memory");
+		errorexit();
+	}
+	l->next = initlist;
+	l->n = n;
+	l->end = nil;
+	initlist = l;
+
+	// make sure that everything n depends on is initialized.
+	// n->defn is an assignment to n
+	if(n->defn != N) {
+		switch(n->defn->op) {
+		default:
+			goto bad;
+
+		case ODCLFUNC:
+			init2list(n->defn->nbody, out);
+			break;
+
+		case OAS:
+			if(n->defn->left != n)
+				goto bad;
+			if(isblank(n->defn->left) && candiscard(n->defn->right)) {
+				n->defn->op = OEMPTY;
+				n->defn->left = N;
+				n->defn->right = N;
+				break;
+			}
+
+			init2(n->defn->right, out);
+			if(debug['j'])
+				print("%S\n", n->sym);
+			if(isblank(n) || !staticinit(n, out)) {
+				if(debug['%'])
+					dump("nonstatic", n->defn);
+				*out = list(*out, n->defn);
+			}
+			break;
+
+		case OAS2FUNC:
+		case OAS2MAPR:
+		case OAS2DOTTYPE:
+		case OAS2RECV:
+			if(n->defn->initorder != InitNotStarted)
+				break;
+			n->defn->initorder = InitDone;
+			for(l=n->defn->rlist; l; l=l->next)
+				init1(l->n, out);
+			if(debug['%']) dump("nonstatic", n->defn);
+			*out = list(*out, n->defn);
+			break;
+		}
+	}
+	l = initlist;
+	initlist = l->next;
+	if(l->n != n)
+		fatal("bad initlist");
+	free(l);
+	n->initorder = InitDone;
+	return;
+
+bad:
+	dump("defn", n->defn);
+	fatal("init1: bad defn");
+}
+
+// recurse over n, doing init1 everywhere.
+static void
+init2(Node *n, NodeList **out)
+{
+	if(n == N || n->initorder == InitDone)
+		return;
+
+	if(n->op == ONAME && n->ninit)
+		fatal("name %S with ninit: %+N\n", n->sym, n);
+
+	init1(n, out);
+	init2(n->left, out);
+	init2(n->right, out);
+	init2(n->ntest, out);
+	init2list(n->ninit, out);
+	init2list(n->list, out);
+	init2list(n->rlist, out);
+	init2list(n->nbody, out);
+	init2list(n->nelse, out);
+	
+	if(n->op == OCLOSURE)
+		init2list(n->closure->nbody, out);
+	if(n->op == ODOTMETH || n->op == OCALLPART)
+		init2(n->type->nname, out);
+}
+
+static void
+init2list(NodeList *l, NodeList **out)
+{
+	for(; l; l=l->next)
+		init2(l->n, out);
+}
+
+static void
+initreorder(NodeList *l, NodeList **out)
+{
+	Node *n;
+
+	for(; l; l=l->next) {
+		n = l->n;
+		switch(n->op) {
+		case ODCLFUNC:
+		case ODCLCONST:
+		case ODCLTYPE:
+			continue;
+		}
+		initreorder(n->ninit, out);
+		n->ninit = nil;
+		init1(n, out);
+	}
+}
+
+// initfix computes initialization order for a list l of top-level
+// declarations and outputs the corresponding list of statements
+// to include in the init() function body.
+NodeList*
+initfix(NodeList *l)
+{
+	NodeList *lout;
+	int lno;
+
+	lout = nil;
+	lno = lineno;
+	initreorder(l, &lout);
+	lineno = lno;
+	return lout;
+}
+
+/*
+ * compilation of top-level (static) assignments
+ * into DATA statements if at all possible.
+ */
+
+static int staticassign(Node*, Node*, NodeList**);
+
+static int
+staticinit(Node *n, NodeList **out)
+{
+	Node *l, *r;
+
+	if(n->op != ONAME || n->class != PEXTERN || n->defn == N || n->defn->op != OAS)
+		fatal("staticinit");
+
+	lineno = n->lineno;
+	l = n->defn->left;
+	r = n->defn->right;
+	return staticassign(l, r, out);
+}
+
+// like staticassign but we are copying an already
+// initialized value r.
+static int
+staticcopy(Node *l, Node *r, NodeList **out)
+{
+	int i;
+	InitEntry *e;
+	InitPlan *p;
+	Node *a, *ll, *rr, *orig, n1;
+
+	if(r->op != ONAME || r->class != PEXTERN || r->sym->pkg != localpkg)
+		return 0;
+	if(r->defn == N)	// probably zeroed but perhaps supplied externally and of unknown value
+		return 0;
+	if(r->defn->op != OAS)
+		return 0;
+	orig = r;
+	r = r->defn->right;
+	
+	switch(r->op) {
+	case ONAME:
+		if(staticcopy(l, r, out))
+			return 1;
+		*out = list(*out, nod(OAS, l, r));
+		return 1;
+	
+	case OLITERAL:
+		if(iszero(r))
+			return 1;
+		gdata(l, r, l->type->width);
+		return 1;
+
+	case OADDR:
+		switch(r->left->op) {
+		case ONAME:
+			gdata(l, r, l->type->width);
+			return 1;
+		}
+		break;
+	
+	case OPTRLIT:
+		switch(r->left->op) {
+		default:
+			//dump("not static addr", r);
+			break;
+		case OARRAYLIT:
+		case OSTRUCTLIT:
+		case OMAPLIT:
+			// copy pointer
+			gdata(l, nod(OADDR, r->nname, N), l->type->width);
+			return 1;
+		}
+		break;
+
+	case OARRAYLIT:
+		if(isslice(r->type)) {
+			// copy slice
+			a = r->nname;
+			n1 = *l;
+			n1.xoffset = l->xoffset + Array_array;
+			gdata(&n1, nod(OADDR, a, N), widthptr);
+			n1.xoffset = l->xoffset + Array_nel;
+			gdata(&n1, r->right, widthint);
+			n1.xoffset = l->xoffset + Array_cap;
+			gdata(&n1, r->right, widthint);
+			return 1;
+		}
+		// fall through
+	case OSTRUCTLIT:
+		p = r->initplan;
+		n1 = *l;
+		for(i=0; i<p->len; i++) {
+			e = &p->e[i];
+			n1.xoffset = l->xoffset + e->xoffset;
+			n1.type = e->expr->type;
+			if(e->expr->op == OLITERAL)
+				gdata(&n1, e->expr, n1.type->width);
+			else {
+				ll = nod(OXXX, N, N);
+				*ll = n1;
+				ll->orig = ll; // completely separate copy
+				if(!staticassign(ll, e->expr, out)) {
+					// Requires computation, but we're
+					// copying someone else's computation.
+					rr = nod(OXXX, N, N);
+					*rr = *orig;
+					rr->orig = rr; // completely separate copy
+					rr->type = ll->type;
+					rr->xoffset += e->xoffset;
+					*out = list(*out, nod(OAS, ll, rr));
+				}
+			}
+		}
+		return 1;
+	}
+	return 0;
+}
+
+static int
+staticassign(Node *l, Node *r, NodeList **out)
+{
+	Node *a, n1;
+	Type *ta;
+	InitPlan *p;
+	InitEntry *e;
+	int i;
+	Strlit *sval;
+	
+	switch(r->op) {
+	default:
+		//dump("not static", r);
+		break;
+	
+	case ONAME:
+		if(r->class == PEXTERN && r->sym->pkg == localpkg)
+			return staticcopy(l, r, out);
+		break;
+
+	case OLITERAL:
+		if(iszero(r))
+			return 1;
+		gdata(l, r, l->type->width);
+		return 1;
+
+	case OADDR:
+		switch(r->left->op) {
+		default:
+			//dump("not static addr", r);
+			break;
+
+		case ONAME:
+			gdata(l, r, l->type->width);
+			return 1;
+		}
+	
+	case OPTRLIT:
+		switch(r->left->op) {
+		default:
+			//dump("not static ptrlit", r);
+			break;
+
+		case OARRAYLIT:
+		case OMAPLIT:
+		case OSTRUCTLIT:
+			// Init pointer.
+			a = staticname(r->left->type, 1);
+			r->nname = a;
+			gdata(l, nod(OADDR, a, N), l->type->width);
+			// Init underlying literal.
+			if(!staticassign(a, r->left, out))
+				*out = list(*out, nod(OAS, a, r->left));
+			return 1;
+		}
+		break;
+
+	case OSTRARRAYBYTE:
+		if(l->class == PEXTERN && r->left->op == OLITERAL) {
+			sval = r->left->val.u.sval;
+			slicebytes(l, sval->s, sval->len);
+			return 1;
+		}
+		break;
+
+	case OARRAYLIT:
+		initplan(r);
+		if(isslice(r->type)) {
+			// Init slice.
+			ta = typ(TARRAY);
+			ta->type = r->type->type;
+			ta->bound = mpgetfix(r->right->val.u.xval);
+			a = staticname(ta, 1);
+			r->nname = a;
+			n1 = *l;
+			n1.xoffset = l->xoffset + Array_array;
+			gdata(&n1, nod(OADDR, a, N), widthptr);
+			n1.xoffset = l->xoffset + Array_nel;
+			gdata(&n1, r->right, widthint);
+			n1.xoffset = l->xoffset + Array_cap;
+			gdata(&n1, r->right, widthint);
+			// Fall through to init underlying array.
+			l = a;
+		}
+		// fall through
+	case OSTRUCTLIT:
+		initplan(r);
+		p = r->initplan;
+		n1 = *l;
+		for(i=0; i<p->len; i++) {
+			e = &p->e[i];
+			n1.xoffset = l->xoffset + e->xoffset;
+			n1.type = e->expr->type;
+			if(e->expr->op == OLITERAL)
+				gdata(&n1, e->expr, n1.type->width);
+			else {
+				a = nod(OXXX, N, N);
+				*a = n1;
+				a->orig = a; // completely separate copy
+				if(!staticassign(a, e->expr, out))
+					*out = list(*out, nod(OAS, a, e->expr));
+			}
+		}
+		return 1;
+
+	case OMAPLIT:
+		// TODO: Table-driven map insert.
+		break;
+	}
+	return 0;
+}
+
+/*
+ * from here down is the walk analysis
+ * of composite literals.
+ * most of the work is to generate
+ * data statements for the constant
+ * part of the composite literal.
+ */
+
+static	void	structlit(int ctxt, int pass, Node *n, Node *var, NodeList **init);
+static	void	arraylit(int ctxt, int pass, Node *n, Node *var, NodeList **init);
+static	void	slicelit(int ctxt, Node *n, Node *var, NodeList **init);
+static	void	maplit(int ctxt, Node *n, Node *var, NodeList **init);
+
+static Node*
+staticname(Type *t, int ctxt)
+{
+	Node *n;
+
+	snprint(namebuf, sizeof(namebuf), "statictmp_%.4d", statuniqgen);
+	statuniqgen++;
+	n = newname(lookup(namebuf));
+	if(!ctxt)
+		n->readonly = 1;
+	addvar(n, t, PEXTERN);
+	return n;
+}
+
+static int
+isliteral(Node *n)
+{
+	if(n->op == OLITERAL)
+		if(n->val.ctype != CTNIL)
+			return 1;
+	return 0;
+}
+
+static int
+simplename(Node *n)
+{
+	if(n->op != ONAME)
+		goto no;
+	if(!n->addable)
+		goto no;
+	if(n->class & PHEAP)
+		goto no;
+	if(n->class == PPARAMREF)
+		goto no;
+	return 1;
+
+no:
+	return 0;
+}
+
+static void
+litas(Node *l, Node *r, NodeList **init)
+{
+	Node *a;
+
+	a = nod(OAS, l, r);
+	typecheck(&a, Etop);
+	walkexpr(&a, init);
+	*init = list(*init, a);
+}
+
+enum
+{
+	MODEDYNAM	= 1,
+	MODECONST	= 2,
+};
+
+static int
+getdyn(Node *n, int top)
+{
+	NodeList *nl;
+	Node *value;
+	int mode;
+
+	mode = 0;
+	switch(n->op) {
+	default:
+		if(isliteral(n))
+			return MODECONST;
+		return MODEDYNAM;
+	case OARRAYLIT:
+		if(!top && n->type->bound < 0)
+			return MODEDYNAM;
+	case OSTRUCTLIT:
+		break;
+	}
+
+	for(nl=n->list; nl; nl=nl->next) {
+		value = nl->n->right;
+		mode |= getdyn(value, 0);
+		if(mode == (MODEDYNAM|MODECONST))
+			break;
+	}
+	return mode;
+}
+
+static void
+structlit(int ctxt, int pass, Node *n, Node *var, NodeList **init)
+{
+	Node *r, *a;
+	NodeList *nl;
+	Node *index, *value;
+
+	for(nl=n->list; nl; nl=nl->next) {
+		r = nl->n;
+		if(r->op != OKEY)
+			fatal("structlit: rhs not OKEY: %N", r);
+		index = r->left;
+		value = r->right;
+
+		switch(value->op) {
+		case OARRAYLIT:
+			if(value->type->bound < 0) {
+				if(pass == 1 && ctxt != 0) {
+					a = nod(ODOT, var, newname(index->sym));
+					slicelit(ctxt, value, a, init);
+				} else
+				if(pass == 2 && ctxt == 0) {
+					a = nod(ODOT, var, newname(index->sym));
+					slicelit(ctxt, value, a, init);
+				} else
+				if(pass == 3)
+					break;
+				continue;
+			}
+			a = nod(ODOT, var, newname(index->sym));
+			arraylit(ctxt, pass, value, a, init);
+			continue;
+
+		case OSTRUCTLIT:
+			a = nod(ODOT, var, newname(index->sym));
+			structlit(ctxt, pass, value, a, init);
+			continue;
+		}
+
+		if(isliteral(value)) {
+			if(pass == 2)
+				continue;
+		} else
+			if(pass == 1)
+				continue;
+
+		// build list of var.field = expr
+		a = nod(ODOT, var, newname(index->sym));
+		a = nod(OAS, a, value);
+		typecheck(&a, Etop);
+		if(pass == 1) {
+			walkexpr(&a, init);	// add any assignments in r to top
+			if(a->op != OAS)
+				fatal("structlit: not as");
+			a->dodata = 2;
+		} else {
+			orderstmtinplace(&a);
+			walkstmt(&a);
+		}
+		*init = list(*init, a);
+	}
+}
+
+static void
+arraylit(int ctxt, int pass, Node *n, Node *var, NodeList **init)
+{
+	Node *r, *a;
+	NodeList *l;
+	Node *index, *value;
+
+	for(l=n->list; l; l=l->next) {
+		r = l->n;
+		if(r->op != OKEY)
+			fatal("arraylit: rhs not OKEY: %N", r);
+		index = r->left;
+		value = r->right;
+
+		switch(value->op) {
+		case OARRAYLIT:
+			if(value->type->bound < 0) {
+				if(pass == 1 && ctxt != 0) {
+					a = nod(OINDEX, var, index);
+					slicelit(ctxt, value, a, init);
+				} else
+				if(pass == 2 && ctxt == 0) {
+					a = nod(OINDEX, var, index);
+					slicelit(ctxt, value, a, init);
+				} else
+				if(pass == 3)
+					break;
+				continue;
+			}
+			a = nod(OINDEX, var, index);
+			arraylit(ctxt, pass, value, a, init);
+			continue;
+
+		case OSTRUCTLIT:
+			a = nod(OINDEX, var, index);
+			structlit(ctxt, pass, value, a, init);
+			continue;
+		}
+
+		if(isliteral(index) && isliteral(value)) {
+			if(pass == 2)
+				continue;
+		} else
+			if(pass == 1)
+				continue;
+
+		// build list of var[index] = value
+		a = nod(OINDEX, var, index);
+		a = nod(OAS, a, value);
+		typecheck(&a, Etop);
+		if(pass == 1) {
+			walkexpr(&a, init);
+			if(a->op != OAS)
+				fatal("arraylit: not as");
+			a->dodata = 2;
+		} else {
+			orderstmtinplace(&a);
+			walkstmt(&a);
+		}
+		*init = list(*init, a);
+	}
+}
+
+static void
+slicelit(int ctxt, Node *n, Node *var, NodeList **init)
+{
+	Node *r, *a;
+	NodeList *l;
+	Type *t;
+	Node *vstat, *vauto;
+	Node *index, *value;
+	int mode;
+
+	// make an array type
+	t = shallow(n->type);
+	t->bound = mpgetfix(n->right->val.u.xval);
+	t->width = 0;
+	t->sym = nil;
+	t->haspointers = 0;
+	dowidth(t);
+
+	if(ctxt != 0) {
+		// put everything into static array
+		vstat = staticname(t, ctxt);
+		arraylit(ctxt, 1, n, vstat, init);
+		arraylit(ctxt, 2, n, vstat, init);
+
+		// copy static to slice
+		a = nod(OSLICE, vstat, nod(OKEY, N, N));
+		a = nod(OAS, var, a);
+		typecheck(&a, Etop);
+		a->dodata = 2;
+		*init = list(*init, a);
+		return;
+	}
+
+	// recipe for var = []t{...}
+	// 1. make a static array
+	//	var vstat [...]t
+	// 2. assign (data statements) the constant part
+	//	vstat = constpart{}
+	// 3. make an auto pointer to array and allocate heap to it
+	//	var vauto *[...]t = new([...]t)
+	// 4. copy the static array to the auto array
+	//	*vauto = vstat
+	// 5. assign slice of allocated heap to var
+	//	var = [0:]*auto
+	// 6. for each dynamic part assign to the slice
+	//	var[i] = dynamic part
+	//
+	// an optimization is done if there is no constant part
+	//	3. var vauto *[...]t = new([...]t)
+	//	5. var = [0:]*auto
+	//	6. var[i] = dynamic part
+
+	// if the literal contains constants,
+	// make static initialized array (1),(2)
+	vstat = N;
+	mode = getdyn(n, 1);
+	if(mode & MODECONST) {
+		vstat = staticname(t, ctxt);
+		arraylit(ctxt, 1, n, vstat, init);
+	}
+
+	// make new auto *array (3 declare)
+	vauto = temp(ptrto(t));
+
+	// set auto to point at new temp or heap (3 assign)
+	if(n->alloc != N) {
+		// temp allocated during order.c for dddarg
+		n->alloc->type = t;
+		if(vstat == N) {
+			a = nod(OAS, n->alloc, N);
+			typecheck(&a, Etop);
+			*init = list(*init, a);  // zero new temp
+		}
+		a = nod(OADDR, n->alloc, N);
+	} else if(n->esc == EscNone) {
+		a = temp(t);
+		if(vstat == N) {
+			a = nod(OAS, temp(t), N);
+			typecheck(&a, Etop);
+			*init = list(*init, a);  // zero new temp
+			a = a->left;
+		}
+		a = nod(OADDR, a, N);
+	} else {
+		a = nod(ONEW, N, N);
+		a->list = list1(typenod(t));
+	}
+	a = nod(OAS, vauto, a);
+	typecheck(&a, Etop);
+	walkexpr(&a, init);
+	*init = list(*init, a);
+
+	if(vstat != N) {
+		// copy static to heap (4)
+		a = nod(OIND, vauto, N);
+		a = nod(OAS, a, vstat);
+		typecheck(&a, Etop);
+		walkexpr(&a, init);
+		*init = list(*init, a);
+	}
+
+	// make slice out of heap (5)
+	a = nod(OAS, var, nod(OSLICE, vauto, nod(OKEY, N, N)));
+	typecheck(&a, Etop);
+	orderstmtinplace(&a);
+	walkstmt(&a);
+	*init = list(*init, a);
+
+	// put dynamics into slice (6)
+	for(l=n->list; l; l=l->next) {
+		r = l->n;
+		if(r->op != OKEY)
+			fatal("slicelit: rhs not OKEY: %N", r);
+		index = r->left;
+		value = r->right;
+		a = nod(OINDEX, var, index);
+		a->bounded = 1;
+		// TODO need to check bounds?
+
+		switch(value->op) {
+		case OARRAYLIT:
+			if(value->type->bound < 0)
+				break;
+			arraylit(ctxt, 2, value, a, init);
+			continue;
+
+		case OSTRUCTLIT:
+			structlit(ctxt, 2, value, a, init);
+			continue;
+		}
+
+		if(isliteral(index) && isliteral(value))
+			continue;
+
+		// build list of var[c] = expr
+		a = nod(OAS, a, value);
+		typecheck(&a, Etop);
+		orderstmtinplace(&a);
+		walkstmt(&a);
+		*init = list(*init, a);
+	}
+}
+
+static void
+maplit(int ctxt, Node *n, Node *var, NodeList **init)
+{
+	Node *r, *a;
+	NodeList *l;
+	int nerr;
+	int64 b;
+	Type *t, *tk, *tv, *t1;
+	Node *vstat, *index, *value, *key, *val;
+	Sym *syma, *symb;
+
+USED(ctxt);
+ctxt = 0;
+
+	// make the map var
+	nerr = nerrors;
+
+	a = nod(OMAKE, N, N);
+	a->list = list1(typenod(n->type));
+	litas(var, a, init);
+
+	// count the initializers
+	b = 0;
+	for(l=n->list; l; l=l->next) {
+		r = l->n;
+
+		if(r->op != OKEY)
+			fatal("maplit: rhs not OKEY: %N", r);
+		index = r->left;
+		value = r->right;
+
+		if(isliteral(index) && isliteral(value))
+			b++;
+	}
+
+	if(b != 0) {
+		// build type [count]struct { a Tindex, b Tvalue }
+		t = n->type;
+		tk = t->down;
+		tv = t->type;
+
+		symb = lookup("b");
+		t = typ(TFIELD);
+		t->type = tv;
+		t->sym = symb;
+
+		syma = lookup("a");
+		t1 = t;
+		t = typ(TFIELD);
+		t->type = tk;
+		t->sym = syma;
+		t->down = t1;
+
+		t1 = t;
+		t = typ(TSTRUCT);
+		t->type = t1;
+
+		t1 = t;
+		t = typ(TARRAY);
+		t->bound = b;
+		t->type = t1;
+
+		dowidth(t);
+
+		// make and initialize static array
+		vstat = staticname(t, ctxt);
+		b = 0;
+		for(l=n->list; l; l=l->next) {
+			r = l->n;
+
+			if(r->op != OKEY)
+				fatal("maplit: rhs not OKEY: %N", r);
+			index = r->left;
+			value = r->right;
+
+			if(isliteral(index) && isliteral(value)) {
+				// build vstat[b].a = key;
+				a = nodintconst(b);
+				a = nod(OINDEX, vstat, a);
+				a = nod(ODOT, a, newname(syma));
+				a = nod(OAS, a, index);
+				typecheck(&a, Etop);
+				walkexpr(&a, init);
+				a->dodata = 2;
+				*init = list(*init, a);
+
+				// build vstat[b].b = value;
+				a = nodintconst(b);
+				a = nod(OINDEX, vstat, a);
+				a = nod(ODOT, a, newname(symb));
+				a = nod(OAS, a, value);
+				typecheck(&a, Etop);
+				walkexpr(&a, init);
+				a->dodata = 2;
+				*init = list(*init, a);
+
+				b++;
+			}
+		}
+
+		// loop adding structure elements to map
+		// for i = 0; i < len(vstat); i++ {
+		//	map[vstat[i].a] = vstat[i].b
+		// }
+		index = temp(types[TINT]);
+
+		a = nod(OINDEX, vstat, index);
+		a->bounded = 1;
+		a = nod(ODOT, a, newname(symb));
+
+		r = nod(OINDEX, vstat, index);
+		r->bounded = 1;
+		r = nod(ODOT, r, newname(syma));
+		r = nod(OINDEX, var, r);
+
+		r = nod(OAS, r, a);
+
+		a = nod(OFOR, N, N);
+		a->nbody = list1(r);
+
+		a->ninit = list1(nod(OAS, index, nodintconst(0)));
+		a->ntest = nod(OLT, index, nodintconst(t->bound));
+		a->nincr = nod(OAS, index, nod(OADD, index, nodintconst(1)));
+
+		typecheck(&a, Etop);
+		walkstmt(&a);
+		*init = list(*init, a);
+	}
+
+	// put in dynamic entries one-at-a-time
+	key = nil;
+	val = nil;
+	for(l=n->list; l; l=l->next) {
+		r = l->n;
+
+		if(r->op != OKEY)
+			fatal("maplit: rhs not OKEY: %N", r);
+		index = r->left;
+		value = r->right;
+
+		if(isliteral(index) && isliteral(value))
+			continue;
+			
+		// build list of var[c] = expr.
+		// use temporary so that mapassign1 can have addressable key, val.
+		if(key == nil) {
+			key = temp(var->type->down);
+			val = temp(var->type->type);
+		}
+		a = nod(OAS, key, r->left);
+		typecheck(&a, Etop);
+		walkstmt(&a);
+		*init = list(*init, a);
+		a = nod(OAS, val, r->right);
+		typecheck(&a, Etop);
+		walkstmt(&a);
+		*init = list(*init, a);
+
+		a = nod(OAS, nod(OINDEX, var, key), val);
+		typecheck(&a, Etop);
+		walkstmt(&a);
+		*init = list(*init, a);
+
+		if(nerr != nerrors)
+			break;
+	}
+	
+	if(key != nil) {
+		a = nod(OVARKILL, key, N);
+		typecheck(&a, Etop);
+		*init = list(*init, a);
+		a = nod(OVARKILL, val, N);
+		typecheck(&a, Etop);
+		*init = list(*init, a);
+	}
+}
+
+void
+anylit(int ctxt, Node *n, Node *var, NodeList **init)
+{
+	Type *t;
+	Node *a, *vstat, *r;
+
+	t = n->type;
+	switch(n->op) {
+	default:
+		fatal("anylit: not lit");
+
+	case OPTRLIT:
+		if(!isptr[t->etype])
+			fatal("anylit: not ptr");
+
+		if(n->right != N) {
+			r = nod(OADDR, n->right, N);
+			typecheck(&r, Erv);
+		} else {
+			r = nod(ONEW, N, N);
+			r->typecheck = 1;
+			r->type = t;
+			r->esc = n->esc;
+		}
+		walkexpr(&r, init);
+		a = nod(OAS, var, r);
+
+		typecheck(&a, Etop);
+		*init = list(*init, a);
+
+		var = nod(OIND, var, N);
+		typecheck(&var, Erv | Easgn);
+		anylit(ctxt, n->left, var, init);
+		break;
+
+	case OSTRUCTLIT:
+		if(t->etype != TSTRUCT)
+			fatal("anylit: not struct");
+
+		if(simplename(var) && count(n->list) > 4) {
+
+			if(ctxt == 0) {
+				// lay out static data
+				vstat = staticname(t, ctxt);
+				structlit(ctxt, 1, n, vstat, init);
+
+				// copy static to var
+				a = nod(OAS, var, vstat);
+				typecheck(&a, Etop);
+				walkexpr(&a, init);
+				*init = list(*init, a);
+
+				// add expressions to automatic
+				structlit(ctxt, 2, n, var, init);
+				break;
+			}
+			structlit(ctxt, 1, n, var, init);
+			structlit(ctxt, 2, n, var, init);
+			break;
+		}
+
+		// initialize of not completely specified
+		if(simplename(var) || count(n->list) < structcount(t)) {
+			a = nod(OAS, var, N);
+			typecheck(&a, Etop);
+			walkexpr(&a, init);
+			*init = list(*init, a);
+		}
+		structlit(ctxt, 3, n, var, init);
+		break;
+
+	case OARRAYLIT:
+		if(t->etype != TARRAY)
+			fatal("anylit: not array");
+		if(t->bound < 0) {
+			slicelit(ctxt, n, var, init);
+			break;
+		}
+
+		if(simplename(var) && count(n->list) > 4) {
+
+			if(ctxt == 0) {
+				// lay out static data
+				vstat = staticname(t, ctxt);
+				arraylit(1, 1, n, vstat, init);
+
+				// copy static to automatic
+				a = nod(OAS, var, vstat);
+				typecheck(&a, Etop);
+				walkexpr(&a, init);
+				*init = list(*init, a);
+
+				// add expressions to automatic
+				arraylit(ctxt, 2, n, var, init);
+				break;
+			}
+			arraylit(ctxt, 1, n, var, init);
+			arraylit(ctxt, 2, n, var, init);
+			break;
+		}
+
+		// initialize of not completely specified
+		if(simplename(var) || count(n->list) < t->bound) {
+			a = nod(OAS, var, N);
+			typecheck(&a, Etop);
+			walkexpr(&a, init);
+			*init = list(*init, a);
+		}
+		arraylit(ctxt, 3, n, var, init);
+		break;
+
+	case OMAPLIT:
+		if(t->etype != TMAP)
+			fatal("anylit: not map");
+		maplit(ctxt, n, var, init);
+		break;
+	}
+}
+
+int
+oaslit(Node *n, NodeList **init)
+{
+	int ctxt;
+
+	if(n->left == N || n->right == N)
+		goto no;
+	if(n->left->type == T || n->right->type == T)
+		goto no;
+	if(!simplename(n->left))
+		goto no;
+	if(!eqtype(n->left->type, n->right->type))
+		goto no;
+
+	// context is init() function.
+	// implies generated data executed
+	// exactly once and not subject to races.
+	ctxt = 0;
+//	if(n->dodata == 1)
+//		ctxt = 1;
+
+	switch(n->right->op) {
+	default:
+		goto no;
+
+	case OSTRUCTLIT:
+	case OARRAYLIT:
+	case OMAPLIT:
+		if(vmatch1(n->left, n->right))
+			goto no;
+		anylit(ctxt, n->right, n->left, init);
+		break;
+	}
+	n->op = OEMPTY;
+	return 1;
+
+no:
+	// not a special composit literal assignment
+	return 0;
+}
+
+static int
+getlit(Node *lit)
+{
+	if(smallintconst(lit))
+		return mpgetfix(lit->val.u.xval);
+	return -1;
+}
+
+int
+stataddr(Node *nam, Node *n)
+{
+	int l;
+
+	if(n == N)
+		goto no;
+
+	switch(n->op) {
+
+	case ONAME:
+		*nam = *n;
+		return n->addable;
+
+	case ODOT:
+		if(!stataddr(nam, n->left))
+			break;
+		nam->xoffset += n->xoffset;
+		nam->type = n->type;
+		return 1;
+
+	case OINDEX:
+		if(n->left->type->bound < 0)
+			break;
+		if(!stataddr(nam, n->left))
+			break;
+		l = getlit(n->right);
+		if(l < 0)
+			break;
+		// Check for overflow.
+		if(n->type->width != 0 && MAXWIDTH/n->type->width <= l)
+			break;
+ 		nam->xoffset += l*n->type->width;
+		nam->type = n->type;
+		return 1;
+	}
+
+no:
+	return 0;
+}
+
+int
+gen_as_init(Node *n)
+{
+	Node *nr, *nl;
+	Node nam, nod1;
+
+	if(n->dodata == 0)
+		goto no;
+
+	nr = n->right;
+	nl = n->left;
+	if(nr == N) {
+		if(!stataddr(&nam, nl))
+			goto no;
+		if(nam.class != PEXTERN)
+			goto no;
+		goto yes;
+	}
+
+	if(nr->type == T || !eqtype(nl->type, nr->type))
+		goto no;
+
+	if(!stataddr(&nam, nl))
+		goto no;
+
+	if(nam.class != PEXTERN)
+		goto no;
+
+	switch(nr->op) {
+	default:
+		goto no;
+
+	case OCONVNOP:
+		nr = nr->left;
+		if(nr == N || nr->op != OSLICEARR)
+			goto no;
+		// fall through
+	
+	case OSLICEARR:
+		if(nr->right->op == OKEY && nr->right->left == N && nr->right->right == N) {
+			nr = nr->left;
+			goto slice;
+		}
+		goto no;
+
+	case OLITERAL:
+		break;
+	}
+
+	switch(nr->type->etype) {
+	default:
+		goto no;
+
+	case TBOOL:
+	case TINT8:
+	case TUINT8:
+	case TINT16:
+	case TUINT16:
+	case TINT32:
+	case TUINT32:
+	case TINT64:
+	case TUINT64:
+	case TINT:
+	case TUINT:
+	case TUINTPTR:
+	case TPTR32:
+	case TPTR64:
+	case TFLOAT32:
+	case TFLOAT64:
+		gdata(&nam, nr, nr->type->width);
+		break;
+
+	case TCOMPLEX64:
+	case TCOMPLEX128:
+		gdatacomplex(&nam, nr->val.u.cval);
+		break;
+
+	case TSTRING:
+		gdatastring(&nam, nr->val.u.sval);
+		break;
+	}
+
+yes:
+	return 1;
+
+slice:
+	gused(N); // in case the data is the dest of a goto
+	nl = nr;
+	if(nr == N || nr->op != OADDR)
+		goto no;
+	nr = nr->left;
+	if(nr == N || nr->op != ONAME)
+		goto no;
+
+	// nr is the array being converted to a slice
+	if(nr->type == T || nr->type->etype != TARRAY || nr->type->bound < 0)
+		goto no;
+
+	nam.xoffset += Array_array;
+	gdata(&nam, nl, types[tptr]->width);
+
+	nam.xoffset += Array_nel-Array_array;
+	nodconst(&nod1, types[TINT], nr->type->bound);
+	gdata(&nam, &nod1, widthint);
+
+	nam.xoffset += Array_cap-Array_nel;
+	gdata(&nam, &nod1, widthint);
+
+	goto yes;
+
+no:
+	if(n->dodata == 2) {
+		dump("\ngen_as_init", n);
+		fatal("gen_as_init couldnt make data statement");
+	}
+	return 0;
+}
+
+static int isvaluelit(Node*);
+static InitEntry* entry(InitPlan*);
+static void addvalue(InitPlan*, vlong, Node*, Node*);
+
+static void
+initplan(Node *n)
+{
+	InitPlan *p;
+	Node *a;
+	NodeList *l;
+
+	if(n->initplan != nil)
+		return;
+	p = mal(sizeof *p);
+	n->initplan = p;
+	switch(n->op) {
+	default:
+		fatal("initplan");
+	case OARRAYLIT:
+		for(l=n->list; l; l=l->next) {
+			a = l->n;
+			if(a->op != OKEY || !smallintconst(a->left))
+				fatal("initplan arraylit");
+			addvalue(p, n->type->type->width*mpgetfix(a->left->val.u.xval), N, a->right);
+		}
+		break;
+	case OSTRUCTLIT:
+		for(l=n->list; l; l=l->next) {
+			a = l->n;
+			if(a->op != OKEY || a->left->type == T)
+				fatal("initplan structlit");
+			addvalue(p, a->left->type->width, N, a->right);
+		}
+		break;
+	case OMAPLIT:
+		for(l=n->list; l; l=l->next) {
+			a = l->n;
+			if(a->op != OKEY)
+				fatal("initplan maplit");
+			addvalue(p, -1, a->left, a->right);
+		}
+		break;
+	}
+}
+
+static void
+addvalue(InitPlan *p, vlong xoffset, Node *key, Node *n)
+{
+	int i;
+	InitPlan *q;
+	InitEntry *e;
+
+	USED(key);
+
+	// special case: zero can be dropped entirely
+	if(iszero(n)) {
+		p->zero += n->type->width;
+		return;
+	}
+	
+	// special case: inline struct and array (not slice) literals
+	if(isvaluelit(n)) {
+		initplan(n);
+		q = n->initplan;
+		for(i=0; i<q->len; i++) {
+			e = entry(p);
+			*e = q->e[i];
+			e->xoffset += xoffset;
+		}
+		return;
+	}
+	
+	// add to plan
+	if(n->op == OLITERAL)
+		p->lit += n->type->width;
+	else
+		p->expr += n->type->width;
+
+	e = entry(p);
+	e->xoffset = xoffset;
+	e->expr = n;
+}
+
+int
+iszero(Node *n)
+{
+	NodeList *l;
+
+	switch(n->op) {
+	case OLITERAL:
+		switch(n->val.ctype) {
+		default:
+			dump("unexpected literal", n);
+			fatal("iszero");
+	
+		case CTNIL:
+			return 1;
+		
+		case CTSTR:
+			return n->val.u.sval == nil || n->val.u.sval->len == 0;
+	
+		case CTBOOL:
+			return n->val.u.bval == 0;
+			
+		case CTINT:
+		case CTRUNE:
+			return mpcmpfixc(n->val.u.xval, 0) == 0;
+	
+		case CTFLT:
+			return mpcmpfltc(n->val.u.fval, 0) == 0;
+	
+		case CTCPLX:
+			return mpcmpfltc(&n->val.u.cval->real, 0) == 0 && mpcmpfltc(&n->val.u.cval->imag, 0) == 0;
+		}
+		break;
+	case OARRAYLIT:
+		if(isslice(n->type))
+			break;
+		// fall through
+	case OSTRUCTLIT:
+		for(l=n->list; l; l=l->next)
+			if(!iszero(l->n->right))
+				return 0;
+		return 1;
+	}
+	return 0;
+}
+
+static int
+isvaluelit(Node *n)
+{
+	return (n->op == OARRAYLIT && isfixedarray(n->type)) || n->op == OSTRUCTLIT;
+}
+
+static InitEntry*
+entry(InitPlan *p)
+{
+	if(p->len >= p->cap) {
+		if(p->cap == 0)
+			p->cap = 4;
+		else
+			p->cap *= 2;
+		p->e = realloc(p->e, p->cap*sizeof p->e[0]);
+		if(p->e == nil)
+			fatal("out of memory");
+	}
+	return &p->e[p->len++];
+}
diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c
new file mode 100644
index 0000000..26153d3
--- /dev/null
+++ b/src/cmd/gc/subr.c
@@ -0,0 +1,3849 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include	<u.h>
+#include	<libc.h>
+#include	"go.h"
+#include	"md5.h"
+#include	"y.tab.h"
+#include	"yerr.h"
+
+typedef struct Error Error;
+struct Error
+{
+	int lineno;
+	int seq;
+	char *msg;
+};
+static Error *err;
+static int nerr;
+static int merr;
+
+void
+errorexit(void)
+{
+	flusherrors();
+	if(outfile)
+		remove(outfile);
+	exits("error");
+}
+
+extern int yychar;
+int
+parserline(void)
+{
+	if(yychar != 0 && yychar != -2)	// parser has one symbol lookahead
+		return prevlineno;
+	return lineno;
+}
+
+static void
+adderr(int line, char *fmt, va_list arg)
+{
+	Fmt f;
+	Error *p;
+
+	fmtstrinit(&f);
+	fmtprint(&f, "%L: ", line);
+	fmtvprint(&f, fmt, arg);
+	fmtprint(&f, "\n");
+
+	if(nerr >= merr) {
+		if(merr == 0)
+			merr = 16;
+		else
+			merr *= 2;
+		p = realloc(err, merr*sizeof err[0]);
+		if(p == nil) {
+			merr = nerr;
+			flusherrors();
+			print("out of memory\n");
+			errorexit();
+		}
+		err = p;
+	}
+	err[nerr].seq = nerr;
+	err[nerr].lineno = line;
+	err[nerr].msg = fmtstrflush(&f);
+	nerr++;
+}
+
+static int
+errcmp(const void *va, const void *vb)
+{
+	Error *a, *b;
+
+	a = (Error*)va;
+	b = (Error*)vb;
+	if(a->lineno != b->lineno)
+		return a->lineno - b->lineno;
+	if(a->seq != b->seq)
+		return a->seq - b->seq;
+	return strcmp(a->msg, b->msg);
+}
+
+void
+flusherrors(void)
+{
+	int i;
+
+	Bflush(&bstdout);
+	if(nerr == 0)
+		return;
+	qsort(err, nerr, sizeof err[0], errcmp);
+	for(i=0; i<nerr; i++)
+		if(i==0 || strcmp(err[i].msg, err[i-1].msg) != 0)
+			print("%s", err[i].msg);
+	nerr = 0;
+}
+
+static void
+hcrash(void)
+{
+	if(debug['h']) {
+		flusherrors();
+		if(outfile)
+			remove(outfile);
+		*(volatile int*)0 = 0;
+	}
+}
+
+void
+yyerrorl(int line, char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	adderr(line, fmt, arg);
+	va_end(arg);
+
+	hcrash();
+	nerrors++;
+	if(nsavederrors+nerrors >= 10 && !debug['e']) {
+		flusherrors();
+		print("%L: too many errors\n", line);
+		errorexit();
+	}
+}
+
+extern int yystate, yychar;
+
+void
+yyerror(char *fmt, ...)
+{
+	int i;
+	static int lastsyntax;
+	va_list arg;
+	char buf[512], *p;
+
+	if(strncmp(fmt, "syntax error", 12) == 0) {
+		nsyntaxerrors++;
+		
+		if(debug['x'])	
+			print("yyerror: yystate=%d yychar=%d\n", yystate, yychar);
+
+		// An unexpected EOF caused a syntax error. Use the previous
+		// line number since getc generated a fake newline character.
+		if(curio.eofnl)
+			lexlineno = prevlineno;
+
+		// only one syntax error per line
+		if(lastsyntax == lexlineno)
+			return;
+		lastsyntax = lexlineno;
+			
+		if(strstr(fmt, "{ or {") || strstr(fmt, " or ?") || strstr(fmt, " or @")) {
+			// The grammar has { and LBRACE but both show up as {.
+			// Rewrite syntax error referring to "{ or {" to say just "{".
+			strecpy(buf, buf+sizeof buf, fmt);
+			p = strstr(buf, "{ or {");
+			if(p)
+				memmove(p+1, p+6, strlen(p+6)+1);
+			
+			// The grammar has ? and @ but only for reading imports.
+			// Silence them in ordinary errors.
+			p = strstr(buf, " or ?");
+			if(p)
+				memmove(p, p+5, strlen(p+5)+1);
+			p = strstr(buf, " or @");
+			if(p)
+				memmove(p, p+5, strlen(p+5)+1);
+			fmt = buf;
+		}
+		
+		// look for parse state-specific errors in list (see go.errors).
+		for(i=0; i<nelem(yymsg); i++) {
+			if(yymsg[i].yystate == yystate && yymsg[i].yychar == yychar) {
+				yyerrorl(lexlineno, "syntax error: %s", yymsg[i].msg);
+				return;
+			}
+		}
+		
+		// plain "syntax error" gets "near foo" added
+		if(strcmp(fmt, "syntax error") == 0) {
+			yyerrorl(lexlineno, "syntax error near %s", lexbuf);
+			return;
+		}
+		
+		// if bison says "syntax error, more info"; print "syntax error: more info".
+		if(fmt[12] == ',') {
+			yyerrorl(lexlineno, "syntax error:%s", fmt+13);
+			return;
+		}
+
+		yyerrorl(lexlineno, "%s", fmt);
+		return;
+	}
+
+	va_start(arg, fmt);
+	adderr(parserline(), fmt, arg);
+	va_end(arg);
+
+	hcrash();
+	nerrors++;
+	if(nsavederrors+nerrors >= 10 && !debug['e']) {
+		flusherrors();
+		print("%L: too many errors\n", parserline());
+		errorexit();
+	}
+}
+
+void
+warn(char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	adderr(parserline(), fmt, arg);
+	va_end(arg);
+
+	hcrash();
+}
+
+void
+warnl(int line, char *fmt, ...)
+{
+	va_list arg;
+
+	va_start(arg, fmt);
+	adderr(line, fmt, arg);
+	va_end(arg);
+	if(debug['m'])
+		flusherrors();
+}
+
+void
+fatal(char *fmt, ...)
+{
+	va_list arg;
+
+	flusherrors();
+
+	print("%L: internal compiler error: ", lineno);
+	va_start(arg, fmt);
+	vfprint(1, fmt, arg);
+	va_end(arg);
+	print("\n");
+	
+	// If this is a released compiler version, ask for a bug report.
+	if(strncmp(getgoversion(), "release", 7) == 0) {
+		print("\n");
+		print("Please file a bug report including a short program that triggers the error.\n");
+		print("http://code.google.com/p/go/issues/entry?template=compilerbug\n");
+	}
+	hcrash();
+	errorexit();
+}
+
+void
+linehist(char *file, int32 off, int relative)
+{
+	if(debug['i']) {
+		if(file != nil) {
+			if(off < 0)
+				print("pragma %s", file);
+			else
+			if(off > 0)
+				print("line %s", file);
+			else
+				print("import %s", file);
+		} else
+			print("end of import");
+		print(" at line %L\n", lexlineno);
+	}
+	
+	if(off < 0 && file[0] != '/' && !relative)
+		file = smprint("%s/%s", ctxt->pathname, file);
+	linklinehist(ctxt, lexlineno, file, off);
+}
+
+int32
+setlineno(Node *n)
+{
+	int32 lno;
+
+	lno = lineno;
+	if(n != N)
+	switch(n->op) {
+	case ONAME:
+	case OTYPE:
+	case OPACK:
+	case OLITERAL:
+		break;
+	default:
+		lineno = n->lineno;
+		if(lineno == 0) {
+			if(debug['K'])
+				warn("setlineno: line 0");
+			lineno = lno;
+		}
+	}
+	return lno;
+}
+
+uint32
+stringhash(char *p)
+{
+	uint32 h;
+	int c;
+
+	h = 0;
+	for(;;) {
+		c = *p++;
+		if(c == 0)
+			break;
+		h = h*PRIME1 + c;
+	}
+
+	if((int32)h < 0) {
+		h = -h;
+		if((int32)h < 0)
+			h = 0;
+	}
+	return h;
+}
+
+Sym*
+lookup(char *name)
+{
+	return pkglookup(name, localpkg);
+}
+
+Sym*
+pkglookup(char *name, Pkg *pkg)
+{
+	Sym *s;
+	uint32 h;
+	int c;
+
+	h = stringhash(name) % NHASH;
+	c = name[0];
+	for(s = hash[h]; s != S; s = s->link) {
+		if(s->name[0] != c || s->pkg != pkg)
+			continue;
+		if(strcmp(s->name, name) == 0)
+			return s;
+	}
+
+	s = mal(sizeof(*s));
+	s->name = mal(strlen(name)+1);
+	strcpy(s->name, name);
+
+	s->pkg = pkg;
+
+	s->link = hash[h];
+	hash[h] = s;
+	s->lexical = LNAME;
+
+	return s;
+}
+
+Sym*
+restrictlookup(char *name, Pkg *pkg)
+{
+	if(!exportname(name) && pkg != localpkg)
+		yyerror("cannot refer to unexported name %s.%s", pkg->name, name);
+	return pkglookup(name, pkg);
+}
+
+
+// find all the exported symbols in package opkg
+// and make them available in the current package
+void
+importdot(Pkg *opkg, Node *pack)
+{
+	Sym *s, *s1;
+	uint32 h;
+	int n;
+	char *pkgerror;
+
+	n = 0;
+	for(h=0; h<NHASH; h++) {
+		for(s = hash[h]; s != S; s = s->link) {
+			if(s->pkg != opkg)
+				continue;
+			if(s->def == N)
+				continue;
+			if(!exportname(s->name) || utfrune(s->name, 0xb7))	// 0xb7 = center dot
+				continue;
+			s1 = lookup(s->name);
+			if(s1->def != N) {
+				pkgerror = smprint("during import \"%Z\"", opkg->path);
+				redeclare(s1, pkgerror);
+				continue;
+			}
+			s1->def = s->def;
+			s1->block = s->block;
+			s1->def->pack = pack;
+			s1->origpkg = opkg;
+			n++;
+		}
+	}
+	if(n == 0) {
+		// can't possibly be used - there were no symbols
+		yyerrorl(pack->lineno, "imported and not used: \"%Z\"", opkg->path);
+	}
+}
+
+static void
+gethunk(void)
+{
+	char *h;
+	int32 nh;
+
+	nh = NHUNK;
+	if(thunk >= 10L*NHUNK)
+		nh = 10L*NHUNK;
+	h = (char*)malloc(nh);
+	if(h == nil) {
+		flusherrors();
+		yyerror("out of memory");
+		errorexit();
+	}
+	hunk = h;
+	nhunk = nh;
+	thunk += nh;
+}
+
+void*
+mal(int32 n)
+{
+	void *p;
+
+	if(n >= NHUNK) {
+		p = malloc(n);
+		if(p == nil) {
+			flusherrors();
+			yyerror("out of memory");
+			errorexit();
+		}
+		memset(p, 0, n);
+		return p;
+	}
+
+	while((uintptr)hunk & MAXALIGN) {
+		hunk++;
+		nhunk--;
+	}
+	if(nhunk < n)
+		gethunk();
+
+	p = hunk;
+	nhunk -= n;
+	hunk += n;
+	memset(p, 0, n);
+	return p;
+}
+
+void*
+remal(void *p, int32 on, int32 n)
+{
+	void *q;
+
+	q = (uchar*)p + on;
+	if(q != hunk || nhunk < n) {
+		if(on+n >= NHUNK) {
+			q = mal(on+n);
+			memmove(q, p, on);
+			return q;
+		}
+		if(nhunk < on+n)
+			gethunk();
+		memmove(hunk, p, on);
+		p = hunk;
+		hunk += on;
+		nhunk -= on;
+	}
+	hunk += n;
+	nhunk -= n;
+	return p;
+}
+
+Node*
+nod(int op, Node *nleft, Node *nright)
+{
+	Node *n;
+
+	n = mal(sizeof(*n));
+	n->op = op;
+	n->left = nleft;
+	n->right = nright;
+	n->lineno = parserline();
+	n->xoffset = BADWIDTH;
+	n->orig = n;
+	n->curfn = curfn;
+	return n;
+}
+
+void
+saveorignode(Node *n)
+{
+	Node *norig;
+
+	if(n->orig != N)
+		return;
+	norig = nod(n->op, N, N);
+	*norig = *n;
+	n->orig = norig;
+}
+
+// ispaddedfield reports whether the given field
+// is followed by padding. For the case where t is
+// the last field, total gives the size of the enclosing struct.
+static int
+ispaddedfield(Type *t, vlong total)
+{
+	if(t->etype != TFIELD)
+		fatal("ispaddedfield called non-field %T", t);
+	if(t->down == T)
+		return t->width + t->type->width != total;
+	return t->width + t->type->width != t->down->width;
+}
+
+int
+algtype1(Type *t, Type **bad)
+{
+	int a, ret;
+	Type *t1;
+	
+	if(bad)
+		*bad = T;
+	if(t->broke)
+		return AMEM;
+	if(t->noalg)
+		return ANOEQ;
+
+	switch(t->etype) {
+	case TANY:
+	case TFORW:
+		// will be defined later.
+		*bad = t;
+		return -1;
+
+	case TINT8:
+	case TUINT8:
+	case TINT16:
+	case TUINT16:
+	case TINT32:
+	case TUINT32:
+	case TINT64:
+	case TUINT64:
+	case TINT:
+	case TUINT:
+	case TUINTPTR:
+	case TBOOL:
+	case TPTR32:
+	case TPTR64:
+	case TCHAN:
+	case TUNSAFEPTR:
+		return AMEM;
+
+	case TFUNC:
+	case TMAP:
+		if(bad)
+			*bad = t;
+		return ANOEQ;
+
+	case TFLOAT32:
+		return AFLOAT32;
+
+	case TFLOAT64:
+		return AFLOAT64;
+
+	case TCOMPLEX64:
+		return ACPLX64;
+
+	case TCOMPLEX128:
+		return ACPLX128;
+
+	case TSTRING:
+		return ASTRING;
+	
+	case TINTER:
+		if(isnilinter(t))
+			return ANILINTER;
+		return AINTER;
+	
+	case TARRAY:
+		if(isslice(t)) {
+			if(bad)
+				*bad = t;
+			return ANOEQ;
+		}
+		a = algtype1(t->type, bad);
+		if(a == ANOEQ || a == AMEM) {
+			if(a == ANOEQ && bad)
+				*bad = t;
+			return a;
+		}
+		return -1;  // needs special compare
+
+	case TSTRUCT:
+		if(t->type != T && t->type->down == T && !isblanksym(t->type->sym)) {
+			// One-field struct is same as that one field alone.
+			return algtype1(t->type->type, bad);
+		}
+		ret = AMEM;
+		for(t1=t->type; t1!=T; t1=t1->down) {
+			// All fields must be comparable.
+			a = algtype1(t1->type, bad);
+			if(a == ANOEQ)
+				return ANOEQ;
+
+			// Blank fields, padded fields, fields with non-memory
+			// equality need special compare.
+			if(a != AMEM || isblanksym(t1->sym) || ispaddedfield(t1, t->width)) {
+				ret = -1;
+				continue;
+			}
+		}
+		return ret;
+	}
+
+	fatal("algtype1: unexpected type %T", t);
+	return 0;
+}
+
+int
+algtype(Type *t)
+{
+	int a;
+	
+	a = algtype1(t, nil);
+	if(a == AMEM || a == ANOEQ) {
+		if(isslice(t))
+			return ASLICE;
+		switch(t->width) {
+		case 0:
+			return a + AMEM0 - AMEM;
+		case 1:
+			return a + AMEM8 - AMEM;
+		case 2:
+			return a + AMEM16 - AMEM;
+		case 4:
+			return a + AMEM32 - AMEM;
+		case 8:
+			return a + AMEM64 - AMEM;
+		case 16:
+			return a + AMEM128 - AMEM;
+		}
+	}
+	return a;
+}
+
+Type*
+maptype(Type *key, Type *val)
+{
+	Type *t;
+	Type *bad;
+	int atype, mtype;
+
+	if(key != nil) {
+		atype = algtype1(key, &bad);
+		if(bad == T)
+			mtype = key->etype;
+		else
+			mtype = bad->etype;
+		switch(mtype) {
+		default:
+			if(atype == ANOEQ)
+				yyerror("invalid map key type %T", key);
+			break;
+		case TANY:
+			// will be resolved later.
+			break;
+		case TFORW:
+			// map[key] used during definition of key.
+			// postpone check until key is fully defined.
+			// if there are multiple uses of map[key]
+			// before key is fully defined, the error
+			// will only be printed for the first one.
+			// good enough.
+			if(key->maplineno == 0)
+				key->maplineno = lineno;
+			break;
+		}
+	}
+	t = typ(TMAP);
+	t->down = key;
+	t->type = val;
+	return t;
+}
+
+Type*
+typ(int et)
+{
+	Type *t;
+
+	t = mal(sizeof(*t));
+	t->etype = et;
+	t->width = BADWIDTH;
+	t->lineno = lineno;
+	t->orig = t;
+	return t;
+}
+
+static int
+methcmp(const void *va, const void *vb)
+{
+	Type *a, *b;
+	int i;
+	
+	a = *(Type**)va;
+	b = *(Type**)vb;
+	if(a->sym == S && b->sym == S)
+		return 0;
+	if(a->sym == S)
+		return -1;
+	if(b->sym == S)
+		return 1;
+	i = strcmp(a->sym->name, b->sym->name);
+	if(i != 0)
+		return i;
+	if(!exportname(a->sym->name)) {
+		i = strcmp(a->sym->pkg->path->s, b->sym->pkg->path->s);
+		if(i != 0)
+			return i;
+	}
+	return 0;
+}
+
+Type*
+sortinter(Type *t)
+{
+	Type *f;
+	int i;
+	Type **a;
+	
+	if(t->type == nil || t->type->down == nil)
+		return t;
+
+	i=0;
+	for(f=t->type; f; f=f->down)
+		i++;
+	a = mal(i*sizeof f);
+	i = 0;
+	for(f=t->type; f; f=f->down)
+		a[i++] = f;
+	qsort(a, i, sizeof a[0], methcmp);
+	while(i-- > 0) {
+		a[i]->down = f;
+		f = a[i];
+	}
+	t->type = f;
+	return t;
+}
+
+Node*
+nodintconst(int64 v)
+{
+	Node *c;
+
+	c = nod(OLITERAL, N, N);
+	c->addable = 1;
+	c->val.u.xval = mal(sizeof(*c->val.u.xval));
+	mpmovecfix(c->val.u.xval, v);
+	c->val.ctype = CTINT;
+	c->type = types[TIDEAL];
+	ullmancalc(c);
+	return c;
+}
+
+Node*
+nodfltconst(Mpflt* v)
+{
+	Node *c;
+
+	c = nod(OLITERAL, N, N);
+	c->addable = 1;
+	c->val.u.fval = mal(sizeof(*c->val.u.fval));
+	mpmovefltflt(c->val.u.fval, v);
+	c->val.ctype = CTFLT;
+	c->type = types[TIDEAL];
+	ullmancalc(c);
+	return c;
+}
+
+void
+nodconst(Node *n, Type *t, int64 v)
+{
+	memset(n, 0, sizeof(*n));
+	n->op = OLITERAL;
+	n->addable = 1;
+	ullmancalc(n);
+	n->val.u.xval = mal(sizeof(*n->val.u.xval));
+	mpmovecfix(n->val.u.xval, v);
+	n->val.ctype = CTINT;
+	n->type = t;
+
+	if(isfloat[t->etype])
+		fatal("nodconst: bad type %T", t);
+}
+
+Node*
+nodnil(void)
+{
+	Node *c;
+
+	c = nodintconst(0);
+	c->val.ctype = CTNIL;
+	c->type = types[TNIL];
+	return c;
+}
+
+Node*
+nodbool(int b)
+{
+	Node *c;
+
+	c = nodintconst(0);
+	c->val.ctype = CTBOOL;
+	c->val.u.bval = b;
+	c->type = idealbool;
+	return c;
+}
+
+Type*
+aindex(Node *b, Type *t)
+{
+	Type *r;
+	int64 bound;
+
+	bound = -1;	// open bound
+	typecheck(&b, Erv);
+	if(b != nil) {
+		switch(consttype(b)) {
+		default:
+			yyerror("array bound must be an integer expression");
+			break;
+		case CTINT:
+		case CTRUNE:
+			bound = mpgetfix(b->val.u.xval);
+			if(bound < 0)
+				yyerror("array bound must be non negative");
+			break;
+		}
+	}
+
+	// fixed array
+	r = typ(TARRAY);
+	r->type = t;
+	r->bound = bound;
+	return r;
+}
+
+Node*
+treecopy(Node *n)
+{
+	Node *m;
+
+	if(n == N)
+		return N;
+
+	switch(n->op) {
+	default:
+		m = nod(OXXX, N, N);
+		*m = *n;
+		m->orig = m;
+		m->left = treecopy(n->left);
+		m->right = treecopy(n->right);
+		m->list = listtreecopy(n->list);
+		if(m->defn)
+			abort();
+		break;
+
+	case ONONAME:
+		if(n->sym == lookup("iota")) {
+			// Not sure yet whether this is the real iota,
+			// but make a copy of the Node* just in case,
+			// so that all the copies of this const definition
+			// don't have the same iota value.
+			m = nod(OXXX, N, N);
+			*m = *n;
+			m->iota = iota;
+			break;
+		}
+		// fall through
+	case ONAME:
+	case OLITERAL:
+	case OTYPE:
+		m = n;
+		break;
+	}
+	return m;
+}
+
+
+int
+isnil(Node *n)
+{
+	if(n == N)
+		return 0;
+	if(n->op != OLITERAL)
+		return 0;
+	if(n->val.ctype != CTNIL)
+		return 0;
+	return 1;
+}
+
+int
+isptrto(Type *t, int et)
+{
+	if(t == T)
+		return 0;
+	if(!isptr[t->etype])
+		return 0;
+	t = t->type;
+	if(t == T)
+		return 0;
+	if(t->etype != et)
+		return 0;
+	return 1;
+}
+
+int
+istype(Type *t, int et)
+{
+	return t != T && t->etype == et;
+}
+
+int
+isfixedarray(Type *t)
+{
+	return t != T && t->etype == TARRAY && t->bound >= 0;
+}
+
+int
+isslice(Type *t)
+{
+	return t != T && t->etype == TARRAY && t->bound < 0;
+}
+
+int
+isblank(Node *n)
+{
+	if(n == N)
+		return 0;
+	return isblanksym(n->sym);
+}
+
+int
+isblanksym(Sym *s)
+{
+	char *p;
+
+	if(s == S)
+		return 0;
+	p = s->name;
+	if(p == nil)
+		return 0;
+	return p[0] == '_' && p[1] == '\0';
+}
+
+int
+isinter(Type *t)
+{
+	return t != T && t->etype == TINTER;
+}
+
+int
+isnilinter(Type *t)
+{
+	if(!isinter(t))
+		return 0;
+	if(t->type != T)
+		return 0;
+	return 1;
+}
+
+int
+isideal(Type *t)
+{
+	if(t == T)
+		return 0;
+	if(t == idealstring || t == idealbool)
+		return 1;
+	switch(t->etype) {
+	case TNIL:
+	case TIDEAL:
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * given receiver of type t (t == r or t == *r)
+ * return type to hang methods off (r).
+ */
+Type*
+methtype(Type *t, int mustname)
+{
+	if(t == T)
+		return T;
+
+	// strip away pointer if it's there
+	if(isptr[t->etype]) {
+		if(t->sym != S)
+			return T;
+		t = t->type;
+		if(t == T)
+			return T;
+	}
+
+	// need a type name
+	if(t->sym == S && (mustname || t->etype != TSTRUCT))
+		return T;
+
+	// check types
+	if(!issimple[t->etype])
+	switch(t->etype) {
+	default:
+		return T;
+	case TSTRUCT:
+	case TARRAY:
+	case TMAP:
+	case TCHAN:
+	case TSTRING:
+	case TFUNC:
+		break;
+	}
+
+	return t;
+}
+
+int
+cplxsubtype(int et)
+{
+	switch(et) {
+	case TCOMPLEX64:
+		return TFLOAT32;
+	case TCOMPLEX128:
+		return TFLOAT64;
+	}
+	fatal("cplxsubtype: %E\n", et);
+	return 0;
+}
+
+static int
+eqnote(Strlit *a, Strlit *b)
+{
+	if(a == b)
+		return 1;
+	if(a == nil || b == nil)
+		return 0;
+	if(a->len != b->len)
+		return 0;
+	return memcmp(a->s, b->s, a->len) == 0;
+}
+
+typedef struct TypePairList TypePairList;
+struct TypePairList
+{
+	Type *t1;
+	Type *t2;
+	TypePairList *next;
+};
+
+static int
+onlist(TypePairList *l, Type *t1, Type *t2) 
+{
+	for(; l; l=l->next)
+		if((l->t1 == t1 && l->t2 == t2) || (l->t1 == t2 && l->t2 == t1))
+			return 1;
+	return 0;
+}
+
+static int eqtype1(Type*, Type*, TypePairList*);
+
+// Return 1 if t1 and t2 are identical, following the spec rules.
+//
+// Any cyclic type must go through a named type, and if one is
+// named, it is only identical to the other if they are the same
+// pointer (t1 == t2), so there's no chance of chasing cycles
+// ad infinitum, so no need for a depth counter.
+int
+eqtype(Type *t1, Type *t2)
+{
+	return eqtype1(t1, t2, nil);
+}
+
+static int
+eqtype1(Type *t1, Type *t2, TypePairList *assumed_equal)
+{
+	TypePairList l;
+
+	if(t1 == t2)
+		return 1;
+	if(t1 == T || t2 == T || t1->etype != t2->etype)
+		return 0;
+	if(t1->sym || t2->sym) {
+		// Special case: we keep byte and uint8 separate
+		// for error messages.  Treat them as equal.
+		switch(t1->etype) {
+		case TUINT8:
+			if((t1 == types[TUINT8] || t1 == bytetype) && (t2 == types[TUINT8] || t2 == bytetype))
+				return 1;
+			break;
+		case TINT:
+		case TINT32:
+			if((t1 == types[runetype->etype] || t1 == runetype) && (t2 == types[runetype->etype] || t2 == runetype))
+				return 1;
+			break;
+		}
+		return 0;
+	}
+
+	if(onlist(assumed_equal, t1, t2))
+		return 1;
+	l.next = assumed_equal;
+	l.t1 = t1;
+	l.t2 = t2;
+
+	switch(t1->etype) {
+	case TINTER:
+	case TSTRUCT:
+		for(t1=t1->type, t2=t2->type; t1 && t2; t1=t1->down, t2=t2->down) {
+			if(t1->etype != TFIELD || t2->etype != TFIELD)
+				fatal("struct/interface missing field: %T %T", t1, t2);
+			if(t1->sym != t2->sym || t1->embedded != t2->embedded || !eqtype1(t1->type, t2->type, &l) || !eqnote(t1->note, t2->note))
+				goto no;
+		}
+		if(t1 == T && t2 == T)
+			goto yes;
+		goto no;
+
+	case TFUNC:
+		// Loop over structs: receiver, in, out.
+		for(t1=t1->type, t2=t2->type; t1 && t2; t1=t1->down, t2=t2->down) {
+			Type *ta, *tb;
+
+			if(t1->etype != TSTRUCT || t2->etype != TSTRUCT)
+				fatal("func missing struct: %T %T", t1, t2);
+
+			// Loop over fields in structs, ignoring argument names.
+			for(ta=t1->type, tb=t2->type; ta && tb; ta=ta->down, tb=tb->down) {
+				if(ta->etype != TFIELD || tb->etype != TFIELD)
+					fatal("func struct missing field: %T %T", ta, tb);
+				if(ta->isddd != tb->isddd || !eqtype1(ta->type, tb->type, &l))
+					goto no;
+			}
+			if(ta != T || tb != T)
+				goto no;
+		}
+		if(t1 == T && t2 == T)
+			goto yes;
+		goto no;
+	
+	case TARRAY:
+		if(t1->bound != t2->bound)
+			goto no;
+		break;
+	
+	case TCHAN:
+		if(t1->chan != t2->chan)
+			goto no;
+		break;
+	}
+
+	if(eqtype1(t1->down, t2->down, &l) && eqtype1(t1->type, t2->type, &l))
+		goto yes;
+	goto no;
+
+yes:
+	return 1;
+
+no:
+	return 0;
+}
+
+// Are t1 and t2 equal struct types when field names are ignored?
+// For deciding whether the result struct from g can be copied
+// directly when compiling f(g()).
+int
+eqtypenoname(Type *t1, Type *t2)
+{
+	if(t1 == T || t2 == T || t1->etype != TSTRUCT || t2->etype != TSTRUCT)
+		return 0;
+
+	t1 = t1->type;
+	t2 = t2->type;
+	for(;;) {
+		if(!eqtype(t1, t2))
+			return 0;
+		if(t1 == T)
+			return 1;
+		t1 = t1->down;
+		t2 = t2->down;
+	}
+}
+
+// Is type src assignment compatible to type dst?
+// If so, return op code to use in conversion.
+// If not, return 0.
+int
+assignop(Type *src, Type *dst, char **why)
+{
+	Type *missing, *have;
+	int ptr;
+
+	if(why != nil)
+		*why = "";
+
+	// TODO(rsc,lvd): This behaves poorly in the presence of inlining.
+	// https://code.google.com/p/go/issues/detail?id=2795
+	if(safemode && importpkg == nil && src != T && src->etype == TUNSAFEPTR) {
+		yyerror("cannot use unsafe.Pointer");
+		errorexit();
+	}
+
+	if(src == dst)
+		return OCONVNOP;
+	if(src == T || dst == T || src->etype == TFORW || dst->etype == TFORW || src->orig == T || dst->orig == T)
+		return 0;
+
+	// 1. src type is identical to dst.
+	if(eqtype(src, dst))
+		return OCONVNOP;
+	
+	// 2. src and dst have identical underlying types
+	// and either src or dst is not a named type or
+	// both are empty interface types.
+	// For assignable but different non-empty interface types,
+	// we want to recompute the itab.
+	if(eqtype(src->orig, dst->orig) && (src->sym == S || dst->sym == S || isnilinter(src)))
+		return OCONVNOP;
+
+	// 3. dst is an interface type and src implements dst.
+	if(dst->etype == TINTER && src->etype != TNIL) {
+		if(implements(src, dst, &missing, &have, &ptr))
+			return OCONVIFACE;
+
+		// we'll have complained about this method anyway, suppress spurious messages.
+		if(have && have->sym == missing->sym && (have->type->broke || missing->type->broke))
+			return OCONVIFACE;
+
+		if(why != nil) {
+			if(isptrto(src, TINTER))
+				*why = smprint(":\n\t%T is pointer to interface, not interface", src);
+			else if(have && have->sym == missing->sym && have->nointerface)
+				*why = smprint(":\n\t%T does not implement %T (%S method is marked 'nointerface')",
+					src, dst, missing->sym);
+			else if(have && have->sym == missing->sym)
+				*why = smprint(":\n\t%T does not implement %T (wrong type for %S method)\n"
+					"\t\thave %S%hhT\n\t\twant %S%hhT", src, dst, missing->sym,
+					have->sym, have->type, missing->sym, missing->type);
+			else if(ptr)
+				*why = smprint(":\n\t%T does not implement %T (%S method has pointer receiver)",
+					src, dst, missing->sym);
+			else if(have)
+				*why = smprint(":\n\t%T does not implement %T (missing %S method)\n"
+					"\t\thave %S%hhT\n\t\twant %S%hhT", src, dst, missing->sym,
+					have->sym, have->type, missing->sym, missing->type);
+			else
+				*why = smprint(":\n\t%T does not implement %T (missing %S method)",
+					src, dst, missing->sym);
+		}
+		return 0;
+	}
+	if(isptrto(dst, TINTER)) {
+		if(why != nil)
+			*why = smprint(":\n\t%T is pointer to interface, not interface", dst);
+		return 0;
+	}
+	if(src->etype == TINTER && dst->etype != TBLANK) {
+		if(why != nil && implements(dst, src, &missing, &have, &ptr))
+			*why = ": need type assertion";
+		return 0;
+	}
+
+	// 4. src is a bidirectional channel value, dst is a channel type,
+	// src and dst have identical element types, and
+	// either src or dst is not a named type.
+	if(src->etype == TCHAN && src->chan == Cboth && dst->etype == TCHAN)
+	if(eqtype(src->type, dst->type) && (src->sym == S || dst->sym == S))
+		return OCONVNOP;
+
+	// 5. src is the predeclared identifier nil and dst is a nillable type.
+	if(src->etype == TNIL) {
+		switch(dst->etype) {
+		case TARRAY:
+			if(dst->bound != -100)	// not slice
+				break;
+		case TPTR32:
+		case TPTR64:
+		case TFUNC:
+		case TMAP:
+		case TCHAN:
+		case TINTER:
+			return OCONVNOP;
+		}
+	}
+
+	// 6. rule about untyped constants - already converted by defaultlit.
+	
+	// 7. Any typed value can be assigned to the blank identifier.
+	if(dst->etype == TBLANK)
+		return OCONVNOP;
+
+	return 0;
+}
+
+// Can we convert a value of type src to a value of type dst?
+// If so, return op code to use in conversion (maybe OCONVNOP).
+// If not, return 0.
+int
+convertop(Type *src, Type *dst, char **why)
+{
+	int op;
+	
+	if(why != nil)
+		*why = "";
+
+	if(src == dst)
+		return OCONVNOP;
+	if(src == T || dst == T)
+		return 0;
+	
+	// 1. src can be assigned to dst.
+	if((op = assignop(src, dst, why)) != 0)
+		return op;
+
+	// The rules for interfaces are no different in conversions
+	// than assignments.  If interfaces are involved, stop now
+	// with the good message from assignop.
+	// Otherwise clear the error.
+	if(src->etype == TINTER || dst->etype == TINTER)
+		return 0;
+	if(why != nil)
+		*why = "";
+
+	// 2. src and dst have identical underlying types.
+	if(eqtype(src->orig, dst->orig))
+		return OCONVNOP;
+	
+	// 3. src and dst are unnamed pointer types 
+	// and their base types have identical underlying types.
+	if(isptr[src->etype] && isptr[dst->etype] && src->sym == S && dst->sym == S)
+	if(eqtype(src->type->orig, dst->type->orig))
+		return OCONVNOP;
+
+	// 4. src and dst are both integer or floating point types.
+	if((isint[src->etype] || isfloat[src->etype]) && (isint[dst->etype] || isfloat[dst->etype])) {
+		if(simtype[src->etype] == simtype[dst->etype])
+			return OCONVNOP;
+		return OCONV;
+	}
+
+	// 5. src and dst are both complex types.
+	if(iscomplex[src->etype] && iscomplex[dst->etype]) {
+		if(simtype[src->etype] == simtype[dst->etype])
+			return OCONVNOP;
+		return OCONV;
+	}
+
+	// 6. src is an integer or has type []byte or []rune
+	// and dst is a string type.
+	if(isint[src->etype] && dst->etype == TSTRING)
+		return ORUNESTR;
+
+	if(isslice(src) && dst->etype == TSTRING) {
+		if(src->type->etype == bytetype->etype)
+			return OARRAYBYTESTR;
+		if(src->type->etype == runetype->etype)
+			return OARRAYRUNESTR;
+	}
+	
+	// 7. src is a string and dst is []byte or []rune.
+	// String to slice.
+	if(src->etype == TSTRING && isslice(dst)) {
+		if(dst->type->etype == bytetype->etype)
+			return OSTRARRAYBYTE;
+		if(dst->type->etype == runetype->etype)
+			return OSTRARRAYRUNE;
+	}
+	
+	// 8. src is a pointer or uintptr and dst is unsafe.Pointer.
+	if((isptr[src->etype] || src->etype == TUINTPTR) && dst->etype == TUNSAFEPTR)
+		return OCONVNOP;
+
+	// 9. src is unsafe.Pointer and dst is a pointer or uintptr.
+	if(src->etype == TUNSAFEPTR && (isptr[dst->etype] || dst->etype == TUINTPTR))
+		return OCONVNOP;
+
+	return 0;
+}
+
+// Convert node n for assignment to type t.
+Node*
+assignconv(Node *n, Type *t, char *context)
+{
+	int op;
+	Node *r, *old;
+	char *why;
+	
+	if(n == N || n->type == T || n->type->broke)
+		return n;
+
+	if(t->etype == TBLANK && n->type->etype == TNIL)
+		yyerror("use of untyped nil");
+
+	old = n;
+	old->diag++;  // silence errors about n; we'll issue one below
+	defaultlit(&n, t);
+	old->diag--;
+	if(t->etype == TBLANK)
+		return n;
+
+	// Convert ideal bool from comparison to plain bool
+	// if the next step is non-bool (like interface{}).
+	if(n->type == idealbool && t->etype != TBOOL) {
+		if(n->op == ONAME || n->op == OLITERAL) {
+			r = nod(OCONVNOP, n, N);
+			r->type = types[TBOOL];
+			r->typecheck = 1;
+			r->implicit = 1;
+			n = r;
+		}
+	}
+
+	if(eqtype(n->type, t))
+		return n;
+
+	op = assignop(n->type, t, &why);
+	if(op == 0) {
+		yyerror("cannot use %lN as type %T in %s%s", n, t, context, why);
+		op = OCONV;
+	}
+
+	r = nod(op, n, N);
+	r->type = t;
+	r->typecheck = 1;
+	r->implicit = 1;
+	r->orig = n->orig;
+	return r;
+}
+
+static int
+subtype(Type **stp, Type *t, int d)
+{
+	Type *st;
+
+loop:
+	st = *stp;
+	if(st == T)
+		return 0;
+
+	d++;
+	if(d >= 10)
+		return 0;
+
+	switch(st->etype) {
+	default:
+		return 0;
+
+	case TPTR32:
+	case TPTR64:
+	case TCHAN:
+	case TARRAY:
+		stp = &st->type;
+		goto loop;
+
+	case TANY:
+		if(!st->copyany)
+			return 0;
+		*stp = t;
+		break;
+
+	case TMAP:
+		if(subtype(&st->down, t, d))
+			break;
+		stp = &st->type;
+		goto loop;
+
+	case TFUNC:
+		for(;;) {
+			if(subtype(&st->type, t, d))
+				break;
+			if(subtype(&st->type->down->down, t, d))
+				break;
+			if(subtype(&st->type->down, t, d))
+				break;
+			return 0;
+		}
+		break;
+
+	case TSTRUCT:
+		for(st=st->type; st!=T; st=st->down)
+			if(subtype(&st->type, t, d))
+				return 1;
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * Is this a 64-bit type?
+ */
+int
+is64(Type *t)
+{
+	if(t == T)
+		return 0;
+	switch(simtype[t->etype]) {
+	case TINT64:
+	case TUINT64:
+	case TPTR64:
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * Is a conversion between t1 and t2 a no-op?
+ */
+int
+noconv(Type *t1, Type *t2)
+{
+	int e1, e2;
+
+	e1 = simtype[t1->etype];
+	e2 = simtype[t2->etype];
+
+	switch(e1) {
+	case TINT8:
+	case TUINT8:
+		return e2 == TINT8 || e2 == TUINT8;
+
+	case TINT16:
+	case TUINT16:
+		return e2 == TINT16 || e2 == TUINT16;
+
+	case TINT32:
+	case TUINT32:
+	case TPTR32:
+		return e2 == TINT32 || e2 == TUINT32 || e2 == TPTR32;
+
+	case TINT64:
+	case TUINT64:
+	case TPTR64:
+		return e2 == TINT64 || e2 == TUINT64 || e2 == TPTR64;
+
+	case TFLOAT32:
+		return e2 == TFLOAT32;
+
+	case TFLOAT64:
+		return e2 == TFLOAT64;
+	}
+	return 0;
+}
+
+void
+argtype(Node *on, Type *t)
+{
+	dowidth(t);
+	if(!subtype(&on->type, t, 0))
+		fatal("argtype: failed %N %T\n", on, t);
+}
+
+Type*
+shallow(Type *t)
+{
+	Type *nt;
+
+	if(t == T)
+		return T;
+	nt = typ(0);
+	*nt = *t;
+	if(t->orig == t)
+		nt->orig = nt;
+	return nt;
+}
+
+static Type*
+deep(Type *t)
+{
+	Type *nt, *xt;
+
+	if(t == T)
+		return T;
+
+	switch(t->etype) {
+	default:
+		nt = t;	// share from here down
+		break;
+
+	case TANY:
+		nt = shallow(t);
+		nt->copyany = 1;
+		break;
+
+	case TPTR32:
+	case TPTR64:
+	case TCHAN:
+	case TARRAY:
+		nt = shallow(t);
+		nt->type = deep(t->type);
+		break;
+
+	case TMAP:
+		nt = shallow(t);
+		nt->down = deep(t->down);
+		nt->type = deep(t->type);
+		break;
+
+	case TFUNC:
+		nt = shallow(t);
+		nt->type = deep(t->type);
+		nt->type->down = deep(t->type->down);
+		nt->type->down->down = deep(t->type->down->down);
+		break;
+
+	case TSTRUCT:
+		nt = shallow(t);
+		nt->type = shallow(t->type);
+		xt = nt->type;
+
+		for(t=t->type; t!=T; t=t->down) {
+			xt->type = deep(t->type);
+			xt->down = shallow(t->down);
+			xt = xt->down;
+		}
+		break;
+	}
+	return nt;
+}
+
+Node*
+syslook(char *name, int copy)
+{
+	Sym *s;
+	Node *n;
+
+	s = pkglookup(name, runtimepkg);
+	if(s == S || s->def == N)
+		fatal("syslook: can't find runtime.%s", name);
+
+	if(!copy)
+		return s->def;
+
+	n = nod(0, N, N);
+	*n = *s->def;
+	n->type = deep(s->def->type);
+
+	return n;
+}
+
+/*
+ * compute a hash value for type t.
+ * if t is a method type, ignore the receiver
+ * so that the hash can be used in interface checks.
+ * %T already contains
+ * all the necessary logic to generate a representation
+ * of the type that completely describes it.
+ * using smprint here avoids duplicating that code.
+ * using md5 here is overkill, but i got tired of
+ * accidental collisions making the runtime think
+ * two types are equal when they really aren't.
+ */
+uint32
+typehash(Type *t)
+{
+	char *p;
+	MD5 d;
+
+	if(t->thistuple) {
+		// hide method receiver from Tpretty
+		t->thistuple = 0;
+		p = smprint("%-uT", t);
+		t->thistuple = 1;
+	} else
+		p = smprint("%-uT", t);
+	//print("typehash: %s\n", p);
+	md5reset(&d);
+	md5write(&d, (uchar*)p, strlen(p));
+	free(p);
+	return md5sum(&d, nil);
+}
+
+Type*
+ptrto(Type *t)
+{
+	Type *t1;
+
+	if(tptr == 0)
+		fatal("ptrto: no tptr");
+	t1 = typ(tptr);
+	t1->type = t;
+	t1->width = widthptr;
+	t1->align = widthptr;
+	return t1;
+}
+
+void
+frame(int context)
+{
+	char *p;
+	NodeList *l;
+	Node *n;
+	int flag;
+
+	p = "stack";
+	l = nil;
+	if(curfn)
+		l = curfn->dcl;
+	if(context) {
+		p = "external";
+		l = externdcl;
+	}
+
+	flag = 1;
+	for(; l; l=l->next) {
+		n = l->n;
+		switch(n->op) {
+		case ONAME:
+			if(flag)
+				print("--- %s frame ---\n", p);
+			print("%O %S G%d %T\n", n->op, n->sym, n->vargen, n->type);
+			flag = 0;
+			break;
+
+		case OTYPE:
+			if(flag)
+				print("--- %s frame ---\n", p);
+			print("%O %T\n", n->op, n->type);
+			flag = 0;
+			break;
+		}
+	}
+}
+
+/*
+ * calculate sethi/ullman number
+ * roughly how many registers needed to
+ * compile a node. used to compile the
+ * hardest side first to minimize registers.
+ */
+void
+ullmancalc(Node *n)
+{
+	int ul, ur;
+
+	if(n == N)
+		return;
+
+	if(n->ninit != nil) {
+		ul = UINF;
+		goto out;
+	}
+
+	switch(n->op) {
+	case OREGISTER:
+	case OLITERAL:
+	case ONAME:
+		ul = 1;
+		if(n->class == PPARAMREF || (n->class & PHEAP))
+			ul++;
+		goto out;
+	case OCALL:
+	case OCALLFUNC:
+	case OCALLMETH:
+	case OCALLINTER:
+		ul = UINF;
+		goto out;
+	case OANDAND:
+	case OOROR:
+		// hard with race detector
+		if(flag_race) {
+			ul = UINF;
+			goto out;
+		}
+	}
+	ul = 1;
+	if(n->left != N)
+		ul = n->left->ullman;
+	ur = 1;
+	if(n->right != N)
+		ur = n->right->ullman;
+	if(ul == ur)
+		ul += 1;
+	if(ur > ul)
+		ul = ur;
+
+out:
+	if(ul > 200)
+		ul = 200; // clamp to uchar with room to grow
+	n->ullman = ul;
+}
+
+void
+badtype(int o, Type *tl, Type *tr)
+{
+	Fmt fmt;
+	char *s;
+	
+	fmtstrinit(&fmt);
+	if(tl != T)
+		fmtprint(&fmt, "\n	%T", tl);
+	if(tr != T)
+		fmtprint(&fmt, "\n	%T", tr);
+
+	// common mistake: *struct and *interface.
+	if(tl && tr && isptr[tl->etype] && isptr[tr->etype]) {
+		if(tl->type->etype == TSTRUCT && tr->type->etype == TINTER)
+			fmtprint(&fmt, "\n	(*struct vs *interface)");
+		else if(tl->type->etype == TINTER && tr->type->etype == TSTRUCT)
+			fmtprint(&fmt, "\n	(*interface vs *struct)");
+	}
+	s = fmtstrflush(&fmt);
+	yyerror("illegal types for operand: %O%s", o, s);
+}
+
+/*
+ * iterator to walk a structure declaration
+ */
+Type*
+structfirst(Iter *s, Type **nn)
+{
+	Type *n, *t;
+
+	n = *nn;
+	if(n == T)
+		goto bad;
+
+	switch(n->etype) {
+	default:
+		goto bad;
+
+	case TSTRUCT:
+	case TINTER:
+	case TFUNC:
+		break;
+	}
+
+	t = n->type;
+	if(t == T)
+		goto rnil;
+
+	if(t->etype != TFIELD)
+		fatal("structfirst: not field %T", t);
+
+	s->t = t;
+	return t;
+
+bad:
+	fatal("structfirst: not struct %T", n);
+
+rnil:
+	return T;
+}
+
+Type*
+structnext(Iter *s)
+{
+	Type *n, *t;
+
+	n = s->t;
+	t = n->down;
+	if(t == T)
+		goto rnil;
+
+	if(t->etype != TFIELD)
+		goto bad;
+
+	s->t = t;
+	return t;
+
+bad:
+	fatal("structnext: not struct %T", n);
+
+rnil:
+	return T;
+}
+
+/*
+ * iterator to this and inargs in a function
+ */
+Type*
+funcfirst(Iter *s, Type *t)
+{
+	Type *fp;
+
+	if(t == T)
+		goto bad;
+
+	if(t->etype != TFUNC)
+		goto bad;
+
+	s->tfunc = t;
+	s->done = 0;
+	fp = structfirst(s, getthis(t));
+	if(fp == T) {
+		s->done = 1;
+		fp = structfirst(s, getinarg(t));
+	}
+	return fp;
+
+bad:
+	fatal("funcfirst: not func %T", t);
+	return T;
+}
+
+Type*
+funcnext(Iter *s)
+{
+	Type *fp;
+
+	fp = structnext(s);
+	if(fp == T && !s->done) {
+		s->done = 1;
+		fp = structfirst(s, getinarg(s->tfunc));
+	}
+	return fp;
+}
+
+Type**
+getthis(Type *t)
+{
+	if(t->etype != TFUNC)
+		fatal("getthis: not a func %T", t);
+	return &t->type;
+}
+
+Type**
+getoutarg(Type *t)
+{
+	if(t->etype != TFUNC)
+		fatal("getoutarg: not a func %T", t);
+	return &t->type->down;
+}
+
+Type**
+getinarg(Type *t)
+{
+	if(t->etype != TFUNC)
+		fatal("getinarg: not a func %T", t);
+	return &t->type->down->down;
+}
+
+Type*
+getthisx(Type *t)
+{
+	return *getthis(t);
+}
+
+Type*
+getoutargx(Type *t)
+{
+	return *getoutarg(t);
+}
+
+Type*
+getinargx(Type *t)
+{
+	return *getinarg(t);
+}
+
+/*
+ * return !(op)
+ * eg == <=> !=
+ */
+int
+brcom(int a)
+{
+	switch(a) {
+	case OEQ:	return ONE;
+	case ONE:	return OEQ;
+	case OLT:	return OGE;
+	case OGT:	return OLE;
+	case OLE:	return OGT;
+	case OGE:	return OLT;
+	}
+	fatal("brcom: no com for %A\n", a);
+	return a;
+}
+
+/*
+ * return reverse(op)
+ * eg a op b <=> b r(op) a
+ */
+int
+brrev(int a)
+{
+	switch(a) {
+	case OEQ:	return OEQ;
+	case ONE:	return ONE;
+	case OLT:	return OGT;
+	case OGT:	return OLT;
+	case OLE:	return OGE;
+	case OGE:	return OLE;
+	}
+	fatal("brcom: no rev for %A\n", a);
+	return a;
+}
+
+/*
+ * return side effect-free n, appending side effects to init.
+ * result is assignable if n is.
+ */
+Node*
+safeexpr(Node *n, NodeList **init)
+{
+	Node *l;
+	Node *r;
+	Node *a;
+
+	if(n == N)
+		return N;
+
+	if(n->ninit) {
+		walkstmtlist(n->ninit);
+		*init = concat(*init, n->ninit);
+		n->ninit = nil;
+	}
+
+	switch(n->op) {
+	case ONAME:
+	case OLITERAL:
+		return n;
+
+	case ODOT:
+		l = safeexpr(n->left, init);
+		if(l == n->left)
+			return n;
+		r = nod(OXXX, N, N);
+		*r = *n;
+		r->left = l;
+		typecheck(&r, Erv);
+		walkexpr(&r, init);
+		return r;
+
+	case ODOTPTR:
+	case OIND:
+		l = safeexpr(n->left, init);
+		if(l == n->left)
+			return n;
+		a = nod(OXXX, N, N);
+		*a = *n;
+		a->left = l;
+		walkexpr(&a, init);
+		return a;
+
+	case OINDEX:
+	case OINDEXMAP:
+		l = safeexpr(n->left, init);
+		r = safeexpr(n->right, init);
+		if(l == n->left && r == n->right)
+			return n;
+		a = nod(OXXX, N, N);
+		*a = *n;
+		a->left = l;
+		a->right = r;
+		walkexpr(&a, init);
+		return a;
+	}
+
+	// make a copy; must not be used as an lvalue
+	if(islvalue(n))
+		fatal("missing lvalue case in safeexpr: %N", n);
+	return cheapexpr(n, init);
+}
+
+Node*
+copyexpr(Node *n, Type *t, NodeList **init)
+{
+	Node *a, *l;
+	
+	l = temp(t);
+	a = nod(OAS, l, n);
+	typecheck(&a, Etop);
+	walkexpr(&a, init);
+	*init = list(*init, a);
+	return l;
+}
+
+/*
+ * return side-effect free and cheap n, appending side effects to init.
+ * result may not be assignable.
+ */
+Node*
+cheapexpr(Node *n, NodeList **init)
+{
+	switch(n->op) {
+	case ONAME:
+	case OLITERAL:
+		return n;
+	}
+
+	return copyexpr(n, n->type, init);
+}
+
+/*
+ * return n in a local variable of type t if it is not already.
+ * the value is guaranteed not to change except by direct
+ * assignment to it.
+ */
+Node*
+localexpr(Node *n, Type *t, NodeList **init)
+{
+	if(n->op == ONAME && (!n->addrtaken || strncmp(n->sym->name, "autotmp_", 8) == 0) &&
+		(n->class == PAUTO || n->class == PPARAM || n->class == PPARAMOUT) &&
+		convertop(n->type, t, nil) == OCONVNOP)
+		return n;
+	
+	return copyexpr(n, t, init);
+}
+
+void
+setmaxarg(Type *t)
+{
+	int64 w;
+
+	dowidth(t);
+	w = t->argwid;
+	if(t->argwid >= MAXWIDTH)
+		fatal("bad argwid %T", t);
+	if(w > maxarg)
+		maxarg = w;
+}
+
+/*
+ * unicode-aware case-insensitive strcmp
+ */
+
+static int
+ucistrcmp(char *p, char *q)
+{
+	Rune rp, rq;
+
+	while(*p || *q) {
+		if(*p == 0)
+			return +1;
+		if(*q == 0)
+			return -1;
+		p += chartorune(&rp, p);
+		q += chartorune(&rq, q);
+		rp = tolowerrune(rp);
+		rq = tolowerrune(rq);
+		if(rp < rq)
+			return -1;
+		if(rp > rq)
+			return +1;
+	}
+	return 0;
+}
+
+/*
+ * code to resolve elided DOTs
+ * in embedded types
+ */
+
+// search depth 0 --
+// return count of fields+methods
+// found with a given name
+static int
+lookdot0(Sym *s, Type *t, Type **save, int ignorecase)
+{
+	Type *f, *u;
+	int c;
+
+	u = t;
+	if(isptr[u->etype])
+		u = u->type;
+
+	c = 0;
+	if(u->etype == TSTRUCT || u->etype == TINTER) {
+		for(f=u->type; f!=T; f=f->down)
+			if(f->sym == s || (ignorecase && f->type->etype == TFUNC && f->type->thistuple > 0 && ucistrcmp(f->sym->name, s->name) == 0)) {
+				if(save)
+					*save = f;
+				c++;
+			}
+	}
+	u = methtype(t, 0);
+	if(u != T) {
+		for(f=u->method; f!=T; f=f->down)
+			if(f->embedded == 0 && (f->sym == s || (ignorecase && ucistrcmp(f->sym->name, s->name) == 0))) {
+				if(save)
+					*save = f;
+				c++;
+			}
+	}
+	return c;
+}
+
+// search depth d for field/method s --
+// return count of fields+methods
+// found at search depth.
+// answer is in dotlist array and
+// count of number of ways is returned.
+int
+adddot1(Sym *s, Type *t, int d, Type **save, int ignorecase)
+{
+	Type *f, *u;
+	int c, a;
+
+	if(t->trecur)
+		return 0;
+	t->trecur = 1;
+
+	if(d == 0) {
+		c = lookdot0(s, t, save, ignorecase);
+		goto out;
+	}
+
+	c = 0;
+	u = t;
+	if(isptr[u->etype])
+		u = u->type;
+	if(u->etype != TSTRUCT && u->etype != TINTER)
+		goto out;
+
+	d--;
+	for(f=u->type; f!=T; f=f->down) {
+		if(!f->embedded)
+			continue;
+		if(f->sym == S)
+			continue;
+		a = adddot1(s, f->type, d, save, ignorecase);
+		if(a != 0 && c == 0)
+			dotlist[d].field = f;
+		c += a;
+	}
+
+out:
+	t->trecur = 0;
+	return c;
+}
+
+// in T.field
+// find missing fields that
+// will give shortest unique addressing.
+// modify the tree with missing type names.
+Node*
+adddot(Node *n)
+{
+	Type *t;
+	Sym *s;
+	int c, d;
+
+	typecheck(&n->left, Etype|Erv);
+	n->diag |= n->left->diag;
+	t = n->left->type;
+	if(t == T)
+		goto ret;
+	
+	if(n->left->op == OTYPE)
+		goto ret;
+
+	if(n->right->op != ONAME)
+		goto ret;
+	s = n->right->sym;
+	if(s == S)
+		goto ret;
+
+	for(d=0; d<nelem(dotlist); d++) {
+		c = adddot1(s, t, d, nil, 0);
+		if(c > 0)
+			goto out;
+	}
+	goto ret;
+
+out:
+	if(c > 1) {
+		yyerror("ambiguous selector %N", n);
+		n->left = N;
+		return n;
+	}
+
+	// rebuild elided dots
+	for(c=d-1; c>=0; c--)
+		n->left = nod(ODOT, n->left, newname(dotlist[c].field->sym));
+ret:
+	return n;
+}
+
+
+/*
+ * code to help generate trampoline
+ * functions for methods on embedded
+ * subtypes.
+ * these are approx the same as
+ * the corresponding adddot routines
+ * except that they expect to be called
+ * with unique tasks and they return
+ * the actual methods.
+ */
+
+typedef	struct	Symlink	Symlink;
+struct	Symlink
+{
+	Type*		field;
+	uchar		good;
+	uchar		followptr;
+	Symlink*	link;
+};
+static	Symlink*	slist;
+
+static void
+expand0(Type *t, int followptr)
+{
+	Type *f, *u;
+	Symlink *sl;
+
+	u = t;
+	if(isptr[u->etype]) {
+		followptr = 1;
+		u = u->type;
+	}
+
+	if(u->etype == TINTER) {
+		for(f=u->type; f!=T; f=f->down) {
+			if(f->sym->flags & SymUniq)
+				continue;
+			f->sym->flags |= SymUniq;
+			sl = mal(sizeof(*sl));
+			sl->field = f;
+			sl->link = slist;
+			sl->followptr = followptr;
+			slist = sl;
+		}
+		return;
+	}
+
+	u = methtype(t, 0);
+	if(u != T) {
+		for(f=u->method; f!=T; f=f->down) {
+			if(f->sym->flags & SymUniq)
+				continue;
+			f->sym->flags |= SymUniq;
+			sl = mal(sizeof(*sl));
+			sl->field = f;
+			sl->link = slist;
+			sl->followptr = followptr;
+			slist = sl;
+		}
+	}
+}
+
+static void
+expand1(Type *t, int d, int followptr)
+{
+	Type *f, *u;
+
+	if(t->trecur)
+		return;
+	if(d == 0)
+		return;
+	t->trecur = 1;
+
+	if(d != nelem(dotlist)-1)
+		expand0(t, followptr);
+
+	u = t;
+	if(isptr[u->etype]) {
+		followptr = 1;
+		u = u->type;
+	}
+	if(u->etype != TSTRUCT && u->etype != TINTER)
+		goto out;
+
+	for(f=u->type; f!=T; f=f->down) {
+		if(!f->embedded)
+			continue;
+		if(f->sym == S)
+			continue;
+		expand1(f->type, d-1, followptr);
+	}
+
+out:
+	t->trecur = 0;
+}
+
+void
+expandmeth(Type *t)
+{
+	Symlink *sl;
+	Type *f;
+	int c, d;
+
+	if(t == T || t->xmethod != nil)
+		return;
+
+	// mark top-level method symbols
+	// so that expand1 doesn't consider them.
+	for(f=t->method; f != nil; f=f->down)
+		f->sym->flags |= SymUniq;
+
+	// generate all reachable methods
+	slist = nil;
+	expand1(t, nelem(dotlist)-1, 0);
+
+	// check each method to be uniquely reachable
+	for(sl=slist; sl!=nil; sl=sl->link) {
+		sl->field->sym->flags &= ~SymUniq;
+		for(d=0; d<nelem(dotlist); d++) {
+			c = adddot1(sl->field->sym, t, d, &f, 0);
+			if(c == 0)
+				continue;
+			if(c == 1) {
+				// addot1 may have dug out arbitrary fields, we only want methods.
+				if(f->type->etype == TFUNC && f->type->thistuple > 0) {
+					sl->good = 1;
+					sl->field = f;
+				}
+			}
+			break;
+		}
+	}
+
+	for(f=t->method; f != nil; f=f->down)
+		f->sym->flags &= ~SymUniq;
+
+	t->xmethod = t->method;
+	for(sl=slist; sl!=nil; sl=sl->link) {
+		if(sl->good) {
+			// add it to the base type method list
+			f = typ(TFIELD);
+			*f = *sl->field;
+			f->embedded = 1;	// needs a trampoline
+			if(sl->followptr)
+				f->embedded = 2;
+			f->down = t->xmethod;
+			t->xmethod = f;
+		}
+	}
+}
+
+/*
+ * Given funarg struct list, return list of ODCLFIELD Node fn args.
+ */
+static NodeList*
+structargs(Type **tl, int mustname)
+{
+	Iter savet;
+	Node *a, *n;
+	NodeList *args;
+	Type *t;
+	char buf[100];
+	int gen;
+
+	args = nil;
+	gen = 0;
+	for(t = structfirst(&savet, tl); t != T; t = structnext(&savet)) {
+		n = N;
+		if(mustname && (t->sym == nil || strcmp(t->sym->name, "_") == 0)) {
+			// invent a name so that we can refer to it in the trampoline
+			snprint(buf, sizeof buf, ".anon%d", gen++);
+			n = newname(lookup(buf));
+		} else if(t->sym)
+			n = newname(t->sym);
+		a = nod(ODCLFIELD, n, typenod(t->type));
+		a->isddd = t->isddd;
+		if(n != N)
+			n->isddd = t->isddd;
+		args = list(args, a);
+	}
+	return args;
+}
+
+/*
+ * Generate a wrapper function to convert from
+ * a receiver of type T to a receiver of type U.
+ * That is,
+ *
+ *	func (t T) M() {
+ *		...
+ *	}
+ *
+ * already exists; this function generates
+ *
+ *	func (u U) M() {
+ *		u.M()
+ *	}
+ *
+ * where the types T and U are such that u.M() is valid
+ * and calls the T.M method.
+ * The resulting function is for use in method tables.
+ *
+ *	rcvr - U
+ *	method - M func (t T)(), a TFIELD type struct
+ *	newnam - the eventual mangled name of this function
+ */
+void
+genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
+{
+	Node *this, *fn, *call, *n, *t, *pad, *dot, *as;
+	NodeList *l, *args, *in, *out;
+	Type *tpad, *methodrcvr;
+	int isddd;
+	Val v;
+	static int linehistdone = 0;
+
+	if(0 && debug['r'])
+		print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
+			rcvr, method, newnam);
+
+	lexlineno++;
+	lineno = lexlineno;
+	if (linehistdone == 0) {
+		// All the wrappers can share the same linehist entry.
+		linehist("<autogenerated>", 0, 0);
+		linehistdone = 1;
+	}
+
+	dclcontext = PEXTERN;
+	markdcl();
+
+	this = nod(ODCLFIELD, newname(lookup(".this")), typenod(rcvr));
+	this->left->ntype = this->right;
+	in = structargs(getinarg(method->type), 1);
+	out = structargs(getoutarg(method->type), 0);
+
+	t = nod(OTFUNC, N, N);
+	l = list1(this);
+	if(iface && rcvr->width < types[tptr]->width) {
+		// Building method for interface table and receiver
+		// is smaller than the single pointer-sized word
+		// that the interface call will pass in.
+		// Add a dummy padding argument after the
+		// receiver to make up the difference.
+		tpad = typ(TARRAY);
+		tpad->type = types[TUINT8];
+		tpad->bound = types[tptr]->width - rcvr->width;
+		pad = nod(ODCLFIELD, newname(lookup(".pad")), typenod(tpad));
+		l = list(l, pad);
+	}
+	t->list = concat(l, in);
+	t->rlist = out;
+
+	fn = nod(ODCLFUNC, N, N);
+	fn->nname = newname(newnam);
+	fn->nname->defn = fn;
+	fn->nname->ntype = t;
+	declare(fn->nname, PFUNC);
+	funchdr(fn);
+
+	// arg list
+	args = nil;
+	isddd = 0;
+	for(l=in; l; l=l->next) {
+		args = list(args, l->n->left);
+		isddd = l->n->left->isddd;
+	}
+	
+	methodrcvr = getthisx(method->type)->type->type;
+
+	// generate nil pointer check for better error
+	if(isptr[rcvr->etype] && rcvr->type == methodrcvr) {
+		// generating wrapper from *T to T.
+		n = nod(OIF, N, N);
+		n->ntest = nod(OEQ, this->left, nodnil());
+		// these strings are already in the reflect tables,
+		// so no space cost to use them here.
+		l = nil;
+		v.ctype = CTSTR;
+		v.u.sval = strlit(rcvr->type->sym->pkg->name);  // package name
+		l = list(l, nodlit(v));
+		v.u.sval = strlit(rcvr->type->sym->name);  // type name
+		l = list(l, nodlit(v));
+		v.u.sval = strlit(method->sym->name);
+		l = list(l, nodlit(v));  // method name
+		call = nod(OCALL, syslook("panicwrap", 0), N);
+		call->list = l;
+		n->nbody = list1(call);
+		fn->nbody = list(fn->nbody, n);
+	}
+	
+	dot = adddot(nod(OXDOT, this->left, newname(method->sym)));
+	
+	// generate call
+	if(!flag_race && isptr[rcvr->etype] && isptr[methodrcvr->etype] && method->embedded && !isifacemethod(method->type)) {
+		// generate tail call: adjust pointer receiver and jump to embedded method.
+		dot = dot->left;	// skip final .M
+		if(!isptr[dotlist[0].field->type->etype])
+			dot = nod(OADDR, dot, N);
+		as = nod(OAS, this->left, nod(OCONVNOP, dot, N));
+		as->right->type = rcvr;
+		fn->nbody = list(fn->nbody, as);
+		n = nod(ORETJMP, N, N);
+		n->left = newname(methodsym(method->sym, methodrcvr, 0));
+		fn->nbody = list(fn->nbody, n);
+	} else {
+		fn->wrapper = 1; // ignore frame for panic+recover matching
+		call = nod(OCALL, dot, N);
+		call->list = args;
+		call->isddd = isddd;
+		if(method->type->outtuple > 0) {
+			n = nod(ORETURN, N, N);
+			n->list = list1(call);
+			call = n;
+		}
+		fn->nbody = list(fn->nbody, call);
+	}
+
+	if(0 && debug['r'])
+		dumplist("genwrapper body", fn->nbody);
+
+	funcbody(fn);
+	curfn = fn;
+	// wrappers where T is anonymous (struct or interface) can be duplicated.
+	if(rcvr->etype == TSTRUCT ||
+		rcvr->etype == TINTER ||
+		isptr[rcvr->etype] && rcvr->type->etype == TSTRUCT)
+		fn->dupok = 1;
+	typecheck(&fn, Etop);
+	typechecklist(fn->nbody, Etop);
+
+	// Set inl_nonlocal to whether we are calling a method on a
+	// type defined in a different package.  Checked in inlvar.
+	if(!methodrcvr->local)
+		inl_nonlocal = 1;
+
+	inlcalls(fn);
+
+	inl_nonlocal = 0;
+
+	curfn = nil;
+	funccompile(fn, 0);
+}
+
+static Node*
+hashmem(Type *t)
+{
+	Node *tfn, *n;
+	Sym *sym;
+	
+	sym = pkglookup("memhash", runtimepkg);
+
+	n = newname(sym);
+	n->class = PFUNC;
+	tfn = nod(OTFUNC, N, N);
+	tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
+	tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+	tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+	tfn->rlist = list(tfn->rlist, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+	typecheck(&tfn, Etype);
+	n->type = tfn->type;
+	return n;
+}
+
+static Node*
+hashfor(Type *t)
+{
+	int a;
+	Sym *sym;
+	Node *tfn, *n;
+
+	a = algtype1(t, nil);
+	switch(a) {
+	case AMEM:
+		return hashmem(t);
+	case AINTER:
+		sym = pkglookup("interhash", runtimepkg);
+		break;
+	case ANILINTER:
+		sym = pkglookup("nilinterhash", runtimepkg);
+		break;
+	case ASTRING:
+		sym = pkglookup("strhash", runtimepkg);
+		break;
+	case AFLOAT32:
+		sym = pkglookup("f32hash", runtimepkg);
+		break;
+	case AFLOAT64:
+		sym = pkglookup("f64hash", runtimepkg);
+		break;
+	case ACPLX64:
+		sym = pkglookup("c64hash", runtimepkg);
+		break;
+	case ACPLX128:
+		sym = pkglookup("c128hash", runtimepkg);
+		break;
+	default:
+		sym = typesymprefix(".hash", t);
+		break;
+	}
+
+	n = newname(sym);
+	n->class = PFUNC;
+	tfn = nod(OTFUNC, N, N);
+	tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
+	tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+	tfn->list = list(tfn->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+	tfn->rlist = list(tfn->rlist, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+	typecheck(&tfn, Etype);
+	n->type = tfn->type;
+	return n;
+}
+
+/*
+ * Generate a helper function to compute the hash of a value of type t.
+ */
+void
+genhash(Sym *sym, Type *t)
+{
+	Node *n, *fn, *np, *nh, *ni, *call, *nx, *na, *tfn, *r;
+	Node *hashel;
+	Type *first, *t1;
+	int old_safemode;
+	int64 size, mul, offend;
+
+	if(debug['r'])
+		print("genhash %S %T\n", sym, t);
+
+	lineno = 1;  // less confusing than end of input
+	dclcontext = PEXTERN;
+	markdcl();
+
+	// func sym(p *T, s uintptr, h uintptr) uintptr
+	fn = nod(ODCLFUNC, N, N);
+	fn->nname = newname(sym);
+	fn->nname->class = PFUNC;
+	tfn = nod(OTFUNC, N, N);
+	fn->nname->ntype = tfn;
+
+	n = nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)));
+	tfn->list = list(tfn->list, n);
+	np = n->left;
+	n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR]));
+	tfn->list = list(tfn->list, n);
+	n = nod(ODCLFIELD, newname(lookup("h")), typenod(types[TUINTPTR]));
+	tfn->list = list(tfn->list, n);
+	nh = n->left;
+	n = nod(ODCLFIELD, N, typenod(types[TUINTPTR])); // return value
+	tfn->rlist = list(tfn->rlist, n);
+
+	funchdr(fn);
+	typecheck(&fn->nname->ntype, Etype);
+
+	// genhash is only called for types that have equality but
+	// cannot be handled by the standard algorithms,
+	// so t must be either an array or a struct.
+	switch(t->etype) {
+	default:
+		fatal("genhash %T", t);
+	case TARRAY:
+		if(isslice(t))
+			fatal("genhash %T", t);
+		// An array of pure memory would be handled by the
+		// standard algorithm, so the element type must not be
+		// pure memory.
+		hashel = hashfor(t->type);
+		n = nod(ORANGE, N, nod(OIND, np, N));
+		ni = newname(lookup("i"));
+		ni->type = types[TINT];
+		n->list = list1(ni);
+		n->colas = 1;
+		colasdefn(n->list, n);
+		ni = n->list->n;
+
+		// TODO: with aeshash we don't need these shift/mul parts
+
+		// h = h<<3 | h>>61
+		n->nbody = list(n->nbody,
+			nod(OAS,
+			    nh,
+				nod(OOR,
+					nod(OLSH, nh, nodintconst(3)),
+					nod(ORSH, nh, nodintconst(widthptr*8-3)))));
+
+		// h *= mul
+		// Same multipliers as in runtime.memhash.
+		if(widthptr == 4)
+			mul = 3267000013LL;
+		else
+			mul = 23344194077549503LL;
+		n->nbody = list(n->nbody,
+			nod(OAS,
+				nh,
+				nod(OMUL, nh, nodintconst(mul))));
+
+		// h = hashel(&p[i], sizeof(p[i]), h)
+		call = nod(OCALL, hashel, N);
+		nx = nod(OINDEX, np, ni);
+		nx->bounded = 1;
+		na = nod(OADDR, nx, N);
+		na->etype = 1;  // no escape to heap
+		call->list = list(call->list, na);
+		call->list = list(call->list, nodintconst(t->type->width));
+		call->list = list(call->list, nh);
+		n->nbody = list(n->nbody, nod(OAS, nh, call));
+
+		fn->nbody = list(fn->nbody, n);
+		break;
+
+	case TSTRUCT:
+		// Walk the struct using memhash for runs of AMEM
+		// and calling specific hash functions for the others.
+		first = T;
+		offend = 0;
+		for(t1=t->type;; t1=t1->down) {
+			if(t1 != T && algtype1(t1->type, nil) == AMEM && !isblanksym(t1->sym)) {
+				offend = t1->width + t1->type->width;
+				if(first == T)
+					first = t1;
+				// If it's a memory field but it's padded, stop here.
+				if(ispaddedfield(t1, t->width))
+					t1 = t1->down;
+				else
+					continue;
+			}
+			// Run memhash for fields up to this one.
+			if(first != T) {
+				size = offend - first->width; // first->width is offset
+				hashel = hashmem(first->type);
+				// h = hashel(&p.first, size, h)
+				call = nod(OCALL, hashel, N);
+				nx = nod(OXDOT, np, newname(first->sym));  // TODO: fields from other packages?
+				na = nod(OADDR, nx, N);
+				na->etype = 1;  // no escape to heap
+				call->list = list(call->list, na);
+				call->list = list(call->list, nodintconst(size));
+				call->list = list(call->list, nh);
+				fn->nbody = list(fn->nbody, nod(OAS, nh, call));
+
+				first = T;
+			}
+			if(t1 == T)
+				break;
+			if(isblanksym(t1->sym))
+				continue;
+
+			// Run hash for this field.
+			hashel = hashfor(t1->type);
+			// h = hashel(&p.t1, size, h)
+			call = nod(OCALL, hashel, N);
+			nx = nod(OXDOT, np, newname(t1->sym));  // TODO: fields from other packages?
+			na = nod(OADDR, nx, N);
+			na->etype = 1;  // no escape to heap
+			call->list = list(call->list, na);
+			call->list = list(call->list, nodintconst(t1->type->width));
+			call->list = list(call->list, nh);
+			fn->nbody = list(fn->nbody, nod(OAS, nh, call));
+		}
+		break;
+	}
+	r = nod(ORETURN, N, N);
+	r->list = list(r->list, nh);
+	fn->nbody = list(fn->nbody, r);
+
+	if(debug['r'])
+		dumplist("genhash body", fn->nbody);
+
+	funcbody(fn);
+	curfn = fn;
+	fn->dupok = 1;
+	typecheck(&fn, Etop);
+	typechecklist(fn->nbody, Etop);
+	curfn = nil;
+
+	// Disable safemode while compiling this code: the code we
+	// generate internally can refer to unsafe.Pointer.
+	// In this case it can happen if we need to generate an ==
+	// for a struct containing a reflect.Value, which itself has
+	// an unexported field of type unsafe.Pointer.
+	old_safemode = safemode;
+	safemode = 0;
+	funccompile(fn, 0);
+	safemode = old_safemode;
+}
+
+// Return node for
+//	if p.field != q.field { return false }
+static Node*
+eqfield(Node *p, Node *q, Node *field)
+{
+	Node *nif, *nx, *ny, *r;
+
+	nx = nod(OXDOT, p, field);
+	ny = nod(OXDOT, q, field);
+	nif = nod(OIF, N, N);
+	nif->ntest = nod(ONE, nx, ny);
+	r = nod(ORETURN, N, N);
+	r->list = list(r->list, nodbool(0));
+	nif->nbody = list(nif->nbody, r);
+	return nif;
+}
+
+static Node*
+eqmemfunc(vlong size, Type *type)
+{
+	char buf[30];
+	Node *fn;
+
+	switch(size) {
+	default:
+		fn = syslook("memequal", 1);
+		break;
+	case 1:
+	case 2:
+	case 4:
+	case 8:
+	case 16:
+		snprint(buf, sizeof buf, "memequal%d", (int)size*8);
+		fn = syslook(buf, 1);
+		break;
+	}
+	argtype(fn, type);
+	argtype(fn, type);
+	return fn;
+}
+
+// Return node for
+//	if !memequal(&p.field, &q.field, size) { return false }
+static Node*
+eqmem(Node *p, Node *q, Node *field, vlong size)
+{
+	Node *nif, *nx, *ny, *call, *r;
+
+	nx = nod(OADDR, nod(OXDOT, p, field), N);
+	nx->etype = 1;  // does not escape
+	ny = nod(OADDR, nod(OXDOT, q, field), N);
+	ny->etype = 1;  // does not escape
+	typecheck(&nx, Erv);
+	typecheck(&ny, Erv);
+
+	call = nod(OCALL, eqmemfunc(size, nx->type->type), N);
+	call->list = list(call->list, nx);
+	call->list = list(call->list, ny);
+	call->list = list(call->list, nodintconst(size));
+
+	nif = nod(OIF, N, N);
+	nif->ninit = list(nif->ninit, call);
+	nif->ntest = nod(ONOT, call, N);
+	r = nod(ORETURN, N, N);
+	r->list = list(r->list, nodbool(0));
+	nif->nbody = list(nif->nbody, r);
+	return nif;
+}
+
+/*
+ * Generate a helper function to check equality of two values of type t.
+ */
+void
+geneq(Sym *sym, Type *t)
+{
+	Node *n, *fn, *np, *nq, *tfn, *nif, *ni, *nx, *ny, *nrange, *r;
+	Type *t1, *first;
+	int old_safemode;
+	int64 size;
+	int64 offend;
+
+	if(debug['r'])
+		print("geneq %S %T\n", sym, t);
+
+	lineno = 1;  // less confusing than end of input
+	dclcontext = PEXTERN;
+	markdcl();
+
+	// func sym(p, q *T, s uintptr) bool
+	fn = nod(ODCLFUNC, N, N);
+	fn->nname = newname(sym);
+	fn->nname->class = PFUNC;
+	tfn = nod(OTFUNC, N, N);
+	fn->nname->ntype = tfn;
+
+	n = nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t)));
+	tfn->list = list(tfn->list, n);
+	np = n->left;
+	n = nod(ODCLFIELD, newname(lookup("q")), typenod(ptrto(t)));
+	tfn->list = list(tfn->list, n);
+	nq = n->left;
+	n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR]));
+	tfn->list = list(tfn->list, n);
+	n = nod(ODCLFIELD, N, typenod(types[TBOOL]));
+	tfn->rlist = list(tfn->rlist, n);
+
+	funchdr(fn);
+
+	// geneq is only called for types that have equality but
+	// cannot be handled by the standard algorithms,
+	// so t must be either an array or a struct.
+	switch(t->etype) {
+	default:
+		fatal("geneq %T", t);
+	case TARRAY:
+		if(isslice(t))
+			fatal("geneq %T", t);
+		// An array of pure memory would be handled by the
+		// standard memequal, so the element type must not be
+		// pure memory.  Even if we unrolled the range loop,
+		// each iteration would be a function call, so don't bother
+		// unrolling.
+		nrange = nod(ORANGE, N, nod(OIND, np, N));
+		ni = newname(lookup("i"));
+		ni->type = types[TINT];
+		nrange->list = list1(ni);
+		nrange->colas = 1;
+		colasdefn(nrange->list, nrange);
+		ni = nrange->list->n;
+		
+		// if p[i] != q[i] { return false }
+		nx = nod(OINDEX, np, ni);
+		nx->bounded = 1;
+		ny = nod(OINDEX, nq, ni);
+		ny->bounded = 1;
+
+		nif = nod(OIF, N, N);
+		nif->ntest = nod(ONE, nx, ny);
+		r = nod(ORETURN, N, N);
+		r->list = list(r->list, nodbool(0));
+		nif->nbody = list(nif->nbody, r);
+		nrange->nbody = list(nrange->nbody, nif);
+		fn->nbody = list(fn->nbody, nrange);
+		break;
+
+	case TSTRUCT:
+		// Walk the struct using memequal for runs of AMEM
+		// and calling specific equality tests for the others.
+		// Skip blank-named fields.
+		first = T;
+		offend = 0;
+		for(t1=t->type;; t1=t1->down) {
+			if(t1 != T && algtype1(t1->type, nil) == AMEM && !isblanksym(t1->sym)) {
+				offend = t1->width + t1->type->width;
+				if(first == T)
+					first = t1;
+				// If it's a memory field but it's padded, stop here.
+				if(ispaddedfield(t1, t->width))
+					t1 = t1->down;
+				else
+					continue;
+			}
+			// Run memequal for fields up to this one.
+			// TODO(rsc): All the calls to newname are wrong for
+			// cross-package unexported fields.
+			if(first != T) {
+				if(first->down == t1) {
+					fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym)));
+				} else if(first->down->down == t1) {
+					fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym)));
+					first = first->down;
+					if(!isblanksym(first->sym))
+						fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym)));
+				} else {
+					// More than two fields: use memequal.
+					size = offend - first->width; // first->width is offset
+					fn->nbody = list(fn->nbody, eqmem(np, nq, newname(first->sym), size));
+				}
+				first = T;
+			}
+			if(t1 == T)
+				break;
+			if(isblanksym(t1->sym))
+				continue;
+
+			// Check this field, which is not just memory.
+			fn->nbody = list(fn->nbody, eqfield(np, nq, newname(t1->sym)));
+		}
+
+		break;
+	}
+
+	// return true
+	r = nod(ORETURN, N, N);
+	r->list = list(r->list, nodbool(1));
+	fn->nbody = list(fn->nbody, r);
+
+	if(debug['r'])
+		dumplist("geneq body", fn->nbody);
+
+	funcbody(fn);
+	curfn = fn;
+	fn->dupok = 1;
+	typecheck(&fn, Etop);
+	typechecklist(fn->nbody, Etop);
+	curfn = nil;
+	
+	// Disable safemode while compiling this code: the code we
+	// generate internally can refer to unsafe.Pointer.
+	// In this case it can happen if we need to generate an ==
+	// for a struct containing a reflect.Value, which itself has
+	// an unexported field of type unsafe.Pointer.
+	old_safemode = safemode;
+	safemode = 0;
+	funccompile(fn, 0);
+	safemode = old_safemode;
+}
+
+static Type*
+ifacelookdot(Sym *s, Type *t, int *followptr, int ignorecase)
+{
+	int i, c, d;
+	Type *m;
+
+	*followptr = 0;
+
+	if(t == T)
+		return T;
+
+	for(d=0; d<nelem(dotlist); d++) {
+		c = adddot1(s, t, d, &m, ignorecase);
+		if(c > 1) {
+			yyerror("%T.%S is ambiguous", t, s);
+			return T;
+		}
+		if(c == 1) {
+			for(i=0; i<d; i++) {
+				if(isptr[dotlist[i].field->type->etype]) {
+					*followptr = 1;
+					break;
+				}
+			}
+			if(m->type->etype != TFUNC || m->type->thistuple == 0) {
+				yyerror("%T.%S is a field, not a method", t, s);
+				return T;
+			}
+			return m;
+		}
+	}
+	return T;
+}
+
+int
+implements(Type *t, Type *iface, Type **m, Type **samename, int *ptr)
+{
+	Type *t0, *im, *tm, *rcvr, *imtype;
+	int followptr;
+
+	t0 = t;
+	if(t == T)
+		return 0;
+
+	// if this is too slow,
+	// could sort these first
+	// and then do one loop.
+
+	if(t->etype == TINTER) {
+		for(im=iface->type; im; im=im->down) {
+			for(tm=t->type; tm; tm=tm->down) {
+				if(tm->sym == im->sym) {
+					if(eqtype(tm->type, im->type))
+						goto found;
+					*m = im;
+					*samename = tm;
+					*ptr = 0;
+					return 0;
+				}
+			}
+			*m = im;
+			*samename = nil;
+			*ptr = 0;
+			return 0;
+		found:;
+		}
+		return 1;
+	}
+
+	t = methtype(t, 0);
+	if(t != T)
+		expandmeth(t);
+	for(im=iface->type; im; im=im->down) {
+		imtype = methodfunc(im->type, 0);
+		tm = ifacelookdot(im->sym, t, &followptr, 0);
+		if(tm == T || tm->nointerface || !eqtype(methodfunc(tm->type, 0), imtype)) {
+			if(tm == T)
+				tm = ifacelookdot(im->sym, t, &followptr, 1);
+			*m = im;
+			*samename = tm;
+			*ptr = 0;
+			return 0;
+		}
+		// if pointer receiver in method,
+		// the method does not exist for value types.
+		rcvr = getthisx(tm->type)->type->type;
+		if(isptr[rcvr->etype] && !isptr[t0->etype] && !followptr && !isifacemethod(tm->type)) {
+			if(0 && debug['r'])
+				yyerror("interface pointer mismatch");
+
+			*m = im;
+			*samename = nil;
+			*ptr = 1;
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/*
+ * even simpler simtype; get rid of ptr, bool.
+ * assuming that the front end has rejected
+ * all the invalid conversions (like ptr -> bool)
+ */
+int
+simsimtype(Type *t)
+{
+	int et;
+
+	if(t == 0)
+		return 0;
+
+	et = simtype[t->etype];
+	switch(et) {
+	case TPTR32:
+		et = TUINT32;
+		break;
+	case TPTR64:
+		et = TUINT64;
+		break;
+	case TBOOL:
+		et = TUINT8;
+		break;
+	}
+	return et;
+}
+
+NodeList*
+concat(NodeList *a, NodeList *b)
+{
+	if(a == nil)
+		return b;
+	if(b == nil)
+		return a;
+
+	a->end->next = b;
+	a->end = b->end;
+	b->end = nil;
+	return a;
+}
+
+NodeList*
+list1(Node *n)
+{
+	NodeList *l;
+
+	if(n == nil)
+		return nil;
+	if(n->op == OBLOCK && n->ninit == nil) {
+		// Flatten list and steal storage.
+		// Poison pointer to catch errant uses.
+		l = n->list;
+		n->list = (NodeList*)1;
+		return l;
+	}
+	l = mal(sizeof *l);
+	l->n = n;
+	l->end = l;
+	return l;
+}
+
+NodeList*
+list(NodeList *l, Node *n)
+{
+	return concat(l, list1(n));
+}
+
+void
+listsort(NodeList** l, int(*f)(Node*, Node*))
+{
+	NodeList *l1, *l2, *le;
+
+	if(*l == nil || (*l)->next == nil)
+		return;
+
+	l1 = *l;
+	l2 = *l;
+	for(;;) {
+		l2 = l2->next;
+		if(l2 == nil)
+			break;
+		l2 = l2->next;
+		if(l2 == nil)
+			break;
+		l1 = l1->next;
+	}
+
+	l2 = l1->next;
+	l1->next = nil;
+	l2->end = (*l)->end;
+	(*l)->end = l1;
+
+	l1 = *l;
+	listsort(&l1, f);
+	listsort(&l2, f);
+
+	if((*f)(l1->n, l2->n) < 0) {
+		*l = l1;
+	} else {
+		*l = l2;
+		l2 = l1;
+		l1 = *l;
+	}
+
+	// now l1 == *l; and l1 < l2
+
+	while ((l1 != nil) && (l2 != nil)) {
+		while ((l1->next != nil) && (*f)(l1->next->n, l2->n) < 0)
+			l1 = l1->next;
+		
+		// l1 is last one from l1 that is < l2
+		le = l1->next;		// le is the rest of l1, first one that is >= l2
+		if(le != nil)
+			le->end = (*l)->end;
+
+		(*l)->end = l1;		// cut *l at l1
+		*l = concat(*l, l2);	// glue l2 to *l's tail
+
+		l1 = l2;		// l1 is the first element of *l that is < the new l2
+		l2 = le;		// ... because l2 now is the old tail of l1
+	}
+
+	*l = concat(*l, l2);		// any remainder 
+}
+
+NodeList*
+listtreecopy(NodeList *l)
+{
+	NodeList *out;
+
+	out = nil;
+	for(; l; l=l->next)
+		out = list(out, treecopy(l->n));
+	return out;
+}
+
+Node*
+liststmt(NodeList *l)
+{
+	Node *n;
+
+	n = nod(OBLOCK, N, N);
+	n->list = l;
+	if(l)
+		n->lineno = l->n->lineno;
+	return n;
+}
+
+/*
+ * return nelem of list
+ */
+int
+count(NodeList *l)
+{
+	vlong n;
+
+	n = 0;
+	for(; l; l=l->next)
+		n++;
+	if((int)n != n) { // Overflow.
+		yyerror("too many elements in list");
+	}
+	return n;
+}
+
+/*
+ * return nelem of list
+ */
+int
+structcount(Type *t)
+{
+	int v;
+	Iter s;
+
+	v = 0;
+	for(t = structfirst(&s, &t); t != T; t = structnext(&s))
+		v++;
+	return v;
+}
+
+/*
+ * return power of 2 of the constant
+ * operand. -1 if it is not a power of 2.
+ * 1000+ if it is a -(power of 2)
+ */
+int
+powtwo(Node *n)
+{
+	uvlong v, b;
+	int i;
+
+	if(n == N || n->op != OLITERAL || n->type == T)
+		goto no;
+	if(!isint[n->type->etype])
+		goto no;
+
+	v = mpgetfix(n->val.u.xval);
+	b = 1ULL;
+	for(i=0; i<64; i++) {
+		if(b == v)
+			return i;
+		b = b<<1;
+	}
+
+	if(!issigned[n->type->etype])
+		goto no;
+
+	v = -v;
+	b = 1ULL;
+	for(i=0; i<64; i++) {
+		if(b == v)
+			return i+1000;
+		b = b<<1;
+	}
+
+no:
+	return -1;
+}
+
+/*
+ * return the unsigned type for
+ * a signed integer type.
+ * returns T if input is not a
+ * signed integer type.
+ */
+Type*
+tounsigned(Type *t)
+{
+
+	// this is types[et+1], but not sure
+	// that this relation is immutable
+	switch(t->etype) {
+	default:
+		print("tounsigned: unknown type %T\n", t);
+		t = T;
+		break;
+	case TINT:
+		t = types[TUINT];
+		break;
+	case TINT8:
+		t = types[TUINT8];
+		break;
+	case TINT16:
+		t = types[TUINT16];
+		break;
+	case TINT32:
+		t = types[TUINT32];
+		break;
+	case TINT64:
+		t = types[TUINT64];
+		break;
+	}
+	return t;
+}
+
+/*
+ * magic number for signed division
+ * see hacker's delight chapter 10
+ */
+void
+smagic(Magic *m)
+{
+	int p;
+	uint64 ad, anc, delta, q1, r1, q2, r2, t;
+	uint64 mask, two31;
+
+	m->bad = 0;
+	switch(m->w) {
+	default:
+		m->bad = 1;
+		return;
+	case 8:
+		mask = 0xffLL;
+		break;
+	case 16:
+		mask = 0xffffLL;
+		break;
+	case 32:
+		mask = 0xffffffffLL;
+		break;
+	case 64:
+		mask = 0xffffffffffffffffULL;
+		break;
+	}
+	two31 = mask ^ (mask>>1);
+
+	p = m->w-1;
+	ad = m->sd;
+	if(m->sd < 0)
+		ad = -(uvlong)m->sd;
+
+	// bad denominators
+	if(ad == 0 || ad == 1 || ad == two31) {
+		m->bad = 1;
+		return;
+	}
+
+	t = two31;
+	ad &= mask;
+
+	anc = t - 1 - t%ad;
+	anc &= mask;
+
+	q1 = two31/anc;
+	r1 = two31 - q1*anc;
+	q1 &= mask;
+	r1 &= mask;
+
+	q2 = two31/ad;
+	r2 = two31 - q2*ad;
+	q2 &= mask;
+	r2 &= mask;
+
+	for(;;) {
+		p++;
+		q1 <<= 1;
+		r1 <<= 1;
+		q1 &= mask;
+		r1 &= mask;
+		if(r1 >= anc) {
+			q1++;
+			r1 -= anc;
+			q1 &= mask;
+			r1 &= mask;
+		}
+
+		q2 <<= 1;
+		r2 <<= 1;
+		q2 &= mask;
+		r2 &= mask;
+		if(r2 >= ad) {
+			q2++;
+			r2 -= ad;
+			q2 &= mask;
+			r2 &= mask;
+		}
+
+		delta = ad - r2;
+		delta &= mask;
+		if(q1 < delta || (q1 == delta && r1 == 0)) {
+			continue;
+		}
+		break;
+	}
+
+	m->sm = q2+1;
+	if(m->sm & two31)
+		m->sm |= ~mask;
+	m->s = p-m->w;
+}
+
+/*
+ * magic number for unsigned division
+ * see hacker's delight chapter 10
+ */
+void
+umagic(Magic *m)
+{
+	int p;
+	uint64 nc, delta, q1, r1, q2, r2;
+	uint64 mask, two31;
+
+	m->bad = 0;
+	m->ua = 0;
+
+	switch(m->w) {
+	default:
+		m->bad = 1;
+		return;
+	case 8:
+		mask = 0xffLL;
+		break;
+	case 16:
+		mask = 0xffffLL;
+		break;
+	case 32:
+		mask = 0xffffffffLL;
+		break;
+	case 64:
+		mask = 0xffffffffffffffffULL;
+		break;
+	}
+	two31 = mask ^ (mask>>1);
+
+	m->ud &= mask;
+	if(m->ud == 0 || m->ud == two31) {
+		m->bad = 1;
+		return;
+	}
+	nc = mask - (-m->ud&mask)%m->ud;
+	p = m->w-1;
+
+	q1 = two31/nc;
+	r1 = two31 - q1*nc;
+	q1 &= mask;
+	r1 &= mask;
+
+	q2 = (two31-1) / m->ud;
+	r2 = (two31-1) - q2*m->ud;
+	q2 &= mask;
+	r2 &= mask;
+
+	for(;;) {
+		p++;
+		if(r1 >= nc-r1) {
+			q1 <<= 1;
+			q1++;
+			r1 <<= 1;
+			r1 -= nc;
+		} else {
+			q1 <<= 1;
+			r1 <<= 1;
+		}
+		q1 &= mask;
+		r1 &= mask;
+		if(r2+1 >= m->ud-r2) {
+			if(q2 >= two31-1) {
+				m->ua = 1;
+			}
+			q2 <<= 1;
+			q2++;
+			r2 <<= 1;
+			r2++;
+			r2 -= m->ud;
+		} else {
+			if(q2 >= two31) {
+				m->ua = 1;
+			}
+			q2 <<= 1;
+			r2 <<= 1;
+			r2++;
+		}
+		q2 &= mask;
+		r2 &= mask;
+
+		delta = m->ud - 1 - r2;
+		delta &= mask;
+
+		if(p < m->w+m->w)
+		if(q1 < delta || (q1 == delta && r1 == 0)) {
+			continue;
+		}
+		break;
+	}
+	m->um = q2+1;
+	m->s = p-m->w;
+}
+
+Sym*
+ngotype(Node *n)
+{
+	if(n->type != T)
+		return typenamesym(n->type);
+	return S;
+}
+
+/*
+ * Convert raw string to the prefix that will be used in the symbol
+ * table.  All control characters, space, '%' and '"', as well as
+ * non-7-bit clean bytes turn into %xx.  The period needs escaping
+ * only in the last segment of the path, and it makes for happier
+ * users if we escape that as little as possible.
+ *
+ * If you edit this, edit ../ld/lib.c:/^pathtoprefix too.
+ * If you edit this, edit ../../debug/goobj/read.go:/importPathToPrefix too.
+ */
+static char*
+pathtoprefix(char *s)
+{
+	static char hex[] = "0123456789abcdef";
+	char *p, *r, *w, *l;
+	int n;
+
+	// find first character past the last slash, if any.
+	l = s;
+	for(r=s; *r; r++)
+		if(*r == '/')
+			l = r+1;
+
+	// check for chars that need escaping
+	n = 0;
+	for(r=s; *r; r++)
+		if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f)
+			n++;
+
+	// quick exit
+	if(n == 0)
+		return s;
+
+	// escape
+	p = mal((r-s)+1+2*n);
+	for(r=s, w=p; *r; r++) {
+		if(*r <= ' ' || (*r == '.' && r >= l) || *r == '%' || *r == '"' || *r >= 0x7f) {
+			*w++ = '%';
+			*w++ = hex[(*r>>4)&0xF];
+			*w++ = hex[*r&0xF];
+		} else
+			*w++ = *r;
+	}
+	*w = '\0';
+	return p;
+}
+
+Pkg*
+mkpkg(Strlit *path)
+{
+	Pkg *p;
+	int h;
+
+	h = stringhash(path->s) & (nelem(phash)-1);
+	for(p=phash[h]; p; p=p->link)
+		if(p->path->len == path->len && memcmp(path->s, p->path->s, path->len) == 0)
+			return p;
+
+	p = mal(sizeof *p);
+	p->path = path;
+	p->prefix = pathtoprefix(path->s);
+	p->link = phash[h];
+	phash[h] = p;
+	return p;
+}
+
+Strlit*
+strlit(char *s)
+{
+	Strlit *t;
+	
+	t = mal(sizeof *t + strlen(s));
+	strcpy(t->s, s);
+	t->len = strlen(s);
+	return t;
+}
+
+void
+addinit(Node **np, NodeList *init)
+{
+	Node *n;
+	
+	if(init == nil)
+		return;
+
+	n = *np;
+	switch(n->op) {
+	case ONAME:
+	case OLITERAL:
+		// There may be multiple refs to this node;
+		// introduce OCONVNOP to hold init list.
+		n = nod(OCONVNOP, n, N);
+		n->type = n->left->type;
+		n->typecheck = 1;
+		*np = n;
+		break;
+	}
+	n->ninit = concat(init, n->ninit);
+	n->ullman = UINF;
+}
+
+static char* reservedimports[] = {
+	"go",
+	"type",
+};
+
+int
+isbadimport(Strlit *path)
+{
+	int i;
+	char *s;
+	Rune r;
+
+	if(strlen(path->s) != path->len) {
+		yyerror("import path contains NUL");
+		return 1;
+	}
+	
+	for(i=0; i<nelem(reservedimports); i++) {
+		if(strcmp(path->s, reservedimports[i]) == 0) {
+			yyerror("import path \"%s\" is reserved and cannot be used", path->s);
+			return 1;
+		}
+	}
+
+	s = path->s;
+	while(*s) {
+		s += chartorune(&r, s);
+		if(r == Runeerror) {
+			yyerror("import path contains invalid UTF-8 sequence: \"%Z\"", path);
+			return 1;
+		}
+		if(r < 0x20 || r == 0x7f) {
+			yyerror("import path contains control character: \"%Z\"", path);
+			return 1;
+		}
+		if(r == '\\') {
+			yyerror("import path contains backslash; use slash: \"%Z\"", path);
+			return 1;
+		}
+		if(isspacerune(r)) {
+			yyerror("import path contains space character: \"%Z\"", path);
+			return 1;
+		}
+		if(utfrune("!\"#$%&'()*,:;<=>?[]^`{|}", r)) {
+			yyerror("import path contains invalid character '%C': \"%Z\"", r, path);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+void
+checknil(Node *x, NodeList **init)
+{
+	Node *n;
+	
+	if(isinter(x->type)) {
+		x = nod(OITAB, x, N);
+		typecheck(&x, Erv);
+	}
+	n = nod(OCHECKNIL, x, N);
+	n->typecheck = 1;
+	*init = list(*init, n);
+}
+
+/*
+ * Can this type be stored directly in an interface word?
+ */
+int
+isdirectiface(Type *t)
+{
+	// Setting IfacePointerOnly = 1 changes the
+	// interface representation so that the data word
+	// in an interface value must always be a pointer.
+	// Setting it to 0 uses the original representation,
+	// where the data word can hold a pointer or any
+	// non-pointer value no bigger than a pointer.
+	enum {
+		IfacePointerOnly = 1,
+	};
+
+	if(IfacePointerOnly) {
+		switch(t->etype) {
+		case TPTR32:
+		case TPTR64:
+		case TCHAN:
+		case TMAP:
+		case TFUNC:
+		case TUNSAFEPTR:
+			return 1;
+		case TARRAY:
+			// Array of 1 direct iface type can be direct.
+			return t->bound == 1 && isdirectiface(t->type);
+		case TSTRUCT:
+			// Struct with 1 field of direct iface type can be direct.
+			return t->type != T && t->type->down == T && isdirectiface(t->type->type);
+		}
+		return 0;
+	}
+	
+	dowidth(t);
+	return t->width <= widthptr;
+}
diff --git a/src/cmd/gc/swt.c b/src/cmd/gc/swt.c
new file mode 100644
index 0000000..e1d8af8
--- /dev/null
+++ b/src/cmd/gc/swt.c
@@ -0,0 +1,948 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include	<u.h>
+#include	<libc.h>
+#include	"go.h"
+
+enum
+{
+	Snorm		= 0,
+	Strue,
+	Sfalse,
+	Stype,
+
+	Tdefault,	// default case
+	Texprconst,	// normal constant case
+	Texprvar,	// normal variable case
+	Ttypenil,	// case nil
+	Ttypeconst,	// type hashes
+	Ttypevar,	// interface type
+
+	Ncase	= 4,	// count needed to split
+};
+
+typedef	struct	Case	Case;
+struct	Case
+{
+	Node*	node;		// points at case statement
+	uint32	hash;		// hash of a type switch
+	uint8	type;		// type of case
+	uint8	diag;		// suppress multiple diagnostics
+	uint16	ordinal;	// position in switch
+	Case*	link;		// linked list to link
+};
+#define	C	((Case*)nil)
+/*c2go Case *C; */
+
+void
+dumpcase(Case *c0)
+{
+	Case *c;
+
+	for(c=c0; c!=C; c=c->link) {
+		switch(c->type) {
+		case Tdefault:
+			print("case-default\n");
+			print("	ord=%d\n", c->ordinal);
+			break;
+		case Texprconst:
+			print("case-exprconst\n");
+			print("	ord=%d\n", c->ordinal);
+			break;
+		case Texprvar:
+			print("case-exprvar\n");
+			print("	ord=%d\n", c->ordinal);
+			print("	op=%O\n", c->node->left->op);
+			break;
+		case Ttypenil:
+			print("case-typenil\n");
+			print("	ord=%d\n", c->ordinal);
+			break;
+		case Ttypeconst:
+			print("case-typeconst\n");
+			print("	ord=%d\n", c->ordinal);
+			print("	hash=%ux\n", c->hash);
+			break;
+		case Ttypevar:
+			print("case-typevar\n");
+			print("	ord=%d\n", c->ordinal);
+			break;
+		default:
+			print("case-???\n");
+			print("	ord=%d\n", c->ordinal);
+			print("	op=%O\n", c->node->left->op);
+			print("	hash=%ux\n", c->hash);
+			break;
+		}
+	}
+	print("\n");
+}
+
+static int
+ordlcmp(Case *c1, Case *c2)
+{
+	// sort default first
+	if(c1->type == Tdefault)
+		return -1;
+	if(c2->type == Tdefault)
+		return +1;
+
+	// sort nil second
+	if(c1->type == Ttypenil)
+		return -1;
+	if(c2->type == Ttypenil)
+		return +1;
+
+	// sort by ordinal
+	if(c1->ordinal > c2->ordinal)
+		return +1;
+	if(c1->ordinal < c2->ordinal)
+		return -1;
+	return 0;
+}
+
+static int
+exprcmp(Case *c1, Case *c2)
+{
+	int ct, n;
+	Node *n1, *n2;
+
+	// sort non-constants last
+	if(c1->type != Texprconst)
+		return +1;
+	if(c2->type != Texprconst)
+		return -1;
+
+	n1 = c1->node->left;
+	n2 = c2->node->left;
+
+	// sort by type (for switches on interface)
+	ct = n1->val.ctype;
+	if(ct != n2->val.ctype)
+		return ct - n2->val.ctype;
+	if(!eqtype(n1->type, n2->type)) {
+		if(n1->type->vargen > n2->type->vargen)
+			return +1;
+		else
+			return -1;
+	}
+
+	// sort by constant value
+	n = 0;
+	switch(ct) {
+	case CTFLT:
+		n = mpcmpfltflt(n1->val.u.fval, n2->val.u.fval);
+		break;
+	case CTINT:
+	case CTRUNE:
+		n = mpcmpfixfix(n1->val.u.xval, n2->val.u.xval);
+		break;
+	case CTSTR:
+		n = cmpslit(n1, n2);
+		break;
+	}
+
+	return n;
+}
+
+static int
+typecmp(Case *c1, Case *c2)
+{
+
+	// sort non-constants last
+	if(c1->type != Ttypeconst)
+		return +1;
+	if(c2->type != Ttypeconst)
+		return -1;
+
+	// sort by hash code
+	if(c1->hash > c2->hash)
+		return +1;
+	if(c1->hash < c2->hash)
+		return -1;
+
+	// sort by ordinal so duplicate error
+	// happens on later case.
+	if(c1->ordinal > c2->ordinal)
+		return +1;
+	if(c1->ordinal < c2->ordinal)
+		return -1;
+	return 0;
+}
+
+static Case*
+csort(Case *l, int(*f)(Case*, Case*))
+{
+	Case *l1, *l2, *le;
+
+	if(l == C || l->link == C)
+		return l;
+
+	l1 = l;
+	l2 = l;
+	for(;;) {
+		l2 = l2->link;
+		if(l2 == C)
+			break;
+		l2 = l2->link;
+		if(l2 == C)
+			break;
+		l1 = l1->link;
+	}
+
+	l2 = l1->link;
+	l1->link = C;
+	l1 = csort(l, f);
+	l2 = csort(l2, f);
+
+	/* set up lead element */
+	if((*f)(l1, l2) < 0) {
+		l = l1;
+		l1 = l1->link;
+	} else {
+		l = l2;
+		l2 = l2->link;
+	}
+	le = l;
+
+	for(;;) {
+		if(l1 == C) {
+			while(l2) {
+				le->link = l2;
+				le = l2;
+				l2 = l2->link;
+			}
+			le->link = C;
+			break;
+		}
+		if(l2 == C) {
+			while(l1) {
+				le->link = l1;
+				le = l1;
+				l1 = l1->link;
+			}
+			break;
+		}
+		if((*f)(l1, l2) < 0) {
+			le->link = l1;
+			le = l1;
+			l1 = l1->link;
+		} else {
+			le->link = l2;
+			le = l2;
+			l2 = l2->link;
+		}
+	}
+	le->link = C;
+	return l;
+}
+
+static Node*
+newlabel(void)
+{
+	static int label;
+
+	label++;
+	snprint(namebuf, sizeof(namebuf), "%.6d", label);
+	return newname(lookup(namebuf));
+}
+
+/*
+ * build separate list of statements and cases
+ * make labels between cases and statements
+ * deal with fallthrough, break, unreachable statements
+ */
+static void
+casebody(Node *sw, Node *typeswvar)
+{
+	Node *n, *c, *last;
+	Node *def;
+	NodeList *cas, *stat, *l, *lc;
+	Node *go, *br;
+	int32 lno, needvar;
+
+	if(sw->list == nil)
+		return;
+
+	lno = setlineno(sw);
+
+	cas = nil;	// cases
+	stat = nil;	// statements
+	def = N;	// defaults
+	br = nod(OBREAK, N, N);
+
+	for(l=sw->list; l; l=l->next) {
+		n = l->n;
+		setlineno(n);
+		if(n->op != OXCASE)
+			fatal("casebody %O", n->op);
+		n->op = OCASE;
+		needvar = count(n->list) != 1 || n->list->n->op == OLITERAL;
+
+		go = nod(OGOTO, newlabel(), N);
+		if(n->list == nil) {
+			if(def != N)
+				yyerror("more than one default case");
+			// reuse original default case
+			n->right = go;
+			def = n;
+		}
+
+		if(n->list != nil && n->list->next == nil) {
+			// one case - reuse OCASE node.
+			c = n->list->n;
+			n->left = c;
+			n->right = go;
+			n->list = nil;
+			cas = list(cas, n);
+		} else {
+			// expand multi-valued cases
+			for(lc=n->list; lc; lc=lc->next) {
+				c = lc->n;
+				cas = list(cas, nod(OCASE, c, go));
+			}
+		}
+
+		stat = list(stat, nod(OLABEL, go->left, N));
+		if(typeswvar && needvar && n->nname != N) {
+			NodeList *l;
+
+			l = list1(nod(ODCL, n->nname, N));
+			l = list(l, nod(OAS, n->nname, typeswvar));
+			typechecklist(l, Etop);
+			stat = concat(stat, l);
+		}
+		stat = concat(stat, n->nbody);
+
+		// botch - shouldn't fall thru declaration
+		last = stat->end->n;
+		if(last->xoffset == n->xoffset && last->op == OXFALL) {
+			if(typeswvar) {
+				setlineno(last);
+				yyerror("cannot fallthrough in type switch");
+			}
+			if(l->next == nil) {
+				setlineno(last);
+				yyerror("cannot fallthrough final case in switch");
+			}
+			last->op = OFALL;
+		} else
+			stat = list(stat, br);
+	}
+
+	stat = list(stat, br);
+	if(def)
+		cas = list(cas, def);
+
+	sw->list = cas;
+	sw->nbody = stat;
+	lineno = lno;
+}
+
+static Case*
+mkcaselist(Node *sw, int arg)
+{
+	Node *n;
+	Case *c, *c1, *c2;
+	NodeList *l;
+	int ord;
+
+	c = C;
+	ord = 0;
+
+	for(l=sw->list; l; l=l->next) {
+		n = l->n;
+		c1 = mal(sizeof(*c1));
+		c1->link = c;
+		c = c1;
+
+		ord++;
+		if((uint16)ord != ord)
+			fatal("too many cases in switch");
+		c->ordinal = ord;
+		c->node = n;
+
+		if(n->left == N) {
+			c->type = Tdefault;
+			continue;
+		}
+
+		switch(arg) {
+		case Stype:
+			c->hash = 0;
+			if(n->left->op == OLITERAL) {
+				c->type = Ttypenil;
+				continue;
+			}
+			if(istype(n->left->type, TINTER)) {
+				c->type = Ttypevar;
+				continue;
+			}
+
+			c->hash = typehash(n->left->type);
+			c->type = Ttypeconst;
+			continue;
+
+		case Snorm:
+		case Strue:
+		case Sfalse:
+			c->type = Texprvar;
+			c->hash = typehash(n->left->type);
+			switch(consttype(n->left)) {
+			case CTFLT:
+			case CTINT:
+			case CTRUNE:
+			case CTSTR:
+				c->type = Texprconst;
+			}
+			continue;
+		}
+	}
+
+	if(c == C)
+		return C;
+
+	// sort by value and diagnose duplicate cases
+	switch(arg) {
+	case Stype:
+		c = csort(c, typecmp);
+		for(c1=c; c1!=C; c1=c1->link) {
+			for(c2=c1->link; c2!=C && c2->hash==c1->hash; c2=c2->link) {
+				if(c1->type == Ttypenil || c1->type == Tdefault)
+					break;
+				if(c2->type == Ttypenil || c2->type == Tdefault)
+					break;
+				if(!eqtype(c1->node->left->type, c2->node->left->type))
+					continue;
+				yyerrorl(c2->node->lineno, "duplicate case %T in type switch\n\tprevious case at %L", c2->node->left->type, c1->node->lineno);
+			}
+		}
+		break;
+	case Snorm:
+	case Strue:
+	case Sfalse:
+		c = csort(c, exprcmp);
+		for(c1=c; c1->link!=C; c1=c1->link) {
+			if(exprcmp(c1, c1->link) != 0)
+				continue;
+			setlineno(c1->link->node);
+			yyerror("duplicate case %N in switch\n\tprevious case at %L", c1->node->left, c1->node->lineno);
+		}
+		break;
+	}
+
+	// put list back in processing order
+	c = csort(c, ordlcmp);
+	return c;
+}
+
+static	Node*	exprname;
+
+static Node*
+exprbsw(Case *c0, int ncase, int arg)
+{
+	NodeList *cas;
+	Node *a, *n;
+	Case *c;
+	int i, half, lno;
+
+	cas = nil;
+	if(ncase < Ncase) {
+		for(i=0; i<ncase; i++) {
+			n = c0->node;
+			lno = setlineno(n);
+
+			if(assignop(n->left->type, exprname->type, nil) == OCONVIFACE ||
+			   assignop(exprname->type, n->left->type, nil) == OCONVIFACE)
+				goto snorm;
+
+			switch(arg) {
+			case Strue:
+				a = nod(OIF, N, N);
+				a->ntest = n->left;			// if val
+				a->nbody = list1(n->right);			// then goto l
+				break;
+
+			case Sfalse:
+				a = nod(OIF, N, N);
+				a->ntest = nod(ONOT, n->left, N);	// if !val
+				typecheck(&a->ntest, Erv);
+				a->nbody = list1(n->right);			// then goto l
+				break;
+
+			default:
+			snorm:
+				a = nod(OIF, N, N);
+				a->ntest = nod(OEQ, exprname, n->left);	// if name == val
+				typecheck(&a->ntest, Erv);
+				a->nbody = list1(n->right);			// then goto l
+				break;
+			}
+
+			cas = list(cas, a);
+			c0 = c0->link;
+			lineno = lno;
+		}
+		return liststmt(cas);
+	}
+
+	// find the middle and recur
+	c = c0;
+	half = ncase>>1;
+	for(i=1; i<half; i++)
+		c = c->link;
+	a = nod(OIF, N, N);
+	a->ntest = nod(OLE, exprname, c->node->left);
+	typecheck(&a->ntest, Erv);
+	a->nbody = list1(exprbsw(c0, half, arg));
+	a->nelse = list1(exprbsw(c->link, ncase-half, arg));
+	return a;
+}
+
+/*
+ * normal (expression) switch.
+ * rebulid case statements into if .. goto
+ */
+static void
+exprswitch(Node *sw)
+{
+	Node *def;
+	NodeList *cas;
+	Node *a;
+	Case *c0, *c, *c1;
+	Type *t;
+	int arg, ncase;
+
+	casebody(sw, N);
+
+	arg = Snorm;
+	if(isconst(sw->ntest, CTBOOL)) {
+		arg = Strue;
+		if(sw->ntest->val.u.bval == 0)
+			arg = Sfalse;
+	}
+	walkexpr(&sw->ntest, &sw->ninit);
+	t = sw->type;
+	if(t == T)
+		return;
+
+	/*
+	 * convert the switch into OIF statements
+	 */
+	exprname = N;
+	cas = nil;
+	if(arg != Strue && arg != Sfalse) {
+		exprname = temp(sw->ntest->type);
+		cas = list1(nod(OAS, exprname, sw->ntest));
+		typechecklist(cas, Etop);
+	} else {
+		exprname = nodbool(arg == Strue);
+	}
+
+	c0 = mkcaselist(sw, arg);
+	if(c0 != C && c0->type == Tdefault) {
+		def = c0->node->right;
+		c0 = c0->link;
+	} else {
+		def = nod(OBREAK, N, N);
+	}
+
+loop:
+	if(c0 == C) {
+		cas = list(cas, def);
+		sw->nbody = concat(cas, sw->nbody);
+		sw->list = nil;
+		walkstmtlist(sw->nbody);
+		return;
+	}
+
+	// deal with the variables one-at-a-time
+	if(!okforcmp[t->etype] || c0->type != Texprconst) {
+		a = exprbsw(c0, 1, arg);
+		cas = list(cas, a);
+		c0 = c0->link;
+		goto loop;
+	}
+
+	// do binary search on run of constants
+	ncase = 1;
+	for(c=c0; c->link!=C; c=c->link) {
+		if(c->link->type != Texprconst)
+			break;
+		ncase++;
+	}
+
+	// break the chain at the count
+	c1 = c->link;
+	c->link = C;
+
+	// sort and compile constants
+	c0 = csort(c0, exprcmp);
+	a = exprbsw(c0, ncase, arg);
+	cas = list(cas, a);
+
+	c0 = c1;
+	goto loop;
+
+}
+
+static	Node*	hashname;
+static	Node*	facename;
+static	Node*	boolname;
+
+static Node*
+typeone(Node *t)
+{
+	NodeList *init;
+	Node *a, *b, *var;
+
+	var = t->nname;
+	init = nil;
+	if(var == N) {
+		typecheck(&nblank, Erv | Easgn);
+		var = nblank;
+	} else
+		init = list1(nod(ODCL, var, N));
+
+	a = nod(OAS2, N, N);
+	a->list = list(list1(var), boolname);	// var,bool =
+	b = nod(ODOTTYPE, facename, N);
+	b->type = t->left->type;		// interface.(type)
+	a->rlist = list1(b);
+	typecheck(&a, Etop);
+	init = list(init, a);
+
+	b = nod(OIF, N, N);
+	b->ntest = boolname;
+	b->nbody = list1(t->right);		// if bool { goto l }
+	a = liststmt(list(init, b));
+	return a;
+}
+
+static Node*
+typebsw(Case *c0, int ncase)
+{
+	NodeList *cas;
+	Node *a, *n;
+	Case *c;
+	int i, half;
+
+	cas = nil;
+
+	if(ncase < Ncase) {
+		for(i=0; i<ncase; i++) {
+			n = c0->node;
+			if(c0->type != Ttypeconst)
+				fatal("typebsw");
+			a = nod(OIF, N, N);
+			a->ntest = nod(OEQ, hashname, nodintconst(c0->hash));
+			typecheck(&a->ntest, Erv);
+			a->nbody = list1(n->right);
+			cas = list(cas, a);
+			c0 = c0->link;
+		}
+		return liststmt(cas);
+	}
+
+	// find the middle and recur
+	c = c0;
+	half = ncase>>1;
+	for(i=1; i<half; i++)
+		c = c->link;
+	a = nod(OIF, N, N);
+	a->ntest = nod(OLE, hashname, nodintconst(c->hash));
+	typecheck(&a->ntest, Erv);
+	a->nbody = list1(typebsw(c0, half));
+	a->nelse = list1(typebsw(c->link, ncase-half));
+	return a;
+}
+
+/*
+ * convert switch of the form
+ *	switch v := i.(type) { case t1: ..; case t2: ..; }
+ * into if statements
+ */
+static void
+typeswitch(Node *sw)
+{
+	Node *def;
+	NodeList *cas, *hash;
+	Node *a, *n;
+	Case *c, *c0, *c1;
+	int ncase;
+	Type *t;
+	Val v;
+
+	if(sw->ntest == nil)
+		return;
+	if(sw->ntest->right == nil) {
+		setlineno(sw);
+		yyerror("type switch must have an assignment");
+		return;
+	}
+	walkexpr(&sw->ntest->right, &sw->ninit);
+	if(!istype(sw->ntest->right->type, TINTER)) {
+		yyerror("type switch must be on an interface");
+		return;
+	}
+	cas = nil;
+
+	/*
+	 * predeclare temporary variables
+	 * and the boolean var
+	 */
+	facename = temp(sw->ntest->right->type);
+	a = nod(OAS, facename, sw->ntest->right);
+	typecheck(&a, Etop);
+	cas = list(cas, a);
+
+	casebody(sw, facename);
+
+	boolname = temp(types[TBOOL]);
+	typecheck(&boolname, Erv);
+
+	hashname = temp(types[TUINT32]);
+	typecheck(&hashname, Erv);
+
+	t = sw->ntest->right->type;
+	if(isnilinter(t))
+		a = syslook("efacethash", 1);
+	else
+		a = syslook("ifacethash", 1);
+	argtype(a, t);
+	a = nod(OCALL, a, N);
+	a->list = list1(facename);
+	a = nod(OAS, hashname, a);
+	typecheck(&a, Etop);
+	cas = list(cas, a);
+
+	c0 = mkcaselist(sw, Stype);
+	if(c0 != C && c0->type == Tdefault) {
+		def = c0->node->right;
+		c0 = c0->link;
+	} else {
+		def = nod(OBREAK, N, N);
+	}
+	
+	/*
+	 * insert if statement into each case block
+	 */
+	for(c=c0; c!=C; c=c->link) {
+		n = c->node;
+		switch(c->type) {
+
+		case Ttypenil:
+			v.ctype = CTNIL;
+			a = nod(OIF, N, N);
+			a->ntest = nod(OEQ, facename, nodlit(v));
+			typecheck(&a->ntest, Erv);
+			a->nbody = list1(n->right);		// if i==nil { goto l }
+			n->right = a;
+			break;
+		
+		case Ttypevar:
+		case Ttypeconst:
+			n->right = typeone(n);
+			break;
+		}
+	}
+
+	/*
+	 * generate list of if statements, binary search for constant sequences
+	 */
+	while(c0 != C) {
+		if(c0->type != Ttypeconst) {
+			n = c0->node;
+			cas = list(cas, n->right);
+			c0=c0->link;
+			continue;
+		}
+		
+		// identify run of constants
+		c1 = c = c0;
+		while(c->link!=C && c->link->type==Ttypeconst)
+			c = c->link;
+		c0 = c->link;
+		c->link = nil;
+
+		// sort by hash
+		c1 = csort(c1, typecmp);
+		
+		// for debugging: linear search
+		if(0) {
+			for(c=c1; c!=C; c=c->link) {
+				n = c->node;
+				cas = list(cas, n->right);
+			}
+			continue;
+		}
+
+		// combine adjacent cases with the same hash
+		ncase = 0;
+		for(c=c1; c!=C; c=c->link) {
+			ncase++;
+			hash = list1(c->node->right);
+			while(c->link != C && c->link->hash == c->hash) {
+				hash = list(hash, c->link->node->right);
+				c->link = c->link->link;
+			}
+			c->node->right = liststmt(hash);
+		}
+		
+		// binary search among cases to narrow by hash
+		cas = list(cas, typebsw(c1, ncase));
+	}
+	if(nerrors == 0) {
+		cas = list(cas, def);
+		sw->nbody = concat(cas, sw->nbody);
+		sw->list = nil;
+		walkstmtlist(sw->nbody);
+	}
+}
+
+void
+walkswitch(Node *sw)
+{
+	/*
+	 * reorder the body into (OLIST, cases, statements)
+	 * cases have OGOTO into statements.
+	 * both have inserted OBREAK statements
+	 */
+	if(sw->ntest == N) {
+		sw->ntest = nodbool(1);
+		typecheck(&sw->ntest, Erv);
+	}
+
+	if(sw->ntest->op == OTYPESW) {
+		typeswitch(sw);
+//dump("sw", sw);
+		return;
+	}
+	exprswitch(sw);
+	// Discard old AST elements after a walk. They can confuse racewealk.
+	sw->ntest = nil;
+	sw->list = nil;
+}
+
+/*
+ * type check switch statement
+ */
+void
+typecheckswitch(Node *n)
+{
+	int top, lno, ptr;
+	char *nilonly;
+	Type *t, *badtype, *missing, *have;
+	NodeList *l, *ll;
+	Node *ncase, *nvar;
+	Node *def;
+
+	lno = lineno;
+	typechecklist(n->ninit, Etop);
+	nilonly = nil;
+
+	if(n->ntest != N && n->ntest->op == OTYPESW) {
+		// type switch
+		top = Etype;
+		typecheck(&n->ntest->right, Erv);
+		t = n->ntest->right->type;
+		if(t != T && t->etype != TINTER)
+			yyerror("cannot type switch on non-interface value %lN", n->ntest->right);
+	} else {
+		// value switch
+		top = Erv;
+		if(n->ntest) {
+			typecheck(&n->ntest, Erv);
+			defaultlit(&n->ntest, T);
+			t = n->ntest->type;
+		} else
+			t = types[TBOOL];
+		if(t) {
+			if(!okforeq[t->etype])
+				yyerror("cannot switch on %lN", n->ntest);
+			else if(t->etype == TARRAY && !isfixedarray(t))
+				nilonly = "slice";
+			else if(t->etype == TARRAY && isfixedarray(t) && algtype1(t, nil) == ANOEQ)
+				yyerror("cannot switch on %lN", n->ntest);
+			else if(t->etype == TSTRUCT && algtype1(t, &badtype) == ANOEQ)
+				yyerror("cannot switch on %lN (struct containing %T cannot be compared)", n->ntest, badtype);
+			else if(t->etype == TFUNC)
+				nilonly = "func";
+			else if(t->etype == TMAP)
+				nilonly = "map";
+		}
+	}
+	n->type = t;
+
+	def = N;
+	for(l=n->list; l; l=l->next) {
+		ncase = l->n;
+		setlineno(n);
+		if(ncase->list == nil) {
+			// default
+			if(def != N)
+				yyerror("multiple defaults in switch (first at %L)", def->lineno);
+			else
+				def = ncase;
+		} else {
+			for(ll=ncase->list; ll; ll=ll->next) {
+				setlineno(ll->n);
+				typecheck(&ll->n, Erv | Etype);
+				if(ll->n->type == T || t == T)
+					continue;
+				setlineno(ncase);
+				switch(top) {
+				case Erv:	// expression switch
+					defaultlit(&ll->n, t);
+					if(ll->n->op == OTYPE)
+						yyerror("type %T is not an expression", ll->n->type);
+					else if(ll->n->type != T && !assignop(ll->n->type, t, nil) && !assignop(t, ll->n->type, nil)) {
+						if(n->ntest)
+							yyerror("invalid case %N in switch on %N (mismatched types %T and %T)", ll->n, n->ntest, ll->n->type, t);
+						else
+							yyerror("invalid case %N in switch (mismatched types %T and bool)", ll->n, ll->n->type);
+					} else if(nilonly && !isconst(ll->n, CTNIL)) {
+						yyerror("invalid case %N in switch (can only compare %s %N to nil)", ll->n, nilonly, n->ntest);
+					}
+					break;
+				case Etype:	// type switch
+					if(ll->n->op == OLITERAL && istype(ll->n->type, TNIL)) {
+						;
+					} else if(ll->n->op != OTYPE && ll->n->type != T) {  // should this be ||?
+						yyerror("%lN is not a type", ll->n);
+						// reset to original type
+						ll->n = n->ntest->right;
+					} else if(ll->n->type->etype != TINTER && t->etype == TINTER && !implements(ll->n->type, t, &missing, &have, &ptr)) {
+						if(have && !missing->broke && !have->broke)
+							yyerror("impossible type switch case: %lN cannot have dynamic type %T"
+								" (wrong type for %S method)\n\thave %S%hT\n\twant %S%hT",
+								n->ntest->right, ll->n->type, missing->sym, have->sym, have->type,
+								missing->sym, missing->type);
+						else if(!missing->broke)
+							yyerror("impossible type switch case: %lN cannot have dynamic type %T"
+								" (missing %S method)", n->ntest->right, ll->n->type, missing->sym);
+					}
+					break;
+				}
+			}
+		}
+		if(top == Etype && n->type != T) {
+			ll = ncase->list;
+			nvar = ncase->nname;
+			if(nvar != N) {
+				if(ll && ll->next == nil && ll->n->type != T && !istype(ll->n->type, TNIL)) {
+					// single entry type switch
+					nvar->ntype = typenod(ll->n->type);
+				} else {
+					// multiple entry type switch or default
+					nvar->ntype = typenod(n->type);
+				}
+			}
+		}
+		typechecklist(ncase->nbody, Etop);
+	}
+
+	lineno = lno;
+}
diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c
new file mode 100644
index 0000000..ce12f15
--- /dev/null
+++ b/src/cmd/gc/typecheck.c
@@ -0,0 +1,3582 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+/*
+ * type check the whole tree of an expression.
+ * calculates expression types.
+ * evaluates compile time constants.
+ * marks variables that escape the local frame.
+ * rewrites n->op to be more specific in some cases.
+ */
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+static void	implicitstar(Node**);
+static int	onearg(Node*, char*, ...);
+static int	twoarg(Node*);
+static int	lookdot(Node*, Type*, int);
+static int	looktypedot(Node*, Type*, int);
+static void	typecheckaste(int, Node*, int, Type*, NodeList*, char*);
+static Type*	lookdot1(Node*, Sym *s, Type *t, Type *f, int);
+static int	nokeys(NodeList*);
+static void	typecheckcomplit(Node**);
+static void	typecheckas2(Node*);
+static void	typecheckas(Node*);
+static void	typecheckfunc(Node*);
+static void	checklvalue(Node*, char*);
+static void	checkassign(Node*);
+static void	checkassignlist(NodeList*);
+static void	stringtoarraylit(Node**);
+static Node*	resolve(Node*);
+static void	checkdefergo(Node*);
+static int	checkmake(Type*, char*, Node*);
+static int	checksliceindex(Node*, Node*, Type*);
+static int	checksliceconst(Node*, Node*);
+
+static	NodeList*	typecheckdefstack;
+
+/*
+ * resolve ONONAME to definition, if any.
+ */
+static Node*
+resolve(Node *n)
+{
+	Node *r;
+
+	if(n != N && n->op == ONONAME && n->sym != S && (r = n->sym->def) != N) {
+		if(r->op != OIOTA)
+			n = r;
+		else if(n->iota >= 0)
+			n = nodintconst(n->iota);
+	}
+	return n;
+}
+
+void
+typechecklist(NodeList *l, int top)
+{
+	for(; l; l=l->next)
+		typecheck(&l->n, top);
+}
+
+static char* _typekind[] = {
+	[TINT]		= "int",
+	[TUINT]		= "uint",
+	[TINT8]		= "int8",
+	[TUINT8]	= "uint8",
+	[TINT16]	= "int16",
+	[TUINT16]	= "uint16",
+	[TINT32]	= "int32",
+	[TUINT32]	= "uint32",
+	[TINT64]	= "int64",
+	[TUINT64]	= "uint64",
+	[TUINTPTR]	= "uintptr",
+	[TCOMPLEX64]	= "complex64",
+	[TCOMPLEX128]	= "complex128",
+	[TFLOAT32]	= "float32",
+	[TFLOAT64]	= "float64",
+	[TBOOL]		= "bool",
+	[TSTRING]	= "string",
+	[TPTR32]	= "pointer",
+	[TPTR64]	= "pointer",
+	[TUNSAFEPTR]	= "unsafe.Pointer",
+	[TSTRUCT]	= "struct",
+	[TINTER]	= "interface",
+	[TCHAN]		= "chan",
+	[TMAP]		= "map",
+	[TARRAY]	= "array",
+	[TFUNC]		= "func",
+	[TNIL]		= "nil",
+	[TIDEAL]	= "untyped number",
+};
+
+static char*
+typekind(Type *t)
+{
+	int et;
+	static char buf[50];
+	char *s;
+	
+	if(isslice(t))
+		return "slice";
+	et = t->etype;
+	if(0 <= et && et < nelem(_typekind) && (s=_typekind[et]) != nil)
+		return s;
+	snprint(buf, sizeof buf, "etype=%d", et);
+	return buf;
+}
+
+/*
+ * sprint_depchain prints a dependency chain
+ * of nodes into fmt.
+ * It is used by typecheck in the case of OLITERAL nodes
+ * to print constant definition loops.
+ */
+static void
+sprint_depchain(Fmt *fmt, NodeList *stack, Node *cur, Node *first)
+{
+	NodeList *l;
+
+	for(l = stack; l; l=l->next) {
+		if(l->n->op == cur->op) {
+			if(l->n != first)
+				sprint_depchain(fmt, l->next, l->n, first);
+			fmtprint(fmt, "\n\t%L: %N uses %N", l->n->lineno, l->n, cur);
+			return;
+		}
+	}
+}
+
+/*
+ * type check node *np.
+ * replaces *np with a new pointer in some cases.
+ * returns the final value of *np as a convenience.
+ */
+static void typecheck1(Node **, int);
+Node*
+typecheck(Node **np, int top)
+{
+	Node *n;
+	int lno;
+	Fmt fmt;
+	NodeList *l;
+	static NodeList *tcstack, *tcfree;
+
+	// cannot type check until all the source has been parsed
+	if(!typecheckok)
+		fatal("early typecheck");
+
+	n = *np;
+	if(n == N)
+		return N;
+	
+	lno = setlineno(n);
+
+	// Skip over parens.
+	while(n->op == OPAREN)
+		n = n->left;
+
+	// Resolve definition of name and value of iota lazily.
+	n = resolve(n);
+
+	*np = n;
+
+	// Skip typecheck if already done.
+	// But re-typecheck ONAME/OTYPE/OLITERAL/OPACK node in case context has changed.
+	if(n->typecheck == 1) {
+		switch(n->op) {
+		case ONAME:
+		case OTYPE:
+		case OLITERAL:
+		case OPACK:
+			break;
+		default:
+			lineno = lno;
+			return n;
+		}
+	}
+
+	if(n->typecheck == 2) {
+		// Typechecking loop. Trying printing a meaningful message,
+		// otherwise a stack trace of typechecking.
+		switch(n->op) {
+		case ONAME:
+			// We can already diagnose variables used as types.
+			if((top & (Erv|Etype)) == Etype)
+				yyerror("%N is not a type", n);
+			break;
+		case OLITERAL:
+			if((top & (Erv|Etype)) == Etype) {
+				yyerror("%N is not a type", n);
+				break;
+			}
+			fmtstrinit(&fmt);
+			sprint_depchain(&fmt, tcstack, n, n);
+			yyerrorl(n->lineno, "constant definition loop%s", fmtstrflush(&fmt));
+			break;
+		}
+		if(nsavederrors+nerrors == 0) {
+			fmtstrinit(&fmt);
+			for(l=tcstack; l; l=l->next)
+				fmtprint(&fmt, "\n\t%L %N", l->n->lineno, l->n);
+			yyerror("typechecking loop involving %N%s", n, fmtstrflush(&fmt));
+		}
+		lineno = lno;
+		return n;
+	}
+	n->typecheck = 2;
+
+	if(tcfree != nil) {
+		l = tcfree;
+		tcfree = l->next;
+	} else
+		l = mal(sizeof *l);
+	l->next = tcstack;
+	l->n = n;
+	tcstack = l;
+
+	typecheck1(&n, top);
+	*np = n;
+	n->typecheck = 1;
+
+	if(tcstack != l)
+		fatal("typecheck stack out of sync");
+	tcstack = l->next;
+	l->next = tcfree;
+	tcfree = l;
+
+	lineno = lno;
+	return n;
+}
+
+/*
+ * does n contain a call or receive operation?
+ */
+static int callrecvlist(NodeList*);
+
+static int
+callrecv(Node *n)
+{
+	if(n == nil)
+		return 0;
+	
+	switch(n->op) {
+	case OCALL:
+	case OCALLMETH:
+	case OCALLINTER:
+	case OCALLFUNC:
+	case ORECV:
+	case OCAP:
+	case OLEN:
+	case OCOPY:
+	case ONEW:
+	case OAPPEND:
+	case ODELETE:
+		return 1;
+	}
+
+	return callrecv(n->left) ||
+		callrecv(n->right) ||
+		callrecv(n->ntest) ||
+		callrecv(n->nincr) ||
+		callrecvlist(n->ninit) ||
+		callrecvlist(n->nbody) ||
+		callrecvlist(n->nelse) ||
+		callrecvlist(n->list) ||
+		callrecvlist(n->rlist);
+}
+
+static int
+callrecvlist(NodeList *l)
+{
+	for(; l; l=l->next)
+		if(callrecv(l->n))
+			return 1;
+	return 0;
+}
+
+// indexlit implements typechecking of untyped values as
+// array/slice indexes. It is equivalent to defaultlit
+// except for constants of numerical kind, which are acceptable
+// whenever they can be represented by a value of type int.
+static void
+indexlit(Node **np)
+{
+	Node *n;
+
+	n = *np;
+	if(n == N || !isideal(n->type))
+		return;
+	switch(consttype(n)) {
+	case CTINT:
+	case CTRUNE:
+	case CTFLT:
+	case CTCPLX:
+		defaultlit(np, types[TINT]);
+		break;
+	}
+	defaultlit(np, T);
+}
+
+static void
+typecheck1(Node **np, int top)
+{
+	int et, aop, op, ptr;
+	Node *n, *l, *r, *lo, *mid, *hi;
+	NodeList *args;
+	int ok, ntop;
+	Type *t, *tp, *missing, *have, *badtype;
+	Val v;
+	char *why, *desc, descbuf[64];
+	vlong x;
+	
+	n = *np;
+
+	if(n->sym) {
+		if(n->op == ONAME && n->etype != 0 && !(top & Ecall)) {
+			yyerror("use of builtin %S not in function call", n->sym);
+			goto error;
+		}
+
+		typecheckdef(n);
+		if(n->op == ONONAME)
+			goto error;
+	}
+	*np = n;
+
+reswitch:
+	ok = 0;
+	switch(n->op) {
+	default:
+		// until typecheck is complete, do nothing.
+		dump("typecheck", n);
+		fatal("typecheck %O", n->op);
+
+	/*
+	 * names
+	 */
+	case OLITERAL:
+		ok |= Erv;
+		if(n->type == T && n->val.ctype == CTSTR)
+			n->type = idealstring;
+		goto ret;
+
+	case ONONAME:
+		ok |= Erv;
+		goto ret;
+
+	case ONAME:
+		if(n->etype != 0) {
+			ok |= Ecall;
+			goto ret;
+		}
+		if(!(top & Easgn)) {
+			// not a write to the variable
+			if(isblank(n)) {
+				yyerror("cannot use _ as value");
+				goto error;
+			}
+			n->used = 1;
+		}
+		if(!(top &Ecall) && isunsafebuiltin(n)) {
+			yyerror("%N is not an expression, must be called", n);
+			goto error;
+		}
+		ok |= Erv;
+		goto ret;
+
+	case OPACK:
+		yyerror("use of package %S without selector", n->sym);
+		goto error;
+
+	case ODDD:
+		break;
+
+	/*
+	 * types (OIND is with exprs)
+	 */
+	case OTYPE:
+		ok |= Etype;
+		if(n->type == T)
+			goto error;
+		break;
+	
+	case OTARRAY:
+		ok |= Etype;
+		t = typ(TARRAY);
+		l = n->left;
+		r = n->right;
+		if(l == nil) {
+			t->bound = -1;	// slice
+		} else if(l->op == ODDD) {
+			t->bound = -100;	// to be filled in
+			if(!(top&Ecomplit) && !n->diag) {
+				t->broke = 1;
+				n->diag = 1;
+				yyerror("use of [...] array outside of array literal");
+			}
+		} else {
+			l = typecheck(&n->left, Erv);
+			switch(consttype(l)) {
+			case CTINT:
+			case CTRUNE:
+				v = l->val;
+				break;
+			case CTFLT:
+				v = toint(l->val);
+				break;
+			default:
+				if(l->type != T && isint[l->type->etype] && l->op != OLITERAL)
+					yyerror("non-constant array bound %N", l);
+				else
+					yyerror("invalid array bound %N", l);
+				goto error;
+			}
+			t->bound = mpgetfix(v.u.xval);
+			if(doesoverflow(v, types[TINT])) {
+				yyerror("array bound is too large"); 
+				goto error;
+			} else if(t->bound < 0) {
+				yyerror("array bound must be non-negative");
+				goto error;
+			}
+		}
+		typecheck(&r, Etype);
+		if(r->type == T)
+			goto error;
+		t->type = r->type;
+		n->op = OTYPE;
+		n->type = t;
+		n->left = N;
+		n->right = N;
+		if(t->bound != -100)
+			checkwidth(t);
+		break;
+
+	case OTMAP:
+		ok |= Etype;
+		l = typecheck(&n->left, Etype);
+		r = typecheck(&n->right, Etype);
+		if(l->type == T || r->type == T)
+			goto error;
+		n->op = OTYPE;
+		n->type = maptype(l->type, r->type);
+		n->left = N;
+		n->right = N;
+		break;
+
+	case OTCHAN:
+		ok |= Etype;
+		l = typecheck(&n->left, Etype);
+		if(l->type == T)
+			goto error;
+		t = typ(TCHAN);
+		t->type = l->type;
+		t->chan = n->etype;
+		n->op = OTYPE;
+		n->type = t;
+		n->left = N;
+		n->etype = 0;
+		break;
+
+	case OTSTRUCT:
+		ok |= Etype;
+		n->op = OTYPE;
+		n->type = tostruct(n->list);
+		if(n->type == T || n->type->broke)
+			goto error;
+		n->list = nil;
+		break;
+
+	case OTINTER:
+		ok |= Etype;
+		n->op = OTYPE;
+		n->type = tointerface(n->list);
+		if(n->type == T)
+			goto error;
+		break;
+
+	case OTFUNC:
+		ok |= Etype;
+		n->op = OTYPE;
+		n->type = functype(n->left, n->list, n->rlist);
+		if(n->type == T)
+			goto error;
+		break;
+
+	/*
+	 * type or expr
+	 */
+	case OIND:
+		ntop = Erv | Etype;
+		if(!(top & Eaddr))  		// The *x in &*x is not an indirect.
+			ntop |= Eindir;
+		ntop |= top & Ecomplit;
+		l = typecheck(&n->left, ntop);
+		if((t = l->type) == T)
+			goto error;
+		if(l->op == OTYPE) {
+			ok |= Etype;
+			n->op = OTYPE;
+			n->type = ptrto(l->type);
+			n->left = N;
+			goto ret;
+		}
+		if(!isptr[t->etype]) {
+			if(top & (Erv | Etop)) {
+				yyerror("invalid indirect of %lN", n->left);
+				goto error;
+			}
+			goto ret;
+		}
+		ok |= Erv;
+		n->type = t->type;
+		goto ret;
+
+	/*
+	 * arithmetic exprs
+	 */
+	case OASOP:
+		ok |= Etop;
+		l = typecheck(&n->left, Erv);
+		checkassign(n->left);
+		r = typecheck(&n->right, Erv);
+		if(l->type == T || r->type == T)
+			goto error;
+		op = n->etype;
+		goto arith;
+
+	case OADD:
+	case OAND:
+	case OANDAND:
+	case OANDNOT:
+	case ODIV:
+	case OEQ:
+	case OGE:
+	case OGT:
+	case OLE:
+	case OLT:
+	case OLSH:
+	case ORSH:
+	case OMOD:
+	case OMUL:
+	case ONE:
+	case OOR:
+	case OOROR:
+	case OSUB:
+	case OXOR:
+		ok |= Erv;
+		l = typecheck(&n->left, Erv | (top & Eiota));
+		r = typecheck(&n->right, Erv | (top & Eiota));
+		if(l->type == T || r->type == T)
+			goto error;
+		op = n->op;
+	arith:
+		if(op == OLSH || op == ORSH)
+			goto shift;
+		// ideal mixed with non-ideal
+		defaultlit2(&l, &r, 0);
+		n->left = l;
+		n->right = r;
+		if(l->type == T || r->type == T)
+			goto error;
+		t = l->type;
+		if(t->etype == TIDEAL)
+			t = r->type;
+		et = t->etype;
+		if(et == TIDEAL)
+			et = TINT;
+		if(iscmp[n->op] && t->etype != TIDEAL && !eqtype(l->type, r->type)) {
+			// comparison is okay as long as one side is
+			// assignable to the other.  convert so they have
+			// the same type.
+			//
+			// the only conversion that isn't a no-op is concrete == interface.
+			// in that case, check comparability of the concrete type.
+			if(r->type->etype != TBLANK && (aop = assignop(l->type, r->type, nil)) != 0) {
+				if(isinter(r->type) && !isinter(l->type) && algtype1(l->type, nil) == ANOEQ) {
+					yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(l->type));
+					goto error;
+				}
+				l = nod(aop, l, N);
+				l->type = r->type;
+				l->typecheck = 1;
+				n->left = l;
+				t = l->type;
+			} else if(l->type->etype != TBLANK && (aop = assignop(r->type, l->type, nil)) != 0) {
+				if(isinter(l->type) && !isinter(r->type) && algtype1(r->type, nil) == ANOEQ) {
+					yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(r->type));
+					goto error;
+				}
+				r = nod(aop, r, N);
+				r->type = l->type;
+				r->typecheck = 1;
+				n->right = r;
+				t = r->type;
+			}
+			et = t->etype;
+		}
+		if(t->etype != TIDEAL && !eqtype(l->type, r->type)) {
+			defaultlit2(&l, &r, 1);
+			if(n->op == OASOP && n->implicit) {
+				yyerror("invalid operation: %N (non-numeric type %T)", n, l->type);
+				goto error;
+			}
+			yyerror("invalid operation: %N (mismatched types %T and %T)", n, l->type, r->type);
+			goto error;
+		}
+		if(!okfor[op][et]) {
+			yyerror("invalid operation: %N (operator %O not defined on %s)", n, op, typekind(t));
+			goto error;
+		}
+		// okfor allows any array == array, map == map, func == func.
+		// restrict to slice/map/func == nil and nil == slice/map/func.
+		if(isfixedarray(l->type) && algtype1(l->type, nil) == ANOEQ) {
+			yyerror("invalid operation: %N (%T cannot be compared)", n, l->type);
+			goto error;
+		}
+		if(isslice(l->type) && !isnil(l) && !isnil(r)) {
+			yyerror("invalid operation: %N (slice can only be compared to nil)", n);
+			goto error;
+		}
+		if(l->type->etype == TMAP && !isnil(l) && !isnil(r)) {
+			yyerror("invalid operation: %N (map can only be compared to nil)", n);
+			goto error;
+		}
+		if(l->type->etype == TFUNC && !isnil(l) && !isnil(r)) {
+			yyerror("invalid operation: %N (func can only be compared to nil)", n);
+			goto error;
+		}
+		if(l->type->etype == TSTRUCT && algtype1(l->type, &badtype) == ANOEQ) {
+			yyerror("invalid operation: %N (struct containing %T cannot be compared)", n, badtype);
+			goto error;
+		}
+		
+		t = l->type;
+		if(iscmp[n->op]) {
+			evconst(n);
+			t = idealbool;
+			if(n->op != OLITERAL) {
+				defaultlit2(&l, &r, 1);
+				n->left = l;
+				n->right = r;
+			}
+		// non-comparison operators on ideal bools should make them lose their ideal-ness
+		} else if(t == idealbool)
+			t = types[TBOOL];
+
+		if(et == TSTRING) {
+			if(iscmp[n->op]) {
+				n->etype = n->op;
+				n->op = OCMPSTR;
+			} else if(n->op == OADD) {
+				// create OADDSTR node with list of strings in x + y + z + (w + v) + ...
+				n->op = OADDSTR;
+				if(l->op == OADDSTR)
+					n->list = l->list;
+				else
+					n->list = list1(l);
+				if(r->op == OADDSTR)
+					n->list = concat(n->list, r->list);
+				else
+					n->list = list(n->list, r);
+				n->left = N;
+				n->right = N;
+			}
+		}
+		if(et == TINTER) {
+			if(l->op == OLITERAL && l->val.ctype == CTNIL) {
+				// swap for back end
+				n->left = r;
+				n->right = l;
+			} else if(r->op == OLITERAL && r->val.ctype == CTNIL) {
+				// leave alone for back end
+			} else {
+				n->etype = n->op;
+				n->op = OCMPIFACE;
+			}
+		}
+
+		if((op == ODIV || op == OMOD) && isconst(r, CTINT))
+		if(mpcmpfixc(r->val.u.xval, 0) == 0) {
+			yyerror("division by zero");
+			goto error;
+		} 
+
+		n->type = t;
+		goto ret;
+
+	shift:
+		defaultlit(&r, types[TUINT]);
+		n->right = r;
+		t = r->type;
+		if(!isint[t->etype] || issigned[t->etype]) {
+			yyerror("invalid operation: %N (shift count type %T, must be unsigned integer)", n, r->type);
+			goto error;
+		}
+		t = l->type;
+		if(t != T && t->etype != TIDEAL && !isint[t->etype]) {
+			yyerror("invalid operation: %N (shift of type %T)", n, t);
+			goto error;
+		}
+		// no defaultlit for left
+		// the outer context gives the type
+		n->type = l->type;
+		goto ret;
+
+	case OCOM:
+	case OMINUS:
+	case ONOT:
+	case OPLUS:
+		ok |= Erv;
+		l = typecheck(&n->left, Erv | (top & Eiota));
+		if((t = l->type) == T)
+			goto error;
+		if(!okfor[n->op][t->etype]) {
+			yyerror("invalid operation: %O %T", n->op, t);
+			goto error;
+		}
+		n->type = t;
+		goto ret;
+
+	/*
+	 * exprs
+	 */
+	case OADDR:
+		ok |= Erv;
+		typecheck(&n->left, Erv | Eaddr);
+		if(n->left->type == T)
+			goto error;
+		checklvalue(n->left, "take the address of");
+		r = outervalue(n->left);
+		for(l = n->left; l != r; l = l->left)
+			l->addrtaken = 1;
+		if(l->orig != l && l->op == ONAME)
+			fatal("found non-orig name node %N", l);
+		l->addrtaken = 1;
+		defaultlit(&n->left, T);
+		l = n->left;
+		if((t = l->type) == T)
+			goto error;
+		n->type = ptrto(t);
+		goto ret;
+
+	case OCOMPLIT:
+		ok |= Erv;
+		typecheckcomplit(&n);
+		if(n->type == T)
+			goto error;
+		goto ret;
+
+	case OXDOT:
+		n = adddot(n);
+		n->op = ODOT;
+		if(n->left == N)
+			goto error;
+		// fall through
+	case ODOT:
+		typecheck(&n->left, Erv|Etype);
+		defaultlit(&n->left, T);
+		if((t = n->left->type) == T)
+			goto error;
+		if(n->right->op != ONAME) {
+			yyerror("rhs of . must be a name");	// impossible
+			goto error;
+		}
+		r = n->right;
+
+		if(n->left->op == OTYPE) {
+			if(!looktypedot(n, t, 0)) {
+				if(looktypedot(n, t, 1))
+					yyerror("%N undefined (cannot refer to unexported method %S)", n, n->right->sym);
+				else
+					yyerror("%N undefined (type %T has no method %S)", n, t, n->right->sym);
+				goto error;
+			}
+			if(n->type->etype != TFUNC || n->type->thistuple != 1) {
+				yyerror("type %T has no method %hS", n->left->type, n->right->sym);
+				n->type = T;
+				goto error;
+			}
+			n->op = ONAME;
+			n->sym = n->right->sym;
+			n->type = methodfunc(n->type, n->left->type);
+			n->xoffset = 0;
+			n->class = PFUNC;
+			ok = Erv;
+			goto ret;
+		}
+		if(isptr[t->etype] && t->type->etype != TINTER) {
+			t = t->type;
+			if(t == T)
+				goto error;
+			n->op = ODOTPTR;
+			checkwidth(t);
+		}
+		if(isblank(n->right)) {
+			yyerror("cannot refer to blank field or method");
+			goto error;
+		}
+		if(!lookdot(n, t, 0)) {
+			if(lookdot(n, t, 1))
+				yyerror("%N undefined (cannot refer to unexported field or method %S)", n, n->right->sym);
+			else
+				yyerror("%N undefined (type %T has no field or method %S)", n, n->left->type, n->right->sym);
+			goto error;
+		}
+		switch(n->op) {
+		case ODOTINTER:
+		case ODOTMETH:
+			if(top&Ecall)
+				ok |= Ecall;
+			else {
+				typecheckpartialcall(n, r);
+				ok |= Erv;
+			}
+			break;
+		default:
+			ok |= Erv;
+			break;
+		}
+		goto ret;
+
+	case ODOTTYPE:
+		ok |= Erv;
+		typecheck(&n->left, Erv);
+		defaultlit(&n->left, T);
+		l = n->left;
+		if((t = l->type) == T)
+			goto error;
+		if(!isinter(t)) {
+			yyerror("invalid type assertion: %N (non-interface type %T on left)", n, t);
+			goto error;
+		}
+		if(n->right != N) {
+			typecheck(&n->right, Etype);
+			n->type = n->right->type;
+			n->right = N;
+			if(n->type == T)
+				goto error;
+		}
+		if(n->type != T && n->type->etype != TINTER)
+		if(!implements(n->type, t, &missing, &have, &ptr)) {
+			if(have && have->sym == missing->sym)
+				yyerror("impossible type assertion:\n\t%T does not implement %T (wrong type for %S method)\n"
+					"\t\thave %S%hhT\n\t\twant %S%hhT", n->type, t, missing->sym,
+					have->sym, have->type, missing->sym, missing->type);
+			else if(ptr)
+				yyerror("impossible type assertion:\n\t%T does not implement %T (%S method has pointer receiver)",
+					n->type, t, missing->sym);
+			else if(have)
+				yyerror("impossible type assertion:\n\t%T does not implement %T (missing %S method)\n"
+					"\t\thave %S%hhT\n\t\twant %S%hhT", n->type, t, missing->sym,
+					have->sym, have->type, missing->sym, missing->type);
+			else
+				yyerror("impossible type assertion:\n\t%T does not implement %T (missing %S method)",
+					n->type, t, missing->sym);
+			goto error;
+		}
+		goto ret;
+
+	case OINDEX:
+		ok |= Erv;
+		typecheck(&n->left, Erv);
+		defaultlit(&n->left, T);
+		implicitstar(&n->left);
+		l = n->left;
+		typecheck(&n->right, Erv);
+		r = n->right;
+		if((t = l->type) == T || r->type == T)
+			goto error;
+		switch(t->etype) {
+		default:
+			yyerror("invalid operation: %N (type %T does not support indexing)", n, t);
+			goto error;
+
+
+		case TSTRING:
+		case TARRAY:
+			indexlit(&n->right);
+			if(t->etype == TSTRING)
+				n->type = types[TUINT8];
+			else
+				n->type = t->type;
+			why = "string";
+			if(t->etype == TARRAY) {
+				if(isfixedarray(t))
+					why = "array";
+				else
+					why = "slice";
+			}
+			if(n->right->type != T && !isint[n->right->type->etype]) {
+				yyerror("non-integer %s index %N", why, n->right);
+				break;
+			}
+			if(isconst(n->right, CTINT)) {
+				x = mpgetfix(n->right->val.u.xval);
+				if(x < 0)
+					yyerror("invalid %s index %N (index must be non-negative)", why, n->right);
+				else if(isfixedarray(t) && t->bound > 0 && x >= t->bound)
+					yyerror("invalid array index %N (out of bounds for %d-element array)", n->right, t->bound);
+				else if(isconst(n->left, CTSTR) && x >= n->left->val.u.sval->len)
+					yyerror("invalid string index %N (out of bounds for %d-byte string)", n->right, n->left->val.u.sval->len);
+				else if(mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0)
+					yyerror("invalid %s index %N (index too large)", why, n->right);
+			}
+			break;
+
+		case TMAP:
+			n->etype = 0;
+			defaultlit(&n->right, t->down);
+			if(n->right->type != T)
+				n->right = assignconv(n->right, t->down, "map index");
+			n->type = t->type;
+			n->op = OINDEXMAP;
+			break;
+		}
+		goto ret;
+
+	case ORECV:
+		ok |= Etop | Erv;
+		typecheck(&n->left, Erv);
+		defaultlit(&n->left, T);
+		l = n->left;
+		if((t = l->type) == T)
+			goto error;
+		if(t->etype != TCHAN) {
+			yyerror("invalid operation: %N (receive from non-chan type %T)", n, t);
+			goto error;
+		}
+		if(!(t->chan & Crecv)) {
+			yyerror("invalid operation: %N (receive from send-only type %T)", n, t);
+			goto error;
+		}
+		n->type = t->type;
+		goto ret;
+
+	case OSEND:
+		ok |= Etop;
+		l = typecheck(&n->left, Erv);
+		typecheck(&n->right, Erv);
+		defaultlit(&n->left, T);
+		l = n->left;
+		if((t = l->type) == T)
+			goto error;
+		if(t->etype != TCHAN) {
+			yyerror("invalid operation: %N (send to non-chan type %T)", n, t);
+			goto error;
+		}
+		if(!(t->chan & Csend)) {
+			yyerror("invalid operation: %N (send to receive-only type %T)", n, t);
+			goto error;
+		}
+		defaultlit(&n->right, t->type);
+		r = n->right;
+		if(r->type == T)
+			goto error;
+		n->right = assignconv(r, l->type->type, "send");
+		// TODO: more aggressive
+		n->etype = 0;
+		n->type = T;
+		goto ret;
+
+	case OSLICE:
+		ok |= Erv;
+		typecheck(&n->left, top);
+		typecheck(&n->right->left, Erv);
+		typecheck(&n->right->right, Erv);
+		defaultlit(&n->left, T);
+		indexlit(&n->right->left);
+		indexlit(&n->right->right);
+		l = n->left;
+		if(isfixedarray(l->type)) {
+			if(!islvalue(n->left)) {
+				yyerror("invalid operation %N (slice of unaddressable value)", n);
+				goto error;
+			}
+			n->left = nod(OADDR, n->left, N);
+			n->left->implicit = 1;
+			typecheck(&n->left, Erv);
+			l = n->left;
+		}
+		if((t = l->type) == T)
+			goto error;
+		tp = nil;
+		if(istype(t, TSTRING)) {
+			n->type = t;
+			n->op = OSLICESTR;
+		} else if(isptr[t->etype] && isfixedarray(t->type)) {
+			tp = t->type;
+			n->type = typ(TARRAY);
+			n->type->type = tp->type;
+			n->type->bound = -1;
+			dowidth(n->type);
+			n->op = OSLICEARR;
+		} else if(isslice(t)) {
+			n->type = t;
+		} else {
+			yyerror("cannot slice %N (type %T)", l, t);
+			goto error;
+		}
+		if((lo = n->right->left) != N && checksliceindex(l, lo, tp) < 0)
+			goto error;
+		if((hi = n->right->right) != N && checksliceindex(l, hi, tp) < 0)
+			goto error;
+		if(checksliceconst(lo, hi) < 0)
+			goto error;
+		goto ret;
+
+	case OSLICE3:
+		ok |= Erv;
+		typecheck(&n->left, top);
+		typecheck(&n->right->left, Erv);
+		typecheck(&n->right->right->left, Erv);
+		typecheck(&n->right->right->right, Erv);
+		defaultlit(&n->left, T);
+		indexlit(&n->right->left);
+		indexlit(&n->right->right->left);
+		indexlit(&n->right->right->right);
+		l = n->left;
+		if(isfixedarray(l->type)) {
+			if(!islvalue(n->left)) {
+				yyerror("invalid operation %N (slice of unaddressable value)", n);
+				goto error;
+			}
+			n->left = nod(OADDR, n->left, N);
+			n->left->implicit = 1;
+			typecheck(&n->left, Erv);
+			l = n->left;
+		}
+		if((t = l->type) == T)
+			goto error;
+		tp = nil;
+		if(istype(t, TSTRING)) {
+			yyerror("invalid operation %N (3-index slice of string)", n);
+			goto error;
+		}
+		if(isptr[t->etype] && isfixedarray(t->type)) {
+			tp = t->type;
+			n->type = typ(TARRAY);
+			n->type->type = tp->type;
+			n->type->bound = -1;
+			dowidth(n->type);
+			n->op = OSLICE3ARR;
+		} else if(isslice(t)) {
+			n->type = t;
+		} else {
+			yyerror("cannot slice %N (type %T)", l, t);
+			goto error;
+		}
+		if((lo = n->right->left) != N && checksliceindex(l, lo, tp) < 0)
+			goto error;
+		if((mid = n->right->right->left) != N && checksliceindex(l, mid, tp) < 0)
+			goto error;
+		if((hi = n->right->right->right) != N && checksliceindex(l, hi, tp) < 0)
+			goto error;
+		if(checksliceconst(lo, hi) < 0 || checksliceconst(lo, mid) < 0 || checksliceconst(mid, hi) < 0)
+			goto error;
+		goto ret;
+
+	/*
+	 * call and call like
+	 */
+	case OCALL:
+		l = n->left;
+		if(l->op == ONAME && (r = unsafenmagic(n)) != N) {
+			if(n->isddd)
+				yyerror("invalid use of ... with builtin %N", l);
+			n = r;
+			goto reswitch;
+		}
+		typecheck(&n->left, Erv | Etype | Ecall |(top&Eproc));
+		n->diag |= n->left->diag;
+		l = n->left;
+		if(l->op == ONAME && l->etype != 0) {
+			if(n->isddd && l->etype != OAPPEND)
+				yyerror("invalid use of ... with builtin %N", l);
+			// builtin: OLEN, OCAP, etc.
+			n->op = l->etype;
+			n->left = n->right;
+			n->right = N;
+			goto reswitch;
+		}
+		defaultlit(&n->left, T);
+		l = n->left;
+		if(l->op == OTYPE) {
+			if(n->isddd || l->type->bound == -100) {
+				if(!l->type->broke)
+					yyerror("invalid use of ... in type conversion", l);
+				n->diag = 1;
+			}
+			// pick off before type-checking arguments
+			ok |= Erv;
+			// turn CALL(type, arg) into CONV(arg) w/ type
+			n->left = N;
+			n->op = OCONV;
+			n->type = l->type;
+			if(onearg(n, "conversion to %T", l->type) < 0)
+				goto error;
+			goto doconv;
+		}
+
+		if(count(n->list) == 1 && !n->isddd)
+			typecheck(&n->list->n, Erv | Efnstruct);
+		else
+			typechecklist(n->list, Erv);
+		if((t = l->type) == T)
+			goto error;
+		checkwidth(t);
+
+		switch(l->op) {
+		case ODOTINTER:
+			n->op = OCALLINTER;
+			break;
+
+		case ODOTMETH:
+			n->op = OCALLMETH;
+			// typecheckaste was used here but there wasn't enough
+			// information further down the call chain to know if we
+			// were testing a method receiver for unexported fields.
+			// It isn't necessary, so just do a sanity check.
+			tp = getthisx(t)->type->type;
+			if(l->left == N || !eqtype(l->left->type, tp))
+				fatal("method receiver");
+			break;
+
+		default:
+			n->op = OCALLFUNC;
+			if(t->etype != TFUNC) {
+				yyerror("cannot call non-function %N (type %T)", l, t);
+				goto error;
+			}
+			break;
+		}
+		if(snprint(descbuf, sizeof descbuf, "argument to %N", n->left) < sizeof descbuf)
+			desc = descbuf;
+		else
+			desc = "function argument";
+		typecheckaste(OCALL, n->left, n->isddd, getinargx(t), n->list, desc);
+		ok |= Etop;
+		if(t->outtuple == 0)
+			goto ret;
+		ok |= Erv;
+		if(t->outtuple == 1) {
+			t = getoutargx(l->type)->type;
+			if(t == T)
+				goto error;
+			if(t->etype == TFIELD)
+				t = t->type;
+			n->type = t;
+			goto ret;
+		}
+		// multiple return
+		if(!(top & (Efnstruct | Etop))) {
+			yyerror("multiple-value %N() in single-value context", l);
+			goto ret;
+		}
+		n->type = getoutargx(l->type);
+		goto ret;
+
+	case OCAP:
+	case OLEN:
+	case OREAL:
+	case OIMAG:
+		ok |= Erv;
+		if(onearg(n, "%O", n->op) < 0)
+			goto error;
+		typecheck(&n->left, Erv);
+		defaultlit(&n->left, T);
+		implicitstar(&n->left);
+		l = n->left;
+		t = l->type;
+		if(t == T)
+			goto error;
+		switch(n->op) {
+		case OCAP:
+			if(!okforcap[t->etype])
+				goto badcall1;
+			break;
+		case OLEN:
+			if(!okforlen[t->etype])
+				goto badcall1;
+			break;
+		case OREAL:
+		case OIMAG:
+			if(!iscomplex[t->etype])
+				goto badcall1;
+			if(isconst(l, CTCPLX)){
+				r = n;
+				if(n->op == OREAL)
+					n = nodfltconst(&l->val.u.cval->real);
+				else
+					n = nodfltconst(&l->val.u.cval->imag);
+				n->orig = r;
+			}
+			n->type = types[cplxsubtype(t->etype)];
+			goto ret;
+		}
+		// might be constant
+		switch(t->etype) {
+		case TSTRING:
+			if(isconst(l, CTSTR)) {
+				r = nod(OXXX, N, N);
+				nodconst(r, types[TINT], l->val.u.sval->len);
+				r->orig = n;
+				n = r;
+			}
+			break;
+		case TARRAY:
+			if(t->bound < 0) // slice
+				break;
+			if(callrecv(l)) // has call or receive
+				break;
+			r = nod(OXXX, N, N);
+			nodconst(r, types[TINT], t->bound);
+			r->orig = n;
+			n = r;
+			break;
+		}
+		n->type = types[TINT];
+		goto ret;
+
+	case OCOMPLEX:
+		ok |= Erv;
+		if(count(n->list) == 1) {
+			typechecklist(n->list, Efnstruct);
+			t = n->list->n->left->type;
+			if(t->outtuple != 2) {
+				yyerror("invalid operation: complex expects two arguments, %N returns %d results", n->list->n, t->outtuple);
+				goto error;
+			}
+			t = n->list->n->type->type;
+			l = t->nname;
+			r = t->down->nname;
+		} else {
+			if(twoarg(n) < 0)
+				goto error;
+			l = typecheck(&n->left, Erv | (top & Eiota));
+			r = typecheck(&n->right, Erv | (top & Eiota));
+			if(l->type == T || r->type == T)
+				goto error;
+			defaultlit2(&l, &r, 0);
+			if(l->type == T || r->type == T)
+				goto error;
+			n->left = l;
+			n->right = r;
+		}
+		if(!eqtype(l->type, r->type)) {
+			yyerror("invalid operation: %N (mismatched types %T and %T)", n, l->type, r->type);
+			goto error;
+		}
+		switch(l->type->etype) {
+		default:
+			yyerror("invalid operation: %N (arguments have type %T, expected floating-point)", n, l->type, r->type);
+			goto error;
+		case TIDEAL:
+			t = types[TIDEAL];
+			break;
+		case TFLOAT32:
+			t = types[TCOMPLEX64];
+			break;
+		case TFLOAT64:
+			t = types[TCOMPLEX128];
+			break;
+		}
+		if(l->op == OLITERAL && r->op == OLITERAL) {
+			// make it a complex literal
+			r = nodcplxlit(l->val, r->val);
+			r->orig = n;
+			n = r;
+		}
+		n->type = t;
+		goto ret;
+
+	case OCLOSE:
+		if(onearg(n, "%O", n->op) < 0)
+			goto error;
+		typecheck(&n->left, Erv);
+		defaultlit(&n->left, T);
+		l = n->left;
+		if((t = l->type) == T)
+			goto error;
+		if(t->etype != TCHAN) {
+			yyerror("invalid operation: %N (non-chan type %T)", n, t);
+			goto error;
+		}
+		if(!(t->chan & Csend)) {
+			yyerror("invalid operation: %N (cannot close receive-only channel)", n);
+			goto error;
+		}
+		ok |= Etop;
+		goto ret;
+
+	case ODELETE:
+		args = n->list;
+		if(args == nil) {
+			yyerror("missing arguments to delete");
+			goto error;
+		}
+		if(args->next == nil) {
+			yyerror("missing second (key) argument to delete");
+			goto error;
+		}
+		if(args->next->next != nil) {
+			yyerror("too many arguments to delete");
+			goto error;
+		}
+		ok |= Etop;
+		typechecklist(args, Erv);
+		l = args->n;
+		r = args->next->n;
+		if(l->type != T && l->type->etype != TMAP) {
+			yyerror("first argument to delete must be map; have %lT", l->type);
+			goto error;
+		}
+		args->next->n = assignconv(r, l->type->down, "delete");
+		goto ret;
+
+	case OAPPEND:
+		ok |= Erv;
+		args = n->list;
+		if(args == nil) {
+			yyerror("missing arguments to append");
+			goto error;
+		}
+
+		if(count(args) == 1 && !n->isddd)
+			typecheck(&args->n, Erv | Efnstruct);
+		else
+			typechecklist(args, Erv);
+
+		if((t = args->n->type) == T)
+			goto error;
+
+		// Unpack multiple-return result before type-checking.
+		if(istype(t, TSTRUCT) && t->funarg) {
+			t = t->type;
+			if(istype(t, TFIELD))
+				t = t->type;
+		}
+
+		n->type = t;
+		if(!isslice(t)) {
+			if(isconst(args->n, CTNIL)) {
+				yyerror("first argument to append must be typed slice; have untyped nil", t);
+				goto error;
+			}
+			yyerror("first argument to append must be slice; have %lT", t);
+			goto error;
+		}
+
+		if(n->isddd) {
+			if(args->next == nil) {
+				yyerror("cannot use ... on first argument to append");
+				goto error;
+			}
+			if(args->next->next != nil) {
+				yyerror("too many arguments to append");
+				goto error;
+			}
+			if(istype(t->type, TUINT8) && istype(args->next->n->type, TSTRING)) {
+				defaultlit(&args->next->n, types[TSTRING]);
+				goto ret;
+			}
+			args->next->n = assignconv(args->next->n, t->orig, "append");
+			goto ret;
+		}
+		for(args=args->next; args != nil; args=args->next) {
+			if(args->n->type == T)
+				continue;
+			args->n = assignconv(args->n, t->type, "append");
+		}
+		goto ret;
+
+	case OCOPY:
+		ok |= Etop|Erv;
+		args = n->list;
+		if(args == nil || args->next == nil) {
+			yyerror("missing arguments to copy");
+			goto error;
+		}
+		if(args->next->next != nil) {
+			yyerror("too many arguments to copy");
+			goto error;
+		}
+		n->left = args->n;
+		n->right = args->next->n;
+		n->list = nil;
+		n->type = types[TINT];
+		typecheck(&n->left, Erv);
+		typecheck(&n->right, Erv);
+		if(n->left->type == T || n->right->type == T)
+			goto error;
+		defaultlit(&n->left, T);
+		defaultlit(&n->right, T);
+		if(n->left->type == T || n->right->type == T)
+			goto error;
+
+		// copy([]byte, string)
+		if(isslice(n->left->type) && n->right->type->etype == TSTRING) {
+			if(eqtype(n->left->type->type, bytetype))
+				goto ret;
+			yyerror("arguments to copy have different element types: %lT and string", n->left->type);
+			goto error;
+		}
+
+		if(!isslice(n->left->type) || !isslice(n->right->type)) {
+			if(!isslice(n->left->type) && !isslice(n->right->type))
+				yyerror("arguments to copy must be slices; have %lT, %lT", n->left->type, n->right->type);
+			else if(!isslice(n->left->type))
+				yyerror("first argument to copy should be slice; have %lT", n->left->type);
+			else
+				yyerror("second argument to copy should be slice or string; have %lT", n->right->type);
+			goto error;
+		}
+		if(!eqtype(n->left->type->type, n->right->type->type)) {
+			yyerror("arguments to copy have different element types: %lT and %lT", n->left->type, n->right->type);
+			goto error;
+		}
+		goto ret;
+
+	case OCONV:
+	doconv:
+		ok |= Erv;
+		saveorignode(n);
+		typecheck(&n->left, Erv | (top & (Eindir | Eiota)));
+		convlit1(&n->left, n->type, 1);
+		if((t = n->left->type) == T || n->type == T)
+			goto error;
+		if((n->op = convertop(t, n->type, &why)) == 0) {
+			if(!n->diag && !n->type->broke) {
+				yyerror("cannot convert %lN to type %T%s", n->left, n->type, why);
+				n->diag = 1;
+			}
+			n->op = OCONV;
+		}
+		switch(n->op) {
+		case OCONVNOP:
+			if(n->left->op == OLITERAL) {
+				r = nod(OXXX, N, N);
+				n->op = OCONV;
+				n->orig = r;
+				*r = *n;
+				n->op = OLITERAL;
+				n->val = n->left->val;
+			}
+			break;
+		case OSTRARRAYBYTE:
+			// do not use stringtoarraylit.
+			// generated code and compiler memory footprint is better without it.
+			break;
+		case OSTRARRAYRUNE:
+			if(n->left->op == OLITERAL)
+				stringtoarraylit(&n);
+			break;
+		}
+		goto ret;
+
+	case OMAKE:
+		ok |= Erv;
+		args = n->list;
+		if(args == nil) {
+			yyerror("missing argument to make");
+			goto error;
+		}
+		n->list = nil;
+		l = args->n;
+		args = args->next;
+		typecheck(&l, Etype);
+		if((t = l->type) == T)
+			goto error;
+
+		switch(t->etype) {
+		default:
+		badmake:
+			yyerror("cannot make type %T", t);
+			goto error;
+
+		case TARRAY:
+			if(!isslice(t))
+				goto badmake;
+			if(args == nil) {
+				yyerror("missing len argument to make(%T)", t);
+				goto error;
+			}
+			l = args->n;
+			args = args->next;
+			typecheck(&l, Erv);
+			r = N;
+			if(args != nil) {
+				r = args->n;
+				args = args->next;
+				typecheck(&r, Erv);
+			}
+			if(l->type == T || (r && r->type == T))
+				goto error;
+			et = checkmake(t, "len", l) < 0;
+			et |= r && checkmake(t, "cap", r) < 0;
+			if(et)
+				goto error;
+			if(isconst(l, CTINT) && r && isconst(r, CTINT) && mpcmpfixfix(l->val.u.xval, r->val.u.xval) > 0) {
+				yyerror("len larger than cap in make(%T)", t);
+				goto error;
+			}
+			n->left = l;
+			n->right = r;
+			n->op = OMAKESLICE;
+			break;
+
+		case TMAP:
+			if(args != nil) {
+				l = args->n;
+				args = args->next;
+				typecheck(&l, Erv);
+				defaultlit(&l, types[TINT]);
+				if(l->type == T)
+					goto error;
+				if(checkmake(t, "size", l) < 0)
+					goto error;
+				n->left = l;
+			} else
+				n->left = nodintconst(0);
+			n->op = OMAKEMAP;
+			break;
+
+		case TCHAN:
+			l = N;
+			if(args != nil) {
+				l = args->n;
+				args = args->next;
+				typecheck(&l, Erv);
+				defaultlit(&l, types[TINT]);
+				if(l->type == T)
+					goto error;
+				if(checkmake(t, "buffer", l) < 0)
+					goto error;
+				n->left = l;
+			} else
+				n->left = nodintconst(0);
+			n->op = OMAKECHAN;
+			break;
+		}
+		if(args != nil) {
+			yyerror("too many arguments to make(%T)", t);
+			n->op = OMAKE;
+			goto error;
+		}
+		n->type = t;
+		goto ret;
+
+	case ONEW:
+		ok |= Erv;
+		args = n->list;
+		if(args == nil) {
+			yyerror("missing argument to new");
+			goto error;
+		}
+		l = args->n;
+		typecheck(&l, Etype);
+		if((t = l->type) == T)
+			goto error;
+		if(args->next != nil) {
+			yyerror("too many arguments to new(%T)", t);
+			goto error;
+		}
+		n->left = l;
+		n->type = ptrto(t);
+		goto ret;
+
+	case OPRINT:
+	case OPRINTN:
+		ok |= Etop;
+		typechecklist(n->list, Erv | Eindir);  // Eindir: address does not escape
+		for(args=n->list; args; args=args->next) {
+			// Special case for print: int constant is int64, not int.
+			if(isconst(args->n, CTINT))
+				defaultlit(&args->n, types[TINT64]);
+			else
+				defaultlit(&args->n, T);
+		}
+		goto ret;
+
+	case OPANIC:
+		ok |= Etop;
+		if(onearg(n, "panic") < 0)
+			goto error;
+		typecheck(&n->left, Erv);
+		defaultlit(&n->left, types[TINTER]);
+		if(n->left->type == T)
+			goto error;
+		goto ret;
+	
+	case ORECOVER:
+		ok |= Erv|Etop;
+		if(n->list != nil) {
+			yyerror("too many arguments to recover");
+			goto error;
+		}
+		n->type = types[TINTER];
+		goto ret;
+
+	case OCLOSURE:
+		ok |= Erv;
+		typecheckclosure(n, top);
+		if(n->type == T)
+			goto error;
+		goto ret;
+	
+	case OITAB:
+		ok |= Erv;
+		typecheck(&n->left, Erv);
+		if((t = n->left->type) == T)
+			goto error;
+		if(t->etype != TINTER)
+			fatal("OITAB of %T", t);
+		n->type = ptrto(types[TUINTPTR]);
+		goto ret;
+
+	case OSPTR:
+		ok |= Erv;
+		typecheck(&n->left, Erv);
+		if((t = n->left->type) == T)
+			goto error;
+		if(!isslice(t) && t->etype != TSTRING)
+			fatal("OSPTR of %T", t);
+		if(t->etype == TSTRING)
+			n->type = ptrto(types[TUINT8]);
+		else
+			n->type = ptrto(t->type);
+		goto ret;
+
+	case OCLOSUREVAR:
+		ok |= Erv;
+		goto ret;
+	
+	case OCFUNC:
+		ok |= Erv;
+		typecheck(&n->left, Erv);
+		n->type = types[TUINTPTR];
+		goto ret;
+
+	case OCONVNOP:
+		ok |= Erv;
+		typecheck(&n->left, Erv);
+		goto ret;
+
+	/*
+	 * statements
+	 */
+	case OAS:
+		ok |= Etop;
+		typecheckas(n);
+		goto ret;
+
+	case OAS2:
+		ok |= Etop;
+		typecheckas2(n);
+		goto ret;
+
+	case OBREAK:
+	case OCONTINUE:
+	case ODCL:
+	case OEMPTY:
+	case OGOTO:
+	case OLABEL:
+	case OXFALL:
+	case OVARKILL:
+		ok |= Etop;
+		goto ret;
+
+	case ODEFER:
+		ok |= Etop;
+		typecheck(&n->left, Etop|Erv);
+		if(!n->left->diag)
+			checkdefergo(n);
+		goto ret;
+
+	case OPROC:
+		ok |= Etop;
+		typecheck(&n->left, Etop|Eproc|Erv);
+		checkdefergo(n);
+		goto ret;
+
+	case OFOR:
+		ok |= Etop;
+		typechecklist(n->ninit, Etop);
+		typecheck(&n->ntest, Erv);
+		if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL)
+			yyerror("non-bool %lN used as for condition", n->ntest);
+		typecheck(&n->nincr, Etop);
+		typechecklist(n->nbody, Etop);
+		goto ret;
+
+	case OIF:
+		ok |= Etop;
+		typechecklist(n->ninit, Etop);
+		typecheck(&n->ntest, Erv);
+		if(n->ntest != N && (t = n->ntest->type) != T && t->etype != TBOOL)
+			yyerror("non-bool %lN used as if condition", n->ntest);
+		typechecklist(n->nbody, Etop);
+		typechecklist(n->nelse, Etop);
+		goto ret;
+
+	case ORETURN:
+		ok |= Etop;
+		if(count(n->list) == 1)
+			typechecklist(n->list, Erv | Efnstruct);
+		else
+			typechecklist(n->list, Erv);
+		if(curfn == N) {
+			yyerror("return outside function");
+			goto error;
+		}
+		if(curfn->type->outnamed && n->list == nil)
+			goto ret;
+		typecheckaste(ORETURN, nil, 0, getoutargx(curfn->type), n->list, "return argument");
+		goto ret;
+	
+	case ORETJMP:
+		ok |= Etop;
+		goto ret;
+
+	case OSELECT:
+		ok |= Etop;
+		typecheckselect(n);
+		goto ret;
+
+	case OSWITCH:
+		ok |= Etop;
+		typecheckswitch(n);
+		goto ret;
+
+	case ORANGE:
+		ok |= Etop;
+		typecheckrange(n);
+		goto ret;
+
+	case OTYPESW:
+		yyerror("use of .(type) outside type switch");
+		goto error;
+
+	case OXCASE:
+		ok |= Etop;
+		typechecklist(n->list, Erv);
+		typechecklist(n->nbody, Etop);
+		goto ret;
+
+	case ODCLFUNC:
+		ok |= Etop;
+		typecheckfunc(n);
+		goto ret;
+
+	case ODCLCONST:
+		ok |= Etop;
+		typecheck(&n->left, Erv);
+		goto ret;
+
+	case ODCLTYPE:
+		ok |= Etop;
+		typecheck(&n->left, Etype);
+		if(!incannedimport)
+			checkwidth(n->left->type);
+		goto ret;
+	}
+
+ret:
+	t = n->type;
+	if(t && !t->funarg && n->op != OTYPE) {
+		switch(t->etype) {
+		case TFUNC:	// might have TANY; wait until its called
+		case TANY:
+		case TFORW:
+		case TIDEAL:
+		case TNIL:
+		case TBLANK:
+			break;
+		default:
+			checkwidth(t);
+		}
+	}
+
+	if(safemode && !incannedimport && !importpkg && !compiling_wrappers && t && t->etype == TUNSAFEPTR)
+		yyerror("cannot use unsafe.Pointer");
+
+	evconst(n);
+	if(n->op == OTYPE && !(top & Etype)) {
+		yyerror("type %T is not an expression", n->type);
+		goto error;
+	}
+	if((top & (Erv|Etype)) == Etype && n->op != OTYPE) {
+		yyerror("%N is not a type", n);
+		goto error;
+	}
+	// TODO(rsc): simplify
+	if((top & (Ecall|Erv|Etype)) && !(top & Etop) && !(ok & (Erv|Etype|Ecall))) {
+		yyerror("%N used as value", n);
+		goto error;
+	}
+	if((top & Etop) && !(top & (Ecall|Erv|Etype)) && !(ok & Etop)) {
+		if(n->diag == 0) {
+			yyerror("%N evaluated but not used", n);
+			n->diag = 1;
+		}
+		goto error;
+	}
+
+	/* TODO
+	if(n->type == T)
+		fatal("typecheck nil type");
+	*/
+	goto out;
+
+badcall1:
+	yyerror("invalid argument %lN for %O", n->left, n->op);
+	goto error;
+
+error:
+	n->type = T;
+
+out:
+	*np = n;
+}
+
+static int
+checksliceindex(Node *l, Node *r, Type *tp)
+{
+	Type *t;
+
+	if((t = r->type) == T)
+		return -1;
+	if(!isint[t->etype]) {
+		yyerror("invalid slice index %N (type %T)", r, t);
+		return -1;
+	}
+	if(r->op == OLITERAL) {
+		if(mpgetfix(r->val.u.xval) < 0) {
+			yyerror("invalid slice index %N (index must be non-negative)", r);
+			return -1;
+		} else if(tp != nil && tp->bound > 0 && mpgetfix(r->val.u.xval) > tp->bound) {
+			yyerror("invalid slice index %N (out of bounds for %d-element array)", r, tp->bound);
+			return -1;
+		} else if(isconst(l, CTSTR) && mpgetfix(r->val.u.xval) > l->val.u.sval->len) {
+			yyerror("invalid slice index %N (out of bounds for %d-byte string)", r, l->val.u.sval->len);
+			return -1;
+		} else if(mpcmpfixfix(r->val.u.xval, maxintval[TINT]) > 0) {
+			yyerror("invalid slice index %N (index too large)", r);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int
+checksliceconst(Node *lo, Node *hi)
+{
+	if(lo != N && hi != N && lo->op == OLITERAL && hi->op == OLITERAL
+	   && mpcmpfixfix(lo->val.u.xval, hi->val.u.xval) > 0) {
+		yyerror("invalid slice index: %N > %N", lo, hi);
+		return -1;
+	}
+	return 0;
+}
+
+static void
+checkdefergo(Node *n)
+{
+	char *what;
+	
+	what = "defer";
+	if(n->op == OPROC)
+		what = "go";
+
+	switch(n->left->op) {
+	case OCALLINTER:
+	case OCALLMETH:
+	case OCALLFUNC:
+	case OCLOSE:
+	case OCOPY:
+	case ODELETE:
+	case OPANIC:
+	case OPRINT:
+	case OPRINTN:
+	case ORECOVER:
+		// ok
+		break;
+	case OAPPEND:
+	case OCAP:
+	case OCOMPLEX:
+	case OIMAG:
+	case OLEN:
+	case OMAKE:
+	case OMAKESLICE:
+	case OMAKECHAN:
+	case OMAKEMAP:
+	case ONEW:
+	case OREAL:
+	case OLITERAL: // conversion or unsafe.Alignof, Offsetof, Sizeof
+		if(n->left->orig != N && n->left->orig->op == OCONV)
+			goto conv;
+		yyerror("%s discards result of %N", what, n->left);
+		break;
+	default:
+	conv:
+		// type is broken or missing, most likely a method call on a broken type
+		// we will warn about the broken type elsewhere. no need to emit a potentially confusing error
+		if(n->left->type == T || n->left->type->broke)
+			break;
+
+		if(!n->diag) {
+			// The syntax made sure it was a call, so this must be
+			// a conversion.
+			n->diag = 1;
+			yyerror("%s requires function call, not conversion", what);
+		}
+		break;
+	}
+}
+
+static void
+implicitstar(Node **nn)
+{
+	Type *t;
+	Node *n;
+
+	// insert implicit * if needed for fixed array
+	n = *nn;
+	t = n->type;
+	if(t == T || !isptr[t->etype])
+		return;
+	t = t->type;
+	if(t == T)
+		return;
+	if(!isfixedarray(t))
+		return;
+	n = nod(OIND, n, N);
+	n->implicit = 1;
+	typecheck(&n, Erv);
+	*nn = n;
+}
+
+static int
+onearg(Node *n, char *f, ...)
+{
+	va_list arg;
+	char *p;
+
+	if(n->left != N)
+		return 0;
+	if(n->list == nil) {
+		va_start(arg, f);
+		p = vsmprint(f, arg);
+		va_end(arg);
+		yyerror("missing argument to %s: %N", p, n);
+		return -1;
+	}
+	if(n->list->next != nil) {
+		va_start(arg, f);
+		p = vsmprint(f, arg);
+		va_end(arg);
+		yyerror("too many arguments to %s: %N", p, n);
+		n->left = n->list->n;
+		n->list = nil;
+		return -1;
+	}
+	n->left = n->list->n;
+	n->list = nil;
+	return 0;
+}
+
+static int
+twoarg(Node *n)
+{
+	if(n->left != N)
+		return 0;
+	if(n->list == nil) {
+		yyerror("missing argument to %O - %N", n->op, n);
+		return -1;
+	}
+	n->left = n->list->n;
+	if(n->list->next == nil) {
+		yyerror("missing argument to %O - %N", n->op, n);
+		n->list = nil;
+		return -1;
+	}
+	if(n->list->next->next != nil) {
+		yyerror("too many arguments to %O - %N", n->op, n);
+		n->list = nil;
+		return -1;
+	}
+	n->right = n->list->next->n;
+	n->list = nil;
+	return 0;
+}
+
+static Type*
+lookdot1(Node *errnode, Sym *s, Type *t, Type *f, int dostrcmp)
+{
+	Type *r;
+
+	r = T;
+	for(; f!=T; f=f->down) {
+		if(dostrcmp && strcmp(f->sym->name, s->name) == 0)
+			return f;
+		if(f->sym != s)
+			continue;
+		if(r != T) {
+			if(errnode)
+				yyerror("ambiguous selector %N", errnode);
+			else if(isptr[t->etype])
+				yyerror("ambiguous selector (%T).%S", t, s);
+			else
+				yyerror("ambiguous selector %T.%S", t, s);
+			break;
+		}
+		r = f;
+	}
+	return r;
+}
+
+static int
+looktypedot(Node *n, Type *t, int dostrcmp)
+{
+	Type *f1, *f2;
+	Sym *s;
+	
+	s = n->right->sym;
+
+	if(t->etype == TINTER) {
+		f1 = lookdot1(n, s, t, t->type, dostrcmp);
+		if(f1 == T)
+			return 0;
+
+		n->right = methodname(n->right, t);
+		n->xoffset = f1->width;
+		n->type = f1->type;
+		n->op = ODOTINTER;
+		return 1;
+	}
+
+	// Find the base type: methtype will fail if t
+	// is not of the form T or *T.
+	f2 = methtype(t, 0);
+	if(f2 == T)
+		return 0;
+
+	expandmeth(f2);
+	f2 = lookdot1(n, s, f2, f2->xmethod, dostrcmp);
+	if(f2 == T)
+		return 0;
+
+	// disallow T.m if m requires *T receiver
+	if(isptr[getthisx(f2->type)->type->type->etype]
+	&& !isptr[t->etype]
+	&& f2->embedded != 2
+	&& !isifacemethod(f2->type)) {
+		yyerror("invalid method expression %N (needs pointer receiver: (*%T).%hS)", n, t, f2->sym);
+		return 0;
+	}
+
+	n->right = methodname(n->right, t);
+	n->xoffset = f2->width;
+	n->type = f2->type;
+	n->op = ODOTMETH;
+	return 1;
+}
+
+static Type*
+derefall(Type* t)
+{
+	while(t && t->etype == tptr)
+		t = t->type;
+	return t;
+}
+
+static int
+lookdot(Node *n, Type *t, int dostrcmp)
+{
+	Type *f1, *f2, *tt, *rcvr;
+	Sym *s;
+
+	s = n->right->sym;
+
+	dowidth(t);
+	f1 = T;
+	if(t->etype == TSTRUCT || t->etype == TINTER)
+		f1 = lookdot1(n, s, t, t->type, dostrcmp);
+
+	f2 = T;
+	if(n->left->type == t || n->left->type->sym == S) {
+		f2 = methtype(t, 0);
+		if(f2 != T) {
+			// Use f2->method, not f2->xmethod: adddot has
+			// already inserted all the necessary embedded dots.
+			f2 = lookdot1(n, s, f2, f2->method, dostrcmp);
+		}
+	}
+
+	if(f1 != T) {
+		if(f2 != T)
+			yyerror("%S is both field and method",
+				n->right->sym);
+		if(f1->width == BADWIDTH)
+			fatal("lookdot badwidth %T %p", f1, f1);
+		n->xoffset = f1->width;
+		n->type = f1->type;
+		n->paramfld = f1;
+		if(t->etype == TINTER) {
+			if(isptr[n->left->type->etype]) {
+				n->left = nod(OIND, n->left, N);	// implicitstar
+				n->left->implicit = 1;
+				typecheck(&n->left, Erv);
+			}
+			n->op = ODOTINTER;
+		}
+		return 1;
+	}
+
+	if(f2 != T) {
+		tt = n->left->type;
+		dowidth(tt);
+		rcvr = getthisx(f2->type)->type->type;
+		if(!eqtype(rcvr, tt)) {
+			if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) {
+				checklvalue(n->left, "call pointer method on");
+				n->left = nod(OADDR, n->left, N);
+				n->left->implicit = 1;
+				typecheck(&n->left, Etype|Erv);
+			} else if(tt->etype == tptr && rcvr->etype != tptr && eqtype(tt->type, rcvr)) {
+				n->left = nod(OIND, n->left, N);
+				n->left->implicit = 1;
+				typecheck(&n->left, Etype|Erv);
+			} else if(tt->etype == tptr && tt->type->etype == tptr && eqtype(derefall(tt), derefall(rcvr))) {
+				yyerror("calling method %N with receiver %lN requires explicit dereference", n->right, n->left);
+				while(tt->etype == tptr) {
+					// Stop one level early for method with pointer receiver.
+					if(rcvr->etype == tptr && tt->type->etype != tptr)
+						break;
+					n->left = nod(OIND, n->left, N);
+					n->left->implicit = 1;
+					typecheck(&n->left, Etype|Erv);
+					tt = tt->type;
+				}
+			} else {
+				fatal("method mismatch: %T for %T", rcvr, tt);
+			}
+		}
+		n->right = methodname(n->right, n->left->type);
+		n->xoffset = f2->width;
+		n->type = f2->type;
+//		print("lookdot found [%p] %T\n", f2->type, f2->type);
+		n->op = ODOTMETH;
+		return 1;
+	}
+
+	return 0;
+}
+
+static int
+nokeys(NodeList *l)
+{
+	for(; l; l=l->next)
+		if(l->n->op == OKEY)
+			return 0;
+	return 1;
+}
+
+static int
+hasddd(Type *t)
+{
+	Type *tl;
+
+	for(tl=t->type; tl; tl=tl->down) {
+		if(tl->isddd)
+			return 1;
+	}
+	return 0;
+}
+
+static int
+downcount(Type *t)
+{
+	Type *tl;
+	int n;
+
+	n = 0;
+	for(tl=t->type; tl; tl=tl->down) {
+		n++;
+	}
+	return n;
+}
+
+/*
+ * typecheck assignment: type list = expression list
+ */
+static void
+typecheckaste(int op, Node *call, int isddd, Type *tstruct, NodeList *nl, char *desc)
+{
+	Type *t, *tl, *tn;
+	Node *n;
+	int lno;
+	char *why;
+	int n1, n2;
+
+	lno = lineno;
+
+	if(tstruct->broke)
+		goto out;
+
+	n = N;
+	if(nl != nil && nl->next == nil && (n = nl->n)->type != T)
+	if(n->type->etype == TSTRUCT && n->type->funarg) {
+		if(!hasddd(tstruct)) {
+			n1 = downcount(tstruct);
+			n2 = downcount(n->type);
+			if(n2 > n1)
+				goto toomany;
+			if(n2 < n1)
+				goto notenough;
+		}
+		
+		tn = n->type->type;
+		for(tl=tstruct->type; tl; tl=tl->down) {
+			if(tl->isddd) {
+				for(; tn; tn=tn->down) {
+					if(assignop(tn->type, tl->type->type, &why) == 0) {
+						if(call != N)
+							yyerror("cannot use %T as type %T in argument to %N%s", tn->type, tl->type->type, call, why);
+						else
+							yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type->type, desc, why);
+					}
+				}
+				goto out;
+			}
+			if(tn == T)
+				goto notenough;
+			if(assignop(tn->type, tl->type, &why) == 0) {
+				if(call != N)
+					yyerror("cannot use %T as type %T in argument to %N%s", tn->type, tl->type, call, why);
+				else
+					yyerror("cannot use %T as type %T in %s%s", tn->type, tl->type, desc, why);
+			}
+			tn = tn->down;
+		}
+		if(tn != T)
+			goto toomany;
+		goto out;
+	}
+
+	n1 = downcount(tstruct);
+	n2 = count(nl);
+	if(!hasddd(tstruct)) {
+		if(n2 > n1)
+			goto toomany;
+		if(n2 < n1)
+			goto notenough;
+	}
+	else {
+		if(!isddd) {
+			if(n2 < n1-1)
+				goto notenough;
+		} else {
+			if(n2 > n1)
+				goto toomany;
+			if(n2 < n1)
+				goto notenough;
+		}
+	}
+
+	for(tl=tstruct->type; tl; tl=tl->down) {
+		t = tl->type;
+		if(tl->isddd) {
+			if(isddd) {
+				if(nl == nil)
+					goto notenough;
+				if(nl->next != nil)
+					goto toomany;
+				n = nl->n;
+				setlineno(n);
+				if(n->type != T)
+					nl->n = assignconv(n, t, desc);
+				goto out;
+			}
+			for(; nl; nl=nl->next) {
+				n = nl->n;
+				setlineno(nl->n);
+				if(n->type != T)
+					nl->n = assignconv(n, t->type, desc);
+			}
+			goto out;
+		}
+		if(nl == nil)
+			goto notenough;
+		n = nl->n;
+		setlineno(n);
+		if(n->type != T)
+			nl->n = assignconv(n, t, desc);
+		nl = nl->next;
+	}
+	if(nl != nil)
+		goto toomany;
+	if(isddd) {
+		if(call != N)
+			yyerror("invalid use of ... in call to %N", call);
+		else
+			yyerror("invalid use of ... in %O", op);
+	}
+
+out:
+	lineno = lno;
+	return;
+
+notenough:
+	if(n == N || !n->diag) {
+		if(call != N)
+			yyerror("not enough arguments in call to %N", call);
+		else
+			yyerror("not enough arguments to %O", op);
+		if(n != N)
+			n->diag = 1;
+	}
+	goto out;
+
+toomany:
+	if(call != N)
+		yyerror("too many arguments in call to %N", call);
+	else
+		yyerror("too many arguments to %O", op);
+	goto out;
+}
+
+/*
+ * type check composite
+ */
+
+static void
+fielddup(Node *n, Node *hash[], ulong nhash)
+{
+	uint h;
+	char *s;
+	Node *a;
+
+	if(n->op != ONAME)
+		fatal("fielddup: not ONAME");
+	s = n->sym->name;
+	h = stringhash(s)%nhash;
+	for(a=hash[h]; a!=N; a=a->ntest) {
+		if(strcmp(a->sym->name, s) == 0) {
+			yyerror("duplicate field name in struct literal: %s", s);
+			return;
+		}
+	}
+	n->ntest = hash[h];
+	hash[h] = n;
+}
+
+static void
+keydup(Node *n, Node *hash[], ulong nhash)
+{
+	uint h;
+	ulong b;
+	double d;
+	int i;
+	Node *a, *orign;
+	Node cmp;
+	char *s;
+
+	orign = n;
+	if(n->op == OCONVIFACE)
+		n = n->left;
+	evconst(n);
+	if(n->op != OLITERAL)
+		return;	// we dont check variables
+
+	switch(n->val.ctype) {
+	default:	// unknown, bool, nil
+		b = 23;
+		break;
+	case CTINT:
+	case CTRUNE:
+		b = mpgetfix(n->val.u.xval);
+		break;
+	case CTFLT:
+		d = mpgetflt(n->val.u.fval);
+		s = (char*)&d;
+		b = 0;
+		for(i=sizeof(d); i>0; i--)
+			b = b*PRIME1 + *s++;
+		break;
+	case CTSTR:
+		b = 0;
+		s = n->val.u.sval->s;
+		for(i=n->val.u.sval->len; i>0; i--)
+			b = b*PRIME1 + *s++;
+		break;
+	}
+
+	h = b%nhash;
+	memset(&cmp, 0, sizeof(cmp));
+	for(a=hash[h]; a!=N; a=a->ntest) {
+		cmp.op = OEQ;
+		cmp.left = n;
+		b = 0;
+		if(a->op == OCONVIFACE && orign->op == OCONVIFACE) {
+			if(eqtype(a->left->type, n->type)) {
+				cmp.right = a->left;
+				evconst(&cmp);
+				b = cmp.val.u.bval;
+			}
+		} else if(eqtype(a->type, n->type)) {
+			cmp.right = a;
+			evconst(&cmp);
+			b = cmp.val.u.bval;
+		}
+		if(b) {
+			yyerror("duplicate key %N in map literal", n);
+			return;
+		}
+	}
+	orign->ntest = hash[h];
+	hash[h] = orign;
+}
+
+static void
+indexdup(Node *n, Node *hash[], ulong nhash)
+{
+	uint h;
+	Node *a;
+	ulong b, c;
+
+	if(n->op != OLITERAL)
+		fatal("indexdup: not OLITERAL");
+
+	b = mpgetfix(n->val.u.xval);
+	h = b%nhash;
+	for(a=hash[h]; a!=N; a=a->ntest) {
+		c = mpgetfix(a->val.u.xval);
+		if(b == c) {
+			yyerror("duplicate index in array literal: %ld", b);
+			return;
+		}
+	}
+	n->ntest = hash[h];
+	hash[h] = n;
+}
+
+static int
+prime(ulong h, ulong sr)
+{
+	ulong n;
+
+	for(n=3; n<=sr; n+=2)
+		if(h%n == 0)
+			return 0;
+	return 1;
+}
+
+static ulong
+inithash(Node *n, Node ***hash, Node **autohash, ulong nautohash)
+{
+	ulong h, sr;
+	NodeList *ll;
+	int i;
+
+	// count the number of entries
+	h = 0;
+	for(ll=n->list; ll; ll=ll->next)
+		h++;
+
+	// if the auto hash table is
+	// large enough use it.
+	if(h <= nautohash) {
+		*hash = autohash;
+		memset(*hash, 0, nautohash * sizeof(**hash));
+		return nautohash;
+	}
+
+	// make hash size odd and 12% larger than entries
+	h += h/8;
+	h |= 1;
+
+	// calculate sqrt of h
+	sr = h/2;
+	for(i=0; i<5; i++)
+		sr = (sr + h/sr)/2;
+
+	// check for primeality
+	while(!prime(h, sr))
+		h += 2;
+
+	// build and return a throw-away hash table
+	*hash = mal(h * sizeof(**hash));
+	memset(*hash, 0, h * sizeof(**hash));
+	return h;
+}
+
+static int
+iscomptype(Type *t)
+{
+	switch(t->etype) {
+	case TARRAY:
+	case TSTRUCT:
+	case TMAP:
+		return 1;
+	case TPTR32:
+	case TPTR64:
+		switch(t->type->etype) {
+		case TARRAY:
+		case TSTRUCT:
+		case TMAP:
+			return 1;
+		}
+		break;
+	}
+	return 0;
+}
+
+static void
+pushtype(Node *n, Type *t)
+{
+	if(n == N || n->op != OCOMPLIT || !iscomptype(t))
+		return;
+	
+	if(n->right == N) {
+		n->right = typenod(t);
+		n->implicit = 1;  // don't print
+		n->right->implicit = 1;  // * is okay
+	}
+	else if(debug['s']) {
+		typecheck(&n->right, Etype);
+		if(n->right->type != T && eqtype(n->right->type, t))
+			print("%lL: redundant type: %T\n", n->lineno, t);
+	}
+}
+
+static void
+typecheckcomplit(Node **np)
+{
+	int bad, i, nerr;
+	int64 len;
+	Node *l, *n, *norig, *r, **hash;
+	NodeList *ll;
+	Type *t, *f;
+	Sym *s, *s1;
+	int32 lno;
+	ulong nhash;
+	Node *autohash[101];
+
+	n = *np;
+	lno = lineno;
+
+	if(n->right == N) {
+		if(n->list != nil)
+			setlineno(n->list->n);
+		yyerror("missing type in composite literal");
+		goto error;
+	}
+
+	// Save original node (including n->right)
+	norig = nod(n->op, N, N);
+	*norig = *n;
+
+	setlineno(n->right);
+	l = typecheck(&n->right /* sic */, Etype|Ecomplit);
+	if((t = l->type) == T)
+		goto error;
+	nerr = nerrors;
+	n->type = t;
+
+	if(isptr[t->etype]) {
+		// For better or worse, we don't allow pointers as the composite literal type,
+		// except when using the &T syntax, which sets implicit on the OIND.
+		if(!n->right->implicit) {
+			yyerror("invalid pointer type %T for composite literal (use &%T instead)", t, t->type);
+			goto error;
+		}
+		// Also, the underlying type must be a struct, map, slice, or array.
+		if(!iscomptype(t)) {
+			yyerror("invalid pointer type %T for composite literal", t);
+			goto error;
+		}
+		t = t->type;
+	}
+
+	switch(t->etype) {
+	default:
+		yyerror("invalid type for composite literal: %T", t);
+		n->type = T;
+		break;
+
+	case TARRAY:
+		nhash = inithash(n, &hash, autohash, nelem(autohash));
+
+		len = 0;
+		i = 0;
+		for(ll=n->list; ll; ll=ll->next) {
+			l = ll->n;
+			setlineno(l);
+			if(l->op != OKEY) {
+				l = nod(OKEY, nodintconst(i), l);
+				l->left->type = types[TINT];
+				l->left->typecheck = 1;
+				ll->n = l;
+			}
+
+			typecheck(&l->left, Erv);
+			evconst(l->left);
+			i = nonnegconst(l->left);
+			if(i < 0 && !l->left->diag) {
+				yyerror("array index must be non-negative integer constant");
+				l->left->diag = 1;
+				i = -(1<<30);	// stay negative for a while
+			}
+			if(i >= 0)
+				indexdup(l->left, hash, nhash);
+			i++;
+			if(i > len) {
+				len = i;
+				if(t->bound >= 0 && len > t->bound) {
+					setlineno(l);
+					yyerror("array index %lld out of bounds [0:%lld]", len-1, t->bound);
+					t->bound = -1;	// no more errors
+				}
+			}
+
+			r = l->right;
+			pushtype(r, t->type);
+			typecheck(&r, Erv);
+			defaultlit(&r, t->type);
+			l->right = assignconv(r, t->type, "array element");
+		}
+		if(t->bound == -100)
+			t->bound = len;
+		if(t->bound < 0)
+			n->right = nodintconst(len);
+		n->op = OARRAYLIT;
+		break;
+
+	case TMAP:
+		nhash = inithash(n, &hash, autohash, nelem(autohash));
+
+		for(ll=n->list; ll; ll=ll->next) {
+			l = ll->n;
+			setlineno(l);
+			if(l->op != OKEY) {
+				typecheck(&ll->n, Erv);
+				yyerror("missing key in map literal");
+				continue;
+			}
+
+			typecheck(&l->left, Erv);
+			defaultlit(&l->left, t->down);
+			l->left = assignconv(l->left, t->down, "map key");
+			if (l->left->op != OCONV)
+				keydup(l->left, hash, nhash);
+
+			r = l->right;
+			pushtype(r, t->type);
+			typecheck(&r, Erv);
+			defaultlit(&r, t->type);
+			l->right = assignconv(r, t->type, "map value");
+		}
+		n->op = OMAPLIT;
+		break;
+
+	case TSTRUCT:
+		bad = 0;
+		if(n->list != nil && nokeys(n->list)) {
+			// simple list of variables
+			f = t->type;
+			for(ll=n->list; ll; ll=ll->next) {
+				setlineno(ll->n);
+				typecheck(&ll->n, Erv);
+				if(f == nil) {
+					if(!bad++)
+						yyerror("too many values in struct initializer");
+					continue;
+				}
+				s = f->sym;
+				if(s != nil && !exportname(s->name) && s->pkg != localpkg)
+					yyerror("implicit assignment of unexported field '%s' in %T literal", s->name, t);
+				// No pushtype allowed here.  Must name fields for that.
+				ll->n = assignconv(ll->n, f->type, "field value");
+				ll->n = nod(OKEY, newname(f->sym), ll->n);
+				ll->n->left->type = f;
+				ll->n->left->typecheck = 1;
+				f = f->down;
+			}
+			if(f != nil)
+				yyerror("too few values in struct initializer");
+		} else {
+			nhash = inithash(n, &hash, autohash, nelem(autohash));
+
+			// keyed list
+			for(ll=n->list; ll; ll=ll->next) {
+				l = ll->n;
+				setlineno(l);
+				if(l->op != OKEY) {
+					if(!bad++)
+						yyerror("mixture of field:value and value initializers");
+					typecheck(&ll->n, Erv);
+					continue;
+				}
+				s = l->left->sym;
+				if(s == S) {
+					yyerror("invalid field name %N in struct initializer", l->left);
+					typecheck(&l->right, Erv);
+					continue;
+				}
+
+				// Sym might have resolved to name in other top-level
+				// package, because of import dot.  Redirect to correct sym
+				// before we do the lookup.
+				if(s->pkg != localpkg && exportname(s->name)) {
+					s1 = lookup(s->name);
+					if(s1->origpkg == s->pkg)
+						s = s1;
+				}
+				f = lookdot1(nil, s, t, t->type, 0);
+				if(f == nil) {
+					yyerror("unknown %T field '%S' in struct literal", t, s);
+					continue;
+				}
+				l->left = newname(s);
+				l->left->typecheck = 1;
+				l->left->type = f;
+				s = f->sym;
+				fielddup(newname(s), hash, nhash);
+				r = l->right;
+				// No pushtype allowed here.  Tried and rejected.
+				typecheck(&r, Erv);
+				l->right = assignconv(r, f->type, "field value");
+			}
+		}
+		n->op = OSTRUCTLIT;
+		break;
+	}
+	if(nerr != nerrors)
+		goto error;
+	
+	n->orig = norig;
+	if(isptr[n->type->etype]) {
+		n = nod(OPTRLIT, n, N);
+		n->typecheck = 1;
+		n->type = n->left->type;
+		n->left->type = t;
+		n->left->typecheck = 1;
+	}
+
+	n->orig = norig;
+	*np = n;
+	lineno = lno;
+	return;
+
+error:
+	n->type = T;
+	*np = n;
+	lineno = lno;
+}
+
+/*
+ * lvalue etc
+ */
+int
+islvalue(Node *n)
+{
+	switch(n->op) {
+	case OINDEX:
+		if(isfixedarray(n->left->type))
+			return islvalue(n->left);
+		if(n->left->type != T && n->left->type->etype == TSTRING)
+			return 0;
+		// fall through
+	case OIND:
+	case ODOTPTR:
+	case OCLOSUREVAR:
+		return 1;
+	case ODOT:
+		return islvalue(n->left);
+	case ONAME:
+		if(n->class == PFUNC)
+			return 0;
+		return 1;
+	}
+	return 0;
+}
+
+static void
+checklvalue(Node *n, char *verb)
+{
+	if(!islvalue(n))
+		yyerror("cannot %s %N", verb, n);
+}
+
+static void
+checkassign(Node *n)
+{
+	if(islvalue(n))
+		return;
+	if(n->op == OINDEXMAP) {
+		n->etype = 1;
+		return;
+	}
+
+	// have already complained about n being undefined
+	if(n->op == ONONAME)
+		return;
+
+	yyerror("cannot assign to %N", n);
+}
+
+static void
+checkassignlist(NodeList *l)
+{
+	for(; l; l=l->next)
+		checkassign(l->n);
+}
+
+// Check whether l and r are the same side effect-free expression,
+// so that it is safe to reuse one instead of computing both.
+static int
+samesafeexpr(Node *l, Node *r)
+{
+	if(l->op != r->op || !eqtype(l->type, r->type))
+		return 0;
+	
+	switch(l->op) {
+	case ONAME:
+	case OCLOSUREVAR:
+		return l == r;
+	
+	case ODOT:
+	case ODOTPTR:
+		return l->right != nil && r->right != nil && l->right->sym == r->right->sym && samesafeexpr(l->left, r->left);
+	
+	case OIND:
+		return samesafeexpr(l->left, r->left);
+	
+	case OINDEX:
+		return samesafeexpr(l->left, r->left) && samesafeexpr(l->right, r->right);
+	}
+	
+	return 0;
+}
+
+/*
+ * type check assignment.
+ * if this assignment is the definition of a var on the left side,
+ * fill in the var's type.
+ */
+
+static void
+typecheckas(Node *n)
+{
+	// delicate little dance.
+	// the definition of n may refer to this assignment
+	// as its definition, in which case it will call typecheckas.
+	// in that case, do not call typecheck back, or it will cycle.
+	// if the variable has a type (ntype) then typechecking
+	// will not look at defn, so it is okay (and desirable,
+	// so that the conversion below happens).
+	n->left = resolve(n->left);
+	if(n->left->defn != n || n->left->ntype)
+		typecheck(&n->left, Erv | Easgn);
+
+	checkassign(n->left);
+	typecheck(&n->right, Erv);
+	if(n->right && n->right->type != T) {
+		if(n->left->type != T)
+			n->right = assignconv(n->right, n->left->type, "assignment");
+	}
+	if(n->left->defn == n && n->left->ntype == N) {
+		defaultlit(&n->right, T);
+		n->left->type = n->right->type;
+	}
+
+	// second half of dance.
+	// now that right is done, typecheck the left
+	// just to get it over with.  see dance above.
+	n->typecheck = 1;
+	if(n->left->typecheck == 0)
+		typecheck(&n->left, Erv | Easgn);
+	
+	// Recognize slices being updated in place, for better code generation later.
+	// Don't rewrite if using race detector, to avoid needing to teach race detector
+	// about this optimization.
+	if(n->left && n->left->op != OINDEXMAP && n->right && !flag_race) {
+		switch(n->right->op) {
+		case OSLICE:
+		case OSLICE3:
+		case OSLICESTR:
+			// For x = x[0:y], x can be updated in place, without touching pointer.
+			if(samesafeexpr(n->left, n->right->left) && (n->right->right->left == N || iszero(n->right->right->left)))
+				n->right->reslice = 1;
+			break;
+		
+		case OAPPEND:
+			// For x = append(x, ...), x can be updated in place when there is capacity,
+			// without touching the pointer; otherwise the emitted code to growslice
+			// can take care of updating the pointer, and only in that case.
+			if(n->right->list != nil && samesafeexpr(n->left, n->right->list->n))
+				n->right->reslice = 1;
+			break;
+		}
+	}
+}
+
+static void
+checkassignto(Type *src, Node *dst)
+{
+	char *why;
+
+	if(assignop(src, dst->type, &why) == 0) {
+		yyerror("cannot assign %T to %lN in multiple assignment%s", src, dst, why);
+		return;
+	}
+}
+
+static void
+typecheckas2(Node *n)
+{
+	int cl, cr;
+	NodeList *ll, *lr;
+	Node *l, *r;
+	Iter s;
+	Type *t;
+
+	for(ll=n->list; ll; ll=ll->next) {
+		// delicate little dance.
+		ll->n = resolve(ll->n);
+		if(ll->n->defn != n || ll->n->ntype)
+			typecheck(&ll->n, Erv | Easgn);
+	}
+	cl = count(n->list);
+	cr = count(n->rlist);
+	checkassignlist(n->list);
+	if(cl > 1 && cr == 1)
+		typecheck(&n->rlist->n, Erv | Efnstruct);
+	else
+		typechecklist(n->rlist, Erv);
+
+	if(cl == cr) {
+		// easy
+		for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next) {
+			if(ll->n->type != T && lr->n->type != T)
+				lr->n = assignconv(lr->n, ll->n->type, "assignment");
+			if(ll->n->defn == n && ll->n->ntype == N) {
+				defaultlit(&lr->n, T);
+				ll->n->type = lr->n->type;
+			}
+		}
+		goto out;
+	}
+
+
+	l = n->list->n;
+	r = n->rlist->n;
+
+	// m[i] = x, ok
+	if(cl == 1 && cr == 2 && l->op == OINDEXMAP) {
+		if(l->type == T)
+			goto out;
+		yyerror("assignment count mismatch: %d = %d (use delete)", cl, cr);
+		goto out;
+	}
+
+	// x,y,z = f()
+	if(cr == 1) {
+		if(r->type == T)
+			goto out;
+		switch(r->op) {
+		case OCALLMETH:
+		case OCALLINTER:
+		case OCALLFUNC:
+			if(r->type->etype != TSTRUCT || r->type->funarg == 0)
+				break;
+			cr = structcount(r->type);
+			if(cr != cl)
+				goto mismatch;
+			n->op = OAS2FUNC;
+			t = structfirst(&s, &r->type);
+			for(ll=n->list; ll; ll=ll->next) {
+				if(t->type != T && ll->n->type != T)
+					checkassignto(t->type, ll->n);
+				if(ll->n->defn == n && ll->n->ntype == N)
+					ll->n->type = t->type;
+				t = structnext(&s);
+			}
+			goto out;
+		}
+	}
+
+	// x, ok = y
+	if(cl == 2 && cr == 1) {
+		if(r->type == T)
+			goto out;
+		switch(r->op) {
+		case OINDEXMAP:
+			n->op = OAS2MAPR;
+			goto common;
+		case ORECV:
+			n->op = OAS2RECV;
+			goto common;
+		case ODOTTYPE:
+			n->op = OAS2DOTTYPE;
+			r->op = ODOTTYPE2;
+		common:
+			if(l->type != T)
+				checkassignto(r->type, l);
+			if(l->defn == n)
+				l->type = r->type;
+			l = n->list->next->n;
+			if(l->type != T && l->type->etype != TBOOL)
+				checkassignto(types[TBOOL], l);
+			if(l->defn == n && l->ntype == N)
+				l->type = types[TBOOL];
+			goto out;
+		}
+	}
+
+mismatch:
+	yyerror("assignment count mismatch: %d = %d", cl, cr);
+
+out:
+	// second half of dance
+	n->typecheck = 1;
+	for(ll=n->list; ll; ll=ll->next)
+		if(ll->n->typecheck == 0)
+			typecheck(&ll->n, Erv | Easgn);
+}
+
+/*
+ * type check function definition
+ */
+static void
+typecheckfunc(Node *n)
+{
+	Type *t, *rcvr;
+
+	typecheck(&n->nname, Erv | Easgn);
+	if((t = n->nname->type) == T)
+		return;
+	n->type = t;
+	t->nname = n->nname;
+	rcvr = getthisx(t)->type;
+	if(rcvr != nil && n->shortname != N && !isblank(n->shortname))
+		addmethod(n->shortname->sym, t, 1, n->nname->nointerface);
+}
+
+static void
+stringtoarraylit(Node **np)
+{
+	int32 i;
+	NodeList *l;
+	Strlit *s;
+	char *p, *ep;
+	Rune r;
+	Node *nn, *n;
+
+	n = *np;
+	if(n->left->op != OLITERAL || n->left->val.ctype != CTSTR)
+		fatal("stringtoarraylit %N", n);
+
+	s = n->left->val.u.sval;
+	l = nil;
+	p = s->s;
+	ep = s->s + s->len;
+	i = 0;
+	if(n->type->type->etype == TUINT8) {
+		// raw []byte
+		while(p < ep)
+			l = list(l, nod(OKEY, nodintconst(i++), nodintconst((uchar)*p++)));
+	} else {
+		// utf-8 []rune
+		while(p < ep) {
+			p += chartorune(&r, p);
+			l = list(l, nod(OKEY, nodintconst(i++), nodintconst(r)));
+		}
+	}
+	nn = nod(OCOMPLIT, N, typenod(n->type));
+	nn->list = l;
+	typecheck(&nn, Erv);
+	*np = nn;
+}
+
+
+static int ntypecheckdeftype;
+static NodeList *methodqueue;
+
+static void
+domethod(Node *n)
+{
+	Node *nt;
+	Type *t;
+
+	nt = n->type->nname;
+	typecheck(&nt, Etype);
+	if(nt->type == T) {
+		// type check failed; leave empty func
+		n->type->etype = TFUNC;
+		n->type->nod = N;
+		return;
+	}
+	
+	// If we have
+	//	type I interface {
+	//		M(_ int)
+	//	}
+	// then even though I.M looks like it doesn't care about the
+	// value of its argument, a specific implementation of I may
+	// care.  The _ would suppress the assignment to that argument
+	// while generating a call, so remove it.
+	for(t=getinargx(nt->type)->type; t; t=t->down) {
+		if(t->sym != nil && strcmp(t->sym->name, "_") == 0)
+			t->sym = nil;
+	}
+
+	*n->type = *nt->type;
+	n->type->nod = N;
+	checkwidth(n->type);
+}
+
+static NodeList *mapqueue;
+
+void
+copytype(Node *n, Type *t)
+{
+	int maplineno, embedlineno, lno;
+	NodeList *l;
+
+	if(t->etype == TFORW) {
+		// This type isn't computed yet; when it is, update n.
+		t->copyto = list(t->copyto, n);
+		return;
+	}
+
+	maplineno = n->type->maplineno;
+	embedlineno = n->type->embedlineno;
+
+	l = n->type->copyto;
+	*n->type = *t;
+
+	t = n->type;
+	t->sym = n->sym;
+	t->local = n->local;
+	t->vargen = n->vargen;
+	t->siggen = 0;
+	t->method = nil;
+	t->xmethod = nil;
+	t->nod = N;
+	t->printed = 0;
+	t->deferwidth = 0;
+	t->copyto = nil;
+	
+	// Update nodes waiting on this type.
+	for(; l; l=l->next)
+		copytype(l->n, t);
+
+	// Double-check use of type as embedded type.
+	lno = lineno;
+	if(embedlineno) {
+		lineno = embedlineno;
+		if(isptr[t->etype])
+			yyerror("embedded type cannot be a pointer");
+	}
+	lineno = lno;
+	
+	// Queue check for map until all the types are done settling.
+	if(maplineno) {
+		t->maplineno = maplineno;
+		mapqueue = list(mapqueue, n);
+	}
+}
+
+static void
+typecheckdeftype(Node *n)
+{
+	int lno;
+	Type *t;
+	NodeList *l;
+
+	ntypecheckdeftype++;
+	lno = lineno;
+	setlineno(n);
+	n->type->sym = n->sym;
+	n->typecheck = 1;
+	typecheck(&n->ntype, Etype);
+	if((t = n->ntype->type) == T) {
+		n->diag = 1;
+		n->type = T;
+		goto ret;
+	}
+	if(n->type == T) {
+		n->diag = 1;
+		goto ret;
+	}
+
+	// copy new type and clear fields
+	// that don't come along.
+	// anything zeroed here must be zeroed in
+	// typedcl2 too.
+	copytype(n, t);
+
+ret:
+	lineno = lno;
+
+	// if there are no type definitions going on, it's safe to
+	// try to resolve the method types for the interfaces
+	// we just read.
+	if(ntypecheckdeftype == 1) {
+		while((l = methodqueue) != nil) {
+			methodqueue = nil;
+			for(; l; l=l->next)
+				domethod(l->n);
+		}
+		for(l=mapqueue; l; l=l->next) {
+			lineno = l->n->type->maplineno;
+			maptype(l->n->type, types[TBOOL]);
+		}
+		lineno = lno;
+	}
+	ntypecheckdeftype--;
+}
+
+void
+queuemethod(Node *n)
+{
+	if(ntypecheckdeftype == 0) {
+		domethod(n);
+		return;
+	}
+	methodqueue = list(methodqueue, n);
+}
+
+Node*
+typecheckdef(Node *n)
+{
+	int lno, nerrors0;
+	Node *e;
+	Type *t;
+	NodeList *l;
+
+	lno = lineno;
+	setlineno(n);
+
+	if(n->op == ONONAME) {
+		if(!n->diag) {
+			n->diag = 1;
+			if(n->lineno != 0)
+				lineno = n->lineno;
+			yyerror("undefined: %S", n->sym);
+		}
+		return n;
+	}
+
+	if(n->walkdef == 1)
+		return n;
+
+	l = mal(sizeof *l);
+	l->n = n;
+	l->next = typecheckdefstack;
+	typecheckdefstack = l;
+
+	if(n->walkdef == 2) {
+		flusherrors();
+		print("typecheckdef loop:");
+		for(l=typecheckdefstack; l; l=l->next)
+			print(" %S", l->n->sym);
+		print("\n");
+		fatal("typecheckdef loop");
+	}
+	n->walkdef = 2;
+
+	if(n->type != T || n->sym == S)	// builtin or no name
+		goto ret;
+
+	switch(n->op) {
+	default:
+		fatal("typecheckdef %O", n->op);
+
+	case OGOTO:
+	case OLABEL:
+		// not really syms
+		break;
+
+	case OLITERAL:
+		if(n->ntype != N) {
+			typecheck(&n->ntype, Etype);
+			n->type = n->ntype->type;
+			n->ntype = N;
+			if(n->type == T) {
+				n->diag = 1;
+				goto ret;
+			}
+		}
+		e = n->defn;
+		n->defn = N;
+		if(e == N) {
+			lineno = n->lineno;
+			dump("typecheckdef nil defn", n);
+			yyerror("xxx");
+		}
+		typecheck(&e, Erv | Eiota);
+		if(isconst(e, CTNIL)) {
+			yyerror("const initializer cannot be nil");
+			goto ret;
+		}
+		if(e->type != T && e->op != OLITERAL || !isgoconst(e)) {
+			if(!e->diag) {
+				yyerror("const initializer %N is not a constant", e);
+				e->diag = 1;
+			}
+			goto ret;
+		}
+		t = n->type;
+		if(t != T) {
+			if(!okforconst[t->etype]) {
+				yyerror("invalid constant type %T", t);
+				goto ret;
+			}
+			if(!isideal(e->type) && !eqtype(t, e->type)) {
+				yyerror("cannot use %lN as type %T in const initializer", e, t);
+				goto ret;
+			}
+			convlit(&e, t);
+		}
+		n->val = e->val;
+		n->type = e->type;
+		break;
+
+	case ONAME:
+		if(n->ntype != N) {
+			typecheck(&n->ntype, Etype);
+			n->type = n->ntype->type;
+
+			if(n->type == T) {
+				n->diag = 1;
+				goto ret;
+			}
+		}
+		if(n->type != T)
+			break;
+		if(n->defn == N) {
+			if(n->etype != 0)	// like OPRINTN
+				break;
+			if(nsavederrors+nerrors > 0) {
+				// Can have undefined variables in x := foo
+				// that make x have an n->ndefn == nil.
+				// If there are other errors anyway, don't
+				// bother adding to the noise.
+				break;
+			}
+			fatal("var without type, init: %S", n->sym);
+		}
+		if(n->defn->op == ONAME) {
+			typecheck(&n->defn, Erv);
+			n->type = n->defn->type;
+			break;
+		}
+		typecheck(&n->defn, Etop);	// fills in n->type
+		break;
+
+	case OTYPE:
+		if(curfn)
+			defercheckwidth();
+		n->walkdef = 1;
+		n->type = typ(TFORW);
+		n->type->sym = n->sym;
+		nerrors0 = nerrors;
+		typecheckdeftype(n);
+		if(n->type->etype == TFORW && nerrors > nerrors0) {
+			// Something went wrong during type-checking,
+			// but it was reported. Silence future errors.
+			n->type->broke = 1;
+		}
+		if(curfn)
+			resumecheckwidth();
+		break;
+
+	case OPACK:
+		// nothing to see here
+		break;
+	}
+
+ret:
+	if(n->op != OLITERAL && n->type != T && isideal(n->type))
+		fatal("got %T for %N", n->type, n);
+	if(typecheckdefstack->n != n)
+		fatal("typecheckdefstack mismatch");
+	l = typecheckdefstack;
+	typecheckdefstack = l->next;
+
+	lineno = lno;
+	n->walkdef = 1;
+	return n;
+}
+
+static int
+checkmake(Type *t, char *arg, Node *n)
+{
+	if(n->op == OLITERAL) {
+		switch(n->val.ctype) {
+		case CTINT:
+		case CTRUNE:
+		case CTFLT:
+		case CTCPLX:
+			n->val = toint(n->val);
+			if(mpcmpfixc(n->val.u.xval, 0) < 0) {
+				yyerror("negative %s argument in make(%T)", arg, t);
+				return -1;
+			}
+			if(mpcmpfixfix(n->val.u.xval, maxintval[TINT]) > 0) {
+				yyerror("%s argument too large in make(%T)", arg, t);
+				return -1;
+			}
+			
+			// Delay defaultlit until after we've checked range, to avoid
+			// a redundant "constant NNN overflows int" error.
+			defaultlit(&n, types[TINT]);
+			return 0;
+		default:
+		       	break;
+		}
+	}
+
+	if(!isint[n->type->etype] && n->type->etype != TIDEAL) {
+		yyerror("non-integer %s argument in make(%T) - %T", arg, t, n->type);
+		return -1;
+	}
+
+	// Defaultlit still necessary for non-constant: n might be 1<<k.
+	defaultlit(&n, types[TINT]);
+
+	return 0;
+}
+
+static void	markbreaklist(NodeList*, Node*);
+
+static void
+markbreak(Node *n, Node *implicit)
+{
+	Label *lab;
+
+	if(n == N)
+		return;
+
+	switch(n->op) {
+	case OBREAK:
+		if(n->left == N) {
+			if(implicit)
+				implicit->hasbreak = 1;
+		} else {
+			lab = n->left->sym->label;
+			if(lab != L)
+				lab->def->hasbreak = 1;
+		}
+		break;
+	
+	case OFOR:
+	case OSWITCH:
+	case OTYPESW:
+	case OSELECT:
+	case ORANGE:
+		implicit = n;
+		// fall through
+	
+	default:
+		markbreak(n->left, implicit);
+		markbreak(n->right, implicit);
+		markbreak(n->ntest, implicit);
+		markbreak(n->nincr, implicit);
+		markbreaklist(n->ninit, implicit);
+		markbreaklist(n->nbody, implicit);
+		markbreaklist(n->nelse, implicit);
+		markbreaklist(n->list, implicit);
+		markbreaklist(n->rlist, implicit);
+		break;
+	}
+}
+
+static void
+markbreaklist(NodeList *l, Node *implicit)
+{
+	Node *n;
+	Label *lab;
+
+	for(; l; l=l->next) {
+		n = l->n;
+		if(n->op == OLABEL && l->next && n->defn == l->next->n) {
+			switch(n->defn->op) {
+			case OFOR:
+			case OSWITCH:
+			case OTYPESW:
+			case OSELECT:
+			case ORANGE:
+				lab = mal(sizeof *lab);
+				lab->def = n->defn;
+				n->left->sym->label = lab;
+				markbreak(n->defn, n->defn);
+				n->left->sym->label = L;
+				l = l->next;
+				continue;
+			}
+		}
+		markbreak(n, implicit);
+	}
+}
+
+static int
+isterminating(NodeList *l, int top)
+{
+	int def;
+	Node *n;
+
+	if(l == nil)
+		return 0;
+	if(top) {
+		while(l->next && l->n->op != OLABEL)
+			l = l->next;
+		markbreaklist(l, nil);
+	}
+	while(l->next)
+		l = l->next;
+	n = l->n;
+
+	if(n == N)
+		return 0;
+
+	switch(n->op) {
+	// NOTE: OLABEL is treated as a separate statement,
+	// not a separate prefix, so skipping to the last statement
+	// in the block handles the labeled statement case by
+	// skipping over the label. No case OLABEL here.
+
+	case OBLOCK:
+		return isterminating(n->list, 0);
+
+	case OGOTO:
+	case ORETURN:
+	case ORETJMP:
+	case OPANIC:
+	case OXFALL:
+		return 1;
+
+	case OFOR:
+		if(n->ntest != N)
+			return 0;
+		if(n->hasbreak)
+			return 0;
+		return 1;
+
+	case OIF:
+		return isterminating(n->nbody, 0) && isterminating(n->nelse, 0);
+
+	case OSWITCH:
+	case OTYPESW:
+	case OSELECT:
+		if(n->hasbreak)
+			return 0;
+		def = 0;
+		for(l=n->list; l; l=l->next) {
+			if(!isterminating(l->n->nbody, 0))
+				return 0;
+			if(l->n->list == nil) // default
+				def = 1;
+		}
+		if(n->op != OSELECT && !def)
+			return 0;
+		return 1;
+	}
+	
+	return 0;
+}
+
+void
+checkreturn(Node *fn)
+{
+	if(fn->type->outtuple && fn->nbody != nil)
+		if(!isterminating(fn->nbody, 1))
+			yyerrorl(fn->endlineno, "missing return at end of function");
+}
diff --git a/src/cmd/gc/unsafe.c b/src/cmd/gc/unsafe.c
new file mode 100644
index 0000000..ff08c0e
--- /dev/null
+++ b/src/cmd/gc/unsafe.c
@@ -0,0 +1,148 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include <u.h>
+#include <libc.h>
+#include "go.h"
+
+/*
+ * look for
+ *	unsafe.Sizeof
+ *	unsafe.Offsetof
+ *	unsafe.Alignof
+ * rewrite with a constant
+ */
+Node*
+unsafenmagic(Node *nn)
+{
+	Node *r, *n, *base, *r1;
+	Sym *s;
+	Type *t, *tr;
+	vlong v;
+	Val val;
+	Node *fn;
+	NodeList *args;
+
+	fn = nn->left;
+	args = nn->list;
+
+	if(safemode || fn == N || fn->op != ONAME || (s = fn->sym) == S)
+		goto no;
+	if(s->pkg != unsafepkg)
+		goto no;
+
+	if(args == nil) {
+		yyerror("missing argument for %S", s);
+		goto no;
+	}
+	r = args->n;
+
+	if(strcmp(s->name, "Sizeof") == 0) {
+		typecheck(&r, Erv);
+		defaultlit(&r, T);
+		tr = r->type;
+		if(tr == T)
+			goto bad;
+		dowidth(tr);
+		v = tr->width;
+		goto yes;
+	}
+	if(strcmp(s->name, "Offsetof") == 0) {
+		// must be a selector.
+		if(r->op != OXDOT)
+			goto bad;
+		// Remember base of selector to find it back after dot insertion.
+		// Since r->left may be mutated by typechecking, check it explicitly
+		// first to track it correctly.
+		typecheck(&r->left, Erv);
+		base = r->left;
+		typecheck(&r, Erv);
+		switch(r->op) {
+		case ODOT:
+		case ODOTPTR:
+			break;
+		case OCALLPART:
+			yyerror("invalid expression %N: argument is a method value", nn);
+			v = 0;
+			goto ret;
+		default:
+			goto bad;
+		}
+		v = 0;
+		// add offsets for inserted dots.
+		for(r1=r; r1->left!=base; r1=r1->left) {
+			switch(r1->op) {
+			case ODOT:
+				v += r1->xoffset;
+				break;
+			case ODOTPTR:
+				yyerror("invalid expression %N: selector implies indirection of embedded %N", nn, r1->left);
+				goto ret;
+			default:
+				dump("unsafenmagic", r);
+				fatal("impossible %#O node after dot insertion", r1->op);
+				goto bad;
+			}
+		}
+		v += r1->xoffset;
+		goto yes;
+	}
+	if(strcmp(s->name, "Alignof") == 0) {
+		typecheck(&r, Erv);
+		defaultlit(&r, T);
+		tr = r->type;
+		if(tr == T)
+			goto bad;
+
+		// make struct { byte; T; }
+		t = typ(TSTRUCT);
+		t->type = typ(TFIELD);
+		t->type->type = types[TUINT8];
+		t->type->down = typ(TFIELD);
+		t->type->down->type = tr;
+		// compute struct widths
+		dowidth(t);
+
+		// the offset of T is its required alignment
+		v = t->type->down->width;
+		goto yes;
+	}
+
+no:
+	return N;
+
+bad:
+	yyerror("invalid expression %N", nn);
+	v = 0;
+	goto ret;
+
+yes:
+	if(args->next != nil)
+		yyerror("extra arguments for %S", s);
+ret:
+	// any side effects disappear; ignore init
+	val.ctype = CTINT;
+	val.u.xval = mal(sizeof(*n->val.u.xval));
+	mpmovecfix(val.u.xval, v);
+	n = nod(OLITERAL, N, N);
+	n->orig = nn;
+	n->val = val;
+	n->type = types[TUINTPTR];
+	nn->type = types[TUINTPTR];
+	return n;
+}
+
+int
+isunsafebuiltin(Node *n)
+{
+	if(n == N || n->op != ONAME || n->sym == S || n->sym->pkg != unsafepkg)
+		return 0;
+	if(strcmp(n->sym->name, "Sizeof") == 0)
+		return 1;
+	if(strcmp(n->sym->name, "Offsetof") == 0)
+		return 1;
+	if(strcmp(n->sym->name, "Alignof") == 0)
+		return 1;
+	return 0;
+}
diff --git a/src/cmd/gc/unsafe.go b/src/cmd/gc/unsafe.go
new file mode 100644
index 0000000..c3c6278
--- /dev/null
+++ b/src/cmd/gc/unsafe.go
@@ -0,0 +1,18 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// NOTE: If you change this file you must run "./mkbuiltin"
+// to update builtin.c.boot.  This is not done automatically
+// to avoid depending on having a working compiler binary.
+
+// +build ignore
+
+package PACKAGE
+
+type Pointer uintptr // not really; filled in by compiler
+
+// return types here are ignored; see unsafe.c
+func Offsetof(any) uintptr
+func Sizeof(any) uintptr
+func Alignof(any) uintptr
diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c
new file mode 100644
index 0000000..ff9b362
--- /dev/null
+++ b/src/cmd/gc/walk.c
@@ -0,0 +1,3937 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include	<u.h>
+#include	<libc.h>
+#include	"go.h"
+#include	"../ld/textflag.h"
+
+static	Node*	walkprint(Node*, NodeList**);
+static	Node*	writebarrierfn(char*, Type*, Type*);
+static	Node*	applywritebarrier(Node*, NodeList**);
+static	Node*	mapfn(char*, Type*);
+static	Node*	mapfndel(char*, Type*);
+static	Node*	ascompatee1(int, Node*, Node*, NodeList**);
+static	NodeList*	ascompatee(int, NodeList*, NodeList*, NodeList**);
+static	NodeList*	ascompatet(int, NodeList*, Type**, int, NodeList**);
+static	NodeList*	ascompatte(int, Node*, int, Type**, NodeList*, int, NodeList**);
+static	Node*	convas(Node*, NodeList**);
+static	void	heapmoves(void);
+static	NodeList*	paramstoheap(Type **argin, int out);
+static	NodeList*	reorder1(NodeList*);
+static	NodeList*	reorder3(NodeList*);
+static	Node*	addstr(Node*, NodeList**);
+static	Node*	appendslice(Node*, NodeList**);
+static	Node*	append(Node*, NodeList**);
+static	Node*	copyany(Node*, NodeList**, int);
+static	Node*	sliceany(Node*, NodeList**);
+static	void	walkcompare(Node**, NodeList**);
+static	void	walkrotate(Node**);
+static	void	walkmul(Node**, NodeList**);
+static	void	walkdiv(Node**, NodeList**);
+static	int	bounded(Node*, int64);
+static	Mpint	mpzero;
+static	void	walkprintfunc(Node**, NodeList**);
+
+void
+walk(Node *fn)
+{
+	char s[50];
+	NodeList *l;
+	int lno;
+
+	curfn = fn;
+
+	if(debug['W']) {
+		snprint(s, sizeof(s), "\nbefore %S", curfn->nname->sym);
+		dumplist(s, curfn->nbody);
+	}
+
+	lno = lineno;
+
+	// Final typecheck for any unused variables.
+	// It's hard to be on the heap when not-used, but best to be consistent about &~PHEAP here and below.
+	for(l=fn->dcl; l; l=l->next)
+		if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO)
+			typecheck(&l->n, Erv | Easgn);
+
+	// Propagate the used flag for typeswitch variables up to the NONAME in it's definition.
+	for(l=fn->dcl; l; l=l->next)
+		if(l->n->op == ONAME && (l->n->class&~PHEAP) == PAUTO && l->n->defn && l->n->defn->op == OTYPESW && l->n->used)
+			l->n->defn->left->used++;
+	
+	for(l=fn->dcl; l; l=l->next) {
+		if(l->n->op != ONAME || (l->n->class&~PHEAP) != PAUTO || l->n->sym->name[0] == '&' || l->n->used)
+			continue;
+		if(l->n->defn && l->n->defn->op == OTYPESW) {
+			if(l->n->defn->left->used)
+				continue;
+			lineno = l->n->defn->left->lineno;
+			yyerror("%S declared and not used", l->n->sym);
+			l->n->defn->left->used = 1; // suppress repeats
+		} else {
+			lineno = l->n->lineno;
+			yyerror("%S declared and not used", l->n->sym);
+		}
+	}	
+
+	lineno = lno;
+	if(nerrors != 0)
+		return;
+	walkstmtlist(curfn->nbody);
+	if(debug['W']) {
+		snprint(s, sizeof(s), "after walk %S", curfn->nname->sym);
+		dumplist(s, curfn->nbody);
+	}
+	heapmoves();
+	if(debug['W'] && curfn->enter != nil) {
+		snprint(s, sizeof(s), "enter %S", curfn->nname->sym);
+		dumplist(s, curfn->enter);
+	}
+}
+
+
+void
+walkstmtlist(NodeList *l)
+{
+	for(; l; l=l->next)
+		walkstmt(&l->n);
+}
+
+static int
+samelist(NodeList *a, NodeList *b)
+{
+	for(; a && b; a=a->next, b=b->next)
+		if(a->n != b->n)
+			return 0;
+	return a == b;
+}
+
+static int
+paramoutheap(Node *fn)
+{
+	NodeList *l;
+
+	for(l=fn->dcl; l; l=l->next) {
+		switch(l->n->class) {
+		case PPARAMOUT:
+		case PPARAMOUT|PHEAP:
+			return l->n->addrtaken;
+		case PAUTO:
+		case PAUTO|PHEAP:
+			// stop early - parameters are over
+			return 0;
+		}
+	}
+	return 0;
+}
+
+void
+walkstmt(Node **np)
+{
+	NodeList *init;
+	NodeList *ll, *rl;
+	int cl;
+	Node *n, *f;
+
+	n = *np;
+	if(n == N)
+		return;
+	if(n->dodata == 2) // don't walk, generated by anylit.
+		return;
+
+	setlineno(n);
+
+	walkstmtlist(n->ninit);
+
+	switch(n->op) {
+	default:
+		if(n->op == ONAME)
+			yyerror("%S is not a top level statement", n->sym);
+		else
+			yyerror("%O is not a top level statement", n->op);
+		dump("nottop", n);
+		break;
+
+	case OAS:
+	case OASOP:
+	case OAS2:
+	case OAS2DOTTYPE:
+	case OAS2RECV:
+	case OAS2FUNC:
+	case OAS2MAPR:
+	case OCLOSE:
+	case OCOPY:
+	case OCALLMETH:
+	case OCALLINTER:
+	case OCALL:
+	case OCALLFUNC:
+	case ODELETE:
+	case OSEND:
+	case OPRINT:
+	case OPRINTN:
+	case OPANIC:
+	case OEMPTY:
+	case ORECOVER:
+		if(n->typecheck == 0)
+			fatal("missing typecheck: %+N", n);
+		init = n->ninit;
+		n->ninit = nil;
+		walkexpr(&n, &init);
+		addinit(&n, init);
+		if((*np)->op == OCOPY && n->op == OCONVNOP)
+			n->op = OEMPTY; // don't leave plain values as statements.
+		break;
+
+	case ORECV:
+		// special case for a receive where we throw away
+		// the value received.
+		if(n->typecheck == 0)
+			fatal("missing typecheck: %+N", n);
+		init = n->ninit;
+		n->ninit = nil;
+
+		walkexpr(&n->left, &init);
+		n = mkcall1(chanfn("chanrecv1", 2, n->left->type), T, &init, typename(n->left->type), n->left, nodnil());
+		walkexpr(&n, &init);
+
+		addinit(&n, init);
+		break;
+
+	case OBREAK:
+	case ODCL:
+	case OCONTINUE:
+	case OFALL:
+	case OGOTO:
+	case OLABEL:
+	case ODCLCONST:
+	case ODCLTYPE:
+	case OCHECKNIL:
+	case OVARKILL:
+		break;
+
+	case OBLOCK:
+		walkstmtlist(n->list);
+		break;
+
+	case OXCASE:
+		yyerror("case statement out of place");
+		n->op = OCASE;
+	case OCASE:
+		walkstmt(&n->right);
+		break;
+
+	case ODEFER:
+		hasdefer = 1;
+		switch(n->left->op) {
+		case OPRINT:
+		case OPRINTN:
+			walkprintfunc(&n->left, &n->ninit);
+			break;
+		case OCOPY:
+			n->left = copyany(n->left, &n->ninit, 1);
+			break;
+		default:
+			walkexpr(&n->left, &n->ninit);
+			break;
+		}
+		break;
+
+	case OFOR:
+		if(n->ntest != N) {
+			walkstmtlist(n->ntest->ninit);
+			init = n->ntest->ninit;
+			n->ntest->ninit = nil;
+			walkexpr(&n->ntest, &init);
+			addinit(&n->ntest, init);
+		}
+		walkstmt(&n->nincr);
+		walkstmtlist(n->nbody);
+		break;
+
+	case OIF:
+		walkexpr(&n->ntest, &n->ninit);
+		walkstmtlist(n->nbody);
+		walkstmtlist(n->nelse);
+		break;
+
+	case OPROC:
+		switch(n->left->op) {
+		case OPRINT:
+		case OPRINTN:
+			walkprintfunc(&n->left, &n->ninit);
+			break;
+		case OCOPY:
+			n->left = copyany(n->left, &n->ninit, 1);
+			break;
+		default:
+			walkexpr(&n->left, &n->ninit);
+			break;
+		}
+		break;
+
+	case ORETURN:
+		walkexprlist(n->list, &n->ninit);
+		if(n->list == nil)
+			break;
+		if((curfn->type->outnamed && count(n->list) > 1) || paramoutheap(curfn)) {
+			// assign to the function out parameters,
+			// so that reorder3 can fix up conflicts
+			rl = nil;
+			for(ll=curfn->dcl; ll != nil; ll=ll->next) {
+				cl = ll->n->class & ~PHEAP;
+				if(cl == PAUTO)
+					break;
+				if(cl == PPARAMOUT)
+					rl = list(rl, ll->n);
+			}
+			if(samelist(rl, n->list)) {
+				// special return in disguise
+				n->list = nil;
+				break;
+			}
+			if(count(n->list) == 1 && count(rl) > 1) {
+				// OAS2FUNC in disguise
+				f = n->list->n;
+				if(f->op != OCALLFUNC && f->op != OCALLMETH && f->op != OCALLINTER)
+					fatal("expected return of call, have %N", f);
+				n->list = concat(list1(f), ascompatet(n->op, rl, &f->type, 0, &n->ninit));
+				break;
+			}
+
+			// move function calls out, to make reorder3's job easier.
+			walkexprlistsafe(n->list, &n->ninit);
+			ll = ascompatee(n->op, rl, n->list, &n->ninit);
+			n->list = reorder3(ll);
+			break;
+		}
+		ll = ascompatte(n->op, nil, 0, getoutarg(curfn->type), n->list, 1, &n->ninit);
+		n->list = ll;
+		break;
+
+	case ORETJMP:
+		break;
+
+	case OSELECT:
+		walkselect(n);
+		break;
+
+	case OSWITCH:
+		walkswitch(n);
+		break;
+
+	case ORANGE:
+		walkrange(n);
+		break;
+
+	case OXFALL:
+		yyerror("fallthrough statement out of place");
+		n->op = OFALL;
+		break;
+	}
+
+	if(n->op == ONAME)
+		fatal("walkstmt ended up with name: %+N", n);
+	
+	*np = n;
+}
+
+
+/*
+ * walk the whole tree of the body of an
+ * expression or simple statement.
+ * the types expressions are calculated.
+ * compile-time constants are evaluated.
+ * complex side effects like statements are appended to init
+ */
+
+void
+walkexprlist(NodeList *l, NodeList **init)
+{
+	for(; l; l=l->next)
+		walkexpr(&l->n, init);
+}
+
+void
+walkexprlistsafe(NodeList *l, NodeList **init)
+{
+	for(; l; l=l->next) {
+		l->n = safeexpr(l->n, init);
+		walkexpr(&l->n, init);
+	}
+}
+
+void
+walkexpr(Node **np, NodeList **init)
+{
+	Node *r, *l, *var, *a;
+	Node *map, *key;
+	NodeList *ll, *lr;
+	Type *t;
+	int et, old_safemode;
+	int64 v;
+	int32 lno;
+	Node *n, *fn, *n1, *n2;
+	Sym *sym;
+	char buf[100], *p;
+
+	n = *np;
+
+	if(n == N)
+		return;
+
+	if(init == &n->ninit) {
+		// not okay to use n->ninit when walking n,
+		// because we might replace n with some other node
+		// and would lose the init list.
+		fatal("walkexpr init == &n->ninit");
+	}
+
+	if(n->ninit != nil) {
+		walkstmtlist(n->ninit);
+		*init = concat(*init, n->ninit);
+		n->ninit = nil;
+	}
+
+	// annoying case - not typechecked
+	if(n->op == OKEY) {
+		walkexpr(&n->left, init);
+		walkexpr(&n->right, init);
+		return;
+	}
+
+	lno = setlineno(n);
+
+	if(debug['w'] > 1)
+		dump("walk-before", n);
+
+	if(n->typecheck != 1)
+		fatal("missed typecheck: %+N\n", n);
+
+	switch(n->op) {
+	default:
+		dump("walk", n);
+		fatal("walkexpr: switch 1 unknown op %+hN", n);
+		break;
+
+	case OTYPE:
+	case ONONAME:
+	case OINDREG:
+	case OEMPTY:
+		goto ret;
+
+	case ONOT:
+	case OMINUS:
+	case OPLUS:
+	case OCOM:
+	case OREAL:
+	case OIMAG:
+	case ODOTMETH:
+	case ODOTINTER:
+		walkexpr(&n->left, init);
+		goto ret;
+
+	case OIND:
+		walkexpr(&n->left, init);
+		goto ret;
+
+	case ODOT:
+		usefield(n);
+		walkexpr(&n->left, init);
+		goto ret;
+
+	case ODOTPTR:
+		usefield(n);
+		if(n->op == ODOTPTR && n->left->type->type->width == 0) {
+			// No actual copy will be generated, so emit an explicit nil check.
+			n->left = cheapexpr(n->left, init);
+			checknil(n->left, init);
+		}
+		walkexpr(&n->left, init);
+		goto ret;
+
+	case OEFACE:
+		walkexpr(&n->left, init);
+		walkexpr(&n->right, init);
+		goto ret;
+
+	case OSPTR:
+	case OITAB:
+		walkexpr(&n->left, init);
+		goto ret;
+
+	case OLEN:
+	case OCAP:
+		walkexpr(&n->left, init);
+
+		// replace len(*[10]int) with 10.
+		// delayed until now to preserve side effects.
+		t = n->left->type;
+		if(isptr[t->etype])
+			t = t->type;
+		if(isfixedarray(t)) {
+			safeexpr(n->left, init);
+			nodconst(n, n->type, t->bound);
+			n->typecheck = 1;
+		}
+		goto ret;
+
+	case OLSH:
+	case ORSH:
+		walkexpr(&n->left, init);
+		walkexpr(&n->right, init);
+		t = n->left->type;
+		n->bounded = bounded(n->right, 8*t->width);
+		if(debug['m'] && n->etype && !isconst(n->right, CTINT))
+			warn("shift bounds check elided");
+		goto ret;
+
+	case OAND:
+	case OSUB:
+	case OHMUL:
+	case OLT:
+	case OLE:
+	case OGE:
+	case OGT:
+	case OADD:
+	case OCOMPLEX:
+	case OLROT:
+		// Use results from call expression as arguments for complex.
+		if(n->op == OCOMPLEX && n->left == N && n->right == N) {
+			n->left = n->list->n;
+			n->right = n->list->next->n;
+		}
+		walkexpr(&n->left, init);
+		walkexpr(&n->right, init);
+		goto ret;
+
+	case OOR:
+	case OXOR:
+		walkexpr(&n->left, init);
+		walkexpr(&n->right, init);
+		walkrotate(&n);
+		goto ret;
+
+	case OEQ:
+	case ONE:
+		walkexpr(&n->left, init);
+		walkexpr(&n->right, init);
+		// Disable safemode while compiling this code: the code we
+		// generate internally can refer to unsafe.Pointer.
+		// In this case it can happen if we need to generate an ==
+		// for a struct containing a reflect.Value, which itself has
+		// an unexported field of type unsafe.Pointer.
+		old_safemode = safemode;
+		safemode = 0;
+		walkcompare(&n, init);
+		safemode = old_safemode;
+		goto ret;
+
+	case OANDAND:
+	case OOROR:
+		walkexpr(&n->left, init);
+		// cannot put side effects from n->right on init,
+		// because they cannot run before n->left is checked.
+		// save elsewhere and store on the eventual n->right.
+		ll = nil;
+		walkexpr(&n->right, &ll);
+		addinit(&n->right, ll);
+		goto ret;
+
+	case OPRINT:
+	case OPRINTN:
+		walkexprlist(n->list, init);
+		n = walkprint(n, init);
+		goto ret;
+
+	case OPANIC:
+		n = mkcall("gopanic", T, init, n->left);
+		goto ret;
+
+	case ORECOVER:
+		n = mkcall("gorecover", n->type, init, nod(OADDR, nodfp, N));
+		goto ret;
+
+	case OLITERAL:
+		n->addable = 1;
+		goto ret;
+
+	case OCLOSUREVAR:
+	case OCFUNC:
+		n->addable = 1;
+		goto ret;
+
+	case ONAME:
+		if(!(n->class & PHEAP) && n->class != PPARAMREF)
+			n->addable = 1;
+		goto ret;
+
+	case OCALLINTER:
+		t = n->left->type;
+		if(n->list && n->list->n->op == OAS)
+			goto ret;
+		walkexpr(&n->left, init);
+		walkexprlist(n->list, init);
+		ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
+		n->list = reorder1(ll);
+		goto ret;
+
+	case OCALLFUNC:
+		t = n->left->type;
+		if(n->list && n->list->n->op == OAS)
+			goto ret;
+
+		walkexpr(&n->left, init);
+		walkexprlist(n->list, init);
+
+		ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
+		n->list = reorder1(ll);
+		goto ret;
+
+	case OCALLMETH:
+		t = n->left->type;
+		if(n->list && n->list->n->op == OAS)
+			goto ret;
+		walkexpr(&n->left, init);
+		walkexprlist(n->list, init);
+		ll = ascompatte(n->op, n, 0, getthis(t), list1(n->left->left), 0, init);
+		lr = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
+		ll = concat(ll, lr);
+		n->left->left = N;
+		ullmancalc(n->left);
+		n->list = reorder1(ll);
+		goto ret;
+
+	case OAS:
+		*init = concat(*init, n->ninit);
+		n->ninit = nil;
+
+		walkexpr(&n->left, init);
+		n->left = safeexpr(n->left, init);
+
+		if(oaslit(n, init))
+			goto ret;
+
+		if(n->right == N || iszero(n->right) && !flag_race)
+			goto ret;
+
+		switch(n->right->op) {
+		default:
+			walkexpr(&n->right, init);
+			break;
+		
+		case ORECV:
+			// x = <-c; n->left is x, n->right->left is c.
+			// orderstmt made sure x is addressable.
+			walkexpr(&n->right->left, init);
+			n1 = nod(OADDR, n->left, N);
+			r = n->right->left; // the channel
+			n = mkcall1(chanfn("chanrecv1", 2, r->type), T, init, typename(r->type), r, n1);
+			walkexpr(&n, init);
+			goto ret;
+		}
+
+		if(n->left != N && n->right != N) {
+			r = convas(nod(OAS, n->left, n->right), init);
+			r->dodata = n->dodata;
+			n = r;
+			n = applywritebarrier(n, init);
+		}
+
+		goto ret;
+
+	case OAS2:
+		*init = concat(*init, n->ninit);
+		n->ninit = nil;
+		walkexprlistsafe(n->list, init);
+		walkexprlistsafe(n->rlist, init);
+		ll = ascompatee(OAS, n->list, n->rlist, init);
+		ll = reorder3(ll);
+		for(lr = ll; lr != nil; lr = lr->next)
+			lr->n = applywritebarrier(lr->n, init);
+		n = liststmt(ll);
+		goto ret;
+
+	case OAS2FUNC:
+		// a,b,... = fn()
+		*init = concat(*init, n->ninit);
+		n->ninit = nil;
+		r = n->rlist->n;
+		walkexprlistsafe(n->list, init);
+		walkexpr(&r, init);
+
+		ll = ascompatet(n->op, n->list, &r->type, 0, init);
+		for(lr = ll; lr != nil; lr = lr->next)
+			lr->n = applywritebarrier(lr->n, init);
+		n = liststmt(concat(list1(r), ll));
+		goto ret;
+
+	case OAS2RECV:
+		// x, y = <-c
+		// orderstmt made sure x is addressable.
+		*init = concat(*init, n->ninit);
+		n->ninit = nil;
+		r = n->rlist->n;
+		walkexprlistsafe(n->list, init);
+		walkexpr(&r->left, init);
+		if(isblank(n->list->n))
+			n1 = nodnil();
+		else
+			n1 = nod(OADDR, n->list->n, N);
+		n1->etype = 1; // addr does not escape
+		fn = chanfn("chanrecv2", 2, r->left->type);
+		r = mkcall1(fn, n->list->next->n->type, init, typename(r->left->type), r->left, n1);
+		n = nod(OAS, n->list->next->n, r);
+		typecheck(&n, Etop);
+		goto ret;
+
+	case OAS2MAPR:
+		// a,b = m[i];
+		*init = concat(*init, n->ninit);
+		n->ninit = nil;
+		r = n->rlist->n;
+		walkexprlistsafe(n->list, init);
+		walkexpr(&r->left, init);
+		walkexpr(&r->right, init);
+		t = r->left->type;
+		p = nil;
+		if(t->type->width <= 128) { // Check ../../runtime/hashmap.c:MAXVALUESIZE before changing.
+			switch(simsimtype(t->down)) {
+			case TINT32:
+			case TUINT32:
+				p = "mapaccess2_fast32";
+				break;
+			case TINT64:
+			case TUINT64:
+				p = "mapaccess2_fast64";
+				break;
+			case TSTRING:
+				p = "mapaccess2_faststr";
+				break;
+			}
+		}
+		if(p != nil) {
+			// fast versions take key by value
+			key = r->right;
+		} else {
+			// standard version takes key by reference
+			// orderexpr made sure key is addressable.
+			key = nod(OADDR, r->right, N);
+			p = "mapaccess2";
+		}
+
+		// from:
+		//   a,b = m[i]
+		// to:
+		//   var,b = mapaccess2*(t, m, i)
+		//   a = *var
+		a = n->list->n;
+		var = temp(ptrto(t->type));
+		var->typecheck = 1;
+		fn = mapfn(p, t);
+		r = mkcall1(fn, getoutargx(fn->type), init, typename(t), r->left, key);
+
+		// mapaccess2* returns a typed bool, but due to spec changes,
+		// the boolean result of i.(T) is now untyped so we make it the
+		// same type as the variable on the lhs.
+		if(!isblank(n->list->next->n))
+			r->type->type->down->type = n->list->next->n->type;
+		n->rlist = list1(r);
+		n->op = OAS2FUNC;
+		n->list->n = var;
+		walkexpr(&n, init);
+		*init = list(*init, n);
+		n = nod(OAS, a, nod(OIND, var, N));
+		typecheck(&n, Etop);
+		walkexpr(&n, init);
+		// mapaccess needs a zero value to be at least this big.
+		if(zerosize < t->type->width)
+			zerosize = t->type->width;
+		// TODO: ptr is always non-nil, so disable nil check for this OIND op.
+		goto ret;
+
+	case ODELETE:
+		*init = concat(*init, n->ninit);
+		n->ninit = nil;
+		map = n->list->n;
+		key = n->list->next->n;
+		walkexpr(&map, init);
+		walkexpr(&key, init);
+		// orderstmt made sure key is addressable.
+		key = nod(OADDR, key, N);
+		t = map->type;
+		n = mkcall1(mapfndel("mapdelete", t), T, init, typename(t), map, key);
+		goto ret;
+
+	case OAS2DOTTYPE:
+		// a,b = i.(T)
+		*init = concat(*init, n->ninit);
+		n->ninit = nil;
+		r = n->rlist->n;
+		walkexprlistsafe(n->list, init);
+		if(isblank(n->list->n) && !isinter(r->type)) {
+			strcpy(buf, "assert");
+			p = buf+strlen(buf);
+			if(isnilinter(r->left->type))
+				*p++ = 'E';
+			else
+				*p++ = 'I';
+			*p++ = '2';
+			*p++ = 'T';
+			*p++ = 'O';
+			*p++ = 'K';
+			*p = '\0';
+			
+			fn = syslook(buf, 1);
+
+			// runtime.assert(E|I)2TOK returns a typed bool, but due
+			// to spec changes, the boolean result of i.(T) is now untyped
+			// so we make it the same type as the variable on the lhs.
+			if(!isblank(n->list->next->n))
+				fn->type->type->down->type->type = n->list->next->n->type;
+			ll = list1(typename(r->type));
+			ll = list(ll, r->left);
+			argtype(fn, r->left->type);
+			n1 = nod(OCALL, fn, N);
+			n1->list = ll;
+			n = nod(OAS, n->list->next->n, n1);
+			typecheck(&n, Etop);
+			walkexpr(&n, init);
+			goto ret;
+		}
+
+		r->op = ODOTTYPE2;
+		walkexpr(&r, init);
+		ll = ascompatet(n->op, n->list, &r->type, 0, init);
+		n = liststmt(concat(list1(r), ll));
+		goto ret;
+
+	case ODOTTYPE:
+	case ODOTTYPE2:
+		// Build name of function: assertI2E2 etc.
+		strcpy(buf, "assert");
+		p = buf+strlen(buf);
+		if(isnilinter(n->left->type))
+			*p++ = 'E';
+		else
+			*p++ = 'I';
+		*p++ = '2';
+		if(isnilinter(n->type))
+			*p++ = 'E';
+		else if(isinter(n->type))
+			*p++ = 'I';
+		else
+			*p++ = 'T';
+		if(n->op == ODOTTYPE2)
+			*p++ = '2';
+		*p = '\0';
+
+		fn = syslook(buf, 1);
+		ll = list1(typename(n->type));
+		ll = list(ll, n->left);
+		argtype(fn, n->left->type);
+		argtype(fn, n->type);
+		n = nod(OCALL, fn, N);
+		n->list = ll;
+		typecheck(&n, Erv | Efnstruct);
+		walkexpr(&n, init);
+		goto ret;
+
+	case OCONVIFACE:
+		walkexpr(&n->left, init);
+
+		// Optimize convT2E as a two-word copy when T is uintptr-shaped.
+		if(isnilinter(n->type) && isdirectiface(n->left->type) && n->left->type->width == widthptr && isint[simsimtype(n->left->type)]) {
+			l = nod(OEFACE, typename(n->left->type), n->left);
+			l->type = n->type;
+			l->typecheck = n->typecheck;
+			n = l;
+			goto ret;
+		}
+
+		// Build name of function: convI2E etc.
+		// Not all names are possible
+		// (e.g., we'll never generate convE2E or convE2I).
+		strcpy(buf, "conv");
+		p = buf+strlen(buf);
+		if(isnilinter(n->left->type))
+			*p++ = 'E';
+		else if(isinter(n->left->type))
+			*p++ = 'I';
+		else
+			*p++ = 'T';
+		*p++ = '2';
+		if(isnilinter(n->type))
+			*p++ = 'E';
+		else
+			*p++ = 'I';
+		*p = '\0';
+
+		fn = syslook(buf, 1);
+		ll = nil;
+		if(!isinter(n->left->type))
+			ll = list(ll, typename(n->left->type));
+		if(!isnilinter(n->type))
+			ll = list(ll, typename(n->type));
+		if(!isinter(n->left->type) && !isnilinter(n->type)){
+			sym = pkglookup(smprint("%-T.%-T", n->left->type, n->type), itabpkg);
+			if(sym->def == N) {
+				l = nod(ONAME, N, N);
+				l->sym = sym;
+				l->type = ptrto(types[TUINT8]);
+				l->addable = 1;
+				l->class = PEXTERN;
+				l->xoffset = 0;
+				sym->def = l;
+				ggloblsym(sym, widthptr, DUPOK|NOPTR);
+			}
+			l = nod(OADDR, sym->def, N);
+			l->addable = 1;
+			ll = list(ll, l);
+
+			if(isdirectiface(n->left->type) && n->left->type->width == widthptr && isint[simsimtype(n->left->type)]) {
+				/* For pointer types, we can make a special form of optimization
+				 *
+				 * These statements are put onto the expression init list:
+				 * 	Itab *tab = atomicloadtype(&cache);
+				 * 	if(tab == nil)
+				 * 		tab = typ2Itab(type, itype, &cache);
+				 *
+				 * The CONVIFACE expression is replaced with this:
+				 * 	OEFACE{tab, ptr};
+				 */
+				l = temp(ptrto(types[TUINT8]));
+
+				n1 = nod(OAS, l, sym->def);
+				typecheck(&n1, Etop);
+				*init = list(*init, n1);
+
+				fn = syslook("typ2Itab", 1);
+				n1 = nod(OCALL, fn, N);
+				n1->list = ll;
+				typecheck(&n1, Erv);
+				walkexpr(&n1, init);
+
+				n2 = nod(OIF, N, N);
+				n2->ntest = nod(OEQ, l, nodnil());
+				n2->nbody = list1(nod(OAS, l, n1));
+				n2->likely = -1;
+				typecheck(&n2, Etop);
+				*init = list(*init, n2);
+
+				l = nod(OEFACE, l, n->left);
+				l->typecheck = n->typecheck; 
+				l->type = n->type;
+				n = l;
+				goto ret;
+			}
+		}
+		if(isinter(n->left->type)) {
+			ll = list(ll, n->left);
+		} else {
+			// regular types are passed by reference to avoid C vararg calls
+			// orderexpr arranged for n->left to be a temporary for all
+			// the conversions it could see. comparison of an interface
+			// with a non-interface, especially in a switch on interface value
+			// with non-interface cases, is not visible to orderstmt, so we
+			// have to fall back on allocating a temp here.
+			if(islvalue(n->left))
+				ll = list(ll, nod(OADDR, n->left, N));
+			else
+				ll = list(ll, nod(OADDR, copyexpr(n->left, n->left->type, init), N));
+		}
+		argtype(fn, n->left->type);
+		argtype(fn, n->type);
+		dowidth(fn->type);
+		n = nod(OCALL, fn, N);
+		n->list = ll;
+		typecheck(&n, Erv);
+		walkexpr(&n, init);
+		goto ret;
+
+	case OCONV:
+	case OCONVNOP:
+		if(thechar == '5') {
+			if(isfloat[n->left->type->etype]) {
+				if(n->type->etype == TINT64) {
+					n = mkcall("float64toint64", n->type, init, conv(n->left, types[TFLOAT64]));
+					goto ret;
+				}
+				if(n->type->etype == TUINT64) {
+					n = mkcall("float64touint64", n->type, init, conv(n->left, types[TFLOAT64]));
+					goto ret;
+				}
+			}
+			if(isfloat[n->type->etype]) {
+				if(n->left->type->etype == TINT64) {
+					n = mkcall("int64tofloat64", n->type, init, conv(n->left, types[TINT64]));
+					goto ret;
+				}
+				if(n->left->type->etype == TUINT64) {
+					n = mkcall("uint64tofloat64", n->type, init, conv(n->left, types[TUINT64]));
+					goto ret;
+				}
+			}
+		}
+		walkexpr(&n->left, init);
+		goto ret;
+
+	case OANDNOT:
+		walkexpr(&n->left, init);
+		n->op = OAND;
+		n->right = nod(OCOM, n->right, N);
+		typecheck(&n->right, Erv);
+		walkexpr(&n->right, init);
+		goto ret;
+
+	case OMUL:
+		walkexpr(&n->left, init);
+		walkexpr(&n->right, init);
+		walkmul(&n, init);
+		goto ret;
+
+	case ODIV:
+	case OMOD:
+		walkexpr(&n->left, init);
+		walkexpr(&n->right, init);
+		/*
+		 * rewrite complex div into function call.
+		 */
+		et = n->left->type->etype;
+		if(iscomplex[et] && n->op == ODIV) {
+			t = n->type;
+			n = mkcall("complex128div", types[TCOMPLEX128], init,
+				conv(n->left, types[TCOMPLEX128]),
+				conv(n->right, types[TCOMPLEX128]));
+			n = conv(n, t);
+			goto ret;
+		}
+		// Nothing to do for float divisions.
+		if(isfloat[et])
+			goto ret;
+
+		// Try rewriting as shifts or magic multiplies.
+		walkdiv(&n, init);
+
+		/*
+		 * rewrite 64-bit div and mod into function calls
+		 * on 32-bit architectures.
+		 */
+		switch(n->op) {
+		case OMOD:
+		case ODIV:
+			if(widthreg >= 8 || (et != TUINT64 && et != TINT64))
+				goto ret;
+			if(et == TINT64)
+				strcpy(namebuf, "int64");
+			else
+				strcpy(namebuf, "uint64");
+			if(n->op == ODIV)
+				strcat(namebuf, "div");
+			else
+				strcat(namebuf, "mod");
+			n = mkcall(namebuf, n->type, init,
+				conv(n->left, types[et]), conv(n->right, types[et]));
+			break;
+		default:
+			break;
+		}
+		goto ret;
+
+	case OINDEX:
+		walkexpr(&n->left, init);
+		// save the original node for bounds checking elision.
+		// If it was a ODIV/OMOD walk might rewrite it.
+		r = n->right;
+		walkexpr(&n->right, init);
+
+		// if range of type cannot exceed static array bound,
+		// disable bounds check.
+		if(n->bounded)
+			goto ret;
+		t = n->left->type;
+		if(t != T && isptr[t->etype])
+			t = t->type;
+		if(isfixedarray(t)) {
+			n->bounded = bounded(r, t->bound);
+			if(debug['m'] && n->bounded && !isconst(n->right, CTINT))
+				warn("index bounds check elided");
+			if(smallintconst(n->right) && !n->bounded)
+				yyerror("index out of bounds");
+		} else if(isconst(n->left, CTSTR)) {
+			n->bounded = bounded(r, n->left->val.u.sval->len);
+			if(debug['m'] && n->bounded && !isconst(n->right, CTINT))
+				warn("index bounds check elided");
+			if(smallintconst(n->right)) {
+				if(!n->bounded)
+					yyerror("index out of bounds");
+				else {
+					// replace "abc"[1] with 'b'.
+					// delayed until now because "abc"[1] is not
+					// an ideal constant.
+					v = mpgetfix(n->right->val.u.xval);
+					nodconst(n, n->type, n->left->val.u.sval->s[v]);
+					n->typecheck = 1;
+				}
+			}
+		}
+
+		if(isconst(n->right, CTINT))
+		if(mpcmpfixfix(n->right->val.u.xval, &mpzero) < 0 ||
+		   mpcmpfixfix(n->right->val.u.xval, maxintval[TINT]) > 0)
+			yyerror("index out of bounds");
+		goto ret;
+
+	case OINDEXMAP:
+		if(n->etype == 1)
+			goto ret;
+		walkexpr(&n->left, init);
+		walkexpr(&n->right, init);
+
+		t = n->left->type;
+		p = nil;
+		if(t->type->width <= 128) {  // Check ../../runtime/hashmap.c:MAXVALUESIZE before changing.
+			switch(simsimtype(t->down)) {
+			case TINT32:
+			case TUINT32:
+				p = "mapaccess1_fast32";
+				break;
+			case TINT64:
+			case TUINT64:
+				p = "mapaccess1_fast64";
+				break;
+			case TSTRING:
+				p = "mapaccess1_faststr";
+				break;
+			}
+		}
+		if(p != nil) {
+			// fast versions take key by value
+			key = n->right;
+		} else {
+			// standard version takes key by reference.
+			// orderexpr made sure key is addressable.
+			key = nod(OADDR, n->right, N);
+			p = "mapaccess1";
+		}
+		n = mkcall1(mapfn(p, t), ptrto(t->type), init, typename(t), n->left, key);
+		n = nod(OIND, n, N);
+		n->type = t->type;
+		n->typecheck = 1;
+		// mapaccess needs a zero value to be at least this big.
+		if(zerosize < t->type->width)
+			zerosize = t->type->width;
+		goto ret;
+
+	case ORECV:
+		fatal("walkexpr ORECV"); // should see inside OAS only
+
+	case OSLICE:
+		if(n->right != N && n->right->left == N && n->right->right == N) { // noop
+			walkexpr(&n->left, init);
+			n = n->left;
+			goto ret;
+		}
+		// fallthrough
+	case OSLICEARR:
+	case OSLICESTR:
+		if(n->right == N) // already processed
+			goto ret;
+
+		walkexpr(&n->left, init);
+		// cgen_slice can't handle string literals as source
+		// TODO the OINDEX case is a bug elsewhere that needs to be traced.  it causes a crash on ([2][]int{ ... })[1][lo:hi]
+		if((n->op == OSLICESTR && n->left->op == OLITERAL) || (n->left->op == OINDEX))
+			n->left = copyexpr(n->left, n->left->type, init);
+		else
+			n->left = safeexpr(n->left, init);
+		walkexpr(&n->right->left, init);
+		n->right->left = safeexpr(n->right->left, init);
+		walkexpr(&n->right->right, init);
+		n->right->right = safeexpr(n->right->right, init);
+		n = sliceany(n, init);  // chops n->right, sets n->list
+		goto ret;
+	
+	case OSLICE3:
+	case OSLICE3ARR:
+		if(n->right == N) // already processed
+			goto ret;
+
+		walkexpr(&n->left, init);
+		// TODO the OINDEX case is a bug elsewhere that needs to be traced.  it causes a crash on ([2][]int{ ... })[1][lo:hi]
+		// TODO the comment on the previous line was copied from case OSLICE. it might not even be true.
+		if(n->left->op == OINDEX)
+			n->left = copyexpr(n->left, n->left->type, init);
+		else
+			n->left = safeexpr(n->left, init);
+		walkexpr(&n->right->left, init);
+		n->right->left = safeexpr(n->right->left, init);
+		walkexpr(&n->right->right->left, init);
+		n->right->right->left = safeexpr(n->right->right->left, init);
+		walkexpr(&n->right->right->right, init);
+		n->right->right->right = safeexpr(n->right->right->right, init);
+		n = sliceany(n, init);  // chops n->right, sets n->list
+		goto ret;
+
+	case OADDR:
+		walkexpr(&n->left, init);
+		goto ret;
+
+	case ONEW:
+		if(n->esc == EscNone && n->type->type->width < (1<<16)) {
+			r = temp(n->type->type);
+			r = nod(OAS, r, N);  // zero temp
+			typecheck(&r, Etop);
+			*init = list(*init, r);
+			r = nod(OADDR, r->left, N);
+			typecheck(&r, Erv);
+			n = r;
+		} else {
+			n = callnew(n->type->type);
+		}
+		goto ret;
+
+	case OCMPSTR:
+		// If one argument to the comparison is an empty string,
+		// comparing the lengths instead will yield the same result
+		// without the function call.
+		if((isconst(n->left, CTSTR) && n->left->val.u.sval->len == 0) ||
+		   (isconst(n->right, CTSTR) && n->right->val.u.sval->len == 0)) {
+			r = nod(n->etype, nod(OLEN, n->left, N), nod(OLEN, n->right, N));
+			typecheck(&r, Erv);
+			walkexpr(&r, init);
+			r->type = n->type;
+			n = r;
+			goto ret;
+		}
+
+		// s + "badgerbadgerbadger" == "badgerbadgerbadger"
+		if((n->etype == OEQ || n->etype == ONE) &&
+		   isconst(n->right, CTSTR) &&
+		   n->left->op == OADDSTR && count(n->left->list) == 2 &&
+		   isconst(n->left->list->next->n, CTSTR) &&
+		   cmpslit(n->right, n->left->list->next->n) == 0) {
+			r = nod(n->etype, nod(OLEN, n->left->list->n, N), nodintconst(0));
+			typecheck(&r, Erv);
+			walkexpr(&r, init);
+			r->type = n->type;
+			n = r;
+			goto ret;
+		}
+
+		if(n->etype == OEQ || n->etype == ONE) {
+			// prepare for rewrite below
+			n->left = cheapexpr(n->left, init);
+			n->right = cheapexpr(n->right, init);
+
+			r = mkcall("eqstring", types[TBOOL], init,
+				conv(n->left, types[TSTRING]),
+				conv(n->right, types[TSTRING]));
+
+			// quick check of len before full compare for == or !=
+			if(n->etype == OEQ) {
+				// len(left) == len(right) && eqstring(left, right)
+				r = nod(OANDAND, nod(OEQ, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r);
+			} else {
+				// len(left) != len(right) || !eqstring(left, right)
+				r = nod(ONOT, r, N);
+				r = nod(OOROR, nod(ONE, nod(OLEN, n->left, N), nod(OLEN, n->right, N)), r);
+			}
+			typecheck(&r, Erv);
+			walkexpr(&r, nil);
+		} else {
+			// sys_cmpstring(s1, s2) :: 0
+			r = mkcall("cmpstring", types[TINT], init,
+				conv(n->left, types[TSTRING]),
+				conv(n->right, types[TSTRING]));
+			r = nod(n->etype, r, nodintconst(0));
+		}
+
+		typecheck(&r, Erv);
+		if(n->type->etype != TBOOL) fatal("cmp %T", n->type);
+		r->type = n->type;
+		n = r;
+		goto ret;
+
+	case OADDSTR:
+		n = addstr(n, init);
+		goto ret;
+	
+	case OAPPEND:
+		if(n->isddd)
+			n = appendslice(n, init); // also works for append(slice, string).
+		else
+			n = append(n, init);
+		goto ret;
+
+	case OCOPY:
+		n = copyany(n, init, flag_race);
+		goto ret;
+
+	case OCLOSE:
+		// cannot use chanfn - closechan takes any, not chan any
+		fn = syslook("closechan", 1);
+		argtype(fn, n->left->type);
+		n = mkcall1(fn, T, init, n->left);
+		goto ret;
+
+	case OMAKECHAN:
+		n = mkcall1(chanfn("makechan", 1, n->type), n->type, init,
+			typename(n->type),
+			conv(n->left, types[TINT64]));
+		goto ret;
+
+	case OMAKEMAP:
+		t = n->type;
+
+		fn = syslook("makemap", 1);
+		argtype(fn, t->down);	// any-1
+		argtype(fn, t->type);	// any-2
+
+		n = mkcall1(fn, n->type, init,
+			typename(n->type),
+			conv(n->left, types[TINT64]));
+		goto ret;
+
+	case OMAKESLICE:
+		l = n->left;
+		r = n->right;
+		if(r == nil)
+			l = r = safeexpr(l, init);
+		t = n->type;
+		if(n->esc == EscNone
+			&& smallintconst(l) && smallintconst(r)
+			&& (t->type->width == 0 || mpgetfix(r->val.u.xval) < (1ULL<<16) / t->type->width)) {
+			// var arr [r]T
+			// n = arr[:l]
+			t = aindex(r, t->type); // [r]T
+			var = temp(t);
+			a = nod(OAS, var, N); // zero temp
+			typecheck(&a, Etop);
+			*init = list(*init, a);
+			r = nod(OSLICE, var, nod(OKEY, N, l)); // arr[:l]
+			r = conv(r, n->type); // in case n->type is named.
+			typecheck(&r, Erv);
+			walkexpr(&r, init);
+			n = r;
+		} else {
+			// makeslice(t *Type, nel int64, max int64) (ary []any)
+			fn = syslook("makeslice", 1);
+			argtype(fn, t->type);			// any-1
+			n = mkcall1(fn, n->type, init,
+				typename(n->type),
+				conv(l, types[TINT64]),
+				conv(r, types[TINT64]));
+		}
+		goto ret;
+
+	case ORUNESTR:
+		// sys_intstring(v)
+		n = mkcall("intstring", n->type, init,
+			conv(n->left, types[TINT64]));
+		goto ret;
+
+	case OARRAYBYTESTR:
+		// slicebytetostring([]byte) string;
+		n = mkcall("slicebytetostring", n->type, init, n->left);
+		goto ret;
+
+	case OARRAYBYTESTRTMP:
+		// slicebytetostringtmp([]byte) string;
+		n = mkcall("slicebytetostringtmp", n->type, init, n->left);
+		goto ret;
+
+	case OARRAYRUNESTR:
+		// slicerunetostring([]rune) string;
+		n = mkcall("slicerunetostring", n->type, init, n->left);
+		goto ret;
+
+	case OSTRARRAYBYTE:
+		// stringtoslicebyte(string) []byte;
+		n = mkcall("stringtoslicebyte", n->type, init, conv(n->left, types[TSTRING]));
+		goto ret;
+
+	case OSTRARRAYRUNE:
+		// stringtoslicerune(string) []rune
+		n = mkcall("stringtoslicerune", n->type, init, n->left);
+		goto ret;
+
+	case OCMPIFACE:
+		// ifaceeq(i1 any-1, i2 any-2) (ret bool);
+		if(!eqtype(n->left->type, n->right->type))
+			fatal("ifaceeq %O %T %T", n->op, n->left->type, n->right->type);
+		if(isnilinter(n->left->type))
+			fn = syslook("efaceeq", 1);
+		else
+			fn = syslook("ifaceeq", 1);
+
+		n->right = cheapexpr(n->right, init);
+		n->left = cheapexpr(n->left, init);
+		argtype(fn, n->right->type);
+		argtype(fn, n->left->type);
+		r = mkcall1(fn, n->type, init, n->left, n->right);
+		if(n->etype == ONE)
+			r = nod(ONOT, r, N);
+		
+		// check itable/type before full compare.
+		if(n->etype == OEQ)
+			r = nod(OANDAND, nod(OEQ, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r);
+		else
+			r = nod(OOROR, nod(ONE, nod(OITAB, n->left, N), nod(OITAB, n->right, N)), r);
+		typecheck(&r, Erv);
+		walkexpr(&r, init);
+		r->type = n->type;
+		n = r;
+		goto ret;
+
+	case OARRAYLIT:
+	case OMAPLIT:
+	case OSTRUCTLIT:
+	case OPTRLIT:
+		var = temp(n->type);
+		anylit(0, n, var, init);
+		n = var;
+		goto ret;
+
+	case OSEND:
+		n1 = n->right;
+		n1 = assignconv(n1, n->left->type->type, "chan send");
+		walkexpr(&n1, init);
+		n1 = nod(OADDR, n1, N);
+		n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, typename(n->left->type), n->left, n1);
+		goto ret;
+
+	case OCLOSURE:
+		n = walkclosure(n, init);
+		goto ret;
+	
+	case OCALLPART:
+		n = walkpartialcall(n, init);
+		goto ret;
+	}
+	fatal("missing switch %O", n->op);
+
+ret:
+	// Expressions that are constant at run time but not
+	// considered const by the language spec are not turned into
+	// constants until walk. For example, if n is y%1 == 0, the
+	// walk of y%1 may have replaced it by 0.
+	// Check whether n with its updated args is itself now a constant.
+	t = n->type;
+	evconst(n);
+	n->type = t;
+	if(n->op == OLITERAL)
+		typecheck(&n, Erv);
+
+	ullmancalc(n);
+
+	if(debug['w'] && n != N)
+		dump("walk", n);
+
+	lineno = lno;
+	*np = n;
+}
+
+static Node*
+ascompatee1(int op, Node *l, Node *r, NodeList **init)
+{
+	Node *n;
+	USED(op);
+	
+	// convas will turn map assigns into function calls,
+	// making it impossible for reorder3 to work.
+	n = nod(OAS, l, r);
+	if(l->op == OINDEXMAP)
+		return n;
+
+	return convas(n, init);
+}
+
+static NodeList*
+ascompatee(int op, NodeList *nl, NodeList *nr, NodeList **init)
+{
+	NodeList *ll, *lr, *nn;
+
+	/*
+	 * check assign expression list to
+	 * a expression list. called in
+	 *	expr-list = expr-list
+	 */
+
+	// ensure order of evaluation for function calls
+	for(ll=nl; ll; ll=ll->next)
+		ll->n = safeexpr(ll->n, init);
+	for(lr=nr; lr; lr=lr->next)
+		lr->n = safeexpr(lr->n, init);
+
+	nn = nil;
+	for(ll=nl, lr=nr; ll && lr; ll=ll->next, lr=lr->next) {
+		// Do not generate 'x = x' during return. See issue 4014.
+		if(op == ORETURN && ll->n == lr->n)
+			continue;
+		nn = list(nn, ascompatee1(op, ll->n, lr->n, init));
+	}
+
+	// cannot happen: caller checked that lists had same length
+	if(ll || lr)
+		yyerror("error in shape across %+H %O %+H / %d %d [%s]", nl, op, nr, count(nl), count(nr), curfn->nname->sym->name);
+	return nn;
+}
+
+/*
+ * l is an lv and rt is the type of an rv
+ * return 1 if this implies a function call
+ * evaluating the lv or a function call
+ * in the conversion of the types
+ */
+static int
+fncall(Node *l, Type *rt)
+{
+	Node r;
+
+	if(l->ullman >= UINF || l->op == OINDEXMAP)
+		return 1;
+	memset(&r, 0, sizeof r);
+	if(needwritebarrier(l, &r))
+		return 1;
+	if(eqtype(l->type, rt))
+		return 0;
+	return 1;
+}
+
+static NodeList*
+ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
+{
+	Node *l, *tmp, *a;
+	NodeList *ll;
+	Type *r;
+	Iter saver;
+	int ucount;
+	NodeList *nn, *mm;
+
+	USED(op);
+
+	/*
+	 * check assign type list to
+	 * a expression list. called in
+	 *	expr-list = func()
+	 */
+	r = structfirst(&saver, nr);
+	nn = nil;
+	mm = nil;
+	ucount = 0;
+	for(ll=nl; ll; ll=ll->next) {
+		if(r == T)
+			break;
+		l = ll->n;
+		if(isblank(l)) {
+			r = structnext(&saver);
+			continue;
+		}
+
+		// any lv that causes a fn call must be
+		// deferred until all the return arguments
+		// have been pulled from the output arguments
+		if(fncall(l, r->type)) {
+			tmp = temp(r->type);
+			typecheck(&tmp, Erv);
+			a = nod(OAS, l, tmp);
+			a = convas(a, init);
+			mm = list(mm, a);
+			l = tmp;
+		}
+
+		a = nod(OAS, l, nodarg(r, fp));
+		a = convas(a, init);
+		ullmancalc(a);
+		if(a->ullman >= UINF) {
+			dump("ascompatet ucount", a);
+			ucount++;
+		}
+		nn = list(nn, a);
+		r = structnext(&saver);
+	}
+
+	if(ll != nil || r != T)
+		yyerror("ascompatet: assignment count mismatch: %d = %d",
+			count(nl), structcount(*nr));
+
+	if(ucount)
+		fatal("ascompatet: too many function calls evaluating parameters");
+	return concat(nn, mm);
+}
+
+ /*
+ * package all the arguments that match a ... T parameter into a []T.
+ */
+static NodeList*
+mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, Node *ddd)
+{
+	Node *a, *n;
+	Type *tslice;
+	int esc;
+	
+	esc = EscUnknown;
+	if(ddd != nil)
+		esc = ddd->esc;
+	
+	tslice = typ(TARRAY);
+	tslice->type = l->type->type;
+	tslice->bound = -1;
+
+	if(count(lr0) == 0) {
+		n = nodnil();
+		n->type = tslice;
+	} else {
+		n = nod(OCOMPLIT, N, typenod(tslice));
+		if(ddd != nil)
+			n->alloc = ddd->alloc; // temporary to use
+		n->list = lr0;
+		n->esc = esc;
+		typecheck(&n, Erv);
+		if(n->type == T)
+			fatal("mkdotargslice: typecheck failed");
+		walkexpr(&n, init);
+	}
+
+	a = nod(OAS, nodarg(l, fp), n);
+	nn = list(nn, convas(a, init));
+	return nn;
+}
+
+/*
+ * helpers for shape errors
+ */
+static char*
+dumptypes(Type **nl, char *what)
+{
+	int first;
+	Type *l;
+	Iter savel;
+	Fmt fmt;
+
+	fmtstrinit(&fmt);
+	fmtprint(&fmt, "\t");
+	first = 1;
+	for(l = structfirst(&savel, nl); l != T; l = structnext(&savel)) {
+		if(first)
+			first = 0;
+		else
+			fmtprint(&fmt, ", ");
+		fmtprint(&fmt, "%T", l);
+	}
+	if(first)
+		fmtprint(&fmt, "[no arguments %s]", what);
+	return fmtstrflush(&fmt);
+}
+
+static char*
+dumpnodetypes(NodeList *l, char *what)
+{
+	int first;
+	Node *r;
+	Fmt fmt;
+
+	fmtstrinit(&fmt);
+	fmtprint(&fmt, "\t");
+	first = 1;
+	for(; l; l=l->next) {
+		r = l->n;
+		if(first)
+			first = 0;
+		else
+			fmtprint(&fmt, ", ");
+		fmtprint(&fmt, "%T", r->type);
+	}
+	if(first)
+		fmtprint(&fmt, "[no arguments %s]", what);
+	return fmtstrflush(&fmt);
+}
+
+/*
+ * check assign expression list to
+ * a type list. called in
+ *	return expr-list
+ *	func(expr-list)
+ */
+static NodeList*
+ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
+{
+	Type *l, *ll;
+	Node *r, *a;
+	NodeList *nn, *lr0, *alist;
+	Iter savel;
+	char *l1, *l2;
+
+	lr0 = lr;
+	l = structfirst(&savel, nl);
+	r = N;
+	if(lr)
+		r = lr->n;
+	nn = nil;
+
+	// f(g()) where g has multiple return values
+	if(r != N && lr->next == nil && r->type->etype == TSTRUCT && r->type->funarg) {
+		// optimization - can do block copy
+		if(eqtypenoname(r->type, *nl)) {
+			a = nodarg(*nl, fp);
+			r = nod(OCONVNOP, r, N);
+			r->type = a->type;
+			nn = list1(convas(nod(OAS, a, r), init));
+			goto ret;
+		}
+
+		// conversions involved.
+		// copy into temporaries.
+		alist = nil;
+		for(l=structfirst(&savel, &r->type); l; l=structnext(&savel)) {
+			a = temp(l->type);
+			alist = list(alist, a);
+		}
+		a = nod(OAS2, N, N);
+		a->list = alist;
+		a->rlist = lr;
+		typecheck(&a, Etop);
+		walkstmt(&a);
+		*init = list(*init, a);
+		lr = alist;
+		r = lr->n;
+		l = structfirst(&savel, nl);
+	}
+
+loop:
+	if(l != T && l->isddd) {
+		// the ddd parameter must be last
+		ll = structnext(&savel);
+		if(ll != T)
+			yyerror("... must be last argument");
+
+		// special case --
+		// only if we are assigning a single ddd
+		// argument to a ddd parameter then it is
+		// passed thru unencapsulated
+		if(r != N && lr->next == nil && isddd && eqtype(l->type, r->type)) {
+			a = nod(OAS, nodarg(l, fp), r);
+			a = convas(a, init);
+			nn = list(nn, a);
+			goto ret;
+		}
+
+		// normal case -- make a slice of all
+		// remaining arguments and pass it to
+		// the ddd parameter.
+		nn = mkdotargslice(lr, nn, l, fp, init, call->right);
+		goto ret;
+	}
+
+	if(l == T || r == N) {
+		if(l != T || r != N) {
+			l1 = dumptypes(nl, "expected");
+			l2 = dumpnodetypes(lr0, "given");
+			if(l != T)
+				yyerror("not enough arguments to %O\n%s\n%s", op, l1, l2);
+			else
+				yyerror("too many arguments to %O\n%s\n%s", op, l1, l2);
+		}
+		goto ret;
+	}
+
+	a = nod(OAS, nodarg(l, fp), r);
+	a = convas(a, init);
+	nn = list(nn, a);
+
+	l = structnext(&savel);
+	r = N;
+	lr = lr->next;
+	if(lr != nil)
+		r = lr->n;
+	goto loop;
+
+ret:
+	for(lr=nn; lr; lr=lr->next)
+		lr->n->typecheck = 1;
+	return nn;
+}
+
+// generate code for print
+static Node*
+walkprint(Node *nn, NodeList **init)
+{
+	Node *r;
+	Node *n;
+	NodeList *l, *all;
+	Node *on;
+	Type *t;
+	int notfirst, et, op;
+	NodeList *calls;
+
+	on = nil;
+	op = nn->op;
+	all = nn->list;
+	calls = nil;
+	notfirst = 0;
+
+	for(l=all; l; l=l->next) {
+		if(notfirst) {
+			calls = list(calls, mkcall("printsp", T, init));
+		}
+		notfirst = op == OPRINTN;
+
+		n = l->n;
+		if(n->op == OLITERAL) {
+			switch(n->val.ctype) {
+			case CTRUNE:
+				defaultlit(&n, runetype);
+				break;
+			case CTINT:
+				defaultlit(&n, types[TINT64]);
+				break;
+			case CTFLT:
+				defaultlit(&n, types[TFLOAT64]);
+				break;
+			}
+		}
+		if(n->op != OLITERAL && n->type && n->type->etype == TIDEAL)
+			defaultlit(&n, types[TINT64]);
+		defaultlit(&n, nil);
+		l->n = n;
+		if(n->type == T || n->type->etype == TFORW)
+			continue;
+
+		t = n->type;
+		et = n->type->etype;
+		if(isinter(n->type)) {
+			if(isnilinter(n->type))
+				on = syslook("printeface", 1);
+			else
+				on = syslook("printiface", 1);
+			argtype(on, n->type);		// any-1
+		} else if(isptr[et] || et == TCHAN || et == TMAP || et == TFUNC || et == TUNSAFEPTR) {
+			on = syslook("printpointer", 1);
+			argtype(on, n->type);	// any-1
+		} else if(isslice(n->type)) {
+			on = syslook("printslice", 1);
+			argtype(on, n->type);	// any-1
+		} else if(isint[et]) {
+			if(et == TUINT64) {
+				if((t->sym->pkg == runtimepkg || compiling_runtime) && strcmp(t->sym->name, "hex") == 0)
+					on = syslook("printhex", 0);
+				else
+					on = syslook("printuint", 0);
+			} else
+				on = syslook("printint", 0);
+		} else if(isfloat[et]) {
+			on = syslook("printfloat", 0);
+		} else if(iscomplex[et]) {
+			on = syslook("printcomplex", 0);
+		} else if(et == TBOOL) {
+			on = syslook("printbool", 0);
+		} else if(et == TSTRING) {
+			on = syslook("printstring", 0);
+		} else {
+			badtype(OPRINT, n->type, T);
+			continue;
+		}
+
+		t = *getinarg(on->type);
+		if(t != nil)
+			t = t->type;
+		if(t != nil)
+			t = t->type;
+
+		if(!eqtype(t, n->type)) {
+			n = nod(OCONV, n, N);
+			n->type = t;
+		}
+
+		r = nod(OCALL, on, N);
+		r->list = list1(n);
+		calls = list(calls, r);
+	}
+
+	if(op == OPRINTN)
+		calls = list(calls, mkcall("printnl", T, nil));
+	typechecklist(calls, Etop);
+	walkexprlist(calls, init);
+
+	r = nod(OEMPTY, N, N);
+	typecheck(&r, Etop);
+	walkexpr(&r, init);
+	r->ninit = calls;
+	return r;
+}
+
+Node*
+callnew(Type *t)
+{
+	Node *fn;
+
+	dowidth(t);
+	fn = syslook("newobject", 1);
+	argtype(fn, t);
+	return mkcall1(fn, ptrto(t), nil, typename(t));
+}
+
+static int
+isstack(Node *n)
+{
+	while(n->op == ODOT || n->op == OPAREN || n->op == OCONVNOP || n->op == OINDEX && isfixedarray(n->left->type))
+		n = n->left;
+	
+	switch(n->op) {
+	case OINDREG:
+		// OINDREG only ends up in walk if it's indirect of SP.
+		return 1;
+
+	case ONAME:
+		switch(n->class) {
+		case PAUTO:
+		case PPARAM:
+		case PPARAMOUT:
+			return 1;
+		}
+		break;
+	}
+	
+	return 0;
+}
+
+static int
+isglobal(Node *n)
+{
+	while(n->op == ODOT || n->op == OPAREN || n->op == OCONVNOP || n->op == OINDEX && isfixedarray(n->left->type))
+		n = n->left;
+	
+	switch(n->op) {
+	case ONAME:
+		switch(n->class) {
+		case PEXTERN:
+			return 1;
+		}
+		break;
+	}
+	
+	return 0;
+}
+
+// Do we need a write barrier for the assignment l = r?
+int
+needwritebarrier(Node *l, Node *r)
+{
+	if(!use_writebarrier)
+		return 0;
+
+	if(l == N || isblank(l))
+		return 0;
+
+	// No write barrier for write of non-pointers.
+	dowidth(l->type);
+	if(!haspointers(l->type))
+		return 0;
+
+	// No write barrier for write to stack.
+	if(isstack(l))
+		return 0;
+
+	// No write barrier for implicit or explicit zeroing.
+	if(r == N || iszero(r))
+		return 0;
+
+	// No write barrier for initialization to constant.
+	if(r->op == OLITERAL)
+		return 0;
+
+	// No write barrier for storing static (read-only) data.
+	if(r->op == ONAME && strncmp(r->sym->name, "statictmp_", 10) == 0)
+		return 0;
+
+	// No write barrier for storing address of stack values,
+	// which are guaranteed only to be written to the stack.
+	if(r->op == OADDR && isstack(r->left))
+		return 0;
+
+	// No write barrier for storing address of global, which
+	// is live no matter what.
+	if(r->op == OADDR && isglobal(r->left))
+		return 0;
+
+	// No write barrier for reslice: x = x[0:y] or x = append(x, ...).
+	// Both are compiled to modify x directly.
+	// In the case of append, a write barrier may still be needed
+	// if the underlying array grows, but the append code can
+	// generate the write barrier directly in that case.
+	// (It does not yet, but the cost of the write barrier will be
+	// small compared to the cost of the allocation.)
+	if(r->reslice) {
+		switch(r->op) {
+		case OSLICE:
+		case OSLICE3:
+		case OSLICESTR:
+		case OAPPEND:
+			break;
+		default:
+			dump("bad reslice-l", l);
+			dump("bad reslice-r", r);
+			break;
+		}
+		return 0;
+	}
+
+	// Otherwise, be conservative and use write barrier.
+	return 1;
+}
+
+// TODO(rsc): Perhaps componentgen should run before this.
+static Node*
+applywritebarrier(Node *n, NodeList **init)
+{
+	Node *l, *r;
+	Type *t;
+
+	if(n->left && n->right && needwritebarrier(n->left, n->right)) {
+		t = n->left->type;
+		l = nod(OADDR, n->left, N);
+		l->etype = 1; // addr does not escape
+		if(t->width == widthptr) {
+			n = mkcall1(writebarrierfn("writebarrierptr", t, n->right->type), T, init,
+				l, n->right);
+		} else if(t->etype == TSTRING) {
+			n = mkcall1(writebarrierfn("writebarrierstring", t, n->right->type), T, init,
+				l, n->right);
+		} else if(isslice(t)) {
+			n = mkcall1(writebarrierfn("writebarrierslice", t, n->right->type), T, init,
+				l, n->right);
+		} else if(isinter(t)) {
+			n = mkcall1(writebarrierfn("writebarrieriface", t, n->right->type), T, init,
+				l, n->right);
+		} else if(t->width == 2*widthptr) {
+			n = mkcall1(writebarrierfn("writebarrierfat2", t, n->right->type), T, init,
+				l, nodnil(), n->right);
+		} else if(t->width == 3*widthptr) {
+			n = mkcall1(writebarrierfn("writebarrierfat3", t, n->right->type), T, init,
+				l, nodnil(), n->right);
+		} else if(t->width == 4*widthptr) {
+			n = mkcall1(writebarrierfn("writebarrierfat4", t, n->right->type), T, init,
+				l, nodnil(), n->right);
+		} else {
+			r = n->right;
+			while(r->op == OCONVNOP)
+				r = r->left;
+			r = nod(OADDR, r, N);
+			r->etype = 1; // addr does not escape
+			//warnl(n->lineno, "writebarrierfat %T %N", t, r);
+			n = mkcall1(writebarrierfn("writebarrierfat", t, r->left->type), T, init,
+				typename(t), l, r);
+		}
+	}
+	return n;
+}
+
+static Node*
+convas(Node *n, NodeList **init)
+{
+	Type *lt, *rt;
+	Node *map, *key, *val;
+
+	if(n->op != OAS)
+		fatal("convas: not OAS %O", n->op);
+
+	n->typecheck = 1;
+
+	if(n->left == N || n->right == N)
+		goto out;
+
+	lt = n->left->type;
+	rt = n->right->type;
+	if(lt == T || rt == T)
+		goto out;
+
+	if(isblank(n->left)) {
+		defaultlit(&n->right, T);
+		goto out;
+	}
+
+	if(n->left->op == OINDEXMAP) {
+		map = n->left->left;
+		key = n->left->right;
+		val = n->right;
+		walkexpr(&map, init);
+		walkexpr(&key, init);
+		walkexpr(&val, init);
+		// orderexpr made sure key and val are addressable.
+		key = nod(OADDR, key, N);
+		val = nod(OADDR, val, N);
+		n = mkcall1(mapfn("mapassign1", map->type), T, init,
+			typename(map->type), map, key, val);
+		goto out;
+	}
+
+	if(!eqtype(lt, rt)) {
+		n->right = assignconv(n->right, lt, "assignment");
+		walkexpr(&n->right, init);
+	}
+
+out:
+	ullmancalc(n);
+	return n;
+}
+
+/*
+ * from ascompat[te]
+ * evaluating actual function arguments.
+ *	f(a,b)
+ * if there is exactly one function expr,
+ * then it is done first. otherwise must
+ * make temp variables
+ */
+static NodeList*
+reorder1(NodeList *all)
+{
+	Node *f, *a, *n;
+	NodeList *l, *r, *g;
+	int c, d, t;
+
+	c = 0;	// function calls
+	t = 0;	// total parameters
+
+	for(l=all; l; l=l->next) {
+		n = l->n;
+		t++;
+		ullmancalc(n);
+		if(n->ullman >= UINF)
+			c++;
+	}
+	if(c == 0 || t == 1)
+		return all;
+
+	g = nil;	// fncalls assigned to tempnames
+	f = N;	// last fncall assigned to stack
+	r = nil;	// non fncalls and tempnames assigned to stack
+	d = 0;
+	for(l=all; l; l=l->next) {
+		n = l->n;
+		if(n->ullman < UINF) {
+			r = list(r, n);
+			continue;
+		}
+		d++;
+		if(d == c) {
+			f = n;
+			continue;
+		}
+
+		// make assignment of fncall to tempname
+		a = temp(n->right->type);
+		a = nod(OAS, a, n->right);
+		g = list(g, a);
+
+		// put normal arg assignment on list
+		// with fncall replaced by tempname
+		n->right = a->left;
+		r = list(r, n);
+	}
+
+	if(f != N)
+		g = list(g, f);
+	return concat(g, r);
+}
+
+static void reorder3save(Node**, NodeList*, NodeList*, NodeList**);
+static int aliased(Node*, NodeList*, NodeList*);
+
+/*
+ * from ascompat[ee]
+ *	a,b = c,d
+ * simultaneous assignment. there cannot
+ * be later use of an earlier lvalue.
+ *
+ * function calls have been removed.
+ */
+static NodeList*
+reorder3(NodeList *all)
+{
+	NodeList *list, *early, *mapinit;
+	Node *l;
+
+	// If a needed expression may be affected by an
+	// earlier assignment, make an early copy of that
+	// expression and use the copy instead.
+	early = nil;
+	mapinit = nil;
+	for(list=all; list; list=list->next) {
+		l = list->n->left;
+
+		// Save subexpressions needed on left side.
+		// Drill through non-dereferences.
+		for(;;) {
+			if(l->op == ODOT || l->op == OPAREN) {
+				l = l->left;
+				continue;
+			}
+			if(l->op == OINDEX && isfixedarray(l->left->type)) {
+				reorder3save(&l->right, all, list, &early);
+				l = l->left;
+				continue;
+			}
+			break;
+		}
+		switch(l->op) {
+		default:
+			fatal("reorder3 unexpected lvalue %#O", l->op);
+		case ONAME:
+			break;
+		case OINDEX:
+		case OINDEXMAP:
+			reorder3save(&l->left, all, list, &early);
+			reorder3save(&l->right, all, list, &early);
+			if(l->op == OINDEXMAP)
+				list->n = convas(list->n, &mapinit);
+			break;
+		case OIND:
+		case ODOTPTR:
+			reorder3save(&l->left, all, list, &early);
+		}
+
+		// Save expression on right side.
+		reorder3save(&list->n->right, all, list, &early);
+	}
+
+	early = concat(mapinit, early);
+	return concat(early, all);
+}
+
+static int vmatch2(Node*, Node*);
+static int varexpr(Node*);
+
+/*
+ * if the evaluation of *np would be affected by the 
+ * assignments in all up to but not including stop,
+ * copy into a temporary during *early and
+ * replace *np with that temp.
+ */
+static void
+reorder3save(Node **np, NodeList *all, NodeList *stop, NodeList **early)
+{
+	Node *n, *q;
+
+	n = *np;
+	if(!aliased(n, all, stop))
+		return;
+	
+	q = temp(n->type);
+	q = nod(OAS, q, n);
+	typecheck(&q, Etop);
+	*early = list(*early, q);
+	*np = q->left;
+}
+
+/*
+ * what's the outer value that a write to n affects?
+ * outer value means containing struct or array.
+ */
+Node*
+outervalue(Node *n)
+{	
+	for(;;) {
+		if(n->op == ODOT || n->op == OPAREN) {
+			n = n->left;
+			continue;
+		}
+		if(n->op == OINDEX && isfixedarray(n->left->type)) {
+			n = n->left;
+			continue;
+		}
+		break;
+	}
+	return n;
+}
+
+/*
+ * Is it possible that the computation of n might be
+ * affected by writes in as up to but not including stop?
+ */
+static int
+aliased(Node *n, NodeList *all, NodeList *stop)
+{
+	int memwrite, varwrite;
+	Node *a;
+	NodeList *l;
+
+	if(n == N)
+		return 0;
+
+	// Look for obvious aliasing: a variable being assigned
+	// during the all list and appearing in n.
+	// Also record whether there are any writes to main memory.
+	// Also record whether there are any writes to variables
+	// whose addresses have been taken.
+	memwrite = 0;
+	varwrite = 0;
+	for(l=all; l!=stop; l=l->next) {
+		a = outervalue(l->n->left);
+		if(a->op != ONAME) {
+			memwrite = 1;
+			continue;
+		}
+		switch(n->class) {
+		default:
+			varwrite = 1;
+			continue;
+		case PAUTO:
+		case PPARAM:
+		case PPARAMOUT:
+			if(n->addrtaken) {
+				varwrite = 1;
+				continue;
+			}
+			if(vmatch2(a, n)) {
+				// Direct hit.
+				return 1;
+			}
+		}
+	}
+
+	// The variables being written do not appear in n.
+	// However, n might refer to computed addresses
+	// that are being written.
+	
+	// If no computed addresses are affected by the writes, no aliasing.
+	if(!memwrite && !varwrite)
+		return 0;
+
+	// If n does not refer to computed addresses
+	// (that is, if n only refers to variables whose addresses
+	// have not been taken), no aliasing.
+	if(varexpr(n))
+		return 0;
+
+	// Otherwise, both the writes and n refer to computed memory addresses.
+	// Assume that they might conflict.
+	return 1;
+}
+
+/*
+ * does the evaluation of n only refer to variables
+ * whose addresses have not been taken?
+ * (and no other memory)
+ */
+static int
+varexpr(Node *n)
+{
+	if(n == N)
+		return 1;
+
+	switch(n->op) {
+	case OLITERAL:	
+		return 1;
+	case ONAME:
+		switch(n->class) {
+		case PAUTO:
+		case PPARAM:
+		case PPARAMOUT:
+			if(!n->addrtaken)
+				return 1;
+		}
+		return 0;
+
+	case OADD:
+	case OSUB:
+	case OOR:
+	case OXOR:
+	case OMUL:
+	case ODIV:
+	case OMOD:
+	case OLSH:
+	case ORSH:
+	case OAND:
+	case OANDNOT:
+	case OPLUS:
+	case OMINUS:
+	case OCOM:
+	case OPAREN:
+	case OANDAND:
+	case OOROR:
+	case ODOT:  // but not ODOTPTR
+	case OCONV:
+	case OCONVNOP:
+	case OCONVIFACE:
+	case ODOTTYPE:
+		return varexpr(n->left) && varexpr(n->right);
+	}
+
+	// Be conservative.
+	return 0;
+}
+
+/*
+ * is the name l mentioned in r?
+ */
+static int
+vmatch2(Node *l, Node *r)
+{
+	NodeList *ll;
+
+	if(r == N)
+		return 0;
+	switch(r->op) {
+	case ONAME:
+		// match each right given left
+		return l == r;
+	case OLITERAL:
+		return 0;
+	}
+	if(vmatch2(l, r->left))
+		return 1;
+	if(vmatch2(l, r->right))
+		return 1;
+	for(ll=r->list; ll; ll=ll->next)
+		if(vmatch2(l, ll->n))
+			return 1;
+	return 0;
+}
+
+/*
+ * is any name mentioned in l also mentioned in r?
+ * called by sinit.c
+ */
+int
+vmatch1(Node *l, Node *r)
+{
+	NodeList *ll;
+
+	/*
+	 * isolate all left sides
+	 */
+	if(l == N || r == N)
+		return 0;
+	switch(l->op) {
+	case ONAME:
+		switch(l->class) {
+		case PPARAM:
+		case PPARAMREF:
+		case PAUTO:
+			break;
+		default:
+			// assignment to non-stack variable
+			// must be delayed if right has function calls.
+			if(r->ullman >= UINF)
+				return 1;
+			break;
+		}
+		return vmatch2(l, r);
+	case OLITERAL:
+		return 0;
+	}
+	if(vmatch1(l->left, r))
+		return 1;
+	if(vmatch1(l->right, r))
+		return 1;
+	for(ll=l->list; ll; ll=ll->next)
+		if(vmatch1(ll->n, r))
+			return 1;
+	return 0;
+}
+
+/*
+ * walk through argin parameters.
+ * generate and return code to allocate
+ * copies of escaped parameters to the heap.
+ */
+static NodeList*
+paramstoheap(Type **argin, int out)
+{
+	Type *t;
+	Iter savet;
+	Node *v;
+	NodeList *nn;
+
+	nn = nil;
+	for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) {
+		v = t->nname;
+		if(v && v->sym && v->sym->name[0] == '~' && v->sym->name[1] == 'r') // unnamed result
+			v = N;
+		// In precisestack mode, the garbage collector assumes results
+		// are always live, so zero them always.
+		if(out && (precisestack_enabled || (v == N && hasdefer))) {
+			// Defer might stop a panic and show the
+			// return values as they exist at the time of panic.
+			// Make sure to zero them on entry to the function.
+			nn = list(nn, nod(OAS, nodarg(t, 1), N));
+		}
+		if(v == N || !(v->class & PHEAP))
+			continue;
+
+		// generate allocation & copying code
+		if(compiling_runtime)
+			yyerror("%N escapes to heap, not allowed in runtime.", v);
+		if(v->alloc == nil)
+			v->alloc = callnew(v->type);
+		nn = list(nn, nod(OAS, v->heapaddr, v->alloc));
+		if((v->class & ~PHEAP) != PPARAMOUT)
+			nn = list(nn, nod(OAS, v, v->stackparam));
+	}
+	return nn;
+}
+
+/*
+ * walk through argout parameters copying back to stack
+ */
+static NodeList*
+returnsfromheap(Type **argin)
+{
+	Type *t;
+	Iter savet;
+	Node *v;
+	NodeList *nn;
+
+	nn = nil;
+	for(t = structfirst(&savet, argin); t != T; t = structnext(&savet)) {
+		v = t->nname;
+		if(v == N || v->class != (PHEAP|PPARAMOUT))
+			continue;
+		nn = list(nn, nod(OAS, v->stackparam, v));
+	}
+	return nn;
+}
+
+/*
+ * take care of migrating any function in/out args
+ * between the stack and the heap.  adds code to
+ * curfn's before and after lists.
+ */
+static void
+heapmoves(void)
+{
+	NodeList *nn;
+	int32 lno;
+
+	lno = lineno;
+	lineno = curfn->lineno;
+	nn = paramstoheap(getthis(curfn->type), 0);
+	nn = concat(nn, paramstoheap(getinarg(curfn->type), 0));
+	nn = concat(nn, paramstoheap(getoutarg(curfn->type), 1));
+	curfn->enter = concat(curfn->enter, nn);
+	lineno = curfn->endlineno;
+	curfn->exit = returnsfromheap(getoutarg(curfn->type));
+	lineno = lno;
+}
+
+static Node*
+vmkcall(Node *fn, Type *t, NodeList **init, va_list va)
+{
+	int i, n;
+	Node *r;
+	NodeList *args;
+
+	if(fn->type == T || fn->type->etype != TFUNC)
+		fatal("mkcall %N %T", fn, fn->type);
+
+	args = nil;
+	n = fn->type->intuple;
+	for(i=0; i<n; i++)
+		args = list(args, va_arg(va, Node*));
+
+	r = nod(OCALL, fn, N);
+	r->list = args;
+	if(fn->type->outtuple > 0)
+		typecheck(&r, Erv | Efnstruct);
+	else
+		typecheck(&r, Etop);
+	walkexpr(&r, init);
+	r->type = t;
+	return r;
+}
+
+Node*
+mkcall(char *name, Type *t, NodeList **init, ...)
+{
+	Node *r;
+	va_list va;
+
+	va_start(va, init);
+	r = vmkcall(syslook(name, 0), t, init, va);
+	va_end(va);
+	return r;
+}
+
+Node*
+mkcall1(Node *fn, Type *t, NodeList **init, ...)
+{
+	Node *r;
+	va_list va;
+
+	va_start(va, init);
+	r = vmkcall(fn, t, init, va);
+	va_end(va);
+	return r;
+}
+
+Node*
+conv(Node *n, Type *t)
+{
+	if(eqtype(n->type, t))
+		return n;
+	n = nod(OCONV, n, N);
+	n->type = t;
+	typecheck(&n, Erv);
+	return n;
+}
+
+Node*
+chanfn(char *name, int n, Type *t)
+{
+	Node *fn;
+	int i;
+
+	if(t->etype != TCHAN)
+		fatal("chanfn %T", t);
+	fn = syslook(name, 1);
+	for(i=0; i<n; i++)
+		argtype(fn, t->type);
+	return fn;
+}
+
+static Node*
+mapfn(char *name, Type *t)
+{
+	Node *fn;
+
+	if(t->etype != TMAP)
+		fatal("mapfn %T", t);
+	fn = syslook(name, 1);
+	argtype(fn, t->down);
+	argtype(fn, t->type);
+	argtype(fn, t->down);
+	argtype(fn, t->type);
+	return fn;
+}
+
+static Node*
+mapfndel(char *name, Type *t)
+{
+	Node *fn;
+
+	if(t->etype != TMAP)
+		fatal("mapfn %T", t);
+	fn = syslook(name, 1);
+	argtype(fn, t->down);
+	argtype(fn, t->type);
+	argtype(fn, t->down);
+	return fn;
+}
+
+static Node*
+writebarrierfn(char *name, Type *l, Type *r)
+{
+	Node *fn;
+
+	fn = syslook(name, 1);
+	argtype(fn, l);
+	argtype(fn, r);
+	return fn;
+}
+
+static Node*
+addstr(Node *n, NodeList **init)
+{
+	Node *r, *cat, *slice;
+	NodeList *args, *l;
+	int c;
+	Type *t;
+
+	// orderexpr rewrote OADDSTR to have a list of strings.
+	c = count(n->list);
+	if(c < 2)
+		yyerror("addstr count %d too small", c);
+
+	// build list of string arguments
+	args = nil;
+	for(l=n->list; l != nil; l=l->next)
+		args = list(args, conv(l->n, types[TSTRING]));
+
+	if(c <= 5) {
+		// small numbers of strings use direct runtime helpers.
+		// note: orderexpr knows this cutoff too.
+		snprint(namebuf, sizeof(namebuf), "concatstring%d", c);
+	} else {
+		// large numbers of strings are passed to the runtime as a slice.
+		strcpy(namebuf, "concatstrings");
+		t = typ(TARRAY);
+		t->type = types[TSTRING];
+		t->bound = -1;
+		slice = nod(OCOMPLIT, N, typenod(t));
+		slice->alloc = n->alloc;
+		slice->list = args;
+		slice->esc = EscNone;
+		args = list1(slice);
+	}
+	cat = syslook(namebuf, 1);
+	r = nod(OCALL, cat, N);
+	r->list = args;
+	typecheck(&r, Erv);
+	walkexpr(&r, init);
+	r->type = n->type;
+
+	return r;
+}
+
+// expand append(l1, l2...) to
+//   init {
+//     s := l1
+//     if n := len(l1) + len(l2) - cap(s); n > 0 {
+//       s = growslice(s, n)
+//     }
+//     s = s[:len(l1)+len(l2)]
+//     memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
+//   }
+//   s
+//
+// l2 is allowed to be a string.
+static Node*
+appendslice(Node *n, NodeList **init)
+{
+	NodeList *l;
+	Node *l1, *l2, *nt, *nif, *fn;
+	Node *nptr1, *nptr2, *nwid;
+	Node *s;
+
+	walkexprlistsafe(n->list, init);
+
+	// walkexprlistsafe will leave OINDEX (s[n]) alone if both s
+	// and n are name or literal, but those may index the slice we're
+	// modifying here.  Fix explicitly.
+	for(l=n->list; l; l=l->next)
+		l->n = cheapexpr(l->n, init);
+
+	l1 = n->list->n;
+	l2 = n->list->next->n;
+
+	s = temp(l1->type); // var s []T
+	l = nil;
+	l = list(l, nod(OAS, s, l1)); // s = l1
+
+	nt = temp(types[TINT]);
+	nif = nod(OIF, N, N);
+	// n := len(s) + len(l2) - cap(s)
+	nif->ninit = list1(nod(OAS, nt,
+		nod(OSUB, nod(OADD, nod(OLEN, s, N), nod(OLEN, l2, N)), nod(OCAP, s, N))));
+	nif->ntest = nod(OGT, nt, nodintconst(0));
+	// instantiate growslice(Type*, []any, int64) []any
+	fn = syslook("growslice", 1);
+	argtype(fn, s->type->type);
+	argtype(fn, s->type->type);
+
+	// s = growslice(T, s, n)
+	nif->nbody = list1(nod(OAS, s, mkcall1(fn, s->type, &nif->ninit,
+					       typename(s->type),
+					       s,
+					       conv(nt, types[TINT64]))));
+
+	l = list(l, nif);
+
+	if(flag_race) {
+		// rely on runtime to instrument copy.
+		// copy(s[len(l1):len(l1)+len(l2)], l2)
+		nptr1 = nod(OSLICE, s, nod(OKEY,
+			nod(OLEN, l1, N),
+			nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N))));
+		nptr1->etype = 1;
+		nptr2 = l2;
+		if(l2->type->etype == TSTRING)
+			fn = syslook("slicestringcopy", 1);
+		else
+			fn = syslook("slicecopy", 1);
+		argtype(fn, l1->type);
+		argtype(fn, l2->type);
+		nt = mkcall1(fn, types[TINT], &l,
+				nptr1, nptr2,
+				nodintconst(s->type->type->width));
+		l = list(l, nt);
+	} else {
+		// memmove(&s[len(l1)], &l2[0], len(l2)*sizeof(T))
+		nptr1 = nod(OINDEX, s, nod(OLEN, l1, N));
+		nptr1->bounded = 1;
+		nptr1 = nod(OADDR, nptr1, N);
+
+		nptr2 = nod(OSPTR, l2, N);
+
+		fn = syslook("memmove", 1);
+		argtype(fn, s->type->type);	// 1 old []any
+		argtype(fn, s->type->type);	// 2 ret []any
+
+		nwid = cheapexpr(conv(nod(OLEN, l2, N), types[TUINTPTR]), &l);
+		nwid = nod(OMUL, nwid, nodintconst(s->type->type->width));
+		nt = mkcall1(fn, T, &l, nptr1, nptr2, nwid);
+		l = list(l, nt);
+	}
+
+	// s = s[:len(l1)+len(l2)]
+	nt = nod(OADD, nod(OLEN, l1, N), nod(OLEN, l2, N));
+	nt = nod(OSLICE, s, nod(OKEY, N, nt));
+	nt->etype = 1;
+	l = list(l, nod(OAS, s, nt));
+
+	typechecklist(l, Etop);
+	walkstmtlist(l);
+	*init = concat(*init, l);
+	return s;
+}
+
+// expand append(src, a [, b]* ) to
+//
+//   init {
+//     s := src
+//     const argc = len(args) - 1
+//     if cap(s) - len(s) < argc {
+//	    s = growslice(s, argc)
+//     }
+//     n := len(s)
+//     s = s[:n+argc]
+//     s[n] = a
+//     s[n+1] = b
+//     ...
+//   }
+//   s
+static Node*
+append(Node *n, NodeList **init)
+{
+	NodeList *l, *a;
+	Node *nsrc, *ns, *nn, *na, *nx, *fn;
+	int argc;
+
+	walkexprlistsafe(n->list, init);
+
+	// walkexprlistsafe will leave OINDEX (s[n]) alone if both s
+	// and n are name or literal, but those may index the slice we're
+	// modifying here.  Fix explicitly.
+	for(l=n->list; l; l=l->next)
+		l->n = cheapexpr(l->n, init);
+
+	nsrc = n->list->n;
+
+	// Resolve slice type of multi-valued return.
+	if(istype(nsrc->type, TSTRUCT))
+		nsrc->type = nsrc->type->type->type;
+	argc = count(n->list) - 1;
+	if (argc < 1) {
+		return nsrc;
+	}
+
+	l = nil;
+
+	ns = temp(nsrc->type);
+	l = list(l, nod(OAS, ns, nsrc));  // s = src
+
+	na = nodintconst(argc);		// const argc
+	nx = nod(OIF, N, N);		// if cap(s) - len(s) < argc
+	nx->ntest = nod(OLT, nod(OSUB, nod(OCAP, ns, N), nod(OLEN, ns, N)), na);
+
+	fn = syslook("growslice", 1);	//   growslice(<type>, old []T, n int64) (ret []T)
+	argtype(fn, ns->type->type);	// 1 old []any
+	argtype(fn, ns->type->type);	// 2 ret []any
+
+	nx->nbody = list1(nod(OAS, ns, mkcall1(fn,  ns->type, &nx->ninit,
+					       typename(ns->type),
+					       ns,
+					       conv(na, types[TINT64]))));
+	l = list(l, nx);
+
+	nn = temp(types[TINT]);
+	l = list(l, nod(OAS, nn, nod(OLEN, ns, N)));	 // n = len(s)
+
+	nx = nod(OSLICE, ns, nod(OKEY, N, nod(OADD, nn, na)));	 // ...s[:n+argc]
+	nx->etype = 1;
+	l = list(l, nod(OAS, ns, nx));			// s = s[:n+argc]
+
+	for (a = n->list->next;	 a != nil; a = a->next) {
+		nx = nod(OINDEX, ns, nn);		// s[n] ...
+		nx->bounded = 1;
+		l = list(l, nod(OAS, nx, a->n));	// s[n] = arg
+		if (a->next != nil)
+			l = list(l, nod(OAS, nn, nod(OADD, nn, nodintconst(1))));  // n = n + 1
+	}
+
+	typechecklist(l, Etop);
+	walkstmtlist(l);
+	*init = concat(*init, l);
+	return ns;
+}
+
+// Lower copy(a, b) to a memmove call or a runtime call.
+//
+// init {
+//   n := len(a)
+//   if n > len(b) { n = len(b) }
+//   memmove(a.ptr, b.ptr, n*sizeof(elem(a)))
+// }
+// n;
+//
+// Also works if b is a string.
+//
+static Node*
+copyany(Node *n, NodeList **init, int runtimecall)
+{
+	Node *nl, *nr, *nfrm, *nto, *nif, *nlen, *nwid, *fn;
+	NodeList *l;
+
+	if(runtimecall) {
+		if(n->right->type->etype == TSTRING)
+			fn = syslook("slicestringcopy", 1);
+		else
+			fn = syslook("slicecopy", 1);
+		argtype(fn, n->left->type);
+		argtype(fn, n->right->type);
+		return mkcall1(fn, n->type, init,
+				n->left, n->right,
+				nodintconst(n->left->type->type->width));
+	}
+	walkexpr(&n->left, init);
+	walkexpr(&n->right, init);
+	nl = temp(n->left->type);
+	nr = temp(n->right->type);
+	l = nil;
+	l = list(l, nod(OAS, nl, n->left));
+	l = list(l, nod(OAS, nr, n->right));
+
+	nfrm = nod(OSPTR, nr, N);
+	nto = nod(OSPTR, nl, N);
+
+	nlen = temp(types[TINT]);
+	// n = len(to)
+	l = list(l, nod(OAS, nlen, nod(OLEN, nl, N)));
+	// if n > len(frm) { n = len(frm) }
+	nif = nod(OIF, N, N);
+	nif->ntest = nod(OGT, nlen, nod(OLEN, nr, N));
+	nif->nbody = list(nif->nbody,
+		nod(OAS, nlen, nod(OLEN, nr, N)));
+	l = list(l, nif);
+
+	// Call memmove.
+	fn = syslook("memmove", 1);
+	argtype(fn, nl->type->type);
+	argtype(fn, nl->type->type);
+	nwid = temp(types[TUINTPTR]);
+	l = list(l, nod(OAS, nwid, conv(nlen, types[TUINTPTR])));
+	nwid = nod(OMUL, nwid, nodintconst(nl->type->type->width));
+	l = list(l, mkcall1(fn, T, init, nto, nfrm, nwid));
+
+	typechecklist(l, Etop);
+	walkstmtlist(l);
+	*init = concat(*init, l);
+	return nlen;
+}
+
+// Generate frontend part for OSLICE[3][ARR|STR]
+// 
+static	Node*
+sliceany(Node* n, NodeList **init)
+{
+	int bounded, slice3;
+	Node *src, *lb, *hb, *cb, *bound, *chk, *chk0, *chk1, *chk2;
+	int64 lbv, hbv, cbv, bv, w;
+	Type *bt;
+
+//	print("before sliceany: %+N\n", n);
+
+	src = n->left;
+	lb = n->right->left;
+	slice3 = n->op == OSLICE3 || n->op == OSLICE3ARR;
+	if(slice3) {
+		hb = n->right->right->left;
+		cb = n->right->right->right;
+	} else {
+		hb = n->right->right;
+		cb = N;
+	}
+
+	bounded = n->etype;
+	
+	if(n->op == OSLICESTR)
+		bound = nod(OLEN, src, N);
+	else
+		bound = nod(OCAP, src, N);
+
+	typecheck(&bound, Erv);
+	walkexpr(&bound, init);  // if src is an array, bound will be a const now.
+
+	// static checks if possible
+	bv = 1LL<<50;
+	if(isconst(bound, CTINT)) {
+		if(!smallintconst(bound))
+			yyerror("array len too large");
+		else
+			bv = mpgetfix(bound->val.u.xval);
+	}
+
+	if(isconst(cb, CTINT)) {
+		cbv = mpgetfix(cb->val.u.xval);
+		if(cbv < 0 || cbv > bv)
+			yyerror("slice index out of bounds");
+	}
+	if(isconst(hb, CTINT)) {
+		hbv = mpgetfix(hb->val.u.xval);
+		if(hbv < 0 || hbv > bv)
+			yyerror("slice index out of bounds");
+	}
+	if(isconst(lb, CTINT)) {
+		lbv = mpgetfix(lb->val.u.xval);
+		if(lbv < 0 || lbv > bv) {
+			yyerror("slice index out of bounds");
+			lbv = -1;
+		}
+		if(lbv == 0)
+			lb = N;
+	}
+
+	// Checking src[lb:hb:cb] or src[lb:hb].
+	// if chk0 || chk1 || chk2 { panicslice() }
+	chk = N;
+	chk0 = N; // cap(src) < cb
+	chk1 = N; // cb < hb for src[lb:hb:cb]; cap(src) < hb for src[lb:hb]
+	chk2 = N; // hb < lb
+
+	// All comparisons are unsigned to avoid testing < 0.
+	bt = types[simtype[TUINT]];
+	if(cb != N && cb->type->width > 4)
+		bt = types[TUINT64];
+	if(hb != N && hb->type->width > 4)
+		bt = types[TUINT64];
+	if(lb != N && lb->type->width > 4)
+		bt = types[TUINT64];
+
+	bound = cheapexpr(conv(bound, bt), init);
+
+	if(cb != N) {
+		cb = cheapexpr(conv(cb, bt), init);
+		if(!bounded)
+			chk0 = nod(OLT, bound, cb);
+	} else if(slice3) {
+		// When we figure out what this means, implement it.
+		fatal("slice3 with cb == N"); // rejected by parser
+	}
+		
+	if(hb != N) {
+		hb = cheapexpr(conv(hb, bt), init);
+		if(!bounded) {
+			if(cb != N)
+				chk1 = nod(OLT, cb, hb);
+			else
+				chk1 = nod(OLT, bound, hb);
+		}
+	} else if(slice3) {
+		// When we figure out what this means, implement it.
+		fatal("slice3 with hb == N"); // rejected by parser
+	} else if(n->op == OSLICEARR) {
+		hb = bound;
+	} else {
+		hb = nod(OLEN, src, N);
+		typecheck(&hb, Erv);
+		walkexpr(&hb, init);
+		hb = cheapexpr(conv(hb, bt), init);
+	}
+
+	if(lb != N) {
+		lb = cheapexpr(conv(lb, bt), init);
+		if(!bounded)
+			chk2 = nod(OLT, hb, lb);  
+	}
+
+	if(chk0 != N || chk1 != N || chk2 != N) {
+		chk = nod(OIF, N, N);
+		chk->nbody = list1(mkcall("panicslice", T, init));
+		chk->likely = -1;
+		if(chk0 != N)
+			chk->ntest = chk0;
+		if(chk1 != N) {
+			if(chk->ntest == N)
+				chk->ntest = chk1;
+			else
+				chk->ntest = nod(OOROR, chk->ntest, chk1);
+		}
+		if(chk2 != N) {
+			if(chk->ntest == N)
+				chk->ntest = chk2;
+			else
+				chk->ntest = nod(OOROR, chk->ntest, chk2);
+		}
+		typecheck(&chk, Etop);
+		walkstmt(&chk);
+		*init = concat(*init, chk->ninit);
+		chk->ninit = nil;
+		*init = list(*init, chk);
+	}
+	
+	// prepare new cap, len and offs for backend cgen_slice
+	// cap = bound [ - lo ]
+	n->right = N;
+	n->list = nil;
+	if(!slice3)
+		cb = bound;
+	if(lb == N)
+		bound = conv(cb, types[simtype[TUINT]]);
+	else
+		bound = nod(OSUB, conv(cb, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]]));
+	typecheck(&bound, Erv);
+	walkexpr(&bound, init);
+	n->list = list(n->list, bound);
+
+	// len = hi [ - lo]
+	if(lb == N)
+		hb = conv(hb, types[simtype[TUINT]]);
+	else
+		hb = nod(OSUB, conv(hb, types[simtype[TUINT]]), conv(lb, types[simtype[TUINT]]));
+	typecheck(&hb, Erv);
+	walkexpr(&hb, init);
+	n->list = list(n->list, hb);
+
+	// offs = [width *] lo, but omit if zero
+	if(lb != N) {
+		if(n->op == OSLICESTR)
+			w = 1;
+		else
+			w = n->type->type->width;
+		lb = conv(lb, types[TUINTPTR]);
+		if(w > 1)
+			lb = nod(OMUL, nodintconst(w), lb);
+		typecheck(&lb, Erv);
+		walkexpr(&lb, init);
+		n->list = list(n->list, lb);
+	}
+
+//	print("after sliceany: %+N\n", n);
+
+	return n;
+}
+
+static Node*
+eqfor(Type *t)
+{
+	int a;
+	Node *n;
+	Node *ntype;
+	Sym *sym;
+
+	// Should only arrive here with large memory or
+	// a struct/array containing a non-memory field/element.
+	// Small memory is handled inline, and single non-memory
+	// is handled during type check (OCMPSTR etc).
+	a = algtype1(t, nil);
+	if(a != AMEM && a != -1)
+		fatal("eqfor %T", t);
+
+	if(a == AMEM) {
+		n = syslook("memequal", 1);
+		argtype(n, t);
+		argtype(n, t);
+		return n;
+	}
+
+	sym = typesymprefix(".eq", t);
+	n = newname(sym);
+	n->class = PFUNC;
+	ntype = nod(OTFUNC, N, N);
+	ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
+	ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t))));
+	ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR])));
+	ntype->rlist = list(ntype->rlist, nod(ODCLFIELD, N, typenod(types[TBOOL])));
+	typecheck(&ntype, Etype);
+	n->type = ntype->type;
+	return n;
+}
+
+static int
+countfield(Type *t)
+{
+	Type *t1;
+	int n;
+	
+	n = 0;
+	for(t1=t->type; t1!=T; t1=t1->down)
+		n++;
+	return n;
+}
+
+static void
+walkcompare(Node **np, NodeList **init)
+{
+	Node *n, *l, *r, *call, *a, *li, *ri, *expr, *cmpl, *cmpr;
+	int andor, i;
+	Type *t, *t1;
+	
+	n = *np;
+	
+	// Must be comparison of array or struct.
+	// Otherwise back end handles it.
+	t = n->left->type;
+	switch(t->etype) {
+	default:
+		return;
+	case TARRAY:
+		if(isslice(t))
+			return;
+		break;
+	case TSTRUCT:
+		break;
+	}
+	
+	cmpl = n->left;
+	while(cmpl != N && cmpl->op == OCONVNOP)
+		cmpl = cmpl->left;
+	cmpr = n->right;
+	while(cmpr != N && cmpr->op == OCONVNOP)
+		cmpr = cmpr->left;
+	
+	if(!islvalue(cmpl) || !islvalue(cmpr)) {
+		fatal("arguments of comparison must be lvalues - %N %N", cmpl, cmpr);
+	}
+
+	l = temp(ptrto(t));
+	a = nod(OAS, l, nod(OADDR, cmpl, N));
+	a->right->etype = 1;  // addr does not escape
+	typecheck(&a, Etop);
+	*init = list(*init, a);
+
+	r = temp(ptrto(t));
+	a = nod(OAS, r, nod(OADDR, cmpr, N));
+	a->right->etype = 1;  // addr does not escape
+	typecheck(&a, Etop);
+	*init = list(*init, a);
+
+	expr = N;
+	andor = OANDAND;
+	if(n->op == ONE)
+		andor = OOROR;
+
+	if(t->etype == TARRAY &&
+		t->bound <= 4 &&
+		issimple[t->type->etype]) {
+		// Four or fewer elements of a basic type.
+		// Unroll comparisons.
+		for(i=0; i<t->bound; i++) {
+			li = nod(OINDEX, l, nodintconst(i));
+			ri = nod(OINDEX, r, nodintconst(i));
+			a = nod(n->op, li, ri);
+			if(expr == N)
+				expr = a;
+			else
+				expr = nod(andor, expr, a);
+		}
+		if(expr == N)
+			expr = nodbool(n->op == OEQ);
+		r = expr;
+		goto ret;
+	}
+
+	if(t->etype == TSTRUCT && countfield(t) <= 4) {
+		// Struct of four or fewer fields.
+		// Inline comparisons.
+		for(t1=t->type; t1; t1=t1->down) {
+			if(isblanksym(t1->sym))
+				continue;
+			li = nod(OXDOT, l, newname(t1->sym));
+			ri = nod(OXDOT, r, newname(t1->sym));
+			a = nod(n->op, li, ri);
+			if(expr == N)
+				expr = a;
+			else
+				expr = nod(andor, expr, a);
+		}
+		if(expr == N)
+			expr = nodbool(n->op == OEQ);
+		r = expr;
+		goto ret;
+	}
+
+	// Chose not to inline.  Call equality function directly.
+	call = nod(OCALL, eqfor(t), N);
+	call->list = list(call->list, l);
+	call->list = list(call->list, r);
+	call->list = list(call->list, nodintconst(t->width));
+	r = call;
+	if(n->op != OEQ)
+		r = nod(ONOT, r, N);
+	goto ret;
+
+ret:
+	typecheck(&r, Erv);
+	walkexpr(&r, init);
+	if(r->type != n->type) {
+		r = nod(OCONVNOP, r, N);
+		r->type = n->type;
+		r->typecheck = 1;
+	}
+	*np = r;
+	return;
+}
+
+static int
+samecheap(Node *a, Node *b)
+{
+	Node *ar, *br;
+	while(a != N && b != N && a->op == b->op) {
+		switch(a->op) {
+		default:
+			return 0;
+		case ONAME:
+			return a == b;
+		case ODOT:
+		case ODOTPTR:
+			ar = a->right;
+			br = b->right;
+			if(ar->op != ONAME || br->op != ONAME || ar->sym != br->sym)
+				return 0;
+			break;
+		case OINDEX:
+			ar = a->right;
+			br = b->right;
+			if(!isconst(ar, CTINT) || !isconst(br, CTINT) || mpcmpfixfix(ar->val.u.xval, br->val.u.xval) != 0)
+				return 0;
+			break;
+		}
+		a = a->left;
+		b = b->left;
+	}
+	return 0;
+}
+
+static void
+walkrotate(Node **np)
+{
+	int w, sl, sr, s;
+	Node *l, *r;
+	Node *n;
+	
+	n = *np;
+
+	// Want << | >> or >> | << or << ^ >> or >> ^ << on unsigned value.
+	l = n->left;
+	r = n->right;
+	if((n->op != OOR && n->op != OXOR) ||
+	   (l->op != OLSH && l->op != ORSH) ||
+	   (r->op != OLSH && r->op != ORSH) ||
+	   n->type == T || issigned[n->type->etype] ||
+	   l->op == r->op) {
+		return;
+	}
+
+	// Want same, side effect-free expression on lhs of both shifts.
+	if(!samecheap(l->left, r->left))
+		return;
+	
+	// Constants adding to width?
+	w = l->type->width * 8;
+	if(smallintconst(l->right) && smallintconst(r->right)) {
+		if((sl=mpgetfix(l->right->val.u.xval)) >= 0 && (sr=mpgetfix(r->right->val.u.xval)) >= 0 && sl+sr == w)
+			goto yes;
+		return;
+	}
+	
+	// TODO: Could allow s and 32-s if s is bounded (maybe s&31 and 32-s&31).
+	return;
+	
+yes:
+	// Rewrite left shift half to left rotate.
+	if(l->op == OLSH)
+		n = l;
+	else
+		n = r;
+	n->op = OLROT;
+	
+	// Remove rotate 0 and rotate w.
+	s = mpgetfix(n->right->val.u.xval);
+	if(s == 0 || s == w)
+		n = n->left;
+
+	*np = n;
+	return;
+}
+
+/*
+ * walkmul rewrites integer multiplication by powers of two as shifts.
+ */
+static void
+walkmul(Node **np, NodeList **init)
+{
+	Node *n, *nl, *nr;
+	int pow, neg, w;
+	
+	n = *np;
+	if(!isint[n->type->etype])
+		return;
+
+	if(n->right->op == OLITERAL) {
+		nl = n->left;
+		nr = n->right;
+	} else if(n->left->op == OLITERAL) {
+		nl = n->right;
+		nr = n->left;
+	} else
+		return;
+
+	neg = 0;
+
+	// x*0 is 0 (and side effects of x).
+	if(mpgetfix(nr->val.u.xval) == 0) {
+		cheapexpr(nl, init);
+		nodconst(n, n->type, 0);
+		goto ret;
+	}
+
+	// nr is a constant.
+	pow = powtwo(nr);
+	if(pow < 0)
+		return;
+	if(pow >= 1000) {
+		// negative power of 2, like -16
+		neg = 1;
+		pow -= 1000;
+	}
+
+	w = nl->type->width*8;
+	if(pow+1 >= w)// too big, shouldn't happen
+		return;
+
+	nl = cheapexpr(nl, init);
+
+	if(pow == 0) {
+		// x*1 is x
+		n = nl;
+		goto ret;
+	}
+	
+	n = nod(OLSH, nl, nodintconst(pow));
+
+ret:
+	if(neg)
+		n = nod(OMINUS, n, N);
+
+	typecheck(&n, Erv);
+	walkexpr(&n, init);
+	*np = n;
+}
+
+/*
+ * walkdiv rewrites division by a constant as less expensive
+ * operations.
+ */
+static void
+walkdiv(Node **np, NodeList **init)
+{
+	Node *n, *nl, *nr, *nc;
+	Node *n1, *n2, *n3, *n4;
+	int pow; // if >= 0, nr is 1<<pow
+	int s; // 1 if nr is negative.
+	int w;
+	Type *twide;
+	Magic m;
+
+	n = *np;
+	if(n->right->op != OLITERAL)
+		return;
+	// nr is a constant.
+	nl = cheapexpr(n->left, init);
+	nr = n->right;
+
+	// special cases of mod/div
+	// by a constant
+	w = nl->type->width*8;
+	s = 0;
+	pow = powtwo(nr);
+	if(pow >= 1000) {
+		// negative power of 2
+		s = 1;
+		pow -= 1000;
+	}
+
+	if(pow+1 >= w) {
+		// divisor too large.
+		return;
+	}
+	if(pow < 0) {
+		goto divbymul;
+	}
+
+	switch(pow) {
+	case 0:
+		if(n->op == OMOD) {
+			// nl % 1 is zero.
+			nodconst(n, n->type, 0);
+		} else if(s) {
+			// divide by -1
+			n->op = OMINUS;
+			n->right = N;
+		} else {
+			// divide by 1
+			n = nl;
+		}
+		break;
+	default:
+		if(issigned[n->type->etype]) {
+			if(n->op == OMOD) {
+				// signed modulo 2^pow is like ANDing
+				// with the last pow bits, but if nl < 0,
+				// nl & (2^pow-1) is (nl+1)%2^pow - 1.
+				nc = nod(OXXX, N, N);
+				nodconst(nc, types[simtype[TUINT]], w-1);
+				n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0.
+				if(pow == 1) {
+					typecheck(&n1, Erv);
+					n1 = cheapexpr(n1, init);
+					// n = (nl+ε)&1 -ε where ε=1 iff nl<0.
+					n2 = nod(OSUB, nl, n1);
+					nc = nod(OXXX, N, N);
+					nodconst(nc, nl->type, 1);
+					n3 = nod(OAND, n2, nc);
+					n = nod(OADD, n3, n1);
+				} else {
+					// n = (nl+ε)&(nr-1) - ε where ε=2^pow-1 iff nl<0.
+					nc = nod(OXXX, N, N);
+					nodconst(nc, nl->type, (1LL<<pow)-1);
+					n2 = nod(OAND, n1, nc); // n2 = 2^pow-1 iff nl<0.
+					typecheck(&n2, Erv);
+					n2 = cheapexpr(n2, init);
+
+					n3 = nod(OADD, nl, n2);
+					n4 = nod(OAND, n3, nc);
+					n = nod(OSUB, n4, n2);
+				}
+				break;
+			} else {
+				// arithmetic right shift does not give the correct rounding.
+				// if nl >= 0, nl >> n == nl / nr
+				// if nl < 0, we want to add 2^n-1 first.
+				nc = nod(OXXX, N, N);
+				nodconst(nc, types[simtype[TUINT]], w-1);
+				n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0.
+				if(pow == 1) {
+					// nl+1 is nl-(-1)
+					n->left = nod(OSUB, nl, n1);
+				} else {
+					// Do a logical right right on -1 to keep pow bits.
+					nc = nod(OXXX, N, N);
+					nodconst(nc, types[simtype[TUINT]], w-pow);
+					n2 = nod(ORSH, conv(n1, tounsigned(nl->type)), nc);
+					n->left = nod(OADD, nl, conv(n2, nl->type));
+				}
+				// n = (nl + 2^pow-1) >> pow
+				n->op = ORSH;
+				nc = nod(OXXX, N, N);
+				nodconst(nc, types[simtype[TUINT]], pow);
+				n->right = nc;
+				n->typecheck = 0;
+			}
+			if(s)
+				n = nod(OMINUS, n, N);
+			break;
+		}
+		nc = nod(OXXX, N, N);
+		if(n->op == OMOD) {
+			// n = nl & (nr-1)
+			n->op = OAND;
+			nodconst(nc, nl->type, mpgetfix(nr->val.u.xval)-1);
+		} else {
+			// n = nl >> pow
+			n->op = ORSH;
+			nodconst(nc, types[simtype[TUINT]], pow);
+		}
+		n->typecheck = 0;
+		n->right = nc;
+		break;
+	}
+	goto ret;
+
+divbymul:
+	// try to do division by multiply by (2^w)/d
+	// see hacker's delight chapter 10
+	// TODO: support 64-bit magic multiply here.
+	m.w = w;
+	if(issigned[nl->type->etype]) {
+		m.sd = mpgetfix(nr->val.u.xval);
+		smagic(&m);
+	} else {
+		m.ud = mpgetfix(nr->val.u.xval);
+		umagic(&m);
+	}
+	if(m.bad)
+		return;
+
+	// We have a quick division method so use it
+	// for modulo too.
+	if(n->op == OMOD)
+		goto longmod;
+
+	switch(simtype[nl->type->etype]) {
+	default:
+		return;
+
+	case TUINT8:
+	case TUINT16:
+	case TUINT32:
+		// n1 = nl * magic >> w (HMUL)
+		nc = nod(OXXX, N, N);
+		nodconst(nc, nl->type, m.um);
+		n1 = nod(OMUL, nl, nc);
+		typecheck(&n1, Erv);
+		n1->op = OHMUL;
+		if(m.ua) {
+			// Select a Go type with (at least) twice the width.
+			switch(simtype[nl->type->etype]) {
+			default:
+				return;
+			case TUINT8:
+			case TUINT16:
+				twide = types[TUINT32];
+				break;
+			case TUINT32:
+				twide = types[TUINT64];
+				break;
+			case TINT8:
+			case TINT16:
+				twide = types[TINT32];
+				break;
+			case TINT32:
+				twide = types[TINT64];
+				break;
+			}
+
+			// add numerator (might overflow).
+			// n2 = (n1 + nl)
+			n2 = nod(OADD, conv(n1, twide), conv(nl, twide));
+
+			// shift by m.s
+			nc = nod(OXXX, N, N);
+			nodconst(nc, types[TUINT], m.s);
+			n = conv(nod(ORSH, n2, nc), nl->type);
+		} else {
+			// n = n1 >> m.s
+			nc = nod(OXXX, N, N);
+			nodconst(nc, types[TUINT], m.s);
+			n = nod(ORSH, n1, nc);
+		}
+		break;
+
+	case TINT8:
+	case TINT16:
+	case TINT32:
+		// n1 = nl * magic >> w
+		nc = nod(OXXX, N, N);
+		nodconst(nc, nl->type, m.sm);
+		n1 = nod(OMUL, nl, nc);
+		typecheck(&n1, Erv);
+		n1->op = OHMUL;
+		if(m.sm < 0) {
+			// add the numerator.
+			n1 = nod(OADD, n1, nl);
+		}
+		// shift by m.s
+		nc = nod(OXXX, N, N);
+		nodconst(nc, types[TUINT], m.s);
+		n2 = conv(nod(ORSH, n1, nc), nl->type);
+		// add 1 iff n1 is negative.
+		nc = nod(OXXX, N, N);
+		nodconst(nc, types[TUINT], w-1);
+		n3 = nod(ORSH, nl, nc); // n4 = -1 iff n1 is negative.
+		n = nod(OSUB, n2, n3);
+		// apply sign.
+		if(m.sd < 0)
+			n = nod(OMINUS, n, N);
+		break;
+	}
+	goto ret;
+
+longmod:
+	// rewrite as A%B = A - (A/B*B).
+	n1 = nod(ODIV, nl, nr);
+	n2 = nod(OMUL, n1, nr);
+	n = nod(OSUB, nl, n2);
+	goto ret;
+
+ret:
+	typecheck(&n, Erv);
+	walkexpr(&n, init);
+	*np = n;
+}
+
+// return 1 if integer n must be in range [0, max), 0 otherwise
+static int
+bounded(Node *n, int64 max)
+{
+	int64 v;
+	int32 bits;
+	int sign;
+
+	if(n->type == T || !isint[n->type->etype])
+		return 0;
+
+	sign = issigned[n->type->etype];
+	bits = 8*n->type->width;
+
+	if(smallintconst(n)) {
+		v = mpgetfix(n->val.u.xval);
+		return 0 <= v && v < max;
+	}
+
+	switch(n->op) {
+	case OAND:
+		v = -1;
+		if(smallintconst(n->left)) {
+			v = mpgetfix(n->left->val.u.xval);
+		} else if(smallintconst(n->right)) {
+			v = mpgetfix(n->right->val.u.xval);
+		}
+		if(0 <= v && v < max)
+			return 1;
+		break;
+
+	case OMOD:
+		if(!sign && smallintconst(n->right)) {
+			v = mpgetfix(n->right->val.u.xval);
+			if(0 <= v && v <= max)
+				return 1;
+		}
+		break;
+	
+	case ODIV:
+		if(!sign && smallintconst(n->right)) {
+			v = mpgetfix(n->right->val.u.xval);
+			while(bits > 0 && v >= 2) {
+				bits--;
+				v >>= 1;
+			}
+		}
+		break;
+	
+	case ORSH:
+		if(!sign && smallintconst(n->right)) {
+			v = mpgetfix(n->right->val.u.xval);
+			if(v > bits)
+				return 1;
+			bits -= v;
+		}
+		break;
+	}
+	
+	if(!sign && bits <= 62 && (1LL<<bits) <= max)
+		return 1;
+	
+	return 0;
+}
+
+void
+usefield(Node *n)
+{
+	Type *field, *l;
+
+	if(!fieldtrack_enabled)
+		return;
+
+	switch(n->op) {
+	default:
+		fatal("usefield %O", n->op);
+	case ODOT:
+	case ODOTPTR:
+		break;
+	}
+	
+	field = n->paramfld;
+	if(field == T)
+		fatal("usefield %T %S without paramfld", n->left->type, n->right->sym);
+	if(field->note == nil || strstr(field->note->s, "go:\"track\"") == nil)
+		return;
+
+	// dedup on list
+	if(field->lastfn == curfn)
+		return;
+	field->lastfn = curfn;
+	field->outer = n->left->type;
+	if(isptr[field->outer->etype])
+		field->outer = field->outer->type;
+	if(field->outer->sym == S)
+		yyerror("tracked field must be in named struct type");
+	if(!exportname(field->sym->name))
+		yyerror("tracked field must be exported (upper case)");
+
+	l = typ(0);
+	l->type = field;
+	l->down = curfn->paramfld;
+	curfn->paramfld = l;
+}
+
+static int
+candiscardlist(NodeList *l)
+{
+	for(; l; l=l->next)
+		if(!candiscard(l->n))
+			return 0;
+	return 1;
+}
+
+int
+candiscard(Node *n)
+{
+	if(n == N)
+		return 1;
+	
+	switch(n->op) {
+	default:
+		return 0;
+
+	case ONAME:
+	case ONONAME:
+	case OTYPE:
+	case OPACK:
+	case OLITERAL:
+	case OADD:
+	case OSUB:
+	case OOR:
+	case OXOR:
+	case OADDSTR:
+	case OADDR:
+	case OANDAND:
+	case OARRAYBYTESTR:
+	case OARRAYRUNESTR:
+	case OSTRARRAYBYTE:
+	case OSTRARRAYRUNE:
+	case OCAP:
+	case OCMPIFACE:
+	case OCMPSTR:
+	case OCOMPLIT:
+	case OMAPLIT:
+	case OSTRUCTLIT:
+	case OARRAYLIT:
+	case OPTRLIT:
+	case OCONV:
+	case OCONVIFACE:
+	case OCONVNOP:
+	case ODOT:
+	case OEQ:
+	case ONE:
+	case OLT:
+	case OLE:
+	case OGT:
+	case OGE:
+	case OKEY:
+	case OLEN:
+	case OMUL:
+	case OLSH:
+	case ORSH:
+	case OAND:
+	case OANDNOT:
+	case ONEW:
+	case ONOT:
+	case OCOM:
+	case OPLUS:
+	case OMINUS:
+	case OOROR:
+	case OPAREN:
+	case ORUNESTR:
+	case OREAL:
+	case OIMAG:
+	case OCOMPLEX:
+		// Discardable as long as the subpieces are.
+		break;
+
+	case ODIV:
+	case OMOD:
+		// Discardable as long as we know it's not division by zero.
+		if(isconst(n->right, CTINT) && mpcmpfixc(n->right->val.u.xval, 0) != 0)
+			break;
+		if(isconst(n->right, CTFLT) && mpcmpfltc(n->right->val.u.fval, 0) != 0)
+			break;
+		return 0;
+
+	case OMAKECHAN:
+	case OMAKEMAP:
+		// Discardable as long as we know it won't fail because of a bad size.
+		if(isconst(n->left, CTINT) && mpcmpfixc(n->left->val.u.xval, 0) == 0)
+			break;
+		return 0;
+	
+	case OMAKESLICE:
+		// Difficult to tell what sizes are okay.
+		return 0;		
+	}
+	
+	if(!candiscard(n->left) ||
+	   !candiscard(n->right) ||
+	   !candiscard(n->ntest) ||
+	   !candiscard(n->nincr) ||
+	   !candiscardlist(n->ninit) ||
+	   !candiscardlist(n->nbody) ||
+	   !candiscardlist(n->nelse) ||
+	   !candiscardlist(n->list) ||
+	   !candiscardlist(n->rlist)) {
+		return 0;
+	}
+	
+	return 1;
+}
+
+// rewrite
+//	print(x, y, z)
+// into
+//	func(a1, a2, a3) {
+//		print(a1, a2, a3)
+//	}(x, y, z)
+// and same for println.
+static void
+walkprintfunc(Node **np, NodeList **init)
+{
+	Node *n;
+	Node *a, *fn, *t, *oldfn;
+	NodeList *l, *printargs;
+	int num;
+	char buf[100];
+	static int prgen;
+	
+	n = *np;
+
+	if(n->ninit != nil) {
+		walkstmtlist(n->ninit);
+		*init = concat(*init, n->ninit);
+		n->ninit = nil;
+	}
+
+	t = nod(OTFUNC, N, N);
+	num = 0;
+	printargs = nil;
+	for(l=n->list; l != nil; l=l->next) {
+		snprint(buf, sizeof buf, "a%d", num++);
+		a = nod(ODCLFIELD, newname(lookup(buf)), typenod(l->n->type));
+		t->list = list(t->list, a);
+		printargs = list(printargs, a->left);
+	}
+
+	fn = nod(ODCLFUNC, N, N);
+	snprint(buf, sizeof buf, "print·%d", ++prgen);
+	fn->nname = newname(lookup(buf));
+	fn->nname->defn = fn;
+	fn->nname->ntype = t;
+	declare(fn->nname, PFUNC);
+
+	oldfn = curfn;
+	curfn = nil;
+	funchdr(fn);
+	
+	a = nod(n->op, N, N);
+	a->list = printargs;
+	typecheck(&a, Etop);
+	walkstmt(&a);
+	
+	fn->nbody = list1(a);
+
+	funcbody(fn);
+	
+	typecheck(&fn, Etop);
+	typechecklist(fn->nbody, Etop);
+	xtop = list(xtop, fn);
+	curfn = oldfn;
+
+	a = nod(OCALL, N, N);
+	a->left = fn->nname;
+	a->list = n->list;
+	typecheck(&a, Etop);
+	walkexpr(&a, init);
+	*np = a;
+}
diff --git a/src/cmd/gc/y.tab.c b/src/cmd/gc/y.tab.c
new file mode 100644
index 0000000..f464126
--- /dev/null
+++ b/src/cmd/gc/y.tab.c
@@ -0,0 +1,5132 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton implementation for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "2.3"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Using locations.  */
+#define YYLSP_NEEDED 0
+
+
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     LLITERAL = 258,
+     LASOP = 259,
+     LCOLAS = 260,
+     LBREAK = 261,
+     LCASE = 262,
+     LCHAN = 263,
+     LCONST = 264,
+     LCONTINUE = 265,
+     LDDD = 266,
+     LDEFAULT = 267,
+     LDEFER = 268,
+     LELSE = 269,
+     LFALL = 270,
+     LFOR = 271,
+     LFUNC = 272,
+     LGO = 273,
+     LGOTO = 274,
+     LIF = 275,
+     LIMPORT = 276,
+     LINTERFACE = 277,
+     LMAP = 278,
+     LNAME = 279,
+     LPACKAGE = 280,
+     LRANGE = 281,
+     LRETURN = 282,
+     LSELECT = 283,
+     LSTRUCT = 284,
+     LSWITCH = 285,
+     LTYPE = 286,
+     LVAR = 287,
+     LANDAND = 288,
+     LANDNOT = 289,
+     LBODY = 290,
+     LCOMM = 291,
+     LDEC = 292,
+     LEQ = 293,
+     LGE = 294,
+     LGT = 295,
+     LIGNORE = 296,
+     LINC = 297,
+     LLE = 298,
+     LLSH = 299,
+     LLT = 300,
+     LNE = 301,
+     LOROR = 302,
+     LRSH = 303,
+     NotPackage = 304,
+     NotParen = 305,
+     PreferToRightParen = 306
+   };
+#endif
+/* Tokens.  */
+#define LLITERAL 258
+#define LASOP 259
+#define LCOLAS 260
+#define LBREAK 261
+#define LCASE 262
+#define LCHAN 263
+#define LCONST 264
+#define LCONTINUE 265
+#define LDDD 266
+#define LDEFAULT 267
+#define LDEFER 268
+#define LELSE 269
+#define LFALL 270
+#define LFOR 271
+#define LFUNC 272
+#define LGO 273
+#define LGOTO 274
+#define LIF 275
+#define LIMPORT 276
+#define LINTERFACE 277
+#define LMAP 278
+#define LNAME 279
+#define LPACKAGE 280
+#define LRANGE 281
+#define LRETURN 282
+#define LSELECT 283
+#define LSTRUCT 284
+#define LSWITCH 285
+#define LTYPE 286
+#define LVAR 287
+#define LANDAND 288
+#define LANDNOT 289
+#define LBODY 290
+#define LCOMM 291
+#define LDEC 292
+#define LEQ 293
+#define LGE 294
+#define LGT 295
+#define LIGNORE 296
+#define LINC 297
+#define LLE 298
+#define LLSH 299
+#define LLT 300
+#define LNE 301
+#define LOROR 302
+#define LRSH 303
+#define NotPackage 304
+#define NotParen 305
+#define PreferToRightParen 306
+
+
+
+
+/* Copy the first part of user declarations.  */
+#line 20 "go.y"
+
+#include <u.h>
+#include <stdio.h>	/* if we don't, bison will, and go.h re-#defines getc */
+#include <libc.h>
+#include "go.h"
+
+static void fixlbrace(int);
+
+
+/* Enabling traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 1
+#endif
+
+/* Enabling the token table.  */
+#ifndef YYTOKEN_TABLE
+# define YYTOKEN_TABLE 0
+#endif
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 28 "go.y"
+{
+	Node*		node;
+	NodeList*		list;
+	Type*		type;
+	Sym*		sym;
+	struct	Val	val;
+	int		i;
+}
+/* Line 193 of yacc.c.  */
+#line 216 "y.tab.c"
+	YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations.  */
+
+
+/* Line 216 of yacc.c.  */
+#line 229 "y.tab.c"
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#elif (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+typedef signed char yytype_int8;
+#else
+typedef short int yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(msgid) dgettext ("bison-runtime", msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(msgid) msgid
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(e) ((void) (e))
+#else
+# define YYUSE(e) /* empty */
+#endif
+
+/* Identity function, used to suppress warnings about constant conditions.  */
+#ifndef lint
+# define YYID(n) (n)
+#else
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static int
+YYID (int i)
+#else
+static int
+YYID (i)
+    int i;
+#endif
+{
+  return i;
+}
+#endif
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#     ifndef _STDLIB_H
+#      define _STDLIB_H 1
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's `empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0))
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined _STDLIB_H \
+       && ! ((defined YYMALLOC || defined malloc) \
+	     && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef _STDLIB_H
+#    define _STDLIB_H 1
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+	 || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss;
+  YYSTYPE yyvs;
+  };
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(To, From, Count) \
+      __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+#  else
+#   define YYCOPY(To, From, Count)		\
+      do					\
+	{					\
+	  YYSIZE_T yyi;				\
+	  for (yyi = 0; yyi < (Count); yyi++)	\
+	    (To)[yyi] = (From)[yyi];		\
+	}					\
+      while (YYID (0))
+#  endif
+# endif
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack)					\
+    do									\
+      {									\
+	YYSIZE_T yynewbytes;						\
+	YYCOPY (&yyptr->Stack, Stack, yysize);				\
+	Stack = &yyptr->Stack;						\
+	yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+	yyptr += yynewbytes / sizeof (*yyptr);				\
+      }									\
+    while (YYID (0))
+
+#endif
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  4
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   2201
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  76
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  142
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  352
+/* YYNRULES -- Number of states.  */
+#define YYNSTATES  669
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   306
+
+#define YYTRANSLATE(YYX)						\
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,    69,     2,     2,    64,    55,    56,     2,
+      59,    60,    53,    49,    75,    50,    63,    54,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,    66,    62,
+       2,    65,     2,    73,    74,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,    71,     2,    72,    52,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,    67,    51,    68,    70,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      35,    36,    37,    38,    39,    40,    41,    42,    43,    44,
+      45,    46,    47,    48,    57,    58,    61
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+   YYRHS.  */
+static const yytype_uint16 yyprhs[] =
+{
+       0,     0,     3,     8,     9,    13,    14,    18,    19,    23,
+      26,    32,    36,    40,    43,    45,    49,    51,    54,    57,
+      62,    63,    65,    66,    71,    72,    74,    76,    78,    80,
+      83,    89,    93,    96,   102,   110,   114,   117,   123,   127,
+     129,   132,   137,   141,   146,   150,   152,   155,   157,   159,
+     162,   164,   168,   172,   176,   179,   182,   186,   192,   198,
+     201,   202,   207,   208,   212,   213,   216,   217,   222,   227,
+     232,   235,   241,   243,   245,   248,   249,   253,   255,   259,
+     260,   261,   262,   271,   272,   278,   279,   282,   283,   286,
+     287,   288,   296,   297,   303,   305,   309,   313,   317,   321,
+     325,   329,   333,   337,   341,   345,   349,   353,   357,   361,
+     365,   369,   373,   377,   381,   385,   387,   390,   393,   396,
+     399,   402,   405,   408,   411,   415,   421,   428,   430,   432,
+     436,   442,   448,   453,   460,   469,   471,   477,   483,   489,
+     497,   499,   500,   504,   506,   511,   513,   518,   520,   524,
+     526,   528,   530,   532,   534,   536,   538,   539,   541,   543,
+     545,   547,   552,   557,   559,   561,   563,   566,   568,   570,
+     572,   574,   576,   580,   582,   584,   586,   589,   591,   593,
+     595,   597,   601,   603,   605,   607,   609,   611,   613,   615,
+     617,   619,   623,   628,   633,   636,   640,   646,   648,   650,
+     653,   657,   663,   667,   673,   677,   681,   687,   696,   702,
+     711,   717,   718,   722,   723,   725,   729,   731,   736,   739,
+     740,   744,   746,   750,   752,   756,   758,   762,   764,   768,
+     770,   774,   778,   781,   786,   790,   796,   802,   804,   808,
+     810,   813,   815,   819,   824,   826,   829,   832,   834,   836,
+     840,   841,   844,   845,   847,   849,   851,   853,   855,   857,
+     859,   861,   863,   864,   869,   871,   874,   877,   880,   883,
+     886,   889,   891,   895,   897,   901,   903,   907,   909,   913,
+     915,   919,   921,   923,   927,   931,   932,   935,   936,   938,
+     939,   941,   942,   944,   945,   947,   948,   950,   951,   953,
+     954,   956,   957,   959,   960,   962,   967,   972,   978,   985,
+     990,   995,   997,   999,  1001,  1003,  1005,  1007,  1009,  1011,
+    1013,  1017,  1022,  1028,  1033,  1038,  1041,  1044,  1049,  1053,
+    1057,  1063,  1067,  1072,  1076,  1082,  1084,  1085,  1087,  1091,
+    1093,  1095,  1098,  1100,  1102,  1108,  1109,  1112,  1114,  1118,
+    1120,  1124,  1126
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS.  */
+static const yytype_int16 yyrhs[] =
+{
+      77,     0,    -1,    79,    78,    81,   166,    -1,    -1,    25,
+     141,    62,    -1,    -1,    80,    86,    88,    -1,    -1,    81,
+      82,    62,    -1,    21,    83,    -1,    21,    59,    84,   190,
+      60,    -1,    21,    59,    60,    -1,    85,    86,    88,    -1,
+      85,    88,    -1,    83,    -1,    84,    62,    83,    -1,     3,
+      -1,   141,     3,    -1,    63,     3,    -1,    25,    24,    87,
+      62,    -1,    -1,    24,    -1,    -1,    89,   214,    64,    64,
+      -1,    -1,    91,    -1,   158,    -1,   181,    -1,     1,    -1,
+      32,    93,    -1,    32,    59,   167,   190,    60,    -1,    32,
+      59,    60,    -1,    92,    94,    -1,    92,    59,    94,   190,
+      60,    -1,    92,    59,    94,    62,   168,   190,    60,    -1,
+      92,    59,    60,    -1,    31,    97,    -1,    31,    59,   169,
+     190,    60,    -1,    31,    59,    60,    -1,     9,    -1,   185,
+     146,    -1,   185,   146,    65,   186,    -1,   185,    65,   186,
+      -1,   185,   146,    65,   186,    -1,   185,    65,   186,    -1,
+      94,    -1,   185,   146,    -1,   185,    -1,   141,    -1,    96,
+     146,    -1,   126,    -1,   126,     4,   126,    -1,   186,    65,
+     186,    -1,   186,     5,   186,    -1,   126,    42,    -1,   126,
+      37,    -1,     7,   187,    66,    -1,     7,   187,    65,   126,
+      66,    -1,     7,   187,     5,   126,    66,    -1,    12,    66,
+      -1,    -1,    67,   101,   183,    68,    -1,    -1,    99,   103,
+     183,    -1,    -1,   104,   102,    -1,    -1,    35,   106,   183,
+      68,    -1,   186,    65,    26,   126,    -1,   186,     5,    26,
+     126,    -1,    26,   126,    -1,   194,    62,   194,    62,   194,
+      -1,   194,    -1,   107,    -1,   108,   105,    -1,    -1,    16,
+     111,   109,    -1,   194,    -1,   194,    62,   194,    -1,    -1,
+      -1,    -1,    20,   114,   112,   115,   105,   116,   119,   120,
+      -1,    -1,    14,    20,   118,   112,   105,    -1,    -1,   119,
+     117,    -1,    -1,    14,   100,    -1,    -1,    -1,    30,   122,
+     112,   123,    35,   104,    68,    -1,    -1,    28,   125,    35,
+     104,    68,    -1,   127,    -1,   126,    47,   126,    -1,   126,
+      33,   126,    -1,   126,    38,   126,    -1,   126,    46,   126,
+      -1,   126,    45,   126,    -1,   126,    43,   126,    -1,   126,
+      39,   126,    -1,   126,    40,   126,    -1,   126,    49,   126,
+      -1,   126,    50,   126,    -1,   126,    51,   126,    -1,   126,
+      52,   126,    -1,   126,    53,   126,    -1,   126,    54,   126,
+      -1,   126,    55,   126,    -1,   126,    56,   126,    -1,   126,
+      34,   126,    -1,   126,    44,   126,    -1,   126,    48,   126,
+      -1,   126,    36,   126,    -1,   134,    -1,    53,   127,    -1,
+      56,   127,    -1,    49,   127,    -1,    50,   127,    -1,    69,
+     127,    -1,    70,   127,    -1,    52,   127,    -1,    36,   127,
+      -1,   134,    59,    60,    -1,   134,    59,   187,   191,    60,
+      -1,   134,    59,   187,    11,   191,    60,    -1,     3,    -1,
+     143,    -1,   134,    63,   141,    -1,   134,    63,    59,   135,
+      60,    -1,   134,    63,    59,    31,    60,    -1,   134,    71,
+     126,    72,    -1,   134,    71,   192,    66,   192,    72,    -1,
+     134,    71,   192,    66,   192,    66,   192,    72,    -1,   128,
+      -1,   149,    59,   126,   191,    60,    -1,   150,   137,   130,
+     189,    68,    -1,   129,    67,   130,   189,    68,    -1,    59,
+     135,    60,    67,   130,   189,    68,    -1,   165,    -1,    -1,
+     126,    66,   133,    -1,   126,    -1,    67,   130,   189,    68,
+      -1,   126,    -1,    67,   130,   189,    68,    -1,   129,    -1,
+      59,   135,    60,    -1,   126,    -1,   147,    -1,   146,    -1,
+      35,    -1,    67,    -1,   141,    -1,   141,    -1,    -1,   138,
+      -1,    24,    -1,   142,    -1,    73,    -1,    74,     3,    63,
+      24,    -1,    74,     3,    63,    73,    -1,   141,    -1,   138,
+      -1,    11,    -1,    11,   146,    -1,   155,    -1,   161,    -1,
+     153,    -1,   154,    -1,   152,    -1,    59,   146,    60,    -1,
+     155,    -1,   161,    -1,   153,    -1,    53,   147,    -1,   161,
+      -1,   153,    -1,   154,    -1,   152,    -1,    59,   146,    60,
+      -1,   161,    -1,   153,    -1,   153,    -1,   155,    -1,   161,
+      -1,   153,    -1,   154,    -1,   152,    -1,   143,    -1,   143,
+      63,   141,    -1,    71,   192,    72,   146,    -1,    71,    11,
+      72,   146,    -1,     8,   148,    -1,     8,    36,   146,    -1,
+      23,    71,   146,    72,   146,    -1,   156,    -1,   157,    -1,
+      53,   146,    -1,    36,     8,   146,    -1,    29,   137,   170,
+     190,    68,    -1,    29,   137,    68,    -1,    22,   137,   171,
+     190,    68,    -1,    22,   137,    68,    -1,    17,   159,   162,
+      -1,   141,    59,   179,    60,   163,    -1,    59,   179,    60,
+     141,    59,   179,    60,   163,    -1,   200,    59,   195,    60,
+     210,    -1,    59,   215,    60,   141,    59,   195,    60,   210,
+      -1,    17,    59,   179,    60,   163,    -1,    -1,    67,   183,
+      68,    -1,    -1,   151,    -1,    59,   179,    60,    -1,   161,
+      -1,   164,   137,   183,    68,    -1,   164,     1,    -1,    -1,
+     166,    90,    62,    -1,    93,    -1,   167,    62,    93,    -1,
+      95,    -1,   168,    62,    95,    -1,    97,    -1,   169,    62,
+      97,    -1,   172,    -1,   170,    62,   172,    -1,   175,    -1,
+     171,    62,   175,    -1,   184,   146,   198,    -1,   174,   198,
+      -1,    59,   174,    60,   198,    -1,    53,   174,   198,    -1,
+      59,    53,   174,    60,   198,    -1,    53,    59,   174,    60,
+     198,    -1,    24,    -1,    24,    63,   141,    -1,   173,    -1,
+     138,   176,    -1,   173,    -1,    59,   173,    60,    -1,    59,
+     179,    60,   163,    -1,   136,    -1,   141,   136,    -1,   141,
+     145,    -1,   145,    -1,   177,    -1,   178,    75,   177,    -1,
+      -1,   178,   191,    -1,    -1,   100,    -1,    91,    -1,   181,
+      -1,     1,    -1,    98,    -1,   110,    -1,   121,    -1,   124,
+      -1,   113,    -1,    -1,   144,    66,   182,   180,    -1,    15,
+      -1,     6,   140,    -1,    10,   140,    -1,    18,   128,    -1,
+      13,   128,    -1,    19,   138,    -1,    27,   193,    -1,   180,
+      -1,   183,    62,   180,    -1,   138,    -1,   184,    75,   138,
+      -1,   139,    -1,   185,    75,   139,    -1,   126,    -1,   186,
+      75,   126,    -1,   135,    -1,   187,    75,   135,    -1,   131,
+      -1,   132,    -1,   188,    75,   131,    -1,   188,    75,   132,
+      -1,    -1,   188,   191,    -1,    -1,    62,    -1,    -1,    75,
+      -1,    -1,   126,    -1,    -1,   186,    -1,    -1,    98,    -1,
+      -1,   215,    -1,    -1,   216,    -1,    -1,   217,    -1,    -1,
+       3,    -1,    21,    24,     3,    62,    -1,    32,   200,   202,
+      62,    -1,     9,   200,    65,   213,    62,    -1,     9,   200,
+     202,    65,   213,    62,    -1,    31,   201,   202,    62,    -1,
+      17,   160,   162,    62,    -1,   142,    -1,   200,    -1,   204,
+      -1,   205,    -1,   206,    -1,   204,    -1,   206,    -1,   142,
+      -1,    24,    -1,    71,    72,   202,    -1,    71,     3,    72,
+     202,    -1,    23,    71,   202,    72,   202,    -1,    29,    67,
+     196,    68,    -1,    22,    67,   197,    68,    -1,    53,   202,
+      -1,     8,   203,    -1,     8,    59,   205,    60,    -1,     8,
+      36,   202,    -1,    36,     8,   202,    -1,    17,    59,   195,
+      60,   210,    -1,   141,   202,   198,    -1,   141,    11,   202,
+     198,    -1,   141,   202,   198,    -1,   141,    59,   195,    60,
+     210,    -1,   202,    -1,    -1,   211,    -1,    59,   195,    60,
+      -1,   202,    -1,     3,    -1,    50,     3,    -1,   141,    -1,
+     212,    -1,    59,   212,    49,   212,    60,    -1,    -1,   214,
+     199,    -1,   207,    -1,   215,    75,   207,    -1,   208,    -1,
+     216,    62,   208,    -1,   209,    -1,   217,    62,   209,    -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined.  */
+static const yytype_uint16 yyrline[] =
+{
+       0,   124,   124,   133,   139,   150,   150,   165,   166,   169,
+     170,   171,   174,   211,   222,   223,   226,   233,   240,   249,
+     263,   264,   271,   271,   284,   288,   289,   293,   298,   304,
+     308,   312,   316,   322,   328,   334,   339,   343,   347,   353,
+     359,   363,   367,   373,   377,   383,   384,   388,   394,   403,
+     409,   427,   432,   444,   460,   466,   474,   494,   512,   521,
+     540,   539,   554,   553,   585,   588,   595,   594,   605,   611,
+     618,   625,   636,   642,   645,   653,   652,   663,   669,   681,
+     685,   690,   680,   711,   710,   723,   726,   732,   735,   747,
+     751,   746,   769,   768,   784,   785,   789,   793,   797,   801,
+     805,   809,   813,   817,   821,   825,   829,   833,   837,   841,
+     845,   849,   853,   857,   862,   868,   869,   873,   884,   888,
+     892,   896,   901,   905,   915,   919,   924,   932,   936,   937,
+     948,   952,   956,   960,   964,   972,   973,   979,   986,   992,
+     999,  1002,  1009,  1015,  1032,  1039,  1040,  1047,  1048,  1067,
+    1068,  1071,  1074,  1078,  1089,  1098,  1104,  1107,  1110,  1117,
+    1118,  1124,  1137,  1152,  1160,  1172,  1177,  1183,  1184,  1185,
+    1186,  1187,  1188,  1194,  1195,  1196,  1197,  1203,  1204,  1205,
+    1206,  1207,  1213,  1214,  1217,  1220,  1221,  1222,  1223,  1224,
+    1227,  1228,  1241,  1245,  1250,  1255,  1260,  1264,  1265,  1268,
+    1274,  1281,  1287,  1294,  1300,  1311,  1326,  1355,  1393,  1418,
+    1436,  1445,  1448,  1456,  1460,  1464,  1471,  1477,  1482,  1494,
+    1497,  1508,  1509,  1515,  1516,  1522,  1526,  1532,  1533,  1539,
+    1543,  1549,  1572,  1577,  1583,  1589,  1596,  1605,  1614,  1629,
+    1635,  1640,  1644,  1651,  1664,  1665,  1671,  1677,  1680,  1684,
+    1690,  1693,  1702,  1705,  1706,  1710,  1711,  1717,  1718,  1719,
+    1720,  1721,  1723,  1722,  1737,  1743,  1747,  1751,  1755,  1759,
+    1764,  1783,  1789,  1797,  1801,  1807,  1811,  1817,  1821,  1827,
+    1831,  1840,  1844,  1848,  1852,  1858,  1861,  1869,  1870,  1872,
+    1873,  1876,  1879,  1882,  1885,  1888,  1891,  1894,  1897,  1900,
+    1903,  1906,  1909,  1912,  1915,  1921,  1925,  1929,  1933,  1937,
+    1941,  1961,  1968,  1979,  1980,  1981,  1984,  1985,  1988,  1992,
+    2002,  2006,  2010,  2014,  2018,  2022,  2026,  2032,  2038,  2046,
+    2054,  2060,  2067,  2083,  2105,  2109,  2115,  2118,  2121,  2125,
+    2135,  2139,  2158,  2166,  2167,  2179,  2180,  2183,  2187,  2193,
+    2197,  2203,  2207
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+const char *yytname[] =
+{
+  "$end", "error", "$undefined", "LLITERAL", "LASOP", "LCOLAS", "LBREAK",
+  "LCASE", "LCHAN", "LCONST", "LCONTINUE", "LDDD", "LDEFAULT", "LDEFER",
+  "LELSE", "LFALL", "LFOR", "LFUNC", "LGO", "LGOTO", "LIF", "LIMPORT",
+  "LINTERFACE", "LMAP", "LNAME", "LPACKAGE", "LRANGE", "LRETURN",
+  "LSELECT", "LSTRUCT", "LSWITCH", "LTYPE", "LVAR", "LANDAND", "LANDNOT",
+  "LBODY", "LCOMM", "LDEC", "LEQ", "LGE", "LGT", "LIGNORE", "LINC", "LLE",
+  "LLSH", "LLT", "LNE", "LOROR", "LRSH", "'+'", "'-'", "'|'", "'^'", "'*'",
+  "'/'", "'%'", "'&'", "NotPackage", "NotParen", "'('", "')'",
+  "PreferToRightParen", "';'", "'.'", "'$'", "'='", "':'", "'{'", "'}'",
+  "'!'", "'~'", "'['", "']'", "'?'", "'@'", "','", "$accept", "file",
+  "package", "loadsys", "@1", "imports", "import", "import_stmt",
+  "import_stmt_list", "import_here", "import_package", "import_safety",
+  "import_there", "@2", "xdcl", "common_dcl", "lconst", "vardcl",
+  "constdcl", "constdcl1", "typedclname", "typedcl", "simple_stmt", "case",
+  "compound_stmt", "@3", "caseblock", "@4", "caseblock_list", "loop_body",
+  "@5", "range_stmt", "for_header", "for_body", "for_stmt", "@6",
+  "if_header", "if_stmt", "@7", "@8", "@9", "elseif", "@10", "elseif_list",
+  "else", "switch_stmt", "@11", "@12", "select_stmt", "@13", "expr",
+  "uexpr", "pseudocall", "pexpr_no_paren", "start_complit", "keyval",
+  "bare_complitexpr", "complitexpr", "pexpr", "expr_or_type",
+  "name_or_type", "lbrace", "new_name", "dcl_name", "onew_name", "sym",
+  "hidden_importsym", "name", "labelname", "dotdotdot", "ntype",
+  "non_expr_type", "non_recvchantype", "convtype", "comptype",
+  "fnret_type", "dotname", "othertype", "ptrtype", "recvchantype",
+  "structtype", "interfacetype", "xfndcl", "fndcl", "hidden_fndcl",
+  "fntype", "fnbody", "fnres", "fnlitdcl", "fnliteral", "xdcl_list",
+  "vardcl_list", "constdcl_list", "typedcl_list", "structdcl_list",
+  "interfacedcl_list", "structdcl", "packname", "embed", "interfacedcl",
+  "indcl", "arg_type", "arg_type_list", "oarg_type_list_ocomma", "stmt",
+  "non_dcl_stmt", "@14", "stmt_list", "new_name_list", "dcl_name_list",
+  "expr_list", "expr_or_type_list", "keyval_list", "braced_keyval_list",
+  "osemi", "ocomma", "oexpr", "oexpr_list", "osimple_stmt",
+  "ohidden_funarg_list", "ohidden_structdcl_list",
+  "ohidden_interfacedcl_list", "oliteral", "hidden_import",
+  "hidden_pkg_importsym", "hidden_pkgtype", "hidden_type",
+  "hidden_type_non_recv_chan", "hidden_type_misc", "hidden_type_recv_chan",
+  "hidden_type_func", "hidden_funarg", "hidden_structdcl",
+  "hidden_interfacedcl", "ohidden_funres", "hidden_funres",
+  "hidden_literal", "hidden_constant", "hidden_import_list",
+  "hidden_funarg_list", "hidden_structdcl_list",
+  "hidden_interfacedcl_list", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+   token YYLEX-NUM.  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
+     285,   286,   287,   288,   289,   290,   291,   292,   293,   294,
+     295,   296,   297,   298,   299,   300,   301,   302,   303,    43,
+      45,   124,    94,    42,    47,    37,    38,   304,   305,    40,
+      41,   306,    59,    46,    36,    61,    58,   123,   125,    33,
+     126,    91,    93,    63,    64,    44
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,    76,    77,    78,    78,    80,    79,    81,    81,    82,
+      82,    82,    83,    83,    84,    84,    85,    85,    85,    86,
+      87,    87,    89,    88,    90,    90,    90,    90,    90,    91,
+      91,    91,    91,    91,    91,    91,    91,    91,    91,    92,
+      93,    93,    93,    94,    94,    95,    95,    95,    96,    97,
+      98,    98,    98,    98,    98,    98,    99,    99,    99,    99,
+     101,   100,   103,   102,   104,   104,   106,   105,   107,   107,
+     107,   108,   108,   108,   109,   111,   110,   112,   112,   114,
+     115,   116,   113,   118,   117,   119,   119,   120,   120,   122,
+     123,   121,   125,   124,   126,   126,   126,   126,   126,   126,
+     126,   126,   126,   126,   126,   126,   126,   126,   126,   126,
+     126,   126,   126,   126,   126,   127,   127,   127,   127,   127,
+     127,   127,   127,   127,   128,   128,   128,   129,   129,   129,
+     129,   129,   129,   129,   129,   129,   129,   129,   129,   129,
+     129,   130,   131,   132,   132,   133,   133,   134,   134,   135,
+     135,   136,   137,   137,   138,   139,   140,   140,   141,   141,
+     141,   142,   142,   143,   144,   145,   145,   146,   146,   146,
+     146,   146,   146,   147,   147,   147,   147,   148,   148,   148,
+     148,   148,   149,   149,   150,   151,   151,   151,   151,   151,
+     152,   152,   153,   153,   153,   153,   153,   153,   153,   154,
+     155,   156,   156,   157,   157,   158,   159,   159,   160,   160,
+     161,   162,   162,   163,   163,   163,   164,   165,   165,   166,
+     166,   167,   167,   168,   168,   169,   169,   170,   170,   171,
+     171,   172,   172,   172,   172,   172,   172,   173,   173,   174,
+     175,   175,   175,   176,   177,   177,   177,   177,   178,   178,
+     179,   179,   180,   180,   180,   180,   180,   181,   181,   181,
+     181,   181,   182,   181,   181,   181,   181,   181,   181,   181,
+     181,   183,   183,   184,   184,   185,   185,   186,   186,   187,
+     187,   188,   188,   188,   188,   189,   189,   190,   190,   191,
+     191,   192,   192,   193,   193,   194,   194,   195,   195,   196,
+     196,   197,   197,   198,   198,   199,   199,   199,   199,   199,
+     199,   200,   201,   202,   202,   202,   203,   203,   204,   204,
+     204,   204,   204,   204,   204,   204,   204,   204,   204,   205,
+     206,   207,   207,   208,   209,   209,   210,   210,   211,   211,
+     212,   212,   212,   213,   213,   214,   214,   215,   215,   216,
+     216,   217,   217
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     4,     0,     3,     0,     3,     0,     3,     2,
+       5,     3,     3,     2,     1,     3,     1,     2,     2,     4,
+       0,     1,     0,     4,     0,     1,     1,     1,     1,     2,
+       5,     3,     2,     5,     7,     3,     2,     5,     3,     1,
+       2,     4,     3,     4,     3,     1,     2,     1,     1,     2,
+       1,     3,     3,     3,     2,     2,     3,     5,     5,     2,
+       0,     4,     0,     3,     0,     2,     0,     4,     4,     4,
+       2,     5,     1,     1,     2,     0,     3,     1,     3,     0,
+       0,     0,     8,     0,     5,     0,     2,     0,     2,     0,
+       0,     7,     0,     5,     1,     3,     3,     3,     3,     3,
+       3,     3,     3,     3,     3,     3,     3,     3,     3,     3,
+       3,     3,     3,     3,     3,     1,     2,     2,     2,     2,
+       2,     2,     2,     2,     3,     5,     6,     1,     1,     3,
+       5,     5,     4,     6,     8,     1,     5,     5,     5,     7,
+       1,     0,     3,     1,     4,     1,     4,     1,     3,     1,
+       1,     1,     1,     1,     1,     1,     0,     1,     1,     1,
+       1,     4,     4,     1,     1,     1,     2,     1,     1,     1,
+       1,     1,     3,     1,     1,     1,     2,     1,     1,     1,
+       1,     3,     1,     1,     1,     1,     1,     1,     1,     1,
+       1,     3,     4,     4,     2,     3,     5,     1,     1,     2,
+       3,     5,     3,     5,     3,     3,     5,     8,     5,     8,
+       5,     0,     3,     0,     1,     3,     1,     4,     2,     0,
+       3,     1,     3,     1,     3,     1,     3,     1,     3,     1,
+       3,     3,     2,     4,     3,     5,     5,     1,     3,     1,
+       2,     1,     3,     4,     1,     2,     2,     1,     1,     3,
+       0,     2,     0,     1,     1,     1,     1,     1,     1,     1,
+       1,     1,     0,     4,     1,     2,     2,     2,     2,     2,
+       2,     1,     3,     1,     3,     1,     3,     1,     3,     1,
+       3,     1,     1,     3,     3,     0,     2,     0,     1,     0,
+       1,     0,     1,     0,     1,     0,     1,     0,     1,     0,
+       1,     0,     1,     0,     1,     4,     4,     5,     6,     4,
+       4,     1,     1,     1,     1,     1,     1,     1,     1,     1,
+       3,     4,     5,     4,     4,     2,     2,     4,     3,     3,
+       5,     3,     4,     3,     5,     1,     0,     1,     3,     1,
+       1,     2,     1,     1,     5,     0,     2,     1,     3,     1,
+       3,     1,     3
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+   STATE-NUM when YYTABLE doesn't specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_uint16 yydefact[] =
+{
+       5,     0,     3,     0,     1,     0,     7,     0,    22,   158,
+     160,     0,     0,   159,   219,    20,     6,   345,     0,     4,
+       0,     0,     0,    21,     0,     0,     0,    16,     0,     0,
+       9,    22,     0,     8,    28,   127,   156,     0,    39,   156,
+       0,   264,    75,     0,     0,     0,    79,     0,     0,   293,
+      92,     0,    89,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,   291,     0,    25,     0,   257,   258,
+     261,   259,   260,    50,    94,   135,   147,   115,   164,   163,
+     128,     0,     0,     0,   184,   197,   198,    26,   216,     0,
+     140,    27,     0,    19,     0,     0,     0,     0,     0,     0,
+     346,   161,   162,    11,    14,   287,    18,    22,    13,    17,
+     157,   265,   154,     0,     0,     0,     0,   163,   190,   194,
+     180,   178,   179,   177,   266,   135,     0,   295,   250,     0,
+     211,   135,   269,   295,   152,   153,     0,     0,   277,   294,
+     270,     0,     0,   295,     0,     0,    36,    48,     0,    29,
+     275,   155,     0,   123,   118,   119,   122,   116,   117,     0,
+       0,   149,     0,   150,   175,   173,   174,   120,   121,     0,
+     292,     0,   220,     0,    32,     0,     0,     0,     0,     0,
+      55,     0,     0,     0,    54,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,   141,
+       0,     0,   291,   262,     0,   141,   218,     0,     0,     0,
+       0,   311,     0,     0,   211,     0,     0,   312,     0,     0,
+      23,   288,     0,    12,   250,     0,     0,   195,   171,   169,
+     170,   167,   168,   199,     0,     0,     0,   296,    73,     0,
+      76,     0,    72,   165,   244,   163,   247,   151,   248,   289,
+       0,   250,     0,   205,    80,    77,   158,     0,   204,     0,
+     287,   241,   229,     0,    64,     0,     0,   202,   273,   287,
+     227,   239,   303,     0,    90,    38,   225,   287,    49,    31,
+     221,   287,     0,     0,    40,     0,   176,   148,     0,     0,
+      35,   287,     0,     0,    51,    96,   111,   114,    97,   101,
+     102,   100,   112,    99,    98,    95,   113,   103,   104,   105,
+     106,   107,   108,   109,   110,   285,   124,   279,   289,     0,
+     129,   292,     0,     0,   289,   285,   256,    60,   254,   253,
+     271,   255,     0,    53,    52,   278,     0,     0,     0,     0,
+     319,     0,     0,     0,     0,     0,   318,     0,   313,   314,
+     315,     0,   347,     0,     0,   297,     0,     0,     0,    15,
+      10,     0,     0,     0,   181,   191,    70,    66,    74,     0,
+       0,   295,   166,   245,   246,   290,   251,   213,     0,     0,
+       0,   295,     0,   237,     0,   250,   240,   288,     0,     0,
+       0,     0,   303,     0,     0,   288,     0,   304,   232,     0,
+     303,     0,   288,     0,   288,     0,    42,   276,     0,     0,
+       0,   200,   171,   169,   170,   168,   141,   193,   192,   288,
+       0,    44,     0,   141,   143,   281,   282,   289,     0,   289,
+     290,     0,     0,     0,   132,   291,   263,   290,     0,     0,
+       0,     0,   217,     0,     0,   326,   316,   317,   297,   301,
+       0,   299,     0,   325,   340,     0,     0,   342,   343,     0,
+       0,     0,     0,     0,   303,     0,     0,   310,     0,   298,
+     305,   309,   306,   213,   172,     0,     0,     0,     0,   249,
+     250,   163,   214,   189,   187,   188,   185,   186,   210,   213,
+     212,    81,    78,   238,   242,     0,   230,   203,   196,     0,
+       0,    93,    62,    65,     0,   234,     0,   303,   228,   201,
+     274,   231,    64,   226,    37,   222,    30,    41,     0,   285,
+      45,   223,   287,    47,    33,    43,   285,     0,   290,   286,
+     138,     0,   280,   125,   131,   130,     0,   136,   137,     0,
+     272,   328,     0,     0,   319,     0,   318,     0,   335,   351,
+     302,     0,     0,     0,   349,   300,   329,   341,     0,   307,
+       0,   320,     0,   303,   331,     0,   348,   336,     0,    69,
+      68,   295,     0,   250,   206,    85,   213,     0,    59,     0,
+     303,   303,   233,     0,   172,     0,   288,     0,    46,     0,
+     141,   145,   142,   283,   284,   126,   291,   133,    61,   327,
+     336,   297,   324,     0,     0,   303,   323,     0,     0,   321,
+     308,   332,   297,   297,   339,   208,   337,    67,    71,   215,
+       0,    87,   243,     0,     0,    56,     0,    63,   236,   235,
+      91,   139,   224,    34,   144,   285,     0,   330,     0,   352,
+     322,   333,   350,     0,     0,     0,   213,     0,    86,    82,
+       0,     0,     0,   134,   336,   344,   336,   338,   207,    83,
+      88,    58,    57,   146,   334,   209,   295,     0,    84
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int16 yydefgoto[] =
+{
+      -1,     1,     6,     2,     3,    14,    21,    30,   105,    31,
+       8,    24,    16,    17,    65,   328,    67,   149,   520,   521,
+     145,   146,    68,   502,   329,   440,   503,   579,   390,   368,
+     475,   238,   239,   240,    69,   127,   254,    70,   133,   380,
+     575,   648,   666,   621,   649,    71,   143,   401,    72,   141,
+      73,    74,    75,    76,   315,   425,   426,   592,    77,   317,
+     244,   136,    78,   150,   111,   117,    13,    80,    81,   246,
+     247,   163,   119,    82,    83,   482,   228,    84,   230,   231,
+      85,    86,    87,   130,   214,    88,   253,   488,    89,    90,
+      22,   281,   522,   277,   269,   260,   270,   271,   272,   262,
+     386,   248,   249,   250,   330,   331,   323,   332,   273,   152,
+      92,   318,   427,   428,   222,   376,   171,   140,   255,   468,
+     553,   547,   398,   100,   212,   218,   614,   445,   348,   349,
+     350,   352,   554,   549,   615,   616,   458,   459,    25,   469,
+     555,   550
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+#define YYPACT_NINF -473
+static const yytype_int16 yypact[] =
+{
+    -473,    65,    22,    49,  -473,   261,  -473,    64,  -473,  -473,
+    -473,    95,    52,  -473,   143,   145,  -473,  -473,   104,  -473,
+      68,   128,  1049,  -473,   142,   305,    16,  -473,    56,   204,
+    -473,    49,   220,  -473,  -473,  -473,   261,   974,  -473,   261,
+     562,  -473,  -473,   288,   562,   261,  -473,    14,   147,  1615,
+    -473,    14,  -473,   395,   401,  1615,  1615,  1615,  1615,  1615,
+    1615,  1658,  1615,  1615,   737,   168,  -473,   414,  -473,  -473,
+    -473,  -473,  -473,   649,  -473,  -473,   165,   122,  -473,   169,
+    -473,   177,   218,    14,   219,  -473,  -473,  -473,   235,    89,
+    -473,  -473,    34,  -473,   206,   124,   286,   206,   206,   260,
+    -473,  -473,  -473,  -473,  -473,   265,  -473,  -473,  -473,  -473,
+    -473,  -473,  -473,   270,  1803,  1803,  1803,  -473,   269,  -473,
+    -473,  -473,  -473,  -473,  -473,    39,   122,   882,  1777,   283,
+     277,   230,  -473,  1615,  -473,  -473,   292,  1803,  2097,   280,
+    -473,   332,   315,  1615,   215,  1803,  -473,  -473,   244,  -473,
+    -473,  -473,   949,  -473,  -473,  -473,  -473,  -473,  -473,  1701,
+    1658,  2097,   298,  -473,     9,  -473,    59,  -473,  -473,   303,
+    2097,   319,  -473,   330,  -473,  1744,  1615,  1615,  1615,  1615,
+    -473,  1615,  1615,  1615,  -473,  1615,  1615,  1615,  1615,  1615,
+    1615,  1615,  1615,  1615,  1615,  1615,  1615,  1615,  1615,  -473,
+    1297,   455,  1615,  -473,  1615,  -473,  -473,  1225,  1615,  1615,
+    1615,  -473,   594,   261,   277,   328,   403,  -473,  1308,  1308,
+    -473,   152,   352,  -473,  1777,   405,  1803,  -473,  -473,  -473,
+    -473,  -473,  -473,  -473,   354,   261,  1615,  -473,  -473,   382,
+    -473,    47,   360,  1803,  -473,  1777,  -473,  -473,  -473,   351,
+     367,  1777,  1225,  -473,  -473,   366,    84,   407,  -473,   374,
+     373,  -473,  -473,   372,  -473,   138,    42,  -473,  -473,   377,
+    -473,  -473,   442,  1769,  -473,  -473,  -473,   384,  -473,  -473,
+    -473,   389,  1615,   261,   391,  1830,  -473,   394,  1803,  1803,
+    -473,   409,  1615,   411,  2097,  1935,  -473,  2121,  1080,  1080,
+    1080,  1080,  -473,  1080,  1080,  2145,  -473,   503,   503,   503,
+     503,  -473,  -473,  -473,  -473,  1352,  -473,  -473,    27,  1407,
+    -473,  1995,   412,  1147,  1962,  1352,  -473,  -473,  -473,  -473,
+    -473,  -473,     7,   280,   280,  2097,   698,   418,   415,   413,
+    -473,   416,   477,  1308,   188,    31,  -473,   425,  -473,  -473,
+    -473,  1897,  -473,   221,   433,   261,   434,   436,   439,  -473,
+    -473,   432,  1803,   452,  -473,  -473,  2097,  -473,  -473,  1462,
+    1517,  1615,  -473,  -473,  -473,  1777,  -473,  1856,   453,    91,
+     382,  1615,   261,   454,   456,  1777,  -473,   475,   451,  1803,
+     133,   407,   442,   407,   460,   326,   462,  -473,  -473,   261,
+     442,   467,   261,   478,   261,   486,   280,  -473,  1615,  1864,
+    1803,  -473,    26,   248,   264,   430,  -473,  -473,  -473,   261,
+     492,   280,  1615,  -473,  2025,  -473,  -473,   485,   493,   487,
+    1658,   504,   506,   508,  -473,  1615,  -473,  -473,   512,   505,
+    1225,  1147,  -473,  1308,   517,  -473,  -473,  -473,   261,  1889,
+    1308,   261,  1308,  -473,  -473,   571,   155,  -473,  -473,   514,
+     509,  1308,   188,  1308,   442,   261,   261,  -473,   518,   507,
+    -473,  -473,  -473,  1856,  -473,  1225,  1615,  1615,   521,  -473,
+    1777,   528,  -473,  -473,  -473,  -473,  -473,  -473,  -473,  1856,
+    -473,  -473,  -473,  -473,  -473,   520,  -473,  -473,  -473,  1658,
+     522,  -473,  -473,  -473,   530,  -473,   532,   442,  -473,  -473,
+    -473,  -473,  -473,  -473,  -473,  -473,  -473,   280,   535,  1352,
+    -473,  -473,   536,  1744,  -473,   280,  1352,  1560,  1352,  -473,
+    -473,   539,  -473,  -473,  -473,  -473,   247,  -473,  -473,   308,
+    -473,  -473,   541,   543,   545,   546,   547,   544,  -473,  -473,
+     551,   548,  1308,   554,  -473,   557,  -473,  -473,   576,  -473,
+    1308,  -473,   564,   442,  -473,   568,  -473,  1923,   318,  2097,
+    2097,  1615,   569,  1777,  -473,  -473,  1856,   156,  -473,  1147,
+     442,   442,  -473,   243,   483,   563,   261,   577,   411,   570,
+    -473,  2097,  -473,  -473,  -473,  -473,  1615,  -473,  -473,  -473,
+    1923,   261,  -473,  1889,  1308,   442,  -473,   261,   155,  -473,
+    -473,  -473,   261,   261,  -473,  -473,  -473,  -473,  -473,  -473,
+     579,   627,  -473,  1615,  1615,  -473,  1658,   580,  -473,  -473,
+    -473,  -473,  -473,  -473,  -473,  1352,   572,  -473,   583,  -473,
+    -473,  -473,  -473,   585,   586,   590,  1856,    77,  -473,  -473,
+    2049,  2073,   584,  -473,  1923,  -473,  1923,  -473,  -473,  -473,
+    -473,  -473,  -473,  -473,  -473,  -473,  1615,   382,  -473
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int16 yypgoto[] =
+{
+    -473,  -473,  -473,  -473,  -473,  -473,  -473,   -12,  -473,  -473,
+     624,  -473,    -1,  -473,  -473,   635,  -473,  -137,   -48,    74,
+    -473,  -130,  -112,  -473,    11,  -473,  -473,  -473,   149,  -372,
+    -473,  -473,  -473,  -473,  -473,  -473,  -140,  -473,  -473,  -473,
+    -473,  -473,  -473,  -473,  -473,  -473,  -473,  -473,  -473,  -473,
+     662,   448,   257,  -473,  -196,   135,   139,  -473,   262,   -59,
+     424,   -16,    -3,   387,   632,   427,   313,    20,  -473,   428,
+     -89,   524,  -473,  -473,  -473,  -473,   -36,   -37,   -31,   -49,
+    -473,  -473,  -473,  -473,  -473,   -32,   458,  -472,  -473,  -473,
+    -473,  -473,  -473,  -473,  -473,  -473,   279,  -108,  -211,   290,
+    -473,   306,  -473,  -214,  -291,   658,  -473,  -230,  -473,   -63,
+      -6,   191,  -473,  -302,  -219,  -254,  -195,  -473,  -107,  -435,
+    -473,  -473,  -347,  -473,   323,  -473,    72,  -473,   371,   268,
+     380,   242,   102,   110,  -468,  -473,  -438,   255,  -473,   515,
+    -473,  -473
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]].  What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule which
+   number is the opposite.  If zero, do what YYDEFACT says.
+   If YYTABLE_NINF, syntax error.  */
+#define YYTABLE_NINF -278
+static const yytype_int16 yytable[] =
+{
+     121,   120,   162,   274,   175,   123,   122,   322,   491,   325,
+     361,   280,   165,   543,   276,   237,   104,   574,   558,   174,
+     242,   237,   379,   439,   164,   227,   233,   234,   261,   166,
+     108,   237,   436,   110,   460,   142,   110,   378,   429,   208,
+     101,   388,   132,   139,  -184,   505,  -268,     5,   263,   134,
+     396,  -268,   369,   511,   392,   394,   278,   118,   403,    27,
+    -216,  -180,   405,   284,   431,     4,   383,   205,  -183,   441,
+     438,    27,   420,   207,     7,   442,  -184,   229,   229,   229,
+       9,   135,   232,   232,   232,  -180,   293,  -237,    15,   102,
+     206,   229,     9,  -180,  -216,   393,   232,   659,    18,   209,
+     229,  -268,   430,   461,   622,   232,   223,  -268,   229,   210,
+     175,   165,   370,   232,    19,   229,   103,   564,  -182,    29,
+     232,   241,   210,   164,   134,   291,  -216,    28,   166,    10,
+      11,    29,   637,   259,   118,   118,   118,   363,   229,   268,
+     499,    10,    11,   232,   327,   500,  -237,   382,   118,   384,
+     540,   165,  -237,   441,   372,    27,   135,   118,   454,   490,
+     582,   623,   383,   164,    20,   118,   638,    26,   166,    23,
+     643,   495,   118,   529,   658,   531,     9,   644,   645,     9,
+     504,   200,   506,   213,   400,   201,   664,   229,   665,   229,
+      33,   454,   232,   202,   232,   118,   411,   391,    11,   417,
+     418,   501,   333,   334,    93,   455,   229,   106,   229,   359,
+     539,   232,     9,   232,   229,    29,   611,   585,   137,   232,
+     519,   624,   625,   109,   589,    10,    11,   526,    10,    11,
+     172,   626,   199,   628,   629,  -154,   229,  -267,   455,     9,
+     536,   232,  -267,   203,   118,   568,   118,   456,   413,   412,
+     499,   229,   229,   415,   414,   500,   232,   232,   641,   237,
+     433,    10,    11,   118,   478,   118,   572,   515,     9,   237,
+     165,   118,   513,   411,   492,   275,   406,   204,  -183,   261,
+      11,   465,   164,  -178,   347,     9,   421,   166,    10,    11,
+     357,   358,  -267,   118,  -182,   668,   466,   125,  -267,  -179,
+     498,   131,   126,   587,   279,   118,   126,  -178,   118,   118,
+     216,   630,     9,   596,    94,  -178,   256,    10,    11,   597,
+     227,   518,    95,  -179,   220,   229,    96,   221,   486,   224,
+     232,  -179,   235,   652,    10,    11,    97,    98,   229,   256,
+     484,   483,   251,   232,   252,   487,   485,   128,   229,   627,
+     256,   257,   229,   232,     9,   210,   523,   232,   287,   620,
+     258,    10,    11,   333,   334,    10,    11,   264,   265,    99,
+     441,   532,   229,   229,   266,   288,   598,   232,   232,   265,
+     441,   165,   118,   267,   259,   266,   617,   355,    10,    11,
+     290,   289,   268,   164,   635,   118,   510,   118,   166,    10,
+      11,   636,   517,    10,    11,   118,   356,   211,   211,   118,
+     211,   211,   360,   362,   364,   453,   525,   367,   215,     9,
+     217,   219,   371,   464,   486,     9,   375,   377,   381,   118,
+     118,   383,    12,   385,   588,   387,   484,   483,     9,   395,
+     486,   487,   485,   229,   389,   397,   402,    32,   232,    79,
+     165,   404,   484,   483,   144,    32,   408,   487,   485,   237,
+     148,   416,   164,   112,   618,  -177,   112,   166,    10,    11,
+     129,   419,   112,   173,    10,    11,   422,   448,   435,     9,
+     147,   151,   449,   451,   450,   452,   229,    10,    11,  -177,
+     462,   232,   473,   118,   151,   467,   470,  -177,   471,   256,
+     118,   472,   512,   153,   154,   155,   156,   157,   158,   118,
+     167,   168,   474,   489,   319,   541,   494,   382,  -181,   497,
+     507,   548,   551,   523,   556,   346,   667,   486,    10,    11,
+     509,   346,   346,   561,   257,   563,   229,   178,   514,   484,
+     483,   232,  -181,   118,   487,   485,   516,   186,    10,    11,
+    -181,   190,   524,   342,   237,   245,   195,   196,   197,   198,
+     528,   530,   437,   112,   533,    35,   534,   532,   535,   112,
+      37,   147,   537,   538,   557,   151,   559,   165,   567,   113,
+     576,   560,   466,   571,    47,    48,     9,   573,   578,   164,
+     580,    51,   581,   118,   166,   584,   118,   486,   586,   595,
+     151,   599,   336,   600,  -158,   601,  -159,   153,   157,   484,
+     483,   337,   602,   603,   487,   485,   338,   339,   340,   607,
+     604,    61,   606,   341,   605,   608,   610,   612,   320,   619,
+     342,   631,   609,    64,    79,    10,    11,   633,   634,   646,
+     351,   647,   441,   654,   653,   655,   656,   343,    32,   346,
+     657,   245,   663,   176,  -277,   107,   346,    66,   660,   344,
+     632,   583,   365,   593,   346,   345,   118,   594,    11,   373,
+     407,   124,   354,   374,   508,   548,   640,   496,   245,    79,
+      91,   479,   177,   178,   286,   179,   180,   181,   182,   183,
+     577,   184,   185,   186,   187,   188,   189,   190,   191,   192,
+     193,   194,   195,   196,   197,   198,   336,   446,   566,   642,
+     151,   138,   542,   639,  -277,   337,   447,   562,     0,     0,
+     338,   339,   340,   161,  -277,     0,   170,   341,   353,     0,
+       0,     0,     0,     0,   443,     0,     0,     0,     0,     0,
+      35,     0,     0,     0,     0,    37,     0,     0,   169,     0,
+      79,   343,     0,     0,   113,     0,   346,   444,     0,    47,
+      48,     9,   546,   346,     0,   346,    51,     0,     0,   345,
+       0,   457,    11,    55,   346,     0,   346,     0,     0,     0,
+       0,     0,   351,     0,     0,     0,    56,    57,     0,    58,
+      59,     0,     0,    60,     0,     0,    61,     0,     0,     0,
+       0,     0,   245,     0,   481,     0,    62,    63,    64,   493,
+      10,    11,   245,     0,   112,     0,     0,     0,     0,     0,
+       0,     0,   112,     0,     0,     0,   112,     0,     0,   147,
+       0,   151,     0,     0,     0,     0,     0,     0,   294,   295,
+     296,   297,     0,   298,   299,   300,   151,   301,   302,   303,
+     304,   305,   306,   307,   308,   309,   310,   311,   312,   313,
+     314,     0,   161,     0,   321,   346,   324,    79,    79,     0,
+     138,   138,   335,   346,     0,   351,   545,     0,   552,     0,
+     346,     0,     0,   457,     0,    35,     0,     0,     0,   457,
+      37,     0,   565,   351,     0,     0,     0,     0,   366,   113,
+       0,     0,    79,     0,    47,    48,     9,   245,   236,     0,
+       0,    51,     0,   346,     0,     0,   546,   346,    55,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,    56,    57,     0,    58,    59,     0,     0,    60,     0,
+       0,    61,     0,     0,   138,     0,     0,     0,     0,     0,
+       0,    62,    63,    64,   138,    10,    11,    37,     0,     0,
+       0,     0,     0,     0,     0,     0,   113,   346,     0,   346,
+       0,    47,    48,     9,     0,     0,     0,   424,    51,     0,
+       0,   161,    37,     0,     0,   225,     0,   424,     0,     0,
+       0,   113,     0,     0,     0,     0,    47,    48,     9,     0,
+     245,     0,   115,    51,     0,     0,    79,     0,   226,     0,
+     114,     0,     0,   151,   282,     0,     0,     0,     0,     0,
+      64,     0,    10,    11,   283,     0,     0,   115,   351,     0,
+     545,   138,   138,   116,   552,   457,     0,     0,     0,   351,
+     351,     0,     0,     0,     0,    64,     0,    10,    11,    -2,
+      34,     0,    35,     0,     0,    36,     0,    37,    38,    39,
+       0,     0,    40,     0,    41,    42,    43,    44,    45,    46,
+     138,    47,    48,     9,     0,     0,    49,    50,    51,    52,
+      53,    54,     0,     0,   138,    55,     0,     0,     0,     0,
+       0,     0,   161,     0,     0,     0,     0,   170,    56,    57,
+       0,    58,    59,     0,     0,    60,     0,     0,    61,     0,
+       0,   -24,     0,     0,   178,     0,     0,     0,    62,    63,
+      64,     0,    10,    11,   186,     0,     0,     0,   190,   191,
+     192,   193,   194,   195,   196,   197,   198,     0,   569,   570,
+       0,     0,     0,     0,     0,     0,     0,     0,   326,     0,
+      35,     0,     0,    36,  -252,    37,    38,    39,     0,  -252,
+      40,   161,    41,    42,   113,    44,    45,    46,     0,    47,
+      48,     9,     0,     0,    49,    50,    51,    52,    53,    54,
+       0,   424,     0,    55,     0,     0,     0,     0,   424,   591,
+     424,     0,     0,     0,     0,     0,    56,    57,     0,    58,
+      59,     0,     0,    60,     0,     0,    61,     0,     0,  -252,
+       0,     0,     0,     0,   327,  -252,    62,    63,    64,     0,
+      10,    11,     0,     0,     0,     0,   326,     0,    35,     0,
+       0,    36,     0,    37,    38,    39,     0,     0,    40,     0,
+      41,    42,   113,    44,    45,    46,     0,    47,    48,     9,
+       0,     0,    49,    50,    51,    52,    53,    54,   170,     0,
+       0,    55,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,    56,    57,     0,    58,    59,     0,
+       0,    60,     0,     0,    61,   650,   651,  -252,   161,     0,
+       0,     0,   327,  -252,    62,    63,    64,   424,    10,    11,
+      35,     0,     0,     0,     0,    37,     0,     0,     0,     0,
+       0,     0,     0,     0,   113,     0,   336,     0,     0,    47,
+      48,     9,     0,     0,     0,   337,    51,     0,     0,     0,
+     338,   339,   340,   159,     0,     0,     0,   341,     0,     0,
+       0,     0,     0,     0,   342,     0,    56,    57,     0,    58,
+     160,     0,     0,    60,     0,    35,    61,   316,     0,     0,
+      37,   343,     0,     0,     0,     0,    62,    63,    64,   113,
+      10,    11,     0,     0,    47,    48,     9,     0,     0,   345,
+       0,    51,    11,     0,     0,     0,     0,     0,    55,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,    56,    57,     0,    58,    59,     0,     0,    60,     0,
+      35,    61,     0,     0,     0,    37,     0,     0,     0,   423,
+       0,    62,    63,    64,   113,    10,    11,     0,     0,    47,
+      48,     9,     0,     0,     0,     0,    51,     0,   432,     0,
+       0,     0,     0,   159,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,    56,    57,     0,    58,
+     160,     0,     0,    60,     0,    35,    61,     0,     0,     0,
+      37,     0,     0,     0,     0,     0,    62,    63,    64,   113,
+      10,    11,     0,     0,    47,    48,     9,     0,   476,     0,
+       0,    51,     0,     0,     0,     0,     0,     0,    55,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,    56,    57,     0,    58,    59,     0,     0,    60,     0,
+      35,    61,     0,     0,     0,    37,     0,     0,     0,     0,
+       0,    62,    63,    64,   113,    10,    11,     0,     0,    47,
+      48,     9,     0,   477,     0,     0,    51,     0,     0,     0,
+       0,     0,     0,    55,     0,     0,     0,     0,     0,     0,
+       0,     0,     0,    35,     0,     0,    56,    57,    37,    58,
+      59,     0,     0,    60,     0,     0,    61,   113,     0,     0,
+       0,     0,    47,    48,     9,     0,    62,    63,    64,    51,
+      10,    11,     0,     0,     0,     0,    55,     0,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,    56,
+      57,     0,    58,    59,     0,     0,    60,     0,    35,    61,
+       0,     0,     0,    37,     0,     0,     0,   590,     0,    62,
+      63,    64,   113,    10,    11,     0,     0,    47,    48,     9,
+       0,     0,     0,     0,    51,     0,     0,     0,     0,     0,
+       0,    55,     0,     0,     0,     0,     0,     0,     0,     0,
+       0,    35,     0,     0,    56,    57,    37,    58,    59,     0,
+       0,    60,     0,     0,    61,   113,     0,     0,     0,     0,
+      47,    48,     9,     0,    62,    63,    64,    51,    10,    11,
+       0,     0,     0,     0,   159,     0,     0,     0,     0,     0,
+       0,     0,     0,     0,    35,     0,     0,    56,    57,   285,
+      58,   160,     0,     0,    60,     0,     0,    61,   113,     0,
+       0,     0,     0,    47,    48,     9,     0,    62,    63,    64,
+      51,    10,    11,     0,     0,     0,     0,    55,     0,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+      56,    57,    37,    58,    59,     0,     0,    60,     0,     0,
+      61,   113,     0,     0,     0,     0,    47,    48,     9,     0,
+      62,    63,    64,    51,    10,    11,     0,    37,     0,     0,
+     225,     0,     0,     0,     0,    37,   113,     0,   243,     0,
+       0,    47,    48,     9,   113,     0,     0,   115,    51,    47,
+      48,     9,     0,   226,     0,   225,    51,     0,     0,   292,
+       0,    37,     0,   225,     0,    64,     0,    10,    11,   283,
+     113,     0,   115,     0,     0,    47,    48,     9,   226,     0,
+     115,     0,    51,     0,     0,     0,   226,     0,    37,   225,
+      64,     0,    10,    11,   399,     0,     0,   113,    64,     0,
+      10,    11,    47,    48,     9,     0,   115,     0,     0,    51,
+       0,     0,   226,     0,    37,     0,   409,     0,     0,     0,
+       0,     0,   285,   113,    64,     0,    10,    11,    47,    48,
+       9,   113,     0,   115,     0,    51,    47,    48,     9,   410,
+       0,     0,   225,    51,     0,     0,     0,   336,     0,     0,
+     225,    64,     0,    10,    11,   336,   337,     0,   463,   115,
+       0,   338,   339,   544,   337,   480,     0,   115,   341,   338,
+     339,   340,     0,   226,     0,   342,   341,    64,     0,    10,
+      11,   336,     0,   342,     0,    64,     0,    10,    11,     0,
+     337,     0,   343,     0,     0,   338,   339,   340,     0,     0,
+     343,     0,   341,     0,     0,     0,     0,     0,     0,   342,
+     345,     0,    10,    11,     0,     0,     0,     0,   345,   178,
+       0,    11,     0,   181,   182,   183,   343,     0,   185,   186,
+     187,   188,   613,   190,   191,   192,   193,   194,   195,   196,
+     197,   198,     0,     0,   345,   177,   178,    11,   179,     0,
+     181,   182,   183,     0,     0,   185,   186,   187,   188,   189,
+     190,   191,   192,   193,   194,   195,   196,   197,   198,     0,
+       0,     0,     0,     0,     0,     0,     0,     0,   177,   178,
+       0,   179,     0,   181,   182,   183,     0,   437,   185,   186,
+     187,   188,   189,   190,   191,   192,   193,   194,   195,   196,
+     197,   198,     0,     0,     0,     0,     0,     0,   177,   178,
+       0,   179,     0,   181,   182,   183,     0,   434,   185,   186,
+     187,   188,   189,   190,   191,   192,   193,   194,   195,   196,
+     197,   198,   177,   178,     0,   179,     0,   181,   182,   183,
+       0,   527,   185,   186,   187,   188,   189,   190,   191,   192,
+     193,   194,   195,   196,   197,   198,   177,   178,     0,   179,
+       0,   181,   182,   183,     0,   661,   185,   186,   187,   188,
+     189,   190,   191,   192,   193,   194,   195,   196,   197,   198,
+     177,   178,     0,   179,     0,   181,   182,   183,     0,   662,
+     185,   186,   187,   188,   189,   190,   191,   192,   193,   194,
+     195,   196,   197,   198,   177,   178,     0,     0,     0,   181,
+     182,   183,     0,     0,   185,   186,   187,   188,   189,   190,
+     191,   192,   193,   194,   195,   196,   197,   198,   177,   178,
+       0,     0,     0,   181,   182,   183,     0,     0,   185,   186,
+     187,   188,     0,   190,   191,   192,   193,   194,   195,   196,
+     197,   198
+};
+
+static const yytype_int16 yycheck[] =
+{
+      37,    37,    61,   143,    67,    37,    37,   202,   380,   205,
+     224,   148,    61,   448,   144,   127,    28,   489,   456,    67,
+     127,   133,   252,   325,    61,   114,   115,   116,   136,    61,
+      31,   143,   323,    36,     3,    51,    39,   251,    11,     5,
+      24,   260,    45,    49,    35,   392,     7,    25,   137,    35,
+     269,    12,     5,   400,   265,   266,   145,    37,   277,     3,
+       1,    35,   281,   152,   318,     0,    24,    83,    59,    62,
+     324,     3,   291,    89,    25,    68,    67,   114,   115,   116,
+      24,    67,   114,   115,   116,    59,   175,     3,    24,    73,
+       1,   128,    24,    67,    35,    53,   128,    20,     3,    65,
+     137,    62,    75,    72,   576,   137,   107,    68,   145,    75,
+     173,   160,    65,   145,    62,   152,    60,   464,    59,    63,
+     152,   127,    75,   160,    35,   173,    67,    59,   160,    73,
+      74,    63,   600,   136,   114,   115,   116,   226,   175,   142,
+       7,    73,    74,   175,    67,    12,    62,    63,   128,   257,
+     441,   200,    68,    62,   243,     3,    67,   137,     3,    68,
+     507,     5,    24,   200,    21,   145,   601,    63,   200,    24,
+     608,   385,   152,   427,   646,   429,    24,   612,   613,    24,
+     391,    59,   393,    59,   273,    63,   654,   224,   656,   226,
+      62,     3,   224,    71,   226,   175,   285,    59,    74,   288,
+     289,    68,   208,   209,    62,    50,   243,     3,   245,   221,
+     440,   243,    24,   245,   251,    63,   563,   519,    71,   251,
+     416,    65,    66,     3,   526,    73,    74,   423,    73,    74,
+      62,    75,    67,   580,   581,    66,   273,     7,    50,    24,
+     435,   273,    12,    66,   224,   475,   226,    59,   285,   285,
+       7,   288,   289,   285,   285,    12,   288,   289,   605,   371,
+     319,    73,    74,   243,   371,   245,   480,   404,    24,   381,
+     319,   251,   402,   362,   381,    60,   282,    59,    59,   387,
+      74,    60,   319,    35,   212,    24,   292,   319,    73,    74,
+     218,   219,    62,   273,    59,   667,    75,    40,    68,    35,
+     389,    44,    40,   522,    60,   285,    44,    59,   288,   289,
+      24,    68,    24,    66,     9,    67,    24,    73,    74,    72,
+     409,   410,    17,    59,    64,   362,    21,    62,   377,    59,
+     362,    67,    63,   635,    73,    74,    31,    32,   375,    24,
+     377,   377,    59,   375,    67,   377,   377,    59,   385,   579,
+      24,    59,   389,   385,    24,    75,   419,   389,    60,   573,
+      68,    73,    74,   369,   370,    73,    74,    35,    53,    64,
+      62,   430,   409,   410,    59,    72,    68,   409,   410,    53,
+      62,   430,   362,    68,   387,    59,    68,    59,    73,    74,
+      60,    72,   395,   430,   590,   375,   399,   377,   430,    73,
+      74,   596,   408,    73,    74,   385,     3,    94,    95,   389,
+      97,    98,    60,     8,    60,   343,   422,    35,    95,    24,
+      97,    98,    62,   351,   473,    24,    75,    60,    62,   409,
+     410,    24,     5,    59,   523,    62,   473,   473,    24,    62,
+     489,   473,   473,   480,    72,     3,    62,    20,   480,    22,
+     499,    62,   489,   489,    59,    28,    65,   489,   489,   571,
+      59,    67,   499,    36,   571,    35,    39,   499,    73,    74,
+      43,    62,    45,    59,    73,    74,    65,    59,    66,    24,
+      53,    54,    67,    67,    71,     8,   523,    73,    74,    59,
+      65,   523,    60,   473,    67,    62,    62,    67,    62,    24,
+     480,    62,    35,    55,    56,    57,    58,    59,    60,   489,
+      62,    63,    60,    60,    59,   443,    60,    63,    35,    68,
+      60,   449,   450,   586,   452,   212,   666,   576,    73,    74,
+      68,   218,   219,   461,    59,   463,   573,    34,    60,   576,
+     576,   573,    59,   523,   576,   576,    60,    44,    73,    74,
+      67,    48,    60,    36,   666,   128,    53,    54,    55,    56,
+      75,    68,    75,   136,    60,     3,    60,   626,    60,   142,
+       8,   144,    60,    68,     3,   148,    62,   626,    60,    17,
+      60,    72,    75,    62,    22,    23,    24,    59,    66,   626,
+      60,    29,    60,   573,   626,    60,   576,   646,    62,    60,
+     173,    60,     8,    60,    59,    59,    59,   159,   160,   646,
+     646,    17,    68,    62,   646,   646,    22,    23,    24,    62,
+      72,    59,    68,    29,   552,    49,    62,    59,   201,    60,
+      36,    68,   560,    71,   207,    73,    74,    60,    68,    60,
+     213,    14,    62,    60,    72,    60,    60,    53,   221,   336,
+      60,   224,    68,     4,     5,    31,   343,    22,   647,    65,
+     586,   512,   235,   528,   351,    71,   646,   528,    74,   245,
+     283,    39,   214,   245,   395,   603,   604,   387,   251,   252,
+      22,   375,    33,    34,   160,    36,    37,    38,    39,    40,
+     499,    42,    43,    44,    45,    46,    47,    48,    49,    50,
+      51,    52,    53,    54,    55,    56,     8,   336,   466,   607,
+     283,    49,   444,   603,    65,    17,   336,   462,    -1,    -1,
+      22,    23,    24,    61,    75,    -1,    64,    29,   213,    -1,
+      -1,    -1,    -1,    -1,    36,    -1,    -1,    -1,    -1,    -1,
+       3,    -1,    -1,    -1,    -1,     8,    -1,    -1,    11,    -1,
+     323,    53,    -1,    -1,    17,    -1,   443,    59,    -1,    22,
+      23,    24,   449,   450,    -1,   452,    29,    -1,    -1,    71,
+      -1,   344,    74,    36,   461,    -1,   463,    -1,    -1,    -1,
+      -1,    -1,   355,    -1,    -1,    -1,    49,    50,    -1,    52,
+      53,    -1,    -1,    56,    -1,    -1,    59,    -1,    -1,    -1,
+      -1,    -1,   375,    -1,   377,    -1,    69,    70,    71,   382,
+      73,    74,   385,    -1,   387,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,   395,    -1,    -1,    -1,   399,    -1,    -1,   402,
+      -1,   404,    -1,    -1,    -1,    -1,    -1,    -1,   176,   177,
+     178,   179,    -1,   181,   182,   183,   419,   185,   186,   187,
+     188,   189,   190,   191,   192,   193,   194,   195,   196,   197,
+     198,    -1,   200,    -1,   202,   552,   204,   440,   441,    -1,
+     208,   209,   210,   560,    -1,   448,   449,    -1,   451,    -1,
+     567,    -1,    -1,   456,    -1,     3,    -1,    -1,    -1,   462,
+       8,    -1,   465,   466,    -1,    -1,    -1,    -1,   236,    17,
+      -1,    -1,   475,    -1,    22,    23,    24,   480,    26,    -1,
+      -1,    29,    -1,   600,    -1,    -1,   603,   604,    36,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    49,    50,    -1,    52,    53,    -1,    -1,    56,    -1,
+      -1,    59,    -1,    -1,   282,    -1,    -1,    -1,    -1,    -1,
+      -1,    69,    70,    71,   292,    73,    74,     8,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    17,   654,    -1,   656,
+      -1,    22,    23,    24,    -1,    -1,    -1,   315,    29,    -1,
+      -1,   319,     8,    -1,    -1,    36,    -1,   325,    -1,    -1,
+      -1,    17,    -1,    -1,    -1,    -1,    22,    23,    24,    -1,
+     573,    -1,    53,    29,    -1,    -1,   579,    -1,    59,    -1,
+      36,    -1,    -1,   586,    65,    -1,    -1,    -1,    -1,    -1,
+      71,    -1,    73,    74,    75,    -1,    -1,    53,   601,    -1,
+     603,   369,   370,    59,   607,   608,    -1,    -1,    -1,   612,
+     613,    -1,    -1,    -1,    -1,    71,    -1,    73,    74,     0,
+       1,    -1,     3,    -1,    -1,     6,    -1,     8,     9,    10,
+      -1,    -1,    13,    -1,    15,    16,    17,    18,    19,    20,
+     408,    22,    23,    24,    -1,    -1,    27,    28,    29,    30,
+      31,    32,    -1,    -1,   422,    36,    -1,    -1,    -1,    -1,
+      -1,    -1,   430,    -1,    -1,    -1,    -1,   435,    49,    50,
+      -1,    52,    53,    -1,    -1,    56,    -1,    -1,    59,    -1,
+      -1,    62,    -1,    -1,    34,    -1,    -1,    -1,    69,    70,
+      71,    -1,    73,    74,    44,    -1,    -1,    -1,    48,    49,
+      50,    51,    52,    53,    54,    55,    56,    -1,   476,   477,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,     1,    -1,
+       3,    -1,    -1,     6,     7,     8,     9,    10,    -1,    12,
+      13,   499,    15,    16,    17,    18,    19,    20,    -1,    22,
+      23,    24,    -1,    -1,    27,    28,    29,    30,    31,    32,
+      -1,   519,    -1,    36,    -1,    -1,    -1,    -1,   526,   527,
+     528,    -1,    -1,    -1,    -1,    -1,    49,    50,    -1,    52,
+      53,    -1,    -1,    56,    -1,    -1,    59,    -1,    -1,    62,
+      -1,    -1,    -1,    -1,    67,    68,    69,    70,    71,    -1,
+      73,    74,    -1,    -1,    -1,    -1,     1,    -1,     3,    -1,
+      -1,     6,    -1,     8,     9,    10,    -1,    -1,    13,    -1,
+      15,    16,    17,    18,    19,    20,    -1,    22,    23,    24,
+      -1,    -1,    27,    28,    29,    30,    31,    32,   596,    -1,
+      -1,    36,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    49,    50,    -1,    52,    53,    -1,
+      -1,    56,    -1,    -1,    59,   623,   624,    62,   626,    -1,
+      -1,    -1,    67,    68,    69,    70,    71,   635,    73,    74,
+       3,    -1,    -1,    -1,    -1,     8,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    17,    -1,     8,    -1,    -1,    22,
+      23,    24,    -1,    -1,    -1,    17,    29,    -1,    -1,    -1,
+      22,    23,    24,    36,    -1,    -1,    -1,    29,    -1,    -1,
+      -1,    -1,    -1,    -1,    36,    -1,    49,    50,    -1,    52,
+      53,    -1,    -1,    56,    -1,     3,    59,    60,    -1,    -1,
+       8,    53,    -1,    -1,    -1,    -1,    69,    70,    71,    17,
+      73,    74,    -1,    -1,    22,    23,    24,    -1,    -1,    71,
+      -1,    29,    74,    -1,    -1,    -1,    -1,    -1,    36,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    49,    50,    -1,    52,    53,    -1,    -1,    56,    -1,
+       3,    59,    -1,    -1,    -1,     8,    -1,    -1,    -1,    67,
+      -1,    69,    70,    71,    17,    73,    74,    -1,    -1,    22,
+      23,    24,    -1,    -1,    -1,    -1,    29,    -1,    31,    -1,
+      -1,    -1,    -1,    36,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    49,    50,    -1,    52,
+      53,    -1,    -1,    56,    -1,     3,    59,    -1,    -1,    -1,
+       8,    -1,    -1,    -1,    -1,    -1,    69,    70,    71,    17,
+      73,    74,    -1,    -1,    22,    23,    24,    -1,    26,    -1,
+      -1,    29,    -1,    -1,    -1,    -1,    -1,    -1,    36,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    49,    50,    -1,    52,    53,    -1,    -1,    56,    -1,
+       3,    59,    -1,    -1,    -1,     8,    -1,    -1,    -1,    -1,
+      -1,    69,    70,    71,    17,    73,    74,    -1,    -1,    22,
+      23,    24,    -1,    26,    -1,    -1,    29,    -1,    -1,    -1,
+      -1,    -1,    -1,    36,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,     3,    -1,    -1,    49,    50,     8,    52,
+      53,    -1,    -1,    56,    -1,    -1,    59,    17,    -1,    -1,
+      -1,    -1,    22,    23,    24,    -1,    69,    70,    71,    29,
+      73,    74,    -1,    -1,    -1,    -1,    36,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    49,
+      50,    -1,    52,    53,    -1,    -1,    56,    -1,     3,    59,
+      -1,    -1,    -1,     8,    -1,    -1,    -1,    67,    -1,    69,
+      70,    71,    17,    73,    74,    -1,    -1,    22,    23,    24,
+      -1,    -1,    -1,    -1,    29,    -1,    -1,    -1,    -1,    -1,
+      -1,    36,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      -1,     3,    -1,    -1,    49,    50,     8,    52,    53,    -1,
+      -1,    56,    -1,    -1,    59,    17,    -1,    -1,    -1,    -1,
+      22,    23,    24,    -1,    69,    70,    71,    29,    73,    74,
+      -1,    -1,    -1,    -1,    36,    -1,    -1,    -1,    -1,    -1,
+      -1,    -1,    -1,    -1,     3,    -1,    -1,    49,    50,     8,
+      52,    53,    -1,    -1,    56,    -1,    -1,    59,    17,    -1,
+      -1,    -1,    -1,    22,    23,    24,    -1,    69,    70,    71,
+      29,    73,    74,    -1,    -1,    -1,    -1,    36,    -1,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
+      49,    50,     8,    52,    53,    -1,    -1,    56,    -1,    -1,
+      59,    17,    -1,    -1,    -1,    -1,    22,    23,    24,    -1,
+      69,    70,    71,    29,    73,    74,    -1,     8,    -1,    -1,
+      36,    -1,    -1,    -1,    -1,     8,    17,    -1,    11,    -1,
+      -1,    22,    23,    24,    17,    -1,    -1,    53,    29,    22,
+      23,    24,    -1,    59,    -1,    36,    29,    -1,    -1,    65,
+      -1,     8,    -1,    36,    -1,    71,    -1,    73,    74,    75,
+      17,    -1,    53,    -1,    -1,    22,    23,    24,    59,    -1,
+      53,    -1,    29,    -1,    -1,    -1,    59,    -1,     8,    36,
+      71,    -1,    73,    74,    75,    -1,    -1,    17,    71,    -1,
+      73,    74,    22,    23,    24,    -1,    53,    -1,    -1,    29,
+      -1,    -1,    59,    -1,     8,    -1,    36,    -1,    -1,    -1,
+      -1,    -1,     8,    17,    71,    -1,    73,    74,    22,    23,
+      24,    17,    -1,    53,    -1,    29,    22,    23,    24,    59,
+      -1,    -1,    36,    29,    -1,    -1,    -1,     8,    -1,    -1,
+      36,    71,    -1,    73,    74,     8,    17,    -1,    11,    53,
+      -1,    22,    23,    24,    17,    59,    -1,    53,    29,    22,
+      23,    24,    -1,    59,    -1,    36,    29,    71,    -1,    73,
+      74,     8,    -1,    36,    -1,    71,    -1,    73,    74,    -1,
+      17,    -1,    53,    -1,    -1,    22,    23,    24,    -1,    -1,
+      53,    -1,    29,    -1,    -1,    -1,    -1,    -1,    -1,    36,
+      71,    -1,    73,    74,    -1,    -1,    -1,    -1,    71,    34,
+      -1,    74,    -1,    38,    39,    40,    53,    -1,    43,    44,
+      45,    46,    59,    48,    49,    50,    51,    52,    53,    54,
+      55,    56,    -1,    -1,    71,    33,    34,    74,    36,    -1,
+      38,    39,    40,    -1,    -1,    43,    44,    45,    46,    47,
+      48,    49,    50,    51,    52,    53,    54,    55,    56,    -1,
+      -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    33,    34,
+      -1,    36,    -1,    38,    39,    40,    -1,    75,    43,    44,
+      45,    46,    47,    48,    49,    50,    51,    52,    53,    54,
+      55,    56,    -1,    -1,    -1,    -1,    -1,    -1,    33,    34,
+      -1,    36,    -1,    38,    39,    40,    -1,    72,    43,    44,
+      45,    46,    47,    48,    49,    50,    51,    52,    53,    54,
+      55,    56,    33,    34,    -1,    36,    -1,    38,    39,    40,
+      -1,    66,    43,    44,    45,    46,    47,    48,    49,    50,
+      51,    52,    53,    54,    55,    56,    33,    34,    -1,    36,
+      -1,    38,    39,    40,    -1,    66,    43,    44,    45,    46,
+      47,    48,    49,    50,    51,    52,    53,    54,    55,    56,
+      33,    34,    -1,    36,    -1,    38,    39,    40,    -1,    66,
+      43,    44,    45,    46,    47,    48,    49,    50,    51,    52,
+      53,    54,    55,    56,    33,    34,    -1,    -1,    -1,    38,
+      39,    40,    -1,    -1,    43,    44,    45,    46,    47,    48,
+      49,    50,    51,    52,    53,    54,    55,    56,    33,    34,
+      -1,    -1,    -1,    38,    39,    40,    -1,    -1,    43,    44,
+      45,    46,    -1,    48,    49,    50,    51,    52,    53,    54,
+      55,    56
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+   symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,    77,    79,    80,     0,    25,    78,    25,    86,    24,
+      73,    74,   141,   142,    81,    24,    88,    89,     3,    62,
+      21,    82,   166,    24,    87,   214,    63,     3,    59,    63,
+      83,    85,   141,    62,     1,     3,     6,     8,     9,    10,
+      13,    15,    16,    17,    18,    19,    20,    22,    23,    27,
+      28,    29,    30,    31,    32,    36,    49,    50,    52,    53,
+      56,    59,    69,    70,    71,    90,    91,    92,    98,   110,
+     113,   121,   124,   126,   127,   128,   129,   134,   138,   141,
+     143,   144,   149,   150,   153,   156,   157,   158,   161,   164,
+     165,   181,   186,    62,     9,    17,    21,    31,    32,    64,
+     199,    24,    73,    60,    83,    84,     3,    86,    88,     3,
+     138,   140,   141,    17,    36,    53,    59,   141,   143,   148,
+     152,   153,   154,   161,   140,   128,   134,   111,    59,   141,
+     159,   128,   138,   114,    35,    67,   137,    71,   126,   186,
+     193,   125,   137,   122,    59,    96,    97,   141,    59,    93,
+     139,   141,   185,   127,   127,   127,   127,   127,   127,    36,
+      53,   126,   135,   147,   153,   155,   161,   127,   127,    11,
+     126,   192,    62,    59,    94,   185,     4,    33,    34,    36,
+      37,    38,    39,    40,    42,    43,    44,    45,    46,    47,
+      48,    49,    50,    51,    52,    53,    54,    55,    56,    67,
+      59,    63,    71,    66,    59,   137,     1,   137,     5,    65,
+      75,   142,   200,    59,   160,   200,    24,   200,   201,   200,
+      64,    62,   190,    88,    59,    36,    59,   146,   152,   153,
+     154,   155,   161,   146,   146,    63,    26,    98,   107,   108,
+     109,   186,   194,    11,   136,   141,   145,   146,   177,   178,
+     179,    59,    67,   162,   112,   194,    24,    59,    68,   138,
+     171,   173,   175,   146,    35,    53,    59,    68,   138,   170,
+     172,   173,   174,   184,   112,    60,    97,   169,   146,    60,
+      93,   167,    65,    75,   146,     8,   147,    60,    72,    72,
+      60,    94,    65,   146,   126,   126,   126,   126,   126,   126,
+     126,   126,   126,   126,   126,   126,   126,   126,   126,   126,
+     126,   126,   126,   126,   126,   130,    60,   135,   187,    59,
+     141,   126,   192,   182,   126,   130,     1,    67,    91,   100,
+     180,   181,   183,   186,   186,   126,     8,    17,    22,    23,
+      24,    29,    36,    53,    65,    71,   142,   202,   204,   205,
+     206,   141,   207,   215,   162,    59,     3,   202,   202,    83,
+      60,   179,     8,   146,    60,   141,   126,    35,   105,     5,
+      65,    62,   146,   136,   145,    75,   191,    60,   179,   183,
+     115,    62,    63,    24,   173,    59,   176,    62,   190,    72,
+     104,    59,   174,    53,   174,    62,   190,     3,   198,    75,
+     146,   123,    62,   190,    62,   190,   186,   139,    65,    36,
+      59,   146,   152,   153,   154,   161,    67,   146,   146,    62,
+     190,   186,    65,    67,   126,   131,   132,   188,   189,    11,
+      75,   191,    31,   135,    72,    66,   180,    75,   191,   189,
+     101,    62,    68,    36,    59,   203,   204,   206,    59,    67,
+      71,    67,     8,   202,     3,    50,    59,   141,   212,   213,
+       3,    72,    65,    11,   202,    60,    75,    62,   195,   215,
+      62,    62,    62,    60,    60,   106,    26,    26,   194,   177,
+      59,   141,   151,   152,   153,   154,   155,   161,   163,    60,
+      68,   105,   194,   141,    60,   179,   175,    68,   146,     7,
+      12,    68,    99,   102,   174,   198,   174,    60,   172,    68,
+     138,   198,    35,    97,    60,    93,    60,   186,   146,   130,
+      94,    95,   168,   185,    60,   186,   130,    66,    75,   191,
+      68,   191,   135,    60,    60,    60,   192,    60,    68,   183,
+     180,   202,   205,   195,    24,   141,   142,   197,   202,   209,
+     217,   202,   141,   196,   208,   216,   202,     3,   212,    62,
+      72,   202,   213,   202,   198,   141,   207,    60,   183,   126,
+     126,    62,   179,    59,   163,   116,    60,   187,    66,   103,
+      60,    60,   198,   104,    60,   189,    62,   190,   146,   189,
+      67,   126,   133,   131,   132,    60,    66,    72,    68,    60,
+      60,    59,    68,    62,    72,   202,    68,    62,    49,   202,
+      62,   198,    59,    59,   202,   210,   211,    68,   194,    60,
+     179,   119,   163,     5,    65,    66,    75,   183,   198,   198,
+      68,    68,    95,    60,    68,   130,   192,   210,   195,   209,
+     202,   198,   208,   212,   195,   195,    60,    14,   117,   120,
+     126,   126,   189,    72,    60,    60,    60,    60,   163,    20,
+     100,    66,    66,    68,   210,   210,   118,   112,   105
+};
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		(-2)
+#define YYEOF		0
+
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT		goto yyabortlab
+#define YYERROR		goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror.  This remains here temporarily
+   to ease the transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+
+#define YYFAIL		goto yyerrlab
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)					\
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    {								\
+      yychar = (Token);						\
+      yylval = (Value);						\
+      yytoken = YYTRANSLATE (yychar);				\
+      YYPOPSTACK (1);						\
+      goto yybackup;						\
+    }								\
+  else								\
+    {								\
+      yyerror (YY_("syntax error: cannot back up")); \
+      YYERROR;							\
+    }								\
+while (YYID (0))
+
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+   If N is 0, then set CURRENT to the empty location which ends
+   the previous symbol: RHS[0] (always defined).  */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N)				\
+    do									\
+      if (YYID (N))                                                    \
+	{								\
+	  (Current).first_line   = YYRHSLOC (Rhs, 1).first_line;	\
+	  (Current).first_column = YYRHSLOC (Rhs, 1).first_column;	\
+	  (Current).last_line    = YYRHSLOC (Rhs, N).last_line;		\
+	  (Current).last_column  = YYRHSLOC (Rhs, N).last_column;	\
+	}								\
+      else								\
+	{								\
+	  (Current).first_line   = (Current).last_line   =		\
+	    YYRHSLOC (Rhs, 0).last_line;				\
+	  (Current).first_column = (Current).last_column =		\
+	    YYRHSLOC (Rhs, 0).last_column;				\
+	}								\
+    while (YYID (0))
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+   This macro was not mandated originally: define only if we know
+   we won't break user code: when these are the locations we know.  */
+
+#ifndef YY_LOCATION_PRINT
+# if defined YYLTYPE_IS_TRIVIAL && YYLTYPE_IS_TRIVIAL
+#  define YY_LOCATION_PRINT(File, Loc)			\
+     fprintf (File, "%d.%d-%d.%d",			\
+	      (Loc).first_line, (Loc).first_column,	\
+	      (Loc).last_line,  (Loc).last_column)
+# else
+#  define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments.  */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)			\
+do {						\
+  if (yydebug)					\
+    YYFPRINTF Args;				\
+} while (YYID (0))
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)			  \
+do {									  \
+  if (yydebug)								  \
+    {									  \
+      YYFPRINTF (stderr, "%s ", Title);					  \
+      yy_symbol_print (stderr,						  \
+		  Type, Value); \
+      YYFPRINTF (stderr, "\n");						  \
+    }									  \
+} while (YYID (0))
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_value_print (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+#endif
+{
+  if (!yyvaluep)
+    return;
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# else
+  YYUSE (yyoutput);
+# endif
+  switch (yytype)
+    {
+      default:
+	break;
+    }
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+#else
+static void
+yy_symbol_print (yyoutput, yytype, yyvaluep)
+    FILE *yyoutput;
+    int yytype;
+    YYSTYPE const * const yyvaluep;
+#endif
+{
+  if (yytype < YYNTOKENS)
+    YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+  else
+    YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_stack_print (yytype_int16 *bottom, yytype_int16 *top)
+#else
+static void
+yy_stack_print (bottom, top)
+    yytype_int16 *bottom;
+    yytype_int16 *top;
+#endif
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; bottom <= top; ++bottom)
+    YYFPRINTF (stderr, " %d", *bottom);
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)				\
+do {								\
+  if (yydebug)							\
+    yy_stack_print ((Bottom), (Top));				\
+} while (YYID (0))
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yy_reduce_print (YYSTYPE *yyvsp, int yyrule)
+#else
+static void
+yy_reduce_print (yyvsp, yyrule)
+    YYSTYPE *yyvsp;
+    int yyrule;
+#endif
+{
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  unsigned long int yylno = yyrline[yyrule];
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+	     yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      fprintf (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi],
+		       &(yyvsp[(yyi + 1) - (yynrhs)])
+		       		       );
+      fprintf (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)		\
+do {					\
+  if (yydebug)				\
+    yy_reduce_print (yyvsp, Rule); \
+} while (YYID (0))
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef	YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static YYSIZE_T
+yystrlen (const char *yystr)
+#else
+static YYSIZE_T
+yystrlen (yystr)
+    const char *yystr;
+#endif
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+#else
+static char *
+yystpcpy (yydest, yysrc)
+    char *yydest;
+    const char *yysrc;
+#endif
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+	switch (*++yyp)
+	  {
+	  case '\'':
+	  case ',':
+	    goto do_not_strip_quotes;
+
+	  case '\\':
+	    if (*++yyp != '\\')
+	      goto do_not_strip_quotes;
+	    /* Fall through.  */
+	  default:
+	    if (yyres)
+	      yyres[yyn] = *yyp;
+	    yyn++;
+	    break;
+
+	  case '"':
+	    if (yyres)
+	      yyres[yyn] = '\0';
+	    return yyn;
+	  }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into YYRESULT an error message about the unexpected token
+   YYCHAR while in state YYSTATE.  Return the number of bytes copied,
+   including the terminating null byte.  If YYRESULT is null, do not
+   copy anything; just return the number of bytes that would be
+   copied.  As a special case, return 0 if an ordinary "syntax error"
+   message will do.  Return YYSIZE_MAXIMUM if overflow occurs during
+   size calculation.  */
+static YYSIZE_T
+yysyntax_error (char *yyresult, int yystate, int yychar)
+{
+  int yyn = yypact[yystate];
+
+  if (! (YYPACT_NINF < yyn && yyn <= YYLAST))
+    return 0;
+  else
+    {
+      int yytype = YYTRANSLATE (yychar);
+      YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]);
+      YYSIZE_T yysize = yysize0;
+      YYSIZE_T yysize1;
+      int yysize_overflow = 0;
+      enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+      char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+      int yyx;
+
+# if 0
+      /* This is so xgettext sees the translatable formats that are
+	 constructed on the fly.  */
+      YY_("syntax error, unexpected %s");
+      YY_("syntax error, unexpected %s, expecting %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s");
+      YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s");
+# endif
+      char *yyfmt;
+      char const *yyf;
+      static char const yyunexpected[] = "syntax error, unexpected %s";
+      static char const yyexpecting[] = ", expecting %s";
+      static char const yyor[] = " or %s";
+      char yyformat[sizeof yyunexpected
+		    + sizeof yyexpecting - 1
+		    + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2)
+		       * (sizeof yyor - 1))];
+      char const *yyprefix = yyexpecting;
+
+      /* Start YYX at -YYN if negative to avoid negative indexes in
+	 YYCHECK.  */
+      int yyxbegin = yyn < 0 ? -yyn : 0;
+
+      /* Stay within bounds of both yycheck and yytname.  */
+      int yychecklim = YYLAST - yyn + 1;
+      int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+      int yycount = 1;
+
+      yyarg[0] = yytname[yytype];
+      yyfmt = yystpcpy (yyformat, yyunexpected);
+
+      for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+	if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+	  {
+	    if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+	      {
+		yycount = 1;
+		yysize = yysize0;
+		yyformat[sizeof yyunexpected - 1] = '\0';
+		break;
+	      }
+	    yyarg[yycount++] = yytname[yyx];
+	    yysize1 = yysize + yytnamerr (0, yytname[yyx]);
+	    yysize_overflow |= (yysize1 < yysize);
+	    yysize = yysize1;
+	    yyfmt = yystpcpy (yyfmt, yyprefix);
+	    yyprefix = yyor;
+	  }
+
+      yyf = YY_(yyformat);
+      yysize1 = yysize + yystrlen (yyf);
+      yysize_overflow |= (yysize1 < yysize);
+      yysize = yysize1;
+
+      if (yysize_overflow)
+	return YYSIZE_MAXIMUM;
+
+      if (yyresult)
+	{
+	  /* Avoid sprintf, as that infringes on the user's name space.
+	     Don't have undefined behavior even if the translation
+	     produced a string with the wrong number of "%s"s.  */
+	  char *yyp = yyresult;
+	  int yyi = 0;
+	  while ((*yyp = *yyf) != '\0')
+	    {
+	      if (*yyp == '%' && yyf[1] == 's' && yyi < yycount)
+		{
+		  yyp += yytnamerr (yyp, yyarg[yyi++]);
+		  yyf += 2;
+		}
+	      else
+		{
+		  yyp++;
+		  yyf++;
+		}
+	    }
+	}
+      return yysize;
+    }
+}
+#endif /* YYERROR_VERBOSE */
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+/*ARGSUSED*/
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+    const char *yymsg;
+    int yytype;
+    YYSTYPE *yyvaluep;
+#endif
+{
+  YYUSE (yyvaluep);
+
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  switch (yytype)
+    {
+
+      default:
+	break;
+    }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes.  */
+
+#ifdef YYPARSE_PARAM
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void *YYPARSE_PARAM);
+#else
+int yyparse ();
+#endif
+#else /* ! YYPARSE_PARAM */
+#if defined __STDC__ || defined __cplusplus
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol.  */
+int yychar, yystate;
+
+/* The semantic value of the look-ahead symbol.  */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void *YYPARSE_PARAM)
+#else
+int
+yyparse (YYPARSE_PARAM)
+    void *YYPARSE_PARAM;
+#endif
+#else /* ! YYPARSE_PARAM */
+#if (defined __STDC__ || defined __C99__FUNC__ \
+     || defined __cplusplus || defined _MSC_VER)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+  
+  int yyn;
+  int yyresult;
+  /* Number of tokens to shift before error messages enabled.  */
+  int yyerrstatus;
+  /* Look-ahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+  /* Three stacks and their tools:
+     `yyss': related to states,
+     `yyvs': related to semantic values,
+     `yyls': related to locations.
+
+     Refer to the stacks thru separate pointers, to allow yyoverflow
+     to reallocate them elsewhere.  */
+
+  /* The state stack.  */
+  yytype_int16 yyssa[YYINITDEPTH];
+  yytype_int16 *yyss = yyssa;
+  yytype_int16 *yyssp;
+
+  /* The semantic value stack.  */
+  YYSTYPE yyvsa[YYINITDEPTH];
+  YYSTYPE *yyvs = yyvsa;
+  YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
+
+  YYSIZE_T yystacksize = YYINITDEPTH;
+
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss;
+  yyvsp = yyvs;
+
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+	/* Give user a chance to reallocate the stack.  Use copies of
+	   these so that the &'s don't force the real ones into
+	   memory.  */
+	YYSTYPE *yyvs1 = yyvs;
+	yytype_int16 *yyss1 = yyss;
+
+
+	/* Each stack pointer address is followed by the size of the
+	   data in use in that stack, in bytes.  This used to be a
+	   conditional around just the two extra args, but that might
+	   be undefined if yyoverflow is a macro.  */
+	yyoverflow (YY_("memory exhausted"),
+		    &yyss1, yysize * sizeof (*yyssp),
+		    &yyvs1, yysize * sizeof (*yyvsp),
+
+		    &yystacksize);
+
+	yyss = yyss1;
+	yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+	goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+	yystacksize = YYMAXDEPTH;
+
+      {
+	yytype_int16 *yyss1 = yyss;
+	union yyalloc *yyptr =
+	  (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+	if (! yyptr)
+	  goto yyexhaustedlab;
+	YYSTACK_RELOCATE (yyss);
+	YYSTACK_RELOCATE (yyvs);
+
+#  undef YYSTACK_RELOCATE
+	if (yyss1 != yyssa)
+	  YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+		  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+	YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     look-ahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to look-ahead token.  */
+  yyn = yypact[yystate];
+  if (yyn == YYPACT_NINF)
+    goto yydefault;
+
+  /* Not known => get a look-ahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = YYLEX;
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yyn == 0 || yyn == YYTABLE_NINF)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the look-ahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  yystate = yyn;
+  *++yyvsp = yylval;
+
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     `$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 2:
+#line 128 "go.y"
+    {
+		xtop = concat(xtop, (yyvsp[(4) - (4)].list));
+	}
+    break;
+
+  case 3:
+#line 134 "go.y"
+    {
+		prevlineno = lineno;
+		yyerror("package statement must be first");
+		errorexit();
+	}
+    break;
+
+  case 4:
+#line 140 "go.y"
+    {
+		mkpackage((yyvsp[(2) - (3)].sym)->name);
+	}
+    break;
+
+  case 5:
+#line 150 "go.y"
+    {
+		importpkg = runtimepkg;
+
+		if(debug['A'])
+			cannedimports("runtime.builtin", "package runtime\n\n$$\n\n");
+		else
+			cannedimports("runtime.builtin", runtimeimport);
+		curio.importsafe = 1;
+	}
+    break;
+
+  case 6:
+#line 161 "go.y"
+    {
+		importpkg = nil;
+	}
+    break;
+
+  case 12:
+#line 175 "go.y"
+    {
+		Pkg *ipkg;
+		Sym *my;
+		Node *pack;
+		
+		ipkg = importpkg;
+		my = importmyname;
+		importpkg = nil;
+		importmyname = S;
+
+		if(my == nil)
+			my = lookup(ipkg->name);
+
+		pack = nod(OPACK, N, N);
+		pack->sym = my;
+		pack->pkg = ipkg;
+		pack->lineno = (yyvsp[(1) - (3)].i);
+
+		if(my->name[0] == '.') {
+			importdot(ipkg, pack);
+			break;
+		}
+		if(strcmp(my->name, "init") == 0) {
+			yyerror("cannot import package as init - init must be a func");
+			break;
+		}
+		if(my->name[0] == '_' && my->name[1] == '\0')
+			break;
+		if(my->def) {
+			lineno = (yyvsp[(1) - (3)].i);
+			redeclare(my, "as imported package name");
+		}
+		my->def = pack;
+		my->lastlineno = (yyvsp[(1) - (3)].i);
+		my->block = 1;	// at top level
+	}
+    break;
+
+  case 13:
+#line 212 "go.y"
+    {
+		// When an invalid import path is passed to importfile,
+		// it calls yyerror and then sets up a fake import with
+		// no package statement. This allows us to test more
+		// than one invalid import statement in a single file.
+		if(nerrors == 0)
+			fatal("phase error in import");
+	}
+    break;
+
+  case 16:
+#line 227 "go.y"
+    {
+		// import with original name
+		(yyval.i) = parserline();
+		importmyname = S;
+		importfile(&(yyvsp[(1) - (1)].val), (yyval.i));
+	}
+    break;
+
+  case 17:
+#line 234 "go.y"
+    {
+		// import with given name
+		(yyval.i) = parserline();
+		importmyname = (yyvsp[(1) - (2)].sym);
+		importfile(&(yyvsp[(2) - (2)].val), (yyval.i));
+	}
+    break;
+
+  case 18:
+#line 241 "go.y"
+    {
+		// import into my name space
+		(yyval.i) = parserline();
+		importmyname = lookup(".");
+		importfile(&(yyvsp[(2) - (2)].val), (yyval.i));
+	}
+    break;
+
+  case 19:
+#line 250 "go.y"
+    {
+		if(importpkg->name == nil) {
+			importpkg->name = (yyvsp[(2) - (4)].sym)->name;
+			pkglookup((yyvsp[(2) - (4)].sym)->name, nil)->npkg++;
+		} else if(strcmp(importpkg->name, (yyvsp[(2) - (4)].sym)->name) != 0)
+			yyerror("conflicting names %s and %s for package \"%Z\"", importpkg->name, (yyvsp[(2) - (4)].sym)->name, importpkg->path);
+		importpkg->direct = 1;
+		importpkg->safe = curio.importsafe;
+
+		if(safemode && !curio.importsafe)
+			yyerror("cannot import unsafe package \"%Z\"", importpkg->path);
+	}
+    break;
+
+  case 21:
+#line 265 "go.y"
+    {
+		if(strcmp((yyvsp[(1) - (1)].sym)->name, "safe") == 0)
+			curio.importsafe = 1;
+	}
+    break;
+
+  case 22:
+#line 271 "go.y"
+    {
+		defercheckwidth();
+	}
+    break;
+
+  case 23:
+#line 275 "go.y"
+    {
+		resumecheckwidth();
+		unimportfile();
+	}
+    break;
+
+  case 24:
+#line 284 "go.y"
+    {
+		yyerror("empty top-level declaration");
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 26:
+#line 290 "go.y"
+    {
+		(yyval.list) = list1((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 27:
+#line 294 "go.y"
+    {
+		yyerror("non-declaration statement outside function body");
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 28:
+#line 299 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 29:
+#line 305 "go.y"
+    {
+		(yyval.list) = (yyvsp[(2) - (2)].list);
+	}
+    break;
+
+  case 30:
+#line 309 "go.y"
+    {
+		(yyval.list) = (yyvsp[(3) - (5)].list);
+	}
+    break;
+
+  case 31:
+#line 313 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 32:
+#line 317 "go.y"
+    {
+		(yyval.list) = (yyvsp[(2) - (2)].list);
+		iota = -100000;
+		lastconst = nil;
+	}
+    break;
+
+  case 33:
+#line 323 "go.y"
+    {
+		(yyval.list) = (yyvsp[(3) - (5)].list);
+		iota = -100000;
+		lastconst = nil;
+	}
+    break;
+
+  case 34:
+#line 329 "go.y"
+    {
+		(yyval.list) = concat((yyvsp[(3) - (7)].list), (yyvsp[(5) - (7)].list));
+		iota = -100000;
+		lastconst = nil;
+	}
+    break;
+
+  case 35:
+#line 335 "go.y"
+    {
+		(yyval.list) = nil;
+		iota = -100000;
+	}
+    break;
+
+  case 36:
+#line 340 "go.y"
+    {
+		(yyval.list) = list1((yyvsp[(2) - (2)].node));
+	}
+    break;
+
+  case 37:
+#line 344 "go.y"
+    {
+		(yyval.list) = (yyvsp[(3) - (5)].list);
+	}
+    break;
+
+  case 38:
+#line 348 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 39:
+#line 354 "go.y"
+    {
+		iota = 0;
+	}
+    break;
+
+  case 40:
+#line 360 "go.y"
+    {
+		(yyval.list) = variter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil);
+	}
+    break;
+
+  case 41:
+#line 364 "go.y"
+    {
+		(yyval.list) = variter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list));
+	}
+    break;
+
+  case 42:
+#line 368 "go.y"
+    {
+		(yyval.list) = variter((yyvsp[(1) - (3)].list), nil, (yyvsp[(3) - (3)].list));
+	}
+    break;
+
+  case 43:
+#line 374 "go.y"
+    {
+		(yyval.list) = constiter((yyvsp[(1) - (4)].list), (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].list));
+	}
+    break;
+
+  case 44:
+#line 378 "go.y"
+    {
+		(yyval.list) = constiter((yyvsp[(1) - (3)].list), N, (yyvsp[(3) - (3)].list));
+	}
+    break;
+
+  case 46:
+#line 385 "go.y"
+    {
+		(yyval.list) = constiter((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node), nil);
+	}
+    break;
+
+  case 47:
+#line 389 "go.y"
+    {
+		(yyval.list) = constiter((yyvsp[(1) - (1)].list), N, nil);
+	}
+    break;
+
+  case 48:
+#line 395 "go.y"
+    {
+		// different from dclname because the name
+		// becomes visible right here, not at the end
+		// of the declaration.
+		(yyval.node) = typedcl0((yyvsp[(1) - (1)].sym));
+	}
+    break;
+
+  case 49:
+#line 404 "go.y"
+    {
+		(yyval.node) = typedcl1((yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node), 1);
+	}
+    break;
+
+  case 50:
+#line 410 "go.y"
+    {
+		(yyval.node) = (yyvsp[(1) - (1)].node);
+
+		// These nodes do not carry line numbers.
+		// Since a bare name used as an expression is an error,
+		// introduce a wrapper node to give the correct line.
+		switch((yyval.node)->op) {
+		case ONAME:
+		case ONONAME:
+		case OTYPE:
+		case OPACK:
+		case OLITERAL:
+			(yyval.node) = nod(OPAREN, (yyval.node), N);
+			(yyval.node)->implicit = 1;
+			break;
+		}
+	}
+    break;
+
+  case 51:
+#line 428 "go.y"
+    {
+		(yyval.node) = nod(OASOP, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+		(yyval.node)->etype = (yyvsp[(2) - (3)].i);			// rathole to pass opcode
+	}
+    break;
+
+  case 52:
+#line 433 "go.y"
+    {
+		if((yyvsp[(1) - (3)].list)->next == nil && (yyvsp[(3) - (3)].list)->next == nil) {
+			// simple
+			(yyval.node) = nod(OAS, (yyvsp[(1) - (3)].list)->n, (yyvsp[(3) - (3)].list)->n);
+			break;
+		}
+		// multiple
+		(yyval.node) = nod(OAS2, N, N);
+		(yyval.node)->list = (yyvsp[(1) - (3)].list);
+		(yyval.node)->rlist = (yyvsp[(3) - (3)].list);
+	}
+    break;
+
+  case 53:
+#line 445 "go.y"
+    {
+		if((yyvsp[(3) - (3)].list)->n->op == OTYPESW) {
+			(yyval.node) = nod(OTYPESW, N, (yyvsp[(3) - (3)].list)->n->right);
+			if((yyvsp[(3) - (3)].list)->next != nil)
+				yyerror("expr.(type) must be alone in list");
+			if((yyvsp[(1) - (3)].list)->next != nil)
+				yyerror("argument count mismatch: %d = %d", count((yyvsp[(1) - (3)].list)), 1);
+			else if(((yyvsp[(1) - (3)].list)->n->op != ONAME && (yyvsp[(1) - (3)].list)->n->op != OTYPE && (yyvsp[(1) - (3)].list)->n->op != ONONAME) || isblank((yyvsp[(1) - (3)].list)->n))
+				yyerror("invalid variable name %N in type switch", (yyvsp[(1) - (3)].list)->n);
+			else
+				(yyval.node)->left = dclname((yyvsp[(1) - (3)].list)->n->sym);  // it's a colas, so must not re-use an oldname.
+			break;
+		}
+		(yyval.node) = colas((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list), (yyvsp[(2) - (3)].i));
+	}
+    break;
+
+  case 54:
+#line 461 "go.y"
+    {
+		(yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1));
+		(yyval.node)->implicit = 1;
+		(yyval.node)->etype = OADD;
+	}
+    break;
+
+  case 55:
+#line 467 "go.y"
+    {
+		(yyval.node) = nod(OASOP, (yyvsp[(1) - (2)].node), nodintconst(1));
+		(yyval.node)->implicit = 1;
+		(yyval.node)->etype = OSUB;
+	}
+    break;
+
+  case 56:
+#line 475 "go.y"
+    {
+		Node *n, *nn;
+
+		// will be converted to OCASE
+		// right will point to next case
+		// done in casebody()
+		markdcl();
+		(yyval.node) = nod(OXCASE, N, N);
+		(yyval.node)->list = (yyvsp[(2) - (3)].list);
+		if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
+			// type switch - declare variable
+			nn = newname(n->sym);
+			declare(nn, dclcontext);
+			(yyval.node)->nname = nn;
+
+			// keep track of the instances for reporting unused
+			nn->defn = typesw->right;
+		}
+	}
+    break;
+
+  case 57:
+#line 495 "go.y"
+    {
+		Node *n;
+
+		// will be converted to OCASE
+		// right will point to next case
+		// done in casebody()
+		markdcl();
+		(yyval.node) = nod(OXCASE, N, N);
+		if((yyvsp[(2) - (5)].list)->next == nil)
+			n = nod(OAS, (yyvsp[(2) - (5)].list)->n, (yyvsp[(4) - (5)].node));
+		else {
+			n = nod(OAS2, N, N);
+			n->list = (yyvsp[(2) - (5)].list);
+			n->rlist = list1((yyvsp[(4) - (5)].node));
+		}
+		(yyval.node)->list = list1(n);
+	}
+    break;
+
+  case 58:
+#line 513 "go.y"
+    {
+		// will be converted to OCASE
+		// right will point to next case
+		// done in casebody()
+		markdcl();
+		(yyval.node) = nod(OXCASE, N, N);
+		(yyval.node)->list = list1(colas((yyvsp[(2) - (5)].list), list1((yyvsp[(4) - (5)].node)), (yyvsp[(3) - (5)].i)));
+	}
+    break;
+
+  case 59:
+#line 522 "go.y"
+    {
+		Node *n, *nn;
+
+		markdcl();
+		(yyval.node) = nod(OXCASE, N, N);
+		if(typesw != N && typesw->right != N && (n=typesw->right->left) != N) {
+			// type switch - declare variable
+			nn = newname(n->sym);
+			declare(nn, dclcontext);
+			(yyval.node)->nname = nn;
+
+			// keep track of the instances for reporting unused
+			nn->defn = typesw->right;
+		}
+	}
+    break;
+
+  case 60:
+#line 540 "go.y"
+    {
+		markdcl();
+	}
+    break;
+
+  case 61:
+#line 544 "go.y"
+    {
+		if((yyvsp[(3) - (4)].list) == nil)
+			(yyval.node) = nod(OEMPTY, N, N);
+		else
+			(yyval.node) = liststmt((yyvsp[(3) - (4)].list));
+		popdcl();
+	}
+    break;
+
+  case 62:
+#line 554 "go.y"
+    {
+		// If the last token read by the lexer was consumed
+		// as part of the case, clear it (parser has cleared yychar).
+		// If the last token read by the lexer was the lookahead
+		// leave it alone (parser has it cached in yychar).
+		// This is so that the stmt_list action doesn't look at
+		// the case tokens if the stmt_list is empty.
+		yylast = yychar;
+		(yyvsp[(1) - (1)].node)->xoffset = block;
+	}
+    break;
+
+  case 63:
+#line 565 "go.y"
+    {
+		int last;
+
+		// This is the only place in the language where a statement
+		// list is not allowed to drop the final semicolon, because
+		// it's the only place where a statement list is not followed 
+		// by a closing brace.  Handle the error for pedantry.
+
+		// Find the final token of the statement list.
+		// yylast is lookahead; yyprev is last of stmt_list
+		last = yyprev;
+
+		if(last > 0 && last != ';' && yychar != '}')
+			yyerror("missing statement after label");
+		(yyval.node) = (yyvsp[(1) - (3)].node);
+		(yyval.node)->nbody = (yyvsp[(3) - (3)].list);
+		popdcl();
+	}
+    break;
+
+  case 64:
+#line 585 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 65:
+#line 589 "go.y"
+    {
+		(yyval.list) = list((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].node));
+	}
+    break;
+
+  case 66:
+#line 595 "go.y"
+    {
+		markdcl();
+	}
+    break;
+
+  case 67:
+#line 599 "go.y"
+    {
+		(yyval.list) = (yyvsp[(3) - (4)].list);
+		popdcl();
+	}
+    break;
+
+  case 68:
+#line 606 "go.y"
+    {
+		(yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node));
+		(yyval.node)->list = (yyvsp[(1) - (4)].list);
+		(yyval.node)->etype = 0;	// := flag
+	}
+    break;
+
+  case 69:
+#line 612 "go.y"
+    {
+		(yyval.node) = nod(ORANGE, N, (yyvsp[(4) - (4)].node));
+		(yyval.node)->list = (yyvsp[(1) - (4)].list);
+		(yyval.node)->colas = 1;
+		colasdefn((yyvsp[(1) - (4)].list), (yyval.node));
+	}
+    break;
+
+  case 70:
+#line 619 "go.y"
+    {
+		(yyval.node) = nod(ORANGE, N, (yyvsp[(2) - (2)].node));
+		(yyval.node)->etype = 0; // := flag
+	}
+    break;
+
+  case 71:
+#line 626 "go.y"
+    {
+		// init ; test ; incr
+		if((yyvsp[(5) - (5)].node) != N && (yyvsp[(5) - (5)].node)->colas != 0)
+			yyerror("cannot declare in the for-increment");
+		(yyval.node) = nod(OFOR, N, N);
+		if((yyvsp[(1) - (5)].node) != N)
+			(yyval.node)->ninit = list1((yyvsp[(1) - (5)].node));
+		(yyval.node)->ntest = (yyvsp[(3) - (5)].node);
+		(yyval.node)->nincr = (yyvsp[(5) - (5)].node);
+	}
+    break;
+
+  case 72:
+#line 637 "go.y"
+    {
+		// normal test
+		(yyval.node) = nod(OFOR, N, N);
+		(yyval.node)->ntest = (yyvsp[(1) - (1)].node);
+	}
+    break;
+
+  case 74:
+#line 646 "go.y"
+    {
+		(yyval.node) = (yyvsp[(1) - (2)].node);
+		(yyval.node)->nbody = concat((yyval.node)->nbody, (yyvsp[(2) - (2)].list));
+	}
+    break;
+
+  case 75:
+#line 653 "go.y"
+    {
+		markdcl();
+	}
+    break;
+
+  case 76:
+#line 657 "go.y"
+    {
+		(yyval.node) = (yyvsp[(3) - (3)].node);
+		popdcl();
+	}
+    break;
+
+  case 77:
+#line 664 "go.y"
+    {
+		// test
+		(yyval.node) = nod(OIF, N, N);
+		(yyval.node)->ntest = (yyvsp[(1) - (1)].node);
+	}
+    break;
+
+  case 78:
+#line 670 "go.y"
+    {
+		// init ; test
+		(yyval.node) = nod(OIF, N, N);
+		if((yyvsp[(1) - (3)].node) != N)
+			(yyval.node)->ninit = list1((yyvsp[(1) - (3)].node));
+		(yyval.node)->ntest = (yyvsp[(3) - (3)].node);
+	}
+    break;
+
+  case 79:
+#line 681 "go.y"
+    {
+		markdcl();
+	}
+    break;
+
+  case 80:
+#line 685 "go.y"
+    {
+		if((yyvsp[(3) - (3)].node)->ntest == N)
+			yyerror("missing condition in if statement");
+	}
+    break;
+
+  case 81:
+#line 690 "go.y"
+    {
+		(yyvsp[(3) - (5)].node)->nbody = (yyvsp[(5) - (5)].list);
+	}
+    break;
+
+  case 82:
+#line 694 "go.y"
+    {
+		Node *n;
+		NodeList *nn;
+
+		(yyval.node) = (yyvsp[(3) - (8)].node);
+		n = (yyvsp[(3) - (8)].node);
+		popdcl();
+		for(nn = concat((yyvsp[(7) - (8)].list), (yyvsp[(8) - (8)].list)); nn; nn = nn->next) {
+			if(nn->n->op == OIF)
+				popdcl();
+			n->nelse = list1(nn->n);
+			n = nn->n;
+		}
+	}
+    break;
+
+  case 83:
+#line 711 "go.y"
+    {
+		markdcl();
+	}
+    break;
+
+  case 84:
+#line 715 "go.y"
+    {
+		if((yyvsp[(4) - (5)].node)->ntest == N)
+			yyerror("missing condition in if statement");
+		(yyvsp[(4) - (5)].node)->nbody = (yyvsp[(5) - (5)].list);
+		(yyval.list) = list1((yyvsp[(4) - (5)].node));
+	}
+    break;
+
+  case 85:
+#line 723 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 86:
+#line 727 "go.y"
+    {
+		(yyval.list) = concat((yyvsp[(1) - (2)].list), (yyvsp[(2) - (2)].list));
+	}
+    break;
+
+  case 87:
+#line 732 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 88:
+#line 736 "go.y"
+    {
+		NodeList *node;
+		
+		node = mal(sizeof *node);
+		node->n = (yyvsp[(2) - (2)].node);
+		node->end = node;
+		(yyval.list) = node;
+	}
+    break;
+
+  case 89:
+#line 747 "go.y"
+    {
+		markdcl();
+	}
+    break;
+
+  case 90:
+#line 751 "go.y"
+    {
+		Node *n;
+		n = (yyvsp[(3) - (3)].node)->ntest;
+		if(n != N && n->op != OTYPESW)
+			n = N;
+		typesw = nod(OXXX, typesw, n);
+	}
+    break;
+
+  case 91:
+#line 759 "go.y"
+    {
+		(yyval.node) = (yyvsp[(3) - (7)].node);
+		(yyval.node)->op = OSWITCH;
+		(yyval.node)->list = (yyvsp[(6) - (7)].list);
+		typesw = typesw->left;
+		popdcl();
+	}
+    break;
+
+  case 92:
+#line 769 "go.y"
+    {
+		typesw = nod(OXXX, typesw, N);
+	}
+    break;
+
+  case 93:
+#line 773 "go.y"
+    {
+		(yyval.node) = nod(OSELECT, N, N);
+		(yyval.node)->lineno = typesw->lineno;
+		(yyval.node)->list = (yyvsp[(4) - (5)].list);
+		typesw = typesw->left;
+	}
+    break;
+
+  case 95:
+#line 786 "go.y"
+    {
+		(yyval.node) = nod(OOROR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 96:
+#line 790 "go.y"
+    {
+		(yyval.node) = nod(OANDAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 97:
+#line 794 "go.y"
+    {
+		(yyval.node) = nod(OEQ, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 98:
+#line 798 "go.y"
+    {
+		(yyval.node) = nod(ONE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 99:
+#line 802 "go.y"
+    {
+		(yyval.node) = nod(OLT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 100:
+#line 806 "go.y"
+    {
+		(yyval.node) = nod(OLE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 101:
+#line 810 "go.y"
+    {
+		(yyval.node) = nod(OGE, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 102:
+#line 814 "go.y"
+    {
+		(yyval.node) = nod(OGT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 103:
+#line 818 "go.y"
+    {
+		(yyval.node) = nod(OADD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 104:
+#line 822 "go.y"
+    {
+		(yyval.node) = nod(OSUB, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 105:
+#line 826 "go.y"
+    {
+		(yyval.node) = nod(OOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 106:
+#line 830 "go.y"
+    {
+		(yyval.node) = nod(OXOR, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 107:
+#line 834 "go.y"
+    {
+		(yyval.node) = nod(OMUL, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 108:
+#line 838 "go.y"
+    {
+		(yyval.node) = nod(ODIV, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 109:
+#line 842 "go.y"
+    {
+		(yyval.node) = nod(OMOD, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 110:
+#line 846 "go.y"
+    {
+		(yyval.node) = nod(OAND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 111:
+#line 850 "go.y"
+    {
+		(yyval.node) = nod(OANDNOT, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 112:
+#line 854 "go.y"
+    {
+		(yyval.node) = nod(OLSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 113:
+#line 858 "go.y"
+    {
+		(yyval.node) = nod(ORSH, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 114:
+#line 863 "go.y"
+    {
+		(yyval.node) = nod(OSEND, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 116:
+#line 870 "go.y"
+    {
+		(yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
+	}
+    break;
+
+  case 117:
+#line 874 "go.y"
+    {
+		if((yyvsp[(2) - (2)].node)->op == OCOMPLIT) {
+			// Special case for &T{...}: turn into (*T){...}.
+			(yyval.node) = (yyvsp[(2) - (2)].node);
+			(yyval.node)->right = nod(OIND, (yyval.node)->right, N);
+			(yyval.node)->right->implicit = 1;
+		} else {
+			(yyval.node) = nod(OADDR, (yyvsp[(2) - (2)].node), N);
+		}
+	}
+    break;
+
+  case 118:
+#line 885 "go.y"
+    {
+		(yyval.node) = nod(OPLUS, (yyvsp[(2) - (2)].node), N);
+	}
+    break;
+
+  case 119:
+#line 889 "go.y"
+    {
+		(yyval.node) = nod(OMINUS, (yyvsp[(2) - (2)].node), N);
+	}
+    break;
+
+  case 120:
+#line 893 "go.y"
+    {
+		(yyval.node) = nod(ONOT, (yyvsp[(2) - (2)].node), N);
+	}
+    break;
+
+  case 121:
+#line 897 "go.y"
+    {
+		yyerror("the bitwise complement operator is ^");
+		(yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N);
+	}
+    break;
+
+  case 122:
+#line 902 "go.y"
+    {
+		(yyval.node) = nod(OCOM, (yyvsp[(2) - (2)].node), N);
+	}
+    break;
+
+  case 123:
+#line 906 "go.y"
+    {
+		(yyval.node) = nod(ORECV, (yyvsp[(2) - (2)].node), N);
+	}
+    break;
+
+  case 124:
+#line 916 "go.y"
+    {
+		(yyval.node) = nod(OCALL, (yyvsp[(1) - (3)].node), N);
+	}
+    break;
+
+  case 125:
+#line 920 "go.y"
+    {
+		(yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N);
+		(yyval.node)->list = (yyvsp[(3) - (5)].list);
+	}
+    break;
+
+  case 126:
+#line 925 "go.y"
+    {
+		(yyval.node) = nod(OCALL, (yyvsp[(1) - (6)].node), N);
+		(yyval.node)->list = (yyvsp[(3) - (6)].list);
+		(yyval.node)->isddd = 1;
+	}
+    break;
+
+  case 127:
+#line 933 "go.y"
+    {
+		(yyval.node) = nodlit((yyvsp[(1) - (1)].val));
+	}
+    break;
+
+  case 129:
+#line 938 "go.y"
+    {
+		if((yyvsp[(1) - (3)].node)->op == OPACK) {
+			Sym *s;
+			s = restrictlookup((yyvsp[(3) - (3)].sym)->name, (yyvsp[(1) - (3)].node)->pkg);
+			(yyvsp[(1) - (3)].node)->used = 1;
+			(yyval.node) = oldname(s);
+			break;
+		}
+		(yyval.node) = nod(OXDOT, (yyvsp[(1) - (3)].node), newname((yyvsp[(3) - (3)].sym)));
+	}
+    break;
+
+  case 130:
+#line 949 "go.y"
+    {
+		(yyval.node) = nod(ODOTTYPE, (yyvsp[(1) - (5)].node), (yyvsp[(4) - (5)].node));
+	}
+    break;
+
+  case 131:
+#line 953 "go.y"
+    {
+		(yyval.node) = nod(OTYPESW, N, (yyvsp[(1) - (5)].node));
+	}
+    break;
+
+  case 132:
+#line 957 "go.y"
+    {
+		(yyval.node) = nod(OINDEX, (yyvsp[(1) - (4)].node), (yyvsp[(3) - (4)].node));
+	}
+    break;
+
+  case 133:
+#line 961 "go.y"
+    {
+		(yyval.node) = nod(OSLICE, (yyvsp[(1) - (6)].node), nod(OKEY, (yyvsp[(3) - (6)].node), (yyvsp[(5) - (6)].node)));
+	}
+    break;
+
+  case 134:
+#line 965 "go.y"
+    {
+		if((yyvsp[(5) - (8)].node) == N)
+			yyerror("middle index required in 3-index slice");
+		if((yyvsp[(7) - (8)].node) == N)
+			yyerror("final index required in 3-index slice");
+		(yyval.node) = nod(OSLICE3, (yyvsp[(1) - (8)].node), nod(OKEY, (yyvsp[(3) - (8)].node), nod(OKEY, (yyvsp[(5) - (8)].node), (yyvsp[(7) - (8)].node))));
+	}
+    break;
+
+  case 136:
+#line 974 "go.y"
+    {
+		// conversion
+		(yyval.node) = nod(OCALL, (yyvsp[(1) - (5)].node), N);
+		(yyval.node)->list = list1((yyvsp[(3) - (5)].node));
+	}
+    break;
+
+  case 137:
+#line 980 "go.y"
+    {
+		(yyval.node) = (yyvsp[(3) - (5)].node);
+		(yyval.node)->right = (yyvsp[(1) - (5)].node);
+		(yyval.node)->list = (yyvsp[(4) - (5)].list);
+		fixlbrace((yyvsp[(2) - (5)].i));
+	}
+    break;
+
+  case 138:
+#line 987 "go.y"
+    {
+		(yyval.node) = (yyvsp[(3) - (5)].node);
+		(yyval.node)->right = (yyvsp[(1) - (5)].node);
+		(yyval.node)->list = (yyvsp[(4) - (5)].list);
+	}
+    break;
+
+  case 139:
+#line 993 "go.y"
+    {
+		yyerror("cannot parenthesize type in composite literal");
+		(yyval.node) = (yyvsp[(5) - (7)].node);
+		(yyval.node)->right = (yyvsp[(2) - (7)].node);
+		(yyval.node)->list = (yyvsp[(6) - (7)].list);
+	}
+    break;
+
+  case 141:
+#line 1002 "go.y"
+    {
+		// composite expression.
+		// make node early so we get the right line number.
+		(yyval.node) = nod(OCOMPLIT, N, N);
+	}
+    break;
+
+  case 142:
+#line 1010 "go.y"
+    {
+		(yyval.node) = nod(OKEY, (yyvsp[(1) - (3)].node), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 143:
+#line 1016 "go.y"
+    {
+		// These nodes do not carry line numbers.
+		// Since a composite literal commonly spans several lines,
+		// the line number on errors may be misleading.
+		// Introduce a wrapper node to give the correct line.
+		(yyval.node) = (yyvsp[(1) - (1)].node);
+		switch((yyval.node)->op) {
+		case ONAME:
+		case ONONAME:
+		case OTYPE:
+		case OPACK:
+		case OLITERAL:
+			(yyval.node) = nod(OPAREN, (yyval.node), N);
+			(yyval.node)->implicit = 1;
+		}
+	}
+    break;
+
+  case 144:
+#line 1033 "go.y"
+    {
+		(yyval.node) = (yyvsp[(2) - (4)].node);
+		(yyval.node)->list = (yyvsp[(3) - (4)].list);
+	}
+    break;
+
+  case 146:
+#line 1041 "go.y"
+    {
+		(yyval.node) = (yyvsp[(2) - (4)].node);
+		(yyval.node)->list = (yyvsp[(3) - (4)].list);
+	}
+    break;
+
+  case 148:
+#line 1049 "go.y"
+    {
+		(yyval.node) = (yyvsp[(2) - (3)].node);
+		
+		// Need to know on lhs of := whether there are ( ).
+		// Don't bother with the OPAREN in other cases:
+		// it's just a waste of memory and time.
+		switch((yyval.node)->op) {
+		case ONAME:
+		case ONONAME:
+		case OPACK:
+		case OTYPE:
+		case OLITERAL:
+		case OTYPESW:
+			(yyval.node) = nod(OPAREN, (yyval.node), N);
+		}
+	}
+    break;
+
+  case 152:
+#line 1075 "go.y"
+    {
+		(yyval.i) = LBODY;
+	}
+    break;
+
+  case 153:
+#line 1079 "go.y"
+    {
+		(yyval.i) = '{';
+	}
+    break;
+
+  case 154:
+#line 1090 "go.y"
+    {
+		if((yyvsp[(1) - (1)].sym) == S)
+			(yyval.node) = N;
+		else
+			(yyval.node) = newname((yyvsp[(1) - (1)].sym));
+	}
+    break;
+
+  case 155:
+#line 1099 "go.y"
+    {
+		(yyval.node) = dclname((yyvsp[(1) - (1)].sym));
+	}
+    break;
+
+  case 156:
+#line 1104 "go.y"
+    {
+		(yyval.node) = N;
+	}
+    break;
+
+  case 158:
+#line 1111 "go.y"
+    {
+		(yyval.sym) = (yyvsp[(1) - (1)].sym);
+		// during imports, unqualified non-exported identifiers are from builtinpkg
+		if(importpkg != nil && !exportname((yyvsp[(1) - (1)].sym)->name))
+			(yyval.sym) = pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg);
+	}
+    break;
+
+  case 160:
+#line 1119 "go.y"
+    {
+		(yyval.sym) = S;
+	}
+    break;
+
+  case 161:
+#line 1125 "go.y"
+    {
+		Pkg *p;
+
+		if((yyvsp[(2) - (4)].val).u.sval->len == 0)
+			p = importpkg;
+		else {
+			if(isbadimport((yyvsp[(2) - (4)].val).u.sval))
+				errorexit();
+			p = mkpkg((yyvsp[(2) - (4)].val).u.sval);
+		}
+		(yyval.sym) = pkglookup((yyvsp[(4) - (4)].sym)->name, p);
+	}
+    break;
+
+  case 162:
+#line 1138 "go.y"
+    {
+		Pkg *p;
+
+		if((yyvsp[(2) - (4)].val).u.sval->len == 0)
+			p = importpkg;
+		else {
+			if(isbadimport((yyvsp[(2) - (4)].val).u.sval))
+				errorexit();
+			p = mkpkg((yyvsp[(2) - (4)].val).u.sval);
+		}
+		(yyval.sym) = pkglookup("?", p);
+	}
+    break;
+
+  case 163:
+#line 1153 "go.y"
+    {
+		(yyval.node) = oldname((yyvsp[(1) - (1)].sym));
+		if((yyval.node)->pack != N)
+			(yyval.node)->pack->used = 1;
+	}
+    break;
+
+  case 165:
+#line 1173 "go.y"
+    {
+		yyerror("final argument in variadic function missing type");
+		(yyval.node) = nod(ODDD, typenod(typ(TINTER)), N);
+	}
+    break;
+
+  case 166:
+#line 1178 "go.y"
+    {
+		(yyval.node) = nod(ODDD, (yyvsp[(2) - (2)].node), N);
+	}
+    break;
+
+  case 172:
+#line 1189 "go.y"
+    {
+		(yyval.node) = (yyvsp[(2) - (3)].node);
+	}
+    break;
+
+  case 176:
+#line 1198 "go.y"
+    {
+		(yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
+	}
+    break;
+
+  case 181:
+#line 1208 "go.y"
+    {
+		(yyval.node) = (yyvsp[(2) - (3)].node);
+	}
+    break;
+
+  case 191:
+#line 1229 "go.y"
+    {
+		if((yyvsp[(1) - (3)].node)->op == OPACK) {
+			Sym *s;
+			s = restrictlookup((yyvsp[(3) - (3)].sym)->name, (yyvsp[(1) - (3)].node)->pkg);
+			(yyvsp[(1) - (3)].node)->used = 1;
+			(yyval.node) = oldname(s);
+			break;
+		}
+		(yyval.node) = nod(OXDOT, (yyvsp[(1) - (3)].node), newname((yyvsp[(3) - (3)].sym)));
+	}
+    break;
+
+  case 192:
+#line 1242 "go.y"
+    {
+		(yyval.node) = nod(OTARRAY, (yyvsp[(2) - (4)].node), (yyvsp[(4) - (4)].node));
+	}
+    break;
+
+  case 193:
+#line 1246 "go.y"
+    {
+		// array literal of nelem
+		(yyval.node) = nod(OTARRAY, nod(ODDD, N, N), (yyvsp[(4) - (4)].node));
+	}
+    break;
+
+  case 194:
+#line 1251 "go.y"
+    {
+		(yyval.node) = nod(OTCHAN, (yyvsp[(2) - (2)].node), N);
+		(yyval.node)->etype = Cboth;
+	}
+    break;
+
+  case 195:
+#line 1256 "go.y"
+    {
+		(yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N);
+		(yyval.node)->etype = Csend;
+	}
+    break;
+
+  case 196:
+#line 1261 "go.y"
+    {
+		(yyval.node) = nod(OTMAP, (yyvsp[(3) - (5)].node), (yyvsp[(5) - (5)].node));
+	}
+    break;
+
+  case 199:
+#line 1269 "go.y"
+    {
+		(yyval.node) = nod(OIND, (yyvsp[(2) - (2)].node), N);
+	}
+    break;
+
+  case 200:
+#line 1275 "go.y"
+    {
+		(yyval.node) = nod(OTCHAN, (yyvsp[(3) - (3)].node), N);
+		(yyval.node)->etype = Crecv;
+	}
+    break;
+
+  case 201:
+#line 1282 "go.y"
+    {
+		(yyval.node) = nod(OTSTRUCT, N, N);
+		(yyval.node)->list = (yyvsp[(3) - (5)].list);
+		fixlbrace((yyvsp[(2) - (5)].i));
+	}
+    break;
+
+  case 202:
+#line 1288 "go.y"
+    {
+		(yyval.node) = nod(OTSTRUCT, N, N);
+		fixlbrace((yyvsp[(2) - (3)].i));
+	}
+    break;
+
+  case 203:
+#line 1295 "go.y"
+    {
+		(yyval.node) = nod(OTINTER, N, N);
+		(yyval.node)->list = (yyvsp[(3) - (5)].list);
+		fixlbrace((yyvsp[(2) - (5)].i));
+	}
+    break;
+
+  case 204:
+#line 1301 "go.y"
+    {
+		(yyval.node) = nod(OTINTER, N, N);
+		fixlbrace((yyvsp[(2) - (3)].i));
+	}
+    break;
+
+  case 205:
+#line 1312 "go.y"
+    {
+		(yyval.node) = (yyvsp[(2) - (3)].node);
+		if((yyval.node) == N)
+			break;
+		if(noescape && (yyvsp[(3) - (3)].list) != nil)
+			yyerror("can only use //go:noescape with external func implementations");
+		(yyval.node)->nbody = (yyvsp[(3) - (3)].list);
+		(yyval.node)->endlineno = lineno;
+		(yyval.node)->noescape = noescape;
+		(yyval.node)->nosplit = nosplit;
+		funcbody((yyval.node));
+	}
+    break;
+
+  case 206:
+#line 1327 "go.y"
+    {
+		Node *t;
+
+		(yyval.node) = N;
+		(yyvsp[(3) - (5)].list) = checkarglist((yyvsp[(3) - (5)].list), 1);
+
+		if(strcmp((yyvsp[(1) - (5)].sym)->name, "init") == 0) {
+			(yyvsp[(1) - (5)].sym) = renameinit();
+			if((yyvsp[(3) - (5)].list) != nil || (yyvsp[(5) - (5)].list) != nil)
+				yyerror("func init must have no arguments and no return values");
+		}
+		if(strcmp(localpkg->name, "main") == 0 && strcmp((yyvsp[(1) - (5)].sym)->name, "main") == 0) {
+			if((yyvsp[(3) - (5)].list) != nil || (yyvsp[(5) - (5)].list) != nil)
+				yyerror("func main must have no arguments and no return values");
+		}
+
+		t = nod(OTFUNC, N, N);
+		t->list = (yyvsp[(3) - (5)].list);
+		t->rlist = (yyvsp[(5) - (5)].list);
+
+		(yyval.node) = nod(ODCLFUNC, N, N);
+		(yyval.node)->nname = newname((yyvsp[(1) - (5)].sym));
+		(yyval.node)->nname->defn = (yyval.node);
+		(yyval.node)->nname->ntype = t;		// TODO: check if nname already has an ntype
+		declare((yyval.node)->nname, PFUNC);
+
+		funchdr((yyval.node));
+	}
+    break;
+
+  case 207:
+#line 1356 "go.y"
+    {
+		Node *rcvr, *t;
+
+		(yyval.node) = N;
+		(yyvsp[(2) - (8)].list) = checkarglist((yyvsp[(2) - (8)].list), 0);
+		(yyvsp[(6) - (8)].list) = checkarglist((yyvsp[(6) - (8)].list), 1);
+
+		if((yyvsp[(2) - (8)].list) == nil) {
+			yyerror("method has no receiver");
+			break;
+		}
+		if((yyvsp[(2) - (8)].list)->next != nil) {
+			yyerror("method has multiple receivers");
+			break;
+		}
+		rcvr = (yyvsp[(2) - (8)].list)->n;
+		if(rcvr->op != ODCLFIELD) {
+			yyerror("bad receiver in method");
+			break;
+		}
+
+		t = nod(OTFUNC, rcvr, N);
+		t->list = (yyvsp[(6) - (8)].list);
+		t->rlist = (yyvsp[(8) - (8)].list);
+
+		(yyval.node) = nod(ODCLFUNC, N, N);
+		(yyval.node)->shortname = newname((yyvsp[(4) - (8)].sym));
+		(yyval.node)->nname = methodname1((yyval.node)->shortname, rcvr->right);
+		(yyval.node)->nname->defn = (yyval.node);
+		(yyval.node)->nname->ntype = t;
+		(yyval.node)->nname->nointerface = nointerface;
+		declare((yyval.node)->nname, PFUNC);
+
+		funchdr((yyval.node));
+	}
+    break;
+
+  case 208:
+#line 1394 "go.y"
+    {
+		Sym *s;
+		Type *t;
+
+		(yyval.node) = N;
+
+		s = (yyvsp[(1) - (5)].sym);
+		t = functype(N, (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list));
+
+		importsym(s, ONAME);
+		if(s->def != N && s->def->op == ONAME) {
+			if(eqtype(t, s->def->type)) {
+				dclcontext = PDISCARD;  // since we skip funchdr below
+				break;
+			}
+			yyerror("inconsistent definition for func %S during import\n\t%T\n\t%T", s, s->def->type, t);
+		}
+
+		(yyval.node) = newname(s);
+		(yyval.node)->type = t;
+		declare((yyval.node), PFUNC);
+
+		funchdr((yyval.node));
+	}
+    break;
+
+  case 209:
+#line 1419 "go.y"
+    {
+		(yyval.node) = methodname1(newname((yyvsp[(4) - (8)].sym)), (yyvsp[(2) - (8)].list)->n->right); 
+		(yyval.node)->type = functype((yyvsp[(2) - (8)].list)->n, (yyvsp[(6) - (8)].list), (yyvsp[(8) - (8)].list));
+
+		checkwidth((yyval.node)->type);
+		addmethod((yyvsp[(4) - (8)].sym), (yyval.node)->type, 0, nointerface);
+		nointerface = 0;
+		funchdr((yyval.node));
+		
+		// inl.c's inlnode in on a dotmeth node expects to find the inlineable body as
+		// (dotmeth's type)->nname->inl, and dotmeth's type has been pulled
+		// out by typecheck's lookdot as this $$->ttype.  So by providing
+		// this back link here we avoid special casing there.
+		(yyval.node)->type->nname = (yyval.node);
+	}
+    break;
+
+  case 210:
+#line 1437 "go.y"
+    {
+		(yyvsp[(3) - (5)].list) = checkarglist((yyvsp[(3) - (5)].list), 1);
+		(yyval.node) = nod(OTFUNC, N, N);
+		(yyval.node)->list = (yyvsp[(3) - (5)].list);
+		(yyval.node)->rlist = (yyvsp[(5) - (5)].list);
+	}
+    break;
+
+  case 211:
+#line 1445 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 212:
+#line 1449 "go.y"
+    {
+		(yyval.list) = (yyvsp[(2) - (3)].list);
+		if((yyval.list) == nil)
+			(yyval.list) = list1(nod(OEMPTY, N, N));
+	}
+    break;
+
+  case 213:
+#line 1457 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 214:
+#line 1461 "go.y"
+    {
+		(yyval.list) = list1(nod(ODCLFIELD, N, (yyvsp[(1) - (1)].node)));
+	}
+    break;
+
+  case 215:
+#line 1465 "go.y"
+    {
+		(yyvsp[(2) - (3)].list) = checkarglist((yyvsp[(2) - (3)].list), 0);
+		(yyval.list) = (yyvsp[(2) - (3)].list);
+	}
+    break;
+
+  case 216:
+#line 1472 "go.y"
+    {
+		closurehdr((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 217:
+#line 1478 "go.y"
+    {
+		(yyval.node) = closurebody((yyvsp[(3) - (4)].list));
+		fixlbrace((yyvsp[(2) - (4)].i));
+	}
+    break;
+
+  case 218:
+#line 1483 "go.y"
+    {
+		(yyval.node) = closurebody(nil);
+	}
+    break;
+
+  case 219:
+#line 1494 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 220:
+#line 1498 "go.y"
+    {
+		(yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(2) - (3)].list));
+		if(nsyntaxerrors == 0)
+			testdclstack();
+		nointerface = 0;
+		noescape = 0;
+		nosplit = 0;
+	}
+    break;
+
+  case 222:
+#line 1510 "go.y"
+    {
+		(yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
+	}
+    break;
+
+  case 224:
+#line 1517 "go.y"
+    {
+		(yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
+	}
+    break;
+
+  case 225:
+#line 1523 "go.y"
+    {
+		(yyval.list) = list1((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 226:
+#line 1527 "go.y"
+    {
+		(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 228:
+#line 1534 "go.y"
+    {
+		(yyval.list) = concat((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].list));
+	}
+    break;
+
+  case 229:
+#line 1540 "go.y"
+    {
+		(yyval.list) = list1((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 230:
+#line 1544 "go.y"
+    {
+		(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 231:
+#line 1550 "go.y"
+    {
+		NodeList *l;
+
+		Node *n;
+		l = (yyvsp[(1) - (3)].list);
+		if(l == nil) {
+			// ? symbol, during import (list1(N) == nil)
+			n = (yyvsp[(2) - (3)].node);
+			if(n->op == OIND)
+				n = n->left;
+			n = embedded(n->sym, importpkg);
+			n->right = (yyvsp[(2) - (3)].node);
+			n->val = (yyvsp[(3) - (3)].val);
+			(yyval.list) = list1(n);
+			break;
+		}
+
+		for(l=(yyvsp[(1) - (3)].list); l; l=l->next) {
+			l->n = nod(ODCLFIELD, l->n, (yyvsp[(2) - (3)].node));
+			l->n->val = (yyvsp[(3) - (3)].val);
+		}
+	}
+    break;
+
+  case 232:
+#line 1573 "go.y"
+    {
+		(yyvsp[(1) - (2)].node)->val = (yyvsp[(2) - (2)].val);
+		(yyval.list) = list1((yyvsp[(1) - (2)].node));
+	}
+    break;
+
+  case 233:
+#line 1578 "go.y"
+    {
+		(yyvsp[(2) - (4)].node)->val = (yyvsp[(4) - (4)].val);
+		(yyval.list) = list1((yyvsp[(2) - (4)].node));
+		yyerror("cannot parenthesize embedded type");
+	}
+    break;
+
+  case 234:
+#line 1584 "go.y"
+    {
+		(yyvsp[(2) - (3)].node)->right = nod(OIND, (yyvsp[(2) - (3)].node)->right, N);
+		(yyvsp[(2) - (3)].node)->val = (yyvsp[(3) - (3)].val);
+		(yyval.list) = list1((yyvsp[(2) - (3)].node));
+	}
+    break;
+
+  case 235:
+#line 1590 "go.y"
+    {
+		(yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N);
+		(yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val);
+		(yyval.list) = list1((yyvsp[(3) - (5)].node));
+		yyerror("cannot parenthesize embedded type");
+	}
+    break;
+
+  case 236:
+#line 1597 "go.y"
+    {
+		(yyvsp[(3) - (5)].node)->right = nod(OIND, (yyvsp[(3) - (5)].node)->right, N);
+		(yyvsp[(3) - (5)].node)->val = (yyvsp[(5) - (5)].val);
+		(yyval.list) = list1((yyvsp[(3) - (5)].node));
+		yyerror("cannot parenthesize embedded type");
+	}
+    break;
+
+  case 237:
+#line 1606 "go.y"
+    {
+		Node *n;
+
+		(yyval.sym) = (yyvsp[(1) - (1)].sym);
+		n = oldname((yyvsp[(1) - (1)].sym));
+		if(n->pack != N)
+			n->pack->used = 1;
+	}
+    break;
+
+  case 238:
+#line 1615 "go.y"
+    {
+		Pkg *pkg;
+
+		if((yyvsp[(1) - (3)].sym)->def == N || (yyvsp[(1) - (3)].sym)->def->op != OPACK) {
+			yyerror("%S is not a package", (yyvsp[(1) - (3)].sym));
+			pkg = localpkg;
+		} else {
+			(yyvsp[(1) - (3)].sym)->def->used = 1;
+			pkg = (yyvsp[(1) - (3)].sym)->def->pkg;
+		}
+		(yyval.sym) = restrictlookup((yyvsp[(3) - (3)].sym)->name, pkg);
+	}
+    break;
+
+  case 239:
+#line 1630 "go.y"
+    {
+		(yyval.node) = embedded((yyvsp[(1) - (1)].sym), localpkg);
+	}
+    break;
+
+  case 240:
+#line 1636 "go.y"
+    {
+		(yyval.node) = nod(ODCLFIELD, (yyvsp[(1) - (2)].node), (yyvsp[(2) - (2)].node));
+		ifacedcl((yyval.node));
+	}
+    break;
+
+  case 241:
+#line 1641 "go.y"
+    {
+		(yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(1) - (1)].sym)));
+	}
+    break;
+
+  case 242:
+#line 1645 "go.y"
+    {
+		(yyval.node) = nod(ODCLFIELD, N, oldname((yyvsp[(2) - (3)].sym)));
+		yyerror("cannot parenthesize embedded type");
+	}
+    break;
+
+  case 243:
+#line 1652 "go.y"
+    {
+		// without func keyword
+		(yyvsp[(2) - (4)].list) = checkarglist((yyvsp[(2) - (4)].list), 1);
+		(yyval.node) = nod(OTFUNC, fakethis(), N);
+		(yyval.node)->list = (yyvsp[(2) - (4)].list);
+		(yyval.node)->rlist = (yyvsp[(4) - (4)].list);
+	}
+    break;
+
+  case 245:
+#line 1666 "go.y"
+    {
+		(yyval.node) = nod(ONONAME, N, N);
+		(yyval.node)->sym = (yyvsp[(1) - (2)].sym);
+		(yyval.node) = nod(OKEY, (yyval.node), (yyvsp[(2) - (2)].node));
+	}
+    break;
+
+  case 246:
+#line 1672 "go.y"
+    {
+		(yyval.node) = nod(ONONAME, N, N);
+		(yyval.node)->sym = (yyvsp[(1) - (2)].sym);
+		(yyval.node) = nod(OKEY, (yyval.node), (yyvsp[(2) - (2)].node));
+	}
+    break;
+
+  case 248:
+#line 1681 "go.y"
+    {
+		(yyval.list) = list1((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 249:
+#line 1685 "go.y"
+    {
+		(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 250:
+#line 1690 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 251:
+#line 1694 "go.y"
+    {
+		(yyval.list) = (yyvsp[(1) - (2)].list);
+	}
+    break;
+
+  case 252:
+#line 1702 "go.y"
+    {
+		(yyval.node) = N;
+	}
+    break;
+
+  case 254:
+#line 1707 "go.y"
+    {
+		(yyval.node) = liststmt((yyvsp[(1) - (1)].list));
+	}
+    break;
+
+  case 256:
+#line 1712 "go.y"
+    {
+		(yyval.node) = N;
+	}
+    break;
+
+  case 262:
+#line 1723 "go.y"
+    {
+		(yyvsp[(1) - (2)].node) = nod(OLABEL, (yyvsp[(1) - (2)].node), N);
+		(yyvsp[(1) - (2)].node)->sym = dclstack;  // context, for goto restrictions
+	}
+    break;
+
+  case 263:
+#line 1728 "go.y"
+    {
+		NodeList *l;
+
+		(yyvsp[(1) - (4)].node)->defn = (yyvsp[(4) - (4)].node);
+		l = list1((yyvsp[(1) - (4)].node));
+		if((yyvsp[(4) - (4)].node))
+			l = list(l, (yyvsp[(4) - (4)].node));
+		(yyval.node) = liststmt(l);
+	}
+    break;
+
+  case 264:
+#line 1738 "go.y"
+    {
+		// will be converted to OFALL
+		(yyval.node) = nod(OXFALL, N, N);
+		(yyval.node)->xoffset = block;
+	}
+    break;
+
+  case 265:
+#line 1744 "go.y"
+    {
+		(yyval.node) = nod(OBREAK, (yyvsp[(2) - (2)].node), N);
+	}
+    break;
+
+  case 266:
+#line 1748 "go.y"
+    {
+		(yyval.node) = nod(OCONTINUE, (yyvsp[(2) - (2)].node), N);
+	}
+    break;
+
+  case 267:
+#line 1752 "go.y"
+    {
+		(yyval.node) = nod(OPROC, (yyvsp[(2) - (2)].node), N);
+	}
+    break;
+
+  case 268:
+#line 1756 "go.y"
+    {
+		(yyval.node) = nod(ODEFER, (yyvsp[(2) - (2)].node), N);
+	}
+    break;
+
+  case 269:
+#line 1760 "go.y"
+    {
+		(yyval.node) = nod(OGOTO, (yyvsp[(2) - (2)].node), N);
+		(yyval.node)->sym = dclstack;  // context, for goto restrictions
+	}
+    break;
+
+  case 270:
+#line 1765 "go.y"
+    {
+		(yyval.node) = nod(ORETURN, N, N);
+		(yyval.node)->list = (yyvsp[(2) - (2)].list);
+		if((yyval.node)->list == nil && curfn != N) {
+			NodeList *l;
+
+			for(l=curfn->dcl; l; l=l->next) {
+				if(l->n->class == PPARAM)
+					continue;
+				if(l->n->class != PPARAMOUT)
+					break;
+				if(l->n->sym->def != l->n)
+					yyerror("%s is shadowed during return", l->n->sym->name);
+			}
+		}
+	}
+    break;
+
+  case 271:
+#line 1784 "go.y"
+    {
+		(yyval.list) = nil;
+		if((yyvsp[(1) - (1)].node) != N)
+			(yyval.list) = list1((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 272:
+#line 1790 "go.y"
+    {
+		(yyval.list) = (yyvsp[(1) - (3)].list);
+		if((yyvsp[(3) - (3)].node) != N)
+			(yyval.list) = list((yyval.list), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 273:
+#line 1798 "go.y"
+    {
+		(yyval.list) = list1((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 274:
+#line 1802 "go.y"
+    {
+		(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 275:
+#line 1808 "go.y"
+    {
+		(yyval.list) = list1((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 276:
+#line 1812 "go.y"
+    {
+		(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 277:
+#line 1818 "go.y"
+    {
+		(yyval.list) = list1((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 278:
+#line 1822 "go.y"
+    {
+		(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 279:
+#line 1828 "go.y"
+    {
+		(yyval.list) = list1((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 280:
+#line 1832 "go.y"
+    {
+		(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 281:
+#line 1841 "go.y"
+    {
+		(yyval.list) = list1((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 282:
+#line 1845 "go.y"
+    {
+		(yyval.list) = list1((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 283:
+#line 1849 "go.y"
+    {
+		(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 284:
+#line 1853 "go.y"
+    {
+		(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 285:
+#line 1858 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 286:
+#line 1862 "go.y"
+    {
+		(yyval.list) = (yyvsp[(1) - (2)].list);
+	}
+    break;
+
+  case 291:
+#line 1876 "go.y"
+    {
+		(yyval.node) = N;
+	}
+    break;
+
+  case 293:
+#line 1882 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 295:
+#line 1888 "go.y"
+    {
+		(yyval.node) = N;
+	}
+    break;
+
+  case 297:
+#line 1894 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 299:
+#line 1900 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 301:
+#line 1906 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 303:
+#line 1912 "go.y"
+    {
+		(yyval.val).ctype = CTxxx;
+	}
+    break;
+
+  case 305:
+#line 1922 "go.y"
+    {
+		importimport((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].val).u.sval);
+	}
+    break;
+
+  case 306:
+#line 1926 "go.y"
+    {
+		importvar((yyvsp[(2) - (4)].sym), (yyvsp[(3) - (4)].type));
+	}
+    break;
+
+  case 307:
+#line 1930 "go.y"
+    {
+		importconst((yyvsp[(2) - (5)].sym), types[TIDEAL], (yyvsp[(4) - (5)].node));
+	}
+    break;
+
+  case 308:
+#line 1934 "go.y"
+    {
+		importconst((yyvsp[(2) - (6)].sym), (yyvsp[(3) - (6)].type), (yyvsp[(5) - (6)].node));
+	}
+    break;
+
+  case 309:
+#line 1938 "go.y"
+    {
+		importtype((yyvsp[(2) - (4)].type), (yyvsp[(3) - (4)].type));
+	}
+    break;
+
+  case 310:
+#line 1942 "go.y"
+    {
+		if((yyvsp[(2) - (4)].node) == N) {
+			dclcontext = PEXTERN;  // since we skip the funcbody below
+			break;
+		}
+
+		(yyvsp[(2) - (4)].node)->inl = (yyvsp[(3) - (4)].list);
+
+		funcbody((yyvsp[(2) - (4)].node));
+		importlist = list(importlist, (yyvsp[(2) - (4)].node));
+
+		if(debug['E']) {
+			print("import [%Z] func %lN \n", importpkg->path, (yyvsp[(2) - (4)].node));
+			if(debug['m'] > 2 && (yyvsp[(2) - (4)].node)->inl)
+				print("inl body:%+H\n", (yyvsp[(2) - (4)].node)->inl);
+		}
+	}
+    break;
+
+  case 311:
+#line 1962 "go.y"
+    {
+		(yyval.sym) = (yyvsp[(1) - (1)].sym);
+		structpkg = (yyval.sym)->pkg;
+	}
+    break;
+
+  case 312:
+#line 1969 "go.y"
+    {
+		(yyval.type) = pkgtype((yyvsp[(1) - (1)].sym));
+		importsym((yyvsp[(1) - (1)].sym), OTYPE);
+	}
+    break;
+
+  case 318:
+#line 1989 "go.y"
+    {
+		(yyval.type) = pkgtype((yyvsp[(1) - (1)].sym));
+	}
+    break;
+
+  case 319:
+#line 1993 "go.y"
+    {
+		// predefined name like uint8
+		(yyvsp[(1) - (1)].sym) = pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg);
+		if((yyvsp[(1) - (1)].sym)->def == N || (yyvsp[(1) - (1)].sym)->def->op != OTYPE) {
+			yyerror("%s is not a type", (yyvsp[(1) - (1)].sym)->name);
+			(yyval.type) = T;
+		} else
+			(yyval.type) = (yyvsp[(1) - (1)].sym)->def->type;
+	}
+    break;
+
+  case 320:
+#line 2003 "go.y"
+    {
+		(yyval.type) = aindex(N, (yyvsp[(3) - (3)].type));
+	}
+    break;
+
+  case 321:
+#line 2007 "go.y"
+    {
+		(yyval.type) = aindex(nodlit((yyvsp[(2) - (4)].val)), (yyvsp[(4) - (4)].type));
+	}
+    break;
+
+  case 322:
+#line 2011 "go.y"
+    {
+		(yyval.type) = maptype((yyvsp[(3) - (5)].type), (yyvsp[(5) - (5)].type));
+	}
+    break;
+
+  case 323:
+#line 2015 "go.y"
+    {
+		(yyval.type) = tostruct((yyvsp[(3) - (4)].list));
+	}
+    break;
+
+  case 324:
+#line 2019 "go.y"
+    {
+		(yyval.type) = tointerface((yyvsp[(3) - (4)].list));
+	}
+    break;
+
+  case 325:
+#line 2023 "go.y"
+    {
+		(yyval.type) = ptrto((yyvsp[(2) - (2)].type));
+	}
+    break;
+
+  case 326:
+#line 2027 "go.y"
+    {
+		(yyval.type) = typ(TCHAN);
+		(yyval.type)->type = (yyvsp[(2) - (2)].type);
+		(yyval.type)->chan = Cboth;
+	}
+    break;
+
+  case 327:
+#line 2033 "go.y"
+    {
+		(yyval.type) = typ(TCHAN);
+		(yyval.type)->type = (yyvsp[(3) - (4)].type);
+		(yyval.type)->chan = Cboth;
+	}
+    break;
+
+  case 328:
+#line 2039 "go.y"
+    {
+		(yyval.type) = typ(TCHAN);
+		(yyval.type)->type = (yyvsp[(3) - (3)].type);
+		(yyval.type)->chan = Csend;
+	}
+    break;
+
+  case 329:
+#line 2047 "go.y"
+    {
+		(yyval.type) = typ(TCHAN);
+		(yyval.type)->type = (yyvsp[(3) - (3)].type);
+		(yyval.type)->chan = Crecv;
+	}
+    break;
+
+  case 330:
+#line 2055 "go.y"
+    {
+		(yyval.type) = functype(nil, (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list));
+	}
+    break;
+
+  case 331:
+#line 2061 "go.y"
+    {
+		(yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(2) - (3)].type)));
+		if((yyvsp[(1) - (3)].sym))
+			(yyval.node)->left = newname((yyvsp[(1) - (3)].sym));
+		(yyval.node)->val = (yyvsp[(3) - (3)].val);
+	}
+    break;
+
+  case 332:
+#line 2068 "go.y"
+    {
+		Type *t;
+	
+		t = typ(TARRAY);
+		t->bound = -1;
+		t->type = (yyvsp[(3) - (4)].type);
+
+		(yyval.node) = nod(ODCLFIELD, N, typenod(t));
+		if((yyvsp[(1) - (4)].sym))
+			(yyval.node)->left = newname((yyvsp[(1) - (4)].sym));
+		(yyval.node)->isddd = 1;
+		(yyval.node)->val = (yyvsp[(4) - (4)].val);
+	}
+    break;
+
+  case 333:
+#line 2084 "go.y"
+    {
+		Sym *s;
+		Pkg *p;
+
+		if((yyvsp[(1) - (3)].sym) != S && strcmp((yyvsp[(1) - (3)].sym)->name, "?") != 0) {
+			(yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (3)].sym)), typenod((yyvsp[(2) - (3)].type)));
+			(yyval.node)->val = (yyvsp[(3) - (3)].val);
+		} else {
+			s = (yyvsp[(2) - (3)].type)->sym;
+			if(s == S && isptr[(yyvsp[(2) - (3)].type)->etype])
+				s = (yyvsp[(2) - (3)].type)->type->sym;
+			p = importpkg;
+			if((yyvsp[(1) - (3)].sym) != S)
+				p = (yyvsp[(1) - (3)].sym)->pkg;
+			(yyval.node) = embedded(s, p);
+			(yyval.node)->right = typenod((yyvsp[(2) - (3)].type));
+			(yyval.node)->val = (yyvsp[(3) - (3)].val);
+		}
+	}
+    break;
+
+  case 334:
+#line 2106 "go.y"
+    {
+		(yyval.node) = nod(ODCLFIELD, newname((yyvsp[(1) - (5)].sym)), typenod(functype(fakethis(), (yyvsp[(3) - (5)].list), (yyvsp[(5) - (5)].list))));
+	}
+    break;
+
+  case 335:
+#line 2110 "go.y"
+    {
+		(yyval.node) = nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type)));
+	}
+    break;
+
+  case 336:
+#line 2115 "go.y"
+    {
+		(yyval.list) = nil;
+	}
+    break;
+
+  case 338:
+#line 2122 "go.y"
+    {
+		(yyval.list) = (yyvsp[(2) - (3)].list);
+	}
+    break;
+
+  case 339:
+#line 2126 "go.y"
+    {
+		(yyval.list) = list1(nod(ODCLFIELD, N, typenod((yyvsp[(1) - (1)].type))));
+	}
+    break;
+
+  case 340:
+#line 2136 "go.y"
+    {
+		(yyval.node) = nodlit((yyvsp[(1) - (1)].val));
+	}
+    break;
+
+  case 341:
+#line 2140 "go.y"
+    {
+		(yyval.node) = nodlit((yyvsp[(2) - (2)].val));
+		switch((yyval.node)->val.ctype){
+		case CTINT:
+		case CTRUNE:
+			mpnegfix((yyval.node)->val.u.xval);
+			break;
+		case CTFLT:
+			mpnegflt((yyval.node)->val.u.fval);
+			break;
+		case CTCPLX:
+			mpnegflt(&(yyval.node)->val.u.cval->real);
+			mpnegflt(&(yyval.node)->val.u.cval->imag);
+			break;
+		default:
+			yyerror("bad negated constant");
+		}
+	}
+    break;
+
+  case 342:
+#line 2159 "go.y"
+    {
+		(yyval.node) = oldname(pkglookup((yyvsp[(1) - (1)].sym)->name, builtinpkg));
+		if((yyval.node)->op != OLITERAL)
+			yyerror("bad constant %S", (yyval.node)->sym);
+	}
+    break;
+
+  case 344:
+#line 2168 "go.y"
+    {
+		if((yyvsp[(2) - (5)].node)->val.ctype == CTRUNE && (yyvsp[(4) - (5)].node)->val.ctype == CTINT) {
+			(yyval.node) = (yyvsp[(2) - (5)].node);
+			mpaddfixfix((yyvsp[(2) - (5)].node)->val.u.xval, (yyvsp[(4) - (5)].node)->val.u.xval, 0);
+			break;
+		}
+		(yyvsp[(4) - (5)].node)->val.u.cval->real = (yyvsp[(4) - (5)].node)->val.u.cval->imag;
+		mpmovecflt(&(yyvsp[(4) - (5)].node)->val.u.cval->imag, 0.0);
+		(yyval.node) = nodcplxlit((yyvsp[(2) - (5)].node)->val, (yyvsp[(4) - (5)].node)->val);
+	}
+    break;
+
+  case 347:
+#line 2184 "go.y"
+    {
+		(yyval.list) = list1((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 348:
+#line 2188 "go.y"
+    {
+		(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 349:
+#line 2194 "go.y"
+    {
+		(yyval.list) = list1((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 350:
+#line 2198 "go.y"
+    {
+		(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+  case 351:
+#line 2204 "go.y"
+    {
+		(yyval.list) = list1((yyvsp[(1) - (1)].node));
+	}
+    break;
+
+  case 352:
+#line 2208 "go.y"
+    {
+		(yyval.list) = list((yyvsp[(1) - (3)].list), (yyvsp[(3) - (3)].node));
+	}
+    break;
+
+
+/* Line 1267 of yacc.c.  */
+#line 4907 "y.tab.c"
+      default: break;
+    }
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+
+  /* Now `shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (YY_("syntax error"));
+#else
+      {
+	YYSIZE_T yysize = yysyntax_error (0, yystate, yychar);
+	if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM)
+	  {
+	    YYSIZE_T yyalloc = 2 * yysize;
+	    if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM))
+	      yyalloc = YYSTACK_ALLOC_MAXIMUM;
+	    if (yymsg != yymsgbuf)
+	      YYSTACK_FREE (yymsg);
+	    yymsg = (char *) YYSTACK_ALLOC (yyalloc);
+	    if (yymsg)
+	      yymsg_alloc = yyalloc;
+	    else
+	      {
+		yymsg = yymsgbuf;
+		yymsg_alloc = sizeof yymsgbuf;
+	      }
+	  }
+
+	if (0 < yysize && yysize <= yymsg_alloc)
+	  {
+	    (void) yysyntax_error (yymsg, yystate, yychar);
+	    yyerror (yymsg);
+	  }
+	else
+	  {
+	    yyerror (YY_("syntax error"));
+	    if (yysize != 0)
+	      goto yyexhaustedlab;
+	  }
+      }
+#endif
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse look-ahead token after an
+	 error, discard it.  */
+
+      if (yychar <= YYEOF)
+	{
+	  /* Return failure if at end of input.  */
+	  if (yychar == YYEOF)
+	    YYABORT;
+	}
+      else
+	{
+	  yydestruct ("Error: discarding",
+		      yytoken, &yylval);
+	  yychar = YYEMPTY;
+	}
+    }
+
+  /* Else will try to reuse look-ahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;	/* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (yyn != YYPACT_NINF)
+	{
+	  yyn += YYTERROR;
+	  if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+	    {
+	      yyn = yytable[yyn];
+	      if (0 < yyn)
+		break;
+	    }
+	}
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+	YYABORT;
+
+
+      yydestruct ("Error: popping",
+		  yystos[yystate], yyvsp);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  *++yyvsp = yylval;
+
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#ifndef yyoverflow
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEOF && yychar != YYEMPTY)
+     yydestruct ("Cleanup: discarding lookahead",
+		 yytoken, &yylval);
+  /* Do not reclaim the symbols of the rule which action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+		  yystos[*yyssp], yyvsp);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  /* Make sure YYID is used.  */
+  return YYID (yyresult);
+}
+
+
+#line 2212 "go.y"
+
+
+static void
+fixlbrace(int lbr)
+{
+	// If the opening brace was an LBODY,
+	// set up for another one now that we're done.
+	// See comment in lex.c about loophack.
+	if(lbr == LBODY)
+		loophack = 1;
+}
+
+
diff --git a/src/cmd/gc/y.tab.h b/src/cmd/gc/y.tab.h
new file mode 100644
index 0000000..d01fbe1
--- /dev/null
+++ b/src/cmd/gc/y.tab.h
@@ -0,0 +1,167 @@
+/* A Bison parser, made by GNU Bison 2.3.  */
+
+/* Skeleton interface for Bison's Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006
+   Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor,
+   Boston, MA 02110-1301, USA.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* Tokens.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+   /* Put the tokens into the symbol table, so that GDB and other debuggers
+      know about them.  */
+   enum yytokentype {
+     LLITERAL = 258,
+     LASOP = 259,
+     LCOLAS = 260,
+     LBREAK = 261,
+     LCASE = 262,
+     LCHAN = 263,
+     LCONST = 264,
+     LCONTINUE = 265,
+     LDDD = 266,
+     LDEFAULT = 267,
+     LDEFER = 268,
+     LELSE = 269,
+     LFALL = 270,
+     LFOR = 271,
+     LFUNC = 272,
+     LGO = 273,
+     LGOTO = 274,
+     LIF = 275,
+     LIMPORT = 276,
+     LINTERFACE = 277,
+     LMAP = 278,
+     LNAME = 279,
+     LPACKAGE = 280,
+     LRANGE = 281,
+     LRETURN = 282,
+     LSELECT = 283,
+     LSTRUCT = 284,
+     LSWITCH = 285,
+     LTYPE = 286,
+     LVAR = 287,
+     LANDAND = 288,
+     LANDNOT = 289,
+     LBODY = 290,
+     LCOMM = 291,
+     LDEC = 292,
+     LEQ = 293,
+     LGE = 294,
+     LGT = 295,
+     LIGNORE = 296,
+     LINC = 297,
+     LLE = 298,
+     LLSH = 299,
+     LLT = 300,
+     LNE = 301,
+     LOROR = 302,
+     LRSH = 303,
+     NotPackage = 304,
+     NotParen = 305,
+     PreferToRightParen = 306
+   };
+#endif
+/* Tokens.  */
+#define LLITERAL 258
+#define LASOP 259
+#define LCOLAS 260
+#define LBREAK 261
+#define LCASE 262
+#define LCHAN 263
+#define LCONST 264
+#define LCONTINUE 265
+#define LDDD 266
+#define LDEFAULT 267
+#define LDEFER 268
+#define LELSE 269
+#define LFALL 270
+#define LFOR 271
+#define LFUNC 272
+#define LGO 273
+#define LGOTO 274
+#define LIF 275
+#define LIMPORT 276
+#define LINTERFACE 277
+#define LMAP 278
+#define LNAME 279
+#define LPACKAGE 280
+#define LRANGE 281
+#define LRETURN 282
+#define LSELECT 283
+#define LSTRUCT 284
+#define LSWITCH 285
+#define LTYPE 286
+#define LVAR 287
+#define LANDAND 288
+#define LANDNOT 289
+#define LBODY 290
+#define LCOMM 291
+#define LDEC 292
+#define LEQ 293
+#define LGE 294
+#define LGT 295
+#define LIGNORE 296
+#define LINC 297
+#define LLE 298
+#define LLSH 299
+#define LLT 300
+#define LNE 301
+#define LOROR 302
+#define LRSH 303
+#define NotPackage 304
+#define NotParen 305
+#define PreferToRightParen 306
+
+
+
+
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE
+#line 28 "go.y"
+{
+	Node*		node;
+	NodeList*		list;
+	Type*		type;
+	Sym*		sym;
+	struct	Val	val;
+	int		i;
+}
+/* Line 1529 of yacc.c.  */
+#line 160 "y.tab.h"
+	YYSTYPE;
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE yylval;
+
diff --git a/src/cmd/gc/yerr.h b/src/cmd/gc/yerr.h
new file mode 100644
index 0000000..d0dd639
--- /dev/null
+++ b/src/cmd/gc/yerr.h
@@ -0,0 +1,79 @@
+// Copyright 2010 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Example-based syntax error messages.
+// See bisonerrors, Makefile, go.y.
+
+static struct {
+	int yystate;
+	int yychar;
+	char *msg;
+} yymsg[] = {
+	// Each line of the form % token list
+	// is converted by bisonerrors into the yystate and yychar caused
+	// by that token list.
+
+	{222, ',',
+	"unexpected comma during import block"},
+
+	{32, ';',
+	"missing import path; require quoted string"},
+
+	{380, ';',
+	"missing { after if clause"},
+
+	{401, ';',
+	"missing { after switch clause"},
+
+	{239, ';',
+	"missing { after for clause"},
+
+	{478, LBODY,
+	"missing { after for clause"},
+
+	{22, '{',
+	"unexpected semicolon or newline before {"},
+
+	{145, ';',
+	"unexpected semicolon or newline in type declaration"},
+
+	{37, '}',
+	"unexpected } in channel type"},
+	
+	{37, ')',
+	"unexpected ) in channel type"},
+	
+	{37, ',',
+	"unexpected comma in channel type"},
+
+	{441, LELSE,
+	"unexpected semicolon or newline before else"},
+
+	{259, ',',
+	"name list not allowed in interface type"},
+
+	{239, LVAR,
+	"var declaration not allowed in for initializer"},
+
+	{65, '{',
+	"unexpected { at end of statement"},
+
+	{379, '{',
+	"unexpected { at end of statement"},
+	
+	{126, ';',
+	"argument to go/defer must be function call"},
+	
+	{428, ';',
+	"need trailing comma before newline in composite literal"},
+	
+	{439, ';',
+	"need trailing comma before newline in composite literal"},
+	
+	{113, LNAME,
+	"nested func not allowed"},
+
+	{647, ';',
+	"else must be followed by if or statement block"}
+};