mirror of https://github.com/golang/go.git
gc: Escape analysis.
For now it's switch-on-and-offable with -s, and the effects can be inspected with -m. Defaults are the old codepaths. R=rsc CC=golang-dev https://golang.org/cl/4634073
This commit is contained in:
parent
52818f4583
commit
847b61b554
|
|
@ -22,6 +22,7 @@ OFILES=\
|
||||||
closure.$O\
|
closure.$O\
|
||||||
const.$O\
|
const.$O\
|
||||||
dcl.$O\
|
dcl.$O\
|
||||||
|
esc.$O\
|
||||||
export.$O\
|
export.$O\
|
||||||
gen.$O\
|
gen.$O\
|
||||||
init.$O\
|
init.$O\
|
||||||
|
|
|
||||||
|
|
@ -820,6 +820,10 @@ stotype(NodeList *l, int et, Type **t, int funarg)
|
||||||
f->width = BADWIDTH;
|
f->width = BADWIDTH;
|
||||||
f->isddd = n->isddd;
|
f->isddd = n->isddd;
|
||||||
|
|
||||||
|
// esc.c needs to find f given a PPARAM to add the tag.
|
||||||
|
if(funarg && n->left && n->left->class == PPARAM)
|
||||||
|
n->left->paramfld = f;
|
||||||
|
|
||||||
if(left != N && left->op == ONAME) {
|
if(left != N && left->op == ONAME) {
|
||||||
f->nname = left;
|
f->nname = left;
|
||||||
f->embedded = n->embedded;
|
f->embedded = n->embedded;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,762 @@
|
||||||
|
// Copyright 2011 The Go Authors. All rights reserved.
|
||||||
|
// 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:
|
||||||
|
//
|
||||||
|
// First escfunc, escstmt and escexpr 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
|
||||||
|
// flow(closure, &var) is recorded and the flow of a closure itself to
|
||||||
|
// an outer scope is tracked the same way as other variables.
|
||||||
|
//
|
||||||
|
// Then escflood walks the graph starting at theSink and tags all
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "go.h"
|
||||||
|
|
||||||
|
static void escfunc(Node *func);
|
||||||
|
static void escstmtlist(NodeList *stmts);
|
||||||
|
static void escstmt(Node *stmt);
|
||||||
|
static void escexpr(Node *dst, Node *expr);
|
||||||
|
static void escexprcall(Node *dst, Node *callexpr);
|
||||||
|
static void escflows(Node* dst, Node* src);
|
||||||
|
static void escflood(Node *dst);
|
||||||
|
static void escwalk(int level, Node *dst, Node *src);
|
||||||
|
static void esctag(Node *func);
|
||||||
|
|
||||||
|
// Fake node that all
|
||||||
|
// - return values and output variables
|
||||||
|
// - parameters on imported functions not marked 'safe'
|
||||||
|
// - assignments to global variables
|
||||||
|
// flow to.
|
||||||
|
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
|
||||||
|
|
||||||
|
void
|
||||||
|
escapes(void)
|
||||||
|
{
|
||||||
|
NodeList *l;
|
||||||
|
|
||||||
|
theSink.op = ONAME;
|
||||||
|
theSink.class = PEXTERN;
|
||||||
|
theSink.sym = lookup(".sink");
|
||||||
|
theSink.escloopdepth = -1;
|
||||||
|
|
||||||
|
safetag = strlit("noescape");
|
||||||
|
|
||||||
|
// flow-analyze top level functions
|
||||||
|
for(l=xtop; l; l=l->next)
|
||||||
|
if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE)
|
||||||
|
escfunc(l->n);
|
||||||
|
|
||||||
|
// print("escapes: %d dsts, %d edges\n", dstcount, edgecount);
|
||||||
|
|
||||||
|
// visit the updstream of each dst, mark address nodes with
|
||||||
|
// addrescapes, mark parameters unsafe
|
||||||
|
for (l = dsts; l; l=l->next)
|
||||||
|
escflood(l->n);
|
||||||
|
|
||||||
|
// for all top level functions, tag the typenodes corresponding to the param nodes
|
||||||
|
for(l=xtop; l; l=l->next)
|
||||||
|
if(l->n->op == ODCLFUNC)
|
||||||
|
esctag(l->n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
escfunc(Node *func)
|
||||||
|
{
|
||||||
|
Node *savefn, *n;
|
||||||
|
NodeList *ll;
|
||||||
|
int saveld;
|
||||||
|
|
||||||
|
saveld = loopdepth;
|
||||||
|
loopdepth = 1;
|
||||||
|
savefn = curfn;
|
||||||
|
curfn = func;
|
||||||
|
|
||||||
|
for(ll=curfn->dcl; ll; ll=ll->next) {
|
||||||
|
if(ll->n->op != ONAME)
|
||||||
|
continue;
|
||||||
|
switch (ll->n->class) {
|
||||||
|
case PPARAMOUT:
|
||||||
|
// output parameters flow to the sink
|
||||||
|
escflows(&theSink, ll->n);
|
||||||
|
ll->n->escloopdepth = loopdepth;
|
||||||
|
break;
|
||||||
|
case PPARAM:
|
||||||
|
ll->n->esc = EscNone; // prime for escflood later
|
||||||
|
ll->n->escloopdepth = loopdepth;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// walk will take the address of cvar->closure later and assign it to cvar.
|
||||||
|
// handle that here by linking a fake oaddr node directly to the closure.
|
||||||
|
for (ll=curfn->cvars; ll; ll=ll->next) {
|
||||||
|
if(ll->n->op == OXXX) // see dcl.c:398
|
||||||
|
continue;
|
||||||
|
|
||||||
|
n = nod(OADDR, ll->n->closure, N);
|
||||||
|
n->lineno = ll->n->lineno;
|
||||||
|
typecheck(&n, Erv);
|
||||||
|
escexpr(curfn, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
escstmtlist(curfn->nbody);
|
||||||
|
curfn = savefn;
|
||||||
|
loopdepth = saveld;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
escstmtlist(NodeList* stmts)
|
||||||
|
{
|
||||||
|
for(; stmts; stmts=stmts->next)
|
||||||
|
escstmt(stmts->n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
escstmt(Node *stmt)
|
||||||
|
{
|
||||||
|
int cl, cr, lno;
|
||||||
|
NodeList *ll, *lr;
|
||||||
|
Node *dst;
|
||||||
|
|
||||||
|
if(stmt == N)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lno = setlineno(stmt);
|
||||||
|
|
||||||
|
if(stmt->typecheck == 0 && stmt->op != ODCL) { // TODO something with OAS2
|
||||||
|
dump("escstmt missing typecheck", stmt);
|
||||||
|
fatal("missing typecheck.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common to almost all statements, and nil if n/a.
|
||||||
|
escstmtlist(stmt->ninit);
|
||||||
|
|
||||||
|
if(debug['m'] > 1)
|
||||||
|
print("%L:[%d] %#S statement: %#N\n", lineno, loopdepth,
|
||||||
|
(curfn && curfn->nname) ? curfn->nname->sym : S, stmt);
|
||||||
|
|
||||||
|
switch(stmt->op) {
|
||||||
|
case ODCL:
|
||||||
|
case ODCLFIELD:
|
||||||
|
// a declaration ties the node to the current
|
||||||
|
// function, but we already have that edge in
|
||||||
|
// curfn->dcl and will follow it explicitly in
|
||||||
|
// escflood to avoid storing redundant information
|
||||||
|
// What does have to happen here is note if the name
|
||||||
|
// is declared inside a looping scope.
|
||||||
|
stmt->left->escloopdepth = loopdepth;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OLABEL: // TODO: new loop/scope only if there are backjumps to it.
|
||||||
|
loopdepth++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OBLOCK:
|
||||||
|
escstmtlist(stmt->list);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OFOR:
|
||||||
|
if(stmt->ntest != N) {
|
||||||
|
escstmtlist(stmt->ntest->ninit);
|
||||||
|
escexpr(N, stmt->ntest);
|
||||||
|
}
|
||||||
|
escstmt(stmt->nincr);
|
||||||
|
loopdepth++;
|
||||||
|
escstmtlist(stmt->nbody);
|
||||||
|
loopdepth--;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ORANGE: // for <list> = range <right> { <nbody> }
|
||||||
|
switch(stmt->type->etype) {
|
||||||
|
case TSTRING: // never flows
|
||||||
|
escexpr(stmt->list->n, N);
|
||||||
|
if(stmt->list->next)
|
||||||
|
escexpr(stmt->list->next->n, N);
|
||||||
|
escexpr(N, stmt->right);
|
||||||
|
break;
|
||||||
|
case TARRAY: // i, v = range sliceorarray
|
||||||
|
escexpr(stmt->list->n, N);
|
||||||
|
if(stmt->list->next)
|
||||||
|
escexpr(stmt->list->next->n, stmt->right);
|
||||||
|
break;
|
||||||
|
case TMAP: // k [, v] = range map
|
||||||
|
escexpr(stmt->list->n, stmt->right);
|
||||||
|
if(stmt->list->next)
|
||||||
|
escexpr(stmt->list->next->n, stmt->right);
|
||||||
|
break;
|
||||||
|
case TCHAN: // v = range chan
|
||||||
|
escexpr(stmt->list->n, stmt->right);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loopdepth++;
|
||||||
|
escstmtlist(stmt->nbody);
|
||||||
|
loopdepth--;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OIF:
|
||||||
|
escexpr(N, stmt->ntest);
|
||||||
|
escstmtlist(stmt->nbody);
|
||||||
|
escstmtlist(stmt->nelse);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OSELECT:
|
||||||
|
for(ll=stmt->list; ll; ll=ll->next) { // cases
|
||||||
|
escstmt(ll->n->left);
|
||||||
|
escstmtlist(ll->n->nbody);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OSELRECV2: // v, ok := <-ch ntest:ok
|
||||||
|
escexpr(N, stmt->ntest);
|
||||||
|
// fallthrough
|
||||||
|
case OSELRECV: // v := <-ch left: v right->op = ORECV
|
||||||
|
escexpr(N, stmt->left);
|
||||||
|
escexpr(stmt->left, stmt->right);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OSWITCH:
|
||||||
|
if(stmt->ntest && stmt->ntest->op == OTYPESW) {
|
||||||
|
for(ll=stmt->list; ll; ll=ll->next) { // cases
|
||||||
|
// ntest->right is the argument of the .(type),
|
||||||
|
// ll->n->nname is the variable per case
|
||||||
|
escexpr(ll->n->nname, stmt->ntest->right);
|
||||||
|
escstmtlist(ll->n->nbody);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
escexpr(N, stmt->ntest);
|
||||||
|
for(ll=stmt->list; ll; ll=ll->next) { // cases
|
||||||
|
for(lr=ll->n->list; lr; lr=lr->next)
|
||||||
|
escexpr(N, lr->n);
|
||||||
|
escstmtlist(ll->n->nbody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OAS:
|
||||||
|
case OASOP:
|
||||||
|
escexpr(stmt->left, stmt->right);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// escape analysis happens after typecheck, so the
|
||||||
|
// OAS2xxx have already been substituted.
|
||||||
|
case OAS2: // x,y = a,b
|
||||||
|
cl = count(stmt->list);
|
||||||
|
cr = count(stmt->rlist);
|
||||||
|
if(cl > 1 && cr == 1) {
|
||||||
|
for(ll=stmt->list; ll; ll=ll->next)
|
||||||
|
escexpr(ll->n, stmt->rlist->n);
|
||||||
|
} else {
|
||||||
|
if(cl != cr)
|
||||||
|
fatal("escstmt: bad OAS2: %N", stmt);
|
||||||
|
for(ll=stmt->list, lr=stmt->rlist; ll; ll=ll->next, lr=lr->next)
|
||||||
|
escexpr(ll->n, lr->n);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OAS2RECV: // v, ok = <-ch
|
||||||
|
case OAS2MAPR: // v, ok = m[k]
|
||||||
|
case OAS2DOTTYPE: // v, ok = x.(type)
|
||||||
|
escexpr(stmt->list->n, stmt->rlist->n);
|
||||||
|
escexpr(stmt->list->next->n, N);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OAS2MAPW: // m[k] = x, ok.. stmt->list->n is the INDEXMAP, k is handled in escexpr(dst...)
|
||||||
|
escexpr(stmt->list->n, stmt->rlist->n);
|
||||||
|
escexpr(N, stmt->rlist->next->n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ORECV: // unary <-ch as statement
|
||||||
|
escexpr(N, stmt->left);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OSEND: // ch <- x
|
||||||
|
escexpr(&theSink, stmt->right); // for now. TODO escexpr(stmt->left, stmt->right);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OCOPY: // todo: treat as *dst=*src instead of as dst=src
|
||||||
|
escexpr(stmt->left, stmt->right);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OAS2FUNC: // x,y,z = f()
|
||||||
|
for(ll = stmt->list; ll; ll=ll->next)
|
||||||
|
escexpr(ll->n, N);
|
||||||
|
escexpr(N, stmt->rlist->n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OCALLINTER:
|
||||||
|
case OCALLFUNC:
|
||||||
|
case OCALLMETH:
|
||||||
|
escexpr(N, stmt);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPROC:
|
||||||
|
case ODEFER:
|
||||||
|
// stmt->left is a (pseud)ocall, stmt->left->left is
|
||||||
|
// the function being called. if this defer is at
|
||||||
|
// loopdepth >1, everything leaks. TODO this is
|
||||||
|
// overly conservative, it's enough if it leaks to a
|
||||||
|
// fake node at the function's top level
|
||||||
|
dst = &theSink;
|
||||||
|
if (stmt->op == ODEFER && loopdepth <= 1)
|
||||||
|
dst = nil;
|
||||||
|
escexpr(dst, stmt->left->left);
|
||||||
|
for(ll=stmt->left->list; ll; ll=ll->next)
|
||||||
|
escexpr(dst, ll->n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ORETURN:
|
||||||
|
for(ll=stmt->list; ll; ll=ll->next)
|
||||||
|
escexpr(&theSink, ll->n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OCLOSE:
|
||||||
|
case OPRINT:
|
||||||
|
case OPRINTN:
|
||||||
|
escexpr(N, stmt->left);
|
||||||
|
for(ll=stmt->list; ll; ll=ll->next)
|
||||||
|
escexpr(N, ll->n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPANIC:
|
||||||
|
// Argument could leak through recover.
|
||||||
|
escexpr(&theSink, stmt->left);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
lineno = lno;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that expr somehow gets assigned to dst, if non nil. for
|
||||||
|
// dst==nil, any name node expr still must be marked as being
|
||||||
|
// evaluated in curfn. For expr==nil, dst must still be examined for
|
||||||
|
// evaluations inside it (e.g *f(x) = y)
|
||||||
|
static void
|
||||||
|
escexpr(Node *dst, Node *expr)
|
||||||
|
{
|
||||||
|
int lno;
|
||||||
|
NodeList *ll;
|
||||||
|
|
||||||
|
if(isblank(dst)) dst = N;
|
||||||
|
|
||||||
|
// the lhs of an assignment needs recursive analysis too
|
||||||
|
// these are the only interesting cases
|
||||||
|
// todo:check channel case
|
||||||
|
if(dst) {
|
||||||
|
setlineno(dst);
|
||||||
|
|
||||||
|
switch(dst->op) {
|
||||||
|
case OINDEX:
|
||||||
|
case OSLICE:
|
||||||
|
escexpr(N, dst->right);
|
||||||
|
|
||||||
|
// slice: "dst[x] = src" is like *(underlying array)[x] = src
|
||||||
|
// TODO maybe this never occurs b/c of OSLICEARR and it's inserted OADDR
|
||||||
|
if(!isfixedarray(dst->left->type))
|
||||||
|
goto doref;
|
||||||
|
|
||||||
|
// fallthrough; treat "dst[x] = src" as "dst = src"
|
||||||
|
case ODOT: // treat "dst.x = src" as "dst = src"
|
||||||
|
escexpr(dst->left, expr);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case OINDEXMAP:
|
||||||
|
escexpr(&theSink, dst->right); // map key is put in map
|
||||||
|
// fallthrough
|
||||||
|
case OIND:
|
||||||
|
case ODOTPTR:
|
||||||
|
case OSLICEARR: // ->left is the OADDR of the array
|
||||||
|
doref:
|
||||||
|
escexpr(N, dst->left);
|
||||||
|
// assignment to dereferences: for now we lose track
|
||||||
|
escexpr(&theSink, expr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(expr == N || expr->op == ONONAME || expr->op == OXXX)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(expr->typecheck == 0 && expr->op != OKEY) {
|
||||||
|
dump("escexpr missing typecheck", expr);
|
||||||
|
fatal("Missing typecheck.");
|
||||||
|
}
|
||||||
|
|
||||||
|
lno = setlineno(expr);
|
||||||
|
pdepth++;
|
||||||
|
|
||||||
|
if(debug['m'] > 1)
|
||||||
|
print("%L:[%d] %#S \t%hN %.*s<= %hN\n", lineno, loopdepth,
|
||||||
|
(curfn && curfn->nname) ? curfn->nname->sym : S, dst,
|
||||||
|
2*pdepth, ".\t.\t.\t.\t.\t", expr);
|
||||||
|
|
||||||
|
|
||||||
|
switch(expr->op) {
|
||||||
|
case OADDR: // dst = &x
|
||||||
|
case OIND: // dst = *x
|
||||||
|
case ODOTPTR: // dst = (*x).f
|
||||||
|
// restart the recursion at x to figure out where it came from
|
||||||
|
escexpr(expr->left, expr->left);
|
||||||
|
// fallthrough
|
||||||
|
case ONAME:
|
||||||
|
case OPARAM:
|
||||||
|
// loopdepth was set in the defining statement or function header
|
||||||
|
escflows(dst, expr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OARRAYLIT:
|
||||||
|
case OSTRUCTLIT:
|
||||||
|
case OMAPLIT:
|
||||||
|
expr->escloopdepth = loopdepth;
|
||||||
|
escflows(dst, expr);
|
||||||
|
for(ll=expr->list; ll; ll=ll->next) {
|
||||||
|
escexpr(expr, ll->n->left);
|
||||||
|
escexpr(expr, ll->n->right);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OMAKECHAN:
|
||||||
|
case OMAKEMAP:
|
||||||
|
case OMAKESLICE:
|
||||||
|
case ONEW:
|
||||||
|
expr->curfn = curfn; // should have been done in parse, but patch it up here.
|
||||||
|
expr->escloopdepth = loopdepth;
|
||||||
|
escflows(dst, expr);
|
||||||
|
// first arg is type, all others need checking
|
||||||
|
for(ll=expr->list->next; ll; ll=ll->next)
|
||||||
|
escexpr(N, ll->n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OCLOSURE:
|
||||||
|
expr->curfn = curfn; // should have been done in parse, but patch it up here.
|
||||||
|
expr->escloopdepth = loopdepth;
|
||||||
|
escflows(dst, expr);
|
||||||
|
escfunc(expr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// end of the leaf cases. no calls to escflows() in the cases below.
|
||||||
|
|
||||||
|
|
||||||
|
case OCONV: // unaries that pass the value through
|
||||||
|
case OCONVIFACE:
|
||||||
|
case OCONVNOP:
|
||||||
|
case ODOTTYPE:
|
||||||
|
case ODOTTYPE2:
|
||||||
|
case ORECV: // leaks the whole channel
|
||||||
|
case ODOTMETH: // expr->right is just the field or method name
|
||||||
|
case ODOTINTER:
|
||||||
|
case ODOT:
|
||||||
|
escexpr(dst, expr->left);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OCOPY:
|
||||||
|
// left leaks to right, but the return value is harmless
|
||||||
|
// TODO: treat as *dst = *src, rather than as dst = src
|
||||||
|
escexpr(expr->left, expr->right);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OAPPEND:
|
||||||
|
// See TODO for OCOPY
|
||||||
|
escexpr(dst, expr->list->n);
|
||||||
|
for(ll=expr->list->next; ll; ll=ll->next)
|
||||||
|
escexpr(expr->list->n, ll->n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OCALLMETH:
|
||||||
|
case OCALLFUNC:
|
||||||
|
case OCALLINTER:
|
||||||
|
// Moved to separate function to isolate the hair.
|
||||||
|
escexprcall(dst, expr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OSLICEARR: // like an implicit OIND to the underlying buffer, but typecheck has inserted an OADDR
|
||||||
|
case OSLICESTR:
|
||||||
|
case OSLICE:
|
||||||
|
case OINDEX:
|
||||||
|
case OINDEXMAP:
|
||||||
|
// the big thing flows, the keys just need checking
|
||||||
|
escexpr(dst, expr->left);
|
||||||
|
escexpr(N, expr->right); // expr->right is the OKEY
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: // all other harmless leaf, unary or binary cases end up here
|
||||||
|
escexpr(N, expr->left);
|
||||||
|
escexpr(N, expr->right);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdepth--;
|
||||||
|
lineno = lno;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This is a bit messier than fortunate, pulled out of escexpr's big
|
||||||
|
// switch for clarity. We either have the paramnodes, which may be
|
||||||
|
// connected to other things throug flows or we have the parameter type
|
||||||
|
// nodes, which may be marked 'n(ofloworescape)'. Navigating the ast is slightly
|
||||||
|
// different for methods vs plain functions and for imported vs
|
||||||
|
// this-package
|
||||||
|
static void
|
||||||
|
escexprcall(Node *dst, Node *expr)
|
||||||
|
{
|
||||||
|
NodeList *ll, *lr;
|
||||||
|
Node *fn;
|
||||||
|
Type *t, *fntype, *thisarg, *inargs;
|
||||||
|
|
||||||
|
fn = nil;
|
||||||
|
fntype = nil;
|
||||||
|
|
||||||
|
switch(expr->op) {
|
||||||
|
case OCALLFUNC:
|
||||||
|
fn = expr->left;
|
||||||
|
escexpr(N, fn);
|
||||||
|
fntype = fn->type;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OCALLMETH:
|
||||||
|
fn = expr->left->right; // ODOTxx name
|
||||||
|
fn = fn->sym->def; // resolve to definition if we have it
|
||||||
|
if(fn)
|
||||||
|
fntype = fn->type;
|
||||||
|
else
|
||||||
|
fntype = expr->left->type;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OCALLINTER:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fatal("escexprcall called with non-call expression");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fn && fn->ntype) {
|
||||||
|
if(debug['m'] > 2)
|
||||||
|
print("escexprcall: have param nodes: %N\n", fn->ntype);
|
||||||
|
|
||||||
|
if(expr->op == OCALLMETH) {
|
||||||
|
if(debug['m'] > 2)
|
||||||
|
print("escexprcall: this: %N\n",fn->ntype->left->left);
|
||||||
|
escexpr(fn->ntype->left->left, expr->left->left);
|
||||||
|
}
|
||||||
|
|
||||||
|
// lr->n is the dclfield, ->left is the ONAME param node
|
||||||
|
for(ll=expr->list, lr=fn->ntype->list; ll && lr; ll=ll->next) {
|
||||||
|
if(debug['m'] > 2)
|
||||||
|
print("escexprcall: field param: %N\n", lr->n->left);
|
||||||
|
if (lr->n->left)
|
||||||
|
escexpr(lr->n->left, ll->n);
|
||||||
|
else
|
||||||
|
escexpr(&theSink, ll->n);
|
||||||
|
if(lr->n->left && !lr->n->left->isddd)
|
||||||
|
lr=lr->next;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fntype) {
|
||||||
|
if(debug['m'] > 2)
|
||||||
|
print("escexprcall: have param types: %T\n", fntype);
|
||||||
|
|
||||||
|
if(expr->op == OCALLMETH) {
|
||||||
|
thisarg = getthisx(fntype);
|
||||||
|
t = thisarg->type;
|
||||||
|
if(debug['m'] > 2)
|
||||||
|
print("escexprcall: this: %T\n", t);
|
||||||
|
if(!t->note || strcmp(t->note->s, safetag->s) != 0)
|
||||||
|
escexpr(&theSink, expr->left->left);
|
||||||
|
else
|
||||||
|
escexpr(N, expr->left->left);
|
||||||
|
}
|
||||||
|
|
||||||
|
inargs = getinargx(fntype);
|
||||||
|
for(ll=expr->list, t=inargs->type; ll; ll=ll->next) {
|
||||||
|
if(debug['m'] > 2)
|
||||||
|
print("escexprcall: field type: %T\n", t);
|
||||||
|
if(!t->note || strcmp(t->note->s, safetag->s))
|
||||||
|
escexpr(&theSink, ll->n);
|
||||||
|
else
|
||||||
|
escexpr(N, ll->n);
|
||||||
|
if(t->down)
|
||||||
|
t=t->down;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallthrough if we don't have enough information:
|
||||||
|
// can only assume all parameters are unsafe
|
||||||
|
// OCALLINTER always ends up here
|
||||||
|
|
||||||
|
if(debug['m']>1 && expr->op != OCALLINTER) {
|
||||||
|
// dump("escexprcall", expr);
|
||||||
|
print("escexprcall: %O, no nodes, no types: %N\n", expr->op, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
escexpr(&theSink, expr->left->left); // the this argument
|
||||||
|
for(ll=expr->list; ll; ll=ll->next)
|
||||||
|
escexpr(&theSink, ll->n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the link src->dst in dst, throwing out some quick wins.
|
||||||
|
static void
|
||||||
|
escflows(Node* dst, Node* src)
|
||||||
|
{
|
||||||
|
if(dst == nil || src == nil || dst == src)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Don't bother building a graph for scalars.
|
||||||
|
if (src->type && !haspointers(src->type))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(debug['m']>2)
|
||||||
|
print("%L::flows:: %hN <- %hN\n", lineno, dst, src);
|
||||||
|
|
||||||
|
// Assignments to global variables get lumped into theSink.
|
||||||
|
if (dst->op == ONAME && dst->class == PEXTERN)
|
||||||
|
dst = &theSink;
|
||||||
|
|
||||||
|
if (dst->escflowsrc == nil) {
|
||||||
|
dsts = list(dsts, dst);
|
||||||
|
dstcount++;
|
||||||
|
}
|
||||||
|
edgecount++;
|
||||||
|
|
||||||
|
dst->escflowsrc = list(dst->escflowsrc, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whenever we hit a reference node, the level goes up by one, and whenever
|
||||||
|
// we hit an OADDR, the level goes down by one. as long as we're on a level > 0
|
||||||
|
// finding an OADDR just means we're following the upstream of a dereference,
|
||||||
|
// so this address doesn't leak (yet).
|
||||||
|
// If level == 0, it means the /value/ of this node can reach the root of this flood.
|
||||||
|
// so if this node is an OADDR, it's argument should be marked as escaping iff
|
||||||
|
// it's currfn/loopdepth are different from the flood's root.
|
||||||
|
// Once an object has been moved to the heap, all of it's upstream should be considered
|
||||||
|
// escaping to the global scope.
|
||||||
|
static void
|
||||||
|
escflood(Node *dst)
|
||||||
|
{
|
||||||
|
NodeList *l;
|
||||||
|
|
||||||
|
switch(dst->op) {
|
||||||
|
case ONAME:
|
||||||
|
case OCLOSURE:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(debug['m']>1)
|
||||||
|
print("\nescflood:%d: dst %hN scope:%#S[%d]\n", floodgen, dst,
|
||||||
|
(dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S,
|
||||||
|
dst->escloopdepth);
|
||||||
|
|
||||||
|
for (l = dst->escflowsrc; l; l=l->next) {
|
||||||
|
floodgen++;
|
||||||
|
escwalk(0, dst, l->n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
escwalk(int level, Node *dst, Node *src)
|
||||||
|
{
|
||||||
|
NodeList* ll;
|
||||||
|
int leaks;
|
||||||
|
|
||||||
|
if (src->escfloodgen == floodgen)
|
||||||
|
return;
|
||||||
|
src->escfloodgen = floodgen;
|
||||||
|
|
||||||
|
if(debug['m']>1)
|
||||||
|
print("escwalk: level:%d depth:%d %.*s %hN scope:%#S[%d]\n",
|
||||||
|
level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src,
|
||||||
|
(src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->escloopdepth);
|
||||||
|
|
||||||
|
pdepth++;
|
||||||
|
|
||||||
|
leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth);
|
||||||
|
|
||||||
|
switch(src->op) {
|
||||||
|
case ONAME:
|
||||||
|
if (src->class == PPARAM && leaks && src->esc == EscNone) {
|
||||||
|
src->esc = EscScope;
|
||||||
|
if(debug['m'])
|
||||||
|
print("%L:leaking param: %hN\n", src->lineno, src);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OADDR:
|
||||||
|
if (leaks)
|
||||||
|
addrescapes(src->left);
|
||||||
|
escwalk(level-1, dst, src->left);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OINDEX:
|
||||||
|
if(isfixedarray(src->type))
|
||||||
|
break;
|
||||||
|
case OSLICE:
|
||||||
|
case ODOTPTR:
|
||||||
|
case OINDEXMAP:
|
||||||
|
case OIND:
|
||||||
|
escwalk(level+1, dst, src->left);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ll=src->escflowsrc; ll; ll=ll->next)
|
||||||
|
escwalk(level, dst, ll->n);
|
||||||
|
|
||||||
|
pdepth--;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
esctag(Node *func)
|
||||||
|
{
|
||||||
|
Node *savefn;
|
||||||
|
NodeList *ll;
|
||||||
|
|
||||||
|
savefn = curfn;
|
||||||
|
curfn = func;
|
||||||
|
|
||||||
|
for(ll=curfn->dcl; ll; ll=ll->next) {
|
||||||
|
if(ll->n->op != ONAME || ll->n->class != PPARAM)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (ll->n->esc) {
|
||||||
|
case EscNone: // not touched by escflood
|
||||||
|
if (haspointers(ll->n->type)) // don't bother tagging for scalars
|
||||||
|
ll->n->paramfld->note = safetag;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curfn = savefn;
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
static void cgen_dcl(Node *n);
|
static void cgen_dcl(Node *n);
|
||||||
static void cgen_proc(Node *n, int proc);
|
static void cgen_proc(Node *n, int proc);
|
||||||
static void checkgoto(Node*, Node*);
|
static void checkgoto(Node*, Node*);
|
||||||
|
|
||||||
static Label *labellist;
|
static Label *labellist;
|
||||||
static Label *lastlabel;
|
static Label *lastlabel;
|
||||||
|
|
@ -55,7 +55,7 @@ allocparams(void)
|
||||||
}
|
}
|
||||||
if(n->op != ONAME || n->class != PAUTO)
|
if(n->op != ONAME || n->class != PAUTO)
|
||||||
continue;
|
continue;
|
||||||
if (n->xoffset != BADWIDTH)
|
if(n->xoffset != BADWIDTH)
|
||||||
continue;
|
continue;
|
||||||
if(n->type == T)
|
if(n->type == T)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -72,6 +72,96 @@ allocparams(void)
|
||||||
lineno = lno;
|
lineno = lno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the address of n has been taken and might be used after
|
||||||
|
* the current function returns. mark any local vars
|
||||||
|
* as needing to move to the heap.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
addrescapes(Node *n)
|
||||||
|
{
|
||||||
|
char buf[100];
|
||||||
|
switch(n->op) {
|
||||||
|
default:
|
||||||
|
// probably a type error already.
|
||||||
|
// dump("addrescapes", n);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ONAME:
|
||||||
|
if(n == nodfp)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// if this is a tmpname (PAUTO), it was tagged by tmpname as not escaping.
|
||||||
|
// on PPARAM it means something different.
|
||||||
|
if(n->class == PAUTO && n->esc == EscNever)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if(!debug['s'] && n->esc != EscUnknown)
|
||||||
|
fatal("without escape analysis, only PAUTO's should have esc: %N", n);
|
||||||
|
|
||||||
|
switch(n->class) {
|
||||||
|
case PPARAMREF:
|
||||||
|
addrescapes(n->defn);
|
||||||
|
break;
|
||||||
|
case PPARAM:
|
||||||
|
case PPARAMOUT:
|
||||||
|
// if func param, need separate temporary
|
||||||
|
// to hold heap pointer.
|
||||||
|
// the function type has already been checked
|
||||||
|
// (we're in the function body)
|
||||||
|
// so the param already has a valid xoffset.
|
||||||
|
|
||||||
|
// expression to refer to stack copy
|
||||||
|
n->stackparam = nod(OPARAM, n, N);
|
||||||
|
n->stackparam->type = n->type;
|
||||||
|
n->stackparam->addable = 1;
|
||||||
|
if(n->xoffset == BADWIDTH)
|
||||||
|
fatal("addrescapes before param assignment");
|
||||||
|
n->stackparam->xoffset = n->xoffset;
|
||||||
|
// fallthrough
|
||||||
|
case PAUTO:
|
||||||
|
|
||||||
|
n->class |= PHEAP;
|
||||||
|
n->addable = 0;
|
||||||
|
n->ullman = 2;
|
||||||
|
n->xoffset = 0;
|
||||||
|
|
||||||
|
// create stack variable to hold pointer to heap
|
||||||
|
n->heapaddr = nod(ONAME, N, N);
|
||||||
|
n->heapaddr->type = ptrto(n->type);
|
||||||
|
snprint(buf, sizeof buf, "&%S", n->sym);
|
||||||
|
n->heapaddr->sym = lookup(buf);
|
||||||
|
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'])
|
||||||
|
n->esc = EscHeap;
|
||||||
|
|
||||||
|
if(debug['m'])
|
||||||
|
print("%L: moved to heap: %hN\n", n->lineno, n);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OIND:
|
||||||
|
case ODOTPTR:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ODOT:
|
||||||
|
case OINDEX:
|
||||||
|
// ODOTPTR has already been introduced,
|
||||||
|
// so these are the non-pointer ODOT and OINDEX.
|
||||||
|
// In &x[0], if x is a slice, then x does not
|
||||||
|
// escape--the pointer inside x does, but that
|
||||||
|
// is always a heap pointer anyway.
|
||||||
|
if(!isslice(n->left->type))
|
||||||
|
addrescapes(n->left);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
clearlabels(void)
|
clearlabels(void)
|
||||||
{
|
{
|
||||||
|
|
@ -753,7 +843,7 @@ tempname(Node *nn, Type *t)
|
||||||
if(stksize < 0)
|
if(stksize < 0)
|
||||||
fatal("tempname not during code generation");
|
fatal("tempname not during code generation");
|
||||||
|
|
||||||
if (curfn == N)
|
if(curfn == N)
|
||||||
fatal("no curfn for tempname");
|
fatal("no curfn for tempname");
|
||||||
|
|
||||||
if(t == T) {
|
if(t == T) {
|
||||||
|
|
@ -772,7 +862,7 @@ tempname(Node *nn, Type *t)
|
||||||
n->class = PAUTO;
|
n->class = PAUTO;
|
||||||
n->addable = 1;
|
n->addable = 1;
|
||||||
n->ullman = 1;
|
n->ullman = 1;
|
||||||
n->noescape = 1;
|
n->esc = EscNever;
|
||||||
n->curfn = curfn;
|
n->curfn = curfn;
|
||||||
curfn->dcl = list(curfn->dcl, n);
|
curfn->dcl = list(curfn->dcl, n);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,6 @@ struct Type
|
||||||
{
|
{
|
||||||
uchar etype;
|
uchar etype;
|
||||||
uchar chan;
|
uchar chan;
|
||||||
uchar recur; // to detect loops
|
|
||||||
uchar trecur; // to detect loops
|
uchar trecur; // to detect loops
|
||||||
uchar printed;
|
uchar printed;
|
||||||
uchar embedded; // TFIELD embedded type
|
uchar embedded; // TFIELD embedded type
|
||||||
|
|
@ -203,6 +202,15 @@ struct Type
|
||||||
};
|
};
|
||||||
#define T ((Type*)0)
|
#define T ((Type*)0)
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
EscUnknown,
|
||||||
|
EscHeap,
|
||||||
|
EscScope,
|
||||||
|
EscNone,
|
||||||
|
EscNever,
|
||||||
|
};
|
||||||
|
|
||||||
struct Node
|
struct Node
|
||||||
{
|
{
|
||||||
uchar op;
|
uchar op;
|
||||||
|
|
@ -215,7 +223,7 @@ struct Node
|
||||||
uchar embedded; // ODCLFIELD embedded type
|
uchar embedded; // ODCLFIELD embedded type
|
||||||
uchar colas; // OAS resulting from :=
|
uchar colas; // OAS resulting from :=
|
||||||
uchar diag; // already printed error about this
|
uchar diag; // already printed error about this
|
||||||
uchar noescape; // ONAME never move to heap
|
uchar esc; // EscXXX
|
||||||
uchar funcdepth;
|
uchar funcdepth;
|
||||||
uchar builtin; // built-in name, like len or close
|
uchar builtin; // built-in name, like len or close
|
||||||
uchar walkdef;
|
uchar walkdef;
|
||||||
|
|
@ -266,6 +274,7 @@ struct Node
|
||||||
Node* defn;
|
Node* defn;
|
||||||
Node* pack; // real package for import . names
|
Node* pack; // real package for import . names
|
||||||
Node* curfn; // function for local variables
|
Node* curfn; // function for local variables
|
||||||
|
Type* paramfld; // TFIELD for this PPARAM
|
||||||
|
|
||||||
// ONAME func param with PHEAP
|
// ONAME func param with PHEAP
|
||||||
Node* heapaddr; // temp holding heap address of param
|
Node* heapaddr; // temp holding heap address of param
|
||||||
|
|
@ -279,6 +288,11 @@ struct Node
|
||||||
// OPACK
|
// OPACK
|
||||||
Pkg* pkg;
|
Pkg* pkg;
|
||||||
|
|
||||||
|
// 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
|
Sym* sym; // various
|
||||||
int32 vargen; // unique name for OTYPE/ONAME
|
int32 vargen; // unique name for OTYPE/ONAME
|
||||||
int32 lineno;
|
int32 lineno;
|
||||||
|
|
@ -374,7 +388,6 @@ enum
|
||||||
OADDR,
|
OADDR,
|
||||||
OANDAND,
|
OANDAND,
|
||||||
OAPPEND,
|
OAPPEND,
|
||||||
OARRAY,
|
|
||||||
OARRAYBYTESTR, OARRAYRUNESTR,
|
OARRAYBYTESTR, OARRAYRUNESTR,
|
||||||
OSTRARRAYBYTE, OSTRARRAYRUNE,
|
OSTRARRAYBYTE, OSTRARRAYRUNE,
|
||||||
OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, OASOP,
|
OAS, OAS2, OAS2MAPW, OAS2FUNC, OAS2RECV, OAS2MAPR, OAS2DOTTYPE, OASOP,
|
||||||
|
|
@ -444,6 +457,7 @@ enum
|
||||||
|
|
||||||
// misc
|
// misc
|
||||||
ODDD,
|
ODDD,
|
||||||
|
ODDDARG,
|
||||||
|
|
||||||
// for back ends
|
// for back ends
|
||||||
OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG,
|
OCMP, ODEC, OEXTEND, OINC, OREGISTER, OINDREG,
|
||||||
|
|
@ -910,6 +924,11 @@ void typedcl2(Type *pt, Type *t);
|
||||||
Node* typenod(Type *t);
|
Node* typenod(Type *t);
|
||||||
NodeList* variter(NodeList *vl, Node *t, NodeList *el);
|
NodeList* variter(NodeList *vl, Node *t, NodeList *el);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* esc.c
|
||||||
|
*/
|
||||||
|
void escapes(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* export.c
|
* export.c
|
||||||
*/
|
*/
|
||||||
|
|
@ -927,6 +946,7 @@ Type* pkgtype(Sym *s);
|
||||||
/*
|
/*
|
||||||
* gen.c
|
* gen.c
|
||||||
*/
|
*/
|
||||||
|
void addrescapes(Node *n);
|
||||||
void allocparams(void);
|
void allocparams(void);
|
||||||
void cgen_as(Node *nl, Node *nr);
|
void cgen_as(Node *nl, Node *nr);
|
||||||
void cgen_callmeth(Node *n, int proc);
|
void cgen_callmeth(Node *n, int proc);
|
||||||
|
|
@ -1050,6 +1070,7 @@ void dumptypestructs(void);
|
||||||
Type* methodfunc(Type *f, Type*);
|
Type* methodfunc(Type *f, Type*);
|
||||||
Node* typename(Type *t);
|
Node* typename(Type *t);
|
||||||
Sym* typesym(Type *t);
|
Sym* typesym(Type *t);
|
||||||
|
int haspointers(Type *t);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* select.c
|
* select.c
|
||||||
|
|
|
||||||
|
|
@ -235,24 +235,24 @@ main(int argc, char *argv[])
|
||||||
if(debug['f'])
|
if(debug['f'])
|
||||||
frame(1);
|
frame(1);
|
||||||
|
|
||||||
// Process top-level declarations in four phases.
|
// Process top-level declarations in phases.
|
||||||
// Phase 1: const, type, and names and types of funcs.
|
// Phase 1: const, type, and names and types of funcs.
|
||||||
// This will gather all the information about types
|
// This will gather all the information about types
|
||||||
// and methods but doesn't depend on any of it.
|
// and methods but doesn't depend on any of it.
|
||||||
// Phase 2: Variable assignments.
|
|
||||||
// To check interface assignments, depends on phase 1.
|
|
||||||
// Phase 3: Type check function bodies.
|
|
||||||
// Phase 4: Compile function bodies.
|
|
||||||
defercheckwidth();
|
defercheckwidth();
|
||||||
for(l=xtop; l; l=l->next)
|
for(l=xtop; l; l=l->next)
|
||||||
if(l->n->op != ODCL && l->n->op != OAS)
|
if(l->n->op != ODCL && l->n->op != OAS)
|
||||||
typecheck(&l->n, Etop);
|
typecheck(&l->n, Etop);
|
||||||
|
|
||||||
|
// Phase 2: Variable assignments.
|
||||||
|
// To check interface assignments, depends on phase 1.
|
||||||
for(l=xtop; l; l=l->next)
|
for(l=xtop; l; l=l->next)
|
||||||
if(l->n->op == ODCL || l->n->op == OAS)
|
if(l->n->op == ODCL || l->n->op == OAS)
|
||||||
typecheck(&l->n, Etop);
|
typecheck(&l->n, Etop);
|
||||||
resumetypecopy();
|
resumetypecopy();
|
||||||
resumecheckwidth();
|
resumecheckwidth();
|
||||||
|
|
||||||
|
// Phase 3: Type check function bodies.
|
||||||
for(l=xtop; l; l=l->next) {
|
for(l=xtop; l; l=l->next) {
|
||||||
if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) {
|
if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) {
|
||||||
curfn = l->n;
|
curfn = l->n;
|
||||||
|
|
@ -268,6 +268,11 @@ main(int argc, char *argv[])
|
||||||
if(nsavederrors+nerrors)
|
if(nsavederrors+nerrors)
|
||||||
errorexit();
|
errorexit();
|
||||||
|
|
||||||
|
// Phase 3b: escape analysis.
|
||||||
|
if(debug['s'])
|
||||||
|
escapes();
|
||||||
|
|
||||||
|
// Phase 4: Compile function bodies.
|
||||||
for(l=xtop; l; l=l->next)
|
for(l=xtop; l; l=l->next)
|
||||||
if(l->n->op == ODCLFUNC)
|
if(l->n->op == ODCLFUNC)
|
||||||
funccompile(l->n, 0);
|
funccompile(l->n, 0);
|
||||||
|
|
@ -275,6 +280,7 @@ main(int argc, char *argv[])
|
||||||
if(nsavederrors+nerrors == 0)
|
if(nsavederrors+nerrors == 0)
|
||||||
fninit(xtop);
|
fninit(xtop);
|
||||||
|
|
||||||
|
// Phase 4b: Compile all closures.
|
||||||
while(closures) {
|
while(closures) {
|
||||||
l = closures;
|
l = closures;
|
||||||
closures = nil;
|
closures = nil;
|
||||||
|
|
@ -283,6 +289,7 @@ main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Phase 5: check external declarations.
|
||||||
for(l=externdcl; l; l=l->next)
|
for(l=externdcl; l; l=l->next)
|
||||||
if(l->n->op == ONAME)
|
if(l->n->op == ONAME)
|
||||||
typecheck(&l->n, Erv);
|
typecheck(&l->n, Erv);
|
||||||
|
|
@ -1739,7 +1746,6 @@ lexfini(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
nodfp = nod(ONAME, N, N);
|
nodfp = nod(ONAME, N, N);
|
||||||
nodfp->noescape = 1;
|
|
||||||
nodfp->type = types[TINT32];
|
nodfp->type = types[TINT32];
|
||||||
nodfp->xoffset = 0;
|
nodfp->xoffset = 0;
|
||||||
nodfp->class = PPARAM;
|
nodfp->class = PPARAM;
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,10 @@ exprfmt(Fmt *f, Node *n, int prec)
|
||||||
fmtprint(f, "(%#N)", n->left);
|
fmtprint(f, "(%#N)", n->left);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ODDDARG:
|
||||||
|
fmtprint(f, "... argument");
|
||||||
|
break;
|
||||||
|
|
||||||
case OREGISTER:
|
case OREGISTER:
|
||||||
fmtprint(f, "%R", n->val.u.reg);
|
fmtprint(f, "%R", n->val.u.reg);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -528,7 +528,7 @@ typestruct(Type *t)
|
||||||
return pkglookup(name, typepkg);
|
return pkglookup(name, typepkg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
haspointers(Type *t)
|
haspointers(Type *t)
|
||||||
{
|
{
|
||||||
Type *t1;
|
Type *t1;
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ typecheckselect(Node *sel)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OAS2RECV:
|
case OAS2RECV:
|
||||||
// convert x, ok = <-c into OSELRECV(x, <-c) with ntest=ok
|
// convert x, ok = <-c into OSELRECV2(x, <-c) with ntest=ok
|
||||||
if(n->right->op != ORECV) {
|
if(n->right->op != ORECV) {
|
||||||
yyerror("select assignment must have receive on right hand side");
|
yyerror("select assignment must have receive on right hand side");
|
||||||
break;
|
break;
|
||||||
|
|
@ -73,6 +73,7 @@ typecheckselect(Node *sel)
|
||||||
case ORECV:
|
case ORECV:
|
||||||
// convert <-c into OSELRECV(N, <-c)
|
// convert <-c into OSELRECV(N, <-c)
|
||||||
n = nod(OSELRECV, N, n);
|
n = nod(OSELRECV, N, n);
|
||||||
|
n->typecheck = 1;
|
||||||
ncase->left = n;
|
ncase->left = n;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1094,8 +1094,8 @@ Jconv(Fmt *fp)
|
||||||
|
|
||||||
if(n->class != 0) {
|
if(n->class != 0) {
|
||||||
s = "";
|
s = "";
|
||||||
if (n->class & PHEAP) s = ",heap";
|
if(n->class & PHEAP) s = ",heap";
|
||||||
if ((n->class & ~PHEAP) < nelem(classnames))
|
if((n->class & ~PHEAP) < nelem(classnames))
|
||||||
fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s);
|
fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s);
|
||||||
else
|
else
|
||||||
fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s);
|
fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s);
|
||||||
|
|
@ -1107,8 +1107,29 @@ Jconv(Fmt *fp)
|
||||||
if(n->funcdepth != 0)
|
if(n->funcdepth != 0)
|
||||||
fmtprint(fp, " f(%d)", n->funcdepth);
|
fmtprint(fp, " f(%d)", n->funcdepth);
|
||||||
|
|
||||||
if(n->noescape != 0)
|
switch(n->esc) {
|
||||||
fmtprint(fp, " ne(%d)", n->noescape);
|
case EscUnknown:
|
||||||
|
break;
|
||||||
|
case EscHeap:
|
||||||
|
fmtprint(fp, " esc(h)");
|
||||||
|
break;
|
||||||
|
case EscScope:
|
||||||
|
fmtprint(fp, " esc(s)");
|
||||||
|
break;
|
||||||
|
case EscNone:
|
||||||
|
fmtprint(fp, " esc(no)");
|
||||||
|
break;
|
||||||
|
case EscNever:
|
||||||
|
if(!c)
|
||||||
|
fmtprint(fp, " esc(N)");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fmtprint(fp, " esc(%d)", n->esc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(n->escloopdepth)
|
||||||
|
fmtprint(fp, " ld(%d)", n->escloopdepth);
|
||||||
|
|
||||||
if(!c && n->typecheck != 0)
|
if(!c && n->typecheck != 0)
|
||||||
fmtprint(fp, " tc(%d)", n->typecheck);
|
fmtprint(fp, " tc(%d)", n->typecheck);
|
||||||
|
|
@ -1523,7 +1544,7 @@ Nconv(Fmt *fp)
|
||||||
|
|
||||||
switch(n->op) {
|
switch(n->op) {
|
||||||
default:
|
default:
|
||||||
if (fp->flags & FmtShort)
|
if(fp->flags & FmtShort)
|
||||||
fmtprint(fp, "%O%hJ", n->op, n);
|
fmtprint(fp, "%O%hJ", n->op, n);
|
||||||
else
|
else
|
||||||
fmtprint(fp, "%O%J", n->op, n);
|
fmtprint(fp, "%O%J", n->op, n);
|
||||||
|
|
@ -1532,13 +1553,13 @@ Nconv(Fmt *fp)
|
||||||
case ONAME:
|
case ONAME:
|
||||||
case ONONAME:
|
case ONONAME:
|
||||||
if(n->sym == S) {
|
if(n->sym == S) {
|
||||||
if (fp->flags & FmtShort)
|
if(fp->flags & FmtShort)
|
||||||
fmtprint(fp, "%O%hJ", n->op, n);
|
fmtprint(fp, "%O%hJ", n->op, n);
|
||||||
else
|
else
|
||||||
fmtprint(fp, "%O%J", n->op, n);
|
fmtprint(fp, "%O%J", n->op, n);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (fp->flags & FmtShort)
|
if(fp->flags & FmtShort)
|
||||||
fmtprint(fp, "%O-%S%hJ", n->op, n->sym, n);
|
fmtprint(fp, "%O-%S%hJ", n->op, n->sym, n);
|
||||||
else
|
else
|
||||||
fmtprint(fp, "%O-%S%J", n->op, n->sym, n);
|
fmtprint(fp, "%O-%S%J", n->op, n->sym, n);
|
||||||
|
|
@ -3176,7 +3197,7 @@ genwrapper(Type *rcvr, Type *method, Sym *newnam, int iface)
|
||||||
int isddd;
|
int isddd;
|
||||||
Val v;
|
Val v;
|
||||||
|
|
||||||
if(debug['r'])
|
if(0 && debug['r'])
|
||||||
print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
|
print("genwrapper rcvrtype=%T method=%T newnam=%S\n",
|
||||||
rcvr, method, newnam);
|
rcvr, method, newnam);
|
||||||
|
|
||||||
|
|
@ -3453,7 +3474,7 @@ listsort(NodeList** l, int(*f)(Node*, Node*))
|
||||||
listsort(&l1, f);
|
listsort(&l1, f);
|
||||||
listsort(&l2, f);
|
listsort(&l2, f);
|
||||||
|
|
||||||
if ((*f)(l1->n, l2->n) < 0) {
|
if((*f)(l1->n, l2->n) < 0) {
|
||||||
*l = l1;
|
*l = l1;
|
||||||
} else {
|
} else {
|
||||||
*l = l2;
|
*l = l2;
|
||||||
|
|
@ -3469,7 +3490,7 @@ listsort(NodeList** l, int(*f)(Node*, Node*))
|
||||||
|
|
||||||
// l1 is last one from l1 that is < l2
|
// l1 is last one from l1 that is < l2
|
||||||
le = l1->next; // le is the rest of l1, first one that is >= l2
|
le = l1->next; // le is the rest of l1, first one that is >= l2
|
||||||
if (le != nil)
|
if(le != nil)
|
||||||
le->end = (*l)->end;
|
le->end = (*l)->end;
|
||||||
|
|
||||||
(*l)->end = l1; // cut *l at l1
|
(*l)->end = l1; // cut *l at l1
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ static void typecheckaste(int, Node*, int, Type*, NodeList*, char*);
|
||||||
static Type* lookdot1(Sym *s, Type *t, Type *f, int);
|
static Type* lookdot1(Sym *s, Type *t, Type *f, int);
|
||||||
static int nokeys(NodeList*);
|
static int nokeys(NodeList*);
|
||||||
static void typecheckcomplit(Node**);
|
static void typecheckcomplit(Node**);
|
||||||
static void addrescapes(Node*);
|
|
||||||
static void typecheckas2(Node*);
|
static void typecheckas2(Node*);
|
||||||
static void typecheckas(Node*);
|
static void typecheckas(Node*);
|
||||||
static void typecheckfunc(Node*);
|
static void typecheckfunc(Node*);
|
||||||
|
|
@ -337,7 +336,7 @@ reswitch:
|
||||||
*/
|
*/
|
||||||
case OIND:
|
case OIND:
|
||||||
ntop = Erv | Etype;
|
ntop = Erv | Etype;
|
||||||
if(!(top & Eaddr))
|
if(!(top & Eaddr)) // The *x in &*x is not an indirect.
|
||||||
ntop |= Eindir;
|
ntop |= Eindir;
|
||||||
l = typecheck(&n->left, ntop);
|
l = typecheck(&n->left, ntop);
|
||||||
if((t = l->type) == T)
|
if((t = l->type) == T)
|
||||||
|
|
@ -535,7 +534,9 @@ reswitch:
|
||||||
l = n->left;
|
l = n->left;
|
||||||
if((t = l->type) == T)
|
if((t = l->type) == T)
|
||||||
goto error;
|
goto error;
|
||||||
if(!(top & Eindir) && !n->etype)
|
// 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'])
|
||||||
addrescapes(n->left);
|
addrescapes(n->left);
|
||||||
n->type = ptrto(t);
|
n->type = ptrto(t);
|
||||||
goto ret;
|
goto ret;
|
||||||
|
|
@ -1028,6 +1029,8 @@ reswitch:
|
||||||
}
|
}
|
||||||
n->left = args->n;
|
n->left = args->n;
|
||||||
n->right = args->next->n;
|
n->right = args->next->n;
|
||||||
|
args = nil;
|
||||||
|
n->list = nil;
|
||||||
n->type = types[TINT];
|
n->type = types[TINT];
|
||||||
typecheck(&n->left, Erv);
|
typecheck(&n->left, Erv);
|
||||||
typecheck(&n->right, Erv);
|
typecheck(&n->right, Erv);
|
||||||
|
|
@ -1038,7 +1041,7 @@ reswitch:
|
||||||
|
|
||||||
// copy([]byte, string)
|
// copy([]byte, string)
|
||||||
if(isslice(n->left->type) && n->right->type->etype == TSTRING) {
|
if(isslice(n->left->type) && n->right->type->etype == TSTRING) {
|
||||||
if (n->left->type->type == types[TUINT8])
|
if(n->left->type->type == types[TUINT8])
|
||||||
goto ret;
|
goto ret;
|
||||||
yyerror("arguments to copy have different element types: %lT and string", n->left->type);
|
yyerror("arguments to copy have different element types: %lT and string", n->left->type);
|
||||||
goto error;
|
goto error;
|
||||||
|
|
@ -1602,7 +1605,8 @@ 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");
|
||||||
addrescapes(n->left);
|
if(!debug['s'])
|
||||||
|
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;
|
||||||
typecheck(&n->left, Etype|Erv);
|
typecheck(&n->left, Etype|Erv);
|
||||||
|
|
@ -2156,82 +2160,6 @@ error:
|
||||||
lineno = lno;
|
lineno = lno;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* the address of n has been taken and might be used after
|
|
||||||
* the current function returns. mark any local vars
|
|
||||||
* as needing to move to the heap.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
addrescapes(Node *n)
|
|
||||||
{
|
|
||||||
char buf[100];
|
|
||||||
switch(n->op) {
|
|
||||||
default:
|
|
||||||
// probably a type error already.
|
|
||||||
// dump("addrescapes", n);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ONAME:
|
|
||||||
if(n->noescape)
|
|
||||||
break;
|
|
||||||
switch(n->class) {
|
|
||||||
case PPARAMREF:
|
|
||||||
addrescapes(n->defn);
|
|
||||||
break;
|
|
||||||
case PPARAM:
|
|
||||||
case PPARAMOUT:
|
|
||||||
// if func param, need separate temporary
|
|
||||||
// to hold heap pointer.
|
|
||||||
// the function type has already been checked
|
|
||||||
// (we're in the function body)
|
|
||||||
// so the param already has a valid xoffset.
|
|
||||||
|
|
||||||
// expression to refer to stack copy
|
|
||||||
n->stackparam = nod(OPARAM, n, N);
|
|
||||||
n->stackparam->type = n->type;
|
|
||||||
n->stackparam->addable = 1;
|
|
||||||
if(n->xoffset == BADWIDTH)
|
|
||||||
fatal("addrescapes before param assignment");
|
|
||||||
n->stackparam->xoffset = n->xoffset;
|
|
||||||
n->xoffset = 0;
|
|
||||||
// fallthrough
|
|
||||||
case PAUTO:
|
|
||||||
|
|
||||||
n->class |= PHEAP;
|
|
||||||
n->addable = 0;
|
|
||||||
n->ullman = 2;
|
|
||||||
n->xoffset = 0;
|
|
||||||
|
|
||||||
// create stack variable to hold pointer to heap
|
|
||||||
n->heapaddr = nod(ONAME, N, N);
|
|
||||||
n->heapaddr->type = ptrto(n->type);
|
|
||||||
snprint(buf, sizeof buf, "&%S", n->sym);
|
|
||||||
n->heapaddr->sym = lookup(buf);
|
|
||||||
n->heapaddr->class = PHEAP-1; // defer tempname to allocparams
|
|
||||||
n->heapaddr->ullman = 1;
|
|
||||||
n->curfn->dcl = list(n->curfn->dcl, n->heapaddr);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OIND:
|
|
||||||
case ODOTPTR:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ODOT:
|
|
||||||
case OINDEX:
|
|
||||||
// ODOTPTR has already been introduced,
|
|
||||||
// so these are the non-pointer ODOT and OINDEX.
|
|
||||||
// In &x[0], if x is a slice, then x does not
|
|
||||||
// escape--the pointer inside x does, but that
|
|
||||||
// is always a heap pointer anyway.
|
|
||||||
if(!isslice(n->left->type))
|
|
||||||
addrescapes(n->left);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* lvalue etc
|
* lvalue etc
|
||||||
*/
|
*/
|
||||||
|
|
@ -2462,7 +2390,6 @@ typecheckfunc(Node *n)
|
||||||
{
|
{
|
||||||
Type *t, *rcvr;
|
Type *t, *rcvr;
|
||||||
|
|
||||||
//dump("nname", n->nname);
|
|
||||||
typecheck(&n->nname, Erv | Easgn);
|
typecheck(&n->nname, Erv | Easgn);
|
||||||
if((t = n->nname->type) == T)
|
if((t = n->nname->type) == T)
|
||||||
return;
|
return;
|
||||||
|
|
@ -2772,6 +2699,7 @@ typecheckdef(Node *n)
|
||||||
if(n->ntype != N) {
|
if(n->ntype != N) {
|
||||||
typecheck(&n->ntype, Etype);
|
typecheck(&n->ntype, Etype);
|
||||||
n->type = n->ntype->type;
|
n->type = n->ntype->type;
|
||||||
|
|
||||||
if(n->type == T) {
|
if(n->type == T) {
|
||||||
n->diag = 1;
|
n->diag = 1;
|
||||||
goto ret;
|
goto ret;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,615 @@
|
||||||
|
// errchk -0 $G -sm $D/$F.go
|
||||||
|
|
||||||
|
// Copyright 2010 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package foo
|
||||||
|
|
||||||
|
import "unsafe"
|
||||||
|
|
||||||
|
var gxx *int
|
||||||
|
|
||||||
|
func foo1(x int) { // ERROR "moved to heap: NAME-x"
|
||||||
|
gxx = &x
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo2(yy *int) { // ERROR "leaking param: NAME-yy"
|
||||||
|
gxx = yy
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo3(x int) *int { // ERROR "moved to heap: NAME-x"
|
||||||
|
return &x
|
||||||
|
}
|
||||||
|
|
||||||
|
type T *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) {
|
||||||
|
xx = yy
|
||||||
|
}
|
||||||
|
|
||||||
|
// xx isn't going anywhere, so taking address of yy is ok
|
||||||
|
func foo5(xx **int, yy *int) {
|
||||||
|
xx = &yy
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo6(xx **int, yy *int) { // ERROR "leaking param: NAME-yy"
|
||||||
|
*xx = yy
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo7(xx **int, yy *int) {
|
||||||
|
**xx = *yy
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo8(xx, yy *int) int {
|
||||||
|
xx = yy
|
||||||
|
return *xx
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo9(xx, yy *int) *int { // ERROR "leaking param: NAME-xx" "leaking param: NAME-yy"
|
||||||
|
xx = yy
|
||||||
|
return xx
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo10(xx, yy *int) {
|
||||||
|
*xx = *yy
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo11() int {
|
||||||
|
x, y := 0, 42
|
||||||
|
xx := &x
|
||||||
|
yy := &y
|
||||||
|
*xx = *yy
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var xxx **int
|
||||||
|
|
||||||
|
func foo12(yyy **int) { // ERROR "leaking param: NAME-yyy"
|
||||||
|
xxx = yyy
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo13(yyy **int) {
|
||||||
|
*xxx = *yyy
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo14(yyy **int) {
|
||||||
|
**xxx = **yyy
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo15(yy *int) { // ERROR "moved to heap: NAME-yy"
|
||||||
|
xxx = &yy
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo16(yy *int) { // ERROR "leaking param: NAME-yy"
|
||||||
|
*xxx = yy
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo17(yy *int) {
|
||||||
|
**xxx = *yy
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo18(y int) { // ERROR "moved to heap: "NAME-y"
|
||||||
|
*xxx = &y
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo19(y int) {
|
||||||
|
**xxx = y
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bar struct {
|
||||||
|
i int
|
||||||
|
ii *int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBar() *Bar {
|
||||||
|
return &Bar{ 42, nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBarp(x *int) *Bar { // ERROR "leaking param: NAME-x"
|
||||||
|
return &Bar{ 42, x }
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBarp2(x *int) *Bar {
|
||||||
|
return &Bar{ *x, nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bar) NoLeak() int {
|
||||||
|
return *(b.ii)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bar) AlsoNoLeak() *int {
|
||||||
|
return b.ii
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bar2 struct {
|
||||||
|
i [12]int
|
||||||
|
ii []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBar2() *Bar2 {
|
||||||
|
return &Bar2{ [12]int{ 42 }, nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bar2) NoLeak() int {
|
||||||
|
return b.i[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bar2) Leak() []int { // ERROR "leaking param: NAME-b"
|
||||||
|
return b.i[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bar2) AlsoNoLeak() []int {
|
||||||
|
return b.ii[0:1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bar2) LeakSelf() { // ERROR "leaking param: NAME-b"
|
||||||
|
b.ii = b.i[0:4]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bar2) LeakSelf2() { // ERROR "leaking param: NAME-b"
|
||||||
|
var buf []int
|
||||||
|
buf = b.i[0:]
|
||||||
|
b.ii = buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo21() func() int {
|
||||||
|
x := 42 // ERROR "moved to heap: NAME-x"
|
||||||
|
return func() int {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo22() int {
|
||||||
|
x := 42
|
||||||
|
return func() int {
|
||||||
|
return x
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo23(x int) func() int { // ERROR "moved to heap: NAME-x"
|
||||||
|
return func() int {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo23a(x int) (func() int) { // ERROR "moved to heap: NAME-x"
|
||||||
|
f := func() int {
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
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 foo24(x int) int {
|
||||||
|
return func() int {
|
||||||
|
return x
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var x *int
|
||||||
|
|
||||||
|
func fooleak(xx *int) int { // ERROR "leaking param: NAME-xx"
|
||||||
|
x = xx
|
||||||
|
return *x
|
||||||
|
}
|
||||||
|
|
||||||
|
func foonoleak(xx *int) int {
|
||||||
|
return *x + *xx
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo31(x int) int { // ERROR "moved to heap: NAME-x"
|
||||||
|
return fooleak(&x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo32(x int) int {
|
||||||
|
return foonoleak(&x)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
xx *int
|
||||||
|
x int
|
||||||
|
}
|
||||||
|
|
||||||
|
var F Foo
|
||||||
|
var pf *Foo
|
||||||
|
|
||||||
|
func (f *Foo) fooleak() { // ERROR "leaking param: NAME-f"
|
||||||
|
pf = f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Foo) foonoleak() {
|
||||||
|
F.x = f.x
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Foo) Leak() { // ERROR "leaking param: NAME-f"
|
||||||
|
f.fooleak()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Foo) NoLeak() {
|
||||||
|
f.foonoleak()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func foo41(x int) { // ERROR "moved to heap: NAME-x"
|
||||||
|
F.xx = &x
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Foo) foo42(x int) { // ERROR "moved to heap: NAME-x"
|
||||||
|
f.xx = &x
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo43(f *Foo, x int) { // ERROR "moved to heap: NAME-x"
|
||||||
|
f.xx = &x
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo44(yy *int) { // ERROR "leaking param: NAME-yy"
|
||||||
|
F.xx = yy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Foo) foo45() {
|
||||||
|
F.x = f.x
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Foo) foo46() {
|
||||||
|
F.xx = f.xx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Foo) foo47() { // ERROR "leaking param: NAME-f"
|
||||||
|
f.xx = &f.x
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var ptrSlice []*int
|
||||||
|
|
||||||
|
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"
|
||||||
|
ptrMap[i] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func indaddr1(x int) *int { // ERROR "moved to heap: NAME-x"
|
||||||
|
return &x
|
||||||
|
}
|
||||||
|
|
||||||
|
func indaddr2(x *int) *int { // ERROR "leaking param: NAME-x"
|
||||||
|
return *&x
|
||||||
|
}
|
||||||
|
|
||||||
|
func indaddr3(x *int32) *int { // ERROR "leaking param: NAME-x"
|
||||||
|
return *(**int)(unsafe.Pointer(&x))
|
||||||
|
}
|
||||||
|
|
||||||
|
// From package math:
|
||||||
|
|
||||||
|
func Float32bits(f float32) uint32 {
|
||||||
|
return *(*uint32)(unsafe.Pointer(&f))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Float32frombits(b uint32) float32 {
|
||||||
|
return *(*float32)(unsafe.Pointer(&b))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Float64bits(f float64) uint64 {
|
||||||
|
return *(*uint64)(unsafe.Pointer(&f))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Float64frombits(b uint64) float64 {
|
||||||
|
return *(*float64)(unsafe.Pointer(&b))
|
||||||
|
}
|
||||||
|
|
||||||
|
// contrast with
|
||||||
|
func float64bitsptr(f float64) *uint64 { // ERROR "moved to heap: NAME-f"
|
||||||
|
return (*uint64)(unsafe.Pointer(&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"
|
||||||
|
switch val := i.(type) {
|
||||||
|
case *int:
|
||||||
|
return val
|
||||||
|
case *int8:
|
||||||
|
v := int(*val) // ERROR "moved to heap: NAME-v"
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func exprsw(i *int) *int { // ERROR "leaking param: NAME-i"
|
||||||
|
switch j := i; *j + 110 {
|
||||||
|
case 12:
|
||||||
|
return j
|
||||||
|
case 42:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// assigning to an array element is like assigning to the array
|
||||||
|
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 {
|
||||||
|
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"
|
||||||
|
type S struct {
|
||||||
|
a,b *int
|
||||||
|
}
|
||||||
|
var s S
|
||||||
|
s.a = i
|
||||||
|
return s.b
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo61a(i *int) *int {
|
||||||
|
type S struct {
|
||||||
|
a,b *int
|
||||||
|
}
|
||||||
|
var s S
|
||||||
|
s.a = i
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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"
|
||||||
|
type S struct {
|
||||||
|
a,b *int
|
||||||
|
}
|
||||||
|
s := new(S)
|
||||||
|
s.a = i
|
||||||
|
return nil // s.b
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type M interface { M() }
|
||||||
|
|
||||||
|
func foo63(m M) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo64(m M) { // ERROR "leaking param: NAME-m"
|
||||||
|
m.M()
|
||||||
|
}
|
||||||
|
|
||||||
|
type MV int
|
||||||
|
func (MV) M() {}
|
||||||
|
|
||||||
|
func foo65() {
|
||||||
|
var mv MV
|
||||||
|
foo63(&mv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo66() {
|
||||||
|
var mv MV // ERROR "moved to heap: NAME-mv"
|
||||||
|
foo64(&mv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo67() {
|
||||||
|
var mv MV
|
||||||
|
foo63(mv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo68() {
|
||||||
|
var mv MV
|
||||||
|
foo64(mv) // escapes but it's an int so irrelevant
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
m = mv1
|
||||||
|
foo64(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
var y []*int
|
||||||
|
y = append(y, &x)
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo72() {
|
||||||
|
var x int
|
||||||
|
var y [1]*int
|
||||||
|
y[0] = &x
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo72aa() [10]*int {
|
||||||
|
var x int // ERROR "moved to heap: NAME-x"
|
||||||
|
var y [10]*int
|
||||||
|
y[0] = &x
|
||||||
|
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
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// issue 2145
|
||||||
|
func foo73() {
|
||||||
|
s := []int{3,2,1}
|
||||||
|
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)
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo74() {
|
||||||
|
s := []int{3,2,1}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
defer fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func myprint(y *int, x ...interface{}) *int { // ERROR "leaking param: NAME-y"
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
|
||||||
|
func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "leaking param: NAME-x"
|
||||||
|
return &x[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo75(z *int) { // ERROR "leaking param: NAME-z"
|
||||||
|
myprint(z, 1, 2, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo75a(z *int) {
|
||||||
|
myprint1(z, 1, 2, 3) // "[.][.][.] argument escapes to heap"
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo76(z *int) {
|
||||||
|
myprint(nil, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo76a(z *int) { // ERROR "leaking param: NAME-z"
|
||||||
|
myprint1(nil, z) // "[.][.][.] argument escapes to heap"
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo76b() {
|
||||||
|
myprint(nil, 1, 2, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo76c() {
|
||||||
|
myprint1(nil, 1, 2, 3) // "[.][.][.] argument escapes to heap"
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo76d() {
|
||||||
|
defer myprint(nil, 1, 2, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo76e() {
|
||||||
|
defer myprint1(nil, 1, 2, 3) // "[.][.][.] argument escapes to heap"
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo76f() {
|
||||||
|
for {
|
||||||
|
defer myprint(nil, 1, 2, 3) // "[.][.][.] argument escapes its scope"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo76g() {
|
||||||
|
for {
|
||||||
|
defer myprint1(nil, 1, 2, 3) // "[.][.][.] argument escapes to heap"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo77(z []interface{}) {
|
||||||
|
myprint(nil, z...) // z does not escape
|
||||||
|
}
|
||||||
|
|
||||||
|
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 foo78a(z int) *int { // ERROR "moved to heap: NAME-z"
|
||||||
|
y := &z
|
||||||
|
x := &y
|
||||||
|
return *x // really return y
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo79() *int {
|
||||||
|
return new(int) // "moved to heap: new[(]int[)]"
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo80() *int {
|
||||||
|
var z *int
|
||||||
|
for {
|
||||||
|
z = new(int) // "new[(]int[)] escapes its scope"
|
||||||
|
}
|
||||||
|
_ = z
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo81() *int {
|
||||||
|
for {
|
||||||
|
z := new(int)
|
||||||
|
_ = z
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fooer interface {
|
||||||
|
Foo()
|
||||||
|
}
|
||||||
|
|
||||||
|
type LimitedFooer struct {
|
||||||
|
Fooer
|
||||||
|
N int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: NAME-r"
|
||||||
|
return &LimitedFooer{r, n}
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo90(x *int) map[*int]*int { // ERROR "leaking param: NAME-x"
|
||||||
|
return map[*int]*int{ nil: x }
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo91(x *int) map[*int]*int { // ERROR "leaking param: NAME-x"
|
||||||
|
return map[*int]*int{ x:nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
func foo92(x *int) [2]*int { // ERROR "leaking param: NAME-x"
|
||||||
|
return [2]*int{ x, nil }
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue