mirror of
https://github.com/golang/go
synced 2024-10-04 20:21:22 -06:00
0a55958b52
Fixes test/reorder2.go for all cases tripped up with -lll in 5555072 R=rsc CC=golang-dev https://golang.org/cl/5569069
359 lines
6.2 KiB
C
359 lines
6.2 KiB
C
// 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;
|
|
}
|