1
0
mirror of https://github.com/golang/go synced 2024-11-21 19:24:45 -07:00

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
This commit is contained in:
Russ Cox 2011-08-28 12:05:00 -04:00
parent 2a80882601
commit db5f9da425
11 changed files with 567 additions and 321 deletions

View File

@ -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

View File

@ -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 <u.h>
#include <libc.h>
@ -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);
}
}

View File

@ -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);

View File

@ -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, ...);

View File

@ -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.

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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))

View File

@ -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"
}