// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Rewrite tree to use separate statements to enforce
// order of evaluation.  Makes walk easier, because it
// can (after this runs) reorder at will within an expression.
//
// Rewrite x op= y into x = x op y.
//
// Introduce temporaries as needed by runtime routines.
// For example, the map runtime routines take the map key
// by reference, so make sure all map keys are addressable
// by copying them to temporaries as needed.
// The same is true for channel operations.
//
// Arrange that map index expressions only appear in direct
// assignments x = m[k] or m[k] = x, never in larger expressions.
//
// Arrange that receive expressions only appear in direct assignments
// x = <-c or as standalone statements <-c, never in larger expressions.

// TODO(rsc): The temporary introduction during multiple assignments
// should be moved into this file, so that the temporaries can be cleaned
// and so that conversions implicit in the OAS2FUNC and OAS2RECV
// nodes can be made explicit and then have their temporaries cleaned.

// TODO(rsc): Goto and multilevel break/continue can jump over
// inserted VARKILL annotations. Work out a way to handle these.
// The current implementation is safe, in that it will execute correctly.
// But it won't reuse temporaries as aggressively as it might, and
// it can result in unnecessary zeroing of those variables in the function
// prologue.

#include	<u.h>
#include	<libc.h>
#include	"go.h"

// Order holds state during the ordering process.
typedef struct Order Order;
struct Order
{
	NodeList *out; // list of generated statements
	NodeList *temp; // head of stack of temporary variables
	NodeList *free; // free list of NodeList* structs (for use in temp)
};

static void	orderstmt(Node*, Order*);
static void	orderstmtlist(NodeList*, Order*);
static void	orderblock(NodeList **l);
static void	orderexpr(Node**, Order*);
static void orderexprinplace(Node**, Order*);
static void	orderexprlist(NodeList*, Order*);
static void	orderexprlistinplace(NodeList*, Order*);

// Order rewrites fn->nbody to apply the ordering constraints
// described in the comment at the top of the file.
void
order(Node *fn)
{
	orderblock(&fn->nbody);
}

// Ordertemp allocates a new temporary with the given type,
// pushes it onto the temp stack, and returns it.
// If clear is true, ordertemp emits code to zero the temporary.
static Node*
ordertemp(Type *t, Order *order, int clear)
{
	Node *var, *a;
	NodeList *l;

	var = temp(t);
	if(clear) {
		a = nod(OAS, var, N);
		typecheck(&a, Etop);
		order->out = list(order->out, a);
	}
	if((l = order->free) == nil)
		l = mal(sizeof *l);
	order->free = l->next;
	l->next = order->temp;
	l->n = var;
	order->temp = l;
	return var;
}

// Ordercopyexpr behaves like ordertemp but also emits
// code to initialize the temporary to the value n.
//
// The clear argument is provided for use when the evaluation
// of tmp = n turns into a function call that is passed a pointer
// to the temporary as the output space. If the call blocks before
// tmp has been written, the garbage collector will still treat the
// temporary as live, so we must zero it before entering that call.
// Today, this only happens for channel receive operations.
// (The other candidate would be map access, but map access
// returns a pointer to the result data instead of taking a pointer
// to be filled in.)
static Node*
ordercopyexpr(Node *n, Type *t, Order *order, int clear)
{
	Node *a, *var;

	var = ordertemp(t, order, clear);
	a = nod(OAS, var, n);
	typecheck(&a, Etop);
	order->out = list(order->out, a);
	return var;
}

// Ordercheapexpr returns a cheap version of n.
// The definition of cheap is that n is a variable or constant.
// If not, ordercheapexpr allocates a new tmp, emits tmp = n,
// and then returns tmp.
static Node*
ordercheapexpr(Node *n, Order *order)
{
	switch(n->op) {
	case ONAME:
	case OLITERAL:
		return n;
	}
	return ordercopyexpr(n, n->type, order, 0);
}

