github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/bootstrap/internal/gc/esc.go (about)

     1  // Do not edit. Bootstrap copy of /Users/rsc/g/go/src/cmd/internal/gc/esc.go
     2  
     3  // Copyright 2011 The Go Authors. All rights reserved.
     4  // Use of this source code is governed by a BSD-style
     5  // license that can be found in the LICENSE file.
     6  
     7  package gc
     8  
     9  import (
    10  	"rsc.io/tmp/bootstrap/internal/obj"
    11  	"fmt"
    12  	"strings"
    13  )
    14  
    15  // Run analysis on minimal sets of mutually recursive functions
    16  // or single non-recursive functions, bottom up.
    17  //
    18  // Finding these sets is finding strongly connected components
    19  // in the static call graph.  The algorithm for doing that is taken
    20  // from Sedgewick, Algorithms, Second Edition, p. 482, with two
    21  // adaptations.
    22  //
    23  // First, a hidden closure function (n->curfn != N) cannot be the
    24  // root of a connected component. Refusing to use it as a root
    25  // forces it into the component of the function in which it appears.
    26  // This is more convenient for escape analysis.
    27  //
    28  // Second, each function becomes two virtual nodes in the graph,
    29  // with numbers n and n+1. We record the function's node number as n
    30  // but search from node n+1. If the search tells us that the component
    31  // number (min) is n+1, we know that this is a trivial component: one function
    32  // plus its closures. If the search tells us that the component number is
    33  // n, then there was a path from node n+1 back to node n, meaning that
    34  // the function set is mutually recursive. The escape analysis can be
    35  // more precise when analyzing a single non-recursive function than
    36  // when analyzing a set of mutually recursive functions.
    37  
    38  // TODO(rsc): Look into using a map[*Node]bool instead of walkgen,
    39  // to allow analysis passes to use walkgen themselves.
    40  
    41  type bottomUpVisitor struct {
    42  	analyze  func(*NodeList, bool)
    43  	visitgen uint32
    44  	stack    *NodeList
    45  }
    46  
    47  // visitBottomUp invokes analyze on the ODCLFUNC nodes listed in list.
    48  // It calls analyze with successive groups of functions, working from
    49  // the bottom of the call graph upward. Each time analyze is called with
    50  // a list of functions, every function on that list only calls other functions
    51  // on the list or functions that have been passed in previous invocations of
    52  // analyze. Closures appear in the same list as their outer functions.
    53  // The lists are as short as possible while preserving those requirements.
    54  // (In a typical program, many invocations of analyze will be passed just
    55  // a single function.) The boolean argument 'recursive' passed to analyze
    56  // specifies whether the functions on the list are mutually recursive.
    57  // If recursive is false, the list consists of only a single function and its closures.
    58  // If recursive is true, the list may still contain only a single function,
    59  // if that function is itself recursive.
    60  func visitBottomUp(list *NodeList, analyze func(list *NodeList, recursive bool)) {
    61  	for l := list; l != nil; l = l.Next {
    62  		l.N.Walkgen = 0
    63  	}
    64  
    65  	var v bottomUpVisitor
    66  	v.analyze = analyze
    67  	for l := list; l != nil; l = l.Next {
    68  		if l.N.Op == ODCLFUNC && l.N.Curfn == nil {
    69  			v.visit(l.N)
    70  		}
    71  	}
    72  
    73  	for l := list; l != nil; l = l.Next {
    74  		l.N.Walkgen = 0
    75  	}
    76  }
    77  
    78  func (v *bottomUpVisitor) visit(n *Node) uint32 {
    79  	if n.Walkgen > 0 {
    80  		// already visited
    81  		return n.Walkgen
    82  	}
    83  
    84  	v.visitgen++
    85  	n.Walkgen = v.visitgen
    86  	v.visitgen++
    87  	min := v.visitgen
    88  
    89  	l := new(NodeList)
    90  	l.Next = v.stack
    91  	l.N = n
    92  	v.stack = l
    93  	min = v.visitcodelist(n.Nbody, min)
    94  	if (min == n.Walkgen || min == n.Walkgen+1) && n.Curfn == nil {
    95  		// This node is the root of a strongly connected component.
    96  
    97  		// The original min passed to visitcodelist was n->walkgen+1.
    98  		// If visitcodelist found its way back to n->walkgen, then this
    99  		// block is a set of mutually recursive functions.
   100  		// Otherwise it's just a lone function that does not recurse.
   101  		recursive := min == n.Walkgen
   102  
   103  		// Remove connected component from stack.
   104  		// Mark walkgen so that future visits return a large number
   105  		// so as not to affect the caller's min.
   106  		block := v.stack
   107  
   108  		var l *NodeList
   109  		for l = v.stack; l.N != n; l = l.Next {
   110  			l.N.Walkgen = ^uint32(0)
   111  		}
   112  		n.Walkgen = ^uint32(0)
   113  		v.stack = l.Next
   114  		l.Next = nil
   115  
   116  		// Run escape analysis on this set of functions.
   117  		v.analyze(block, recursive)
   118  	}
   119  
   120  	return min
   121  }
   122  
   123  func (v *bottomUpVisitor) visitcodelist(l *NodeList, min uint32) uint32 {
   124  	for ; l != nil; l = l.Next {
   125  		min = v.visitcode(l.N, min)
   126  	}
   127  	return min
   128  }
   129  
   130  func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 {
   131  	if n == nil {
   132  		return min
   133  	}
   134  
   135  	min = v.visitcodelist(n.Ninit, min)
   136  	min = v.visitcode(n.Left, min)
   137  	min = v.visitcode(n.Right, min)
   138  	min = v.visitcodelist(n.List, min)
   139  	min = v.visitcode(n.Ntest, min)
   140  	min = v.visitcode(n.Nincr, min)
   141  	min = v.visitcodelist(n.Nbody, min)
   142  	min = v.visitcodelist(n.Nelse, min)
   143  	min = v.visitcodelist(n.Rlist, min)
   144  
   145  	if n.Op == OCALLFUNC || n.Op == OCALLMETH {
   146  		fn := n.Left
   147  		if n.Op == OCALLMETH {
   148  			fn = n.Left.Right.Sym.Def
   149  		}
   150  		if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && fn.Defn != nil {
   151  			m := v.visit(fn.Defn)
   152  			if m < min {
   153  				min = m
   154  			}
   155  		}
   156  	}
   157  
   158  	if n.Op == OCLOSURE {
   159  		m := v.visit(n.Closure)
   160  		if m < min {
   161  			min = m
   162  		}
   163  	}
   164  
   165  	return min
   166  }
   167  
   168  // Escape analysis.
   169  
   170  // An escape analysis pass for a set of functions.
   171  // The analysis assumes that closures and the functions in which they
   172  // appear are analyzed together, so that the aliasing between their
   173  // variables can be modeled more precisely.
   174  //
   175  // First escfunc, esc and escassign recurse over the ast of each
   176  // function to dig out flow(dst,src) edges between any
   177  // pointer-containing nodes and store them in dst->escflowsrc.  For
   178  // variables assigned to a variable in an outer scope or used as a
   179  // return value, they store a flow(theSink, src) edge to a fake node
   180  // 'the Sink'.  For variables referenced in closures, an edge
   181  // flow(closure, &var) is recorded and the flow of a closure itself to
   182  // an outer scope is tracked the same way as other variables.
   183  //
   184  // Then escflood walks the graph starting at theSink and tags all
   185  // variables of it can reach an & node as escaping and all function
   186  // parameters it can reach as leaking.
   187  //
   188  // If a value's address is taken but the address does not escape,
   189  // then the value can stay on the stack.  If the value new(T) does
   190  // not escape, then new(T) can be rewritten into a stack allocation.
   191  // The same is true of slice literals.
   192  //
   193  // If optimizations are disabled (-N), this code is not used.
   194  // Instead, the compiler assumes that any value whose address
   195  // is taken without being immediately dereferenced
   196  // needs to be moved to the heap, and new(T) and slice
   197  // literals are always real allocations.
   198  
   199  func escapes(all *NodeList) {
   200  	visitBottomUp(all, escAnalyze)
   201  }
   202  
   203  const (
   204  	EscFuncUnknown = 0 + iota
   205  	EscFuncPlanned
   206  	EscFuncStarted
   207  	EscFuncTagged
   208  )
   209  
   210  type EscState struct {
   211  	// Fake node that all
   212  	//   - return values and output variables
   213  	//   - parameters on imported functions not marked 'safe'
   214  	//   - assignments to global variables
   215  	// flow to.
   216  	theSink Node
   217  
   218  	// If an analyzed function is recorded to return
   219  	// pieces obtained via indirection from a parameter,
   220  	// and later there is a call f(x) to that function,
   221  	// we create a link funcParam <- x to record that fact.
   222  	// The funcParam node is handled specially in escflood.
   223  	funcParam Node
   224  
   225  	dsts      *NodeList // all dst nodes
   226  	loopdepth int       // for detecting nested loop scopes
   227  	pdepth    int       // for debug printing in recursions.
   228  	dstcount  int       // diagnostic
   229  	edgecount int       // diagnostic
   230  	noesc     *NodeList // list of possible non-escaping nodes, for printing
   231  	recursive bool      // recursive function or group of mutually recursive functions.
   232  }
   233  
   234  var tags [16]*string
   235  
   236  // mktag returns the string representation for an escape analysis tag.
   237  func mktag(mask int) *string {
   238  	switch mask & EscMask {
   239  	case EscNone, EscReturn:
   240  		break
   241  
   242  	default:
   243  		Fatal("escape mktag")
   244  	}
   245  
   246  	mask >>= EscBits
   247  
   248  	if mask < len(tags) && tags[mask] != nil {
   249  		return tags[mask]
   250  	}
   251  
   252  	s := fmt.Sprintf("esc:0x%x", mask)
   253  	if mask < len(tags) {
   254  		tags[mask] = &s
   255  	}
   256  	return &s
   257  }
   258  
   259  func parsetag(note *string) int {
   260  	if note == nil || !strings.HasPrefix(*note, "esc:") {
   261  		return EscUnknown
   262  	}
   263  	em := atoi((*note)[4:])
   264  	if em == 0 {
   265  		return EscNone
   266  	}
   267  	return EscReturn | em<<EscBits
   268  }
   269  
   270  func escAnalyze(all *NodeList, recursive bool) {
   271  	var es EscState
   272  	e := &es
   273  	e.theSink.Op = ONAME
   274  	e.theSink.Orig = &e.theSink
   275  	e.theSink.Class = PEXTERN
   276  	e.theSink.Sym = Lookup(".sink")
   277  	e.theSink.Escloopdepth = -1
   278  	e.recursive = recursive
   279  
   280  	e.funcParam.Op = ONAME
   281  	e.funcParam.Orig = &e.funcParam
   282  	e.funcParam.Class = PAUTO
   283  	e.funcParam.Sym = Lookup(".param")
   284  	e.funcParam.Escloopdepth = 10000000
   285  
   286  	for l := all; l != nil; l = l.Next {
   287  		if l.N.Op == ODCLFUNC {
   288  			l.N.Esc = EscFuncPlanned
   289  		}
   290  	}
   291  
   292  	// flow-analyze functions
   293  	for l := all; l != nil; l = l.Next {
   294  		if l.N.Op == ODCLFUNC {
   295  			escfunc(e, l.N)
   296  		}
   297  	}
   298  
   299  	// print("escapes: %d e->dsts, %d edges\n", e->dstcount, e->edgecount);
   300  
   301  	// visit the upstream of each dst, mark address nodes with
   302  	// addrescapes, mark parameters unsafe
   303  	for l := e.dsts; l != nil; l = l.Next {
   304  		escflood(e, l.N)
   305  	}
   306  
   307  	// for all top level functions, tag the typenodes corresponding to the param nodes
   308  	for l := all; l != nil; l = l.Next {
   309  		if l.N.Op == ODCLFUNC {
   310  			esctag(e, l.N)
   311  		}
   312  	}
   313  
   314  	if Debug['m'] != 0 {
   315  		for l := e.noesc; l != nil; l = l.Next {
   316  			if l.N.Esc == EscNone {
   317  				var tmp *Sym
   318  				if l.N.Curfn != nil && l.N.Curfn.Nname != nil {
   319  					tmp = l.N.Curfn.Nname.Sym
   320  				} else {
   321  					tmp = nil
   322  				}
   323  				Warnl(int(l.N.Lineno), "%v %v does not escape", tmp, Nconv(l.N, obj.FmtShort))
   324  			}
   325  		}
   326  	}
   327  }
   328  
   329  func escfunc(e *EscState, func_ *Node) {
   330  	//	print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":"");
   331  
   332  	if func_.Esc != 1 {
   333  		Fatal("repeat escfunc %v", func_.Nname)
   334  	}
   335  	func_.Esc = EscFuncStarted
   336  
   337  	saveld := e.loopdepth
   338  	e.loopdepth = 1
   339  	savefn := Curfn
   340  	Curfn = func_
   341  
   342  	for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
   343  		if ll.N.Op != ONAME {
   344  			continue
   345  		}
   346  		switch ll.N.Class {
   347  		// out params are in a loopdepth between the sink and all local variables
   348  		case PPARAMOUT:
   349  			ll.N.Escloopdepth = 0
   350  
   351  		case PPARAM:
   352  			ll.N.Escloopdepth = 1
   353  			if ll.N.Type != nil && !haspointers(ll.N.Type) {
   354  				break
   355  			}
   356  			if Curfn.Nbody == nil && !Curfn.Noescape {
   357  				ll.N.Esc = EscHeap
   358  			} else {
   359  				ll.N.Esc = EscNone // prime for escflood later
   360  			}
   361  			e.noesc = list(e.noesc, ll.N)
   362  		}
   363  	}
   364  
   365  	// in a mutually recursive group we lose track of the return values
   366  	if e.recursive {
   367  		for ll := Curfn.Func.Dcl; ll != nil; ll = ll.Next {
   368  			if ll.N.Op == ONAME && ll.N.Class == PPARAMOUT {
   369  				escflows(e, &e.theSink, ll.N)
   370  			}
   371  		}
   372  	}
   373  
   374  	escloopdepthlist(e, Curfn.Nbody)
   375  	esclist(e, Curfn.Nbody, Curfn)
   376  	Curfn = savefn
   377  	e.loopdepth = saveld
   378  }
   379  
   380  // Mark labels that have no backjumps to them as not increasing e->loopdepth.
   381  // Walk hasn't generated (goto|label)->left->sym->label yet, so we'll cheat
   382  // and set it to one of the following two.  Then in esc we'll clear it again.
   383  var looping Label
   384  
   385  var nonlooping Label
   386  
   387  func escloopdepthlist(e *EscState, l *NodeList) {
   388  	for ; l != nil; l = l.Next {
   389  		escloopdepth(e, l.N)
   390  	}
   391  }
   392  
   393  func escloopdepth(e *EscState, n *Node) {
   394  	if n == nil {
   395  		return
   396  	}
   397  
   398  	escloopdepthlist(e, n.Ninit)
   399  
   400  	switch n.Op {
   401  	case OLABEL:
   402  		if n.Left == nil || n.Left.Sym == nil {
   403  			Fatal("esc:label without label: %v", Nconv(n, obj.FmtSign))
   404  		}
   405  
   406  		// Walk will complain about this label being already defined, but that's not until
   407  		// after escape analysis. in the future, maybe pull label & goto analysis out of walk and put before esc
   408  		// if(n->left->sym->label != nil)
   409  		//	fatal("escape analysis messed up analyzing label: %+N", n);
   410  		n.Left.Sym.Label = &nonlooping
   411  
   412  	case OGOTO:
   413  		if n.Left == nil || n.Left.Sym == nil {
   414  			Fatal("esc:goto without label: %v", Nconv(n, obj.FmtSign))
   415  		}
   416  
   417  		// If we come past one that's uninitialized, this must be a (harmless) forward jump
   418  		// but if it's set to nonlooping the label must have preceded this goto.
   419  		if n.Left.Sym.Label == &nonlooping {
   420  			n.Left.Sym.Label = &looping
   421  		}
   422  	}
   423  
   424  	escloopdepth(e, n.Left)
   425  	escloopdepth(e, n.Right)
   426  	escloopdepthlist(e, n.List)
   427  	escloopdepth(e, n.Ntest)
   428  	escloopdepth(e, n.Nincr)
   429  	escloopdepthlist(e, n.Nbody)
   430  	escloopdepthlist(e, n.Nelse)
   431  	escloopdepthlist(e, n.Rlist)
   432  }
   433  
   434  func esclist(e *EscState, l *NodeList, up *Node) {
   435  	for ; l != nil; l = l.Next {
   436  		esc(e, l.N, up)
   437  	}
   438  }
   439  
   440  func esc(e *EscState, n *Node, up *Node) {
   441  	if n == nil {
   442  		return
   443  	}
   444  
   445  	lno := int(setlineno(n))
   446  
   447  	// ninit logically runs at a different loopdepth than the rest of the for loop.
   448  	esclist(e, n.Ninit, n)
   449  
   450  	if n.Op == OFOR || n.Op == ORANGE {
   451  		e.loopdepth++
   452  	}
   453  
   454  	// type switch variables have no ODCL.
   455  	// process type switch as declaration.
   456  	// must happen before processing of switch body,
   457  	// so before recursion.
   458  	if n.Op == OSWITCH && n.Ntest != nil && n.Ntest.Op == OTYPESW {
   459  		for ll := n.List; ll != nil; ll = ll.Next { // cases
   460  
   461  			// ll->n->nname is the variable per case
   462  			if ll.N.Nname != nil {
   463  				ll.N.Nname.Escloopdepth = e.loopdepth
   464  			}
   465  		}
   466  	}
   467  
   468  	esc(e, n.Left, n)
   469  	esc(e, n.Right, n)
   470  	esc(e, n.Ntest, n)
   471  	esc(e, n.Nincr, n)
   472  	esclist(e, n.Nbody, n)
   473  	esclist(e, n.Nelse, n)
   474  	esclist(e, n.List, n)
   475  	esclist(e, n.Rlist, n)
   476  
   477  	if n.Op == OFOR || n.Op == ORANGE {
   478  		e.loopdepth--
   479  	}
   480  
   481  	if Debug['m'] > 1 {
   482  		var tmp *Sym
   483  		if Curfn != nil && Curfn.Nname != nil {
   484  			tmp = Curfn.Nname.Sym
   485  		} else {
   486  			tmp = nil
   487  		}
   488  		fmt.Printf("%v:[%d] %v esc: %v\n", Ctxt.Line(int(lineno)), e.loopdepth, tmp, n)
   489  	}
   490  
   491  	switch n.Op {
   492  	// Record loop depth at declaration.
   493  	case ODCL:
   494  		if n.Left != nil {
   495  			n.Left.Escloopdepth = e.loopdepth
   496  		}
   497  
   498  	case OLABEL:
   499  		if n.Left.Sym.Label == &nonlooping {
   500  			if Debug['m'] > 1 {
   501  				fmt.Printf("%v:%v non-looping label\n", Ctxt.Line(int(lineno)), n)
   502  			}
   503  		} else if n.Left.Sym.Label == &looping {
   504  			if Debug['m'] > 1 {
   505  				fmt.Printf("%v: %v looping label\n", Ctxt.Line(int(lineno)), n)
   506  			}
   507  			e.loopdepth++
   508  		}
   509  
   510  		// See case OLABEL in escloopdepth above
   511  		// else if(n->left->sym->label == nil)
   512  		//	fatal("escape analysis missed or messed up a label: %+N", n);
   513  
   514  		n.Left.Sym.Label = nil
   515  
   516  		// Everything but fixed array is a dereference.
   517  	case ORANGE:
   518  		if Isfixedarray(n.Type) && n.List != nil && n.List.Next != nil {
   519  			escassign(e, n.List.Next.N, n.Right)
   520  		}
   521  
   522  	case OSWITCH:
   523  		if n.Ntest != nil && n.Ntest.Op == OTYPESW {
   524  			for ll := n.List; ll != nil; ll = ll.Next { // cases
   525  
   526  				// ntest->right is the argument of the .(type),
   527  				// ll->n->nname is the variable per case
   528  				escassign(e, ll.N.Nname, n.Ntest.Right)
   529  			}
   530  		}
   531  
   532  		// Filter out the following special case.
   533  	//
   534  	//	func (b *Buffer) Foo() {
   535  	//		n, m := ...
   536  	//		b.buf = b.buf[n:m]
   537  	//	}
   538  	//
   539  	// This assignment is a no-op for escape analysis,
   540  	// it does not store any new pointers into b that were not already there.
   541  	// However, without this special case b will escape, because we assign to OIND/ODOTPTR.
   542  	case OAS, OASOP, OASWB:
   543  		if (n.Left.Op == OIND || n.Left.Op == ODOTPTR) && n.Left.Left.Op == ONAME && // dst is ONAME dereference
   544  			(n.Right.Op == OSLICE || n.Right.Op == OSLICE3 || n.Right.Op == OSLICESTR) && // src is slice operation
   545  			(n.Right.Left.Op == OIND || n.Right.Left.Op == ODOTPTR) && n.Right.Left.Left.Op == ONAME && // slice is applied to ONAME dereference
   546  			n.Left.Left == n.Right.Left.Left { // dst and src reference the same base ONAME
   547  
   548  			// Here we also assume that the statement will not contain calls,
   549  			// that is, that order will move any calls to init.
   550  			// Otherwise base ONAME value could change between the moments
   551  			// when we evaluate it for dst and for src.
   552  			//
   553  			// Note, this optimization does not apply to OSLICEARR,
   554  			// because it does introduce a new pointer into b that was not already there
   555  			// (pointer to b itself). After such assignment, if b contents escape,
   556  			// b escapes as well. If we ignore such OSLICEARR, we will conclude
   557  			// that b does not escape when b contents do.
   558  			if Debug['m'] != 0 {
   559  				var tmp *Sym
   560  				if n.Curfn != nil && n.Curfn.Nname != nil {
   561  					tmp = n.Curfn.Nname.Sym
   562  				} else {
   563  					tmp = nil
   564  				}
   565  				Warnl(int(n.Lineno), "%v ignoring self-assignment to %v", tmp, Nconv(n.Left, obj.FmtShort))
   566  			}
   567  
   568  			break
   569  		}
   570  
   571  		escassign(e, n.Left, n.Right)
   572  
   573  	case OAS2: // x,y = a,b
   574  		if count(n.List) == count(n.Rlist) {
   575  			ll := n.List
   576  			lr := n.Rlist
   577  			for ; ll != nil; ll, lr = ll.Next, lr.Next {
   578  				escassign(e, ll.N, lr.N)
   579  			}
   580  		}
   581  
   582  	case OAS2RECV, // v, ok = <-ch
   583  		OAS2MAPR,    // v, ok = m[k]
   584  		OAS2DOTTYPE: // v, ok = x.(type)
   585  		escassign(e, n.List.N, n.Rlist.N)
   586  
   587  	case OSEND: // ch <- x
   588  		escassign(e, &e.theSink, n.Right)
   589  
   590  	case ODEFER:
   591  		if e.loopdepth == 1 { // top level
   592  			break
   593  		}
   594  		// arguments leak out of scope
   595  		// TODO: leak to a dummy node instead
   596  		fallthrough
   597  
   598  	case OPROC:
   599  		// go f(x) - f and x escape
   600  		escassign(e, &e.theSink, n.Left.Left)
   601  
   602  		escassign(e, &e.theSink, n.Left.Right) // ODDDARG for call
   603  		for ll := n.Left.List; ll != nil; ll = ll.Next {
   604  			escassign(e, &e.theSink, ll.N)
   605  		}
   606  
   607  	case OCALLMETH, OCALLFUNC, OCALLINTER:
   608  		esccall(e, n, up)
   609  
   610  		// esccall already done on n->rlist->n. tie it's escretval to n->list
   611  	case OAS2FUNC: // x,y = f()
   612  		lr := n.Rlist.N.Escretval
   613  
   614  		var ll *NodeList
   615  		for ll = n.List; lr != nil && ll != nil; lr, ll = lr.Next, ll.Next {
   616  			escassign(e, ll.N, lr.N)
   617  		}
   618  		if lr != nil || ll != nil {
   619  			Fatal("esc oas2func")
   620  		}
   621  
   622  	case ORETURN:
   623  		ll := n.List
   624  		if count(n.List) == 1 && Curfn.Type.Outtuple > 1 {
   625  			// OAS2FUNC in disguise
   626  			// esccall already done on n->list->n
   627  			// tie n->list->n->escretval to curfn->dcl PPARAMOUT's
   628  			ll = n.List.N.Escretval
   629  		}
   630  
   631  		for lr := Curfn.Func.Dcl; lr != nil && ll != nil; lr = lr.Next {
   632  			if lr.N.Op != ONAME || lr.N.Class != PPARAMOUT {
   633  				continue
   634  			}
   635  			escassign(e, lr.N, ll.N)
   636  			ll = ll.Next
   637  		}
   638  
   639  		if ll != nil {
   640  			Fatal("esc return list")
   641  		}
   642  
   643  		// Argument could leak through recover.
   644  	case OPANIC:
   645  		escassign(e, &e.theSink, n.Left)
   646  
   647  	case OAPPEND:
   648  		if !n.Isddd {
   649  			for ll := n.List.Next; ll != nil; ll = ll.Next {
   650  				escassign(e, &e.theSink, ll.N) // lose track of assign to dereference
   651  			}
   652  		}
   653  
   654  	case OCONV, OCONVNOP:
   655  		escassign(e, n, n.Left)
   656  
   657  	case OCONVIFACE:
   658  		n.Esc = EscNone // until proven otherwise
   659  		e.noesc = list(e.noesc, n)
   660  		n.Escloopdepth = e.loopdepth
   661  		escassign(e, n, n.Left)
   662  
   663  	case OARRAYLIT:
   664  		if Isslice(n.Type) {
   665  			n.Esc = EscNone // until proven otherwise
   666  			e.noesc = list(e.noesc, n)
   667  			n.Escloopdepth = e.loopdepth
   668  
   669  			// Values make it to memory, lose track.
   670  			for ll := n.List; ll != nil; ll = ll.Next {
   671  				escassign(e, &e.theSink, ll.N.Right)
   672  			}
   673  		} else {
   674  			// Link values to array.
   675  			for ll := n.List; ll != nil; ll = ll.Next {
   676  				escassign(e, n, ll.N.Right)
   677  			}
   678  		}
   679  
   680  		// Link values to struct.
   681  	case OSTRUCTLIT:
   682  		for ll := n.List; ll != nil; ll = ll.Next {
   683  			escassign(e, n, ll.N.Right)
   684  		}
   685  
   686  	case OPTRLIT:
   687  		n.Esc = EscNone // until proven otherwise
   688  		e.noesc = list(e.noesc, n)
   689  		n.Escloopdepth = e.loopdepth
   690  
   691  		// Link OSTRUCTLIT to OPTRLIT; if OPTRLIT escapes, OSTRUCTLIT elements do too.
   692  		escassign(e, n, n.Left)
   693  
   694  	case OCALLPART:
   695  		n.Esc = EscNone // until proven otherwise
   696  		e.noesc = list(e.noesc, n)
   697  		n.Escloopdepth = e.loopdepth
   698  
   699  		// Contents make it to memory, lose track.
   700  		escassign(e, &e.theSink, n.Left)
   701  
   702  	case OMAPLIT:
   703  		n.Esc = EscNone // until proven otherwise
   704  		e.noesc = list(e.noesc, n)
   705  		n.Escloopdepth = e.loopdepth
   706  
   707  		// Keys and values make it to memory, lose track.
   708  		for ll := n.List; ll != nil; ll = ll.Next {
   709  			escassign(e, &e.theSink, ll.N.Left)
   710  			escassign(e, &e.theSink, ll.N.Right)
   711  		}
   712  
   713  		// Link addresses of captured variables to closure.
   714  	case OCLOSURE:
   715  		var a *Node
   716  		var v *Node
   717  		for ll := n.Func.Cvars; ll != nil; ll = ll.Next {
   718  			v = ll.N
   719  			if v.Op == OXXX { // unnamed out argument; see dcl.c:/^funcargs
   720  				continue
   721  			}
   722  			a = v.Closure
   723  			if !v.Byval {
   724  				a = Nod(OADDR, a, nil)
   725  				a.Lineno = v.Lineno
   726  				a.Escloopdepth = e.loopdepth
   727  				typecheck(&a, Erv)
   728  			}
   729  
   730  			escassign(e, n, a)
   731  		}
   732  		fallthrough
   733  
   734  	case OMAKECHAN,
   735  		OMAKEMAP,
   736  		OMAKESLICE,
   737  		ONEW,
   738  		OARRAYRUNESTR,
   739  		OARRAYBYTESTR,
   740  		OSTRARRAYRUNE,
   741  		OSTRARRAYBYTE,
   742  		ORUNESTR:
   743  		n.Escloopdepth = e.loopdepth
   744  
   745  		n.Esc = EscNone // until proven otherwise
   746  		e.noesc = list(e.noesc, n)
   747  
   748  	case OADDSTR:
   749  		n.Escloopdepth = e.loopdepth
   750  		n.Esc = EscNone // until proven otherwise
   751  		e.noesc = list(e.noesc, n)
   752  
   753  	// Arguments of OADDSTR do not escape.
   754  
   755  	case OADDR:
   756  		n.Esc = EscNone // until proven otherwise
   757  		e.noesc = list(e.noesc, n)
   758  
   759  		// current loop depth is an upper bound on actual loop depth
   760  		// of addressed value.
   761  		n.Escloopdepth = e.loopdepth
   762  
   763  		// for &x, use loop depth of x if known.
   764  		// it should always be known, but if not, be conservative
   765  		// and keep the current loop depth.
   766  		if n.Left.Op == ONAME {
   767  			switch n.Left.Class {
   768  			case PAUTO:
   769  				if n.Left.Escloopdepth != 0 {
   770  					n.Escloopdepth = n.Left.Escloopdepth
   771  				}
   772  
   773  				// PPARAM is loop depth 1 always.
   774  			// PPARAMOUT is loop depth 0 for writes
   775  			// but considered loop depth 1 for address-of,
   776  			// so that writing the address of one result
   777  			// to another (or the same) result makes the
   778  			// first result move to the heap.
   779  			case PPARAM, PPARAMOUT:
   780  				n.Escloopdepth = 1
   781  			}
   782  		}
   783  	}
   784  
   785  	lineno = int32(lno)
   786  }
   787  
   788  // Assert that expr somehow gets assigned to dst, if non nil.  for
   789  // dst==nil, any name node expr still must be marked as being
   790  // evaluated in curfn.	For expr==nil, dst must still be examined for
   791  // evaluations inside it (e.g *f(x) = y)
   792  func escassign(e *EscState, dst *Node, src *Node) {
   793  	if isblank(dst) || dst == nil || src == nil || src.Op == ONONAME || src.Op == OXXX {
   794  		return
   795  	}
   796  
   797  	if Debug['m'] > 1 {
   798  		var tmp *Sym
   799  		if Curfn != nil && Curfn.Nname != nil {
   800  			tmp = Curfn.Nname.Sym
   801  		} else {
   802  			tmp = nil
   803  		}
   804  		fmt.Printf("%v:[%d] %v escassign: %v(%v) = %v(%v)\n", Ctxt.Line(int(lineno)), e.loopdepth, tmp, Nconv(dst, obj.FmtShort), Jconv(dst, obj.FmtShort), Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort))
   805  	}
   806  
   807  	setlineno(dst)
   808  
   809  	// Analyze lhs of assignment.
   810  	// Replace dst with e->theSink if we can't track it.
   811  	switch dst.Op {
   812  	default:
   813  		Dump("dst", dst)
   814  		Fatal("escassign: unexpected dst")
   815  
   816  	case OARRAYLIT,
   817  		OCLOSURE,
   818  		OCONV,
   819  		OCONVIFACE,
   820  		OCONVNOP,
   821  		OMAPLIT,
   822  		OSTRUCTLIT,
   823  		OPTRLIT,
   824  		OCALLPART:
   825  		break
   826  
   827  	case ONAME:
   828  		if dst.Class == PEXTERN {
   829  			dst = &e.theSink
   830  		}
   831  
   832  	case ODOT: // treat "dst.x  = src" as "dst = src"
   833  		escassign(e, dst.Left, src)
   834  
   835  		return
   836  
   837  	case OINDEX:
   838  		if Isfixedarray(dst.Left.Type) {
   839  			escassign(e, dst.Left, src)
   840  			return
   841  		}
   842  
   843  		dst = &e.theSink // lose track of dereference
   844  
   845  	case OIND, ODOTPTR:
   846  		dst = &e.theSink // lose track of dereference
   847  
   848  		// lose track of key and value
   849  	case OINDEXMAP:
   850  		escassign(e, &e.theSink, dst.Right)
   851  
   852  		dst = &e.theSink
   853  	}
   854  
   855  	lno := int(setlineno(src))
   856  	e.pdepth++
   857  
   858  	switch src.Op {
   859  	case OADDR, // dst = &x
   860  		OIND,    // dst = *x
   861  		ODOTPTR, // dst = (*x).f
   862  		ONAME,
   863  		OPARAM,
   864  		ODDDARG,
   865  		OPTRLIT,
   866  		OARRAYLIT,
   867  		OMAPLIT,
   868  		OSTRUCTLIT,
   869  		OMAKECHAN,
   870  		OMAKEMAP,
   871  		OMAKESLICE,
   872  		OARRAYRUNESTR,
   873  		OARRAYBYTESTR,
   874  		OSTRARRAYRUNE,
   875  		OSTRARRAYBYTE,
   876  		OADDSTR,
   877  		ONEW,
   878  		OCALLPART,
   879  		ORUNESTR,
   880  		OCONVIFACE:
   881  		escflows(e, dst, src)
   882  
   883  	case OCLOSURE:
   884  		// OCLOSURE is lowered to OPTRLIT,
   885  		// insert OADDR to account for the additional indirection.
   886  		a := Nod(OADDR, src, nil)
   887  		a.Lineno = src.Lineno
   888  		a.Escloopdepth = src.Escloopdepth
   889  		a.Type = Ptrto(src.Type)
   890  		escflows(e, dst, a)
   891  
   892  		// Flowing multiple returns to a single dst happens when
   893  	// analyzing "go f(g())": here g() flows to sink (issue 4529).
   894  	case OCALLMETH, OCALLFUNC, OCALLINTER:
   895  		for ll := src.Escretval; ll != nil; ll = ll.Next {
   896  			escflows(e, dst, ll.N)
   897  		}
   898  
   899  		// A non-pointer escaping from a struct does not concern us.
   900  	case ODOT:
   901  		if src.Type != nil && !haspointers(src.Type) {
   902  			break
   903  		}
   904  		fallthrough
   905  
   906  		// Conversions, field access, slice all preserve the input value.
   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), tmp, 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), tmp, 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), dst.Sym)
  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", src.Curfn.Nname, Nconv(src, obj.FmtShort), dst.Sym)
  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  }