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.
13 // Run analysis on minimal sets of mutually recursive functions
14 // or single non-recursive functions, bottom up.
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
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.
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.
36 // TODO(rsc): Look into using a map[*Node]bool instead of walkgen,
37 // to allow analysis passes to use walkgen themselves.
39 type bottomUpVisitor struct {
40 analyze func(*NodeList, bool)
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 {
65 for l := list; l != nil; l = l.Next {
66 if l.N.Op == ODCLFUNC && l.N.Curfn == nil {
71 for l := list; l != nil; l = l.Next {
76 func (v *bottomUpVisitor) visit(n *Node) uint32 {
83 n.Walkgen = v.visitgen
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.
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
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.
107 for l = v.stack; l.N != n; l = l.Next {
108 l.N.Walkgen = ^uint32(0)
110 n.Walkgen = ^uint32(0)
114 // Run escape analysis on this set of functions.
115 v.analyze(block, recursive)
121 func (v *bottomUpVisitor) visitcodelist(l *NodeList, min uint32) uint32 {
122 for ; l != nil; l = l.Next {
123 min = v.visitcode(l.N, min)
128 func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 {
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)
143 if n.Op == OCALLFUNC || n.Op == OCALLMETH {
145 if n.Op == OCALLMETH {
146 fn = n.Left.Right.Sym.Def
148 if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && fn.Defn != nil {
149 m := v.visit(fn.Defn)
156 if n.Op == OCLOSURE {
157 m := v.visit(n.Closure)
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.
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.
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.
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.
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.
197 func escapes(all *NodeList) {
198 visitBottomUp(all, escAnalyze)
202 EscFuncUnknown = 0 + iota
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
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.
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.
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:
241 Fatal("escape mktag")
246 if mask < len(tags) && tags[mask] != nil {
250 s := fmt.Sprintf("esc:0x%x", mask)
251 if mask < len(tags) {
257 func parsetag(note *string) int {
258 if note == nil || !strings.HasPrefix(*note, "esc:") {
261 em := atoi((*note)[4:])
265 return EscReturn | em<<EscBits
268 func escAnalyze(all *NodeList, recursive bool) {
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
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
284 for l := all; l != nil; l = l.Next {
285 if l.N.Op == ODCLFUNC {
286 l.N.Esc = EscFuncPlanned
290 // flow-analyze functions
291 for l := all; l != nil; l = l.Next {
292 if l.N.Op == ODCLFUNC {
297 // print("escapes: %d e->dsts, %d edges\n", e->dstcount, e->edgecount);
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 {
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 {
313 for l := e.noesc; l != nil; l = l.Next {
314 if l.N.Esc == EscNone {
316 if l.N.Curfn != nil && l.N.Curfn.Nname != nil {
317 tmp = l.N.Curfn.Nname.Sym
321 Warnl(int(l.N.Lineno), "%v %v does not escape", Sconv(tmp, 0), Nconv(l.N, obj.FmtShort))
327 func escfunc(e *EscState, func_ *Node) {
328 // print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":"");
331 Fatal("repeat escfunc %v", Nconv(func_.Nname, 0))
333 func_.Esc = EscFuncStarted
335 saveld := e.loopdepth
340 for ll := Curfn.Dcl; ll != nil; ll = ll.Next {
341 if ll.N.Op != ONAME {
345 // out params are in a loopdepth between the sink and all local variables
347 ll.N.Escloopdepth = 0
350 ll.N.Escloopdepth = 1
351 if ll.N.Type != nil && !haspointers(ll.N.Type) {
354 if Curfn.Nbody == nil && !Curfn.Noescape {
357 ll.N.Esc = EscNone // prime for escflood later
359 e.noesc = list(e.noesc, ll.N)
363 // in a mutually recursive group we lose track of the return values
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)
372 escloopdepthlist(e, Curfn.Nbody)
373 esclist(e, Curfn.Nbody, Curfn)
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.
385 func escloopdepthlist(e *EscState, l *NodeList) {
386 for ; l != nil; l = l.Next {
391 func escloopdepth(e *EscState, n *Node) {
396 escloopdepthlist(e, n.Ninit)
400 if n.Left == nil || n.Left.Sym == nil {
401 Fatal("esc:label without label: %v", Nconv(n, obj.FmtSign))
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
411 if n.Left == nil || n.Left.Sym == nil {
412 Fatal("esc:goto without label: %v", Nconv(n, obj.FmtSign))
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
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)
432 func esclist(e *EscState, l *NodeList, up *Node) {
433 for ; l != nil; l = l.Next {
438 func esc(e *EscState, n *Node, up *Node) {
443 lno := int(setlineno(n))
445 // ninit logically runs at a different loopdepth than the rest of the for loop.
446 esclist(e, n.Ninit, n)
448 if n.Op == OFOR || n.Op == ORANGE {
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
459 // ll->n->nname is the variable per case
460 if ll.N.Nname != nil {
461 ll.N.Nname.Escloopdepth = e.loopdepth
470 esclist(e, n.Nbody, n)
471 esclist(e, n.Nelse, n)
472 esclist(e, n.List, n)
473 esclist(e, n.Rlist, n)
475 if n.Op == OFOR || n.Op == ORANGE {
481 if Curfn != nil && Curfn.Nname != nil {
482 tmp = Curfn.Nname.Sym
486 fmt.Printf("%v:[%d] %v esc: %v\n", Ctxt.Line(int(lineno)), e.loopdepth, Sconv(tmp, 0), Nconv(n, 0))
490 // Record loop depth at declaration.
493 n.Left.Escloopdepth = e.loopdepth
497 if n.Left.Sym.Label == &nonlooping {
499 fmt.Printf("%v:%v non-looping label\n", Ctxt.Line(int(lineno)), Nconv(n, 0))
501 } else if n.Left.Sym.Label == &looping {
503 fmt.Printf("%v: %v looping label\n", Ctxt.Line(int(lineno)), Nconv(n, 0))
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);
512 n.Left.Sym.Label = nil
514 // Everything but fixed array is a dereference.
516 if Isfixedarray(n.Type) && n.List != nil && n.List.Next != nil {
517 escassign(e, n.List.Next.N, n.Right)
521 if n.Ntest != nil && n.Ntest.Op == OTYPESW {
522 for ll := n.List; ll != nil; ll = ll.Next { // cases
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)
530 // Filter out the following special case.
532 // func (b *Buffer) Foo() {
534 // b.buf = b.buf[n:m]
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.
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
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.
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.
558 if n.Curfn != nil && n.Curfn.Nname != nil {
559 tmp = n.Curfn.Nname.Sym
563 Warnl(int(n.Lineno), "%v ignoring self-assignment to %v", Sconv(tmp, 0), Nconv(n.Left, obj.FmtShort))
569 escassign(e, n.Left, n.Right)
571 case OAS2: // x,y = a,b
572 if count(n.List) == count(n.Rlist) {
575 for ; ll != nil; ll, lr = ll.Next, lr.Next {
576 escassign(e, ll.N, lr.N)
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)
585 case OSEND: // ch <- x
586 escassign(e, &e.theSink, n.Right)
589 if e.loopdepth == 1 { // top level
592 // arguments leak out of scope
593 // TODO: leak to a dummy node instead
597 // go f(x) - f and x escape
598 escassign(e, &e.theSink, n.Left.Left)
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)
605 case OCALLMETH, OCALLFUNC, OCALLINTER:
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
613 for ll = n.List; lr != nil && ll != nil; lr, ll = lr.Next, ll.Next {
614 escassign(e, ll.N, lr.N)
616 if lr != nil || ll != nil {
617 Fatal("esc oas2func")
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
629 for lr := Curfn.Dcl; lr != nil && ll != nil; lr = lr.Next {
630 if lr.N.Op != ONAME || lr.N.Class != PPARAMOUT {
633 escassign(e, lr.N, ll.N)
638 Fatal("esc return list")
641 // Argument could leak through recover.
643 escassign(e, &e.theSink, n.Left)
647 for ll := n.List.Next; ll != nil; ll = ll.Next {
648 escassign(e, &e.theSink, ll.N) // lose track of assign to dereference
652 case OCONV, OCONVNOP:
653 escassign(e, n, n.Left)
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)
663 n.Esc = EscNone // until proven otherwise
664 e.noesc = list(e.noesc, n)
665 n.Escloopdepth = e.loopdepth
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)
672 // Link values to array.
673 for ll := n.List; ll != nil; ll = ll.Next {
674 escassign(e, n, ll.N.Right)
678 // Link values to struct.
680 for ll := n.List; ll != nil; ll = ll.Next {
681 escassign(e, n, ll.N.Right)
685 n.Esc = EscNone // until proven otherwise
686 e.noesc = list(e.noesc, n)
687 n.Escloopdepth = e.loopdepth
689 // Link OSTRUCTLIT to OPTRLIT; if OPTRLIT escapes, OSTRUCTLIT elements do too.
690 escassign(e, n, n.Left)
693 n.Esc = EscNone // until proven otherwise
694 e.noesc = list(e.noesc, n)
695 n.Escloopdepth = e.loopdepth
697 // Contents make it to memory, lose track.
698 escassign(e, &e.theSink, n.Left)
701 n.Esc = EscNone // until proven otherwise
702 e.noesc = list(e.noesc, n)
703 n.Escloopdepth = e.loopdepth
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)
711 // Link addresses of captured variables to closure.
715 for ll := n.Cvars; ll != nil; ll = ll.Next {
717 if v.Op == OXXX { // unnamed out argument; see dcl.c:/^funcargs
722 a = Nod(OADDR, a, nil)
724 a.Escloopdepth = e.loopdepth
742 n.Escloopdepth = e.loopdepth
744 n.Esc = EscNone // until proven otherwise
745 e.noesc = list(e.noesc, n)
748 n.Escloopdepth = e.loopdepth
749 n.Esc = EscNone // until proven otherwise
750 e.noesc = list(e.noesc, n)
752 // Arguments of OADDSTR do not escape.
755 n.Esc = EscNone // until proven otherwise
756 e.noesc = list(e.noesc, n)
758 // current loop depth is an upper bound on actual loop depth
759 // of addressed value.
760 n.Escloopdepth = e.loopdepth
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 {
768 if n.Left.Escloopdepth != 0 {
769 n.Escloopdepth = n.Left.Escloopdepth
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:
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 {
798 if Curfn != nil && Curfn.Nname != nil {
799 tmp = Curfn.Nname.Sym
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))
808 // Analyze lhs of assignment.
809 // Replace dst with e->theSink if we can't track it.
813 Fatal("escassign: unexpected dst")
827 if dst.Class == PEXTERN {
831 case ODOT: // treat "dst.x = src" as "dst = src"
832 escassign(e, dst.Left, src)
837 if Isfixedarray(dst.Left.Type) {
838 escassign(e, dst.Left, src)
842 dst = &e.theSink // lose track of dereference
845 dst = &e.theSink // lose track of dereference
847 // lose track of key and value
849 escassign(e, &e.theSink, dst.Right)
854 lno := int(setlineno(src))
858 case OADDR, // dst = &x
860 ODOTPTR, // dst = (*x).f
881 escflows(e, dst, src)
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)
890 // A non-pointer escaping from a struct does not concern us.
892 if src.Type != nil && !haspointers(src.Type) {
897 // Conversions, field access, slice all preserve the input value.
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
911 // Conversions, field access, slice all preserve the input value.
912 escassign(e, dst, src.Left)
915 // Append returns first argument.
916 escassign(e, dst, src.List.N)
919 // Index of array preserves input value.
920 if Isfixedarray(src.Left.Type) {
921 escassign(e, dst, src.Left)
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.
941 escassign(e, dst, src.Left)
943 escassign(e, dst, src.Right)
950 func escassignfromtag(e *EscState, note *string, dsts *NodeList, src *Node) int {
953 if em == EscUnknown {
954 escassign(e, &e.theSink, src)
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)
969 for em >>= EscReturnBits; em != 0 && dsts != nil; em, dsts = em>>1, dsts.Next {
971 escassign(e, dsts.N, src)
975 if em != 0 && dsts == nil {
976 Fatal("corrupt esc tag %q or messed up escretval list\n", note)
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
987 func esccall(e *EscState, n *Node, up *Node) {
1000 fn = n.Left.Right.Sym.Def
1004 fntype = n.Left.Type
1008 fntype = n.Left.Type
1012 if n.List != nil && n.List.Next == nil {
1014 if a.Type.Etype == TSTRUCT && a.Type.Funarg != 0 { // f(g()).
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")
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)
1032 if n.Op != OCALLFUNC {
1033 escassign(e, fn.Ntype.Left.Left, n.Left.Left)
1037 for lr := fn.Ntype.List; ll != nil && lr != nil; ll, lr = ll.Next, lr.Next {
1039 if lr.N.Isddd && !n.Isddd {
1040 // Introduce ODDDARG node to represent ... allocation.
1041 src = Nod(ODDDARG, nil, nil)
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)
1054 if lr.N.Left != nil {
1055 escassign(e, lr.N.Left, src)
1062 // "..." arguments are untracked
1063 for ; ll != nil; ll = ll.Next {
1064 escassign(e, &e.theSink, ll.N)
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))
1075 // set up out list on this call node with dummy auto ONAMES in the current (calling) function.
1080 for t := getoutargx(fntype).Type; t != nil; t = t.Down {
1081 src = Nod(ONAME, nil, nil)
1082 buf = fmt.Sprintf(".dum%d", i)
1084 src.Sym = Lookup(buf)
1088 src.Escloopdepth = e.loopdepth
1090 src.Lineno = n.Lineno
1091 n.Escretval = list(n.Escretval, src)
1094 // print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval);
1097 if n.Op != OCALLFUNC {
1098 t := getthisx(fntype).Type
1100 if haspointers(t.Type) {
1101 escassignfromtag(e, t.Note, n.Escretval, src)
1106 for t := getinargx(fntype).Type; ll != nil; ll = ll.Next {
1108 if t.Isddd && !n.Isddd {
1109 // Introduce ODDDARG node to represent ... allocation.
1110 src = Nod(ODDDARG, nil, nil)
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)
1123 if haspointers(t.Type) {
1124 if escassignfromtag(e, t.Note, n.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC {
1126 for a.Op == OCONVNOP {
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.
1156 // "..." arguments are untracked
1157 for ; ll != nil; ll = ll.Next {
1158 escassign(e, &e.theSink, ll.N)
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 {
1168 // Don't bother building a graph for scalars.
1169 if src.Type != nil && !haspointers(src.Type) {
1174 fmt.Printf("%v::flows:: %v <- %v\n", Ctxt.Line(int(lineno)), Nconv(dst, obj.FmtShort), Nconv(src, obj.FmtShort))
1177 if dst.Escflowsrc == nil {
1178 e.dsts = list(e.dsts, dst)
1184 dst.Escflowsrc = list(dst.Escflowsrc, src)
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) {
1198 case ONAME, OCLOSURE:
1207 if dst.Curfn != nil && dst.Curfn.Nname != nil {
1208 tmp = dst.Curfn.Nname.Sym
1212 fmt.Printf("\nescflood:%d: dst %v scope:%v[%d]\n", walkgen, Nconv(dst, obj.FmtShort), Sconv(tmp, 0), dst.Escloopdepth)
1215 for l := dst.Escflowsrc; l != nil; l = l.Next {
1217 escwalk(e, 0, dst, l.N)
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.
1236 func escwalk(e *EscState, level int, dst *Node, src *Node) {
1237 if src.Walkgen == walkgen && src.Esclevel <= int32(level) {
1240 src.Walkgen = walkgen
1241 src.Esclevel = int32(level)
1245 if src.Curfn != nil && src.Curfn.Nname != nil {
1246 tmp = src.Curfn.Nname.Sym
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)
1255 // Input parameter flowing to output parameter?
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 {
1260 if Debug['m'] != 0 {
1261 Warnl(int(src.Lineno), "leaking param: %v to result %v", Nconv(src, obj.FmtShort), Sconv(dst.Sym, 0))
1263 if src.Esc&EscMask != EscReturn {
1266 src.Esc |= 1 << uint((dst.Vargen-1)+EscReturnBits)
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))
1272 if src.Esc&EscMask != EscReturn {
1275 src.Esc |= EscContentEscapes
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)
1288 if src.Class == PPARAM && (leaks || dst.Escloopdepth < 0) && src.Esc != EscHeap {
1290 if Debug['m'] != 0 {
1291 Warnl(int(src.Lineno), "leaking param: %v", Nconv(src, obj.FmtShort))
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))
1301 escwalk(e, level, dst, src.Closure)
1304 case OPTRLIT, OADDR:
1307 addrescapes(src.Left)
1308 if Debug['m'] != 0 {
1309 Warnl(int(src.Lineno), "%v escapes to heap", Nconv(src, obj.FmtShort))
1314 if level > MinLevel {
1317 escwalk(e, newlevel, dst, src.Left)
1320 if Isfixedarray(src.Type) {
1343 if Debug['m'] != 0 {
1344 Warnl(int(src.Lineno), "%v escapes to heap", Nconv(src, obj.FmtShort))
1354 escwalk(e, level, dst, src.Left)
1357 if Isfixedarray(src.Left.Type) {
1358 escwalk(e, level, dst, src.Left)
1364 case ODOTPTR, OINDEXMAP, OIND:
1367 if level > MinLevel {
1370 escwalk(e, newlevel, dst, src.Left)
1374 for ll := src.Escflowsrc; ll != nil; ll = ll.Next {
1375 escwalk(e, level, dst, ll.N)
1381 func esctag(e *EscState, func_ *Node) {
1382 func_.Esc = EscFuncTagged
1384 // External functions are assumed unsafe,
1385 // unless //go:noescape is given before the declaration.
1386 if func_.Nbody == nil {
1388 for t := getinargx(func_.Type).Type; t != nil; t = t.Down {
1389 if haspointers(t.Type) {
1390 t.Note = mktag(EscNone)
1401 for ll := Curfn.Dcl; ll != nil; ll = ll.Next {
1402 if ll.N.Op != ONAME || ll.N.Class != PPARAM {
1406 switch ll.N.Esc & EscMask {
1407 case EscNone, // not touched by escflood
1409 if haspointers(ll.N.Type) { // don't bother tagging for scalars
1410 ll.N.Paramfld.Note = mktag(int(ll.N.Esc))
1413 case EscHeap, // touched by escflood, moved to heap
1414 EscScope: // touched by escflood, value leaves scope