Commit 507fcf37 authored by Luuk van Dijk's avatar Luuk van Dijk

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
parent c8fe9c76
......@@ -209,9 +209,10 @@ struct EscState {
int pdepth; // for debug printing in recursions.
int dstcount, edgecount; // diagnostic
NodeList* noesc; // list of possible non-escaping nodes, for printing
int recursive; // recursive function or group of mutually recursive functions.
};
static Strlit *tags[16] = { nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil };
static Strlit *tags[16];
static Strlit*
mktag(int mask)
......@@ -260,8 +261,6 @@ analyze(NodeList *all, int recursive)
NodeList *l;
EscState es, *e;
USED(recursive);
memset(&es, 0, sizeof es);
e = &es;
e->theSink.op = ONAME;
......@@ -269,6 +268,7 @@ analyze(NodeList *all, int recursive)
e->theSink.class = PEXTERN;
e->theSink.sym = lookup(".sink");
e->theSink.escloopdepth = -1;
e->recursive = recursive;
for(l=all; l; l=l->next)
if(l->n->op == ODCLFUNC)
......@@ -308,6 +308,8 @@ escfunc(EscState *e, Node *func)
NodeList *ll;
int saveld;
// print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":"");
if(func->esc != 1)
fatal("repeat escfunc %N", func->nname);
func->esc = EscFuncStarted;
......@@ -335,6 +337,12 @@ escfunc(EscState *e, Node *func)
}
}
// 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);
escloopdepthlist(e, curfn->nbody);
esclist(e, curfn->nbody);
curfn = savefn;
......@@ -450,7 +458,7 @@ esc(EscState *e, Node *n)
}
// See case OLABEL in escloopdepth above
// else if(n->left->sym->label == nil)
// fatal("escape anaylysis missed or messed up a label: %+N", n);
// fatal("escape analysis missed or messed up a label: %+N", n);
n->left->sym->label = nil;
break;
......@@ -506,13 +514,30 @@ esc(EscState *e, Node *n)
escassign(e, &e->theSink, ll->n);
break;
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;
case ORETURN:
ll=n->list;
if(count(n->list) == 1 && curfn->type->outtuple > 1) {
// OAS2FUNC in disguise
break;
// esccall already done on n->list->n
// tie n->list->n->escretval to curfn->dcl PPARAMOUT's
ll = n->list->n->escretval;
}
ll=n->list;
for(lr = curfn->dcl; lr && ll; lr=lr->next) {
if (lr->n->op != ONAME || lr->n->class != PPARAMOUT)
continue;
......@@ -534,12 +559,6 @@ esc(EscState *e, Node *n)
escassign(e, &e->theSink, ll->n); // lose track of assign to dereference
break;
case OCALLMETH:
case OCALLFUNC:
case OCALLINTER:
esccall(e, n);
break;
case OCONV:
case OCONVNOP:
case OCONVIFACE:
......@@ -693,6 +712,14 @@ escassign(EscState *e, Node *dst, Node *src)
escflows(e, dst, src);
break;
case OCALLMETH:
case OCALLFUNC:
case OCALLINTER:
if(count(src->escretval) != 1)
fatal("escassign from call %+N", src);
escflows(e, dst, src->escretval->n);
break;
case ODOT:
// A non-pointer escaping from a struct does not concern us.
if(src->type && !haspointers(src->type))
......@@ -748,6 +775,26 @@ escassign(EscState *e, Node *dst, Node *src)
lineno = lno;
}
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);
}
// This is a bit messier than fortunate, pulled out of esc'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
......@@ -760,6 +807,8 @@ esccall(EscState *e, Node *n)
NodeList *ll, *lr;
Node *a, *fn, *src;
Type *t, *fntype;
char buf[40];
int i;
fn = N;
switch(n->op) {
......@@ -787,19 +836,20 @@ esccall(EscState *e, Node *n)
ll = n->list;
if(n->list != nil && n->list->next == nil) {
a = n->list->n;
if(a->type->etype == TSTRUCT && a->type->funarg) {
// f(g()).
// Since f's arguments are g's results and
// all function results escape, we're done.
ll = nil;
}
if(a->type->etype == TSTRUCT && a->type->funarg) // f(g()).
ll = a->escretval;
}
if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype && fn->defn->esc < EscFuncTagged) {
// Local function in this round. Incorporate into flow graph.
if(fn->defn->esc == EscFuncUnknown)
// 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)
fatal("graph inconsistency");
// 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)
// Receiver.
if(n->op != OCALLFUNC)
escassign(e, fn->ntype->left->left, n->left->left);
......@@ -823,15 +873,35 @@ esccall(EscState *e, Node *n)
// "..." arguments are untracked
for(; ll; ll=ll->next)
escassign(e, &e->theSink, ll->n);
return;
}
// Imported or completely analyzed function. Use the escape tags.
if(n->op != OCALLFUNC) {
t = getthisx(fntype)->type;
if(parsetag(t->note) != EscNone)
escassign(e, &e->theSink, n->left->left);
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);
}
// 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);
for(t=getinargx(fntype)->type; ll; ll=ll->next) {
src = ll->n;
if(t->isddd && !n->isddd) {
......@@ -843,8 +913,7 @@ esccall(EscState *e, Node *n)
e->noesc = list(e->noesc, src);
n->right = src;
}
if(parsetag(t->note) != EscNone)
escassign(e, &e->theSink, src);
escassignfromtag(e, t->note, n->escretval, src);
if(src != ll->n)
break;
t = t->down;
......
......@@ -306,7 +306,8 @@ struct Node
// Escape analysis.
NodeList* escflowsrc; // flow(this, src)
int escloopdepth; // -1: global, 0: not set, function top level:1, increased inside function for every loop or label to mark scopes
NodeList* escretval; // on OCALLxxx, list of dummy return values
int escloopdepth; // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
Sym* sym; // various
int32 vargen; // unique name for OTYPE/ONAME
......
......@@ -561,12 +561,21 @@ func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "y does not esca
return &x[0] // ERROR "&x.0. escapes to heap"
}
func foo75(z *int) { // ERROR "leaking param: z"
func foo75(z *int) { // ERROR "z does not escape"
myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
}
func foo75a(z *int) { // ERROR "z does not escape"
myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
}
func foo75esc(z *int) { // ERROR "leaking param: z"
gxx = myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
}
func foo75aesc(z *int) { // ERROR "z does not escape"
var ppi **interface{} // assignments to pointer dereferences lose track
*ppi = myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
}
func foo76(z *int) { // ERROR "leaking param: z"
......@@ -574,7 +583,7 @@ func foo76(z *int) { // ERROR "leaking param: z"
}
func foo76a(z *int) { // ERROR "leaking param: z"
myprint1(nil, z) // ERROR "[.][.][.] argument escapes to heap"
myprint1(nil, z) // ERROR "[.][.][.] argument does not escape"
}
func foo76b() {
......@@ -582,7 +591,7 @@ func foo76b() {
}
func foo76c() {
myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
}
func foo76d() {
......@@ -590,7 +599,7 @@ func foo76d() {
}
func foo76e() {
defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
}
func foo76f() {
......@@ -610,10 +619,15 @@ func foo77(z []interface{}) { // ERROR "z does not escape"
myprint(nil, z...) // z does not escape
}
func foo77a(z []interface{}) { // ERROR "leaking param: z"
func foo77a(z []interface{}) { // ERROR "z does not escape"
myprint1(nil, z...)
}
func foo77b(z []interface{}) { // ERROR "leaking param: z"
var ppi **interface{}
*ppi = myprint1(nil, z...)
}
func foo78(z int) *int { // ERROR "moved to heap: z"
return &z // ERROR "&z escapes to heap"
}
......
// errorcheck -0 -m -l
// Copyright 2012 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.
// Test, using compiler diagnostic flags, that the escape analysis is working.
// Compiles but does not run. Inlining is disabled.
package foo
func noleak(p *int) int { // ERROR "p does not escape"
return *p
}
func leaktoret(p *int) *int { // ERROR "leaking param: p to result"
return p
}
func leaktoret2(p *int) (*int, *int) { // ERROR "leaking param: p to result .anon1" "leaking param: p to result .anon2"
return p, p
}
func leaktoret22(p, q *int) (*int, *int) { // ERROR "leaking param: p to result .anon2" "leaking param: q to result .anon3"
return p, q
}
func leaktoret22b(p, q *int) (*int, *int) { // ERROR "leaking param: p to result .anon3" "leaking param: q to result .anon2"
return leaktoret22(q, p)
}
func leaktoret22c(p, q *int) (*int, *int) { // ERROR "leaking param: p to result .anon3" "leaking param: q to result .anon2"
r, s := leaktoret22(q, p)
return r, s
}
func leaktoret22d(p, q *int) (r, s *int) { // ERROR "leaking param: p to result s" "leaking param: q to result r"
r, s = leaktoret22(q, p)
return
}
func leaktoret22e(p, q *int) (r, s *int) { // ERROR "leaking param: p to result s" "leaking param: q to result r"
r, s = leaktoret22(q, p)
return r, s
}
func leaktoret22f(p, q *int) (r, s *int) { // ERROR "leaking param: p to result s" "leaking param: q to result r"
rr, ss := leaktoret22(q, p)
return rr, ss
}
var gp *int
func leaktosink(p *int) *int { // ERROR "leaking param: p"
gp = p
return p
}
func f1() {
var x int
p := noleak(&x) // ERROR "&x does not escape"
_ = p
}
func f2() {
var x int
p := leaktoret(&x) // ERROR "&x does not escape"
_ = p
}
func f3() {
var x int // ERROR "moved to heap: x"
p := leaktoret(&x) // ERROR "&x escapes to heap"
gp = p
}
func f4() {
var x int // ERROR "moved to heap: x"
p, q := leaktoret2(&x) // ERROR "&x escapes to heap"
gp = p
gp = q
}
func f5() {
var x int
leaktoret22(leaktoret2(&x)) // ERROR "&x does not escape"
}
func f6() {
var x int // ERROR "moved to heap: x"
px1, px2 := leaktoret22(leaktoret2(&x)) // ERROR "&x escapes to heap"
gp = px1
_ = px2
}
type T struct{ x int }
func (t *T) Foo(u int) (*T, bool) { // ERROR "leaking param: t to result"
t.x += u
return t, true
}
func f7() *T {
r, _ := new(T).Foo(42) // ERROR "new.T. escapes to heap"
return r
}
func leakrecursive1(p, q *int) (*int, *int) { // ERROR "leaking param: p" "leaking param: q"
return leakrecursive2(q, p)
}
func leakrecursive2(p, q *int) (*int, *int) { // ERROR "leaking param: p" "leaking param: q"
if *p > *q {
return leakrecursive1(q, p)
}
// without this, leakrecursive? are safe for p and q, b/c in fact their graph does not have leaking edges.
return p, q
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment