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
// 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);
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)
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);
}
switch(nl->type->etype) {
case TARRAY:
+ if(nl->op == ONAME)
+ gvardef(nl);
nodl.xoffset += Array_array;
nodl.type = ptrto(nl->type->type);
goto yes;
case TSTRING:
+ if(nl->op == ONAME)
+ gvardef(nl);
nodl.xoffset += Array_array;
nodl.type = ptrto(types[TUINT8]);
goto yes;
case TINTER:
+ if(nl->op == ONAME)
+ gvardef(nl);
nodl.xoffset += Array_array;
nodl.type = ptrto(types[TUINT8]);
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;
}
}
*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;
if(debug['g'])
dump("\nclearfat", nl);
- gvardef(nl);
-
w = nl->type->width;
// Avoid taking the address for simple enough types.
if(componentgen(N, nl))
if(uniqs(r) == nil)
break;
p = r->prog;
+ if(p->as == AVARDEF)
+ continue;
proginfo(&info, p);
if(info.flags & Call)
return 0;
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.
// 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);
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)
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);
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;
goto yes;
case TSTRING:
+ if(nl->op == ONAME && nl->class != PEXTERN)
+ gvardef(nl);
nodl.xoffset += Array_array;
nodl.type = ptrto(types[TUINT8]);
goto yes;
case TINTER:
+ if(nl->op == ONAME && nl->class != PEXTERN)
+ gvardef(nl);
nodl.xoffset += Array_array;
nodl.type = ptrto(types[TUINT8]);
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.
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;
}
}
*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;
if(debug['g'])
dump("\nclearfat", nl);
- gvardef(nl);
-
w = nl->type->width;
// Avoid taking the address for simple enough types.
if(componentgen(N, nl))
break;
}
p = r->prog;
+ if(p->as == AVARDEF)
+ continue;
proginfo(&info, p);
if(info.flags & Call) {
if(debug['P'] && debug['v'])
return 0;
}
+ if(p->as == AVARDEF)
+ return 0;
proginfo(&info, p);
if((info.reguse|info.regset) & RtoB(v->type))
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.
// 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);
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)
agen(n, &src);
else
gmove(&tsrc, &src);
+
+ if(res->op == ONAME)
+ gvardef(res);
+
if(res->addable)
agen(res, &dst);
else
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);
goto yes;
case TSTRING:
+ if(nl->op == ONAME && nl->class != PEXTERN)
+ gvardef(nl);
nodl.xoffset += Array_array;
nodl.type = ptrto(types[TUINT8]);
goto yes;
case TINTER:
+ if(nl->op == ONAME && nl->class != PEXTERN)
+ gvardef(nl);
nodl.xoffset += Array_array;
nodl.type = ptrto(types[TUINT8]);
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;
}
}
*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;
if(debug['g'])
dump("\nclearfat", nl);
- gvardef(nl);
-
w = nl->type->width;
// Avoid taking the address for simple enough types.
if(componentgen(N, nl))
if(uniqs(r) == nil)
break;
p = r->prog;
+ if(p->as == AVARDEF)
+ continue;
proginfo(&info, p);
if(info.flags & Call)
return 0;
return 0;
}
+ if(p->as == AVARDEF)
+ return 0;
proginfo(&info, p);
if((info.reguse|info.regset) & RtoB(v->type))
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.
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;
}
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;
/*
* 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)
dump("cgen_dcl", n);
fatal("cgen_dcl");
}
+ if(isfat(n->type))
+ gvardef(n);
if(!(n->class & PHEAP))
return;
if(n->alloc == nil)
// special enough to just evaluate
default:
tempname(&tmp, nr->type);
+ gvardef(&tmp);
cgen_as(&tmp, nr);
gused(&tmp);
}
if(tl == T)
return;
if(isfat(tl)) {
+ if(nl->op == ONAME)
+ gvardef(nl);
clearfat(nl);
return;
}
* 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);
}
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;
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);
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);
}
}
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:
// 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;
}
}
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");
}
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
// 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);
}
}
# 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
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
// 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 {
}
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)
+}
+