diff --git a/src/cmd/gc/doc.go b/src/cmd/gc/doc.go index 3fe7fafddb6..83be8b7c09d 100644 --- a/src/cmd/gc/doc.go +++ b/src/cmd/gc/doc.go @@ -39,8 +39,6 @@ Flags: show entire file path when printing line numbers in errors -I dir1 -I dir2 add dir1 and dir2 to the list of paths to check for imported packages - -N - disable optimization -S write assembly language text to standard output -u diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c index 2cc7d7a486c..3a28a8ba73b 100644 --- a/src/cmd/gc/esc.c +++ b/src/cmd/gc/esc.c @@ -2,22 +2,14 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // -// The base version before this file existed, active with debug['s'] -// == 0, assumes any node that has a reference to it created at some -// point, may flow to the global scope except -// - if its address is dereferenced immediately with only CONVNOPs in -// between the * and the & -// - if it is for a closure variable and the closure executed at the -// place it's defined -// -// Flag -s disables the old codepaths and switches on the code here: +// Escape analysis. // // First escfunc, esc and escassign recurse over the ast of each // function to dig out flow(dst,src) edges between any // pointer-containing nodes and store them in dst->escflowsrc. For // variables assigned to a variable in an outer scope or used as a // return value, they store a flow(theSink, src) edge to a fake node -// 'the Sink'. For variables referenced in closures, an edge +// 'the Sink'. For variables referenced in closures, an edge // flow(closure, &var) is recorded and the flow of a closure itself to // an outer scope is tracked the same way as other variables. // @@ -25,9 +17,16 @@ // variables of it can reach an & node as escaping and all function // parameters it can reach as leaking. // -// Watch the variables moved to the heap and parameters tagged as -// unsafe with -m, more detailed analysis output with -mm +// If a value's address is taken but the address does not escape, +// then the value can stay on the stack. If the value new(T) does +// not escape, then new(T) can be rewritten into a stack allocation. +// The same is true of slice literals. // +// If escape analysis is disabled (-s), this code is not used. +// Instead, the compiler assumes that any value whose address +// is taken without being immediately dereferenced +// needs to be moved to the heap, and new(T) and slice +// literals are always real allocations. #include #include @@ -53,9 +52,9 @@ static Node theSink; static NodeList* dsts; // all dst nodes static int loopdepth; // for detecting nested loop scopes static int pdepth; // for debug printing in recursions. -static int floodgen; // loop prevention in flood/walk static Strlit* safetag; // gets slapped on safe parameters' field types for export static int dstcount, edgecount; // diagnostic +static NodeList* noesc; // list of possible non-escaping nodes, for printing void escapes(void) @@ -85,8 +84,17 @@ escapes(void) for(l=xtop; l; l=l->next) if(l->n->op == ODCLFUNC) esctag(l->n); + + if(debug['m']) { + for(l=noesc; l; l=l->next) + if(l->n->esc == EscNone) + warnl(l->n->lineno, "%S %#N does not escape", + (l->n->curfn && l->n->curfn->nname) ? l->n->curfn->nname->sym : S, + l->n); + } } + static void escfunc(Node *func) { @@ -109,7 +117,10 @@ escfunc(Node *func) ll->n->escloopdepth = loopdepth; break; case PPARAM: + if(ll->n->type && !haspointers(ll->n->type)) + break; ll->n->esc = EscNone; // prime for escflood later + noesc = list(noesc, ll->n); ll->n->escloopdepth = loopdepth; break; } @@ -143,7 +154,7 @@ static void esc(Node *n) { int lno; - NodeList *ll, *lr, *l; + NodeList *ll, *lr; if(n == N) return; @@ -153,14 +164,15 @@ esc(Node *n) if(n->op == OFOR || n->op == ORANGE) loopdepth++; - esclist(n->ninit); - esclist(n->list); - esclist(n->rlist); - esc(n->ntest); - esc(n->nincr); - esclist(n->nbody); esc(n->left); esc(n->right); + esc(n->ntest); + esc(n->nincr); + esclist(n->ninit); + esclist(n->nbody); + esclist(n->nelse); + esclist(n->list); + esclist(n->rlist); if(n->op == OFOR || n->op == ORANGE) loopdepth--; @@ -182,7 +194,7 @@ esc(Node *n) case ORANGE: // Everything but fixed array is a dereference. - if(isfixedarray(n->type)) + if(isfixedarray(n->type) && n->list->next) escassign(n->list->next->n, n->right); break; @@ -192,7 +204,6 @@ esc(Node *n) // ntest->right is the argument of the .(type), // ll->n->nname is the variable per case escassign(ll->n->nname, n->ntest->right); - esclist(ll->n->nbody); } } break; @@ -253,24 +264,55 @@ esc(Node *n) case OCALLINTER: esccall(n); break; - + case OCONV: case OCONVNOP: case OCONVIFACE: escassign(n, n->left); break; - + case OARRAYLIT: - case OSTRUCTLIT: - for(l=n->list; l; l=l->next) - escassign(n, l->n->right); - break; - case OMAPLIT: - for(l=n->list; l; l=l->next) { - escassign(n, l->n->left); - escassign(n, l->n->right); + if(isslice(n->type)) { + n->esc = EscNone; // until proven otherwise + noesc = list(noesc, n); + n->escloopdepth = loopdepth; + // Values make it to memory, lose track. + for(ll=n->list; ll; ll=ll->next) + escassign(&theSink, ll->n->right); + } else { + // Link values to array. + for(ll=n->list; ll; ll=ll->next) + escassign(n, ll->n->right); } break; + + case OSTRUCTLIT: + // Link values to struct. + for(ll=n->list; ll; ll=ll->next) + escassign(n, ll->n->right); + break; + + case OMAPLIT: + n->esc = EscNone; // until proven otherwise + noesc = list(noesc, n); + n->escloopdepth = loopdepth; + // Keys and values make it to memory, lose track. + for(ll=n->list; ll; ll=ll->next) { + escassign(&theSink, ll->n->left); + escassign(&theSink, ll->n->right); + } + break; + + case OADDR: + case OCLOSURE: + case OMAKECHAN: + case OMAKEMAP: + case OMAKESLICE: + case ONEW: + n->escloopdepth = loopdepth; + n->esc = EscNone; // until proven otherwise + noesc = list(noesc, n); + break; } lineno = lno; @@ -284,7 +326,6 @@ static void escassign(Node *dst, Node *src) { int lno; - NodeList *ll; if(isblank(dst) || dst == N || src == N || src->op == ONONAME || src->op == OXXX) return; @@ -336,11 +377,6 @@ escassign(Node *dst, Node *src) break; } - if(src->typecheck == 0 && src->op != OKEY) { - dump("escassign missing typecheck", src); - fatal("escassign"); - } - lno = setlineno(src); pdepth++; @@ -350,6 +386,10 @@ escassign(Node *dst, Node *src) case ODOTPTR: // dst = (*x).f case ONAME: case OPARAM: + case ODDDARG: + case OARRAYLIT: + case OMAPLIT: + case OSTRUCTLIT: // loopdepth was set in the defining statement or function header escflows(dst, src); break; @@ -377,32 +417,39 @@ escassign(Node *dst, Node *src) escassign(dst, src->left); break; - case OARRAYLIT: - case OSTRUCTLIT: - case OMAPLIT: - src->escloopdepth = loopdepth; - escflows(dst, src); - for(ll=src->list; ll; ll=ll->next) { - escassign(src, ll->n->left); - escassign(src, ll->n->right); - } - break; - case OMAKECHAN: case OMAKEMAP: case OMAKESLICE: case ONEW: - src->curfn = curfn; // should have been done in parse, but patch it up here. - src->escloopdepth = loopdepth; escflows(dst, src); break; case OCLOSURE: - src->curfn = curfn; // should have been done in parse, but patch it up here. - src->escloopdepth = loopdepth; escflows(dst, src); escfunc(src); break; + + case OADD: + case OSUB: + case OOR: + case OXOR: + case OMUL: + case ODIV: + case OMOD: + case OLSH: + case ORSH: + case OAND: + case OANDNOT: + case OPLUS: + case OMINUS: + case OCOM: + // Might be pointer arithmetic, in which case + // the operands flow into the result. + // TODO(rsc): Decide what the story is here. This is unsettling. + escassign(dst, src->left); + escassign(dst, src->right); + break; + } pdepth--; @@ -420,7 +467,7 @@ static void esccall(Node *n) { NodeList *ll, *lr; - Node *a, *fn; + Node *a, *fn, *src; Type *t, *fntype; fn = N; @@ -458,21 +505,32 @@ esccall(Node *n) } } - if(fn && fn->ntype) { + if(fn && fn->op == ONAME && fn->class == PFUNC && fn->ntype) { // Local function. Incorporate into flow graph. // Receiver. if(n->op != OCALLFUNC) escassign(fn->ntype->left->left, n->left->left); - for(ll=n->list, lr=fn->ntype->list; ll && lr; ll=ll->next) { - if (lr->n->left) - escassign(lr->n->left, ll->n); - else - escassign(&theSink, ll->n); - if(lr->n->left && !lr->n->left->isddd) - lr=lr->next; + for(lr=fn->ntype->list; ll && lr; ll=ll->next, lr=lr->next) { + src = ll->n; + if(lr->n->isddd && !n->isddd) { + // Introduce ODDDARG node to represent ... allocation. + src = nod(ODDDARG, N, N); + src->escloopdepth = loopdepth; + src->lineno = n->lineno; + src->esc = EscNone; // until we find otherwise + noesc = list(noesc, src); + n->right = src; + } + if(lr->n->left != N) + escassign(lr->n->left, src); + if(src != ll->n) + break; } + // "..." arguments are untracked + for(; ll; ll=ll->next) + escassign(&theSink, ll->n); return; } @@ -483,11 +541,25 @@ esccall(Node *n) escassign(&theSink, n->left->left); } for(t=getinargx(fntype)->type; ll; ll=ll->next) { + src = ll->n; + if(t->isddd && !n->isddd) { + // Introduce ODDDARG node to represent ... allocation. + src = nod(ODDDARG, N, N); + src->escloopdepth = loopdepth; + src->lineno = n->lineno; + src->esc = EscNone; // until we find otherwise + noesc = list(noesc, src); + n->right = src; + } if(!t->note || strcmp(t->note->s, safetag->s) != 0) - escassign(&theSink, ll->n); - if(t->down) - t = t->down; + escassign(&theSink, src); + if(src != ll->n) + break; + t = t->down; } + // "..." arguments are untracked + for(; ll; ll=ll->next) + escassign(&theSink, ll->n); } // Store the link src->dst in dst, throwing out some quick wins. @@ -536,12 +608,12 @@ escflood(Node *dst) } if(debug['m']>1) - print("\nescflood:%d: dst %hN scope:%#S[%d]\n", floodgen, dst, + print("\nescflood:%d: dst %hN scope:%#S[%d]\n", walkgen, dst, (dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S, dst->escloopdepth); for(l = dst->escflowsrc; l; l=l->next) { - floodgen++; + walkgen++; escwalk(0, dst, l->n); } } @@ -552,9 +624,9 @@ escwalk(int level, Node *dst, Node *src) NodeList *ll; int leaks; - if(src->escfloodgen == floodgen) + if(src->walkgen == walkgen) return; - src->escfloodgen = floodgen; + src->walkgen = walkgen; if(debug['m']>1) print("escwalk: level:%d depth:%d %.*s %hN scope:%#S[%d]\n", @@ -570,19 +642,42 @@ escwalk(int level, Node *dst, Node *src) if(src->class == PPARAM && leaks && src->esc == EscNone) { src->esc = EscScope; if(debug['m']) - print("%L:leaking param: %hN\n", src->lineno, src); + warnl(src->lineno, "leaking param: %hN", src); } break; case OADDR: - if(leaks) + if(leaks) { + src->esc = EscHeap; addrescapes(src->left); + if(debug['m']) + warnl(src->lineno, "%#N escapes to heap", src); + } escwalk(level-1, dst, src->left); break; + case OARRAYLIT: + if(isfixedarray(src->type)) + break; + // fall through + case ODDDARG: + case OMAKECHAN: + case OMAKEMAP: + case OMAKESLICE: + case OMAPLIT: + case ONEW: + case OCLOSURE: + if(leaks) { + src->esc = EscHeap; + if(debug['m']) + warnl(src->lineno, "%#N escapes to heap", src); + } + break; + case OINDEX: if(isfixedarray(src->type)) break; + // fall through case OSLICE: case ODOTPTR: case OINDEXMAP: @@ -616,8 +711,6 @@ esctag(Node *func) case EscHeap: // touched by escflood, moved to heap case EscScope: // touched by escflood, value leaves scope break; - default: - fatal("messed up escape tagging: %N::%N", curfn, ll->n); } } diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index ddd6cae6d28..bc2ea33d7aa 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -98,7 +98,7 @@ addrescapes(Node *n) if(n->class == PAUTO && n->esc == EscNever) break; - if(!debug['s'] && n->esc != EscUnknown) + if(debug['s'] && n->esc != EscUnknown) fatal("without escape analysis, only PAUTO's should have esc: %N", n); switch(n->class) { @@ -121,8 +121,8 @@ addrescapes(Node *n) fatal("addrescapes before param assignment"); n->stackparam->xoffset = n->xoffset; // fallthrough - case PAUTO: + case PAUTO: n->class |= PHEAP; n->addable = 0; n->ullman = 2; @@ -136,10 +136,8 @@ addrescapes(Node *n) n->heapaddr->class = PHEAP-1; // defer tempname to allocparams n->heapaddr->ullman = 1; n->curfn->dcl = list(n->curfn->dcl, n->heapaddr); - - if(debug['s']) + if(!debug['s']) n->esc = EscHeap; - if(debug['m']) print("%L: moved to heap: %hN\n", n->lineno, n); diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index f5c2c387501..ecd48ea6bbf 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -211,6 +211,18 @@ enum struct Node { + // Tree structure. + // Generic recursive walks should follow these fields. + Node* left; + Node* right; + Node* ntest; + Node* nincr; + NodeList* ninit; + NodeList* nbody; + NodeList* nelse; + NodeList* list; + NodeList* rlist; + uchar op; uchar ullman; // sethi/ullman number uchar addable; // type of addressability - 0 is not addressable @@ -236,26 +248,10 @@ struct Node uchar implicit; // don't show in printout // most nodes - Node* left; - Node* right; Type* type; Type* realtype; // as determined by typecheck - NodeList* list; - NodeList* rlist; Node* orig; // original form, for printing, and tracking copies of ONAMEs - // for-body - NodeList* ninit; - Node* ntest; - Node* nincr; - NodeList* nbody; - - // if-body - NodeList* nelse; - - // cases - Node* ncase; - // func Node* nname; Node* shortname; @@ -289,7 +285,6 @@ struct Node // Escape analysis. NodeList* escflowsrc; // flow(this, src) int escloopdepth; // -1: global, 0: not set, function top level:1, increased inside function for every loop or label to mark scopes - int escfloodgen; // increased for every flood to detect loops Sym* sym; // various int32 vargen; // unique name for OTYPE/ONAME @@ -299,9 +294,25 @@ struct Node int32 stkdelta; // offset added by stack frame compaction phase. int32 ostk; int32 iota; + uint32 walkgen; }; #define N ((Node*)0) -EXTERN int32 walkgen; + +/* + * Every node has a walkgen field. + * If you want to do a traversal of a node graph that + * might contain duplicates and want to avoid + * visiting the same nodes twice, increment walkgen + * before starting. Then before processing a node, do + * + * if(n->walkgen == walkgen) + * return; + * n->walkgen = walkgen; + * + * Such a walk cannot call another such walk recursively, + * because of the use of the global walkgen. + */ +EXTERN uint32 walkgen; struct NodeList { @@ -1189,6 +1200,7 @@ uint32 typehash(Type *t); void ullmancalc(Node *n); void umagic(Magic *m); void warn(char *fmt, ...); +void warnl(int line, char *fmt, ...); void yyerror(char *fmt, ...); void yyerrorl(int line, char *fmt, ...); diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 4d30dcde8b7..3fba453839e 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -78,13 +78,17 @@ usage(void) print("flags:\n"); // -A is allow use of "any" type, for bootstrapping print(" -I DIR search for packages in DIR\n"); + print(" -L show full path in file:line prints\n"); + print(" -N disable optimizer\n"); + print(" -S print the assembly language\n"); + print(" -V print the compiler version\n"); print(" -d print declarations\n"); print(" -e no limit on number of errors printed\n"); print(" -f print stack frame structure\n"); print(" -h panic on an error\n"); + print(" -m print about moves to heap\n"); print(" -o file specify output file\n"); - print(" -S print the assembly language\n"); - print(" -V print the compiler version\n"); + print(" -s disable escape analysis\n"); print(" -u disable package unsafe\n"); print(" -w print the parse tree after typing\n"); print(" -x print lex tokens\n"); @@ -273,7 +277,7 @@ main(int argc, char *argv[]) errorexit(); // Phase 3b: escape analysis. - if(debug['s']) + if(!debug['s']) escapes(); // Phase 4: Compile function bodies. diff --git a/src/cmd/gc/sinit.c b/src/cmd/gc/sinit.c index 2f5a44b4047..58fa5b9ec4c 100644 --- a/src/cmd/gc/sinit.c +++ b/src/cmd/gc/sinit.c @@ -410,7 +410,6 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) dowidth(t); if(ctxt != 0) { - // put everything into static array vstat = staticname(t, ctxt); arraylit(ctxt, 1, n, vstat, init); @@ -457,9 +456,15 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) vauto = nod(OXXX, N, N); tempname(vauto, ptrto(t)); - // set auto to point at new heap (3 assign) - a = nod(ONEW, N, N); - a->list = list1(typenod(t)); + // set auto to point at new temp or heap (3 assign) + if(n->esc == EscNone) { + a = nod(OXXX, N, N); + tempname(a, t); + a = nod(OADDR, a, N); + } else { + a = nod(ONEW, N, N); + a->list = list1(typenod(t)); + } a = nod(OAS, vauto, a); typecheck(&a, Etop); walkexpr(&a, init); diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 2b7f5e1c912..8699b0d222f 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -211,6 +211,16 @@ warn(char *fmt, ...) hcrash(); } +void +warnl(int line, char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + adderr(line, fmt, arg); + va_end(arg); +} + void fatal(char *fmt, ...) { @@ -485,6 +495,7 @@ nod(int op, Node *nleft, Node *nright) n->lineno = parserline(); n->xoffset = BADWIDTH; n->orig = n; + n->curfn = curfn; return n; } diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 7d67e1e73ee..0f164078597 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -538,7 +538,7 @@ reswitch: goto error; // top&Eindir means this is &x in *&x. (or the arg to built-in print) // n->etype means code generator flagged it as non-escaping. - if(!(top & Eindir) && !n->etype && !debug['s']) + if(debug['s'] && !(top & Eindir) && !n->etype) addrescapes(n->left); n->type = ptrto(t); goto ret; @@ -1607,7 +1607,7 @@ lookdot(Node *n, Type *t, int dostrcmp) if(!eqtype(rcvr, tt)) { if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) { checklvalue(n->left, "call pointer method on"); - if(!debug['s']) + if(debug['s']) addrescapes(n->left); n->left = nod(OADDR, n->left, N); n->left->implicit = 1; diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index b172013aa8b..794d65205a8 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -13,7 +13,7 @@ static Node* makenewvar(Type*, NodeList**, Node**); static Node* ascompatee1(int, Node*, Node*, NodeList**); static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**); static NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**); -static NodeList* ascompatte(int, int, Type**, NodeList*, int, NodeList**); +static NodeList* ascompatte(int, Node*, int, Type**, NodeList*, int, NodeList**); static Node* convas(Node*, NodeList**); static void heapmoves(void); static NodeList* paramstoheap(Type **argin, int out); @@ -289,7 +289,7 @@ walkstmt(Node **np) n->list = reorder3(ll); break; } - ll = ascompatte(n->op, 0, getoutarg(curfn->type), n->list, 1, &n->ninit); + ll = ascompatte(n->op, nil, 0, getoutarg(curfn->type), n->list, 1, &n->ninit); n->list = ll; break; @@ -484,7 +484,7 @@ walkexpr(Node **np, NodeList **init) goto ret; walkexpr(&n->left, init); walkexprlist(n->list, init); - ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init); + ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init); n->list = reorder1(ll); goto ret; @@ -501,7 +501,7 @@ walkexpr(Node **np, NodeList **init) walkexpr(&n->left, init); walkexprlist(n->list, init); - ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init); + ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init); n->list = reorder1(ll); goto ret; @@ -511,8 +511,8 @@ walkexpr(Node **np, NodeList **init) goto ret; walkexpr(&n->left, init); walkexprlist(n->list, init); - ll = ascompatte(n->op, 0, getthis(t), list1(n->left->left), 0, init); - lr = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init); + ll = ascompatte(n->op, n, 0, getthis(t), list1(n->left->left), 0, init); + lr = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init); ll = concat(ll, lr); n->left->left = N; ullmancalc(n->left); @@ -977,7 +977,16 @@ walkexpr(Node **np, NodeList **init) goto ret; case ONEW: - n = callnew(n->type->type); + if(n->esc == EscNone && n->type->type->width < (1<<16)) { + r = nod(OXXX, N, N); + tempname(r, n->type->type); + *init = list(*init, nod(OAS, r, N)); // zero temp + r = nod(OADDR, r, N); + typecheck(&r, Erv); + n = r; + } else { + n = callnew(n->type->type); + } goto ret; case OCMPSTR: @@ -1315,21 +1324,27 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init) * package all the arguments that match a ... T parameter into a []T. */ static NodeList* -mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init) +mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, int esc) { Node *a, *n; Type *tslice; - + tslice = typ(TARRAY); tslice->type = l->type->type; tslice->bound = -1; - n = nod(OCOMPLIT, N, typenod(tslice)); - n->list = lr0; - typecheck(&n, Erv); - if(n->type == T) - fatal("mkdotargslice: typecheck failed"); - walkexpr(&n, init); + if(count(lr0) == 0) { + n = nodnil(); + n->type = tslice; + } else { + n = nod(OCOMPLIT, N, typenod(tslice)); + n->list = lr0; + n->esc = esc; + typecheck(&n, Erv); + if(n->type == T) + fatal("mkdotargslice: typecheck failed"); + walkexpr(&n, init); + } a = nod(OAS, nodarg(l, fp), n); nn = list(nn, convas(a, init)); @@ -1393,8 +1408,9 @@ dumpnodetypes(NodeList *l, char *what) * func(expr-list) */ static NodeList* -ascompatte(int op, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init) +ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init) { + int esc; Type *l, *ll; Node *r, *a; NodeList *nn, *lr0, *alist; @@ -1458,7 +1474,10 @@ loop: // normal case -- make a slice of all // remaining arguments and pass it to // the ddd parameter. - nn = mkdotargslice(lr, nn, l, fp, init); + esc = EscUnknown; + if(call->right) + esc = call->right->esc; + nn = mkdotargslice(lr, nn, l, fp, init, esc); goto ret; } diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go index 99b1f24eafd..8e5a6282cfa 100644 --- a/src/pkg/reflect/value.go +++ b/src/pkg/reflect/value.go @@ -322,8 +322,31 @@ func packValue(flag uint32, typ *runtime.Type, word iword) Value { return Value{Internal: *(*interface{})(unsafe.Pointer(&eface))} } +var dummy struct { + b bool + x interface{} +} + +// Dummy annotation marking that the value x escapes, +// for use in cases where the reflect code is so clever that +// the compiler cannot follow. +func escapes(x interface{}) { + if dummy.b { + dummy.x = x + } +} + // valueFromAddr returns a Value using the given type and address. func valueFromAddr(flag uint32, typ Type, addr unsafe.Pointer) Value { + // TODO(rsc): Eliminate this terrible hack. + // The escape analysis knows that addr is a pointer + // but it doesn't see addr get passed to anything + // that keeps it. packValue keeps it, but packValue + // takes a uintptr (iword(addr)), and integers (non-pointers) + // are assumed not to matter. The escapes function works + // because return values always escape (for now). + escapes(addr) + if flag&flagAddr != 0 { // Addressable, so the internal value is // an interface containing a pointer to the real value. @@ -1678,6 +1701,14 @@ func ValueOf(i interface{}) Value { if i == nil { return Value{} } + + // TODO(rsc): Eliminate this terrible hack. + // In the call to packValue, eface.typ doesn't escape, + // and eface.word is an integer. So it looks like + // i (= eface) doesn't escape. But really it does, + // because eface.word is actually a pointer. + escapes(i) + // For an interface value with the noAddr bit set, // the representation is identical to an empty interface. eface := *(*emptyInterface)(unsafe.Pointer(&i)) diff --git a/test/escape2.go b/test/escape2.go index f9d377acf0d..24a88f751ea 100644 --- a/test/escape2.go +++ b/test/escape2.go @@ -1,4 +1,4 @@ -// errchk -0 $G -sm $D/$F.go +// errchk -0 $G -m $D/$F.go // Copyright 2010 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style @@ -10,92 +10,92 @@ import "unsafe" var gxx *int -func foo1(x int) { // ERROR "moved to heap: NAME-x" - gxx = &x +func foo1(x int) { // ERROR "moved to heap: NAME-x" + gxx = &x // ERROR "&x escapes to heap" } -func foo2(yy *int) { // ERROR "leaking param: NAME-yy" +func foo2(yy *int) { // ERROR "leaking param: NAME-yy" gxx = yy } -func foo3(x int) *int { // ERROR "moved to heap: NAME-x" - return &x +func foo3(x int) *int { // ERROR "moved to heap: NAME-x" + return &x // ERROR "&x escapes to heap" } type T *T -func foo3b(t T) { // ERROR "leaking param: NAME-t" + +func foo3b(t T) { // ERROR "leaking param: NAME-t" *t = t } // xx isn't going anywhere, so use of yy is ok -func foo4(xx, yy *int) { +func foo4(xx, yy *int) { // ERROR "xx does not escape" "yy does not escape" xx = yy } // xx isn't going anywhere, so taking address of yy is ok -func foo5(xx **int, yy *int) { - xx = &yy +func foo5(xx **int, yy *int) { // ERROR "xx does not escape" "yy does not escape" + xx = &yy // ERROR "&yy does not escape" } -func foo6(xx **int, yy *int) { // ERROR "leaking param: NAME-yy" +func foo6(xx **int, yy *int) { // ERROR "xx does not escape" "leaking param: NAME-yy" *xx = yy } -func foo7(xx **int, yy *int) { +func foo7(xx **int, yy *int) { // ERROR "xx does not escape" "yy does not escape" **xx = *yy } -func foo8(xx, yy *int) int { +func foo8(xx, yy *int) int { // ERROR "xx does not escape" "yy does not escape" xx = yy return *xx } -func foo9(xx, yy *int) *int { // ERROR "leaking param: NAME-xx" "leaking param: NAME-yy" +func foo9(xx, yy *int) *int { // ERROR "leaking param: NAME-xx" "leaking param: NAME-yy" xx = yy return xx } -func foo10(xx, yy *int) { +func foo10(xx, yy *int) { // ERROR "xx does not escape" "yy does not escape" *xx = *yy } func foo11() int { x, y := 0, 42 - xx := &x - yy := &y + xx := &x // ERROR "&x does not escape" + yy := &y // ERROR "&y does not escape" *xx = *yy return x } - var xxx **int -func foo12(yyy **int) { // ERROR "leaking param: NAME-yyy" +func foo12(yyy **int) { // ERROR "leaking param: NAME-yyy" xxx = yyy } -func foo13(yyy **int) { +func foo13(yyy **int) { // ERROR "yyy does not escape" *xxx = *yyy } -func foo14(yyy **int) { +func foo14(yyy **int) { // ERROR "yyy does not escape" **xxx = **yyy } -func foo15(yy *int) { // ERROR "moved to heap: NAME-yy" - xxx = &yy +func foo15(yy *int) { // ERROR "moved to heap: NAME-yy" + xxx = &yy // ERROR "&yy escapes to heap" } -func foo16(yy *int) { // ERROR "leaking param: NAME-yy" +func foo16(yy *int) { // ERROR "leaking param: NAME-yy" *xxx = yy } -func foo17(yy *int) { +func foo17(yy *int) { // ERROR "yy does not escape" **xxx = *yy } -func foo18(y int) { // ERROR "moved to heap: "NAME-y" - *xxx = &y +func foo18(y int) { // ERROR "moved to heap: "NAME-y" + *xxx = &y // ERROR "&y escapes to heap" } func foo19(y int) { @@ -103,238 +103,233 @@ func foo19(y int) { } type Bar struct { - i int + i int ii *int } func NewBar() *Bar { - return &Bar{ 42, nil } + return &Bar{42, nil} // ERROR "&struct literal escapes to heap" } -func NewBarp(x *int) *Bar { // ERROR "leaking param: NAME-x" - return &Bar{ 42, x } +func NewBarp(x *int) *Bar { // ERROR "leaking param: NAME-x" + return &Bar{42, x} // ERROR "&struct literal escapes to heap" } -func NewBarp2(x *int) *Bar { - return &Bar{ *x, nil } +func NewBarp2(x *int) *Bar { // ERROR "x does not escape" + return &Bar{*x, nil} // ERROR "&struct literal escapes to heap" } -func (b *Bar) NoLeak() int { +func (b *Bar) NoLeak() int { // ERROR "b does not escape" return *(b.ii) } -func (b *Bar) AlsoNoLeak() *int { +func (b *Bar) AlsoNoLeak() *int { // ERROR "b does not escape" return b.ii } type Bar2 struct { - i [12]int + i [12]int ii []int } func NewBar2() *Bar2 { - return &Bar2{ [12]int{ 42 }, nil } + return &Bar2{[12]int{42}, nil} // ERROR "&struct literal escapes to heap" } -func (b *Bar2) NoLeak() int { +func (b *Bar2) NoLeak() int { // ERROR "b does not escape" return b.i[0] } -func (b *Bar2) Leak() []int { // ERROR "leaking param: NAME-b" - return b.i[:] +func (b *Bar2) Leak() []int { // ERROR "leaking param: NAME-b" + return b.i[:] // ERROR "&b.i escapes to heap" } -func (b *Bar2) AlsoNoLeak() []int { +func (b *Bar2) AlsoNoLeak() []int { // ERROR "b does not escape" return b.ii[0:1] } -func (b *Bar2) LeakSelf() { // ERROR "leaking param: NAME-b" - b.ii = b.i[0:4] +func (b *Bar2) LeakSelf() { // ERROR "leaking param: NAME-b" + b.ii = b.i[0:4] // ERROR "&b.i escapes to heap" } -func (b *Bar2) LeakSelf2() { // ERROR "leaking param: NAME-b" +func (b *Bar2) LeakSelf2() { // ERROR "leaking param: NAME-b" var buf []int - buf = b.i[0:] + buf = b.i[0:] // ERROR "&b.i escapes to heap" b.ii = buf } func foo21() func() int { - x := 42 // ERROR "moved to heap: NAME-x" - return func() int { - return x + x := 42 // ERROR "moved to heap: NAME-x" + return func() int { // ERROR "func literal escapes to heap" + return x // ERROR "&x escapes to heap" } } func foo22() int { x := 42 - return func() int { + return func() int { // ERROR "func literal does not escape" return x }() } -func foo23(x int) func() int { // ERROR "moved to heap: NAME-x" - return func() int { - return x +func foo23(x int) func() int { // ERROR "moved to heap: NAME-x" + return func() int { // ERROR "func literal escapes to heap" + return x // ERROR "&x escapes to heap" } } -func foo23a(x int) (func() int) { // ERROR "moved to heap: NAME-x" - f := func() int { - return x +func foo23a(x int) func() int { // ERROR "moved to heap: NAME-x" + f := func() int { // ERROR "func literal escapes to heap" + return x // ERROR "&x escapes to heap" } return f } -func foo23b(x int) *(func() int) { // ERROR "moved to heap: NAME-x" - f := func() int { return x } // ERROR "moved to heap: NAME-f" - return &f +func foo23b(x int) *(func() int) { // ERROR "moved to heap: NAME-x" + f := func() int { return x } // ERROR "moved to heap: NAME-f" "func literal escapes to heap" "&x escapes to heap" + return &f // ERROR "&f escapes to heap" } func foo24(x int) int { - return func() int { + return func() int { // ERROR "func literal does not escape" return x }() } - var x *int -func fooleak(xx *int) int { // ERROR "leaking param: NAME-xx" +func fooleak(xx *int) int { // ERROR "leaking param: NAME-xx" x = xx return *x } -func foonoleak(xx *int) int { +func foonoleak(xx *int) int { // ERROR "xx does not escape" return *x + *xx } -func foo31(x int) int { // ERROR "moved to heap: NAME-x" - return fooleak(&x) +func foo31(x int) int { // ERROR "moved to heap: NAME-x" + return fooleak(&x) // ERROR "&x escapes to heap" } func foo32(x int) int { - return foonoleak(&x) + return foonoleak(&x) // ERROR "&x does not escape" } type Foo struct { xx *int - x int + x int } var F Foo var pf *Foo -func (f *Foo) fooleak() { // ERROR "leaking param: NAME-f" +func (f *Foo) fooleak() { // ERROR "leaking param: NAME-f" pf = f } -func (f *Foo) foonoleak() { +func (f *Foo) foonoleak() { // ERROR "f does not escape" F.x = f.x } -func (f *Foo) Leak() { // ERROR "leaking param: NAME-f" +func (f *Foo) Leak() { // ERROR "leaking param: NAME-f" f.fooleak() } -func (f *Foo) NoLeak() { +func (f *Foo) NoLeak() { // ERROR "f does not escape" f.foonoleak() } - -func foo41(x int) { // ERROR "moved to heap: NAME-x" - F.xx = &x +func foo41(x int) { // ERROR "moved to heap: NAME-x" + F.xx = &x // ERROR "&x escapes to heap" } -func (f *Foo) foo42(x int) { // ERROR "moved to heap: NAME-x" - f.xx = &x +func (f *Foo) foo42(x int) { // ERROR "f does not escape" "moved to heap: NAME-x" + f.xx = &x // ERROR "&x escapes to heap" } -func foo43(f *Foo, x int) { // ERROR "moved to heap: NAME-x" - f.xx = &x +func foo43(f *Foo, x int) { // ERROR "f does not escape" "moved to heap: NAME-x" + f.xx = &x // ERROR "&x escapes to heap" } -func foo44(yy *int) { // ERROR "leaking param: NAME-yy" +func foo44(yy *int) { // ERROR "leaking param: NAME-yy" F.xx = yy } -func (f *Foo) foo45() { - F.x = f.x +func (f *Foo) foo45() { // ERROR "f does not escape" + F.x = f.x } -func (f *Foo) foo46() { - F.xx = f.xx +func (f *Foo) foo46() { // ERROR "f does not escape" + F.xx = f.xx } -func (f *Foo) foo47() { // ERROR "leaking param: NAME-f" - f.xx = &f.x +func (f *Foo) foo47() { // ERROR "leaking param: NAME-f" + f.xx = &f.x // ERROR "&f.x escapes to heap" } - var ptrSlice []*int -func foo50(i *int) { // ERROR "leaking param: NAME-i" +func foo50(i *int) { // ERROR "leaking param: NAME-i" ptrSlice[0] = i } - var ptrMap map[*int]*int -func foo51(i *int) { // ERROR "leaking param: NAME-i" +func foo51(i *int) { // ERROR "leaking param: NAME-i" ptrMap[i] = i } - func indaddr1(x int) *int { // ERROR "moved to heap: NAME-x" - return &x + return &x // ERROR "&x escapes to heap" } -func indaddr2(x *int) *int { // ERROR "leaking param: NAME-x" - return *&x +func indaddr2(x *int) *int { // ERROR "leaking param: NAME-x" + return *&x // ERROR "&x does not escape" } -func indaddr3(x *int32) *int { // ERROR "leaking param: NAME-x" - return *(**int)(unsafe.Pointer(&x)) +func indaddr3(x *int32) *int { // ERROR "leaking param: NAME-x" + return *(**int)(unsafe.Pointer(&x)) // ERROR "&x does not escape" } // From package math: func Float32bits(f float32) uint32 { - return *(*uint32)(unsafe.Pointer(&f)) + return *(*uint32)(unsafe.Pointer(&f)) // ERROR "&f does not escape" } func Float32frombits(b uint32) float32 { - return *(*float32)(unsafe.Pointer(&b)) + return *(*float32)(unsafe.Pointer(&b)) // ERROR "&b does not escape" } func Float64bits(f float64) uint64 { - return *(*uint64)(unsafe.Pointer(&f)) + return *(*uint64)(unsafe.Pointer(&f)) // ERROR "&f does not escape" } func Float64frombits(b uint64) float64 { - return *(*float64)(unsafe.Pointer(&b)) + return *(*float64)(unsafe.Pointer(&b)) // ERROR "&b does not escape" } // contrast with -func float64bitsptr(f float64) *uint64 { // ERROR "moved to heap: NAME-f" - return (*uint64)(unsafe.Pointer(&f)) +func float64bitsptr(f float64) *uint64 { // ERROR "moved to heap: NAME-f" + return (*uint64)(unsafe.Pointer(&f)) // ERROR "&f escapes to heap" } -func float64ptrbitsptr(f *float64) *uint64 { // ERROR "leaking param: NAME-f" +func float64ptrbitsptr(f *float64) *uint64 { // ERROR "leaking param: NAME-f" return (*uint64)(unsafe.Pointer(f)) } -func typesw(i interface{}) *int { // ERROR "leaking param: NAME-i" +func typesw(i interface{}) *int { // ERROR "leaking param: NAME-i" switch val := i.(type) { case *int: return val case *int8: - v := int(*val) // ERROR "moved to heap: NAME-v" - return &v + v := int(*val) // ERROR "moved to heap: NAME-v" + return &v // ERROR "&v escapes to heap" } return nil } -func exprsw(i *int) *int { // ERROR "leaking param: NAME-i" +func exprsw(i *int) *int { // ERROR "leaking param: NAME-i" switch j := i; *j + 110 { case 12: return j @@ -346,31 +341,31 @@ func exprsw(i *int) *int { // ERROR "leaking param: NAME-i" } // assigning to an array element is like assigning to the array -func foo60(i *int) *int { // ERROR "leaking param: NAME-i" +func foo60(i *int) *int { // ERROR "leaking param: NAME-i" var a [12]*int a[0] = i return a[1] } -func foo60a(i *int) *int { +func foo60a(i *int) *int { // ERROR "i does not escape" var a [12]*int a[0] = i return nil } // assigning to a struct field is like assigning to the struct -func foo61(i *int) *int { // ERROR "leaking param: NAME-i" +func foo61(i *int) *int { // ERROR "leaking param: NAME-i" type S struct { - a,b *int + a, b *int } var s S s.a = i return s.b } -func foo61a(i *int) *int { +func foo61a(i *int) *int { // ERROR "i does not escape" type S struct { - a,b *int + a, b *int } var s S s.a = i @@ -380,36 +375,38 @@ func foo61a(i *int) *int { // assigning to a struct field is like assigning to the struct but // here this subtlety is lost, since s.a counts as an assignment to a // track-losing dereference. -func foo62(i *int) *int { // ERROR "leaking param: NAME-i" +func foo62(i *int) *int { // ERROR "leaking param: NAME-i" type S struct { - a,b *int + a, b *int } - s := new(S) + s := new(S) // ERROR "new[(]S[)] does not escape" s.a = i - return nil // s.b + return nil // s.b } - -type M interface { M() } - -func foo63(m M) { +type M interface { + M() } -func foo64(m M) { // ERROR "leaking param: NAME-m" +func foo63(m M) { // ERROR "m does not escape" +} + +func foo64(m M) { // ERROR "leaking param: NAME-m" m.M() } type MV int + func (MV) M() {} func foo65() { var mv MV - foo63(&mv) + foo63(&mv) // ERROR "&mv does not escape" } func foo66() { - var mv MV // ERROR "moved to heap: NAME-mv" - foo64(&mv) + var mv MV // ERROR "moved to heap: NAME-mv" + foo64(&mv) // ERROR "&mv escapes to heap" } func foo67() { @@ -419,48 +416,49 @@ func foo67() { func foo68() { var mv MV - foo64(mv) // escapes but it's an int so irrelevant + foo64(mv) // escapes but it's an int so irrelevant } -func foo69(m M) { // ERROR "leaking param: NAME-m" +func foo69(m M) { // ERROR "leaking param: NAME-m" foo64(m) } -func foo70(mv1 *MV, m M) { // ERROR "leaking param: NAME-mv1" "leaking param: NAME-m" +func foo70(mv1 *MV, m M) { // ERROR "leaking param: NAME-mv1" "leaking param: NAME-m" m = mv1 foo64(m) } -func foo71(x *int) []*int { // ERROR "leaking param: NAME-x" +func foo71(x *int) []*int { // ERROR "leaking param: NAME-x" var y []*int y = append(y, x) return y } -func foo71a(x int) []*int { // ERROR "moved to heap: NAME-x" +func foo71a(x int) []*int { // ERROR "moved to heap: NAME-x" var y []*int - y = append(y, &x) + y = append(y, &x) // ERROR "&x escapes to heap" return y } func foo72() { var x int var y [1]*int - y[0] = &x + y[0] = &x // ERROR "&x does not escape" } func foo72aa() [10]*int { - var x int // ERROR "moved to heap: NAME-x" + var x int // ERROR "moved to heap: NAME-x" var y [10]*int - y[0] = &x + y[0] = &x // ERROR "&x escapes to heap" return y } func foo72a() { var y [10]*int for i := 0; i < 10; i++ { - x := i // not moved to heap b/c y goes nowhere - y[i] = &x + // escapes its scope + x := i // ERROR "moved to heap: NAME-x" + y[i] = &x // ERROR "&x escapes to heap" } return } @@ -468,113 +466,116 @@ func foo72a() { func foo72b() [10]*int { var y [10]*int for i := 0; i < 10; i++ { - x := i // ERROR "moved to heap: NAME-x" - y[i] = &x + x := i // ERROR "moved to heap: NAME-x" + y[i] = &x // ERROR "&x escapes to heap" } return y } - // issue 2145 func foo73() { - s := []int{3,2,1} + s := []int{3, 2, 1} // ERROR "slice literal does not escape" for _, v := range s { - vv := v // ERROR "moved to heap: NAME-vv" - defer func() { // "func literal escapes its scope" "&vv escapes its scope" - println(vv) + vv := v // ERROR "moved to heap: NAME-vv" + // actually just escapes its scope + defer func() { // ERROR "func literal escapes to heap" + println(vv) // ERROR "&vv escapes to heap" }() } } func foo74() { - s := []int{3,2,1} + s := []int{3, 2, 1} // ERROR "slice literal does not escape" for _, v := range s { - vv := v // ERROR "moved to heap: NAME-vv" - fn := func() { // "func literal escapes its scope" "&vv escapes its scope" - println(vv) + vv := v // ERROR "moved to heap: NAME-vv" + // actually just escapes its scope + fn := func() { // ERROR "func literal escapes to heap" + println(vv) // ERROR "&vv escapes to heap" } defer fn() } } -func myprint(y *int, x ...interface{}) *int { // ERROR "leaking param: NAME-y" +func myprint(y *int, x ...interface{}) *int { // ERROR "x does not escape" "leaking param: NAME-y" return y } -func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "leaking param: NAME-x" - return &x[0] +func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "y does not escape" "leaking param: NAME-x" + return &x[0] // ERROR "&x.0. escapes to heap" } func foo75(z *int) { // ERROR "leaking param: NAME-z" - myprint(z, 1, 2, 3) + myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" } -func foo75a(z *int) { - myprint1(z, 1, 2, 3) // "[.][.][.] argument escapes to heap" +func foo75a(z *int) { // ERROR "z does not escape" + myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap" } -func foo76(z *int) { - myprint(nil, z) +func foo76(z *int) { // ERROR "leaking param: NAME-z" + myprint(nil, z) // ERROR "[.][.][.] argument does not escape" } -func foo76a(z *int) { // ERROR "leaking param: NAME-z" - myprint1(nil, z) // "[.][.][.] argument escapes to heap" +func foo76a(z *int) { // ERROR "leaking param: NAME-z" + myprint1(nil, z) // ERROR "[.][.][.] argument escapes to heap" } func foo76b() { - myprint(nil, 1, 2, 3) + myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" } func foo76c() { - myprint1(nil, 1, 2, 3) // "[.][.][.] argument escapes to heap" + myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap" } func foo76d() { - defer myprint(nil, 1, 2, 3) + defer myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape" } func foo76e() { - defer myprint1(nil, 1, 2, 3) // "[.][.][.] argument escapes to heap" + defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap" } func foo76f() { for { - defer myprint(nil, 1, 2, 3) // "[.][.][.] argument escapes its scope" + // TODO: This one really only escapes its scope, but we don't distinguish yet. + defer myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap" } } func foo76g() { for { - defer myprint1(nil, 1, 2, 3) // "[.][.][.] argument escapes to heap" + defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap" } } -func foo77(z []interface{}) { - myprint(nil, z...) // z does not escape +func foo77(z []interface{}) { // ERROR "z does not escape" + myprint(nil, z...) // z does not escape } -func foo77a(z []interface{}) { // ERROR "leaking param: NAME-z" +func foo77a(z []interface{}) { // ERROR "leaking param: NAME-z" myprint1(nil, z...) } -func foo78(z int) *int { // ERROR "moved to heap: NAME-z" - return &z // "&z escapes" +func foo78(z int) *int { // ERROR "moved to heap: NAME-z" + return &z // ERROR "&z escapes to heap" } -func foo78a(z int) *int { // ERROR "moved to heap: NAME-z" - y := &z - x := &y - return *x // really return y +func foo78a(z int) *int { // ERROR "moved to heap: NAME-z" + y := &z // ERROR "&z escapes to heap" + x := &y // ERROR "&y does not escape" + return *x // really return y } func foo79() *int { - return new(int) // "moved to heap: new[(]int[)]" + return new(int) // ERROR "new[(]int[)] escapes to heap" } func foo80() *int { var z *int for { - z = new(int) // "new[(]int[)] escapes its scope" + // Really just escapes its scope but we don't distinguish + z = new(int) // ERROR "new[(]int[)] escapes to heap" } _ = z return nil @@ -582,7 +583,7 @@ func foo80() *int { func foo81() *int { for { - z := new(int) + z := new(int) // ERROR "new[(]int[)] does not escape" _ = z } return nil @@ -593,28 +594,28 @@ type Fooer interface { } type LimitedFooer struct { - Fooer - N int64 + Fooer + N int64 } -func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: NAME-r" - return &LimitedFooer{r, n} +func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: NAME-r" + return &LimitedFooer{r, n} // ERROR "&struct literal escapes to heap" } -func foo90(x *int) map[*int]*int { // ERROR "leaking param: NAME-x" - return map[*int]*int{ nil: x } +func foo90(x *int) map[*int]*int { // ERROR "leaking param: NAME-x" + return map[*int]*int{nil: x} // ERROR "map literal escapes to heap" } -func foo91(x *int) map[*int]*int { // ERROR "leaking param: NAME-x" - return map[*int]*int{ x:nil } +func foo91(x *int) map[*int]*int { // ERROR "leaking param: NAME-x" + return map[*int]*int{x: nil} // ERROR "map literal escapes to heap" } -func foo92(x *int) [2]*int { // ERROR "leaking param: NAME-x" - return [2]*int{ x, nil } +func foo92(x *int) [2]*int { // ERROR "leaking param: NAME-x" + return [2]*int{x, nil} } // does not leak c -func foo93(c chan *int) *int { +func foo93(c chan *int) *int { // ERROR "c does not escape" for v := range c { return v } @@ -622,7 +623,7 @@ func foo93(c chan *int) *int { } // does not leak m -func foo94(m map[*int]*int, b bool) *int { +func foo94(m map[*int]*int, b bool) *int { // ERROR "m does not escape" for k, v := range m { if b { return k @@ -633,32 +634,32 @@ func foo94(m map[*int]*int, b bool) *int { } // does leak x -func foo95(m map[*int]*int, x *int) { // ERROR "leaking param: NAME-x" +func foo95(m map[*int]*int, x *int) { // ERROR "m does not escape" "leaking param: NAME-x" m[x] = x } // does not leak m -func foo96(m []*int) *int { +func foo96(m []*int) *int { // ERROR "m does not escape" return m[0] } // does leak m -func foo97(m [1]*int) *int { // ERROR "leaking param: NAME-m" +func foo97(m [1]*int) *int { // ERROR "leaking param: NAME-m" return m[0] } // does not leak m -func foo98(m map[int]*int) *int { +func foo98(m map[int]*int) *int { // ERROR "m does not escape" return m[0] } // does leak m -func foo99(m *[1]*int) []*int { // ERROR "leaking param: NAME-m" +func foo99(m *[1]*int) []*int { // ERROR "leaking param: NAME-m" return m[:] } // does not leak m -func foo100(m []*int) *int { +func foo100(m []*int) *int { // ERROR "m does not escape" for _, v := range m { return v } @@ -666,36 +667,110 @@ func foo100(m []*int) *int { } // does leak m -func foo101(m [1]*int) *int { // ERROR "leaking param: NAME-m" +func foo101(m [1]*int) *int { // ERROR "leaking param: NAME-m" for _, v := range m { return v } return nil } +// does not leak m +func foo101a(m [1]*int) *int { // ERROR "m does not escape" + for i := range m { // ERROR "moved to heap: NAME-i" + return &i // ERROR "&i escapes to heap" + } + return nil +} + // does leak x -func foo102(m []*int, x *int) { // ERROR "leaking param: NAME-x" +func foo102(m []*int, x *int) { // ERROR "m does not escape" "leaking param: NAME-x" m[0] = x } // does not leak x -func foo103(m [1]*int, x *int) { +func foo103(m [1]*int, x *int) { // ERROR "m does not escape" "x does not escape" m[0] = x } var y []*int // does not leak x -func foo104(x []*int) { +func foo104(x []*int) { // ERROR "x does not escape" copy(y, x) } // does not leak x -func foo105(x []*int) { +func foo105(x []*int) { // ERROR "x does not escape" _ = append(y, x...) } // does leak x -func foo106(x *int) { // ERROR "leaking param: NAME-x" +func foo106(x *int) { // ERROR "leaking param: NAME-x" _ = append(y, x) } + +func foo107(x *int) map[*int]*int { // ERROR "leaking param: NAME-x" + return map[*int]*int{x: nil} // ERROR "map literal escapes to heap" +} + +func foo108(x *int) map[*int]*int { // ERROR "leaking param: NAME-x" + return map[*int]*int{nil: x} // ERROR "map literal escapes to heap" +} + +func foo109(x *int) *int { // ERROR "leaking param: NAME-x" + m := map[*int]*int{x: nil} // ERROR "map literal does not escape" + for k, _ := range m { + return k + } + return nil +} + +func foo110(x *int) *int { // ERROR "leaking param: NAME-x" + m := map[*int]*int{nil: x} // ERROR "map literal does not escape" + return m[nil] +} + +func foo111(x *int) *int { // ERROR "leaking param: NAME-x" + m := []*int{x} // ERROR "slice literal does not escape" + return m[0] +} + +func foo112(x *int) *int { // ERROR "leaking param: NAME-x" + m := [1]*int{x} + return m[0] +} + +func foo113(x *int) *int { // ERROR "leaking param: NAME-x" + m := Bar{ii: x} + return m.ii +} + +func foo114(x *int) *int { // ERROR "leaking param: NAME-x" + m := &Bar{ii: x} // ERROR "&struct literal does not escape" + return m.ii +} + +func foo115(x *int) *int { // ERROR "leaking param: NAME-x" + return (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(x)) + 1)) +} + +func foo116(b bool) *int { + if b { + x := 1 // ERROR "moved to heap: NAME-x" + return &x // ERROR "&x escapes to heap" + } else { + y := 1 // ERROR "moved to heap: NAME-y" + return &y // ERROR "&y escapes to heap" + } + return nil +} + +func foo117(unknown func(interface{})) { // ERROR "unknown does not escape" + x := 1 // ERROR "moved to heap: NAME-x" + unknown(&x) // ERROR "&x escapes to heap" +} + +func foo118(unknown func(*int)) { // ERROR "unknown does not escape" + x := 1 // ERROR "moved to heap: NAME-x" + unknown(&x) // ERROR "&x escapes to heap" +}