]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/gc: shorten more temporary lifetimes
authorRuss Cox <rsc@golang.org>
Wed, 2 Apr 2014 00:02:54 +0000 (20:02 -0400)
committerRuss Cox <rsc@golang.org>
Wed, 2 Apr 2014 00:02:54 +0000 (20:02 -0400)
1. In functions with heap-allocated result variables or with
defer statements, the return sequence requires more than
just a single RET instruction. There is an optimization that
arranges for all returns to jump to a single copy of the return
epilogue in this case. Unfortunately, that optimization is
fundamentally incompatible with PC-based liveness information:
it takes PCs at many different points in the function and makes
them all land at one PC, making the combined liveness information
at that target PC a mess. Disable this optimization, so that each
return site gets its own copy of the 'call deferreturn' and the
copying of result variables back from the heap.
This removes quite a few spurious 'ambiguously live' variables.

2. Let orderexpr allocate temporaries that are passed by address
to a function call and then die on return, so that we can arrange
an appropriate VARKILL.

2a. Do this for ... slices.

2b. Do this for closure structs.

2c. Do this for runtime.concatstring, which is the implementation
of large string additions. Change representation of OADDSTR to
an explicit list in typecheck to avoid reconstructing list in both
walk and order.

3. Let orderexpr allocate the temporary variable copies used for
range loops, so that they can be killed when the loop is over.
Similarly, let it allocate the temporary holding the map iterator.

CL 81940043 reduced the number of ambiguously live temps
in the godoc binary from 860 to 711.

This CL reduces the number to 121. Still more to do, but another
good checkpoint.

Update #7345

LGTM=khr
R=khr
CC=golang-codereviews
https://golang.org/cl/83090046

15 files changed:
src/cmd/5g/ggen.c
src/cmd/6g/ggen.c
src/cmd/8g/ggen.c
src/cmd/gc/closure.c
src/cmd/gc/const.c
src/cmd/gc/esc.c
src/cmd/gc/fmt.c
src/cmd/gc/go.h
src/cmd/gc/order.c
src/cmd/gc/pgen.c
src/cmd/gc/range.c
src/cmd/gc/sinit.c
src/cmd/gc/typecheck.c
src/cmd/gc/walk.c
test/live.go

index c81de1fd87d6374d2134a8de3d7304790f436202..c85f680cdad1c233bbadb9df1efe1af6944b4774 100644 (file)
@@ -256,7 +256,9 @@ ginscall(Node *f, int proc)
                        nodconst(&con, types[TINT32], 0);
                        p = gins(ACMP, &con, N);
                        p->reg = 0;
-                       patch(gbranch(ABNE, T, -1), retpc);
+                       p = gbranch(ABEQ, T, +1);
+                       cgen_ret(N);
+                       patch(p, pc);
                }
                break;
        }
index 9465f4d0eafd9ec1d05ad2242ca1bbc6b6067ac6..6b159e2e37e29e716bc2dc494adcaa863d2840c8 100644 (file)
@@ -232,7 +232,9 @@ ginscall(Node *f, int proc)
                if(proc == 2) {
                        nodreg(&reg, types[TINT64], D_AX);
                        gins(ATESTQ, &reg, &reg);
-                       patch(gbranch(AJNE, T, -1), retpc);
+                       p = gbranch(AJEQ, T, +1);
+                       cgen_ret(N);
+                       patch(p, pc);
                }
                break;
        }
@@ -432,13 +434,13 @@ cgen_ret(Node *n)
 {
        Prog *p;
 
-       genlist(n->list);               // copy out args
-       if(hasdefer || curfn->exit) {
-               gjmp(retpc);
-               return;
-       }
+       if(n != N)
+               genlist(n->list);               // copy out args
+       if(hasdefer)
+               ginscall(deferreturn, 0);
+       genlist(curfn->exit);
        p = gins(ARET, N, N);
-       if(n->op == ORETJMP) {
+       if(n != N && n->op == ORETJMP) {
                p->to.type = D_EXTERN;
                p->to.sym = linksym(n->left->sym);
        }
index afe80316b493a88963ee8f990d300db7fd5de92b..2ece1881280497e73e297b2d8923850796cc7e1a 100644 (file)
@@ -259,7 +259,9 @@ ginscall(Node *f, int proc)
                if(proc == 2) {
                        nodreg(&reg, types[TINT64], D_AX);
                        gins(ATESTL, &reg, &reg);
-                       patch(gbranch(AJNE, T, -1), retpc);
+                       p = gbranch(AJEQ, T, +1);
+                       cgen_ret(N);
+                       patch(p, pc);
                }
                break;
        }
index ee2750b5827f994e1df8d76aac220ced535a69f8..ef6f7e004d619ad1d82ecc2344309296f7cbc5c8 100644 (file)
@@ -253,6 +253,14 @@ walkclosure(Node *func, NodeList **init)
        // typecheck will insert a PTRLIT node under CONVNOP,
        // tag it with escape analysis result.
        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;
+       }
        walkexpr(&clos, init);
 
        return clos;