// Ordersafeexpr returns a safe version of n.
// The definition of safe is that n can appear multiple times
// without violating the semantics of the original program,
// and that assigning to the safe version has the same effect
// as assigning to the original n.
//
// The intended use is to apply to x when rewriting x += y into x = x + y.
static Node*
ordersafeexpr(Node *n, Order *order)
{
	Node *l, *r, *a;
	
	switch(n->op) {
	default:
		fatal("ordersafeexpr %O", n->op);

	case ONAME:
	case OLITERAL:
		return n;

	case ODOT:
		l = ordersafeexpr(n->left, order);
		if(l == n->left)
			return n;
		a = nod(OXXX, N, N);
		*a = *n;
		a->orig = a;
		a->left = l;
		typecheck(&a, Erv);
		return a;

	case ODOTPTR:
	case OIND:
		l = ordercheapexpr(n->left, order);
		if(l == n->left)
			return n;
		a = nod(OXXX, N, N);
		*a = *n;
		a->orig = a;
		a->left = l;
		typecheck(&a, Erv);
		return a;
		
	case OINDEX:
	case OINDEXMAP:
		if(isfixedarray(n->left->type))
			l = ordersafeexpr(n->left, order);
		else
			l = ordercheapexpr(n->left, order);
		r = ordercheapexpr(n->right, order);
		if(l == n->left && r == n->right)
			return n;
		a = nod(OXXX, N, N);
		*a = *n;
		a->orig = a;
		a->left = l;
		a->right = r;
		typecheck(&a, Erv);
		return a;
	}
}		

// Istemp reports whether n is a temporary variable.
static int
istemp(Node *n)
{
	if(n->op != ONAME)
		return 0;
	return strncmp(n->sym->name, "autotmp_", 8) == 0;
}

// Isaddrokay reports whether it is okay to pass n's address to runtime routines.
// Taking the address of a variable makes the liveness and optimization analyses
// lose track of where the variable's lifetime ends. To avoid hurting the analyses
// of ordinary stack variables, those are not 'isaddrokay'. Temporaries are okay,
// because we emit explicit VARKILL instructions marking the end of those
// temporaries' lifetimes.
static int
isaddrokay(Node *n)
{
	return islvalue(n) && (n->op != ONAME || n->class == PEXTERN || istemp(n));
}

// Orderaddrtemp ensures that *np is okay to pass by address to runtime routines.
// If the original argument *np is not okay, orderaddrtemp creates a tmp, emits
// tmp = *np, and then sets *np to the tmp variable.
static void
orderaddrtemp(Node **np, Order *order)
{
	Node *n;
	
	n = *np;
	if(isaddrokay(n))
		return;
	*np = ordercopyexpr(n, n->type, order, 0);
}

// Marktemp returns the top of the temporary variable stack.
static NodeList*
marktemp(Order *order)
{
	return order->temp;
}

// Poptemp pops temporaries off the stack until reaching the mark,
// which must have been returned by marktemp.
static void
poptemp(NodeList *mark, Order *order)
{
	NodeList *l;

	while((l = order->temp) != mark) {
		order->temp = l->next;
		l->next = order->free;
		order->free = l;
	}
}

// Cleantempnopop emits to *out VARKILL instructions for each temporary
// above the mark on the temporary stack, but it does not pop them
// from the stack.
static void
cleantempnopop(NodeList *mark, Order *order, NodeList **out)
{
	NodeList *l;
	Node *kill;

	for(l=order->temp; l != mark; l=l->next) {
		kill = nod(OVARKILL, l->n, N);
		typecheck(&kill, Etop);
		*out = list(*out, kill);
	}
}

// Cleantemp emits VARKILL instructions for each temporary above the
// mark on the temporary stack and removes them from the stack.
static void
cleantemp(NodeList *top, Order *order)
{
	cleantempnopop(top, order, &order->out);
	poptemp(top, order);
}

// Orderstmtlist orders each of the statements in the list.
static void
orderstmtlist(NodeList *l, Order *order)
{
	for(; l; l=l->next)
		orderstmt(l->n, order);
}

