]> Cypherpunks repositories - gostls13.git/blob
2215045
[gostls13.git] /
1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package gc
6
7 import (
8         "cmd/internal/obj"
9         "fmt"
10         "strings"
11 )
12
13 // Run analysis on minimal sets of mutually recursive functions
14 // or single non-recursive functions, bottom up.
15 //
16 // Finding these sets is finding strongly connected components
17 // in the static call graph.  The algorithm for doing that is taken
18 // from Sedgewick, Algorithms, Second Edition, p. 482, with two
19 // adaptations.
20 //
21 // First, a hidden closure function (n->curfn != N) cannot be the
22 // root of a connected component. Refusing to use it as a root
23 // forces it into the component of the function in which it appears.
24 // This is more convenient for escape analysis.
25 //
26 // Second, each function becomes two virtual nodes in the graph,
27 // with numbers n and n+1. We record the function's node number as n
28 // but search from node n+1. If the search tells us that the component
29 // number (min) is n+1, we know that this is a trivial component: one function
30 // plus its closures. If the search tells us that the component number is
31 // n, then there was a path from node n+1 back to node n, meaning that
32 // the function set is mutually recursive. The escape analysis can be
33 // more precise when analyzing a single non-recursive function than
34 // when analyzing a set of mutually recursive functions.
35
36 // TODO(rsc): Look into using a map[*Node]bool instead of walkgen,
37 // to allow analysis passes to use walkgen themselves.
38
39 type bottomUpVisitor struct {
40         analyze  func(*NodeList, bool)
41         visitgen uint32
42         stack    *NodeList
43 }
44
45 // visitBottomUp invokes analyze on the ODCLFUNC nodes listed in list.
46 // It calls analyze with successive groups of functions, working from
47 // the bottom of the call graph upward. Each time analyze is called with
48 // a list of functions, every function on that list only calls other functions
49 // on the list or functions that have been passed in previous invocations of
50 // analyze. Closures appear in the same list as their outer functions.
51 // The lists are as short as possible while preserving those requirements.
52 // (In a typical program, many invocations of analyze will be passed just
53 // a single function.) The boolean argument 'recursive' passed to analyze
54 // specifies whether the functions on the list are mutually recursive.
55 // If recursive is false, the list consists of only a single function and its closures.
56 // If recursive is true, the list may still contain only a single function,
57 // if that function is itself recursive.
58 func visitBottomUp(list *NodeList, analyze func(list *NodeList, recursive bool)) {
59         for l := list; l != nil; l = l.Next {
60                 l.N.Walkgen = 0
61         }
62
63         var v bottomUpVisitor
64         v.analyze = analyze
65         for l := list; l != nil; l = l.Next {
66                 if l.N.Op == ODCLFUNC && l.N.Curfn == nil {
67                         v.visit(l.N)
68                 }
69         }
70
71         for l := list; l != nil; l = l.Next {
72                 l.N.Walkgen = 0
73         }
74 }
75
76 func (v *bottomUpVisitor) visit(n *Node) uint32 {
77         if n.Walkgen > 0 {
78                 // already visited
79                 return n.Walkgen
80         }
81
82         v.visitgen++
83         n.Walkgen = v.visitgen
84         v.visitgen++
85         min := v.visitgen
86
87         l := new(NodeList)
88         l.Next = v.stack
89         l.N = n
90         v.stack = l
91         min = v.visitcodelist(n.Nbody, min)
92         if (min == n.Walkgen || min == n.Walkgen+1) && n.Curfn == nil {
93                 // This node is the root of a strongly connected component.
94
95                 // The original min passed to visitcodelist was n->walkgen+1.
96                 // If visitcodelist found its way back to n->walkgen, then this
97                 // block is a set of mutually recursive functions.
98                 // Otherwise it's just a lone function that does not recurse.
99                 recursive := min == n.Walkgen
100
101                 // Remove connected component from stack.
102                 // Mark walkgen so that future visits return a large number
103                 // so as not to affect the caller's min.
104                 block := v.stack
105
106                 var l *NodeList
107                 for l = v.stack; l.N != n; l = l.Next {
108                         l.N.Walkgen = ^uint32(0)
109                 }
110                 n.Walkgen = ^uint32(0)
111                 v.stack = l.Next
112                 l.Next = nil
113
114                 // Run escape analysis on this set of functions.
115                 v.analyze(block, recursive)
116         }
117
118         return min
119 }
120
121 func (v *bottomUpVisitor) visitcodelist(l *NodeList, min uint32) uint32 {
122         for ; l != nil; l = l.Next {
123                 min = v.visitcode(l.N, min)
124         }
125         return min
126 }
127
128 func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 {
129         if n == nil {
130                 return min
131         }
132
133         min = v.visitcodelist(n.Ninit, min)
134         min = v.visitcode(n.Left, min)
135         min = v.visitcode(n.Right, min)
136         min = v.visitcodelist(n.List, min)
137         min = v.visitcode(n.Ntest, min)
138         min = v.visitcode(n.Nincr, min)
139         min = v.visitcodelist(n.Nbody, min)
140         min = v.visitcodelist(n.Nelse, min)
141         min = v.visitcodelist(n.Rlist, min)
142
143         if n.Op == OCALLFUNC || n.Op == OCALLMETH {
144                 fn := n.Left
145                 if n.Op == OCALLMETH {
146                         fn = n.Left.Right.Sym.Def
147                 }
148                 if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && fn.Defn != nil {
149                         m := v.visit(fn.Defn)
150                         if m < min {
151                                 min = m
152                         }
153                 }
154         }
155
156         if n.Op == OCLOSURE {
157                 m := v.visit(n.Closure)
158                 if m < min {
159                         min = m
160                 }
161         }
162
163         return min
164 }
165
166 // Escape analysis.
167
168 // An escape analysis pass for a set of functions.
169 // The analysis assumes that closures and the functions in which they
170 // appear are analyzed together, so that the aliasing between their
171 // variables can be modeled more precisely.
172 //
173 // First escfunc, esc and escassign recurse over the ast of each
174 // function to dig out flow(dst,src) edges between any
175 // pointer-containing nodes and store them in dst->escflowsrc.  For
176 // variables assigned to a variable in an outer scope or used as a
177 // return value, they store a flow(theSink, src) edge to a fake node
178 // 'the Sink'.  For variables referenced in closures, an edge
179 // flow(closure, &var) is recorded and the flow of a closure itself to
180 // an outer scope is tracked the same way as other variables.
181 //
182 // Then escflood walks the graph starting at theSink and tags all
183 // variables of it can reach an & node as escaping and all function
184 // parameters it can reach as leaking.
185 //
186 // If a value's address is taken but the address does not escape,
187 // then the value can stay on the stack.  If the value new(T) does
188 // not escape, then new(T) can be rewritten into a stack allocation.
189 // The same is true of slice literals.
190 //
191 // If optimizations are disabled (-N), this code is not used.
192 // Instead, the compiler assumes that any value whose address
193 // is taken without being immediately dereferenced
194 // needs to be moved to the heap, and new(T) and slice
195 // literals are always real allocations.
196
197 func escapes(all *NodeList) {
198         visitBottomUp(all, escAnalyze)
199 }
200
201 const (
202         EscFuncUnknown = 0 + iota
203         EscFuncPlanned
204         EscFuncStarted
205         EscFuncTagged
206 )
207
208 type EscState struct {
209         // Fake node that all
210         //   - return values and output variables
211         //   - parameters on imported functions not marked 'safe'
212         //   - assignments to global variables
213         // flow to.
214         theSink Node
215
216         // If an analyzed function is recorded to return
217         // pieces obtained via indirection from a parameter,
218         // and later there is a call f(x) to that function,
219         // we create a link funcParam <- x to record that fact.
220         // The funcParam node is handled specially in escflood.
221         funcParam Node
222
223         dsts      *NodeList // all dst nodes
224         loopdepth int       // for detecting nested loop scopes
225         pdepth    int       // for debug printing in recursions.
226         dstcount  int       // diagnostic
227         edgecount int       // diagnostic
228         noesc     *NodeList // list of possible non-escaping nodes, for printing
229         recursive bool      // recursive function or group of mutually recursive functions.
230 }
231
232 var tags [16]*string
233
234 // mktag returns the string representation for an escape analysis tag.
235 func mktag(mask int) *string {
236         switch mask & EscMask {
237         case EscNone, EscReturn:
238                 break
239
240         default:
241                 Fatal("escape mktag")
242         }
243
244         mask >>= EscBits
245
246         if mask < len(tags) && tags[mask] != nil {
247                 return tags[mask]
248         }
249
250         s := fmt.Sprintf("esc:0x%x", mask)
251         if mask < len(tags) {
252                 tags[mask] = &s
253         }
254         return &s
255 }
256
257 func parsetag(note *string) int {
258         if note == nil || !strings.HasPrefix(*note, "esc:") {
259                 return EscUnknown
260         }
261         em := atoi((*note)[4:])
262         if em == 0 {
263                 return EscNone
264         }
265         return EscReturn | em<<EscBits
266 }
267
268 func escAnalyze(all *NodeList, recursive bool) {
269         var es EscState
270         e := &es
271         e.theSink.Op = ONAME
272         e.theSink.Orig = &e.theSink
273         e.theSink.Class = PEXTERN
274         e.theSink.Sym = Lookup(".sink")
275         e.theSink.Escloopdepth = -1
276         e.recursive = recursive
277
278         e.funcParam.Op = ONAME
279         e.funcParam.Orig = &e.funcParam
280         e.funcParam.Class = PAUTO
281         e.funcParam.Sym = Lookup(".param")
282         e.funcParam.Escloopdepth = 10000000
283
284         for l := all; l != nil; l = l.Next {
285                 if l.N.Op == ODCLFUNC {
286                         l.N.Esc = EscFuncPlanned
287                 }
288         }
289
290         // flow-analyze functions
291         for l := all; l != nil; l = l.Next {
292                 if l.N.Op == ODCLFUNC {
293                         escfunc(e, l.N)
294                 }
295         }
296
297         // print("escapes: %d e->dsts, %d edges\n", e->dstcount, e->edgecount);
298
299         // visit the upstream of each dst, mark address nodes with
300         // addrescapes, mark parameters unsafe
301         for l := e.dsts; l != nil; l = l.Next {
302                 escflood(e, l.N)
303         }
304
305         // for all top level functions, tag the typenodes corresponding to the param nodes
306         for l := all; l != nil; l = l.Next {
307                 if l.N.Op == ODCLFUNC {
308                         esctag(e, l.N)
309                 }
310         }
311
312         if Debug['m'] != 0 {
313                 for l := e.noesc; l != nil; l = l.Next {
314                         if l.N.Esc == EscNone {
315                                 var tmp *Sym
316                                 if l.N.Curfn != nil && l.N.Curfn.Nname != nil {
317                                         tmp = l.N.Curfn.Nname.Sym
318                                 } else {
319                                         tmp = nil
320                                 }
321                                 Warnl(int(l.N.Lineno), "%v %v does not escape", Sconv(tmp, 0), Nconv(l.N, obj.FmtShort))
322                         }
323                 }
324         }
325 }
326
327 func escfunc(e *EscState, func_ *Node) {
328         //      print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":"");
329
330         if func_.Esc != 1 {
331                 Fatal("repeat escfunc %v", Nconv(func_.Nname, 0))
332         }
333         func_.Esc = EscFuncStarted
334
335         saveld := e.loopdepth
336         e.loopdepth = 1
337         savefn := Curfn
338         Curfn = func_
339
340         for ll := Curfn.Dcl; ll != nil; ll = ll.Next {
341                 if ll.N.Op != ONAME {
342                         continue
343                 }
344                 switch ll.N.Class {
345                 // out params are in a loopdepth between the sink and all local variables
346                 case PPARAMOUT:
347                         ll.N.Escloopdepth = 0
348
349                 case PPARAM:
350                         ll.N.Escloopdepth = 1
351                         if ll.N.Type != nil && !haspointers(ll.N.Type) {
352                                 break
353                         }
354                         if Curfn.Nbody == nil && !Curfn.Noescape {
355                                 ll.N.Esc = EscHeap
356                         } else {
357                                 ll.N.Esc = EscNone // prime for escflood later
358                         }
359                         e.noesc = list(e.noesc, ll.N)
360                 }
361         }
362
363         // in a mutually recursive group we lose track of the return values
364         if e.recursive {
365                 for ll := Curfn.Dcl; ll != nil; ll = ll.Next {
366                         if ll.N.Op == ONAME && ll.N.Class == PPARAMOUT {
367                                 escflows(e, &e.theSink, ll.N)
368                         }
369                 }
370         }
371
372         escloopdepthlist(e, Curfn.Nbody)
373         esclist(e, Curfn.Nbody, Curfn)
374         Curfn = savefn
375         e.loopdepth = saveld
376 }
377
378 // Mark labels that have no backjumps to them as not increasing e->loopdepth.
379 // Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat
380 // and set it to one of the following two.  Then in esc we'll clear it again.
381 var looping Label
382
383 var nonlooping Label
384
385 func escloopdepthlist(e *EscState, l *NodeList) {
386         for ; l != nil; l = l.Next {
387                 escloopdepth(e, l.N)
388         }
389 }
390
391 func escloopdepth(e *EscState, n *Node) {
392         if n == nil {
393                 return
394         }
395
396         escloopdepthlist(e, n.Ninit)
397
398         switch n.Op {
399         case OLABEL:
400                 if n.Left == nil || n.Left.Sym == nil {
401                         Fatal("esc:label without label: %v", Nconv(n, obj.FmtSign))
402                 }
403
404                 // Walk will complain about this label being already defined, but that's not until
405                 // after escape analysis. in the future, maybe pull label & goto analysis out of walk and put before esc
406                 // if(n->left->sym->label != nil)
407                 //      fatal("escape analysis messed up analyzing label: %+N", n);
408                 n.Left.Sym.Label = &nonlooping
409
410         case OGOTO:
411                 if n.Left == nil || n.Left.Sym == nil {
412                         Fatal("esc:goto without label: %v", Nconv(n, obj.FmtSign))
413                 }
414
415                 // If we come past one that's uninitialized, this must be a (harmless) forward jump
416                 // but if it's set to nonlooping the label must have preceded this goto.
417                 if n.Left.Sym.Label == &nonlooping {
418                         n.Left.Sym.Label = &looping
419                 }
420         }
421
422         escloopdepth(e, n.Left)
423         escloopdepth(e, n.Right)
424         escloopdepthlist(e, n.List)
425         escloopdepth(e, n.Ntest)
426         escloopdepth(e, n.Nincr)
427         escloopdepthlist(e, n.Nbody)
428         escloopdepthlist(e, n.Nelse)
429         escloopdepthlist(e, n.Rlist)
430 }
431
432 func esclist(e *EscState, l *NodeList, up *Node) {
433         for ; l != nil; l = l.Next {
434                 esc(e, l.N, up)
435         }
436 }
437
438 func esc(e *EscState, n *Node, up *Node) {
439         if n == nil {
440                 return
441         }
442
443         lno := int(setlineno(n))
444
445         // ninit logically runs at a different loopdepth than the rest of the for loop.
446         esclist(e, n.Ninit, n)
447
448         if n.Op == OFOR || n.Op == ORANGE {
449                 e.loopdepth++
450         }
451
452         // type switch variables have no ODCL.
453         // process type switch as declaration.
454         // must happen before processing of switch body,
455         // so before recursion.
456         if n.Op == OSWITCH && n.Ntest != nil && n.Ntest.Op == OTYPESW {
457                 for ll := n.List; ll != nil; ll = ll.Next { // cases
458
459                         // ll->n->nname is the variable per case
460                         if ll.N.Nname != nil {
461                                 ll.N.Nname.Escloopdepth = e.loopdepth
462                         }
463                 }
464         }
465
466         esc(e, n.Left, n)
467         esc(e, n.Right, n)
468         esc(e, n.Ntest, n)
469         esc(e, n.Nincr, n)
470         esclist(e, n.Nbody, n)
471         esclist(e, n.Nelse, n)
472         esclist(e, n.List, n)
473         esclist(e, n.Rlist, n)
474
475         if n.Op == OFOR || n.Op == ORANGE {
476                 e.loopdepth--
477         }
478
479         if Debug['m'] > 1 {
480                 var tmp *Sym
481                 if Curfn != nil && Curfn.Nname != nil {
482                         tmp = Curfn.Nname.Sym
483                 } else {
484                         tmp = nil
485                 }
486                 fmt.Printf("%v:[%d] %v esc: %v\n", Ctxt.Line(int(lineno)), e.loopdepth, Sconv(tmp, 0), Nconv(n, 0))
487         }
488
489         switch n.Op {
490         // Record loop depth at declaration.
491         case ODCL:
492                 if n.Left != nil {
493                         n.Left.Escloopdepth = e.loopdepth
494                 }
495
496         case OLABEL:
497                 if n.Left.Sym.Label == &nonlooping {
498                         if Debug['m'] > 1 {
499                                 fmt.Printf("%v:%v non-looping label\n", Ctxt.Line(int(lineno)), Nconv(n, 0))
500                         }
501                 } else if n.Left.Sym.Label == &looping {
502                         if Debug['m'] > 1 {
503                                 fmt.Printf("%v: %v looping label\n", Ctxt.Line(int(lineno)), Nconv(n, 0))
504                         }
505                         e.loopdepth++
506                 }
507
508                 // See case OLABEL in escloopdepth above
509                 // else if(n->left->sym->label == nil)
510                 //      fatal("escape analysis missed or messed up a label: %+N", n);
511
512                 n.Left.Sym.Label = nil
513
514                 // Everything but fixed array is a dereference.
515         case ORANGE:
516                 if Isfixedarray(n.Type) && n.List != nil && n.List.Next != nil {
517                         escassign(e, n.List.Next.N, n.Right)
518                 }
519
520         case OSWITCH:
521                 if n.Ntest != nil && n.Ntest.Op == OTYPESW {
522                         for ll := n.List; ll != nil; ll = ll.Next { // cases
523
524                                 // ntest->right is the argument of the .(type),
525                                 // ll->n->nname is the variable per case
526                                 escassign(e, ll.N.Nname, n.Ntest.Right)
527                         }
528                 }
529
530                 // Filter out the following special case.
531         //
532         //      func (b *Buffer) Foo() {
533         //              n, m := ...
534         //              b.buf = b.buf[n:m]
535         //      }
536         //
537         // This assignment is a no-op for escape analysis,
538         // it does not store any new pointers into b that were not already there.
539         // However, without this special case b will escape, because we assign to OIND/ODOTPTR.
540         case OAS, OASOP:
541                 if (n.Left.Op == OIND || n.Left.Op == ODOTPTR) && n.Left.Left.Op == ONAME && // dst is ONAME dereference
542                         (n.Right.Op == OSLICE || n.Right.Op == OSLICE3 || n.Right.Op == OSLICESTR) && // src is slice operation
543                         (n.Right.Left.Op == OIND || n.Right.Left.Op == ODOTPTR) && n.Right.Left.Left.Op == ONAME && // slice is applied to ONAME dereference
544                         n.Left.Left == n.Right.Left.Left { // dst and src reference the same base ONAME
545
546                         // Here we also assume that the statement will not contain calls,
547                         // that is, that order will move any calls to init.
548                         // Otherwise base ONAME value could change between the moments
549                         // when we evaluate it for dst and for src.
550                         //
551                         // Note, this optimization does not apply to OSLICEARR,
552                         // because it does introduce a new pointer into b that was not already there
553                         // (pointer to b itself). After such assignment, if b contents escape,
554                         // b escapes as well. If we ignore such OSLICEARR, we will conclude
555                         // that b does not escape when b contents do.
556                         if Debug['m'] != 0 {
557                                 var tmp *Sym
558                                 if n.Curfn != nil && n.Curfn.Nname != nil {
559                                         tmp = n.Curfn.Nname.Sym
560                                 } else {
561                                         tmp = nil
562                                 }
563                                 Warnl(int(n.Lineno), "%v ignoring self-assignment to %v", Sconv(tmp, 0), Nconv(n.Left, obj.FmtShort))
564                         }
565
566                         break
567                 }
568
569                 escassign(e, n.Left, n.Right)
570
571         case OAS2: // x,y = a,b
572                 if count(n.List) == count(n.Rlist) {
573                         ll := n.List
574                         lr := n.Rlist
575                         for ; ll != nil; ll, lr = ll.Next, lr.Next {
576                                 escassign(e, ll.N, lr.N)
577                         }
578                 }
579
580         case OAS2RECV, // v, ok = <-ch
581                 OAS2MAPR,    // v, ok = m[k]
582                 OAS2DOTTYPE: // v, ok = x.(type)
583                 escassign(e, n.List.N, n.Rlist.N)
584
585         case OSEND: // ch <- x
586                 escassign(e, &e.theSink, n.Right)
587
588         case ODEFER:
589                 if e.loopdepth == 1 { // top level
590                         break
591                 }
592                 // arguments leak out of scope
593                 // TODO: leak to a dummy node instead
594                 fallthrough
595
596         case OPROC:
597                 // go f(x) - f and x escape
598                 escassign(e, &e.theSink, n.Left.Left)
599
600                 escassign(e, &e.theSink, n.Left.Right) // ODDDARG for call
601                 for ll := n.Left.List; ll != nil; ll = ll.Next {
602                         escassign(e, &e.theSink, ll.N)
603                 }
604
605         case OCALLMETH, OCALLFUNC, OCALLINTER:
606                 esccall(e, n, up)
607
608                 // esccall already done on n->rlist->n. tie it's escretval to n->list
609         case OAS2FUNC: // x,y = f()
610                 lr := n.Rlist.N.Escretval
611
612                 var ll *NodeList
613                 for ll = n.List; lr != nil && ll != nil; lr, ll = lr.Next, ll.Next {
614                         escassign(e, ll.N, lr.N)
615                 }
616                 if lr != nil || ll != nil {
617                         Fatal("esc oas2func")
618                 }
619
620         case ORETURN:
621                 ll := n.List
622                 if count(n.List) == 1 && Curfn.Type.Outtuple > 1 {
623                         // OAS2FUNC in disguise
624                         // esccall already done on n->list->n
625                         // tie n->list->n->escretval to curfn->dcl PPARAMOUT's
626                         ll = n.List.N.Escretval
627                 }
628
629                 for lr := Curfn.Dcl; lr != nil && ll != nil; lr = lr.Next {
630                         if lr.N.Op != ONAME || lr.N.Class != PPARAMOUT {
631                                 continue
632                         }
633                         escassign(e, lr.N, ll.N)
634                         ll = ll.Next
635                 }
636
637                 if ll != nil {
638                         Fatal("esc return list")
639                 }
640
641                 // Argument could leak through recover.
642         case OPANIC:
643                 escassign(e, &e.theSink, n.Left)
644
645         case OAPPEND:
646                 if !n.Isddd {
647                         for ll := n.List.Next; ll != nil; ll = ll.Next {
648                                 escassign(e, &e.theSink, ll.N) // lose track of assign to dereference
649                         }
650                 }
651
652         case OCONV, OCONVNOP:
653                 escassign(e, n, n.Left)
654
655         case OCONVIFACE:
656                 n.Esc = EscNone // until proven otherwise
657                 e.noesc = list(e.noesc, n)
658                 n.Escloopdepth = e.loopdepth
659                 escassign(e, n, n.Left)
660
661         case OARRAYLIT:
662                 if Isslice(n.Type) {
663                         n.Esc = EscNone // until proven otherwise
664                         e.noesc = list(e.noesc, n)
665                         n.Escloopdepth = e.loopdepth
666
667                         // Values make it to memory, lose track.
668                         for ll := n.List; ll != nil; ll = ll.Next {
669                                 escassign(e, &e.theSink, ll.N.Right)
670                         }
671                 } else {
672                         // Link values to array.
673                         for ll := n.List; ll != nil; ll = ll.Next {
674                                 escassign(e, n, ll.N.Right)
675                         }
676                 }
677
678                 // Link values to struct.
679         case OSTRUCTLIT:
680                 for ll := n.List; ll != nil; ll = ll.Next {
681                         escassign(e, n, ll.N.Right)
682                 }
683
684         case OPTRLIT:
685                 n.Esc = EscNone // until proven otherwise
686                 e.noesc = list(e.noesc, n)
687                 n.Escloopdepth = e.loopdepth
688
689                 // Link OSTRUCTLIT to OPTRLIT; if OPTRLIT escapes, OSTRUCTLIT elements do too.
690                 escassign(e, n, n.Left)
691
692         case OCALLPART:
693                 n.Esc = EscNone // until proven otherwise
694                 e.noesc = list(e.noesc, n)
695                 n.Escloopdepth = e.loopdepth
696
697                 // Contents make it to memory, lose track.
698                 escassign(e, &e.theSink, n.Left)
699
700         case OMAPLIT:
701                 n.Esc = EscNone // until proven otherwise
702                 e.noesc = list(e.noesc, n)
703                 n.Escloopdepth = e.loopdepth
704
705                 // Keys and values make it to memory, lose track.
706                 for ll := n.List; ll != nil; ll = ll.Next {
707                         escassign(e, &e.theSink, ll.N.Left)
708                         escassign(e, &e.theSink, ll.N.Right)
709                 }
710
711                 // Link addresses of captured variables to closure.
712         case OCLOSURE:
713                 var a *Node
714                 var v *Node
715                 for ll := n.Cvars; ll != nil; ll = ll.Next {
716                         v = ll.N
717                         if v.Op == OXXX { // unnamed out argument; see dcl.c:/^funcargs
718                                 continue
719                         }
720                         a = v.Closure
721                         if !v.Byval {
722                                 a = Nod(OADDR, a, nil)
723                                 a.Lineno = v.Lineno
724                                 a.Escloopdepth = e.loopdepth
725                                 typecheck(&a, Erv)
726                         }
727
728                         escassign(e, n, a)
729                 }
730                 fallthrough
731
732                 // fallthrough
733         case OMAKECHAN,
734                 OMAKEMAP,
735                 OMAKESLICE,
736                 ONEW,
737                 OARRAYRUNESTR,
738                 OARRAYBYTESTR,
739                 OSTRARRAYRUNE,
740                 OSTRARRAYBYTE,
741                 ORUNESTR:
742                 n.Escloopdepth = e.loopdepth
743
744                 n.Esc = EscNone // until proven otherwise
745                 e.noesc = list(e.noesc, n)
746
747         case OADDSTR:
748                 n.Escloopdepth = e.loopdepth
749                 n.Esc = EscNone // until proven otherwise
750                 e.noesc = list(e.noesc, n)
751
752         // Arguments of OADDSTR do not escape.
753
754         case OADDR:
755                 n.Esc = EscNone // until proven otherwise
756                 e.noesc = list(e.noesc, n)
757
758                 // current loop depth is an upper bound on actual loop depth
759                 // of addressed value.
760                 n.Escloopdepth = e.loopdepth
761
762                 // for &x, use loop depth of x if known.
763                 // it should always be known, but if not, be conservative
764                 // and keep the current loop depth.
765                 if n.Left.Op == ONAME {
766                         switch n.Left.Class {
767                         case PAUTO:
768                                 if n.Left.Escloopdepth != 0 {
769                                         n.Escloopdepth = n.Left.Escloopdepth
770                                 }
771
772                                 // PPARAM is loop depth 1 always.
773                         // PPARAMOUT is loop depth 0 for writes
774                         // but considered loop depth 1 for address-of,
775                         // so that writing the address of one result
776                         // to another (or the same) result makes the
777                         // first result move to the heap.
778                         case PPARAM, PPARAMOUT:
779                                 n.Escloopdepth = 1
780                         }
781                 }
782         }
783
784         lineno = int32(lno)
785 }
786
787 // Assert that expr somehow gets assigned to dst, if non nil.  for
788 // dst==nil, any name node expr still must be marked as being
789 // evaluated in curfn.  For expr==nil, dst must still be examined for
790 // evaluations inside it (e.g *f(x) = y)
791 func escassign(e *EscState, dst *Node, src *Node) {
792         if isblank(dst) || dst == nil || src == nil || src.Op == ONONAME || src.Op == OXXX {
793                 return
794         }
795
796         if Debug['m'] > 1 {
797                 var tmp *Sym
798                 if Curfn != nil && Curfn.Nname != nil {
799                         tmp = Curfn.Nname.Sym
800                 } else {
801                         tmp = nil
802                 }
803                 fmt.Printf("%v:[%d] %v escassign: %v(%v) = %v(%v)\n", Ctxt.Line(int(lineno)), e.loopdepth, Sconv(tmp, 0), Nconv(dst, obj.FmtShort), Jconv(dst, obj.FmtShort), Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort))
804         }
805
806         setlineno(dst)
807
808         // Analyze lhs of assignment.
809         // Replace dst with e->theSink if we can't track it.
810         switch dst.Op {
811         default:
812                 Dump("dst", dst)
813                 Fatal("escassign: unexpected dst")
814
815         case OARRAYLIT,
816                 OCLOSURE,
817                 OCONV,
818                 OCONVIFACE,
819                 OCONVNOP,
820                 OMAPLIT,
821                 OSTRUCTLIT,
822                 OPTRLIT,
823                 OCALLPART:
824                 break
825
826         case ONAME:
827                 if dst.Class == PEXTERN {
828                         dst = &e.theSink
829                 }
830
831         case ODOT: // treat "dst.x  = src" as "dst = src"
832                 escassign(e, dst.Left, src)
833
834                 return
835
836         case OINDEX:
837                 if Isfixedarray(dst.Left.Type) {
838                         escassign(e, dst.Left, src)
839                         return
840                 }
841
842                 dst = &e.theSink // lose track of dereference
843
844         case OIND, ODOTPTR:
845                 dst = &e.theSink // lose track of dereference
846
847                 // lose track of key and value
848         case OINDEXMAP:
849                 escassign(e, &e.theSink, dst.Right)
850
851                 dst = &e.theSink
852         }
853
854         lno := int(setlineno(src))
855         e.pdepth++
856
857         switch src.Op {
858         case OADDR, // dst = &x
859                 OIND,    // dst = *x
860                 ODOTPTR, // dst = (*x).f
861                 ONAME,
862                 OPARAM,
863                 ODDDARG,
864                 OPTRLIT,
865                 OARRAYLIT,
866                 OMAPLIT,
867                 OSTRUCTLIT,
868                 OMAKECHAN,
869                 OMAKEMAP,
870                 OMAKESLICE,
871                 OARRAYRUNESTR,
872                 OARRAYBYTESTR,
873                 OSTRARRAYRUNE,
874                 OSTRARRAYBYTE,
875                 OADDSTR,
876                 ONEW,
877                 OCLOSURE,
878                 OCALLPART,
879                 ORUNESTR,
880                 OCONVIFACE:
881                 escflows(e, dst, src)
882
883                 // Flowing multiple returns to a single dst happens when
884         // analyzing "go f(g())": here g() flows to sink (issue 4529).
885         case OCALLMETH, OCALLFUNC, OCALLINTER:
886                 for ll := src.Escretval; ll != nil; ll = ll.Next {
887                         escflows(e, dst, ll.N)
888                 }
889
890                 // A non-pointer escaping from a struct does not concern us.
891         case ODOT:
892                 if src.Type != nil && !haspointers(src.Type) {
893                         break
894                 }
895                 fallthrough
896
897                 // Conversions, field access, slice all preserve the input value.
898         // fallthrough
899         case OCONV,
900                 OCONVNOP,
901                 ODOTMETH,
902                 // treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC
903                 // iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here
904                 ODOTTYPE,
905                 ODOTTYPE2,
906                 OSLICE,
907                 OSLICE3,
908                 OSLICEARR,
909                 OSLICE3ARR,
910                 OSLICESTR:
911                 // Conversions, field access, slice all preserve the input value.
912                 escassign(e, dst, src.Left)
913
914         case OAPPEND:
915                 // Append returns first argument.
916                 escassign(e, dst, src.List.N)
917
918         case OINDEX:
919                 // Index of array preserves input value.
920                 if Isfixedarray(src.Left.Type) {
921                         escassign(e, dst, src.Left)
922                 }
923
924                 // Might be pointer arithmetic, in which case
925         // the operands flow into the result.
926         // TODO(rsc): Decide what the story is here.  This is unsettling.
927         case OADD,
928                 OSUB,
929                 OOR,
930                 OXOR,
931                 OMUL,
932                 ODIV,
933                 OMOD,
934                 OLSH,
935                 ORSH,
936                 OAND,
937                 OANDNOT,
938                 OPLUS,
939                 OMINUS,
940                 OCOM:
941                 escassign(e, dst, src.Left)
942
943                 escassign(e, dst, src.Right)
944         }
945
946         e.pdepth--
947         lineno = int32(lno)
948 }
949
950 func escassignfromtag(e *EscState, note *string, dsts *NodeList, src *Node) int {
951         em := parsetag(note)
952
953         if em == EscUnknown {
954                 escassign(e, &e.theSink, src)
955                 return em
956         }
957
958         if em == EscNone {
959                 return em
960         }
961
962         // If content inside parameter (reached via indirection)
963         // escapes back to results, mark as such.
964         if em&EscContentEscapes != 0 {
965                 escassign(e, &e.funcParam, src)
966         }
967
968         em0 := em
969         for em >>= EscReturnBits; em != 0 && dsts != nil; em, dsts = em>>1, dsts.Next {
970                 if em&1 != 0 {
971                         escassign(e, dsts.N, src)
972                 }
973         }
974
975         if em != 0 && dsts == nil {
976                 Fatal("corrupt esc tag %q or messed up escretval list\n", note)
977         }
978         return em0
979 }
980
981 // This is a bit messier than fortunate, pulled out of esc's big
982 // switch for clarity.  We either have the paramnodes, which may be
983 // connected to other things through flows or we have the parameter type
984 // nodes, which may be marked "noescape". Navigating the ast is slightly
985 // different for methods vs plain functions and for imported vs
986 // this-package
987 func esccall(e *EscState, n *Node, up *Node) {
988         var fntype *Type
989
990         var fn *Node
991         switch n.Op {
992         default:
993                 Fatal("esccall")
994
995         case OCALLFUNC:
996                 fn = n.Left
997                 fntype = fn.Type
998
999         case OCALLMETH:
1000                 fn = n.Left.Right.Sym.Def
1001                 if fn != nil {
1002                         fntype = fn.Type
1003                 } else {
1004                         fntype = n.Left.Type
1005                 }
1006
1007         case OCALLINTER:
1008                 fntype = n.Left.Type
1009         }
1010
1011         ll := n.List
1012         if n.List != nil && n.List.Next == nil {
1013                 a := n.List.N
1014                 if a.Type.Etype == TSTRUCT && a.Type.Funarg != 0 { // f(g()).
1015                         ll = a.Escretval
1016                 }
1017         }
1018
1019         if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && fn.Defn != nil && fn.Defn.Nbody != nil && fn.Ntype != nil && fn.Defn.Esc < EscFuncTagged {
1020                 // function in same mutually recursive group.  Incorporate into flow graph.
1021                 //              print("esc local fn: %N\n", fn->ntype);
1022                 if fn.Defn.Esc == EscFuncUnknown || n.Escretval != nil {
1023                         Fatal("graph inconsistency")
1024                 }
1025
1026                 // set up out list on this call node
1027                 for lr := fn.Ntype.Rlist; lr != nil; lr = lr.Next {
1028                         n.Escretval = list(n.Escretval, lr.N.Left) // type.rlist ->  dclfield -> ONAME (PPARAMOUT)
1029                 }
1030
1031                 // Receiver.
1032                 if n.Op != OCALLFUNC {
1033                         escassign(e, fn.Ntype.Left.Left, n.Left.Left)
1034                 }
1035
1036                 var src *Node
1037                 for lr := fn.Ntype.List; ll != nil && lr != nil; ll, lr = ll.Next, lr.Next {
1038                         src = ll.N
1039                         if lr.N.Isddd && !n.Isddd {
1040                                 // Introduce ODDDARG node to represent ... allocation.
1041                                 src = Nod(ODDDARG, nil, nil)
1042
1043                                 src.Type = typ(TARRAY)
1044                                 src.Type.Type = lr.N.Type.Type
1045                                 src.Type.Bound = int64(count(ll))
1046                                 src.Type = Ptrto(src.Type) // make pointer so it will be tracked
1047                                 src.Escloopdepth = e.loopdepth
1048                                 src.Lineno = n.Lineno
1049                                 src.Esc = EscNone // until we find otherwise
1050                                 e.noesc = list(e.noesc, src)
1051                                 n.Right = src
1052                         }
1053
1054                         if lr.N.Left != nil {
1055                                 escassign(e, lr.N.Left, src)
1056                         }
1057                         if src != ll.N {
1058                                 break
1059                         }
1060                 }
1061
1062                 // "..." arguments are untracked
1063                 for ; ll != nil; ll = ll.Next {
1064                         escassign(e, &e.theSink, ll.N)
1065                 }
1066
1067                 return
1068         }
1069
1070         // Imported or completely analyzed function.  Use the escape tags.
1071         if n.Escretval != nil {
1072                 Fatal("esc already decorated call %v\n", Nconv(n, obj.FmtSign))
1073         }
1074
1075         // set up out list on this call node with dummy auto ONAMES in the current (calling) function.
1076         i := 0
1077
1078         var src *Node
1079         var buf string
1080         for t := getoutargx(fntype).Type; t != nil; t = t.Down {
1081                 src = Nod(ONAME, nil, nil)
1082                 buf = fmt.Sprintf(".dum%d", i)
1083                 i++
1084                 src.Sym = Lookup(buf)
1085                 src.Type = t.Type
1086                 src.Class = PAUTO
1087                 src.Curfn = Curfn
1088                 src.Escloopdepth = e.loopdepth
1089                 src.Used = true
1090                 src.Lineno = n.Lineno
1091                 n.Escretval = list(n.Escretval, src)
1092         }
1093
1094         //      print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval);
1095
1096         // Receiver.
1097         if n.Op != OCALLFUNC {
1098                 t := getthisx(fntype).Type
1099                 src := n.Left.Left
1100                 if haspointers(t.Type) {
1101                         escassignfromtag(e, t.Note, n.Escretval, src)
1102                 }
1103         }
1104
1105         var a *Node
1106         for t := getinargx(fntype).Type; ll != nil; ll = ll.Next {
1107                 src = ll.N
1108                 if t.Isddd && !n.Isddd {
1109                         // Introduce ODDDARG node to represent ... allocation.
1110                         src = Nod(ODDDARG, nil, nil)
1111
1112                         src.Escloopdepth = e.loopdepth
1113                         src.Lineno = n.Lineno
1114                         src.Type = typ(TARRAY)
1115                         src.Type.Type = t.Type.Type
1116                         src.Type.Bound = int64(count(ll))
1117                         src.Type = Ptrto(src.Type) // make pointer so it will be tracked
1118                         src.Esc = EscNone          // until we find otherwise
1119                         e.noesc = list(e.noesc, src)
1120                         n.Right = src
1121                 }
1122
1123                 if haspointers(t.Type) {
1124                         if escassignfromtag(e, t.Note, n.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC {
1125                                 a = src
1126                                 for a.Op == OCONVNOP {
1127                                         a = a.Left
1128                                 }
1129                                 switch a.Op {
1130                                 // The callee has already been analyzed, so its arguments have esc tags.
1131                                 // The argument is marked as not escaping at all.
1132                                 // Record that fact so that any temporary used for
1133                                 // synthesizing this expression can be reclaimed when
1134                                 // the function returns.
1135                                 // This 'noescape' is even stronger than the usual esc == EscNone.
1136                                 // src->esc == EscNone means that src does not escape the current function.
1137                                 // src->noescape = 1 here means that src does not escape this statement
1138                                 // in the current function.
1139                                 case OCALLPART,
1140                                         OCLOSURE,
1141                                         ODDDARG,
1142                                         OARRAYLIT,
1143                                         OPTRLIT,
1144                                         OSTRUCTLIT:
1145                                         a.Noescape = true
1146                                 }
1147                         }
1148                 }
1149
1150                 if src != ll.N {
1151                         break
1152                 }
1153                 t = t.Down
1154         }
1155
1156         // "..." arguments are untracked
1157         for ; ll != nil; ll = ll.Next {
1158                 escassign(e, &e.theSink, ll.N)
1159         }
1160 }
1161
1162 // Store the link src->dst in dst, throwing out some quick wins.
1163 func escflows(e *EscState, dst *Node, src *Node) {
1164         if dst == nil || src == nil || dst == src {
1165                 return
1166         }
1167
1168         // Don't bother building a graph for scalars.
1169         if src.Type != nil && !haspointers(src.Type) {
1170                 return
1171         }
1172
1173         if Debug['m'] > 2 {
1174                 fmt.Printf("%v::flows:: %v <- %v\n", Ctxt.Line(int(lineno)), Nconv(dst, obj.FmtShort), Nconv(src, obj.FmtShort))
1175         }
1176
1177         if dst.Escflowsrc == nil {
1178                 e.dsts = list(e.dsts, dst)
1179                 e.dstcount++
1180         }
1181
1182         e.edgecount++
1183
1184         dst.Escflowsrc = list(dst.Escflowsrc, src)
1185 }
1186
1187 // Whenever we hit a reference node, the level goes up by one, and whenever
1188 // we hit an OADDR, the level goes down by one. as long as we're on a level > 0
1189 // finding an OADDR just means we're following the upstream of a dereference,
1190 // so this address doesn't leak (yet).
1191 // If level == 0, it means the /value/ of this node can reach the root of this flood.
1192 // so if this node is an OADDR, it's argument should be marked as escaping iff
1193 // it's currfn/e->loopdepth are different from the flood's root.
1194 // Once an object has been moved to the heap, all of it's upstream should be considered
1195 // escaping to the global scope.
1196 func escflood(e *EscState, dst *Node) {
1197         switch dst.Op {
1198         case ONAME, OCLOSURE:
1199                 break
1200
1201         default:
1202                 return
1203         }
1204
1205         if Debug['m'] > 1 {
1206                 var tmp *Sym
1207                 if dst.Curfn != nil && dst.Curfn.Nname != nil {
1208                         tmp = dst.Curfn.Nname.Sym
1209                 } else {
1210                         tmp = nil
1211                 }
1212                 fmt.Printf("\nescflood:%d: dst %v scope:%v[%d]\n", walkgen, Nconv(dst, obj.FmtShort), Sconv(tmp, 0), dst.Escloopdepth)
1213         }
1214
1215         for l := dst.Escflowsrc; l != nil; l = l.Next {
1216                 walkgen++
1217                 escwalk(e, 0, dst, l.N)
1218         }
1219 }
1220
1221 // There appear to be some loops in the escape graph, causing
1222 // arbitrary recursion into deeper and deeper levels.
1223 // Cut this off safely by making minLevel sticky: once you
1224 // get that deep, you cannot go down any further but you also
1225 // cannot go up any further. This is a conservative fix.
1226 // Making minLevel smaller (more negative) would handle more
1227 // complex chains of indirections followed by address-of operations,
1228 // at the cost of repeating the traversal once for each additional
1229 // allowed level when a loop is encountered. Using -2 suffices to
1230 // pass all the tests we have written so far, which we assume matches
1231 // the level of complexity we want the escape analysis code to handle.
1232 const (
1233         MinLevel = -2
1234 )
1235
1236 func escwalk(e *EscState, level int, dst *Node, src *Node) {
1237         if src.Walkgen == walkgen && src.Esclevel <= int32(level) {
1238                 return
1239         }
1240         src.Walkgen = walkgen
1241         src.Esclevel = int32(level)
1242
1243         if Debug['m'] > 1 {
1244                 var tmp *Sym
1245                 if src.Curfn != nil && src.Curfn.Nname != nil {
1246                         tmp = src.Curfn.Nname.Sym
1247                 } else {
1248                         tmp = nil
1249                 }
1250                 fmt.Printf("escwalk: level:%d depth:%d %.*s %v(%v) scope:%v[%d]\n", level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), Sconv(tmp, 0), src.Escloopdepth)
1251         }
1252
1253         e.pdepth++
1254
1255         // Input parameter flowing to output parameter?
1256         var leaks bool
1257         if dst.Op == ONAME && dst.Class == PPARAMOUT && dst.Vargen <= 20 {
1258                 if src.Op == ONAME && src.Class == PPARAM && src.Curfn == dst.Curfn && src.Esc != EscScope && src.Esc != EscHeap {
1259                         if level == 0 {
1260                                 if Debug['m'] != 0 {
1261                                         Warnl(int(src.Lineno), "leaking param: %v to result %v", Nconv(src, obj.FmtShort), Sconv(dst.Sym, 0))
1262                                 }
1263                                 if src.Esc&EscMask != EscReturn {
1264                                         src.Esc = EscReturn
1265                                 }
1266                                 src.Esc |= 1 << uint((dst.Vargen-1)+EscReturnBits)
1267                                 goto recurse
1268                         } else if level > 0 {
1269                                 if Debug['m'] != 0 {
1270                                         Warnl(int(src.Lineno), "%v leaking param %v content to result %v", Nconv(src.Curfn.Nname, 0), Nconv(src, obj.FmtShort), Sconv(dst.Sym, 0))
1271                                 }
1272                                 if src.Esc&EscMask != EscReturn {
1273                                         src.Esc = EscReturn
1274                                 }
1275                                 src.Esc |= EscContentEscapes
1276                                 goto recurse
1277                         }
1278                 }
1279         }
1280
1281         // The second clause is for values pointed at by an object passed to a call
1282         // that returns something reached via indirect from the object.
1283         // We don't know which result it is or how many indirects, so we treat it as leaking.
1284         leaks = level <= 0 && dst.Escloopdepth < src.Escloopdepth || level < 0 && dst == &e.funcParam && haspointers(src.Type)
1285
1286         switch src.Op {
1287         case ONAME:
1288                 if src.Class == PPARAM && (leaks || dst.Escloopdepth < 0) && src.Esc != EscHeap {
1289                         src.Esc = EscScope
1290                         if Debug['m'] != 0 {
1291                                 Warnl(int(src.Lineno), "leaking param: %v", Nconv(src, obj.FmtShort))
1292                         }
1293                 }
1294
1295                 // Treat a PPARAMREF closure variable as equivalent to the
1296                 // original variable.
1297                 if src.Class == PPARAMREF {
1298                         if leaks && Debug['m'] != 0 {
1299                                 Warnl(int(src.Lineno), "leaking closure reference %v", Nconv(src, obj.FmtShort))
1300                         }
1301                         escwalk(e, level, dst, src.Closure)
1302                 }
1303
1304         case OPTRLIT, OADDR:
1305                 if leaks {
1306                         src.Esc = EscHeap
1307                         addrescapes(src.Left)
1308                         if Debug['m'] != 0 {
1309                                 Warnl(int(src.Lineno), "%v escapes to heap", Nconv(src, obj.FmtShort))
1310                         }
1311                 }
1312
1313                 newlevel := level
1314                 if level > MinLevel {
1315                         newlevel--
1316                 }
1317                 escwalk(e, newlevel, dst, src.Left)
1318
1319         case OARRAYLIT:
1320                 if Isfixedarray(src.Type) {
1321                         break
1322                 }
1323                 fallthrough
1324
1325                 // fall through
1326         case ODDDARG,
1327                 OMAKECHAN,
1328                 OMAKEMAP,
1329                 OMAKESLICE,
1330                 OARRAYRUNESTR,
1331                 OARRAYBYTESTR,
1332                 OSTRARRAYRUNE,
1333                 OSTRARRAYBYTE,
1334                 OADDSTR,
1335                 OMAPLIT,
1336                 ONEW,
1337                 OCLOSURE,
1338                 OCALLPART,
1339                 ORUNESTR,
1340                 OCONVIFACE:
1341                 if leaks {
1342                         src.Esc = EscHeap
1343                         if Debug['m'] != 0 {
1344                                 Warnl(int(src.Lineno), "%v escapes to heap", Nconv(src, obj.FmtShort))
1345                         }
1346                 }
1347
1348         case ODOT,
1349                 OSLICE,
1350                 OSLICEARR,
1351                 OSLICE3,
1352                 OSLICE3ARR,
1353                 OSLICESTR:
1354                 escwalk(e, level, dst, src.Left)
1355
1356         case OINDEX:
1357                 if Isfixedarray(src.Left.Type) {
1358                         escwalk(e, level, dst, src.Left)
1359                         break
1360                 }
1361                 fallthrough
1362
1363                 // fall through
1364         case ODOTPTR, OINDEXMAP, OIND:
1365                 newlevel := level
1366
1367                 if level > MinLevel {
1368                         newlevel++
1369                 }
1370                 escwalk(e, newlevel, dst, src.Left)
1371         }
1372
1373 recurse:
1374         for ll := src.Escflowsrc; ll != nil; ll = ll.Next {
1375                 escwalk(e, level, dst, ll.N)
1376         }
1377
1378         e.pdepth--
1379 }
1380
1381 func esctag(e *EscState, func_ *Node) {
1382         func_.Esc = EscFuncTagged
1383
1384         // External functions are assumed unsafe,
1385         // unless //go:noescape is given before the declaration.
1386         if func_.Nbody == nil {
1387                 if func_.Noescape {
1388                         for t := getinargx(func_.Type).Type; t != nil; t = t.Down {
1389                                 if haspointers(t.Type) {
1390                                         t.Note = mktag(EscNone)
1391                                 }
1392                         }
1393                 }
1394
1395                 return
1396         }
1397
1398         savefn := Curfn
1399         Curfn = func_
1400
1401         for ll := Curfn.Dcl; ll != nil; ll = ll.Next {
1402                 if ll.N.Op != ONAME || ll.N.Class != PPARAM {
1403                         continue
1404                 }
1405
1406                 switch ll.N.Esc & EscMask {
1407                 case EscNone, // not touched by escflood
1408                         EscReturn:
1409                         if haspointers(ll.N.Type) { // don't bother tagging for scalars
1410                                 ll.N.Paramfld.Note = mktag(int(ll.N.Esc))
1411                         }
1412
1413                 case EscHeap, // touched by escflood, moved to heap
1414                         EscScope: // touched by escflood, value leaves scope
1415                         break
1416                 }
1417         }
1418
1419         Curfn = savefn
1420 }