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