index a725ea971cd3f8bb5b2bd9ccec8a82f9a2a61a8f..cfd81e86f5987281e3b7ad6db13854267ec5924f 100644 (file)
@@ -521,6 +521,7 @@ evconst(Node *n)
        int wl, wr, lno, et;
        Val v, rv;
        Mpint b;
+       NodeList *l1, *l2;
 
        // pick off just the opcodes that can be
        // constant evaluated.
@@ -528,7 +529,6 @@ evconst(Node *n)
        default:
                return;
        case OADD:
-       case OADDSTR:
        case OAND:
        case OANDAND:
        case OANDNOT:
@@ -559,6 +559,47 @@ evconst(Node *n)
                if(!okforconst[n->type->etype] && n->type->etype != TNIL)
                        return;
                break;
+       
+       case OADDSTR:
+               // merge adjacent constants in the argument list.
+               for(l1=n->list; l1 != nil; l1= l1->next) {
+                       if(isconst(l1->n, CTSTR) && l1->next != nil && isconst(l1->next->n, CTSTR)) {
+                               l2 = l1;
+                               len = 0;
+                               while(l2 != nil && isconst(l2->n, CTSTR)) {
+                                       nr = l2->n;
+                                       len += nr->val.u.sval->len;
+                                       l2 = l2->next;
+                               }
+                               // merge from l1 up to but not including l2
+                               str = mal(sizeof(*str) + len);
+                               str->len = len;
+                               len = 0;
+                               l2 = l1;
+                               while(l2 != nil && isconst(l2->n, CTSTR)) {
+                                       nr = l2->n;
+                                       memmove(str->s+len, nr->val.u.sval->s, nr->val.u.sval->len);
+                                       len += nr->val.u.sval->len;
+                                       l2 = l2->next;
+                               }
+                               nl = nod(OXXX, N, N);
+                               *nl = *l1->n;
+                               nl->orig = nl;
+                               nl->val.ctype = CTSTR;
+                               nl->val.u.sval = str;
+                               l1->n = nl;
+                               l1->next = l2;
+                       }
+               }
+               // fix list end pointer.
+               for(l2=n->list; l2 != nil; l2=l2->next)
+                       n->list->end = l2;
+               // collapse single-constant list to single constant.
+               if(count(n->list) == 1 && isconst(n->list->n, CTSTR)) {
+                       n->op = OLITERAL;
+                       n->val = n->list->n->val;
+               }
+               return;
        }
 
        nl = n->left;
@@ -861,15 +902,6 @@ evconst(Node *n)
                if(cmpslit(nl, nr) > 0)
                        goto settrue;
                goto setfalse;
-       case TUP(OADDSTR, CTSTR):
-               len = v.u.sval->len + rv.u.sval->len;
-               str = mal(sizeof(*str) + len);
-               str->len = len;
-               memcpy(str->s, v.u.sval->s, v.u.sval->len);
-               memcpy(str->s+v.u.sval->len, rv.u.sval->s, rv.u.sval->len);
-               str->len = len;
-               v.u.sval = str;
-               break;
 
        case TUP(OOROR, CTBOOL):
                if(v.u.bval || rv.u.bval)
index 5a1a9ed21cff8306715fd5a280d03f0fa8d927d2..579d7dc08a45b3fc8cda4d48de0928cd0d27671d 100644 (file)
@@ -811,24 +811,29 @@ escassign(EscState *e, Node *dst, Node *src)
        lineno = lno;
 }
 