// Orderblock orders the block of statements *l onto a new list,
// and then replaces *l with that list.
static void
orderblock(NodeList **l)
{
	Order order;
	NodeList *mark;
	
	memset(&order, 0, sizeof order);
	mark = marktemp(&order);
	orderstmtlist(*l, &order);
	cleantemp(mark, &order);
	*l = order.out;
}

// Orderexprinplace orders the side effects in *np and
// leaves them as the init list of the final *np.
static void
orderexprinplace(Node **np, Order *outer)
{
	Node *n;
	NodeList **lp;
	Order order;
	
	n = *np;
	memset(&order, 0, sizeof order);
	orderexpr(&n, &order);
	addinit(&n, order.out);
	
	// insert new temporaries from order
	// at head of outer list.
	lp = &order.temp;
	while(*lp != nil)
		lp = &(*lp)->next;
	*lp = outer->temp;
	outer->temp = order.temp;

	*np = n;
}

// Orderstmtinplace orders the side effects of the single statement *np
// and replaces it with the resulting statement list.
static void
orderstmtinplace(Node **np)
{
	Node *n;
	Order order;
	NodeList *mark;
	
	n = *np;
	memset(&order, 0, sizeof order);
	mark = marktemp(&order);
	orderstmt(n, &order);
	cleantemp(mark, &order);
	*np = liststmt(order.out);
}

// Orderinit moves n's init list to order->out.
static void
orderinit(Node *n, Order *order)
{
	orderstmtlist(n->ninit, order);
	n->ninit = nil;
}

// Ismulticall reports whether the list l is f() for a multi-value function.
// Such an f() could appear as the lone argument to a multi-arg function.
static int
ismulticall(NodeList *l)
{
	Node *n;
	
	// one arg only
	if(l == nil || l->next != nil)
		return 0;
	n = l->n;
	
	// must be call
	switch(n->op) {
	default:
		return 0;
	case OCALLFUNC:
	case OCALLMETH:
	case OCALLINTER:
		break;
	}
	
	// call must return multiple values
	return n->left->type->outtuple > 1;
}

// Copyret emits t1, t2, ... = n, where n is a function call,
// and then returns the list t1, t2, ....
static NodeList*
copyret(Node *n, Order *order)
{
	Type *t;
	Node *tmp, *as;
	NodeList *l1, *l2;
	Iter tl;
	
	if(n->type->etype != TSTRUCT || !n->type->funarg)
		fatal("copyret %T %d", n->type, n->left->type->outtuple);

	l1 = nil;
	l2 = nil;
	for(t=structfirst(&tl, &n->type); t; t=structnext(&tl)) {
		tmp = temp(t->type);
		l1 = list(l1, tmp);
		l2 = list(l2, tmp);
	}
	
	as = nod(OAS2, N, N);
	as->list = l1;
	as->rlist = list1(n);
	typecheck(&as, Etop);
	orderstmt(as, order);

	return l2;
}

// Ordercallargs orders the list of call arguments *l.
static void
ordercallargs(NodeList **l, Order *order)
{
	if(ismulticall(*l)) {
		// return f() where f() is multiple values.
		*l = copyret((*l)->n, order);
	} else {
		orderexprlist(*l, order);
	}
}

// Ordercall orders the call expression n.
// n->op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY.
static void
ordercall(Node *n, Order *order)
{
	orderexpr(&n->left, order);
	orderexpr(&n->right, order); // ODDDARG temp
	ordercallargs(&n->list, order);
}

