mirror of
https://github.com/golang/go
synced 2024-11-23 05:30:07 -07:00
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
This commit is contained in:
parent
2f3776ac27
commit
daca06f2e3
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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]);
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
93
test/live.go
93
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]+$"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user