2011-08-24 11:07:08 -06:00
|
|
|
// 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.
|
2012-06-07 01:15:09 -06:00
|
|
|
|
2011-08-28 10:05:00 -06:00
|
|
|
// Escape analysis.
|
2012-06-07 01:15:09 -06:00
|
|
|
|
|
|
|
#include <u.h>
|
|
|
|
#include <libc.h>
|
|
|
|
#include "go.h"
|
|
|
|
|
|
|
|
// Run analysis on minimal sets of mutually recursive functions
|
|
|
|
// or single non-recursive functions, bottom up.
|
|
|
|
//
|
|
|
|
// Finding these sets is finding strongly connected components
|
|
|
|
// in the static call graph. The algorithm for doing that is taken
|
|
|
|
// from Sedgewick, Algorithms, Second Edition, p. 482, with two
|
|
|
|
// adaptations.
|
|
|
|
//
|
|
|
|
// First, a hidden closure function (n->curfn != N) cannot be the
|
|
|
|
// root of a connected component. Refusing to use it as a root
|
|
|
|
// forces it into the component of the function in which it appears.
|
|
|
|
// The analysis assumes that closures and the functions in which they
|
|
|
|
// appear are analyzed together, so that the aliasing between their
|
|
|
|
// variables can be modeled more precisely.
|
|
|
|
//
|
|
|
|
// Second, each function becomes two virtual nodes in the graph,
|
|
|
|
// with numbers n and n+1. We record the function's node number as n
|
|
|
|
// but search from node n+1. If the search tells us that the component
|
|
|
|
// number (min) is n+1, we know that this is a trivial component: one function
|
|
|
|
// plus its closures. If the search tells us that the component number is
|
|
|
|
// n, then there was a path from node n+1 back to node n, meaning that
|
|
|
|
// the function set is mutually recursive. The escape analysis can be
|
|
|
|
// more precise when analyzing a single non-recursive function than
|
|
|
|
// when analyzing a set of mutually recursive functions.
|
|
|
|
|
|
|
|
static NodeList *stack;
|
|
|
|
static uint32 visitgen;
|
|
|
|
static uint32 visit(Node*);
|
|
|
|
static uint32 visitcode(Node*, uint32);
|
|
|
|
static uint32 visitcodelist(NodeList*, uint32);
|
|
|
|
|
|
|
|
static void analyze(NodeList*, int);
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
EscFuncUnknown = 0,
|
|
|
|
EscFuncPlanned,
|
|
|
|
EscFuncStarted,
|
|
|
|
EscFuncTagged,
|
|
|
|
};
|
|
|
|
|
|
|
|
void
|
|
|
|
escapes(NodeList *all)
|
|
|
|
{
|
|
|
|
NodeList *l;
|
|
|
|
|
|
|
|
for(l=all; l; l=l->next)
|
|
|
|
l->n->walkgen = 0;
|
|
|
|
|
|
|
|
visitgen = 0;
|
|
|
|
for(l=all; l; l=l->next)
|
|
|
|
if(l->n->op == ODCLFUNC && l->n->curfn == N)
|
|
|
|
visit(l->n);
|
|
|
|
|
|
|
|
for(l=all; l; l=l->next)
|
|
|
|
l->n->walkgen = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32
|
|
|
|
visit(Node *n)
|
|
|
|
{
|
|
|
|
uint32 min, recursive;
|
|
|
|
NodeList *l, *block;
|
|
|
|
|
|
|
|
if(n->walkgen > 0) {
|
|
|
|
// already visited
|
|
|
|
return n->walkgen;
|
|
|
|
}
|
|
|
|
|
|
|
|
visitgen++;
|
|
|
|
n->walkgen = visitgen;
|
|
|
|
visitgen++;
|
|
|
|
min = visitgen;
|
|
|
|
|
|
|
|
l = mal(sizeof *l);
|
|
|
|
l->next = stack;
|
|
|
|
l->n = n;
|
|
|
|
stack = l;
|
|
|
|
min = visitcodelist(n->nbody, min);
|
|
|
|
if((min == n->walkgen || min == n->walkgen+1) && n->curfn == N) {
|
|
|
|
// This node is the root of a strongly connected component.
|
|
|
|
|
|
|
|
// The original min passed to visitcodelist was n->walkgen+1.
|
|
|
|
// If visitcodelist found its way back to n->walkgen, then this
|
|
|
|
// block is a set of mutually recursive functions.
|
|
|
|
// Otherwise it's just a lone function that does not recurse.
|
|
|
|
recursive = min == n->walkgen;
|
|
|
|
|
|
|
|
// Remove connected component from stack.
|
|
|
|
// Mark walkgen so that future visits return a large number
|
|
|
|
// so as not to affect the caller's min.
|
|
|
|
block = stack;
|
|
|
|
for(l=stack; l->n != n; l=l->next)
|
|
|
|
l->n->walkgen = (uint32)~0U;
|
|
|
|
n->walkgen = (uint32)~0U;
|
|
|
|
stack = l->next;
|
|
|
|
l->next = nil;
|
|
|
|
|
|
|
|
// Run escape analysis on this set of functions.
|
|
|
|
analyze(block, recursive);
|
|
|
|
}
|
|
|
|
|
|
|
|
return min;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32
|
|
|
|
visitcodelist(NodeList *l, uint32 min)
|
|
|
|
{
|
|
|
|
for(; l; l=l->next)
|
|
|
|
min = visitcode(l->n, min);
|
|
|
|
return min;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32
|
|
|
|
visitcode(Node *n, uint32 min)
|
|
|
|
{
|
|
|
|
Node *fn;
|
|
|
|
uint32 m;
|
|
|
|
|
|
|
|
if(n == N)
|
|
|
|
return min;
|
|
|
|
|
|
|
|
min = visitcodelist(n->ninit, min);
|
|
|
|
min = visitcode(n->left, min);
|
|
|
|
min = visitcode(n->right, min);
|
|
|
|
min = visitcodelist(n->list, min);
|
|
|
|
min = visitcode(n->ntest, min);
|
|
|
|
min = visitcode(n->nincr, min);
|
|
|
|
min = visitcodelist(n->nbody, min);
|
|
|
|
min = visitcodelist(n->nelse, min);
|
|
|
|
min = visitcodelist(n->rlist, min);
|
|
|
|
|
|
|
|
if(n->op == OCALLFUNC || n->op == OCALLMETH) {
|
|
|
|
fn = n->left;
|
|
|
|
if(n->op == OCALLMETH)
|
|
|
|
fn = n->left->right->sym->def;
|
2013-06-25 15:28:49 -06:00
|
|
|
if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn)
|
2012-06-07 01:15:09 -06:00
|
|
|
if((m = visit(fn->defn)) < min)
|
|
|
|
min = m;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(n->op == OCLOSURE)
|
|
|
|
if((m = visit(n->closure)) < min)
|
|
|
|
min = m;
|
|
|
|
|
|
|
|
return min;
|
|
|
|
}
|
|
|
|
|
|
|
|
// An escape analysis pass for a set of functions.
|
2011-08-24 11:07:08 -06:00
|
|
|
//
|
2011-08-24 14:47:32 -06:00
|
|
|
// First escfunc, esc and escassign recurse over the ast of each
|
2011-08-24 11:07:08 -06:00
|
|
|
// 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
|
2011-08-28 10:05:00 -06:00
|
|
|
// 'the Sink'. For variables referenced in closures, an edge
|
2011-08-24 11:07:08 -06:00
|
|
|
// 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.
|
|
|
|
//
|
2011-08-28 10:05:00 -06:00
|
|
|
// 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.
|
2011-08-24 11:07:08 -06:00
|
|
|
//
|
2012-06-07 01:15:09 -06:00
|
|
|
// If optimizations are disabled (-N), this code is not used.
|
2011-08-28 10:05:00 -06:00
|
|
|
// 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.
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2012-06-07 01:15:09 -06:00
|
|
|
typedef struct EscState EscState;
|
|
|
|
|
|
|
|
static void escfunc(EscState*, Node *func);
|
|
|
|
static void esclist(EscState*, NodeList *l);
|
|
|
|
static void esc(EscState*, Node *n);
|
|
|
|
static void escloopdepthlist(EscState*, NodeList *l);
|
|
|
|
static void escloopdepth(EscState*, Node *n);
|
|
|
|
static void escassign(EscState*, Node *dst, Node *src);
|
|
|
|
static void esccall(EscState*, Node*);
|
|
|
|
static void escflows(EscState*, Node *dst, Node *src);
|
|
|
|
static void escflood(EscState*, Node *dst);
|
|
|
|
static void escwalk(EscState*, int level, Node *dst, Node *src);
|
|
|
|
static void esctag(EscState*, Node *func);
|
|
|
|
|
|
|
|
struct EscState {
|
|
|
|
// Fake node that all
|
|
|
|
// - return values and output variables
|
|
|
|
// - parameters on imported functions not marked 'safe'
|
|
|
|
// - assignments to global variables
|
|
|
|
// flow to.
|
|
|
|
Node theSink;
|
|
|
|
|
|
|
|
NodeList* dsts; // all dst nodes
|
|
|
|
int loopdepth; // for detecting nested loop scopes
|
|
|
|
int pdepth; // for debug printing in recursions.
|
|
|
|
int dstcount, edgecount; // diagnostic
|
|
|
|
NodeList* noesc; // list of possible non-escaping nodes, for printing
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
int recursive; // recursive function or group of mutually recursive functions.
|
2012-06-07 01:15:09 -06:00
|
|
|
};
|
2011-08-24 11:07:08 -06:00
|
|
|
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
static Strlit *tags[16];
|
2012-10-22 02:18:17 -06:00
|
|
|
|
|
|
|
static Strlit*
|
|
|
|
mktag(int mask)
|
|
|
|
{
|
|
|
|
Strlit *s;
|
|
|
|
char buf[40];
|
|
|
|
|
|
|
|
switch(mask&EscMask) {
|
|
|
|
case EscNone:
|
|
|
|
case EscReturn:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fatal("escape mktag");
|
|
|
|
}
|
|
|
|
|
|
|
|
mask >>= EscBits;
|
|
|
|
|
|
|
|
if(mask < nelem(tags) && tags[mask] != nil)
|
|
|
|
return tags[mask];
|
|
|
|
|
2012-11-01 10:55:21 -06:00
|
|
|
snprint(buf, sizeof buf, "esc:0x%x", mask);
|
2012-10-22 02:18:17 -06:00
|
|
|
s = strlit(buf);
|
|
|
|
if(mask < nelem(tags))
|
|
|
|
tags[mask] = s;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
parsetag(Strlit *note)
|
|
|
|
{
|
|
|
|
int em;
|
|
|
|
|
|
|
|
if(note == nil)
|
|
|
|
return EscUnknown;
|
|
|
|
if(strncmp(note->s, "esc:", 4) != 0)
|
|
|
|
return EscUnknown;
|
|
|
|
em = atoi(note->s + 4);
|
|
|
|
if (em == 0)
|
|
|
|
return EscNone;
|
|
|
|
return EscReturn | (em << EscBits);
|
|
|
|
}
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2012-06-07 01:15:09 -06:00
|
|
|
static void
|
|
|
|
analyze(NodeList *all, int recursive)
|
2011-08-24 11:07:08 -06:00
|
|
|
{
|
|
|
|
NodeList *l;
|
2012-06-07 01:15:09 -06:00
|
|
|
EscState es, *e;
|
|
|
|
|
|
|
|
memset(&es, 0, sizeof es);
|
|
|
|
e = &es;
|
|
|
|
e->theSink.op = ONAME;
|
|
|
|
e->theSink.orig = &e->theSink;
|
|
|
|
e->theSink.class = PEXTERN;
|
|
|
|
e->theSink.sym = lookup(".sink");
|
|
|
|
e->theSink.escloopdepth = -1;
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
e->recursive = recursive;
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2012-06-07 01:15:09 -06:00
|
|
|
for(l=all; l; l=l->next)
|
|
|
|
if(l->n->op == ODCLFUNC)
|
|
|
|
l->n->esc = EscFuncPlanned;
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2012-02-23 21:09:53 -07:00
|
|
|
// flow-analyze functions
|
|
|
|
for(l=all; l; l=l->next)
|
2012-06-04 15:07:59 -06:00
|
|
|
if(l->n->op == ODCLFUNC)
|
2012-06-07 01:15:09 -06:00
|
|
|
escfunc(e, l->n);
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2012-06-07 01:15:09 -06:00
|
|
|
// print("escapes: %d e->dsts, %d edges\n", e->dstcount, e->edgecount);
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2013-02-14 16:38:57 -07:00
|
|
|
// visit the upstream of each dst, mark address nodes with
|
2011-08-24 11:07:08 -06:00
|
|
|
// addrescapes, mark parameters unsafe
|
2012-06-07 01:15:09 -06:00
|
|
|
for(l = e->dsts; l; l=l->next)
|
|
|
|
escflood(e, l->n);
|
2011-08-24 11:07:08 -06:00
|
|
|
|
|
|
|
// for all top level functions, tag the typenodes corresponding to the param nodes
|
2012-02-23 21:09:53 -07:00
|
|
|
for(l=all; l; l=l->next)
|
2011-08-24 11:07:08 -06:00
|
|
|
if(l->n->op == ODCLFUNC)
|
2012-06-07 01:15:09 -06:00
|
|
|
esctag(e, l->n);
|
2011-08-28 10:05:00 -06:00
|
|
|
|
|
|
|
if(debug['m']) {
|
2012-06-07 01:15:09 -06:00
|
|
|
for(l=e->noesc; l; l=l->next)
|
2011-08-28 10:05:00 -06:00
|
|
|
if(l->n->esc == EscNone)
|
2011-10-31 11:09:40 -06:00
|
|
|
warnl(l->n->lineno, "%S %hN does not escape",
|
2011-08-28 10:05:00 -06:00
|
|
|
(l->n->curfn && l->n->curfn->nname) ? l->n->curfn->nname->sym : S,
|
|
|
|
l->n);
|
|
|
|
}
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
|
|
|
|
2011-08-28 10:05:00 -06:00
|
|
|
|
2011-08-24 11:07:08 -06:00
|
|
|
static void
|
2012-06-07 01:15:09 -06:00
|
|
|
escfunc(EscState *e, Node *func)
|
2011-08-24 11:07:08 -06:00
|
|
|
{
|
2012-06-04 15:07:59 -06:00
|
|
|
Node *savefn;
|
2011-08-24 11:07:08 -06:00
|
|
|
NodeList *ll;
|
|
|
|
int saveld;
|
|
|
|
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
// print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":"");
|
|
|
|
|
2012-06-07 01:15:09 -06:00
|
|
|
if(func->esc != 1)
|
|
|
|
fatal("repeat escfunc %N", func->nname);
|
|
|
|
func->esc = EscFuncStarted;
|
|
|
|
|
|
|
|
saveld = e->loopdepth;
|
|
|
|
e->loopdepth = 1;
|
2011-08-24 11:07:08 -06:00
|
|
|
savefn = curfn;
|
|
|
|
curfn = func;
|
|
|
|
|
|
|
|
for(ll=curfn->dcl; ll; ll=ll->next) {
|
|
|
|
if(ll->n->op != ONAME)
|
|
|
|
continue;
|
|
|
|
switch (ll->n->class) {
|
|
|
|
case PPARAMOUT:
|
2012-10-22 02:18:17 -06:00
|
|
|
// out params are in a loopdepth between the sink and all local variables
|
|
|
|
ll->n->escloopdepth = 0;
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
case PPARAM:
|
2011-08-28 10:05:00 -06:00
|
|
|
if(ll->n->type && !haspointers(ll->n->type))
|
|
|
|
break;
|
2013-02-05 05:00:38 -07:00
|
|
|
if(curfn->nbody == nil && !curfn->noescape)
|
|
|
|
ll->n->esc = EscHeap;
|
|
|
|
else
|
|
|
|
ll->n->esc = EscNone; // prime for escflood later
|
2012-06-07 01:15:09 -06:00
|
|
|
e->noesc = list(e->noesc, ll->n);
|
2012-10-22 02:18:17 -06:00
|
|
|
ll->n->escloopdepth = 1;
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
// in a mutually recursive group we lose track of the return values
|
|
|
|
if(e->recursive)
|
|
|
|
for(ll=curfn->dcl; ll; ll=ll->next)
|
|
|
|
if(ll->n->op == ONAME && ll->n->class == PPARAMOUT)
|
|
|
|
escflows(e, &e->theSink, ll->n);
|
|
|
|
|
2012-06-07 01:15:09 -06:00
|
|
|
escloopdepthlist(e, curfn->nbody);
|
|
|
|
esclist(e, curfn->nbody);
|
2011-08-24 11:07:08 -06:00
|
|
|
curfn = savefn;
|
2012-06-07 01:15:09 -06:00
|
|
|
e->loopdepth = saveld;
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
|
|
|
|
2012-06-07 01:15:09 -06:00
|
|
|
// Mark labels that have no backjumps to them as not increasing e->loopdepth.
|
2011-12-15 09:35:59 -07:00
|
|
|
// Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat
|
|
|
|
// and set it to one of the following two. Then in esc we'll clear it again.
|
|
|
|
static Label looping;
|
|
|
|
static Label nonlooping;
|
|
|
|
|
|
|
|
static void
|
2012-06-07 01:15:09 -06:00
|
|
|
escloopdepthlist(EscState *e, NodeList *l)
|
2011-12-15 09:35:59 -07:00
|
|
|
{
|
|
|
|
for(; l; l=l->next)
|
2012-06-07 01:15:09 -06:00
|
|
|
escloopdepth(e, l->n);
|
2011-12-15 09:35:59 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-06-07 01:15:09 -06:00
|
|
|
escloopdepth(EscState *e, Node *n)
|
2011-12-15 09:35:59 -07:00
|
|
|
{
|
|
|
|
if(n == N)
|
|
|
|
return;
|
|
|
|
|
2012-06-07 01:15:09 -06:00
|
|
|
escloopdepthlist(e, n->ninit);
|
2011-12-15 09:35:59 -07:00
|
|
|
|
|
|
|
switch(n->op) {
|
|
|
|
case OLABEL:
|
|
|
|
if(!n->left || !n->left->sym)
|
|
|
|
fatal("esc:label without label: %+N", n);
|
|
|
|
// Walk will complain about this label being already defined, but that's not until
|
|
|
|
// after escape analysis. in the future, maybe pull label & goto analysis out of walk and put before esc
|
|
|
|
// if(n->left->sym->label != nil)
|
|
|
|
// fatal("escape analysis messed up analyzing label: %+N", n);
|
|
|
|
n->left->sym->label = &nonlooping;
|
|
|
|
break;
|
|
|
|
case OGOTO:
|
|
|
|
if(!n->left || !n->left->sym)
|
|
|
|
fatal("esc:goto without label: %+N", n);
|
|
|
|
// If we come past one that's uninitialized, this must be a (harmless) forward jump
|
|
|
|
// but if it's set to nonlooping the label must have preceded this goto.
|
|
|
|
if(n->left->sym->label == &nonlooping)
|
|
|
|
n->left->sym->label = &looping;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2012-06-07 01:15:09 -06:00
|
|
|
escloopdepth(e, n->left);
|
|
|
|
escloopdepth(e, n->right);
|
|
|
|
escloopdepthlist(e, n->list);
|
|
|
|
escloopdepth(e, n->ntest);
|
|
|
|
escloopdepth(e, n->nincr);
|
|
|
|
escloopdepthlist(e, n->nbody);
|
|
|
|
escloopdepthlist(e, n->nelse);
|
|
|
|
escloopdepthlist(e, n->rlist);
|
2011-12-15 09:35:59 -07:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-08-24 11:07:08 -06:00
|
|
|
static void
|
2012-06-07 01:15:09 -06:00
|
|
|
esclist(EscState *e, NodeList *l)
|
2011-08-24 11:07:08 -06:00
|
|
|
{
|
2011-08-24 14:47:32 -06:00
|
|
|
for(; l; l=l->next)
|
2012-06-07 01:15:09 -06:00
|
|
|
esc(e, l->n);
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-06-07 01:15:09 -06:00
|
|
|
esc(EscState *e, Node *n)
|
2011-08-24 11:07:08 -06:00
|
|
|
{
|
2011-08-24 14:47:32 -06:00
|
|
|
int lno;
|
2011-08-28 10:05:00 -06:00
|
|
|
NodeList *ll, *lr;
|
2012-06-04 15:07:59 -06:00
|
|
|
Node *a;
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
if(n == N)
|
2011-08-24 11:07:08 -06:00
|
|
|
return;
|
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
lno = setlineno(n);
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2011-08-25 07:26:13 -06:00
|
|
|
if(n->op == OFOR || n->op == ORANGE)
|
2012-06-07 01:15:09 -06:00
|
|
|
e->loopdepth++;
|
|
|
|
|
|
|
|
esc(e, n->left);
|
|
|
|
esc(e, n->right);
|
|
|
|
esc(e, n->ntest);
|
|
|
|
esc(e, n->nincr);
|
|
|
|
esclist(e, n->ninit);
|
|
|
|
esclist(e, n->nbody);
|
|
|
|
esclist(e, n->nelse);
|
|
|
|
esclist(e, n->list);
|
|
|
|
esclist(e, n->rlist);
|
2012-06-04 15:07:59 -06:00
|
|
|
|
2011-08-25 07:26:13 -06:00
|
|
|
if(n->op == OFOR || n->op == ORANGE)
|
2012-06-07 01:15:09 -06:00
|
|
|
e->loopdepth--;
|
2011-08-24 11:07:08 -06:00
|
|
|
|
|
|
|
if(debug['m'] > 1)
|
2012-06-07 01:15:09 -06:00
|
|
|
print("%L:[%d] %S esc: %N\n", lineno, e->loopdepth,
|
2011-08-24 14:47:32 -06:00
|
|
|
(curfn && curfn->nname) ? curfn->nname->sym : S, n);
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
switch(n->op) {
|
2011-08-24 11:07:08 -06:00
|
|
|
case ODCL:
|
2011-08-25 07:26:13 -06:00
|
|
|
// Record loop depth at declaration.
|
2011-08-24 14:47:32 -06:00
|
|
|
if(n->left)
|
2012-06-07 01:15:09 -06:00
|
|
|
n->left->escloopdepth = e->loopdepth;
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
|
2011-12-15 09:35:59 -07:00
|
|
|
case OLABEL:
|
|
|
|
if(n->left->sym->label == &nonlooping) {
|
|
|
|
if(debug['m'] > 1)
|
|
|
|
print("%L:%N non-looping label\n", lineno, n);
|
|
|
|
} else if(n->left->sym->label == &looping) {
|
|
|
|
if(debug['m'] > 1)
|
|
|
|
print("%L: %N looping label\n", lineno, n);
|
2012-06-07 01:15:09 -06:00
|
|
|
e->loopdepth++;
|
2011-12-15 09:35:59 -07:00
|
|
|
}
|
|
|
|
// See case OLABEL in escloopdepth above
|
|
|
|
// else if(n->left->sym->label == nil)
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
// fatal("escape analysis missed or messed up a label: %+N", n);
|
2011-12-15 09:35:59 -07:00
|
|
|
|
|
|
|
n->left->sym->label = nil;
|
2012-10-01 08:33:06 -06:00
|
|
|
break;
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2011-08-25 07:26:13 -06:00
|
|
|
case ORANGE:
|
|
|
|
// Everything but fixed array is a dereference.
|
2011-08-28 10:05:00 -06:00
|
|
|
if(isfixedarray(n->type) && n->list->next)
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, n->list->next->n, n->right);
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OSWITCH:
|
2011-08-24 14:47:32 -06:00
|
|
|
if(n->ntest && n->ntest->op == OTYPESW) {
|
|
|
|
for(ll=n->list; ll; ll=ll->next) { // cases
|
2011-08-24 11:07:08 -06:00
|
|
|
// ntest->right is the argument of the .(type),
|
|
|
|
// ll->n->nname is the variable per case
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, ll->n->nname, n->ntest->right);
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OAS:
|
|
|
|
case OASOP:
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, n->left, n->right);
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OAS2: // x,y = a,b
|
2011-08-24 14:47:32 -06:00
|
|
|
if(count(n->list) == count(n->rlist))
|
|
|
|
for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->next)
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, ll->n, lr->n);
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OAS2RECV: // v, ok = <-ch
|
|
|
|
case OAS2MAPR: // v, ok = m[k]
|
|
|
|
case OAS2DOTTYPE: // v, ok = x.(type)
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, n->list->n, n->rlist->n);
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
case OSEND: // ch <- x
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, &e->theSink, n->right);
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
case ODEFER:
|
2012-06-07 01:15:09 -06:00
|
|
|
if(e->loopdepth == 1) // top level
|
2011-08-24 14:47:32 -06:00
|
|
|
break;
|
|
|
|
// arguments leak out of scope
|
|
|
|
// TODO: leak to a dummy node instead
|
|
|
|
// fallthrough
|
|
|
|
case OPROC:
|
|
|
|
// go f(x) - f and x escape
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, &e->theSink, n->left->left);
|
|
|
|
escassign(e, &e->theSink, n->left->right); // ODDDARG for call
|
2011-08-24 14:47:32 -06:00
|
|
|
for(ll=n->left->list; ll; ll=ll->next)
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, &e->theSink, ll->n);
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
case OCALLMETH:
|
|
|
|
case OCALLFUNC:
|
|
|
|
case OCALLINTER:
|
|
|
|
esccall(e, n);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OAS2FUNC: // x,y = f()
|
|
|
|
// esccall already done on n->rlist->n. tie it's escretval to n->list
|
|
|
|
lr=n->rlist->n->escretval;
|
|
|
|
for(ll=n->list; lr && ll; lr=lr->next, ll=ll->next)
|
|
|
|
escassign(e, ll->n, lr->n);
|
|
|
|
if(lr || ll)
|
|
|
|
fatal("esc oas2func");
|
|
|
|
break;
|
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
case ORETURN:
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
ll=n->list;
|
2012-10-22 02:18:17 -06:00
|
|
|
if(count(n->list) == 1 && curfn->type->outtuple > 1) {
|
|
|
|
// OAS2FUNC in disguise
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
// esccall already done on n->list->n
|
|
|
|
// tie n->list->n->escretval to curfn->dcl PPARAMOUT's
|
|
|
|
ll = n->list->n->escretval;
|
2012-10-22 02:18:17 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
for(lr = curfn->dcl; lr && ll; lr=lr->next) {
|
|
|
|
if (lr->n->op != ONAME || lr->n->class != PPARAMOUT)
|
|
|
|
continue;
|
|
|
|
escassign(e, lr->n, ll->n);
|
|
|
|
ll = ll->next;
|
|
|
|
}
|
|
|
|
if (ll != nil)
|
|
|
|
fatal("esc return list");
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
case OPANIC:
|
|
|
|
// Argument could leak through recover.
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, &e->theSink, n->left);
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
case OAPPEND:
|
2011-08-25 07:26:13 -06:00
|
|
|
if(!n->isddd)
|
|
|
|
for(ll=n->list->next; ll; ll=ll->next)
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, &e->theSink, ll->n); // lose track of assign to dereference
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
case OCONV:
|
|
|
|
case OCONVNOP:
|
|
|
|
case OCONVIFACE:
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, n, n->left);
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
2011-08-28 10:05:00 -06:00
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
case OARRAYLIT:
|
2011-08-28 10:05:00 -06:00
|
|
|
if(isslice(n->type)) {
|
|
|
|
n->esc = EscNone; // until proven otherwise
|
2012-06-07 01:15:09 -06:00
|
|
|
e->noesc = list(e->noesc, n);
|
|
|
|
n->escloopdepth = e->loopdepth;
|
2011-08-28 10:05:00 -06:00
|
|
|
// Values make it to memory, lose track.
|
|
|
|
for(ll=n->list; ll; ll=ll->next)
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, &e->theSink, ll->n->right);
|
2011-08-28 10:05:00 -06:00
|
|
|
} else {
|
|
|
|
// Link values to array.
|
|
|
|
for(ll=n->list; ll; ll=ll->next)
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, n, ll->n->right);
|
2011-08-28 10:05:00 -06:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
case OSTRUCTLIT:
|
2011-08-28 10:05:00 -06:00
|
|
|
// Link values to struct.
|
|
|
|
for(ll=n->list; ll; ll=ll->next)
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, n, ll->n->right);
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
2011-12-02 12:13:12 -07:00
|
|
|
|
|
|
|
case OPTRLIT:
|
|
|
|
n->esc = EscNone; // until proven otherwise
|
2012-06-07 01:15:09 -06:00
|
|
|
e->noesc = list(e->noesc, n);
|
|
|
|
n->escloopdepth = e->loopdepth;
|
2011-12-02 12:13:12 -07:00
|
|
|
// Contents make it to memory, lose track.
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, &e->theSink, n->left);
|
2011-12-02 12:13:12 -07:00
|
|
|
break;
|
2013-03-20 21:53:27 -06:00
|
|
|
|
|
|
|
case OCALLPART:
|
|
|
|
n->esc = EscNone; // until proven otherwise
|
|
|
|
e->noesc = list(e->noesc, n);
|
|
|
|
n->escloopdepth = e->loopdepth;
|
|
|
|
// Contents make it to memory, lose track.
|
|
|
|
escassign(e, &e->theSink, n->left);
|
|
|
|
break;
|
2011-08-28 10:05:00 -06:00
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
case OMAPLIT:
|
2011-08-28 10:05:00 -06:00
|
|
|
n->esc = EscNone; // until proven otherwise
|
2012-06-07 01:15:09 -06:00
|
|
|
e->noesc = list(e->noesc, n);
|
|
|
|
n->escloopdepth = e->loopdepth;
|
2011-08-28 10:05:00 -06:00
|
|
|
// Keys and values make it to memory, lose track.
|
|
|
|
for(ll=n->list; ll; ll=ll->next) {
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, &e->theSink, ll->n->left);
|
|
|
|
escassign(e, &e->theSink, ll->n->right);
|
2011-08-24 14:47:32 -06:00
|
|
|
}
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
2011-08-28 10:05:00 -06:00
|
|
|
|
|
|
|
case OCLOSURE:
|
2012-06-04 15:07:59 -06:00
|
|
|
// Link addresses of captured variables to closure.
|
|
|
|
for(ll=n->cvars; ll; ll=ll->next) {
|
|
|
|
if(ll->n->op == OXXX) // unnamed out argument; see dcl.c:/^funcargs
|
|
|
|
continue;
|
|
|
|
a = nod(OADDR, ll->n->closure, N);
|
|
|
|
a->lineno = ll->n->lineno;
|
2012-08-31 14:23:37 -06:00
|
|
|
a->escloopdepth = e->loopdepth;
|
2012-06-04 15:07:59 -06:00
|
|
|
typecheck(&a, Erv);
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, n, a);
|
2012-06-04 15:07:59 -06:00
|
|
|
}
|
|
|
|
// fallthrough
|
2012-04-23 13:39:01 -06:00
|
|
|
case OADDR:
|
2011-08-28 10:05:00 -06:00
|
|
|
case OMAKECHAN:
|
|
|
|
case OMAKEMAP:
|
|
|
|
case OMAKESLICE:
|
|
|
|
case ONEW:
|
2012-06-07 01:15:09 -06:00
|
|
|
n->escloopdepth = e->loopdepth;
|
2011-08-28 10:05:00 -06:00
|
|
|
n->esc = EscNone; // until proven otherwise
|
2012-06-07 01:15:09 -06:00
|
|
|
e->noesc = list(e->noesc, n);
|
2011-08-28 10:05:00 -06:00
|
|
|
break;
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(EscState *e, Node *dst, Node *src)
|
2011-08-24 11:07:08 -06:00
|
|
|
{
|
|
|
|
int lno;
|
2012-12-20 15:27:28 -07:00
|
|
|
NodeList *ll;
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
if(isblank(dst) || dst == N || src == N || src->op == ONONAME || src->op == OXXX)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(debug['m'] > 1)
|
2012-06-07 01:15:09 -06:00
|
|
|
print("%L:[%d] %S escassign: %hN(%hJ) = %hN(%hJ)\n", lineno, e->loopdepth,
|
2012-04-23 13:39:01 -06:00
|
|
|
(curfn && curfn->nname) ? curfn->nname->sym : S, dst, dst, src, src);
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
setlineno(dst);
|
2011-08-25 07:26:13 -06:00
|
|
|
|
|
|
|
// Analyze lhs of assignment.
|
2012-06-07 01:15:09 -06:00
|
|
|
// Replace dst with e->theSink if we can't track it.
|
2011-08-24 14:47:32 -06:00
|
|
|
switch(dst->op) {
|
2011-08-25 07:26:13 -06:00
|
|
|
default:
|
|
|
|
dump("dst", dst);
|
|
|
|
fatal("escassign: unexpected dst");
|
|
|
|
|
|
|
|
case OARRAYLIT:
|
|
|
|
case OCLOSURE:
|
|
|
|
case OCONV:
|
|
|
|
case OCONVIFACE:
|
|
|
|
case OCONVNOP:
|
|
|
|
case OMAPLIT:
|
|
|
|
case OSTRUCTLIT:
|
2013-03-20 21:53:27 -06:00
|
|
|
case OCALLPART:
|
2011-08-25 07:26:13 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ONAME:
|
|
|
|
if(dst->class == PEXTERN)
|
2012-06-07 01:15:09 -06:00
|
|
|
dst = &e->theSink;
|
2011-08-25 07:26:13 -06:00
|
|
|
break;
|
2011-08-24 14:47:32 -06:00
|
|
|
case ODOT: // treat "dst.x = src" as "dst = src"
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, dst->left, src);
|
2011-08-24 11:07:08 -06:00
|
|
|
return;
|
2011-08-25 07:26:13 -06:00
|
|
|
case OINDEX:
|
|
|
|
if(isfixedarray(dst->left->type)) {
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, dst->left, src);
|
2011-08-25 07:26:13 -06:00
|
|
|
return;
|
|
|
|
}
|
2012-06-07 01:15:09 -06:00
|
|
|
dst = &e->theSink; // lose track of dereference
|
2011-08-25 07:26:13 -06:00
|
|
|
break;
|
2011-08-24 14:47:32 -06:00
|
|
|
case OIND:
|
|
|
|
case ODOTPTR:
|
2012-06-07 01:15:09 -06:00
|
|
|
dst = &e->theSink; // lose track of dereference
|
2011-08-25 07:26:13 -06:00
|
|
|
break;
|
|
|
|
case OINDEXMAP:
|
|
|
|
// lose track of key and value
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, &e->theSink, dst->right);
|
|
|
|
dst = &e->theSink;
|
2011-08-25 07:26:13 -06:00
|
|
|
break;
|
2011-08-24 14:47:32 -06:00
|
|
|
}
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
lno = setlineno(src);
|
2012-06-07 01:15:09 -06:00
|
|
|
e->pdepth++;
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
switch(src->op) {
|
2011-08-24 11:07:08 -06:00
|
|
|
case OADDR: // dst = &x
|
|
|
|
case OIND: // dst = *x
|
|
|
|
case ODOTPTR: // dst = (*x).f
|
|
|
|
case ONAME:
|
|
|
|
case OPARAM:
|
2011-08-28 10:05:00 -06:00
|
|
|
case ODDDARG:
|
2011-12-02 12:13:12 -07:00
|
|
|
case OPTRLIT:
|
2011-08-28 10:05:00 -06:00
|
|
|
case OARRAYLIT:
|
|
|
|
case OMAPLIT:
|
|
|
|
case OSTRUCTLIT:
|
2012-04-23 13:39:01 -06:00
|
|
|
case OMAKECHAN:
|
|
|
|
case OMAKEMAP:
|
|
|
|
case OMAKESLICE:
|
|
|
|
case ONEW:
|
|
|
|
case OCLOSURE:
|
2013-03-20 21:53:27 -06:00
|
|
|
case OCALLPART:
|
2012-06-07 01:15:09 -06:00
|
|
|
escflows(e, dst, src);
|
2011-08-24 14:47:32 -06:00
|
|
|
break;
|
|
|
|
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
case OCALLMETH:
|
|
|
|
case OCALLFUNC:
|
|
|
|
case OCALLINTER:
|
2012-12-20 15:27:28 -07:00
|
|
|
// Flowing multiple returns to a single dst happens when
|
|
|
|
// analyzing "go f(g())": here g() flows to sink (issue 4529).
|
|
|
|
for(ll=src->escretval; ll; ll=ll->next)
|
|
|
|
escflows(e, dst, ll->n);
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
break;
|
|
|
|
|
2012-01-12 04:08:40 -07:00
|
|
|
case ODOT:
|
|
|
|
// A non-pointer escaping from a struct does not concern us.
|
|
|
|
if(src->type && !haspointers(src->type))
|
|
|
|
break;
|
|
|
|
// fallthrough
|
2011-08-24 14:47:32 -06:00
|
|
|
case OCONV:
|
|
|
|
case OCONVIFACE:
|
|
|
|
case OCONVNOP:
|
2011-09-07 11:03:11 -06:00
|
|
|
case ODOTMETH: // treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC
|
|
|
|
// iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here
|
2011-08-24 14:47:32 -06:00
|
|
|
case ODOTTYPE:
|
|
|
|
case ODOTTYPE2:
|
2011-08-25 07:26:13 -06:00
|
|
|
case OSLICE:
|
|
|
|
case OSLICEARR:
|
2011-08-24 14:47:32 -06:00
|
|
|
// Conversions, field access, slice all preserve the input value.
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, dst, src->left);
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
|
2011-08-25 07:26:13 -06:00
|
|
|
case OAPPEND:
|
|
|
|
// Append returns first argument.
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, dst, src->list->n);
|
2011-08-25 07:26:13 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OINDEX:
|
|
|
|
// Index of array preserves input value.
|
|
|
|
if(isfixedarray(src->left->type))
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, dst, src->left);
|
2011-08-25 07:26:13 -06:00
|
|
|
break;
|
|
|
|
|
2011-08-28 10:05:00 -06:00
|
|
|
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.
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, dst, src->left);
|
|
|
|
escassign(e, dst, src->right);
|
2011-08-28 10:05:00 -06:00
|
|
|
break;
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
|
|
|
|
2012-06-07 01:15:09 -06:00
|
|
|
e->pdepth--;
|
2011-08-24 11:07:08 -06:00
|
|
|
lineno = lno;
|
|
|
|
}
|
|
|
|
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
static void
|
|
|
|
escassignfromtag(EscState *e, Strlit *note, NodeList *dsts, Node *src)
|
|
|
|
{
|
|
|
|
int em;
|
|
|
|
|
|
|
|
em = parsetag(note);
|
|
|
|
|
|
|
|
if(em == EscUnknown) {
|
|
|
|
escassign(e, &e->theSink, src);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(em >>= EscBits; em && dsts; em >>= 1, dsts=dsts->next)
|
|
|
|
if(em & 1)
|
|
|
|
escassign(e, dsts->n, src);
|
|
|
|
|
|
|
|
if (em != 0 && dsts == nil)
|
|
|
|
fatal("corrupt esc tag %Z or messed up escretval list\n", note);
|
|
|
|
}
|
|
|
|
|
2012-10-22 02:18:17 -06:00
|
|
|
// This is a bit messier than fortunate, pulled out of esc's big
|
2011-08-24 11:07:08 -06:00
|
|
|
// switch for clarity. We either have the paramnodes, which may be
|
2013-02-14 16:38:57 -07:00
|
|
|
// connected to other things through flows or we have the parameter type
|
2012-04-23 13:39:01 -06:00
|
|
|
// nodes, which may be marked "noescape". Navigating the ast is slightly
|
2011-08-24 11:07:08 -06:00
|
|
|
// different for methods vs plain functions and for imported vs
|
|
|
|
// this-package
|
|
|
|
static void
|
2012-06-07 01:15:09 -06:00
|
|
|
esccall(EscState *e, Node *n)
|
2011-08-24 11:07:08 -06:00
|
|
|
{
|
|
|
|
NodeList *ll, *lr;
|
2011-08-28 10:05:00 -06:00
|
|
|
Node *a, *fn, *src;
|
2011-08-24 14:47:32 -06:00
|
|
|
Type *t, *fntype;
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
char buf[40];
|
|
|
|
int i;
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
fn = N;
|
|
|
|
switch(n->op) {
|
|
|
|
default:
|
|
|
|
fatal("esccall");
|
2011-08-24 11:07:08 -06:00
|
|
|
|
|
|
|
case OCALLFUNC:
|
2011-08-24 14:47:32 -06:00
|
|
|
fn = n->left;
|
2011-08-24 11:07:08 -06:00
|
|
|
fntype = fn->type;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OCALLMETH:
|
2011-08-24 14:47:32 -06:00
|
|
|
fn = n->left->right->sym->def;
|
2011-08-24 11:07:08 -06:00
|
|
|
if(fn)
|
|
|
|
fntype = fn->type;
|
|
|
|
else
|
2011-08-24 14:47:32 -06:00
|
|
|
fntype = n->left->type;
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OCALLINTER:
|
2011-08-24 14:47:32 -06:00
|
|
|
fntype = n->left->type;
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
ll = n->list;
|
|
|
|
if(n->list != nil && n->list->next == nil) {
|
|
|
|
a = n->list->n;
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
if(a->type->etype == TSTRUCT && a->type->funarg) // f(g()).
|
|
|
|
ll = a->escretval;
|
2011-08-24 14:47:32 -06:00
|
|
|
}
|
|
|
|
|
2012-06-07 01:15:09 -06:00
|
|
|
if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype && fn->defn->esc < EscFuncTagged) {
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
// function in same mutually recursive group. Incorporate into flow graph.
|
|
|
|
// print("esc local fn: %N\n", fn->ntype);
|
|
|
|
if(fn->defn->esc == EscFuncUnknown || n->escretval != nil)
|
2012-06-07 01:15:09 -06:00
|
|
|
fatal("graph inconsistency");
|
2011-08-24 11:07:08 -06:00
|
|
|
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
// set up out list on this call node
|
|
|
|
for(lr=fn->ntype->rlist; lr; lr=lr->next)
|
|
|
|
n->escretval = list(n->escretval, lr->n->left); // type.rlist -> dclfield -> ONAME (PPARAMOUT)
|
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
// Receiver.
|
|
|
|
if(n->op != OCALLFUNC)
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, fn->ntype->left->left, n->left->left);
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2011-08-28 10:05:00 -06:00
|
|
|
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);
|
2012-06-07 01:15:09 -06:00
|
|
|
src->escloopdepth = e->loopdepth;
|
2011-08-28 10:05:00 -06:00
|
|
|
src->lineno = n->lineno;
|
|
|
|
src->esc = EscNone; // until we find otherwise
|
2012-06-07 01:15:09 -06:00
|
|
|
e->noesc = list(e->noesc, src);
|
2011-08-28 10:05:00 -06:00
|
|
|
n->right = src;
|
|
|
|
}
|
|
|
|
if(lr->n->left != N)
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, lr->n->left, src);
|
2011-08-28 10:05:00 -06:00
|
|
|
if(src != ll->n)
|
|
|
|
break;
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
2011-08-28 10:05:00 -06:00
|
|
|
// "..." arguments are untracked
|
|
|
|
for(; ll; ll=ll->next)
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, &e->theSink, ll->n);
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
|
2011-08-24 11:07:08 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-06-07 01:15:09 -06:00
|
|
|
// Imported or completely analyzed function. Use the escape tags.
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
if(n->escretval != nil)
|
|
|
|
fatal("esc already decorated call %+N\n", n);
|
|
|
|
|
|
|
|
// set up out list on this call node with dummy auto ONAMES in the current (calling) function.
|
|
|
|
i = 0;
|
|
|
|
for(t=getoutargx(fntype)->type; t; t=t->down) {
|
|
|
|
src = nod(ONAME, N, N);
|
|
|
|
snprint(buf, sizeof buf, ".dum%d", i++);
|
|
|
|
src->sym = lookup(buf);
|
|
|
|
src->type = t->type;
|
|
|
|
src->class = PAUTO;
|
|
|
|
src->curfn = curfn;
|
|
|
|
src->escloopdepth = e->loopdepth;
|
|
|
|
src->used = 1;
|
|
|
|
src->lineno = n->lineno;
|
|
|
|
n->escretval = list(n->escretval, src);
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
|
|
|
|
// print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval);
|
|
|
|
|
|
|
|
// Receiver.
|
|
|
|
if(n->op != OCALLFUNC)
|
|
|
|
escassignfromtag(e, getthisx(fntype)->type->note, n->escretval, n->left->left);
|
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
for(t=getinargx(fntype)->type; ll; ll=ll->next) {
|
2011-08-28 10:05:00 -06:00
|
|
|
src = ll->n;
|
|
|
|
if(t->isddd && !n->isddd) {
|
|
|
|
// Introduce ODDDARG node to represent ... allocation.
|
|
|
|
src = nod(ODDDARG, N, N);
|
2012-06-07 01:15:09 -06:00
|
|
|
src->escloopdepth = e->loopdepth;
|
2011-08-28 10:05:00 -06:00
|
|
|
src->lineno = n->lineno;
|
|
|
|
src->esc = EscNone; // until we find otherwise
|
2012-06-07 01:15:09 -06:00
|
|
|
e->noesc = list(e->noesc, src);
|
2011-08-28 10:05:00 -06:00
|
|
|
n->right = src;
|
|
|
|
}
|
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
2012-10-29 06:38:21 -06:00
|
|
|
escassignfromtag(e, t->note, n->escretval, src);
|
2011-08-28 10:05:00 -06:00
|
|
|
if(src != ll->n)
|
|
|
|
break;
|
|
|
|
t = t->down;
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
2011-08-28 10:05:00 -06:00
|
|
|
// "..." arguments are untracked
|
|
|
|
for(; ll; ll=ll->next)
|
2012-06-07 01:15:09 -06:00
|
|
|
escassign(e, &e->theSink, ll->n);
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Store the link src->dst in dst, throwing out some quick wins.
|
|
|
|
static void
|
2012-06-07 01:15:09 -06:00
|
|
|
escflows(EscState *e, Node *dst, Node *src)
|
2011-08-24 11:07:08 -06:00
|
|
|
{
|
|
|
|
if(dst == nil || src == nil || dst == src)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Don't bother building a graph for scalars.
|
2011-08-24 14:47:32 -06:00
|
|
|
if(src->type && !haspointers(src->type))
|
2011-08-24 11:07:08 -06:00
|
|
|
return;
|
|
|
|
|
|
|
|
if(debug['m']>2)
|
|
|
|
print("%L::flows:: %hN <- %hN\n", lineno, dst, src);
|
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
if(dst->escflowsrc == nil) {
|
2012-06-07 01:15:09 -06:00
|
|
|
e->dsts = list(e->dsts, dst);
|
|
|
|
e->dstcount++;
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
2012-06-07 01:15:09 -06:00
|
|
|
e->edgecount++;
|
2011-08-24 11:07:08 -06:00
|
|
|
|
|
|
|
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
|
2012-06-07 01:15:09 -06:00
|
|
|
// it's currfn/e->loopdepth are different from the flood's root.
|
2011-08-24 11:07:08 -06:00
|
|
|
// Once an object has been moved to the heap, all of it's upstream should be considered
|
|
|
|
// escaping to the global scope.
|
|
|
|
static void
|
2012-06-07 01:15:09 -06:00
|
|
|
escflood(EscState *e, Node *dst)
|
2011-08-24 11:07:08 -06:00
|
|
|
{
|
|
|
|
NodeList *l;
|
|
|
|
|
|
|
|
switch(dst->op) {
|
|
|
|
case ONAME:
|
|
|
|
case OCLOSURE:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(debug['m']>1)
|
2011-10-31 11:09:40 -06:00
|
|
|
print("\nescflood:%d: dst %hN scope:%S[%d]\n", walkgen, dst,
|
2011-08-24 11:07:08 -06:00
|
|
|
(dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S,
|
|
|
|
dst->escloopdepth);
|
|
|
|
|
2011-08-24 14:47:32 -06:00
|
|
|
for(l = dst->escflowsrc; l; l=l->next) {
|
2011-08-28 10:05:00 -06:00
|
|
|
walkgen++;
|
2012-06-07 01:15:09 -06:00
|
|
|
escwalk(e, 0, dst, l->n);
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-04 20:48:31 -07:00
|
|
|
// There appear to be some loops in the escape graph, causing
|
|
|
|
// arbitrary recursion into deeper and deeper levels.
|
|
|
|
// Cut this off safely by making minLevel sticky: once you
|
|
|
|
// get that deep, you cannot go down any further but you also
|
|
|
|
// cannot go up any further. This is a conservative fix.
|
|
|
|
// Making minLevel smaller (more negative) would handle more
|
|
|
|
// complex chains of indirections followed by address-of operations,
|
|
|
|
// at the cost of repeating the traversal once for each additional
|
|
|
|
// allowed level when a loop is encountered. Using -2 suffices to
|
|
|
|
// pass all the tests we have written so far, which we assume matches
|
|
|
|
// the level of complexity we want the escape analysis code to handle.
|
|
|
|
#define MinLevel (-2)
|
|
|
|
|
2011-08-24 11:07:08 -06:00
|
|
|
static void
|
2012-06-07 01:15:09 -06:00
|
|
|
escwalk(EscState *e, int level, Node *dst, Node *src)
|
2011-08-24 11:07:08 -06:00
|
|
|
{
|
2011-08-24 14:47:32 -06:00
|
|
|
NodeList *ll;
|
2013-02-04 20:48:31 -07:00
|
|
|
int leaks, newlevel;
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2013-02-04 20:48:31 -07:00
|
|
|
if(src->walkgen == walkgen && src->esclevel <= level)
|
2011-08-24 11:07:08 -06:00
|
|
|
return;
|
2011-08-28 10:05:00 -06:00
|
|
|
src->walkgen = walkgen;
|
2013-02-04 20:48:31 -07:00
|
|
|
src->esclevel = level;
|
2011-08-24 11:07:08 -06:00
|
|
|
|
|
|
|
if(debug['m']>1)
|
2012-04-23 13:39:01 -06:00
|
|
|
print("escwalk: level:%d depth:%d %.*s %hN(%hJ) scope:%S[%d]\n",
|
2012-06-07 01:15:09 -06:00
|
|
|
level, e->pdepth, e->pdepth, "\t\t\t\t\t\t\t\t\t\t", src, src,
|
2011-08-24 11:07:08 -06:00
|
|
|
(src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->escloopdepth);
|
|
|
|
|
2012-06-07 01:15:09 -06:00
|
|
|
e->pdepth++;
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2012-10-22 02:18:17 -06:00
|
|
|
// Input parameter flowing to output parameter?
|
2012-11-07 07:59:19 -07:00
|
|
|
if(dst->op == ONAME && dst->class == PPARAMOUT && dst->vargen <= 20) {
|
2012-10-22 02:18:17 -06:00
|
|
|
if(src->op == ONAME && src->class == PPARAM && level == 0 && src->curfn == dst->curfn) {
|
|
|
|
if(src->esc != EscScope && src->esc != EscHeap) {
|
|
|
|
if(debug['m'])
|
|
|
|
warnl(src->lineno, "leaking param: %hN to result %S", src, dst->sym);
|
|
|
|
if((src->esc&EscMask) != EscReturn)
|
|
|
|
src->esc = EscReturn;
|
2012-11-07 07:59:19 -07:00
|
|
|
src->esc |= 1<<((dst->vargen-1) + EscBits);
|
2012-11-07 13:15:21 -07:00
|
|
|
goto recurse;
|
2012-10-22 02:18:17 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-24 11:07:08 -06:00
|
|
|
leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth);
|
|
|
|
|
|
|
|
switch(src->op) {
|
|
|
|
case ONAME:
|
2013-03-15 02:03:45 -06:00
|
|
|
if(src->class == PPARAM && (leaks || dst->escloopdepth < 0) && src->esc != EscHeap) {
|
2011-08-24 11:07:08 -06:00
|
|
|
src->esc = EscScope;
|
|
|
|
if(debug['m'])
|
2011-10-31 11:09:40 -06:00
|
|
|
warnl(src->lineno, "leaking param: %hN", src);
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
2012-06-04 15:07:59 -06:00
|
|
|
|
|
|
|
// Treat a PPARAMREF closure variable as equivalent to the
|
|
|
|
// original variable.
|
2012-04-23 13:39:01 -06:00
|
|
|
if(src->class == PPARAMREF) {
|
|
|
|
if(leaks && debug['m'])
|
|
|
|
warnl(src->lineno, "leaking closure reference %hN", src);
|
2012-06-07 01:15:09 -06:00
|
|
|
escwalk(e, level, dst, src->closure);
|
2012-04-23 13:39:01 -06:00
|
|
|
}
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
|
2011-12-02 12:13:12 -07:00
|
|
|
case OPTRLIT:
|
2011-08-24 11:07:08 -06:00
|
|
|
case OADDR:
|
2011-08-28 10:05:00 -06:00
|
|
|
if(leaks) {
|
|
|
|
src->esc = EscHeap;
|
2011-08-24 11:07:08 -06:00
|
|
|
addrescapes(src->left);
|
2011-08-28 10:05:00 -06:00
|
|
|
if(debug['m'])
|
2011-10-31 11:09:40 -06:00
|
|
|
warnl(src->lineno, "%hN escapes to heap", src);
|
2011-08-28 10:05:00 -06:00
|
|
|
}
|
2013-02-04 20:48:31 -07:00
|
|
|
newlevel = level;
|
|
|
|
if(level > MinLevel)
|
|
|
|
newlevel--;
|
|
|
|
escwalk(e, newlevel, dst, src->left);
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
|
|
|
|
2011-08-28 10:05:00 -06:00
|
|
|
case OARRAYLIT:
|
|
|
|
if(isfixedarray(src->type))
|
|
|
|
break;
|
|
|
|
// fall through
|
|
|
|
case ODDDARG:
|
|
|
|
case OMAKECHAN:
|
|
|
|
case OMAKEMAP:
|
|
|
|
case OMAKESLICE:
|
|
|
|
case OMAPLIT:
|
|
|
|
case ONEW:
|
|
|
|
case OCLOSURE:
|
2013-03-20 21:53:27 -06:00
|
|
|
case OCALLPART:
|
2011-08-28 10:05:00 -06:00
|
|
|
if(leaks) {
|
|
|
|
src->esc = EscHeap;
|
|
|
|
if(debug['m'])
|
2011-10-31 11:09:40 -06:00
|
|
|
warnl(src->lineno, "%hN escapes to heap", src);
|
2011-08-28 10:05:00 -06:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2012-09-24 13:53:12 -06:00
|
|
|
case ODOT:
|
|
|
|
escwalk(e, level, dst, src->left);
|
|
|
|
break;
|
|
|
|
|
2011-08-24 11:07:08 -06:00
|
|
|
case OINDEX:
|
2012-09-24 13:53:12 -06:00
|
|
|
if(isfixedarray(src->left->type)) {
|
|
|
|
escwalk(e, level, dst, src->left);
|
2011-08-24 11:07:08 -06:00
|
|
|
break;
|
2012-09-24 13:53:12 -06:00
|
|
|
}
|
2011-08-28 10:05:00 -06:00
|
|
|
// fall through
|
2011-08-24 11:07:08 -06:00
|
|
|
case OSLICE:
|
|
|
|
case ODOTPTR:
|
|
|
|
case OINDEXMAP:
|
|
|
|
case OIND:
|
2013-02-04 20:48:31 -07:00
|
|
|
newlevel = level;
|
|
|
|
if(level > MinLevel)
|
|
|
|
newlevel++;
|
|
|
|
escwalk(e, newlevel, dst, src->left);
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
|
|
|
|
2012-10-22 02:18:17 -06:00
|
|
|
recurse:
|
2011-08-24 14:47:32 -06:00
|
|
|
for(ll=src->escflowsrc; ll; ll=ll->next)
|
2012-06-07 01:15:09 -06:00
|
|
|
escwalk(e, level, dst, ll->n);
|
2011-08-24 11:07:08 -06:00
|
|
|
|
2012-06-07 01:15:09 -06:00
|
|
|
e->pdepth--;
|
2011-08-24 11:07:08 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2012-06-07 01:15:09 -06:00
|
|
|
esctag(EscState *e, Node *func)
|
2011-08-24 11:07:08 -06:00
|
|
|
{
|
|
|
|
Node *savefn;
|
|
|
|
NodeList *ll;
|
2013-02-05 05:00:38 -07:00
|
|
|
Type *t;
|
|
|
|
|
2012-09-17 15:25:26 -06:00
|
|
|
USED(e);
|
2012-06-07 01:15:09 -06:00
|
|
|
func->esc = EscFuncTagged;
|
|
|
|
|
2013-02-05 05:00:38 -07:00
|
|
|
// External functions are assumed unsafe,
|
|
|
|
// unless //go:noescape is given before the declaration.
|
|
|
|
if(func->nbody == nil) {
|
|
|
|
if(func->noescape) {
|
|
|
|
for(t=getinargx(func->type)->type; t; t=t->down)
|
|
|
|
if(haspointers(t->type))
|
|
|
|
t->note = mktag(EscNone);
|
|
|
|
}
|
2011-08-28 21:29:34 -06:00
|
|
|
return;
|
2013-02-05 05:00:38 -07:00
|
|
|
}
|
2011-08-24 11:07:08 -06:00
|
|
|
|
|
|
|
savefn = curfn;
|
|
|
|
curfn = func;
|
|
|
|
|
|
|
|
for(ll=curfn->dcl; ll; ll=ll->next) {
|
|
|
|
if(ll->n->op != ONAME || ll->n->class != PPARAM)
|
|
|
|
continue;
|
|
|
|
|
2012-10-22 02:18:17 -06:00
|
|
|
switch (ll->n->esc&EscMask) {
|
2011-08-24 11:07:08 -06:00
|
|
|
case EscNone: // not touched by escflood
|
2012-10-22 02:18:17 -06:00
|
|
|
case EscReturn:
|
2011-08-24 14:47:32 -06:00
|
|
|
if(haspointers(ll->n->type)) // don't bother tagging for scalars
|
2012-10-22 02:18:17 -06:00
|
|
|
ll->n->paramfld->note = mktag(ll->n->esc);
|
|
|
|
break;
|
2011-08-24 11:07:08 -06:00
|
|
|
case EscHeap: // touched by escflood, moved to heap
|
|
|
|
case EscScope: // touched by escflood, value leaves scope
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
curfn = savefn;
|
|
|
|
}
|