Home | History | Annotate | Download | only in gc
      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. Additional levels (beyond -l) may be buggy and
     12 // are not supported.
     13 //      0: disabled
     14 //      1: 80-nodes leaf functions, oneliners, lazy typechecking (default)
     15 //      2: (unassigned)
     16 //      3: allow variadic functions
     17 //      4: allow non-leaf functions
     18 //
     19 // At some point this may get another default and become switch-offable with -N.
     20 //
     21 // The -d typcheckinl flag enables early typechecking of all imported bodies,
     22 // which is useful to flush out bugs.
     23 //
     24 // The debug['m'] flag enables diagnostic output.  a single -m is useful for verifying
     25 // which calls get inlined or not, more is for debugging, and may go away at any point.
     26 //
     27 // TODO:
     28 //   - inline functions with ... args
     29 
     30 package gc
     31 
     32 import (
     33 	"cmd/compile/internal/types"
     34 	"cmd/internal/obj"
     35 	"cmd/internal/src"
     36 	"fmt"
     37 	"strings"
     38 )
     39 
     40 // Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
     41 // the ->sym can be re-used in the local package, so peel it off the receiver's type.
     42 func fnpkg(fn *Node) *types.Pkg {
     43 	if fn.IsMethod() {
     44 		// method
     45 		rcvr := fn.Type.Recv().Type
     46 
     47 		if rcvr.IsPtr() {
     48 			rcvr = rcvr.Elem()
     49 		}
     50 		if rcvr.Sym == nil {
     51 			Fatalf("receiver with no sym: [%v] %L  (%v)", fn.Sym, fn, rcvr)
     52 		}
     53 		return rcvr.Sym.Pkg
     54 	}
     55 
     56 	// non-method
     57 	return fn.Sym.Pkg
     58 }
     59 
     60 // Lazy typechecking of imported bodies. For local functions, caninl will set ->typecheck
     61 // because they're a copy of an already checked body.
     62 func typecheckinl(fn *Node) {
     63 	lno := setlineno(fn)
     64 
     65 	// typecheckinl is only for imported functions;
     66 	// their bodies may refer to unsafe as long as the package
     67 	// was marked safe during import (which was checked then).
     68 	// the ->inl of a local function has been typechecked before caninl copied it.
     69 	pkg := fnpkg(fn)
     70 
     71 	if pkg == localpkg || pkg == nil {
     72 		return // typecheckinl on local function
     73 	}
     74 
     75 	if Debug['m'] > 2 || Debug_export != 0 {
     76 		fmt.Printf("typecheck import [%v] %L { %#v }\n", fn.Sym, fn, fn.Func.Inl)
     77 	}
     78 
     79 	save_safemode := safemode
     80 	safemode = false
     81 
     82 	savefn := Curfn
     83 	Curfn = fn
     84 	typecheckslice(fn.Func.Inl.Slice(), Etop)
     85 	Curfn = savefn
     86 
     87 	safemode = save_safemode
     88 
     89 	lineno = lno
     90 }
     91 
     92 // Caninl determines whether fn is inlineable.
     93 // If so, caninl saves fn->nbody in fn->inl and substitutes it with a copy.
     94 // fn and ->nbody will already have been typechecked.
     95 func caninl(fn *Node) {
     96 	if fn.Op != ODCLFUNC {
     97 		Fatalf("caninl %v", fn)
     98 	}
     99 	if fn.Func.Nname == nil {
    100 		Fatalf("caninl no nname %+v", fn)
    101 	}
    102 
    103 	var reason string // reason, if any, that the function was not inlined
    104 	if Debug['m'] > 1 {
    105 		defer func() {
    106 			if reason != "" {
    107 				fmt.Printf("%v: cannot inline %v: %s\n", fn.Line(), fn.Func.Nname, reason)
    108 			}
    109 		}()
    110 	}
    111 
    112 	// If marked "go:noinline", don't inline
    113 	if fn.Func.Pragma&Noinline != 0 {
    114 		reason = "marked go:noinline"
    115 		return
    116 	}
    117 
    118 	// If marked "go:cgo_unsafe_args", don't inline, since the
    119 	// function makes assumptions about its argument frame layout.
    120 	if fn.Func.Pragma&CgoUnsafeArgs != 0 {
    121 		reason = "marked go:cgo_unsafe_args"
    122 		return
    123 	}
    124 
    125 	// The nowritebarrierrec checker currently works at function
    126 	// granularity, so inlining yeswritebarrierrec functions can
    127 	// confuse it (#22342). As a workaround, disallow inlining
    128 	// them for now.
    129 	if fn.Func.Pragma&Yeswritebarrierrec != 0 {
    130 		reason = "marked go:yeswritebarrierrec"
    131 		return
    132 	}
    133 
    134 	// If fn has no body (is defined outside of Go), cannot inline it.
    135 	if fn.Nbody.Len() == 0 {
    136 		reason = "no function body"
    137 		return
    138 	}
    139 
    140 	if fn.Typecheck() == 0 {
    141 		Fatalf("caninl on non-typechecked function %v", fn)
    142 	}
    143 
    144 	// can't handle ... args yet
    145 	if Debug['l'] < 3 {
    146 		f := fn.Type.Params().Fields()
    147 		if len := f.Len(); len > 0 {
    148 			if t := f.Index(len - 1); t.Isddd() {
    149 				reason = "has ... args"
    150 				return
    151 			}
    152 		}
    153 	}
    154 
    155 	// Runtime package must not be instrumented.
    156 	// Instrument skips runtime package. However, some runtime code can be
    157 	// inlined into other packages and instrumented there. To avoid this,
    158 	// we disable inlining of runtime functions when instrumenting.
    159 	// The example that we observed is inlining of LockOSThread,
    160 	// which lead to false race reports on m contents.
    161 	if instrumenting && myimportpath == "runtime" {
    162 		reason = "instrumenting and is runtime function"
    163 		return
    164 	}
    165 
    166 	n := fn.Func.Nname
    167 	if n.Func.InlinabilityChecked() {
    168 		return
    169 	}
    170 	defer n.Func.SetInlinabilityChecked(true)
    171 
    172 	const maxBudget = 80
    173 	visitor := hairyVisitor{budget: maxBudget}
    174 	if visitor.visitList(fn.Nbody) {
    175 		reason = visitor.reason
    176 		return
    177 	}
    178 	if visitor.budget < 0 {
    179 		reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", maxBudget-visitor.budget, maxBudget)
    180 		return
    181 	}
    182 
    183 	savefn := Curfn
    184 	Curfn = fn
    185 
    186 	n.Func.Inl.Set(fn.Nbody.Slice())
    187 	fn.Nbody.Set(inlcopylist(n.Func.Inl.Slice()))
    188 	inldcl := inlcopylist(n.Name.Defn.Func.Dcl)
    189 	n.Func.Inldcl.Set(inldcl)
    190 	n.Func.InlCost = maxBudget - visitor.budget
    191 
    192 	// hack, TODO, check for better way to link method nodes back to the thing with the ->inl
    193 	// this is so export can find the body of a method
    194 	fn.Type.FuncType().Nname = asTypesNode(n)
    195 
    196 	if Debug['m'] > 1 {
    197 		fmt.Printf("%v: can inline %#v as: %#v { %#v }\n", fn.Line(), n, fn.Type, n.Func.Inl)
    198 	} else if Debug['m'] != 0 {
    199 		fmt.Printf("%v: can inline %v\n", fn.Line(), n)
    200 	}
    201 
    202 	Curfn = savefn
    203 }
    204 
    205 // inlFlood marks n's inline body for export and recursively ensures
    206 // all called functions are marked too.
    207 func inlFlood(n *Node) {
    208 	if n == nil {
    209 		return
    210 	}
    211 	if n.Op != ONAME || n.Class() != PFUNC {
    212 		Fatalf("inlFlood: unexpected %v, %v, %v", n, n.Op, n.Class())
    213 	}
    214 	if n.Func == nil {
    215 		// TODO(mdempsky): Should init have a Func too?
    216 		if n.Sym.Name == "init" {
    217 			return
    218 		}
    219 		Fatalf("inlFlood: missing Func on %v", n)
    220 	}
    221 	if n.Func.Inl.Len() == 0 {
    222 		return
    223 	}
    224 
    225 	if n.Func.ExportInline() {
    226 		return
    227 	}
    228 	n.Func.SetExportInline(true)
    229 
    230 	typecheckinl(n)
    231 
    232 	// Recursively flood any functions called by this one.
    233 	inspectList(n.Func.Inl, func(n *Node) bool {
    234 		switch n.Op {
    235 		case OCALLFUNC, OCALLMETH:
    236 			inlFlood(asNode(n.Left.Type.Nname()))
    237 		}
    238 		return true
    239 	})
    240 }
    241 
    242 // hairyVisitor visits a function body to determine its inlining
    243 // hairiness and whether or not it can be inlined.
    244 type hairyVisitor struct {
    245 	budget int32
    246 	reason string
    247 }
    248 
    249 // Look for anything we want to punt on.
    250 func (v *hairyVisitor) visitList(ll Nodes) bool {
    251 	for _, n := range ll.Slice() {
    252 		if v.visit(n) {
    253 			return true
    254 		}
    255 	}
    256 	return false
    257 }
    258 
    259 func (v *hairyVisitor) visit(n *Node) bool {
    260 	if n == nil {
    261 		return false
    262 	}
    263 
    264 	switch n.Op {
    265 	// Call is okay if inlinable and we have the budget for the body.
    266 	case OCALLFUNC:
    267 		if isIntrinsicCall(n) {
    268 			v.budget--
    269 			break
    270 		}
    271 		// Functions that call runtime.getcaller{pc,sp} can not be inlined
    272 		// because getcaller{pc,sp} expect a pointer to the caller's first argument.
    273 		if n.Left.Op == ONAME && n.Left.Class() == PFUNC && isRuntimePkg(n.Left.Sym.Pkg) {
    274 			fn := n.Left.Sym.Name
    275 			if fn == "getcallerpc" || fn == "getcallersp" {
    276 				v.reason = "call to " + fn
    277 				return true
    278 			}
    279 		}
    280 
    281 		if fn := n.Left.Func; fn != nil && fn.Inl.Len() != 0 {
    282 			v.budget -= fn.InlCost
    283 			break
    284 		}
    285 		if n.Left.isMethodExpression() {
    286 			if d := asNode(n.Left.Sym.Def); d != nil && d.Func.Inl.Len() != 0 {
    287 				v.budget -= d.Func.InlCost
    288 				break
    289 			}
    290 		}
    291 		// TODO(mdempsky): Budget for OCLOSURE calls if we
    292 		// ever allow that. See #15561 and #23093.
    293 		if Debug['l'] < 4 {
    294 			v.reason = "non-leaf function"
    295 			return true
    296 		}
    297 
    298 	// Call is okay if inlinable and we have the budget for the body.
    299 	case OCALLMETH:
    300 		t := n.Left.Type
    301 		if t == nil {
    302 			Fatalf("no function type for [%p] %+v\n", n.Left, n.Left)
    303 		}
    304 		if t.Nname() == nil {
    305 			Fatalf("no function definition for [%p] %+v\n", t, t)
    306 		}
    307 		if inlfn := asNode(t.FuncType().Nname).Func; inlfn.Inl.Len() != 0 {
    308 			v.budget -= inlfn.InlCost
    309 			break
    310 		}
    311 		if Debug['l'] < 4 {
    312 			v.reason = "non-leaf method"
    313 			return true
    314 		}
    315 
    316 	// Things that are too hairy, irrespective of the budget
    317 	case OCALL, OCALLINTER, OPANIC:
    318 		if Debug['l'] < 4 {
    319 			v.reason = "non-leaf op " + n.Op.String()
    320 			return true
    321 		}
    322 
    323 	case ORECOVER:
    324 		// recover matches the argument frame pointer to find
    325 		// the right panic value, so it needs an argument frame.
    326 		v.reason = "call to recover"
    327 		return true
    328 
    329 	case OCLOSURE,
    330 		OCALLPART,
    331 		ORANGE,
    332 		OFOR,
    333 		OFORUNTIL,
    334 		OSELECT,
    335 		OTYPESW,
    336 		OPROC,
    337 		ODEFER,
    338 		ODCLTYPE, // can't print yet
    339 		OBREAK,
    340 		ORETJMP:
    341 		v.reason = "unhandled op " + n.Op.String()
    342 		return true
    343 
    344 	case ODCLCONST, OEMPTY, OFALL, OLABEL:
    345 		// These nodes don't produce code; omit from inlining budget.
    346 		return false
    347 	}
    348 
    349 	v.budget--
    350 	// TODO(mdempsky/josharian): Hacks to appease toolstash; remove.
    351 	// See issue 17566 and CL 31674 for discussion.
    352 	switch n.Op {
    353 	case OSTRUCTKEY:
    354 		v.budget--
    355 	case OSLICE, OSLICEARR, OSLICESTR:
    356 		v.budget--
    357 	case OSLICE3, OSLICE3ARR:
    358 		v.budget -= 2
    359 	}
    360 
    361 	// When debugging, don't stop early, to get full cost of inlining this function
    362 	if v.budget < 0 && Debug['m'] < 2 {
    363 		return true
    364 	}
    365 
    366 	return v.visit(n.Left) || v.visit(n.Right) ||
    367 		v.visitList(n.List) || v.visitList(n.Rlist) ||
    368 		v.visitList(n.Ninit) || v.visitList(n.Nbody)
    369 }
    370 
    371 // Inlcopy and inlcopylist recursively copy the body of a function.
    372 // Any name-like node of non-local class is marked for re-export by adding it to
    373 // the exportlist.
    374 func inlcopylist(ll []*Node) []*Node {
    375 	s := make([]*Node, 0, len(ll))
    376 	for _, n := range ll {
    377 		s = append(s, inlcopy(n))
    378 	}
    379 	return s
    380 }
    381 
    382 func inlcopy(n *Node) *Node {
    383 	if n == nil {
    384 		return nil
    385 	}
    386 
    387 	switch n.Op {
    388 	case ONAME, OTYPE, OLITERAL:
    389 		return n
    390 	}
    391 
    392 	m := *n
    393 	if m.Func != nil {
    394 		m.Func.Inl.Set(nil)
    395 	}
    396 	m.Left = inlcopy(n.Left)
    397 	m.Right = inlcopy(n.Right)
    398 	m.List.Set(inlcopylist(n.List.Slice()))
    399 	m.Rlist.Set(inlcopylist(n.Rlist.Slice()))
    400 	m.Ninit.Set(inlcopylist(n.Ninit.Slice()))
    401 	m.Nbody.Set(inlcopylist(n.Nbody.Slice()))
    402 
    403 	return &m
    404 }
    405 
    406 // Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
    407 // calls made to inlineable functions. This is the external entry point.
    408 func inlcalls(fn *Node) {
    409 	savefn := Curfn
    410 	Curfn = fn
    411 	fn = inlnode(fn)
    412 	if fn != Curfn {
    413 		Fatalf("inlnode replaced curfn")
    414 	}
    415 	Curfn = savefn
    416 }
    417 
    418 // Turn an OINLCALL into a statement.
    419 func inlconv2stmt(n *Node) {
    420 	n.Op = OBLOCK
    421 
    422 	// n->ninit stays
    423 	n.List.Set(n.Nbody.Slice())
    424 
    425 	n.Nbody.Set(nil)
    426 	n.Rlist.Set(nil)
    427 }
    428 
    429 // Turn an OINLCALL into a single valued expression.
    430 // The result of inlconv2expr MUST be assigned back to n, e.g.
    431 // 	n.Left = inlconv2expr(n.Left)
    432 func inlconv2expr(n *Node) *Node {
    433 	r := n.Rlist.First()
    434 	return addinit(r, append(n.Ninit.Slice(), n.Nbody.Slice()...))
    435 }
    436 
    437 // Turn the rlist (with the return values) of the OINLCALL in
    438 // n into an expression list lumping the ninit and body
    439 // containing the inlined statements on the first list element so
    440 // order will be preserved Used in return, oas2func and call
    441 // statements.
    442 func inlconv2list(n *Node) []*Node {
    443 	if n.Op != OINLCALL || n.Rlist.Len() == 0 {
    444 		Fatalf("inlconv2list %+v\n", n)
    445 	}
    446 
    447 	s := n.Rlist.Slice()
    448 	s[0] = addinit(s[0], append(n.Ninit.Slice(), n.Nbody.Slice()...))
    449 	return s
    450 }
    451 
    452 func inlnodelist(l Nodes) {
    453 	s := l.Slice()
    454 	for i := range s {
    455 		s[i] = inlnode(s[i])
    456 	}
    457 }
    458 
    459 // inlnode recurses over the tree to find inlineable calls, which will
    460 // be turned into OINLCALLs by mkinlcall. When the recursion comes
    461 // back up will examine left, right, list, rlist, ninit, ntest, nincr,
    462 // nbody and nelse and use one of the 4 inlconv/glue functions above
    463 // to turn the OINLCALL into an expression, a statement, or patch it
    464 // in to this nodes list or rlist as appropriate.
    465 // NOTE it makes no sense to pass the glue functions down the
    466 // recursion to the level where the OINLCALL gets created because they
    467 // have to edit /this/ n, so you'd have to push that one down as well,
    468 // but then you may as well do it here.  so this is cleaner and
    469 // shorter and less complicated.
    470 // The result of inlnode MUST be assigned back to n, e.g.
    471 // 	n.Left = inlnode(n.Left)
    472 func inlnode(n *Node) *Node {
    473 	if n == nil {
    474 		return n
    475 	}
    476 
    477 	switch n.Op {
    478 	// inhibit inlining of their argument
    479 	case ODEFER, OPROC:
    480 		switch n.Left.Op {
    481 		case OCALLFUNC, OCALLMETH:
    482 			n.Left.SetNoInline(true)
    483 		}
    484 		return n
    485 
    486 	// TODO do them here (or earlier),
    487 	// so escape analysis can avoid more heapmoves.
    488 	case OCLOSURE:
    489 		return n
    490 	}
    491 
    492 	lno := setlineno(n)
    493 
    494 	inlnodelist(n.Ninit)
    495 	for _, n1 := range n.Ninit.Slice() {
    496 		if n1.Op == OINLCALL {
    497 			inlconv2stmt(n1)
    498 		}
    499 	}
    500 
    501 	n.Left = inlnode(n.Left)
    502 	if n.Left != nil && n.Left.Op == OINLCALL {
    503 		n.Left = inlconv2expr(n.Left)
    504 	}
    505 
    506 	n.Right = inlnode(n.Right)
    507 	if n.Right != nil && n.Right.Op == OINLCALL {
    508 		if n.Op == OFOR || n.Op == OFORUNTIL {
    509 			inlconv2stmt(n.Right)
    510 		} else {
    511 			n.Right = inlconv2expr(n.Right)
    512 		}
    513 	}
    514 
    515 	inlnodelist(n.List)
    516 	switch n.Op {
    517 	case OBLOCK:
    518 		for _, n2 := range n.List.Slice() {
    519 			if n2.Op == OINLCALL {
    520 				inlconv2stmt(n2)
    521 			}
    522 		}
    523 
    524 	case ORETURN, OCALLFUNC, OCALLMETH, OCALLINTER, OAPPEND, OCOMPLEX:
    525 		// if we just replaced arg in f(arg()) or return arg with an inlined call
    526 		// and arg returns multiple values, glue as list
    527 		if n.List.Len() == 1 && n.List.First().Op == OINLCALL && n.List.First().Rlist.Len() > 1 {
    528 			n.List.Set(inlconv2list(n.List.First()))
    529 			break
    530 		}
    531 		fallthrough
    532 
    533 	default:
    534 		s := n.List.Slice()
    535 		for i1, n1 := range s {
    536 			if n1 != nil && n1.Op == OINLCALL {
    537 				s[i1] = inlconv2expr(s[i1])
    538 			}
    539 		}
    540 	}
    541 
    542 	inlnodelist(n.Rlist)
    543 	if n.Op == OAS2FUNC && n.Rlist.First().Op == OINLCALL {
    544 		n.Rlist.Set(inlconv2list(n.Rlist.First()))
    545 		n.Op = OAS2
    546 		n.SetTypecheck(0)
    547 		n = typecheck(n, Etop)
    548 	} else {
    549 		s := n.Rlist.Slice()
    550 		for i1, n1 := range s {
    551 			if n1.Op == OINLCALL {
    552 				if n.Op == OIF {
    553 					inlconv2stmt(n1)
    554 				} else {
    555 					s[i1] = inlconv2expr(s[i1])
    556 				}
    557 			}
    558 		}
    559 	}
    560 
    561 	inlnodelist(n.Nbody)
    562 	for _, n := range n.Nbody.Slice() {
    563 		if n.Op == OINLCALL {
    564 			inlconv2stmt(n)
    565 		}
    566 	}
    567 
    568 	// with all the branches out of the way, it is now time to
    569 	// transmogrify this node itself unless inhibited by the
    570 	// switch at the top of this function.
    571 	switch n.Op {
    572 	case OCALLFUNC, OCALLMETH:
    573 		if n.NoInline() {
    574 			return n
    575 		}
    576 	}
    577 
    578 	switch n.Op {
    579 	case OCALLFUNC:
    580 		if Debug['m'] > 3 {
    581 			fmt.Printf("%v:call to func %+v\n", n.Line(), n.Left)
    582 		}
    583 		if n.Left.Func != nil && n.Left.Func.Inl.Len() != 0 && !isIntrinsicCall(n) { // normal case
    584 			n = mkinlcall(n, n.Left, n.Isddd())
    585 		} else if n.Left.isMethodExpression() && asNode(n.Left.Sym.Def) != nil {
    586 			n = mkinlcall(n, asNode(n.Left.Sym.Def), n.Isddd())
    587 		} else if n.Left.Op == OCLOSURE {
    588 			if f := inlinableClosure(n.Left); f != nil {
    589 				n = mkinlcall(n, f, n.Isddd())
    590 			}
    591 		} else if n.Left.Op == ONAME && n.Left.Name != nil && n.Left.Name.Defn != nil {
    592 			if d := n.Left.Name.Defn; d.Op == OAS && d.Right.Op == OCLOSURE {
    593 				if f := inlinableClosure(d.Right); f != nil {
    594 					// NB: this check is necessary to prevent indirect re-assignment of the variable
    595 					// having the address taken after the invocation or only used for reads is actually fine
    596 					// but we have no easy way to distinguish the safe cases
    597 					if d.Left.Addrtaken() {
    598 						if Debug['m'] > 1 {
    599 							fmt.Printf("%v: cannot inline escaping closure variable %v\n", n.Line(), n.Left)
    600 						}
    601 						break
    602 					}
    603 
    604 					// ensure the variable is never re-assigned
    605 					if unsafe, a := reassigned(n.Left); unsafe {
    606 						if Debug['m'] > 1 {
    607 							if a != nil {
    608 								fmt.Printf("%v: cannot inline re-assigned closure variable at %v: %v\n", n.Line(), a.Line(), a)
    609 							} else {
    610 								fmt.Printf("%v: cannot inline global closure variable %v\n", n.Line(), n.Left)
    611 							}
    612 						}
    613 						break
    614 					}
    615 					n = mkinlcall(n, f, n.Isddd())
    616 				}
    617 			}
    618 		}
    619 
    620 	case OCALLMETH:
    621 		if Debug['m'] > 3 {
    622 			fmt.Printf("%v:call to meth %L\n", n.Line(), n.Left.Right)
    623 		}
    624 
    625 		// typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
    626 		if n.Left.Type == nil {
    627 			Fatalf("no function type for [%p] %+v\n", n.Left, n.Left)
    628 		}
    629 
    630 		if n.Left.Type.Nname() == nil {
    631 			Fatalf("no function definition for [%p] %+v\n", n.Left.Type, n.Left.Type)
    632 		}
    633 
    634 		n = mkinlcall(n, asNode(n.Left.Type.FuncType().Nname), n.Isddd())
    635 	}
    636 
    637 	lineno = lno
    638 	return n
    639 }
    640 
    641 // inlinableClosure takes an OCLOSURE node and follows linkage to the matching ONAME with
    642 // the inlinable body. Returns nil if the function is not inlinable.
    643 func inlinableClosure(n *Node) *Node {
    644 	c := n.Func.Closure
    645 	caninl(c)
    646 	f := c.Func.Nname
    647 	if f == nil || f.Func.Inl.Len() == 0 {
    648 		return nil
    649 	}
    650 	return f
    651 }
    652 
    653 // reassigned takes an ONAME node, walks the function in which it is defined, and returns a boolean
    654 // indicating whether the name has any assignments other than its declaration.
    655 // The second return value is the first such assignment encountered in the walk, if any. It is mostly
    656 // useful for -m output documenting the reason for inhibited optimizations.
    657 // NB: global variables are always considered to be re-assigned.
    658 // TODO: handle initial declaration not including an assignment and followed by a single assignment?
    659 func reassigned(n *Node) (bool, *Node) {
    660 	if n.Op != ONAME {
    661 		Fatalf("reassigned %v", n)
    662 	}
    663 	// no way to reliably check for no-reassignment of globals, assume it can be
    664 	if n.Name.Curfn == nil {
    665 		return true, nil
    666 	}
    667 	f := n.Name.Curfn
    668 	// There just might be a good reason for this although this can be pretty surprising:
    669 	// local variables inside a closure have Curfn pointing to the OCLOSURE node instead
    670 	// of the corresponding ODCLFUNC.
    671 	// We need to walk the function body to check for reassignments so we follow the
    672 	// linkage to the ODCLFUNC node as that is where body is held.
    673 	if f.Op == OCLOSURE {
    674 		f = f.Func.Closure
    675 	}
    676 	v := reassignVisitor{name: n}
    677 	a := v.visitList(f.Nbody)
    678 	return a != nil, a
    679 }
    680 
    681 type reassignVisitor struct {
    682 	name *Node
    683 }
    684 
    685 func (v *reassignVisitor) visit(n *Node) *Node {
    686 	if n == nil {
    687 		return nil
    688 	}
    689 	switch n.Op {
    690 	case OAS:
    691 		if n.Left == v.name && n != v.name.Name.Defn {
    692 			return n
    693 		}
    694 		return nil
    695 	case OAS2, OAS2FUNC, OAS2MAPR, OAS2DOTTYPE:
    696 		for _, p := range n.List.Slice() {
    697 			if p == v.name && n != v.name.Name.Defn {
    698 				return n
    699 			}
    700 		}
    701 		return nil
    702 	}
    703 	if a := v.visit(n.Left); a != nil {
    704 		return a
    705 	}
    706 	if a := v.visit(n.Right); a != nil {
    707 		return a
    708 	}
    709 	if a := v.visitList(n.List); a != nil {
    710 		return a
    711 	}
    712 	if a := v.visitList(n.Rlist); a != nil {
    713 		return a
    714 	}
    715 	if a := v.visitList(n.Ninit); a != nil {
    716 		return a
    717 	}
    718 	if a := v.visitList(n.Nbody); a != nil {
    719 		return a
    720 	}
    721 	return nil
    722 }
    723 
    724 func (v *reassignVisitor) visitList(l Nodes) *Node {
    725 	for _, n := range l.Slice() {
    726 		if a := v.visit(n); a != nil {
    727 			return a
    728 		}
    729 	}
    730 	return nil
    731 }
    732 
    733 // The result of mkinlcall MUST be assigned back to n, e.g.
    734 // 	n.Left = mkinlcall(n.Left, fn, isddd)
    735 func mkinlcall(n *Node, fn *Node, isddd bool) *Node {
    736 	save_safemode := safemode
    737 
    738 	// imported functions may refer to unsafe as long as the
    739 	// package was marked safe during import (already checked).
    740 	pkg := fnpkg(fn)
    741 
    742 	if pkg != localpkg && pkg != nil {
    743 		safemode = false
    744 	}
    745 	n = mkinlcall1(n, fn, isddd)
    746 	safemode = save_safemode
    747 	return n
    748 }
    749 
    750 func tinlvar(t *types.Field, inlvars map[*Node]*Node) *Node {
    751 	if asNode(t.Nname) != nil && !isblank(asNode(t.Nname)) {
    752 		inlvar := inlvars[asNode(t.Nname)]
    753 		if inlvar == nil {
    754 			Fatalf("missing inlvar for %v\n", asNode(t.Nname))
    755 		}
    756 		return inlvar
    757 	}
    758 
    759 	return typecheck(nblank, Erv|Easgn)
    760 }
    761 
    762 var inlgen int
    763 
    764 // If n is a call, and fn is a function with an inlinable body,
    765 // return an OINLCALL.
    766 // On return ninit has the parameter assignments, the nbody is the
    767 // inlined function body and list, rlist contain the input, output
    768 // parameters.
    769 // The result of mkinlcall1 MUST be assigned back to n, e.g.
    770 // 	n.Left = mkinlcall1(n.Left, fn, isddd)
    771 func mkinlcall1(n, fn *Node, isddd bool) *Node {
    772 	if fn.Func.Inl.Len() == 0 {
    773 		// No inlinable body.
    774 		return n
    775 	}
    776 
    777 	if fn == Curfn || fn.Name.Defn == Curfn {
    778 		// Can't recursively inline a function into itself.
    779 		return n
    780 	}
    781 
    782 	if Debug_typecheckinl == 0 {
    783 		typecheckinl(fn)
    784 	}
    785 
    786 	// We have a function node, and it has an inlineable body.
    787 	if Debug['m'] > 1 {
    788 		fmt.Printf("%v: inlining call to %v %#v { %#v }\n", n.Line(), fn.Sym, fn.Type, fn.Func.Inl)
    789 	} else if Debug['m'] != 0 {
    790 		fmt.Printf("%v: inlining call to %v\n", n.Line(), fn)
    791 	}
    792 	if Debug['m'] > 2 {
    793 		fmt.Printf("%v: Before inlining: %+v\n", n.Line(), n)
    794 	}
    795 
    796 	ninit := n.Ninit
    797 
    798 	// Make temp names to use instead of the originals.
    799 	inlvars := make(map[*Node]*Node)
    800 
    801 	// record formals/locals for later post-processing
    802 	var inlfvars []*Node
    803 
    804 	// Find declarations corresponding to inlineable body.
    805 	var dcl []*Node
    806 	if fn.Name.Defn != nil {
    807 		dcl = fn.Func.Inldcl.Slice() // local function
    808 
    809 		// handle captured variables when inlining closures
    810 		if c := fn.Name.Defn.Func.Closure; c != nil {
    811 			for _, v := range c.Func.Cvars.Slice() {
    812 				if v.Op == OXXX {
    813 					continue
    814 				}
    815 
    816 				o := v.Name.Param.Outer
    817 				// make sure the outer param matches the inlining location
    818 				// NB: if we enabled inlining of functions containing OCLOSURE or refined
    819 				// the reassigned check via some sort of copy propagation this would most
    820 				// likely need to be changed to a loop to walk up to the correct Param
    821 				if o == nil || (o.Name.Curfn != Curfn && o.Name.Curfn.Func.Closure != Curfn) {
    822 					Fatalf("%v: unresolvable capture %v %v\n", n.Line(), fn, v)
    823 				}
    824 
    825 				if v.Name.Byval() {
    826 					iv := typecheck(inlvar(v), Erv)
    827 					ninit.Append(nod(ODCL, iv, nil))
    828 					ninit.Append(typecheck(nod(OAS, iv, o), Etop))
    829 					inlvars[v] = iv
    830 				} else {
    831 					addr := newname(lookup("&" + v.Sym.Name))
    832 					addr.Type = types.NewPtr(v.Type)
    833 					ia := typecheck(inlvar(addr), Erv)
    834 					ninit.Append(nod(ODCL, ia, nil))
    835 					ninit.Append(typecheck(nod(OAS, ia, nod(OADDR, o, nil)), Etop))
    836 					inlvars[addr] = ia
    837 
    838 					// When capturing by reference, all occurrence of the captured var
    839 					// must be substituted with dereference of the temporary address
    840 					inlvars[v] = typecheck(nod(OIND, ia, nil), Erv)
    841 				}
    842 			}
    843 		}
    844 	} else {
    845 		dcl = fn.Func.Dcl // imported function
    846 	}
    847 
    848 	for _, ln := range dcl {
    849 		if ln.Op != ONAME {
    850 			continue
    851 		}
    852 		if ln.Class() == PPARAMOUT { // return values handled below.
    853 			continue
    854 		}
    855 		if ln.isParamStackCopy() { // ignore the on-stack copy of a parameter that moved to the heap
    856 			continue
    857 		}
    858 		inlvars[ln] = typecheck(inlvar(ln), Erv)
    859 		if ln.Class() == PPARAM || ln.Name.Param.Stackcopy != nil && ln.Name.Param.Stackcopy.Class() == PPARAM {
    860 			ninit.Append(nod(ODCL, inlvars[ln], nil))
    861 		}
    862 		if genDwarfInline > 0 {
    863 			inlf := inlvars[ln]
    864 			if ln.Class() == PPARAM {
    865 				inlf.SetInlFormal(true)
    866 			} else {
    867 				inlf.SetInlLocal(true)
    868 			}
    869 			inlf.Pos = ln.Pos
    870 			inlfvars = append(inlfvars, inlf)
    871 		}
    872 	}
    873 
    874 	// temporaries for return values.
    875 	var retvars []*Node
    876 	for i, t := range fn.Type.Results().Fields().Slice() {
    877 		var m *Node
    878 		var mpos src.XPos
    879 		if t != nil && asNode(t.Nname) != nil && !isblank(asNode(t.Nname)) {
    880 			mpos = asNode(t.Nname).Pos
    881 			m = inlvar(asNode(t.Nname))
    882 			m = typecheck(m, Erv)
    883 			inlvars[asNode(t.Nname)] = m
    884 		} else {
    885 			// anonymous return values, synthesize names for use in assignment that replaces return
    886 			m = retvar(t, i)
    887 		}
    888 
    889 		if genDwarfInline > 0 {
    890 			// Don't update the src.Pos on a return variable if it
    891 			// was manufactured by the inliner (e.g. "~R2"); such vars
    892 			// were not part of the original callee.
    893 			if !strings.HasPrefix(m.Sym.Name, "~R") {
    894 				m.SetInlFormal(true)
    895 				m.Pos = mpos
    896 				inlfvars = append(inlfvars, m)
    897 			}
    898 		}
    899 
    900 		ninit.Append(nod(ODCL, m, nil))
    901 		retvars = append(retvars, m)
    902 	}
    903 
    904 	// Assign arguments to the parameters' temp names.
    905 	as := nod(OAS2, nil, nil)
    906 	as.Rlist.Set(n.List.Slice())
    907 
    908 	// For non-dotted calls to variadic functions, we assign the
    909 	// variadic parameter's temp name separately.
    910 	var vas *Node
    911 
    912 	if fn.IsMethod() {
    913 		rcv := fn.Type.Recv()
    914 
    915 		if n.Left.Op == ODOTMETH {
    916 			// For x.M(...), assign x directly to the
    917 			// receiver parameter.
    918 			if n.Left.Left == nil {
    919 				Fatalf("method call without receiver: %+v", n)
    920 			}
    921 			ras := nod(OAS, tinlvar(rcv, inlvars), n.Left.Left)
    922 			ras = typecheck(ras, Etop)
    923 			ninit.Append(ras)
    924 		} else {
    925 			// For T.M(...), add the receiver parameter to
    926 			// as.List, so it's assigned by the normal
    927 			// arguments.
    928 			if as.Rlist.Len() == 0 {
    929 				Fatalf("non-method call to method without first arg: %+v", n)
    930 			}
    931 			as.List.Append(tinlvar(rcv, inlvars))
    932 		}
    933 	}
    934 
    935 	for _, param := range fn.Type.Params().Fields().Slice() {
    936 		// For ordinary parameters or variadic parameters in
    937 		// dotted calls, just add the variable to the
    938 		// assignment list, and we're done.
    939 		if !param.Isddd() || isddd {
    940 			as.List.Append(tinlvar(param, inlvars))
    941 			continue
    942 		}
    943 
    944 		// Otherwise, we need to collect the remaining values
    945 		// to pass as a slice.
    946 
    947 		numvals := n.List.Len()
    948 		if numvals == 1 && n.List.First().Type.IsFuncArgStruct() {
    949 			numvals = n.List.First().Type.NumFields()
    950 		}
    951 
    952 		x := as.List.Len()
    953 		for as.List.Len() < numvals {
    954 			as.List.Append(argvar(param.Type, as.List.Len()))
    955 		}
    956 		varargs := as.List.Slice()[x:]
    957 
    958 		vas = nod(OAS, tinlvar(param, inlvars), nil)
    959 		if len(varargs) == 0 {
    960 			vas.Right = nodnil()
    961 			vas.Right.Type = param.Type
    962 		} else {
    963 			vas.Right = nod(OCOMPLIT, nil, typenod(param.Type))
    964 			vas.Right.List.Set(varargs)
    965 		}
    966 	}
    967 
    968 	if as.Rlist.Len() != 0 {
    969 		as = typecheck(as, Etop)
    970 		ninit.Append(as)
    971 	}
    972 
    973 	if vas != nil {
    974 		vas = typecheck(vas, Etop)
    975 		ninit.Append(vas)
    976 	}
    977 
    978 	// Zero the return parameters.
    979 	for _, n := range retvars {
    980 		ras := nod(OAS, n, nil)
    981 		ras = typecheck(ras, Etop)
    982 		ninit.Append(ras)
    983 	}
    984 
    985 	retlabel := autolabel(".i")
    986 	retlabel.Etype = 1 // flag 'safe' for escape analysis (no backjumps)
    987 
    988 	inlgen++
    989 
    990 	parent := -1
    991 	if b := Ctxt.PosTable.Pos(n.Pos).Base(); b != nil {
    992 		parent = b.InliningIndex()
    993 	}
    994 	newIndex := Ctxt.InlTree.Add(parent, n.Pos, fn.Sym.Linksym())
    995 
    996 	if genDwarfInline > 0 {
    997 		if !fn.Sym.Linksym().WasInlined() {
    998 			Ctxt.DwFixups.SetPrecursorFunc(fn.Sym.Linksym(), fn)
    999 			fn.Sym.Linksym().Set(obj.AttrWasInlined, true)
   1000 		}
   1001 	}
   1002 
   1003 	subst := inlsubst{
   1004 		retlabel:    retlabel,
   1005 		retvars:     retvars,
   1006 		inlvars:     inlvars,
   1007 		bases:       make(map[*src.PosBase]*src.PosBase),
   1008 		newInlIndex: newIndex,
   1009 	}
   1010 
   1011 	body := subst.list(fn.Func.Inl)
   1012 
   1013 	lab := nod(OLABEL, retlabel, nil)
   1014 	body = append(body, lab)
   1015 
   1016 	typecheckslice(body, Etop)
   1017 
   1018 	if genDwarfInline > 0 {
   1019 		for _, v := range inlfvars {
   1020 			v.Pos = subst.updatedPos(v.Pos)
   1021 		}
   1022 	}
   1023 
   1024 	//dumplist("ninit post", ninit);
   1025 
   1026 	call := nod(OINLCALL, nil, nil)
   1027 	call.Ninit.Set(ninit.Slice())
   1028 	call.Nbody.Set(body)
   1029 	call.Rlist.Set(retvars)
   1030 	call.Type = n.Type
   1031 	call.SetTypecheck(1)
   1032 
   1033 	// transitive inlining
   1034 	// might be nice to do this before exporting the body,
   1035 	// but can't emit the body with inlining expanded.
   1036 	// instead we emit the things that the body needs
   1037 	// and each use must redo the inlining.
   1038 	// luckily these are small.
   1039 	inlnodelist(call.Nbody)
   1040 	for _, n := range call.Nbody.Slice() {
   1041 		if n.Op == OINLCALL {
   1042 			inlconv2stmt(n)
   1043 		}
   1044 	}
   1045 
   1046 	if Debug['m'] > 2 {
   1047 		fmt.Printf("%v: After inlining %+v\n\n", call.Line(), call)
   1048 	}
   1049 
   1050 	return call
   1051 }
   1052 
   1053 // Every time we expand a function we generate a new set of tmpnames,
   1054 // PAUTO's in the calling functions, and link them off of the
   1055 // PPARAM's, PAUTOS and PPARAMOUTs of the called function.
   1056 func inlvar(var_ *Node) *Node {
   1057 	if Debug['m'] > 3 {
   1058 		fmt.Printf("inlvar %+v\n", var_)
   1059 	}
   1060 
   1061 	n := newname(var_.Sym)
   1062 	n.Type = var_.Type
   1063 	n.SetClass(PAUTO)
   1064 	n.Name.SetUsed(true)
   1065 	n.Name.Curfn = Curfn // the calling function, not the called one
   1066 	n.SetAddrtaken(var_.Addrtaken())
   1067 
   1068 	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
   1069 	return n
   1070 }
   1071 
   1072 // Synthesize a variable to store the inlined function's results in.
   1073 func retvar(t *types.Field, i int) *Node {
   1074 	n := newname(lookupN("~R", i))
   1075 	n.Type = t.Type
   1076 	n.SetClass(PAUTO)
   1077 	n.Name.SetUsed(true)
   1078 	n.Name.Curfn = Curfn // the calling function, not the called one
   1079 	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
   1080 	return n
   1081 }
   1082 
   1083 // Synthesize a variable to store the inlined function's arguments
   1084 // when they come from a multiple return call.
   1085 func argvar(t *types.Type, i int) *Node {
   1086 	n := newname(lookupN("~arg", i))
   1087 	n.Type = t.Elem()
   1088 	n.SetClass(PAUTO)
   1089 	n.Name.SetUsed(true)
   1090 	n.Name.Curfn = Curfn // the calling function, not the called one
   1091 	Curfn.Func.Dcl = append(Curfn.Func.Dcl, n)
   1092 	return n
   1093 }
   1094 
   1095 // The inlsubst type implements the actual inlining of a single
   1096 // function call.
   1097 type inlsubst struct {
   1098 	// Target of the goto substituted in place of a return.
   1099 	retlabel *Node
   1100 
   1101 	// Temporary result variables.
   1102 	retvars []*Node
   1103 
   1104 	inlvars map[*Node]*Node
   1105 
   1106 	// bases maps from original PosBase to PosBase with an extra
   1107 	// inlined call frame.
   1108 	bases map[*src.PosBase]*src.PosBase
   1109 
   1110 	// newInlIndex is the index of the inlined call frame to
   1111 	// insert for inlined nodes.
   1112 	newInlIndex int
   1113 }
   1114 
   1115 // list inlines a list of nodes.
   1116 func (subst *inlsubst) list(ll Nodes) []*Node {
   1117 	s := make([]*Node, 0, ll.Len())
   1118 	for _, n := range ll.Slice() {
   1119 		s = append(s, subst.node(n))
   1120 	}
   1121 	return s
   1122 }
   1123 
   1124 // node recursively copies a node from the saved pristine body of the
   1125 // inlined function, substituting references to input/output
   1126 // parameters with ones to the tmpnames, and substituting returns with
   1127 // assignments to the output.
   1128 func (subst *inlsubst) node(n *Node) *Node {
   1129 	if n == nil {
   1130 		return nil
   1131 	}
   1132 
   1133 	switch n.Op {
   1134 	case ONAME:
   1135 		if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode
   1136 			if Debug['m'] > 2 {
   1137 				fmt.Printf("substituting name %+v  ->  %+v\n", n, inlvar)
   1138 			}
   1139 			return inlvar
   1140 		}
   1141 
   1142 		if Debug['m'] > 2 {
   1143 			fmt.Printf("not substituting name %+v\n", n)
   1144 		}
   1145 		return n
   1146 
   1147 	case OLITERAL, OTYPE:
   1148 		// If n is a named constant or type, we can continue
   1149 		// using it in the inline copy. Otherwise, make a copy
   1150 		// so we can update the line number.
   1151 		if n.Sym != nil {
   1152 			return n
   1153 		}
   1154 
   1155 		// Since we don't handle bodies with closures, this return is guaranteed to belong to the current inlined function.
   1156 
   1157 	//		dump("Return before substitution", n);
   1158 	case ORETURN:
   1159 		m := nod(OGOTO, subst.retlabel, nil)
   1160 		m.Ninit.Set(subst.list(n.Ninit))
   1161 
   1162 		if len(subst.retvars) != 0 && n.List.Len() != 0 {
   1163 			as := nod(OAS2, nil, nil)
   1164 
   1165 			// Make a shallow copy of retvars.
   1166 			// Otherwise OINLCALL.Rlist will be the same list,
   1167 			// and later walk and typecheck may clobber it.
   1168 			for _, n := range subst.retvars {
   1169 				as.List.Append(n)
   1170 			}
   1171 			as.Rlist.Set(subst.list(n.List))
   1172 			as = typecheck(as, Etop)
   1173 			m.Ninit.Append(as)
   1174 		}
   1175 
   1176 		typecheckslice(m.Ninit.Slice(), Etop)
   1177 		m = typecheck(m, Etop)
   1178 
   1179 		//		dump("Return after substitution", m);
   1180 		return m
   1181 
   1182 	case OGOTO, OLABEL:
   1183 		m := nod(OXXX, nil, nil)
   1184 		*m = *n
   1185 		m.Pos = subst.updatedPos(m.Pos)
   1186 		m.Ninit.Set(nil)
   1187 		p := fmt.Sprintf("%s%d", n.Left.Sym.Name, inlgen)
   1188 		m.Left = newname(lookup(p))
   1189 
   1190 		return m
   1191 	}
   1192 
   1193 	m := nod(OXXX, nil, nil)
   1194 	*m = *n
   1195 	m.Pos = subst.updatedPos(m.Pos)
   1196 	m.Ninit.Set(nil)
   1197 
   1198 	if n.Op == OCLOSURE {
   1199 		Fatalf("cannot inline function containing closure: %+v", n)
   1200 	}
   1201 
   1202 	m.Left = subst.node(n.Left)
   1203 	m.Right = subst.node(n.Right)
   1204 	m.List.Set(subst.list(n.List))
   1205 	m.Rlist.Set(subst.list(n.Rlist))
   1206 	m.Ninit.Set(append(m.Ninit.Slice(), subst.list(n.Ninit)...))
   1207 	m.Nbody.Set(subst.list(n.Nbody))
   1208 
   1209 	return m
   1210 }
   1211 
   1212 func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos {
   1213 	pos := Ctxt.PosTable.Pos(xpos)
   1214 	oldbase := pos.Base() // can be nil
   1215 	newbase := subst.bases[oldbase]
   1216 	if newbase == nil {
   1217 		newbase = src.NewInliningBase(oldbase, subst.newInlIndex)
   1218 		subst.bases[oldbase] = newbase
   1219 	}
   1220 	pos.SetBase(newbase)
   1221 	return Ctxt.PosTable.XPos(pos)
   1222 }
   1223