text: // 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"
  
  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
  walkexprlistcheap(NodeList *l, NodeList **init)
  {
  	for(; l; l=l->next) {
  		l->n = cheapexpr(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;
  
  	op = nn->op;
  	all = nn->list;
  	calls = nil;
  	notfirst = 0;
  
  	// Hoist all the argument evaluation up before the lock.
  	walkexprlistcheap(all, init);
  
  	calls = list(calls, mkcall("printlock", T, init));
  
  	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));
  
  	calls = list(calls, mkcall("printunlock", T, init));
  
  	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;
  	vlong x;
  	static Bvec *bv;
  	char name[32];
  
  	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 <= 4*widthptr) {
  			x = 0;
  			if(bv == nil)
  				bv = bvalloc(BitsPerPointer*4);
  			bvresetall(bv);
  			twobitwalktype1(t, &x, bv);
  			// The bvgets are looking for BitsPointer in successive slots.
  			enum {
  				PtrBit = 1,
  			};
  			if(BitsPointer != (1<<PtrBit))
  				fatal("wrong PtrBit");
  			switch(t->width/widthptr) {
  			default:
  				fatal("found writebarrierfat for %d-byte object of type %T", (int)t->width, t);
  			case 2:
  				snprint(name, sizeof name, "writebarrierfat%d%d",
  					bvget(bv, PtrBit), bvget(bv, BitsPerPointer+PtrBit));
  				break;
  			case 3:
  				snprint(name, sizeof name, "writebarrierfat%d%d%d",
  					bvget(bv, PtrBit), bvget(bv, BitsPerPointer+PtrBit), bvget(bv, 2*BitsPerPointer+PtrBit));
  				break;
  			case 4:
  				snprint(name, sizeof name, "writebarrierfat%d%d%d%d",
  					bvget(bv, PtrBit), bvget(bv, BitsPerPointer+PtrBit), bvget(bv, 2*BitsPerPointer+PtrBit), bvget(bv, 3*BitsPerPointer+PtrBit));
  				break;
  			}
  			n = mkcall1(writebarrierfn(name, 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(haspointers(n->left->type->type)) {
  		fn = writebarrierfn("writebarriercopy", n->left->type, n->right->type);
  		return mkcall1(fn, n->type, init, typename(n->left->type->type), n->left, n->right);
  	}
  
  	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;
  
  	if(thechar == '9')
  		return;
  	
  	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;
  
  	// TODO(minux)
  	if(thechar == '9')
  		return;
  
  	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;
  }
  