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