Commit db5f9da4 authored by Russ Cox's avatar Russ Cox

gc: tweak and enable escape analysis

-s now means *disable* escape analysis.

Fix escape leaks for struct/slice/map literals.
Add ... tracking.
Rewrite new(T) and slice literal into stack allocation when safe.

Add annotations to reflect.
Reflect is too chummy with the compiler,
so changes like these affect it more than they should.

R=lvd, dave, gustavo
CC=golang-dev
https://golang.org/cl/4954043
parent 2a808826
...@@ -39,8 +39,6 @@ Flags: ...@@ -39,8 +39,6 @@ Flags:
show entire file path when printing line numbers in errors show entire file path when printing line numbers in errors
-I dir1 -I dir2 -I dir1 -I dir2
add dir1 and dir2 to the list of paths to check for imported packages add dir1 and dir2 to the list of paths to check for imported packages
-N
disable optimization
-S -S
write assembly language text to standard output write assembly language text to standard output
-u -u
......
...@@ -2,15 +2,7 @@ ...@@ -2,15 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// //
// The base version before this file existed, active with debug['s'] // Escape analysis.
// == 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:
// //
// First escfunc, esc and escassign recurse over the ast of each // First escfunc, esc and escassign recurse over the ast of each
// function to dig out flow(dst,src) edges between any // function to dig out flow(dst,src) edges between any
...@@ -25,9 +17,16 @@ ...@@ -25,9 +17,16 @@
// variables of it can reach an & node as escaping and all function // variables of it can reach an & node as escaping and all function
// parameters it can reach as leaking. // parameters it can reach as leaking.
// //
// Watch the variables moved to the heap and parameters tagged as // If a value's address is taken but the address does not escape,
// unsafe with -m, more detailed analysis output with -mm // 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 <u.h> #include <u.h>
#include <libc.h> #include <libc.h>
...@@ -53,9 +52,9 @@ static Node theSink; ...@@ -53,9 +52,9 @@ static Node theSink;
static NodeList* dsts; // all dst nodes static NodeList* dsts; // all dst nodes
static int loopdepth; // for detecting nested loop scopes static int loopdepth; // for detecting nested loop scopes
static int pdepth; // for debug printing in recursions. 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 Strlit* safetag; // gets slapped on safe parameters' field types for export
static int dstcount, edgecount; // diagnostic static int dstcount, edgecount; // diagnostic
static NodeList* noesc; // list of possible non-escaping nodes, for printing
void void
escapes(void) escapes(void)
...@@ -85,8 +84,17 @@ escapes(void) ...@@ -85,8 +84,17 @@ escapes(void)
for(l=xtop; l; l=l->next) for(l=xtop; l; l=l->next)
if(l->n->op == ODCLFUNC) if(l->n->op == ODCLFUNC)
esctag(l->n); 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 static void
escfunc(Node *func) escfunc(Node *func)
{ {
...@@ -109,7 +117,10 @@ escfunc(Node *func) ...@@ -109,7 +117,10 @@ escfunc(Node *func)
ll->n->escloopdepth = loopdepth; ll->n->escloopdepth = loopdepth;
break; break;
case PPARAM: case PPARAM:
if(ll->n->type && !haspointers(ll->n->type))
break;
ll->n->esc = EscNone; // prime for escflood later ll->n->esc = EscNone; // prime for escflood later
noesc = list(noesc, ll->n);
ll->n->escloopdepth = loopdepth; ll->n->escloopdepth = loopdepth;
break; break;
} }
...@@ -143,7 +154,7 @@ static void ...@@ -143,7 +154,7 @@ static void
esc(Node *n) esc(Node *n)
{ {
int lno; int lno;
NodeList *ll, *lr, *l; NodeList *ll, *lr;
if(n == N) if(n == N)
return; return;
...@@ -153,14 +164,15 @@ esc(Node *n) ...@@ -153,14 +164,15 @@ esc(Node *n)
if(n->op == OFOR || n->op == ORANGE) if(n->op == OFOR || n->op == ORANGE)
loopdepth++; loopdepth++;
esclist(n->ninit); esc(n->left);
esclist(n->list); esc(n->right);
esclist(n->rlist);
esc(n->ntest); esc(n->ntest);
esc(n->nincr); esc(n->nincr);
esclist(n->ninit);
esclist(n->nbody); esclist(n->nbody);
esc(n->left); esclist(n->nelse);
esc(n->right); esclist(n->list);
esclist(n->rlist);
if(n->op == OFOR || n->op == ORANGE) if(n->op == OFOR || n->op == ORANGE)
loopdepth--; loopdepth--;
...@@ -182,7 +194,7 @@ esc(Node *n) ...@@ -182,7 +194,7 @@ esc(Node *n)
case ORANGE: case ORANGE:
// Everything but fixed array is a dereference. // 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); escassign(n->list->next->n, n->right);
break; break;
...@@ -192,7 +204,6 @@ esc(Node *n) ...@@ -192,7 +204,6 @@ esc(Node *n)
// ntest->right is the argument of the .(type), // ntest->right is the argument of the .(type),
// ll->n->nname is the variable per case // ll->n->nname is the variable per case
escassign(ll->n->nname, n->ntest->right); escassign(ll->n->nname, n->ntest->right);
esclist(ll->n->nbody);
} }
} }
break; break;
...@@ -261,16 +272,47 @@ esc(Node *n) ...@@ -261,16 +272,47 @@ esc(Node *n)
break; break;
case OARRAYLIT: case OARRAYLIT:
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: case OSTRUCTLIT:
for(l=n->list; l; l=l->next) // Link values to struct.
escassign(n, l->n->right); for(ll=n->list; ll; ll=ll->next)
escassign(n, ll->n->right);
break; break;
case OMAPLIT: case OMAPLIT:
for(l=n->list; l; l=l->next) { n->esc = EscNone; // until proven otherwise
escassign(n, l->n->left); noesc = list(noesc, n);
escassign(n, l->n->right); 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; 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; lineno = lno;
...@@ -284,7 +326,6 @@ static void ...@@ -284,7 +326,6 @@ static void
escassign(Node *dst, Node *src) escassign(Node *dst, Node *src)
{ {
int lno; int lno;
NodeList *ll;
if(isblank(dst) || dst == N || src == N || src->op == ONONAME || src->op == OXXX) if(isblank(dst) || dst == N || src == N || src->op == ONONAME || src->op == OXXX)
return; return;
...@@ -336,11 +377,6 @@ escassign(Node *dst, Node *src) ...@@ -336,11 +377,6 @@ escassign(Node *dst, Node *src)
break; break;
} }
if(src->typecheck == 0 && src->op != OKEY) {
dump("escassign missing typecheck", src);
fatal("escassign");
}
lno = setlineno(src); lno = setlineno(src);
pdepth++; pdepth++;
...@@ -350,6 +386,10 @@ escassign(Node *dst, Node *src) ...@@ -350,6 +386,10 @@ escassign(Node *dst, Node *src)
case ODOTPTR: // dst = (*x).f case ODOTPTR: // dst = (*x).f
case ONAME: case ONAME:
case OPARAM: case OPARAM:
case ODDDARG:
case OARRAYLIT:
case OMAPLIT:
case OSTRUCTLIT:
// loopdepth was set in the defining statement or function header // loopdepth was set in the defining statement or function header
escflows(dst, src); escflows(dst, src);
break; break;
...@@ -377,32 +417,39 @@ escassign(Node *dst, Node *src) ...@@ -377,32 +417,39 @@ escassign(Node *dst, Node *src)
escassign(dst, src->left); escassign(dst, src->left);
break; 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 OMAKECHAN:
case OMAKEMAP: case OMAKEMAP:
case OMAKESLICE: case OMAKESLICE:
case ONEW: case ONEW:
src->curfn = curfn; // should have been done in parse, but patch it up here.
src->escloopdepth = loopdepth;
escflows(dst, src); escflows(dst, src);
break; break;
case OCLOSURE: case OCLOSURE:
src->curfn = curfn; // should have been done in parse, but patch it up here.
src->escloopdepth = loopdepth;
escflows(dst, src); escflows(dst, src);
escfunc(src); escfunc(src);
break; 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--; pdepth--;
...@@ -420,7 +467,7 @@ static void ...@@ -420,7 +467,7 @@ static void
esccall(Node *n) esccall(Node *n)
{ {
NodeList *ll, *lr; NodeList *ll, *lr;
Node *a, *fn; Node *a, *fn, *src;
Type *t, *fntype; Type *t, *fntype;
fn = N; fn = N;
...@@ -458,21 +505,32 @@ esccall(Node *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. // Local function. Incorporate into flow graph.
// Receiver. // Receiver.
if(n->op != OCALLFUNC) if(n->op != OCALLFUNC)
escassign(fn->ntype->left->left, n->left->left); escassign(fn->ntype->left->left, n->left->left);
for(ll=n->list, lr=fn->ntype->list; ll && lr; ll=ll->next) { for(lr=fn->ntype->list; ll && lr; ll=ll->next, lr=lr->next) {
if (lr->n->left) src = ll->n;
escassign(lr->n->left, ll->n); if(lr->n->isddd && !n->isddd) {
else // Introduce ODDDARG node to represent ... allocation.
escassign(&theSink, ll->n); src = nod(ODDDARG, N, N);
if(lr->n->left && !lr->n->left->isddd) src->escloopdepth = loopdepth;
lr=lr->next; 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; return;
} }
...@@ -483,11 +541,25 @@ esccall(Node *n) ...@@ -483,11 +541,25 @@ esccall(Node *n)
escassign(&theSink, n->left->left); escassign(&theSink, n->left->left);
} }
for(t=getinargx(fntype)->type; ll; ll=ll->next) { 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) if(!t->note || strcmp(t->note->s, safetag->s) != 0)
escassign(&theSink, ll->n); escassign(&theSink, src);
if(t->down) if(src != ll->n)
break;
t = t->down; 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. // Store the link src->dst in dst, throwing out some quick wins.
...@@ -536,12 +608,12 @@ escflood(Node *dst) ...@@ -536,12 +608,12 @@ escflood(Node *dst)
} }
if(debug['m']>1) 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->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S,
dst->escloopdepth); dst->escloopdepth);
for(l = dst->escflowsrc; l; l=l->next) { for(l = dst->escflowsrc; l; l=l->next) {
floodgen++; walkgen++;
escwalk(0, dst, l->n); escwalk(0, dst, l->n);
} }
} }
...@@ -552,9 +624,9 @@ escwalk(int level, Node *dst, Node *src) ...@@ -552,9 +624,9 @@ escwalk(int level, Node *dst, Node *src)
NodeList *ll; NodeList *ll;
int leaks; int leaks;
if(src->escfloodgen == floodgen) if(src->walkgen == walkgen)
return; return;
src->escfloodgen = floodgen; src->walkgen = walkgen;
if(debug['m']>1) if(debug['m']>1)
print("escwalk: level:%d depth:%d %.*s %hN scope:%#S[%d]\n", print("escwalk: level:%d depth:%d %.*s %hN scope:%#S[%d]\n",
...@@ -570,19 +642,42 @@ escwalk(int level, Node *dst, Node *src) ...@@ -570,19 +642,42 @@ escwalk(int level, Node *dst, Node *src)
if(src->class == PPARAM && leaks && src->esc == EscNone) { if(src->class == PPARAM && leaks && src->esc == EscNone) {
src->esc = EscScope; src->esc = EscScope;
if(debug['m']) if(debug['m'])
print("%L:leaking param: %hN\n", src->lineno, src); warnl(src->lineno, "leaking param: %hN", src);
} }
break; break;
case OADDR: case OADDR:
if(leaks) if(leaks) {
src->esc = EscHeap;
addrescapes(src->left); addrescapes(src->left);
if(debug['m'])
warnl(src->lineno, "%#N escapes to heap", src);
}
escwalk(level-1, dst, src->left); escwalk(level-1, dst, src->left);
break; 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: case OINDEX:
if(isfixedarray(src->type)) if(isfixedarray(src->type))
break; break;
// fall through
case OSLICE: case OSLICE:
case ODOTPTR: case ODOTPTR:
case OINDEXMAP: case OINDEXMAP:
...@@ -616,8 +711,6 @@ esctag(Node *func) ...@@ -616,8 +711,6 @@ esctag(Node *func)
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;
default:
fatal("messed up escape tagging: %N::%N", curfn, ll->n);
} }
} }
......
...@@ -98,7 +98,7 @@ addrescapes(Node *n) ...@@ -98,7 +98,7 @@ addrescapes(Node *n)
if(n->class == PAUTO && n->esc == EscNever) if(n->class == PAUTO && n->esc == EscNever)
break; 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); fatal("without escape analysis, only PAUTO's should have esc: %N", n);
switch(n->class) { switch(n->class) {
...@@ -121,8 +121,8 @@ addrescapes(Node *n) ...@@ -121,8 +121,8 @@ addrescapes(Node *n)
fatal("addrescapes before param assignment"); fatal("addrescapes before param assignment");
n->stackparam->xoffset = n->xoffset; n->stackparam->xoffset = n->xoffset;
// fallthrough // fallthrough
case PAUTO:
case PAUTO:
n->class |= PHEAP; n->class |= PHEAP;
n->addable = 0; n->addable = 0;
n->ullman = 2; n->ullman = 2;
...@@ -136,10 +136,8 @@ addrescapes(Node *n) ...@@ -136,10 +136,8 @@ addrescapes(Node *n)
n->heapaddr->class = PHEAP-1; // defer tempname to allocparams n->heapaddr->class = PHEAP-1; // defer tempname to allocparams
n->heapaddr->ullman = 1; n->heapaddr->ullman = 1;
n->curfn->dcl = list(n->curfn->dcl, n->heapaddr); n->curfn->dcl = list(n->curfn->dcl, n->heapaddr);
if(!debug['s'])
if(debug['s'])
n->esc = EscHeap; n->esc = EscHeap;
if(debug['m']) if(debug['m'])
print("%L: moved to heap: %hN\n", n->lineno, n); print("%L: moved to heap: %hN\n", n->lineno, n);
......
...@@ -211,6 +211,18 @@ enum ...@@ -211,6 +211,18 @@ enum
struct Node 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 op;
uchar ullman; // sethi/ullman number uchar ullman; // sethi/ullman number
uchar addable; // type of addressability - 0 is not addressable uchar addable; // type of addressability - 0 is not addressable
...@@ -236,26 +248,10 @@ struct Node ...@@ -236,26 +248,10 @@ struct Node
uchar implicit; // don't show in printout uchar implicit; // don't show in printout
// most nodes // most nodes
Node* left;
Node* right;
Type* type; Type* type;
Type* realtype; // as determined by typecheck Type* realtype; // as determined by typecheck
NodeList* list;
NodeList* rlist;
Node* orig; // original form, for printing, and tracking copies of ONAMEs 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 // func
Node* nname; Node* nname;
Node* shortname; Node* shortname;
...@@ -289,7 +285,6 @@ struct Node ...@@ -289,7 +285,6 @@ struct Node
// Escape analysis. // Escape analysis.
NodeList* escflowsrc; // flow(this, src) 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 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 Sym* sym; // various
int32 vargen; // unique name for OTYPE/ONAME int32 vargen; // unique name for OTYPE/ONAME
...@@ -299,9 +294,25 @@ struct Node ...@@ -299,9 +294,25 @@ struct Node
int32 stkdelta; // offset added by stack frame compaction phase. int32 stkdelta; // offset added by stack frame compaction phase.
int32 ostk; int32 ostk;
int32 iota; int32 iota;
uint32 walkgen;
}; };
#define N ((Node*)0) #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 struct NodeList
{ {
...@@ -1189,6 +1200,7 @@ uint32 typehash(Type *t); ...@@ -1189,6 +1200,7 @@ uint32 typehash(Type *t);
void ullmancalc(Node *n); void ullmancalc(Node *n);
void umagic(Magic *m); void umagic(Magic *m);
void warn(char *fmt, ...); void warn(char *fmt, ...);
void warnl(int line, char *fmt, ...);
void yyerror(char *fmt, ...); void yyerror(char *fmt, ...);
void yyerrorl(int line, char *fmt, ...); void yyerrorl(int line, char *fmt, ...);
......
...@@ -78,13 +78,17 @@ usage(void) ...@@ -78,13 +78,17 @@ usage(void)
print("flags:\n"); print("flags:\n");
// -A is allow use of "any" type, for bootstrapping // -A is allow use of "any" type, for bootstrapping
print(" -I DIR search for packages in DIR\n"); 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(" -d print declarations\n");
print(" -e no limit on number of errors printed\n"); print(" -e no limit on number of errors printed\n");
print(" -f print stack frame structure\n"); print(" -f print stack frame structure\n");
print(" -h panic on an error\n"); print(" -h panic on an error\n");
print(" -m print about moves to heap\n");
print(" -o file specify output file\n"); print(" -o file specify output file\n");
print(" -S print the assembly language\n"); print(" -s disable escape analysis\n");
print(" -V print the compiler version\n");
print(" -u disable package unsafe\n"); print(" -u disable package unsafe\n");
print(" -w print the parse tree after typing\n"); print(" -w print the parse tree after typing\n");
print(" -x print lex tokens\n"); print(" -x print lex tokens\n");
...@@ -273,7 +277,7 @@ main(int argc, char *argv[]) ...@@ -273,7 +277,7 @@ main(int argc, char *argv[])
errorexit(); errorexit();
// Phase 3b: escape analysis. // Phase 3b: escape analysis.
if(debug['s']) if(!debug['s'])
escapes(); escapes();
// Phase 4: Compile function bodies. // Phase 4: Compile function bodies.
......
...@@ -410,7 +410,6 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) ...@@ -410,7 +410,6 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init)
dowidth(t); dowidth(t);
if(ctxt != 0) { if(ctxt != 0) {
// put everything into static array // put everything into static array
vstat = staticname(t, ctxt); vstat = staticname(t, ctxt);
arraylit(ctxt, 1, n, vstat, init); arraylit(ctxt, 1, n, vstat, init);
...@@ -457,9 +456,15 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init) ...@@ -457,9 +456,15 @@ slicelit(int ctxt, Node *n, Node *var, NodeList **init)
vauto = nod(OXXX, N, N); vauto = nod(OXXX, N, N);
tempname(vauto, ptrto(t)); tempname(vauto, ptrto(t));
// set auto to point at new heap (3 assign) // 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 = nod(ONEW, N, N);
a->list = list1(typenod(t)); a->list = list1(typenod(t));
}
a = nod(OAS, vauto, a); a = nod(OAS, vauto, a);
typecheck(&a, Etop); typecheck(&a, Etop);
walkexpr(&a, init); walkexpr(&a, init);
......
...@@ -211,6 +211,16 @@ warn(char *fmt, ...) ...@@ -211,6 +211,16 @@ warn(char *fmt, ...)
hcrash(); hcrash();
} }
void
warnl(int line, char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
adderr(line, fmt, arg);
va_end(arg);
}
void void
fatal(char *fmt, ...) fatal(char *fmt, ...)
{ {
...@@ -485,6 +495,7 @@ nod(int op, Node *nleft, Node *nright) ...@@ -485,6 +495,7 @@ nod(int op, Node *nleft, Node *nright)
n->lineno = parserline(); n->lineno = parserline();
n->xoffset = BADWIDTH; n->xoffset = BADWIDTH;
n->orig = n; n->orig = n;
n->curfn = curfn;
return n; return n;
} }
......
...@@ -538,7 +538,7 @@ reswitch: ...@@ -538,7 +538,7 @@ reswitch:
goto error; goto error;
// top&Eindir means this is &x in *&x. (or the arg to built-in print) // 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. // 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); addrescapes(n->left);
n->type = ptrto(t); n->type = ptrto(t);
goto ret; goto ret;
...@@ -1607,7 +1607,7 @@ lookdot(Node *n, Type *t, int dostrcmp) ...@@ -1607,7 +1607,7 @@ lookdot(Node *n, Type *t, int dostrcmp)
if(!eqtype(rcvr, tt)) { if(!eqtype(rcvr, tt)) {
if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) { if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) {
checklvalue(n->left, "call pointer method on"); checklvalue(n->left, "call pointer method on");
if(!debug['s']) if(debug['s'])
addrescapes(n->left); addrescapes(n->left);
n->left = nod(OADDR, n->left, N); n->left = nod(OADDR, n->left, N);
n->left->implicit = 1; n->left->implicit = 1;
......
...@@ -13,7 +13,7 @@ static Node* makenewvar(Type*, NodeList**, Node**); ...@@ -13,7 +13,7 @@ static Node* makenewvar(Type*, NodeList**, Node**);
static Node* ascompatee1(int, Node*, Node*, NodeList**); static Node* ascompatee1(int, Node*, Node*, NodeList**);
static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**); static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**);
static NodeList* ascompatet(int, NodeList*, Type**, int, 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 Node* convas(Node*, NodeList**);
static void heapmoves(void); static void heapmoves(void);
static NodeList* paramstoheap(Type **argin, int out); static NodeList* paramstoheap(Type **argin, int out);
...@@ -289,7 +289,7 @@ walkstmt(Node **np) ...@@ -289,7 +289,7 @@ walkstmt(Node **np)
n->list = reorder3(ll); n->list = reorder3(ll);
break; 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; n->list = ll;
break; break;
...@@ -484,7 +484,7 @@ walkexpr(Node **np, NodeList **init) ...@@ -484,7 +484,7 @@ walkexpr(Node **np, NodeList **init)
goto ret; goto ret;
walkexpr(&n->left, init); walkexpr(&n->left, init);
walkexprlist(n->list, 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); n->list = reorder1(ll);
goto ret; goto ret;
...@@ -501,7 +501,7 @@ walkexpr(Node **np, NodeList **init) ...@@ -501,7 +501,7 @@ walkexpr(Node **np, NodeList **init)
walkexpr(&n->left, init); walkexpr(&n->left, init);
walkexprlist(n->list, 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); n->list = reorder1(ll);
goto ret; goto ret;
...@@ -511,8 +511,8 @@ walkexpr(Node **np, NodeList **init) ...@@ -511,8 +511,8 @@ walkexpr(Node **np, NodeList **init)
goto ret; goto ret;
walkexpr(&n->left, init); walkexpr(&n->left, init);
walkexprlist(n->list, init); walkexprlist(n->list, init);
ll = ascompatte(n->op, 0, getthis(t), list1(n->left->left), 0, init); ll = ascompatte(n->op, n, 0, getthis(t), list1(n->left->left), 0, init);
lr = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init); lr = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
ll = concat(ll, lr); ll = concat(ll, lr);
n->left->left = N; n->left->left = N;
ullmancalc(n->left); ullmancalc(n->left);
...@@ -977,7 +977,16 @@ walkexpr(Node **np, NodeList **init) ...@@ -977,7 +977,16 @@ walkexpr(Node **np, NodeList **init)
goto ret; goto ret;
case ONEW: case ONEW:
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); n = callnew(n->type->type);
}
goto ret; goto ret;
case OCMPSTR: case OCMPSTR:
...@@ -1315,7 +1324,7 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init) ...@@ -1315,7 +1324,7 @@ ascompatet(int op, NodeList *nl, Type **nr, int fp, NodeList **init)
* package all the arguments that match a ... T parameter into a []T. * package all the arguments that match a ... T parameter into a []T.
*/ */
static NodeList* 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; Node *a, *n;
Type *tslice; Type *tslice;
...@@ -1324,12 +1333,18 @@ mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init) ...@@ -1324,12 +1333,18 @@ mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init)
tslice->type = l->type->type; tslice->type = l->type->type;
tslice->bound = -1; tslice->bound = -1;
if(count(lr0) == 0) {
n = nodnil();
n->type = tslice;
} else {
n = nod(OCOMPLIT, N, typenod(tslice)); n = nod(OCOMPLIT, N, typenod(tslice));
n->list = lr0; n->list = lr0;
n->esc = esc;
typecheck(&n, Erv); typecheck(&n, Erv);
if(n->type == T) if(n->type == T)
fatal("mkdotargslice: typecheck failed"); fatal("mkdotargslice: typecheck failed");
walkexpr(&n, init); walkexpr(&n, init);
}
a = nod(OAS, nodarg(l, fp), n); a = nod(OAS, nodarg(l, fp), n);
nn = list(nn, convas(a, init)); nn = list(nn, convas(a, init));
...@@ -1393,8 +1408,9 @@ dumpnodetypes(NodeList *l, char *what) ...@@ -1393,8 +1408,9 @@ dumpnodetypes(NodeList *l, char *what)
* func(expr-list) * func(expr-list)
*/ */
static NodeList* 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; Type *l, *ll;
Node *r, *a; Node *r, *a;
NodeList *nn, *lr0, *alist; NodeList *nn, *lr0, *alist;
...@@ -1458,7 +1474,10 @@ loop: ...@@ -1458,7 +1474,10 @@ loop:
// normal case -- make a slice of all // normal case -- make a slice of all
// remaining arguments and pass it to // remaining arguments and pass it to
// the ddd parameter. // 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; goto ret;
} }
......
...@@ -322,8 +322,31 @@ func packValue(flag uint32, typ *runtime.Type, word iword) Value { ...@@ -322,8 +322,31 @@ func packValue(flag uint32, typ *runtime.Type, word iword) Value {
return Value{Internal: *(*interface{})(unsafe.Pointer(&eface))} 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. // valueFromAddr returns a Value using the given type and address.
func valueFromAddr(flag uint32, typ Type, addr unsafe.Pointer) Value { 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 { if flag&flagAddr != 0 {
// Addressable, so the internal value is // Addressable, so the internal value is
// an interface containing a pointer to the real value. // an interface containing a pointer to the real value.
...@@ -1678,6 +1701,14 @@ func ValueOf(i interface{}) Value { ...@@ -1678,6 +1701,14 @@ func ValueOf(i interface{}) Value {
if i == nil { if i == nil {
return Value{} 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, // For an interface value with the noAddr bit set,
// the representation is identical to an empty interface. // the representation is identical to an empty interface.
eface := *(*emptyInterface)(unsafe.Pointer(&i)) eface := *(*emptyInterface)(unsafe.Pointer(&i))
......
// errchk -0 $G -sm $D/$F.go // errchk -0 $G -m $D/$F.go
// Copyright 2010 The Go Authors. All rights reserved. // Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
...@@ -11,7 +11,7 @@ import "unsafe" ...@@ -11,7 +11,7 @@ import "unsafe"
var gxx *int var gxx *int
func foo1(x int) { // ERROR "moved to heap: NAME-x" func foo1(x int) { // ERROR "moved to heap: NAME-x"
gxx = &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"
...@@ -19,33 +19,34 @@ func foo2(yy *int) { // ERROR "leaking param: NAME-yy" ...@@ -19,33 +19,34 @@ func foo2(yy *int) { // ERROR "leaking param: NAME-yy"
} }
func foo3(x int) *int { // ERROR "moved to heap: NAME-x" func foo3(x int) *int { // ERROR "moved to heap: NAME-x"
return &x return &x // ERROR "&x escapes to heap"
} }
type T *T type T *T
func foo3b(t T) { // ERROR "leaking param: NAME-t" func foo3b(t T) { // ERROR "leaking param: NAME-t"
*t = t *t = t
} }
// xx isn't going anywhere, so use of yy is ok // 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 = yy
} }
// xx isn't going anywhere, so taking address of yy is ok // xx isn't going anywhere, so taking address of yy is ok
func foo5(xx **int, yy *int) { func foo5(xx **int, yy *int) { // ERROR "xx does not escape" "yy does not escape"
xx = &yy 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 *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 **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 xx = yy
return *xx return *xx
} }
...@@ -55,47 +56,46 @@ func foo9(xx, yy *int) *int { // ERROR "leaking param: NAME-xx" "leaking param: ...@@ -55,47 +56,46 @@ func foo9(xx, yy *int) *int { // ERROR "leaking param: NAME-xx" "leaking param:
return xx return xx
} }
func foo10(xx, yy *int) { func foo10(xx, yy *int) { // ERROR "xx does not escape" "yy does not escape"
*xx = *yy *xx = *yy
} }
func foo11() int { func foo11() int {
x, y := 0, 42 x, y := 0, 42
xx := &x xx := &x // ERROR "&x does not escape"
yy := &y yy := &y // ERROR "&y does not escape"
*xx = *yy *xx = *yy
return x return x
} }
var xxx **int var xxx **int
func foo12(yyy **int) { // ERROR "leaking param: NAME-yyy" func foo12(yyy **int) { // ERROR "leaking param: NAME-yyy"
xxx = yyy xxx = yyy
} }
func foo13(yyy **int) { func foo13(yyy **int) { // ERROR "yyy does not escape"
*xxx = *yyy *xxx = *yyy
} }
func foo14(yyy **int) { func foo14(yyy **int) { // ERROR "yyy does not escape"
**xxx = **yyy **xxx = **yyy
} }
func foo15(yy *int) { // ERROR "moved to heap: NAME-yy" func foo15(yy *int) { // ERROR "moved to heap: NAME-yy"
xxx = &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 *xxx = yy
} }
func foo17(yy *int) { func foo17(yy *int) { // ERROR "yy does not escape"
**xxx = *yy **xxx = *yy
} }
func foo18(y int) { // ERROR "moved to heap: "NAME-y" func foo18(y int) { // ERROR "moved to heap: "NAME-y"
*xxx = &y *xxx = &y // ERROR "&y escapes to heap"
} }
func foo19(y int) { func foo19(y int) {
...@@ -108,22 +108,22 @@ type Bar struct { ...@@ -108,22 +108,22 @@ type Bar struct {
} }
func NewBar() *Bar { 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" func NewBarp(x *int) *Bar { // ERROR "leaking param: NAME-x"
return &Bar{ 42, x } return &Bar{42, x} // ERROR "&struct literal escapes to heap"
} }
func NewBarp2(x *int) *Bar { func NewBarp2(x *int) *Bar { // ERROR "x does not escape"
return &Bar{ *x, nil } 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) return *(b.ii)
} }
func (b *Bar) AlsoNoLeak() *int { func (b *Bar) AlsoNoLeak() *int { // ERROR "b does not escape"
return b.ii return b.ii
} }
...@@ -133,70 +133,69 @@ type Bar2 struct { ...@@ -133,70 +133,69 @@ type Bar2 struct {
} }
func NewBar2() *Bar2 { 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] return b.i[0]
} }
func (b *Bar2) Leak() []int { // ERROR "leaking param: NAME-b" func (b *Bar2) Leak() []int { // ERROR "leaking param: NAME-b"
return b.i[:] 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] return b.ii[0:1]
} }
func (b *Bar2) LeakSelf() { // ERROR "leaking param: NAME-b" func (b *Bar2) LeakSelf() { // ERROR "leaking param: NAME-b"
b.ii = b.i[0:4] 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 var buf []int
buf = b.i[0:] buf = b.i[0:] // ERROR "&b.i escapes to heap"
b.ii = buf b.ii = buf
} }
func foo21() func() int { func foo21() func() int {
x := 42 // ERROR "moved to heap: NAME-x" x := 42 // ERROR "moved to heap: NAME-x"
return func() int { return func() int { // ERROR "func literal escapes to heap"
return x return x // ERROR "&x escapes to heap"
} }
} }
func foo22() int { func foo22() int {
x := 42 x := 42
return func() int { return func() int { // ERROR "func literal does not escape"
return x return x
}() }()
} }
func foo23(x int) func() int { // ERROR "moved to heap: NAME-x" func foo23(x int) func() int { // ERROR "moved to heap: NAME-x"
return func() int { return func() int { // ERROR "func literal escapes to heap"
return x return x // ERROR "&x escapes to heap"
} }
} }
func foo23a(x int) (func() int) { // ERROR "moved to heap: NAME-x" func foo23a(x int) func() int { // ERROR "moved to heap: NAME-x"
f := func() int { f := func() int { // ERROR "func literal escapes to heap"
return x return x // ERROR "&x escapes to heap"
} }
return f return f
} }
func foo23b(x int) *(func() int) { // ERROR "moved to heap: NAME-x" func foo23b(x int) *(func() int) { // ERROR "moved to heap: NAME-x"
f := func() int { return x } // ERROR "moved to heap: NAME-f" f := func() int { return x } // ERROR "moved to heap: NAME-f" "func literal escapes to heap" "&x escapes to heap"
return &f return &f // ERROR "&f escapes to heap"
} }
func foo24(x int) int { func foo24(x int) int {
return func() int { return func() int { // ERROR "func literal does not escape"
return x return x
}() }()
} }
var x *int var x *int
func fooleak(xx *int) int { // ERROR "leaking param: NAME-xx" func fooleak(xx *int) int { // ERROR "leaking param: NAME-xx"
...@@ -204,16 +203,16 @@ func fooleak(xx *int) int { // ERROR "leaking param: NAME-xx" ...@@ -204,16 +203,16 @@ func fooleak(xx *int) int { // ERROR "leaking param: NAME-xx"
return *x return *x
} }
func foonoleak(xx *int) int { func foonoleak(xx *int) int { // ERROR "xx does not escape"
return *x + *xx return *x + *xx
} }
func foo31(x int) int { // ERROR "moved to heap: NAME-x" func foo31(x int) int { // ERROR "moved to heap: NAME-x"
return fooleak(&x) return fooleak(&x) // ERROR "&x escapes to heap"
} }
func foo32(x int) int { func foo32(x int) int {
return foonoleak(&x) return foonoleak(&x) // ERROR "&x does not escape"
} }
type Foo struct { type Foo struct {
...@@ -228,7 +227,7 @@ func (f *Foo) fooleak() { // ERROR "leaking param: NAME-f" ...@@ -228,7 +227,7 @@ func (f *Foo) fooleak() { // ERROR "leaking param: NAME-f"
pf = f pf = f
} }
func (f *Foo) foonoleak() { func (f *Foo) foonoleak() { // ERROR "f does not escape"
F.x = f.x F.x = f.x
} }
...@@ -236,87 +235,83 @@ func (f *Foo) Leak() { // ERROR "leaking param: NAME-f" ...@@ -236,87 +235,83 @@ func (f *Foo) Leak() { // ERROR "leaking param: NAME-f"
f.fooleak() f.fooleak()
} }
func (f *Foo) NoLeak() { func (f *Foo) NoLeak() { // ERROR "f does not escape"
f.foonoleak() f.foonoleak()
} }
func foo41(x int) { // ERROR "moved to heap: NAME-x" func foo41(x int) { // ERROR "moved to heap: NAME-x"
F.xx = &x F.xx = &x // ERROR "&x escapes to heap"
} }
func (f *Foo) foo42(x int) { // ERROR "moved to heap: NAME-x" func (f *Foo) foo42(x int) { // ERROR "f does not escape" "moved to heap: NAME-x"
f.xx = &x f.xx = &x // ERROR "&x escapes to heap"
} }
func foo43(f *Foo, x int) { // ERROR "moved to heap: NAME-x" func foo43(f *Foo, x int) { // ERROR "f does not escape" "moved to heap: NAME-x"
f.xx = &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 F.xx = yy
} }
func (f *Foo) foo45() { func (f *Foo) foo45() { // ERROR "f does not escape"
F.x = f.x F.x = f.x
} }
func (f *Foo) foo46() { func (f *Foo) foo46() { // ERROR "f does not escape"
F.xx = f.xx F.xx = f.xx
} }
func (f *Foo) foo47() { // ERROR "leaking param: NAME-f" func (f *Foo) foo47() { // ERROR "leaking param: NAME-f"
f.xx = &f.x f.xx = &f.x // ERROR "&f.x escapes to heap"
} }
var ptrSlice []*int var ptrSlice []*int
func foo50(i *int) { // ERROR "leaking param: NAME-i" func foo50(i *int) { // ERROR "leaking param: NAME-i"
ptrSlice[0] = i ptrSlice[0] = i
} }
var ptrMap map[*int]*int 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 ptrMap[i] = i
} }
func indaddr1(x int) *int { // ERROR "moved to heap: NAME-x" 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" func indaddr2(x *int) *int { // ERROR "leaking param: NAME-x"
return *&x return *&x // ERROR "&x does not escape"
} }
func indaddr3(x *int32) *int { // ERROR "leaking param: NAME-x" func indaddr3(x *int32) *int { // ERROR "leaking param: NAME-x"
return *(**int)(unsafe.Pointer(&x)) return *(**int)(unsafe.Pointer(&x)) // ERROR "&x does not escape"
} }
// From package math: // From package math:
func Float32bits(f float32) uint32 { 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 { 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 { 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 { func Float64frombits(b uint64) float64 {
return *(*float64)(unsafe.Pointer(&b)) return *(*float64)(unsafe.Pointer(&b)) // ERROR "&b does not escape"
} }
// contrast with // contrast with
func float64bitsptr(f float64) *uint64 { // ERROR "moved to heap: NAME-f" func float64bitsptr(f float64) *uint64 { // ERROR "moved to heap: NAME-f"
return (*uint64)(unsafe.Pointer(&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"
...@@ -329,7 +324,7 @@ func typesw(i interface{}) *int { // ERROR "leaking param: NAME-i" ...@@ -329,7 +324,7 @@ func typesw(i interface{}) *int { // ERROR "leaking param: NAME-i"
return val return val
case *int8: case *int8:
v := int(*val) // ERROR "moved to heap: NAME-v" v := int(*val) // ERROR "moved to heap: NAME-v"
return &v return &v // ERROR "&v escapes to heap"
} }
return nil return nil
} }
...@@ -352,7 +347,7 @@ func foo60(i *int) *int { // ERROR "leaking param: NAME-i" ...@@ -352,7 +347,7 @@ func foo60(i *int) *int { // ERROR "leaking param: NAME-i"
return a[1] return a[1]
} }
func foo60a(i *int) *int { func foo60a(i *int) *int { // ERROR "i does not escape"
var a [12]*int var a [12]*int
a[0] = i a[0] = i
return nil return nil
...@@ -361,16 +356,16 @@ func foo60a(i *int) *int { ...@@ -361,16 +356,16 @@ func foo60a(i *int) *int {
// assigning to a struct field is like assigning to the struct // 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 { type S struct {
a,b *int a, b *int
} }
var s S var s S
s.a = i s.a = i
return s.b return s.b
} }
func foo61a(i *int) *int { func foo61a(i *int) *int { // ERROR "i does not escape"
type S struct { type S struct {
a,b *int a, b *int
} }
var s S var s S
s.a = i s.a = i
...@@ -382,17 +377,18 @@ func foo61a(i *int) *int { ...@@ -382,17 +377,18 @@ func foo61a(i *int) *int {
// track-losing dereference. // 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 { type S struct {
a,b *int a, b *int
} }
s := new(S) s := new(S) // ERROR "new[(]S[)] does not escape"
s.a = i s.a = i
return nil // s.b return nil // s.b
} }
type M interface {
M()
}
type M interface { M() } func foo63(m M) { // ERROR "m does not escape"
func foo63(m M) {
} }
func foo64(m M) { // ERROR "leaking param: NAME-m" func foo64(m M) { // ERROR "leaking param: NAME-m"
...@@ -400,16 +396,17 @@ func foo64(m M) { // ERROR "leaking param: NAME-m" ...@@ -400,16 +396,17 @@ func foo64(m M) { // ERROR "leaking param: NAME-m"
} }
type MV int type MV int
func (MV) M() {} func (MV) M() {}
func foo65() { func foo65() {
var mv MV var mv MV
foo63(&mv) foo63(&mv) // ERROR "&mv does not escape"
} }
func foo66() { func foo66() {
var mv MV // ERROR "moved to heap: NAME-mv" var mv MV // ERROR "moved to heap: NAME-mv"
foo64(&mv) foo64(&mv) // ERROR "&mv escapes to heap"
} }
func foo67() { func foo67() {
...@@ -439,28 +436,29 @@ func foo71(x *int) []*int { // ERROR "leaking param: NAME-x" ...@@ -439,28 +436,29 @@ func foo71(x *int) []*int { // ERROR "leaking param: NAME-x"
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 var y []*int
y = append(y, &x) y = append(y, &x) // ERROR "&x escapes to heap"
return y return y
} }
func foo72() { func foo72() {
var x int var x int
var y [1]*int var y [1]*int
y[0] = &x y[0] = &x // ERROR "&x does not escape"
} }
func foo72aa() [10]*int { 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 var y [10]*int
y[0] = &x y[0] = &x // ERROR "&x escapes to heap"
return y return y
} }
func foo72a() { func foo72a() {
var y [10]*int var y [10]*int
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
x := i // not moved to heap b/c y goes nowhere // escapes its scope
y[i] = &x x := i // ERROR "moved to heap: NAME-x"
y[i] = &x // ERROR "&x escapes to heap"
} }
return return
} }
...@@ -469,87 +467,89 @@ func foo72b() [10]*int { ...@@ -469,87 +467,89 @@ func foo72b() [10]*int {
var y [10]*int var y [10]*int
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
x := i // ERROR "moved to heap: NAME-x" x := i // ERROR "moved to heap: NAME-x"
y[i] = &x y[i] = &x // ERROR "&x escapes to heap"
} }
return y return y
} }
// issue 2145 // issue 2145
func foo73() { func foo73() {
s := []int{3,2,1} s := []int{3, 2, 1} // ERROR "slice literal does not escape"
for _, v := range s { for _, v := range s {
vv := v // ERROR "moved to heap: NAME-vv" vv := v // ERROR "moved to heap: NAME-vv"
defer func() { // "func literal escapes its scope" "&vv escapes its scope" // actually just escapes its scope
println(vv) defer func() { // ERROR "func literal escapes to heap"
println(vv) // ERROR "&vv escapes to heap"
}() }()
} }
} }
func foo74() { func foo74() {
s := []int{3,2,1} s := []int{3, 2, 1} // ERROR "slice literal does not escape"
for _, v := range s { for _, v := range s {
vv := v // ERROR "moved to heap: NAME-vv" vv := v // ERROR "moved to heap: NAME-vv"
fn := func() { // "func literal escapes its scope" "&vv escapes its scope" // actually just escapes its scope
println(vv) fn := func() { // ERROR "func literal escapes to heap"
println(vv) // ERROR "&vv escapes to heap"
} }
defer fn() 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 return y
} }
func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "leaking param: NAME-x" func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "y does not escape" "leaking param: NAME-x"
return &x[0] return &x[0] // ERROR "&x.0. escapes to heap"
} }
func foo75(z *int) { // ERROR "leaking param: NAME-z" 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) { func foo75a(z *int) { // ERROR "z does not escape"
myprint1(z, 1, 2, 3) // "[.][.][.] argument escapes to heap" myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
} }
func foo76(z *int) { func foo76(z *int) { // ERROR "leaking param: NAME-z"
myprint(nil, z) myprint(nil, z) // ERROR "[.][.][.] argument does not escape"
} }
func foo76a(z *int) { // ERROR "leaking param: NAME-z" func foo76a(z *int) { // ERROR "leaking param: NAME-z"
myprint1(nil, z) // "[.][.][.] argument escapes to heap" myprint1(nil, z) // ERROR "[.][.][.] argument escapes to heap"
} }
func foo76b() { func foo76b() {
myprint(nil, 1, 2, 3) myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
} }
func foo76c() { func foo76c() {
myprint1(nil, 1, 2, 3) // "[.][.][.] argument escapes to heap" myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
} }
func foo76d() { func foo76d() {
defer myprint(nil, 1, 2, 3) defer myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
} }
func foo76e() { 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() { func foo76f() {
for { 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() { func foo76g() {
for { 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{}) { func foo77(z []interface{}) { // ERROR "z does not escape"
myprint(nil, z...) // z does not escape myprint(nil, z...) // z does not escape
} }
...@@ -558,23 +558,24 @@ func foo77a(z []interface{}) { // ERROR "leaking param: NAME-z" ...@@ -558,23 +558,24 @@ func foo77a(z []interface{}) { // ERROR "leaking param: NAME-z"
} }
func foo78(z int) *int { // ERROR "moved to heap: NAME-z" func foo78(z int) *int { // ERROR "moved to heap: NAME-z"
return &z // "&z escapes" return &z // ERROR "&z escapes to heap"
} }
func foo78a(z int) *int { // ERROR "moved to heap: NAME-z" func foo78a(z int) *int { // ERROR "moved to heap: NAME-z"
y := &z y := &z // ERROR "&z escapes to heap"
x := &y x := &y // ERROR "&y does not escape"
return *x // really return y return *x // really return y
} }
func foo79() *int { func foo79() *int {
return new(int) // "moved to heap: new[(]int[)]" return new(int) // ERROR "new[(]int[)] escapes to heap"
} }
func foo80() *int { func foo80() *int {
var z *int var z *int
for { 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 _ = z
return nil return nil
...@@ -582,7 +583,7 @@ func foo80() *int { ...@@ -582,7 +583,7 @@ func foo80() *int {
func foo81() *int { func foo81() *int {
for { for {
z := new(int) z := new(int) // ERROR "new[(]int[)] does not escape"
_ = z _ = z
} }
return nil return nil
...@@ -598,23 +599,23 @@ type LimitedFooer struct { ...@@ -598,23 +599,23 @@ type LimitedFooer struct {
} }
func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: NAME-r" func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: NAME-r"
return &LimitedFooer{r, n} return &LimitedFooer{r, n} // ERROR "&struct literal escapes to heap"
} }
func foo90(x *int) map[*int]*int { // ERROR "leaking param: NAME-x" func foo90(x *int) map[*int]*int { // ERROR "leaking param: NAME-x"
return map[*int]*int{ nil: 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" func foo91(x *int) map[*int]*int { // ERROR "leaking param: NAME-x"
return map[*int]*int{ x:nil } return map[*int]*int{x: nil} // ERROR "map literal escapes to heap"
} }
func foo92(x *int) [2]*int { // ERROR "leaking param: NAME-x" func foo92(x *int) [2]*int { // ERROR "leaking param: NAME-x"
return [2]*int{ x, nil } return [2]*int{x, nil}
} }
// does not leak c // 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 { for v := range c {
return v return v
} }
...@@ -622,7 +623,7 @@ func foo93(c chan *int) *int { ...@@ -622,7 +623,7 @@ func foo93(c chan *int) *int {
} }
// does not leak m // 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 { for k, v := range m {
if b { if b {
return k return k
...@@ -633,12 +634,12 @@ func foo94(m map[*int]*int, b bool) *int { ...@@ -633,12 +634,12 @@ func foo94(m map[*int]*int, b bool) *int {
} }
// does leak x // 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 m[x] = x
} }
// does not leak m // does not leak m
func foo96(m []*int) *int { func foo96(m []*int) *int { // ERROR "m does not escape"
return m[0] return m[0]
} }
...@@ -648,7 +649,7 @@ func foo97(m [1]*int) *int { // ERROR "leaking param: NAME-m" ...@@ -648,7 +649,7 @@ func foo97(m [1]*int) *int { // ERROR "leaking param: NAME-m"
} }
// does not leak m // 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] return m[0]
} }
...@@ -658,7 +659,7 @@ func foo99(m *[1]*int) []*int { // ERROR "leaking param: NAME-m" ...@@ -658,7 +659,7 @@ func foo99(m *[1]*int) []*int { // ERROR "leaking param: NAME-m"
} }
// does not leak m // does not leak m
func foo100(m []*int) *int { func foo100(m []*int) *int { // ERROR "m does not escape"
for _, v := range m { for _, v := range m {
return v return v
} }
...@@ -673,25 +674,33 @@ func foo101(m [1]*int) *int { // ERROR "leaking param: NAME-m" ...@@ -673,25 +674,33 @@ func foo101(m [1]*int) *int { // ERROR "leaking param: NAME-m"
return nil 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 // 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 m[0] = x
} }
// does not leak 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 m[0] = x
} }
var y []*int var y []*int
// does not leak x // does not leak x
func foo104(x []*int) { func foo104(x []*int) { // ERROR "x does not escape"
copy(y, x) copy(y, x)
} }
// does not leak x // does not leak x
func foo105(x []*int) { func foo105(x []*int) { // ERROR "x does not escape"
_ = append(y, x...) _ = append(y, x...)
} }
...@@ -699,3 +708,69 @@ func foo105(x []*int) { ...@@ -699,3 +708,69 @@ func foo105(x []*int) {
func foo106(x *int) { // ERROR "leaking param: NAME-x" func foo106(x *int) { // ERROR "leaking param: NAME-x"
_ = append(y, 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"
}
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