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