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

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

Issue 4634073: code review 4634073: gc: Escape analysis. (Closed)
Left Patch Set: diff -r 67b160cd5fa4 https://go.googlecode.com/hg/ Created 13 years, 8 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: 5 // The base version before this file existed, active with debug['s']
6 // ======= 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
7 // 12 //
8 // A subset of all AST nodes holds values. These are the 'expression' 13 // Flag -s disables the old codepaths and switches on the code here:
9 // nodes. Nodes have a scope: the function they are defined in or the
10 // global scope. Nodes are visible in their scope and all scopes
11 // contained in it (closures). The scope of a function parameter is
12 // that function.
13 // 14 //
14 // A node ref can be a reference to another node n. 15 // First escfunc, escstmt and escexpr recurse over the ast of each
15 // - References to nodes are created with the & operator (OADDR). 16 // function to dig out flow(dst,src) edges between any
16 // - Go slices, maps, and channels are references to arrays,· 17 // pointer-containing nodes and store them in dst->escflowsrc. For
17 // runtime.Hashmap<K,V> and runtime.Chan<T> (abusing C++ notation) resp. 18 // variables assigned to a variable in an outer scope or used as a
18 // The last two are hidden from the programmer but relevant here. 19 // return value, they store a flow(theSink, src) edge to a fake node
19 // - 'closure variables': any variable from an outer scope has its 20 // 'the Sink'.» For variables referenced in closures, an edge
20 // reference implicitly taken. 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.
21 // 23 //
22 // References can be 'dereferenced' explicitly or implictly: 24 // Then escflood walks the graph starting at theSink and tags all
23 // - the * operator (OIND) explicitly dereferences its operand 25 // variables of it can reach an & node as escaping and all function
24 // - the slice index implicitly dereferences the slice (and accesses 26 // parameters it can reach as leaking.
25 // an element of the underlying array)
26 // - the map index operator implicitly dereferences the map (and selects
27 // an element of it)
28 // - a channel send or receive operation implicitly dereferences the
29 // channel (and selects the head or tail element of its queue).
30 // - access to a 'closure variable' of an outer scope from the inner scope·
31 // happens by implictly dereferencing it.
32 // 27 //
33 // The re-slice operator creates a new reference to the array the 28 // Watch the variables moved to the heap and parameters tagged as
34 // slice is a reference to, the index operator on an array does not 29 // unsafe with -m, more detailed analysis output with -mm
35 // dereference the array, as an array is not a reference. It just
36 // selects a part, like the ODOT does on a struct.
37 // 30 //
38 // An expression node src, irrespective of whether it's a reference
39 // to another node or not, 'flows' to another node dst, flows(dst, src)
40 // if at any point during the execution of the program:
41 // - src is assigned to dst
42 // - src is a struct and one of its fields is assigned to dst
43 // - dst is a struct and src is assigned to one of its fields
44 // - src is an array and one of its elements is assigned to dst
45 // - dst is an array, and src is assigned to one if its elements
46 // - dst is a runtime.Hashmap<K,V> and src is assigned to the key or value pa rt of one of its elements
47 // - src is a runtime.Hashmap<K,V> and the key or value part of one of its el ements is assigned to dst
48 // - dst is a runtime.Chan<T> and src is sent to it (inserted as the head ele ment)
49 // - src is a runtime.Chan<T> and dst is assigned the result of a receive
50 // operation (the tail element) from it
51 // - dst is a closure and src is a variable declared in its scope
52 // - dst is a closure and src is the implicit reference taken
53 // to a variable (not that variable itself!) in an enclosing scope. (this i s
54 // really a special case of the previous one, combined with an assignment)
55 // - WHAT ABOUT lvalue expressions and function expressions? leak to fun?
56 //
57 // Finally, nodes may alias other nodes, which will be defined below, but in
58 // that case src also flows to dst if·
59 // - dst is an alias of src
60 // - src flows to dst', dst' is _not_ an alias of src and dst' is an alias o f dst,·
61 //
62 // (it may be tempting to just say src is an alias of dst or vice versa,
63 // but that introduces spurious flows in cases like this: x=&y0, x=&y1, y0=z)
64 //
65 // This way, 'flow' forms edges in directed graph between nodes, so we can
66 // extend the above definition of flow with it's transitive
67 // closure:·
68 // - there exists a node n such that flows(dst, n) and flows(n, src)
69 //
70 // If we imagine the flow edges as horizontal, we can imagine
71 // referencing as taking a step down, and dereferencing taking a step
72 // up, and we can draw nice pictures to figure out what's going on.
73 // A struct may contain references at different levels in this
74 // picture, so be careful before drawing your whiteboard full of
75 // nonsense.
76
77 // The Alias rule means that for any arrow you draw /to/ an alias, you have
78 // to draw extra ones to the nodes it aliases.
79 //
80 // Let's call the downstream of src the set of all nodes dst such
81 // that dst==src or flows(dst, src). and vice versa the upstream of
82 // dst as the set of all nodes src such that dst==src or flows(dst, src).
83 //
84 // Now we can precisely define:
85 // A node img is an alias of a node org, alias(img, org), if
86 // - img is the dereference of a node that is downstream to a reference of o rg
87 //
88 // It looks circular, but it is just recursion: aliassing at one
89 // level happens through the flow of references a level below.
90 //
91 // TODO: consider function this,in and out args in detail. all calls
92 // end up as edges to the same in parameter nodes and from the same
93 // out parameter nodes. for imported functions we dont have the full
94 // graph, just if parameters are safe or not, and for exported
95 // functions pretend the outparams flow to theSink. one interesting
96 // extra case: if we mark the return value of constructors and the
97 // like as 'only flows from here' we can tell the gc its safe to delete
98 // them if the retval doesnt leave the calling function.
99 //
100 // For most values all this is of passing interest and theoretical
101 // navel gazing, but any node src that is aliased to a node outside
102 // of src's scope must be moved to the heap, which is the whole point
103 // of this excercise. (*note to self: i've muddled lexical scopes
104 // and activations here, untangle)
105 //
106 // This can happen in 4 ways, all starting with a node src and a reference ref to it:
107 // - ref is assigned to a global variable
108 // - ref is returned from a function or is one of its output parameters
109 // - ref is passed as a value to a function f as a parameter p (including the ' this' parameter) ,·
110 // and the downstream of p includes nodes outside of the scope f.
111 // - ref is passed as a the function or a value to the function called in a go (OPROC) statement
112 //
113 // (we can actually see the last case as a special case of the 3rd:
114 // go foo(a,b,c) is really runtime.closure(foo, closurerefs, a,b,c)
115 // and all of runtime.Closure's parameters are unsafe)
116 //
117 // (maybe for unexported data we could do intra-package analysis and
118 // eliminate the cases where the ref never gets dereferenced, but why
119 // bother.)
120 //
121 // Since the implicit references of closure variables, by the
122 // definition above, flow to the closure node this covers the cases
123 // where the closure gets executed only in its scope or gets returned
124 // or exectuted in a go (OPROC) statement.
125 //
126 //
127 // Practice
128 // ========
129 //
130 // First we recurse over the ast of each function to dig out the
131 // flow(dst,src) edges and store them in the dst's asrc field.
132 // For the 4 cases mentioned above we store a flow(theSink, src) edge
133 // to a fake node 'the Sink'.
134 //
135 // Then we have to complete the 'aliases' edges by following the upstream flows
136 // from all dereferences to their origins.·
137 //
138 // Then...
139 //
140 // The base version before this file existed, active with debug['s'] == 0,
141 // assumes any node that has a reference to it created at some point, may
142 // flow to the global scope except
143 // - if its address is dereferenced immediately with only CONVNOPs in between
144 // - if it is for a closure variable and the closure executed at the place it' s defined
145 //
146 // debug['s'] == 1 analyzes the flow of all variables one level of referencing deep
147 // and assumes any assignment of a reference type to a deeper pointer is a flow to the sink.
148 // debug['s'] == 2 does full alias analysis.
149 //
150 // Watch the variables moved to the heap and parameters tagged as unsafe with - m.
151 //
152 31
153 #include "go.h" 32 #include "go.h"
154 33
155 static void flows(Node* dst, Node* src); 34 static void escfunc(Node *func);
156 static void eastmtlist(Node* dst, NodeList *l); 35 static void escstmtlist(NodeList *stmts);
157 static void eastmt(Node* dst, Node *stmt); 36 static void escstmt(Node *stmt);
158 static void eaexpr(Node *dst, Node *expr); 37 static void escexpr(Node *dst, Node *expr);
159 static void eawalk(Node *sink); 38 static void escexprcall(Node *dst, Node *callexpr);
160 static void resolvealiases(); 39 static void escflows(Node* dst, Node* src);
40 static void escflood(Node *dst);
41 static void escwalk(int level, Node *dst, Node *src);
42 static void esctag(Node *func);
161 43
162 // Fake node that all 44 // Fake node that all
163 // - global variables (including imported ones), 45 // - return values and output variables
164 // - return values of exported functions and
165 // - parameters on imported functions not marked 'safe' 46 // - parameters on imported functions not marked 'safe'
47 // - assignments to global variables
166 // flow to. 48 // flow to.
167 static Node theSink; 49 static Node» theSink;
168 50
169 // All functions that are not in this package that eaexpr bumped into. 51 static NodeList* dsts;» » // all dst nodes
170 static NodeList* importfunc; 52 static int» loopdepth;» // for detecting nested loop scopes
171 53 static int» pdepth;»» // for debug printing in recursions.
172 // Table indexed by derefdepth, of lists of derefererence nodes. 54 static int» floodgen;» // loop prevention in flood/walk
173 static NodeList* dereftab[256]; 55 static Strlit*» safetag;» // gets slapped on safe parameters' field types for export
56 static int» dstcount, edgecount;» // diagnostic
174 57
175 void 58 void
176 escapes() 59 escapes(void)
177 { 60 {
178 » NodeList *l, *ll; 61 » NodeList *l;
179 62
180 » // All extern variables leak to theSink 63 » theSink.op = ONAME;
181 » for(l=externdcl; l; l=l->next) 64 » theSink.class = PEXTERN;
182 » » if (l->n->op == ONAME) 65 » theSink.sym = lookup(".sink");
183 » » » flows(&theSink, l->n); 66 » theSink.escloopdepth = -1;
67
68 » safetag = strlit("noescape");
184 69
185 // flow-analyze top level functions 70 // flow-analyze top level functions
186 » for(l=xtop; l; l=l->next) { 71 » for(l=xtop; l; l=l->next)
187 » » if (l->n->op == ODCLFUNC) { 72 » » if(l->n->op == ODCLFUNC || l->n->op == OCLOSURE)
188 » » » curfn = l->n; 73 » » » escfunc(l->n);
189 » » » // all out parameters flow to the sink 74
190 » » » for (ll=l->n->dcl; ll; ll=ll->next) 75 » // print("escapes: %d dsts, %d edges\n", dstcount, edgecount);
191 » » » » if (ll->n->op == ONAME && ll->n->class == PPARAM OUT) 76
192 » » » » » flows(&theSink, ll->n); 77 » // visit the updstream of each dst, mark address nodes with
193 » » » eastmtlist(N, l->n->nbody); 78 » // addrescapes, mark parameters unsafe
194 » » » curfn = nil; 79 » for (l = dsts; l; l=l->next)
195 » » } 80 » » escflood(l->n);
196 » } 81
197 82 » // for all top level functions, tag the typenodes corresponding to the p aram nodes
198 » // connect the non-safe parameters of all imported functions 83 » for(l=xtop; l; l=l->next)
199 » // we bumped into to the sink. 84 » » if(l->n->op == ODCLFUNC)
200 » for (l=importfunc; l; l=l->next) 85 » » » esctag(l->n);
201 » » print("imported function: %N\n", l->n); 86 }
202 87
203 » if (debug['s'] > 1) 88 static void
204 » » resolvealiases(); 89 escfunc(Node *func)
205 90 {
206 » // find all nodes that reach the sink 91 » Node *savefn, *n;
207 » // marking address nodes with addrescapes 92 » NodeList *ll;
208 » // mark parameters unsafe 93 » int saveld;
209 » eawalk(&theSink); 94
210 } 95 » saveld = loopdepth;
211 96 » loopdepth = 1;
212 static void 97 » savefn = curfn;
213 eastmtlist(Node* dst, NodeList* l) 98 » curfn = func;
214 { 99
215 » for (; l; l=l->next) 100 » for(ll=curfn->dcl; ll; ll=ll->next) {
216 » » eastmt(dst, l->n); 101 » » if(ll->n->op != ONAME)
217 } 102 » » » continue;
218 103 » » switch (ll->n->class) {
219 static void 104 » » case PPARAMOUT:
220 eastmt(Node* fun, Node *stmt) 105 » » » // output parameters flow to the sink
221 { 106 » » » escflows(&theSink, ll->n);
222 » int cl, cr; 107 » » » ll->n->escloopdepth = loopdepth;
108 » » » break;
109 » » case PPARAM:
110 » » » ll->n->esc = EscNone;» // prime for escflood later
111 » » » ll->n->escloopdepth = loopdepth;
112 » » » break;
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);
126 » }
127
128 » escstmtlist(curfn->nbody);
129 » curfn = savefn;
130 » loopdepth = saveld;
131 }
132
133 static void
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;
223 NodeList *ll, *lr; 144 NodeList *ll, *lr;
145 Node *dst;
224 146
225 if(stmt == N) 147 if(stmt == N)
226 return; 148 return;
227 149
228 » if (stmt->trecur) 150 » lno = setlineno(stmt);
229 » » return; 151
230 » stmt->trecur = 1; 152 » if(stmt->typecheck == 0 && stmt->op != ODCL) {» // TODO something with OAS2
231 153 » » dump("escstmt missing typecheck", stmt);
232 » setlineno(stmt); 154 » » fatal("missing typecheck.");
233 155 » }
234 » if (stmt->typecheck == 0 && stmt->op != ODCL) {» // todo, something with OAS2 156
235 » » dump("eastmt missing typecheck", stmt); 157 » // Common to almost all statements, and nil if n/a.
236 » » fatal("Missing typecheck."); 158 » escstmtlist(stmt->ninit);
237 » } 159
238 160 » if(debug['m'] > 1)
239 » if (fun && fun != &theSink && fun->op != OCLOSURE) 161 » » print("%L:[%d] %#S statement: %#N\n", lineno, loopdepth,
240 » » fatal("eastmt fun is not a closure: %N", fun); 162 » » (curfn && curfn->nname) ? curfn->nname->sym : S, stmt);
241
242 » if (debug['m'] > 1)
243 » » print("%L %N eastmt: %N\n", lineno, fun, stmt);
244
245
246 » eastmtlist(fun, stmt->ninit);
247 163
248 switch(stmt->op) { 164 switch(stmt->op) {
249 default:
250 // can get here with typecheck errors
251 dump("eastmt", stmt);
252 fatal("eastmt %O", stmt->op);
253 break;
254
255 case ODCL: 165 case ODCL:
256 case ODCLFIELD: 166 case ODCLFIELD:
257 » » // a declaration ties the node to the current function 167 » » // a declaration ties the node to the current
258 » » eaexpr(fun, stmt->left); 168 » » // function, but we already have that edge in
259 » » break; 169 » » // curfn->dcl and will follow it explicitly in
260 170 » » // escflood to avoid storing redundant information
261 » case ODCLCONST: 171 » » // What does have to happen here is note if the name
262 » case ODCLTYPE: 172 » » // is declared inside a looping scope.
263 » case OBREAK: 173 » » stmt->left->escloopdepth = loopdepth;
264 » case OCONTINUE: 174 » » break;
265 » case OXFALL: 175
266 » case OGOTO: 176 » case OLABEL: // TODO: new loop/scope only if there are backjumps to it.
267 » case OLABEL: 177 » » loopdepth++;
268 » case OEMPTY: 178 » » break;
269 » case ORECOVER: 179
270 » » // all harmless 180 » case OBLOCK:
271 » » break; 181 » » escstmtlist(stmt->list);
272
273 » case OBLOCK:
274 » » eastmtlist(fun, stmt->list);
275 break; 182 break;
276 183
277 case OFOR: 184 case OFOR:
278 if(stmt->ntest != N) { 185 if(stmt->ntest != N) {
279 » » » eastmtlist(fun, stmt->ntest->ninit); 186 » » » escstmtlist(stmt->ntest->ninit);
280 » » » eaexpr(N, stmt->ntest); 187 » » » escexpr(N, stmt->ntest);
281 » » } 188 » » }
282 » » eastmt(N, stmt->nincr); 189 » » escstmt(stmt->nincr);
283 » » eastmtlist(fun, stmt->nbody); 190 » » loopdepth++;
191 » » escstmtlist(stmt->nbody);
192 » » loopdepth--;
284 break; 193 break;
285 194
286 case ORANGE: // for <list> = range <right> { <nbody> } 195 case ORANGE: // for <list> = range <right> { <nbody> }
287 switch(stmt->type->etype) { 196 switch(stmt->type->etype) {
288 » » case TARRAY:» // i, v = range sliceorarrayorstring 197 » » case TSTRING:» // never flows
289 » » case TSTRING: 198 » » » escexpr(stmt->list->n, N);
290 if(stmt->list->next) 199 if(stmt->list->next)
291 » » » » eaexpr(stmt->list->next->n, stmt->right); 200 » » » » escexpr(stmt->list->next->n, N);
292 » » » break;» » »······· 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;
293 case TMAP: // k [, v] = range map 208 case TMAP: // k [, v] = range map
294 » » » eaexpr(stmt->list->n, stmt->right); 209 » » » escexpr(stmt->list->n, stmt->right);
295 if(stmt->list->next) 210 if(stmt->list->next)
296 » » » » eaexpr(stmt->list->next->n, stmt->right); 211 » » » » escexpr(stmt->list->next->n, stmt->right);
297 break; 212 break;
298 case TCHAN: // v = range chan 213 case TCHAN: // v = range chan
299 » » » eaexpr(stmt->list->n, stmt->right); 214 » » » escexpr(stmt->list->n, stmt->right);
300 break; 215 break;
301 } 216 }
302 » » eastmtlist(fun, stmt->nbody); 217 » » loopdepth++;
218 » » escstmtlist(stmt->nbody);
219 » » loopdepth--;
303 break; 220 break;
304 221
305 case OIF: 222 case OIF:
306 » » eaexpr(N, stmt->ntest); 223 » » escexpr(N, stmt->ntest);
307 » » eastmtlist(fun, stmt->nbody); 224 » » escstmtlist(stmt->nbody);
308 » » eastmtlist(fun, stmt->nelse); 225 » » escstmtlist(stmt->nelse);
309 break; 226 break;
310 227
311 case OSELECT: 228 case OSELECT:
312 for(ll=stmt->list; ll; ll=ll->next) { // cases 229 for(ll=stmt->list; ll; ll=ll->next) { // cases
313 » » » eastmt(fun, ll->n->left); 230 » » » escstmt(ll->n->left);
314 » » » eastmtlist(fun, ll->n->nbody); 231 » » » escstmtlist(ll->n->nbody);
315 » » } 232 » » }
316 » » break; 233 » » break;
317 234
318 » case OSELRECV2: 235 » case OSELRECV2:» // v, ok := <-ch ntest:ok
319 » » eaexpr(N, stmt->ntest); 236 » » escexpr(N, stmt->ntest);
320 // fallthrough 237 // fallthrough
321 » case OSELRECV: 238 » case OSELRECV:» // v := <-ch» left: v right->op = ORECV
322 » » eaexpr(N, stmt->left); 239 » » escexpr(N, stmt->left);
323 » » eaexpr(stmt->left, stmt->right); 240 » » escexpr(stmt->left, stmt->right);
324 break; 241 break;
325 242
326 case OSWITCH: 243 case OSWITCH:
327 if(stmt->ntest && stmt->ntest->op == OTYPESW) { 244 if(stmt->ntest && stmt->ntest->op == OTYPESW) {
328 » » » eaexpr(N, stmt->ntest->left); 245 » » » for(ll=stmt->list; ll; ll=ll->next) { // cases
329 » » » eaexpr(stmt->ntest->left, stmt->ntest->right); 246 » » » » // ntest->right is the argument of the .(type),
330 » » » for(ll=stmt->list; ll; ll=ll->next) 247 » » » » // ll->n->nname is the variable per case
331 » » » » eastmtlist(fun, ll->n->nbody); 248 » » » » escexpr(ll->n->nname, stmt->ntest->right);
249 » » » » escstmtlist(ll->n->nbody);
250 » » » }
332 } else { 251 } else {
333 » » » eaexpr(fun, stmt->ntest); 252 » » » escexpr(N, stmt->ntest);
334 for(ll=stmt->list; ll; ll=ll->next) { // cases 253 for(ll=stmt->list; ll; ll=ll->next) { // cases
335 » » » » for (lr=ll->n->list; lr; lr=lr->next) { 254 » » » » for(lr=ll->n->list; lr; lr=lr->next)
336 » » » » » eaexpr(fun, lr->n); 255 » » » » » escexpr(N, lr->n);
337 » » » » } 256 » » » » escstmtlist(ll->n->nbody);
338 » » » » eastmtlist(fun, ll->n->nbody);
339 } 257 }
340 } 258 }
341 break; 259 break;
342 260
343 » case OAS: 261 » case OAS:
344 case OASOP: 262 case OASOP:
345 //» » eaexpr(N, stmt->left); 263 » » escexpr(stmt->left, stmt->right);
346 » » eaexpr(stmt->left, stmt->right); 264 » » break;
347 » » break; 265
348 266 » » // escape analysis happens after typecheck, so the
349 » // escape analysis happens after typecheck, so the 267 » » // OAS2xxx have already been substituted.
350 » // OAS2xxx have already been substituted.
351 case OAS2: // x,y = a,b 268 case OAS2: // x,y = a,b
352 cl = count(stmt->list); 269 cl = count(stmt->list);
353 cr = count(stmt->rlist); 270 cr = count(stmt->rlist);
354 » » if (cl > 1 && cr == 1) { 271 » » if(cl > 1 && cr == 1) {
355 » » » for(ll=stmt->list; ll; ll=ll->next) { 272 » » » for(ll=stmt->list; ll; ll=ll->next)
356 //» » » » eaexpr(N, ll->n); 273 » » » » escexpr(ll->n, stmt->rlist->n);
357 » » » » eaexpr(ll->n, stmt->rlist->n); 274 » » } else {
358 » » » }
359 » » } else {
360 if(cl != cr) 275 if(cl != cr)
361 » » » » fatal("eastmt: bad OAS2: %N", stmt); 276 » » » » fatal("escstmt: bad OAS2: %N", stmt);
362 » » » for(ll=stmt->list, lr=stmt->rlist; ll; ll=ll->next, lr=l r->next) { 277 » » » for(ll=stmt->list, lr=stmt->rlist; ll; ll=ll->next, lr=l r->next)
363 //» » » » eaexpr(N, ll->n); 278 » » » » escexpr(ll->n, lr->n);
364 » » » » eaexpr(ll->n, lr->n); 279 » » }
365 » » » } 280 » » break;
366 » » } 281
367 » » break;
368 » »
369 case OAS2RECV: // v, ok = <-ch 282 case OAS2RECV: // v, ok = <-ch
370 case OAS2MAPR: // v, ok = m[k] 283 case OAS2MAPR: // v, ok = m[k]
371 case OAS2DOTTYPE: // v, ok = x.(type) 284 case OAS2DOTTYPE: // v, ok = x.(type)
372 » case OAS2MAPW:» » // m[k] = x, ok 285 » » escexpr(stmt->list->n, stmt->rlist->n);
373 //» » eaexpr(N, stmt->list->n); 286 » » escexpr(stmt->list->next->n, N);
374 » » eaexpr(stmt->list->n, stmt->rlist->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);
375 break; 292 break;
376 293
377 case ORECV: // unary <-ch as statement 294 case ORECV: // unary <-ch as statement
378 » » eaexpr(N, stmt->left); 295 » » escexpr(N, stmt->left);
379 break; 296 break;
380 297
381 case OSEND: // ch <- x 298 case OSEND: // ch <- x
382 //» » eaexpr(N, stmt->left); 299 » » escexpr(&theSink, stmt->right);» // for now. TODO escexpr(stmt-> left, stmt->right);
383 » » eaexpr(stmt->left, stmt->right); 300 » » break;
384 » » break; 301
385 302 » case OCOPY:» // todo: treat as *dst=*src instead of as dst=src
386 » case OCOPY:» // second arguments leaks to the first 303 » » escexpr(stmt->left, stmt->right);
387 //» » eaexpr(N, stmt->left); 304 » » break;
388 » » eaexpr(stmt->left, stmt->right);
389 » » break;
390
391 305
392 case OAS2FUNC: // x,y,z = f() 306 case OAS2FUNC: // x,y,z = f()
393 //» » for (ll = stmt->list; ll; ll=ll->next) 307 » » for(ll = stmt->list; ll; ll=ll->next)
394 //» » » eaexpr(N, ll->n); 308 » » » escexpr(ll->n, N);
395 » » eaexpr(N, stmt->rlist->n); 309 » » escexpr(N, stmt->rlist->n);
396 break; 310 break;
397 311
398 case OCALLINTER: 312 case OCALLINTER:
399 case OCALLFUNC: 313 case OCALLFUNC:
400 case OCALLMETH: 314 case OCALLMETH:
401 » » stmt->trecur = 0; 315 » » escexpr(N, stmt);
402 » » eaexpr(N, stmt); 316 » » break;
403 » » break; 317
404 318 » case OPROC:
405 case ODEFER: 319 case ODEFER:
406 » » eastmt(fun, stmt->left); 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);
407 break; 331 break;
408 332
409 case ORETURN: 333 case ORETURN:
410 » » for (ll=stmt->list; ll; ll=ll->next) 334 » » for(ll=stmt->list; ll; ll=ll->next)
411 » » » eaexpr(&theSink, ll->n); 335 » » » escexpr(&theSink, ll->n);
412 » » break;
413
414 » case OPROC:
415 » » // stmt->left is a (pseud)ocall,
416 » » // stmt->left->left is the function being called.
417 » » // everything leaks.
418 » » eaexpr(&theSink, stmt->left->left);
419 » » for (ll=stmt->left->list; ll; ll=ll->next)
420 » » » eaexpr(&theSink, ll->n);
421 break; 336 break;
422 337
423 case OCLOSE: 338 case OCLOSE:
424 case OPRINT: 339 case OPRINT:
425 case OPRINTN: 340 case OPRINTN:
426 » » eaexpr(N, stmt->left); 341 » » escexpr(N, stmt->left);
342 » » for(ll=stmt->list; ll; ll=ll->next)
343 » » » escexpr(N, ll->n);
427 break; 344 break;
428 345
429 case OPANIC: 346 case OPANIC:
430 // Argument could leak through recover. 347 // Argument could leak through recover.
431 » » eaexpr(&theSink, stmt->left); 348 » » escexpr(&theSink, stmt->left);
432 » » break; 349 » » break;
433 » } 350 » }
434 351
435 » stmt->trecur = 0; 352 » lineno = lno;
436 } 353 }
437 354
438 static void 355 // Assert that expr somehow gets assigned to dst, if non nil. for
439 eaexpr(Node *dst, Node *expr) 356 // dst==nil, any name node expr still must be marked as being
440 { 357 // evaluated in curfn.» For expr==nil, dst must still be examined for
441 » NodeList *ll, *lr; 358 // evaluations inside it (e.g *f(x) = y)
442 » Node* n; 359 static void
443 360 escexpr(Node *dst, Node *expr)
444 » if(expr == N || expr == dst) 361 {
445 » » return; 362 » int lno;
446 363 » NodeList *ll;
447 » setlineno(expr); 364
448 365 » if(isblank(dst)) dst = N;
449 » if (expr->typecheck == 0 && expr->op != ONONAME) { // TODO where do these come from 366
450 » » dump("eaexpr missing typecheck", expr); 367 » // the lhs of an assignment needs recursive analysis too
451 » » fatal("Missing typecheck."); 368 » // these are the only interesting cases
452 » } 369 » // todo:check channel case
453 370 » if(dst) {
454 » // If dst is a part, make expr go to the whole 371 » » setlineno(dst);
455 » if (dst) { 372
456 switch(dst->op) { 373 switch(dst->op) {
457 case OINDEX: 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
458 if(!isfixedarray(dst->left->type)) 380 if(!isfixedarray(dst->left->type))
459 » » » » break; 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
460 // fallthrough 390 // fallthrough
391 case OIND:
461 case ODOTPTR: 392 case ODOTPTR:
462 » » case OINDEXMAP: 393 » » case OSLICEARR:» // ->left is the OADDR of the array
463 » » » eaexpr(dst->left, expr); 394 » » doref:
395 » » » escexpr(N, dst->left);
396 » » » // assignment to dereferences: for now we lose track
397 » » » escexpr(&theSink, expr);
464 return; 398 return;
465 } 399 }
466 » } 400
467 401 » }
468 » if (debug['m'] > 1) 402
469 » » print("%L %S:: ... examining %N <= %N\n", lineno, curfn ? curfn- >nname->sym : S, dst, expr); 403 » if(expr == N || expr->op == ONONAME || expr->op == OXXX)
470
471 » if (expr->trecur)
472 return; 404 return;
473 » expr->trecur = 1; 405
406 » if(expr->typecheck == 0 && expr->op != OKEY) {
407 » » dump("escexpr missing typecheck", expr);
408 » » fatal("Missing typecheck.");
409 » }
410
411 » lno = setlineno(expr);
412 » pdepth++;
413
414 » if(debug['m'] > 1)
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);
418
474 419
475 switch(expr->op) { 420 switch(expr->op) {
476 » default: 421 » case OADDR:» // dst = &x
477 » » // if we get here, typecheck has probably already complained, co mment out before submitting 422 » case OIND:» // dst = *x
478 » » dump("eaexpr", expr); 423 » case ODOTPTR:» // dst = (*x).f
479 » » fatal("eaexpr %O", expr->op); 424 » » // restart the recursion at x to figure out where it came from
480 » » break; 425 » » escexpr(expr->left, expr->left);
481 426 » » // fallthrough
482 » case OADDR:
483 » » flows(dst, expr);» // dst = &x
484 » » // to the right of & there can be only·
485 » » // - an array-,map- or struct- literal,
486 » » // - a name (not of a func),·
487 » » // - an index or member selection operation &x[y] (OINDEX),» &x.y (ODOT)
488 » » // - or an indirection: &*x (OIND) or &(*x).y (ODOTPTR, wri tten as &x.y , but x of pointer type)
489 » » // in the last two cases, the flow is actually not interrupted,
490 » » if (expr->left->op == OIND || expr->left->op == ODOTPTR)
491 » » » eaexpr(dst, expr->left);····
492 » » else
493 » » » eaexpr(N, expr->left);»···
494 » » break;
495
496 » case OIND:
497 » » // even if dst = * .... &x, that doesnt mean &x flows to dst, ju st x
498 » » // if this OIND is to the right of an OADDR, things are differen t, but that's handled above.
499 » » eaexpr(dst, expr->left);
500 » » break;
501
502 case ONAME: 427 case ONAME:
503 case ONONAME: // not typechecked yet?
504 case OPARAM: 428 case OPARAM:
505 » » flows(dst, expr); 429 » » // loopdepth was set in the defining statement or function heade r
430 » » escflows(dst, expr);
506 break; 431 break;
507 432
508 case OARRAYLIT: 433 case OARRAYLIT:
509 case OSTRUCTLIT: 434 case OSTRUCTLIT:
510 » » flows(dst, expr); 435 » case OMAPLIT:
511 » » // ll->n is key, left is fieldname or constant index 436 » » expr->escloopdepth = loopdepth;
512 » » for (ll=expr->list; ll; ll=ll->next) 437 » » escflows(dst, expr);
513 » » » eaexpr(expr, ll->n->right); 438 » » for(ll=expr->list; ll; ll=ll->next) {
514 » » break; 439 » » » escexpr(expr, ll->n->left);
515 440 » » » escexpr(expr, ll->n->right);
516 » case OMAPLIT:» » »······· 441 » » }
517 » » flows(dst, expr); 442 » » break;
518 » » for (ll=expr->list; ll; ll=ll->next) { 443
519 » » » eaexpr(expr, ll->n->left); 444 » case OMAKECHAN:
520 » » » eaexpr(expr, ll->n->right); 445 » case OMAKEMAP:
521 » » } 446 » case OMAKESLICE:
522 » » break; 447 » case ONEW:
523 448 » » expr->curfn = curfn; // should have been done in parse, but pat ch it up here.
524 » case OCOMPLIT: 449 » » expr->escloopdepth = loopdepth;
525 » case OLITERAL: 450 » » escflows(dst, expr);
526 » case ORECOVER: 451 » » // first arg is type, all others need checking
527 » » break; 452 » » for(ll=expr->list->next; ll; ll=ll->next)
528 453 » » » escexpr(N, ll->n);
529 » // harmless unary 454 » » break;
530 » case ONOT:» case OCOM: 455
531 » case OCAP:» case OLEN: 456 » case OCLOSURE:
532 » case OREAL:» case OIMAG: 457 » » expr->curfn = curfn; // should have been done in parse, but pat ch it up here.
533 » case OARRAYBYTESTR: 458 » » expr->escloopdepth = loopdepth;
534 » case OARRAYRUNESTR: 459 » » escflows(dst, expr);
535 » case OSTRARRAYBYTE: 460 » » escfunc(expr);
536 » case OSTRARRAYRUNE: 461 » » break;
537 » case ORUNESTR: 462
538 » » eaexpr(N, expr->left); 463 » // end of the leaf cases. no calls to escflows() in the cases below.
539 » » break; 464
540 465
541 » // harmless binary 466 » case OCONV:» // unaries that pass the value through
542 » case OADD:» case OAND:» case OANDAND:
543 » case OANDNOT:» case ODIV:» case OEQ:
544 » case OGE:» case OGT:» case OLE:
545 » case OLT:» case OLSH:» case ORSH:
546 » case OMOD:» case OMUL:» case ONE:
547 » case OOR:» case OOROR:» case OSUB:
548 » case OXOR:» case OADDSTR:» case OASOP:
549 » case OCMPIFACE:»case OCMPSTR:» case OCOMPLEX:
550 » case OPLUS: case OMINUS:
551 » » eaexpr(N, expr->left);
552 » » eaexpr(N, expr->right);
553 » » break;
554
555 » // unaries that pass the value through
556 » case OCONV:
557 case OCONVIFACE: 467 case OCONVIFACE:
558 case OCONVNOP: 468 case OCONVNOP:
559 case ODOTTYPE: 469 case ODOTTYPE:
560 case ODOTTYPE2: 470 case ODOTTYPE2:
561 » case ORECV: // leaks the whole channel 471 » case ORECV:» // leaks the whole channel
562 » » eaexpr(dst, expr->left); 472 » case ODOTMETH:» // expr->right is just the field or method name
473 » case ODOTINTER:
474 » case ODOT:
475 » » escexpr(dst, expr->left);
563 break; 476 break;
564 477
565 case OCOPY: 478 case OCOPY:
566 // left leaks to right, but the return value is harmless 479 // left leaks to right, but the return value is harmless
567 » » eaexpr(expr->left, expr->right); 480 » » // TODO: treat as *dst = *src, rather than as dst = src
481 » » escexpr(expr->left, expr->right);
568 break; 482 break;
569 483
570 case OAPPEND: 484 case OAPPEND:
571 » » eaexpr(dst, expr->list->n); 485 » » // See TODO for OCOPY
572 » » for (ll=expr->list->next; ll; ll=ll->next) 486 » » escexpr(dst, expr->list->n);
573 » » » eaexpr(expr->list->n, ll->n); 487 » » for(ll=expr->list->next; ll; ll=ll->next)
574 » » break; 488 » » » escexpr(expr->list->n, ll->n);
575 489 » » break;
576 » // todo: this and arguments don't leak to the sink but to expr->left->dc l 490
577 » // the return values don't leak to theSink but to dst
578 case OCALLMETH: 491 case OCALLMETH:
492 case OCALLFUNC:
579 case OCALLINTER: 493 case OCALLINTER:
494 // Moved to separate function to isolate the hair.
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) {
580 case OCALLFUNC: 536 case OCALLFUNC:
581 » » eaexpr(N, expr->left);» // the function itself LEAKS TO THE CURR ENT CLOSURE 537 » » fn = expr->left;
582 » » if (expr->left->ntype && expr->left->ntype->list) { 538 » » escexpr(N, fn);
583 » » » // lr->n is the dclfield, ->left is the ONAME param node 539 » » fntype = fn->type;
584 » » » for (ll=expr->list, lr=expr->left->ntype->list; ll; ll=l l->next) { 540 » » break;
585 » » » » eaexpr(lr->n->left, ll->n); 541
586 » » » » if (!lr->n->isddd) lr=lr->next; 542 » case OCALLMETH:
587 » » » } 543 » » fn = expr->left->right;» // ODOTxx name
588 » » } else { 544 » » fn = fn->sym->def;» // resolve to definition if we have it
589 » » » // No type with parameter list for this 545 » » if(fn)
590 » » » // function (e.g. the result of an ODOTINTER 546 » » » fntype = fn->type;
591 » » » // or ODOTMETH or a function variable) 547 » » else
592 » » » for (ll=expr->list; ll; ll=ll->next) 548 » » » fntype = expr->left->type;
593 » » » » eaexpr(&theSink , ll->n); 549 » » break;
594 » » } 550
595 » » break; 551 » case OCALLINTER:
596 552 » » break;
597 » case ODOTMETH: 553
598 » case ODOTINTER: 554 » default:
599 » case ODOT: 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:
600 case ODOTPTR: 724 case ODOTPTR:
601 // expr->right is just an (untypechecked) ONAME
602 eaexpr(dst, expr->left);
603 break;
604
605 case OSLICE: // the big thing flows, the keys just need checking
606 case OSLICEARR:
607 case OSLICESTR:
608 eaexpr(dst, expr->left);
609 eaexpr(N, expr->right->left); // the OKEY
610 eaexpr(N, expr->right->right);
611 break;
612
613 case OINDEX:···
614 case OINDEXMAP: 725 case OINDEXMAP:
615 » » eaexpr(dst, expr->left); 726 » case OIND:
616 » » eaexpr(N, expr->right); 727 » » escwalk(level+1, dst, src->left);
617 » » break; 728 » }
618 729
619 » case OMAKECHAN: 730 » for (ll=src->escflowsrc; ll; ll=ll->next)
620 » case OMAKEMAP: 731 » » escwalk(level, dst, ll->n);
621 » case OMAKESLICE: 732
622 » case ONEW:» » // first arg is type, all others need checking 733 » pdepth--;
623 » » for (ll=expr->list->next; ll; ll=ll->next) 734 }
624 » » » eaexpr(N, ll->n); 735
625 » » flows(dst, expr); 736 static void
626 » » break; 737 esctag(Node *func)
627 738 {
628 » case OCLOSURE: 739 » Node *savefn;
629 » » // the closure flows to the lhs of the assignment, or to theSink if we're in a return. 740 » NodeList *ll;
630 » » flows(dst, expr); 741
631 » » // out parameters flow to the sink 742 » savefn = curfn;
632 » » for (ll=expr->dcl; ll; ll=ll->next) 743 » curfn = func;
633 » » » if (ll->n->op == ONAME && ll->n->class == PPARAMOUT) 744
634 » » » » flows(&theSink, ll->n); 745 » for(ll=curfn->dcl; ll; ll=ll->next) {
635 » » // the cvars will have their address taken later by walk. 746 » » if(ll->n->op != ONAME || ll->n->class != PPARAM)
636 » » // introduce a fake OADDR node so eawalk can pick that up. 747 » » » continue;
637 » » for (ll=expr->cvars; ll; ll=ll->next) { 748
638 » » » if (ll->n->op == OXXX) // see dcl.c:398, todo: figure o ut exactly what is going on here 749 » » switch (ll->n->esc) {
639 » » » » continue; 750 » » case EscNone:» // not touched by escflood
640 » » » n = nod(OADDR, ll->n, N); 751 » » » if (haspointers(ll->n->type)) // don't bother tagging fo r scalars
641 » » » typecheck(&n, Erv); 752 » » » » ll->n->paramfld->note = safetag;
642 » » » flows(expr, n); 753 » » case EscHeap:» // touched by escflood, moved to heap
643 » » } 754 » » case EscScope:» // touched by escflood, value leaves scope
644 » » eastmtlist(expr, expr->nbody);» // the declarations in the body flow to the closure
645 » » break;
646 » }
647
648 » expr->trecur = 0;
649 }
650
651 // 0 is scalar, 1 is *scalar, 2 is **scalar etc.
652 // slices maps and channels count as a reference to their contents
653 // arrays count as their contents and structs as the max of any of its fields.
654 static int
655 derefdepth(Type* t)
656 {
657 » int d1, d2;
658 » Type *t1;
659
660 » if (t == T)
661 » » return 0;
662
663 » if (t->recur == 0xff) // being checked
664 » » return 1;
665
666 » // what we store is 1+ what we return
667 » // t->recur 0 means not checked. 1 is scalar, 2 is *scalar, 3 is **scal ar etc.
668 » if (t->recur == 0) {
669 » » t->recur = 0xff; // being checked
670
671 » » switch(t->etype) {
672 » » case TPTR32:
673 » » case TPTR64:
674 » » » t->recur = derefdepth(t->type);
675 » » » break;
676 » » case TINTER:
677 » » » t->recur = 1;
678 » » » break;
679 » » case TARRAY:
680 » » » t->recur = derefdepth(t->type);
681 » » » if(!isfixedarray(t))
682 » » » » t->recur++;
683 » » » break;
684 » » case TCHAN:
685 » » » t->recur = 1 + derefdepth(t->type);
686 » » » break;
687 » » case TMAP:
688 » » » d1 = derefdepth(t->type);
689 » » » d2 = derefdepth(t->down);
690 » » » if (d1 > d2)
691 » » » » t->recur = 1 + d1;
692 » » » else
693 » » » » t->recur = 1 + d2;
694 » » » break;
695 » » case TSTRUCT:
696 » » » d1 = 0;
697 » » » for(t1=t->type; t1!=T; t1=t1->down) {»··
698 » » » » if(t1->etype != TFIELD)»
699 » » » » » fatal("isreftype struct: not TFIELD: %lT ", t1);»
700 » » » » d2 = derefdepth(t1->type);
701 » » » » if (d1 < d2) d1 = d2;
702 » » » }
703 » » » t->recur = d1;
704 break; 755 break;
705 default: 756 default:
706 » » » t->recur = 1; 757 » » » fatal("messed up escape tagging: %N::%N", curfn, ll->n);
707 » » } 758 » » }
708 759 » }
709 » » if (t->recur > 250) 760
710 » » » fatal("Reference types too deeply nested: %d", t->recur) ; // todo, just tie to sink in flows() 761 » curfn = savefn;
711 762 }
712 » » t->recur++; // what we store is 1+ what we return
713 » }
714
715 » return t->recur - 1;
716 }
717
718 static void
719 flows(Node* dst, Node* src)
720 {
721 » int d;
722
723 » if (dst == nil || src == nil || dst == src)
724 » » return;
725
726 » d = derefdepth(src->type); // dst may be theSink
727
728 » if (debug['m']>2)
729 » » print("%L %S:%d:flows(%N <- %N)\n", lineno, curfn ? curfn->nname ->sym : S, d, dst, src, src->type);
730
731
732 » // external variables arent typechecked yet,·
733 » // so they return d==0·
734 » if (d == 0 && !(src->op == ONAME && src->class == PEXTERN))» // scala r type, don't bother
735 » » return;
736
737 » // v := &object, w = &v -> treat as global leak
738 » if ((d > 1) && (debug['s'] < 2)) {
739 » » dst = &theSink;
740 » } else {
741 » » if (d > 0)
742 » » » dereftab[d] = list(dereftab[d], dst);
743 » }
744
745 » dst->asrc = list(dst->asrc, src);
746
747 » if (debug['m'] > 1) {
748 » » if (dst != &theSink)
749 » » » print("%L %S::%#N <- %#N", lineno, curfn ? curfn->nname- >sym : S, dst, src);
750 » » else
751 » » » print("%L %S::leaks %#N", lineno, curfn ? curfn->nname-> sym : S, src);
752 » »·······
753 » » if (src->op == OADDR) print(" <= %#N", src->left);
754 » » print("\n");
755 » }
756 }
757
758 static void
759 resolvealiases()
760 {
761 » NodeList* ll;
762 » int i;
763
764 » for (i = 250; i>= 0; i--) {
765 » » for (ll=dereftab[i]; ll; ll=ll->next) {
766 » » » // follow ll->n->left upstream, and copy
767 » » » // ll->n->asrc to the ->left of any reference
768 » » » // nodes we find
769 » » }
770 » }
771 }
772
773
774 static int depth;
775
776 static void
777 eawalk(Node *sink)
778 {
779 » NodeList *ll;
780
781 » if (debug['m']>1)
782 » » print("eawalk %d: %N\n", depth, sink);
783
784 » depth++;
785 » ll = sink->asrc;
786 » sink->asrc = nil;
787
788 » for (; ll; ll=ll->next)
789 » » eawalk(ll->n);
790
791 » if (sink->op == OADDR) {
792 » » addrescapes(sink->left);
793 » » eawalk(sink->left);
794 » }
795
796 » if (sink->op == ONAME && sink->class == PPARAMREF) {
797 » » addrescapes(sink->closure);
798 » » eawalk(sink->closure);
799 » }
800
801 » depth--;
802 }
LEFTRIGHT

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