Commit b700cb49 authored by Russ Cox's avatar Russ Cox

cmd/gc: shorten temporary lifetimes when possible

The new channel and map runtime routines take pointers
to values, typically temporaries. Without help, the compiler
cannot tell when those temporaries stop being needed,
because it isn't sure what happened to the pointer.
Arrange to insert explicit VARKILL instructions for these
temporaries so that the liveness analysis can avoid seeing
them as "ambiguously live".

The change is made in order.c, which was already in charge of
introducing temporaries to preserve the order-of-evaluation
guarantees. Now its job has expanded to include introducing
temporaries as needed by runtime routines, and then also
inserting the VARKILL annotations for all these temporaries,
so that their lifetimes can be shortened.

In order to do its job for the map runtime routines, order.c arranges
that all map lookups or map assignments have the form:

        x = m[k]
        x, y = m[k]
        m[k] = x

where x, y, and k are simple variables (often temporaries).
Likewise, receiving from a channel is now always:

        x = <-c

In order to provide the map guarantee, order.c is responsible for
rewriting x op= y into x = x op y, so that m[k] += z becomes

        t = m[k]
        t2 = t + z
        m[k] = t2

While here, fix a few bugs in order.c's traversal: it was failing to
walk into select and switch case bodies, so order of evaluation
guarantees were not preserved in those situations.
Added tests to test/reorder2.go.

Fixes #7671.

In gc/popt's temporary-merging optimization, allow merging
of temporaries with their address taken as long as the liveness
ranges do not intersect. (There is a good chance of that now
that we have VARKILL annotations to limit the liveness range.)

Explicitly killing temporaries cuts the number of ambiguously
live temporaries that must be zeroed in the godoc binary from
860 to 711, or -17%. There is more work to be done, but this
is a good checkpoint.

Update #7345