// Ordermapassign appends n to order->out, introducing temporaries
// to make sure that all map assignments have the form m[k] = x,
// where x is adressable.
// (Orderexpr has already been called on n, so we know k is addressable.)
//
// If n is m[k] = x where x is not addressable, the rewrite is:
//	tmp = x
//	m[k] = tmp
//
// If n is the multiple assignment form ..., m[k], ... = ..., the rewrite is
//	t1 = m
//	t2 = k
//	...., t3, ... = x
//	t1[t2] = t3
//
// The temporaries t1, t2 are needed in case the ... being assigned
// contain m or k. They are usually unnecessary, but in the unnecessary
// cases they are also typically registerizable, so not much harm done.
// And this only applies to the multiple-assignment form.
// We could do a more precise analysis if needed, like in walk.c.
static void
ordermapassign(Node *n, Order *order)
{
	Node *m, *a;
	NodeList *l;
	NodeList *post;

	switch(n->op) {
	default:
		fatal("ordermapassign %O", n->op);

	case OAS:
		order->out = list(order->out, n);
		if(n->left->op == OINDEXMAP && !isaddrokay(n->right)) {
			m = n->left;
			n->left = ordertemp(m->type, order, 0);
			a = nod(OAS, m, n->left);
			typecheck(&a, Etop);
			order->out = list(order->out, a);
		}
		break;

	case OAS2:
	case OAS2DOTTYPE:
	case OAS2MAPR:
	case OAS2FUNC:
		post = nil;
		for(l=n->list; l != nil; l=l->next) {
			if(l->n->op == OINDEXMAP) {
				m = l->n;
				if(!istemp(m->left))
					m->left = ordercopyexpr(m->left, m->left->type, order, 0);
				if(!istemp(m->right))
					m->right = ordercopyexpr(m->right, m->right->type, order, 0);
				l->n = ordertemp(m->type, order, 0);
				a = nod(OAS, m, l->n);
				typecheck(&a, Etop);
				post = list(post, a);
			}
		}
		order->out = list(order->out, n);
		order->out = concat(order->out, post);
		break;
	}
}

