| // 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; |
| } |