-static void
+static int
 escassignfromtag(EscState *e, Strlit *note, NodeList *dsts, Node *src)
 {
-       int em;
+       int em, em0;
        
        em = parsetag(note);
-       
+
        if(em == EscUnknown) {
                escassign(e, &e->theSink, src);
-               return;
+               return em;
        }
-               
+       
+       if(em == EscNone)
+               return em;
+
+       em0 = em;
        for(em >>= EscBits; em && dsts; em >>= 1, dsts=dsts->next)
                if(em & 1)
                        escassign(e, dsts->n, src);
 
        if (em != 0 && dsts == nil)
                fatal("corrupt esc tag %Z or messed up escretval list\n", note);
+       return em0;
 }
 
 // This is a bit messier than fortunate, pulled out of esc's big
@@ -875,7 +880,7 @@ esccall(EscState *e, Node *n)
                if(a->type->etype == TSTRUCT && a->type->funarg) // f(g()).
                        ll = a->escretval;
        }
-                       
+
        if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype && fn->defn->esc < EscFuncTagged) {
                // function in same mutually recursive group.  Incorporate into flow graph.
 //             print("esc local fn: %N\n", fn->ntype);
@@ -895,6 +900,9 @@ esccall(EscState *e, Node *n)
                        if(lr->n->isddd && !n->isddd) {
                                // Introduce ODDDARG node to represent ... allocation.
                                src = nod(ODDDARG, N, N);
+                               src->type = typ(TARRAY);
+                               src->type->type = lr->n->type->type;
+                               src->type->bound = count(ll);
                                src->escloopdepth = e->loopdepth;
                                src->lineno = n->lineno;
                                src->esc = EscNone;  // until we find otherwise
@@ -949,12 +957,32 @@ esccall(EscState *e, Node *n)
                        src = nod(ODDDARG, N, N);
                        src->escloopdepth = e->loopdepth;
                        src->lineno = n->lineno;
+                       src->type = typ(TARRAY);
+                       src->type->type = t->type->type;
+                       src->type->bound = count(ll);
                        src->esc = EscNone;  // until we find otherwise
                        e->noesc = list(e->noesc, src);
                        n->right = src;
                }
-               if(haspointers(t->type))
-                       escassignfromtag(e, t->note, n->escretval, src);
+               if(haspointers(t->type)) {
+                       if(escassignfromtag(e, t->note, n->escretval, src) == EscNone) {
+                               switch(src->op) {
+                               case OCLOSURE:
+                               case ODDDARG:
+                                       // 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
+                                       // synthesizing this expression can be reclaimed when
+                                       // the function returns.
+                                       // This 'noescape' is even stronger than the usual esc == EscNone.
+                                       // 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;
+                                       break;
+                               }
+                       }
+               }
                if(src != ll->n)
                        break;
                t = t->down;
index bffe8dfc7e69de04e67f0b447fa519d15e07a4ea..b5f8a834f1620e55b5f9ebfd6601303dc0f85fb1 100644 (file)
@@ -1339,7 +1339,6 @@ exprfmt(Fmt *f, Node *n, int prec)
 
        // Binary
        case OADD:
-       case OADDSTR:
        case OAND:
        case OANDAND:
        case OANDNOT:
@@ -1364,6 +1363,14 @@ exprfmt(Fmt *f, Node *n, int prec)
                exprfmt(f, n->right, nprec+1);
                return 0;
 
+       case OADDSTR:
+               for(l=n->list; l; l=l->next) {
+                       if(l != n->list)
+                               fmtprint(f, " + ");
+                       exprfmt(f, l->n, nprec);
+               }
+               return 0;
+
        case OCMPSTR:
        case OCMPIFACE:
                exprfmt(f, n->left, nprec);
index 6e2cae7320d0ad47b65c94442c7ad64699073f9c..bd2b38d88e615d0047f10a1af1e4a89ed4dd63fa 100644 (file)
@@ -1470,7 +1470,6 @@ EXTERN    Prog*   continpc;
 EXTERN Prog*   breakpc;
 EXTERN Prog*   pc;
 EXTERN Prog*   firstpc;
-EXTERN Prog*   retpc;
 
 EXTERN Node*   nodfp;
 EXTERN int     disable_checknil;
index bdf94a4469aae163e1a9fc5533615fae40899598..c9257f9b60f832f34837fdc79e52a65bf84ab8e4 100644 (file)
@@ -417,10 +417,12 @@ ordercallargs(NodeList **l, Order *order)
 // Ordercall orders the call expression n.
 // n->op is  OCALLMETH/OCALLFUNC/OCALLINTER.
 static void
-ordercall(Node *n, Order *order)
+ordercall(Node *n, Order *order, int special)
 {
        orderexpr(&n->left, order);
        ordercallargs(&n->list, order);
+       if(!special)
+               orderexpr(&n->right, order); // ODDDARG temp
 }
 
 // Ordermapassign appends n to order->out, introducing temporaries
@@ -547,7 +549,6 @@ orderstmt(Node *n, Order *order)
                // a map index expression.
                t = marktemp(order);
                orderexpr(&n->left, order);
-               orderexpr(&n->right, order);
                n->left = ordersafeexpr(n->left, order);
                tmp1 = treecopy(n->left);
                if(tmp1->op == OINDEXMAP)
@@ -555,6 +556,7 @@ orderstmt(Node *n, Order *order)
                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);
