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