github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/cmd/internal/gc/esc.go (about)

     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.Func.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.Func.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.Func.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.Func.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  		OCALLPART,
   878  		ORUNESTR,
   879  		OCONVIFACE:
   880  		escflows(e, dst, src)
   881  
   882  	case OCLOSURE:
   883  		// OCLOSURE is lowered to OPTRLIT,
   884  		// insert OADDR to account for the additional indirection.
   885  		a := Nod(OADDR, src, nil)
   886  		a.Lineno = src.Lineno
   887  		a.Escloopdepth = src.Escloopdepth
   888  		a.Type = Ptrto(src.Type)
   889  		escflows(e, dst, a)
   890  
   891  		// Flowing multiple returns to a single dst happens when
   892  	// analyzing "go f(g())": here g() flows to sink (issue 4529).
   893  	case OCALLMETH, OCALLFUNC, OCALLINTER:
   894  		for ll := src.Escretval; ll != nil; ll = ll.Next {
   895  			escflows(e, dst, ll.N)
   896  		}
   897  
   898  		// A non-pointer escaping from a struct does not concern us.
   899  	case ODOT:
   900  		if src.Type != nil && !haspointers(src.Type) {
   901  			break
   902  		}
   903  		fallthrough
   904  
   905  		// Conversions, field access, slice all preserve the input value.
   906  	// fallthrough
   907  	case OCONV,
   908  		OCONVNOP,
   909  		ODOTMETH,
   910  		// treat recv.meth as a value with recv in it, only happens in ODEFER and OPROC
   911  		// iface.method already leaks iface in esccall, no need to put in extra ODOTINTER edge here
   912  		ODOTTYPE,
   913  		ODOTTYPE2,
   914  		OSLICE,
   915  		OSLICE3,
   916  		OSLICEARR,
   917  		OSLICE3ARR,
   918  		OSLICESTR:
   919  		// Conversions, field access, slice all preserve the input value.
   920  		escassign(e, dst, src.Left)
   921  
   922  	case OAPPEND:
   923  		// Append returns first argument.
   924  		escassign(e, dst, src.List.N)
   925  
   926  	case OINDEX:
   927  		// Index of array preserves input value.
   928  		if Isfixedarray(src.Left.Type) {
   929  			escassign(e, dst, src.Left)
   930  		}
   931  
   932  		// Might be pointer arithmetic, in which case
   933  	// the operands flow into the result.
   934  	// TODO(rsc): Decide what the story is here.  This is unsettling.
   935  	case OADD,
   936  		OSUB,
   937  		OOR,
   938  		OXOR,
   939  		OMUL,
   940  		ODIV,
   941  		OMOD,
   942  		OLSH,
   943  		ORSH,
   944  		OAND,
   945  		OANDNOT,
   946  		OPLUS,
   947  		OMINUS,
   948  		OCOM:
   949  		escassign(e, dst, src.Left)
   950  
   951  		escassign(e, dst, src.Right)
   952  	}
   953  
   954  	e.pdepth--
   955  	lineno = int32(lno)
   956  }
   957  
   958  func escassignfromtag(e *EscState, note *string, dsts *NodeList, src *Node) int {
   959  	em := parsetag(note)
   960  
   961  	if em == EscUnknown {
   962  		escassign(e, &e.theSink, src)
   963  		return em
   964  	}
   965  
   966  	if em == EscNone {
   967  		return em
   968  	}
   969  
   970  	// If content inside parameter (reached via indirection)
   971  	// escapes back to results, mark as such.
   972  	if em&EscContentEscapes != 0 {
   973  		escassign(e, &e.funcParam, src)
   974  	}
   975  
   976  	em0 := em
   977  	for em >>= EscReturnBits; em != 0 && dsts != nil; em, dsts = em>>1, dsts.Next {
   978  		if em&1 != 0 {
   979  			escassign(e, dsts.N, src)
   980  		}
   981  	}
   982  
   983  	if em != 0 && dsts == nil {
   984  		Fatal("corrupt esc tag %q or messed up escretval list\n", note)
   985  	}
   986  	return em0
   987  }
   988  
   989  // This is a bit messier than fortunate, pulled out of esc's big
   990  // switch for clarity.	We either have the paramnodes, which may be
   991  // connected to other things through flows or we have the parameter type
   992  // nodes, which may be marked "noescape". Navigating the ast is slightly
   993  // different for methods vs plain functions and for imported vs
   994  // this-package
   995  func esccall(e *EscState, n *Node, up *Node) {
   996  	var fntype *Type
   997  
   998  	var fn *Node
   999  	switch n.Op {
  1000  	default:
  1001  		Fatal("esccall")
  1002  
  1003  	case OCALLFUNC:
  1004  		fn = n.Left
  1005  		fntype = fn.Type
  1006  
  1007  	case OCALLMETH:
  1008  		fn = n.Left.Right.Sym.Def
  1009  		if fn != nil {
  1010  			fntype = fn.Type
  1011  		} else {
  1012  			fntype = n.Left.Type
  1013  		}
  1014  
  1015  	case OCALLINTER:
  1016  		fntype = n.Left.Type
  1017  	}
  1018  
  1019  	ll := n.List
  1020  	if n.List != nil && n.List.Next == nil {
  1021  		a := n.List.N
  1022  		if a.Type.Etype == TSTRUCT && a.Type.Funarg != 0 { // f(g()).
  1023  			ll = a.Escretval
  1024  		}
  1025  	}
  1026  
  1027  	if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && fn.Defn != nil && fn.Defn.Nbody != nil && fn.Ntype != nil && fn.Defn.Esc < EscFuncTagged {
  1028  		// function in same mutually recursive group.  Incorporate into flow graph.
  1029  		//		print("esc local fn: %N\n", fn->ntype);
  1030  		if fn.Defn.Esc == EscFuncUnknown || n.Escretval != nil {
  1031  			Fatal("graph inconsistency")
  1032  		}
  1033  
  1034  		// set up out list on this call node
  1035  		for lr := fn.Ntype.Rlist; lr != nil; lr = lr.Next {
  1036  			n.Escretval = list(n.Escretval, lr.N.Left) // type.rlist ->  dclfield -> ONAME (PPARAMOUT)
  1037  		}
  1038  
  1039  		// Receiver.
  1040  		if n.Op != OCALLFUNC {
  1041  			escassign(e, fn.Ntype.Left.Left, n.Left.Left)
  1042  		}
  1043  
  1044  		var src *Node
  1045  		for lr := fn.Ntype.List; ll != nil && lr != nil; ll, lr = ll.Next, lr.Next {
  1046  			src = ll.N
  1047  			if lr.N.Isddd && !n.Isddd {
  1048  				// Introduce ODDDARG node to represent ... allocation.
  1049  				src = Nod(ODDDARG, nil, nil)
  1050  
  1051  				src.Type = typ(TARRAY)
  1052  				src.Type.Type = lr.N.Type.Type
  1053  				src.Type.Bound = int64(count(ll))
  1054  				src.Type = Ptrto(src.Type) // make pointer so it will be tracked
  1055  				src.Escloopdepth = e.loopdepth
  1056  				src.Lineno = n.Lineno
  1057  				src.Esc = EscNone // until we find otherwise
  1058  				e.noesc = list(e.noesc, src)
  1059  				n.Right = src
  1060  			}
  1061  
  1062  			if lr.N.Left != nil {
  1063  				escassign(e, lr.N.Left, src)
  1064  			}
  1065  			if src != ll.N {
  1066  				break
  1067  			}
  1068  		}
  1069  
  1070  		// "..." arguments are untracked
  1071  		for ; ll != nil; ll = ll.Next {
  1072  			escassign(e, &e.theSink, ll.N)
  1073  		}
  1074  
  1075  		return
  1076  	}
  1077  
  1078  	// Imported or completely analyzed function.  Use the escape tags.
  1079  	if n.Escretval != nil {
  1080  		Fatal("esc already decorated call %v\n", Nconv(n, obj.FmtSign))
  1081  	}
  1082  
  1083  	// set up out list on this call node with dummy auto ONAMES in the current (calling) function.
  1084  	i := 0
  1085  
  1086  	var src *Node
  1087  	var buf string
  1088  	for t := getoutargx(fntype).Type; t != nil; t = t.Down {
  1089  		src = Nod(ONAME, nil, nil)
  1090  		buf = fmt.Sprintf(".dum%d", i)
  1091  		i++
  1092  		src.Sym = Lookup(buf)
  1093  		src.Type = t.Type
  1094  		src.Class = PAUTO
  1095  		src.Curfn = Curfn
  1096  		src.Escloopdepth = e.loopdepth
  1097  		src.Used = true
  1098  		src.Lineno = n.Lineno
  1099  		n.Escretval = list(n.Escretval, src)
  1100  	}
  1101  
  1102  	//	print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval);
  1103  
  1104  	// Receiver.
  1105  	if n.Op != OCALLFUNC {
  1106  		t := getthisx(fntype).Type
  1107  		src := n.Left.Left
  1108  		if haspointers(t.Type) {
  1109  			escassignfromtag(e, t.Note, n.Escretval, src)
  1110  		}
  1111  	}
  1112  
  1113  	var a *Node
  1114  	for t := getinargx(fntype).Type; ll != nil; ll = ll.Next {
  1115  		src = ll.N
  1116  		if t.Isddd && !n.Isddd {
  1117  			// Introduce ODDDARG node to represent ... allocation.
  1118  			src = Nod(ODDDARG, nil, nil)
  1119  
  1120  			src.Escloopdepth = e.loopdepth
  1121  			src.Lineno = n.Lineno
  1122  			src.Type = typ(TARRAY)
  1123  			src.Type.Type = t.Type.Type
  1124  			src.Type.Bound = int64(count(ll))
  1125  			src.Type = Ptrto(src.Type) // make pointer so it will be tracked
  1126  			src.Esc = EscNone          // until we find otherwise
  1127  			e.noesc = list(e.noesc, src)
  1128  			n.Right = src
  1129  		}
  1130  
  1131  		if haspointers(t.Type) {
  1132  			if escassignfromtag(e, t.Note, n.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC {
  1133  				a = src
  1134  				for a.Op == OCONVNOP {
  1135  					a = a.Left
  1136  				}
  1137  				switch a.Op {
  1138  				// The callee has already been analyzed, so its arguments have esc tags.
  1139  				// The argument is marked as not escaping at all.
  1140  				// Record that fact so that any temporary used for
  1141  				// synthesizing this expression can be reclaimed when
  1142  				// the function returns.
  1143  				// This 'noescape' is even stronger than the usual esc == EscNone.
  1144  				// src->esc == EscNone means that src does not escape the current function.
  1145  				// src->noescape = 1 here means that src does not escape this statement
  1146  				// in the current function.
  1147  				case OCALLPART,
  1148  					OCLOSURE,
  1149  					ODDDARG,
  1150  					OARRAYLIT,
  1151  					OPTRLIT,
  1152  					OSTRUCTLIT:
  1153  					a.Noescape = true
  1154  				}
  1155  			}
  1156  		}
  1157  
  1158  		if src != ll.N {
  1159  			break
  1160  		}
  1161  		t = t.Down
  1162  	}
  1163  
  1164  	// "..." arguments are untracked
  1165  	for ; ll != nil; ll = ll.Next {
  1166  		escassign(e, &e.theSink, ll.N)
  1167  	}
  1168  }
  1169  
  1170  // Store the link src->dst in dst, throwing out some quick wins.
  1171  func escflows(e *EscState, dst *Node, src *Node) {
  1172  	if dst == nil || src == nil || dst == src {
  1173  		return
  1174  	}
  1175  
  1176  	// Don't bother building a graph for scalars.
  1177  	if src.Type != nil && !haspointers(src.Type) {
  1178  		return
  1179  	}
  1180  
  1181  	if Debug['m'] > 2 {
  1182  		fmt.Printf("%v::flows:: %v <- %v\n", Ctxt.Line(int(lineno)), Nconv(dst, obj.FmtShort), Nconv(src, obj.FmtShort))
  1183  	}
  1184  
  1185  	if dst.Escflowsrc == nil {
  1186  		e.dsts = list(e.dsts, dst)
  1187  		e.dstcount++
  1188  	}
  1189  
  1190  	e.edgecount++
  1191  
  1192  	dst.Escflowsrc = list(dst.Escflowsrc, src)
  1193  }
  1194  
  1195  // Whenever we hit a reference node, the level goes up by one, and whenever
  1196  // we hit an OADDR, the level goes down by one. as long as we're on a level > 0
  1197  // finding an OADDR just means we're following the upstream of a dereference,
  1198  // so this address doesn't leak (yet).
  1199  // If level == 0, it means the /value/ of this node can reach the root of this flood.
  1200  // so if this node is an OADDR, it's argument should be marked as escaping iff
  1201  // it's currfn/e->loopdepth are different from the flood's root.
  1202  // Once an object has been moved to the heap, all of it's upstream should be considered
  1203  // escaping to the global scope.
  1204  func escflood(e *EscState, dst *Node) {
  1205  	switch dst.Op {
  1206  	case ONAME, OCLOSURE:
  1207  		break
  1208  
  1209  	default:
  1210  		return
  1211  	}
  1212  
  1213  	if Debug['m'] > 1 {
  1214  		var tmp *Sym
  1215  		if dst.Curfn != nil && dst.Curfn.Nname != nil {
  1216  			tmp = dst.Curfn.Nname.Sym
  1217  		} else {
  1218  			tmp = nil
  1219  		}
  1220  		fmt.Printf("\nescflood:%d: dst %v scope:%v[%d]\n", walkgen, Nconv(dst, obj.FmtShort), Sconv(tmp, 0), dst.Escloopdepth)
  1221  	}
  1222  
  1223  	for l := dst.Escflowsrc; l != nil; l = l.Next {
  1224  		walkgen++
  1225  		escwalk(e, 0, dst, l.N)
  1226  	}
  1227  }
  1228  
  1229  // There appear to be some loops in the escape graph, causing
  1230  // arbitrary recursion into deeper and deeper levels.
  1231  // Cut this off safely by making minLevel sticky: once you
  1232  // get that deep, you cannot go down any further but you also
  1233  // cannot go up any further. This is a conservative fix.
  1234  // Making minLevel smaller (more negative) would handle more
  1235  // complex chains of indirections followed by address-of operations,
  1236  // at the cost of repeating the traversal once for each additional
  1237  // allowed level when a loop is encountered. Using -2 suffices to
  1238  // pass all the tests we have written so far, which we assume matches
  1239  // the level of complexity we want the escape analysis code to handle.
  1240  const (
  1241  	MinLevel = -2
  1242  )
  1243  
  1244  func escwalk(e *EscState, level int, dst *Node, src *Node) {
  1245  	if src.Walkgen == walkgen && src.Esclevel <= int32(level) {
  1246  		return
  1247  	}
  1248  	src.Walkgen = walkgen
  1249  	src.Esclevel = int32(level)
  1250  
  1251  	if Debug['m'] > 1 {
  1252  		var tmp *Sym
  1253  		if src.Curfn != nil && src.Curfn.Nname != nil {
  1254  			tmp = src.Curfn.Nname.Sym
  1255  		} else {
  1256  			tmp = nil
  1257  		}
  1258  		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)
  1259  	}
  1260  
  1261  	e.pdepth++
  1262  
  1263  	// Input parameter flowing to output parameter?
  1264  	var leaks bool
  1265  	if dst.Op == ONAME && dst.Class == PPARAMOUT && dst.Vargen <= 20 {
  1266  		if src.Op == ONAME && src.Class == PPARAM && src.Curfn == dst.Curfn && src.Esc != EscScope && src.Esc != EscHeap {
  1267  			if level == 0 {
  1268  				if Debug['m'] != 0 {
  1269  					Warnl(int(src.Lineno), "leaking param: %v to result %v", Nconv(src, obj.FmtShort), Sconv(dst.Sym, 0))
  1270  				}
  1271  				if src.Esc&EscMask != EscReturn {
  1272  					src.Esc = EscReturn
  1273  				}
  1274  				src.Esc |= 1 << uint((dst.Vargen-1)+EscReturnBits)
  1275  				goto recurse
  1276  			} else if level > 0 {
  1277  				if Debug['m'] != 0 {
  1278  					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))
  1279  				}
  1280  				if src.Esc&EscMask != EscReturn {
  1281  					src.Esc = EscReturn
  1282  				}
  1283  				src.Esc |= EscContentEscapes
  1284  				goto recurse
  1285  			}
  1286  		}
  1287  	}
  1288  
  1289  	// The second clause is for values pointed at by an object passed to a call
  1290  	// that returns something reached via indirect from the object.
  1291  	// We don't know which result it is or how many indirects, so we treat it as leaking.
  1292  	leaks = level <= 0 && dst.Escloopdepth < src.Escloopdepth || level < 0 && dst == &e.funcParam && haspointers(src.Type)
  1293  
  1294  	switch src.Op {
  1295  	case ONAME:
  1296  		if src.Class == PPARAM && (leaks || dst.Escloopdepth < 0) && src.Esc != EscHeap {
  1297  			src.Esc = EscScope
  1298  			if Debug['m'] != 0 {
  1299  				Warnl(int(src.Lineno), "leaking param: %v", Nconv(src, obj.FmtShort))
  1300  			}
  1301  		}
  1302  
  1303  		// Treat a PPARAMREF closure variable as equivalent to the
  1304  		// original variable.
  1305  		if src.Class == PPARAMREF {
  1306  			if leaks && Debug['m'] != 0 {
  1307  				Warnl(int(src.Lineno), "leaking closure reference %v", Nconv(src, obj.FmtShort))
  1308  			}
  1309  			escwalk(e, level, dst, src.Closure)
  1310  		}
  1311  
  1312  	case OPTRLIT, OADDR:
  1313  		if leaks {
  1314  			src.Esc = EscHeap
  1315  			addrescapes(src.Left)
  1316  			if Debug['m'] != 0 {
  1317  				p := src
  1318  				if p.Left.Op == OCLOSURE {
  1319  					p = p.Left // merely to satisfy error messages in tests
  1320  				}
  1321  				Warnl(int(src.Lineno), "%v escapes to heap", Nconv(p, obj.FmtShort))
  1322  			}
  1323  		}
  1324  
  1325  		newlevel := level
  1326  		if level > MinLevel {
  1327  			newlevel--
  1328  		}
  1329  		escwalk(e, newlevel, dst, src.Left)
  1330  
  1331  	case OARRAYLIT:
  1332  		if Isfixedarray(src.Type) {
  1333  			break
  1334  		}
  1335  		fallthrough
  1336  
  1337  		// fall through
  1338  	case ODDDARG,
  1339  		OMAKECHAN,
  1340  		OMAKEMAP,
  1341  		OMAKESLICE,
  1342  		OARRAYRUNESTR,
  1343  		OARRAYBYTESTR,
  1344  		OSTRARRAYRUNE,
  1345  		OSTRARRAYBYTE,
  1346  		OADDSTR,
  1347  		OMAPLIT,
  1348  		ONEW,
  1349  		OCLOSURE,
  1350  		OCALLPART,
  1351  		ORUNESTR,
  1352  		OCONVIFACE:
  1353  		if leaks {
  1354  			src.Esc = EscHeap
  1355  			if Debug['m'] != 0 {
  1356  				Warnl(int(src.Lineno), "%v escapes to heap", Nconv(src, obj.FmtShort))
  1357  			}
  1358  		}
  1359  
  1360  	case ODOT,
  1361  		OSLICE,
  1362  		OSLICEARR,
  1363  		OSLICE3,
  1364  		OSLICE3ARR,
  1365  		OSLICESTR:
  1366  		escwalk(e, level, dst, src.Left)
  1367  
  1368  	case OINDEX:
  1369  		if Isfixedarray(src.Left.Type) {
  1370  			escwalk(e, level, dst, src.Left)
  1371  			break
  1372  		}
  1373  		fallthrough
  1374  
  1375  		// fall through
  1376  	case ODOTPTR, OINDEXMAP, OIND:
  1377  		newlevel := level
  1378  
  1379  		if level > MinLevel {
  1380  			newlevel++
  1381  		}
  1382  		escwalk(e, newlevel, dst, src.Left)
  1383  	}
  1384  
  1385  recurse:
  1386  	for ll := src.Escflowsrc; ll != nil; ll = ll.Next {
  1387  		escwalk(e, level, dst, ll.N)
  1388  	}
  1389  
  1390  	e.pdepth--
  1391  }
  1392  
  1393  func esctag(e *EscState, func_ *Node) {
  1394  	func_.Esc = EscFuncTagged
  1395  
  1396  	// External functions are assumed unsafe,
  1397  	// unless //go:noescape is given before the declaration.
  1398  	if func_.Nbody == nil {
  1399  		if func_.Noescape {
  1400  			for t := getinargx(func_.Type).Type; t != nil; t = t.Down {
  1401  				if haspointers(t.Type) {
  1402  					t.Note = mktag(EscNone)
  1403  				}
  1404  			}
  1405  		}
  1406  
  1407  		return
  1408  	}
  1409  
  1410  	savefn := Curfn
  1411  	Curfn = func_
  1412  
  1413  	for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
  1414  		if ll.N.Op != ONAME || ll.N.Class != PPARAM {
  1415  			continue
  1416  		}
  1417  
  1418  		switch ll.N.Esc & EscMask {
  1419  		case EscNone, // not touched by escflood
  1420  			EscReturn:
  1421  			if haspointers(ll.N.Type) { // don't bother tagging for scalars
  1422  				ll.N.Paramfld.Note = mktag(int(ll.N.Esc))
  1423  			}
  1424  
  1425  		case EscHeap, // touched by escflood, moved to heap
  1426  			EscScope: // touched by escflood, value leaves scope
  1427  			break
  1428  		}
  1429  	}
  1430  
  1431  	Curfn = savefn
  1432  }