github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/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.  -ll and more is useful to flush out bugs.
    12  // These additional levels (beyond -l) may be buggy and are not supported.
    13  //      0: disabled
    14  //      1: 40-nodes leaf functions, oneliners, lazy typechecking (default)
    15  //      2: early typechecking of all imported bodies
    16  //      3: allow variadic functions
    17  //      4: allow non-leaf functions , (breaks runtime.Caller)
    18  //
    19  //  At some point this may get another default and become switch-offable with -N.
    20  //
    21  //  The debug['m'] flag enables diagnostic output.  a single -m is useful for verifying
    22  //  which calls get inlined or not, more is for debugging, and may go away at any point.
    23  //
    24  // TODO:
    25  //   - inline functions with ... args
    26  //   - handle T.meth(f()) with func f() (t T, arg, arg, )
    27  
    28  package gc
    29  
    30  import (
    31  	"cmd/compile/internal/types"
    32  	"cmd/internal/src"
    33  	"fmt"
    34  )
    35  
    36  // Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
    37  // the ->sym can be re-used in the local package, so peel it off the receiver's type.
    38  func fnpkg(fn *Node) *types.Pkg {
    39  	if fn.IsMethod() {
    40  		// method
    41  		rcvr := fn.Type.Recv().Type
    42  
    43  		if rcvr.IsPtr() {
    44  			rcvr = rcvr.Elem()
    45  		}
    46  		if rcvr.Sym == nil {
    47  			Fatalf("receiver with no sym: [%v] %L  (%v)", fn.Sym, fn, rcvr)
    48  		}
    49  		return rcvr.Sym.Pkg
    50  	}
    51  
    52  	// non-method
    53  	return fn.Sym.Pkg
    54  }
    55  
    56  // Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck
    57  // because they're a copy of an already checked body.
    58  func typecheckinl(fn *Node) {
    59  	lno := setlineno(fn)
    60  
    61  	// typecheckinl is only for imported functions;
    62  	// their bodies may refer to unsafe as long as the package
    63  	// was marked safe during import (which was checked then).
    64  	// the ->inl of a local function has been typechecked before caninl copied it.
    65  	pkg := fnpkg(fn)
    66  
    67  	if pkg == localpkg || pkg == nil {
    68  		return // typecheckinl on local function
    69  	}
    70  
    71  	if Debug['m'] > 2 || Debug_export != 0 {
    72  		fmt.Printf("typecheck import [%v] %L { %#v }\n", fn.Sym, fn, fn.Func.Inl)
    73  	}
    74  
    75  	save_safemode := safemode
    76  	safemode = false
    77  
    78  	savefn := Curfn
    79  	Curfn = fn
    80  	typecheckslice(fn.Func.Inl.Slice(), Etop)
    81  	Curfn = savefn
    82  
    83  	safemode = save_safemode
    84  
    85  	lineno = lno
    86  }
    87  
    88  // Caninl determines whether fn is inlineable.
    89  // If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy.
    90  // fn and ->nbody will already have been typechecked.
    91  func caninl(fn *Node) {
    92  	if fn.Op != ODCLFUNC {
    93  		Fatalf("caninl %v", fn)
    94  	}
    95  	if fn.Func.Nname == nil {
    96  		Fatalf("caninl no nname %+v", fn)
    97  	}
    98  
    99  	var reason string // reason, if any, that the function was not inlined
   100  	if Debug['m'] > 1 {
   101  		defer func() {
   102  			if reason != "" {
   103  				fmt.Printf("%v: cannot inline %v: %s\n", fn.Line(), fn.Func.Nname, reason)
   104  			}
   105  		}()
   106  	}
   107  
   108  	// If marked "go:noinline", don't inline
   109  	if fn.Func.Pragma&Noinline != 0 {
   110  		reason = "marked go:noinline"
   111  		return
   112  	}
   113  
   114  	// If marked "go:cgo_unsafe_args", don't inline
   115  	if fn.Func.Pragma&CgoUnsafeArgs != 0 {
   116  		reason = "marked go:cgo_unsafe_args"
   117  		return
   118  	}
   119  
   120  	// If fn has no body (is defined outside of Go), cannot inline it.
   121  	if fn.Nbody.Len() == 0 {
   122  		reason = "no function body"
   123  		return
   124  	}
   125  
   126  	if fn.Typecheck == 0 {
   127  		Fatalf("caninl on non-typechecked function %v", fn)
   128  	}
   129  
   130  	// can't handle ... args yet
   131  	if Debug['l'] < 3 {
   132  		f := fn.Type.Params().Fields()
   133  		if len := f.Len(); len > 0 {
   134  			if t := f.Index(len - 1); t.Isddd() {
   135  				reason = "has ... args"
   136  				return
   137  			}
   138  		}
   139  	}
   140  
   141  	// Runtime package must not be instrumented.
   142  	// Instrument skips runtime package. However, some runtime code can be
   143  	// inlined into other packages and instrumented there. To avoid this,
   144  	// we disable inlining of runtime functions when instrumenting.
   145  	// The example that we observed is inlining of LockOSThread,
   146  	// which lead to false race reports on m contents.
   147  	if instrumenting && myimportpath == "runtime" {
   148  		reason = "instrumenting and is runtime function"
   149  		return
   150  	}
   151  
   152  	const maxBudget = 80
   153  	budget := int32(maxBudget) // allowed hairyness
   154  	if ishairylist(fn.Nbody, &budget, &reason) {
   155  		return
   156  	}
   157  	if budget < 0 {
   158  		reason = "function too complex"
   159  		return
   160  	}
   161  
   162  	savefn := Curfn
   163  	Curfn = fn
   164  
   165  	n := fn.Func.Nname
   166  
   167  	n.Func.Inl.Set(fn.Nbody.Slice())
   168  	fn.Nbody.Set(inlcopylist(n.Func.Inl.Slice()))
   169  	inldcl := inlcopylist(n.Name.Defn.Func.Dcl)
   170  	n.Func.Inldcl.Set(inldcl)
   171  	n.Func.InlCost = maxBudget - budget
   172  
   173  	// hack, TODO, check for better way to link method nodes back to the thing with the ->inl
   174  	// this is so export can find the body of a method
   175  	fn.Type.FuncType().Nname = asTypesNode(n)
   176  
   177  	if Debug['m'] > 1 {
   178  		fmt.Printf("%v: can inline %#v as: %#v { %#v }\n", fn.Line(), n, fn.Type, n.Func.Inl)
   179  	} else if Debug['m'] != 0 {
   180  		fmt.Printf("%v: can inline %v\n", fn.Line(), n)
   181  	}
   182  
   183  	Curfn = savefn
   184  }
   185  
   186  // Look for anything we want to punt on.
   187  func ishairylist(ll Nodes, budget *int32, reason *string) bool {
   188  	for _, n := range ll.Slice() {
   189  		if ishairy(n, budget, reason) {
   190  			return true
   191  		}
   192  	}
   193  	return false
   194  }
   195  
   196  func ishairy(n *Node, budget *int32, reason *string) bool {
   197  	if n == nil {
   198  		return false
   199  	}
   200  
   201  	switch n.Op {
   202  	// Call is okay if inlinable and we have the budget for the body.
   203  	case OCALLFUNC:
   204  		if isIntrinsicCall(n) {
   205  			*budget--
   206  			break
   207  		}
   208  		if fn := n.Left.Func; fn != nil && fn.Inl.Len() != 0 {
   209  			*budget -= fn.InlCost
   210  			break
   211  		}
   212  
   213  		if n.isMethodCalledAsFunction() {
   214  			if d := asNode(n.Left.Sym.Def); d != nil && d.Func.Inl.Len() != 0 {
   215  				*budget -= d.Func.InlCost
   216  				break
   217  			}
   218  		}
   219  		if Debug['l'] < 4 {
   220  			*reason = "non-leaf function"
   221  			return true
   222  		}
   223  
   224  	// Call is okay if inlinable and we have the budget for the body.
   225  	case OCALLMETH:
   226  		t := n.Left.Type
   227  		if t == nil {
   228  			Fatalf("no function type for [%p] %+v\n", n.Left, n.Left)
   229  		}
   230  		if t.Nname() == nil {
   231  			Fatalf("no function definition for [%p] %+v\n", t, t)
   232  		}
   233  		if inlfn := asNode(t.FuncType().Nname).Func; inlfn.Inl.Len() != 0 {
   234  			*budget -= inlfn.InlCost
   235  			break
   236  		}
   237  		if Debug['l'] < 4 {
   238  			*reason = "non-leaf method"
   239  			return true
   240  		}
   241  
   242  	// Things that are too hairy, irrespective of the budget
   243  	case OCALL, OCALLINTER, OPANIC, ORECOVER:
   244  		if Debug['l'] < 4 {
   245  			*reason = "non-leaf op " + n.Op.String()
   246  			return true
   247  		}
   248  
   249  	case OCLOSURE,
   250  		OCALLPART,
   251  		ORANGE,
   252  		OFOR,
   253  		OFORUNTIL,
   254  		OSELECT,
   255  		OTYPESW,
   256  		OPROC,
   257  		ODEFER,
   258  		ODCLTYPE, // can't print yet
   259  		OBREAK,
   260  		ORETJMP:
   261  		*reason = "unhandled op " + n.Op.String()
   262  		return true
   263  	}
   264  
   265  	(*budget)--
   266  	// TODO(mdempsky/josharian): Hacks to appease toolstash; remove.
   267  	// See issue 17566 and CL 31674 for discussion.
   268  	switch n.Op {
   269  	case OSTRUCTKEY:
   270  		(*budget)--
   271  	case OSLICE, OSLICEARR, OSLICESTR:
   272  		(*budget)--
   273  	case OSLICE3, OSLICE3ARR:
   274  		*budget -= 2
   275  	}
   276  
   277  	if *budget < 0 {
   278  		*reason = "function too complex"
   279  		return true
   280  	}
   281  
   282  	if n.Op == OIF && Isconst(n.Left, CTBOOL) {
   283  		var taken Nodes // statements for the branch that is always taken
   284  		if n.Left.Bool() {
   285  			taken = n.Nbody // then case
   286  		} else {
   287  			taken = n.Rlist // else case
   288  		}
   289  		return ishairylist(n.Ninit, budget, reason) || ishairylist(taken, budget, reason)
   290  	}
   291  
   292  	return ishairy(n.Left, budget, reason) || ishairy(n.Right, budget, reason) ||
   293  		ishairylist(n.List, budget, reason) || ishairylist(n.Rlist, budget, reason) ||
   294  		ishairylist(n.Ninit, budget, reason) || ishairylist(n.Nbody, budget, reason)
   295  }
   296  
   297  // Inlcopy and inlcopylist recursively copy the body of a function.
   298  // Any name-like node of non-local class is marked for re-export by adding it to
   299  // the exportlist.
   300  func inlcopylist(ll []*Node) []*Node {
   301  	s := make([]*Node, 0, len(ll))
   302  	for _, n := range ll {
   303  		s = append(s, inlcopy(n))
   304  	}
   305  	return s
   306  }
   307  
   308  func inlcopy(n *Node) *Node {
   309  	if n == nil {
   310  		return nil
   311  	}
   312  
   313  	switch n.Op {
   314  	case ONAME, OTYPE, OLITERAL:
   315  		return n
   316  	}
   317  
   318  	m := *n
   319  	if m.Func != nil {
   320  		m.Func.Inl.Set(nil)
   321  	}
   322  	m.Left = inlcopy(n.Left)
   323  	m.Right = inlcopy(n.Right)
   324  	m.List.Set(inlcopylist(n.List.Slice()))
   325  	m.Rlist.Set(inlcopylist(n.Rlist.Slice()))
   326  	m.Ninit.Set(inlcopylist(n.Ninit.Slice()))
   327  	m.Nbody.Set(inlcopylist(n.Nbody.Slice()))
   328  
   329  	return &m
   330  }
   331  
   332  // Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
   333  // calls made to inlineable functions. This is the external entry point.
   334  func inlcalls(fn *Node) {
   335  	savefn := Curfn
   336  	Curfn = fn
   337  	fn = inlnode(fn)
   338  	if fn != Curfn {
   339  		Fatalf("inlnode replaced curfn")
   340  	}
   341  	Curfn = savefn
   342  }
   343  
   344  // Turn an OINLCALL into a statement.
   345  func inlconv2stmt(n *Node) {
   346  	n.Op = OBLOCK
   347  
   348  	// n->ninit stays
   349  	n.List.Set(n.Nbody.Slice())
   350  
   351  	n.Nbody.Set(nil)
   352  	n.Rlist.Set(nil)
   353  }
   354  
   355  // Turn an OINLCALL into a single valued expression.
   356  // The result of inlconv2expr MUST be assigned back to n, e.g.
   357  // 	n.Left = inlconv2expr(n.Left)
   358  func inlconv2expr(n *Node) *Node {
   359  	r := n.Rlist.First()
   360  	return addinit(r, append(n.Ninit.Slice(), n.Nbody.Slice()...))
   361  }
   362  
   363  // Turn the rlist (with the return values) of the OINLCALL in
   364  // n into an expression list lumping the ninit and body
   365  // containing the inlined statements on the first list element so
   366  // order will be preserved Used in return, oas2func and call
   367  // statements.
   368  func inlconv2list(n *Node) []*Node {
   369  	if n.Op != OINLCALL || n.Rlist.Len() == 0 {
   370  		Fatalf("inlconv2list %+v\n", n)
   371  	}
   372  
   373  	s := n.Rlist.Slice()
   374  	s[0] = addinit(s[0], append(n.Ninit.Slice(), n.Nbody.Slice()...))
   375  	return s
   376  }
   377  
   378  func inlnodelist(l Nodes) {
   379  	s := l.Slice()
   380  	for i := range s {
   381  		s[i] = inlnode(s[i])
   382  	}
   383  }
   384  
   385  // inlnode recurses over the tree to find inlineable calls, which will
   386  // be turned into OINLCALLs by mkinlcall. When the recursion comes
   387  // back up will examine left, right, list, rlist, ninit, ntest, nincr,
   388  // nbody and nelse and use one of the 4 inlconv/glue functions above
   389  // to turn the OINLCALL into an expression, a statement, or patch it
   390  // in to this nodes list or rlist as appropriate.
   391  // NOTE it makes no sense to pass the glue functions down the
   392  // recursion to the level where the OINLCALL gets created because they
   393  // have to edit /this/ n, so you'd have to push that one down as well,
   394  // but then you may as well do it here.  so this is cleaner and
   395  // shorter and less complicated.
   396  // The result of inlnode MUST be assigned back to n, e.g.
   397  // 	n.Left = inlnode(n.Left)
   398  func inlnode(n *Node) *Node {
   399  	if n == nil {
   400  		return n
   401  	}
   402  
   403  	switch n.Op {
   404  	// inhibit inlining of their argument
   405  	case ODEFER, OPROC:
   406  		switch n.Left.Op {
   407  		case OCALLFUNC, OCALLMETH:
   408  			n.Left.SetNoInline(true)
   409  		}
   410  		return n
   411  
   412  	// TODO do them here (or earlier),
   413  	// so escape analysis can avoid more heapmoves.
   414  	case OCLOSURE:
   415  		return n
   416  	}
   417  
   418  	lno := setlineno(n)
   419  
   420  	inlnodelist(n.Ninit)
   421  	for _, n1 := range n.Ninit.Slice() {
   422  		if n1.Op == OINLCALL {
   423  			inlconv2stmt(n1)
   424  		}
   425  	}
   426  
   427  	n.Left = inlnode(n.Left)
   428  	if n.Left != nil && n.Left.Op == OINLCALL {
   429  		n.Left = inlconv2expr(n.Left)
   430  	}
   431  
   432  	n.Right = inlnode(n.Right)
   433  	if n.Right != nil && n.Right.Op == OINLCALL {
   434  		if n.Op == OFOR || n.Op == OFORUNTIL {
   435  			inlconv2stmt(n.Right)
   436  		} else {
   437  			n.Right = inlconv2expr(n.Right)
   438  		}
   439  	}
   440  
   441  	inlnodelist(n.List)
   442  	switch n.Op {
   443  	case OBLOCK:
   444  		for _, n2 := range n.List.Slice() {
   445  			if n2.Op == OINLCALL {
   446  				inlconv2stmt(n2)
   447  			}
   448  		}
   449  
   450  	case ORETURN, OCALLFUNC, OCALLMETH, OCALLINTER, OAPPEND, OCOMPLEX:
   451  		// if we just replaced arg in f(arg()) or return arg with an inlined call
   452  		// and arg returns multiple values, glue as list
   453  		if n.List.Len() == 1 && n.List.First().Op == OINLCALL && n.List.First().Rlist.Len() > 1 {
   454  			n.List.Set(inlconv2list(n.List.First()))
   455  			break
   456  		}
   457  		fallthrough
   458  
   459  	default:
   460  		s := n.List.Slice()
   461  		for i1, n1 := range s {
   462  			if n1 != nil && n1.Op == OINLCALL {
   463  				s[i1] = inlconv2expr(s[i1])
   464  			}
   465  		}
   466  	}
   467  
   468  	inlnodelist(n.Rlist)
   469  	if n.Op == OAS2FUNC && n.Rlist.First().Op == OINLCALL {
   470  		n.Rlist.Set(inlconv2list(n.Rlist.First()))
   471  		n.Op = OAS2
   472  		n.Typecheck = 0
   473  		n = typecheck(n, Etop)
   474  	} else {
   475  		s := n.Rlist.Slice()
   476  		for i1, n1 := range s {
   477  			if n1.Op == OINLCALL {
   478  				if n.Op == OIF {
   479  					inlconv2stmt(n1)
   480  				} else {
   481  					s[i1] = inlconv2expr(s[i1])
   482  				}
   483  			}
   484  		}
   485  	}
   486  
   487  	inlnodelist(n.Nbody)
   488  	for _, n := range n.Nbody.Slice() {
   489  		if n.Op == OINLCALL {
   490  			inlconv2stmt(n)
   491  		}
   492  	}
   493  
   494  	// with all the branches out of the way, it is now time to
   495  	// transmogrify this node itself unless inhibited by the
   496  	// switch at the top of this function.
   497  	switch n.Op {
   498  	case OCALLFUNC, OCALLMETH:
   499  		if n.NoInline() {
   500  			return n
   501  		}
   502  	}
   503  
   504  	switch n.Op {
   505  	case OCALLFUNC:
   506  		if Debug['m'] > 3 {
   507  			fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left)
   508  		}
   509  		if n.Left.Func != nil && n.Left.Func.Inl.Len() != 0 && !isIntrinsicCall(n) { // normal case
   510  			n = mkinlcall(n, n.Left, n.Isddd())
   511  		} else if n.isMethodCalledAsFunction() && asNode(n.Left.Sym.Def) != nil {
   512  			n = mkinlcall(n, asNode(n.Left.Sym.Def), n.Isddd())
   513  		}
   514  
   515  	case OCALLMETH:
   516  		if Debug['m'] > 3 {
   517  			fmt.Printf("%v:call to meth %L\n", n.Line(), n.Left.Right)
   518  		}
   519  
   520  		// typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
   521  		if n.Left.Type == nil {
   522  			Fatalf("no function type for [%p] %+v\n", n.Left, n.Left)
   523  		}
   524  
   525  		if n.Left.Type.Nname() == nil {
   526  			Fatalf("no function definition for [%p] %+v\n", n.Left.Type, n.Left.Type)
   527  		}
   528  
   529  		n = mkinlcall(n, asNode(n.Left.Type.FuncType().Nname), n.Isddd())
   530  	}
   531  
   532  	lineno = lno
   533  	return n
   534  }
   535  
   536  // The result of mkinlcall MUST be assigned back to n, e.g.
   537  // 	n.Left = mkinlcall(n.Left, fn, isddd)
   538  func mkinlcall(n *Node, fn *Node, isddd bool) *Node {
   539  	save_safemode := safemode
   540  
   541  	// imported functions may refer to unsafe as long as the
   542  	// package was marked safe during import (already checked).
   543  	pkg := fnpkg(fn)
   544  
   545  	if pkg != localpkg && pkg != nil {
   546  		safemode = false
   547  	}
   548  	n = mkinlcall1(n, fn, isddd)
   549  	safemode = save_safemode
   550  	return n
   551  }
   552  
   553  func tinlvar(t *types.Field, inlvars map[*Node]*Node) *Node {
   554  	if asNode(t.Nname) != nil && !isblank(asNode(t.Nname)) {
   555  		inlvar := inlvars[asNode(t.Nname)]
   556  		if inlvar == nil {
   557  			Fatalf("missing inlvar for %v\n", asNode(t.Nname))
   558  		}
   559  		return inlvar
   560  	}
   561  
   562  	return typecheck(nblank, Erv|Easgn)
   563  }
   564  
   565  var inlgen int
   566  
   567  // If n is a call, and fn is a function with an inlinable body,
   568  // return an OINLCALL.
   569  // On return ninit has the parameter assignments, the nbody is the
   570  // inlined function body and list, rlist contain the input, output
   571  // parameters.
   572  // The result of mkinlcall1 MUST be assigned back to n, e.g.
   573  // 	n.Left = mkinlcall1(n.Left, fn, isddd)
   574  func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
   575  	if fn.Func.Inl.Len() == 0 {
   576  		// No inlinable body.
   577  		return n
   578  	}
   579  
   580  	if fn == Curfn || fn.Name.Defn == Curfn {
   581  		// Can't recursively inline a function into itself.
   582  		return n
   583  	}
   584  
   585  	if Debug['l'] < 2 {
   586  		typecheckinl(fn)
   587  	}
   588  
   589  	// We have a function node, and it has an inlineable body.
   590  	if Debug['m'] > 1 {
   591  		fmt.Printf("%v: inlining call to %v %#v { %#v }\n", n.Line(), fn.Sym, fn.Type, fn.Func.Inl)
   592  	} else if Debug['m'] != 0 {
   593  		fmt.Printf("%v: inlining call to %v\n", n.Line(), fn)
   594  	}
   595  	if Debug['m'] > 2 {
   596  		fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n)
   597  	}
   598  
   599  	ninit := n.Ninit
   600  
   601  	// Find declarations corresponding to inlineable body.
   602  	var dcl []*Node
   603  	if fn.Name.Defn != nil {
   604  		dcl = fn.Func.Inldcl.Slice() // local function
   605  	} else {
   606  		dcl = fn.Func.Dcl // imported function
   607  	}
   608  
   609  	// Make temp names to use instead of the originals.
   610  	inlvars := make(map[*Node]*Node)
   611  	for _, ln := range dcl {
   612  		if ln.Op != ONAME {
   613  			continue
   614  		}
   615  		if ln.Class == PPARAMOUT { // return values handled below.
   616  			continue
   617  		}
   618  		if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap
   619  			continue
   620  		}
   621  		inlvars[ln] = typecheck(inlvar(ln), Erv)
   622  		if ln.Class == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class == PPARAM {
   623  			ninit.Append(nod(ODCL, inlvars[ln], nil))
   624  		}
   625  	}
   626  
   627  	// temporaries for return values.
   628  	var retvars []*Node
   629  	for i, t := range fn.Type.Results().Fields().Slice() {
   630  		var m *Node
   631  		if t != nil && asNode(t.Nname) != nil && !isblank(asNode(t.Nname)) {
   632  			m = inlvar(asNode(t.Nname))
   633  			m = typecheck(m, Erv)
   634  			inlvars[asNode(t.Nname)] = m
   635  		} else {
   636  			// anonymous return values, synthesize names for use in assignment that replaces return
   637  			m = retvar(t, i)
   638  		}
   639  
   640  		ninit.Append(nod(ODCL, m, nil))
   641  		retvars = append(retvars, m)
   642  	}
   643  
   644  	// Assign arguments to the parameters' temp names.
   645  	as := nod(OAS2, nil, nil)
   646  	as.Rlist.Set(n.List.Slice())
   647  
   648  	// For non-dotted calls to variadic functions, we assign the
   649  	// variadic parameter's temp name separately.
   650  	var vas *Node
   651  
   652  	if fn.IsMethod() {
   653  		rcv := fn.Type.Recv()
   654  
   655  		if n.Left.Op == ODOTMETH {
   656  			// For x.M(...), assign x directly to the
   657  			// receiver parameter.
   658  			if n.Left.Left == nil {
   659  				Fatalf("method call without receiver: %+v", n)
   660  			}
   661  			ras := nod(OAS, tinlvar(rcv, inlvars), n.Left.Left)
   662  			ras = typecheck(ras, Etop)
   663  			ninit.Append(ras)
   664  		} else {
   665  			// For T.M(...), add the receiver parameter to
   666  			// as.List, so it's assigned by the normal
   667  			// arguments.
   668  			if as.Rlist.Len() == 0 {
   669  				Fatalf("non-method call to method without first arg: %+v", n)
   670  			}
   671  			as.List.Append(tinlvar(rcv, inlvars))
   672  		}
   673  	}
   674  
   675  	for _, param := range fn.Type.Params().Fields().Slice() {
   676  		// For ordinary parameters or variadic parameters in
   677  		// dotted calls, just add the variable to the
   678  		// assignment list, and we're done.
   679  		if !param.Isddd() || isddd {
   680  			as.List.Append(tinlvar(param, inlvars))
   681  			continue
   682  		}
   683  
   684  		// Otherwise, we need to collect the remaining values
   685  		// to pass as a slice.
   686  
   687  		numvals := n.List.Len()
   688  		if numvals == 1 && n.List.First().Type.IsFuncArgStruct() {
   689  			numvals = n.List.First().Type.NumFields()
   690  		}
   691  
   692  		x := as.List.Len()
   693  		for as.List.Len() < numvals {
   694  			as.List.Append(argvar(param.Type, as.List.Len()))
   695  		}
   696  		varargs := as.List.Slice()[x:]
   697  
   698  		vas = nod(OAS, tinlvar(param, inlvars), nil)
   699  		if len(varargs) == 0 {
   700  			vas.Right = nodnil()
   701  			vas.Right.Type = param.Type
   702  		} else {
   703  			vas.Right = nod(OCOMPLIT, nil, typenod(param.Type))
   704  			vas.Right.List.Set(varargs)
   705  		}
   706  	}
   707  
   708  	if as.Rlist.Len() != 0 {
   709  		as = typecheck(as, Etop)
   710  		ninit.Append(as)
   711  	}
   712  
   713  	if vas != nil {
   714  		vas = typecheck(vas, Etop)
   715  		ninit.Append(vas)
   716  	}
   717  
   718  	// Zero the return parameters.
   719  	for _, n := range retvars {
   720  		as = nod(OAS, n, nil)
   721  		as = typecheck(as, Etop)
   722  		ninit.Append(as)
   723  	}
   724  
   725  	retlabel := autolabel(".i")
   726  	retlabel.Etype = 1 // flag 'safe' for escape analysis (no backjumps)
   727  
   728  	inlgen++
   729  
   730  	subst := inlsubst{
   731  		retlabel: retlabel,
   732  		retvars:  retvars,
   733  		inlvars:  inlvars,
   734  	}
   735  
   736  	body := subst.list(fn.Func.Inl)
   737  
   738  	lab := nod(OLABEL, retlabel, nil)
   739  	lab.SetUsed(true) // avoid 'not used' when function doesn't have return
   740  	body = append(body, lab)
   741  
   742  	typecheckslice(body, Etop)
   743  
   744  	//dumplist("ninit post", ninit);
   745  
   746  	call := nod(OINLCALL, nil, nil)
   747  	call.Ninit.Set(ninit.Slice())
   748  	call.Nbody.Set(body)
   749  	call.Rlist.Set(retvars)
   750  	call.Type = n.Type
   751  	call.Typecheck = 1
   752  
   753  	// Hide the args from setPos -- the parameters to the inlined
   754  	// call already have good line numbers that should be preserved.
   755  	args := as.Rlist
   756  	as.Rlist.Set(nil)
   757  
   758  	// Rewrite the line information for the inlined AST.
   759  	parent := -1
   760  	callBase := Ctxt.PosTable.Pos(n.Pos).Base()
   761  	if callBase != nil {
   762  		parent = callBase.InliningIndex()
   763  	}
   764  	newIndex := Ctxt.InlTree.Add(parent, n.Pos, Linksym(fn.Sym))
   765  	setpos := &setPos{
   766  		bases:       make(map[*src.PosBase]*src.PosBase),
   767  		newInlIndex: newIndex,
   768  	}
   769  	setpos.node(call)
   770  
   771  	as.Rlist.Set(args.Slice())
   772  
   773  	//dumplist("call body", body);
   774  
   775  	n = call
   776  
   777  	// transitive inlining
   778  	// might be nice to do this before exporting the body,
   779  	// but can't emit the body with inlining expanded.
   780  	// instead we emit the things that the body needs
   781  	// and each use must redo the inlining.
   782  	// luckily these are small.
   783  	body = fn.Func.Inl.Slice()
   784  	fn.Func.Inl.Set(nil) // prevent infinite recursion (shouldn't happen anyway)
   785  	inlnodelist(call.Nbody)
   786  	for _, n := range call.Nbody.Slice() {
   787  		if n.Op == OINLCALL {
   788  			inlconv2stmt(n)
   789  		}
   790  	}
   791  	fn.Func.Inl.Set(body)
   792  
   793  	if Debug['m'] > 2 {
   794  		fmt.Printf("%v: After inlining %+v\n\n", n.Line(), n)
   795  	}
   796  
   797  	return n
   798  }
   799  
   800  // Every time we expand a function we generate a new set of tmpnames,
   801  // PAUTO's in the calling functions, and link them off of the
   802  // PPARAM's, PAUTOS and PPARAMOUTs of the called function.
   803  func inlvar(var_ *Node) *Node {
   804  	if Debug['m'] > 3 {
   805  		fmt.Printf("inlvar %+v\n", var_)
   806  	}
   807  
   808  	n := newname(var_.Sym)
   809  	n.Type = var_.Type
   810  	n.Class = PAUTO
   811  	n.SetUsed(true)
   812  	n.Name.Curfn = Curfn // the calling function, not the called one
   813  	n.SetAddrtaken(var_.Addrtaken())
   814  
   815  	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
   816  	return n
   817  }
   818  
   819  // Synthesize a variable to store the inlined function's results in.
   820  func retvar(t *types.Field, i int) *Node {
   821  	n := newname(lookupN("~r", i))
   822  	n.Type = t.Type
   823  	n.Class = PAUTO
   824  	n.SetUsed(true)
   825  	n.Name.Curfn = Curfn // the calling function, not the called one
   826  	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
   827  	return n
   828  }
   829  
   830  // Synthesize a variable to store the inlined function's arguments
   831  // when they come from a multiple return call.
   832  func argvar(t *types.Type, i int) *Node {
   833  	n := newname(lookupN("~arg", i))
   834  	n.Type = t.Elem()
   835  	n.Class = PAUTO
   836  	n.SetUsed(true)
   837  	n.Name.Curfn = Curfn // the calling function, not the called one
   838  	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
   839  	return n
   840  }
   841  
   842  // The inlsubst type implements the actual inlining of a single
   843  // function call.
   844  type inlsubst struct {
   845  	// Target of the goto substituted in place of a return.
   846  	retlabel *Node
   847  
   848  	// Temporary result variables.
   849  	retvars []*Node
   850  
   851  	inlvars map[*Node]*Node
   852  }
   853  
   854  // list inlines a list of nodes.
   855  func (subst *inlsubst) list(ll Nodes) []*Node {
   856  	s := make([]*Node, 0, ll.Len())
   857  	for _, n := range ll.Slice() {
   858  		s = append(s, subst.node(n))
   859  	}
   860  	return s
   861  }
   862  
   863  // node recursively copies a node from the saved pristine body of the
   864  // inlined function, substituting references to input/output
   865  // parameters with ones to the tmpnames, and substituting returns with
   866  // assignments to the output.
   867  func (subst *inlsubst) node(n *Node) *Node {
   868  	if n == nil {
   869  		return nil
   870  	}
   871  
   872  	switch n.Op {
   873  	case ONAME:
   874  		if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode
   875  			if Debug['m'] > 2 {
   876  				fmt.Printf("substituting name %+v  ->  %+v\n", n, inlvar)
   877  			}
   878  			return inlvar
   879  		}
   880  
   881  		if Debug['m'] > 2 {
   882  			fmt.Printf("not substituting name %+v\n", n)
   883  		}
   884  		return n
   885  
   886  	case OLITERAL, OTYPE:
   887  		// If n is a named constant or type, we can continue
   888  		// using it in the inline copy. Otherwise, make a copy
   889  		// so we can update the line number.
   890  		if n.Sym != nil {
   891  			return n
   892  		}
   893  
   894  		// Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function.
   895  
   896  	//		dump("Return before substitution", n);
   897  	case ORETURN:
   898  		m := nod(OGOTO, subst.retlabel, nil)
   899  
   900  		m.Ninit.Set(subst.list(n.Ninit))
   901  
   902  		if len(subst.retvars) != 0 && n.List.Len() != 0 {
   903  			as := nod(OAS2, nil, nil)
   904  
   905  			// Make a shallow copy of retvars.
   906  			// Otherwise OINLCALL.Rlist will be the same list,
   907  			// and later walk and typecheck may clobber it.
   908  			for _, n := range subst.retvars {
   909  				as.List.Append(n)
   910  			}
   911  			as.Rlist.Set(subst.list(n.List))
   912  			as = typecheck(as, Etop)
   913  			m.Ninit.Append(as)
   914  		}
   915  
   916  		typecheckslice(m.Ninit.Slice(), Etop)
   917  		m = typecheck(m, Etop)
   918  
   919  		//		dump("Return after substitution", m);
   920  		return m
   921  
   922  	case OGOTO, OLABEL:
   923  		m := nod(OXXX, nil, nil)
   924  		*m = *n
   925  		m.Ninit.Set(nil)
   926  		p := fmt.Sprintf("%s·%d", n.Left.Sym.Name, inlgen)
   927  		m.Left = newname(lookup(p))
   928  
   929  		return m
   930  	}
   931  
   932  	m := nod(OXXX, nil, nil)
   933  	*m = *n
   934  	m.Ninit.Set(nil)
   935  
   936  	if n.Op == OCLOSURE {
   937  		Fatalf("cannot inline function containing closure: %+v", n)
   938  	}
   939  
   940  	m.Left = subst.node(n.Left)
   941  	m.Right = subst.node(n.Right)
   942  	m.List.Set(subst.list(n.List))
   943  	m.Rlist.Set(subst.list(n.Rlist))
   944  	m.Ninit.Set(append(m.Ninit.Slice(), subst.list(n.Ninit)...))
   945  	m.Nbody.Set(subst.list(n.Nbody))
   946  
   947  	return m
   948  }
   949  
   950  // setPos is a visitor to update position info with a new inlining index.
   951  type setPos struct {
   952  	bases       map[*src.PosBase]*src.PosBase
   953  	newInlIndex int
   954  }
   955  
   956  func (s *setPos) nodelist(ll Nodes) {
   957  	for _, n := range ll.Slice() {
   958  		s.node(n)
   959  	}
   960  }
   961  
   962  func (s *setPos) node(n *Node) {
   963  	if n == nil {
   964  		return
   965  	}
   966  	if n.Op == OLITERAL || n.Op == OTYPE {
   967  		if n.Sym != nil {
   968  			// This node is not a copy, so don't clobber position.
   969  			return
   970  		}
   971  	}
   972  
   973  	// don't clobber names, unless they're freshly synthesized
   974  	if n.Op != ONAME || !n.Pos.IsKnown() {
   975  		n.Pos = s.updatedPos(n)
   976  	}
   977  
   978  	s.node(n.Left)
   979  	s.node(n.Right)
   980  	s.nodelist(n.List)
   981  	s.nodelist(n.Rlist)
   982  	s.nodelist(n.Ninit)
   983  	s.nodelist(n.Nbody)
   984  }
   985  
   986  func (s *setPos) updatedPos(n *Node) src.XPos {
   987  	pos := Ctxt.PosTable.Pos(n.Pos)
   988  	oldbase := pos.Base() // can be nil
   989  	newbase := s.bases[oldbase]
   990  	if newbase == nil {
   991  		newbase = src.NewInliningBase(oldbase, s.newInlIndex)
   992  		pos.SetBase(newbase)
   993  		s.bases[oldbase] = newbase
   994  	}
   995  	pos.SetBase(newbase)
   996  	return Ctxt.PosTable.XPos(pos)
   997  }
   998  
   999  func (n *Node) isMethodCalledAsFunction() bool {
  1000  	return n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME
  1001  }