diff --git a/src/cmd/5g/ggen.c b/src/cmd/5g/ggen.c index c81de1fd87..c85f680cda 100644 --- a/src/cmd/5g/ggen.c +++ b/src/cmd/5g/ggen.c @@ -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; } diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c index 9465f4d0ea..6b159e2e37 100644 --- a/src/cmd/6g/ggen.c +++ b/src/cmd/6g/ggen.c @@ -232,7 +232,9 @@ ginscall(Node *f, int proc) if(proc == 2) { nodreg(®, types[TINT64], D_AX); gins(ATESTQ, ®, ®); - 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); } diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c index afe80316b4..2ece188128 100644 --- a/src/cmd/8g/ggen.c +++ b/src/cmd/8g/ggen.c @@ -259,7 +259,9 @@ ginscall(Node *f, int proc) if(proc == 2) { nodreg(®, types[TINT64], D_AX); gins(ATESTL, ®, ®); - patch(gbranch(AJNE, T, -1), retpc); + p = gbranch(AJEQ, T, +1); + cgen_ret(N); + patch(p, pc); } break; } diff --git a/src/cmd/gc/closure.c b/src/cmd/gc/closure.c index ee2750b582..ef6f7e004d 100644 --- a/src/cmd/gc/closure.c +++ b/src/cmd/gc/closure.c @@ -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; diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c index a725ea971c..cfd81e86f5 100644 --- a/src/cmd/gc/const.c +++ b/src/cmd/gc/const.c @@ -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) diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c index 5a1a9ed21c..579d7dc08a 100644 --- a/src/cmd/gc/esc.c +++ b/src/cmd/gc/esc.c @@ -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; diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c index bffe8dfc7e..b5f8a834f1 100644 --- a/src/cmd/gc/fmt.c +++ b/src/cmd/gc/fmt.c @@ -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); diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 6e2cae7320..bd2b38d88e 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -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; diff --git a/src/cmd/gc/order.c b/src/cmd/gc/order.c index bdf94a4469..c9257f9b60 100644 --- a/src/cmd/gc/order.c +++ b/src/cmd/gc/order.c @@ -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; diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c index 0d19017768..fec4aa5f14 100644 --- a/src/cmd/gc/pgen.c +++ b/src/cmd/gc/pgen.c @@ -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; diff --git a/src/cmd/gc/range.c b/src/cmd/gc/range.c index ba5bd9709d..0cbb6a6f6e 100644 --- a/src/cmd/gc/range.c +++ b/src/cmd/gc/range.c @@ -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]); diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c index bfb8eb8e66..b49222ec64 100644 --- a/src/cmd/gc/sinit.c +++ b/src/cmd/gc/sinit.c @@ -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); diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index b4b5d9eeb2..b6e43b7594 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -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) { diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index c9cff289ba..876d95530b 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -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); diff --git a/test/live.go b/test/live.go index 5a03bf0312..02ff02298a 100644 --- a/test/live.go +++ b/test/live.go @@ -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]+$" + } +}