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 "internal/buildcfg"
33 "strconv"
34
35 "cmd/compile/internal/base"
36 "cmd/compile/internal/inline/inlheur"
37 "cmd/compile/internal/ir"
38 "cmd/compile/internal/logopt"
39 "cmd/compile/internal/pgo"
40 "cmd/compile/internal/typecheck"
41 "cmd/compile/internal/types"
42 "cmd/internal/obj"
43 )
44
45
46 const (
47 inlineMaxBudget = 80
48 inlineExtraAppendCost = 0
49
50 inlineExtraCallCost = 57
51 inlineExtraPanicCost = 1
52 inlineExtraThrowCost = inlineMaxBudget
53
54 inlineBigFunctionNodes = 5000
55 inlineBigFunctionMaxCost = 20
56 )
57
58 var (
59
60
61 candHotCalleeMap = make(map[*pgo.IRNode]struct{})
62
63
64
65 candHotEdgeMap = make(map[pgo.CallSiteInfo]struct{})
66
67
68 inlineHotCallSiteThresholdPercent float64
69
70
71
72
73
74 inlineCDFHotCallSiteThresholdPercent = float64(99)
75
76
77 inlineHotMaxBudget int32 = 2000
78 )
79
80
81 func PGOInlinePrologue(p *pgo.Profile, funcs []*ir.Func) {
82 if base.Debug.PGOInlineCDFThreshold != "" {
83 if s, err := strconv.ParseFloat(base.Debug.PGOInlineCDFThreshold, 64); err == nil && s >= 0 && s <= 100 {
84 inlineCDFHotCallSiteThresholdPercent = s
85 } else {
86 base.Fatalf("invalid PGOInlineCDFThreshold, must be between 0 and 100")
87 }
88 }
89 var hotCallsites []pgo.NamedCallEdge
90 inlineHotCallSiteThresholdPercent, hotCallsites = hotNodesFromCDF(p)
91 if base.Debug.PGODebug > 0 {
92 fmt.Printf("hot-callsite-thres-from-CDF=%v\n", inlineHotCallSiteThresholdPercent)
93 }
94
95 if x := base.Debug.PGOInlineBudget; x != 0 {
96 inlineHotMaxBudget = int32(x)
97 }
98
99 for _, n := range hotCallsites {
100
101 if callee := p.WeightedCG.IRNodes[n.CalleeName]; callee != nil {
102 candHotCalleeMap[callee] = struct{}{}
103 }
104
105 if caller := p.WeightedCG.IRNodes[n.CallerName]; caller != nil && caller.AST != nil {
106 csi := pgo.CallSiteInfo{LineOffset: n.CallSiteOffset, Caller: caller.AST}
107 candHotEdgeMap[csi] = struct{}{}
108 }
109 }
110
111 if base.Debug.PGODebug >= 3 {
112 fmt.Printf("hot-cg before inline in dot format:")
113 p.PrintWeightedCallGraphDOT(inlineHotCallSiteThresholdPercent)
114 }
115 }
116
117
118
119
120
121
122
123 func hotNodesFromCDF(p *pgo.Profile) (float64, []pgo.NamedCallEdge) {
124 cum := int64(0)
125 for i, n := range p.NamedEdgeMap.ByWeight {
126 w := p.NamedEdgeMap.Weight[n]
127 cum += w
128 if pgo.WeightInPercentage(cum, p.TotalWeight) > inlineCDFHotCallSiteThresholdPercent {
129
130
131
132 return pgo.WeightInPercentage(w, p.TotalWeight), p.NamedEdgeMap.ByWeight[:i+1]
133 }
134 }
135 return 0, p.NamedEdgeMap.ByWeight
136 }
137
138
139 func CanInlineFuncs(funcs []*ir.Func, profile *pgo.Profile) {
140 if profile != nil {
141 PGOInlinePrologue(profile, funcs)
142 }
143
144 ir.VisitFuncsBottomUp(funcs, func(list []*ir.Func, recursive bool) {
145 CanInlineSCC(list, recursive, profile)
146 })
147 }
148
149
150
151
152
153
154 func CanInlineSCC(funcs []*ir.Func, recursive bool, profile *pgo.Profile) {
155 if base.Flag.LowerL == 0 {
156 return
157 }
158
159 numfns := numNonClosures(funcs)
160
161 for _, fn := range funcs {
162 if !recursive || numfns > 1 {
163
164
165
166 CanInline(fn, profile)
167 } else {
168 if base.Flag.LowerM > 1 && fn.OClosure == nil {
169 fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(fn), fn.Nname)
170 }
171 }
172 if inlheur.Enabled() {
173 analyzeFuncProps(fn, profile)
174 }
175 }
176 }
177
178
179
180
181
182
183 func GarbageCollectUnreferencedHiddenClosures() {
184
185 liveFuncs := make(map[*ir.Func]bool)
186
187 var markLiveFuncs func(fn *ir.Func)
188 markLiveFuncs = func(fn *ir.Func) {
189 if liveFuncs[fn] {
190 return
191 }
192 liveFuncs[fn] = true
193 ir.Visit(fn, func(n ir.Node) {
194 if clo, ok := n.(*ir.ClosureExpr); ok {
195 markLiveFuncs(clo.Func)
196 }
197 })
198 }
199
200 for i := 0; i < len(typecheck.Target.Funcs); i++ {
201 fn := typecheck.Target.Funcs[i]
202 if fn.IsHiddenClosure() {
203 continue
204 }
205 markLiveFuncs(fn)
206 }
207
208 for i := 0; i < len(typecheck.Target.Funcs); i++ {
209 fn := typecheck.Target.Funcs[i]
210 if !fn.IsHiddenClosure() {
211 continue
212 }
213 if fn.IsDeadcodeClosure() {
214 continue
215 }
216 if liveFuncs[fn] {
217 continue
218 }
219 fn.SetIsDeadcodeClosure(true)
220 if base.Flag.LowerM > 2 {
221 fmt.Printf("%v: unreferenced closure %v marked as dead\n", ir.Line(fn), fn)
222 }
223 if fn.Inl != nil && fn.LSym == nil {
224 ir.InitLSym(fn, true)
225 }
226 }
227 }
228
229
230
231
232
233
234
235
236 func inlineBudget(fn *ir.Func, profile *pgo.Profile, relaxed bool, verbose bool) int32 {
237
238 budget := int32(inlineMaxBudget)
239 if profile != nil {
240 if n, ok := profile.WeightedCG.IRNodes[ir.LinkFuncName(fn)]; ok {
241 if _, ok := candHotCalleeMap[n]; ok {
242 budget = int32(inlineHotMaxBudget)
243 if verbose {
244 fmt.Printf("hot-node enabled increased budget=%v for func=%v\n", budget, ir.PkgFuncName(fn))
245 }
246 }
247 }
248 }
249 if relaxed {
250 budget += inlheur.BudgetExpansion(inlineMaxBudget)
251 }
252 return budget
253 }
254
255
256
257
258 func CanInline(fn *ir.Func, profile *pgo.Profile) {
259 if fn.Nname == nil {
260 base.Fatalf("CanInline no nname %+v", fn)
261 }
262
263 var reason string
264 if base.Flag.LowerM > 1 || logopt.Enabled() {
265 defer func() {
266 if reason != "" {
267 if base.Flag.LowerM > 1 {
268 fmt.Printf("%v: cannot inline %v: %s\n", ir.Line(fn), fn.Nname, reason)
269 }
270 if logopt.Enabled() {
271 logopt.LogOpt(fn.Pos(), "cannotInlineFunction", "inline", ir.FuncName(fn), reason)
272 }
273 }
274 }()
275 }
276
277 reason = InlineImpossible(fn)
278 if reason != "" {
279 return
280 }
281 if fn.Typecheck() == 0 {
282 base.Fatalf("CanInline on non-typechecked function %v", fn)
283 }
284
285 n := fn.Nname
286 if n.Func.InlinabilityChecked() {
287 return
288 }
289 defer n.Func.SetInlinabilityChecked(true)
290
291 cc := int32(inlineExtraCallCost)
292 if base.Flag.LowerL == 4 {
293 cc = 1
294 }
295
296
297 relaxed := inlheur.Enabled()
298
299
300 budget := inlineBudget(fn, profile, relaxed, base.Debug.PGODebug > 0)
301
302
303
304
305
306
307
308
309
310
311 visitor := hairyVisitor{
312 curFunc: fn,
313 isBigFunc: IsBigFunc(fn),
314 budget: budget,
315 maxBudget: budget,
316 extraCallCost: cc,
317 profile: profile,
318 }
319 if visitor.tooHairy(fn) {
320 reason = visitor.reason
321 return
322 }
323
324 n.Func.Inl = &ir.Inline{
325 Cost: budget - visitor.budget,
326 Dcl: pruneUnusedAutos(n.Func.Dcl, &visitor),
327 HaveDcl: true,
328
329 CanDelayResults: canDelayResults(fn),
330 }
331 if base.Flag.LowerM != 0 || logopt.Enabled() {
332 noteInlinableFunc(n, fn, budget-visitor.budget)
333 }
334 }
335
336
337
338 func noteInlinableFunc(n *ir.Name, fn *ir.Func, cost int32) {
339 if base.Flag.LowerM > 1 {
340 fmt.Printf("%v: can inline %v with cost %d as: %v { %v }\n", ir.Line(fn), n, cost, fn.Type(), ir.Nodes(fn.Body))
341 } else if base.Flag.LowerM != 0 {
342 fmt.Printf("%v: can inline %v\n", ir.Line(fn), n)
343 }
344
345 if logopt.Enabled() {
346 logopt.LogOpt(fn.Pos(), "canInlineFunction", "inline", ir.FuncName(fn), fmt.Sprintf("cost: %d", cost))
347 }
348 }
349
350
351
352 func InlineImpossible(fn *ir.Func) string {
353 var reason string
354 if fn.Nname == nil {
355 reason = "no name"
356 return reason
357 }
358
359
360 if fn.Pragma&ir.Noinline != 0 {
361 reason = "marked go:noinline"
362 return reason
363 }
364
365
366 if base.Flag.Race && fn.Pragma&ir.Norace != 0 {
367 reason = "marked go:norace with -race compilation"
368 return reason
369 }
370
371
372 if base.Debug.Checkptr != 0 && fn.Pragma&ir.NoCheckPtr != 0 {
373 reason = "marked go:nocheckptr"
374 return reason
375 }
376
377
378
379 if fn.Pragma&ir.CgoUnsafeArgs != 0 {
380 reason = "marked go:cgo_unsafe_args"
381 return reason
382 }
383
384
385
386
387
388
389
390 if fn.Pragma&ir.UintptrKeepAlive != 0 {
391 reason = "marked as having a keep-alive uintptr argument"
392 return reason
393 }
394
395
396
397 if fn.Pragma&ir.UintptrEscapes != 0 {
398 reason = "marked as having an escaping uintptr argument"
399 return reason
400 }
401
402
403
404
405 if fn.Pragma&ir.Yeswritebarrierrec != 0 {
406 reason = "marked go:yeswritebarrierrec"
407 return reason
408 }
409
410
411
412 if len(fn.Body) == 0 && !typecheck.HaveInlineBody(fn) {
413 reason = "no function body"
414 return reason
415 }
416
417 return ""
418 }
419
420
421
422 func canDelayResults(fn *ir.Func) bool {
423
424
425
426
427
428 nreturns := 0
429 ir.VisitList(fn.Body, func(n ir.Node) {
430 if n, ok := n.(*ir.ReturnStmt); ok {
431 nreturns++
432 if len(n.Results) == 0 {
433 nreturns++
434 }
435 }
436 })
437
438 if nreturns != 1 {
439 return false
440 }
441
442
443 for _, param := range fn.Type().Results() {
444 if sym := param.Sym; sym != nil && !sym.IsBlank() {
445 return false
446 }
447 }
448
449 return true
450 }
451
452
453
454 type hairyVisitor struct {
455
456 curFunc *ir.Func
457 isBigFunc bool
458 budget int32
459 maxBudget int32
460 reason string
461 extraCallCost int32
462 usedLocals ir.NameSet
463 do func(ir.Node) bool
464 profile *pgo.Profile
465 }
466
467 func (v *hairyVisitor) tooHairy(fn *ir.Func) bool {
468 v.do = v.doNode
469 if ir.DoChildren(fn, v.do) {
470 return true
471 }
472 if v.budget < 0 {
473 v.reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", v.maxBudget-v.budget, v.maxBudget)
474 return true
475 }
476 return false
477 }
478
479
480
481 func (v *hairyVisitor) doNode(n ir.Node) bool {
482 if n == nil {
483 return false
484 }
485 opSwitch:
486 switch n.Op() {
487
488 case ir.OCALLFUNC:
489 n := n.(*ir.CallExpr)
490
491
492
493
494 var cheap bool
495 if n.Fun.Op() == ir.ONAME {
496 name := n.Fun.(*ir.Name)
497 if name.Class == ir.PFUNC {
498 switch fn := types.RuntimeSymName(name.Sym()); fn {
499 case "getcallerpc", "getcallersp":
500 v.reason = "call to " + fn
501 return true
502 case "throw":
503 v.budget -= inlineExtraThrowCost
504 break opSwitch
505 case "panicrangeexit":
506 cheap = true
507 }
508
509
510
511 if types.ReflectSymName(name.Sym()) == "noescape" {
512 cheap = true
513 }
514 }
515
516
517
518
519
520
521
522
523
524
525 if isAtomicCoverageCounterUpdate(n) {
526 return false
527 }
528 }
529 if n.Fun.Op() == ir.OMETHEXPR {
530 if meth := ir.MethodExprName(n.Fun); meth != nil {
531 if fn := meth.Func; fn != nil {
532 s := fn.Sym()
533 if types.RuntimeSymName(s) == "heapBits.nextArena" {
534
535
536
537 cheap = true
538 }
539
540
541
542
543 if base.Ctxt.Arch.CanMergeLoads && s.Pkg.Path == "encoding/binary" {
544 switch s.Name {
545 case "littleEndian.Uint64", "littleEndian.Uint32", "littleEndian.Uint16",
546 "bigEndian.Uint64", "bigEndian.Uint32", "bigEndian.Uint16",
547 "littleEndian.PutUint64", "littleEndian.PutUint32", "littleEndian.PutUint16",
548 "bigEndian.PutUint64", "bigEndian.PutUint32", "bigEndian.PutUint16",
549 "littleEndian.AppendUint64", "littleEndian.AppendUint32", "littleEndian.AppendUint16",
550 "bigEndian.AppendUint64", "bigEndian.AppendUint32", "bigEndian.AppendUint16":
551 cheap = true
552 }
553 }
554 }
555 }
556 }
557 if cheap {
558 break
559 }
560
561 if ir.IsIntrinsicCall(n) {
562
563 break
564 }
565
566 if callee := inlCallee(v.curFunc, n.Fun, v.profile); callee != nil && typecheck.HaveInlineBody(callee) {
567
568
569
570 if ok, _ := canInlineCallExpr(v.curFunc, n, callee, v.isBigFunc, false); ok {
571
572
573
574
575
576
577
578
579
580
581
582 v.budget -= callee.Inl.Cost
583 break
584 }
585 }
586
587
588 v.budget -= v.extraCallCost
589
590 case ir.OCALLMETH:
591 base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
592
593
594 case ir.OCALL, ir.OCALLINTER:
595
596 v.budget -= v.extraCallCost
597
598 case ir.OPANIC:
599 n := n.(*ir.UnaryExpr)
600 if n.X.Op() == ir.OCONVIFACE && n.X.(*ir.ConvExpr).Implicit() {
601
602
603
604 v.budget++
605 }
606 v.budget -= inlineExtraPanicCost
607
608 case ir.ORECOVER:
609 base.FatalfAt(n.Pos(), "ORECOVER missed typecheck")
610 case ir.ORECOVERFP:
611
612
613 v.reason = "call to recover"
614 return true
615
616 case ir.OCLOSURE:
617 if base.Debug.InlFuncsWithClosures == 0 {
618 v.reason = "not inlining functions with closures"
619 return true
620 }
621
622
623
624
625
626
627
628 v.budget -= 15
629
630 case ir.OGO, ir.ODEFER, ir.OTAILCALL:
631 v.reason = "unhandled op " + n.Op().String()
632 return true
633
634 case ir.OAPPEND:
635 v.budget -= inlineExtraAppendCost
636
637 case ir.OADDR:
638 n := n.(*ir.AddrExpr)
639
640 if dot, ok := n.X.(*ir.SelectorExpr); ok && (dot.Op() == ir.ODOT || dot.Op() == ir.ODOTPTR) {
641 if _, ok := dot.X.(*ir.Name); ok && dot.Selection.Offset == 0 {
642 v.budget += 2
643 }
644 }
645
646 case ir.ODEREF:
647
648 n := n.(*ir.StarExpr)
649
650 ptr := n.X
651 for ptr.Op() == ir.OCONVNOP {
652 ptr = ptr.(*ir.ConvExpr).X
653 }
654 if ptr.Op() == ir.OADDR {
655 v.budget += 1
656 }
657
658 case ir.OCONVNOP:
659
660 v.budget++
661
662 case ir.OFALL, ir.OTYPE:
663
664 return false
665
666 case ir.OIF:
667 n := n.(*ir.IfStmt)
668 if ir.IsConst(n.Cond, constant.Bool) {
669
670 if doList(n.Init(), v.do) {
671 return true
672 }
673 if ir.BoolVal(n.Cond) {
674 return doList(n.Body, v.do)
675 } else {
676 return doList(n.Else, v.do)
677 }
678 }
679
680 case ir.ONAME:
681 n := n.(*ir.Name)
682 if n.Class == ir.PAUTO {
683 v.usedLocals.Add(n)
684 }
685
686 case ir.OBLOCK:
687
688
689
690 v.budget++
691
692 case ir.OMETHVALUE, ir.OSLICELIT:
693 v.budget--
694
695 case ir.OMETHEXPR:
696 v.budget++
697
698 case ir.OAS2:
699 n := n.(*ir.AssignListStmt)
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718 if len(n.Rhs) > 0 {
719 if init := n.Rhs[0].Init(); len(init) == 1 {
720 if _, ok := init[0].(*ir.AssignListStmt); ok {
721
722
723
724
725 v.budget += 4*int32(len(n.Lhs)) + 1
726 }
727 }
728 }
729
730 case ir.OAS:
731
732
733
734
735
736
737
738
739
740
741 n := n.(*ir.AssignStmt)
742 if n.X.Op() == ir.OINDEX && isIndexingCoverageCounter(n.X) {
743 return false
744 }
745 }
746
747 v.budget--
748
749
750 if v.budget < 0 && base.Flag.LowerM < 2 && !logopt.Enabled() {
751 v.reason = "too expensive"
752 return true
753 }
754
755 return ir.DoChildren(n, v.do)
756 }
757
758
759
760
761 func IsBigFunc(fn *ir.Func) bool {
762 budget := inlineBigFunctionNodes
763 return ir.Any(fn, func(n ir.Node) bool {
764
765
766 if n, ok := n.(*ir.AssignListStmt); ok && n.Op() == ir.OAS2 && len(n.Rhs) > 0 {
767 if init := n.Rhs[0].Init(); len(init) == 1 {
768 if _, ok := init[0].(*ir.AssignListStmt); ok {
769 budget += 4*len(n.Lhs) + 1
770 }
771 }
772 }
773
774 budget--
775 return budget <= 0
776 })
777 }
778
779
780
781 func TryInlineCall(callerfn *ir.Func, call *ir.CallExpr, bigCaller bool, profile *pgo.Profile) *ir.InlinedCallExpr {
782 if base.Flag.LowerL == 0 {
783 return nil
784 }
785 if call.Op() != ir.OCALLFUNC {
786 return nil
787 }
788 if call.GoDefer || call.NoInline {
789 return nil
790 }
791
792
793
794 if base.Debug.Checkptr != 0 && call.Fun.Op() == ir.OMETHEXPR {
795 if method := ir.MethodExprName(call.Fun); method != nil {
796 switch types.ReflectSymName(method.Sym()) {
797 case "Value.UnsafeAddr", "Value.Pointer":
798 return nil
799 }
800 }
801 }
802
803 if base.Flag.LowerM > 3 {
804 fmt.Printf("%v:call to func %+v\n", ir.Line(call), call.Fun)
805 }
806 if ir.IsIntrinsicCall(call) {
807 return nil
808 }
809 if fn := inlCallee(callerfn, call.Fun, profile); fn != nil && typecheck.HaveInlineBody(fn) {
810 return mkinlcall(callerfn, call, fn, bigCaller)
811 }
812 return nil
813 }
814
815
816
817 func inlCallee(caller *ir.Func, fn ir.Node, profile *pgo.Profile) (res *ir.Func) {
818 fn = ir.StaticValue(fn)
819 switch fn.Op() {
820 case ir.OMETHEXPR:
821 fn := fn.(*ir.SelectorExpr)
822 n := ir.MethodExprName(fn)
823
824
825
826 if n == nil || !types.Identical(n.Type().Recv().Type, fn.X.Type()) {
827 return nil
828 }
829 return n.Func
830 case ir.ONAME:
831 fn := fn.(*ir.Name)
832 if fn.Class == ir.PFUNC {
833 return fn.Func
834 }
835 case ir.OCLOSURE:
836 fn := fn.(*ir.ClosureExpr)
837 c := fn.Func
838 if len(c.ClosureVars) != 0 && c.ClosureVars[0].Outer.Curfn != caller {
839 return nil
840 }
841 CanInline(c, profile)
842 return c
843 }
844 return nil
845 }
846
847 var inlgen int
848
849
850
851 var SSADumpInline = func(*ir.Func) {}
852
853
854
855 var InlineCall = func(callerfn *ir.Func, call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr {
856 base.Fatalf("inline.InlineCall not overridden")
857 panic("unreachable")
858 }
859
860
861
862
863
864
865
866 func inlineCostOK(n *ir.CallExpr, caller, callee *ir.Func, bigCaller bool) (bool, int32, int32) {
867 maxCost := int32(inlineMaxBudget)
868 if bigCaller {
869
870
871 maxCost = inlineBigFunctionMaxCost
872 }
873
874 metric := callee.Inl.Cost
875 if inlheur.Enabled() {
876 score, ok := inlheur.GetCallSiteScore(caller, n)
877 if ok {
878 metric = int32(score)
879 }
880 }
881
882 if metric <= maxCost {
883
884 return true, 0, metric
885 }
886
887
888
889
890 lineOffset := pgo.NodeLineOffset(n, caller)
891 csi := pgo.CallSiteInfo{LineOffset: lineOffset, Caller: caller}
892 if _, ok := candHotEdgeMap[csi]; !ok {
893
894 return false, maxCost, metric
895 }
896
897
898
899 if bigCaller {
900 if base.Debug.PGODebug > 0 {
901 fmt.Printf("hot-big check disallows inlining for call %s (cost %d) at %v in big function %s\n", ir.PkgFuncName(callee), callee.Inl.Cost, ir.Line(n), ir.PkgFuncName(caller))
902 }
903 return false, maxCost, metric
904 }
905
906 if metric > inlineHotMaxBudget {
907 return false, inlineHotMaxBudget, metric
908 }
909
910 if !base.PGOHash.MatchPosWithInfo(n.Pos(), "inline", nil) {
911
912 return false, maxCost, metric
913 }
914
915 if base.Debug.PGODebug > 0 {
916 fmt.Printf("hot-budget check allows inlining for call %s (cost %d) at %v in function %s\n", ir.PkgFuncName(callee), callee.Inl.Cost, ir.Line(n), ir.PkgFuncName(caller))
917 }
918
919 return true, 0, metric
920 }
921
922
923
924
925
926
927
928 func canInlineCallExpr(callerfn *ir.Func, n *ir.CallExpr, callee *ir.Func, bigCaller bool, log bool) (bool, int32) {
929 if callee.Inl == nil {
930
931 if log && logopt.Enabled() {
932 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(callerfn),
933 fmt.Sprintf("%s cannot be inlined", ir.PkgFuncName(callee)))
934 }
935 return false, 0
936 }
937
938 ok, maxCost, callSiteScore := inlineCostOK(n, callerfn, callee, bigCaller)
939 if !ok {
940
941 if log && logopt.Enabled() {
942 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(callerfn),
943 fmt.Sprintf("cost %d of %s exceeds max caller cost %d", callee.Inl.Cost, ir.PkgFuncName(callee), maxCost))
944 }
945 return false, 0
946 }
947
948 if callee == callerfn {
949
950 if log && logopt.Enabled() {
951 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to %s", ir.FuncName(callerfn)))
952 }
953 return false, 0
954 }
955
956 if base.Flag.Cfg.Instrumenting && types.IsNoInstrumentPkg(callee.Sym().Pkg) {
957
958
959
960
961
962
963 if log && logopt.Enabled() {
964 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(callerfn),
965 fmt.Sprintf("call to runtime function %s in instrumented build", ir.PkgFuncName(callee)))
966 }
967 return false, 0
968 }
969
970 if base.Flag.Race && types.IsNoRacePkg(callee.Sym().Pkg) {
971 if log && logopt.Enabled() {
972 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(callerfn),
973 fmt.Sprintf(`call to into "no-race" package function %s in race build`, ir.PkgFuncName(callee)))
974 }
975 return false, 0
976 }
977
978
979
980
981
982
983
984
985 parent := base.Ctxt.PosTable.Pos(n.Pos()).Base().InliningIndex()
986 sym := callee.Linksym()
987 for inlIndex := parent; inlIndex >= 0; inlIndex = base.Ctxt.InlTree.Parent(inlIndex) {
988 if base.Ctxt.InlTree.InlinedFunction(inlIndex) == sym {
989 if log {
990 if base.Flag.LowerM > 1 {
991 fmt.Printf("%v: cannot inline %v into %v: repeated recursive cycle\n", ir.Line(n), callee, ir.FuncName(callerfn))
992 }
993 if logopt.Enabled() {
994 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(callerfn),
995 fmt.Sprintf("repeated recursive cycle to %s", ir.PkgFuncName(callee)))
996 }
997 }
998 return false, 0
999 }
1000 }
1001
1002 return true, callSiteScore
1003 }
1004
1005
1006
1007
1008
1009
1010
1011
1012 func mkinlcall(callerfn *ir.Func, n *ir.CallExpr, fn *ir.Func, bigCaller bool) *ir.InlinedCallExpr {
1013 ok, score := canInlineCallExpr(callerfn, n, fn, bigCaller, true)
1014 if !ok {
1015 return nil
1016 }
1017 typecheck.AssertFixedCall(n)
1018
1019 parent := base.Ctxt.PosTable.Pos(n.Pos()).Base().InliningIndex()
1020 sym := fn.Linksym()
1021 inlIndex := base.Ctxt.InlTree.Add(parent, n.Pos(), sym, ir.FuncName(fn))
1022
1023 closureInitLSym := func(n *ir.CallExpr, fn *ir.Func) {
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045 if n.Op() != ir.OCALLFUNC {
1046
1047 return
1048 }
1049 if n.Fun.Op() != ir.OCLOSURE {
1050
1051 return
1052 }
1053
1054 clo := n.Fun.(*ir.ClosureExpr)
1055 if ir.IsTrivialClosure(clo) {
1056
1057 return
1058 }
1059
1060 ir.InitLSym(fn, true)
1061 }
1062
1063 closureInitLSym(n, fn)
1064
1065 if base.Flag.GenDwarfInl > 0 {
1066 if !sym.WasInlined() {
1067 base.Ctxt.DwFixups.SetPrecursorFunc(sym, fn)
1068 sym.Set(obj.AttrWasInlined, true)
1069 }
1070 }
1071
1072 if base.Flag.LowerM != 0 {
1073 if buildcfg.Experiment.NewInliner {
1074 fmt.Printf("%v: inlining call to %v with score %d\n",
1075 ir.Line(n), fn, score)
1076 } else {
1077 fmt.Printf("%v: inlining call to %v\n", ir.Line(n), fn)
1078 }
1079 }
1080 if base.Flag.LowerM > 2 {
1081 fmt.Printf("%v: Before inlining: %+v\n", ir.Line(n), n)
1082 }
1083
1084 res := InlineCall(callerfn, n, fn, inlIndex)
1085
1086 if res == nil {
1087 base.FatalfAt(n.Pos(), "inlining call to %v failed", fn)
1088 }
1089
1090 if base.Flag.LowerM > 2 {
1091 fmt.Printf("%v: After inlining %+v\n\n", ir.Line(res), res)
1092 }
1093
1094 if inlheur.Enabled() {
1095 inlheur.UpdateCallsiteTable(callerfn, n, res)
1096 }
1097
1098 return res
1099 }
1100
1101
1102 func CalleeEffects(init *ir.Nodes, callee ir.Node) {
1103 for {
1104 init.Append(ir.TakeInit(callee)...)
1105
1106 switch callee.Op() {
1107 case ir.ONAME, ir.OCLOSURE, ir.OMETHEXPR:
1108 return
1109
1110 case ir.OCONVNOP:
1111 conv := callee.(*ir.ConvExpr)
1112 callee = conv.X
1113
1114 case ir.OINLCALL:
1115 ic := callee.(*ir.InlinedCallExpr)
1116 init.Append(ic.Body.Take()...)
1117 callee = ic.SingleResult()
1118
1119 default:
1120 base.FatalfAt(callee.Pos(), "unexpected callee expression: %v", callee)
1121 }
1122 }
1123 }
1124
1125 func pruneUnusedAutos(ll []*ir.Name, vis *hairyVisitor) []*ir.Name {
1126 s := make([]*ir.Name, 0, len(ll))
1127 for _, n := range ll {
1128 if n.Class == ir.PAUTO {
1129 if !vis.usedLocals.Has(n) {
1130
1131
1132 base.FatalfAt(n.Pos(), "unused auto: %v", n)
1133 continue
1134 }
1135 }
1136 s = append(s, n)
1137 }
1138 return s
1139 }
1140
1141
1142 func numNonClosures(list []*ir.Func) int {
1143 count := 0
1144 for _, fn := range list {
1145 if fn.OClosure == nil {
1146 count++
1147 }
1148 }
1149 return count
1150 }
1151
1152 func doList(list []ir.Node, do func(ir.Node) bool) bool {
1153 for _, x := range list {
1154 if x != nil {
1155 if do(x) {
1156 return true
1157 }
1158 }
1159 }
1160 return false
1161 }
1162
1163
1164
1165 func isIndexingCoverageCounter(n ir.Node) bool {
1166 if n.Op() != ir.OINDEX {
1167 return false
1168 }
1169 ixn := n.(*ir.IndexExpr)
1170 if ixn.X.Op() != ir.ONAME || !ixn.X.Type().IsArray() {
1171 return false
1172 }
1173 nn := ixn.X.(*ir.Name)
1174 return nn.CoverageCounter()
1175 }
1176
1177
1178
1179
1180 func isAtomicCoverageCounterUpdate(cn *ir.CallExpr) bool {
1181 if cn.Fun.Op() != ir.ONAME {
1182 return false
1183 }
1184 name := cn.Fun.(*ir.Name)
1185 if name.Class != ir.PFUNC {
1186 return false
1187 }
1188 fn := name.Sym().Name
1189 if name.Sym().Pkg.Path != "sync/atomic" ||
1190 (fn != "AddUint32" && fn != "StoreUint32") {
1191 return false
1192 }
1193 if len(cn.Args) != 2 || cn.Args[0].Op() != ir.OADDR {
1194 return false
1195 }
1196 adn := cn.Args[0].(*ir.AddrExpr)
1197 v := isIndexingCoverageCounter(adn.X)
1198 return v
1199 }
1200
1201 func PostProcessCallSites(profile *pgo.Profile) {
1202 if base.Debug.DumpInlCallSiteScores != 0 {
1203 budgetCallback := func(fn *ir.Func, prof *pgo.Profile) (int32, bool) {
1204 v := inlineBudget(fn, prof, false, false)
1205 return v, v == inlineHotMaxBudget
1206 }
1207 inlheur.DumpInlCallSiteScores(profile, budgetCallback)
1208 }
1209 }
1210
1211 func analyzeFuncProps(fn *ir.Func, p *pgo.Profile) {
1212 canInline := func(fn *ir.Func) { CanInline(fn, p) }
1213 budgetForFunc := func(fn *ir.Func) int32 {
1214 return inlineBudget(fn, p, true, false)
1215 }
1216 inlheur.AnalyzeFunc(fn, canInline, budgetForFunc, inlineMaxBudget)
1217 }
1218
View as plain text