github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/compile/internal/gc/inl.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  //
     5  // 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 inlcalls 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 gc
    28  
    29  import (
    30  	"github.com/gagliardetto/golang-go/cmd/compile/internal/logopt"
    31  	"github.com/gagliardetto/golang-go/cmd/compile/internal/types"
    32  	"github.com/gagliardetto/golang-go/cmd/internal/obj"
    33  	"github.com/gagliardetto/golang-go/cmd/internal/src"
    34  	"fmt"
    35  	"strings"
    36  )
    37  
    38  // Inlining budget parameters, gathered in one place
    39  const (
    40  	inlineMaxBudget       = 80
    41  	inlineExtraAppendCost = 0
    42  	// default is to inline if there's at most one call. -l=4 overrides this by using 1 instead.
    43  	inlineExtraCallCost  = 57              // 57 was benchmarked to provided most benefit with no bad surprises; see https://github.com/golang/go/issues/19348#issuecomment-439370742
    44  	inlineExtraPanicCost = 1               // do not penalize inlining panics.
    45  	inlineExtraThrowCost = inlineMaxBudget // with current (2018-05/1.11) code, inlining runtime.throw does not help.
    46  
    47  	inlineBigFunctionNodes   = 5000 // Functions with this many nodes are considered "big".
    48  	inlineBigFunctionMaxCost = 20   // Max cost of inlinee when inlining into a "big" function.
    49  )
    50  
    51  // Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
    52  // the ->sym can be re-used in the local package, so peel it off the receiver's type.
    53  func fnpkg(fn *Node) *types.Pkg {
    54  	if fn.IsMethod() {
    55  		// method
    56  		rcvr := fn.Type.Recv().Type
    57  
    58  		if rcvr.IsPtr() {
    59  			rcvr = rcvr.Elem()
    60  		}
    61  		if rcvr.Sym == nil {
    62  			Fatalf("receiver with no sym: [%v] %L  (%v)", fn.Sym, fn, rcvr)
    63  		}
    64  		return rcvr.Sym.Pkg
    65  	}
    66  
    67  	// non-method
    68  	return fn.Sym.Pkg
    69  }
    70  
    71  // Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck
    72  // because they're a copy of an already checked body.
    73  func typecheckinl(fn *Node) {
    74  	lno := setlineno(fn)
    75  
    76  	expandInline(fn)
    77  
    78  	// typecheckinl is only for imported functions;
    79  	// their bodies may refer to unsafe as long as the package
    80  	// was marked safe during import (which was checked then).
    81  	// the ->inl of a local function has been typechecked before caninl copied it.
    82  	pkg := fnpkg(fn)
    83  
    84  	if pkg == localpkg || pkg == nil {
    85  		return // typecheckinl on local function
    86  	}
    87  
    88  	if Debug['m'] > 2 || Debug_export != 0 {
    89  		fmt.Printf("typecheck import [%v] %L { %#v }\n", fn.Sym, fn, asNodes(fn.Func.Inl.Body))
    90  	}
    91  
    92  	savefn := Curfn
    93  	Curfn = fn
    94  	typecheckslice(fn.Func.Inl.Body, ctxStmt)
    95  	Curfn = savefn
    96  
    97  	// During typechecking, declarations are added to
    98  	// Curfn.Func.Dcl. Move them to Inl.Dcl for consistency with
    99  	// how local functions behave. (Append because typecheckinl
   100  	// may be called multiple times.)
   101  	fn.Func.Inl.Dcl = append(fn.Func.Inl.Dcl, fn.Func.Dcl...)
   102  	fn.Func.Dcl = nil
   103  
   104  	lineno = lno
   105  }
   106  
   107  // Caninl determines whether fn is inlineable.
   108  // If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy.
   109  // fn and ->nbody will already have been typechecked.
   110  func caninl(fn *Node) {
   111  	if fn.Op != ODCLFUNC {
   112  		Fatalf("caninl %v", fn)
   113  	}
   114  	if fn.Func.Nname == nil {
   115  		Fatalf("caninl no nname %+v", fn)
   116  	}
   117  
   118  	var reason string // reason, if any, that the function was not inlined
   119  	if Debug['m'] > 1 || logopt.Enabled() {
   120  		defer func() {
   121  			if reason != "" {
   122  				if Debug['m'] > 1 {
   123  					fmt.Printf("%v: cannot inline %v: %s\n", fn.Line(), fn.Func.Nname, reason)
   124  				}
   125  				if logopt.Enabled() {
   126  					logopt.LogOpt(fn.Pos, "cannotInlineFunction", "inline", fn.funcname(), reason)
   127  				}
   128  			}
   129  		}()
   130  	}
   131  
   132  	// If marked "go:noinline", don't inline
   133  	if fn.Func.Pragma&Noinline != 0 {
   134  		reason = "marked go:noinline"
   135  		return
   136  	}
   137  
   138  	// If marked "go:norace" and -race compilation, don't inline.
   139  	if flag_race && fn.Func.Pragma&Norace != 0 {
   140  		reason = "marked go:norace with -race compilation"
   141  		return
   142  	}
   143  
   144  	// If marked "go:nocheckptr" and -d checkptr compilation, don't inline.
   145  	if Debug_checkptr != 0 && fn.Func.Pragma&NoCheckPtr != 0 {
   146  		reason = "marked go:nocheckptr"
   147  		return
   148  	}
   149  
   150  	// If marked "go:cgo_unsafe_args", don't inline, since the
   151  	// function makes assumptions about its argument frame layout.
   152  	if fn.Func.Pragma&CgoUnsafeArgs != 0 {
   153  		reason = "marked go:cgo_unsafe_args"
   154  		return
   155  	}
   156  
   157  	// If marked as "go:uintptrescapes", don't inline, since the
   158  	// escape information is lost during inlining.
   159  	if fn.Func.Pragma&UintptrEscapes != 0 {
   160  		reason = "marked as having an escaping uintptr argument"
   161  		return
   162  	}
   163  
   164  	// The nowritebarrierrec checker currently works at function
   165  	// granularity, so inlining yeswritebarrierrec functions can
   166  	// confuse it (#22342). As a workaround, disallow inlining
   167  	// them for now.
   168  	if fn.Func.Pragma&Yeswritebarrierrec != 0 {
   169  		reason = "marked go:yeswritebarrierrec"
   170  		return
   171  	}
   172  
   173  	// If fn has no body (is defined outside of Go), cannot inline it.
   174  	if fn.Nbody.Len() == 0 {
   175  		reason = "no function body"
   176  		return
   177  	}
   178  
   179  	if fn.Typecheck() == 0 {
   180  		Fatalf("caninl on non-typechecked function %v", fn)
   181  	}
   182  
   183  	n := fn.Func.Nname
   184  	if n.Func.InlinabilityChecked() {
   185  		return
   186  	}
   187  	defer n.Func.SetInlinabilityChecked(true)
   188  
   189  	cc := int32(inlineExtraCallCost)
   190  	if Debug['l'] == 4 {
   191  		cc = 1 // this appears to yield better performance than 0.
   192  	}
   193  
   194  	// At this point in the game the function we're looking at may
   195  	// have "stale" autos, vars that still appear in the Dcl list, but
   196  	// which no longer have any uses in the function body (due to
   197  	// elimination by deadcode). We'd like to exclude these dead vars
   198  	// when creating the "Inline.Dcl" field below; to accomplish this,
   199  	// the hairyVisitor below builds up a map of used/referenced
   200  	// locals, and we use this map to produce a pruned Inline.Dcl
   201  	// list. See issue 25249 for more context.
   202  
   203  	visitor := hairyVisitor{
   204  		budget:        inlineMaxBudget,
   205  		extraCallCost: cc,
   206  		usedLocals:    make(map[*Node]bool),
   207  	}
   208  	if visitor.visitList(fn.Nbody) {
   209  		reason = visitor.reason
   210  		return
   211  	}
   212  	if visitor.budget < 0 {
   213  		reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", inlineMaxBudget-visitor.budget, inlineMaxBudget)
   214  		return
   215  	}
   216  
   217  	n.Func.Inl = &Inline{
   218  		Cost: inlineMaxBudget - visitor.budget,
   219  		Dcl:  inlcopylist(pruneUnusedAutos(n.Name.Defn.Func.Dcl, &visitor)),
   220  		Body: inlcopylist(fn.Nbody.Slice()),
   221  	}
   222  
   223  	// hack, TODO, check for better way to link method nodes back to the thing with the ->inl
   224  	// this is so export can find the body of a method
   225  	fn.Type.FuncType().Nname = asTypesNode(n)
   226  
   227  	if Debug['m'] > 1 {
   228  		fmt.Printf("%v: can inline %#v as: %#v { %#v }\n", fn.Line(), n, fn.Type, asNodes(n.Func.Inl.Body))
   229  	} else if Debug['m'] != 0 {
   230  		fmt.Printf("%v: can inline %v\n", fn.Line(), n)
   231  	}
   232  	if logopt.Enabled() {
   233  		logopt.LogOpt(fn.Pos, "canInlineFunction", "inline", fn.funcname(), fmt.Sprintf("cost: %d", inlineMaxBudget-visitor.budget))
   234  	}
   235  }
   236  
   237  // inlFlood marks n's inline body for export and recursively ensures
   238  // all called functions are marked too.
   239  func inlFlood(n *Node) {
   240  	if n == nil {
   241  		return
   242  	}
   243  	if n.Op != ONAME || n.Class() != PFUNC {
   244  		Fatalf("inlFlood: unexpected %v, %v, %v", n, n.Op, n.Class())
   245  	}
   246  	if n.Func == nil {
   247  		Fatalf("inlFlood: missing Func on %v", n)
   248  	}
   249  	if n.Func.Inl == nil {
   250  		return
   251  	}
   252  
   253  	if n.Func.ExportInline() {
   254  		return
   255  	}
   256  	n.Func.SetExportInline(true)
   257  
   258  	typecheckinl(n)
   259  
   260  	inspectList(asNodes(n.Func.Inl.Body), func(n *Node) bool {
   261  		switch n.Op {
   262  		case ONAME:
   263  			// Mark any referenced global variables or
   264  			// functions for reexport. Skip methods,
   265  			// because they're reexported alongside their
   266  			// receiver type.
   267  			if n.Class() == PEXTERN || n.Class() == PFUNC && !n.isMethodExpression() {
   268  				exportsym(n)
   269  			}
   270  
   271  		case OCALLFUNC, OCALLMETH:
   272  			// Recursively flood any functions called by
   273  			// this one.
   274  			inlFlood(asNode(n.Left.Type.Nname()))
   275  		}
   276  		return true
   277  	})
   278  }
   279  
   280  // hairyVisitor visits a function body to determine its inlining
   281  // hairiness and whether or not it can be inlined.
   282  type hairyVisitor struct {
   283  	budget        int32
   284  	reason        string
   285  	extraCallCost int32
   286  	usedLocals    map[*Node]bool
   287  }
   288  
   289  // Look for anything we want to punt on.
   290  func (v *hairyVisitor) visitList(ll Nodes) bool {
   291  	for _, n := range ll.Slice() {
   292  		if v.visit(n) {
   293  			return true
   294  		}
   295  	}
   296  	return false
   297  }
   298  
   299  func (v *hairyVisitor) visit(n *Node) bool {
   300  	if n == nil {
   301  		return false
   302  	}
   303  
   304  	switch n.Op {
   305  	// Call is okay if inlinable and we have the budget for the body.
   306  	case OCALLFUNC:
   307  		// Functions that call runtime.getcaller{pc,sp} can not be inlined
   308  		// because getcaller{pc,sp} expect a pointer to the caller's first argument.
   309  		//
   310  		// runtime.throw is a "cheap call" like panic in normal code.
   311  		if n.Left.Op == ONAME && n.Left.Class() == PFUNC && isRuntimePkg(n.Left.Sym.Pkg) {
   312  			fn := n.Left.Sym.Name
   313  			if fn == "getcallerpc" || fn == "getcallersp" {
   314  				v.reason = "call to " + fn
   315  				return true
   316  			}
   317  			if fn == "throw" {
   318  				v.budget -= inlineExtraThrowCost
   319  				break
   320  			}
   321  		}
   322  
   323  		if isIntrinsicCall(n) {
   324  			v.budget--
   325  			break
   326  		}
   327  
   328  		if fn := n.Left.Func; fn != nil && fn.Inl != nil {
   329  			v.budget -= fn.Inl.Cost
   330  			break
   331  		}
   332  		if n.Left.isMethodExpression() {
   333  			if d := asNode(n.Left.Sym.Def); d != nil && d.Func.Inl != nil {
   334  				v.budget -= d.Func.Inl.Cost
   335  				break
   336  			}
   337  		}
   338  		// TODO(mdempsky): Budget for OCLOSURE calls if we
   339  		// ever allow that. See #15561 and #23093.
   340  
   341  		// Call cost for non-leaf inlining.
   342  		v.budget -= v.extraCallCost
   343  
   344  	// Call is okay if inlinable and we have the budget for the body.
   345  	case OCALLMETH:
   346  		t := n.Left.Type
   347  		if t == nil {
   348  			Fatalf("no function type for [%p] %+v\n", n.Left, n.Left)
   349  		}
   350  		if t.Nname() == nil {
   351  			Fatalf("no function definition for [%p] %+v\n", t, t)
   352  		}
   353  		if isRuntimePkg(n.Left.Sym.Pkg) {
   354  			fn := n.Left.Sym.Name
   355  			if fn == "heapBits.nextArena" {
   356  				// Special case: explicitly allow
   357  				// mid-stack inlining of
   358  				// runtime.heapBits.next even though
   359  				// it calls slow-path
   360  				// runtime.heapBits.nextArena.
   361  				break
   362  			}
   363  		}
   364  		if inlfn := asNode(t.FuncType().Nname).Func; inlfn.Inl != nil {
   365  			v.budget -= inlfn.Inl.Cost
   366  			break
   367  		}
   368  		// Call cost for non-leaf inlining.
   369  		v.budget -= v.extraCallCost
   370  
   371  	// Things that are too hairy, irrespective of the budget
   372  	case OCALL, OCALLINTER:
   373  		// Call cost for non-leaf inlining.
   374  		v.budget -= v.extraCallCost
   375  
   376  	case OPANIC:
   377  		v.budget -= inlineExtraPanicCost
   378  
   379  	case ORECOVER:
   380  		// recover matches the argument frame pointer to find
   381  		// the right panic value, so it needs an argument frame.
   382  		v.reason = "call to recover"
   383  		return true
   384  
   385  	case OCLOSURE,
   386  		OCALLPART,
   387  		ORANGE,
   388  		OFOR,
   389  		OFORUNTIL,
   390  		OSELECT,
   391  		OTYPESW,
   392  		OGO,
   393  		ODEFER,
   394  		ODCLTYPE, // can't print yet
   395  		OBREAK,
   396  		ORETJMP:
   397  		v.reason = "unhandled op " + n.Op.String()
   398  		return true
   399  
   400  	case OAPPEND:
   401  		v.budget -= inlineExtraAppendCost
   402  
   403  	case ODCLCONST, OEMPTY, OFALL, OLABEL:
   404  		// These nodes don't produce code; omit from inlining budget.
   405  		return false
   406  
   407  	case OIF:
   408  		if Isconst(n.Left, CTBOOL) {
   409  			// This if and the condition cost nothing.
   410  			return v.visitList(n.Ninit) || v.visitList(n.Nbody) ||
   411  				v.visitList(n.Rlist)
   412  		}
   413  
   414  	case ONAME:
   415  		if n.Class() == PAUTO {
   416  			v.usedLocals[n] = true
   417  		}
   418  
   419  	}
   420  
   421  	v.budget--
   422  
   423  	// When debugging, don't stop early, to get full cost of inlining this function
   424  	if v.budget < 0 && Debug['m'] < 2 && !logopt.Enabled() {
   425  		return true
   426  	}
   427  
   428  	return v.visit(n.Left) || v.visit(n.Right) ||
   429  		v.visitList(n.List) || v.visitList(n.Rlist) ||
   430  		v.visitList(n.Ninit) || v.visitList(n.Nbody)
   431  }
   432  
   433  // Inlcopy and inlcopylist recursively copy the body of a function.
   434  // Any name-like node of non-local class is marked for re-export by adding it to
   435  // the exportlist.
   436  func inlcopylist(ll []*Node) []*Node {
   437  	s := make([]*Node, 0, len(ll))
   438  	for _, n := range ll {
   439  		s = append(s, inlcopy(n))
   440  	}
   441  	return s
   442  }
   443  
   444  func inlcopy(n *Node) *Node {
   445  	if n == nil {
   446  		return nil
   447  	}
   448  
   449  	switch n.Op {
   450  	case ONAME, OTYPE, OLITERAL:
   451  		return n
   452  	}
   453  
   454  	m := n.copy()
   455  	if m.Func != nil {
   456  		Fatalf("unexpected Func: %v", m)
   457  	}
   458  	m.Left = inlcopy(n.Left)
   459  	m.Right = inlcopy(n.Right)
   460  	m.List.Set(inlcopylist(n.List.Slice()))
   461  	m.Rlist.Set(inlcopylist(n.Rlist.Slice()))
   462  	m.Ninit.Set(inlcopylist(n.Ninit.Slice()))
   463  	m.Nbody.Set(inlcopylist(n.Nbody.Slice()))
   464  
   465  	return m
   466  }
   467  
   468  func countNodes(n *Node) int {
   469  	if n == nil {
   470  		return 0
   471  	}
   472  	cnt := 1
   473  	cnt += countNodes(n.Left)
   474  	cnt += countNodes(n.Right)
   475  	for _, n1 := range n.Ninit.Slice() {
   476  		cnt += countNodes(n1)
   477  	}
   478  	for _, n1 := range n.Nbody.Slice() {
   479  		cnt += countNodes(n1)
   480  	}
   481  	for _, n1 := range n.List.Slice() {
   482  		cnt += countNodes(n1)
   483  	}
   484  	for _, n1 := range n.Rlist.Slice() {
   485  		cnt += countNodes(n1)
   486  	}
   487  	return cnt
   488  }
   489  
   490  // Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
   491  // calls made to inlineable functions. This is the external entry point.
   492  func inlcalls(fn *Node) {
   493  	savefn := Curfn
   494  	Curfn = fn
   495  	maxCost := int32(inlineMaxBudget)
   496  	if countNodes(fn) >= inlineBigFunctionNodes {
   497  		maxCost = inlineBigFunctionMaxCost
   498  	}
   499  	fn = inlnode(fn, maxCost)
   500  	if fn != Curfn {
   501  		Fatalf("inlnode replaced curfn")
   502  	}
   503  	Curfn = savefn
   504  }
   505  
   506  // Turn an OINLCALL into a statement.
   507  func inlconv2stmt(n *Node) {
   508  	n.Op = OBLOCK
   509  
   510  	// n->ninit stays
   511  	n.List.Set(n.Nbody.Slice())
   512  
   513  	n.Nbody.Set(nil)
   514  	n.Rlist.Set(nil)
   515  }
   516  
   517  // Turn an OINLCALL into a single valued expression.
   518  // The result of inlconv2expr MUST be assigned back to n, e.g.
   519  // 	n.Left = inlconv2expr(n.Left)
   520  func inlconv2expr(n *Node) *Node {
   521  	r := n.Rlist.First()
   522  	return addinit(r, append(n.Ninit.Slice(), n.Nbody.Slice()...))
   523  }
   524  
   525  // Turn the rlist (with the return values) of the OINLCALL in
   526  // n into an expression list lumping the ninit and body
   527  // containing the inlined statements on the first list element so
   528  // order will be preserved Used in return, oas2func and call
   529  // statements.
   530  func inlconv2list(n *Node) []*Node {
   531  	if n.Op != OINLCALL || n.Rlist.Len() == 0 {
   532  		Fatalf("inlconv2list %+v\n", n)
   533  	}
   534  
   535  	s := n.Rlist.Slice()
   536  	s[0] = addinit(s[0], append(n.Ninit.Slice(), n.Nbody.Slice()...))
   537  	return s
   538  }
   539  
   540  func inlnodelist(l Nodes, maxCost int32) {
   541  	s := l.Slice()
   542  	for i := range s {
   543  		s[i] = inlnode(s[i], maxCost)
   544  	}
   545  }
   546  
   547  // inlnode recurses over the tree to find inlineable calls, which will
   548  // be turned into OINLCALLs by mkinlcall. When the recursion comes
   549  // back up will examine left, right, list, rlist, ninit, ntest, nincr,
   550  // nbody and nelse and use one of the 4 inlconv/glue functions above
   551  // to turn the OINLCALL into an expression, a statement, or patch it
   552  // in to this nodes list or rlist as appropriate.
   553  // NOTE it makes no sense to pass the glue functions down the
   554  // recursion to the level where the OINLCALL gets created because they
   555  // have to edit /this/ n, so you'd have to push that one down as well,
   556  // but then you may as well do it here.  so this is cleaner and
   557  // shorter and less complicated.
   558  // The result of inlnode MUST be assigned back to n, e.g.
   559  // 	n.Left = inlnode(n.Left)
   560  func inlnode(n *Node, maxCost int32) *Node {
   561  	if n == nil {
   562  		return n
   563  	}
   564  
   565  	switch n.Op {
   566  	// inhibit inlining of their argument
   567  	case ODEFER, OGO:
   568  		switch n.Left.Op {
   569  		case OCALLFUNC, OCALLMETH:
   570  			n.Left.SetNoInline(true)
   571  		}
   572  		return n
   573  
   574  	// TODO do them here (or earlier),
   575  	// so escape analysis can avoid more heapmoves.
   576  	case OCLOSURE:
   577  		return n
   578  	}
   579  
   580  	lno := setlineno(n)
   581  
   582  	inlnodelist(n.Ninit, maxCost)
   583  	for _, n1 := range n.Ninit.Slice() {
   584  		if n1.Op == OINLCALL {
   585  			inlconv2stmt(n1)
   586  		}
   587  	}
   588  
   589  	n.Left = inlnode(n.Left, maxCost)
   590  	if n.Left != nil && n.Left.Op == OINLCALL {
   591  		n.Left = inlconv2expr(n.Left)
   592  	}
   593  
   594  	n.Right = inlnode(n.Right, maxCost)
   595  	if n.Right != nil && n.Right.Op == OINLCALL {
   596  		if n.Op == OFOR || n.Op == OFORUNTIL {
   597  			inlconv2stmt(n.Right)
   598  		} else if n.Op == OAS2FUNC {
   599  			n.Rlist.Set(inlconv2list(n.Right))
   600  			n.Right = nil
   601  			n.Op = OAS2
   602  			n.SetTypecheck(0)
   603  			n = typecheck(n, ctxStmt)
   604  		} else {
   605  			n.Right = inlconv2expr(n.Right)
   606  		}
   607  	}
   608  
   609  	inlnodelist(n.List, maxCost)
   610  	if n.Op == OBLOCK {
   611  		for _, n2 := range n.List.Slice() {
   612  			if n2.Op == OINLCALL {
   613  				inlconv2stmt(n2)
   614  			}
   615  		}
   616  	} else {
   617  		s := n.List.Slice()
   618  		for i1, n1 := range s {
   619  			if n1 != nil && n1.Op == OINLCALL {
   620  				s[i1] = inlconv2expr(s[i1])
   621  			}
   622  		}
   623  	}
   624  
   625  	inlnodelist(n.Rlist, maxCost)
   626  	s := n.Rlist.Slice()
   627  	for i1, n1 := range s {
   628  		if n1.Op == OINLCALL {
   629  			if n.Op == OIF {
   630  				inlconv2stmt(n1)
   631  			} else {
   632  				s[i1] = inlconv2expr(s[i1])
   633  			}
   634  		}
   635  	}
   636  
   637  	inlnodelist(n.Nbody, maxCost)
   638  	for _, n := range n.Nbody.Slice() {
   639  		if n.Op == OINLCALL {
   640  			inlconv2stmt(n)
   641  		}
   642  	}
   643  
   644  	// with all the branches out of the way, it is now time to
   645  	// transmogrify this node itself unless inhibited by the
   646  	// switch at the top of this function.
   647  	switch n.Op {
   648  	case OCALLFUNC, OCALLMETH:
   649  		if n.NoInline() {
   650  			return n
   651  		}
   652  	}
   653  
   654  	switch n.Op {
   655  	case OCALLFUNC:
   656  		if Debug['m'] > 3 {
   657  			fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left)
   658  		}
   659  		if n.Left.Func != nil && n.Left.Func.Inl != nil && !isIntrinsicCall(n) { // normal case
   660  			n = mkinlcall(n, n.Left, maxCost)
   661  		} else if n.Left.isMethodExpression() && asNode(n.Left.Sym.Def) != nil {
   662  			n = mkinlcall(n, asNode(n.Left.Sym.Def), maxCost)
   663  		} else if n.Left.Op == OCLOSURE {
   664  			if f := inlinableClosure(n.Left); f != nil {
   665  				n = mkinlcall(n, f, maxCost)
   666  			}
   667  		} else if n.Left.Op == ONAME && n.Left.Name != nil && n.Left.Name.Defn != nil {
   668  			if d := n.Left.Name.Defn; d.Op == OAS && d.Right.Op == OCLOSURE {
   669  				if f := inlinableClosure(d.Right); f != nil {
   670  					// NB: this check is necessary to prevent indirect re-assignment of the variable
   671  					// having the address taken after the invocation or only used for reads is actually fine
   672  					// but we have no easy way to distinguish the safe cases
   673  					if d.Left.Name.Addrtaken() {
   674  						if Debug['m'] > 1 {
   675  							fmt.Printf("%v: cannot inline escaping closure variable %v\n", n.Line(), n.Left)
   676  						}
   677  						break
   678  					}
   679  
   680  					// ensure the variable is never re-assigned
   681  					if unsafe, a := reassigned(n.Left); unsafe {
   682  						if Debug['m'] > 1 {
   683  							if a != nil {
   684  								fmt.Printf("%v: cannot inline re-assigned closure variable at %v: %v\n", n.Line(), a.Line(), a)
   685  							} else {
   686  								fmt.Printf("%v: cannot inline global closure variable %v\n", n.Line(), n.Left)
   687  							}
   688  						}
   689  						break
   690  					}
   691  					n = mkinlcall(n, f, maxCost)
   692  				}
   693  			}
   694  		}
   695  
   696  	case OCALLMETH:
   697  		if Debug['m'] > 3 {
   698  			fmt.Printf("%v:call to meth %L\n", n.Line(), n.Left.Right)
   699  		}
   700  
   701  		// typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
   702  		if n.Left.Type == nil {
   703  			Fatalf("no function type for [%p] %+v\n", n.Left, n.Left)
   704  		}
   705  
   706  		if n.Left.Type.Nname() == nil {
   707  			Fatalf("no function definition for [%p] %+v\n", n.Left.Type, n.Left.Type)
   708  		}
   709  
   710  		n = mkinlcall(n, asNode(n.Left.Type.FuncType().Nname), maxCost)
   711  	}
   712  
   713  	lineno = lno
   714  	return n
   715  }
   716  
   717  // inlinableClosure takes an OCLOSURE node and follows linkage to the matching ONAME with
   718  // the inlinable body. Returns nil if the function is not inlinable.
   719  func inlinableClosure(n *Node) *Node {
   720  	c := n.Func.Closure
   721  	caninl(c)
   722  	f := c.Func.Nname
   723  	if f == nil || f.Func.Inl == nil {
   724  		return nil
   725  	}
   726  	return f
   727  }
   728  
   729  // reassigned takes an ONAME node, walks the function in which it is defined, and returns a boolean
   730  // indicating whether the name has any assignments other than its declaration.
   731  // The second return value is the first such assignment encountered in the walk, if any. It is mostly
   732  // useful for -m output documenting the reason for inhibited optimizations.
   733  // NB: global variables are always considered to be re-assigned.
   734  // TODO: handle initial declaration not including an assignment and followed by a single assignment?
   735  func reassigned(n *Node) (bool, *Node) {
   736  	if n.Op != ONAME {
   737  		Fatalf("reassigned %v", n)
   738  	}
   739  	// no way to reliably check for no-reassignment of globals, assume it can be
   740  	if n.Name.Curfn == nil {
   741  		return true, nil
   742  	}
   743  	f := n.Name.Curfn
   744  	// There just might be a good reason for this although this can be pretty surprising:
   745  	// local variables inside a closure have Curfn pointing to the OCLOSURE node instead
   746  	// of the corresponding ODCLFUNC.
   747  	// We need to walk the function body to check for reassignments so we follow the
   748  	// linkage to the ODCLFUNC node as that is where body is held.
   749  	if f.Op == OCLOSURE {
   750  		f = f.Func.Closure
   751  	}
   752  	v := reassignVisitor{name: n}
   753  	a := v.visitList(f.Nbody)
   754  	return a != nil, a
   755  }
   756  
   757  type reassignVisitor struct {
   758  	name *Node
   759  }
   760  
   761  func (v *reassignVisitor) visit(n *Node) *Node {
   762  	if n == nil {
   763  		return nil
   764  	}
   765  	switch n.Op {
   766  	case OAS:
   767  		if n.Left == v.name && n != v.name.Name.Defn {
   768  			return n
   769  		}
   770  		return nil
   771  	case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE:
   772  		for _, p := range n.List.Slice() {
   773  			if p == v.name && n != v.name.Name.Defn {
   774  				return n
   775  			}
   776  		}
   777  		return nil
   778  	}
   779  	if a := v.visit(n.Left); a != nil {
   780  		return a
   781  	}
   782  	if a := v.visit(n.Right); a != nil {
   783  		return a
   784  	}
   785  	if a := v.visitList(n.List); a != nil {
   786  		return a
   787  	}
   788  	if a := v.visitList(n.Rlist); a != nil {
   789  		return a
   790  	}
   791  	if a := v.visitList(n.Ninit); a != nil {
   792  		return a
   793  	}
   794  	if a := v.visitList(n.Nbody); a != nil {
   795  		return a
   796  	}
   797  	return nil
   798  }
   799  
   800  func (v *reassignVisitor) visitList(l Nodes) *Node {
   801  	for _, n := range l.Slice() {
   802  		if a := v.visit(n); a != nil {
   803  			return a
   804  		}
   805  	}
   806  	return nil
   807  }
   808  
   809  func tinlvar(t *types.Field, inlvars map[*Node]*Node) *Node {
   810  	if n := asNode(t.Nname); n != nil && !n.isBlank() {
   811  		inlvar := inlvars[n]
   812  		if inlvar == nil {
   813  			Fatalf("missing inlvar for %v\n", n)
   814  		}
   815  		return inlvar
   816  	}
   817  
   818  	return typecheck(nblank, ctxExpr|ctxAssign)
   819  }
   820  
   821  var inlgen int
   822  
   823  // If n is a call, and fn is a function with an inlinable body,
   824  // return an OINLCALL.
   825  // On return ninit has the parameter assignments, the nbody is the
   826  // inlined function body and list, rlist contain the input, output
   827  // parameters.
   828  // The result of mkinlcall MUST be assigned back to n, e.g.
   829  // 	n.Left = mkinlcall(n.Left, fn, isddd)
   830  func mkinlcall(n, fn *Node, maxCost int32) *Node {
   831  	if fn.Func.Inl == nil {
   832  		// No inlinable body.
   833  		return n
   834  	}
   835  	if fn.Func.Inl.Cost > maxCost {
   836  		// The inlined function body is too big. Typically we use this check to restrict
   837  		// inlining into very big functions.  See issue 26546 and 17566.
   838  		if logopt.Enabled() {
   839  			logopt.LogOpt(n.Pos, "cannotInlineCall", "inline", Curfn.funcname(),
   840  				fmt.Sprintf("cost %d of %s exceeds max large caller cost %d", fn.Func.Inl.Cost, fn.pkgFuncName(), maxCost))
   841  		}
   842  		return n
   843  	}
   844  
   845  	if fn == Curfn || fn.Name.Defn == Curfn {
   846  		// Can't recursively inline a function into itself.
   847  		if logopt.Enabled() {
   848  			logopt.LogOpt(n.Pos, "cannotInlineCall", "inline", fmt.Sprintf("recursive call to %s", Curfn.funcname()))
   849  		}
   850  		return n
   851  	}
   852  
   853  	if instrumenting && isRuntimePkg(fn.Sym.Pkg) {
   854  		// Runtime package must not be instrumented.
   855  		// Instrument skips runtime package. However, some runtime code can be
   856  		// inlined into other packages and instrumented there. To avoid this,
   857  		// we disable inlining of runtime functions when instrumenting.
   858  		// The example that we observed is inlining of LockOSThread,
   859  		// which lead to false race reports on m contents.
   860  		return n
   861  	}
   862  
   863  	if Debug_typecheckinl == 0 {
   864  		typecheckinl(fn)
   865  	}
   866  
   867  	// We have a function node, and it has an inlineable body.
   868  	if Debug['m'] > 1 {
   869  		fmt.Printf("%v: inlining call to %v %#v { %#v }\n", n.Line(), fn.Sym, fn.Type, asNodes(fn.Func.Inl.Body))
   870  	} else if Debug['m'] != 0 {
   871  		fmt.Printf("%v: inlining call to %v\n", n.Line(), fn)
   872  	}
   873  	if Debug['m'] > 2 {
   874  		fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n)
   875  	}
   876  	if logopt.Enabled() {
   877  		logopt.LogOpt(n.Pos, "inlineCall", "inline", Curfn.funcname(), fn.pkgFuncName())
   878  	}
   879  
   880  	if ssaDump != "" && ssaDump == Curfn.funcname() {
   881  		ssaDumpInlined = append(ssaDumpInlined, fn)
   882  	}
   883  
   884  	ninit := n.Ninit
   885  
   886  	// Make temp names to use instead of the originals.
   887  	inlvars := make(map[*Node]*Node)
   888  
   889  	// record formals/locals for later post-processing
   890  	var inlfvars []*Node
   891  
   892  	// Handle captured variables when inlining closures.
   893  	if fn.Name.Defn != nil {
   894  		if c := fn.Name.Defn.Func.Closure; c != nil {
   895  			for _, v := range c.Func.Closure.Func.Cvars.Slice() {
   896  				if v.Op == OXXX {
   897  					continue
   898  				}
   899  
   900  				o := v.Name.Param.Outer
   901  				// make sure the outer param matches the inlining location
   902  				// NB: if we enabled inlining of functions containing OCLOSURE or refined
   903  				// the reassigned check via some sort of copy propagation this would most
   904  				// likely need to be changed to a loop to walk up to the correct Param
   905  				if o == nil || (o.Name.Curfn != Curfn && o.Name.Curfn.Func.Closure != Curfn) {
   906  					Fatalf("%v: unresolvable capture %v %v\n", n.Line(), fn, v)
   907  				}
   908  
   909  				if v.Name.Byval() {
   910  					iv := typecheck(inlvar(v), ctxExpr)
   911  					ninit.Append(nod(ODCL, iv, nil))
   912  					ninit.Append(typecheck(nod(OAS, iv, o), ctxStmt))
   913  					inlvars[v] = iv
   914  				} else {
   915  					addr := newname(lookup("&" + v.Sym.Name))
   916  					addr.Type = types.NewPtr(v.Type)
   917  					ia := typecheck(inlvar(addr), ctxExpr)
   918  					ninit.Append(nod(ODCL, ia, nil))
   919  					ninit.Append(typecheck(nod(OAS, ia, nod(OADDR, o, nil)), ctxStmt))
   920  					inlvars[addr] = ia
   921  
   922  					// When capturing by reference, all occurrence of the captured var
   923  					// must be substituted with dereference of the temporary address
   924  					inlvars[v] = typecheck(nod(ODEREF, ia, nil), ctxExpr)
   925  				}
   926  			}
   927  		}
   928  	}
   929  
   930  	for _, ln := range fn.Func.Inl.Dcl {
   931  		if ln.Op != ONAME {
   932  			continue
   933  		}
   934  		if ln.Class() == PPARAMOUT { // return values handled below.
   935  			continue
   936  		}
   937  		if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap
   938  			continue
   939  		}
   940  		inlvars[ln] = typecheck(inlvar(ln), ctxExpr)
   941  		if ln.Class() == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class() == PPARAM {
   942  			ninit.Append(nod(ODCL, inlvars[ln], nil))
   943  		}
   944  		if genDwarfInline > 0 {
   945  			inlf := inlvars[ln]
   946  			if ln.Class() == PPARAM {
   947  				inlf.Name.SetInlFormal(true)
   948  			} else {
   949  				inlf.Name.SetInlLocal(true)
   950  			}
   951  			inlf.Pos = ln.Pos
   952  			inlfvars = append(inlfvars, inlf)
   953  		}
   954  	}
   955  
   956  	// temporaries for return values.
   957  	var retvars []*Node
   958  	for i, t := range fn.Type.Results().Fields().Slice() {
   959  		var m *Node
   960  		mpos := t.Pos
   961  		if n := asNode(t.Nname); n != nil && !n.isBlank() {
   962  			m = inlvar(n)
   963  			m = typecheck(m, ctxExpr)
   964  			inlvars[n] = m
   965  		} else {
   966  			// anonymous return values, synthesize names for use in assignment that replaces return
   967  			m = retvar(t, i)
   968  		}
   969  
   970  		if genDwarfInline > 0 {
   971  			// Don't update the src.Pos on a return variable if it
   972  			// was manufactured by the inliner (e.g. "~R2"); such vars
   973  			// were not part of the original callee.
   974  			if !strings.HasPrefix(m.Sym.Name, "~R") {
   975  				m.Name.SetInlFormal(true)
   976  				m.Pos = mpos
   977  				inlfvars = append(inlfvars, m)
   978  			}
   979  		}
   980  
   981  		ninit.Append(nod(ODCL, m, nil))
   982  		retvars = append(retvars, m)
   983  	}
   984  
   985  	// Assign arguments to the parameters' temp names.
   986  	as := nod(OAS2, nil, nil)
   987  	as.Rlist.Set(n.List.Slice())
   988  
   989  	// For non-dotted calls to variadic functions, we assign the
   990  	// variadic parameter's temp name separately.
   991  	var vas *Node
   992  
   993  	if fn.IsMethod() {
   994  		rcv := fn.Type.Recv()
   995  
   996  		if n.Left.Op == ODOTMETH {
   997  			// For x.M(...), assign x directly to the
   998  			// receiver parameter.
   999  			if n.Left.Left == nil {
  1000  				Fatalf("method call without receiver: %+v", n)
  1001  			}
  1002  			ras := nod(OAS, tinlvar(rcv, inlvars), n.Left.Left)
  1003  			ras = typecheck(ras, ctxStmt)
  1004  			ninit.Append(ras)
  1005  		} else {
  1006  			// For T.M(...), add the receiver parameter to
  1007  			// as.List, so it's assigned by the normal
  1008  			// arguments.
  1009  			if as.Rlist.Len() == 0 {
  1010  				Fatalf("non-method call to method without first arg: %+v", n)
  1011  			}
  1012  			as.List.Append(tinlvar(rcv, inlvars))
  1013  		}
  1014  	}
  1015  
  1016  	for _, param := range fn.Type.Params().Fields().Slice() {
  1017  		// For ordinary parameters or variadic parameters in
  1018  		// dotted calls, just add the variable to the
  1019  		// assignment list, and we're done.
  1020  		if !param.IsDDD() || n.IsDDD() {
  1021  			as.List.Append(tinlvar(param, inlvars))
  1022  			continue
  1023  		}
  1024  
  1025  		// Otherwise, we need to collect the remaining values
  1026  		// to pass as a slice.
  1027  
  1028  		numvals := n.List.Len()
  1029  
  1030  		x := as.List.Len()
  1031  		for as.List.Len() < numvals {
  1032  			as.List.Append(argvar(param.Type, as.List.Len()))
  1033  		}
  1034  		varargs := as.List.Slice()[x:]
  1035  
  1036  		vas = nod(OAS, tinlvar(param, inlvars), nil)
  1037  		if len(varargs) == 0 {
  1038  			vas.Right = nodnil()
  1039  			vas.Right.Type = param.Type
  1040  		} else {
  1041  			vas.Right = nod(OCOMPLIT, nil, typenod(param.Type))
  1042  			vas.Right.List.Set(varargs)
  1043  		}
  1044  	}
  1045  
  1046  	if as.Rlist.Len() != 0 {
  1047  		as = typecheck(as, ctxStmt)
  1048  		ninit.Append(as)
  1049  	}
  1050  
  1051  	if vas != nil {
  1052  		vas = typecheck(vas, ctxStmt)
  1053  		ninit.Append(vas)
  1054  	}
  1055  
  1056  	// Zero the return parameters.
  1057  	for _, n := range retvars {
  1058  		ras := nod(OAS, n, nil)
  1059  		ras = typecheck(ras, ctxStmt)
  1060  		ninit.Append(ras)
  1061  	}
  1062  
  1063  	retlabel := autolabel(".i")
  1064  
  1065  	inlgen++
  1066  
  1067  	parent := -1
  1068  	if b := Ctxt.PosTable.Pos(n.Pos).Base(); b != nil {
  1069  		parent = b.InliningIndex()
  1070  	}
  1071  	newIndex := Ctxt.InlTree.Add(parent, n.Pos, fn.Sym.Linksym())
  1072  
  1073  	// Add an inline mark just before the inlined body.
  1074  	// This mark is inline in the code so that it's a reasonable spot
  1075  	// to put a breakpoint. Not sure if that's really necessary or not
  1076  	// (in which case it could go at the end of the function instead).
  1077  	// Note issue 28603.
  1078  	inlMark := nod(OINLMARK, nil, nil)
  1079  	inlMark.Pos = n.Pos.WithIsStmt()
  1080  	inlMark.Xoffset = int64(newIndex)
  1081  	ninit.Append(inlMark)
  1082  
  1083  	if genDwarfInline > 0 {
  1084  		if !fn.Sym.Linksym().WasInlined() {
  1085  			Ctxt.DwFixups.SetPrecursorFunc(fn.Sym.Linksym(), fn)
  1086  			fn.Sym.Linksym().Set(obj.AttrWasInlined, true)
  1087  		}
  1088  	}
  1089  
  1090  	subst := inlsubst{
  1091  		retlabel:    retlabel,
  1092  		retvars:     retvars,
  1093  		inlvars:     inlvars,
  1094  		bases:       make(map[*src.PosBase]*src.PosBase),
  1095  		newInlIndex: newIndex,
  1096  	}
  1097  
  1098  	body := subst.list(asNodes(fn.Func.Inl.Body))
  1099  
  1100  	lab := nodSym(OLABEL, nil, retlabel)
  1101  	body = append(body, lab)
  1102  
  1103  	typecheckslice(body, ctxStmt)
  1104  
  1105  	if genDwarfInline > 0 {
  1106  		for _, v := range inlfvars {
  1107  			v.Pos = subst.updatedPos(v.Pos)
  1108  		}
  1109  	}
  1110  
  1111  	//dumplist("ninit post", ninit);
  1112  
  1113  	call := nod(OINLCALL, nil, nil)
  1114  	call.Ninit.Set(ninit.Slice())
  1115  	call.Nbody.Set(body)
  1116  	call.Rlist.Set(retvars)
  1117  	call.Type = n.Type
  1118  	call.SetTypecheck(1)
  1119  
  1120  	// transitive inlining
  1121  	// might be nice to do this before exporting the body,
  1122  	// but can't emit the body with inlining expanded.
  1123  	// instead we emit the things that the body needs
  1124  	// and each use must redo the inlining.
  1125  	// luckily these are small.
  1126  	inlnodelist(call.Nbody, maxCost)
  1127  	for _, n := range call.Nbody.Slice() {
  1128  		if n.Op == OINLCALL {
  1129  			inlconv2stmt(n)
  1130  		}
  1131  	}
  1132  
  1133  	if Debug['m'] > 2 {
  1134  		fmt.Printf("%v: After inlining %+v\n\n", call.Line(), call)
  1135  	}
  1136  
  1137  	return call
  1138  }
  1139  
  1140  // Every time we expand a function we generate a new set of tmpnames,
  1141  // PAUTO's in the calling functions, and link them off of the
  1142  // PPARAM's, PAUTOS and PPARAMOUTs of the called function.
  1143  func inlvar(var_ *Node) *Node {
  1144  	if Debug['m'] > 3 {
  1145  		fmt.Printf("inlvar %+v\n", var_)
  1146  	}
  1147  
  1148  	n := newname(var_.Sym)
  1149  	n.Type = var_.Type
  1150  	n.SetClass(PAUTO)
  1151  	n.Name.SetUsed(true)
  1152  	n.Name.Curfn = Curfn // the calling function, not the called one
  1153  	n.Name.SetAddrtaken(var_.Name.Addrtaken())
  1154  
  1155  	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
  1156  	return n
  1157  }
  1158  
  1159  // Synthesize a variable to store the inlined function's results in.
  1160  func retvar(t *types.Field, i int) *Node {
  1161  	n := newname(lookupN("~R", i))
  1162  	n.Type = t.Type
  1163  	n.SetClass(PAUTO)
  1164  	n.Name.SetUsed(true)
  1165  	n.Name.Curfn = Curfn // the calling function, not the called one
  1166  	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
  1167  	return n
  1168  }
  1169  
  1170  // Synthesize a variable to store the inlined function's arguments
  1171  // when they come from a multiple return call.
  1172  func argvar(t *types.Type, i int) *Node {
  1173  	n := newname(lookupN("~arg", i))
  1174  	n.Type = t.Elem()
  1175  	n.SetClass(PAUTO)
  1176  	n.Name.SetUsed(true)
  1177  	n.Name.Curfn = Curfn // the calling function, not the called one
  1178  	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
  1179  	return n
  1180  }
  1181  
  1182  // The inlsubst type implements the actual inlining of a single
  1183  // function call.
  1184  type inlsubst struct {
  1185  	// Target of the goto substituted in place of a return.
  1186  	retlabel *types.Sym
  1187  
  1188  	// Temporary result variables.
  1189  	retvars []*Node
  1190  
  1191  	inlvars map[*Node]*Node
  1192  
  1193  	// bases maps from original PosBase to PosBase with an extra
  1194  	// inlined call frame.
  1195  	bases map[*src.PosBase]*src.PosBase
  1196  
  1197  	// newInlIndex is the index of the inlined call frame to
  1198  	// insert for inlined nodes.
  1199  	newInlIndex int
  1200  }
  1201  
  1202  // list inlines a list of nodes.
  1203  func (subst *inlsubst) list(ll Nodes) []*Node {
  1204  	s := make([]*Node, 0, ll.Len())
  1205  	for _, n := range ll.Slice() {
  1206  		s = append(s, subst.node(n))
  1207  	}
  1208  	return s
  1209  }
  1210  
  1211  // node recursively copies a node from the saved pristine body of the
  1212  // inlined function, substituting references to input/output
  1213  // parameters with ones to the tmpnames, and substituting returns with
  1214  // assignments to the output.
  1215  func (subst *inlsubst) node(n *Node) *Node {
  1216  	if n == nil {
  1217  		return nil
  1218  	}
  1219  
  1220  	switch n.Op {
  1221  	case ONAME:
  1222  		if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode
  1223  			if Debug['m'] > 2 {
  1224  				fmt.Printf("substituting name %+v  ->  %+v\n", n, inlvar)
  1225  			}
  1226  			return inlvar
  1227  		}
  1228  
  1229  		if Debug['m'] > 2 {
  1230  			fmt.Printf("not substituting name %+v\n", n)
  1231  		}
  1232  		return n
  1233  
  1234  	case OLITERAL, OTYPE:
  1235  		// If n is a named constant or type, we can continue
  1236  		// using it in the inline copy. Otherwise, make a copy
  1237  		// so we can update the line number.
  1238  		if n.Sym != nil {
  1239  			return n
  1240  		}
  1241  
  1242  		// Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function.
  1243  
  1244  	//		dump("Return before substitution", n);
  1245  	case ORETURN:
  1246  		m := nodSym(OGOTO, nil, subst.retlabel)
  1247  		m.Ninit.Set(subst.list(n.Ninit))
  1248  
  1249  		if len(subst.retvars) != 0 && n.List.Len() != 0 {
  1250  			as := nod(OAS2, nil, nil)
  1251  
  1252  			// Make a shallow copy of retvars.
  1253  			// Otherwise OINLCALL.Rlist will be the same list,
  1254  			// and later walk and typecheck may clobber it.
  1255  			for _, n := range subst.retvars {
  1256  				as.List.Append(n)
  1257  			}
  1258  			as.Rlist.Set(subst.list(n.List))
  1259  			as = typecheck(as, ctxStmt)
  1260  			m.Ninit.Append(as)
  1261  		}
  1262  
  1263  		typecheckslice(m.Ninit.Slice(), ctxStmt)
  1264  		m = typecheck(m, ctxStmt)
  1265  
  1266  		//		dump("Return after substitution", m);
  1267  		return m
  1268  
  1269  	case OGOTO, OLABEL:
  1270  		m := n.copy()
  1271  		m.Pos = subst.updatedPos(m.Pos)
  1272  		m.Ninit.Set(nil)
  1273  		p := fmt.Sprintf("%s·%d", n.Sym.Name, inlgen)
  1274  		m.Sym = lookup(p)
  1275  
  1276  		return m
  1277  	}
  1278  
  1279  	m := n.copy()
  1280  	m.Pos = subst.updatedPos(m.Pos)
  1281  	m.Ninit.Set(nil)
  1282  
  1283  	if n.Op == OCLOSURE {
  1284  		Fatalf("cannot inline function containing closure: %+v", n)
  1285  	}
  1286  
  1287  	m.Left = subst.node(n.Left)
  1288  	m.Right = subst.node(n.Right)
  1289  	m.List.Set(subst.list(n.List))
  1290  	m.Rlist.Set(subst.list(n.Rlist))
  1291  	m.Ninit.Set(append(m.Ninit.Slice(), subst.list(n.Ninit)...))
  1292  	m.Nbody.Set(subst.list(n.Nbody))
  1293  
  1294  	return m
  1295  }
  1296  
  1297  func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos {
  1298  	pos := Ctxt.PosTable.Pos(xpos)
  1299  	oldbase := pos.Base() // can be nil
  1300  	newbase := subst.bases[oldbase]
  1301  	if newbase == nil {
  1302  		newbase = src.NewInliningBase(oldbase, subst.newInlIndex)
  1303  		subst.bases[oldbase] = newbase
  1304  	}
  1305  	pos.SetBase(newbase)
  1306  	return Ctxt.PosTable.XPos(pos)
  1307  }
  1308  
  1309  func pruneUnusedAutos(ll []*Node, vis *hairyVisitor) []*Node {
  1310  	s := make([]*Node, 0, len(ll))
  1311  	for _, n := range ll {
  1312  		if n.Class() == PAUTO {
  1313  			if _, found := vis.usedLocals[n]; !found {
  1314  				continue
  1315  			}
  1316  		}
  1317  		s = append(s, n)
  1318  	}
  1319  	return s
  1320  }