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.
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.
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
14 // 1: 80-nodes leaf functions, oneliners, panic, lazy typechecking (default)
17 // 4: allow non-leaf functions
19 // At some point this may get another default and become switch-offable with -N.
21 // The -d typcheckinl flag enables early typechecking of all imported bodies,
22 // which is useful to flush out bugs.
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.
35 "cmd/compile/internal/base"
36 "cmd/compile/internal/ir"
37 "cmd/compile/internal/logopt"
38 "cmd/compile/internal/typecheck"
39 "cmd/compile/internal/types"
44 // Inlining budget parameters, gathered in one place
47 inlineExtraAppendCost = 0
48 // default is to inline if there's at most one call. -l=4 overrides this by using 1 instead.
49 inlineExtraCallCost = 57 // 57 was benchmarked to provided most benefit with no bad surprises; see https://github.com/golang/go/issues/19348#issuecomment-439370742
50 inlineExtraPanicCost = 1 // do not penalize inlining panics.
51 inlineExtraThrowCost = inlineMaxBudget // with current (2018-05/1.11) code, inlining runtime.throw does not help.
53 inlineBigFunctionNodes = 5000 // Functions with this many nodes are considered "big".
54 inlineBigFunctionMaxCost = 20 // Max cost of inlinee when inlining into a "big" function.
57 func InlinePackage() {
58 // Find functions that can be inlined and clone them before walk expands them.
59 ir.VisitFuncsBottomUp(typecheck.Target.Decls, func(list []*ir.Func, recursive bool) {
60 numfns := numNonClosures(list)
61 for _, n := range list {
62 if !recursive || numfns > 1 {
63 // We allow inlining if there is no
64 // recursion, or the recursion cycle is
65 // across more than one function.
68 if base.Flag.LowerM > 1 {
69 fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(n), n.Nname)
77 // Caninl determines whether fn is inlineable.
78 // If so, CanInline saves fn->nbody in fn->inl and substitutes it with a copy.
79 // fn and ->nbody will already have been typechecked.
80 func CanInline(fn *ir.Func) {
82 base.Fatalf("caninl no nname %+v", fn)
85 var reason string // reason, if any, that the function was not inlined
86 if base.Flag.LowerM > 1 || logopt.Enabled() {
89 if base.Flag.LowerM > 1 {
90 fmt.Printf("%v: cannot inline %v: %s\n", ir.Line(fn), fn.Nname, reason)
93 logopt.LogOpt(fn.Pos(), "cannotInlineFunction", "inline", ir.FuncName(fn), reason)
99 // If marked "go:noinline", don't inline
100 if fn.Pragma&ir.Noinline != 0 {
101 reason = "marked go:noinline"
105 // If marked "go:norace" and -race compilation, don't inline.
106 if base.Flag.Race && fn.Pragma&ir.Norace != 0 {
107 reason = "marked go:norace with -race compilation"
111 // If marked "go:nocheckptr" and -d checkptr compilation, don't inline.
112 if base.Debug.Checkptr != 0 && fn.Pragma&ir.NoCheckPtr != 0 {
113 reason = "marked go:nocheckptr"
117 // If marked "go:cgo_unsafe_args", don't inline, since the
118 // function makes assumptions about its argument frame layout.
119 if fn.Pragma&ir.CgoUnsafeArgs != 0 {
120 reason = "marked go:cgo_unsafe_args"
124 // If marked as "go:uintptrescapes", don't inline, since the
125 // escape information is lost during inlining.
126 if fn.Pragma&ir.UintptrEscapes != 0 {
127 reason = "marked as having an escaping uintptr argument"
131 // The nowritebarrierrec checker currently works at function
132 // granularity, so inlining yeswritebarrierrec functions can
133 // confuse it (#22342). As a workaround, disallow inlining
135 if fn.Pragma&ir.Yeswritebarrierrec != 0 {
136 reason = "marked go:yeswritebarrierrec"
140 // If fn has no body (is defined outside of Go), cannot inline it.
141 if len(fn.Body) == 0 {
142 reason = "no function body"
146 if fn.Typecheck() == 0 {
147 base.Fatalf("caninl on non-typechecked function %v", fn)
151 if n.Func.InlinabilityChecked() {
154 defer n.Func.SetInlinabilityChecked(true)
156 cc := int32(inlineExtraCallCost)
157 if base.Flag.LowerL == 4 {
158 cc = 1 // this appears to yield better performance than 0.
161 // At this point in the game the function we're looking at may
162 // have "stale" autos, vars that still appear in the Dcl list, but
163 // which no longer have any uses in the function body (due to
164 // elimination by deadcode). We'd like to exclude these dead vars
165 // when creating the "Inline.Dcl" field below; to accomplish this,
166 // the hairyVisitor below builds up a map of used/referenced
167 // locals, and we use this map to produce a pruned Inline.Dcl
168 // list. See issue 25249 for more context.
170 visitor := hairyVisitor{
171 budget: inlineMaxBudget,
173 usedLocals: make(map[*ir.Name]bool),
175 if visitor.tooHairy(fn) {
176 reason = visitor.reason
180 n.Func.Inl = &ir.Inline{
181 Cost: inlineMaxBudget - visitor.budget,
182 Dcl: pruneUnusedAutos(n.Defn.(*ir.Func).Dcl, &visitor),
183 Body: ir.DeepCopyList(src.NoXPos, fn.Body),
186 if base.Flag.LowerM > 1 {
187 fmt.Printf("%v: can inline %v with cost %d as: %v { %v }\n", ir.Line(fn), n, inlineMaxBudget-visitor.budget, fn.Type(), ir.Nodes(n.Func.Inl.Body))
188 } else if base.Flag.LowerM != 0 {
189 fmt.Printf("%v: can inline %v\n", ir.Line(fn), n)
191 if logopt.Enabled() {
192 logopt.LogOpt(fn.Pos(), "canInlineFunction", "inline", ir.FuncName(fn), fmt.Sprintf("cost: %d", inlineMaxBudget-visitor.budget))
196 // Inline_Flood marks n's inline body for export and recursively ensures
197 // all called functions are marked too.
198 func Inline_Flood(n *ir.Name, exportsym func(*ir.Name)) {
202 if n.Op() != ir.ONAME || n.Class != ir.PFUNC {
203 base.Fatalf("inlFlood: unexpected %v, %v, %v", n, n.Op(), n.Class)
207 base.Fatalf("inlFlood: missing Func on %v", n)
213 if fn.ExportInline() {
216 fn.SetExportInline(true)
218 typecheck.ImportedBody(fn)
220 // Recursively identify all referenced functions for
221 // reexport. We want to include even non-called functions,
222 // because after inlining they might be callable.
223 ir.VisitList(ir.Nodes(fn.Inl.Body), func(n ir.Node) {
225 case ir.OMETHEXPR, ir.ODOTMETH:
226 Inline_Flood(ir.MethodExprName(n), exportsym)
232 Inline_Flood(n, exportsym)
239 // Okay, because we don't yet inline indirect
240 // calls to method values.
242 // If the closure is inlinable, we'll need to
243 // flood it too. But today we don't support
244 // inlining functions that contain closures.
246 // When we do, we'll probably want:
247 // inlFlood(n.Func.Closure.Func.Nname)
248 base.Fatalf("unexpected closure in inlinable function")
253 // hairyVisitor visits a function body to determine its inlining
254 // hairiness and whether or not it can be inlined.
255 type hairyVisitor struct {
259 usedLocals map[*ir.Name]bool
260 do func(ir.Node) error
263 var errBudget = errors.New("too expensive")
265 func (v *hairyVisitor) tooHairy(fn *ir.Func) bool {
266 v.do = v.doNode // cache closure
268 err := errChildren(fn, v.do)
270 v.reason = err.Error()
274 v.reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", inlineMaxBudget-v.budget, inlineMaxBudget)
280 func (v *hairyVisitor) doNode(n ir.Node) error {
286 // Call is okay if inlinable and we have the budget for the body.
288 n := n.(*ir.CallExpr)
289 // Functions that call runtime.getcaller{pc,sp} can not be inlined
290 // because getcaller{pc,sp} expect a pointer to the caller's first argument.
292 // runtime.throw is a "cheap call" like panic in normal code.
293 if n.X.Op() == ir.ONAME {
294 name := n.X.(*ir.Name)
295 if name.Class == ir.PFUNC && types.IsRuntimePkg(name.Sym().Pkg) {
296 fn := name.Sym().Name
297 if fn == "getcallerpc" || fn == "getcallersp" {
298 return errors.New("call to " + fn)
301 v.budget -= inlineExtraThrowCost
307 if ir.IsIntrinsicCall(n) {
308 // Treat like any other node.
312 if fn := inlCallee(n.X); fn != nil && fn.Inl != nil {
313 v.budget -= fn.Inl.Cost
317 // Call cost for non-leaf inlining.
318 v.budget -= v.extraCallCost
320 // Call is okay if inlinable and we have the budget for the body.
322 n := n.(*ir.CallExpr)
325 base.Fatalf("no function type for [%p] %+v\n", n.X, n.X)
327 fn := ir.MethodExprName(n.X).Func
328 if types.IsRuntimePkg(fn.Sym().Pkg) && fn.Sym().Name == "heapBits.nextArena" {
329 // Special case: explicitly allow
330 // mid-stack inlining of
331 // runtime.heapBits.next even though
332 // it calls slow-path
333 // runtime.heapBits.nextArena.
337 v.budget -= fn.Inl.Cost
340 // Call cost for non-leaf inlining.
341 v.budget -= v.extraCallCost
343 // Things that are too hairy, irrespective of the budget
344 case ir.OCALL, ir.OCALLINTER:
345 // Call cost for non-leaf inlining.
346 v.budget -= v.extraCallCost
349 v.budget -= inlineExtraPanicCost
352 // recover matches the argument frame pointer to find
353 // the right panic value, so it needs an argument frame.
354 return errors.New("call to recover")
361 ir.ODCLTYPE, // can't print yet
363 return errors.New("unhandled op " + n.Op().String())
366 v.budget -= inlineExtraAppendCost
368 case ir.ODCLCONST, ir.OFALL:
369 // These nodes don't produce code; omit from inlining budget.
372 case ir.OFOR, ir.OFORUNTIL:
375 return errors.New("labeled control")
378 n := n.(*ir.SwitchStmt)
380 return errors.New("labeled control")
382 // case ir.ORANGE, ir.OSELECT in "unhandled" above
384 case ir.OBREAK, ir.OCONTINUE:
385 n := n.(*ir.BranchStmt)
387 // Should have short-circuited due to labeled control error above.
388 base.Fatalf("unexpected labeled break/continue: %v", n)
393 if ir.IsConst(n.Cond, constant.Bool) {
394 // This if and the condition cost nothing.
395 // TODO(rsc): It seems strange that we visit the dead branch.
396 if err := errList(n.Init(), v.do); err != nil {
399 if err := errList(n.Body, v.do); err != nil {
402 if err := errList(n.Else, v.do); err != nil {
410 if n.Class == ir.PAUTO {
411 v.usedLocals[n] = true
415 // The only OBLOCK we should see at this point is an empty one.
416 // In any event, let the visitList(n.List()) below take care of the statements,
417 // and don't charge for the OBLOCK itself. The ++ undoes the -- below.
420 case ir.OCALLPART, ir.OSLICELIT:
421 v.budget-- // Hack for toolstash -cmp.
424 v.budget++ // Hack for toolstash -cmp.
429 // When debugging, don't stop early, to get full cost of inlining this function
430 if v.budget < 0 && base.Flag.LowerM < 2 && !logopt.Enabled() {
434 return errChildren(n, v.do)
437 func isBigFunc(fn *ir.Func) bool {
438 budget := inlineBigFunctionNodes
439 return ir.Any(fn, func(n ir.Node) bool {
445 // Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any
446 // calls made to inlineable functions. This is the external entry point.
447 func InlineCalls(fn *ir.Func) {
450 maxCost := int32(inlineMaxBudget)
452 maxCost = inlineBigFunctionMaxCost
454 // Map to keep track of functions that have been inlined at a particular
455 // call site, in order to stop inlining when we reach the beginning of a
456 // recursion cycle again. We don't inline immediately recursive functions,
457 // but allow inlining if there is a recursion cycle of many functions.
458 // Most likely, the inlining will stop before we even hit the beginning of
459 // the cycle again, but the map catches the unusual case.
460 inlMap := make(map[*ir.Func]bool)
461 var edit func(ir.Node) ir.Node
462 edit = func(n ir.Node) ir.Node {
463 return inlnode(n, maxCost, inlMap, edit)
465 ir.EditChildren(fn, edit)
469 // Turn an OINLCALL into a statement.
470 func inlconv2stmt(inlcall *ir.InlinedCallExpr) ir.Node {
471 n := ir.NewBlockStmt(inlcall.Pos(), nil)
472 n.List = inlcall.Init()
473 n.List.Append(inlcall.Body.Take()...)
477 // Turn an OINLCALL into a single valued expression.
478 // The result of inlconv2expr MUST be assigned back to n, e.g.
479 // n.Left = inlconv2expr(n.Left)
480 func inlconv2expr(n *ir.InlinedCallExpr) ir.Node {
482 return ir.InitExpr(append(n.Init(), n.Body...), r)
485 // Turn the rlist (with the return values) of the OINLCALL in
486 // n into an expression list lumping the ninit and body
487 // containing the inlined statements on the first list element so
488 // order will be preserved. Used in return, oas2func and call
490 func inlconv2list(n *ir.InlinedCallExpr) []ir.Node {
491 if n.Op() != ir.OINLCALL || len(n.ReturnVars) == 0 {
492 base.Fatalf("inlconv2list %+v\n", n)
496 s[0] = ir.InitExpr(append(n.Init(), n.Body...), s[0])
500 // inlnode recurses over the tree to find inlineable calls, which will
501 // be turned into OINLCALLs by mkinlcall. When the recursion comes
502 // back up will examine left, right, list, rlist, ninit, ntest, nincr,
503 // nbody and nelse and use one of the 4 inlconv/glue functions above
504 // to turn the OINLCALL into an expression, a statement, or patch it
505 // in to this nodes list or rlist as appropriate.
506 // NOTE it makes no sense to pass the glue functions down the
507 // recursion to the level where the OINLCALL gets created because they
508 // have to edit /this/ n, so you'd have to push that one down as well,
509 // but then you may as well do it here. so this is cleaner and
510 // shorter and less complicated.
511 // The result of inlnode MUST be assigned back to n, e.g.
512 // n.Left = inlnode(n.Left)
513 func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node {
519 case ir.ODEFER, ir.OGO:
520 n := n.(*ir.GoDeferStmt)
521 switch call := n.Call; call.Op() {
522 case ir.OCALLFUNC, ir.OCALLMETH:
523 call := call.(*ir.CallExpr)
527 // TODO do them here (or earlier),
528 // so escape analysis can avoid more heapmoves.
532 // Prevent inlining some reflect.Value methods when using checkptr,
533 // even when package reflect was compiled without it (#35073).
534 n := n.(*ir.CallExpr)
535 if s := ir.MethodExprName(n.X).Sym(); base.Debug.Checkptr != 0 && types.IsReflectPkg(s.Pkg) && (s.Name == "Value.UnsafeAddr" || s.Name == "Value.Pointer") {
542 ir.EditChildren(n, edit)
544 if as := n; as.Op() == ir.OAS2FUNC {
545 as := as.(*ir.AssignListStmt)
546 if as.Rhs[0].Op() == ir.OINLCALL {
547 as.Rhs = inlconv2list(as.Rhs[0].(*ir.InlinedCallExpr))
550 n = typecheck.Stmt(as)
554 // with all the branches out of the way, it is now time to
555 // transmogrify this node itself unless inhibited by the
556 // switch at the top of this function.
558 case ir.OCALLFUNC, ir.OCALLMETH:
559 n := n.(*ir.CallExpr)
565 var call *ir.CallExpr
568 call = n.(*ir.CallExpr)
569 if base.Flag.LowerM > 3 {
570 fmt.Printf("%v:call to func %+v\n", ir.Line(n), call.X)
572 if ir.IsIntrinsicCall(call) {
575 if fn := inlCallee(call.X); fn != nil && fn.Inl != nil {
576 n = mkinlcall(call, fn, maxCost, inlMap, edit)
580 call = n.(*ir.CallExpr)
581 if base.Flag.LowerM > 3 {
582 fmt.Printf("%v:call to meth %v\n", ir.Line(n), call.X.(*ir.SelectorExpr).Sel)
585 // typecheck should have resolved ODOTMETH->type, whose nname points to the actual function.
586 if call.X.Type() == nil {
587 base.Fatalf("no function type for [%p] %+v\n", call.X, call.X)
590 n = mkinlcall(call, ir.MethodExprName(call.X).Func, maxCost, inlMap, edit)
595 if n.Op() == ir.OINLCALL {
596 ic := n.(*ir.InlinedCallExpr)
599 ir.Dump("call", call)
600 base.Fatalf("call missing use")
606 // leave for caller to convert
613 // inlCallee takes a function-typed expression and returns the underlying function ONAME
614 // that it refers to if statically known. Otherwise, it returns nil.
615 func inlCallee(fn ir.Node) *ir.Func {
616 fn = ir.StaticValue(fn)
619 fn := fn.(*ir.SelectorExpr)
620 n := ir.MethodExprName(fn)
621 // Check that receiver type matches fn.X.
622 // TODO(mdempsky): Handle implicit dereference
623 // of pointer receiver argument?
624 if n == nil || !types.Identical(n.Type().Recv().Type, fn.X.Type()) {
630 if fn.Class == ir.PFUNC {
634 fn := fn.(*ir.ClosureExpr)
642 func inlParam(t *types.Field, as ir.InitNode, inlvars map[*ir.Name]*ir.Name) ir.Node {
646 n := t.Nname.(*ir.Name)
652 base.Fatalf("missing inlvar for %v", n)
654 as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, inlvar))
655 inlvar.Name().Defn = as
661 // SSADumpInline gives the SSA back end a chance to dump the function
662 // when producing output for debugging the compiler itself.
663 var SSADumpInline = func(*ir.Func) {}
665 // If n is a call node (OCALLFUNC or OCALLMETH), and fn is an ONAME node for a
666 // function with an inlinable body, return an OINLCALL node that can replace n.
667 // The returned node's Ninit has the parameter assignments, the Nbody is the
668 // inlined function body, and (List, Rlist) contain the (input, output)
670 // The result of mkinlcall MUST be assigned back to n, e.g.
671 // n.Left = mkinlcall(n.Left, fn, isddd)
672 func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node {
674 if logopt.Enabled() {
675 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
676 fmt.Sprintf("%s cannot be inlined", ir.PkgFuncName(fn)))
680 if fn.Inl.Cost > maxCost {
681 // The inlined function body is too big. Typically we use this check to restrict
682 // inlining into very big functions. See issue 26546 and 17566.
683 if logopt.Enabled() {
684 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
685 fmt.Sprintf("cost %d of %s exceeds max large caller cost %d", fn.Inl.Cost, ir.PkgFuncName(fn), maxCost))
690 if fn == ir.CurFunc {
691 // Can't recursively inline a function into itself.
692 if logopt.Enabled() {
693 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to %s", ir.FuncName(ir.CurFunc)))
698 if base.Flag.Cfg.Instrumenting && types.IsRuntimePkg(fn.Sym().Pkg) {
699 // Runtime package must not be instrumented.
700 // Instrument skips runtime package. However, some runtime code can be
701 // inlined into other packages and instrumented there. To avoid this,
702 // we disable inlining of runtime functions when instrumenting.
703 // The example that we observed is inlining of LockOSThread,
704 // which lead to false race reports on m contents.
709 if base.Flag.LowerM > 1 {
710 fmt.Printf("%v: cannot inline %v into %v: repeated recursive cycle\n", ir.Line(n), fn, ir.FuncName(ir.CurFunc))
718 if base.Debug.TypecheckInl == 0 {
719 typecheck.ImportedBody(fn)
722 // We have a function node, and it has an inlineable body.
723 if base.Flag.LowerM > 1 {
724 fmt.Printf("%v: inlining call to %v %v { %v }\n", ir.Line(n), fn.Sym(), fn.Type(), ir.Nodes(fn.Inl.Body))
725 } else if base.Flag.LowerM != 0 {
726 fmt.Printf("%v: inlining call to %v\n", ir.Line(n), fn)
728 if base.Flag.LowerM > 2 {
729 fmt.Printf("%v: Before inlining: %+v\n", ir.Line(n), n)
736 // For normal function calls, the function callee expression
737 // may contain side effects (e.g., added by addinit during
738 // inlconv2expr or inlconv2list). Make sure to preserve these,
739 // if necessary (#42703).
740 if n.Op() == ir.OCALLFUNC {
742 for callee.Op() == ir.OCONVNOP {
743 conv := callee.(*ir.ConvExpr)
744 ninit.Append(ir.TakeInit(conv)...)
747 if callee.Op() != ir.ONAME && callee.Op() != ir.OCLOSURE && callee.Op() != ir.OMETHEXPR {
748 base.Fatalf("unexpected callee expression: %v", callee)
752 // Make temp names to use instead of the originals.
753 inlvars := make(map[*ir.Name]*ir.Name)
755 // record formals/locals for later post-processing
756 var inlfvars []*ir.Name
758 for _, ln := range fn.Inl.Dcl {
759 if ln.Op() != ir.ONAME {
762 if ln.Class == ir.PPARAMOUT { // return values handled below.
765 if ir.IsParamStackCopy(ln) { // ignore the on-stack copy of a parameter that moved to the heap
766 // TODO(mdempsky): Remove once I'm confident
767 // this never actually happens. We currently
768 // perform inlining before escape analysis, so
769 // nothing should have moved to the heap yet.
770 base.Fatalf("impossible: %v", ln)
772 inlf := typecheck.Expr(inlvar(ln)).(*ir.Name)
774 if base.Flag.GenDwarfInl > 0 {
775 if ln.Class == ir.PPARAM {
776 inlf.Name().SetInlFormal(true)
778 inlf.Name().SetInlLocal(true)
780 inlf.SetPos(ln.Pos())
781 inlfvars = append(inlfvars, inlf)
786 ir.VisitList(ir.Nodes(fn.Inl.Body), func(n ir.Node) {
787 if n != nil && n.Op() == ir.ORETURN {
792 // We can delay declaring+initializing result parameters if:
793 // (1) there's only one "return" statement in the inlined
794 // function, and (2) the result parameters aren't named.
795 delayretvars := nreturns == 1
797 // temporaries for return values.
798 var retvars []ir.Node
799 for i, t := range fn.Type().Results().Fields().Slice() {
801 if nn := t.Nname; nn != nil && !ir.IsBlank(nn.(*ir.Name)) && !strings.HasPrefix(nn.Sym().Name, "~r") {
804 m = typecheck.Expr(m).(*ir.Name)
806 delayretvars = false // found a named result parameter
808 // anonymous return values, synthesize names for use in assignment that replaces return
812 if base.Flag.GenDwarfInl > 0 {
813 // Don't update the src.Pos on a return variable if it
814 // was manufactured by the inliner (e.g. "~R2"); such vars
815 // were not part of the original callee.
816 if !strings.HasPrefix(m.Sym().Name, "~R") {
817 m.Name().SetInlFormal(true)
819 inlfvars = append(inlfvars, m)
823 retvars = append(retvars, m)
826 // Assign arguments to the parameters' temp names.
827 as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
829 if n.Op() == ir.OCALLMETH {
830 sel := n.X.(*ir.SelectorExpr)
832 base.Fatalf("method call without receiver: %+v", n)
836 as.Rhs.Append(n.Args...)
838 // For non-dotted calls to variadic functions, we assign the
839 // variadic parameter's temp name separately.
840 var vas *ir.AssignStmt
842 if recv := fn.Type().Recv(); recv != nil {
843 as.Lhs.Append(inlParam(recv, as, inlvars))
845 for _, param := range fn.Type().Params().Fields().Slice() {
846 // For ordinary parameters or variadic parameters in
847 // dotted calls, just add the variable to the
848 // assignment list, and we're done.
849 if !param.IsDDD() || n.IsDDD {
850 as.Lhs.Append(inlParam(param, as, inlvars))
854 // Otherwise, we need to collect the remaining values
855 // to pass as a slice.
858 for len(as.Lhs) < len(as.Rhs) {
859 as.Lhs.Append(argvar(param.Type, len(as.Lhs)))
861 varargs := as.Lhs[x:]
863 vas = ir.NewAssignStmt(base.Pos, nil, nil)
864 vas.X = inlParam(param, vas, inlvars)
865 if len(varargs) == 0 {
866 vas.Y = typecheck.NodNil()
867 vas.Y.SetType(param.Type)
869 lit := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(param.Type), nil)
875 if len(as.Rhs) != 0 {
876 ninit.Append(typecheck.Stmt(as))
880 ninit.Append(typecheck.Stmt(vas))
884 // Zero the return parameters.
885 for _, n := range retvars {
886 ninit.Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
887 ras := ir.NewAssignStmt(base.Pos, n, nil)
888 ninit.Append(typecheck.Stmt(ras))
892 retlabel := typecheck.AutoLabel(".i")
897 if b := base.Ctxt.PosTable.Pos(n.Pos()).Base(); b != nil {
898 parent = b.InliningIndex()
902 newIndex := base.Ctxt.InlTree.Add(parent, n.Pos(), sym)
904 // Add an inline mark just before the inlined body.
905 // This mark is inline in the code so that it's a reasonable spot
906 // to put a breakpoint. Not sure if that's really necessary or not
907 // (in which case it could go at the end of the function instead).
909 inlMark := ir.NewInlineMarkStmt(base.Pos, types.BADWIDTH)
910 inlMark.SetPos(n.Pos().WithIsStmt())
911 inlMark.Index = int64(newIndex)
912 ninit.Append(inlMark)
914 if base.Flag.GenDwarfInl > 0 {
915 if !sym.WasInlined() {
916 base.Ctxt.DwFixups.SetPrecursorFunc(sym, fn)
917 sym.Set(obj.AttrWasInlined, true)
924 delayretvars: delayretvars,
926 bases: make(map[*src.PosBase]*src.PosBase),
927 newInlIndex: newIndex,
929 subst.edit = subst.node
931 body := subst.list(ir.Nodes(fn.Inl.Body))
933 lab := ir.NewLabelStmt(base.Pos, retlabel)
934 body = append(body, lab)
936 typecheck.Stmts(body)
938 if base.Flag.GenDwarfInl > 0 {
939 for _, v := range inlfvars {
940 v.SetPos(subst.updatedPos(v.Pos()))
944 //dumplist("ninit post", ninit);
946 call := ir.NewInlinedCallExpr(base.Pos, nil, nil)
947 *call.PtrInit() = ninit
949 call.ReturnVars = retvars
950 call.SetType(n.Type())
953 // transitive inlining
954 // might be nice to do this before exporting the body,
955 // but can't emit the body with inlining expanded.
956 // instead we emit the things that the body needs
957 // and each use must redo the inlining.
958 // luckily these are small.
959 ir.EditChildren(call, edit)
961 if base.Flag.LowerM > 2 {
962 fmt.Printf("%v: After inlining %+v\n\n", ir.Line(call), call)
968 // Every time we expand a function we generate a new set of tmpnames,
969 // PAUTO's in the calling functions, and link them off of the
970 // PPARAM's, PAUTOS and PPARAMOUTs of the called function.
971 func inlvar(var_ *ir.Name) *ir.Name {
972 if base.Flag.LowerM > 3 {
973 fmt.Printf("inlvar %+v\n", var_)
976 n := typecheck.NewName(var_.Sym())
977 n.SetType(var_.Type())
980 n.Curfn = ir.CurFunc // the calling function, not the called one
981 n.SetAddrtaken(var_.Addrtaken())
983 ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
987 // Synthesize a variable to store the inlined function's results in.
988 func retvar(t *types.Field, i int) *ir.Name {
989 n := typecheck.NewName(typecheck.LookupNum("~R", i))
993 n.Curfn = ir.CurFunc // the calling function, not the called one
994 ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
998 // Synthesize a variable to store the inlined function's arguments
999 // when they come from a multiple return call.
1000 func argvar(t *types.Type, i int) ir.Node {
1001 n := typecheck.NewName(typecheck.LookupNum("~arg", i))
1005 n.Curfn = ir.CurFunc // the calling function, not the called one
1006 ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
1010 // The inlsubst type implements the actual inlining of a single
1012 type inlsubst struct {
1013 // Target of the goto substituted in place of a return.
1016 // Temporary result variables.
1019 // Whether result variables should be initialized at the
1020 // "return" statement.
1023 inlvars map[*ir.Name]*ir.Name
1025 // bases maps from original PosBase to PosBase with an extra
1026 // inlined call frame.
1027 bases map[*src.PosBase]*src.PosBase
1029 // newInlIndex is the index of the inlined call frame to
1030 // insert for inlined nodes.
1033 edit func(ir.Node) ir.Node // cached copy of subst.node method value closure
1036 // list inlines a list of nodes.
1037 func (subst *inlsubst) list(ll ir.Nodes) []ir.Node {
1038 s := make([]ir.Node, 0, len(ll))
1039 for _, n := range ll {
1040 s = append(s, subst.node(n))
1045 // node recursively copies a node from the saved pristine body of the
1046 // inlined function, substituting references to input/output
1047 // parameters with ones to the tmpnames, and substituting returns with
1048 // assignments to the output.
1049 func (subst *inlsubst) node(n ir.Node) ir.Node {
1058 // Handle captured variables when inlining closures.
1059 if n.IsClosureVar() {
1062 // make sure the outer param matches the inlining location
1063 // NB: if we enabled inlining of functions containing OCLOSURE or refined
1064 // the reassigned check via some sort of copy propagation this would most
1065 // likely need to be changed to a loop to walk up to the correct Param
1066 if o == nil || o.Curfn != ir.CurFunc {
1067 base.Fatalf("%v: unresolvable capture %v\n", ir.Line(n), n)
1070 if base.Flag.LowerM > 2 {
1071 fmt.Printf("substituting captured name %+v -> %+v\n", n, o)
1076 if inlvar := subst.inlvars[n]; inlvar != nil { // These will be set during inlnode
1077 if base.Flag.LowerM > 2 {
1078 fmt.Printf("substituting name %+v -> %+v\n", n, inlvar)
1083 if base.Flag.LowerM > 2 {
1084 fmt.Printf("not substituting name %+v\n", n)
1089 n := n.(*ir.SelectorExpr)
1092 case ir.OLITERAL, ir.ONIL, ir.OTYPE:
1093 // If n is a named constant or type, we can continue
1094 // using it in the inline copy. Otherwise, make a copy
1095 // so we can update the line number.
1101 // Since we don't handle bodies with closures,
1102 // this return is guaranteed to belong to the current inlined function.
1103 n := n.(*ir.ReturnStmt)
1104 init := subst.list(n.Init())
1105 if len(subst.retvars) != 0 && len(n.Results) != 0 {
1106 as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
1108 // Make a shallow copy of retvars.
1109 // Otherwise OINLCALL.Rlist will be the same list,
1110 // and later walk and typecheck may clobber it.
1111 for _, n := range subst.retvars {
1114 as.Rhs = subst.list(n.Results)
1116 if subst.delayretvars {
1117 for _, n := range as.Lhs {
1118 as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
1123 init = append(init, typecheck.Stmt(as))
1125 init = append(init, ir.NewBranchStmt(base.Pos, ir.OGOTO, subst.retlabel))
1126 typecheck.Stmts(init)
1127 return ir.NewBlockStmt(base.Pos, init)
1130 n := n.(*ir.BranchStmt)
1131 m := ir.Copy(n).(*ir.BranchStmt)
1132 m.SetPos(subst.updatedPos(m.Pos()))
1134 p := fmt.Sprintf("%s·%d", n.Label.Name, inlgen)
1135 m.Label = typecheck.Lookup(p)
1139 n := n.(*ir.LabelStmt)
1140 m := ir.Copy(n).(*ir.LabelStmt)
1141 m.SetPos(subst.updatedPos(m.Pos()))
1143 p := fmt.Sprintf("%s·%d", n.Label.Name, inlgen)
1144 m.Label = typecheck.Lookup(p)
1148 if n.Op() == ir.OCLOSURE {
1149 base.Fatalf("cannot inline function containing closure: %+v", n)
1153 m.SetPos(subst.updatedPos(m.Pos()))
1154 ir.EditChildren(m, subst.edit)
1158 func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos {
1159 pos := base.Ctxt.PosTable.Pos(xpos)
1160 oldbase := pos.Base() // can be nil
1161 newbase := subst.bases[oldbase]
1163 newbase = src.NewInliningBase(oldbase, subst.newInlIndex)
1164 subst.bases[oldbase] = newbase
1166 pos.SetBase(newbase)
1167 return base.Ctxt.PosTable.XPos(pos)
1170 func pruneUnusedAutos(ll []*ir.Name, vis *hairyVisitor) []*ir.Name {
1171 s := make([]*ir.Name, 0, len(ll))
1172 for _, n := range ll {
1173 if n.Class == ir.PAUTO {
1174 if _, found := vis.usedLocals[n]; !found {
1183 // numNonClosures returns the number of functions in list which are not closures.
1184 func numNonClosures(list []*ir.Func) int {
1186 for _, fn := range list {
1187 if fn.OClosure == nil {
1194 // TODO(mdempsky): Update inl.go to use ir.DoChildren directly.
1195 func errChildren(n ir.Node, do func(ir.Node) error) (err error) {
1196 ir.DoChildren(n, func(x ir.Node) bool {
1202 func errList(list []ir.Node, do func(ir.Node) error) error {
1203 for _, x := range list {
1205 if err := do(x); err != nil {