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 CanDelayResults: canDelayResults(fn),
184 }
185
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)
190 }
191 if logopt.Enabled() {
192 logopt.LogOpt(fn.Pos(), "canInlineFunction", "inline", ir.FuncName(fn), fmt.Sprintf("cost: %d", inlineMaxBudget-visitor.budget))
193 }
194 }
195
196
197
198 func canDelayResults(fn *ir.Func) bool {
199
200
201
202
203
204 nreturns := 0
205 ir.VisitList(fn.Body, func(n ir.Node) {
206 if n, ok := n.(*ir.ReturnStmt); ok {
207 nreturns++
208 if len(n.Results) == 0 {
209 nreturns++
210 }
211 }
212 })
213
214 if nreturns != 1 {
215 return false
216 }
217
218
219 for _, param := range fn.Type().Results().FieldSlice() {
220 if sym := types.OrigSym(param.Sym); sym != nil && !sym.IsBlank() {
221 return false
222 }
223 }
224
225 return true
226 }
227
228
229
230 type hairyVisitor struct {
231 budget int32
232 reason string
233 extraCallCost int32
234 usedLocals ir.NameSet
235 do func(ir.Node) bool
236 }
237
238 func (v *hairyVisitor) tooHairy(fn *ir.Func) bool {
239 v.do = v.doNode
240 if ir.DoChildren(fn, v.do) {
241 return true
242 }
243 if v.budget < 0 {
244 v.reason = fmt.Sprintf("function too complex: cost %d exceeds budget %d", inlineMaxBudget-v.budget, inlineMaxBudget)
245 return true
246 }
247 return false
248 }
249
250 func (v *hairyVisitor) doNode(n ir.Node) bool {
251 if n == nil {
252 return false
253 }
254 switch n.Op() {
255
256 case ir.OCALLFUNC:
257 n := n.(*ir.CallExpr)
258
259
260
261
262 if n.X.Op() == ir.ONAME {
263 name := n.X.(*ir.Name)
264 if name.Class == ir.PFUNC && types.IsRuntimePkg(name.Sym().Pkg) {
265 fn := name.Sym().Name
266 if fn == "getcallerpc" || fn == "getcallersp" {
267 v.reason = "call to " + fn
268 return true
269 }
270 if fn == "throw" {
271 v.budget -= inlineExtraThrowCost
272 break
273 }
274 }
275 }
276 if n.X.Op() == ir.OMETHEXPR {
277 if meth := ir.MethodExprName(n.X); meth != nil {
278 if fn := meth.Func; fn != nil {
279 s := fn.Sym()
280 var cheap bool
281 if types.IsRuntimePkg(s.Pkg) && s.Name == "heapBits.nextArena" {
282
283
284
285 cheap = true
286 }
287
288
289
290
291 if base.Ctxt.Arch.CanMergeLoads && s.Pkg.Path == "encoding/binary" {
292 switch s.Name {
293 case "littleEndian.Uint64", "littleEndian.Uint32", "littleEndian.Uint16",
294 "bigEndian.Uint64", "bigEndian.Uint32", "bigEndian.Uint16",
295 "littleEndian.PutUint64", "littleEndian.PutUint32", "littleEndian.PutUint16",
296 "bigEndian.PutUint64", "bigEndian.PutUint32", "bigEndian.PutUint16":
297 cheap = true
298 }
299 }
300 if cheap {
301 break
302 }
303 }
304 }
305 }
306
307 if ir.IsIntrinsicCall(n) {
308
309 break
310 }
311
312 if fn := inlCallee(n.X); fn != nil && typecheck.HaveInlineBody(fn) {
313 v.budget -= fn.Inl.Cost
314 break
315 }
316
317
318 v.budget -= v.extraCallCost
319
320 case ir.OCALLMETH:
321 base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
322
323
324 case ir.OCALL, ir.OCALLINTER:
325
326 v.budget -= v.extraCallCost
327
328 case ir.OPANIC:
329 n := n.(*ir.UnaryExpr)
330 if n.X.Op() == ir.OCONVIFACE && n.X.(*ir.ConvExpr).Implicit() {
331
332
333
334 v.budget++
335 }
336 v.budget -= inlineExtraPanicCost
337
338 case ir.ORECOVER:
339
340
341 v.reason = "call to recover"
342 return true
343
344 case ir.OCLOSURE:
345 if base.Debug.InlFuncsWithClosures == 0 {
346 v.reason = "not inlining functions with closures"
347 return true
348 }
349
350
351
352
353 v.budget -= 15
354
355
356
357 if doList(n.(*ir.ClosureExpr).Func.Body, v.do) {
358 return true
359 }
360
361 case ir.OSELECT,
362 ir.OGO,
363 ir.ODEFER,
364 ir.ODCLTYPE,
365 ir.OTAILCALL:
366 v.reason = "unhandled op " + n.Op().String()
367 return true
368
369 case ir.OAPPEND:
370 v.budget -= inlineExtraAppendCost
371
372 case ir.ODEREF:
373
374 n := n.(*ir.StarExpr)
375
376 ptr := n.X
377 for ptr.Op() == ir.OCONVNOP {
378 ptr = ptr.(*ir.ConvExpr).X
379 }
380 if ptr.Op() == ir.OADDR {
381 v.budget += 1
382 }
383
384 case ir.OCONVNOP:
385
386 v.budget++
387
388 case ir.ODCLCONST, ir.OFALL:
389
390 return false
391
392 case ir.OIF:
393 n := n.(*ir.IfStmt)
394 if ir.IsConst(n.Cond, constant.Bool) {
395
396 if doList(n.Init(), v.do) {
397 return true
398 }
399 if ir.BoolVal(n.Cond) {
400 return doList(n.Body, v.do)
401 } else {
402 return doList(n.Else, v.do)
403 }
404 }
405
406 case ir.ONAME:
407 n := n.(*ir.Name)
408 if n.Class == ir.PAUTO {
409 v.usedLocals.Add(n)
410 }
411
412 case ir.OBLOCK:
413
414
415
416 v.budget++
417
418 case ir.OMETHVALUE, ir.OSLICELIT:
419 v.budget--
420
421 case ir.OMETHEXPR:
422 v.budget++
423 }
424
425 v.budget--
426
427
428 if v.budget < 0 && base.Flag.LowerM < 2 && !logopt.Enabled() {
429 v.reason = "too expensive"
430 return true
431 }
432
433 return ir.DoChildren(n, v.do)
434 }
435
436 func isBigFunc(fn *ir.Func) bool {
437 budget := inlineBigFunctionNodes
438 return ir.Any(fn, func(n ir.Node) bool {
439 budget--
440 return budget <= 0
441 })
442 }
443
444
445
446
447 func inlcopylist(ll []ir.Node) []ir.Node {
448 s := make([]ir.Node, len(ll))
449 for i, n := range ll {
450 s[i] = inlcopy(n)
451 }
452 return s
453 }
454
455
456 func inlcopy(n ir.Node) ir.Node {
457 var edit func(ir.Node) ir.Node
458 edit = func(x ir.Node) ir.Node {
459 switch x.Op() {
460 case ir.ONAME, ir.OTYPE, ir.OLITERAL, ir.ONIL:
461 return x
462 }
463 m := ir.Copy(x)
464 ir.EditChildren(m, edit)
465 if x.Op() == ir.OCLOSURE {
466 x := x.(*ir.ClosureExpr)
467
468
469
470 oldfn := x.Func
471 newfn := ir.NewFunc(oldfn.Pos())
472 m.(*ir.ClosureExpr).Func = newfn
473 newfn.Nname = ir.NewNameAt(oldfn.Nname.Pos(), oldfn.Nname.Sym())
474
475 newfn.Nname.SetType(oldfn.Nname.Type())
476
477 if oldfn.Nname.Ntype != nil {
478 newfn.Nname.Ntype = inlcopy(oldfn.Nname.Ntype).(ir.Ntype)
479 }
480 newfn.Body = inlcopylist(oldfn.Body)
481
482 newfn.Dcl = append([]*ir.Name(nil), oldfn.Dcl...)
483 newfn.ClosureVars = append([]*ir.Name(nil), oldfn.ClosureVars...)
484 }
485 return m
486 }
487 return edit(n)
488 }
489
490
491
492 func InlineCalls(fn *ir.Func) {
493 savefn := ir.CurFunc
494 ir.CurFunc = fn
495 maxCost := int32(inlineMaxBudget)
496 if isBigFunc(fn) {
497 maxCost = inlineBigFunctionMaxCost
498 }
499
500
501
502
503
504
505 inlMap := make(map[*ir.Func]bool)
506 var edit func(ir.Node) ir.Node
507 edit = func(n ir.Node) ir.Node {
508 return inlnode(n, maxCost, inlMap, edit)
509 }
510 ir.EditChildren(fn, edit)
511 ir.CurFunc = savefn
512 }
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527 func inlnode(n ir.Node, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node {
528 if n == nil {
529 return n
530 }
531
532 switch n.Op() {
533 case ir.ODEFER, ir.OGO:
534 n := n.(*ir.GoDeferStmt)
535 switch call := n.Call; call.Op() {
536 case ir.OCALLMETH:
537 base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
538 case ir.OCALLFUNC:
539 call := call.(*ir.CallExpr)
540 call.NoInline = true
541 }
542 case ir.OTAILCALL:
543 n := n.(*ir.TailCallStmt)
544 n.Call.NoInline = true
545
546
547
548 case ir.OCLOSURE:
549 return n
550 case ir.OCALLMETH:
551 base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
552 case ir.OCALLFUNC:
553 n := n.(*ir.CallExpr)
554 if n.X.Op() == ir.OMETHEXPR {
555
556
557 if meth := ir.MethodExprName(n.X); meth != nil {
558 s := meth.Sym()
559 if base.Debug.Checkptr != 0 && types.IsReflectPkg(s.Pkg) && (s.Name == "Value.UnsafeAddr" || s.Name == "Value.Pointer") {
560 return n
561 }
562 }
563 }
564 }
565
566 lno := ir.SetPos(n)
567
568 ir.EditChildren(n, edit)
569
570
571
572
573 switch n.Op() {
574 case ir.OCALLMETH:
575 base.FatalfAt(n.Pos(), "OCALLMETH missed by typecheck")
576
577 case ir.OCALLFUNC:
578 call := n.(*ir.CallExpr)
579 if call.NoInline {
580 break
581 }
582 if base.Flag.LowerM > 3 {
583 fmt.Printf("%v:call to func %+v\n", ir.Line(n), call.X)
584 }
585 if ir.IsIntrinsicCall(call) {
586 break
587 }
588 if fn := inlCallee(call.X); fn != nil && typecheck.HaveInlineBody(fn) {
589 n = mkinlcall(call, fn, maxCost, inlMap, edit)
590 }
591 }
592
593 base.Pos = lno
594
595 return n
596 }
597
598
599
600 func inlCallee(fn ir.Node) *ir.Func {
601 fn = ir.StaticValue(fn)
602 switch fn.Op() {
603 case ir.OMETHEXPR:
604 fn := fn.(*ir.SelectorExpr)
605 n := ir.MethodExprName(fn)
606
607
608
609 if n == nil || !types.Identical(n.Type().Recv().Type, fn.X.Type()) {
610 return nil
611 }
612 return n.Func
613 case ir.ONAME:
614 fn := fn.(*ir.Name)
615 if fn.Class == ir.PFUNC {
616 return fn.Func
617 }
618 case ir.OCLOSURE:
619 fn := fn.(*ir.ClosureExpr)
620 c := fn.Func
621 CanInline(c)
622 return c
623 }
624 return nil
625 }
626
627 func inlParam(t *types.Field, as ir.InitNode, inlvars map[*ir.Name]*ir.Name) ir.Node {
628 if t.Nname == nil {
629 return ir.BlankNode
630 }
631 n := t.Nname.(*ir.Name)
632 if ir.IsBlank(n) {
633 return ir.BlankNode
634 }
635 inlvar := inlvars[n]
636 if inlvar == nil {
637 base.Fatalf("missing inlvar for %v", n)
638 }
639 as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, inlvar))
640 inlvar.Name().Defn = as
641 return inlvar
642 }
643
644 var inlgen int
645
646
647
648 var SSADumpInline = func(*ir.Func) {}
649
650
651
652
653 var NewInline = func(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr { return nil }
654
655
656
657
658
659
660
661
662 func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]bool, edit func(ir.Node) ir.Node) ir.Node {
663 if fn.Inl == nil {
664 if logopt.Enabled() {
665 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
666 fmt.Sprintf("%s cannot be inlined", ir.PkgFuncName(fn)))
667 }
668 return n
669 }
670 if fn.Inl.Cost > maxCost {
671
672
673 if logopt.Enabled() {
674 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
675 fmt.Sprintf("cost %d of %s exceeds max large caller cost %d", fn.Inl.Cost, ir.PkgFuncName(fn), maxCost))
676 }
677 return n
678 }
679
680 if fn == ir.CurFunc {
681
682 if logopt.Enabled() {
683 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", fmt.Sprintf("recursive call to %s", ir.FuncName(ir.CurFunc)))
684 }
685 return n
686 }
687
688
689
690
691
692
693
694
695
696
697 if !fn.Type().HasShape() {
698 for _, arg := range n.Args {
699 if arg.Type().HasShape() {
700 if logopt.Enabled() {
701 logopt.LogOpt(n.Pos(), "cannotInlineCall", "inline", ir.FuncName(ir.CurFunc),
702 fmt.Sprintf("inlining non-shape function %v with shape args", ir.FuncName(fn)))
703 }
704 return n
705 }
706 }
707 }
708
709 if base.Flag.Cfg.Instrumenting && types.IsRuntimePkg(fn.Sym().Pkg) {
710
711
712
713
714
715
716 return n
717 }
718
719 if inlMap[fn] {
720 if base.Flag.LowerM > 1 {
721 fmt.Printf("%v: cannot inline %v into %v: repeated recursive cycle\n", ir.Line(n), fn, ir.FuncName(ir.CurFunc))
722 }
723 return n
724 }
725 inlMap[fn] = true
726 defer func() {
727 inlMap[fn] = false
728 }()
729
730 typecheck.FixVariadicCall(n)
731
732 parent := base.Ctxt.PosTable.Pos(n.Pos()).Base().InliningIndex()
733
734 sym := fn.Linksym()
735 inlIndex := base.Ctxt.InlTree.Add(parent, n.Pos(), sym)
736
737 if base.Flag.GenDwarfInl > 0 {
738 if !sym.WasInlined() {
739 base.Ctxt.DwFixups.SetPrecursorFunc(sym, fn)
740 sym.Set(obj.AttrWasInlined, true)
741 }
742 }
743
744 if base.Flag.LowerM != 0 {
745 fmt.Printf("%v: inlining call to %v\n", ir.Line(n), fn)
746 }
747 if base.Flag.LowerM > 2 {
748 fmt.Printf("%v: Before inlining: %+v\n", ir.Line(n), n)
749 }
750
751 res := NewInline(n, fn, inlIndex)
752 if res == nil {
753 res = oldInline(n, fn, inlIndex)
754 }
755
756
757
758
759
760
761
762 ir.EditChildren(res, edit)
763
764 if base.Flag.LowerM > 2 {
765 fmt.Printf("%v: After inlining %+v\n\n", ir.Line(res), res)
766 }
767
768 return res
769 }
770
771
772 func CalleeEffects(init *ir.Nodes, callee ir.Node) {
773 for {
774 switch callee.Op() {
775 case ir.ONAME, ir.OCLOSURE, ir.OMETHEXPR:
776 return
777
778 case ir.OCONVNOP:
779 conv := callee.(*ir.ConvExpr)
780 init.Append(ir.TakeInit(conv)...)
781 callee = conv.X
782
783 case ir.OINLCALL:
784 ic := callee.(*ir.InlinedCallExpr)
785 init.Append(ir.TakeInit(ic)...)
786 init.Append(ic.Body.Take()...)
787 callee = ic.SingleResult()
788
789 default:
790 base.FatalfAt(callee.Pos(), "unexpected callee expression: %v", callee)
791 }
792 }
793 }
794
795
796
797
798
799 func oldInline(call *ir.CallExpr, fn *ir.Func, inlIndex int) *ir.InlinedCallExpr {
800 if base.Debug.TypecheckInl == 0 {
801 typecheck.ImportedBody(fn)
802 }
803
804 SSADumpInline(fn)
805
806 ninit := call.Init()
807
808
809
810
811 if call.Op() == ir.OCALLFUNC {
812 CalleeEffects(&ninit, call.X)
813 }
814
815
816 inlvars := make(map[*ir.Name]*ir.Name)
817
818
819 var inlfvars []*ir.Name
820
821 for _, ln := range fn.Inl.Dcl {
822 if ln.Op() != ir.ONAME {
823 continue
824 }
825 if ln.Class == ir.PPARAMOUT {
826 continue
827 }
828 inlf := typecheck.Expr(inlvar(ln)).(*ir.Name)
829 inlvars[ln] = inlf
830 if base.Flag.GenDwarfInl > 0 {
831 if ln.Class == ir.PPARAM {
832 inlf.Name().SetInlFormal(true)
833 } else {
834 inlf.Name().SetInlLocal(true)
835 }
836 inlf.SetPos(ln.Pos())
837 inlfvars = append(inlfvars, inlf)
838 }
839 }
840
841
842
843 var retvars []ir.Node
844 for i, t := range fn.Type().Results().Fields().Slice() {
845 var m *ir.Name
846 if nn := t.Nname; nn != nil && !ir.IsBlank(nn.(*ir.Name)) && !strings.HasPrefix(nn.Sym().Name, "~r") {
847 n := nn.(*ir.Name)
848 m = inlvar(n)
849 m = typecheck.Expr(m).(*ir.Name)
850 inlvars[n] = m
851 } else {
852
853 m = retvar(t, i)
854 }
855
856 if base.Flag.GenDwarfInl > 0 {
857
858
859
860 if !strings.HasPrefix(m.Sym().Name, "~R") {
861 m.Name().SetInlFormal(true)
862 m.SetPos(t.Pos)
863 inlfvars = append(inlfvars, m)
864 }
865 }
866
867 retvars = append(retvars, m)
868 }
869
870
871 as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
872 as.Def = true
873 if call.Op() == ir.OCALLMETH {
874 base.FatalfAt(call.Pos(), "OCALLMETH missed by typecheck")
875 }
876 as.Rhs.Append(call.Args...)
877
878 if recv := fn.Type().Recv(); recv != nil {
879 as.Lhs.Append(inlParam(recv, as, inlvars))
880 }
881 for _, param := range fn.Type().Params().Fields().Slice() {
882 as.Lhs.Append(inlParam(param, as, inlvars))
883 }
884
885 if len(as.Rhs) != 0 {
886 ninit.Append(typecheck.Stmt(as))
887 }
888
889 if !fn.Inl.CanDelayResults {
890
891 for _, n := range retvars {
892 ninit.Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
893 ras := ir.NewAssignStmt(base.Pos, n, nil)
894 ninit.Append(typecheck.Stmt(ras))
895 }
896 }
897
898 retlabel := typecheck.AutoLabel(".i")
899
900 inlgen++
901
902
903
904
905
906
907 ninit.Append(ir.NewInlineMarkStmt(call.Pos().WithIsStmt(), int64(inlIndex)))
908
909 subst := inlsubst{
910 retlabel: retlabel,
911 retvars: retvars,
912 inlvars: inlvars,
913 defnMarker: ir.NilExpr{},
914 bases: make(map[*src.PosBase]*src.PosBase),
915 newInlIndex: inlIndex,
916 fn: fn,
917 }
918 subst.edit = subst.node
919
920 body := subst.list(ir.Nodes(fn.Inl.Body))
921
922 lab := ir.NewLabelStmt(base.Pos, retlabel)
923 body = append(body, lab)
924
925 if !typecheck.Go117ExportTypes {
926 typecheck.Stmts(body)
927 }
928
929 if base.Flag.GenDwarfInl > 0 {
930 for _, v := range inlfvars {
931 v.SetPos(subst.updatedPos(v.Pos()))
932 }
933 }
934
935
936
937 res := ir.NewInlinedCallExpr(base.Pos, body, retvars)
938 res.SetInit(ninit)
939 res.SetType(call.Type())
940 res.SetTypecheck(1)
941 return res
942 }
943
944
945
946
947 func inlvar(var_ *ir.Name) *ir.Name {
948 if base.Flag.LowerM > 3 {
949 fmt.Printf("inlvar %+v\n", var_)
950 }
951
952 n := typecheck.NewName(var_.Sym())
953 n.SetType(var_.Type())
954 n.SetTypecheck(1)
955 n.Class = ir.PAUTO
956 n.SetUsed(true)
957 n.SetAutoTemp(var_.AutoTemp())
958 n.Curfn = ir.CurFunc
959 n.SetAddrtaken(var_.Addrtaken())
960
961 ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
962 return n
963 }
964
965
966 func retvar(t *types.Field, i int) *ir.Name {
967 n := typecheck.NewName(typecheck.LookupNum("~R", i))
968 n.SetType(t.Type)
969 n.SetTypecheck(1)
970 n.Class = ir.PAUTO
971 n.SetUsed(true)
972 n.Curfn = ir.CurFunc
973 ir.CurFunc.Dcl = append(ir.CurFunc.Dcl, n)
974 return n
975 }
976
977
978
979 type inlsubst struct {
980
981 retlabel *types.Sym
982
983
984 retvars []ir.Node
985
986 inlvars map[*ir.Name]*ir.Name
987
988
989
990 defnMarker ir.NilExpr
991
992
993
994 bases map[*src.PosBase]*src.PosBase
995
996
997
998 newInlIndex int
999
1000 edit func(ir.Node) ir.Node
1001
1002
1003
1004 newclofn *ir.Func
1005
1006 fn *ir.Func
1007
1008
1009
1010 noPosUpdate bool
1011 }
1012
1013
1014 func (subst *inlsubst) list(ll ir.Nodes) []ir.Node {
1015 s := make([]ir.Node, 0, len(ll))
1016 for _, n := range ll {
1017 s = append(s, subst.node(n))
1018 }
1019 return s
1020 }
1021
1022
1023
1024
1025 func (subst *inlsubst) fields(oldt *types.Type) []*types.Field {
1026 oldfields := oldt.FieldSlice()
1027 newfields := make([]*types.Field, len(oldfields))
1028 for i := range oldfields {
1029 newfields[i] = oldfields[i].Copy()
1030 if oldfields[i].Nname != nil {
1031 newfields[i].Nname = subst.node(oldfields[i].Nname.(*ir.Name))
1032 }
1033 }
1034 return newfields
1035 }
1036
1037
1038
1039 func (subst *inlsubst) clovar(n *ir.Name) *ir.Name {
1040 m := ir.NewNameAt(n.Pos(), n.Sym())
1041 m.Class = n.Class
1042 m.SetType(n.Type())
1043 m.SetTypecheck(1)
1044 if n.IsClosureVar() {
1045 m.SetIsClosureVar(true)
1046 }
1047 if n.Addrtaken() {
1048 m.SetAddrtaken(true)
1049 }
1050 if n.Used() {
1051 m.SetUsed(true)
1052 }
1053 m.Defn = n.Defn
1054
1055 m.Curfn = subst.newclofn
1056
1057 switch defn := n.Defn.(type) {
1058 case nil:
1059
1060 case *ir.Name:
1061 if !n.IsClosureVar() {
1062 base.FatalfAt(n.Pos(), "want closure variable, got: %+v", n)
1063 }
1064 if n.Sym().Pkg != types.LocalPkg {
1065
1066
1067
1068
1069
1070
1071
1072
1073 m.SetSym(types.LocalPkg.Lookup(n.Sym().Name))
1074 }
1075
1076
1077
1078
1079 if subst.inlvars[n.Defn.(*ir.Name)] != nil {
1080 m.Defn = subst.node(n.Defn)
1081 }
1082 case *ir.AssignStmt, *ir.AssignListStmt:
1083
1084 m.Defn = &subst.defnMarker
1085 case *ir.TypeSwitchGuard:
1086
1087 case *ir.RangeStmt:
1088
1089 default:
1090 base.FatalfAt(n.Pos(), "unexpected Defn: %+v", defn)
1091 }
1092
1093 if n.Outer != nil {
1094
1095
1096
1097
1098
1099 s := subst.node(n.Outer).(*ir.Name)
1100 if s == n.Outer {
1101 s = n.Outer.Outer
1102 }
1103 m.Outer = s
1104 }
1105 return m
1106 }
1107
1108
1109
1110 func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node {
1111
1112
1113
1114
1115
1116
1117
1118
1119 newClosurePos := subst.updatedPos(n.Pos())
1120 defer func(prev bool) { subst.noPosUpdate = prev }(subst.noPosUpdate)
1121 subst.noPosUpdate = true
1122
1123
1124
1125 oldfn := n.Func
1126 newfn := ir.NewClosureFunc(oldfn.Pos(), true)
1127
1128
1129 if oldfn.Nname.Ntype != nil {
1130 newfn.Nname.Ntype = subst.node(oldfn.Nname.Ntype).(ir.Ntype)
1131 }
1132
1133 if subst.newclofn != nil {
1134
1135 }
1136 prevxfunc := subst.newclofn
1137
1138
1139
1140
1141 subst.newclofn = newfn
1142 newfn.Dcl = nil
1143 newfn.ClosureVars = nil
1144 for _, oldv := range oldfn.Dcl {
1145 newv := subst.clovar(oldv)
1146 subst.inlvars[oldv] = newv
1147 newfn.Dcl = append(newfn.Dcl, newv)
1148 }
1149 for _, oldv := range oldfn.ClosureVars {
1150 newv := subst.clovar(oldv)
1151 subst.inlvars[oldv] = newv
1152 newfn.ClosureVars = append(newfn.ClosureVars, newv)
1153 }
1154
1155
1156
1157 oldt := oldfn.Type()
1158 newrecvs := subst.fields(oldt.Recvs())
1159 var newrecv *types.Field
1160 if len(newrecvs) > 0 {
1161 newrecv = newrecvs[0]
1162 }
1163 newt := types.NewSignature(oldt.Pkg(), newrecv,
1164 nil, subst.fields(oldt.Params()), subst.fields(oldt.Results()))
1165
1166 newfn.Nname.SetType(newt)
1167 newfn.Body = subst.list(oldfn.Body)
1168
1169
1170 for _, oldv := range oldfn.Dcl {
1171 delete(subst.inlvars, oldv)
1172 }
1173 for _, oldv := range oldfn.ClosureVars {
1174 delete(subst.inlvars, oldv)
1175 }
1176
1177 subst.newclofn = prevxfunc
1178
1179
1180
1181 newclo := newfn.OClosure
1182 newclo.SetPos(newClosurePos)
1183 newclo.SetInit(subst.list(n.Init()))
1184 return typecheck.Expr(newclo)
1185 }
1186
1187
1188
1189
1190
1191 func (subst *inlsubst) node(n ir.Node) ir.Node {
1192 if n == nil {
1193 return nil
1194 }
1195
1196 switch n.Op() {
1197 case ir.ONAME:
1198 n := n.(*ir.Name)
1199
1200
1201 if n.IsClosureVar() && subst.newclofn == nil {
1202 o := n.Outer
1203
1204
1205
1206
1207 if o.Curfn != ir.CurFunc {
1208 o = o.Outer
1209 }
1210
1211
1212 if o == nil || o.Curfn != ir.CurFunc {
1213 base.Fatalf("%v: unresolvable capture %v\n", ir.Line(n), n)
1214 }
1215
1216 if base.Flag.LowerM > 2 {
1217 fmt.Printf("substituting captured name %+v -> %+v\n", n, o)
1218 }
1219 return o
1220 }
1221
1222 if inlvar := subst.inlvars[n]; inlvar != nil {
1223 if base.Flag.LowerM > 2 {
1224 fmt.Printf("substituting name %+v -> %+v\n", n, inlvar)
1225 }
1226 return inlvar
1227 }
1228
1229 if base.Flag.LowerM > 2 {
1230 fmt.Printf("not substituting name %+v\n", n)
1231 }
1232 return n
1233
1234 case ir.OMETHEXPR:
1235 n := n.(*ir.SelectorExpr)
1236 return n
1237
1238 case ir.OLITERAL, ir.ONIL, ir.OTYPE:
1239
1240
1241
1242 if n.Sym() != nil {
1243 return n
1244 }
1245
1246 case ir.ORETURN:
1247 if subst.newclofn != nil {
1248
1249 break
1250 }
1251
1252
1253 n := n.(*ir.ReturnStmt)
1254 init := subst.list(n.Init())
1255 if len(subst.retvars) != 0 && len(n.Results) != 0 {
1256 as := ir.NewAssignListStmt(base.Pos, ir.OAS2, nil, nil)
1257
1258
1259
1260
1261 for _, n := range subst.retvars {
1262 as.Lhs.Append(n)
1263 }
1264 as.Rhs = subst.list(n.Results)
1265
1266 if subst.fn.Inl.CanDelayResults {
1267 for _, n := range as.Lhs {
1268 as.PtrInit().Append(ir.NewDecl(base.Pos, ir.ODCL, n.(*ir.Name)))
1269 n.Name().Defn = as
1270 }
1271 }
1272
1273 init = append(init, typecheck.Stmt(as))
1274 }
1275 init = append(init, ir.NewBranchStmt(base.Pos, ir.OGOTO, subst.retlabel))
1276 typecheck.Stmts(init)
1277 return ir.NewBlockStmt(base.Pos, init)
1278
1279 case ir.OGOTO, ir.OBREAK, ir.OCONTINUE:
1280 if subst.newclofn != nil {
1281
1282 break
1283 }
1284 n := n.(*ir.BranchStmt)
1285 m := ir.Copy(n).(*ir.BranchStmt)
1286 m.SetPos(subst.updatedPos(m.Pos()))
1287 m.SetInit(nil)
1288 m.Label = translateLabel(n.Label)
1289 return m
1290
1291 case ir.OLABEL:
1292 if subst.newclofn != nil {
1293
1294 break
1295 }
1296 n := n.(*ir.LabelStmt)
1297 m := ir.Copy(n).(*ir.LabelStmt)
1298 m.SetPos(subst.updatedPos(m.Pos()))
1299 m.SetInit(nil)
1300 m.Label = translateLabel(n.Label)
1301 return m
1302
1303 case ir.OCLOSURE:
1304 return subst.closure(n.(*ir.ClosureExpr))
1305
1306 }
1307
1308 m := ir.Copy(n)
1309 m.SetPos(subst.updatedPos(m.Pos()))
1310 ir.EditChildren(m, subst.edit)
1311
1312 if subst.newclofn == nil {
1313
1314 switch m.Op() {
1315 case ir.OFOR:
1316 m := m.(*ir.ForStmt)
1317 m.Label = translateLabel(m.Label)
1318 return m
1319
1320 case ir.ORANGE:
1321 m := m.(*ir.RangeStmt)
1322 m.Label = translateLabel(m.Label)
1323 return m
1324
1325 case ir.OSWITCH:
1326 m := m.(*ir.SwitchStmt)
1327 m.Label = translateLabel(m.Label)
1328 return m
1329 }
1330
1331 }
1332
1333 switch m := m.(type) {
1334 case *ir.AssignStmt:
1335 if lhs, ok := m.X.(*ir.Name); ok && lhs.Defn == &subst.defnMarker {
1336 lhs.Defn = m
1337 }
1338 case *ir.AssignListStmt:
1339 for _, lhs := range m.Lhs {
1340 if lhs, ok := lhs.(*ir.Name); ok && lhs.Defn == &subst.defnMarker {
1341 lhs.Defn = m
1342 }
1343 }
1344 }
1345
1346 return m
1347 }
1348
1349
1350
1351 func translateLabel(l *types.Sym) *types.Sym {
1352 if l == nil {
1353 return nil
1354 }
1355 p := fmt.Sprintf("%s·%d", l.Name, inlgen)
1356 return typecheck.Lookup(p)
1357 }
1358
1359 func (subst *inlsubst) updatedPos(xpos src.XPos) src.XPos {
1360 if subst.noPosUpdate {
1361 return xpos
1362 }
1363 pos := base.Ctxt.PosTable.Pos(xpos)
1364 oldbase := pos.Base()
1365 newbase := subst.bases[oldbase]
1366 if newbase == nil {
1367 newbase = src.NewInliningBase(oldbase, subst.newInlIndex)
1368 subst.bases[oldbase] = newbase
1369 }
1370 pos.SetBase(newbase)
1371 return base.Ctxt.PosTable.XPos(pos)
1372 }
1373
1374 func pruneUnusedAutos(ll []*ir.Name, vis *hairyVisitor) []*ir.Name {
1375 s := make([]*ir.Name, 0, len(ll))
1376 for _, n := range ll {
1377 if n.Class == ir.PAUTO {
1378 if !vis.usedLocals.Has(n) {
1379 continue
1380 }
1381 }
1382 s = append(s, n)
1383 }
1384 return s
1385 }
1386
1387
1388 func numNonClosures(list []*ir.Func) int {
1389 count := 0
1390 for _, fn := range list {
1391 if fn.OClosure == nil {
1392 count++
1393 }
1394 }
1395 return count
1396 }
1397
1398 func doList(list []ir.Node, do func(ir.Node) bool) bool {
1399 for _, x := range list {
1400 if x != nil {
1401 if do(x) {
1402 return true
1403 }
1404 }
1405 }
1406 return false
1407 }
1408
View as plain text