Skip to content

Commit 507fcf3

Browse files
author
Luuk van Dijk
committed
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
1 parent c8fe9c7 commit 507fcf3

File tree

4 files changed

+236
-33
lines changed

4 files changed

+236
-33
lines changed

src/cmd/gc/esc.c

+95-26
Original file line numberDiff line numberDiff line change
@@ -209,9 +209,10 @@ struct EscState {
209209
int pdepth; // for debug printing in recursions.
210210
int dstcount, edgecount; // diagnostic
211211
NodeList* noesc; // list of possible non-escaping nodes, for printing
212+
int recursive; // recursive function or group of mutually recursive functions.
212213
};
213214

214-
static Strlit *tags[16] = { nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil };
215+
static Strlit *tags[16];
215216

216217
static Strlit*
217218
mktag(int mask)
@@ -260,15 +261,14 @@ analyze(NodeList *all, int recursive)
260261
NodeList *l;
261262
EscState es, *e;
262263

263-
USED(recursive);
264-
265264
memset(&es, 0, sizeof es);
266265
e = &es;
267266
e->theSink.op = ONAME;
268267
e->theSink.orig = &e->theSink;
269268
e->theSink.class = PEXTERN;
270269
e->theSink.sym = lookup(".sink");
271270
e->theSink.escloopdepth = -1;
271+
e->recursive = recursive;
272272

273273
for(l=all; l; l=l->next)
274274
if(l->n->op == ODCLFUNC)
@@ -308,6 +308,8 @@ escfunc(EscState *e, Node *func)
308308
NodeList *ll;
309309
int saveld;
310310

311+
// print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":"");
312+
311313
if(func->esc != 1)
312314
fatal("repeat escfunc %N", func->nname);
313315
func->esc = EscFuncStarted;
@@ -335,6 +337,12 @@ escfunc(EscState *e, Node *func)
335337
}
336338
}
337339

340+
// in a mutually recursive group we lose track of the return values
341+
if(e->recursive)
342+
for(ll=curfn->dcl; ll; ll=ll->next)
343+
if(ll->n->op == ONAME && ll->n->class == PPARAMOUT)
344+
escflows(e, &e->theSink, ll->n);
345+
338346
escloopdepthlist(e, curfn->nbody);
339347
esclist(e, curfn->nbody);
340348
curfn = savefn;
@@ -450,7 +458,7 @@ esc(EscState *e, Node *n)
450458
}
451459
// See case OLABEL in escloopdepth above
452460
// else if(n->left->sym->label == nil)
453-
// fatal("escape anaylysis missed or messed up a label: %+N", n);
461+
// fatal("escape analysis missed or messed up a label: %+N", n);
454462

455463
n->left->sym->label = nil;
456464
break;
@@ -506,13 +514,30 @@ esc(EscState *e, Node *n)
506514
escassign(e, &e->theSink, ll->n);
507515
break;
508516

517+
case OCALLMETH:
518+
case OCALLFUNC:
519+
case OCALLINTER:
520+
esccall(e, n);
521+
break;
522+
523+
case OAS2FUNC: // x,y = f()
524+
// esccall already done on n->rlist->n. tie it's escretval to n->list
525+
lr=n->rlist->n->escretval;
526+
for(ll=n->list; lr && ll; lr=lr->next, ll=ll->next)
527+
escassign(e, ll->n, lr->n);
528+
if(lr || ll)
529+
fatal("esc oas2func");
530+
break;
531+
509532
case ORETURN:
533+
ll=n->list;
510534
if(count(n->list) == 1 && curfn->type->outtuple > 1) {
511535
// OAS2FUNC in disguise
512-
break;
536+
// esccall already done on n->list->n
537+
// tie n->list->n->escretval to curfn->dcl PPARAMOUT's
538+
ll = n->list->n->escretval;
513539
}
514540

