Source file src/cmd/compile/internal/escape/escape.go

     1  // Copyright 2018 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 escape
     6  
     7  import (
     8  	"fmt"
     9  	"go/constant"
    10  	"go/token"
    11  	"internal/goexperiment"
    12  	"slices"
    13  
    14  	"cmd/compile/internal/base"
    15  	"cmd/compile/internal/ir"
    16  	"cmd/compile/internal/logopt"
    17  	"cmd/compile/internal/typecheck"
    18  	"cmd/compile/internal/types"
    19  	"cmd/internal/src"
    20  )
    21  
    22  // Escape analysis.
    23  //
    24  // Here we analyze functions to determine which Go variables
    25  // (including implicit allocations such as calls to "new" or "make",
    26  // composite literals, etc.) can be allocated on the stack. The two
    27  // key invariants we have to ensure are: (1) pointers to stack objects
    28  // cannot be stored in the heap, and (2) pointers to a stack object
    29  // cannot outlive that object (e.g., because the declaring function
    30  // returned and destroyed the object's stack frame, or its space is
    31  // reused across loop iterations for logically distinct variables).
    32  //
    33  // We implement this with a static data-flow analysis of the AST.
    34  // First, we construct a directed weighted graph where vertices
    35  // (termed "locations") represent variables allocated by statements
    36  // and expressions, and edges represent assignments between variables
    37  // (with weights representing addressing/dereference counts).
    38  //
    39  // Next we walk the graph looking for assignment paths that might
    40  // violate the invariants stated above. If a variable v's address is
    41  // stored in the heap or elsewhere that may outlive it, then v is
    42  // marked as requiring heap allocation.
    43  //
    44  // To support interprocedural analysis, we also record data-flow from
    45  // each function's parameters to the heap and to its result
    46  // parameters. This information is summarized as "parameter tags",
    47  // which are used at static call sites to improve escape analysis of
    48  // function arguments.
    49  
    50  // Constructing the location graph.
    51  //
    52  // Every allocating statement (e.g., variable declaration) or
    53  // expression (e.g., "new" or "make") is first mapped to a unique
    54  // "location."
    55  //
    56  // We also model every Go assignment as a directed edges between
    57  // locations. The number of dereference operations minus the number of
    58  // addressing operations is recorded as the edge's weight (termed
    59  // "derefs"). For example:
    60  //
    61  //     p = &q    // -1
    62  //     p = q     //  0
    63  //     p = *q    //  1
    64  //     p = **q   //  2
    65  //
    66  //     p = **&**&q  // 2
    67  //
    68  // Note that the & operator can only be applied to addressable
    69  // expressions, and the expression &x itself is not addressable, so
    70  // derefs cannot go below -1.
    71  //
    72  // Every Go language construct is lowered into this representation,
    73  // generally without sensitivity to flow, path, or context; and
    74  // without distinguishing elements within a compound variable. For
    75  // example:
    76  //
    77  //     var x struct { f, g *int }
    78  //     var u []*int
    79  //
    80  //     x.f = u[0]
    81  //
    82  // is modeled simply as
    83  //
    84  //     x = *u
    85  //
    86  // That is, we don't distinguish x.f from x.g, or u[0] from u[1],
    87  // u[2], etc. However, we do record the implicit dereference involved
    88  // in indexing a slice.
    89  
    90  // A batch holds escape analysis state that's shared across an entire
    91  // batch of functions being analyzed at once.
    92  type batch struct {
    93  	allLocs         []*location
    94  	closures        []closure
    95  	reassignOracles map[*ir.Func]*ir.ReassignOracle
    96  
    97  	// staleOracles collects the ReassignOracles that cover a function whose IR
    98  	// was modified by rewriteClosureVarsWithLiterals, so that they can be
    99  	// dropped from reassignOracles once the rewriting is done.
   100  	staleOracles []*ir.ReassignOracle
   101  
   102  	heapLoc    location
   103  	mutatorLoc location
   104  	calleeLoc  location
   105  	blankLoc   location
   106  }
   107  
   108  // A closure holds a closure expression and its spill hole (i.e.,
   109  // where the hole representing storing into its closure record).
   110  type closure struct {
   111  	k   hole
   112  	clo *ir.ClosureExpr
   113  }
   114  
   115  // An escape holds state specific to a single function being analyzed
   116  // within a batch.
   117  type escape struct {
   118  	*batch
   119  
   120  	curfn *ir.Func // function being analyzed
   121  
   122  	labels map[*types.Sym]labelState // known labels
   123  
   124  	// loopDepth counts the current loop nesting depth within
   125  	// curfn. It increments within each "for" loop and at each
   126  	// label with a corresponding backwards "goto" (i.e.,
   127  	// unstructured loop).
   128  	loopDepth int
   129  }
   130  
   131  func Funcs(all []*ir.Func) {
   132  	// Make a cache of ir.ReassignOracles. The cache is lazily populated.
   133  	// TODO(thepudds): consider adding a field on ir.Func instead. We might also be able
   134  	// to use that field elsewhere, like in walk. See discussion in https://go.dev/cl/688075.
   135  	reassignOracles := make(map[*ir.Func]*ir.ReassignOracle)
   136  
   137  	ir.VisitFuncsBottomUp(all, func(list []*ir.Func, recursive bool) {
   138  		Batch(list, reassignOracles)
   139  	})
   140  }
   141  
   142  // Batch performs escape analysis on a minimal batch of
   143  // functions.
   144  func Batch(fns []*ir.Func, reassignOracles map[*ir.Func]*ir.ReassignOracle) {
   145  	var b batch
   146  	b.heapLoc.attrs = attrEscapes | attrPersists | attrMutates | attrCalls
   147  	b.mutatorLoc.attrs = attrMutates
   148  	b.calleeLoc.attrs = attrCalls
   149  	b.reassignOracles = reassignOracles
   150  
   151  	// Construct data-flow graph from syntax trees.
   152  	for _, fn := range fns {
   153  		if base.Flag.W > 1 {
   154  			s := fmt.Sprintf("\nbefore escape %v", fn)
   155  			ir.Dump(s, fn)
   156  		}
   157  		b.initFunc(fn)
   158  	}
   159  	for _, fn := range fns {
   160  		if !fn.IsClosure() {
   161  			b.walkFunc(fn)
   162  		}
   163  	}
   164  
   165  	// We've walked the function bodies, so we've seen everywhere a
   166  	// variable might be reassigned or have its address taken. Now we
   167  	// can decide whether closures should capture their free variables
   168  	// by value or reference.
   169  	for _, closure := range b.closures {
   170  		b.flowClosure(closure.k, closure.clo)
   171  	}
   172  	b.closures = nil
   173  	b.invalidateStaleOracles()
   174  
   175  	for _, loc := range b.allLocs {
   176  		// Try to replace some non-constant expressions with literals.
   177  		b.rewriteWithLiterals(loc.n, loc.curfn)
   178  
   179  		// Check if the node must be heap allocated for certain reasons
   180  		// such as OMAKESLICE for a large slice.
   181  		if why := HeapAllocReason(loc.n); why != "" {
   182  			b.flow(b.heapHole().addr(loc.n, why), loc)
   183  		}
   184  	}
   185  
   186  	b.walkAll()
   187  	b.finish(fns)
   188  }
   189  
   190  func (b *batch) with(fn *ir.Func) *escape {
   191  	return &escape{
   192  		batch:     b,
   193  		curfn:     fn,
   194  		loopDepth: 1,
   195  	}
   196  }
   197  
   198  func (b *batch) initFunc(fn *ir.Func) {
   199  	e := b.with(fn)
   200  	if fn.Esc() != escFuncUnknown {
   201  		base.Fatalf("unexpected node: %v", fn)
   202  	}
   203  	fn.SetEsc(escFuncPlanned)
   204  	if base.Flag.LowerM > 3 {
   205  		ir.Dump("escAnalyze", fn)
   206  	}
   207  
   208  	// Allocate locations for local variables.
   209  	for _, n := range fn.Dcl {
   210  		e.newLoc(n, true)
   211  	}
   212  
   213  	// Also for hidden parameters (e.g., the ".this" parameter to a
   214  	// method value wrapper).
   215  	if fn.OClosure == nil {
   216  		for _, n := range fn.ClosureVars {
   217  			e.newLoc(n.Canonical(), true)
   218  		}
   219  	}
   220  
   221  	// Initialize resultIndex for result parameters.
   222  	for i, f := range fn.Type().Results() {
   223  		e.oldLoc(f.Nname.(*ir.Name)).resultIndex = 1 + i
   224  	}
   225  }
   226  
   227  func (b *batch) walkFunc(fn *ir.Func) {
   228  	e := b.with(fn)
   229  	fn.SetEsc(escFuncStarted)
   230  
   231  	// Identify labels that mark the head of an unstructured loop.
   232  	ir.Visit(fn, func(n ir.Node) {
   233  		switch n.Op() {
   234  		case ir.OLABEL:
   235  			n := n.(*ir.LabelStmt)
   236  			if n.Label.IsBlank() {
   237  				break
   238  			}
   239  			if e.labels == nil {
   240  				e.labels = make(map[*types.Sym]labelState)
   241  			}
   242  			e.labels[n.Label] = nonlooping
   243  
   244  		case ir.OGOTO:
   245  			// If we visited the label before the goto,
   246  			// then this is a looping label.
   247  			n := n.(*ir.BranchStmt)
   248  			if e.labels[n.Label] == nonlooping {
   249  				e.labels[n.Label] = looping
   250  			}
   251  		}
   252  	})
   253  
   254  	e.block(fn.Body)
   255  
   256  	if len(e.labels) != 0 {
   257  		base.FatalfAt(fn.Pos(), "leftover labels after walkFunc")
   258  	}
   259  }
   260  
   261  func (b *batch) flowClosure(k hole, clo *ir.ClosureExpr) {
   262  	for _, cv := range clo.Func.ClosureVars {
   263  		n := cv.Canonical()
   264  		loc := b.oldLoc(cv)
   265  		if !loc.captured {
   266  			base.FatalfAt(cv.Pos(), "closure variable never captured: %v", cv)
   267  		}
   268  
   269  		// Capture by value for variables <= 128 bytes that are never reassigned.
   270  		n.SetByval(!loc.addrtaken && !loc.reassigned && n.Type().Size() <= 128)
   271  		if !n.Byval() {
   272  			n.SetAddrtaken(true)
   273  			if n.Sym().Name == typecheck.LocalDictName {
   274  				base.FatalfAt(n.Pos(), "dictionary variable not captured by value")
   275  			}
   276  		}
   277  	}
   278  
   279  	// Now that we know which variables are captured by value, try to avoid
   280  	// capturing the ones that hold a constant altogether.
   281  	b.rewriteClosureVarsWithLiterals(clo.Func)
   282  
   283  	for _, cv := range clo.Func.ClosureVars {
   284  		n := cv.Canonical()
   285  		loc := b.oldLoc(cv)
   286  
   287  		if base.Flag.LowerM > 1 {
   288  			how := "ref"
   289  			if n.Byval() {
   290  				how = "value"
   291  			}
   292  			base.WarnfAt(n.Pos(), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", n.Curfn, how, n, loc.addrtaken, loc.reassigned, n.Type().Size())
   293  		}
   294  
   295  		// Flow captured variables to closure.
   296  		k := k
   297  		if !cv.Byval() {
   298  			k = k.addr(cv, "reference")
   299  		}
   300  		b.flow(k.note(cv, "captured by a closure"), loc)
   301  	}
   302  }
   303  
   304  // rewriteClosureVarsWithLiterals rewrites the variables that clofn captures by
   305  // value and whose value is a known constant into ordinary local variables of
   306  // clofn, initialized with that constant, so that clofn does not need to
   307  // capture them. A closure that is left capturing nothing is not a closure
   308  // anymore, and walkClosure can then refer to its function directly instead of
   309  // building a closure record for it. See #5370.
   310  //
   311  // Whether a captured variable is rewritten must depend on its canonical
   312  // variable only, so that every closure capturing it reaches the same decision.
   313  // A closure nested inside clofn reads the variables it captures out of clofn's
   314  // closure record, so rewriting a capture here while leaving the nested closure
   315  // capturing it would leave that closure with nothing to read from.
   316  func (b *batch) rewriteClosureVarsWithLiterals(clofn *ir.Func) {
   317  	// All the copies that inlining made of a closure share a single linker
   318  	// symbol, so we must not specialize the body of just one of them: every
   319  	// copy would end up capturing the same constants. In particular, a copy
   320  	// that captures nothing is referred to through its func value symbol,
   321  	// f.func1·f, and there is one of those per copy under the very same name.
   322  	if clofn.IsInlinedClosure() {
   323  		return
   324  	}
   325  
   326  	var ro *ir.ReassignOracle // initialized lazily below, if needed
   327  	var repl map[*ir.Name]*ir.Name
   328  	var prefix ir.Nodes
   329  
   330  	for _, cv := range clofn.ClosureVars {
   331  		// Only variables captured by value can hold a constant: escape analysis
   332  		// has just proven those are neither address taken nor ever reassigned.
   333  		if !cv.Byval() || !ir.ValidTypeForConst(cv.Type(), constant.MakeUnknown()) {
   334  			continue
   335  		}
   336  
   337  		// Look up a cached ReassignOracle for the closure, lazily computing one if needed.
   338  		if ro == nil {
   339  			ro = b.reassignOracle(clofn)
   340  			if ro == nil {
   341  				base.Fatalf("no ReassignOracle for function %v with closure parent %v", clofn, clofn.ClosureParent)
   342  			}
   343  		}
   344  		lit, ok := ro.StaticValue(cv).(*ir.BasicLit)
   345  		if !ok || !ir.ValidTypeForConst(cv.Type(), lit.Val()) {
   346  			continue
   347  		}
   348  		declPos := cv.Canonical().Pos()
   349  		if !base.LiteralAllocHash.MatchPos(declPos, nil) {
   350  			// De-selected by literal alloc optimizations debug hash.
   351  			continue
   352  		}
   353  
   354  		// Redeclare the variable inside clofn, keeping the name and the
   355  		// declaration position of the variable it replaces, so that the debug
   356  		// information still describes it the same way: dwarfgen reports a
   357  		// variable at the position of its canonical variable, which for a
   358  		// closure variable is where it was declared in the enclosing function.
   359  		// The statements below belong to the closure body, though, so they keep
   360  		// the position of the closure itself.
   361  		//
   362  		// Note we assign the constant to a variable instead of substituting it
   363  		// at every use. Substituting it would report a compile time error for
   364  		// expressions like make([]byte, n) with a negative n, where the spec
   365  		// asks for a run time panic instead (see #4085). Assigning it also
   366  		// keeps the value reachable for rewriteWithLiterals below, which knows
   367  		// where replacing an expression with a literal is safe.
   368  		pos := clofn.Pos()
   369  		name := clofn.NewLocal(declPos, cv.Sym(), cv.Type())
   370  		name.SetUsed(true)
   371  		name.SetEsc(ir.EscNever) // a constant never needs to be heap allocated
   372  		as := typecheck.Stmt(ir.NewAssignStmt(pos, name, ir.NewBasicLit(pos, cv.Type(), lit.Val())))
   373  		prefix.Append(typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, name)))
   374  		prefix.Append(as)
   375  		name.Defn = as.(*ir.AssignStmt) // so that a ReassignOracle can still find the constant
   376  
   377  		if repl == nil {
   378  			repl = make(map[*ir.Name]*ir.Name)
   379  		}
   380  		repl[cv] = name
   381  
   382  		if base.Debug.EscapeDebug >= 3 {
   383  			base.WarnfAt(pos, "rewriting closure variable %v (%v) to %v", cv, cv.Type(), lit)
   384  		}
   385  	}
   386  
   387  	if repl == nil {
   388  		return
   389  	}
   390  
   391  	// Substitute the captured variables with the local variables declared above.
   392  	var edit func(ir.Node) ir.Node
   393  	edit = func(n ir.Node) ir.Node {
   394  		if n, ok := n.(*ir.Name); ok {
   395  			if name := repl[n]; name != nil {
   396  				return name
   397  			}
   398  		}
   399  		ir.EditChildren(n, edit)
   400  		return n
   401  	}
   402  	ir.EditChildren(clofn, edit)
   403  
   404  	clofn.Body.Prepend(prefix...)
   405  	clofn.ClosureVars = slices.DeleteFunc(clofn.ClosureVars, func(cv *ir.Name) bool {
   406  		return repl[cv] != nil
   407  	})
   408  
   409  	// We just modified the IR that ro was initialized from.
   410  	b.staleOracles = append(b.staleOracles, ro)
   411  }
   412  
   413  // invalidateStaleOracles drops the cached ReassignOracles that cover a function
   414  // whose IR was modified by rewriteClosureVarsWithLiterals, so that
   415  // reassignOracle initializes them again from the current IR.
   416  // See ir.ReassignOracle.Init.
   417  func (b *batch) invalidateStaleOracles() {
   418  	if len(b.staleOracles) == 0 {
   419  		return
   420  	}
   421  
   422  	stale := make(map[*ir.ReassignOracle]bool, len(b.staleOracles))
   423  	for _, ro := range b.staleOracles {
   424  		stale[ro] = true
   425  	}
   426  	b.staleOracles = nil
   427  
   428  	for fn, ro := range b.reassignOracles {
   429  		if stale[ro] {
   430  			delete(b.reassignOracles, fn)
   431  		}
   432  	}
   433  }
   434  
   435  func (b *batch) finish(fns []*ir.Func) {
   436  	// Record parameter tags for package export data.
   437  	for _, fn := range fns {
   438  		fn.SetEsc(escFuncTagged)
   439  
   440  		for i, param := range fn.Type().RecvParams() {
   441  			param.Note = b.paramTag(fn, 1+i, param)
   442  		}
   443  	}
   444  
   445  	for _, loc := range b.allLocs {
   446  		n := loc.n
   447  		if n == nil {
   448  			continue
   449  		}
   450  
   451  		if n.Op() == ir.ONAME {
   452  			n := n.(*ir.Name)
   453  			n.Opt = nil
   454  		}
   455  
   456  		// Update n.Esc based on escape analysis results.
   457  
   458  		// Omit escape diagnostics for go/defer wrappers, at least for now.
   459  		// Historically, we haven't printed them, and test cases don't expect them.
   460  		// TODO(mdempsky): Update tests to expect this.
   461  		goDeferWrapper := n.Op() == ir.OCLOSURE && n.(*ir.ClosureExpr).Func.Wrapper()
   462  
   463  		if loc.hasAttr(attrEscapes) {
   464  			if n.Op() == ir.ONAME {
   465  				if base.Flag.CompilingRuntime {
   466  					base.ErrorfAt(n.Pos(), 0, "%v escapes to heap, not allowed in runtime", n)
   467  				}
   468  				if base.Flag.LowerM != 0 {
   469  					base.WarnfAt(n.Pos(), "moved to heap: %v", n)
   470  				}
   471  			} else {
   472  				if base.Flag.LowerM != 0 && !goDeferWrapper {
   473  					if n.Op() == ir.OAPPEND {
   474  						base.WarnfAt(n.Pos(), "append escapes to heap")
   475  					} else {
   476  						base.WarnfAt(n.Pos(), "%v escapes to heap", n)
   477  					}
   478  				}
   479  				if logopt.Enabled() {
   480  					var e_curfn *ir.Func // TODO(mdempsky): Fix.
   481  					logopt.LogOpt(n.Pos(), "escape", "escape", ir.FuncName(e_curfn))
   482  				}
   483  			}
   484  			n.SetEsc(ir.EscHeap)
   485  		} else {
   486  			if base.Flag.LowerM != 0 && n.Op() != ir.ONAME && !goDeferWrapper {
   487  				if n.Op() == ir.OAPPEND {
   488  					base.WarnfAt(n.Pos(), "append does not escape")
   489  				} else {
   490  					base.WarnfAt(n.Pos(), "%v does not escape", n)
   491  				}
   492  			}
   493  			n.SetEsc(ir.EscNone)
   494  			if !loc.hasAttr(attrPersists) {
   495  				switch n.Op() {
   496  				case ir.OCLOSURE:
   497  					n := n.(*ir.ClosureExpr)
   498  					n.SetTransient(true)
   499  				case ir.OMETHVALUE:
   500  					n := n.(*ir.SelectorExpr)
   501  					n.SetTransient(true)
   502  				case ir.OSLICELIT:
   503  					n := n.(*ir.CompLitExpr)
   504  					n.SetTransient(true)
   505  				}
   506  			}
   507  		}
   508  
   509  		// If the result of a string->[]byte conversion is never mutated,
   510  		// then it can simply reuse the string's memory directly.
   511  		if base.Debug.ZeroCopy != 0 {
   512  			if n, ok := n.(*ir.ConvExpr); ok && n.Op() == ir.OSTR2BYTES && !loc.hasAttr(attrMutates) {
   513  				if base.Flag.LowerM >= 1 {
   514  					base.WarnfAt(n.Pos(), "zero-copy string->[]byte conversion")
   515  				}
   516  				n.SetOp(ir.OSTR2BYTESTMP)
   517  			}
   518  		}
   519  	}
   520  
   521  	if goexperiment.RuntimeFreegc {
   522  		// Look for specific patterns of usage, such as appends
   523  		// to slices that we can prove are not aliased.
   524  		for _, fn := range fns {
   525  			a := aliasAnalysis{}
   526  			a.analyze(fn)
   527  		}
   528  	}
   529  
   530  	for _, fn := range fns {
   531  		if ir.MatchAstDump(fn, "escape") {
   532  			ir.AstDump(fn, "escape, "+ir.FuncName(fn))
   533  		}
   534  	}
   535  }
   536  
   537  // inMutualBatch reports whether function fn is in the batch of
   538  // mutually recursive functions being analyzed. When this is true,
   539  // fn has not yet been analyzed, so its parameters and results
   540  // should be incorporated directly into the flow graph instead of
   541  // relying on its escape analysis tagging.
   542  func (b *batch) inMutualBatch(fn *ir.Name) bool {
   543  	if fn.Defn != nil && fn.Defn.Esc() < escFuncTagged {
   544  		if fn.Defn.Esc() == escFuncUnknown {
   545  			base.FatalfAt(fn.Pos(), "graph inconsistency: %v", fn)
   546  		}
   547  		return true
   548  	}
   549  	return false
   550  }
   551  
   552  const (
   553  	escFuncUnknown = 0 + iota
   554  	escFuncPlanned
   555  	escFuncStarted
   556  	escFuncTagged
   557  )
   558  
   559  // Mark labels that have no backjumps to them as not increasing e.loopdepth.
   560  type labelState int
   561  
   562  const (
   563  	looping labelState = 1 + iota
   564  	nonlooping
   565  )
   566  
   567  func (b *batch) paramTag(fn *ir.Func, narg int, f *types.Field) string {
   568  	name := func() string {
   569  		if f.Nname != nil {
   570  			return f.Nname.Sym().Name
   571  		}
   572  		return fmt.Sprintf("arg#%d", narg)
   573  	}
   574  
   575  	// Only report diagnostics for user code;
   576  	// not for wrappers generated around them.
   577  	// TODO(mdempsky): Generalize this.
   578  	diagnose := base.Flag.LowerM != 0 && !(fn.Wrapper() || fn.Dupok())
   579  
   580  	if len(fn.Body) == 0 {
   581  		// Assume that uintptr arguments must be held live across the call.
   582  		// This is most important for syscall.Syscall.
   583  		// See golang.org/issue/13372.
   584  		// This really doesn't have much to do with escape analysis per se,
   585  		// but we are reusing the ability to annotate an individual function
   586  		// argument and pass those annotations along to importing code.
   587  		fn.Pragma |= ir.UintptrKeepAlive
   588  
   589  		if f.Type.IsUintptr() {
   590  			if diagnose {
   591  				base.WarnfAt(f.Pos, "assuming %v is unsafe uintptr", name())
   592  			}
   593  			return ""
   594  		}
   595  
   596  		if !f.Type.HasPointers() { // don't bother tagging for scalars
   597  			return ""
   598  		}
   599  
   600  		var esc leaks
   601  
   602  		// External functions are assumed unsafe, unless
   603  		// //go:noescape is given before the declaration.
   604  		if fn.Pragma&ir.Noescape != 0 {
   605  			if diagnose && f.Sym != nil {
   606  				base.WarnfAt(f.Pos, "%v does not escape", name())
   607  			}
   608  			esc.AddMutator(0)
   609  			esc.AddCallee(0)
   610  		} else {
   611  			if diagnose && f.Sym != nil {
   612  				base.WarnfAt(f.Pos, "leaking param: %v", name())
   613  			}
   614  			esc.AddHeap(0)
   615  		}
   616  
   617  		return esc.Encode()
   618  	}
   619  
   620  	if fn.Pragma&ir.UintptrEscapes != 0 {
   621  		if f.Type.IsUintptr() {
   622  			if diagnose {
   623  				base.WarnfAt(f.Pos, "marking %v as escaping uintptr", name())
   624  			}
   625  			return ""
   626  		}
   627  		if f.IsDDD() && f.Type.Elem().IsUintptr() {
   628  			// final argument is ...uintptr.
   629  			if diagnose {
   630  				base.WarnfAt(f.Pos, "marking %v as escaping ...uintptr", name())
   631  			}
   632  			return ""
   633  		}
   634  	}
   635  
   636  	if !f.Type.HasPointers() { // don't bother tagging for scalars
   637  		return ""
   638  	}
   639  
   640  	// Unnamed parameters are unused and therefore do not escape.
   641  	if f.Sym == nil || f.Sym.IsBlank() {
   642  		var esc leaks
   643  		return esc.Encode()
   644  	}
   645  
   646  	n := f.Nname.(*ir.Name)
   647  	loc := b.oldLoc(n)
   648  	esc := loc.paramEsc
   649  	esc.Optimize()
   650  
   651  	if diagnose && !loc.hasAttr(attrEscapes) {
   652  		b.reportLeaks(f.Pos, name(), esc, fn.Type())
   653  	}
   654  
   655  	return esc.Encode()
   656  }
   657  
   658  func (b *batch) reportLeaks(pos src.XPos, name string, esc leaks, sig *types.Type) {
   659  	warned := false
   660  	if x := esc.Heap(); x >= 0 {
   661  		if x == 0 {
   662  			base.WarnfAt(pos, "leaking param: %v", name)
   663  		} else {
   664  			// TODO(mdempsky): Mention level=x like below?
   665  			base.WarnfAt(pos, "leaking param content: %v", name)
   666  		}
   667  		warned = true
   668  	}
   669  	for i := 0; i < numEscResults; i++ {
   670  		if x := esc.Result(i); x >= 0 {
   671  			res := sig.Result(i).Nname.Sym().Name
   672  			base.WarnfAt(pos, "leaking param: %v to result %v level=%d", name, res, x)
   673  			warned = true
   674  		}
   675  	}
   676  
   677  	if base.Debug.EscapeMutationsCalls <= 0 {
   678  		if !warned {
   679  			base.WarnfAt(pos, "%v does not escape", name)
   680  		}
   681  		return
   682  	}
   683  
   684  	if x := esc.Mutator(); x >= 0 {
   685  		base.WarnfAt(pos, "mutates param: %v derefs=%v", name, x)
   686  		warned = true
   687  	}
   688  	if x := esc.Callee(); x >= 0 {
   689  		base.WarnfAt(pos, "calls param: %v derefs=%v", name, x)
   690  		warned = true
   691  	}
   692  
   693  	if !warned {
   694  		base.WarnfAt(pos, "%v does not escape, mutate, or call", name)
   695  	}
   696  }
   697  
   698  // rewriteWithLiterals attempts to replace certain non-constant expressions
   699  // within n with a literal if possible.
   700  func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
   701  	if n == nil || fn == nil {
   702  		return
   703  	}
   704  
   705  	assignTemp := func(pos src.XPos, n ir.Node, init *ir.Nodes) {
   706  		// Preserve any side effects of n by assigning it to an otherwise unused temp.
   707  		tmp := typecheck.TempAt(pos, fn, n.Type())
   708  		init.Append(typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp)))
   709  		init.Append(typecheck.Stmt(ir.NewAssignStmt(pos, tmp, n)))
   710  	}
   711  
   712  	switch n.Op() {
   713  	case ir.OMAKESLICE:
   714  		// Check if we can replace a non-constant argument to make with
   715  		// a literal to allow for this slice to be stack allocated if otherwise allowed.
   716  		n := n.(*ir.MakeExpr)
   717  
   718  		r := &n.Cap
   719  		if n.Cap == nil {
   720  			r = &n.Len
   721  		}
   722  
   723  		if (*r).Op() != ir.OLITERAL {
   724  			// Look up a cached ReassignOracle for the function, lazily computing one if needed.
   725  			ro := b.reassignOracle(fn)
   726  			if ro == nil {
   727  				base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
   728  			}
   729  
   730  			s := ro.StaticValue(*r)
   731  			switch s.Op() {
   732  			case ir.OLITERAL:
   733  				lit, ok := s.(*ir.BasicLit)
   734  				if !ok || lit.Val().Kind() != constant.Int {
   735  					base.Fatalf("unexpected BasicLit Kind")
   736  				}
   737  				if constant.Compare(lit.Val(), token.GEQ, constant.MakeInt64(0)) {
   738  					if !base.LiteralAllocHash.MatchPos(n.Pos(), nil) {
   739  						// De-selected by literal alloc optimizations debug hash.
   740  						return
   741  					}
   742  					// Preserve any side effects of the original expression, then replace it.
   743  					assignTemp(n.Pos(), *r, n.PtrInit())
   744  					*r = ir.NewBasicLit(n.Pos(), (*r).Type(), lit.Val())
   745  				}
   746  			case ir.OLEN:
   747  				x := ro.StaticValue(s.(*ir.UnaryExpr).X)
   748  				if x.Op() == ir.OSLICELIT {
   749  					x := x.(*ir.CompLitExpr)
   750  					// Preserve any side effects of the original expression, then update the value.
   751  					assignTemp(n.Pos(), *r, n.PtrInit())
   752  					*r = ir.NewBasicLit(n.Pos(), types.Types[types.TINT], constant.MakeInt64(x.Len))
   753  				}
   754  			}
   755  		}
   756  	case ir.OCONVIFACE:
   757  		// Check if we can replace a non-constant expression in an interface conversion with
   758  		// a literal to avoid heap allocating the underlying interface value.
   759  		conv := n.(*ir.ConvExpr)
   760  		if conv.X.Op() != ir.OLITERAL && !conv.X.Type().IsInterface() {
   761  			// TODO(thepudds): likely could avoid some work by tightening the check of conv.X's type.
   762  			// Look up a cached ReassignOracle for the function, lazily computing one if needed.
   763  			ro := b.reassignOracle(fn)
   764  			if ro == nil {
   765  				base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
   766  			}
   767  			v := ro.StaticValue(conv.X)
   768  			if v != nil && v.Op() == ir.OLITERAL && ir.ValidTypeForConst(conv.X.Type(), v.Val()) {
   769  				if !base.LiteralAllocHash.MatchPos(n.Pos(), nil) {
   770  					// De-selected by literal alloc optimizations debug hash.
   771  					return
   772  				}
   773  				if base.Debug.EscapeDebug >= 3 {
   774  					base.WarnfAt(n.Pos(), "rewriting OCONVIFACE value from %v (%v) to %v (%v)", conv.X, conv.X.Type(), v, v.Type())
   775  				}
   776  				// Preserve any side effects of the original expression, then replace it.
   777  				assignTemp(conv.Pos(), conv.X, conv.PtrInit())
   778  				v := v.(*ir.BasicLit)
   779  				conv.X = ir.NewBasicLit(conv.Pos(), conv.X.Type(), v.Val())
   780  				typecheck.Expr(conv)
   781  			}
   782  		}
   783  	}
   784  }
   785  
   786  // reassignOracle returns an initialized *ir.ReassignOracle for fn.
   787  // If fn is a closure, it returns the ReassignOracle for the ultimate parent.
   788  //
   789  // A new ReassignOracle is initialized lazily if needed, and the result
   790  // is cached to reduce duplicative work of preparing a ReassignOracle.
   791  func (b *batch) reassignOracle(fn *ir.Func) *ir.ReassignOracle {
   792  	if ro, ok := b.reassignOracles[fn]; ok {
   793  		return ro // Hit.
   794  	}
   795  
   796  	// For closures, we want the ultimate parent's ReassignOracle,
   797  	// so walk up the parent chain, if any.
   798  	f := fn
   799  	for f.ClosureParent != nil && !f.ClosureParent.IsPackageInit() {
   800  		f = f.ClosureParent
   801  	}
   802  
   803  	if f != fn {
   804  		// We found a parent.
   805  		ro := b.reassignOracles[f]
   806  		if ro != nil {
   807  			// Hit, via a parent. Before returning, store this ro for the original fn as well.
   808  			b.reassignOracles[fn] = ro
   809  			return ro
   810  		}
   811  	}
   812  
   813  	// Miss. We did not find a ReassignOracle for fn or a parent, so lazily create one.
   814  	ro := &ir.ReassignOracle{}
   815  	ro.Init(f)
   816  
   817  	// Cache the answer for the original fn.
   818  	b.reassignOracles[fn] = ro
   819  	if f != fn {
   820  		// Cache for the parent as well.
   821  		b.reassignOracles[f] = ro
   822  	}
   823  	return ro
   824  }
   825  

View as plain text