1
2
3
4
5 package staticinit
6
7 import (
8 "fmt"
9 "go/constant"
10 "go/token"
11 "os"
12 "slices"
13 "strings"
14
15 "cmd/compile/internal/base"
16 "cmd/compile/internal/ir"
17 "cmd/compile/internal/reflectdata"
18 "cmd/compile/internal/staticdata"
19 "cmd/compile/internal/typecheck"
20 "cmd/compile/internal/types"
21 "cmd/internal/obj"
22 "cmd/internal/objabi"
23 "cmd/internal/src"
24 )
25
26 type Entry struct {
27 Xoffset int64
28 Expr ir.Node
29 }
30
31 type Plan struct {
32 E []Entry
33 }
34
35
36
37
38
39 type Schedule struct {
40
41
42 Out []ir.Node
43
44 Plans map[ir.Node]*Plan
45 Temps map[ir.Node]*ir.Name
46
47
48
49
50 seenMutation bool
51 }
52
53 func (s *Schedule) append(n ir.Node) {
54 s.Out = append(s.Out, n)
55 }
56
57
58 func (s *Schedule) StaticInit(n ir.Node) {
59 if !s.tryStaticInit(n) {
60 if base.Flag.Percent != 0 {
61 ir.Dump("StaticInit failed", n)
62 }
63 s.append(n)
64 }
65 }
66
67
68
69
70 var varToMapInit map[*ir.Name]*ir.Func
71
72
73
74
75 var MapInitToVar map[*ir.Func]*ir.Name
76
77
78
79
80 func recordFuncForVar(v *ir.Name, fn *ir.Func) {
81 if varToMapInit == nil {
82 varToMapInit = make(map[*ir.Name]*ir.Func)
83 MapInitToVar = make(map[*ir.Func]*ir.Name)
84 }
85 varToMapInit[v] = fn
86 MapInitToVar[fn] = v
87 }
88
89
90 func allBlank(exprs []ir.Node) bool {
91 for _, expr := range exprs {
92 if !ir.IsBlank(expr) {
93 return false
94 }
95 }
96 return true
97 }
98
99
100
101 func (s *Schedule) tryStaticInit(n ir.Node) bool {
102 var lhs []ir.Node
103 var rhs ir.Node
104
105 switch n.Op() {
106 default:
107 base.FatalfAt(n.Pos(), "unexpected initialization statement: %v", n)
108 case ir.OAS:
109 n := n.(*ir.AssignStmt)
110 lhs, rhs = []ir.Node{n.X}, n.Y
111 case ir.OAS2:
112
113
114
115 n := n.(*ir.AssignListStmt)
116 for _, rhs := range n.Rhs {
117 for rhs.Op() == ir.OCONVNOP || rhs.Op() == ir.OCONVIFACE {
118 rhs = rhs.(*ir.ConvExpr).X
119 }
120 if name, ok := rhs.(*ir.Name); !ok || !name.AutoTemp() {
121 base.FatalfAt(n.Pos(), "unexpected rhs, not an autotmp: %+v", rhs)
122 }
123 }
124 return false
125 case ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
126 n := n.(*ir.AssignListStmt)
127 if len(n.Lhs) < 2 || len(n.Rhs) != 1 {
128 base.FatalfAt(n.Pos(), "unexpected shape for %v: %v", n.Op(), n)
129 }
130 lhs, rhs = n.Lhs, n.Rhs[0]
131 case ir.OCALLFUNC:
132 return false
133 }
134
135 if !s.seenMutation {
136 s.seenMutation = mayModifyPkgVar(rhs)
137 }
138
139 if allBlank(lhs) && !AnySideEffects(rhs) {
140 return true
141 }
142
143
144
145 if len(lhs) > 1 {
146 return false
147 }
148
149 lno := ir.SetPos(n)
150 defer func() { base.Pos = lno }()
151
152 nam := lhs[0].(*ir.Name)
153 return s.StaticAssign(nam, 0, rhs, nam.Type())
154 }
155
156
157
158 func (s *Schedule) staticcopy(l *ir.Name, loff int64, rn *ir.Name, typ *types.Type) bool {
159 if rn.Class == ir.PFUNC {
160
161 staticdata.InitAddr(l, loff, staticdata.FuncLinksym(rn))
162 return true
163 }
164 if rn.Class != ir.PEXTERN || rn.Sym().Pkg != types.LocalPkg {
165 return false
166 }
167 if rn.Defn == nil {
168
169
170 return false
171 }
172 if rn.Defn.Op() != ir.OAS {
173 return false
174 }
175 if rn.Type().IsString() {
176 return false
177 }
178 if rn.Embed != nil {
179 return false
180 }
181 orig := rn
182 r := rn.Defn.(*ir.AssignStmt).Y
183 if r == nil {
184
185 base.Fatalf("unexpected initializer: %v", rn.Defn)
186 }
187
188
189
190 if s.seenMutation {
191 if base.Debug.StaticCopy != 0 {
192 base.WarnfAt(l.Pos(), "skipping static copy of %v+%v with %v", l, loff, r)
193 }
194 return false
195 }
196
197 for r.Op() == ir.OCONVNOP && !types.Identical(r.Type(), typ) {
198 r = r.(*ir.ConvExpr).X
199 }
200
201 switch r.Op() {
202 case ir.OMETHEXPR:
203 r = r.(*ir.SelectorExpr).FuncName()
204 fallthrough
205 case ir.ONAME:
206 r := r.(*ir.Name)
207 if s.staticcopy(l, loff, r, typ) {
208 return true
209 }
210
211
212 dst := ir.Node(l)
213 if loff != 0 || !types.Identical(typ, l.Type()) {
214 dst = ir.NewNameOffsetExpr(base.Pos, l, loff, typ)
215 }
216 s.append(ir.NewAssignStmt(base.Pos, dst, typecheck.Conv(r, typ)))
217 return true
218
219 case ir.ONIL:
220 return true
221
222 case ir.OLITERAL:
223 if ir.IsZero(r) {
224 return true
225 }
226 staticdata.InitConst(l, loff, r, int(typ.Size()))
227 return true
228
229 case ir.OADDR:
230 r := r.(*ir.AddrExpr)
231 if a, ok := r.X.(*ir.Name); ok && a.Op() == ir.ONAME {
232 if a.Class != ir.PEXTERN {
233 return false
234 }
235 staticdata.InitAddr(l, loff, staticdata.GlobalLinksym(a))
236 return true
237 }
238
239 case ir.OPTRLIT:
240 r := r.(*ir.AddrExpr)
241 switch r.X.Op() {
242 case ir.OARRAYLIT, ir.OSLICELIT, ir.OSTRUCTLIT, ir.OMAPLIT:
243
244 staticdata.InitAddr(l, loff, staticdata.GlobalLinksym(s.Temps[r]))
245 return true
246 }
247
248 case ir.OSLICELIT:
249 r := r.(*ir.CompLitExpr)
250
251 staticdata.InitSlice(l, loff, staticdata.GlobalLinksym(s.Temps[r]), r.Len)
252 return true
253
254 case ir.OARRAYLIT, ir.OSTRUCTLIT:
255 r := r.(*ir.CompLitExpr)
256 p := s.Plans[r]
257 for i := range p.E {
258 e := &p.E[i]
259 typ := e.Expr.Type()
260 if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL {
261 staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(typ.Size()))
262 continue
263 }
264 x := e.Expr
265 if x.Op() == ir.OMETHEXPR {
266 x = x.(*ir.SelectorExpr).FuncName()
267 }
268 if x.Op() == ir.ONAME && s.staticcopy(l, loff+e.Xoffset, x.(*ir.Name), typ) {
269 continue
270 }
271
272
273 ll := ir.NewNameOffsetExpr(base.Pos, l, loff+e.Xoffset, typ)
274 rr := ir.NewNameOffsetExpr(base.Pos, orig, e.Xoffset, typ)
275 ir.SetPos(rr)
276 s.append(ir.NewAssignStmt(base.Pos, ll, rr))
277 }
278
279 return true
280 }
281
282 return false
283 }
284
285 func (s *Schedule) StaticAssign(l *ir.Name, loff int64, r ir.Node, typ *types.Type) bool {
286
287
288
289
290
291
292 disableGlobalAddrs := base.Ctxt.IsFIPS()
293
294 if r == nil {
295
296
297 return true
298 }
299 for r.Op() == ir.OCONVNOP {
300 r = r.(*ir.ConvExpr).X
301 }
302
303 assign := func(pos src.XPos, a *ir.Name, aoff int64, v ir.Node) {
304 if s.StaticAssign(a, aoff, v, v.Type()) {
305 return
306 }
307 var lhs ir.Node
308 if ir.IsBlank(a) {
309
310 lhs = ir.BlankNode
311 } else {
312 lhs = ir.NewNameOffsetExpr(pos, a, aoff, v.Type())
313 }
314 s.append(ir.NewAssignStmt(pos, lhs, v))
315 }
316
317 switch r.Op() {
318 case ir.ONAME:
319 if disableGlobalAddrs {
320 return false
321 }
322 r := r.(*ir.Name)
323 return s.staticcopy(l, loff, r, typ)
324
325 case ir.OMETHEXPR:
326 if disableGlobalAddrs {
327 return false
328 }
329 r := r.(*ir.SelectorExpr)
330 return s.staticcopy(l, loff, r.FuncName(), typ)
331
332 case ir.ONIL:
333 return true
334
335 case ir.OLITERAL:
336 if ir.IsZero(r) {
337 return true
338 }
339 if disableGlobalAddrs && r.Type().IsString() {
340 return false
341 }
342 staticdata.InitConst(l, loff, r, int(typ.Size()))
343 return true
344
345 case ir.OADDR:
346 if disableGlobalAddrs {
347 return false
348 }
349 r := r.(*ir.AddrExpr)
350 if name, offset, ok := StaticLoc(r.X); ok && name.Class == ir.PEXTERN {
351 staticdata.InitAddrOffset(l, loff, name.Linksym(), offset)
352 return true
353 }
354 fallthrough
355
356 case ir.OPTRLIT:
357 if disableGlobalAddrs {
358 return false
359 }
360 r := r.(*ir.AddrExpr)
361 switch r.X.Op() {
362 case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT, ir.OSTRUCTLIT:
363
364 a := StaticName(r.X.Type())
365
366 s.Temps[r] = a
367 staticdata.InitAddr(l, loff, a.Linksym())
368
369
370 assign(base.Pos, a, 0, r.X)
371 return true
372 }
373
374
375 case ir.OSTR2BYTES:
376 if disableGlobalAddrs {
377 return false
378 }
379 r := r.(*ir.ConvExpr)
380 if l.Class == ir.PEXTERN && r.X.Op() == ir.OLITERAL {
381 sval := ir.StringVal(r.X)
382 staticdata.InitSliceBytes(l, loff, sval)
383 return true
384 }
385
386 case ir.OSLICELIT:
387 if disableGlobalAddrs {
388 return false
389 }
390 r := r.(*ir.CompLitExpr)
391 s.initplan(r)
392
393 ta := types.NewArray(r.Type().Elem(), r.Len)
394 ta.SetNoalg(true)
395 a := StaticName(ta)
396 s.Temps[r] = a
397 staticdata.InitSlice(l, loff, a.Linksym(), r.Len)
398
399 l = a
400 loff = 0
401 fallthrough
402
403 case ir.OARRAYLIT, ir.OSTRUCTLIT:
404 r := r.(*ir.CompLitExpr)
405 s.initplan(r)
406
407 p := s.Plans[r]
408 for i := range p.E {
409 e := &p.E[i]
410 if e.Expr.Op() == ir.OLITERAL && !disableGlobalAddrs || e.Expr.Op() == ir.ONIL {
411 staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(e.Expr.Type().Size()))
412 continue
413 }
414 ir.SetPos(e.Expr)
415 assign(base.Pos, l, loff+e.Xoffset, e.Expr)
416 }
417
418 return true
419
420 case ir.OMAPLIT:
421 break
422
423 case ir.OCLOSURE:
424 if disableGlobalAddrs {
425 return false
426 }
427 r := r.(*ir.ClosureExpr)
428 if !r.Func.IsClosure() {
429 if base.Debug.Closure > 0 {
430 base.WarnfAt(r.Pos(), "closure converted to global")
431 }
432
433
434
435 staticdata.InitAddr(l, loff, staticdata.FuncLinksym(r.Func.Nname))
436 return true
437 }
438 ir.ClosureDebugRuntimeCheck(r)
439
440 case ir.OCONVIFACE:
441
442
443
444 if disableGlobalAddrs {
445 return false
446 }
447
448
449 r := r.(*ir.ConvExpr)
450 val := ir.Node(r)
451 for val.Op() == ir.OCONVIFACE {
452 val = val.(*ir.ConvExpr).X
453 }
454
455 if val.Type().IsInterface() {
456
457
458
459
460
461 return val.Op() == ir.ONIL
462 }
463
464 if val.Type().HasShape() {
465
466 return false
467 }
468
469 reflectdata.MarkTypeUsedInInterface(val.Type(), l.Linksym())
470
471 var itab *ir.AddrExpr
472 if typ.IsEmptyInterface() {
473 itab = reflectdata.TypePtrAt(base.Pos, val.Type())
474 } else {
475 itab = reflectdata.ITabAddrAt(base.Pos, val.Type(), typ)
476 }
477
478
479
480
481 staticdata.InitAddr(l, loff, itab.X.(*ir.LinksymOffsetExpr).Linksym)
482
483
484 if types.IsDirectIface(val.Type()) {
485 if val.Op() == ir.ONIL {
486
487 return true
488 }
489
490 ir.SetPos(val)
491 assign(base.Pos, l, loff+int64(types.PtrSize), val)
492 } else {
493
494 a := StaticName(val.Type())
495 s.Temps[val] = a
496 assign(base.Pos, a, 0, val)
497 staticdata.InitAddr(l, loff+int64(types.PtrSize), a.Linksym())
498 }
499
500 return true
501
502 case ir.OINLCALL:
503 if disableGlobalAddrs {
504 return false
505 }
506 r := r.(*ir.InlinedCallExpr)
507 return s.staticAssignInlinedCall(l, loff, r, typ)
508 }
509
510 if base.Flag.Percent != 0 {
511 ir.Dump("not static", r)
512 }
513 return false
514 }
515
516 func (s *Schedule) initplan(n ir.Node) {
517 if s.Plans[n] != nil {
518 return
519 }
520 p := new(Plan)
521 s.Plans[n] = p
522 switch n.Op() {
523 default:
524 base.Fatalf("initplan")
525
526 case ir.OARRAYLIT, ir.OSLICELIT:
527 n := n.(*ir.CompLitExpr)
528 var k int64
529 for _, a := range n.List {
530 if a.Op() == ir.OKEY {
531 kv := a.(*ir.KeyExpr)
532 k = typecheck.IndexConst(kv.Key)
533 a = kv.Value
534 }
535 s.addvalue(p, k*n.Type().Elem().Size(), a)
536 k++
537 }
538
539 case ir.OSTRUCTLIT:
540 n := n.(*ir.CompLitExpr)
541 for _, a := range n.List {
542 if a.Op() != ir.OSTRUCTKEY {
543 base.Fatalf("initplan structlit")
544 }
545 a := a.(*ir.StructKeyExpr)
546 if a.Sym().IsBlank() {
547 continue
548 }
549 s.addvalue(p, a.Field.Offset, a.Value)
550 }
551
552 case ir.OMAPLIT:
553 n := n.(*ir.CompLitExpr)
554 for _, a := range n.List {
555 if a.Op() != ir.OKEY {
556 base.Fatalf("initplan maplit")
557 }
558 a := a.(*ir.KeyExpr)
559 s.addvalue(p, -1, a.Value)
560 }
561 }
562 }
563
564 func (s *Schedule) addvalue(p *Plan, xoffset int64, n ir.Node) {
565
566 if ir.IsZero(n) {
567 return
568 }
569
570
571 if isvaluelit(n) {
572 s.initplan(n)
573 q := s.Plans[n]
574 for _, qe := range q.E {
575
576 qe.Xoffset += xoffset
577 p.E = append(p.E, qe)
578 }
579 return
580 }
581
582
583 p.E = append(p.E, Entry{Xoffset: xoffset, Expr: n})
584 }
585
586 func (s *Schedule) staticAssignInlinedCall(l *ir.Name, loff int64, call *ir.InlinedCallExpr, typ *types.Type) bool {
587 if base.Debug.InlStaticInit == 0 {
588 return false
589 }
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647 init := call.Init()
648 if len(init) != 2 || init[0].Op() != ir.OAS2 || init[1].Op() != ir.OINLMARK {
649 return false
650 }
651 as2init := init[0].(*ir.AssignListStmt)
652
653 if len(call.Body) != 2 || call.Body[0].Op() != ir.OBLOCK || call.Body[1].Op() != ir.OLABEL {
654 return false
655 }
656 label := call.Body[1].(*ir.LabelStmt).Label
657 block := call.Body[0].(*ir.BlockStmt)
658 list := block.List
659 if len(list) != 3 ||
660 list[0].Op() != ir.ODCL ||
661 list[1].Op() != ir.OAS2 ||
662 list[2].Op() != ir.OGOTO ||
663 list[2].(*ir.BranchStmt).Label != label {
664 return false
665 }
666 dcl := list[0].(*ir.Decl)
667 as2body := list[1].(*ir.AssignListStmt)
668 if len(as2body.Lhs) != 1 || as2body.Lhs[0] != dcl.X {
669 return false
670 }
671
672
673 for _, v := range as2init.Lhs {
674 if v.(*ir.Name).Addrtaken() {
675 return false
676 }
677 }
678
679 for _, r := range as2init.Rhs {
680 if AnySideEffects(r) {
681 return false
682 }
683 }
684
685
686
687 count := make(map[*ir.Name]int)
688 for _, x := range as2init.Lhs {
689 count[x.(*ir.Name)] = 0
690 }
691
692 hasClosure := false
693 ir.Visit(as2body.Rhs[0], func(n ir.Node) {
694 if name, ok := n.(*ir.Name); ok {
695 if c, ok := count[name]; ok {
696 count[name] = c + 1
697 }
698 }
699 if clo, ok := n.(*ir.ClosureExpr); ok {
700 hasClosure = hasClosure || clo.Func.IsClosure()
701 }
702 })
703
704
705
706 if hasClosure {
707 return false
708 }
709
710 for name, c := range count {
711 if c > 1 {
712
713
714
715 for i, n := range as2init.Lhs {
716 if n == name && !canRepeat(as2init.Rhs[i]) {
717 return false
718 }
719 }
720 }
721 }
722
723
724
725 args := make(map[*ir.Name]ir.Node)
726 for i, v := range as2init.Lhs {
727 if ir.IsBlank(v) {
728 continue
729 }
730 args[v.(*ir.Name)] = as2init.Rhs[i]
731 }
732 r, ok := subst(as2body.Rhs[0], args)
733 if !ok {
734 return false
735 }
736 ok = s.StaticAssign(l, loff, r, typ)
737
738 if ok && base.Flag.Percent != 0 {
739 ir.Dump("static inlined-LEFT", l)
740 ir.Dump("static inlined-ORIG", call)
741 ir.Dump("static inlined-RIGHT", r)
742 }
743 return ok
744 }
745
746
747
748
749
750
751
752 var statuniqgen int
753
754
755 func StaticName(t *types.Type) *ir.Name {
756
757 sym := typecheck.Lookup(fmt.Sprintf("%s%d", obj.StaticNamePrefix, statuniqgen))
758 statuniqgen++
759
760 n := ir.NewNameAt(base.Pos, sym, t)
761 sym.Def = n
762
763 n.Class = ir.PEXTERN
764 typecheck.Target.Externs = append(typecheck.Target.Externs, n)
765
766 n.Linksym().Set(obj.AttrStatic, true)
767 n.Linksym().Align = int16(t.Alignment())
768
769 return n
770 }
771
772
773 func StaticLoc(n ir.Node) (name *ir.Name, offset int64, ok bool) {
774 if n == nil {
775 return nil, 0, false
776 }
777
778 switch n.Op() {
779 case ir.ONAME:
780 n := n.(*ir.Name)
781 return n, 0, true
782
783 case ir.OMETHEXPR:
784 n := n.(*ir.SelectorExpr)
785 return StaticLoc(n.FuncName())
786
787 case ir.ODOT:
788 n := n.(*ir.SelectorExpr)
789 if name, offset, ok = StaticLoc(n.X); !ok {
790 break
791 }
792 offset += n.Offset()
793 return name, offset, true
794
795 case ir.OINDEX:
796 n := n.(*ir.IndexExpr)
797 if n.X.Type().IsSlice() {
798 break
799 }
800 if name, offset, ok = StaticLoc(n.X); !ok {
801 break
802 }
803 l := getlit(n.Index)
804 if l < 0 {
805 break
806 }
807
808
809 if n.Type().Size() != 0 && types.MaxWidth/n.Type().Size() <= int64(l) {
810 break
811 }
812 offset += int64(l) * n.Type().Size()
813 return name, offset, true
814 }
815
816 return nil, 0, false
817 }
818
819 func isSideEffect(n ir.Node) bool {
820 switch n.Op() {
821
822 default:
823 return true
824
825
826 case ir.ONAME,
827 ir.ONONAME,
828 ir.OTYPE,
829 ir.OLITERAL,
830 ir.ONIL,
831 ir.OADD,
832 ir.OSUB,
833 ir.OOR,
834 ir.OXOR,
835 ir.OADDSTR,
836 ir.OADDR,
837 ir.OANDAND,
838 ir.OBYTES2STR,
839 ir.ORUNES2STR,
840 ir.OSTR2BYTES,
841 ir.OSTR2RUNES,
842 ir.OCAP,
843 ir.OCOMPLIT,
844 ir.OMAPLIT,
845 ir.OSTRUCTLIT,
846 ir.OARRAYLIT,
847 ir.OSLICELIT,
848 ir.OPTRLIT,
849 ir.OCONV,
850 ir.OCONVIFACE,
851 ir.OCONVNOP,
852 ir.ODOT,
853 ir.OEQ,
854 ir.ONE,
855 ir.OLT,
856 ir.OLE,
857 ir.OGT,
858 ir.OGE,
859 ir.OKEY,
860 ir.OSTRUCTKEY,
861 ir.OLEN,
862 ir.OMUL,
863 ir.OLSH,
864 ir.ORSH,
865 ir.OAND,
866 ir.OANDNOT,
867 ir.ONEW,
868 ir.ONOT,
869 ir.OBITNOT,
870 ir.OPLUS,
871 ir.ONEG,
872 ir.OOROR,
873 ir.OPAREN,
874 ir.ORUNESTR,
875 ir.OREAL,
876 ir.OIMAG,
877 ir.OCOMPLEX:
878 return false
879
880
881 case ir.ODIV, ir.OMOD:
882 n := n.(*ir.BinaryExpr)
883 if n.Y.Op() != ir.OLITERAL || constant.Sign(n.Y.Val()) == 0 {
884 return true
885 }
886
887
888
889 case ir.OMAKECHAN, ir.OMAKEMAP:
890 n := n.(*ir.MakeExpr)
891 if !ir.IsConst(n.Len, constant.Int) || constant.Sign(n.Len.Val()) != 0 {
892 return true
893 }
894
895
896
897 case ir.OMAKESLICE, ir.OMAKESLICECOPY:
898 return true
899 }
900 return false
901 }
902
903
904 func AnySideEffects(n ir.Node) bool {
905 return ir.Any(n, isSideEffect)
906 }
907
908
909
910 func mayModifyPkgVar(n ir.Node) bool {
911
912
913 safeLHS := func(lhs ir.Node) bool {
914 outer := ir.OuterValue(lhs)
915
916
917 for outer.Op() == ir.ODEREF {
918 outer = outer.(*ir.StarExpr).X
919 }
920 v, ok := outer.(*ir.Name)
921 return ok && v.Op() == ir.ONAME && !(v.Class == ir.PEXTERN && v.Sym().Pkg == types.LocalPkg)
922 }
923
924 return ir.Any(n, func(n ir.Node) bool {
925 switch n.Op() {
926 case ir.OCALLFUNC, ir.OCALLINTER:
927 return !ir.IsFuncPCIntrinsic(n.(*ir.CallExpr))
928
929 case ir.OAPPEND, ir.OCLEAR, ir.OCOPY:
930 return true
931
932 case ir.OASOP:
933 n := n.(*ir.AssignOpStmt)
934 if !safeLHS(n.X) {
935 return true
936 }
937
938 case ir.OAS:
939 n := n.(*ir.AssignStmt)
940 if !safeLHS(n.X) {
941 return true
942 }
943
944 case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
945 n := n.(*ir.AssignListStmt)
946 for _, lhs := range n.Lhs {
947 if !safeLHS(lhs) {
948 return true
949 }
950 }
951 }
952
953 return false
954 })
955 }
956
957
958
959 func canRepeat(n ir.Node) bool {
960 bad := func(n ir.Node) bool {
961 if isSideEffect(n) {
962 return true
963 }
964 switch n.Op() {
965 case ir.OMAKECHAN,
966 ir.OMAKEMAP,
967 ir.OMAKESLICE,
968 ir.OMAKESLICECOPY,
969 ir.OMAPLIT,
970 ir.ONEW,
971 ir.OPTRLIT,
972 ir.OSLICELIT,
973 ir.OSTR2BYTES,
974 ir.OSTR2RUNES:
975 return true
976 }
977 return false
978 }
979 return !ir.Any(n, bad)
980 }
981
982 func getlit(lit ir.Node) int {
983 if ir.IsSmallIntConst(lit) {
984 return int(ir.Int64Val(lit))
985 }
986 return -1
987 }
988
989 func isvaluelit(n ir.Node) bool {
990 return n.Op() == ir.OARRAYLIT || n.Op() == ir.OSTRUCTLIT
991 }
992
993 func subst(n ir.Node, m map[*ir.Name]ir.Node) (ir.Node, bool) {
994 valid := true
995 var edit func(ir.Node) ir.Node
996 edit = func(x ir.Node) ir.Node {
997 switch x.Op() {
998 case ir.ONAME:
999 x := x.(*ir.Name)
1000 if v, ok := m[x]; ok {
1001 return ir.DeepCopy(v.Pos(), v)
1002 }
1003 return x
1004 case ir.ONONAME, ir.OLITERAL, ir.ONIL, ir.OTYPE:
1005 return x
1006 }
1007 x = ir.Copy(x)
1008 ir.EditChildrenWithHidden(x, edit)
1009
1010
1011 switch x.Op() {
1012 case ir.OCONV:
1013 x := x.(*ir.ConvExpr)
1014 if x.X.Op() == ir.OLITERAL {
1015 if x, ok := truncate(x.X, x.Type()); ok {
1016 return x
1017 }
1018 valid = false
1019 return x
1020 }
1021 case ir.OADDSTR:
1022 return addStr(x.(*ir.AddStringExpr))
1023 }
1024 return x
1025 }
1026 n = edit(n)
1027 return n, valid
1028 }
1029
1030
1031
1032
1033 func truncate(c ir.Node, t *types.Type) (ir.Node, bool) {
1034 ct := c.Type()
1035 cv := c.Val()
1036 if ct.Kind() != t.Kind() {
1037 switch {
1038 default:
1039
1040
1041
1042
1043
1044 return nil, false
1045
1046 case ct.IsInteger() && t.IsInteger():
1047
1048 bits := t.Size() * 8
1049 cv = constant.BinaryOp(cv, token.AND, constant.MakeUint64(1<<bits-1))
1050 if t.IsSigned() && constant.Compare(cv, token.GEQ, constant.MakeUint64(1<<(bits-1))) {
1051 cv = constant.BinaryOp(cv, token.OR, constant.MakeInt64(-1<<(bits-1)))
1052 }
1053 }
1054 }
1055 c = ir.NewConstExpr(cv, c)
1056 c.SetType(t)
1057 return c, true
1058 }
1059
1060 func addStr(n *ir.AddStringExpr) ir.Node {
1061
1062 s := n.List
1063 need := 0
1064 for i := 0; i < len(s); i++ {
1065 if i == 0 || !ir.IsConst(s[i-1], constant.String) || !ir.IsConst(s[i], constant.String) {
1066
1067 need++
1068 }
1069 }
1070 if need == len(s) {
1071 return n
1072 }
1073 if need == 1 {
1074 var strs []string
1075 for _, c := range s {
1076 strs = append(strs, ir.StringVal(c))
1077 }
1078 return ir.NewConstExpr(constant.MakeString(strings.Join(strs, "")), n)
1079 }
1080 newList := make([]ir.Node, 0, need)
1081 for i := 0; i < len(s); i++ {
1082 if ir.IsConst(s[i], constant.String) && i+1 < len(s) && ir.IsConst(s[i+1], constant.String) {
1083
1084 var strs []string
1085 i2 := i
1086 for i2 < len(s) && ir.IsConst(s[i2], constant.String) {
1087 strs = append(strs, ir.StringVal(s[i2]))
1088 i2++
1089 }
1090
1091 newList = append(newList, ir.NewConstExpr(constant.MakeString(strings.Join(strs, "")), s[i]))
1092 i = i2 - 1
1093 } else {
1094 newList = append(newList, s[i])
1095 }
1096 }
1097
1098 nn := ir.Copy(n).(*ir.AddStringExpr)
1099 nn.List = newList
1100 return nn
1101 }
1102
1103 const wrapGlobalMapInitSizeThreshold = 20
1104
1105
1106
1107
1108
1109
1110
1111 func tryWrapGlobalInit(n ir.Node) *ir.Func {
1112
1113
1114
1115 if n.Op() != ir.OAS {
1116 return nil
1117 }
1118 as := n.(*ir.AssignStmt)
1119 if ir.IsBlank(as.X) || as.X.Op() != ir.ONAME {
1120 return nil
1121 }
1122 nm := as.X.(*ir.Name)
1123 if !nm.Type().IsMap() {
1124 return nil
1125 }
1126
1127
1128 rsiz := 0
1129 ir.Any(as.Y, func(n ir.Node) bool {
1130 rsiz++
1131 return false
1132 })
1133 if base.Debug.WrapGlobalMapDbg > 0 {
1134 fmt.Fprintf(os.Stderr, "=-= mapassign %s %v rhs size %d\n",
1135 base.Ctxt.Pkgpath, n, rsiz)
1136 }
1137
1138
1139 if rsiz < wrapGlobalMapInitSizeThreshold && base.Debug.WrapGlobalMapCtl != 2 {
1140 if base.Debug.WrapGlobalMapDbg > 1 {
1141 fmt.Fprintf(os.Stderr, "=-= skipping %v size too small at %d\n",
1142 nm, rsiz)
1143 }
1144 return nil
1145 }
1146
1147
1148 if AnySideEffects(as.Y) {
1149 if base.Debug.WrapGlobalMapDbg > 0 {
1150 fmt.Fprintf(os.Stderr, "=-= rejected %v due to side effects\n", nm)
1151 }
1152 return nil
1153 }
1154
1155 if base.Debug.WrapGlobalMapDbg > 1 {
1156 fmt.Fprintf(os.Stderr, "=-= committed for: %+v\n", n)
1157 }
1158
1159
1160
1161
1162
1163
1164
1165
1166 minitsym := typecheck.LookupNum("map.init.", mapinitgen)
1167 mapinitgen++
1168
1169 fn := ir.NewFunc(n.Pos(), n.Pos(), minitsym, types.NewSignature(nil, nil, nil))
1170 fn.SetInlinabilityChecked(true)
1171 typecheck.DeclFunc(fn)
1172 if base.Debug.WrapGlobalMapDbg > 0 {
1173 fmt.Fprintf(os.Stderr, "=-= generated func is %v\n", fn)
1174 }
1175
1176
1177
1178
1179
1180
1181 fn.Body = []ir.Node{as}
1182 typecheck.FinishFuncBody()
1183
1184 if base.Debug.WrapGlobalMapDbg > 1 {
1185 fmt.Fprintf(os.Stderr, "=-= mapvar is %v\n", nm)
1186 fmt.Fprintf(os.Stderr, "=-= newfunc is %+v\n", fn)
1187 }
1188
1189 recordFuncForVar(nm, fn)
1190
1191 return fn
1192 }
1193
1194
1195
1196 var mapinitgen int
1197
1198
1199
1200
1201
1202
1203 func AddKeepRelocations() {
1204 if varToMapInit == nil {
1205 return
1206 }
1207 for k, v := range varToMapInit {
1208
1209 fs := v.Linksym()
1210 if fs == nil {
1211 base.Fatalf("bad: func %v has no linksym", v)
1212 }
1213 vs := k.Linksym()
1214 if vs == nil {
1215 base.Fatalf("bad: mapvar %v has no linksym", k)
1216 }
1217 vs.AddRel(base.Ctxt, obj.Reloc{Type: objabi.R_KEEP, Sym: fs})
1218 if base.Debug.WrapGlobalMapDbg > 1 {
1219 fmt.Fprintf(os.Stderr, "=-= add R_KEEP relo from %s to %s\n",
1220 vs.Name, fs.Name)
1221 }
1222 }
1223 varToMapInit = nil
1224 }
1225
1226
1227
1228
1229
1230 func OutlineMapInits(fn *ir.Func) {
1231 if base.Debug.WrapGlobalMapCtl == 1 {
1232 return
1233 }
1234
1235 outlined := 0
1236 for i, stmt := range fn.Body {
1237
1238
1239 if wrapperFn := tryWrapGlobalInit(stmt); wrapperFn != nil {
1240 ir.WithFunc(fn, func() {
1241 fn.Body[i] = typecheck.Call(stmt.Pos(), wrapperFn.Nname, nil, false)
1242 })
1243 outlined++
1244 }
1245 }
1246
1247 if base.Debug.WrapGlobalMapDbg > 1 {
1248 fmt.Fprintf(os.Stderr, "=-= outlined %v map initializations\n", outlined)
1249 }
1250 }
1251
1252 const maxInitStatements = 1000
1253
1254
1255 func SplitLargeInit(fn *ir.Func) {
1256 if !fn.IsPackageInit() || len(fn.Body) <= maxInitStatements {
1257 return
1258 }
1259 var calls []ir.Node
1260 for chunk := range slices.Chunk(fn.Body, maxInitStatements) {
1261 varInitFn := generateVarInitFunc(chunk)
1262 ir.WithFunc(fn, func() {
1263 calls = append(calls, typecheck.Call(varInitFn.Pos(), varInitFn.Nname, nil, false))
1264 })
1265 }
1266 fn.Body = calls
1267 }
1268
1269
1270 func CanOptimize(fn *ir.Func) bool {
1271 name := fn.Sym().Name
1272 return name == "init" || strings.HasPrefix(name, varInitFuncPrefix)
1273 }
1274
1275
1276 var varInitGen int
1277
1278 const varInitFuncPrefix = "init.var."
1279
1280
1281
1282
1283
1284
1285 func generateVarInitFunc(body []ir.Node) *ir.Func {
1286 pos := base.AutogeneratedPos
1287 base.Pos = pos
1288
1289 sym := typecheck.LookupNum(varInitFuncPrefix, varInitGen)
1290 varInitGen++
1291
1292 fn := ir.NewFunc(pos, pos, sym, types.NewSignature(nil, nil, nil))
1293 fn.SetInlinabilityChecked(true)
1294 fn.SetWrapper(true)
1295 typecheck.DeclFunc(fn)
1296
1297 fn.Body = body
1298 typecheck.FinishFuncBody()
1299
1300 return fn
1301 }
1302
View as plain text