aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/gc
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2014-04-01 20:02:54 -0400
committerRuss Cox <rsc@golang.org>2014-04-01 20:02:54 -0400
commitdaca06f2e35035ea2c9d508f9f52a23baa406885 (patch)
tree5fcdb66368e71890bf930cb18d26c4d1cbef68d4 /src/cmd/gc
parent2f3776ac2765a6d9cede9894efafb980c670ddb1 (diff)
downloadgo-daca06f2e35035ea2c9d508f9f52a23baa406885.tar.xz
cmd/gc: shorten more temporary lifetimes
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
Diffstat (limited to 'src/cmd/gc')
-rw-r--r--src/cmd/gc/closure.c8
-rw-r--r--src/cmd/gc/const.c52
-rw-r--r--src/cmd/gc/esc.c44
-rw-r--r--src/cmd/gc/fmt.c9
-rw-r--r--src/cmd/gc/go.h1
-rw-r--r--src/cmd/gc/order.c90
-rw-r--r--src/cmd/gc/pgen.c17
-rw-r--r--src/cmd/gc/range.c32
-rw-r--r--src/cmd/gc/sinit.c36
-rw-r--r--src/cmd/gc/typecheck.c14
-rw-r--r--src/cmd/gc/walk.c49
11 files changed, 260 insertions, 92 deletions
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);