mirror of
https://github.com/golang/go
synced 2024-11-20 06:44:40 -07: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:
parent
976ca1a47d
commit
75692424d2
@ -211,7 +211,48 @@ struct EscState {
|
|||||||
NodeList* noesc; // list of possible non-escaping nodes, for printing
|
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
|
static void
|
||||||
analyze(NodeList *all, int recursive)
|
analyze(NodeList *all, int recursive)
|
||||||
@ -229,9 +270,6 @@ analyze(NodeList *all, int recursive)
|
|||||||
e->theSink.sym = lookup(".sink");
|
e->theSink.sym = lookup(".sink");
|
||||||
e->theSink.escloopdepth = -1;
|
e->theSink.escloopdepth = -1;
|
||||||
|
|
||||||
if(safetag == nil)
|
|
||||||
safetag = strlit("noescape");
|
|
||||||
|
|
||||||
for(l=all; l; l=l->next)
|
for(l=all; l; l=l->next)
|
||||||
if(l->n->op == ODCLFUNC)
|
if(l->n->op == ODCLFUNC)
|
||||||
l->n->esc = EscFuncPlanned;
|
l->n->esc = EscFuncPlanned;
|
||||||
@ -284,16 +322,15 @@ escfunc(EscState *e, Node *func)
|
|||||||
continue;
|
continue;
|
||||||
switch (ll->n->class) {
|
switch (ll->n->class) {
|
||||||
case PPARAMOUT:
|
case PPARAMOUT:
|
||||||
// output parameters flow to the sink
|
// out params are in a loopdepth between the sink and all local variables
|
||||||
escflows(e, &e->theSink, ll->n);
|
ll->n->escloopdepth = 0;
|
||||||
ll->n->escloopdepth = e->loopdepth;
|
|
||||||
break;
|
break;
|
||||||
case PPARAM:
|
case PPARAM:
|
||||||
if(ll->n->type && !haspointers(ll->n->type))
|
if(ll->n->type && !haspointers(ll->n->type))
|
||||||
break;
|
break;
|
||||||
ll->n->esc = EscNone; // prime for escflood later
|
ll->n->esc = EscNone; // prime for escflood later
|
||||||
e->noesc = list(e->noesc, ll->n);
|
e->noesc = list(e->noesc, ll->n);
|
||||||
ll->n->escloopdepth = e->loopdepth;
|
ll->n->escloopdepth = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -470,8 +507,20 @@ esc(EscState *e, Node *n)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ORETURN:
|
case ORETURN:
|
||||||
for(ll=n->list; ll; ll=ll->next)
|
if(count(n->list) == 1 && curfn->type->outtuple > 1) {
|
||||||
escassign(e, &e->theSink, ll->n);
|
// 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;
|
break;
|
||||||
|
|
||||||
case OPANIC:
|
case OPANIC:
|
||||||
@ -699,8 +748,7 @@ escassign(EscState *e, Node *dst, Node *src)
|
|||||||
lineno = lno;
|
lineno = lno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a bit messier than fortunate, pulled out of esc's big
|
||||||
// This is a bit messier than fortunate, pulled out of escassign's big
|
|
||||||
// switch for clarity. We either have the paramnodes, which may be
|
// switch for clarity. We either have the paramnodes, which may be
|
||||||
// connected to other things throug flows or we have the parameter type
|
// connected to other things throug flows or we have the parameter type
|
||||||
// nodes, which may be marked "noescape". Navigating the ast is slightly
|
// 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.
|
// Imported or completely analyzed function. Use the escape tags.
|
||||||
if(n->op != OCALLFUNC) {
|
if(n->op != OCALLFUNC) {
|
||||||
t = getthisx(fntype)->type;
|
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);
|
escassign(e, &e->theSink, n->left->left);
|
||||||
}
|
}
|
||||||
for(t=getinargx(fntype)->type; ll; ll=ll->next) {
|
for(t=getinargx(fntype)->type; ll; ll=ll->next) {
|
||||||
@ -795,7 +843,7 @@ esccall(EscState *e, Node *n)
|
|||||||
e->noesc = list(e->noesc, src);
|
e->noesc = list(e->noesc, src);
|
||||||
n->right = src;
|
n->right = src;
|
||||||
}
|
}
|
||||||
if(!t->note || strcmp(t->note->s, safetag->s) != 0)
|
if(parsetag(t->note) != EscNone)
|
||||||
escassign(e, &e->theSink, src);
|
escassign(e, &e->theSink, src);
|
||||||
if(src != ll->n)
|
if(src != ll->n)
|
||||||
break;
|
break;
|
||||||
@ -879,6 +927,20 @@ escwalk(EscState *e, int level, Node *dst, Node *src)
|
|||||||
|
|
||||||
e->pdepth++;
|
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);
|
leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth);
|
||||||
|
|
||||||
switch(src->op) {
|
switch(src->op) {
|
||||||
@ -944,6 +1006,7 @@ escwalk(EscState *e, int level, Node *dst, Node *src)
|
|||||||
escwalk(e, level+1, dst, src->left);
|
escwalk(e, level+1, dst, src->left);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
recurse:
|
||||||
for(ll=src->escflowsrc; ll; ll=ll->next)
|
for(ll=src->escflowsrc; ll; ll=ll->next)
|
||||||
escwalk(e, level, dst, ll->n);
|
escwalk(e, level, dst, ll->n);
|
||||||
|
|
||||||
@ -970,10 +1033,12 @@ esctag(EscState *e, Node *func)
|
|||||||
if(ll->n->op != ONAME || ll->n->class != PPARAM)
|
if(ll->n->op != ONAME || ll->n->class != PPARAM)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch (ll->n->esc) {
|
switch (ll->n->esc&EscMask) {
|
||||||
case EscNone: // not touched by escflood
|
case EscNone: // not touched by escflood
|
||||||
|
case EscReturn:
|
||||||
if(haspointers(ll->n->type)) // don't bother tagging for scalars
|
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 EscHeap: // touched by escflood, moved to heap
|
||||||
case EscScope: // touched by escflood, value leaves scope
|
case EscScope: // touched by escflood, value leaves scope
|
||||||
break;
|
break;
|
||||||
|
@ -215,7 +215,10 @@ enum
|
|||||||
EscHeap,
|
EscHeap,
|
||||||
EscScope,
|
EscScope,
|
||||||
EscNone,
|
EscNone,
|
||||||
|
EscReturn,
|
||||||
EscNever,
|
EscNever,
|
||||||
|
EscBits = 4,
|
||||||
|
EscMask = (1<<EscBits) - 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Node
|
struct Node
|
||||||
|
Loading…
Reference in New Issue
Block a user