@@ -577,7 +579,7 @@ orderstmt(Node *n, Order *order)
                // Special: avoid copy of func call n->rlist->n.
                t = marktemp(order);
                orderexprlist(n->list, order);
-               ordercall(n->rlist->n, order);
+               ordercall(n->rlist->n, order, 0);
                ordermapassign(n, order);
                cleantemp(t, order);
                break;
@@ -628,7 +630,7 @@ orderstmt(Node *n, Order *order)
        case OCALLMETH:
                // Special: handle call arguments.
                t = marktemp(order);
-               ordercall(n, order);
+               ordercall(n, order, 0);
                order->out = list(order->out, n);
                cleantemp(t, order);
                break;
@@ -649,7 +651,7 @@ orderstmt(Node *n, Order *order)
                        poptemp(t1, order);
                        break;
                default:
-                       ordercall(n->left, order);
+                       ordercall(n->left, order, 1);
                        break;
                }
                order->out = list(order->out, n);
@@ -682,12 +684,53 @@ orderstmt(Node *n, Order *order)
                break;
 
        case ORANGE:
-               // TODO(rsc): Clean temporaries.
+               // 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:
+                       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);
+                       // temp is the iterator instead of the map value.
+                       n->left = ordertemp(hiter(n->right->type), 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:
@@ -769,6 +812,7 @@ static void
 orderexpr(Node **np, Order *order)
 {
        Node *n;
+       Type *t;
        int lno;
 
        n = *np;
@@ -786,6 +830,19 @@ orderexpr(Node **np, Order *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->left = ordertemp(t, order, 0);
+               }
+               break;
+
        case OINDEXMAP:
                // key must be addressable
                orderexpr(&n->left, order);
@@ -809,10 +866,29 @@ orderexpr(Node **np, Order *order)
        case OCALLINTER:
        case OAPPEND:
        case OCOMPLEX:
-               ordercall(n, order);
+               ordercall(n, order, 0);
                n = ordercopyexpr(n, n->type, order, 0);
                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);
+               }
+               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);
+               }
+               break;
+
        case ORECV:
                n = ordercopyexpr(n, n->type, order, 1);
                break;
index 0d1901776860075dadb29b5386818fb7d5b6bafd..fec4aa5f1400abd1894669ac8790980ee365ec39 100644 (file)
@@ -135,7 +135,7 @@ compile(Node *fn)
 {
        Plist *pl;
        Node nod1, *n;
-       Prog *ptxt, *p, *p1;
+       Prog *ptxt, *p;
        int32 lno;
        Type *t;
        Iter save;
@@ -256,14 +256,6 @@ compile(Node *fn)
        }
 
        genlist(curfn->enter);
-
-       retpc = nil;
-       if(hasdefer || curfn->exit) {
-               p1 = gjmp(nil);
-               retpc = gjmp(nil);
-               patch(p1, pc);
-       }
-
        genlist(curfn->nbody);
        gclean();
        checklabels();
@@ -275,18 +267,15 @@ compile(Node *fn)
        if(curfn->type->outtuple != 0)
                ginscall(throwreturn, 0);
 
