blob: b9f2d35ce4dca318ba1087ee3de3f7ff08c422ad [file] [log] [blame]
// 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.
static void
ordercall(Node *n, Order *order, int special)
{
orderexpr(&n->left, order);
ordercallargs(&n->list, order);
if(!special)
orderexpr(&n->right, order); // ODDDARG temp
}
// 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, 0);
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, 0);
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, 1);
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, 0);
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;
}