github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/src/cmd/compile/internal/gc/inl.go (about)

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