mirror of
https://github.com/golang/go
synced 2024-09-04 15:34:21 +00:00
cmd/gc: correct liveness for fat variables
The VARDEF placement must be before the initialization but after any final use. If you have something like s = ... using s ... the rhs must be evaluated, then the VARDEF, then the lhs assigned. There is a large comment in pgen.c on gvardef explaining this in more detail. This CL also includes Ian's suggestions from earlier CLs, namely commenting the use of mode in link.h and fixing the precedence of the ~r check in dcl.c. This CL enables the check that if liveness analysis decides a variable is live on entry to the function, that variable must be a function parameter (not a result, and not a local variable). If this check fails, it indicates a bug in the liveness analysis or in the generated code being analyzed. The race detector generates invalid code for append(x, y...). The code declares a temporary t and then uses cap(t) before initializing t. The new liveness check catches this bug and stops the compiler from writing out the buggy code. Consequently, this CL disables the race detector tests in run.bash until the race detector bug can be fixed (golang.org/issue/7334). Except for the race detector bug, the liveness analysis check does not detect any problems (this CL and the previous CLs fixed all the detected problems). The net test still fails with GOGC=0 but the rest of the tests now pass or time out (because GOGC=0 is so slow). TBR=iant CC=golang-codereviews https://golang.org/cl/64170043
This commit is contained in:
parent
15d294991f
commit
7a7c0ffb47
|
@ -110,7 +110,7 @@ struct Prog
|
|||
uchar optab; // 5l
|
||||
|
||||
char width; /* fake for DATA */
|
||||
char mode; /* 16, 32, or 64 */
|
||||
char mode; /* 16, 32, or 64 in 8l, 8l; internal use in 5g, 6g, 8g */
|
||||
};
|
||||
|
||||
// prevent incompatible type signatures between liblink and 8l on Plan 9
|
||||
|
|
|
@ -604,6 +604,7 @@ agen(Node *n, Node *res)
|
|||
// The generated code is just going to panic, so it need not
|
||||
// be terribly efficient. See issue 3670.
|
||||
tempname(&n1, n->type);
|
||||
gvardef(&n1);
|
||||
clearfat(&n1);
|
||||
regalloc(&n2, types[tptr], res);
|
||||
gins(AMOVW, &n1, &n2);
|
||||
|
@ -1440,10 +1441,6 @@ sgen(Node *n, Node *res, int64 w)
|
|||
return;
|
||||
}
|
||||
|
||||
// Record site of definition of ns for liveness analysis.
|
||||
if(res->op == ONAME && res->class != PEXTERN)
|
||||
gvardef(res);
|
||||
|
||||
// If copying .args, that's all the results, so record definition sites
|
||||
// for them for the liveness analysis.
|
||||
if(res->op == ONAME && strcmp(res->sym->name, ".args") == 0)
|
||||
|
@ -1502,8 +1499,12 @@ sgen(Node *n, Node *res, int64 w)
|
|||
agenr(n, &dst, res); // temporarily use dst
|
||||
regalloc(&src, types[tptr], N);
|
||||
gins(AMOVW, &dst, &src);
|
||||
if(res->op == ONAME)
|
||||
gvardef(res);
|
||||
agen(res, &dst);
|
||||
} else {
|
||||
if(res->op == ONAME)
|
||||
gvardef(res);
|
||||
agenr(res, &dst, res);
|
||||
agenr(n, &src, N);
|
||||
}
|
||||
|
@ -1638,6 +1639,8 @@ componentgen(Node *nr, Node *nl)
|
|||
|
||||
switch(nl->type->etype) {
|
||||
case TARRAY:
|
||||
if(nl->op == ONAME)
|
||||
gvardef(nl);
|
||||
nodl.xoffset += Array_array;
|
||||
nodl.type = ptrto(nl->type->type);
|
||||
|
||||
|
@ -1668,6 +1671,8 @@ componentgen(Node *nr, Node *nl)
|
|||
goto yes;
|
||||
|
||||
case TSTRING:
|
||||
if(nl->op == ONAME)
|
||||
gvardef(nl);
|
||||
nodl.xoffset += Array_array;
|
||||
nodl.type = ptrto(types[TUINT8]);
|
||||
|
||||
|
@ -1689,6 +1694,8 @@ componentgen(Node *nr, Node *nl)
|
|||
goto yes;
|
||||
|
||||
case TINTER:
|
||||
if(nl->op == ONAME)
|
||||
gvardef(nl);
|
||||
nodl.xoffset += Array_array;
|
||||
nodl.type = ptrto(types[TUINT8]);
|
||||
|
||||
|
|
|
@ -31,13 +31,13 @@ void
|
|||
markautoused(Prog* p)
|
||||
{
|
||||
for (; p; p = p->link) {
|
||||
if (p->as == ATYPE)
|
||||
if (p->as == ATYPE || p->as == AVARDEF)
|
||||
continue;
|
||||
|
||||
if (p->from.name == D_AUTO && p->from.node)
|
||||
if (p->from.node)
|
||||
p->from.node->used = 1;
|
||||
|
||||
if (p->to.name == D_AUTO && p->to.node)
|
||||
if (p->to.node)
|
||||
p->to.node->used = 1;
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,16 @@ fixautoused(Prog* p)
|
|||
*lp = p->link;
|
||||
continue;
|
||||
}
|
||||
if (p->as == AVARDEF && p->to.node && !p->to.node->used) {
|
||||
// Cannot remove VARDEF instruction, because - unlike TYPE handled above -
|
||||
// VARDEFs are interspersed with other code, and a jump might be using the
|
||||
// VARDEF as a target. Replace with a no-op instead. A later pass will remove
|
||||
// the no-ops.
|
||||
p->to.type = D_NONE;
|
||||
p->to.node = N;
|
||||
p->as = ANOP;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p->from.name == D_AUTO && p->from.node)
|
||||
p->from.offset += p->from.node->stkdelta;
|
||||
|
@ -766,8 +776,6 @@ clearfat(Node *nl)
|
|||
if(debug['g'])
|
||||
dump("\nclearfat", nl);
|
||||
|
||||
gvardef(nl);
|
||||
|
||||
w = nl->type->width;
|
||||
// Avoid taking the address for simple enough types.
|
||||
if(componentgen(N, nl))
|
||||
|
|
|
@ -287,6 +287,8 @@ subprop(Flow *r0)
|
|||
if(uniqs(r) == nil)
|
||||
break;
|
||||
p = r->prog;
|
||||
if(p->as == AVARDEF)
|
||||
continue;
|
||||
proginfo(&info, p);
|
||||
if(info.flags & Call)
|
||||
return 0;
|
||||
|
|
|
@ -209,6 +209,8 @@ regopt(Prog *firstp)
|
|||
|
||||
for(r = firstr; r != R; r = (Reg*)r->f.link) {
|
||||
p = r->f.prog;
|
||||
if(p->as == AVARDEF)
|
||||
continue;
|
||||
proginfo(&info, p);
|
||||
|
||||
// Avoid making variables for direct-called functions.
|
||||
|
|
|
@ -813,6 +813,7 @@ agen(Node *n, Node *res)
|
|||
// The generated code is just going to panic, so it need not
|
||||
// be terribly efficient. See issue 3670.
|
||||
tempname(&n1, n->type);
|
||||
gvardef(&n1);
|
||||
clearfat(&n1);
|
||||
regalloc(&n2, types[tptr], res);
|
||||
gins(ALEAQ, &n1, &n2);
|
||||
|
@ -1351,10 +1352,6 @@ sgen(Node *n, Node *ns, int64 w)
|
|||
if(w < 0)
|
||||
fatal("sgen copy %lld", w);
|
||||
|
||||
// Record site of definition of ns for liveness analysis.
|
||||
if(ns->op == ONAME && ns->class != PEXTERN)
|
||||
gvardef(ns);
|
||||
|
||||
// If copying .args, that's all the results, so record definition sites
|
||||
// for them for the liveness analysis.
|
||||
if(ns->op == ONAME && strcmp(ns->sym->name, ".args") == 0)
|
||||
|
@ -1392,11 +1389,16 @@ sgen(Node *n, Node *ns, int64 w)
|
|||
|
||||
if(n->ullman >= ns->ullman) {
|
||||
agenr(n, &nodr, N);
|
||||
if(ns->op == ONAME && ns->class != PEXTERN)
|
||||
gvardef(ns);
|
||||
agenr(ns, &nodl, N);
|
||||
} else {
|
||||
if(ns->op == ONAME && ns->class != PEXTERN)
|
||||
gvardef(ns);
|
||||
agenr(ns, &nodl, N);
|
||||
agenr(n, &nodr, N);
|
||||
}
|
||||
|
||||
nodreg(&noddi, types[tptr], D_DI);
|
||||
nodreg(&nodsi, types[tptr], D_SI);
|
||||
gmove(&nodl, &noddi);
|
||||
|
@ -1573,6 +1575,8 @@ componentgen(Node *nr, Node *nl)
|
|||
switch(nl->type->etype) {
|
||||
case TARRAY:
|
||||
// componentgen for arrays.
|
||||
if(nl->op == ONAME && nl->class != PEXTERN)
|
||||
gvardef(nl);
|
||||
t = nl->type;
|
||||
if(!isslice(t)) {
|
||||
nodl.type = t->type;
|
||||
|
@ -1622,6 +1626,8 @@ componentgen(Node *nr, Node *nl)
|
|||
goto yes;
|
||||
|
||||
case TSTRING:
|
||||
if(nl->op == ONAME && nl->class != PEXTERN)
|
||||
gvardef(nl);
|
||||
nodl.xoffset += Array_array;
|
||||
nodl.type = ptrto(types[TUINT8]);
|
||||
|
||||
|
@ -1645,6 +1651,8 @@ componentgen(Node *nr, Node *nl)
|
|||
goto yes;
|
||||
|
||||
case TINTER:
|
||||
if(nl->op == ONAME && nl->class != PEXTERN)
|
||||
gvardef(nl);
|
||||
nodl.xoffset += Array_array;
|
||||
nodl.type = ptrto(types[TUINT8]);
|
||||
|
||||
|
@ -1668,6 +1676,8 @@ componentgen(Node *nr, Node *nl)
|
|||
goto yes;
|
||||
|
||||
case TSTRUCT:
|
||||
if(nl->op == ONAME && nl->class != PEXTERN)
|
||||
gvardef(nl);
|
||||
loffset = nodl.xoffset;
|
||||
roffset = nodr.xoffset;
|
||||
// funarg structs may not begin at offset zero.
|
||||
|
|
|
@ -60,13 +60,13 @@ void
|
|||
markautoused(Prog* p)
|
||||
{
|
||||
for (; p; p = p->link) {
|
||||
if (p->as == ATYPE)
|
||||
if (p->as == ATYPE || p->as == AVARDEF)
|
||||
continue;
|
||||
|
||||
if (p->from.type == D_AUTO && p->from.node)
|
||||
if (p->from.node)
|
||||
p->from.node->used = 1;
|
||||
|
||||
if (p->to.type == D_AUTO && p->to.node)
|
||||
if (p->to.node)
|
||||
p->to.node->used = 1;
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,16 @@ fixautoused(Prog *p)
|
|||
*lp = p->link;
|
||||
continue;
|
||||
}
|
||||
if (p->as == AVARDEF && p->to.node && !p->to.node->used) {
|
||||
// Cannot remove VARDEF instruction, because - unlike TYPE handled above -
|
||||
// VARDEFs are interspersed with other code, and a jump might be using the
|
||||
// VARDEF as a target. Replace with a no-op instead. A later pass will remove
|
||||
// the no-ops.
|
||||
p->to.type = D_NONE;
|
||||
p->to.node = N;
|
||||
p->as = ANOP;
|
||||
continue;
|
||||
}
|
||||
if (p->from.type == D_AUTO && p->from.node)
|
||||
p->from.offset += p->from.node->stkdelta;
|
||||
|
||||
|
@ -1023,8 +1033,6 @@ clearfat(Node *nl)
|
|||
if(debug['g'])
|
||||
dump("\nclearfat", nl);
|
||||
|
||||
gvardef(nl);
|
||||
|
||||
w = nl->type->width;
|
||||
// Avoid taking the address for simple enough types.
|
||||
if(componentgen(N, nl))
|
||||
|
|
|
@ -573,6 +573,8 @@ subprop(Flow *r0)
|
|||
break;
|
||||
}
|
||||
p = r->prog;
|
||||
if(p->as == AVARDEF)
|
||||
continue;
|
||||
proginfo(&info, p);
|
||||
if(info.flags & Call) {
|
||||
if(debug['P'] && debug['v'])
|
||||
|
@ -788,6 +790,8 @@ copyu(Prog *p, Adr *v, Adr *s)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if(p->as == AVARDEF)
|
||||
return 0;
|
||||
proginfo(&info, p);
|
||||
|
||||
if((info.reguse|info.regset) & RtoB(v->type))
|
||||
|
|
|
@ -195,6 +195,8 @@ regopt(Prog *firstp)
|
|||
|
||||
for(r = firstr; r != R; r = (Reg*)r->f.link) {
|
||||
p = r->f.prog;
|
||||
if(p->as == AVARDEF)
|
||||
continue;
|
||||
proginfo(&info, p);
|
||||
|
||||
// Avoid making variables for direct-called functions.
|
||||
|
|
|
@ -522,6 +522,7 @@ agen(Node *n, Node *res)
|
|||
// The generated code is just going to panic, so it need not
|
||||
// be terribly efficient. See issue 3670.
|
||||
tempname(&n1, n->type);
|
||||
gvardef(&n1);
|
||||
clearfat(&n1);
|
||||
regalloc(&n2, types[tptr], res);
|
||||
gins(ALEAL, &n1, &n2);
|
||||
|
@ -1224,10 +1225,6 @@ sgen(Node *n, Node *res, int64 w)
|
|||
return;
|
||||
}
|
||||
|
||||
// Record site of definition of ns for liveness analysis.
|
||||
if(res->op == ONAME && res->class != PEXTERN)
|
||||
gvardef(res);
|
||||
|
||||
// If copying .args, that's all the results, so record definition sites
|
||||
// for them for the liveness analysis.
|
||||
if(res->op == ONAME && strcmp(res->sym->name, ".args") == 0)
|
||||
|
@ -1267,6 +1264,10 @@ sgen(Node *n, Node *res, int64 w)
|
|||
agen(n, &src);
|
||||
else
|
||||
gmove(&tsrc, &src);
|
||||
|
||||
if(res->op == ONAME)
|
||||
gvardef(res);
|
||||
|
||||
if(res->addable)
|
||||
agen(res, &dst);
|
||||
else
|
||||
|
@ -1383,6 +1384,8 @@ componentgen(Node *nr, Node *nl)
|
|||
|
||||
switch(nl->type->etype) {
|
||||
case TARRAY:
|
||||
if(nl->op == ONAME && nl->class != PEXTERN)
|
||||
gvardef(nl);
|
||||
nodl.xoffset += Array_array;
|
||||
nodl.type = ptrto(nl->type->type);
|
||||
|
||||
|
@ -1416,6 +1419,8 @@ componentgen(Node *nr, Node *nl)
|
|||
goto yes;
|
||||
|
||||
case TSTRING:
|
||||
if(nl->op == ONAME && nl->class != PEXTERN)
|
||||
gvardef(nl);
|
||||
nodl.xoffset += Array_array;
|
||||
nodl.type = ptrto(types[TUINT8]);
|
||||
|
||||
|
@ -1439,6 +1444,8 @@ componentgen(Node *nr, Node *nl)
|
|||
goto yes;
|
||||
|
||||
case TINTER:
|
||||
if(nl->op == ONAME && nl->class != PEXTERN)
|
||||
gvardef(nl);
|
||||
nodl.xoffset += Array_array;
|
||||
nodl.type = ptrto(types[TUINT8]);
|
||||
|
||||
|
|
|
@ -30,13 +30,13 @@ void
|
|||
markautoused(Prog* p)
|
||||
{
|
||||
for (; p; p = p->link) {
|
||||
if (p->as == ATYPE)
|
||||
if (p->as == ATYPE || p->as == AVARDEF)
|
||||
continue;
|
||||
|
||||
if (p->from.type == D_AUTO && p->from.node)
|
||||
if (p->from.node)
|
||||
p->from.node->used = 1;
|
||||
|
||||
if (p->to.type == D_AUTO && p->to.node)
|
||||
if (p->to.node)
|
||||
p->to.node->used = 1;
|
||||
}
|
||||
}
|
||||
|
@ -52,6 +52,16 @@ fixautoused(Prog* p)
|
|||
*lp = p->link;
|
||||
continue;
|
||||
}
|
||||
if (p->as == AVARDEF && p->to.node && !p->to.node->used) {
|
||||
// Cannot remove VARDEF instruction, because - unlike TYPE handled above -
|
||||
// VARDEFs are interspersed with other code, and a jump might be using the
|
||||
// VARDEF as a target. Replace with a no-op instead. A later pass will remove
|
||||
// the no-ops.
|
||||
p->to.type = D_NONE;
|
||||
p->to.node = N;
|
||||
p->as = ANOP;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p->from.type == D_AUTO && p->from.node)
|
||||
p->from.offset += p->from.node->stkdelta;
|
||||
|
@ -73,8 +83,6 @@ clearfat(Node *nl)
|
|||
if(debug['g'])
|
||||
dump("\nclearfat", nl);
|
||||
|
||||
gvardef(nl);
|
||||
|
||||
w = nl->type->width;
|
||||
// Avoid taking the address for simple enough types.
|
||||
if(componentgen(N, nl))
|
||||
|
|
|
@ -387,6 +387,8 @@ subprop(Flow *r0)
|
|||
if(uniqs(r) == nil)
|
||||
break;
|
||||
p = r->prog;
|
||||
if(p->as == AVARDEF)
|
||||
continue;
|
||||
proginfo(&info, p);
|
||||
if(info.flags & Call)
|
||||
return 0;
|
||||
|
@ -584,6 +586,8 @@ copyu(Prog *p, Adr *v, Adr *s)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if(p->as == AVARDEF)
|
||||
return 0;
|
||||
proginfo(&info, p);
|
||||
|
||||
if((info.reguse|info.regset) & RtoB(v->type))
|
||||
|
|
|
@ -165,6 +165,8 @@ regopt(Prog *firstp)
|
|||
|
||||
for(r = firstr; r != R; r = (Reg*)r->f.link) {
|
||||
p = r->f.prog;
|
||||
if(p->as == AVARDEF)
|
||||
continue;
|
||||
proginfo(&info, p);
|
||||
|
||||
// Avoid making variables for direct-called functions.
|
||||
|
|
|
@ -1215,7 +1215,7 @@ functype(Node *this, NodeList *in, NodeList *out)
|
|||
t->outnamed = 0;
|
||||
if(t->outtuple > 0 && out->n->left != N && out->n->left->orig != N) {
|
||||
s = out->n->left->orig->sym;
|
||||
if(s != S && s->name[0] != '~' || s->name[1] != 'r') // ~r%d is the name invented for an unnamed result
|
||||
if(s != S && (s->name[0] != '~' || s->name[1] != 'r')) // ~r%d is the name invented for an unnamed result
|
||||
t->outnamed = 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -465,6 +465,8 @@ gen(Node *n)
|
|||
case OAS:
|
||||
if(gen_as_init(n))
|
||||
break;
|
||||
if(n->colas && isfat(n->left->type) && n->left->op == ONAME)
|
||||
gvardef(n->left);
|
||||
cgen_as(n->left, n->right);
|
||||
break;
|
||||
|
||||
|
@ -562,9 +564,9 @@ cgen_proc(Node *n, int proc)
|
|||
|
||||
/*
|
||||
* generate declaration.
|
||||
* nothing to do for on-stack automatics,
|
||||
* but might have to allocate heap copy
|
||||
* have to allocate heap copy
|
||||
* for escaped variables.
|
||||
* also leave VARDEF annotations for liveness analysis.
|
||||
*/
|
||||
static void
|
||||
cgen_dcl(Node *n)
|
||||
|
@ -575,6 +577,8 @@ cgen_dcl(Node *n)
|
|||
dump("cgen_dcl", n);
|
||||
fatal("cgen_dcl");
|
||||
}
|
||||
if(isfat(n->type))
|
||||
gvardef(n);
|
||||
if(!(n->class & PHEAP))
|
||||
return;
|
||||
if(n->alloc == nil)
|
||||
|
@ -637,6 +641,7 @@ cgen_discard(Node *nr)
|
|||
// special enough to just evaluate
|
||||
default:
|
||||
tempname(&tmp, nr->type);
|
||||
gvardef(&tmp);
|
||||
cgen_as(&tmp, nr);
|
||||
gused(&tmp);
|
||||
}
|
||||
|
@ -739,6 +744,8 @@ cgen_as(Node *nl, Node *nr)
|
|||
if(tl == T)
|
||||
return;
|
||||
if(isfat(tl)) {
|
||||
if(nl->op == ONAME)
|
||||
gvardef(nl);
|
||||
clearfat(nl);
|
||||
return;
|
||||
}
|
||||
|
@ -767,12 +774,18 @@ cgen_eface(Node *n, Node *res)
|
|||
* so it's important that it is done first
|
||||
*/
|
||||
Node dst;
|
||||
Node *tmp;
|
||||
|
||||
tmp = temp(types[tptr]);
|
||||
cgen(n->right, tmp);
|
||||
|
||||
gvardef(res);
|
||||
|
||||
dst = *res;
|
||||
dst.type = types[tptr];
|
||||
dst.xoffset += widthptr;
|
||||
cgen(n->right, &dst);
|
||||
cgen(tmp, &dst);
|
||||
|
||||
dst.xoffset -= widthptr;
|
||||
cgen(n->left, &dst);
|
||||
}
|
||||
|
@ -789,7 +802,7 @@ cgen_eface(Node *n, Node *res)
|
|||
void
|
||||
cgen_slice(Node *n, Node *res)
|
||||
{
|
||||
Node src, dst, *cap, *len, *offs, *add;
|
||||
Node src, dst, *cap, *len, *offs, *add, *base;
|
||||
|
||||
cap = n->list->n;
|
||||
len = n->list->next->n;
|
||||
|
@ -797,26 +810,15 @@ cgen_slice(Node *n, Node *res)
|
|||
if(n->list->next->next)
|
||||
offs = n->list->next->next->n;
|
||||
|
||||
gvardef(res);
|
||||
|
||||
// dst.len = hi [ - lo ]
|
||||
dst = *res;
|
||||
dst.xoffset += Array_nel;
|
||||
dst.type = types[simtype[TUINT]];
|
||||
cgen(len, &dst);
|
||||
|
||||
if(n->op != OSLICESTR) {
|
||||
// dst.cap = cap [ - lo ]
|
||||
dst = *res;
|
||||
dst.xoffset += Array_cap;
|
||||
dst.type = types[simtype[TUINT]];
|
||||
cgen(cap, &dst);
|
||||
}
|
||||
|
||||
// dst.array = src.array [ + lo *width ]
|
||||
dst = *res;
|
||||
dst.xoffset += Array_array;
|
||||
dst.type = types[TUINTPTR];
|
||||
// evaluate base pointer first, because it is the only
|
||||
// possibly complex expression. once that is evaluated
|
||||
// and stored, updating the len and cap can be done
|
||||
// without making any calls, so without doing anything that
|
||||
// might cause preemption or garbage collection.
|
||||
// this makes the whole slice update atomic as far as the
|
||||
// garbage collector can see.
|
||||
|
||||
base = temp(types[TUINTPTR]);
|
||||
|
||||
if(isnil(n->left)) {
|
||||
tempname(&src, n->left->type);
|
||||
|
@ -830,19 +832,43 @@ cgen_slice(Node *n, Node *res)
|
|||
if(n->op == OSLICEARR || n->op == OSLICE3ARR) {
|
||||
if(!isptr[n->left->type->etype])
|
||||
fatal("slicearr is supposed to work on pointer: %+N\n", n);
|
||||
cgen(&src, &dst);
|
||||
cgen_checknil(&dst);
|
||||
cgen(&src, base);
|
||||
cgen_checknil(base);
|
||||
if(offs != N) {
|
||||
add = nod(OADD, &dst, offs);
|
||||
add = nod(OADD, base, offs);
|
||||
typecheck(&add, Erv);
|
||||
cgen(add, &dst);
|
||||
cgen(add, base);
|
||||
}
|
||||
} else if(offs == N) {
|
||||
cgen(&src, &dst);
|
||||
cgen(&src, base);
|
||||
} else {
|
||||
add = nod(OADD, &src, offs);
|
||||
typecheck(&add, Erv);
|
||||
cgen(add, &dst);
|
||||
cgen(add, base);
|
||||
}
|
||||
|
||||
// committed to the update
|
||||
gvardef(res);
|
||||
|
||||
// dst.array = src.array [ + lo *width ]
|
||||
dst = *res;
|
||||
dst.xoffset += Array_array;
|
||||
dst.type = types[TUINTPTR];
|
||||
|
||||
cgen(base, &dst);
|
||||
|
||||
// dst.len = hi [ - lo ]
|
||||
dst = *res;
|
||||
dst.xoffset += Array_nel;
|
||||
dst.type = types[simtype[TUINT]];
|
||||
cgen(len, &dst);
|
||||
|
||||
if(n->op != OSLICESTR) {
|
||||
// dst.cap = cap [ - lo ]
|
||||
dst = *res;
|
||||
dst.xoffset += Array_cap;
|
||||
dst.type = types[simtype[TUINT]];
|
||||
cgen(cap, &dst);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,11 +31,65 @@ makefuncdatasym(char *namefmt, int64 funcdatakind)
|
|||
return sym;
|
||||
}
|
||||
|
||||
// gvardef inserts a VARDEF for n into the instruction stream.
|
||||
// VARDEF is an annotation for the liveness analysis, marking a place
|
||||
// where a complete initialization (definition) of a variable begins.
|
||||
// Since the liveness analysis can see initialization of single-word
|
||||
// variables quite easy, gvardef is usually only called for multi-word
|
||||
// or 'fat' variables, those satisfying isfat(n->type).
|
||||
// However, gvardef is also called when a non-fat variable is initialized
|
||||
// via a block move; the only time this happens is when you have
|
||||
// return f()
|
||||
// for a function with multiple return values exactly matching the return
|
||||
// types of the current function.
|
||||
//
|
||||
// A 'VARDEF x' annotation in the instruction stream tells the liveness
|
||||
// analysis to behave as though the variable x is being initialized at that
|
||||
// point in the instruction stream. The VARDEF must appear before the
|
||||
// actual (multi-instruction) initialization, and it must also appear after
|
||||
// any uses of the previous value, if any. For example, if compiling:
|
||||
//
|
||||
// x = x[1:]
|
||||
//
|
||||
// it is important to generate code like:
|
||||
//
|
||||
// base, len, cap = pieces of x[1:]
|
||||
// VARDEF x
|
||||
// x = {base, len, cap}
|
||||
//
|
||||
// If instead the generated code looked like:
|
||||
//
|
||||
// VARDEF x
|
||||
// base, len, cap = pieces of x[1:]
|
||||
// x = {base, len, cap}
|
||||
//
|
||||
// then the liveness analysis would decide the previous value of x was
|
||||
// unnecessary even though it is about to be used by the x[1:] computation.
|
||||
// Similarly, if the generated code looked like:
|
||||
//
|
||||
// base, len, cap = pieces of x[1:]
|
||||
// x = {base, len, cap}
|
||||
// VARDEF x
|
||||
//
|
||||
// then the liveness analysis will not preserve the new value of x, because
|
||||
// the VARDEF appears to have "overwritten" it.
|
||||
//
|
||||
// VARDEF is a bit of a kludge to work around the fact that the instruction
|
||||
// stream is working on single-word values but the liveness analysis
|
||||
// wants to work on individual variables, which might be multi-word
|
||||
// aggregates. It might make sense at some point to look into letting
|
||||
// the liveness analysis work on single-word values as well, although
|
||||
// there are complications around interface values, which cannot be
|
||||
// treated as individual words.
|
||||
void
|
||||
gvardef(Node *n)
|
||||
{
|
||||
if(n == N)
|
||||
fatal("gvardef nil");
|
||||
if(n->op != ONAME) {
|
||||
yyerror("gvardef %#O; %N", n->op, n);
|
||||
return;
|
||||
}
|
||||
switch(n->class) {
|
||||
case PAUTO:
|
||||
case PPARAM:
|
||||
|
|
|
@ -583,8 +583,11 @@ newcfg(Prog *firstp)
|
|||
// Unreachable control flow nodes are indicated by a -1 in the rpo
|
||||
// field. If we see these nodes something must have gone wrong in an
|
||||
// upstream compilation phase.
|
||||
if(bb->rpo == -1)
|
||||
fatal("newcfg: unreferenced basic blocks");
|
||||
if(bb->rpo == -1) {
|
||||
print("newcfg: unreachable basic block for %P\n", bb->last);
|
||||
printcfg(cfg);
|
||||
fatal("newcfg: invalid control flow graph");
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
@ -956,63 +959,39 @@ livenessprintcfg(Liveness *lv)
|
|||
}
|
||||
|
||||
static void
|
||||
checkauto(Node *fn, Prog *p, Node *n, char *where)
|
||||
checkauto(Node *fn, Prog *p, Node *n)
|
||||
{
|
||||
NodeList *ll;
|
||||
int found;
|
||||
char *fnname;
|
||||
char *nname;
|
||||
NodeList *l;
|
||||
|
||||
found = 0;
|
||||
for(ll = fn->dcl; ll != nil; ll = ll->next) {
|
||||
if(ll->n->op == ONAME && ll->n->class == PAUTO) {
|
||||
if(n == ll->n) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(found)
|
||||
return;
|
||||
fnname = fn->nname->sym->name ? fn->nname->sym->name : "<unknown>";
|
||||
nname = n->sym->name ? n->sym->name : "<unknown>";
|
||||
print("D_AUTO '%s' not found: name is '%s' function is '%s' class is %d\n", where, nname, fnname, n->class);
|
||||
print("Here '%P'\nlooking for node %p\n", p, n);
|
||||
for(ll = fn->dcl; ll != nil; ll = ll->next)
|
||||
print("node=%p, node->class=%d\n", (uintptr)ll->n, ll->n->class);
|
||||
for(l = fn->dcl; l != nil; l = l->next)
|
||||
if(l->n->op == ONAME && l->n->class == PAUTO && l->n == n)
|
||||
return;
|
||||
|
||||
print("checkauto %N: %N (%p; class=%d) not found in %P\n", curfn, n, n, n->class, p);
|
||||
for(l = fn->dcl; l != nil; l = l->next)
|
||||
print("\t%N (%p; class=%d)\n", l->n, l->n, l->n->class);
|
||||
yyerror("checkauto: invariant lost");
|
||||
}
|
||||
|
||||
static void
|
||||
checkparam(Node *fn, Prog *p, Node *n, char *where)
|
||||
checkparam(Node *fn, Prog *p, Node *n)
|
||||
{
|
||||
NodeList *ll;
|
||||
int found;
|
||||
char *fnname;
|
||||
char *nname;
|
||||
NodeList *l;
|
||||
Node *a;
|
||||
int class;
|
||||
|
||||
if(isfunny(n))
|
||||
return;
|
||||
found = 0;
|
||||
for(ll = fn->dcl; ll != nil; ll = ll->next) {
|
||||
if(ll->n->op == ONAME && ((ll->n->class & ~PHEAP) == PPARAM ||
|
||||
(ll->n->class & ~PHEAP) == PPARAMOUT)) {
|
||||
if(n == ll->n) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(found)
|
||||
return;
|
||||
if(n->sym) {
|
||||
fnname = fn->nname->sym->name ? fn->nname->sym->name : "<unknown>";
|
||||
nname = n->sym->name ? n->sym->name : "<unknown>";
|
||||
print("D_PARAM '%s' not found: name='%s' function='%s' class is %d\n", where, nname, fnname, n->class);
|
||||
print("Here '%P'\nlooking for node %p\n", p, n);
|
||||
for(ll = fn->dcl; ll != nil; ll = ll->next)
|
||||
print("node=%p, node->class=%d\n", ll->n, ll->n->class);
|
||||
for(l = fn->dcl; l != nil; l = l->next) {
|
||||
a = l->n;
|
||||
class = l->n->class & ~PHEAP;
|
||||
if(a->op == ONAME && (class == PPARAM || class == PPARAMOUT) && a == n)
|
||||
return;
|
||||
}
|
||||
|
||||
print("checkparam %N: %N (%p; class=%d) not found in %P\n", curfn, n, n, n->class, p);
|
||||
for(l = fn->dcl; l != nil; l = l->next)
|
||||
print("\t%N (%p; class=%d)\n", l->n, l->n, l->n->class);
|
||||
yyerror("checkparam: invariant lost");
|
||||
}
|
||||
|
||||
|
@ -1020,13 +999,13 @@ static void
|
|||
checkprog(Node *fn, Prog *p)
|
||||
{
|
||||
if(p->from.type == D_AUTO)
|
||||
checkauto(fn, p, p->from.node, "from");
|
||||
checkauto(fn, p, p->from.node);
|
||||
if(p->from.type == D_PARAM)
|
||||
checkparam(fn, p, p->from.node, "from");
|
||||
checkparam(fn, p, p->from.node);
|
||||
if(p->to.type == D_AUTO)
|
||||
checkauto(fn, p, p->to.node, "to");
|
||||
checkauto(fn, p, p->to.node);
|
||||
if(p->to.type == D_PARAM)
|
||||
checkparam(fn, p, p->to.node, "to");
|
||||
checkparam(fn, p, p->to.node);
|
||||
}
|
||||
|
||||
// Check instruction invariants. We assume that the nodes corresponding to the
|
||||
|
@ -1609,13 +1588,13 @@ livenessepilogue(Liveness *lv)
|
|||
// Useful sanity check: on entry to the function,
|
||||
// the only things that can possibly be live are the
|
||||
// input parameters.
|
||||
if(0 && p->as == ATEXT) {
|
||||
if(p->as == ATEXT) {
|
||||
for(j = 0; j < liveout->n; j++) {
|
||||
if(!bvget(liveout, j))
|
||||
continue;
|
||||
n = *(Node**)arrayget(lv->vars, j);
|
||||
if(n->class != PPARAM)
|
||||
yyerrorl(p->lineno, "internal error: %N %N recorded as live on entry", curfn->nname, n);
|
||||
yyerrorl(p->lineno, "internal error: %N %lN recorded as live on entry", curfn->nname, n);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -57,8 +57,9 @@ go test sync -short -timeout=$(expr 120 \* $timeout_scale)s -cpu=10
|
|||
|
||||
# Race detector only supported on Linux and OS X,
|
||||
# and only on amd64, and only when cgo is enabled.
|
||||
# Disabled due to golang.org/issue/7334; remove XXX below to reenable.
|
||||
case "$GOHOSTOS-$GOOS-$GOARCH-$CGO_ENABLED" in
|
||||
linux-linux-amd64-1 | darwin-darwin-amd64-1)
|
||||
XXXlinux-linux-amd64-1 | XXXdarwin-darwin-amd64-1)
|
||||
echo
|
||||
echo '# Testing race detector.'
|
||||
go test -race -i runtime/race flag
|
||||
|
|
12
test/live.go
12
test/live.go
|
@ -182,3 +182,15 @@ func f12() *int {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// incorrectly placed VARDEF annotations can cause missing liveness annotations.
|
||||
// this used to be missing the fact that s is live during the call to g13 (because it is
|
||||
// needed for the call to h13).
|
||||
|
||||
func f13() {
|
||||
s := "hello"
|
||||
s = h13(s, g13(s)) // ERROR "live at call to g13: s"
|
||||
}
|
||||
|
||||
func g13(string) string
|
||||
func h13(string, string) string
|
||||
|
|
|
@ -7,18 +7,22 @@
|
|||
// Test that code compiles without
|
||||
// "internal error: ... recorded as live on entry" errors
|
||||
// from the liveness code.
|
||||
//
|
||||
// This code contains methods or other construct that
|
||||
// trigger the generation of wrapper functions with no
|
||||
// clear line number (they end up using line 1), and those
|
||||
// would have annotations printed if we used -live=1,
|
||||
// like the live.go test does.
|
||||
// Instead, this test relies on the fact that the liveness
|
||||
// analysis turns any non-live parameter on entry into
|
||||
// a compile error. Compiling successfully means that bug
|
||||
// has been avoided.
|
||||
|
||||
package main
|
||||
|
||||
// The liveness analysis used to get confused by the tail return
|
||||
// instruction in the wrapper methods generated for T1.M and (*T1).M,
|
||||
// causing a spurious "live at entry: ~r1" for the return result.
|
||||
// This test is checking that there is no such message.
|
||||
// We cannot use live.go because it runs with -live on, which will
|
||||
// generate (correct) messages about the wrapper's receivers
|
||||
// being live on entry, but those messages correspond to no
|
||||
// source line in the file, so they are given at line 1, which we
|
||||
// cannot annotate. Not using -live here avoids that problem.
|
||||
|
||||
type T struct {
|
||||
}
|
||||
|
@ -28,3 +32,15 @@ func (t *T) M() *int
|
|||
type T1 struct {
|
||||
*T
|
||||
}
|
||||
|
||||
// Liveness analysis used to have the VARDEFs in the wrong place,
|
||||
// causing a temporary to appear live on entry.
|
||||
|
||||
func f1(pkg, typ, meth string) {
|
||||
panic("value method " + pkg + "." + typ + "." + meth + " called using nil *" + typ + " pointer")
|
||||
}
|
||||
|
||||
func f2() interface{} {
|
||||
return new(int)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue