blob: 2cab5fb956943846ae6405db5e4ba60b1a56cbf1 [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.
#include <u.h>
#include <libc.h>
#include "go.h"
static void orderstmt(Node*, NodeList**);
static void orderstmtlist(NodeList*, NodeList**);
static void orderblock(NodeList **l);
static void orderexpr(Node**, NodeList**);
static void orderexprlist(NodeList*, NodeList**);
void
order(Node *fn)
{
orderblock(&fn->nbody);
}
static void
orderstmtlist(NodeList *l, NodeList **out)
{
for(; l; l=l->next)
orderstmt(l->n, out);
}
// Order the block of statements *l onto a new list,
// and then replace *l with that list.
static void
orderblock(NodeList **l)
{
NodeList *out;
out = nil;
orderstmtlist(*l, &out);
*l = out;
}
// Order the side effects in *np and leave them as
// the init list of the final *np.
static void
orderexprinplace(Node **np)
{
Node *n;
NodeList *out;
n = *np;
out = nil;
orderexpr(&n, &out);
addinit(&n, out);
*np = n;
}
// Like orderblock, but applied to a single statement.
static void
orderstmtinplace(Node **np)
{
Node *n;
NodeList *out;
n = *np;
out = nil;
orderstmt(n, &out);
*np = liststmt(out);
}
// Move n's init list to *out.
static void
orderinit(Node *n, NodeList **out)
{
orderstmtlist(n->ninit, out);
n->ninit = nil;
}
// Is the list l actually just f() for a multi-value 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;
}
// n is a multi-value function call. Add t1, t2, .. = n to out
// and return the list t1, t2, ...
static NodeList*
copyret(Node *n, NodeList **out)
{
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, out);
return l2;
}
static void
ordercallargs(NodeList **l, NodeList **out)
{
if(ismulticall(*l)) {
// return f() where f() is multiple values.
*l = copyret((*l)->n, out);
} else {
orderexprlist(*l, out);
}
}
static void
ordercall(Node *n, NodeList **out)
{
orderexpr(&n->left, out);
ordercallargs(&n->list, out);
}
static void
orderstmt(Node *n, NodeList **out)
{
int lno;
NodeList *l;
Node *r;
if(n == N)
return;
lno = setlineno(n);
orderinit(n, out);
switch(n->op) {
default:
fatal("orderstmt %O", n->op);
case OAS2:
case OAS2DOTTYPE:
case OAS2MAPR:
case OAS:
case OASOP:
case OCLOSE:
case OCOPY:
case ODELETE:
case OPANIC:
case OPRINT:
case OPRINTN:
case ORECOVER:
case ORECV:
case OSEND:
orderexpr(&n->left, out);
orderexpr(&n->right, out);
orderexprlist(n->list, out);
orderexprlist(n->rlist, out);
*out = list(*out, n);
break;
case OAS2FUNC:
// Special: avoid copy of func call n->rlist->n.
orderexprlist(n->list, out);
ordercall(n->rlist->n, out);
*out = list(*out, n);
break;
case OAS2RECV:
// Special: avoid copy of receive.
orderexprlist(n->list, out);
orderexpr(&n->rlist->n->left, out); // arg to recv
*out = list(*out, n);
break;
case OBLOCK:
case OEMPTY:
// Special: does not save n onto out.
orderstmtlist(n->list, out);
break;
case OBREAK:
case OCONTINUE:
case ODCL:
case ODCLCONST:
case ODCLTYPE:
case OFALL:
case_OFALL:
case OGOTO:
case OLABEL:
// Special: n->left is not an expression; save as is.
*out = list(*out, n);
break;
case OCALLFUNC:
case OCALLINTER:
case OCALLMETH:
// Special: handle call arguments.
ordercall(n, out);
*out = list(*out, n);
break;
case ODEFER:
case OPROC:
// Special: order arguments to inner call but not call itself.
ordercall(n->left, out);
*out = list(*out, n);
break;
case OFOR:
orderexprinplace(&n->ntest);
orderstmtinplace(&n->nincr);
orderblock(&n->nbody);
*out = list(*out, n);
break;
case OIF:
orderexprinplace(&n->ntest);
orderblock(&n->nbody);
orderblock(&n->nelse);
*out = list(*out, n);
break;
case ORANGE:
orderexpr(&n->right, out);
for(l=n->list; l; l=l->next)
orderexprinplace(&l->n);
orderblock(&n->nbody);
*out = list(*out, n);
break;
case ORETURN:
ordercallargs(&n->list, out);
*out = list(*out, n);
break;
case OSELECT:
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;
if(r == nil)
continue;
switch(r->op) {
case OSELRECV:
case OSELRECV2:
orderexprinplace(&r->left);
orderexprinplace(&r->ntest);
orderexpr(&r->right->left, out);
break;
case OSEND:
orderexpr(&r->left, out);
orderexpr(&r->right, out);
break;
}
}
*out = list(*out, n);
break;
case OSWITCH:
orderexpr(&n->ntest, out);
for(l=n->list; l; l=l->next) {
if(l->n->op != OXCASE)
fatal("order switch case %O", l->n->op);
orderexpr(&l->n->left, &l->n->ninit);
}
*out = list(*out, n);
break;
case OXFALL:
yyerror("fallthrough statement out of place");
n->op = OFALL;
goto case_OFALL;
}
lineno = lno;
}
static void
orderexprlist(NodeList *l, NodeList **out)
{
for(; l; l=l->next)
orderexpr(&l->n, out);
}
static void
orderexpr(Node **np, NodeList **out)
{
Node *n;
int lno;
n = *np;
if(n == N)
return;
lno = setlineno(n);
orderinit(n, out);
switch(n->op) {
default:
orderexpr(&n->left, out);
orderexpr(&n->right, out);
orderexprlist(n->list, out);
orderexprlist(n->rlist, out);
break;
case OANDAND:
case OOROR:
orderexpr(&n->left, out);
orderexprinplace(&n->right);
break;
case OCALLFUNC:
case OCALLMETH:
case OCALLINTER:
ordercall(n, out);
n = copyexpr(n, n->type, out);
break;
case ORECV:
n = copyexpr(n, n->type, out);
break;
}
lineno = lno;
*np = n;
}