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