// Orderstmt orders the statement n, appending to order->out.
// Temporaries created during the statement are cleaned
// up using VARKILL instructions as possible.
static void
orderstmt(Node *n, Order *order)
{
	int lno;
	NodeList *l, *t, *t1;
	Node *r, *tmp1, *tmp2, **np;
	Type *ch;

	if(n == N)
		return;

	lno = setlineno(n);

	orderinit(n, order);

	switch(n->op) {
	default:
		fatal("orderstmt %O", n->op);

	case OVARKILL:
		order->out = list(order->out, n);
		break;

	case OAS:
	case OAS2:
	case OAS2DOTTYPE:
	case OCLOSE:
	case OCOPY:
	case OPRINT:
	case OPRINTN:
	case ORECOVER:
	case ORECV:
		t = marktemp(order);
		orderexpr(&n->left, order);
		orderexpr(&n->right, order);
		orderexprlist(n->list, order);
		orderexprlist(n->rlist, order);
		switch(n->op) {
		case OAS:
		case OAS2:
		case OAS2DOTTYPE:
			ordermapassign(n, order);
			break;
		default:
			order->out = list(order->out, n);
			break;
		}
		cleantemp(t, order);
		break;

	case OASOP:
		// Special: rewrite l op= r into l = l op r.
		// This simplies quite a few operations;
		// most important is that it lets us separate
		// out map read from map write when l is
		// a map index expression.
		t = marktemp(order);
		orderexpr(&n->left, order);
		n->left = ordersafeexpr(n->left, order);
		tmp1 = treecopy(n->left);
		if(tmp1->op == OINDEXMAP)
			tmp1->etype = 0; // now an rvalue not an lvalue
		tmp1 = ordercopyexpr(tmp1, n->left->type, order, 0);
		n->right = nod(n->etype, tmp1, n->right);
		typecheck(&n->right, Erv);
		orderexpr(&n->right, order);
		n->etype = 0;
		n->op = OAS;
		ordermapassign(n, order);
		cleantemp(t, order);
		break;

	case OAS2MAPR:
		// Special: make sure key is addressable,
		// and make sure OINDEXMAP is not copied out.
		t = marktemp(order);
		orderexprlist(n->list, order);
		r = n->rlist->n;
		orderexpr(&r->left, order);
		orderexpr(&r->right, order);
		// See case OINDEXMAP below.
		if(r->right->op == OARRAYBYTESTR)
			r->right->op = OARRAYBYTESTRTMP;
		orderaddrtemp(&r->right, order);
		ordermapassign(n, order);
		cleantemp(t, order);
		break;

	case OAS2FUNC:
		// Special: avoid copy of func call n->rlist->n.
		t = marktemp(order);
		orderexprlist(n->list, order);
		ordercall(n->rlist->n, order);
		ordermapassign(n, order);
		cleantemp(t, order);
		break;

	case OAS2RECV:
		// Special: avoid copy of receive.
		// Use temporary variables to hold result,
		// so that chanrecv can take address of temporary.
		t = marktemp(order);
		orderexprlist(n->list, order);
		orderexpr(&n->rlist->n->left, order);  // arg to recv
		ch = n->rlist->n->left->type;
		tmp1 = ordertemp(ch->type, order, haspointers(ch->type));
		tmp2 = ordertemp(types[TBOOL], order, 0);
		order->out = list(order->out, n);
		r = nod(OAS, n->list->n, tmp1);
		typecheck(&r, Etop);
		ordermapassign(r, order);
		r = nod(OAS, n->list->next->n, tmp2);
		typecheck(&r, Etop);
		ordermapassign(r, order);
		n->list = list(list1(tmp1), tmp2);
		cleantemp(t, order);
		break;

	case OBLOCK:
	case OEMPTY:
		// Special: does not save n onto out.
		orderstmtlist(n->list, order);
		break;

	case OBREAK:
	case OCONTINUE:
	case ODCL:
	case ODCLCONST:
	case ODCLTYPE:
	case OFALL:
	case OXFALL:
	case OGOTO:
	case OLABEL:
	case ORETJMP:
		// Special: n->left is not an expression; save as is.
		order->out = list(order->out, n);
		break;

	case OCALLFUNC:
	case OCALLINTER:
	case OCALLMETH:
		// Special: handle call arguments.
		t = marktemp(order);
		ordercall(n, order);
		order->out = list(order->out, n);
		cleantemp(t, order);
		break;

	case ODEFER:
	case OPROC:
		// Special: order arguments to inner call but not call itself.
		t = marktemp(order);
		switch(n->left->op) {
		case ODELETE:
			// Delete will take the address of the key.
			// Copy key into new temp and do not clean it
			// (it persists beyond the statement).
			orderexprlist(n->left->list, order);
			t1 = marktemp(order);
			np = &n->left->list->next->n; // map key
			*np = ordercopyexpr(*np, (*np)->type, order, 0);
			poptemp(t1, order);
			break;
		default:
			ordercall(n->left, order);
			break;
		}
		order->out = list(order->out, n);
		cleantemp(t, order);
		break;

	case ODELETE:
		t = marktemp(order);
		orderexpr(&n->list->n, order);
		orderexpr(&n->list->next->n, order);
		orderaddrtemp(&n->list->next->n, order); // map key
		order->out = list(order->out, n);
		cleantemp(t, order);
		break;

	case OFOR:
		// Clean temporaries from condition evaluation at
		// beginning of loop body and after for statement.
		t = marktemp(order);
		orderexprinplace(&n->ntest, order);
		l = nil;
		cleantempnopop(t, order, &l);
		n->nbody = concat(l, n->nbody);
		orderblock(&n->nbody);
		orderstmtinplace(&n->nincr);
		order->out = list(order->out, n);
		cleantemp(t, order);
		break;
		
	case OIF:
		// Clean temporaries from condition at
		// beginning of both branches.
		t = marktemp(order);
		orderexprinplace(&n->ntest, order);
		l = nil;
		cleantempnopop(t, order, &l);
		n->nbody = concat(l, n->nbody);
		l = nil;
		cleantempnopop(t, order, &l);
		n->nelse = concat(l, n->nelse);
		poptemp(t, order);
		orderblock(&n->nbody);
		orderblock(&n->nelse);
		order->out = list(order->out, n);
		break;

	case OPANIC:
		// Special: argument will be converted to interface using convT2E
		// so make sure it is an addressable temporary.
		t = marktemp(order);
		orderexpr(&n->left, order);
		if(!isinter(n->left->type))
			orderaddrtemp(&n->left, order);
		order->out = list(order->out, n);
		cleantemp(t, order);
		break;

	case ORANGE:
		// n->right is the expression being ranged over.
		// order it, and then make a copy if we need one.
		// We almost always do, to ensure that we don't
		// see any value changes made during the loop.
		// Usually the copy is cheap (e.g., array pointer, chan, slice, string are all tiny).
		// The exception is ranging over an array value (not a slice, not a pointer to array),
		// which must make a copy to avoid seeing updates made during
		// the range body. Ranging over an array value is uncommon though.
		t = marktemp(order);
		orderexpr(&n->right, order);
		switch(n->type->etype) {
		default:
			fatal("orderstmt range %T", n->type);
		case TARRAY:
			if(count(n->list) < 2 || isblank(n->list->next->n)) {
				// for i := range x will only use x once, to compute len(x).
				// No need to copy it.
				break;
			}
			// fall through
		case TCHAN:
		case TSTRING:
			// chan, string, slice, array ranges use value multiple times.
			// make copy.
			r = n->right;
			if(r->type->etype == TSTRING && r->type != types[TSTRING]) {
				r = nod(OCONV, r, N);
				r->type = types[TSTRING];
				typecheck(&r, Erv);
			}
			n->right = ordercopyexpr(r, r->type, order, 0);
			break;
		case TMAP:
			// copy the map value in case it is a map literal.
			// TODO(rsc): Make tmp = literal expressions reuse tmp.
			// For maps tmp is just one word so it hardly matters.
			r = n->right;
			n->right = ordercopyexpr(r, r->type, order, 0);
			// n->alloc is the temp for the iterator.
			n->alloc = ordertemp(types[TUINT8], order, 1);
			break;
		}
		for(l=n->list; l; l=l->next)
			orderexprinplace(&l->n, order);
		orderblock(&n->nbody);
		order->out = list(order->out, n);
		cleantemp(t, order);
		break;

	case ORETURN:
		ordercallargs(&n->list, order);
		order->out = list(order->out, n);
		break;
	
	case OSELECT:
		// Special: clean case temporaries in each block entry.
		// Select must enter one of its blocks, so there is no
		// need for a cleaning at the end.
		t = marktemp(order);
		for(l=n->list; l; l=l->next) {
			if(l->n->op != OXCASE)
				fatal("order select case %O", l->n->op);
			r = l->n->left;
			setlineno(l->n);
			// Append any new body prologue to ninit.
			// The next loop will insert ninit into nbody.
			if(l->n->ninit != nil)
				fatal("order select ninit");
			if(r != nil) {
				switch(r->op) {
				default:
					yyerror("unknown op in select %O", r->op);
					dump("select case", r);
					break;

				case OSELRECV:
				case OSELRECV2:
					// If this is case x := <-ch or case x, y := <-ch, the case has
					// the ODCL nodes to declare x and y. We want to delay that
					// declaration (and possible allocation) until inside the case body.
					// Delete the ODCL nodes here and recreate them inside the body below.
					if(r->colas) {
						t = r->ninit;
						if(t != nil && t->n->op == ODCL && t->n->left == r->left)
							t = t->next;
						if(t != nil && t->n->op == ODCL && t->n->left == r->ntest)
							t = t->next;
						if(t == nil)
							r->ninit = nil;
					}
					if(r->ninit != nil) {
						yyerror("ninit on select recv");
						dumplist("ninit", r->ninit);
					}
					// case x = <-c
					// case x, ok = <-c
					// r->left is x, r->ntest is ok, r->right is ORECV, r->right->left is c.
					// r->left == N means 'case <-c'.
					// c is always evaluated; x and ok are only evaluated when assigned.
					orderexpr(&r->right->left, order);

					// Introduce temporary for receive and move actual copy into case body.
					// avoids problems with target being addressed, as usual.
					// NOTE: If we wanted to be clever, we could arrange for just one
					// temporary per distinct type, sharing the temp among all receives
					// with that temp. Similarly one ok bool could be shared among all
					// the x,ok receives. Not worth doing until there's a clear need.
					if(r->left != N && isblank(r->left))
						r->left = N;
					if(r->left != N) {
						// use channel element type for temporary to avoid conversions,
						// such as in case interfacevalue = <-intchan.
						// the conversion happens in the OAS instead.
						tmp1 = r->left;
						if(r->colas) {
							tmp2 = nod(ODCL, tmp1, N);
							typecheck(&tmp2, Etop);
							l->n->ninit = list(l->n->ninit, tmp2);
						}
						r->left = ordertemp(r->right->left->type->type, order, haspointers(r->right->left->type->type));
						tmp2 = nod(OAS, tmp1, r->left);
						typecheck(&tmp2, Etop);
						l->n->ninit = list(l->n->ninit, tmp2);
					}
					if(r->ntest != N && isblank(r->ntest))
						r->ntest = N;
					if(r->ntest != N) {
						tmp1 = r->ntest;
						if(r->colas) {
							tmp2 = nod(ODCL, tmp1, N);
							typecheck(&tmp2, Etop);
							l->n->ninit = list(l->n->ninit, tmp2);
						}
						r->ntest = ordertemp(tmp1->type, order, 0);
						tmp2 = nod(OAS, tmp1, r->ntest);
						typecheck(&tmp2, Etop);
						l->n->ninit = list(l->n->ninit, tmp2);
					}
					orderblock(&l->n->ninit);
					break;

				case OSEND:
					if(r->ninit != nil) {
						yyerror("ninit on select send");
						dumplist("ninit", r->ninit);
					}
					// case c <- x
					// r->left is c, r->right is x, both are always evaluated.
					orderexpr(&r->left, order);
					if(!istemp(r->left))
						r->left = ordercopyexpr(r->left, r->left->type, order, 0);
					orderexpr(&r->right, order);
					if(!istemp(r->right))
						r->right = ordercopyexpr(r->right, r->right->type, order, 0);
					break;
				}
			}
			orderblock(&l->n->nbody);
		}
		// Now that we have accumulated all the temporaries, clean them.
		// Also insert any ninit queued during the previous loop.
		// (The temporary cleaning must follow that ninit work.)
		for(l=n->list; l; l=l->next) {
			cleantempnopop(t, order, &l->n->ninit);
			l->n->nbody = concat(l->n->ninit, l->n->nbody);
			l->n->ninit = nil;
		}
		order->out = list(order->out, n);
		poptemp(t, order);
		break;

	case OSEND:
		// Special: value being sent is passed as a pointer; make it addressable.
		t = marktemp(order);
		orderexpr(&n->left, order);
		orderexpr(&n->right, order);
		orderaddrtemp(&n->right, order);
		order->out = list(order->out, n);
		cleantemp(t, order);
		break;

	case OSWITCH:
		// TODO(rsc): Clean temporaries more aggressively.
		// Note that because walkswitch will rewrite some of the
		// switch into a binary search, this is not as easy as it looks.
		// (If we ran that code here we could invoke orderstmt on
		// the if-else chain instead.)
		// For now just clean all the temporaries at the end.
		// In practice that's fine.
		t = marktemp(order);
		orderexpr(&n->ntest, order);
		for(l=n->list; l; l=l->next) {
			if(l->n->op != OXCASE)
				fatal("order switch case %O", l->n->op);
			orderexprlistinplace(l->n->list, order);
			orderblock(&l->n->nbody);
		}
		order->out = list(order->out, n);
		cleantemp(t, order);
		break;
	}
	
	lineno = lno;
}

