diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c index ef6f7e004d6..07cf13bc2f1 100644 --- a/src/cmd/gc/closure.c +++ b/src/cmd/gc/closure.c @@ -255,11 +255,11 @@ walkclosure(Node *func, NodeList **init) clos->left->esc = func->esc; // non-escaping temp to use, if any. // orderexpr did not compute the type; fill it in now. - if(func->left != N) { - func->left->type = clos->left->left->type; - func->left->orig->type = func->left->type; - clos->left->right = func->left; - func->left = N; + if(func->alloc != N) { + func->alloc->type = clos->left->left->type; + func->alloc->orig->type = func->alloc->type; + clos->left->right = func->alloc; + func->alloc = N; } walkexpr(&clos, init); @@ -451,6 +451,14 @@ walkpartialcall(Node *n, NodeList **init) // typecheck will insert a PTRLIT node under CONVNOP, // tag it with escape analysis result. clos->left->esc = n->esc; + // non-escaping temp to use, if any. + // orderexpr did not compute the type; fill it in now. + if(n->alloc != N) { + n->alloc->type = clos->left->left->type; + n->alloc->orig->type = n->alloc->type; + clos->left->right = n->alloc; + n->alloc = N; + } walkexpr(&clos, init); return clos; diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c index 579d7dc08a4..7429e25ecfa 100644 --- a/src/cmd/gc/esc.c +++ b/src/cmd/gc/esc.c @@ -966,9 +966,16 @@ esccall(EscState *e, Node *n) } if(haspointers(t->type)) { if(escassignfromtag(e, t->note, n->escretval, src) == EscNone) { - switch(src->op) { + a = src; + while(a->op == OCONVNOP) + a = a->left; + switch(a->op) { + case OCALLPART: case OCLOSURE: case ODDDARG: + case OARRAYLIT: + case OPTRLIT: + case OSTRUCTLIT: // The callee has already been analyzed, so its arguments have esc tags. // The argument is marked as not escaping at all. // Record that fact so that any temporary used for @@ -978,7 +985,7 @@ esccall(EscState *e, Node *n) // src->esc == EscNone means that src does not escape the current function. // src->noescape = 1 here means that src does not escape this statement // in the current function. - src->noescape = 1; + a->noescape = 1; break; } } diff --git a/src/cmd/gc/order.c b/src/cmd/gc/order.c index c9257f9b60f..5fec73854d4 100644 --- a/src/cmd/gc/order.c +++ b/src/cmd/gc/order.c @@ -20,11 +20,10 @@ // Arrange that receive expressions only appear in direct assignments // x = <-c or as standalone statements <-c, never in larger expressions. -// TODO(rsc): Temporaries are not cleaned in for, if, select, and swtch -// statements. The cleaning needs to be introduced aggressively, so -// that for example a temporary introduced during evaluation of an -// if condition is killed in both the 'if' and 'else' bodies, not delayed -// until after the entire if statement has completed. +// 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. @@ -243,11 +242,11 @@ poptemp(NodeList *mark, Order *order) } } -// Cleantempnopop emits VARKILL instructions for each temporary +// 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) +cleantempnopop(NodeList *mark, Order *order, NodeList **out) { NodeList *l; Node *kill; @@ -255,7 +254,7 @@ cleantempnopop(NodeList *mark, Order *order) for(l=order->temp; l != mark; l=l->next) { kill = nod(OVARKILL, l->n, N); typecheck(&kill, Etop); - order->out = list(order->out, kill); + *out = list(*out, kill); } } @@ -264,7 +263,7 @@ cleantempnopop(NodeList *mark, Order *order) static void cleantemp(NodeList *top, Order *order) { - cleantempnopop(top, order); + cleantempnopop(top, order, &order->out); poptemp(top, order); } @@ -282,45 +281,37 @@ 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 *TODO) +orderexprinplace(Node **np, Order *outer) { Node *n; + NodeList **lp; Order order; - // TODO(rsc): Decide how much of the passed-in order to use. - // For example, should the temporaries created during the - // ordering of expr be added onto the caller's order temp list - // for freeing? Probably. - USED(TODO); - n = *np; memset(&order, 0, sizeof order); orderexpr(&n, &order); addinit(&n, order.out); - *np = n; -} - -// Orderexprtolist orders the side effects in *np and -// appends them to *out. -static void -orderexprtolist(Node **np, NodeList **out) -{ - Node *n; - Order order; - n = *np; - memset(&order, 0, sizeof order); - orderexpr(&n, &order); - *out = concat(*out, 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; } @@ -331,10 +322,13 @@ 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); } @@ -513,12 +507,15 @@ orderstmt(Node *n, Order *order) 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 OPANIC: case OPRINT: case OPRINTN: case ORECOVER: @@ -668,21 +665,47 @@ orderstmt(Node *n, Order *order) break; case OFOR: - // TODO(rsc): Clean temporaries. + // Clean temporaries from condition evaluation at + // beginning of loop body and after for statement. + t = marktemp(order); orderexprinplace(&n->ntest, order); - orderstmtinplace(&n->nincr); + 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: - // TODO(rsc): Clean temporaries. + // 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. @@ -722,8 +745,8 @@ orderstmt(Node *n, Order *order) // For maps tmp is just one word so it hardly matters. r = n->right; n->right = ordercopyexpr(r, r->type, order, 0); - // temp is the iterator instead of the map value. - n->left = ordertemp(hiter(n->right->type), order, 1); + // n->alloc is the temp for the iterator. + n->alloc = ordertemp(types[TUINT8], order, 1); break; } for(l=n->list; l; l=l->next) @@ -739,28 +762,84 @@ orderstmt(Node *n, Order *order) break; case OSELECT: - // TODO(rsc): Clean temporaries. + // 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) { case OSELRECV: case OSELRECV2: - orderexprinplace(&r->left, order); - orderexprinplace(&r->ntest, order); - orderexprtolist(&r->right->left, &l->n->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; + 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; + 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: - orderexprtolist(&r->left, &l->n->ninit); - orderexprtolist(&r->right, &l->n->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: @@ -774,7 +853,14 @@ orderstmt(Node *n, Order *order) break; case OSWITCH: - // TODO(rsc): Clean temporaries. + // 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) @@ -783,6 +869,7 @@ orderstmt(Node *n, Order *order) orderblock(&l->n->nbody); } order->out = list(order->out, n); + cleantemp(t, order); break; } @@ -812,6 +899,7 @@ static void orderexpr(Node **np, Order *order) { Node *n; + NodeList *mark, *l; Type *t; int lno; @@ -839,7 +927,7 @@ orderexpr(Node **np, Order *order) t = typ(TARRAY); t->bound = count(n->list); t->type = types[TSTRING]; - n->left = ordertemp(t, order, 0); + n->alloc = ordertemp(t, order, 0); } break; @@ -855,9 +943,24 @@ orderexpr(Node **np, Order *order) } 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; @@ -871,21 +974,27 @@ orderexpr(Node **np, Order *order) break; case OCLOSURE: - if(n->noescape && n->cvars != nil) { - t = typ(TARRAY); - t->type = types[TUNSAFEPTR]; - t->bound = 1+count(n->cvars); - n->left = ordertemp(t, order, 0); - } + 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->left = ordertemp(n->type, order, 0); + n->alloc = ordertemp(n->type, order, 0); } break; diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c index 0cbb6a6f6e1..45aa521b3a8 100644 --- a/src/cmd/gc/range.c +++ b/src/cmd/gc/range.c @@ -186,7 +186,8 @@ walkrange(Node *n) // we only use a once, so no copy needed. ha = a; th = hiter(t); - hit = n->left; + hit = n->alloc; + hit->type = th; n->left = N; keyname = newname(th->type->sym); // depends on layout of iterator struct. See reflect.c:hiter valname = newname(th->type->down->sym); // ditto diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c index e9b9f785882..58a12067493 100644 --- a/src/cmd/gc/select.c +++ b/src/cmd/gc/select.c @@ -69,6 +69,7 @@ typecheckselect(Node *sel) n->op = OSELRECV2; n->left = n->list->n; n->ntest = n->list->next->n; + n->list = nil; n->right = n->rlist->n; n->rlist = nil; break; @@ -94,7 +95,7 @@ void walkselect(Node *sel) { int lno, i; - Node *n, *r, *a, *tmp, *var, *cas, *dflt, *ch; + Node *n, *r, *a, *var, *cas, *dflt, *ch; NodeList *l, *init; if(sel->list == nil && sel->xoffset != 0) @@ -112,7 +113,7 @@ walkselect(Node *sel) // optimization: one-case select: single op. // TODO(rsc): Reenable optimization once order.c can handle it. // golang.org/issue/7672. - if(0 && i == 1) { + if(i == 1) { cas = sel->list->n; setlineno(cas); l = cas->ninit; @@ -125,32 +126,34 @@ walkselect(Node *sel) fatal("select %O", n->op); case OSEND: - ch = cheapexpr(n->left, &l); - n->left = ch; + // ok already + ch = n->left; break; case OSELRECV: - r = n->right; - ch = cheapexpr(r->left, &l); - r->left = ch; - + ch = n->right->left; + Selrecv1: if(n->left == N) - n = r; - else { - n = nod(OAS, n->left, r); - typecheck(&n, Etop); - } + n = n->right; + else + n->op = OAS; break; case OSELRECV2: - r = n->right; - ch = cheapexpr(r->left, &l); - r->left = ch; - - a = nod(OAS2, N, N); - a->list = n->list; - a->rlist = list1(n->right); - n = a; + ch = n->right->left; + if(n->ntest == N) + goto Selrecv1; + if(n->left == N) { + typecheck(&nblank, Erv | Easgn); + n->left = nblank; + } + n->op = OAS2; + n->list = list(list1(n->left), n->ntest); + n->rlist = list1(n->right); + n->right = N; + n->left = N; + n->ntest = N; + n->typecheck = 0; typecheck(&n, Etop); break; } @@ -168,7 +171,7 @@ walkselect(Node *sel) goto out; } - // introduce temporary variables for OSELRECV where needed. + // convert case value arguments to addresses. // this rewrite is used by both the general code and the next optimization. for(l=sel->list; l; l=l->next) { cas = l->n; @@ -177,75 +180,24 @@ walkselect(Node *sel) if(n == N) continue; switch(n->op) { + case OSEND: + n->right = nod(OADDR, n->right, N); + typecheck(&n->right, Erv); + break; case OSELRECV: case OSELRECV2: - ch = n->right->left; - - // If we can use the address of the target without - // violating addressability or order of operations, do so. - // Otherwise introduce a temporary. - // Also introduce a temporary for := variables that escape, - // so that we can delay the heap allocation until the case - // is selected. + if(n->op == OSELRECV2 && n->ntest == N) + n->op = OSELRECV; if(n->op == OSELRECV2) { - if(n->ntest == N || isblank(n->ntest)) - n->ntest = nodnil(); - else if(n->ntest->op == ONAME && - (!n->colas || (n->ntest->class&PHEAP) == 0) && - convertop(types[TBOOL], n->ntest->type, nil) == OCONVNOP) { - n->ntest = nod(OADDR, n->ntest, N); - n->ntest->etype = 1; // pointer does not escape - typecheck(&n->ntest, Erv); - } else { - tmp = temp(types[TBOOL]); - a = nod(OADDR, tmp, N); - a->etype = 1; // pointer does not escape - typecheck(&a, Erv); - r = nod(OAS, n->ntest, tmp); - typecheck(&r, Etop); - cas->nbody = concat(list1(r), cas->nbody); - n->ntest = a; - } + n->ntest = nod(OADDR, n->ntest, N); + typecheck(&n->ntest, Erv); } - - if(n->left == N || isblank(n->left)) + if(n->left == N) n->left = nodnil(); - else if(n->left->op == ONAME && - (!n->colas || (n->left->class&PHEAP) == 0) && - convertop(ch->type->type, n->left->type, nil) == OCONVNOP) { - if(n->colas && haspointers(ch->type->type)) { - r = nod(OAS, n->left, N); - typecheck(&r, Etop); - sel->ninit = concat(sel->ninit, list1(r)); - } + else { n->left = nod(OADDR, n->left, N); - n->left->etype = 1; // pointer does not escape typecheck(&n->left, Erv); - if(!eqtype(ch->type->type, n->left->type->type)) { - n->left = nod(OCONVNOP, n->left, N); - n->left->type = ptrto(ch->type->type); - n->left->typecheck = 1; - } - } else { - tmp = temp(ch->type->type); - a = nod(OADDR, tmp, N); - if(haspointers(ch->type->type)) { - // clear tmp for garbage collector, because the recv - // must execute with tmp appearing to be live. - r = nod(OAS, tmp, N); - typecheck(&r, Etop); - sel->ninit = concat(sel->ninit, list1(r)); - } - a->etype = 1; // pointer does not escape - typecheck(&a, Erv); - r = nod(OAS, n->left, tmp); - typecheck(&r, Etop); - cas->nbody = concat(list1(r), cas->nbody); - n->left = a; - } - - cas->nbody = concat(n->ninit, cas->nbody); - n->ninit = nil; + } break; } } @@ -269,29 +221,17 @@ walkselect(Node *sel) fatal("select %O", n->op); case OSEND: - // if c != nil && selectnbsend(c, v) { body } else { default body } - ch = cheapexpr(n->left, &r->ninit); - a = n->right; - a = assignconv(a, ch->type->type, "select chan send"); - walkexpr(&a, &r->ninit); - if(islvalue(a)) { - a = nod(OADDR, a, N); - } else { - var = temp(a->type); - tmp = nod(OAS, var, a); - typecheck(&tmp, Etop); - r->ninit = list(r->ninit, tmp); - a = nod(OADDR, var, N); - } + // if selectnbsend(c, v) { body } else { default body } + ch = n->left; r->ntest = mkcall1(chanfn("selectnbsend", 2, ch->type), - types[TBOOL], &r->ninit, typename(ch->type), ch, a); + types[TBOOL], &r->ninit, typename(ch->type), ch, n->right); break; case OSELRECV: // if c != nil && selectnbrecv(&v, c) { body } else { default body } r = nod(OIF, N, N); r->ninit = cas->ninit; - ch = cheapexpr(n->right->left, &r->ninit); + ch = n->right->left; r->ntest = mkcall1(chanfn("selectnbrecv", 2, ch->type), types[TBOOL], &r->ninit, typename(ch->type), n->left, ch); break; @@ -300,7 +240,7 @@ walkselect(Node *sel) // if c != nil && selectnbrecv2(&v, c) { body } else { default body } r = nod(OIF, N, N); r->ninit = cas->ninit; - ch = cheapexpr(n->right->left, &r->ninit); + ch = n->right->left; r->ntest = mkcall1(chanfn("selectnbrecv2", 2, ch->type), types[TBOOL], &r->ninit, typename(ch->type), n->left, n->ntest, ch); break; @@ -344,18 +284,6 @@ walkselect(Node *sel) case OSEND: // selectsend(sel *byte, hchan *chan any, elem *any) (selected bool); - n->left = localexpr(safeexpr(n->left, &r->ninit), n->left->type, &r->ninit); - n->right = localexpr(n->right, n->left->type->type, &r->ninit); - n->right = nod(OADDR, n->right, N); - n->right->etype = 1; // pointer does not escape - typecheck(&n->right, Erv); - // cast to appropriate type if necessary. - if(!eqtype(n->right->type->type, n->left->type->type) && - assignop(n->right->type->type, n->left->type->type, nil) == OCONVNOP) { - n->right = nod(OCONVNOP, n->right, N); - n->right->type = ptrto(n->left->type->type); - n->right->typecheck = 1; - } r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL], &r->ninit, var, n->left, n->right); break; diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c index b49222ec64a..83d6bad6843 100644 --- a/src/cmd/gc/sinit.c +++ b/src/cmd/gc/sinit.c @@ -767,14 +767,15 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) vauto = temp(ptrto(t)); // set auto to point at new temp or heap (3 assign) - if(n->left != N) { + if(n->alloc != N) { // temp allocated during order.c for dddarg + n->alloc->type = t; if(vstat == N) { - a = nod(OAS, n->left, N); + a = nod(OAS, n->alloc, N); typecheck(&a, Etop); *init = list(*init, a); // zero new temp } - a = nod(OADDR, n->left, N); + a = nod(OADDR, n->alloc, N); } else if(n->esc == EscNone) { a = temp(t); if(vstat == N) { diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index f9746f0278c..feb3af702c3 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -2101,7 +2101,7 @@ cheapexpr(Node *n, NodeList **init) Node* localexpr(Node *n, Type *t, NodeList **init) { - if(n->op == ONAME && !n->addrtaken && + if(n->op == ONAME && (!n->addrtaken || strncmp(n->sym->name, "autotmp_", 8) == 0) && (n->class == PAUTO || n->class == PPARAM || n->class == PPARAMOUT) && convertop(n->type, t, nil) == OCONVNOP) return n; diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index 876d95530b2..4137a79555a 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -912,15 +912,15 @@ walkexpr(Node **np, NodeList **init) ll = list(ll, n->left); } else { // regular types are passed by reference to avoid C vararg calls - if(islvalue(n->left)) { + // orderexpr arranged for n->left to be a temporary for all + // the conversions it could see. comparison of an interface + // with a non-interface, especially in a switch on interface value + // with non-interface cases, is not visible to orderstmt, so we + // have to fall back on allocating a temp here. + if(islvalue(n->left)) ll = list(ll, nod(OADDR, n->left, N)); - } else { - var = temp(n->left->type); - n1 = nod(OAS, var, n->left); - typecheck(&n1, Etop); - *init = list(*init, n1); - ll = list(ll, nod(OADDR, var, N)); - } + else + ll = list(ll, nod(OADDR, copyexpr(n->left, n->left->type, init), N)); } argtype(fn, n->left->type); argtype(fn, n->type); @@ -1556,7 +1556,7 @@ mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, Nod } else { n = nod(OCOMPLIT, N, typenod(tslice)); if(ddd != nil) - n->left = ddd->left; // temporary to use + n->alloc = ddd->alloc; // temporary to use n->list = lr0; n->esc = esc; typecheck(&n, Erv); @@ -2533,7 +2533,7 @@ addstr(Node *n, NodeList **init) t->type = types[TSTRING]; t->bound = -1; slice = nod(OCOMPLIT, N, typenod(t)); - slice->left = n->left; + slice->alloc = n->alloc; slice->list = args; slice->esc = EscNone; args = list1(slice); diff --git a/test/live.go b/test/live.go index 02ff02298a5..43b3c3e4c5f 100644 --- a/test/live.go +++ b/test/live.go @@ -423,3 +423,117 @@ func f30(b bool) { print(p) // ERROR "live at call to printpointer: autotmp_[0-9]+ autotmp_[0-9]+$" } } + +// conversion to interface should not leave temporary behind + +func f31(b1, b2, b3 bool) { + if b1 { + g31("a") // ERROR "live at call to convT2E: autotmp_[0-9]+$" "live at call to g31: autotmp_[0-9]+$" + } + if b2 { + h31("b") // ERROR "live at call to new: autotmp_[0-9]+$" "live at call to convT2E: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to h31: autotmp_[0-9]+$" + } + if b3 { + panic("asdf") // ERROR "live at call to convT2E: autotmp_[0-9]+$" "live at call to panic: autotmp_[0-9]+$" + } + print(b3) +} + +func g31(interface{}) +func h31(...interface{}) + +// non-escaping partial functions passed to function call should die on return + +type T32 int + +func (t *T32) Inc() { // ERROR "live at entry" + *t++ +} + +var t32 T32 + +func f32(b bool) { + if b { + call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$" + } + call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$" + call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$" +} + +//go:noescape +func call32(func()) + +// temporaries introduced during if conditions and && || expressions +// should die once the condition has been acted upon. + +var m33 map[interface{}]int + +func f33() { + if m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + println() + return + } else { + println() + } + println() +} + +func f34() { + if m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + println() + return + } + println() +} + +func f35() { + if m33[nil] == 0 && m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + println() + return + } + println() +} + +func f36() { + if m33[nil] == 0 || m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + println() + return + } + println() +} + +func f37() { + if (m33[nil] == 0 || m33[nil] == 0) && m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$" + println() + return + } + println() +} + +// select temps should disappear in the case bodies + +var c38 chan string + +func fc38() chan string +func fi38(int) *string +func fb38() *bool + +func f38(b bool) { + // we don't care what temps are printed on the lines with output. + // we care that the println lines have no live variables + // and therefore no output. + if b { + select { // ERROR "live at call" + case <-fc38(): // ERROR "live at call" + println() + case fc38() <- *fi38(1): // ERROR "live at call" + println() + case *fi38(2) = <-fc38(): // ERROR "live at call" + println() + case *fi38(3), *fb38() = <-fc38(): // ERROR "live at call" + println() + } + println() + } + println() +}