diff options
Diffstat (limited to 'src/cmd/gc/order.c')
-rw-r--r-- | src/cmd/gc/order.c | 1164 |
1 files changed, 0 insertions, 1164 deletions
diff --git a/src/cmd/gc/order.c b/src/cmd/gc/order.c deleted file mode 100644 index 8e670bdc13..0000000000 --- a/src/cmd/gc/order.c +++ /dev/null @@ -1,1164 +0,0 @@ -// 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) -{ - char s[50]; - - if(debug['W'] > 1) { - snprint(s, sizeof(s), "\nbefore order %S", fn->nname->sym); - dumplist(s, fn->nbody); - } - - 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) { - 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; - } - - fatal("ordersafeexpr %O", n->op); - return nil; // not reached -} - -// 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. -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. -// -// Ordermapassign also inserts these temporaries if needed for -// calling writebarrierfat with a pointer to n->right. -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); - // We call writebarrierfat only for values > 4 pointers long. See walk.c. - if((n->left->op == OINDEXMAP || (needwritebarrier(n->left, n->right) && n->left->type->width > 4*widthptr)) && !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, *typ; - - 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 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 OAS2DOTTYPE: - // Special: use temporary variables to hold result, - // so that assertI2Tetc can take address of temporary. - // No temporary for blank assignment. - t = marktemp(order); - orderexprlist(n->list, order); - orderexpr(&n->rlist->n->left, order); // i in i.(T) - if(isblank(n->list->n)) - order->out = list(order->out, n); - else { - typ = n->rlist->n->type; - tmp1 = ordertemp(typ, order, haspointers(typ)); - order->out = list(order->out, n); - r = nod(OAS, n->list->n, tmp1); - typecheck(&r, Etop); - ordermapassign(r, order); - n->list = list(list1(tmp1), n->list->next->n); - } - cleantemp(t, order); - break; - - case OAS2RECV: - // Special: 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)); - if(!isblank(n->list->next->n)) - tmp2 = ordertemp(n->list->next->n->type, order, 0); - else - 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: - // Mark []byte(str) range expression to reuse string backing storage. - // It is safe because the storage cannot be mutated. - if(n->right->op == OSTRARRAYBYTE) - n->right->op = OSTRARRAYBYTETMP; - 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. - // Doubly special: evaluation order for select is stricter - // than ordinary expressions. Even something like p.c - // has to be hoisted into a temporary, so that it cannot be - // reordered after the channel evaluation for a different - // case (if p were nil, then the timing of the fault would - // give this away). - 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); - if(r->right->left->op != ONAME) - r->right->left = ordercopyexpr(r->right->left, r->right->left->type, order, 0); - - // 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, haslit, hasbyte; - - 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); - } - - // Mark string(byteSlice) arguments to reuse byteSlice backing - // buffer during conversion. String concatenation does not - // memorize the strings for later use, so it is safe. - // However, we can do it only if there is at least one non-empty string literal. - // Otherwise if all other arguments are empty strings, - // concatstrings will return the reference to the temp string - // to the caller. - hasbyte = 0; - haslit = 0; - for(l=n->list; l != nil; l=l->next) { - hasbyte |= l->n->op == OARRAYBYTESTR; - haslit |= l->n->op == OLITERAL && l->n->val.u.sval->len != 0; - } - if(haslit && hasbyte) { - for(l=n->list; l != nil; l=l->next) { - if(l->n->op == OARRAYBYTESTR) - l->n->op = OARRAYBYTESTRTMP; - } - } - break; - - case OCMPSTR: - orderexpr(&n->left, order); - orderexpr(&n->right, order); - // Mark string(byteSlice) arguments to reuse byteSlice backing - // buffer during conversion. String comparison does not - // memorize the strings for later use, so it is safe. - if(n->left->op == OARRAYBYTESTR) - n->left->op = OARRAYBYTESTRTMP; - if(n->right->op == OARRAYBYTESTR) - n->right->op = OARRAYBYTESTRTMP; - 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 OAPPEND: - case OCALLFUNC: - case OCALLINTER: - case OCALLMETH: - case OCAP: - case OCOMPLEX: - case OCOPY: - case OIMAG: - case OLEN: - case OMAKECHAN: - case OMAKEMAP: - case OMAKESLICE: - case ONEW: - case OREAL: - case ORECOVER: - 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: - case ODOTTYPE: - orderexpr(&n->left, order); - n = ordercopyexpr(n, n->type, order, 1); - break; - - case OEQ: - case ONE: - orderexpr(&n->left, order); - orderexpr(&n->right, order); - t = n->left->type; - if(t->etype == TSTRUCT || isfixedarray(t)) { - // for complex comparisons, we need both args to be - // addressable so we can pass them to the runtime. - orderaddrtemp(&n->left, order); - orderaddrtemp(&n->right, order); - } - break; - } - - lineno = lno; - - *np = n; -} |