1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package inline
28
29 import (
30 "fmt"
31 "go/constant"
32 "strings"
33
34 "cmd/compile/internal/base"
35 "cmd/compile/internal/ir"
36 "cmd/compile/internal/logopt"
37 "cmd/compile/internal/typecheck"
38 "cmd/compile/internal/types"
39 "cmd/internal/obj"
40 "cmd/internal/src"
41 )
42
43
44 const (
45 inlineMaxBudget = 80
46 inlineExtraAppendCost = 0
47
48 inlineExtraCallCost = 57
49 inlineExtraPanicCost = 1
50 inlineExtraThrowCost = inlineMaxBudget
51
52 inlineBigFunctionNodes = 5000
53 inlineBigFunctionMaxCost = 20
54 )
55
56
57 func InlinePackage() {
58 ir.VisitFuncsBottomUp(typecheck.Target.Decls, func(list []*ir.Func, recursive bool) {
59 numfns := numNonClosures(list)
60 for _, n := range list {
61 if !recursive || numfns > 1 {
62
63
64
65 CanInline(n)
66 } else {
67 if base.Flag.LowerM > 1 {
68 fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(n), n.Nname)
69 }
70 }
71 InlineCalls(n)
72 }
73 })
74 }
75
76
77
78
79 func CanInline(fn *ir.Func) {
80 if fn.Nname == nil {
81 base.Fatalf("CanInline no nname %+v", fn)
82 }
83
84 var reason string
85 if base.Flag.LowerM > 1 || logopt.Enabled() {
86 defer func() {
87 if reason != "" {
88 if base.Flag.LowerM > 1 {
89 fmt.Printf("%v: cannot inline %v: %s\n", ir.Line(fn), fn.Nname, reason)
90 }
91 if logopt.Enabled() {
92 logopt.LogOpt(fn.Pos(), "cannotInlineFunction", "inline", ir.FuncName(fn), reason)
93 }
94 }
95 }()
96 }
97
98
99 if fn.Pragma&ir.Noinline != 0 {
100 reason = "marked go:noinline"
101 return
102 }
103
104
105 if base.Flag.Race && fn.Pragma&ir.Norace != 0 {
106 reason = "marked go:norace with -race compilation"
107 return
108 }
109
110
111 if base.Debug.Checkptr != 0 && fn.Pragma&ir.NoCheckPtr != 0 {
112 reason = "marked go:nocheckptr"
113 return
114 }
115
116
117
118 if fn.Pragma&ir.CgoUnsafeArgs != 0 {
119 reason = "marked go:cgo_unsafe_args"
120 return
121 }
122
123
124
125 if fn.Pragma&ir.UintptrEscapes != 0 {
126 reason = "marked as having an escaping uintptr argument"
127 return
128 }
129
130
131
132
133
134 if fn.Pragma&ir.Yeswritebarrierrec != 0 {
135 reason = "marked go:yeswritebarrierrec"
136 return
137 }
138
139
140 if len(fn.Body) == 0 {
141 reason = "no function body"
142 return
143 }
144
145 if fn.Typecheck() == 0 {
146 base.Fatalf("CanInline on non-typechecked function %v", fn)
147 }
148
149 n := fn.Nname
150 if n.Func.InlinabilityChecked() {
151 return
152 }
153 defer n.Func.SetInlinabilityChecked(true)
154
155 cc := int32(inlineExtraCallCost)
156 if base.Flag.LowerL == 4 {
157 cc = 1
158 }
159
160
161
162
163
164
165
166
167
168
169 visitor := hairyVisitor{
170 budget: inlineMaxBudget,
171 extraCallCost: cc,
172 }
173 if visitor.tooHairy(fn) {
174 reason = visitor.reason
175 return
176 }
177
178 n.Func.Inl = &ir.Inline{
179 Cost: inlineMaxBudget - visitor.budget,
180 Dcl: pruneUnusedAutos(n.Defn.(*ir.Func).Dcl, &visitor),
181 Body: inlcopylist(fn.Body),
182 }
183
184 if base.Flag.LowerM > 1 {
185 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))
186 } else if base.Flag.LowerM != 0 {
187 fmt.Printf("%v: can inline %v\n", ir.Line(fn), n)
188 }
189 if logopt.Enabled() {
190 logopt.LogOpt(fn.Pos(), "canInlineFunction", "inline", ir.FuncName(fn), fmt.Sprintf("cost: %d", inlineMaxBudget-visitor.budget))
191 }
192 }
193
194
195
196 func Inline_Flood(n *ir.Name, exportsym func(*ir.Name)) {
197 if n == nil {
198 return
199 }
200 if n.Op() != ir.ONAME || n.Class != ir.PFUNC {
201 base.Fatalf("Inline_Flood: unexpected %v, %v, %v", n, n.Op(), n.Class)
202 }
203 fn := n.Func
204 if fn == nil {
205 base.Fatalf("Inline_Flood: missing Func on %v", n)
206 }
207 if fn.Inl == nil {
208 return
209 }
210
211 if fn.ExportInline() {
212 return
213 }
214 fn.SetExportInline(true)
215
216 typecheck.ImportedBody(fn)
217
218 var doFlood func(n ir.Node)
219 doFlood = func(n ir.Node) {
220 switch n.Op() {
221 case ir.OMETHEXPR, ir.ODOTMETH:
222 Inline_Flood(ir.MethodExprName(n), exportsym)
223
224 case ir.ONAME:
225 n := n.(*ir.Name)
226 switch n.Class {
227 case ir.PFUNC:
228 Inline_Flood(n, exportsym)
229 exportsym(n)
230 case ir.PEXTERN:
231 exportsym(n)
232 }
233
234 case ir.OCALLPART:
235
236
237 case ir.OCLOSURE:
238
239
240 ir.VisitList(n.(*ir.ClosureExpr).Func.Body, doFlood)
241 }
242 }
243
244
245
246
247 ir.VisitList(ir.Nodes(fn.Inl.Body), doFlood)
248 }
249
250
251
252 type hairyVisitor struct {
253 budget int32
254 reason string
255 extraCallCost int32
256 usedLocals ir.NameSet
257 do func(ir.Node) bool
258 }
259
260 func (v *hairyVisitor) tooHairy(fn *ir.Func) bool {
261 v.do = v.doNode
262 if ir.DoChildren(fn, v.do) {
263 return true
264 }
265 if v.budget < 0 {
266 v.reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", inlineMaxBudget-v.budget, inlineMaxBudget)
267 return true
268 }
269 return false
270 }
271
272 func (v *hairyVisitor) doNode(n ir.Node) bool {
273 if n == nil {
274 return false
275 }
276 switch n.Op() {
277
278 case ir.OCALLFUNC:
279 n := n.(*ir.CallExpr)
280
281
282
283
284 if n.X.Op() == ir.ONAME {
285 name := n.X.(*ir.Name)
286 if name.Class == ir.PFUNC && types.IsRuntimePkg(name.Sym().Pkg) {
287 fn := name.Sym().Name
288 if fn == "getcallerpc" || fn == "getcallersp" {
289 v.reason = "call to " + fn
290 return true
291 }
292 if fn == "throw" {
293 v.budget -= inlineExtraThrowCost
294 break
295 }
296 }
297 }
298
299 if ir.IsIntrinsicCall(n) {
300
301 break
302 }
303
304 if fn := inlCallee(n.X); fn != nil && fn.Inl != nil {
305 v.budget -= fn.Inl.Cost
306 break
307 }
308
309
310 v.budget -= v.extraCallCost
311
312
313 case ir.OCALLMETH:
314 n := n.(*ir.CallExpr)
315 t := n.X.Type()
316 if t == nil {
317 base.Fatalf("no function type for [%p] %+v\n", n.X, n.X)
318 }
319 fn := ir.MethodExprName(n.X).Func
320 if types.IsRuntimePkg(fn.Sym().Pkg) && fn.Sym().Name == "heapBits.nextArena" {
321
322
323
324
325
326 break
327 }
328 if fn.Inl != nil {
329 v.budget -= fn.Inl.Cost
330 break
331 }
332
333 v.budget -= v.extraCallCost
334
335
336 case ir.OCALL, ir.OCALLINTER:
337
338 v.budget -= v.extraCallCost
339
340 case ir.OPANIC:
341 n := n.(*ir.UnaryExpr)
342 if n.X.Op() == ir.OCONVIFACE && n.X.(*ir.ConvExpr).Implicit() {
343
344
345
346 v.budget++
347 }
348 v.budget -= inlineExtraPanicCost
349
350 case ir.ORECOVER:
351
352
353 v.reason = "call to recover"
354 return true
355
356 case ir.OCLOSURE:
357 if base.Debug.InlFuncsWithClosures == 0 {
358 v.reason = "not inlining functions with closures"
359 return true
360 }
361
362
363
364
365 v.budget -= 15
366
367
368
369 if doList(n.(*ir.ClosureExpr).Func.Body, v.do) {
370 return true
371 }
372
373 case ir.ORANGE,
374 ir.OSELECT,
375 ir.OGO,
376 ir.ODEFER,
377 ir.ODCLTYPE,
378 ir.OTAILCALL:
379 v.reason = "unhandled op " + n.Op().String()
380 return true
381
382 case ir.OAPPEND:
383 v.budget -= inlineExtraAppendCost
384
385 case ir.ODEREF:
386
387 n := n.(*ir.StarExpr)
388
389 ptr := n.X
390 for ptr.Op() == ir.OCONVNOP {
391 ptr = ptr.(*ir.ConvExpr).X
392 }
393 if ptr.Op() == ir.OADDR {
394 v.budget += 1
395 }
396
397 case ir.OCONVNOP:
398
399 v.budget++
400
401 case ir.ODCLCONST, ir.OFALL:
402
403 return false
404
405 case ir.OFOR, ir.OFORUNTIL:
406 n := n.(*ir.ForStmt)
407 if n.Label != nil {
408 v.reason = "labeled control"
409 return true
410 }
411 case ir.OSWITCH:
412 n := n.(*ir.SwitchStmt)
413 if n.Label != nil {
414 v.reason = "labeled control"
415 return true
416 }
417
418
419 case ir.OBREAK, ir.OCONTINUE:
420 n := n.(*ir.BranchStmt)
421 if n.Label != nil {
422
423 base.Fatalf("unexpected labeled break/continue: %v", n)
424 }
425
426 case ir.OIF:
427 n := n.(*ir.IfStmt)
428 if ir.IsConst(n.Cond, constant.Bool) {
429
430
431 return doList(n.Init(), v.do) ||
432 doList(n.Body, v.do) ||
433 doList(n.Else, v.do)
434 }
435
436 case ir.ONAME:
437 n := n.(*ir.Name)
438 if n.Class == ir.PAUTO {
439 v.usedLocals.Add(n)
440 }
441
442 case ir.OBLOCK:
443
444
445
446 v.budget++
447
448 case ir.OCALLPART, ir.OSLICELIT:
449 v.budget--
450
451 case ir.OMETHEXPR:
452 v.budget++
453 }
454
455 v.budget--
456
457
458 if v.budget < 0 && base.Flag.LowerM < 2 && !logopt.Enabled() {
459 v.reason = "too expensive"
460 return true
461 }
462
463 return ir.DoChildren(n, v.do)
464 }
465
466 func isBigFunc(fn *ir.Func) bool {
467 budget := inlineBigFunctionNodes
468 return ir.Any(fn, func(n ir.Node) bool {
469 budget--
470 return budget <= 0
471 })
472 }
473
474
475
476
477 func inlcopylist(ll []ir.Node) []ir.Node {
478 s := make([]ir.Node, len(ll))
479 for i, n := range ll {
480 s[i] = inlcopy(n)
481 }
482 return s
483 }
484
485
486 func inlcopy(n ir.Node) ir.Node {
487 var edit func(ir.Node) ir.Node
488 edit = func(x ir.Node) ir.Node {
489 switch x.Op() {
490 case ir.ONAME, ir.OTYPE, ir.OLITERAL, ir.ONIL:
491 return x
492 }
493 m := ir.Copy(x)
494 ir.EditChildren(m, edit)
495 if x.Op() == ir.OCLOSURE {
496 x := x.(*ir.ClosureExpr)
497
498
499
500 oldfn := x.Func
501 newfn := ir.NewFunc(oldfn.Pos())
502 if oldfn.ClosureCalled() {
503 newfn.SetClosureCalled(true)
504 }
505 m.(*ir.ClosureExpr).Func = newfn
506 newfn.Nname = ir.NewNameAt(oldfn.Nname.Pos(), oldfn.Nname.Sym())
507
508 newfn.Nname.SetType(oldfn.Nname.Type())
509
510 if oldfn.Nname.Ntype != nil {
511 newfn.Nname.Ntype = inlcopy(oldfn.Nname.Ntype).(ir.Ntype)
512 }
513 newfn.Body = inlcopylist(oldfn.Body)
514
515 newfn.Dcl = append([]*ir.Name(nil), oldfn.Dcl...)
516 newfn.ClosureVars = append([]*ir.Name(nil), oldfn.ClosureVars...)
517 }
518 return m
519 }
520 return edit(n)
521 }
522
523
524
525 func InlineCalls(fn *ir.Func) {
526 savefn := ir.CurFunc
527 ir.CurFunc = fn
528 maxCost := int32(inlineMaxBudget)
529 if isBigFunc(fn) {
530 maxCost = inlineBigFunctionMaxCost
531 }
532
533
534
535
536
537
538 inlMap := make(map[*ir.Func]bool)
539 var edit func(ir.Node) ir.Node
540 edit = func(n ir.Node) ir.Node {
541 return inlnode(n, maxCost, inlMap, edit)
542 }
543 ir.EditChildren(fn, edit)
544 ir.CurFunc = savefn
545 }
546
547
548 func inlconv2stmt(inlcall *ir.InlinedCallExpr) ir.Node {
549 n := ir.NewBlockStmt(inlcall.Pos(), nil)
550 n.List = inlcall.Init()
551 n.List.Append(inlcall.Body.Take()...)
552 return n
553 }
554
555
556
557
558 func inlconv2expr(n *ir.InlinedCallExpr) ir.Node {
559 r := n.ReturnVars[0]
560 return ir.InitExpr(append(n.Init(), n.Body...), r)
561 }
562
563
564
565
566
567
568 func inlconv2list(n *ir.InlinedCallExpr) []ir.Node {
569 if n.Op() != ir.OINLCALL || len(n.ReturnVars) == 0 {
570 base.Fatalf("inlconv2list %+v\n", n)
571 }
572
573 s := n.ReturnVars
574 s[0] = ir.InitExpr(append(n.Init(), n.Body...), s[0])
575 return s
576 }
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591 func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node {
592 if n == nil {
593 return n
594 }
595
596 switch n.Op() {
597 case ir.ODEFER, ir.OGO:
598 n := n.(*ir.GoDeferStmt)
599 switch call := n.Call; call.Op() {
600 case ir.OCALLFUNC, ir.OCALLMETH:
601 call := call.(*ir.CallExpr)
602 call.NoInline = true
603 }
604
605
606
607 case ir.OCLOSURE:
608 return n
609 case ir.OCALLMETH:
610
611
612 n := n.(*ir.CallExpr)
613 if s := ir.MethodExprName(n.X).Sym(); base.Debug.Checkptr != 0 && types.IsReflectPkg(s.Pkg) && (s.Name == "Value.UnsafeAddr" || s.Name == "Value.Pointer") {
614 return n
615 }
616 }
617
618 lno := ir.SetPos(n)
619
620 ir.EditChildren(n, edit)
621
622 if as := n; as.Op() == ir.OAS2FUNC {
623 as := as.(*ir.AssignListStmt)
624 if as.Rhs[0].Op() == ir.OINLCALL {
625 as.Rhs = inlconv2list(as.Rhs[0].(*ir.InlinedCallExpr))
626 as.SetOp(ir.OAS2)
627 as.SetTypecheck(0)
628 n = typecheck.Stmt(as)
629 }
630 }
631
632
633
634
635 switch n.Op() {
636 case ir.OCALLFUNC, ir.OCALLMETH:
637 n := n.(*ir.CallExpr)
638 if n.NoInline {
639 return n
640 }
641 }
642
643 var call *ir.CallExpr
644 switch n.Op() {
645 case ir.OCALLFUNC:
646 call = n.(*ir.CallExpr)
647 if base.Flag.LowerM > 3 {
648 fmt.Printf("%v:call to func %+v\n", ir.Line(n), call.X)
649 }
650 if ir.IsIntrinsicCall(call) {
651 break
652 }
653 if fn := inlCallee(call.X); fn != nil && fn.Inl != nil {
654 n = mkinlcall(call, fn, maxCost, inlMap, edit)
655 }
656
657 case ir.OCALLMETH:
658 call = n.(*ir.CallExpr)
659 if base.Flag.LowerM > 3 {
660 fmt.Printf("%v:call to meth %v\n", ir.Line(n), call.X.(*ir.SelectorExpr).Sel)
661 }
662
663
664 if call.X.Type() == nil {
665 base.Fatalf("no function type for [%p] %+v\n", call.X, call.X)
666 }
667
668 n = mkinlcall(call, ir.MethodExprName(call.X).Func, maxCost, inlMap, edit)
669 }
670
671 base.Pos = lno
672
673 if n.Op() == ir.OINLCALL {
674 ic := n.(*ir.InlinedCallExpr)
675 switch call.Use {
676 default:
677 ir.Dump("call", call)
678 base.Fatalf("call missing use")
679 case ir.CallUseExpr:
680 n = inlconv2expr(ic)
681 case ir.CallUseStmt:
682 n = inlconv2stmt(ic)
683 case ir.CallUseList:
684
685 }
686 }
687
688 return n
689 }
690
691
692
693 func inlCallee(fn ir.Node) *ir.Func {
694 fn = ir.StaticValue(fn)
695 switch fn.Op() {
696 case ir.OMETHEXPR:
697 fn := fn.(*ir.SelectorExpr)
698 n := ir.MethodExprName(fn)
699
700
701
702 if n == nil || !types.Identical(n.Type().Recv().Type, fn.X.Type()) {
703 return nil
704 }
705 return n.Func
706 case ir.ONAME:
707 fn := fn.(*ir.Name)
708 if fn.Class == ir.PFUNC {
709 return fn.Func
710 }
711 case ir.OCLOSURE:
712 fn := fn.(*ir.ClosureExpr)
713 c := fn.Func
714 CanInline(c)
715 return c
716 }
717 return nil
718 }
719
720 func inlParam(t *types.Field, as ir.InitNode, inlvars map[*ir.Name]*ir.Name) ir.Node {
721 if t.Nname == nil {
722 return ir.BlankNode
723 }
724 n := t.Nname.(*ir.Name)
725 if ir.IsBlank(n) {
726 return ir.BlankNode
727 }
728 inlvar := inlvars[n]
729 if inlvar == nil {
730 base.Fatalf("missing inlvar for %v", n)
731 }
732 as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, inlvar))
733 inlvar.Name().Defn = as
734 return inlvar
735 }
736
737 var inlgen int
738
739
740
741 var SSADumpInline = func(*ir.Func) {}
742
743
744
745
746
747
748
749
750 func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node {
751 if fn.Inl == nil {
752 if logopt.Enabled() {
753 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
754 fmt.Sprintf("%s cannot be inlined", ir.PkgFuncName(fn)))
755 }
756 return n
757 }
758 if fn.Inl.Cost > maxCost {
759
760
761 if logopt.Enabled() {
762 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
763 fmt.Sprintf("cost %d of %s exceeds max large caller cost %d", fn.Inl.Cost, ir.PkgFuncName(fn), maxCost))
764 }
765 return n
766 }
767
768 if fn == ir.CurFunc {
769
770 if logopt.Enabled() {
771 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to %s", ir.FuncName(ir.CurFunc)))
772 }
773 return n
774 }
775
776 if base.Flag.Cfg.Instrumenting && types.IsRuntimePkg(fn.Sym().Pkg) {
777
778
779
780
781
782
783 return n
784 }
785
786 if inlMap[fn] {
787 if base.Flag.LowerM > 1 {
788 fmt.Printf("%v: cannot inline %v into %v: repeated recursive cycle\n", ir.Line(n), fn, ir.FuncName(ir.CurFunc))
789 }
790 return n
791 }
792 inlMap[fn] = true
793 defer func() {
794 inlMap[fn] = false
795 }()
796 if base.Debug.TypecheckInl == 0 {
797 typecheck.ImportedBody(fn)
798 }
799
800
801 if base.Flag.LowerM > 1 {
802 fmt.Printf("%v: inlining call to %v %v { %v }\n", ir.Line(n), fn.Sym(), fn.Type(), ir.Nodes(fn.Inl.Body))
803 } else if base.Flag.LowerM != 0 {
804 fmt.Printf("%v: inlining call to %v\n", ir.Line(n), fn)
805 }
806 if base.Flag.LowerM > 2 {
807 fmt.Printf("%v: Before inlining: %+v\n", ir.Line(n), n)
808 }
809
810 SSADumpInline(fn)
811
812 ninit := n.Init()
813
814
815
816
817
818 if n.Op() == ir.OCALLFUNC {
819 callee := n.X
820 for callee.Op() == ir.OCONVNOP {
821 conv := callee.(*ir.ConvExpr)
822 ninit.Append(ir.TakeInit(conv)...)
823 callee = conv.X
824 }
825 if callee.Op() != ir.ONAME && callee.Op() != ir.OCLOSURE && callee.Op() != ir.OMETHEXPR {
826 base.Fatalf("unexpected callee expression: %v", callee)
827 }
828 }
829
830
831 inlvars := make(map[*ir.Name]*ir.Name)
832
833
834 var inlfvars []*ir.Name
835
836 for _, ln := range fn.Inl.Dcl {
837 if ln.Op() != ir.ONAME {
838 continue
839 }
840 if ln.Class == ir.PPARAMOUT {
841 continue
842 }
843 inlf := typecheck.Expr(inlvar(ln)).(*ir.Name)
844 inlvars[ln] = inlf
845 if base.Flag.GenDwarfInl > 0 {
846 if ln.Class == ir.PPARAM {
847 inlf.Name().SetInlFormal(true)
848 } else {
849 inlf.Name().SetInlLocal(true)
850 }
851 inlf.SetPos(ln.Pos())
852 inlfvars = append(inlfvars, inlf)
853 }
854 }
855
856
857
858
859
860 delayretvars := true
861
862 nreturns := 0
863 ir.VisitList(ir.Nodes(fn.Inl.Body), func(n ir.Node) {
864 if n, ok := n.(*ir.ReturnStmt); ok {
865 nreturns++
866 if len(n.Results) == 0 {
867 delayretvars = false
868 }
869 }
870 })
871
872 if nreturns != 1 {
873 delayretvars = false
874 }
875
876
877 var retvars []ir.Node
878 for i, t := range fn.Type().Results().Fields().Slice() {
879 var m *ir.Name
880 if nn := t.Nname; nn != nil && !ir.IsBlank(nn.(*ir.Name)) && !strings.HasPrefix(nn.Sym().Name, "~r") {
881 n := nn.(*ir.Name)
882 m = inlvar(n)
883 m = typecheck.Expr(m).(*ir.Name)
884 inlvars[n] = m
885 delayretvars = false
886 } else {
887
888 m = retvar(t, i)
889 }
890
891 if base.Flag.GenDwarfInl > 0 {
892
893
894
895 if !strings.HasPrefix(m.Sym().Name, "~R") {
896 m.Name().SetInlFormal(true)
897 m.SetPos(t.Pos)
898 inlfvars = append(inlfvars, m)
899 }
900 }
901
902 retvars = append(retvars, m)
903 }
904
905
906 as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
907 as.Def = true
908 if n.Op() == ir.OCALLMETH {
909 sel := n.X.(*ir.SelectorExpr)
910 if sel.X == nil {
911 base.Fatalf("method call without receiver: %+v", n)
912 }
913 as.Rhs.Append(sel.X)
914 }
915 as.Rhs.Append(n.Args...)
916
917
918
919 var vas *ir.AssignStmt
920
921 if recv := fn.Type().Recv(); recv != nil {
922 as.Lhs.Append(inlParam(recv, as, inlvars))
923 }
924 for _, param := range fn.Type().Params().Fields().Slice() {
925
926
927
928 if !param.IsDDD() || n.IsDDD {
929 as.Lhs.Append(inlParam(param, as, inlvars))
930 continue
931 }
932
933
934
935
936 x := len(as.Lhs)
937 for len(as.Lhs) < len(as.Rhs) {
938 as.Lhs.Append(argvar(param.Type, len(as.Lhs)))
939 }
940 varargs := as.Lhs[x:]
941
942 vas = ir.NewAssignStmt(base.Pos, nil, nil)
943 vas.X = inlParam(param, vas, inlvars)
944 if len(varargs) == 0 {
945 vas.Y = typecheck.NodNil()
946 vas.Y.SetType(param.Type)
947 } else {
948 lit := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(param.Type), nil)
949 lit.List = varargs
950 vas.Y = lit
951 }
952 }
953
954 if len(as.Rhs) != 0 {
955 ninit.Append(typecheck.Stmt(as))
956 }
957
958 if vas != nil {
959 ninit.Append(typecheck.Stmt(vas))
960 }
961
962 if !delayretvars {
963
964 for _, n := range retvars {
965 ninit.Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
966 ras := ir.NewAssignStmt(base.Pos, n, nil)
967 ninit.Append(typecheck.Stmt(ras))
968 }
969 }
970
971 retlabel := typecheck.AutoLabel(".i")
972
973 inlgen++
974
975 parent := -1
976 if b := base.Ctxt.PosTable.Pos(n.Pos()).Base(); b != nil {
977 parent = b.InliningIndex()
978 }
979
980 sym := fn.Linksym()
981 newIndex := base.Ctxt.InlTree.Add(parent, n.Pos(), sym)
982
983
984
985
986
987
988 inlMark := ir.NewInlineMarkStmt(base.Pos, types.BADWIDTH)
989 inlMark.SetPos(n.Pos().WithIsStmt())
990 inlMark.Index = int64(newIndex)
991 ninit.Append(inlMark)
992
993 if base.Flag.GenDwarfInl > 0 {
994 if !sym.WasInlined() {
995 base.Ctxt.DwFixups.SetPrecursorFunc(sym, fn)
996 sym.Set(obj.AttrWasInlined, true)
997 }
998 }
999
1000 subst := inlsubst{
1001 retlabel: retlabel,
1002 retvars: retvars,
1003 delayretvars: delayretvars,
1004 inlvars: inlvars,
1005 defnMarker: ir.NilExpr{},
1006 bases: make(map[*src.PosBase]*src.PosBase),
1007 newInlIndex: newIndex,
1008 fn: fn,
1009 }
1010 subst.edit = subst.node
1011
1012 body := subst.list(ir.Nodes(fn.Inl.Body))
1013
1014 lab := ir.NewLabelStmt(base.Pos, retlabel)
1015 body = append(body, lab)
1016
1017 if !typecheck.Go117ExportTypes {
1018 typecheck.Stmts(body)
1019 }
1020
1021 if base.Flag.GenDwarfInl > 0 {
1022 for _, v := range inlfvars {
1023 v.SetPos(subst.updatedPos(v.Pos()))
1024 }
1025 }
1026
1027
1028
1029 call := ir.NewInlinedCallExpr(base.Pos, nil, nil)
1030 *call.PtrInit() = ninit
1031 call.Body = body
1032 call.ReturnVars = retvars
1033 call.SetType(n.Type())
1034 call.SetTypecheck(1)
1035
1036
1037
1038
1039
1040
1041
1042 ir.EditChildren(call, edit)
1043
1044 if base.Flag.LowerM > 2 {
1045 fmt.Printf("%v: After inlining %+v\n\n", ir.Line(call), call)
1046 }
1047
1048 return call
1049 }
1050
1051
1052
1053
1054 func inlvar(var_ *ir.Name) *ir.Name {
1055 if base.Flag.LowerM > 3 {
1056 fmt.Printf("inlvar %+v\n", var_)
1057 }
1058
1059 n := typecheck.NewName(var_.Sym())
1060 n.SetType(var_.Type())
1061 n.Class = ir.PAUTO
1062 n.SetUsed(true)
1063 n.Curfn = ir.CurFunc
1064 n.SetAddrtaken(var_.Addrtaken())
1065
1066 ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
1067 return n
1068 }
1069
1070
1071 func retvar(t *types.Field, i int) *ir.Name {
1072 n := typecheck.NewName(typecheck.LookupNum("~R", i))
1073 n.SetType(t.Type)
1074 n.Class = ir.PAUTO
1075 n.SetUsed(true)
1076 n.Curfn = ir.CurFunc
1077 ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
1078 return n
1079 }
1080
1081
1082
1083 func argvar(t *types.Type, i int) ir.Node {
1084 n := typecheck.NewName(typecheck.LookupNum("~arg", i))
1085 n.SetType(t.Elem())
1086 n.Class = ir.PAUTO
1087 n.SetUsed(true)
1088 n.Curfn = ir.CurFunc
1089 ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
1090 return n
1091 }
1092
1093
1094
1095 type inlsubst struct {
1096
1097 retlabel *types.Sym
1098
1099
1100 retvars []ir.Node
1101
1102
1103
1104 delayretvars bool
1105
1106 inlvars map[*ir.Name]*ir.Name
1107
1108
1109
1110 defnMarker ir.NilExpr
1111
1112
1113
1114 bases map[*src.PosBase]*src.PosBase
1115
1116
1117
1118 newInlIndex int
1119
1120 edit func(ir.Node) ir.Node
1121
1122
1123
1124 newclofn *ir.Func
1125
1126 fn *ir.Func
1127
1128
1129
1130 noPosUpdate bool
1131 }
1132
1133
1134 func (subst *inlsubst) list(ll ir.Nodes) []ir.Node {
1135 s := make([]ir.Node, 0, len(ll))
1136 for _, n := range ll {
1137 s = append(s, subst.node(n))
1138 }
1139 return s
1140 }
1141
1142
1143
1144
1145 func (subst *inlsubst) fields(oldt *types.Type) []*types.Field {
1146 oldfields := oldt.FieldSlice()
1147 newfields := make([]*types.Field, len(oldfields))
1148 for i := range oldfields {
1149 newfields[i] = oldfields[i].Copy()
1150 if oldfields[i].Nname != nil {
1151 newfields[i].Nname = subst.node(oldfields[i].Nname.(*ir.Name))
1152 }
1153 }
1154 return newfields
1155 }
1156
1157
1158
1159 func (subst *inlsubst) clovar(n *ir.Name) *ir.Name {
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169 m := &ir.Name{}
1170 *m = *n
1171 m.Curfn = subst.newclofn
1172
1173 switch defn := n.Defn.(type) {
1174 case nil:
1175
1176 case *ir.Name:
1177 if !n.IsClosureVar() {
1178 base.FatalfAt(n.Pos(), "want closure variable, got: %+v", n)
1179 }
1180 if n.Sym().Pkg != types.LocalPkg {
1181
1182
1183
1184
1185
1186
1187
1188
1189 m.SetSym(types.LocalPkg.Lookup(n.Sym().Name))
1190 }
1191
1192
1193
1194
1195 if subst.inlvars[n.Defn.(*ir.Name)] != nil {
1196 m.Defn = subst.node(n.Defn)
1197 }
1198 case *ir.AssignStmt, *ir.AssignListStmt:
1199
1200 m.Defn = &subst.defnMarker
1201 case *ir.TypeSwitchGuard:
1202
1203 default:
1204 base.FatalfAt(n.Pos(), "unexpected Defn: %+v", defn)
1205 }
1206
1207 if n.Outer != nil {
1208
1209
1210
1211
1212
1213 s := subst.node(n.Outer).(*ir.Name)
1214 if s == n.Outer {
1215 s = n.Outer.Outer
1216 }
1217 m.Outer = s
1218 }
1219 return m
1220 }
1221
1222
1223
1224 func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
1225 m := ir.Copy(n)
1226
1227
1228
1229
1230
1231
1232 defer func(prev bool) { subst.noPosUpdate = prev }(subst.noPosUpdate)
1233 subst.noPosUpdate = true
1234 ir.EditChildren(m, subst.edit)
1235
1236
1237
1238
1239 oldfn := n.Func
1240 newfn := ir.NewFunc(oldfn.Pos())
1241
1242
1243 newfn.SetTypecheck(0)
1244 newfn.SetInlinabilityChecked(false)
1245 newfn.Inl = nil
1246 newfn.SetIsHiddenClosure(true)
1247 newfn.Nname = ir.NewNameAt(n.Pos(), ir.BlankNode.Sym())
1248 newfn.Nname.Func = newfn
1249
1250 if oldfn.Nname.Ntype != nil {
1251 newfn.Nname.Ntype = subst.node(oldfn.Nname.Ntype).(ir.Ntype)
1252 }
1253 newfn.Nname.Defn = newfn
1254
1255 m.(*ir.ClosureExpr).Func = newfn
1256 newfn.OClosure = m.(*ir.ClosureExpr)
1257
1258 if subst.newclofn != nil {
1259
1260 }
1261 prevxfunc := subst.newclofn
1262
1263
1264
1265
1266 subst.newclofn = newfn
1267 newfn.Dcl = nil
1268 newfn.ClosureVars = nil
1269 for _, oldv := range oldfn.Dcl {
1270 newv := subst.clovar(oldv)
1271 subst.inlvars[oldv] = newv
1272 newfn.Dcl = append(newfn.Dcl, newv)
1273 }
1274 for _, oldv := range oldfn.ClosureVars {
1275 newv := subst.clovar(oldv)
1276 subst.inlvars[oldv] = newv
1277 newfn.ClosureVars = append(newfn.ClosureVars, newv)
1278 }
1279
1280
1281
1282 oldt := oldfn.Type()
1283 newrecvs := subst.fields(oldt.Recvs())
1284 var newrecv *types.Field
1285 if len(newrecvs) > 0 {
1286 newrecv = newrecvs[0]
1287 }
1288 newt := types.NewSignature(oldt.Pkg(), newrecv,
1289 nil, subst.fields(oldt.Params()), subst.fields(oldt.Results()))
1290
1291 newfn.Nname.SetType(newt)
1292 newfn.Body = subst.list(oldfn.Body)
1293
1294
1295 for _, oldv := range oldfn.Dcl {
1296 delete(subst.inlvars, oldv)
1297 }
1298 for _, oldv := range oldfn.ClosureVars {
1299 delete(subst.inlvars, oldv)
1300 }
1301
1302 subst.newclofn = prevxfunc
1303
1304
1305
1306 m.SetTypecheck(0)
1307 if oldfn.ClosureCalled() {
1308 typecheck.Callee(m)
1309 } else {
1310 typecheck.Expr(m)
1311 }
1312 return m
1313 }
1314
1315
1316
1317
1318
1319 func (subst *inlsubst) node(n ir.Node) ir.Node {
1320 if n == nil {
1321 return nil
1322 }
1323
1324 switch n.Op() {
1325 case ir.ONAME:
1326 n := n.(*ir.Name)
1327
1328
1329 if n.IsClosureVar() && subst.newclofn == nil {
1330 o := n.Outer
1331
1332
1333
1334
1335 if o.Curfn != ir.CurFunc {
1336 o = o.Outer
1337 }
1338
1339
1340 if o == nil || o.Curfn != ir.CurFunc {
1341 base.Fatalf("%v: unresolvable capture %v\n", ir.Line(n), n)
1342 }
1343
1344 if base.Flag.LowerM > 2 {
1345 fmt.Printf("substituting captured name %+v -> %+v\n", n, o)
1346 }
1347 return o
1348 }
1349
1350 if inlvar := subst.inlvars[n]; inlvar != nil {
1351 if base.Flag.LowerM > 2 {
1352 fmt.Printf("substituting name %+v -> %+v\n", n, inlvar)
1353 }
1354 return inlvar
1355 }
1356
1357 if base.Flag.LowerM > 2 {
1358 fmt.Printf("not substituting name %+v\n", n)
1359 }
1360 return n
1361
1362 case ir.OMETHEXPR:
1363 n := n.(*ir.SelectorExpr)
1364 return n
1365
1366 case ir.OLITERAL, ir.ONIL, ir.OTYPE:
1367
1368
1369
1370 if n.Sym() != nil {
1371 return n
1372 }
1373
1374 case ir.ORETURN:
1375 if subst.newclofn != nil {
1376
1377 break
1378 }
1379
1380
1381 n := n.(*ir.ReturnStmt)
1382 init := subst.list(n.Init())
1383 if len(subst.retvars) != 0 && len(n.Results) != 0 {
1384 as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
1385
1386
1387
1388
1389 for _, n := range subst.retvars {
1390 as.Lhs.Append(n)
1391 }
1392 as.Rhs = subst.list(n.Results)
1393
1394 if subst.delayretvars {
1395 for _, n := range as.Lhs {
1396 as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
1397 n.Name().Defn = as
1398 }
1399 }
1400
1401 init = append(init, typecheck.Stmt(as))
1402 }
1403 init = append(init, ir.NewBranchStmt(base.Pos, ir.OGOTO, subst.retlabel))
1404 typecheck.Stmts(init)
1405 return ir.NewBlockStmt(base.Pos, init)
1406
1407 case ir.OGOTO:
1408 if subst.newclofn != nil {
1409
1410 break
1411 }
1412 n := n.(*ir.BranchStmt)
1413 m := ir.Copy(n).(*ir.BranchStmt)
1414 m.SetPos(subst.updatedPos(m.Pos()))
1415 *m.PtrInit() = nil
1416 p := fmt.Sprintf("%s·%d", n.Label.Name, inlgen)
1417 m.Label = typecheck.Lookup(p)
1418 return m
1419
1420 case ir.OLABEL:
1421 if subst.newclofn != nil {
1422
1423 break
1424 }
1425 n := n.(*ir.LabelStmt)
1426 m := ir.Copy(n).(*ir.LabelStmt)
1427 m.SetPos(subst.updatedPos(m.Pos()))
1428 *m.PtrInit() = nil
1429 p := fmt.Sprintf("%s·%d", n.Label.Name, inlgen)
1430 m.Label = typecheck.Lookup(p)
1431 return m
1432
1433 case ir.OCLOSURE:
1434 return subst.closure(n.(*ir.ClosureExpr))
1435
1436 }
1437
1438 m := ir.Copy(n)
1439 m.SetPos(subst.updatedPos(m.Pos()))
1440 ir.EditChildren(m, subst.edit)
1441
1442 switch m := m.(type) {
1443 case *ir.AssignStmt:
1444 if lhs, ok := m.X.(*ir.Name); ok && lhs.Defn == &subst.defnMarker {
1445 lhs.Defn = m
1446 }
1447 case *ir.AssignListStmt:
1448 for _, lhs := range m.Lhs {
1449 if lhs, ok := lhs.(*ir.Name); ok && lhs.Defn == &subst.defnMarker {
1450 lhs.Defn = m
1451 }
1452 }
1453 }
1454
1455 return m
1456 }
1457
1458 func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos {
1459 if subst.noPosUpdate {
1460 return xpos
1461 }
1462 pos := base.Ctxt.PosTable.Pos(xpos)
1463 oldbase := pos.Base()
1464 newbase := subst.bases[oldbase]
1465 if newbase == nil {
1466 newbase = src.NewInliningBase(oldbase, subst.newInlIndex)
1467 subst.bases[oldbase] = newbase
1468 }
1469 pos.SetBase(newbase)
1470 return base.Ctxt.PosTable.XPos(pos)
1471 }
1472
1473 func pruneUnusedAutos(ll []*ir.Name, vis *hairyVisitor) []*ir.Name {
1474 s := make([]*ir.Name, 0, len(ll))
1475 for _, n := range ll {
1476 if n.Class == ir.PAUTO {
1477 if !vis.usedLocals.Has(n) {
1478 continue
1479 }
1480 }
1481 s = append(s, n)
1482 }
1483 return s
1484 }
1485
1486
1487 func numNonClosures(list []*ir.Func) int {
1488 count := 0
1489 for _, fn := range list {
1490 if fn.OClosure == nil {
1491 count++
1492 }
1493 }
1494 return count
1495 }
1496
1497 func doList(list []ir.Node, do func(ir.Node) bool) bool {
1498 for _, x := range list {
1499 if x != nil {
1500 if do(x) {
1501 return true
1502 }
1503 }
1504 }
1505 return false
1506 }
1507
View as plain text