Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1290)

Delta Between Two Patch Sets: src/cmd/gc/esc.c

Issue 4634073: code review 4634073: gc: Escape analysis. (Closed)
Left Patch Set: diff -r 43a8f5250576 https://go.googlecode.com/hg/ Created 13 years, 7 months ago
Right Patch Set: diff -r adfa9f5cca40 https://go.googlecode.com/hg/ Created 13 years, 7 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « src/cmd/gc/dcl.c ('k') | src/cmd/gc/gen.c » ('j') | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
1 // Copyright 2011 The Go Authors. All rights reserved. 1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 // 4 //
5 // The base version before this file existed, active with debug['s'] 5 // The base version before this file existed, active with debug['s']
6 // == 0, assumes any node that has a reference to it created at some 6 // == 0, assumes any node that has a reference to it created at some
7 // point, may flow to the global scope except 7 // point, may flow to the global scope except
8 // - if its address is dereferenced immediately with only CONVNOPs in 8 // - if its address is dereferenced immediately with only CONVNOPs in
9 // between the * and the & 9 // between the * and the &
10 // - if it is for a closure variable and the closure executed at the 10 // - if it is for a closure variable and the closure executed at the
11 // place it's defined 11 // place it's defined
12 // 12 //
13 // Flag -s disables the old codepaths and switches on the code here: 13 // Flag -s disables the old codepaths and switches on the code here:
14 // 14 //
15 // First escfunc, escstmt and escexpr recurse over the ast of each 15 // First escfunc, escstmt and escexpr recurse over the ast of each
16 // function to dig out flow(dst,src) edges between any 16 // function to dig out flow(dst,src) edges between any
17 // pointer-containing nodes and store them in dst->flowsrc. For 17 // pointer-containing nodes and store them in dst->escflowsrc. For
18 // variables assigned to a variable in an outer scope or used as a 18 // variables assigned to a variable in an outer scope or used as a
19 // return value, they store a flow(theSink, src) edge to a fake node 19 // return value, they store a flow(theSink, src) edge to a fake node
20 // 'the Sink'. For variables referenced in closures, an edge 20 // 'the Sink'. For variables referenced in closures, an edge
21 // flow(closure, var) is recorded and the flow of a closure itself to 21 // flow(closure, &var) is recorded and the flow of a closure itself to
22 // an outer scope is tracked the same way as other variables. 22 // an outer scope is tracked the same way as other variables.
23 // 23 //
24 // The link between variables and the function that declares it is 24 // Then escflood walks the graph starting at theSink and tags all
25 // already in the n->dcl list.» Then escflood walks the graph starting 25 // variables of it can reach an & node as escaping and all function
26 // at theSink and tags all variables of it can reach an & node as 26 // parameters it can reach as leaking.
27 // escaping and all function parameters it can reach as leaking.
28 // 27 //
29 // Watch the variables moved to the heap and parameters tagged as 28 // Watch the variables moved to the heap and parameters tagged as
30 // unsafe with -m, more detailed analysis output with -mm 29 // unsafe with -m, more detailed analysis output with -mm
31 // 30 //
32 31
33 #include "go.h" 32 #include "go.h"
34 33
35 static void escfunc(Node *func); 34 static void escfunc(Node *func);
36 static void escstmtlist(NodeList *stmts); 35 static void escstmtlist(NodeList *stmts);
37 static void escstmt(Node *stmt); 36 static void escstmt(Node *stmt);
38 static void escexpr(Node *dst, Node *expr); 37 static void escexpr(Node *dst, Node *expr);
39 static void escexprcall(Node *dst, Node *callexpr); 38 static void escexprcall(Node *dst, Node *callexpr);
40 static void escflows(Node* dst, Node* src); 39 static void escflows(Node* dst, Node* src);
41 static void escflood(Node *dst); 40 static void escflood(Node *dst);
42 static void escwalk(int level, Node *dst, Node *src); 41 static void escwalk(int level, Node *dst, Node *src);
43 static void esctag(Node *func); 42 static void esctag(Node *func);
44 43
45 // Fake node that all 44 // Fake node that all
46 // - return values and output variables 45 // - return values and output variables
47 // - parameters on imported functions not marked 'safe' 46 // - parameters on imported functions not marked 'safe'
48 // - assignments to global variables 47 // - assignments to global variables
49 // flow to. 48 // flow to.
50 static Node theSink; 49 static Node theSink;
51 50
52 static NodeList* dsts; // all dst nodes 51 static NodeList* dsts; // all dst nodes
53 static int dstcount; // diagnostic
54 static int edgecount; // diagnostic
55 static int loopdepth; // for detecting nested loop scopes 52 static int loopdepth; // for detecting nested loop scopes
56 static int pdepth; // for debug printing in recursions. 53 static int pdepth; // for debug printing in recursions.
57 static int floodgen; // loop prevention in flood/walk 54 static int floodgen; // loop prevention in flood/walk
58 static Strlit* safetag; // gets slapped on safe parameters' field types for export 55 static Strlit* safetag; // gets slapped on safe parameters' field types for export
56 static int dstcount, edgecount; // diagnostic
59 57
60 void 58 void
61 escapes(void) 59 escapes(void)
62 { 60 {
63 NodeList *l; 61 NodeList *l;
64 62
65 theSink.op = ONAME; 63 theSink.op = ONAME;
66 theSink.class = PEXTERN; 64 theSink.class = PEXTERN;
67 theSink.sym = lookup(".sink"); 65 theSink.sym = lookup(".sink");
68 » theSink.loopdepth = -1; 66 » theSink.escloopdepth = -1;
69 67
70 » safetag = strlit("noflow"); 68 » safetag = strlit("noescape");
71 69
72 // flow-analyze top level functions 70 // flow-analyze top level functions
73 for(l=xtop; l; l=l->next) 71 for(l=xtop; l; l=l->next)
74 if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE) 72 if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE)
75 escfunc(l->n); 73 escfunc(l->n);
76 74
77 // print("escapes: %d dsts, %d edges\n", dstcount, edgecount); 75 // print("escapes: %d dsts, %d edges\n", dstcount, edgecount);
78 76
79 // visit the updstream of each dst, mark address nodes with 77 // visit the updstream of each dst, mark address nodes with
80 // addrescapes, mark parameters unsafe 78 // addrescapes, mark parameters unsafe
81 for (l = dsts; l; l=l->next) 79 for (l = dsts; l; l=l->next)
82 escflood(l->n); 80 escflood(l->n);
83 81
84 // for all top level functions, tag the typenodes corresponding to the p aram nodes 82 // for all top level functions, tag the typenodes corresponding to the p aram nodes
85 for(l=xtop; l; l=l->next) 83 for(l=xtop; l; l=l->next)
86 if(l->n->op == ODCLFUNC) 84 if(l->n->op == ODCLFUNC)
87 esctag(l->n); 85 esctag(l->n);
88 } 86 }
89 87
90 static void 88 static void
91 escfunc(Node *func) 89 escfunc(Node *func)
92 { 90 {
93 » Node *savefn; 91 » Node *savefn, *n;
94 NodeList *ll; 92 NodeList *ll;
95 int saveld; 93 int saveld;
96 94
97 saveld = loopdepth; 95 saveld = loopdepth;
98 loopdepth = 1; 96 loopdepth = 1;
99 savefn = curfn; 97 savefn = curfn;
100 curfn = func; 98 curfn = func;
101 99
102 for(ll=curfn->dcl; ll; ll=ll->next) { 100 for(ll=curfn->dcl; ll; ll=ll->next) {
103 if(ll->n->op != ONAME) 101 if(ll->n->op != ONAME)
104 continue; 102 continue;
105 switch (ll->n->class) { 103 switch (ll->n->class) {
106 case PPARAMOUT: 104 case PPARAMOUT:
107 // output parameters flow to the sink 105 // output parameters flow to the sink
108 escflows(&theSink, ll->n); 106 escflows(&theSink, ll->n);
109 » » » ll->n->loopdepth = loopdepth; 107 » » » ll->n->escloopdepth = loopdepth;
110 break; 108 break;
111 case PPARAM: 109 case PPARAM:
112 ll->n->esc = EscNone; // prime for escflood later 110 ll->n->esc = EscNone; // prime for escflood later
113 » » » ll->n->loopdepth = loopdepth; 111 » » » ll->n->escloopdepth = loopdepth;
114 break; 112 break;
115 } 113 }
114 }
115
116 // walk will take the address of cvar->closure later and assign it to cv ar.
117 // handle that here by linking a fake oaddr node directly to the closure .
118 for (ll=curfn->cvars; ll; ll=ll->next) {
119 if(ll->n->op == OXXX) // see dcl.c:398
120 continue;
121
122 n = nod(OADDR, ll->n->closure, N);
123 n->lineno = ll->n->lineno;
124 typecheck(&n, Erv);
125 escexpr(curfn, n);
116 } 126 }
117 127
118 escstmtlist(curfn->nbody); 128 escstmtlist(curfn->nbody);
119 curfn = savefn; 129 curfn = savefn;
120 loopdepth = saveld; 130 loopdepth = saveld;
121 } 131 }
122 132
123 static void 133 static void
124 escstmtlist(NodeList* stmts) 134 escstmtlist(NodeList* stmts)
125 { 135 {
(...skipping 15 matching lines...) Expand all
141 151
142 if(stmt->typecheck == 0 && stmt->op != ODCL) { // TODO something with OAS2 152 if(stmt->typecheck == 0 && stmt->op != ODCL) { // TODO something with OAS2
143 dump("escstmt missing typecheck", stmt); 153 dump("escstmt missing typecheck", stmt);
144 fatal("missing typecheck."); 154 fatal("missing typecheck.");
145 } 155 }
146 156
147 // Common to almost all statements, and nil if n/a. 157 // Common to almost all statements, and nil if n/a.
148 escstmtlist(stmt->ninit); 158 escstmtlist(stmt->ninit);
149 159
150 if(debug['m'] > 1) 160 if(debug['m'] > 1)
151 » » print("%L:[%d] %#S statement: %#N\n", lineno, loopdepth, (curfn && curfn->nname) ? curfn->nname->sym : S, stmt); 161 » » print("%L:[%d] %#S statement: %#N\n", lineno, loopdepth,
162 » » (curfn && curfn->nname) ? curfn->nname->sym : S, stmt);
152 163
153 switch(stmt->op) { 164 switch(stmt->op) {
154 default:
155 // Can get here with typecheck errors and will emit
156 // spurious errors, so comment out before committing
157 dump("escstmt", stmt);
158 fatal("escstmt %O", stmt->op);
159 break;
160
161 case ODCL: 165 case ODCL:
162 case ODCLFIELD: 166 case ODCLFIELD:
163 // a declaration ties the node to the current 167 // a declaration ties the node to the current
164 // function, but we already have that edge in 168 // function, but we already have that edge in
165 // curfn->dcl and will follow it explicitly in 169 // curfn->dcl and will follow it explicitly in
166 // escflood to avoid storing redundant information 170 // escflood to avoid storing redundant information
167 // What does have to happen here is note if the name 171 // What does have to happen here is note if the name
168 // is declared inside a looping scope. 172 // is declared inside a looping scope.
169 » » stmt->left->loopdepth = loopdepth; 173 » » stmt->left->escloopdepth = loopdepth;
170 » » break;
171
172 » case ODCLCONST:
173 » case ODCLTYPE:
174 » case OBREAK:
175 » case OCONTINUE:
176 » case OXFALL:
177 » case OGOTO:
178 » case OEMPTY:
179 » case ORECOVER:
180 » » // all harmless
181 break; 174 break;
182 175
183 case OLABEL: // TODO: new loop/scope only if there are backjumps to it. 176 case OLABEL: // TODO: new loop/scope only if there are backjumps to it.
184 loopdepth++; 177 loopdepth++;
185 break; 178 break;
186 179
187 case OBLOCK: 180 case OBLOCK:
188 escstmtlist(stmt->list); 181 escstmtlist(stmt->list);
189 break; 182 break;
190 183
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after
352 345
353 case OPANIC: 346 case OPANIC:
354 // Argument could leak through recover. 347 // Argument could leak through recover.
355 escexpr(&theSink, stmt->left); 348 escexpr(&theSink, stmt->left);
356 break; 349 break;
357 } 350 }
358 351
359 lineno = lno; 352 lineno = lno;
360 } 353 }
361 354
362 // This is a bit messier than fortunate, pulled out of escexpr's big
363 // switch for clarity. We either have the paramnodes, which may be
364 // connected to other things throug flows or we have the parameter type
365 // nodes, which may be marked 'noflow'. Navigating the ast is slightly
366 // different for methods vs plain functions and for imported vs
367 // this-package
368 static void
369 escexprcall(Node *dst, Node *expr)
370 {
371 NodeList *ll, *lr;
372 Node *fn;
373 Type *t, *fntype, *thisarg, *inargs;
374
375 fn = nil;
376 fntype = nil;
377
378 switch(expr->op) {
379 case OCALLFUNC:
380 fn = expr->left;
381 escexpr(N, fn);
382 fntype = fn->type;
383 break;
384
385 case OCALLMETH:
386 fn = expr->left->right; // ODOTxx name
387 fn = fn->sym->def; // resolve to definition if we have it
388 if(fn)
389 fntype = fn->type;
390 else
391 fntype = expr->left->type;
392 break;
393
394 case OCALLINTER:
395 break;
396
397 default:
398 fatal("escexprcall called with non-call expression");
399 }
400
401 if(fn && fn->ntype) {
402 if(debug['m'] > 2)
403 print("escexprcall: have param nodes: %N\n", fn->ntype);
404
405 if(expr->op == OCALLMETH) {
406 if(debug['m'] > 2)
407 print("escexprcall: this: %N\n",fn->ntype->left- >left);
408 escexpr(fn->ntype->left->left, expr->left->left);
409 }
410
411 // lr->n is the dclfield, ->left is the ONAME param node
412 for(ll=expr->list, lr=fn->ntype->list; ll && lr; ll=ll->next) {
413 if(debug['m'] > 2)
414 print("escexprcall: field param: %N\n", lr->n->l eft);
415 if (lr->n->left)
416 escexpr(lr->n->left, ll->n);
417 else
418 escexpr(&theSink, ll->n);
419 if(lr->n->left && !lr->n->left->isddd)
420 lr=lr->next;
421 }
422 return;
423 }
424
425 if(fntype) {
426 if(debug['m'] > 2)
427 print("escexprcall: have param types: %T\n", fntype);
428
429 if(expr->op == OCALLMETH) {
430 thisarg = getthisx(fntype);
431 t = thisarg->type;
432 if(debug['m'] > 2)
433 print("escexprcall: this: %T\n", t);
434 if(!t->note || strcmp(t->note->s, "noflow") != 0)
435 escexpr(&theSink, expr->left->left);
436 else
437 escexpr(N, expr->left->left);
438 }
439
440 inargs = getinargx(fntype);
441 for(ll=expr->list, t=inargs->type; ll; ll=ll->next) {
442 if(debug['m'] > 2)
443 print("escexprcall: field type: %T\n", t);
444 if(!t->note || strcmp(t->note->s, "noflow"))
445 escexpr(&theSink, ll->n);
446 else
447 escexpr(N, ll->n);
448 if(t->down)
449 t=t->down;
450 }
451
452 return;
453 }
454
455 // fallthrough if we don't have enough information:
456 // can only assume all parameters are unsafe
457 // OCALLINTER always ends up here
458
459 if(debug['m']>1 && expr->op != OCALLINTER) {
460 // dump("escexprcall", expr);
461 print("escexprcall: %O, no nodes, no types: %N\n", expr->op, fn) ;
462 }
463
464 escexpr(&theSink, expr->left->left); // the this argument
465 for(ll=expr->list; ll; ll=ll->next)
466 escexpr(&theSink, ll->n);
467 }
468
469 // Assert that expr somehow gets assigned to dst, if non nil. for 355 // Assert that expr somehow gets assigned to dst, if non nil. for
470 // dst==nil, any name node expr still must be marked as being 356 // dst==nil, any name node expr still must be marked as being
471 // evaluated in curfn. For expr==nil, dst must still be examined for 357 // evaluated in curfn. For expr==nil, dst must still be examined for
472 // evaluations inside it (e.g *f(x) = y) 358 // evaluations inside it (e.g *f(x) = y)
473 static void 359 static void
474 escexpr(Node *dst, Node *expr) 360 escexpr(Node *dst, Node *expr)
475 { 361 {
476 int lno; 362 int lno;
477 NodeList *ll; 363 NodeList *ll;
478 364
(...skipping 24 matching lines...) Expand all
503 escexpr(&theSink, dst->right); // map key is put in map 389 escexpr(&theSink, dst->right); // map key is put in map
504 // fallthrough 390 // fallthrough
505 case OIND: 391 case OIND:
506 case ODOTPTR: 392 case ODOTPTR:
507 case OSLICEARR: // ->left is the OADDR of the array 393 case OSLICEARR: // ->left is the OADDR of the array
508 doref: 394 doref:
509 escexpr(N, dst->left); 395 escexpr(N, dst->left);
510 // assignment to dereferences: for now we lose track 396 // assignment to dereferences: for now we lose track
511 escexpr(&theSink, expr); 397 escexpr(&theSink, expr);
512 return; 398 return;
513 399 » » }
514 » » case ONAME: 400
515 » » case OARRAYLIT: 401 » }
516 » » case OSTRUCTLIT: 402
517 » » case OMAPLIT: 403 » if(expr == N || expr->op == ONONAME || expr->op == OXXX)
518 » » » break;
519
520 » » » // these can happen in the OIND recursion
521 » » case OADDR:
522 » » case OCONVNOP:
523 » » » break;
524 » » }
525
526 » }
527
528 » if(expr == N || expr->op == ONONAME || expr->op == OXXX) {
529 return; 404 return;
530 » } 405
531 if(expr->typecheck == 0 && expr->op != OKEY) { 406 if(expr->typecheck == 0 && expr->op != OKEY) {
532 dump("escexpr missing typecheck", expr); 407 dump("escexpr missing typecheck", expr);
533 fatal("Missing typecheck."); 408 fatal("Missing typecheck.");
534 } 409 }
535 410
536 lno = setlineno(expr); 411 lno = setlineno(expr);
537 pdepth++; 412 pdepth++;
538 413
539 if(debug['m'] > 1) 414 if(debug['m'] > 1)
540 » » print("%L:[%d] %#S \t%hN %.*s<= %hN\n", lineno, loopdepth, (curf n && curfn->nname) ? curfn->nname->sym : S, dst, 2*pdepth, ".\t.\t.\t.\t.\t", ex pr); 415 » » print("%L:[%d] %#S \t%hN %.*s<= %hN\n", lineno, loopdepth,
416 » » (curfn && curfn->nname) ? curfn->nname->sym : S, dst,
417 » » 2*pdepth, ".\t.\t.\t.\t.\t", expr);
541 418
542 419
543 switch(expr->op) { 420 switch(expr->op) {
544 » case OADDR:» // dst = &x 421 » case OADDR:» // dst = &x
545 » » escflows(dst, expr);
546 » » escexpr(expr->left, expr->left); // figure out where x came fro m
547 » » break;
548
549 case OIND: // dst = *x 422 case OIND: // dst = *x
550 case ODOTPTR: // dst = (*x).f 423 case ODOTPTR: // dst = (*x).f
551 » » escflows(dst, expr); 424 » » // restart the recursion at x to figure out where it came from
552 » » escexpr(expr->left, expr->left); // figure out where x came from 425 » » escexpr(expr->left, expr->left);
553 » » break; 426 » » // fallthrough
554
555 case ONAME: 427 case ONAME:
556 case OPARAM: 428 case OPARAM:
557 // loopdepth was set in the defining statement or function heade r 429 // loopdepth was set in the defining statement or function heade r
558 escflows(dst, expr); 430 escflows(dst, expr);
559 break; 431 break;
560 432
561 case OARRAYLIT: 433 case OARRAYLIT:
562 case OSTRUCTLIT: 434 case OSTRUCTLIT:
563 expr->loopdepth = loopdepth;
564 escflows(dst, expr);
565 // ll->n is key, left is fieldname or constant index
566 for(ll=expr->list; ll; ll=ll->next)
567 escexpr(expr, ll->n->right);
568 break;
569
570 case OMAPLIT: 435 case OMAPLIT:
571 » » expr->loopdepth = loopdepth; 436 » » expr->escloopdepth = loopdepth;
572 escflows(dst, expr); 437 escflows(dst, expr);
573 for(ll=expr->list; ll; ll=ll->next) { 438 for(ll=expr->list; ll; ll=ll->next) {
574 escexpr(expr, ll->n->left); 439 escexpr(expr, ll->n->left);
575 escexpr(expr, ll->n->right); 440 escexpr(expr, ll->n->right);
576 } 441 }
577 break; 442 break;
578 443
579 case OMAKECHAN: 444 case OMAKECHAN:
580 case OMAKEMAP: 445 case OMAKEMAP:
581 case OMAKESLICE: 446 case OMAKESLICE:
582 case ONEW: 447 case ONEW:
583 expr->curfn = curfn; // should have been done in parse, but pat ch it up here. 448 expr->curfn = curfn; // should have been done in parse, but pat ch it up here.
584 » » expr->loopdepth = loopdepth; 449 » » expr->escloopdepth = loopdepth;
585 escflows(dst, expr); 450 escflows(dst, expr);
586 // first arg is type, all others need checking 451 // first arg is type, all others need checking
587 for(ll=expr->list->next; ll; ll=ll->next) 452 for(ll=expr->list->next; ll; ll=ll->next)
588 escexpr(N, ll->n); 453 escexpr(N, ll->n);
589 break; 454 break;
590 455
591 case OCLOSURE: 456 case OCLOSURE:
592 expr->curfn = curfn; // should have been done in parse, but pat ch it up here. 457 expr->curfn = curfn; // should have been done in parse, but pat ch it up here.
593 » » expr->loopdepth = loopdepth; 458 » » expr->escloopdepth = loopdepth;
594 escflows(dst, expr); 459 escflows(dst, expr);
595 escfunc(expr); 460 escfunc(expr);
596 break; 461 break;
597 462
598 // end of the leaf cases. no calls to escflows() in the cases below. 463 // end of the leaf cases. no calls to escflows() in the cases below.
599 464
600 » case OCOMPLIT: 465
601 » case OLITERAL: 466 » case OCONV:» // unaries that pass the value through
602 » case ORECOVER:
603 » » break;
604
605 » » // harmless unary
606 » case ONOT:
607 » case OCOM:
608 » case OCAP:
609 » case OLEN:
610 » case OREAL:
611 » case OIMAG:
612 » » // array/string conversions make copies
613 » case OARRAYBYTESTR:
614 » case OARRAYRUNESTR:
615 » case OSTRARRAYBYTE:
616 » case OSTRARRAYRUNE:
617 » case ORUNESTR:
618 » » escexpr(N, expr->left);
619 » » break;
620
621 » » // harmless binary
622 » case OADD:
623 » case OAND:
624 » case OANDAND:
625 » case OANDNOT:
626 » case ODIV:
627 » case OEQ:
628 » case OGE:
629 » case OGT:
630 » case OLE:
631 » case OLT:
632 » case OLSH:
633 » case ORSH:
634 » case OMOD:
635 » case OMUL:
636 » case ONE:
637 » case OOR:
638 » case OOROR:
639 » case OSUB:
640 » case OXOR:
641 » case OADDSTR:
642 » case OASOP:
643 » case OCMPIFACE:
644 » case OCMPSTR:
645 » case OCOMPLEX:
646 » case OPLUS:
647 » case OMINUS:
648 » » escexpr(N, expr->left);
649 » » escexpr(N, expr->right);
650 » » break;
651
652 » » // unaries that pass the value through
653 » case OCONV:
654 case OCONVIFACE: 467 case OCONVIFACE:
655 case OCONVNOP: 468 case OCONVNOP:
656 case ODOTTYPE: 469 case ODOTTYPE:
657 case ODOTTYPE2: 470 case ODOTTYPE2:
658 » case ORECV: // leaks the whole channel 471 » case ORECV:» // leaks the whole channel
472 » case ODOTMETH:» // expr->right is just the field or method name
473 » case ODOTINTER:
474 » case ODOT:
659 escexpr(dst, expr->left); 475 escexpr(dst, expr->left);
660 break; 476 break;
661 477
662 case OCOPY: 478 case OCOPY:
663 // left leaks to right, but the return value is harmless 479 // left leaks to right, but the return value is harmless
664 // TODO: treat as *dst = *src, rather than as dst = src 480 // TODO: treat as *dst = *src, rather than as dst = src
665 escexpr(expr->left, expr->right); 481 escexpr(expr->left, expr->right);
666 break; 482 break;
667 483
668 case OAPPEND: 484 case OAPPEND:
669 // See TODO for OCOPY 485 // See TODO for OCOPY
670 escexpr(dst, expr->list->n); 486 escexpr(dst, expr->list->n);
671 for(ll=expr->list->next; ll; ll=ll->next) 487 for(ll=expr->list->next; ll; ll=ll->next)
672 escexpr(expr->list->n, ll->n); 488 escexpr(expr->list->n, ll->n);
673 break; 489 break;
674 490
675 case OCALLMETH: 491 case OCALLMETH:
676 case OCALLFUNC: 492 case OCALLFUNC:
677 case OCALLINTER: 493 case OCALLINTER:
678 // Moved to separate function to isolate the hair. 494 // Moved to separate function to isolate the hair.
679 escexprcall(dst, expr); 495 escexprcall(dst, expr);
680 break; 496 break;
681 497
682 case ODOTMETH:
683 case ODOTINTER:
684 case ODOT:
685 // expr->right is just the field or method name
686 escexpr(dst, expr->left);
687 break;
688
689 case OSLICEARR: // like an implicit OIND to the underlying buffer, but typecheck has inserted an OADDR 498 case OSLICEARR: // like an implicit OIND to the underlying buffer, but typecheck has inserted an OADDR
690 case OSLICESTR: 499 case OSLICESTR:
691 case OSLICE: 500 case OSLICE:
501 case OINDEX:
502 case OINDEXMAP:
692 // the big thing flows, the keys just need checking 503 // the big thing flows, the keys just need checking
693 escexpr(dst, expr->left); 504 escexpr(dst, expr->left);
694 escexpr(N, expr->right); // expr->right is the OKEY 505 escexpr(N, expr->right); // expr->right is the OKEY
695 break; 506 break;
696 507
697 » case OKEY: 508 » default: // all other harmless leaf, unary or binary cases end up here
698 escexpr(N, expr->left); 509 escexpr(N, expr->left);
699 escexpr(N, expr->right); 510 escexpr(N, expr->right);
700 break; 511 break;
701
702 case OINDEX:
703 case OINDEXMAP:
704 escexpr(dst, expr->left);
705 escexpr(N, expr->right);
706 break;
707
708 default:
709 // if we get here, typecheck has probably already complained
710 fatal("escexpr ... <- %O", expr->op);
711 break;
712 } 512 }
713 513
714 pdepth--; 514 pdepth--;
715 lineno = lno; 515 lineno = lno;
716 } 516 }
717 517
518
519 // This is a bit messier than fortunate, pulled out of escexpr's big
520 // switch for clarity. We either have the paramnodes, which may be
521 // connected to other things throug flows or we have the parameter type
522 // nodes, which may be marked 'n(ofloworescape)'. Navigating the ast is slightly
523 // different for methods vs plain functions and for imported vs
524 // this-package
525 static void
526 escexprcall(Node *dst, Node *expr)
527 {
528 NodeList *ll, *lr;
529 Node *fn;
530 Type *t, *fntype, *thisarg, *inargs;
531
532 fn = nil;
533 fntype = nil;
534
535 switch(expr->op) {
536 case OCALLFUNC:
537 fn = expr->left;
538 escexpr(N, fn);
539 fntype = fn->type;
540 break;
541
542 case OCALLMETH:
543 fn = expr->left->right; // ODOTxx name
544 fn = fn->sym->def; // resolve to definition if we have it
545 if(fn)
546 fntype = fn->type;
547 else
548 fntype = expr->left->type;
549 break;
550
551 case OCALLINTER:
552 break;
553
554 default:
555 fatal("escexprcall called with non-call expression");
556 }
557
558 if(fn && fn->ntype) {
559 if(debug['m'] > 2)
560 print("escexprcall: have param nodes: %N\n", fn->ntype);
561
562 if(expr->op == OCALLMETH) {
563 if(debug['m'] > 2)
564 print("escexprcall: this: %N\n",fn->ntype->left- >left);
565 escexpr(fn->ntype->left->left, expr->left->left);
566 }
567
568 // lr->n is the dclfield, ->left is the ONAME param node
569 for(ll=expr->list, lr=fn->ntype->list; ll && lr; ll=ll->next) {
570 if(debug['m'] > 2)
571 print("escexprcall: field param: %N\n", lr->n->l eft);
572 if (lr->n->left)
573 escexpr(lr->n->left, ll->n);
574 else
575 escexpr(&theSink, ll->n);
576 if(lr->n->left && !lr->n->left->isddd)
577 lr=lr->next;
578 }
579 return;
580 }
581
582 if(fntype) {
583 if(debug['m'] > 2)
584 print("escexprcall: have param types: %T\n", fntype);
585
586 if(expr->op == OCALLMETH) {
587 thisarg = getthisx(fntype);
588 t = thisarg->type;
589 if(debug['m'] > 2)
590 print("escexprcall: this: %T\n", t);
591 if(!t->note || strcmp(t->note->s, safetag->s) != 0)
592 escexpr(&theSink, expr->left->left);
593 else
594 escexpr(N, expr->left->left);
595 }
596
597 inargs = getinargx(fntype);
598 for(ll=expr->list, t=inargs->type; ll; ll=ll->next) {
599 if(debug['m'] > 2)
600 print("escexprcall: field type: %T\n", t);
601 if(!t->note || strcmp(t->note->s, safetag->s))
602 escexpr(&theSink, ll->n);
603 else
604 escexpr(N, ll->n);
605 if(t->down)
606 t=t->down;
607 }
608
609 return;
610 }
611
612 // fallthrough if we don't have enough information:
613 // can only assume all parameters are unsafe
614 // OCALLINTER always ends up here
615
616 if(debug['m']>1 && expr->op != OCALLINTER) {
617 // dump("escexprcall", expr);
618 print("escexprcall: %O, no nodes, no types: %N\n", expr->op, fn) ;
619 }
620
621 escexpr(&theSink, expr->left->left); // the this argument
622 for(ll=expr->list; ll; ll=ll->next)
623 escexpr(&theSink, ll->n);
624 }
625
626 // Store the link src->dst in dst, throwing out some quick wins.
718 static void 627 static void
719 escflows(Node* dst, Node* src) 628 escflows(Node* dst, Node* src)
720 { 629 {
721 if(dst == nil || src == nil || dst == src) 630 if(dst == nil || src == nil || dst == src)
722 return; 631 return;
723 632
724 // Don't bother building a graph for scalars. 633 // Don't bother building a graph for scalars.
725 if (src->type && !haspointers(src->type)) 634 if (src->type && !haspointers(src->type))
726 return; 635 return;
727 636
728 if(debug['m']>2) 637 if(debug['m']>2)
729 print("%L::flows:: %hN <- %hN\n", lineno, dst, src); 638 print("%L::flows:: %hN <- %hN\n", lineno, dst, src);
730 639
731 // Assignments to global variables get lumped into theSink. 640 // Assignments to global variables get lumped into theSink.
732 if (dst->op == ONAME && dst->class == PEXTERN) 641 if (dst->op == ONAME && dst->class == PEXTERN)
733 dst = &theSink; 642 dst = &theSink;
734 643
735 » if (dst->flowsrc == nil) { 644 » if (dst->escflowsrc == nil) {
736 dsts = list(dsts, dst); 645 dsts = list(dsts, dst);
737 dstcount++; 646 dstcount++;
738 } 647 }
739 edgecount++; 648 edgecount++;
740 649
741 » dst->flowsrc = list(dst->flowsrc, src); 650 » dst->escflowsrc = list(dst->escflowsrc, src);
742 } 651 }
743 652
744 // Whenever we hit a reference node, the level goes up by one, and whenever 653 // Whenever we hit a reference node, the level goes up by one, and whenever
745 // we hit an OADDR, the level goes down by one. as long as we're on a level > 0 654 // we hit an OADDR, the level goes down by one. as long as we're on a level > 0
746 // finding an OADDR just means we're following the upstream of a dereference, 655 // finding an OADDR just means we're following the upstream of a dereference,
747 // so this address doesn't leak (yet). 656 // so this address doesn't leak (yet).
748 // If level == 0, it means the /value/ of this node can reach the root of this f lood. 657 // If level == 0, it means the /value/ of this node can reach the root of this f lood.
749 // so if this node is an OADDR, it's argument should be marked as escaping iff 658 // so if this node is an OADDR, it's argument should be marked as escaping iff
750 // it's currfn/loopdepth are different from the flood's root. 659 // it's currfn/loopdepth are different from the flood's root.
751 // Once an object has been moved to the heap, all of it's upstream should be con sidered 660 // Once an object has been moved to the heap, all of it's upstream should be con sidered
752 // escaping to the global scope. 661 // escaping to the global scope.
753 static void 662 static void
754 escflood(Node *dst) 663 escflood(Node *dst)
755 { 664 {
756 NodeList *l; 665 NodeList *l;
757 666
758 » if (dst->op != ONAME) 667 » switch(dst->op) {
668 » case ONAME:
669 » case OCLOSURE:
670 » » break;
671 » default:
759 return; 672 return;
673 }
760 674
761 if(debug['m']>1) 675 if(debug['m']>1)
762 print("\nescflood:%d: dst %hN scope:%#S[%d]\n", floodgen, dst, 676 print("\nescflood:%d: dst %hN scope:%#S[%d]\n", floodgen, dst,
763 (dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S, 677 (dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S,
764 » » dst->loopdepth); 678 » » dst->escloopdepth);
765 679
766 » for (l = dst->flowsrc; l; l=l->next) { 680 » for (l = dst->escflowsrc; l; l=l->next) {
767 floodgen++; 681 floodgen++;
768 escwalk(0, dst, l->n); 682 escwalk(0, dst, l->n);
769 } 683 }
770 } 684 }
771 685
772 static void 686 static void
773 escwalk(int level, Node *dst, Node *src) 687 escwalk(int level, Node *dst, Node *src)
774 { 688 {
775 NodeList* ll; 689 NodeList* ll;
776 int leaks; 690 int leaks;
777 691
778 » if (src->floodgen == floodgen) 692 » if (src->escfloodgen == floodgen)
779 return; 693 return;
780 » src->floodgen = floodgen; 694 » src->escfloodgen = floodgen;
781 695
782 if(debug['m']>1) 696 if(debug['m']>1)
783 print("escwalk: level:%d depth:%d %.*s %hN scope:%#S[%d]\n", 697 print("escwalk: level:%d depth:%d %.*s %hN scope:%#S[%d]\n",
784 level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src, 698 level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src,
785 » » (src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->loopdepth); 699 » » (src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->escloopdepth);
786 700
787 pdepth++; 701 pdepth++;
788 702
789 » leaks = (level <= 0) && (dst->loopdepth < src->loopdepth); 703 » leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth);
790 704
791 switch(src->op) { 705 switch(src->op) {
792 case ONAME: 706 case ONAME:
793 » » switch(src->class) { 707 » » if (src->class == PPARAM && leaks && src->esc == EscNone) {
794 » » case PPARAMREF: 708 » » » src->esc = EscScope;
795 » » » // handle implicit OADDR that walk will insert later 709 » » » if(debug['m'])
796 » » » if (leaks) 710 » » » » print("%L:leaking param: %hN\n", src->lineno, sr c);
797 » » » » addrescapes(src->closure);
798 » » » escwalk(level-1, dst, src->closure);
799 » » » break;
800
801 » » case PPARAM:
802 » » » if (leaks && src->esc == EscNone) {
803 » » » » src->esc = EscScope;
804 » » » » if(debug['m'])
805 » » » » » print("%L:leaking param: %hN\n", src->li neno, src);
806 » » » }
807 » » }
808
809 » » break;
810
811 » case OCLOSURE:
812 » » // PPARAMOUT have been linked to the sink explicitly,
813 » » // handle closure vars, in-parameters and local variables here.
814 » » for(ll=src->dcl; ll; ll=ll->next)
815 » » » if(ll->n->class == PPARAM || ll->n->class == PAUTO)
816 » » » » escwalk(0, dst, ll->n);
817
818 » » // the cvars will have their address taken later by walk.
819 » » for(ll=src->cvars; ll; ll=ll->next) {
820 » » » if(ll->n->op == OXXX) // see dcl.c:398
821 » » » » continue;
822 » » » if (leaks)
823 » » » » addrescapes(ll->n);
824 » » » escwalk(0, dst, ll->n);
825 } 711 }
826 break; 712 break;
827 713
828 case OADDR: 714 case OADDR:
829 if (leaks) 715 if (leaks)
830 addrescapes(src->left); 716 addrescapes(src->left);
831 escwalk(level-1, dst, src->left); 717 escwalk(level-1, dst, src->left);
832 break; 718 break;
833 719
834 case OINDEX: 720 case OINDEX:
835 if(isfixedarray(src->type)) 721 if(isfixedarray(src->type))
836 break; 722 break;
837 case OSLICE: 723 case OSLICE:
838 case ODOTPTR: 724 case ODOTPTR:
839 case OINDEXMAP: 725 case OINDEXMAP:
840 case OIND: 726 case OIND:
841 escwalk(level+1, dst, src->left); 727 escwalk(level+1, dst, src->left);
842 } 728 }
843 729
844 » for (ll=src->flowsrc; ll; ll=ll->next) 730 » for (ll=src->escflowsrc; ll; ll=ll->next)
845 escwalk(level, dst, ll->n); 731 escwalk(level, dst, ll->n);
846 732
847 pdepth--; 733 pdepth--;
848 } 734 }
849 735
850 static void 736 static void
851 esctag(Node *func) 737 esctag(Node *func)
852 { 738 {
853 Node *savefn; 739 Node *savefn;
854 NodeList *ll; 740 NodeList *ll;
(...skipping 12 matching lines...) Expand all
867 case EscHeap: // touched by escflood, moved to heap 753 case EscHeap: // touched by escflood, moved to heap
868 case EscScope: // touched by escflood, value leaves scope 754 case EscScope: // touched by escflood, value leaves scope
869 break; 755 break;
870 default: 756 default:
871 fatal("messed up escape tagging: %N::%N", curfn, ll->n); 757 fatal("messed up escape tagging: %N::%N", curfn, ll->n);
872 } 758 }
873 } 759 }
874 760
875 curfn = savefn; 761 curfn = savefn;
876 } 762 }
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b