515-
ll=n->list;
516541
for(lr = curfn->dcl; lr && ll; lr=lr->next) {
517542
if (lr->n->op != ONAME || lr->n->class != PPARAMOUT)
518543
continue;
@@ -534,12 +559,6 @@ esc(EscState *e, Node *n)
534559
escassign(e, &e->theSink, ll->n); // lose track of assign to dereference
535560
break;
536561

537-
case OCALLMETH:
538-
case OCALLFUNC:
539-
case OCALLINTER:
540-
esccall(e, n);
541-
break;
542-
543562
case OCONV:
544563
case OCONVNOP:
545564
case OCONVIFACE:
@@ -693,6 +712,14 @@ escassign(EscState *e, Node *dst, Node *src)
693712
escflows(e, dst, src);
694713
break;
695714

715+
case OCALLMETH:
716+
case OCALLFUNC:
717+
case OCALLINTER:
718+
if(count(src->escretval) != 1)
719+
fatal("escassign from call %+N", src);
720+
escflows(e, dst, src->escretval->n);
721+
break;
722+
696723
case ODOT:
697724
// A non-pointer escaping from a struct does not concern us.
698725
if(src->type && !haspointers(src->type))
@@ -748,6 +775,26 @@ escassign(EscState *e, Node *dst, Node *src)
748775
lineno = lno;
749776
}
750777

778+
static void
779+
escassignfromtag(EscState *e, Strlit *note, NodeList *dsts, Node *src)
780+
{
781+
int em;
782+
783+
em = parsetag(note);
784+
785+
if(em == EscUnknown) {
786+
escassign(e, &e->theSink, src);
787+
return;
788+
}
789+
790+
for(em >>= EscBits; em && dsts; em >>= 1, dsts=dsts->next)
791+
if(em & 1)
792+
escassign(e, dsts->n, src);
793+
794+
if (em != 0 && dsts == nil)
795+
fatal("corrupt esc tag %Z or messed up escretval list\n", note);
796+
}
797+
751798
// This is a bit messier than fortunate, pulled out of esc's big
752799
// switch for clarity. We either have the paramnodes, which may be
753800
// connected to other things throug flows or we have the parameter type
@@ -760,6 +807,8 @@ esccall(EscState *e, Node *n)
760807
NodeList *ll, *lr;
761808
Node *a, *fn, *src;
762809
Type *t, *fntype;
810+
char buf[40];
811+
int i;
763812

764813
fn = N;
765814
switch(n->op) {
@@ -787,19 +836,20 @@ esccall(EscState *e, Node *n)
787836
ll = n->list;
788837
if(n->list != nil && n->list->next == nil) {
789838
a = n->list->n;
790-
if(a->type->etype == TSTRUCT && a->type->funarg) {
791-
// f(g()).
792-
// Since f's arguments are g's results and
793-
// all function results escape, we're done.
794-
ll = nil;
795-
}
839+
if(a->type->etype == TSTRUCT && a->type->funarg) // f(g()).
840+
ll = a->escretval;
796841
}
797842

798843
if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype && fn->defn->esc < EscFuncTagged) {
799-
// Local function in this round. Incorporate into flow graph.
800-
if(fn->defn->esc == EscFuncUnknown)
844+
// function in same mutually recursive group. Incorporate into flow graph.
845+
// print("esc local fn: %N\n", fn->ntype);
846+
if(fn->defn->esc == EscFuncUnknown || n->escretval != nil)
801847
fatal("graph inconsistency");
802848

849+
// set up out list on this call node
850+
for(lr=fn->ntype->rlist; lr; lr=lr->next)
851+
n->escretval = list(n->escretval, lr->n->left); // type.rlist -> dclfield -> ONAME (PPARAMOUT)
852+
803853
// Receiver.
804854
if(n->op != OCALLFUNC)
805855
escassign(e, fn->ntype->left->left, n->left->left);
@@ -823,15 +873,35 @@ esccall(EscState *e, Node *n)
823873
// "..." arguments are untracked
824874
for(; ll; ll=ll->next)
825875
escassign(e, &e->theSink, ll->n);
876+
826877
return;
827878
}
828879

