1
0
mirror of https://github.com/golang/go synced 2024-10-03 09:21:21 -06:00

cmd/gc: escape analysis to track flow of in to out parameters.

includes step 0: synthesize outparams, from 6600044
step 1: give outparams loopdepth 0 and verify unchanged results
step 2: generate esc:$mask tags, but still tie to sink if a param has mask != 0
next step: use in esccall (and ORETURN with implicit OAS2FUNC) to avoid tying to sink

R=rsc
CC=golang-dev
https://golang.org/cl/6610054
This commit is contained in:
Luuk van Dijk 2012-10-22 10:18:17 +02:00
parent 976ca1a47d
commit 75692424d2
2 changed files with 84 additions and 16 deletions

View File

@ -211,7 +211,48 @@ struct EscState {
NodeList* noesc; // list of possible non-escaping nodes, for printing
};
static Strlit* safetag; // gets slapped on safe parameters' field types for export
static Strlit *tags[16] = { nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil };
static Strlit*
mktag(int mask)
{
Strlit *s;
char buf[40];
switch(mask&EscMask) {
case EscNone:
case EscReturn:
break;
default:
fatal("escape mktag");
}
mask >>= EscBits;
if(mask < nelem(tags) && tags[mask] != nil)
return tags[mask];
snprint(buf, sizeof buf, "esc:%#x", mask);
s = strlit(buf);
if(mask < nelem(tags))
tags[mask] = s;
return s;
}
static int
parsetag(Strlit *note)
{
int em;
if(note == nil)
return EscUnknown;
if(strncmp(note->s, "esc:", 4) != 0)
return EscUnknown;
em = atoi(note->s + 4);
if (em == 0)
return EscNone;
return EscReturn | (em << EscBits);
}
static void
analyze(NodeList *all, int recursive)
@ -229,9 +270,6 @@ analyze(NodeList *all, int recursive)
e->theSink.sym = lookup(".sink");
e->theSink.escloopdepth = -1;
if(safetag == nil)
safetag = strlit("noescape");
for(l=all; l; l=l->next)
if(l->n->op == ODCLFUNC)
l->n->esc = EscFuncPlanned;
@ -284,16 +322,15 @@ escfunc(EscState *e, Node *func)
continue;
switch (ll->n->class) {
case PPARAMOUT:
// output parameters flow to the sink
escflows(e, &e->theSink, ll->n);
ll->n->escloopdepth = e->loopdepth;
// out params are in a loopdepth between the sink and all local variables
ll->n->escloopdepth = 0;
break;
case PPARAM:
if(ll->n->type && !haspointers(ll->n->type))
break;
ll->n->esc = EscNone; // prime for escflood later
e->noesc = list(e->noesc, ll->n);
ll->n->escloopdepth = e->loopdepth;
ll->n->escloopdepth = 1;
break;
}
}
@ -470,8 +507,20 @@ esc(EscState *e, Node *n)
break;
case ORETURN:
for(ll=n->list; ll; ll=ll->next)
escassign(e, &e->theSink, ll->n);
if(count(n->list) == 1 && curfn->type->outtuple > 1) {
// OAS2FUNC in disguise
break;
}
ll=n->list;
for(lr = curfn->dcl; lr && ll; lr=lr->next) {
if (lr->n->op != ONAME || lr->n->class != PPARAMOUT)
continue;
escassign(e, lr->n, ll->n);
ll = ll->next;
}
if (ll != nil)
fatal("esc return list");
break;
case OPANIC:
@ -699,8 +748,7 @@ escassign(EscState *e, Node *dst, Node *src)
lineno = lno;
}
// This is a bit messier than fortunate, pulled out of escassign's big
// This is a bit messier than fortunate, pulled out of esc's big
// switch for clarity. We either have the paramnodes, which may be
// connected to other things throug flows or we have the parameter type
// nodes, which may be marked "noescape". Navigating the ast is slightly
@ -781,7 +829,7 @@ esccall(EscState *e, Node *n)
// Imported or completely analyzed function. Use the escape tags.
if(n->op != OCALLFUNC) {
t = getthisx(fntype)->type;
if(!t->note || strcmp(t->note->s, safetag->s) != 0)
if(parsetag(t->note) != EscNone)
escassign(e, &e->theSink, n->left->left);
}
for(t=getinargx(fntype)->type; ll; ll=ll->next) {
@ -795,7 +843,7 @@ esccall(EscState *e, Node *n)
e->noesc = list(e->noesc, src);
n->right = src;
}
if(!t->note || strcmp(t->note->s, safetag->s) != 0)
if(parsetag(t->note) != EscNone)
escassign(e, &e->theSink, src);
if(src != ll->n)
break;
@ -879,6 +927,20 @@ escwalk(EscState *e, int level, Node *dst, Node *src)
e->pdepth++;
// Input parameter flowing to output parameter?
if(dst->op == ONAME && dst->class == PPARAMOUT && dst->vargen < 20) {
if(src->op == ONAME && src->class == PPARAM && level == 0 && src->curfn == dst->curfn) {
if(src->esc != EscScope && src->esc != EscHeap) {
if(debug['m'])
warnl(src->lineno, "leaking param: %hN to result %S", src, dst->sym);
if((src->esc&EscMask) != EscReturn)
src->esc = EscReturn;
src->esc |= 1<<(dst->vargen + EscBits);
}
goto recurse;
}
}
leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth);
switch(src->op) {
@ -944,6 +1006,7 @@ escwalk(EscState *e, int level, Node *dst, Node *src)
escwalk(e, level+1, dst, src->left);
}
recurse:
for(ll=src->escflowsrc; ll; ll=ll->next)
escwalk(e, level, dst, ll->n);
@ -970,10 +1033,12 @@ esctag(EscState *e, Node *func)
if(ll->n->op != ONAME || ll->n->class != PPARAM)
continue;
switch (ll->n->esc) {
switch (ll->n->esc&EscMask) {
case EscNone: // not touched by escflood
case EscReturn:
if(haspointers(ll->n->type)) // don't bother tagging for scalars
ll->n->paramfld->note = safetag;
ll->n->paramfld->note = mktag(ll->n->esc);
break;
case EscHeap: // touched by escflood, moved to heap
case EscScope: // touched by escflood, value leaves scope
break;

View File

@ -215,7 +215,10 @@ enum
EscHeap,
EscScope,
EscNone,
EscReturn,
EscNever,
EscBits = 4,
EscMask = (1<<EscBits) - 1,
};
struct Node