LGTM=khr
R=khr
CC=golang-codereviews
https://golang.org/cl/81940043
parent 1ec4d5e9
......@@ -89,7 +89,7 @@ void
markautoused(Prog* p)
{
for (; p; p = p->link) {
if (p->as == ATYPE || p->as == AVARDEF)
if (p->as == ATYPE || p->as == AVARDEF || p->as == AVARKILL)
continue;
if (p->from.node)
......@@ -111,7 +111,7 @@ fixautoused(Prog* p)
*lp = p->link;
continue;
}
if (p->as == AVARDEF && p->to.node && !p->to.node->used) {
if ((p->as == AVARDEF || p->as == AVARKILL) && 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
......
......@@ -287,7 +287,7 @@ subprop(Flow *r0)
if(uniqs(r) == nil)
break;
p = r->prog;
if(p->as == AVARDEF)
if(p->as == AVARDEF || p->as == AVARKILL)
continue;
proginfo(&info, p);
if(info.flags & Call)
......@@ -1167,6 +1167,7 @@ copyu(Prog *p, Adr *v, Adr *s)
case APCDATA:
case AFUNCDATA:
case AVARDEF:
case AVARKILL:
return 0;
}
}
......
......@@ -30,6 +30,7 @@ static ProgInfo progtable[ALAST] = {
[AUSEFIELD]= {OK},
[ACHECKNIL]= {LeftRead},
[AVARDEF]= {Pseudo | RightWrite},
[AVARKILL]= {Pseudo | RightWrite},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
......
......@@ -213,7 +213,7 @@ regopt(Prog *firstp)
for(r = firstr; r != R; r = (Reg*)r->f.link) {
p = r->f.prog;
if(p->as == AVARDEF)
if(p->as == AVARDEF || p->as == AVARKILL)
continue;
proginfo(&info, p);
......
......@@ -199,6 +199,7 @@ enum as
APCDATA,
ACHECKNIL,
AVARDEF,
AVARKILL,
AMRC, // MRC/MCR
......
......@@ -83,7 +83,7 @@ void
markautoused(Prog* p)
{
for (; p; p = p->link) {
if (p->as == ATYPE || p->as == AVARDEF)
if (p->as == ATYPE || p->as == AVARDEF || p->as == AVARKILL)
continue;
if (p->from.node)
......@@ -105,7 +105,7 @@ fixautoused(Prog *p)
*lp = p->link;
continue;
}
if (p->as == AVARDEF && p->to.node && !p->to.node->used) {
if ((p->as == AVARDEF || p->as == AVARKILL) && 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
......
......@@ -573,7 +573,7 @@ subprop(Flow *r0)
break;
}
p = r->prog;
if(p->as == AVARDEF)
if(p->as == AVARDEF || p->as == AVARKILL)
continue;
proginfo(&info, p);
if(info.flags & Call) {
......@@ -790,7 +790,7 @@ copyu(Prog *p, Adr *v, Adr *s)
return 0;
}
if(p->as == AVARDEF)
if(p->as == AVARDEF || p->as == AVARKILL)
return 0;
proginfo(&info, p);
......
......@@ -42,6 +42,7 @@ static ProgInfo progtable[ALAST] = {
[AUSEFIELD]= {OK},
[ACHECKNIL]= {LeftRead},
[AVARDEF]= {Pseudo | RightWrite},
[AVARKILL]= {Pseudo | RightWrite},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
......
......@@ -199,7 +199,7 @@ regopt(Prog *firstp)
for(r = firstr; r != R; r = (Reg*)r->f.link) {
p = r->f.prog;
if(p->as == AVARDEF)
if(p->as == AVARDEF || p->as == AVARKILL)
continue;
proginfo(&info, p);
......
......@@ -763,6 +763,7 @@ enum as
APCDATA,
ACHECKNIL,
AVARDEF,
AVARKILL,
ALAST
};
......
......@@ -82,7 +82,7 @@ void
markautoused(Prog* p)
{
for (; p; p = p->link) {
if (p->as == ATYPE || p->as == AVARDEF)
if (p->as == ATYPE || p->as == AVARDEF || p->as == AVARKILL)
continue;
if (p->from.node)
......@@ -104,7 +104,7 @@ fixautoused(Prog* p)
*lp = p->link;
continue;
}
if (p->as == AVARDEF && p->to.node && !p->to.node->used) {
if ((p->as == AVARDEF || p->as == AVARKILL) && 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
......
......@@ -387,7 +387,7 @@ subprop(Flow *r0)
if(uniqs(r) == nil)
break;
p = r->prog;
if(p->as == AVARDEF)
if(p->as == AVARDEF || p->as == AVARKILL)
continue;
proginfo(&info, p);
if(info.flags & Call)
......@@ -586,7 +586,7 @@ copyu(Prog *p, Adr *v, Adr *s)
return 0;
}
if(p->as == AVARDEF)
if(p->as == AVARDEF || p->as == AVARKILL)
return 0;
proginfo(&info, p);
......
......@@ -42,6 +42,7 @@ static ProgInfo progtable[ALAST] = {
[AUSEFIELD]= {OK},
[ACHECKNIL]= {LeftRead},
[AVARDEF]= {Pseudo | RightWrite},
[AVARKILL]= {Pseudo | RightWrite},
// NOP is an internal no-op that also stands
// for USED and SET annotations, not the Intel opcode.
......
......@@ -169,7 +169,7 @@ regopt(Prog *firstp)
for(r = firstr; r != R; r = (Reg*)r->f.link) {
p = r->f.prog;
if(p->as == AVARDEF)
if(p->as == AVARDEF || p->as == AVARKILL)
continue;
proginfo(&info, p);
......
......@@ -580,6 +580,7 @@ enum as
APCDATA,
ACHECKNIL,
AVARDEF,
AVARKILL,
ALAST
};
......
......@@ -495,6 +495,11 @@ gen(Node *n)
case OCHECKNIL:
cgen_checknil(n->left);
break;
case OVARKILL:
gvarkill(n->left);
break;
}
ret:
......
......@@ -581,6 +581,7 @@ enum
OCLOSUREVAR, // variable reference at beginning of closure function
OCFUNC, // reference to c function pointer (not go func value)
OCHECKNIL, // emit code to ensure pointer/interface not nil
OVARKILL, // variable is dead
// arch-specific registers
OREGISTER, // a register, such as AX.
......@@ -1502,6 +1503,7 @@ void gdatastring(Node*, Strlit*);
void ggloblnod(Node *nam);
void ggloblsym(Sym *s, int32 width, int dupok, int rodata);
void gvardef(Node*);
void gvarkill(Node*);
Prog* gjmp(Prog*);
void gused(Node*);
void movelarge(NodeList*);
......
This diff is collapsed.
......@@ -79,10 +79,16 @@ makefuncdatasym(char *namefmt, int64 funcdatakind)
// 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)
// there are complications around interface values, slices, and strings,
// all of which cannot be treated as individual words.
//
// VARKILL is the opposite of VARDEF: it marks a value as no longer needed,
// even if its address has been taken. That is, a VARKILL annotation asserts
// that its argument is certainly dead, for use when the liveness analysis
// would not otherwise be able to deduce that fact.
static void
gvardefx(Node *n, int as)
{
if(n == N)
fatal("gvardef nil");
......@@ -94,20 +100,32 @@ gvardef(Node *n)
case PAUTO:
case PPARAM:
case PPARAMOUT:
gins(AVARDEF, N, n);
gins(as, N, n);
}
}
void
gvardef(Node *n)
{
gvardefx(n, AVARDEF);
}
void
gvarkill(Node *n)
{
gvardefx(n, AVARKILL);
}
static void
removevardef(Prog *firstp)
{
Prog *p;
for(p = firstp; p != P; p = p->link) {
while(p->link != P && p->link->as == AVARDEF)
while(p->link != P && (p->link->as == AVARDEF || p->link->as == AVARKILL))
p->link = p->link->link;
if(p->to.type == D_BRANCH)
while(p->to.u.branch != P && p->to.u.branch->as == AVARDEF)
while(p->to.u.branch != P && (p->to.u.branch->as == AVARDEF || p->to.u.branch->as == AVARKILL))
p->to.u.branch = p->to.u.branch->link;
}
}
......
......@@ -743,8 +743,9 @@ Next:
if(pos == -1)
goto Next1;
if(to->node->addrtaken) {
bvset(avarinit, pos);
if(prog->as == AVARDEF)
if(prog->as != AVARKILL)
bvset(avarinit, pos);
if(prog->as == AVARDEF || prog->as == AVARKILL)
bvset(varkill, pos);
} else {
if(info.flags & (RightRead | RightAddr))
......@@ -1310,7 +1311,7 @@ livenessprologue(Liveness *lv)
{
BasicBlock *bb;
Bvec *uevar, *varkill, *avarinit;
Prog *prog;
Prog *p;
int32 i;
int32 nvars;
......@@ -1322,14 +1323,25 @@ livenessprologue(Liveness *lv)
bb = *(BasicBlock**)arrayget(lv->cfg, i);
// Walk the block instructions backward and update the block
// effects with the each prog effects.
for(prog = bb->last; prog != nil; prog = prog->opt) {
progeffects(prog, lv->vars, uevar, varkill, avarinit);
for(p = bb->last; p != nil; p = p->opt) {
progeffects(p, lv->vars, uevar, varkill, avarinit);
if(debuglive >= 3)
printeffects(prog, uevar, varkill, avarinit);
printeffects(p, uevar, varkill, avarinit);
bvor(lv->varkill[i], lv->varkill[i], varkill);
bvandnot(lv->uevar[i], lv->uevar[i], varkill);
bvor(lv->uevar[i], lv->uevar[i], uevar);
}
// Walk the block instructions forward to update avarinit bits.
// avarinit describes the effect at the end of the block, not the beginning.
bvresetall(varkill);
for(p = bb->first;; p = p->link) {
progeffects(p, lv->vars, uevar, varkill, avarinit);
if(debuglive >= 3)
printeffects(p, uevar, varkill, avarinit);
bvandnot(lv->avarinit[i], lv->avarinit[i], varkill);
bvor(lv->avarinit[i], lv->avarinit[i], avarinit);
if(p == bb->last)
break;
}
}
free(uevar);
......@@ -1385,6 +1397,8 @@ livenesssolve(Liveness *lv)
bvand(all, all, lv->avarinitall[pred->rpo]);
}
}
bvandnot(any, any, lv->varkill[rpo]);
bvandnot(all, all, lv->varkill[rpo]);
bvor(any, any, lv->avarinit[rpo]);
bvor(all, all, lv->avarinit[rpo]);
if(bvcmp(any, lv->avarinitany[rpo])) {
......@@ -1511,6 +1525,8 @@ livenessepilogue(Liveness *lv)
// Seed the maps with information about the addrtaken variables.
for(p = bb->first;; p = p->link) {
progeffects(p, lv->vars, uevar, varkill, avarinit);
bvandnot(any, any, varkill);
bvandnot(all, all, varkill);
bvor(any, any, avarinit);
bvor(all, all, avarinit);
......@@ -1572,7 +1588,7 @@ livenessepilogue(Liveness *lv)
break;
}
if(debuglive >= 1 && strcmp(curfn->nname->sym->name, "init") != 0) {
if(debuglive >= 1 && strcmp(curfn->nname->sym->name, "init") != 0 && curfn->nname->sym->name[0] != '.') {
nmsg = arraylength(lv->livepointers);
startmsg = nmsg;
msg = xmalloc(nmsg*sizeof msg[0]);
......
......@@ -531,10 +531,11 @@ startcmp(const void *va, const void *vb)
static int
canmerge(Node *n)
{
return n->class == PAUTO && !n->addrtaken && strncmp(n->sym->name, "autotmp", 7) == 0;
return n->class == PAUTO && strncmp(n->sym->name, "autotmp", 7) == 0;
}
static void mergewalk(TempVar*, TempFlow*, uint32);
static void varkillwalk(TempVar*, TempFlow*, uint32);
void
mergetemp(Prog *firstp)
......@@ -555,7 +556,7 @@ mergetemp(Prog *firstp)
g = flowstart(firstp, sizeof(TempFlow));
if(g == nil)
return;
// Build list of all mergeable variables.
nvar = 0;
for(l = curfn->dcl; l != nil; l = l->next)
......@@ -651,6 +652,11 @@ mergetemp(Prog *firstp)
gen++;
for(r = v->use; r != nil; r = r->uselink)
mergewalk(v, r, gen);
if(v->addr) {
gen++;
for(r = v->use; r != nil; r = r->uselink)
varkillwalk(v, r, gen);
}
}
// Sort variables by start.
......@@ -670,7 +676,7 @@ mergetemp(Prog *firstp)
nfree = nvar;
for(i=0; i<nvar; i++) {
v = bystart[i];
if(v->addr || v->removed)
if(v->removed)
continue;
// Expire no longer in use.
......@@ -683,7 +689,12 @@ mergetemp(Prog *firstp)
t = v->node->type;
for(j=nfree; j<nvar; j++) {
v1 = inuse[j];
if(eqtype(t, v1->node->type)) {
// Require the types to match but also require the addrtaken bits to match.
// If a variable's address is taken, that disables registerization for the individual
// words of the variable (for example, the base,len,cap of a slice).
// We don't want to merge a non-addressed var with an addressed one and
// inhibit registerization of the former.
if(eqtype(t, v1->node->type) && v->node->addrtaken == v1->node->addrtaken) {
inuse[j] = inuse[nfree++];
if(v1->merge)
v->merge = v1->merge;
......@@ -776,6 +787,29 @@ mergewalk(TempVar *v, TempFlow *r0, uint32 gen)
mergewalk(v, r2, gen);
}
static void
varkillwalk(TempVar *v, TempFlow *r0, uint32 gen)
{
Prog *p;
TempFlow *r1, *r;
for(r1 = r0; r1 != nil; r1 = (TempFlow*)r1->f.s1) {
if(r1->f.active == gen)
break;
r1->f.active = gen;
p = r1->f.prog;
if(v->end < p->pc)
v->end = p->pc;
if(v->start > p->pc)
v->start = p->pc;
if(p->as == ARET || (p->as == AVARKILL && p->to.node == v->node))
break;
}
for(r = r0; r != r1; r = (TempFlow*)r->f.s1)
varkillwalk(v, (TempFlow*)r->f.s2, gen);
}
// Eliminate redundant nil pointer checks.
//
// The code generation pass emits a CHECKNIL for every possibly nil pointer.
......
......@@ -166,6 +166,7 @@ racewalknode(Node **np, NodeList **init, int wr, int skip)
goto ret;
case OCFUNC:
case OVARKILL:
// can't matter
goto ret;
......
......@@ -164,8 +164,7 @@ walkrange(Node *n)
}
n->ntest = nod(OLT, hv1, hn);
n->nincr = nod(OASOP, hv1, nodintconst(1));
n->nincr->etype = OADD;
n->nincr = nod(OAS, hv1, nod(OADD, hv1, nodintconst(1)));
if(v2 == N)
body = list1(nod(OAS, v1, hv1));
else {
......
......@@ -110,7 +110,9 @@ walkselect(Node *sel)
}
// optimization: one-case select: single op.
if(i == 1) {
// TODO(rsc): Reenable optimization once order.c can handle it.
// golang.org/issue/7672.
if(0 && i == 1) {
cas = sel->list->n;
setlineno(cas);
l = cas->ninit;
......
......@@ -838,7 +838,7 @@ maplit(int ctxt, Node *n, Node *var, NodeList **init)
int nerr;
int64 b;
Type *t, *tk, *tv, *t1;
Node *vstat, *index, *value;
Node *vstat, *index, *value, *key, *val;
Sym *syma, *symb;
USED(ctxt);
......@@ -952,8 +952,7 @@ ctxt = 0;
a->ninit = list1(nod(OAS, index, nodintconst(0)));
a->ntest = nod(OLT, index, nodintconst(t->bound));
a->nincr = nod(OASOP, index, nodintconst(1));
a->nincr->etype = OADD;
a->nincr = nod(OAS, index, nod(OADD, index, nodintconst(1)));
typecheck(&a, Etop);
walkstmt(&a);
......@@ -961,6 +960,8 @@ ctxt = 0;
}
// put in dynamic entries one-at-a-time
key = nil;
val = nil;
for(l=n->list; l; l=l->next) {
r = l->n;
......@@ -971,15 +972,37 @@ ctxt = 0;
if(isliteral(index) && isliteral(value))
continue;
// build list of var[c] = expr.
// use temporary so that mapassign1 can have addressable key, val.
if(key == nil) {
key = temp(var->type->down);
val = temp(var->type->type);
}
a = nod(OAS, key, r->left);
typecheck(&a, Etop);
walkstmt(&a);
*init = list(*init, a);
a = nod(OAS, val, r->right);
typecheck(&a, Etop);
walkstmt(&a);
*init = list(*init, a);
// build list of var[c] = expr
a = nod(OINDEX, var, r->left);
a = nod(OAS, a, r->right);
a = nod(OAS, nod(OINDEX, var, key), val);
typecheck(&a, Etop);
walkexpr(&a, init);
walkstmt(&a);
*init = list(*init, a);
if(nerr != nerrors)
break;
}
if(key != nil) {
a = nod(OVARKILL, key, N);
typecheck(&a, Etop);
*init = list(*init, a);
a = nod(OVARKILL, var, N);
typecheck(&a, Etop);
*init = list(*init, a);
}
}
......
......@@ -950,7 +950,7 @@ reswitch:
r = n->right;
if(r->type == T)
goto error;
r = assignconv(r, l->type->type, "send");
n->right = assignconv(r, l->type->type, "send");
// TODO: more aggressive
n->etype = 0;
n->type = T;
......@@ -1655,6 +1655,7 @@ reswitch:
case OGOTO:
case OLABEL:
case OXFALL:
case OVARKILL:
ok |= Etop;
goto ret;
......
......@@ -202,6 +202,7 @@ walkstmt(Node **np)
case ODCLCONST:
case ODCLTYPE:
case OCHECKNIL:
case OVARKILL:
break;
case OBLOCK:
......@@ -361,8 +362,8 @@ void
walkexpr(Node **np, NodeList **init)
{
Node *r, *l, *var, *a;
Node *map, *key, *keyvar;
NodeList *ll, *lr, *lpost;
Node *map, *key;
NodeList *ll, *lr;
Type *t;
int et, old_safemode;
int64 v;
......@@ -476,7 +477,6 @@ walkexpr(Node **np, NodeList **init)
case ORSH:
walkexpr(&n->left, init);
walkexpr(&n->right, init);
shiftwalked:
t = n->left->type;
n->bounded = bounded(n->right, 8*t->width);
if(debug['m'] && n->etype && !isconst(n->right, CTINT))
......@@ -602,13 +602,32 @@ walkexpr(Node **np, NodeList **init)
case OAS:
*init = concat(*init, n->ninit);
n->ninit = nil;
walkexpr(&n->left, init);
n->left = safeexpr(n->left, init);
if(oaslit(n, init))
goto ret;
walkexpr(&n->right, init);
if(n->right == N)
goto ret;
switch(n->right->op) {
default:
walkexpr(&n->right, init);
break;
case ORECV:
// x = <-c; n->left is x, n->right->left is c.
// orderstmt made sure x is addressable.
walkexpr(&n->right->left, init);
n1 = nod(OADDR, n->left, N);
r = n->right->left; // the channel
n = mkcall1(chanfn("chanrecv1", 2, r->type), T, init, typename(r->type), r, n1);
walkexpr(&n, init);
goto ret;
}
if(n->left != N && n->right != N) {
r = convas(nod(OAS, n->left, n->right), init);
r->dodata = n->dodata;
......@@ -618,7 +637,6 @@ walkexpr(Node **np, NodeList **init)
goto ret;
case OAS2:
as2:
*init = concat(*init, n->ninit);
n->ninit = nil;
walkexprlistsafe(n->list, init);
......@@ -637,52 +655,26 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&r, init);
l = n->list->n;
// all the really hard stuff - explicit function calls and so on -
// is gone, but map assignments remain.
// if there are map assignments here, assign via
// temporaries, because ascompatet assumes
// the targets can be addressed without function calls
// and map index has an implicit one.
lpost = nil;
if(l->op == OINDEXMAP) {
var = temp(l->type);
n->list->n = var;
a = nod(OAS, l, var);
typecheck(&a, Etop);
lpost = list(lpost, a);
}
l = n->list->next->n;
if(l->op == OINDEXMAP) {
var = temp(l->type);
n->list->next->n = var;
a = nod(OAS, l, var);
typecheck(&a, Etop);
lpost = list(lpost, a);
}
ll = ascompatet(n->op, n->list, &r->type, 0, init);
walkexprlist(lpost, init);
n = liststmt(concat(concat(list1(r), ll), lpost));
n = liststmt(concat(list1(r), ll));
goto ret;
case OAS2RECV:
// x, y = <-c
// orderstmt made sure x is addressable.
*init = concat(*init, n->ninit);
n->ninit = nil;
r = n->rlist->n;
walkexprlistsafe(n->list, init);
walkexpr(&r->left, init);
var = temp(r->left->type->type);
if(haspointers(var->type)) {
// clear for garbage collector - var is live during chanrecv2 call.
a = nod(OAS, var, N);
typecheck(&a, Etop);
*init = concat(*init, list1(a));
}
n1 = nod(OADDR, var, N);
n1 = nod(OADDR, n->list->n, N);
n1->etype = 1; // addr does not escape
fn = chanfn("chanrecv2", 2, r->left->type);
r = mkcall1(fn, types[TBOOL], init, typename(r->left->type), r->left, n1);
n->op = OAS2;
n->rlist = concat(list1(var), list1(r));
goto as2;
n = nod(OAS, n->list->next->n, r);
typecheck(&n, Etop);
goto ret;
case OAS2MAPR:
// a,b = m[i];
......@@ -714,15 +706,8 @@ walkexpr(Node **np, NodeList **init)
key = r->right;
} else {
// standard version takes key by reference
if(islvalue(r->right)) {
key = nod(OADDR, r->right, N);
} else {
keyvar = temp(t->down);
n1 = nod(OAS, keyvar, r->right);
typecheck(&n1, Etop);
*init = list(*init, n1);
key = nod(OADDR, keyvar, N);
}
// orderexpr made sure key is addressable.
key = nod(OADDR, r->right, N);
p = "mapaccess2";
}
......@@ -741,7 +726,6 @@ walkexpr(Node **np, NodeList **init)
n->list->n = var;
walkexpr(&n, init);
*init = list(*init, n);
n = nod(OAS, a, nod(OIND, var, N));
typecheck(&n, Etop);
walkexpr(&n, init);
......@@ -758,15 +742,8 @@ walkexpr(Node **np, NodeList **init)
key = n->list->next->n;
walkexpr(&map, init);
walkexpr(&key, init);
if(islvalue(key)) {
key = nod(OADDR, key, N);
} else {
keyvar = temp(key->type);
n1 = nod(OAS, keyvar, key);
typecheck(&n1, Etop);
*init = list(*init, n1);
key = nod(OADDR, keyvar, N);
}
// orderstmt made sure key is addressable.
key = nod(OADDR, key, N);
t = map->type;
n = mkcall1(mapfndel("mapdelete", t), T, init, typename(t), map, key);
goto ret;
......@@ -981,51 +958,6 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&n->left, init);
goto ret;
case OASOP:
if(n->etype == OANDNOT) {
n->etype = OAND;
n->right = nod(OCOM, n->right, N);
typecheck(&n->right, Erv);
}
n->left = safeexpr(n->left, init);
walkexpr(&n->left, init);
l = n->left;
walkexpr(&n->right, init);
/*
* on 32-bit arch, rewrite 64-bit ops into l = l op r.
* on 386, rewrite float ops into l = l op r.
* everywhere, rewrite map ops into l = l op r.
* everywhere, rewrite string += into l = l op r.
* everywhere, rewrite integer/complex /= into l = l op r.
* TODO(rsc): Maybe this rewrite should be done always?
*/
et = n->left->type->etype;
if((widthptr == 4 && (et == TUINT64 || et == TINT64)) ||
(thechar == '8' && isfloat[et]) ||
l->op == OINDEXMAP ||
et == TSTRING ||
(!isfloat[et] && n->etype == ODIV) ||
n->etype == OMOD) {
l = safeexpr(n->left, init);
a = l;
if(a->op == OINDEXMAP) {
// map index has "lhs" bit set in a->etype.
// make a copy so we can clear it on the rhs.
a = nod(OXXX, N, N);
*a = *l;
a->etype = 0;
}
r = nod(OAS, l, nod(n->etype, a, n->right));
typecheck(&r, Etop);
walkexpr(&r, init);
n = r;
goto ret;
}
if(n->etype == OLSH || n->etype == ORSH)
goto shiftwalked;
goto ret;
case OANDNOT:
walkexpr(&n->left, init);
n->op = OAND;
......@@ -1159,16 +1091,9 @@ walkexpr(Node **np, NodeList **init)
// fast versions take key by value
key = n->right;
} else {
// standard version takes key by reference
if(islvalue(n->right)) {
key = nod(OADDR, n->right, N);
} else {
keyvar = temp(t->down);
n1 = nod(OAS, keyvar, n->right);
typecheck(&n1, Etop);
*init = list(*init, n1);
key = nod(OADDR, keyvar, N);
}
// standard version takes key by reference.
// orderexpr made sure key is addressable.
key = nod(OADDR, n->right, N);
p = "mapaccess1";
}
n = mkcall1(mapfn(p, t), ptrto(t->type), init, typename(t), n->left, key);
......@@ -1181,20 +1106,7 @@ walkexpr(Node **np, NodeList **init)
goto ret;
case ORECV:
walkexpr(&n->left, init);
var = temp(n->left->type->type);
if(haspointers(var->type)) {
// clear for garbage collector - var is live during chanrecv1 call.
a = nod(OAS, var, N);
typecheck(&a, Etop);
*init = concat(*init, list1(a));
}
n1 = nod(OADDR, var, N);
n = mkcall1(chanfn("chanrecv1", 2, n->left->type), T, init, typename(n->left->type), n->left, n1);
walkexpr(&n, init);
*init = list(*init, n);
n = var;
goto ret;
fatal("walkexpr ORECV"); // should see inside OAS only
case OSLICE:
if(n->right != N && n->right->left == N && n->right->right == N) { // noop
......@@ -1462,15 +1374,7 @@ walkexpr(Node **np, NodeList **init)
n1 = n->right;
n1 = assignconv(n1, n->left->type->type, "chan send");
walkexpr(&n1, init);
if(islvalue(n1)) {
n1 = nod(OADDR, n1, N);
} else {
var = temp(n1->type);
n1 = nod(OAS, var, n1);
typecheck(&n1, Etop);
*init = list(*init, n1);
n1 = nod(OADDR, var, N);
}
n1 = nod(OADDR, n1, N);
n = mkcall1(chanfn("chansend1", 2, n->left->type), T, init, typename(n->left->type), n->left, n1);
goto ret;
......@@ -2008,8 +1912,7 @@ static Node*
convas(Node *n, NodeList **init)
{
Type *lt, *rt;
Node *map, *key, *keyvar, *val, *valvar;
Node *n1;
Node *map, *key, *val;
if(n->op != OAS)
fatal("convas: not OAS %O", n->op);
......@@ -2036,24 +1939,9 @@ convas(Node *n, NodeList **init)
walkexpr(&map, init);
walkexpr(&key, init);
walkexpr(&val, init);
if(islvalue(key)) {
key = nod(OADDR, key, N);
} else {
keyvar = temp(key->type);
n1 = nod(OAS, keyvar, key);
typecheck(&n1, Etop);
*init = list(*init, n1);
key = nod(OADDR, keyvar, N);
}
if(islvalue(val)) {
val = nod(OADDR, val, N);
} else {
valvar = temp(val->type);
n1 = nod(OAS, valvar, val);
typecheck(&n1, Etop);
*init = list(*init, n1);
val = nod(OADDR, valvar, N);
}
// orderexpr made sure key and val are addressable.
key = nod(OADDR, key, N);
val = nod(OADDR, val, N);
n = mkcall1(mapfn("mapassign1", map->type), T, init,
typename(map->type), map, key, val);
goto out;
......
......@@ -212,3 +212,121 @@ func f15() {
}
func g15() string
// Checking that various temporaries do not persist or cause
// ambiguously live values that must be zeroed.
// The exact temporary names are inconsequential but we are
// trying to check that there is only one at any given site,
// and also that none show up in "ambiguously live" messages.
var m map[string]int
func f16() {
if b {
delete(m, "hi") // ERROR "live at call to mapdelete: autotmp_[0-9]+$"
}
delete(m, "hi") // ERROR "live at call to mapdelete: autotmp_[0-9]+$"
delete(m, "hi") // ERROR "live at call to mapdelete: autotmp_[0-9]+$"
}
var m2s map[string]*byte
var m2 map[[2]string]*byte
var x2 [2]string
var bp *byte
func f17a() {
// value temporary only
if b {
m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
}
m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
m2[x2] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
}
func f17b() {
// key temporary only
if b {
m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
}
m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
m2s["x"] = bp // ERROR "live at call to mapassign1: autotmp_[0-9]+$"
}
func f17c() {
// key and value temporaries
if b {
m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
}
m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
m2s["x"] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
}
func g18() [2]string
func f18() {
// key temporary for mapaccess.
// temporary introduced by orderexpr.
var z *byte
if b {
z = m2[g18()] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
}
z = m2[g18()] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
z = m2[g18()] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
print(z)
}
var ch chan *byte
func f19() {
// dest temporary for channel receive.
var z *byte
if b {
z = <-ch // ERROR "live at call to chanrecv1: autotmp_[0-9]+$"
}
z = <-ch // ERROR "live at call to chanrecv1: autotmp_[0-9]+$"
z = <-ch // ERROR "live at call to chanrecv1: autotmp_[0-9]+$"
print(z)
}
func f20() {
// src temporary for channel send
if b {
ch <- nil // ERROR "live at call to chansend1: autotmp_[0-9]+$"
}
ch <- nil // ERROR "live at call to chansend1: autotmp_[0-9]+$"
ch <- nil // ERROR "live at call to chansend1: autotmp_[0-9]+$"
}
func f21() {
// key temporary for mapaccess using array literal key.
var z *byte
if b {
z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
}
z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
z = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
print(z)
}
func f23() {
// key temporary for two-result map access using array literal key.
var z *byte
var ok bool
if b {
z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: autotmp_[0-9]+$"
}
z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: autotmp_[0-9]+$"
z, ok = m2[[2]string{"x", "y"}] // ERROR "live at call to mapaccess2: autotmp_[0-9]+$"
print(z, ok)
}
func f24() {
// key temporary for map access using array literal key.
// value temporary too.
if b {
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]+$"
m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
}
......@@ -167,6 +167,175 @@ func main() {
err++
}
log = ""
x := 0
switch x {
case 0:
if a("1")("2")("3"); log != "a(1)a(2)a(3)" {
println("in switch, expecting a(1)a(2)a(3) , got ", log)
err++
}
log = ""
if t.a("1").a(t.b("2")); log != "a(1)b(2)a(2)" {
println("in switch, expecting a(1)b(2)a(2), got ", log)
err++
}
log = ""
if a("3")(b("4"))(b("5")); log != "a(3)b(4)a(4)b(5)a(5)" {
println("in switch, expecting a(3)b(4)a(4)b(5)a(5), got ", log)
err++
}
log = ""
var i I = T1(0)
if i.a("6").a(i.b("7")).a(i.b("8")).a(i.b("9")); log != "a(6)b(7)a(7)b(8)a(8)b(9)a(9)" {
println("in switch, expecting a(6)ba(7)ba(8)ba(9), got", log)
err++
}
log = ""
}
c := make(chan int, 1)
c <- 1
select {
case c <- 0:
case c <- 1:
case <-c:
if a("1")("2")("3"); log != "a(1)a(2)a(3)" {
println("in select1, expecting a(1)a(2)a(3) , got ", log)
err++
}
log = ""
if t.a("1").a(t.b("2")); log != "a(1)b(2)a(2)" {
println("in select1, expecting a(1)b(2)a(2), got ", log)
err++
}
log = ""
if a("3")(b("4"))(b("5")); log != "a(3)b(4)a(4)b(5)a(5)" {
println("in select1, expecting a(3)b(4)a(4)b(5)a(5), got ", log)
err++
}
log = ""
var i I = T1(0)
if i.a("6").a(i.b("7")).a(i.b("8")).a(i.b("9")); log != "a(6)b(7)a(7)b(8)a(8)b(9)a(9)" {
println("in select1, expecting a(6)ba(7)ba(8)ba(9), got", log)
err++
}
log = ""
}
c <- 1
select {
case <-c:
if a("1")("2")("3"); log != "a(1)a(2)a(3)" {
println("in select2, expecting a(1)a(2)a(3) , got ", log)
err++
}
log = ""
if t.a("1").a(t.b("2")); log != "a(1)b(2)a(2)" {
println("in select2, expecting a(1)b(2)a(2), got ", log)
err++
}
log = ""
if a("3")(b("4"))(b("5")); log != "a(3)b(4)a(4)b(5)a(5)" {
println("in select2, expecting a(3)b(4)a(4)b(5)a(5), got ", log)
err++
}
log = ""
var i I = T1(0)
if i.a("6").a(i.b("7")).a(i.b("8")).a(i.b("9")); log != "a(6)b(7)a(7)b(8)a(8)b(9)a(9)" {
println("in select2, expecting a(6)ba(7)ba(8)ba(9), got", log)
err++
}
log = ""
}
c <- 1
select {
default:
case c<-1:
case <-c:
if a("1")("2")("3"); log != "a(1)a(2)a(3)" {
println("in select3, expecting a(1)a(2)a(3) , got ", log)
err++
}
log = ""
if t.a("1").a(t.b("2")); log != "a(1)b(2)a(2)" {
println("in select3, expecting a(1)b(2)a(2), got ", log)
err++
}
log = ""
if a("3")(b("4"))(b("5")); log != "a(3)b(4)a(4)b(5)a(5)" {
println("in select3, expecting a(3)b(4)a(4)b(5)a(5), got ", log)
err++
}
log = ""
var i I = T1(0)
if i.a("6").a(i.b("7")).a(i.b("8")).a(i.b("9")); log != "a(6)b(7)a(7)b(8)a(8)b(9)a(9)" {
println("in select3, expecting a(6)ba(7)ba(8)ba(9), got", log)
err++
}
log = ""
}
c <- 1
select {
default:
case <-c:
if a("1")("2")("3"); log != "a(1)a(2)a(3)" {
println("in select4, expecting a(1)a(2)a(3) , got ", log)
err++
}
log = ""
if t.a("1").a(t.b("2")); log != "a(1)b(2)a(2)" {
println("in select4, expecting a(1)b(2)a(2), got ", log)
err++
}
log = ""
if a("3")(b("4"))(b("5")); log != "a(3)b(4)a(4)b(5)a(5)" {
println("in select4, expecting a(3)b(4)a(4)b(5)a(5), got ", log)
err++
}
log = ""
var i I = T1(0)
if i.a("6").a(i.b("7")).a(i.b("8")).a(i.b("9")); log != "a(6)b(7)a(7)b(8)a(8)b(9)a(9)" {
println("in select4, expecting a(6)ba(7)ba(8)ba(9), got", log)
err++
}
log = ""
}
select {
case <-c:
case <-c:
default:
if a("1")("2")("3"); log != "a(1)a(2)a(3)" {
println("in select5, expecting a(1)a(2)a(3) , got ", log)
err++
}
log = ""
if t.a("1").a(t.b("2")); log != "a(1)b(2)a(2)" {
println("in select5, expecting a(1)b(2)a(2), got ", log)
err++
}
log = ""
if a("3")(b("4"))(b("5")); log != "a(3)b(4)a(4)b(5)a(5)" {
println("in select5, expecting a(3)b(4)a(4)b(5)a(5), got ", log)
err++
}
log = ""
var i I = T1(0)
if i.a("6").a(i.b("7")).a(i.b("8")).a(i.b("9")); log != "a(6)b(7)a(7)b(8)a(8)b(9)a(9)" {
println("in select5, expecting a(6)ba(7)ba(8)ba(9), got", log)
err++
}
log = ""
}
if err > 0 {
panic("fail")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment