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