// Orderexprlist orders the expression list l into order.
static void
orderexprlist(NodeList *l, Order *order)
{
	for(; l; l=l->next)
		orderexpr(&l->n, order);
}

// Orderexprlist orders the expression list l but saves
// the side effects on the individual expression ninit lists.
static void
orderexprlistinplace(NodeList *l, Order *order)
{
	for(; l; l=l->next)
		orderexprinplace(&l->n, order);
}

// Orderexpr orders a single expression, appending side
// effects to order->out as needed.
static void
orderexpr(Node **np, Order *order)
{
	Node *n;
	NodeList *mark, *l;
	Type *t;
	int lno;

	n = *np;
	if(n == N)
		return;

	lno = setlineno(n);
	orderinit(n, order);

	switch(n->op) {
	default:
		orderexpr(&n->left, order);
		orderexpr(&n->right, order);
		orderexprlist(n->list, order);
		orderexprlist(n->rlist, order);
		break;
	
	case OADDSTR:
		// Addition of strings turns into a function call.
		// Allocate a temporary to hold the strings.
		// Fewer than 5 strings use direct runtime helpers.
		orderexprlist(n->list, order);
		if(count(n->list) > 5) {
			t = typ(TARRAY);
			t->bound = count(n->list);
			t->type = types[TSTRING];
			n->alloc = ordertemp(t, order, 0);
		}
		break;

	case OINDEXMAP:
		// key must be addressable
		orderexpr(&n->left, order);
		orderexpr(&n->right, order);

		// For x = m[string(k)] where k is []byte, the allocation of
		// backing bytes for the string can be avoided by reusing
		// the []byte backing array. This is a special case that it
		// would be nice to handle more generally, but because
		// there are no []byte-keyed maps, this specific case comes
		// up in important cases in practice. See issue 3512.
		// Nothing can change the []byte we are not copying before
		// the map index, because the map access is going to
		// be forced to happen immediately following this
		// conversion (by the ordercopyexpr a few lines below).
		if(n->etype == 0 && n->right->op == OARRAYBYTESTR)
			n->right->op = OARRAYBYTESTRTMP;

		orderaddrtemp(&n->right, order);
		if(n->etype == 0) {
			// use of value (not being assigned);
			// make copy in temporary.
			n = ordercopyexpr(n, n->type, order, 0);
		}
		break;
	
	case OCONVIFACE:
		// concrete type (not interface) argument must be addressable
		// temporary to pass to runtime.
		orderexpr(&n->left, order);
		if(!isinter(n->left->type))
			orderaddrtemp(&n->left, order);
		break;
	
	case OANDAND:
	case OOROR:
		mark = marktemp(order);
		orderexpr(&n->left, order);
		// Clean temporaries from first branch at beginning of second.
		// Leave them on the stack so that they can be killed in the outer
		// context in case the short circuit is taken.
		l = nil;
		cleantempnopop(mark, order, &l);
		n->right->ninit = concat(l, n->right->ninit);
		orderexprinplace(&n->right, order);
		break;
	
	case OCALLFUNC:
	case OCALLMETH:
	case OCALLINTER:
	case OAPPEND:
	case OCOMPLEX:
		ordercall(n, order);
		n = ordercopyexpr(n, n->type, order, 0);
		break;

	case OCLOSURE:
		if(n->noescape && n->cvars != nil)
			n->alloc = ordertemp(types[TUINT8], order, 0); // walk will fill in correct type
		break;

	case OARRAYLIT:
	case OCALLPART:
		orderexpr(&n->left, order);
		orderexpr(&n->right, order);
		orderexprlist(n->list, order);
		orderexprlist(n->rlist, order);
		if(n->noescape)
			n->alloc = ordertemp(types[TUINT8], order, 0); // walk will fill in correct type
		break;

	case ODDDARG:
		if(n->noescape) {
			// The ddd argument does not live beyond the call it is created for.
			// Allocate a temporary that will be cleaned up when this statement
			// completes. We could be more aggressive and try to arrange for it
			// to be cleaned up when the call completes.
			n->alloc = ordertemp(n->type->type, order, 0);
		}
		break;

	case ORECV:
		orderexpr(&n->left, order);
		n = ordercopyexpr(n, n->type, order, 1);
		break;
	}
	
	lineno = lno;

	*np = n;
}