-       if(retpc)
-               patch(retpc, pc);
        ginit();
+       // TODO: Determine when the final cgen_ret can be omitted. Perhaps always?
+       cgen_ret(nil);
        if(hasdefer) {
-               ginscall(deferreturn, 0);
                // deferreturn pretends to have one uintptr argument.
                // Reserve space for it so stack scanner is happy.
                if(maxarg < widthptr)
                        maxarg = widthptr;
        }
-       if(curfn->exit)
-               genlist(curfn->exit);
        gclean();
        if(nerrors != 0)
                goto ret;
index ba5bd9709d0512b7af0537f1b5f0ac63f6edc9f1..0cbb6a6f6e115810fd5b686b38b213b53eed5aae 100644 (file)
@@ -122,34 +122,23 @@ walkrange(Node *n)
 
        a = n->right;
        lno = setlineno(a);
-       if(t->etype == TSTRING && !eqtype(t, types[TSTRING])) {
-               a = nod(OCONV, n->right, N);
-               a->type = types[TSTRING];
-       }
 
        v1 = n->list->n;
        v2 = N;
-       if(n->list->next)
+       if(n->list->next && !isblank(n->list->next->n))
                v2 = n->list->next->n;
        // n->list has no meaning anymore, clear it
        // to avoid erroneous processing by racewalk.
        n->list = nil;
        hv2 = N;
 