829880
// Imported or completely analyzed function. Use the escape tags.
830-
if(n->op != OCALLFUNC) {
831-
t = getthisx(fntype)->type;
832-
if(parsetag(t->note) != EscNone)
833-
escassign(e, &e->theSink, n->left->left);
881+
if(n->escretval != nil)
882+
fatal("esc already decorated call %+N\n", n);
883+
884+
// set up out list on this call node with dummy auto ONAMES in the current (calling) function.
885+
i = 0;
886+
for(t=getoutargx(fntype)->type; t; t=t->down) {
887+
src = nod(ONAME, N, N);
888+
snprint(buf, sizeof buf, ".dum%d", i++);
889+
src->sym = lookup(buf);
890+
src->type = t->type;
891+
src->class = PAUTO;
892+
src->curfn = curfn;
893+
src->escloopdepth = e->loopdepth;
894+
src->used = 1;
895+
src->lineno = n->lineno;
896+
n->escretval = list(n->escretval, src);
834897
}
898+
899+
// print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval);
900+
901+
// Receiver.
902+
if(n->op != OCALLFUNC)
903+
escassignfromtag(e, getthisx(fntype)->type->note, n->escretval, n->left->left);
904+
835905
for(t=getinargx(fntype)->type; ll; ll=ll->next) {
836906
src = ll->n;
837907
if(t->isddd && !n->isddd) {
@@ -843,8 +913,7 @@ esccall(EscState *e, Node *n)
843913
e->noesc = list(e->noesc, src);
844914
n->right = src;
845915
}
846-
if(parsetag(t->note) != EscNone)
847-
escassign(e, &e->theSink, src);
916+
escassignfromtag(e, t->note, n->escretval, src);
848917
if(src != ll->n)
849918
break;
850919
t = t->down;

src/cmd/gc/go.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,8 @@ struct Node
306306

307307
// Escape analysis.
308308
NodeList* escflowsrc; // flow(this, src)
309-
int escloopdepth; // -1: global, 0: not set, function top level:1, increased inside function for every loop or label to mark scopes
309+
NodeList* escretval; // on OCALLxxx, list of dummy return values
310+
int escloopdepth; // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
310311

311312
Sym* sym; // various
312313
int32 vargen; // unique name for OTYPE/ONAME

test/escape2.go

+20-6
Original file line numberDiff line numberDiff line change
@@ -561,36 +561,45 @@ func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "y does not esca
561561
return &x[0] // ERROR "&x.0. escapes to heap"
562562
}
563563

564-
func foo75(z *int) { // ERROR "leaking param: z"
564+
func foo75(z *int) { // ERROR "z does not escape"
565565
myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
566566
}
567567

568568
func foo75a(z *int) { // ERROR "z does not escape"
569-
myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
569+
myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
570+
}
571+
572+
func foo75esc(z *int) { // ERROR "leaking param: z"
573+
gxx = myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
574+
}
575+
576+
func foo75aesc(z *int) { // ERROR "z does not escape"
577+
var ppi **interface{} // assignments to pointer dereferences lose track
578+
*ppi = myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
570579
}
571580

572581
func foo76(z *int) { // ERROR "leaking param: z"
573582
myprint(nil, z) // ERROR "[.][.][.] argument does not escape"
574583
}
575584

576585
func foo76a(z *int) { // ERROR "leaking param: z"
577-
myprint1(nil, z) // ERROR "[.][.][.] argument escapes to heap"
586+
myprint1(nil, z) // ERROR "[.][.][.] argument does not escape"
578587
}
579588

580589
func foo76b() {
581590
myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
582591
}
583592

584593
func foo76c() {
585-
myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
594+
myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
586595
}
587596

588597
func foo76d() {
589598
defer myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
590599
}
591600

592601
func foo76e() {
593-
defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
602+
defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
594603
}
595604

596605
func foo76f() {
@@ -610,10 +619,15 @@ func foo77(z []interface{}) { // ERROR "z does not escape"
610619
myprint(nil, z...) // z does not escape
611620
}
612621

613-
func foo77a(z []interface{}) { // ERROR "leaking param: z"
622+
func foo77a(z []interface{}) { // ERROR "z does not escape"
614623
myprint1(nil, z...)
615624
}
616625

626+
func foo77b(z []interface{}) { // ERROR "leaking param: z"
627+
var ppi **interface{}
628+
*ppi = myprint1(nil, z...)
629+
}
630+
617631
func foo78(z int) *int { // ERROR "moved to heap: z"
618632
return &z // ERROR "&z escapes to heap"
619633
}

0 commit comments

Comments
 (0)