Source file src/cmd/compile/internal/inline/inl.go

     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  // The inlining facility makes 2 passes: first caninl determines which
     6  // functions are suitable for inlining, and for those that are it
     7  // saves a copy of the body. Then InlineCalls walks each function body to
     8  // expand calls to inlinable functions.
     9  //
    10  // The Debug.l flag controls the aggressiveness. Note that main() swaps level 0 and 1,
    11  // making 1 the default and -l disable. Additional levels (beyond -l) may be buggy and
    12  // are not supported.
    13  //      0: disabled
    14  //      1: 80-nodes leaf functions, oneliners, panic, lazy typechecking (default)
    15  //      2: (unassigned)
    16  //      3: (unassigned)
    17  //      4: allow non-leaf functions
    18  //
    19  // At some point this may get another default and become switch-offable with -N.
    20  //
    21  // The -d typcheckinl flag enables early typechecking of all imported bodies,
    22  // which is useful to flush out bugs.
    23  //
    24  // The Debug.m flag enables diagnostic output.  a single -m is useful for verifying
    25  // which calls get inlined or not, more is for debugging, and may go away at any point.
    26  
    27  package inline
    28  
    29  import (
    30  	"fmt"
    31  	"go/constant"
    32  	"strings"
    33  
    34  	"cmd/compile/internal/base"
    35  	"cmd/compile/internal/ir"
    36  	"cmd/compile/internal/logopt"
    37  	"cmd/compile/internal/typecheck"
    38  	"cmd/compile/internal/types"
    39  	"cmd/internal/obj"
    40  	"cmd/internal/src"
    41  )
    42  
    43  // Inlining budget parameters, gathered in one place
    44  const (
    45  	inlineMaxBudget       = 80
    46  	inlineExtraAppendCost = 0
    47  	// default is to inline if there's at most one call. -l=4 overrides this by using 1 instead.
    48  	inlineExtraCallCost  = 57              // 57 was benchmarked to provided most benefit with no bad surprises; see https://github.com/golang/go/issues/19348#issuecomment-439370742
    49  	inlineExtraPanicCost = 1               // do not penalize inlining panics.
    50  	inlineExtraThrowCost = inlineMaxBudget // with current (2018-05/1.11) code, inlining runtime.throw does not help.
    51  
    52  	inlineBigFunctionNodes   = 5000 // Functions with this many nodes are considered "big".
    53  	inlineBigFunctionMaxCost = 20   // Max cost of inlinee when inlining into a "big" function.
    54  )
    55  
    56  // InlinePackage finds functions that can be inlined and clones them before walk expands them.
    57  func InlinePackage() {
    58  	ir.VisitFuncsBottomUp(typecheck.Target.Decls, func(list []*ir.Func, recursive bool) {
    59  		numfns := numNonClosures(list)
    60  		for _, n := range list {
    61  			if !recursive || numfns > 1 {
    62  				// We allow inlining if there is no
    63  				// recursion, or the recursion cycle is
    64  				// across more than one function.
    65  				CanInline(n)
    66  			} else {
    67  				if base.Flag.LowerM > 1 {
    68  					fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(n), n.Nname)
    69  				}
    70  			}
    71  			InlineCalls(n)
    72  		}
    73  	})
    74  }
    75  
    76  // CanInline determines whether fn is inlineable.
    77  // If so, CanInline saves copies of fn.Body and fn.Dcl in fn.Inl.
    78  // fn and fn.Body will already have been typechecked.
    79  func CanInline(fn *ir.Func) {
    80  	if fn.Nname == nil {
    81  		base.Fatalf("CanInline no nname %+v", fn)
    82  	}
    83  
    84  	var reason string // reason, if any, that the function was not inlined
    85  	if base.Flag.LowerM > 1 || logopt.Enabled() {
    86  		defer func() {
    87  			if reason != "" {
    88  				if base.Flag.LowerM > 1 {
    89  					fmt.Printf("%v: cannot inline %v: %s\n", ir.Line(fn), fn.Nname, reason)
    90  				}
    91  				if logopt.Enabled() {
    92  					logopt.LogOpt(fn.Pos(), "cannotInlineFunction", "inline", ir.FuncName(fn), reason)
    93  				}
    94  			}
    95  		}()
    96  	}
    97  
    98  	// If marked "go:noinline", don't inline
    99  	if fn.Pragma&ir.Noinline != 0 {
   100  		reason = "marked go:noinline"
   101  		return
   102  	}
   103  
   104  	// If marked "go:norace" and -race compilation, don't inline.
   105  	if base.Flag.Race && fn.Pragma&ir.Norace != 0 {
   106  		reason = "marked go:norace with -race compilation"
   107  		return
   108  	}
   109  
   110  	// If marked "go:nocheckptr" and -d checkptr compilation, don't inline.
   111  	if base.Debug.Checkptr != 0 && fn.Pragma&ir.NoCheckPtr != 0 {
   112  		reason = "marked go:nocheckptr"
   113  		return
   114  	}
   115  
   116  	// If marked "go:cgo_unsafe_args", don't inline, since the
   117  	// function makes assumptions about its argument frame layout.
   118  	if fn.Pragma&ir.CgoUnsafeArgs != 0 {
   119  		reason = "marked go:cgo_unsafe_args"
   120  		return
   121  	}
   122  
   123  	// If marked as "go:uintptrescapes", don't inline, since the
   124  	// escape information is lost during inlining.
   125  	if fn.Pragma&ir.UintptrEscapes != 0 {
   126  		reason = "marked as having an escaping uintptr argument"
   127  		return
   128  	}
   129  
   130  	// The nowritebarrierrec checker currently works at function
   131  	// granularity, so inlining yeswritebarrierrec functions can
   132  	// confuse it (#22342). As a workaround, disallow inlining
   133  	// them for now.
   134  	if fn.Pragma&ir.Yeswritebarrierrec != 0 {
   135  		reason = "marked go:yeswritebarrierrec"
   136  		return
   137  	}
   138  
   139  	// If fn has no body (is defined outside of Go), cannot inline it.
   140  	if len(fn.Body) == 0 {
   141  		reason = "no function body"
   142  		return
   143  	}
   144  
   145  	if fn.Typecheck() == 0 {
   146  		base.Fatalf("CanInline on non-typechecked function %v", fn)
   147  	}
   148  
   149  	n := fn.Nname
   150  	if n.Func.InlinabilityChecked() {
   151  		return
   152  	}
   153  	defer n.Func.SetInlinabilityChecked(true)
   154  
   155  	cc := int32(inlineExtraCallCost)
   156  	if base.Flag.LowerL == 4 {
   157  		cc = 1 // this appears to yield better performance than 0.
   158  	}
   159  
   160  	// At this point in the game the function we're looking at may
   161  	// have "stale" autos, vars that still appear in the Dcl list, but
   162  	// which no longer have any uses in the function body (due to
   163  	// elimination by deadcode). We'd like to exclude these dead vars
   164  	// when creating the "Inline.Dcl" field below; to accomplish this,
   165  	// the hairyVisitor below builds up a map of used/referenced
   166  	// locals, and we use this map to produce a pruned Inline.Dcl
   167  	// list. See issue 25249 for more context.
   168  
   169  	visitor := hairyVisitor{
   170  		budget:        inlineMaxBudget,
   171  		extraCallCost: cc,
   172  	}
   173  	if visitor.tooHairy(fn) {
   174  		reason = visitor.reason
   175  		return
   176  	}
   177  
   178  	n.Func.Inl = &ir.Inline{
   179  		Cost: inlineMaxBudget - visitor.budget,
   180  		Dcl:  pruneUnusedAutos(n.Defn.(*ir.Func).Dcl, &visitor),
   181  		Body: inlcopylist(fn.Body),
   182  
   183  		CanDelayResults: canDelayResults(fn),
   184  	}
   185  
   186  	if base.Flag.LowerM > 1 {
   187  		fmt.Printf("%v: can inline %v with cost %d as: %v { %v }\n", ir.Line(fn), n, inlineMaxBudget-visitor.budget, fn.Type(), ir.Nodes(n.Func.Inl.Body))
   188  	} else if base.Flag.LowerM != 0 {
   189  		fmt.Printf("%v: can inline %v\n", ir.Line(fn), n)
   190  	}
   191  	if logopt.Enabled() {
   192  		logopt.LogOpt(fn.Pos(), "canInlineFunction", "inline", ir.FuncName(fn), fmt.Sprintf("cost: %d", inlineMaxBudget-visitor.budget))
   193  	}
   194  }
   195  
   196  // canDelayResults reports whether inlined calls to fn can delay
   197  // declaring the result parameter until the "return" statement.
   198  func canDelayResults(fn *ir.Func) bool {
   199  	// We can delay declaring+initializing result parameters if:
   200  	// (1) there's exactly one "return" statement in the inlined function;
   201  	// (2) it's not an empty return statement (#44355); and
   202  	// (3) the result parameters aren't named.
   203  
   204  	nreturns := 0
   205  	ir.VisitList(fn.Body, func(n ir.Node) {
   206  		if n, ok := n.(*ir.ReturnStmt); ok {
   207  			nreturns++
   208  			if len(n.Results) == 0 {
   209  				nreturns++ // empty return statement (case 2)
   210  			}
   211  		}
   212  	})
   213  
   214  	if nreturns != 1 {
   215  		return false // not exactly one return statement (case 1)
   216  	}
   217  
   218  	// temporaries for return values.
   219  	for _, param := range fn.Type().Results().FieldSlice() {
   220  		if sym := types.OrigSym(param.Sym); sym != nil && !sym.IsBlank() {
   221  			return false // found a named result parameter (case 3)
   222  		}
   223  	}
   224  
   225  	return true
   226  }
   227  
   228  // hairyVisitor visits a function body to determine its inlining
   229  // hairiness and whether or not it can be inlined.
   230  type hairyVisitor struct {
   231  	budget        int32
   232  	reason        string
   233  	extraCallCost int32
   234  	usedLocals    ir.NameSet
   235  	do            func(ir.Node) bool
   236  }
   237  
   238  func (v *hairyVisitor) tooHairy(fn *ir.Func) bool {
   239  	v.do = v.doNode // cache closure
   240  	if ir.DoChildren(fn, v.do) {
   241  		return true
   242  	}
   243  	if v.budget < 0 {
   244  		v.reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", inlineMaxBudget-v.budget, inlineMaxBudget)
   245  		return true
   246  	}
   247  	return false
   248  }
   249  
   250  func (v *hairyVisitor) doNode(n ir.Node) bool {
   251  	if n == nil {
   252  		return false
   253  	}
   254  	switch n.Op() {
   255  	// Call is okay if inlinable and we have the budget for the body.
   256  	case ir.OCALLFUNC:
   257  		n := n.(*ir.CallExpr)
   258  		// Functions that call runtime.getcaller{pc,sp} can not be inlined
   259  		// because getcaller{pc,sp} expect a pointer to the caller's first argument.
   260  		//
   261  		// runtime.throw is a "cheap call" like panic in normal code.
   262  		if n.X.Op() == ir.ONAME {
   263  			name := n.X.(*ir.Name)
   264  			if name.Class == ir.PFUNC && types.IsRuntimePkg(name.Sym().Pkg) {
   265  				fn := name.Sym().Name
   266  				if fn == "getcallerpc" || fn == "getcallersp" {
   267  					v.reason = "call to " + fn
   268  					return true
   269  				}
   270  				if fn == "throw" {
   271  					v.budget -= inlineExtraThrowCost
   272  					break
   273  				}
   274  			}
   275  		}
   276  		if n.X.Op() == ir.OMETHEXPR {
   277  			if meth := ir.MethodExprName(n.X); meth != nil {
   278  				if fn := meth.Func; fn != nil {
   279  					s := fn.Sym()
   280  					var cheap bool
   281  					if types.IsRuntimePkg(s.Pkg) && s.Name == "heapBits.nextArena" {
   282  						// Special case: explicitly allow mid-stack inlining of
   283  						// runtime.heapBits.next even though it calls slow-path
   284  						// runtime.heapBits.nextArena.
   285  						cheap = true
   286  					}
   287  					// Special case: on architectures that can do unaligned loads,
   288  					// explicitly mark encoding/binary methods as cheap,
   289  					// because in practice they are, even though our inlining
   290  					// budgeting system does not see that. See issue 42958.
   291  					if base.Ctxt.Arch.CanMergeLoads && s.Pkg.Path == "encoding/binary" {
   292  						switch s.Name {
   293  						case "littleEndian.Uint64", "littleEndian.Uint32", "littleEndian.Uint16",
   294  							"bigEndian.Uint64", "bigEndian.Uint32", "bigEndian.Uint16",
   295  							"littleEndian.PutUint64", "littleEndian.PutUint32", "littleEndian.PutUint16",
   296  							"bigEndian.PutUint64", "bigEndian.PutUint32", "bigEndian.PutUint16":
   297  							cheap = true
   298  						}
   299  					}
   300  					if cheap {
   301  						break // treat like any other node, that is, cost of 1
   302  					}
   303  				}
   304  			}
   305  		}
   306  
   307  		if ir.IsIntrinsicCall(n) {
   308  			// Treat like any other node.
   309  			break
   310  		}
   311  
   312  		if fn := inlCallee(n.X); fn != nil && typecheck.HaveInlineBody(fn) {
   313  			v.budget -= fn.Inl.Cost
   314  			break
   315  		}
   316  
   317  		// Call cost for non-leaf inlining.
   318  		v.budget -= v.extraCallCost
   319  
   320  	case ir.OCALLMETH:
   321  		base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
   322  
   323  	// Things that are too hairy, irrespective of the budget
   324  	case ir.OCALL, ir.OCALLINTER:
   325  		// Call cost for non-leaf inlining.
   326  		v.budget -= v.extraCallCost
   327  
   328  	case ir.OPANIC:
   329  		n := n.(*ir.UnaryExpr)
   330  		if n.X.Op() == ir.OCONVIFACE && n.X.(*ir.ConvExpr).Implicit() {
   331  			// Hack to keep reflect.flag.mustBe inlinable for TestIntendedInlining.
   332  			// Before CL 284412, these conversions were introduced later in the
   333  			// compiler, so they didn't count against inlining budget.
   334  			v.budget++
   335  		}
   336  		v.budget -= inlineExtraPanicCost
   337  
   338  	case ir.ORECOVER:
   339  		// recover matches the argument frame pointer to find
   340  		// the right panic value, so it needs an argument frame.
   341  		v.reason = "call to recover"
   342  		return true
   343  
   344  	case ir.OCLOSURE:
   345  		if base.Debug.InlFuncsWithClosures == 0 {
   346  			v.reason = "not inlining functions with closures"
   347  			return true
   348  		}
   349  
   350  		// TODO(danscales): Maybe make budget proportional to number of closure
   351  		// variables, e.g.:
   352  		//v.budget -= int32(len(n.(*ir.ClosureExpr).Func.ClosureVars) * 3)
   353  		v.budget -= 15
   354  		// Scan body of closure (which DoChildren doesn't automatically
   355  		// do) to check for disallowed ops in the body and include the
   356  		// body in the budget.
   357  		if doList(n.(*ir.ClosureExpr).Func.Body, v.do) {
   358  			return true
   359  		}
   360  
   361  	case ir.OSELECT,
   362  		ir.OGO,
   363  		ir.ODEFER,
   364  		ir.ODCLTYPE, // can't print yet
   365  		ir.OTAILCALL:
   366  		v.reason = "unhandled op " + n.Op().String()
   367  		return true
   368  
   369  	case ir.OAPPEND:
   370  		v.budget -= inlineExtraAppendCost
   371  
   372  	case ir.ODEREF:
   373  		// *(*X)(unsafe.Pointer(&x)) is low-cost
   374  		n := n.(*ir.StarExpr)
   375  
   376  		ptr := n.X
   377  		for ptr.Op() == ir.OCONVNOP {
   378  			ptr = ptr.(*ir.ConvExpr).X
   379  		}
   380  		if ptr.Op() == ir.OADDR {
   381  			v.budget += 1 // undo half of default cost of ir.ODEREF+ir.OADDR
   382  		}
   383  
   384  	case ir.OCONVNOP:
   385  		// This doesn't produce code, but the children might.
   386  		v.budget++ // undo default cost
   387  
   388  	case ir.ODCLCONST, ir.OFALL:
   389  		// These nodes don't produce code; omit from inlining budget.
   390  		return false
   391  
   392  	case ir.OIF:
   393  		n := n.(*ir.IfStmt)
   394  		if ir.IsConst(n.Cond, constant.Bool) {
   395  			// This if and the condition cost nothing.
   396  			if doList(n.Init(), v.do) {
   397  				return true
   398  			}
   399  			if ir.BoolVal(n.Cond) {
   400  				return doList(n.Body, v.do)
   401  			} else {
   402  				return doList(n.Else, v.do)
   403  			}
   404  		}
   405  
   406  	case ir.ONAME:
   407  		n := n.(*ir.Name)
   408  		if n.Class == ir.PAUTO {
   409  			v.usedLocals.Add(n)
   410  		}
   411  
   412  	case ir.OBLOCK:
   413  		// The only OBLOCK we should see at this point is an empty one.
   414  		// In any event, let the visitList(n.List()) below take care of the statements,
   415  		// and don't charge for the OBLOCK itself. The ++ undoes the -- below.
   416  		v.budget++
   417  
   418  	case ir.OMETHVALUE, ir.OSLICELIT:
   419  		v.budget-- // Hack for toolstash -cmp.
   420  
   421  	case ir.OMETHEXPR:
   422  		v.budget++ // Hack for toolstash -cmp.
   423  	}
   424  
   425  	v.budget--
   426  
   427  	// When debugging, don't stop early, to get full cost of inlining this function
   428  	if v.budget < 0 && base.Flag.LowerM < 2 && !logopt.Enabled() {
   429  		v.reason = "too expensive"
   430  		return true
   431  	}
   432  
   433  	return ir.DoChildren(n, v.do)
   434  }
   435  
   436  func isBigFunc(fn *ir.Func) bool {
   437  	budget := inlineBigFunctionNodes
   438  	return ir.Any(fn, func(n ir.Node) bool {
   439  		budget--
   440  		return budget <= 0
   441  	})
   442  }
   443  
   444  // inlcopylist (together with inlcopy) recursively copies a list of nodes, except
   445  // that it keeps the same ONAME, OTYPE, and OLITERAL nodes. It is used for copying
   446  // the body and dcls of an inlineable function.
   447  func inlcopylist(ll []ir.Node) []ir.Node {
   448  	s := make([]ir.Node, len(ll))
   449  	for i, n := range ll {
   450  		s[i] = inlcopy(n)
   451  	}
   452  	return s
   453  }
   454  
   455  // inlcopy is like DeepCopy(), but does extra work to copy closures.
   456  func inlcopy(n ir.Node) ir.Node {
   457  	var edit func(ir.Node) ir.Node
   458  	edit = func(x ir.Node) ir.Node {
   459  		switch x.Op() {
   460  		case ir.ONAME, ir.OTYPE, ir.OLITERAL, ir.ONIL:
   461  			return x
   462  		}
   463  		m := ir.Copy(x)
   464  		ir.EditChildren(m, edit)
   465  		if x.Op() == ir.OCLOSURE {
   466  			x := x.(*ir.ClosureExpr)
   467  			// Need to save/duplicate x.Func.Nname,
   468  			// x.Func.Nname.Ntype, x.Func.Dcl, x.Func.ClosureVars, and
   469  			// x.Func.Body for iexport and local inlining.
   470  			oldfn := x.Func
   471  			newfn := ir.NewFunc(oldfn.Pos())
   472  			m.(*ir.ClosureExpr).Func = newfn
   473  			newfn.Nname = ir.NewNameAt(oldfn.Nname.Pos(), oldfn.Nname.Sym())
   474  			// XXX OK to share fn.Type() ??
   475  			newfn.Nname.SetType(oldfn.Nname.Type())
   476  			// Ntype can be nil for -G=3 mode.
   477  			if oldfn.Nname.Ntype != nil {
   478  				newfn.Nname.Ntype = inlcopy(oldfn.Nname.Ntype).(ir.Ntype)
   479  			}
   480  			newfn.Body = inlcopylist(oldfn.Body)
   481  			// Make shallow copy of the Dcl and ClosureVar slices
   482  			newfn.Dcl = append([]*ir.Name(nil), oldfn.Dcl...)
   483  			newfn.ClosureVars = append([]*ir.Name(nil), oldfn.ClosureVars...)
   484  		}
   485  		return m
   486  	}
   487  	return edit(n)
   488  }
   489  
   490  // InlineCalls/inlnode walks fn's statements and expressions and substitutes any
   491  // calls made to inlineable functions. This is the external entry point.
   492  func InlineCalls(fn *ir.Func) {
   493  	savefn := ir.CurFunc
   494  	ir.CurFunc = fn
   495  	maxCost := int32(inlineMaxBudget)
   496  	if isBigFunc(fn) {
   497  		maxCost = inlineBigFunctionMaxCost
   498  	}
   499  	// Map to keep track of functions that have been inlined at a particular
   500  	// call site, in order to stop inlining when we reach the beginning of a
   501  	// recursion cycle again. We don't inline immediately recursive functions,
   502  	// but allow inlining if there is a recursion cycle of many functions.
   503  	// Most likely, the inlining will stop before we even hit the beginning of
   504  	// the cycle again, but the map catches the unusual case.
   505  	inlMap := make(map[*ir.Func]bool)
   506  	var edit func(ir.Node) ir.Node
   507  	edit = func(n ir.Node) ir.Node {
   508  		return inlnode(n, maxCost, inlMap, edit)
   509  	}
   510  	ir.EditChildren(fn, edit)
   511  	ir.CurFunc = savefn
   512  }
   513  
   514  // inlnode recurses over the tree to find inlineable calls, which will
   515  // be turned into OINLCALLs by mkinlcall. When the recursion comes
   516  // back up will examine left, right, list, rlist, ninit, ntest, nincr,
   517  // nbody and nelse and use one of the 4 inlconv/glue functions above
   518  // to turn the OINLCALL into an expression, a statement, or patch it
   519  // in to this nodes list or rlist as appropriate.
   520  // NOTE it makes no sense to pass the glue functions down the
   521  // recursion to the level where the OINLCALL gets created because they
   522  // have to edit /this/ n, so you'd have to push that one down as well,
   523  // but then you may as well do it here.  so this is cleaner and
   524  // shorter and less complicated.
   525  // The result of inlnode MUST be assigned back to n, e.g.
   526  // 	n.Left = inlnode(n.Left)
   527  func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node {
   528  	if n == nil {
   529  		return n
   530  	}
   531  
   532  	switch n.Op() {
   533  	case ir.ODEFER, ir.OGO:
   534  		n := n.(*ir.GoDeferStmt)
   535  		switch call := n.Call; call.Op() {
   536  		case ir.OCALLMETH:
   537  			base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
   538  		case ir.OCALLFUNC:
   539  			call := call.(*ir.CallExpr)
   540  			call.NoInline = true
   541  		}
   542  	case ir.OTAILCALL:
   543  		n := n.(*ir.TailCallStmt)
   544  		n.Call.NoInline = true // Not inline a tail call for now. Maybe we could inline it just like RETURN fn(arg)?
   545  
   546  	// TODO do them here (or earlier),
   547  	// so escape analysis can avoid more heapmoves.
   548  	case ir.OCLOSURE:
   549  		return n
   550  	case ir.OCALLMETH:
   551  		base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
   552  	case ir.OCALLFUNC:
   553  		n := n.(*ir.CallExpr)
   554  		if n.X.Op() == ir.OMETHEXPR {
   555  			// Prevent inlining some reflect.Value methods when using checkptr,
   556  			// even when package reflect was compiled without it (#35073).
   557  			if meth := ir.MethodExprName(n.X); meth != nil {
   558  				s := meth.Sym()
   559  				if base.Debug.Checkptr != 0 && types.IsReflectPkg(s.Pkg) && (s.Name == "Value.UnsafeAddr" || s.Name == "Value.Pointer") {
   560  					return n
   561  				}
   562  			}
   563  		}
   564  	}
   565  
   566  	lno := ir.SetPos(n)
   567  
   568  	ir.EditChildren(n, edit)
   569  
   570  	// with all the branches out of the way, it is now time to
   571  	// transmogrify this node itself unless inhibited by the
   572  	// switch at the top of this function.
   573  	switch n.Op() {
   574  	case ir.OCALLMETH:
   575  		base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
   576  
   577  	case ir.OCALLFUNC:
   578  		call := n.(*ir.CallExpr)
   579  		if call.NoInline {
   580  			break
   581  		}
   582  		if base.Flag.LowerM > 3 {
   583  			fmt.Printf("%v:call to func %+v\n", ir.Line(n), call.X)
   584  		}
   585  		if ir.IsIntrinsicCall(call) {
   586  			break
   587  		}
   588  		if fn := inlCallee(call.X); fn != nil && typecheck.HaveInlineBody(fn) {
   589  			n = mkinlcall(call, fn, maxCost, inlMap, edit)
   590  		}
   591  	}
   592  
   593  	base.Pos = lno
   594  
   595  	return n
   596  }
   597  
   598  // inlCallee takes a function-typed expression and returns the underlying function ONAME
   599  // that it refers to if statically known. Otherwise, it returns nil.
   600  func inlCallee(fn ir.Node) *ir.Func {
   601  	fn = ir.StaticValue(fn)
   602  	switch fn.Op() {
   603  	case ir.OMETHEXPR:
   604  		fn := fn.(*ir.SelectorExpr)
   605  		n := ir.MethodExprName(fn)
   606  		// Check that receiver type matches fn.X.
   607  		// TODO(mdempsky): Handle implicit dereference
   608  		// of pointer receiver argument?
   609  		if n == nil || !types.Identical(n.Type().Recv().Type, fn.X.Type()) {
   610  			return nil
   611  		}
   612  		return n.Func
   613  	case ir.ONAME:
   614  		fn := fn.(*ir.Name)
   615  		if fn.Class == ir.PFUNC {
   616  			return fn.Func
   617  		}
   618  	case ir.OCLOSURE:
   619  		fn := fn.(*ir.ClosureExpr)
   620  		c := fn.Func
   621  		CanInline(c)
   622  		return c
   623  	}
   624  	return nil
   625  }
   626  
   627  func inlParam(t *types.Field, as ir.InitNode, inlvars map[*ir.Name]*ir.Name) ir.Node {
   628  	if t.Nname == nil {
   629  		return ir.BlankNode
   630  	}
   631  	n := t.Nname.(*ir.Name)
   632  	if ir.IsBlank(n) {
   633  		return ir.BlankNode
   634  	}
   635  	inlvar := inlvars[n]
   636  	if inlvar == nil {
   637  		base.Fatalf("missing inlvar for %v", n)
   638  	}
   639  	as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, inlvar))
   640  	inlvar.Name().Defn = as
   641  	return inlvar
   642  }
   643  
   644  var inlgen int
   645  
   646  // SSADumpInline gives the SSA back end a chance to dump the function
   647  // when producing output for debugging the compiler itself.
   648  var SSADumpInline = func(*ir.Func) {}
   649  
   650  // NewInline allows the inliner implementation to be overridden.
   651  // If it returns nil, the legacy inliner will handle this call
   652  // instead.
   653  var NewInline = func(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr { return nil }
   654  
   655  // If n is a OCALLFUNC node, and fn is an ONAME node for a
   656  // function with an inlinable body, return an OINLCALL node that can replace n.
   657  // The returned node's Ninit has the parameter assignments, the Nbody is the
   658  // inlined function body, and (List, Rlist) contain the (input, output)
   659  // parameters.
   660  // The result of mkinlcall MUST be assigned back to n, e.g.
   661  // 	n.Left = mkinlcall(n.Left, fn, isddd)
   662  func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node {
   663  	if fn.Inl == nil {
   664  		if logopt.Enabled() {
   665  			logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
   666  				fmt.Sprintf("%s cannot be inlined", ir.PkgFuncName(fn)))
   667  		}
   668  		return n
   669  	}
   670  	if fn.Inl.Cost > maxCost {
   671  		// The inlined function body is too big. Typically we use this check to restrict
   672  		// inlining into very big functions.  See issue 26546 and 17566.
   673  		if logopt.Enabled() {
   674  			logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
   675  				fmt.Sprintf("cost %d of %s exceeds max large caller cost %d", fn.Inl.Cost, ir.PkgFuncName(fn), maxCost))
   676  		}
   677  		return n
   678  	}
   679  
   680  	if fn == ir.CurFunc {
   681  		// Can't recursively inline a function into itself.
   682  		if logopt.Enabled() {
   683  			logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to %s", ir.FuncName(ir.CurFunc)))
   684  		}
   685  		return n
   686  	}
   687  
   688  	// Don't inline a function fn that has no shape parameters, but is passed at
   689  	// least one shape arg. This means we must be inlining a non-generic function
   690  	// fn that was passed into a generic function, and can be called with a shape
   691  	// arg because it matches an appropriate type parameters. But fn may include
   692  	// an interface conversion (that may be applied to a shape arg) that was not
   693  	// apparent when we first created the instantiation of the generic function.
   694  	// We can't handle this if we actually do the inlining, since we want to know
   695  	// all interface conversions immediately after stenciling. So, we avoid
   696  	// inlining in this case. See #49309.
   697  	if !fn.Type().HasShape() {
   698  		for _, arg := range n.Args {
   699  			if arg.Type().HasShape() {
   700  				if logopt.Enabled() {
   701  					logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
   702  						fmt.Sprintf("inlining non-shape function %v with shape args", ir.FuncName(fn)))
   703  				}
   704  				return n
   705  			}
   706  		}
   707  	}
   708  
   709  	if base.Flag.Cfg.Instrumenting && types.IsRuntimePkg(fn.Sym().Pkg) {
   710  		// Runtime package must not be instrumented.
   711  		// Instrument skips runtime package. However, some runtime code can be
   712  		// inlined into other packages and instrumented there. To avoid this,
   713  		// we disable inlining of runtime functions when instrumenting.
   714  		// The example that we observed is inlining of LockOSThread,
   715  		// which lead to false race reports on m contents.
   716  		return n
   717  	}
   718  
   719  	if inlMap[fn] {
   720  		if base.Flag.LowerM > 1 {
   721  			fmt.Printf("%v: cannot inline %v into %v: repeated recursive cycle\n", ir.Line(n), fn, ir.FuncName(ir.CurFunc))
   722  		}
   723  		return n
   724  	}
   725  	inlMap[fn] = true
   726  	defer func() {
   727  		inlMap[fn] = false
   728  	}()
   729  
   730  	typecheck.FixVariadicCall(n)
   731  
   732  	parent := base.Ctxt.PosTable.Pos(n.Pos()).Base().InliningIndex()
   733  
   734  	sym := fn.Linksym()
   735  	inlIndex := base.Ctxt.InlTree.Add(parent, n.Pos(), sym)
   736  
   737  	if base.Flag.GenDwarfInl > 0 {
   738  		if !sym.WasInlined() {
   739  			base.Ctxt.DwFixups.SetPrecursorFunc(sym, fn)
   740  			sym.Set(obj.AttrWasInlined, true)
   741  		}
   742  	}
   743  
   744  	if base.Flag.LowerM != 0 {
   745  		fmt.Printf("%v: inlining call to %v\n", ir.Line(n), fn)
   746  	}
   747  	if base.Flag.LowerM > 2 {
   748  		fmt.Printf("%v: Before inlining: %+v\n", ir.Line(n), n)
   749  	}
   750  
   751  	res := NewInline(n, fn, inlIndex)
   752  	if res == nil {
   753  		res = oldInline(n, fn, inlIndex)
   754  	}
   755  
   756  	// transitive inlining
   757  	// might be nice to do this before exporting the body,
   758  	// but can't emit the body with inlining expanded.
   759  	// instead we emit the things that the body needs
   760  	// and each use must redo the inlining.
   761  	// luckily these are small.
   762  	ir.EditChildren(res, edit)
   763  
   764  	if base.Flag.LowerM > 2 {
   765  		fmt.Printf("%v: After inlining %+v\n\n", ir.Line(res), res)
   766  	}
   767  
   768  	return res
   769  }
   770  
   771  // CalleeEffects appends any side effects from evaluating callee to init.
   772  func CalleeEffects(init *ir.Nodes, callee ir.Node) {
   773  	for {
   774  		switch callee.Op() {
   775  		case ir.ONAME, ir.OCLOSURE, ir.OMETHEXPR:
   776  			return // done
   777  
   778  		case ir.OCONVNOP:
   779  			conv := callee.(*ir.ConvExpr)
   780  			init.Append(ir.TakeInit(conv)...)
   781  			callee = conv.X
   782  
   783  		case ir.OINLCALL:
   784  			ic := callee.(*ir.InlinedCallExpr)
   785  			init.Append(ir.TakeInit(ic)...)
   786  			init.Append(ic.Body.Take()...)
   787  			callee = ic.SingleResult()
   788  
   789  		default:
   790  			base.FatalfAt(callee.Pos(), "unexpected callee expression: %v", callee)
   791  		}
   792  	}
   793  }
   794  
   795  // oldInline creates an InlinedCallExpr to replace the given call
   796  // expression. fn is the callee function to be inlined. inlIndex is
   797  // the inlining tree position index, for use with src.NewInliningBase
   798  // when rewriting positions.
   799  func oldInline(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr {
   800  	if base.Debug.TypecheckInl == 0 {
   801  		typecheck.ImportedBody(fn)
   802  	}
   803  
   804  	SSADumpInline(fn)
   805  
   806  	ninit := call.Init()
   807  
   808  	// For normal function calls, the function callee expression
   809  	// may contain side effects. Make sure to preserve these,
   810  	// if necessary (#42703).
   811  	if call.Op() == ir.OCALLFUNC {
   812  		CalleeEffects(&ninit, call.X)
   813  	}
   814  
   815  	// Make temp names to use instead of the originals.
   816  	inlvars := make(map[*ir.Name]*ir.Name)
   817  
   818  	// record formals/locals for later post-processing
   819  	var inlfvars []*ir.Name
   820  
   821  	for _, ln := range fn.Inl.Dcl {
   822  		if ln.Op() != ir.ONAME {
   823  			continue
   824  		}
   825  		if ln.Class == ir.PPARAMOUT { // return values handled below.
   826  			continue
   827  		}
   828  		inlf := typecheck.Expr(inlvar(ln)).(*ir.Name)
   829  		inlvars[ln] = inlf
   830  		if base.Flag.GenDwarfInl > 0 {
   831  			if ln.Class == ir.PPARAM {
   832  				inlf.Name().SetInlFormal(true)
   833  			} else {
   834  				inlf.Name().SetInlLocal(true)
   835  			}
   836  			inlf.SetPos(ln.Pos())
   837  			inlfvars = append(inlfvars, inlf)
   838  		}
   839  	}
   840  
   841  	// We can delay declaring+initializing result parameters if:
   842  	// temporaries for return values.
   843  	var retvars []ir.Node
   844  	for i, t := range fn.Type().Results().Fields().Slice() {
   845  		var m *ir.Name
   846  		if nn := t.Nname; nn != nil && !ir.IsBlank(nn.(*ir.Name)) && !strings.HasPrefix(nn.Sym().Name, "~r") {
   847  			n := nn.(*ir.Name)
   848  			m = inlvar(n)
   849  			m = typecheck.Expr(m).(*ir.Name)
   850  			inlvars[n] = m
   851  		} else {
   852  			// anonymous return values, synthesize names for use in assignment that replaces return
   853  			m = retvar(t, i)
   854  		}
   855  
   856  		if base.Flag.GenDwarfInl > 0 {
   857  			// Don't update the src.Pos on a return variable if it
   858  			// was manufactured by the inliner (e.g. "~R2"); such vars
   859  			// were not part of the original callee.
   860  			if !strings.HasPrefix(m.Sym().Name, "~R") {
   861  				m.Name().SetInlFormal(true)
   862  				m.SetPos(t.Pos)
   863  				inlfvars = append(inlfvars, m)
   864  			}
   865  		}
   866  
   867  		retvars = append(retvars, m)
   868  	}
   869  
   870  	// Assign arguments to the parameters' temp names.
   871  	as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
   872  	as.Def = true
   873  	if call.Op() == ir.OCALLMETH {
   874  		base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
   875  	}
   876  	as.Rhs.Append(call.Args...)
   877  
   878  	if recv := fn.Type().Recv(); recv != nil {
   879  		as.Lhs.Append(inlParam(recv, as, inlvars))
   880  	}
   881  	for _, param := range fn.Type().Params().Fields().Slice() {
   882  		as.Lhs.Append(inlParam(param, as, inlvars))
   883  	}
   884  
   885  	if len(as.Rhs) != 0 {
   886  		ninit.Append(typecheck.Stmt(as))
   887  	}
   888  
   889  	if !fn.Inl.CanDelayResults {
   890  		// Zero the return parameters.
   891  		for _, n := range retvars {
   892  			ninit.Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
   893  			ras := ir.NewAssignStmt(base.Pos, n, nil)
   894  			ninit.Append(typecheck.Stmt(ras))
   895  		}
   896  	}
   897  
   898  	retlabel := typecheck.AutoLabel(".i")
   899  
   900  	inlgen++
   901  
   902  	// Add an inline mark just before the inlined body.
   903  	// This mark is inline in the code so that it's a reasonable spot
   904  	// to put a breakpoint. Not sure if that's really necessary or not
   905  	// (in which case it could go at the end of the function instead).
   906  	// Note issue 28603.
   907  	ninit.Append(ir.NewInlineMarkStmt(call.Pos().WithIsStmt(), int64(inlIndex)))
   908  
   909  	subst := inlsubst{
   910  		retlabel:    retlabel,
   911  		retvars:     retvars,
   912  		inlvars:     inlvars,
   913  		defnMarker:  ir.NilExpr{},
   914  		bases:       make(map[*src.PosBase]*src.PosBase),
   915  		newInlIndex: inlIndex,
   916  		fn:          fn,
   917  	}
   918  	subst.edit = subst.node
   919  
   920  	body := subst.list(ir.Nodes(fn.Inl.Body))
   921  
   922  	lab := ir.NewLabelStmt(base.Pos, retlabel)
   923  	body = append(body, lab)
   924  
   925  	if !typecheck.Go117ExportTypes {
   926  		typecheck.Stmts(body)
   927  	}
   928  
   929  	if base.Flag.GenDwarfInl > 0 {
   930  		for _, v := range inlfvars {
   931  			v.SetPos(subst.updatedPos(v.Pos()))
   932  		}
   933  	}
   934  
   935  	//dumplist("ninit post", ninit);
   936  
   937  	res := ir.NewInlinedCallExpr(base.Pos, body, retvars)
   938  	res.SetInit(ninit)
   939  	res.SetType(call.Type())
   940  	res.SetTypecheck(1)
   941  	return res
   942  }
   943  
   944  // Every time we expand a function we generate a new set of tmpnames,
   945  // PAUTO's in the calling functions, and link them off of the
   946  // PPARAM's, PAUTOS and PPARAMOUTs of the called function.
   947  func inlvar(var_ *ir.Name) *ir.Name {
   948  	if base.Flag.LowerM > 3 {
   949  		fmt.Printf("inlvar %+v\n", var_)
   950  	}
   951  
   952  	n := typecheck.NewName(var_.Sym())
   953  	n.SetType(var_.Type())
   954  	n.SetTypecheck(1)
   955  	n.Class = ir.PAUTO
   956  	n.SetUsed(true)
   957  	n.SetAutoTemp(var_.AutoTemp())
   958  	n.Curfn = ir.CurFunc // the calling function, not the called one
   959  	n.SetAddrtaken(var_.Addrtaken())
   960  
   961  	ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
   962  	return n
   963  }
   964  
   965  // Synthesize a variable to store the inlined function's results in.
   966  func retvar(t *types.Field, i int) *ir.Name {
   967  	n := typecheck.NewName(typecheck.LookupNum("~R", i))
   968  	n.SetType(t.Type)
   969  	n.SetTypecheck(1)
   970  	n.Class = ir.PAUTO
   971  	n.SetUsed(true)
   972  	n.Curfn = ir.CurFunc // the calling function, not the called one
   973  	ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
   974  	return n
   975  }
   976  
   977  // The inlsubst type implements the actual inlining of a single
   978  // function call.
   979  type inlsubst struct {
   980  	// Target of the goto substituted in place of a return.
   981  	retlabel *types.Sym
   982  
   983  	// Temporary result variables.
   984  	retvars []ir.Node
   985  
   986  	inlvars map[*ir.Name]*ir.Name
   987  	// defnMarker is used to mark a Node for reassignment.
   988  	// inlsubst.clovar set this during creating new ONAME.
   989  	// inlsubst.node will set the correct Defn for inlvar.
   990  	defnMarker ir.NilExpr
   991  
   992  	// bases maps from original PosBase to PosBase with an extra
   993  	// inlined call frame.
   994  	bases map[*src.PosBase]*src.PosBase
   995  
   996  	// newInlIndex is the index of the inlined call frame to
   997  	// insert for inlined nodes.
   998  	newInlIndex int
   999  
  1000  	edit func(ir.Node) ir.Node // cached copy of subst.node method value closure
  1001  
  1002  	// If non-nil, we are inside a closure inside the inlined function, and
  1003  	// newclofn is the Func of the new inlined closure.
  1004  	newclofn *ir.Func
  1005  
  1006  	fn *ir.Func // For debug -- the func that is being inlined
  1007  
  1008  	// If true, then don't update source positions during substitution
  1009  	// (retain old source positions).
  1010  	noPosUpdate bool
  1011  }
  1012  
  1013  // list inlines a list of nodes.
  1014  func (subst *inlsubst) list(ll ir.Nodes) []ir.Node {
  1015  	s := make([]ir.Node, 0, len(ll))
  1016  	for _, n := range ll {
  1017  		s = append(s, subst.node(n))
  1018  	}
  1019  	return s
  1020  }
  1021  
  1022  // fields returns a list of the fields of a struct type representing receiver,
  1023  // params, or results, after duplicating the field nodes and substituting the
  1024  // Nname nodes inside the field nodes.
  1025  func (subst *inlsubst) fields(oldt *types.Type) []*types.Field {
  1026  	oldfields := oldt.FieldSlice()
  1027  	newfields := make([]*types.Field, len(oldfields))
  1028  	for i := range oldfields {
  1029  		newfields[i] = oldfields[i].Copy()
  1030  		if oldfields[i].Nname != nil {
  1031  			newfields[i].Nname = subst.node(oldfields[i].Nname.(*ir.Name))
  1032  		}
  1033  	}
  1034  	return newfields
  1035  }
  1036  
  1037  // clovar creates a new ONAME node for a local variable or param of a closure
  1038  // inside a function being inlined.
  1039  func (subst *inlsubst) clovar(n *ir.Name) *ir.Name {
  1040  	m := ir.NewNameAt(n.Pos(), n.Sym())
  1041  	m.Class = n.Class
  1042  	m.SetType(n.Type())
  1043  	m.SetTypecheck(1)
  1044  	if n.IsClosureVar() {
  1045  		m.SetIsClosureVar(true)
  1046  	}
  1047  	if n.Addrtaken() {
  1048  		m.SetAddrtaken(true)
  1049  	}
  1050  	if n.Used() {
  1051  		m.SetUsed(true)
  1052  	}
  1053  	m.Defn = n.Defn
  1054  
  1055  	m.Curfn = subst.newclofn
  1056  
  1057  	switch defn := n.Defn.(type) {
  1058  	case nil:
  1059  		// ok
  1060  	case *ir.Name:
  1061  		if !n.IsClosureVar() {
  1062  			base.FatalfAt(n.Pos(), "want closure variable, got: %+v", n)
  1063  		}
  1064  		if n.Sym().Pkg != types.LocalPkg {
  1065  			// If the closure came from inlining a function from
  1066  			// another package, must change package of captured
  1067  			// variable to localpkg, so that the fields of the closure
  1068  			// struct are local package and can be accessed even if
  1069  			// name is not exported. If you disable this code, you can
  1070  			// reproduce the problem by running 'go test
  1071  			// go/internal/srcimporter'. TODO(mdempsky) - maybe change
  1072  			// how we create closure structs?
  1073  			m.SetSym(types.LocalPkg.Lookup(n.Sym().Name))
  1074  		}
  1075  		// Make sure any inlvar which is the Defn
  1076  		// of an ONAME closure var is rewritten
  1077  		// during inlining. Don't substitute
  1078  		// if Defn node is outside inlined function.
  1079  		if subst.inlvars[n.Defn.(*ir.Name)] != nil {
  1080  			m.Defn = subst.node(n.Defn)
  1081  		}
  1082  	case *ir.AssignStmt, *ir.AssignListStmt:
  1083  		// Mark node for reassignment at the end of inlsubst.node.
  1084  		m.Defn = &subst.defnMarker
  1085  	case *ir.TypeSwitchGuard:
  1086  		// TODO(mdempsky): Set m.Defn properly. See discussion on #45743.
  1087  	case *ir.RangeStmt:
  1088  		// TODO: Set m.Defn properly if we support inlining range statement in the future.
  1089  	default:
  1090  		base.FatalfAt(n.Pos(), "unexpected Defn: %+v", defn)
  1091  	}
  1092  
  1093  	if n.Outer != nil {
  1094  		// Either the outer variable is defined in function being inlined,
  1095  		// and we will replace it with the substituted variable, or it is
  1096  		// defined outside the function being inlined, and we should just
  1097  		// skip the outer variable (the closure variable of the function
  1098  		// being inlined).
  1099  		s := subst.node(n.Outer).(*ir.Name)
  1100  		if s == n.Outer {
  1101  			s = n.Outer.Outer
  1102  		}
  1103  		m.Outer = s
  1104  	}
  1105  	return m
  1106  }
  1107  
  1108  // closure does the necessary substitions for a ClosureExpr n and returns the new
  1109  // closure node.
  1110  func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
  1111  	// Prior to the subst edit, set a flag in the inlsubst to indicate
  1112  	// that we don't want to update the source positions in the new
  1113  	// closure function. If we do this, it will appear that the
  1114  	// closure itself has things inlined into it, which is not the
  1115  	// case. See issue #46234 for more details. At the same time, we
  1116  	// do want to update the position in the new ClosureExpr (which is
  1117  	// part of the function we're working on). See #49171 for an
  1118  	// example of what happens if we miss that update.
  1119  	newClosurePos := subst.updatedPos(n.Pos())
  1120  	defer func(prev bool) { subst.noPosUpdate = prev }(subst.noPosUpdate)
  1121  	subst.noPosUpdate = true
  1122  
  1123  	//fmt.Printf("Inlining func %v with closure into %v\n", subst.fn, ir.FuncName(ir.CurFunc))
  1124  
  1125  	oldfn := n.Func
  1126  	newfn := ir.NewClosureFunc(oldfn.Pos(), true)
  1127  
  1128  	// Ntype can be nil for -G=3 mode.
  1129  	if oldfn.Nname.Ntype != nil {
  1130  		newfn.Nname.Ntype = subst.node(oldfn.Nname.Ntype).(ir.Ntype)
  1131  	}
  1132  
  1133  	if subst.newclofn != nil {
  1134  		//fmt.Printf("Inlining a closure with a nested closure\n")
  1135  	}
  1136  	prevxfunc := subst.newclofn
  1137  
  1138  	// Mark that we are now substituting within a closure (within the
  1139  	// inlined function), and create new nodes for all the local
  1140  	// vars/params inside this closure.
  1141  	subst.newclofn = newfn
  1142  	newfn.Dcl = nil
  1143  	newfn.ClosureVars = nil
  1144  	for _, oldv := range oldfn.Dcl {
  1145  		newv := subst.clovar(oldv)
  1146  		subst.inlvars[oldv] = newv
  1147  		newfn.Dcl = append(newfn.Dcl, newv)
  1148  	}
  1149  	for _, oldv := range oldfn.ClosureVars {
  1150  		newv := subst.clovar(oldv)
  1151  		subst.inlvars[oldv] = newv
  1152  		newfn.ClosureVars = append(newfn.ClosureVars, newv)
  1153  	}
  1154  
  1155  	// Need to replace ONAME nodes in
  1156  	// newfn.Type().FuncType().Receiver/Params/Results.FieldSlice().Nname
  1157  	oldt := oldfn.Type()
  1158  	newrecvs := subst.fields(oldt.Recvs())
  1159  	var newrecv *types.Field
  1160  	if len(newrecvs) > 0 {
  1161  		newrecv = newrecvs[0]
  1162  	}
  1163  	newt := types.NewSignature(oldt.Pkg(), newrecv,
  1164  		nil, subst.fields(oldt.Params()), subst.fields(oldt.Results()))
  1165  
  1166  	newfn.Nname.SetType(newt)
  1167  	newfn.Body = subst.list(oldfn.Body)
  1168  
  1169  	// Remove the nodes for the current closure from subst.inlvars
  1170  	for _, oldv := range oldfn.Dcl {
  1171  		delete(subst.inlvars, oldv)
  1172  	}
  1173  	for _, oldv := range oldfn.ClosureVars {
  1174  		delete(subst.inlvars, oldv)
  1175  	}
  1176  	// Go back to previous closure func
  1177  	subst.newclofn = prevxfunc
  1178  
  1179  	// Actually create the named function for the closure, now that
  1180  	// the closure is inlined in a specific function.
  1181  	newclo := newfn.OClosure
  1182  	newclo.SetPos(newClosurePos)
  1183  	newclo.SetInit(subst.list(n.Init()))
  1184  	return typecheck.Expr(newclo)
  1185  }
  1186  
  1187  // node recursively copies a node from the saved pristine body of the
  1188  // inlined function, substituting references to input/output
  1189  // parameters with ones to the tmpnames, and substituting returns with
  1190  // assignments to the output.
  1191  func (subst *inlsubst) node(n ir.Node) ir.Node {
  1192  	if n == nil {
  1193  		return nil
  1194  	}
  1195  
  1196  	switch n.Op() {
  1197  	case ir.ONAME:
  1198  		n := n.(*ir.Name)
  1199  
  1200  		// Handle captured variables when inlining closures.
  1201  		if n.IsClosureVar() && subst.newclofn == nil {
  1202  			o := n.Outer
  1203  
  1204  			// Deal with case where sequence of closures are inlined.
  1205  			// TODO(danscales) - write test case to see if we need to
  1206  			// go up multiple levels.
  1207  			if o.Curfn != ir.CurFunc {
  1208  				o = o.Outer
  1209  			}
  1210  
  1211  			// make sure the outer param matches the inlining location
  1212  			if o == nil || o.Curfn != ir.CurFunc {
  1213  				base.Fatalf("%v: unresolvable capture %v\n", ir.Line(n), n)
  1214  			}
  1215  
  1216  			if base.Flag.LowerM > 2 {
  1217  				fmt.Printf("substituting captured name %+v  ->  %+v\n", n, o)
  1218  			}
  1219  			return o
  1220  		}
  1221  
  1222  		if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode
  1223  			if base.Flag.LowerM > 2 {
  1224  				fmt.Printf("substituting name %+v  ->  %+v\n", n, inlvar)
  1225  			}
  1226  			return inlvar
  1227  		}
  1228  
  1229  		if base.Flag.LowerM > 2 {
  1230  			fmt.Printf("not substituting name %+v\n", n)
  1231  		}
  1232  		return n
  1233  
  1234  	case ir.OMETHEXPR:
  1235  		n := n.(*ir.SelectorExpr)
  1236  		return n
  1237  
  1238  	case ir.OLITERAL, ir.ONIL, ir.OTYPE:
  1239  		// If n is a named constant or type, we can continue
  1240  		// using it in the inline copy. Otherwise, make a copy
  1241  		// so we can update the line number.
  1242  		if n.Sym() != nil {
  1243  			return n
  1244  		}
  1245  
  1246  	case ir.ORETURN:
  1247  		if subst.newclofn != nil {
  1248  			// Don't do special substitutions if inside a closure
  1249  			break
  1250  		}
  1251  		// Because of the above test for subst.newclofn,
  1252  		// this return is guaranteed to belong to the current inlined function.
  1253  		n := n.(*ir.ReturnStmt)
  1254  		init := subst.list(n.Init())
  1255  		if len(subst.retvars) != 0 && len(n.Results) != 0 {
  1256  			as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
  1257  
  1258  			// Make a shallow copy of retvars.
  1259  			// Otherwise OINLCALL.Rlist will be the same list,
  1260  			// and later walk and typecheck may clobber it.
  1261  			for _, n := range subst.retvars {
  1262  				as.Lhs.Append(n)
  1263  			}
  1264  			as.Rhs = subst.list(n.Results)
  1265  
  1266  			if subst.fn.Inl.CanDelayResults {
  1267  				for _, n := range as.Lhs {
  1268  					as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
  1269  					n.Name().Defn = as
  1270  				}
  1271  			}
  1272  
  1273  			init = append(init, typecheck.Stmt(as))
  1274  		}
  1275  		init = append(init, ir.NewBranchStmt(base.Pos, ir.OGOTO, subst.retlabel))
  1276  		typecheck.Stmts(init)
  1277  		return ir.NewBlockStmt(base.Pos, init)
  1278  
  1279  	case ir.OGOTO, ir.OBREAK, ir.OCONTINUE:
  1280  		if subst.newclofn != nil {
  1281  			// Don't do special substitutions if inside a closure
  1282  			break
  1283  		}
  1284  		n := n.(*ir.BranchStmt)
  1285  		m := ir.Copy(n).(*ir.BranchStmt)
  1286  		m.SetPos(subst.updatedPos(m.Pos()))
  1287  		m.SetInit(nil)
  1288  		m.Label = translateLabel(n.Label)
  1289  		return m
  1290  
  1291  	case ir.OLABEL:
  1292  		if subst.newclofn != nil {
  1293  			// Don't do special substitutions if inside a closure
  1294  			break
  1295  		}
  1296  		n := n.(*ir.LabelStmt)
  1297  		m := ir.Copy(n).(*ir.LabelStmt)
  1298  		m.SetPos(subst.updatedPos(m.Pos()))
  1299  		m.SetInit(nil)
  1300  		m.Label = translateLabel(n.Label)
  1301  		return m
  1302  
  1303  	case ir.OCLOSURE:
  1304  		return subst.closure(n.(*ir.ClosureExpr))
  1305  
  1306  	}
  1307  
  1308  	m := ir.Copy(n)
  1309  	m.SetPos(subst.updatedPos(m.Pos()))
  1310  	ir.EditChildren(m, subst.edit)
  1311  
  1312  	if subst.newclofn == nil {
  1313  		// Translate any label on FOR, RANGE loops or SWITCH
  1314  		switch m.Op() {
  1315  		case ir.OFOR:
  1316  			m := m.(*ir.ForStmt)
  1317  			m.Label = translateLabel(m.Label)
  1318  			return m
  1319  
  1320  		case ir.ORANGE:
  1321  			m := m.(*ir.RangeStmt)
  1322  			m.Label = translateLabel(m.Label)
  1323  			return m
  1324  
  1325  		case ir.OSWITCH:
  1326  			m := m.(*ir.SwitchStmt)
  1327  			m.Label = translateLabel(m.Label)
  1328  			return m
  1329  		}
  1330  
  1331  	}
  1332  
  1333  	switch m := m.(type) {
  1334  	case *ir.AssignStmt:
  1335  		if lhs, ok := m.X.(*ir.Name); ok && lhs.Defn == &subst.defnMarker {
  1336  			lhs.Defn = m
  1337  		}
  1338  	case *ir.AssignListStmt:
  1339  		for _, lhs := range m.Lhs {
  1340  			if lhs, ok := lhs.(*ir.Name); ok && lhs.Defn == &subst.defnMarker {
  1341  				lhs.Defn = m
  1342  			}
  1343  		}
  1344  	}
  1345  
  1346  	return m
  1347  }
  1348  
  1349  // translateLabel makes a label from an inlined function (if non-nil) be unique by
  1350  // adding "·inlgen".
  1351  func translateLabel(l *types.Sym) *types.Sym {
  1352  	if l == nil {
  1353  		return nil
  1354  	}
  1355  	p := fmt.Sprintf("%s·%d", l.Name, inlgen)
  1356  	return typecheck.Lookup(p)
  1357  }
  1358  
  1359  func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos {
  1360  	if subst.noPosUpdate {
  1361  		return xpos
  1362  	}
  1363  	pos := base.Ctxt.PosTable.Pos(xpos)
  1364  	oldbase := pos.Base() // can be nil
  1365  	newbase := subst.bases[oldbase]
  1366  	if newbase == nil {
  1367  		newbase = src.NewInliningBase(oldbase, subst.newInlIndex)
  1368  		subst.bases[oldbase] = newbase
  1369  	}
  1370  	pos.SetBase(newbase)
  1371  	return base.Ctxt.PosTable.XPos(pos)
  1372  }
  1373  
  1374  func pruneUnusedAutos(ll []*ir.Name, vis *hairyVisitor) []*ir.Name {
  1375  	s := make([]*ir.Name, 0, len(ll))
  1376  	for _, n := range ll {
  1377  		if n.Class == ir.PAUTO {
  1378  			if !vis.usedLocals.Has(n) {
  1379  				continue
  1380  			}
  1381  		}
  1382  		s = append(s, n)
  1383  	}
  1384  	return s
  1385  }
  1386  
  1387  // numNonClosures returns the number of functions in list which are not closures.
  1388  func numNonClosures(list []*ir.Func) int {
  1389  	count := 0
  1390  	for _, fn := range list {
  1391  		if fn.OClosure == nil {
  1392  			count++
  1393  		}
  1394  	}
  1395  	return count
  1396  }
  1397  
  1398  func doList(list []ir.Node, do func(ir.Node) bool) bool {
  1399  	for _, x := range list {
  1400  		if x != nil {
  1401  			if do(x) {
  1402  				return true
  1403  			}
  1404  		}
  1405  	}
  1406  	return false
  1407  }
  1408  

View as plain text