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

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

Issue 4634073: code review 4634073: gc: Escape analysis. (Closed)
Left Patch Set: diff -r 6e3e06fb2dc3 https://go.googlecode.com/hg/ Created 13 years, 9 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 // Theory of operation: 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
7 // point, may flow to the global scope except
8 // - if its address is dereferenced immediately with only CONVNOPs in
9 // between the * and the &
10 // - if it is for a closure variable and the closure executed at the
11 // place it's defined
6 // 12 //
7 // A subset of all AST node types holds values. These are the 13 // Flag -s disables the old codepaths and switches on the code here:
8 // 'expression' nodes. The scope of the expression node is either
9 // 'global' or the function it is defined in.
10 // 14 //
11 // An expression node src can 'flow' to another node dst. 15 // First escfunc, escstmt and escexpr recurse over the ast of each
12 // Define "flow(dst, src)" iff at one point in the programs execution: 16 // function to dig out flow(dst,src) edges between any
13 // dst = src - dst is a scalar, struct, array, map or chan, and the valu e of src is assigned to dst 17 // pointer-containing nodes and store them in dst->escflowsrc. For
14 // dst.elem = src - dst is a struct and the value of src is assigned to one o f its fields 18 // variables assigned to a variable in an outer scope or used as a
15 // dst[x] = src - dst is an array or map and the value of src is assigned t o one of its elements 19 // return value, they store a flow(theSink, src) edge to a fake node
16 // dst[src] = x - dst is a map and the value of src is used as a key 20 // 'the Sink'.» For variables referenced in closures, an edge
17 // dst <- src - dst is a channel and src is sent on it. 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.
18 // 23 //
19 // Define flow*(dst, src) as "flow(dst, src) or there is an intermediary 24 // Then escflood walks the graph starting at theSink and tags all
20 // Node n such that flow(dst, n) && flow*(n, src)", i.e. if there is a 25 // variables of it can reach an & node as escaping and all function
21 // path trough the directed graph of nodes connected by flow. 26 // parameters it can reach as leaking.
22 // 27 //
23 // If ref is a pointer or a slice element, consider 28 // Watch the variables moved to the heap and parameters tagged as
24 // ref = &dst ref = dst[b:e] 29 // unsafe with -m, more detailed analysis output with -mm
25 // ref1 = ref ref1 = ref
26 // ref2 = ref or ref2 = ref
27 // ref3 = ref ref3 = ref
28 // dst1 = *ref1 dst1 = ref1[y]
29 // *ref2 = src ref2[x] = src
30 // dst3 = *ref3 dst3 = ref3[y]
31 // 30 //
32 // The graph we want to end up with looks like this:
33 //
34 // src --> * <~> ref2 <~> ref <~> & <~> dst
35 // ^
36 // dst1 <-- * <~> ref1 <~ +
37 // dst3 <-- * <~> ref3 <~ +
38 //
39 // The <~> has to be a new kind of edge, and it has to be symmetric,
40 // otherwise the src can not end up in dst3 (even if that leads to a
41 // spurious flow(dst1, src), but that can't be helped because we don't
42 // want to consider the order of execution).
43 //
44
45 //
46 // Now that we have a definition of flow* between nodes, we can define "leak(nod e)"
47 // for nodes with a function scope: a node leaks if
48 // - it is assigned to a global variable, n output parameter or used in a ret urn list.
49 // - it is used in the argument list a function call, and the corresponding p arameter leaks from it's function
50 // - it is used inside a go statement.
51 //·
52 // A variable must be moved to the heap (from the stack) if "escapes" the curren t function scope,
53 // meaning it's *address* leaks,
54 // - the address is taken (with the unary-& operator OADDR)
55 // - AND this (pointer) value leaks outside the current function scope.
56 //
57 // Note that this will cover the previously special case of * (pointer cast)(uns afe.Pointer(& x)):
58 // OADDR x will leak all the way up to the top OREF and stop there, unless someo ne takes the OADDR of that again.
59 // if i code it all up right that is.
60 31
61 #include "go.h" 32 #include "go.h"
62 33
63 static void eastmtlist(NodeList *l); 34 static void escfunc(Node *func);
64 static void eastmt(Node *n); 35 static void escstmtlist(NodeList *stmts);
65 static void eaexpr(Node *dst, Node *expr); 36 static void escstmt(Node *stmt);
66 37 static void escexpr(Node *dst, Node *expr);
67 // Fake node that all return values, global variables and 38 static void escexprcall(Node *dst, Node *callexpr);
68 // goroutine-used variables leak to. 39 static void escflows(Node* dst, Node* src);
69 static Node theSink; 40 static void escflood(Node *dst);
41 static void escwalk(int level, Node *dst, Node *src);
42 static void esctag(Node *func);
43
44 // Fake node that all
45 // - return values and output variables
46 // - parameters on imported functions not marked 'safe'
47 // - assignments to global variables
48 // flow to.
49 static Node» theSink;
50
51 static NodeList* dsts;» » // all dst nodes
52 static int» loopdepth;» // for detecting nested loop scopes
53 static int» pdepth;»» // for debug printing in recursions.
54 static int» floodgen;» // loop prevention in flood/walk
55 static Strlit*» safetag;» // gets slapped on safe parameters' field types for export
56 static int» dstcount, edgecount;» // diagnostic
70 57
71 void 58 void
72 escanalfunc(Node *func) 59 escapes(void)
73 { 60 {
61 » NodeList *l;
62
63 » theSink.op = ONAME;
64 » theSink.class = PEXTERN;
65 » theSink.sym = lookup(".sink");
66 » theSink.escloopdepth = -1;
67
68 » safetag = strlit("noescape");
69
70 » // flow-analyze top level functions
71 » for(l=xtop; l; l=l->next)
72 » » if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE)
73 » » » escfunc(l->n);
74
75 » // print("escapes: %d dsts, %d edges\n", dstcount, edgecount);
76
77 » // visit the updstream of each dst, mark address nodes with
78 » // addrescapes, mark parameters unsafe
79 » for (l = dsts; l; l=l->next)
80 » » escflood(l->n);
81
82 » // for all top level functions, tag the typenodes corresponding to the p aram nodes
83 » for(l=xtop; l; l=l->next)
84 » » if(l->n->op == ODCLFUNC)
85 » » » esctag(l->n);
86 }
87
88 static void
89 escfunc(Node *func)
90 {
91 » Node *savefn, *n;
92 » NodeList *ll;
93 » int saveld;
94
95 » saveld = loopdepth;
96 » loopdepth = 1;
97 » savefn = curfn;
74 curfn = func; 98 curfn = func;
75 » eastmtlist(func->nbody); 99
76 » curfn = nil; 100 » for(ll=curfn->dcl; ll; ll=ll->next) {
77 } 101 » » if(ll->n->op != ONAME)
78 102 » » » continue;
79 void 103 » » switch (ll->n->class) {
80 escanalfinish() 104 » » case PPARAMOUT:
81 { 105 » » » // output parameters flow to the sink
82 » // walk the graph, mark nodes as 'leaking' 106 » » » escflows(&theSink, ll->n);
83 » // and move to heap any OADDR's arguments that do. 107 » » » ll->n->escloopdepth = loopdepth;
84 } 108 » » » break;
85 109 » » case PPARAM:
86 110 » » » ll->n->esc = EscNone;» // prime for escflood later
87 static int 111 » » » ll->n->escloopdepth = loopdepth;
88 isinteresting(Type *t) { 112 » » » break;
89 113 » » }
90 » if (t == T) 114 » }
91 » » return 1; // can happen for lhs of type switch assignment 115
92 //» » fatal("no type for isinteresting"); 116 » // walk will take the address of cvar->closure later and assign it to cv ar.
93 117 » // handle that here by linking a fake oaddr node directly to the closure .
94 » switch(t->etype) { 118 » for (ll=curfn->cvars; ll; ll=ll->next) {
95 » case TPTR32: 119 » » if(ll->n->op == OXXX) // see dcl.c:398
96 » case TPTR64: 120 » » » continue;
97 » case TINTER: 121
98 » » return 1; 122 » » n = nod(OADDR, ll->n->closure, N);
99 » case TARRAY: 123 » » n->lineno = ll->n->lineno;
100 » case TCHAN: 124 » » typecheck(&n, Erv);
101 » » return isinteresting(t->type); 125 » » escexpr(curfn, n);
102 » case TMAP: 126 » }
103 » » return isinteresting(t->type) || isinteresting(t->down); 127
104 » case TSTRUCT: 128 » escstmtlist(curfn->nbody);
105 » » for(t=t->type; t!=T; t=t->down) { 129 » curfn = savefn;
106 » » » if(t->etype != TFIELD) 130 » loopdepth = saveld;
107 » » » » fatal("isinteresting: not TFIELD: %lT", t); 131 }
108 » » » if (isinteresting(t->type)) 132
109 » » » » return 1; 133 static void
110 » » } 134 escstmtlist(NodeList* stmts)
135 {
136 » for(; stmts; stmts=stmts->next)
137 » » escstmt(stmts->n);
138 }
139
140 static void
141 escstmt(Node *stmt)
142 {
143 » int cl, cr, lno;
144 » NodeList *ll, *lr;
145 » Node *dst;
146
147 » if(stmt == N)
148 » » return;
149
150 » lno = setlineno(stmt);
151
152 » if(stmt->typecheck == 0 && stmt->op != ODCL) {» // TODO something with OAS2
153 » » dump("escstmt missing typecheck", stmt);
154 » » fatal("missing typecheck.");
155 » }
156
157 » // Common to almost all statements, and nil if n/a.
158 » escstmtlist(stmt->ninit);
159
160 » if(debug['m'] > 1)
161 » » print("%L:[%d] %#S statement: %#N\n", lineno, loopdepth,
162 » » (curfn && curfn->nname) ? curfn->nname->sym : S, stmt);
163
164 » switch(stmt->op) {
165 » case ODCL:
166 » case ODCLFIELD:
167 » » // a declaration ties the node to the current
168 » » // function, but we already have that edge in
169 » » // curfn->dcl and will follow it explicitly in
170 » » // escflood to avoid storing redundant information
171 » » // What does have to happen here is note if the name
172 » » // is declared inside a looping scope.
173 » » stmt->left->escloopdepth = loopdepth;
174 » » break;
175
176 » case OLABEL: // TODO: new loop/scope only if there are backjumps to it.
177 » » loopdepth++;
178 » » break;
179
180 » case OBLOCK:
181 » » escstmtlist(stmt->list);
182 » » break;
183
184 » case OFOR:
185 » » if(stmt->ntest != N) {
186 » » » escstmtlist(stmt->ntest->ninit);
187 » » » escexpr(N, stmt->ntest);
188 » » }
189 » » escstmt(stmt->nincr);
190 » » loopdepth++;
191 » » escstmtlist(stmt->nbody);
192 » » loopdepth--;
193 » » break;
194
195 » case ORANGE:» » // for» <list> = range <right> { <nbody> }
196 » » switch(stmt->type->etype) {
197 » » case TSTRING:» // never flows
198 » » » escexpr(stmt->list->n, N);
199 » » » if(stmt->list->next)
200 » » » » escexpr(stmt->list->next->n, N);
201 » » » escexpr(N, stmt->right);
202 » » » break;
203 » » case TARRAY:» // i, v = range sliceorarray
204 » » » escexpr(stmt->list->n, N);
205 » » » if(stmt->list->next)
206 » » » » escexpr(stmt->list->next->n, stmt->right);
207 » » » break;
208 » » case TMAP:» // k [, v] = range map
209 » » » escexpr(stmt->list->n, stmt->right);
210 » » » if(stmt->list->next)
211 » » » » escexpr(stmt->list->next->n, stmt->right);
212 » » » break;
213 » » case TCHAN:» // v = range chan
214 » » » escexpr(stmt->list->n, stmt->right);
215 » » » break;
216 » » }
217 » » loopdepth++;
218 » » escstmtlist(stmt->nbody);
219 » » loopdepth--;
220 » » break;
221
222 » case OIF:
223 » » escexpr(N, stmt->ntest);
224 » » escstmtlist(stmt->nbody);
225 » » escstmtlist(stmt->nelse);
226 » » break;
227
228 » case OSELECT:
229 » » for(ll=stmt->list; ll; ll=ll->next) { // cases
230 » » » escstmt(ll->n->left);
231 » » » escstmtlist(ll->n->nbody);
232 » » }
233 » » break;
234
235 » case OSELRECV2:» // v, ok := <-ch ntest:ok
236 » » escexpr(N, stmt->ntest);
111 // fallthrough 237 // fallthrough
112 » } 238 » case OSELRECV:» // v := <-ch» left: v right->op = ORECV
113 » return 0; 239 » » escexpr(N, stmt->left);
114 } 240 » » escexpr(stmt->left, stmt->right);
115 241 » » break;
116 static void 242
117 eaexpr(Node *dst, Node *n) 243 » case OSWITCH:
118 { 244 » » if(stmt->ntest && stmt->ntest->op == OTYPESW) {
245 » » » for(ll=stmt->list; ll; ll=ll->next) { // cases
246 » » » » // ntest->right is the argument of the .(type),
247 » » » » // ll->n->nname is the variable per case
248 » » » » escexpr(ll->n->nname, stmt->ntest->right);
249 » » » » escstmtlist(ll->n->nbody);
250 » » » }
251 » » } else {
252 » » » escexpr(N, stmt->ntest);
253 » » » for(ll=stmt->list; ll; ll=ll->next) { // cases
254 » » » » for(lr=ll->n->list; lr; lr=lr->next)
255 » » » » » escexpr(N, lr->n);
256 » » » » escstmtlist(ll->n->nbody);
257 » » » }
258 » » }
259 » » break;
260
261 » case OAS:
262 » case OASOP:
263 » » escexpr(stmt->left, stmt->right);
264 » » break;
265
266 » » // escape analysis happens after typecheck, so the
267 » » // OAS2xxx have already been substituted.
268 » case OAS2:» // x,y = a,b
269 » » cl = count(stmt->list);
270 » » cr = count(stmt->rlist);
271 » » if(cl > 1 && cr == 1) {
272 » » » for(ll=stmt->list; ll; ll=ll->next)
273 » » » » escexpr(ll->n, stmt->rlist->n);
274 » » } else {
275 » » » if(cl != cr)
276 » » » » fatal("escstmt: bad OAS2: %N", stmt);
277 » » » for(ll=stmt->list, lr=stmt->rlist; ll; ll=ll->next, lr=l r->next)
278 » » » » escexpr(ll->n, lr->n);
279 » » }
280 » » break;
281
282 » case OAS2RECV:» » // v, ok = <-ch
283 » case OAS2MAPR:» » // v, ok = m[k]
284 » case OAS2DOTTYPE:» // v, ok = x.(type)
285 » » escexpr(stmt->list->n, stmt->rlist->n);
286 » » escexpr(stmt->list->next->n, N);
287 » » break;
288
289 » case OAS2MAPW:» » // m[k] = x, ok.. stmt->list->n is the INDEXMAP, k is handled in escexpr(dst...)
290 » » escexpr(stmt->list->n, stmt->rlist->n);
291 » » escexpr(N, stmt->rlist->next->n);
292 » » break;
293
294 » case ORECV:» » // unary <-ch as statement
295 » » escexpr(N, stmt->left);
296 » » break;
297
298 » case OSEND:» » // ch <- x
299 » » escexpr(&theSink, stmt->right);» // for now. TODO escexpr(stmt-> left, stmt->right);
300 » » break;
301
302 » case OCOPY:» // todo: treat as *dst=*src instead of as dst=src
303 » » escexpr(stmt->left, stmt->right);
304 » » break;
305
306 » case OAS2FUNC:» // x,y,z = f()
307 » » for(ll = stmt->list; ll; ll=ll->next)
308 » » » escexpr(ll->n, N);
309 » » escexpr(N, stmt->rlist->n);
310 » » break;
311
312 » case OCALLINTER:
313 » case OCALLFUNC:
314 » case OCALLMETH:
315 » » escexpr(N, stmt);
316 » » break;
317
318 » case OPROC:
319 » case ODEFER:
320 » » // stmt->left is a (pseud)ocall, stmt->left->left is
321 » » // the function being called. if this defer is at
322 » » // loopdepth >1, everything leaks. TODO this is
323 » » // overly conservative, it's enough if it leaks to a
324 » » // fake node at the function's top level
325 » » dst = &theSink;
326 » » if (stmt->op == ODEFER && loopdepth <= 1)
327 » » » dst = nil;
328 » » escexpr(dst, stmt->left->left);
329 » » for(ll=stmt->left->list; ll; ll=ll->next)
330 » » » escexpr(dst, ll->n);
331 » » break;
332
333 » case ORETURN:
334 » » for(ll=stmt->list; ll; ll=ll->next)
335 » » » escexpr(&theSink, ll->n);
336 » » break;
337
338 » case OCLOSE:
339 » case OPRINT:
340 » case OPRINTN:
341 » » escexpr(N, stmt->left);
342 » » for(ll=stmt->list; ll; ll=ll->next)
343 » » » escexpr(N, ll->n);
344 » » break;
345
346 » case OPANIC:
347 » » // Argument could leak through recover.
348 » » escexpr(&theSink, stmt->left);
349 » » break;
350 » }
351
352 » lineno = lno;
353 }
354
355 // Assert that expr somehow gets assigned to dst, if non nil. for
356 // dst==nil, any name node expr still must be marked as being
357 // evaluated in curfn.» For expr==nil, dst must still be examined for
358 // evaluations inside it (e.g *f(x) = y)
359 static void
360 escexpr(Node *dst, Node *expr)
361 {
362 » int lno;
119 NodeList *ll; 363 NodeList *ll;
120 364
121 » if(n == N || n == dst) 365 » if(isblank(dst)) dst = N;
366
367 » // the lhs of an assignment needs recursive analysis too
368 » // these are the only interesting cases
369 » // todo:check channel case
370 » if(dst) {
371 » » setlineno(dst);
372
373 » » switch(dst->op) {
374 » » case OINDEX:
375 » » case OSLICE:
376 » » » escexpr(N, dst->right);
377
378 » » » // slice: "dst[x] = src" is like *(underlying array)[x ] = src
379 » » » // TODO maybe this never occurs b/c of OSLICEARR and it' s inserted OADDR
380 » » » if(!isfixedarray(dst->left->type))
381 » » » » goto doref;
382
383 » » » // fallthrough;» treat "dst[x] = src" as "dst = src"
384 » » case ODOT:» // treat "dst.x = src" as "dst = src"
385 » » » escexpr(dst->left, expr);
386 » » » return;
387
388 » » case OINDEXMAP:
389 » » » escexpr(&theSink, dst->right);» // map key is put in map
390 » » » // fallthrough
391 » » case OIND:
392 » » case ODOTPTR:
393 » » case OSLICEARR:» // ->left is the OADDR of the array
394 » » doref:
395 » » » escexpr(N, dst->left);
396 » » » // assignment to dereferences: for now we lose track
397 » » » escexpr(&theSink, expr);
398 » » » return;
399 » » }
400
401 » }
402
403 » if(expr == N || expr->op == ONONAME || expr->op == OXXX)
122 return; 404 return;
123 405
124 » setlineno(n); 406 » if(expr->typecheck == 0 && expr->op != OKEY) {
125 407 » » dump("escexpr missing typecheck", expr);
126 » if (n->trecur)
127 » » return;
128 » n->trecur = 1;
129
130 » if (n->typecheck == 0) {··
131 » » dump("eaexpr missing typecheck", n);
132 fatal("Missing typecheck."); 408 fatal("Missing typecheck.");
133 } 409 }
134 410
135 » if (dst != &theSink) 411 » lno = setlineno(expr);
136 » » print("%L %S::%#N <= %#N\n", lineno, curfn->nname->sym, dst, n); 412 » pdepth++;
137 » else 413
138 » » print("%L %S:: leaks %#N\n", lineno, curfn->nname->sym, n); 414 » if(debug['m'] > 1)
139 415 » » print("%L:[%d] %#S \t%hN %.*s<= %hN\n", lineno, loopdepth,
140 416 » » (curfn && curfn->nname) ? curfn->nname->sym : S, dst,
141 » // if dst is not interesting, save some work down the recursion 417 » » 2*pdepth, ".\t.\t.\t.\t.\t", expr);
142 » if (dst != &theSink) 418
143 » if ((dst == N) || isblank(dst) || !isinteresting(dst->type)) 419
144 » » dst = N; 420 » switch(expr->op) {
145 421 » case OADDR:» // dst = &x
146 » // here: turn dests x[k] into x, etc. see part-of in top comment 422 » case OIND:» // dst = *x
147 423 » case ODOTPTR:» // dst = (*x).f
148 » if (dst && dst != &theSink && !dst->type) dump("typeless dst", dst); 424 » » // restart the recursion at x to figure out where it came from
149 »······· 425 » » escexpr(expr->left, expr->left);
150 » if (dst && dst->op == ONAME && dst->class == PEXTERN)
151 » » dst = &theSink;
152
153
154
155 » switch(n->op) {
156 » default:
157 » » dump("eaexpr", n);
158 » » fatal("eaexpr %O", n->op);
159 » » break;
160
161 » case OADDR:
162 » » eaexpr(N, n->left);
163 // fallthrough 426 // fallthrough
164 case ONAME: 427 case ONAME:
165 case OPARAM: 428 case OPARAM:
166 » case OCOMPLIT: 429 » » // loopdepth was set in the defining statement or function heade r
430 » » escflows(dst, expr);
431 » » break;
432
433 » case OARRAYLIT:
434 » case OSTRUCTLIT:
167 case OMAPLIT: 435 case OMAPLIT:
168 » case OSTRUCTLIT: 436 » » expr->escloopdepth = loopdepth;
169 » case OARRAYLIT: 437 » » escflows(dst, expr);
170 » » if (dst) dst->asrc = list(dst->asrc, n); 438 » » for(ll=expr->list; ll; ll=ll->next) {
171 » » break; 439 » » » escexpr(expr, ll->n->left);
172 440 » » » escexpr(expr, ll->n->right);
173 » case OIND: // dst = *y 441 » » }
174 » » eaexpr(N, n->left); 442 » » break;
175 » » break; 443
176 444 » case OMAKECHAN:
177 » case OLITERAL: 445 » case OMAKEMAP:
178 » case ORECOVER: 446 » case OMAKESLICE:
179 » » break; 447 » case ONEW:
180 448 » » expr->curfn = curfn; // should have been done in parse, but pat ch it up here.
181 » // harmless unary 449 » » expr->escloopdepth = loopdepth;
182 » case ONOT:» case OCOM: 450 » » escflows(dst, expr);
183 » case OCAP:» case OLEN: 451 » » // first arg is type, all others need checking
184 » case OREAL:» case OIMAG: 452 » » for(ll=expr->list->next; ll; ll=ll->next)
185 » case OARRAYBYTESTR: 453 » » » escexpr(N, ll->n);
186 » case OARRAYRUNESTR: 454 » » break;
187 » case OSTRARRAYBYTE: 455
188 » case OSTRARRAYRUNE: 456 » case OCLOSURE:
189 » case ORUNESTR: 457 » » expr->curfn = curfn; // should have been done in parse, but pat ch it up here.
190 » » eaexpr(N, n->left); 458 » » expr->escloopdepth = loopdepth;
191 » » break; 459 » » escflows(dst, expr);
192 460 » » escfunc(expr);
193 » // harmless binary 461 » » break;
194 » case OADD:» case OAND:» case OANDAND: 462
195 » case OANDNOT:» case ODIV:» case OEQ: 463 » // end of the leaf cases. no calls to escflows() in the cases below.
196 » case OGE:» case OGT:» case OLE: 464
197 » case OLT:» case OLSH:» case ORSH: 465
198 » case OMOD:» case OMUL:» case ONE: 466 » case OCONV:» // unaries that pass the value through
199 » case OOR:» case OOROR:» case OSUB:
200 » case OXOR:» case OADDSTR:» case OASOP:
201 » case OCMPIFACE:»case OCMPSTR: » case OCOMPLEX:
202 » case OPLUS: case OMINUS:
203 » » eaexpr(N, n->left);
204 » » eaexpr(N, n->right);
205 » » break;
206
207 » // unaries that pass the value through
208 » case OCONV:
209 case OCONVIFACE: 467 case OCONVIFACE:
210 case OCONVNOP: 468 case OCONVNOP:
211 case ODOTTYPE: 469 case ODOTTYPE:
212 case ODOTTYPE2: 470 case ODOTTYPE2:
213 » case ORECV: // leaks the whole channel 471 » case ORECV:» // leaks the whole channel
214 » » eaexpr(dst, n->left); 472 » case ODOTMETH:» // expr->right is just the field or method name
473 » case ODOTINTER:
474 » case ODOT:
475 » » escexpr(dst, expr->left);
215 break; 476 break;
216 477
217 case OCOPY: 478 case OCOPY:
218 // left leaks to right, but the return value is harmless 479 // left leaks to right, but the return value is harmless
219 » » eaexpr(n->left, n->right); 480 » » // TODO: treat as *dst = *src, rather than as dst = src
481 » » escexpr(expr->left, expr->right);
220 break; 482 break;
221 483
222 case OAPPEND: 484 case OAPPEND:
223 » » eaexpr(dst, n->list->n); 485 » » // See TODO for OCOPY
224 » » for (ll=n->list->next; ll; ll=ll->next) 486 » » escexpr(dst, expr->list->n);
225 » » » eaexpr(n->list->n, ll->n); 487 » » for(ll=expr->list->next; ll; ll=ll->next)
226 » » break; 488 » » » escexpr(expr->list->n, ll->n);
227 489 » » break;
228 » // all arguments leak to the parameter nodes. 490
229 » // TODO ...when we figure out how to get our hands at them.
230 case OCALLMETH: 491 case OCALLMETH:
492 case OCALLFUNC:
231 case OCALLINTER: 493 case OCALLINTER:
232 » » eaexpr(&theSink, n->left->left); // DOTMETH->this or DOTINTER->t his 494 » » // Moved to separate function to isolate the hair.
233 » » // fallthrough 495 » » escexprcall(dst, expr);
496 » » break;
497
498 » case OSLICEARR:» // like an implicit OIND to the underlying buffer, but typecheck has inserted an OADDR
499 » case OSLICESTR:
500 » case OSLICE:
501 » case OINDEX:
502 » case OINDEXMAP:
503 » » // the big thing flows, the keys just need checking
504 » » escexpr(dst, expr->left);
505 » » escexpr(N, expr->right); // expr->right is the OKEY
506 » » break;
507
508 » default: // all other harmless leaf, unary or binary cases end up here
509 » » escexpr(N, expr->left);
510 » » escexpr(N, expr->right);
511 » » break;
512 » }
513
514 » pdepth--;
515 » lineno = lno;
516 }
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) {
234 case OCALLFUNC: 536 case OCALLFUNC:
235 » » for (ll=n->list; ll; ll=ll->next) 537 » » fn = expr->left;
236 » » » eaexpr(&theSink , ll->n); 538 » » escexpr(N, fn);
237 » » break; 539 » » fntype = fn->type;
238 540 » » break;
239 » case ODOT: 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.
627 static void
628 escflows(Node* dst, Node* src)
629 {
630 » if(dst == nil || src == nil || dst == src)
631 » » return;
632
633 » // Don't bother building a graph for scalars.
634 » if (src->type && !haspointers(src->type))
635 » » return;
636
637 » if(debug['m']>2)
638 » » print("%L::flows:: %hN <- %hN\n", lineno, dst, src);
639
640 » // Assignments to global variables get lumped into theSink.
641 » if (dst->op == ONAME && dst->class == PEXTERN)
642 » » dst = &theSink;
643
644 » if (dst->escflowsrc == nil) {
645 » » dsts = list(dsts, dst);
646 » » dstcount++;
647 » }
648 » edgecount++;
649
650 » dst->escflowsrc = list(dst->escflowsrc, src);
651 }
652
653 // Whenever we hit a reference node, the level goes up by one, and whenever
654 // we hit an OADDR, the level goes down by one. as long as we're on a level > 0
655 // finding an OADDR just means we're following the upstream of a dereference,
656 // so this address doesn't leak (yet).
657 // If level == 0, it means the /value/ of this node can reach the root of this f lood.
658 // so if this node is an OADDR, it's argument should be marked as escaping iff
659 // it's currfn/loopdepth are different from the flood's root.
660 // Once an object has been moved to the heap, all of it's upstream should be con sidered
661 // escaping to the global scope.
662 static void
663 escflood(Node *dst)
664 {
665 » NodeList *l;
666
667 » switch(dst->op) {
668 » case ONAME:
669 » case OCLOSURE:
670 » » break;
671 » default:
672 » » return;
673 » }
674
675 » if(debug['m']>1)
676 » » print("\nescflood:%d: dst %hN scope:%#S[%d]\n", floodgen, dst,
677 » » (dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S,
678 » » dst->escloopdepth);
679
680 » for (l = dst->escflowsrc; l; l=l->next) {
681 » » floodgen++;
682 » » escwalk(0, dst, l->n);
683 » }
684 }
685
686 static void
687 escwalk(int level, Node *dst, Node *src)
688 {
689 » NodeList* ll;
690 » int leaks;
691
692 » if (src->escfloodgen == floodgen)
693 » » return;
694 » src->escfloodgen = floodgen;
695
696 » if(debug['m']>1)
697 » » print("escwalk: level:%d depth:%d %.*s %hN scope:%#S[%d]\n",
698 » » level, pdepth, pdepth, "\t\t\t\t\t\t\t\t\t\t", src,
699 » » (src->curfn && src->curfn->nname) ? src->curfn->nname->sym : S, src->escloopdepth);
700
701 » pdepth++;
702
703 » leaks = (level <= 0) && (dst->escloopdepth < src->escloopdepth);
704
705 » switch(src->op) {
706 » case ONAME:
707 » » if (src->class == PPARAM && leaks && src->esc == EscNone) {
708 » » » src->esc = EscScope;
709 » » » if(debug['m'])
710 » » » » print("%L:leaking param: %hN\n", src->lineno, sr c);
711 » » }
712 » » break;
713
714 » case OADDR:
715 » » if (leaks)
716 » » » addrescapes(src->left);
717 » » escwalk(level-1, dst, src->left);
718 » » break;
719
720 » case OINDEX:
721 » » if(isfixedarray(src->type))
722 » » » break;
723 » case OSLICE:
240 case ODOTPTR: 724 case ODOTPTR:
241 // pretend all of the struct leaked
242 // n->right is just an (untypechecked) ONAME
243 eaexpr(dst, n->left);
244 break;
245
246 case OSLICE: // the big thing leaks, the keys just need checking
247 case OSLICEARR:
248 case OSLICESTR:
249 eaexpr(dst, n->left);
250 eaexpr(N, n->right->left); // the OKEY
251 eaexpr(N, n->right->right);
252 break;
253
254 case OINDEX:···
255 case OINDEXMAP: 725 case OINDEXMAP:
256 » » eaexpr(dst, n->left); 726 » case OIND:
257 » » eaexpr(N, n->right); 727 » » escwalk(level+1, dst, src->left);
258 » » break; 728 » }
259 729
260 » case OMAKECHAN: 730 » for (ll=src->escflowsrc; ll; ll=ll->next)
261 » case OMAKEMAP: 731 » » escwalk(level, dst, ll->n);
262 » case OMAKESLICE: 732
263 » case ONEW:» » // first arg is type, all others need checking 733 » pdepth--;
264 » » for (ll=n->list->next; ll; ll=ll->next) 734 }
265 » » » eaexpr(N, ll->n); 735
266 » » if (dst) dst->asrc = list(dst->asrc, n); // but this is intere sting 736 static void
267 » » break; 737 esctag(Node *func)
268 738 {
269 » » break; 739 » Node *savefn;
270 » } 740 » NodeList *ll;
271 741
272 » n->trecur = 0; 742 » savefn = curfn;
273 } 743 » curfn = func;
274 744
275 static void 745 » for(ll=curfn->dcl; ll; ll=ll->next) {
276 eastmtlist(NodeList* l) 746 » » if(ll->n->op != ONAME || ll->n->class != PPARAM)
277 { 747 » » » continue;
278 » for (; l; l = l->next) 748
279 » » eastmt(l->n); 749 » » switch (ll->n->esc) {
280 } 750 » » case EscNone:» // not touched by escflood
281 751 » » » if (haspointers(ll->n->type)) // don't bother tagging fo r scalars
282 static void 752 » » » » ll->n->paramfld->note = safetag;
283 eastmt(Node *n) 753 » » case EscHeap:» // touched by escflood, moved to heap
284 { 754 » » case EscScope:» // touched by escflood, value leaves scope
285 » int cl, cr;
286 » NodeList *ll, *lr;
287
288 » if(n == N)
289 » » return;
290
291 » if (n->trecur)
292 » » return;
293 » n->trecur = 1;
294
295 » setlineno(n);
296
297 » if (n->typecheck == 0) {
298 » » dump("eastmt missing typecheck", n);
299 » » fatal("Missing typecheck.");
300 » }
301
302 » switch(n->op) {
303 » default:
304 » » // things i haven't thought of
305 » » dump("eastmt", n);
306 » » // fallthrough
307 » » // things that shouldn't happen here
308 » case OCASE: case OXCASE:
309 » case OFALL:
310 » case OCALL:
311 » » fatal("eastmt %O", n->op);
312 » » break;
313
314 » case ODCL:
315 » case ODCLCONST:
316 » case ODCLTYPE:
317 » case OBREAK:
318 » case OCONTINUE:
319 » case OXFALL:
320 » case OGOTO:
321 » case OLABEL:
322 » case OEMPTY:
323 » case OCLOSE:
324 » case OPRINT:
325 » case OPRINTN:
326 » » // all harmless
327 » » break;
328
329 » case OBLOCK:
330 » » eastmtlist(n->list);
331 » » break;
332
333 » case OFOR:
334 » » eastmtlist(n->ninit);
335 » » if(n->ntest != N) {
336 » » » eastmtlist(n->ntest->ninit);
337 » » » eaexpr(N, n->ntest);
338 » » }
339 » » eastmt(n->nincr);
340 » » eastmtlist(n->nbody);
341 » » break;
342
343 » case ORANGE:» » // for <list> = range <right> { <nbody> }
344 » » switch(n->type->etype) {
345 » » case TARRAY:» // i, v = range sliceorarrayorstring
346 » » case TSTRING:
347 » » » if(n->list->next)
348 » » » » eaexpr(n->list->next->n, n->right);
349 » » » break;» » »·······
350 » » case TMAP:» // k, v = range map
351 » » » eaexpr(n->list->n, n->right);
352 » » » if(n->list->next)
353 » » » » eaexpr(n->list->next->n, n->right);
354 break; 755 break;
355 » » case TCHAN:» // v = range chan 756 » » default:
356 » » » eaexpr(n->list->n, n->right); 757 » » » fatal("messed up escape tagging: %N::%N", curfn, ll->n);
357 » » » break; 758 » » }
358 » » } 759 » }
359 »······· 760
360 » » eastmtlist(n->nbody); 761 » curfn = savefn;
361 » » break; 762 }
362
363 » case OIF:
364 » » eastmtlist(n->ninit);
365 » » eaexpr(N, n->ntest);
366 » » eastmtlist(n->nbody);
367 » » eastmtlist(n->nelse);
368 » » break;
369
370 » case OSELECT:
371 » » eastmtlist(n->ninit);
372 » » for(ll=n->list; ll; ll=ll->next) { // todo what about the test?
373 » » » eastmt(ll->n->left);
374 » » » eastmtlist(ll->n->nbody);
375 » » }
376 » » break;
377
378 » case OSELRECV2:
379 » case OSELRECV:
380 » » eaexpr(n->left, n->right);
381 » » break;
382
383 » case OSWITCH:
384 » » eastmtlist(n->ninit);
385 » » if(n->ntest && n->ntest->op == OTYPESW) {
386 » » » eaexpr(n->ntest->left, n->ntest->right);
387 » » » for(ll=n->list; ll; ll=ll->next)
388 » » » » eastmtlist(ll->n->nbody);
389 » » } else {
390 » » » eaexpr(N, n->ntest);
391 » » » for(ll=n->list; ll; ll=ll->next) { // cases
392 » » » » for (lr=ll->n->list; lr; lr=lr->next)
393 » » » » » eaexpr(N, lr->n);
394 » » » » eastmtlist(ll->n->nbody);
395 » » » }
396 » » }
397 » » break;
398
399 » case OAS:·
400 » case OASOP:» » // v += x etc. never applies to pointers, but l eave it to eaexpr to figure that out.
401 » » eaexpr(n->left, n->right);
402 » » break;
403
404 » » // escape analysis happens after typecheck, so the
405 » » // OAS2xxx have already been substituted.
406 » case OAS2:
407 » » cl = count(n->list);
408 » » cr = count(n->rlist);
409 » » if (cl > 1 && cr == 1) {
410 » » » for(ll=n->list; ll; ll=ll->next)
411 » » » » eaexpr(ll->n, n->rlist->n);
412 » » } else {·
413 » » » if(cl != cr)
414 » » » » fatal("eastmt: bad OAS2: %N", n);
415 » » » for(ll=n->list, lr=n->rlist; ll; ll=ll->next, lr=lr->nex t)
416 » » » » eaexpr(ll->n, lr->n);
417 » » }
418 » » break;
419 » »·······
420 » case OAS2RECV:» » // v, ok = <-ch
421 » case OAS2MAPR:» » // v, ok = m[k]
422 » case OAS2DOTTYPE:» // v, ok = x.(type)
423 » » eaexpr(n->list->n, n->rlist->n->left);
424 » » break;
425
426 » case OAS2MAPW: » // m[k] = x, ok
427 » » eaexpr(n->list->n->left, n->rlist->n);
428 » » break;
429
430 » case ORECV:» » // unary <-ch as statement
431 » » eaexpr(N, n->left);
432 » » break;
433
434 » case OSEND:» » // ch <- x
435 » » eaexpr(n->left, n->right);
436 » » break;
437
438 » case OCOPY:» // second arguments leaks to the first
439 » » eaexpr(n->left, n->right);
440 » » break;
441
442 » case OAPPEND: // note: append also leaks all its args in an expression
443 » » for (ll=n->list->next; ll; ll=ll->next)
444 » » » eaexpr(n->list->n, ll->n);
445 » » break;
446
447 » case OAS2FUNC:» // x,y,z = f()
448 » » // the return values already escape because they're return value s
449 » » // no need to tie them to x, y or z here.
450 » » eaexpr(N, n->rlist->n);
451 » » break;
452 » »·······
453 » case OCALLINTER:
454 » case OCALLFUNC:
455 » case OCALLMETH:
456 » » n->trecur = 0;
457 » » eaexpr(N, n);
458 » » break;
459
460 » case ODEFER:
461 » » eastmt(n->left);
462 » » break;
463
464 » case ORETURN:
465 » » for (ll=n->list; ll; ll=ll->next)
466 » » » eaexpr(&theSink, ll->n);
467 » » break;
468
469 » case OPROC: // n->left is a pseudocall, consider all arguments leaking
470 » » for (ll=n->left->list; ll; ll=ll->next)
471 » » » eaexpr(&theSink, ll->n);
472 » » break;
473
474 » case OPANIC:
475 » » // Argument could leak through recover.
476 » » eaexpr(&theSink, n->left);
477 » » break;
478 » }
479
480 » n->trecur = 0;
481 }
482
483
484
485 #if 0
486
487 » for(tl=tstruct->type; tl; tl=tl->down) {
488 » » t = tl->type;
489 » » if(tl->isddd) {
490 » » » if(isddd) {
491 » » » » if(nl == nil)
492 » » » » » goto notenough;
493 » » » » if(nl->next != nil)
494 » » » » » goto toomany;
495 » » » » n = nl->n;
496 » » » » setlineno(n);
497 » » » » if(n->type != T) {
498 » » » » » nl->n = assignconv(n, t, desc);
499 » » » » » if (call) argused(call, tl, n);
500 » » » » }
501 » » » » goto out;
502 » » » }
503 » » » for(; nl; nl=nl->next) {
504 » » » » n = nl->n;
505 » » » » setlineno(nl->n);
506 » » » » if(n->type != T) {
507 » » » » » nl->n = assignconv(n, t->type, desc);
508 » » » » » if (call) argused(call, tl, n);
509 » » » » }
510 » » » }
511 » » » goto out;
512 » » }·
513
514
515 // mark that value has been used as the argument arg
516 static void argused(Node* func, Type* arg, Node* val) {
517 //» if (arg->sym && arg->sym->pkg == localpkg)
518 //» » arg->note = strlit("used");
519 //» print("Using %N :: %#T = %N\n", func, arg, val);
520 }
521
522
523 /*
524 * type check function definition
525 */
526 static void
527 escanalfunc(Node *n)
528 {
529 //» Type *t, *t1, *rcvr;
530 » Type *t, *rcvr;
531
532 » typecheck(&n->nname, Erv | Easgn);
533 » if((t = n->nname->type) == T)
534 » » return;
535 » n->type = t;
536
537 » print("typecheckfunc %T\n", n->type);
538 » for(t1=getinargx(t)->type; t1; t1=t1->down) {
539 » » if (isptr[t1->type->etype])
540 » » » t1->note = strlit("leaks");
541 » » print("\t%T\n", t1);
542
543 » }
544
545 » rcvr = getthisx(t)->type;
546 » if(rcvr != nil && n->shortname != N && !isblank(n->shortname))
547 » » addmethod(n->shortname->sym, t, 1);
548 }
549
550 #endif
LEFTRIGHT

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