From 75692424d2b8e01d24bca015c480af7874373b5e Mon Sep 17 00:00:00 2001 From: Luuk van Dijk Date: Mon, 22 Oct 2012 10:18:17 +0200 Subject: [PATCH] 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 --- src/cmd/gc/esc.c | 97 ++++++++++++++++++++++++++++++++++++++++-------- src/cmd/gc/go.h | 3 ++ 2 files changed, 84 insertions(+), 16 deletions(-) diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c index 20a77c2b1e..a42027ea5f 100644 --- a/src/cmd/gc/esc.c +++ b/src/cmd/gc/esc.c @@ -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; diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index 2fa6d95bef..c1e637120c 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -215,7 +215,10 @@ enum EscHeap, EscScope, EscNone, + EscReturn, EscNever, + EscBits = 4, + EscMask = (1<