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