github.com/rsc/tmp@v0.0.0-20240517235954-6deaab19748b/bootstrap/internal/gc/inl.go (about)

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