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 { ...@@ -209,9 +209,10 @@ struct EscState {
int pdepth; // for debug printing in recursions. int pdepth; // for debug printing in recursions.
int dstcount, edgecount; // diagnostic int dstcount, edgecount; // diagnostic
NodeList* noesc; // list of possible non-escaping nodes, for printing 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* static Strlit*
mktag(int mask) mktag(int mask)
...@@ -260,8 +261,6 @@ analyze(NodeList *all, int recursive) ...@@ -260,8 +261,6 @@ analyze(NodeList *all, int recursive)
NodeList *l; NodeList *l;
EscState es, *e; EscState es, *e;
USED(recursive);
memset(&es, 0, sizeof es); memset(&es, 0, sizeof es);
e = &es; e = &es;
e->theSink.op = ONAME; e->theSink.op = ONAME;
...@@ -269,6 +268,7 @@ analyze(NodeList *all, int recursive) ...@@ -269,6 +268,7 @@ analyze(NodeList *all, int recursive)
e->theSink.class = PEXTERN; e->theSink.class = PEXTERN;
e->theSink.sym = lookup(".sink"); e->theSink.sym = lookup(".sink");
e->theSink.escloopdepth = -1; e->theSink.escloopdepth = -1;
e->recursive = recursive;
for(l=all; l; l=l->next) for(l=all; l; l=l->next)
if(l->n->op == ODCLFUNC) if(l->n->op == ODCLFUNC)
...@@ -308,6 +308,8 @@ escfunc(EscState *e, Node *func) ...@@ -308,6 +308,8 @@ escfunc(EscState *e, Node *func)
NodeList *ll; NodeList *ll;
int saveld; int saveld;
// print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":"");
if(func->esc != 1) if(func->esc != 1)
fatal("repeat escfunc %N", func->nname); fatal("repeat escfunc %N", func->nname);
func->esc = EscFuncStarted; func->esc = EscFuncStarted;
...@@ -335,6 +337,12 @@ escfunc(EscState *e, Node *func) ...@@ -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); escloopdepthlist(e, curfn->nbody);
esclist(e, curfn->nbody); esclist(e, curfn->nbody);
curfn = savefn; curfn = savefn;
...@@ -450,7 +458,7 @@ esc(EscState *e, Node *n) ...@@ -450,7 +458,7 @@ esc(EscState *e, Node *n)
} }
// See case OLABEL in escloopdepth above // See case OLABEL in escloopdepth above
// else if(n->left->sym->label == nil) // 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; n->left->sym->label = nil;
break; break;
...@@ -506,13 +514,30 @@ esc(EscState *e, Node *n) ...@@ -506,13 +514,30 @@ esc(EscState *e, Node *n)
escassign(e, &e->theSink, ll->n); escassign(e, &e->theSink, ll->n);
break; 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: case ORETURN:
ll=n->list;
if(count(n->list) == 1 && curfn->type->outtuple > 1) { if(count(n->list) == 1 && curfn->type->outtuple > 1) {
// OAS2FUNC in disguise // 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) { for(lr = curfn->dcl; lr && ll; lr=lr->next) {
if (lr->n->op != ONAME || lr->n->class != PPARAMOUT) if (lr->n->op != ONAME || lr->n->class != PPARAMOUT)
continue; continue;
...@@ -534,12 +559,6 @@ esc(EscState *e, Node *n) ...@@ -534,12 +559,6 @@ esc(EscState *e, Node *n)
escassign(e, &e->theSink, ll->n); // lose track of assign to dereference escassign(e, &e->theSink, ll->n); // lose track of assign to dereference
break; break;
case OCALLMETH:
case OCALLFUNC:
case OCALLINTER:
esccall(e, n);
break;
case OCONV: case OCONV:
case OCONVNOP: case OCONVNOP:
case OCONVIFACE: case OCONVIFACE:
...@@ -693,6 +712,14 @@ escassign(EscState *e, Node *dst, Node *src) ...@@ -693,6 +712,14 @@ escassign(EscState *e, Node *dst, Node *src)
escflows(e, dst, src); escflows(e, dst, src);
break; 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: case ODOT:
// A non-pointer escaping from a struct does not concern us. // A non-pointer escaping from a struct does not concern us.
if(src->type && !haspointers(src->type)) if(src->type && !haspointers(src->type))
...@@ -748,6 +775,26 @@ escassign(EscState *e, Node *dst, Node *src) ...@@ -748,6 +775,26 @@ escassign(EscState *e, Node *dst, Node *src)
lineno = lno; 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 // This is a bit messier than fortunate, pulled out of esc's big
// switch for clarity. We either have the paramnodes, which may be // switch for clarity. We either have the paramnodes, which may be
// connected to other things throug flows or we have the parameter type // connected to other things throug flows or we have the parameter type
...@@ -760,6 +807,8 @@ esccall(EscState *e, Node *n) ...@@ -760,6 +807,8 @@ esccall(EscState *e, Node *n)
NodeList *ll, *lr; NodeList *ll, *lr;
Node *a, *fn, *src; Node *a, *fn, *src;
Type *t, *fntype; Type *t, *fntype;
char buf[40];
int i;
fn = N; fn = N;
switch(n->op) { switch(n->op) {
...@@ -787,19 +836,20 @@ esccall(EscState *e, Node *n) ...@@ -787,19 +836,20 @@ esccall(EscState *e, Node *n)
ll = n->list; ll = n->list;
if(n->list != nil && n->list->next == nil) { if(n->list != nil && n->list->next == nil) {
a = n->list->n; a = n->list->n;
if(a->type->etype == TSTRUCT && a->type->funarg) { if(a->type->etype == TSTRUCT && a->type->funarg) // f(g()).
// f(g()). ll = a->escretval;
// Since f's arguments are g's results and
// all function results escape, we're done.
ll = nil;
}
} }
if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype && fn->defn->esc < EscFuncTagged) { 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. // function in same mutually recursive group. Incorporate into flow graph.
if(fn->defn->esc == EscFuncUnknown) // print("esc local fn: %N\n", fn->ntype);
if(fn->defn->esc == EscFuncUnknown || n->escretval != nil)
fatal("graph inconsistency"); 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. // Receiver.
if(n->op != OCALLFUNC) if(n->op != OCALLFUNC)
escassign(e, fn->ntype->left->left, n->left->left); escassign(e, fn->ntype->left->left, n->left->left);
...@@ -823,15 +873,35 @@ esccall(EscState *e, Node *n) ...@@ -823,15 +873,35 @@ esccall(EscState *e, Node *n)
// "..." arguments are untracked // "..." arguments are untracked
for(; ll; ll=ll->next) for(; ll; ll=ll->next)
escassign(e, &e->theSink, ll->n); escassign(e, &e->theSink, ll->n);
return; return;
} }
// Imported or completely analyzed function. Use the escape tags. // Imported or completely analyzed function. Use the escape tags.
if(n->op != OCALLFUNC) { if(n->escretval != nil)
t = getthisx(fntype)->type; fatal("esc already decorated call %+N\n", n);
if(parsetag(t->note) != EscNone)
escassign(e, &e->theSink, n->left->left); // 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) { for(t=getinargx(fntype)->type; ll; ll=ll->next) {
src = ll->n; src = ll->n;
if(t->isddd && !n->isddd) { if(t->isddd && !n->isddd) {
...@@ -843,8 +913,7 @@ esccall(EscState *e, Node *n) ...@@ -843,8 +913,7 @@ esccall(EscState *e, Node *n)
e->noesc = list(e->noesc, src); e->noesc = list(e->noesc, src);
n->right = src; n->right = src;
} }
if(parsetag(t->note) != EscNone) escassignfromtag(e, t->note, n->escretval, src);
escassign(e, &e->theSink, src);
if(src != ll->n) if(src != ll->n)
break; break;
t = t->down; t = t->down;
......
...@@ -306,7 +306,8 @@ struct Node ...@@ -306,7 +306,8 @@ struct Node
// Escape analysis. // Escape analysis.
NodeList* escflowsrc; // flow(this, src) NodeList* escflowsrc; // flow(this, src)
int escloopdepth; // -1: global, 0: not set, function top level:1, increased inside function for every loop or label to mark scopes 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 Sym* sym; // various
int32 vargen; // unique name for OTYPE/ONAME int32 vargen; // unique name for OTYPE/ONAME
......
...@@ -561,12 +561,21 @@ func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "y does not esca ...@@ -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" 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" myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
} }
func foo75a(z *int) { // ERROR "z 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" func foo76(z *int) { // ERROR "leaking param: z"
...@@ -574,7 +583,7 @@ 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" 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() { func foo76b() {
...@@ -582,7 +591,7 @@ func foo76b() { ...@@ -582,7 +591,7 @@ func foo76b() {
} }
func foo76c() { func foo76c() {
myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap" myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
} }
func foo76d() { func foo76d() {
...@@ -590,7 +599,7 @@ func foo76d() { ...@@ -590,7 +599,7 @@ func foo76d() {
} }
func foo76e() { 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() { func foo76f() {
...@@ -610,10 +619,15 @@ func foo77(z []interface{}) { // ERROR "z does not escape" ...@@ -610,10 +619,15 @@ func foo77(z []interface{}) { // ERROR "z does not escape"
myprint(nil, z...) // 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...) 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" func foo78(z int) *int { // ERROR "moved to heap: z"
return &z // ERROR "&z escapes to heap" 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