-       if(v2 == N && t->etype == TARRAY) {
-               // will have just one reference to argument.
-               // no need to make a potentially expensive copy.
-               ha = a;
-       } else {
-               ha = temp(a->type);
-               init = list(init, nod(OAS, ha, a));
-       }
-
        switch(t->etype) {
        default:
                fatal("walkrange");
 
        case TARRAY:
+               // orderstmt arranged for a copy of the array/slice variable if needed.
+               ha = a;
                hv1 = temp(types[TINT]);
                hn = temp(types[TINT]);
                hp = nil;
@@ -193,10 +182,12 @@ walkrange(Node *n)
                break;
 
        case TMAP:
-               // allocate an iterator state structure on the stack
+               // orderstmt allocated the iterator for us.
+               // we only use a once, so no copy needed.
+               ha = a;
                th = hiter(t);
-               hit = temp(th);
-               init = list(init, nod(OAS, hit, N));
+               hit = n->left;
+               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
 
@@ -226,6 +217,10 @@ walkrange(Node *n)
                break;
 
        case TCHAN:
+               // orderstmt arranged for a copy of the channel variable.
+               ha = a;
+               n->ntest = N;
+               
                hv1 = temp(t->type);
                if(haspointers(t->type))
                        init = list(init, nod(OAS, hv1, N));
@@ -241,6 +236,9 @@ walkrange(Node *n)
                break;
 
        case TSTRING:
+               // orderstmt arranged for a copy of the string variable.
+               ha = a;
+
                ohv1 = temp(types[TINT]);
 
                hv1 = temp(types[TINT]);
index bfb8eb8e66d9c2bcd41e6ba7ee204e4348a931ef..b49222ec64a9a5f8bf3e7e23b6977cb4b9f0e095 100644 (file)
@@ -767,11 +767,23 @@ 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->esc == EscNone) {
-               a = nod(OAS, temp(t), N);
-               typecheck(&a, Etop);
-               *init = list(*init, a);  // zero new temp
-               a = nod(OADDR, a->left, N);
+       if(n->left != N) {
+               // temp allocated during order.c for dddarg
+               if(vstat == N) {
+                       a = nod(OAS, n->left, N);
+                       typecheck(&a, Etop);
+                       *init = list(*init, a);  // zero new temp
+               }
+               a = nod(OADDR, n->left, N);
+       } else if(n->esc == EscNone) {
+               a = temp(t);
+               if(vstat == N) {
+                       a = nod(OAS, temp(t), N);
+                       typecheck(&a, Etop);
+                       *init = list(*init, a);  // zero new temp
+                       a = a->left;
+               }
+               a = nod(OADDR, a, N);
        } else {
                a = nod(ONEW, N, N);
                a->list = list1(typenod(t));
@@ -1022,12 +1034,16 @@ anylit(int ctxt, Node *n, Node *var, NodeList **init)
                if(!isptr[t->etype])
                        fatal("anylit: not ptr");
 
-               r = nod(ONEW, N, N);
-               r->typecheck = 1;
-               r->type = t;
-               r->esc = n->esc;
+               if(n->right != N) {
+                       r = nod(OADDR, n->right, N);
+                       typecheck(&r, Erv);
+               } else {
+                       r = nod(ONEW, N, N);
+                       r->typecheck = 1;
+                       r->type = t;
+                       r->esc = n->esc;
+               }
                walkexpr(&r, init);
-
                a = nod(OAS, var, r);
 
                typecheck(&a, Etop);
index b4b5d9eeb2ddd5387625d1402d4769ec99cc67fc..b6e43b759497430353cd6a76db96e2da1c7c783d 100644 (file)
@@ -654,8 +654,20 @@ reswitch:
                        if(iscmp[n->op]) {
                                n->etype = n->op;
                                n->op = OCMPSTR;
-                       } else if(n->op == OADD)
+                       } else if(n->op == OADD) {
+                               // create OADDSTR node with list of strings in x + y + z + (w + v) + ...
                                n->op = OADDSTR;
+                               if(l->op == OADDSTR)
+                                       n->list = l->list;
+                               else
+                                       n->list = list1(l);
+                               if(r->op == OADDSTR)
+                                       n->list = concat(n->list, r->list);
+                               else
+                                       n->list = list(n->list, r);
+                               n->left = N;
+                               n->right = N;
+                       }
                }
                if(et == TINTER) {
                        if(l->op == OLITERAL && l->val.ctype == CTNIL) {
index c9cff289ba860db527e9993c6fe032b8b38edaee..876d95530b2e8614a9d3f7b313686a42542baafc 100644 (file)
@@ -1190,9 +1190,10 @@ walkexpr(Node **np, NodeList **init)
                // s + "badgerbadgerbadger" == "badgerbadgerbadger"
                if((n->etype == OEQ || n->etype == ONE) &&
                   isconst(n->right, CTSTR) &&
-                  n->left->op == OADDSTR && isconst(n->left->right, CTSTR) &&
-                  cmpslit(n->right, n->left->right) == 0) {
-                       r = nod(n->etype, nod(OLEN, n->left->left, N), nodintconst(0));
+                  n->left->op == OADDSTR && count(n->left->list) == 2 &&
+                  isconst(n->left->list->next->n, CTSTR) &&
+                  cmpslit(n->right, n->left->list->next->n) == 0) {
+                       r = nod(n->etype, nod(OLEN, n->left->list->n, N), nodintconst(0));
                        typecheck(&r, Erv);
                        walkexpr(&r, init);
                        r->type = n->type;
@@ -1535,11 +1536,16 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
  * package all the arguments that match a ... T parameter into a []T.
  */
 static NodeList*
-mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, int esc)
+mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, Node *ddd)
 {
        Node *a, *n;
        Type *tslice;
-
+       int esc;
+       
+       esc = EscUnknown;
+       if(ddd != nil)
+               esc = ddd->esc;
+       
        tslice = typ(TARRAY);
        tslice->type = l->type->type;
        tslice->bound = -1;
@@ -1549,6 +1555,8 @@ mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, int
                n->type = tslice;
        } else {
                n = nod(OCOMPLIT, N, typenod(tslice));
+               if(ddd != nil)
+                       n->left = ddd->left; // temporary to use
                n->list = lr0;
                n->esc = esc;
                typecheck(&n, Erv);
@@ -1620,7 +1628,6 @@ dumpnodetypes(NodeList *l, char *what)
 static NodeList*
 ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
 {
-       int esc;
        Type *l, *ll;
        Node *r, *a;
        NodeList *nn, *lr0, *alist;
@@ -1683,10 +1690,7 @@ loop:
                // normal case -- make a slice of all
                // remaining arguments and pass it to
                // the ddd parameter.
-               esc = EscUnknown;
-               if(call->right)
-                       esc = call->right->esc;
-               nn = mkdotargslice(lr, nn, l, fp, init, esc);
+               nn = mkdotargslice(lr, nn, l, fp, init, call->right);
                goto ret;
        }
 
@@ -2504,26 +2508,24 @@ static Node*
 addstr(Node *n, NodeList **init)
 {
        Node *r, *cat, *slice;
-       NodeList *args;
-       int count;
+       NodeList *args, *l;
+       int c;
        Type *t;
 
-       count = 0;
-       for(r=n; r->op == OADDSTR; r=r->left)
-               count++;        // r->right
-       count++;        // r
-       if(count < 2)
-               yyerror("addstr count %d too small", count);
+       // orderexpr rewrote OADDSTR to have a list of strings.
+       c = count(n->list);
+       if(c < 2)
+               yyerror("addstr count %d too small", c);
 
        // build list of string arguments
        args = nil;
-       for(r=n; r->op == OADDSTR; r=r->left)
-               args = concat(list1(conv(r->right, types[TSTRING])), args);
-       args = concat(list1(conv(r, types[TSTRING])), args);
+       for(l=n->list; l != nil; l=l->next)
+               args = list(args, conv(l->n, types[TSTRING]));
 
-       if(count <= 5) {
+       if(c <= 5) {
                // small numbers of strings use direct runtime helpers.
-               snprint(namebuf, sizeof(namebuf), "concatstring%d", count);
+               // note: orderexpr knows this cutoff too.
+               snprint(namebuf, sizeof(namebuf), "concatstring%d", c);
        } else {
                // large numbers of strings are passed to the runtime as a slice.
                strcpy(namebuf, "concatstrings");
@@ -2531,6 +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->list = args;
                slice->esc = EscNone;
                args = list1(slice);
index 5a03bf031242490d03c60b6a1751d2ac5f513bb5..02ff02298a5112d60edaa4e9c18eaa6a50f05b40 100644 (file)
@@ -330,3 +330,96 @@ func f24() {
        m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
        m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
 }
+
+// defer should not cause spurious ambiguously live variables
+
+func f25(b bool) {
+       defer g25()
+       if b {
+               return
+       }
+       var x string
+       _ = &x
+       x = g15() // ERROR "live at call to g15: x"
+       print(x) // ERROR "live at call to printstring: x"
+} // ERROR "live at call to deferreturn: x"
+
+func g25()
+       
+// non-escaping ... slices passed to function call should die on return,
+// so that the temporaries do not stack and do not cause ambiguously
+// live variables.
+
+func f26(b bool) {
+       if b {
+               print26(1,2,3) // ERROR "live at call to print26: autotmp_[0-9]+$"
+       }
+       print26(4,5,6) // ERROR "live at call to print26: autotmp_[0-9]+$"
+       print26(7,8,9) // ERROR "live at call to print26: autotmp_[0-9]+$"
+       println()
+}
+
+//go:noescape
+func print26(...interface{})
+
+// non-escaping closures passed to function call should die on return
+
+func f27(b bool) {
+       x := 0
+       if b {
+               call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$"
+       }
+       call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$"
+       call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$"
+}
+
+//go:noescape
+func call27(func())
+
+// concatstring slice should die on return
+
+var s1, s2, s3, s4, s5, s6, s7, s8, s9, s10 string
+
+func f28(b bool) {
+       if b {
+               print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
+       }
+       print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
+       print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
+}      
+
+// map iterator should die on end of range loop
+
+func f29(b bool) {
+       if b {
+               for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$"
+                       print(k) // ERROR "live at call to printstring: autotmp_[0-9]+$"
+               }
+       }
+       for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$"
+               print(k) // ERROR "live at call to printstring: autotmp_[0-9]+$"
+       }
+       for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$"
+               print(k) // ERROR "live at call to printstring: autotmp_[0-9]+$"
+       }
+}
+
+// copy of array of pointers should die at end of range loop
+
+var ptrarr [10]*int
+
+func f30(b bool) {
+       // two live temps during print(p):
+       // the copy of ptrarr and the internal iterator pointer.
+       if b {
+               for _, p := range ptrarr {
+                       print(p) // ERROR "live at call to printpointer: autotmp_[0-9]+ autotmp_[0-9]+$"
+               }
+       }
+       for _, p := range ptrarr {
+               print(p) // ERROR "live at call to printpointer: autotmp_[0-9]+ autotmp_[0-9]+$"
+       }
+       for _, p := range ptrarr {
+               print(p) // ERROR "live at call to printpointer: autotmp_[0-9]+ autotmp_[0-9]+$"
+       }
+}