github.com/dannin/go@v0.0.0-20161031215817-d35dfd405eaa/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.IsMethod() {
    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] %L  (%v)", fn.Sym, fn, 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] %L { %#v }\n", fn.Sym, fn, fn.Func.Inl)
    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", fn)
    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(), n, fn.Type, n.Func.Inl)
   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, n.Left)
   215  		}
   216  		if t.Nname() == nil {
   217  			Fatalf("no function definition for [%p] %+v\n", t, t)
   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  	// TODO(mdempsky/josharian): Hacks to appease toolstash; remove.
   252  	// See issue 17566 and CL 31674 for discussion.
   253  	switch n.Op {
   254  	case OSTRUCTKEY:
   255  		(*budget)--
   256  	case OSLICE, OSLICEARR, OSLICESTR:
   257  		(*budget)--
   258  	case OSLICE3, OSLICE3ARR:
   259  		*budget -= 2
   260  	}
   261  
   262  	return *budget < 0 || ishairy(n.Left, budget, reason) || ishairy(n.Right, budget, reason) ||
   263  		ishairylist(n.List, budget, reason) || ishairylist(n.Rlist, budget, reason) ||
   264  		ishairylist(n.Ninit, budget, reason) || ishairylist(n.Nbody, budget, reason)
   265  }
   266  
   267  // Inlcopy and inlcopylist recursively copy the body of a function.
   268  // Any name-like node of non-local class is marked for re-export by adding it to
   269  // the exportlist.
   270  func inlcopylist(ll []*Node) []*Node {
   271  	s := make([]*Node, 0, len(ll))
   272  	for _, n := range ll {
   273  		s = append(s, inlcopy(n))
   274  	}
   275  	return s
   276  }
   277  
   278  func inlcopy(n *Node) *Node {
   279  	if n == nil {
   280  		return nil
   281  	}
   282  
   283  	switch n.Op {
   284  	case ONAME, OTYPE, OLITERAL:
   285  		return n
   286  	}
   287  
   288  	m := *n
   289  	if m.Func != nil {
   290  		m.Func.Inl.Set(nil)
   291  	}
   292  	m.Left = inlcopy(n.Left)
   293  	m.Right = inlcopy(n.Right)
   294  	m.List.Set(inlcopylist(n.List.Slice()))
   295  	m.Rlist.Set(inlcopylist(n.Rlist.Slice()))
   296  	m.Ninit.Set(inlcopylist(n.Ninit.Slice()))
   297  	m.Nbody.Set(inlcopylist(n.Nbody.Slice()))
   298  
   299  	return &m
   300  }
   301  
   302  // Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
   303  // calls made to inlineable functions. This is the external entry point.
   304  func inlcalls(fn *Node) {
   305  	savefn := Curfn
   306  	Curfn = fn
   307  	fn = inlnode(fn)
   308  	if fn != Curfn {
   309  		Fatalf("inlnode replaced curfn")
   310  	}
   311  	Curfn = savefn
   312  }
   313  
   314  // Turn an OINLCALL into a statement.
   315  func inlconv2stmt(n *Node) {
   316  	n.Op = OBLOCK
   317  
   318  	// n->ninit stays
   319  	n.List.Set(n.Nbody.Slice())
   320  
   321  	n.Nbody.Set(nil)
   322  	n.Rlist.Set(nil)
   323  }
   324  
   325  // Turn an OINLCALL into a single valued expression.
   326  // The result of inlconv2expr MUST be assigned back to n, e.g.
   327  // 	n.Left = inlconv2expr(n.Left)
   328  func inlconv2expr(n *Node) *Node {
   329  	r := n.Rlist.First()
   330  	return addinit(r, append(n.Ninit.Slice(), n.Nbody.Slice()...))
   331  }
   332  
   333  // Turn the rlist (with the return values) of the OINLCALL in
   334  // n into an expression list lumping the ninit and body
   335  // containing the inlined statements on the first list element so
   336  // order will be preserved Used in return, oas2func and call
   337  // statements.
   338  func inlconv2list(n *Node) []*Node {
   339  	if n.Op != OINLCALL || n.Rlist.Len() == 0 {
   340  		Fatalf("inlconv2list %+v\n", n)
   341  	}
   342  
   343  	s := n.Rlist.Slice()
   344  	s[0] = addinit(s[0], append(n.Ninit.Slice(), n.Nbody.Slice()...))
   345  	return s
   346  }
   347  
   348  func inlnodelist(l Nodes) {
   349  	s := l.Slice()
   350  	for i := range s {
   351  		s[i] = inlnode(s[i])
   352  	}
   353  }
   354  
   355  // inlnode recurses over the tree to find inlineable calls, which will
   356  // be turned into OINLCALLs by mkinlcall. When the recursion comes
   357  // back up will examine left, right, list, rlist, ninit, ntest, nincr,
   358  // nbody and nelse and use one of the 4 inlconv/glue functions above
   359  // to turn the OINLCALL into an expression, a statement, or patch it
   360  // in to this nodes list or rlist as appropriate.
   361  // NOTE it makes no sense to pass the glue functions down the
   362  // recursion to the level where the OINLCALL gets created because they
   363  // have to edit /this/ n, so you'd have to push that one down as well,
   364  // but then you may as well do it here.  so this is cleaner and
   365  // shorter and less complicated.
   366  // The result of inlnode MUST be assigned back to n, e.g.
   367  // 	n.Left = inlnode(n.Left)
   368  func inlnode(n *Node) *Node {
   369  	if n == nil {
   370  		return n
   371  	}
   372  
   373  	switch n.Op {
   374  	// inhibit inlining of their argument
   375  	case ODEFER, OPROC:
   376  		switch n.Left.Op {
   377  		case OCALLFUNC, OCALLMETH:
   378  			n.Left.setNoInline(true)
   379  		}
   380  		fallthrough
   381  
   382  	// TODO do them here (or earlier),
   383  	// so escape analysis can avoid more heapmoves.
   384  	case OCLOSURE:
   385  		return n
   386  	}
   387  
   388  	lno := setlineno(n)
   389  
   390  	inlnodelist(n.Ninit)
   391  	for _, n1 := range n.Ninit.Slice() {
   392  		if n1.Op == OINLCALL {
   393  			inlconv2stmt(n1)
   394  		}
   395  	}
   396  
   397  	n.Left = inlnode(n.Left)
   398  	if n.Left != nil && n.Left.Op == OINLCALL {
   399  		n.Left = inlconv2expr(n.Left)
   400  	}
   401  
   402  	n.Right = inlnode(n.Right)
   403  	if n.Right != nil && n.Right.Op == OINLCALL {
   404  		if n.Op == OFOR {
   405  			inlconv2stmt(n.Right)
   406  		} else {
   407  			n.Right = inlconv2expr(n.Right)
   408  		}
   409  	}
   410  
   411  	inlnodelist(n.List)
   412  	switch n.Op {
   413  	case OBLOCK:
   414  		for _, n2 := range n.List.Slice() {
   415  			if n2.Op == OINLCALL {
   416  				inlconv2stmt(n2)
   417  			}
   418  		}
   419  
   420  	// if we just replaced arg in f(arg()) or return arg with an inlined call
   421  	// and arg returns multiple values, glue as list
   422  	case ORETURN,
   423  		OCALLFUNC,
   424  		OCALLMETH,
   425  		OCALLINTER,
   426  		OAPPEND,
   427  		OCOMPLEX:
   428  		if n.List.Len() == 1 && n.List.First().Op == OINLCALL && n.List.First().Rlist.Len() > 1 {
   429  			n.List.Set(inlconv2list(n.List.First()))
   430  			break
   431  		}
   432  		fallthrough
   433  
   434  	default:
   435  		s := n.List.Slice()
   436  		for i1, n1 := range s {
   437  			if n1 != nil && n1.Op == OINLCALL {
   438  				s[i1] = inlconv2expr(s[i1])
   439  			}
   440  		}
   441  	}
   442  
   443  	inlnodelist(n.Rlist)
   444  	switch n.Op {
   445  	case OAS2FUNC:
   446  		if n.Rlist.First().Op == OINLCALL {
   447  			n.Rlist.Set(inlconv2list(n.Rlist.First()))
   448  			n.Op = OAS2
   449  			n.Typecheck = 0
   450  			n = typecheck(n, Etop)
   451  			break
   452  		}
   453  		fallthrough
   454  
   455  	default:
   456  		s := n.Rlist.Slice()
   457  		for i1, n1 := range s {
   458  			if n1.Op == OINLCALL {
   459  				if n.Op == OIF {
   460  					inlconv2stmt(n1)
   461  				} else {
   462  					s[i1] = inlconv2expr(s[i1])
   463  				}
   464  			}
   465  		}
   466  	}
   467  
   468  	inlnodelist(n.Nbody)
   469  	for _, n := range n.Nbody.Slice() {
   470  		if n.Op == OINLCALL {
   471  			inlconv2stmt(n)
   472  		}
   473  	}
   474  
   475  	// with all the branches out of the way, it is now time to
   476  	// transmogrify this node itself unless inhibited by the
   477  	// switch at the top of this function.
   478  	switch n.Op {
   479  	case OCALLFUNC, OCALLMETH:
   480  		if n.noInline() {
   481  			return n
   482  		}
   483  	}
   484  
   485  	switch n.Op {
   486  	case OCALLFUNC:
   487  		if Debug['m'] > 3 {
   488  			fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left)
   489  		}
   490  		if n.Left.Func != nil && n.Left.Func.Inl.Len() != 0 && !isIntrinsicCall(n) { // normal case
   491  			n = mkinlcall(n, n.Left, n.Isddd)
   492  		} else if n.isMethodCalledAsFunction() && n.Left.Sym.Def != nil {
   493  			n = mkinlcall(n, n.Left.Sym.Def, n.Isddd)
   494  		}
   495  
   496  	case OCALLMETH:
   497  		if Debug['m'] > 3 {
   498  			fmt.Printf("%v:call to meth %L\n", n.Line(), n.Left.Right)
   499  		}
   500  
   501  		// typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
   502  		if n.Left.Type == nil {
   503  			Fatalf("no function type for [%p] %+v\n", n.Left, n.Left)
   504  		}
   505  
   506  		if n.Left.Type.Nname() == nil {
   507  			Fatalf("no function definition for [%p] %+v\n", n.Left.Type, n.Left.Type)
   508  		}
   509  
   510  		n = mkinlcall(n, n.Left.Type.Nname(), n.Isddd)
   511  	}
   512  
   513  	lineno = lno
   514  	return n
   515  }
   516  
   517  // The result of mkinlcall MUST be assigned back to n, e.g.
   518  // 	n.Left = mkinlcall(n.Left, fn, isddd)
   519  func mkinlcall(n *Node, fn *Node, isddd bool) *Node {
   520  	save_safemode := safemode
   521  
   522  	// imported functions may refer to unsafe as long as the
   523  	// package was marked safe during import (already checked).
   524  	pkg := fnpkg(fn)
   525  
   526  	if pkg != localpkg && pkg != nil {
   527  		safemode = false
   528  	}
   529  	n = mkinlcall1(n, fn, isddd)
   530  	safemode = save_safemode
   531  	return n
   532  }
   533  
   534  func tinlvar(t *Field, inlvars map[*Node]*Node) *Node {
   535  	if t.Nname != nil && !isblank(t.Nname) {
   536  		inlvar := inlvars[t.Nname]
   537  		if inlvar == nil {
   538  			Fatalf("missing inlvar for %v\n", t.Nname)
   539  		}
   540  		return inlvar
   541  	}
   542  
   543  	return typecheck(nblank, Erv|Easgn)
   544  }
   545  
   546  var inlgen int
   547  
   548  // if *np is a call, and fn is a function with an inlinable body, substitute *np with an OINLCALL.
   549  // On return ninit has the parameter assignments, the nbody is the
   550  // inlined function body and list, rlist contain the input, output
   551  // parameters.
   552  // The result of mkinlcall1 MUST be assigned back to n, e.g.
   553  // 	n.Left = mkinlcall1(n.Left, fn, isddd)
   554  func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
   555  	// For variadic fn.
   556  	if fn.Func.Inl.Len() == 0 {
   557  		return n
   558  	}
   559  
   560  	if fn == Curfn || fn.Name.Defn == Curfn {
   561  		return n
   562  	}
   563  
   564  	inlvars := make(map[*Node]*Node)
   565  
   566  	if Debug['l'] < 2 {
   567  		typecheckinl(fn)
   568  	}
   569  
   570  	// Bingo, we have a function node, and it has an inlineable body
   571  	if Debug['m'] > 1 {
   572  		fmt.Printf("%v: inlining call to %v %#v { %#v }\n", n.Line(), fn.Sym, fn.Type, fn.Func.Inl)
   573  	} else if Debug['m'] != 0 {
   574  		fmt.Printf("%v: inlining call to %v\n", n.Line(), fn)
   575  	}
   576  
   577  	if Debug['m'] > 2 {
   578  		fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n)
   579  	}
   580  
   581  	ninit := n.Ninit
   582  
   583  	//dumplist("ninit pre", ninit);
   584  
   585  	var dcl []*Node
   586  	if fn.Name.Defn != nil {
   587  		// local function
   588  		dcl = fn.Func.Inldcl.Slice()
   589  	} else {
   590  		// imported function
   591  		dcl = fn.Func.Dcl
   592  	}
   593  
   594  	var retvars []*Node
   595  	i := 0
   596  
   597  	// Make temp names to use instead of the originals
   598  	for _, ln := range dcl {
   599  		if ln.Class == PPARAMOUT { // return values handled below.
   600  			continue
   601  		}
   602  		if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap
   603  			continue
   604  		}
   605  		if ln.Op == ONAME {
   606  			inlvars[ln] = typecheck(inlvar(ln), Erv)
   607  			if ln.Class == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class == PPARAM {
   608  				ninit.Append(nod(ODCL, inlvars[ln], nil))
   609  			}
   610  		}
   611  	}
   612  
   613  	// temporaries for return values.
   614  	var m *Node
   615  	for _, t := range fn.Type.Results().Fields().Slice() {
   616  		if t != nil && t.Nname != nil && !isblank(t.Nname) {
   617  			m = inlvar(t.Nname)
   618  			m = typecheck(m, Erv)
   619  			inlvars[t.Nname] = m
   620  		} else {
   621  			// anonymous return values, synthesize names for use in assignment that replaces return
   622  			m = retvar(t, i)
   623  			i++
   624  		}
   625  
   626  		ninit.Append(nod(ODCL, m, nil))
   627  		retvars = append(retvars, m)
   628  	}
   629  
   630  	// assign receiver.
   631  	if fn.IsMethod() && n.Left.Op == ODOTMETH {
   632  		// method call with a receiver.
   633  		t := fn.Type.Recv()
   634  
   635  		if t != nil && t.Nname != nil && !isblank(t.Nname) && inlvars[t.Nname] == nil {
   636  			Fatalf("missing inlvar for %v\n", t.Nname)
   637  		}
   638  		if n.Left.Left == nil {
   639  			Fatalf("method call without receiver: %+v", n)
   640  		}
   641  		if t == nil {
   642  			Fatalf("method call unknown receiver type: %+v", n)
   643  		}
   644  		as := nod(OAS, tinlvar(t, inlvars), n.Left.Left)
   645  		if as != nil {
   646  			as = typecheck(as, Etop)
   647  			ninit.Append(as)
   648  		}
   649  	}
   650  
   651  	// check if inlined function is variadic.
   652  	variadic := false
   653  
   654  	var varargtype *Type
   655  	varargcount := 0
   656  	for _, t := range fn.Type.Params().Fields().Slice() {
   657  		if t.Isddd {
   658  			variadic = true
   659  			varargtype = t.Type
   660  		}
   661  	}
   662  
   663  	// but if argument is dotted too forget about variadicity.
   664  	if variadic && isddd {
   665  		variadic = false
   666  	}
   667  
   668  	// check if argument is actually a returned tuple from call.
   669  	multiret := 0
   670  
   671  	if n.List.Len() == 1 {
   672  		switch n.List.First().Op {
   673  		case OCALL, OCALLFUNC, OCALLINTER, OCALLMETH:
   674  			if n.List.First().Left.Type.Results().NumFields() > 1 {
   675  				multiret = n.List.First().Left.Type.Results().NumFields() - 1
   676  			}
   677  		}
   678  	}
   679  
   680  	if variadic {
   681  		varargcount = n.List.Len() + multiret
   682  		if n.Left.Op != ODOTMETH {
   683  			varargcount -= fn.Type.Recvs().NumFields()
   684  		}
   685  		varargcount -= fn.Type.Params().NumFields() - 1
   686  	}
   687  
   688  	// assign arguments to the parameters' temp names
   689  	as := nod(OAS2, nil, nil)
   690  
   691  	as.Rlist.Set(n.List.Slice())
   692  	li := 0
   693  
   694  	// TODO: if len(nlist) == 1 but multiple args, check that n->list->n is a call?
   695  	if fn.IsMethod() && n.Left.Op != ODOTMETH {
   696  		// non-method call to method
   697  		if n.List.Len() == 0 {
   698  			Fatalf("non-method call to method without first arg: %+v", n)
   699  		}
   700  
   701  		// append receiver inlvar to LHS.
   702  		t := fn.Type.Recv()
   703  
   704  		if t != nil && t.Nname != nil && !isblank(t.Nname) && inlvars[t.Nname] == nil {
   705  			Fatalf("missing inlvar for %v\n", t.Nname)
   706  		}
   707  		if t == nil {
   708  			Fatalf("method call unknown receiver type: %+v", n)
   709  		}
   710  		as.List.Append(tinlvar(t, inlvars))
   711  		li++
   712  	}
   713  
   714  	// append ordinary arguments to LHS.
   715  	chkargcount := n.List.Len() > 1
   716  
   717  	var vararg *Node    // the slice argument to a variadic call
   718  	var varargs []*Node // the list of LHS names to put in vararg.
   719  	if !chkargcount {
   720  		// 0 or 1 expression on RHS.
   721  		var i int
   722  		for _, t := range fn.Type.Params().Fields().Slice() {
   723  			if variadic && t.Isddd {
   724  				vararg = tinlvar(t, inlvars)
   725  				for i = 0; i < varargcount && li < n.List.Len(); i++ {
   726  					m = argvar(varargtype, i)
   727  					varargs = append(varargs, m)
   728  					as.List.Append(m)
   729  				}
   730  
   731  				break
   732  			}
   733  
   734  			as.List.Append(tinlvar(t, inlvars))
   735  		}
   736  	} else {
   737  		// match arguments except final variadic (unless the call is dotted itself)
   738  		t, it := iterFields(fn.Type.Params())
   739  		for t != nil {
   740  			if li >= n.List.Len() {
   741  				break
   742  			}
   743  			if variadic && t.Isddd {
   744  				break
   745  			}
   746  			as.List.Append(tinlvar(t, inlvars))
   747  			t = it.Next()
   748  			li++
   749  		}
   750  
   751  		// match varargcount arguments with variadic parameters.
   752  		if variadic && t != nil && t.Isddd {
   753  			vararg = tinlvar(t, inlvars)
   754  			var i int
   755  			for i = 0; i < varargcount && li < n.List.Len(); i++ {
   756  				m = argvar(varargtype, i)
   757  				varargs = append(varargs, m)
   758  				as.List.Append(m)
   759  				li++
   760  			}
   761  
   762  			if i == varargcount {
   763  				t = it.Next()
   764  			}
   765  		}
   766  
   767  		if li < n.List.Len() || t != nil {
   768  			Fatalf("arg count mismatch: %#v vs %.v\n", fn.Type.Params(), n.List)
   769  		}
   770  	}
   771  
   772  	if as.Rlist.Len() != 0 {
   773  		as = typecheck(as, Etop)
   774  		ninit.Append(as)
   775  	}
   776  
   777  	// turn the variadic args into a slice.
   778  	if variadic {
   779  		as = nod(OAS, vararg, nil)
   780  		if varargcount == 0 {
   781  			as.Right = nodnil()
   782  			as.Right.Type = varargtype
   783  		} else {
   784  			vararrtype := typArray(varargtype.Elem(), int64(varargcount))
   785  			as.Right = nod(OCOMPLIT, nil, typenod(vararrtype))
   786  			as.Right.List.Set(varargs)
   787  			as.Right = nod(OSLICE, as.Right, nil)
   788  		}
   789  
   790  		as = typecheck(as, Etop)
   791  		ninit.Append(as)
   792  	}
   793  
   794  	// zero the outparams
   795  	for _, n := range retvars {
   796  		as = nod(OAS, n, nil)
   797  		as = typecheck(as, Etop)
   798  		ninit.Append(as)
   799  	}
   800  
   801  	retlabel := autolabel(".i")
   802  	retlabel.Etype = 1 // flag 'safe' for escape analysis (no backjumps)
   803  
   804  	inlgen++
   805  
   806  	subst := inlsubst{
   807  		retlabel: retlabel,
   808  		retvars:  retvars,
   809  		inlvars:  inlvars,
   810  	}
   811  
   812  	body := subst.list(fn.Func.Inl)
   813  
   814  	lab := nod(OLABEL, retlabel, nil)
   815  	lab.Used = true // avoid 'not used' when function doesn't have return
   816  	body = append(body, lab)
   817  
   818  	typecheckslice(body, Etop)
   819  
   820  	//dumplist("ninit post", ninit);
   821  
   822  	call := nod(OINLCALL, nil, nil)
   823  
   824  	call.Ninit.Set(ninit.Slice())
   825  	call.Nbody.Set(body)
   826  	call.Rlist.Set(retvars)
   827  	call.Type = n.Type
   828  	call.Typecheck = 1
   829  
   830  	// Hide the args from setlno -- the parameters to the inlined
   831  	// call already have good line numbers that should be preserved.
   832  	args := as.Rlist
   833  	as.Rlist.Set(nil)
   834  
   835  	setlno(call, n.Lineno)
   836  
   837  	as.Rlist.Set(args.Slice())
   838  
   839  	//dumplist("call body", body);
   840  
   841  	n = call
   842  
   843  	// transitive inlining
   844  	// might be nice to do this before exporting the body,
   845  	// but can't emit the body with inlining expanded.
   846  	// instead we emit the things that the body needs
   847  	// and each use must redo the inlining.
   848  	// luckily these are small.
   849  	body = fn.Func.Inl.Slice()
   850  	fn.Func.Inl.Set(nil) // prevent infinite recursion (shouldn't happen anyway)
   851  	inlnodelist(call.Nbody)
   852  	for _, n := range call.Nbody.Slice() {
   853  		if n.Op == OINLCALL {
   854  			inlconv2stmt(n)
   855  		}
   856  	}
   857  	fn.Func.Inl.Set(body)
   858  
   859  	if Debug['m'] > 2 {
   860  		fmt.Printf("%v: After inlining %+v\n\n", n.Line(), n)
   861  	}
   862  
   863  	return n
   864  }
   865  
   866  // Every time we expand a function we generate a new set of tmpnames,
   867  // PAUTO's in the calling functions, and link them off of the
   868  // PPARAM's, PAUTOS and PPARAMOUTs of the called function.
   869  func inlvar(var_ *Node) *Node {
   870  	if Debug['m'] > 3 {
   871  		fmt.Printf("inlvar %+v\n", var_)
   872  	}
   873  
   874  	n := newname(var_.Sym)
   875  	n.Type = var_.Type
   876  	n.Class = PAUTO
   877  	n.Used = true
   878  	n.Name.Curfn = Curfn // the calling function, not the called one
   879  	n.Addrtaken = var_.Addrtaken
   880  
   881  	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
   882  	return n
   883  }
   884  
   885  // Synthesize a variable to store the inlined function's results in.
   886  func retvar(t *Field, i int) *Node {
   887  	n := newname(lookupN("~r", i))
   888  	n.Type = t.Type
   889  	n.Class = PAUTO
   890  	n.Used = true
   891  	n.Name.Curfn = Curfn // the calling function, not the called one
   892  	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
   893  	return n
   894  }
   895  
   896  // Synthesize a variable to store the inlined function's arguments
   897  // when they come from a multiple return call.
   898  func argvar(t *Type, i int) *Node {
   899  	n := newname(lookupN("~arg", i))
   900  	n.Type = t.Elem()
   901  	n.Class = PAUTO
   902  	n.Used = true
   903  	n.Name.Curfn = Curfn // the calling function, not the called one
   904  	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
   905  	return n
   906  }
   907  
   908  // The inlsubst type implements the actual inlining of a single
   909  // function call.
   910  type inlsubst struct {
   911  	// Target of the goto substituted in place of a return.
   912  	retlabel *Node
   913  
   914  	// Temporary result variables.
   915  	retvars []*Node
   916  
   917  	inlvars map[*Node]*Node
   918  }
   919  
   920  // list inlines a list of nodes.
   921  func (subst *inlsubst) list(ll Nodes) []*Node {
   922  	s := make([]*Node, 0, ll.Len())
   923  	for _, n := range ll.Slice() {
   924  		s = append(s, subst.node(n))
   925  	}
   926  	return s
   927  }
   928  
   929  // node recursively copies a node from the saved pristine body of the
   930  // inlined function, substituting references to input/output
   931  // parameters with ones to the tmpnames, and substituting returns with
   932  // assignments to the output.
   933  func (subst *inlsubst) node(n *Node) *Node {
   934  	if n == nil {
   935  		return nil
   936  	}
   937  
   938  	switch n.Op {
   939  	case ONAME:
   940  		if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode
   941  			if Debug['m'] > 2 {
   942  				fmt.Printf("substituting name %+v  ->  %+v\n", n, inlvar)
   943  			}
   944  			return inlvar
   945  		}
   946  
   947  		if Debug['m'] > 2 {
   948  			fmt.Printf("not substituting name %+v\n", n)
   949  		}
   950  		return n
   951  
   952  	case OLITERAL, OTYPE:
   953  		return n
   954  
   955  		// Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function.
   956  
   957  	//		dump("Return before substitution", n);
   958  	case ORETURN:
   959  		m := nod(OGOTO, subst.retlabel, nil)
   960  
   961  		m.Ninit.Set(subst.list(n.Ninit))
   962  
   963  		if len(subst.retvars) != 0 && n.List.Len() != 0 {
   964  			as := nod(OAS2, nil, nil)
   965  
   966  			// Make a shallow copy of retvars.
   967  			// Otherwise OINLCALL.Rlist will be the same list,
   968  			// and later walk and typecheck may clobber it.
   969  			for _, n := range subst.retvars {
   970  				as.List.Append(n)
   971  			}
   972  			as.Rlist.Set(subst.list(n.List))
   973  			as = typecheck(as, Etop)
   974  			m.Ninit.Append(as)
   975  		}
   976  
   977  		typecheckslice(m.Ninit.Slice(), Etop)
   978  		m = typecheck(m, Etop)
   979  
   980  		//		dump("Return after substitution", m);
   981  		return m
   982  
   983  	case OGOTO, OLABEL:
   984  		m := nod(OXXX, nil, nil)
   985  		*m = *n
   986  		m.Ninit.Set(nil)
   987  		p := fmt.Sprintf("%s·%d", n.Left.Sym.Name, inlgen)
   988  		m.Left = newname(lookup(p))
   989  
   990  		return m
   991  	default:
   992  		m := nod(OXXX, nil, nil)
   993  		*m = *n
   994  		m.Ninit.Set(nil)
   995  
   996  		if n.Op == OCLOSURE {
   997  			Fatalf("cannot inline function containing closure: %+v", n)
   998  		}
   999  
  1000  		m.Left = subst.node(n.Left)
  1001  		m.Right = subst.node(n.Right)
  1002  		m.List.Set(subst.list(n.List))
  1003  		m.Rlist.Set(subst.list(n.Rlist))
  1004  		m.Ninit.Set(append(m.Ninit.Slice(), subst.list(n.Ninit)...))
  1005  		m.Nbody.Set(subst.list(n.Nbody))
  1006  
  1007  		return m
  1008  	}
  1009  }
  1010  
  1011  // Plaster over linenumbers
  1012  func setlnolist(ll Nodes, lno int32) {
  1013  	for _, n := range ll.Slice() {
  1014  		setlno(n, lno)
  1015  	}
  1016  }
  1017  
  1018  func setlno(n *Node, lno int32) {
  1019  	if n == nil {
  1020  		return
  1021  	}
  1022  
  1023  	// don't clobber names, unless they're freshly synthesized
  1024  	if n.Op != ONAME || n.Lineno == 0 {
  1025  		n.Lineno = lno
  1026  	}
  1027  
  1028  	setlno(n.Left, lno)
  1029  	setlno(n.Right, lno)
  1030  	setlnolist(n.List, lno)
  1031  	setlnolist(n.Rlist, lno)
  1032  	setlnolist(n.Ninit, lno)
  1033  	setlnolist(n.Nbody, lno)
  1034  }
  1035  
  1036  func (n *Node) isMethodCalledAsFunction() bool {
  1037  	return n.Left.Op == ONAME && n.Left.Left != nil && n.Left.Left.Op == OTYPE && n.Left.Right != nil && n.Left.Right.Op == ONAME
  1038  }