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 v, ok := ir.OuterValue(lhs).(*ir.Name)
885 return ok && v.Op() == ir.ONAME && !(v.Class == ir.PEXTERN && v.Sym().Pkg == types.LocalPkg)
886 }
887
888 return ir.Any(n, func(n ir.Node) bool {
889 switch n.Op() {
890 case ir.OCALLFUNC, ir.OCALLINTER:
891 return !ir.IsFuncPCIntrinsic(n.(*ir.CallExpr))
892
893 case ir.OAPPEND, ir.OCLEAR, ir.OCOPY:
894 return true
895
896 case ir.OAS:
897 n := n.(*ir.AssignStmt)
898 if !safeLHS(n.X) {
899 return true
900 }
901
902 case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
903 n := n.(*ir.AssignListStmt)
904 for _, lhs := range n.Lhs {
905 if !safeLHS(lhs) {
906 return true
907 }
908 }
909 }
910
911 return false
912 })
913 }
914
915
916
917 func canRepeat(n ir.Node) bool {
918 bad := func(n ir.Node) bool {
919 if isSideEffect(n) {
920 return true
921 }
922 switch n.Op() {
923 case ir.OMAKECHAN,
924 ir.OMAKEMAP,
925 ir.OMAKESLICE,
926 ir.OMAKESLICECOPY,
927 ir.OMAPLIT,
928 ir.ONEW,
929 ir.OPTRLIT,
930 ir.OSLICELIT,
931 ir.OSTR2BYTES,
932 ir.OSTR2RUNES:
933 return true
934 }
935 return false
936 }
937 return !ir.Any(n, bad)
938 }
939
940 func getlit(lit ir.Node) int {
941 if ir.IsSmallIntConst(lit) {
942 return int(ir.Int64Val(lit))
943 }
944 return -1
945 }
946
947 func isvaluelit(n ir.Node) bool {
948 return n.Op() == ir.OARRAYLIT || n.Op() == ir.OSTRUCTLIT
949 }
950
951 func subst(n ir.Node, m map[*ir.Name]ir.Node) (ir.Node, bool) {
952 valid := true
953 var edit func(ir.Node) ir.Node
954 edit = func(x ir.Node) ir.Node {
955 switch x.Op() {
956 case ir.ONAME:
957 x := x.(*ir.Name)
958 if v, ok := m[x]; ok {
959 return ir.DeepCopy(v.Pos(), v)
960 }
961 return x
962 case ir.ONONAME, ir.OLITERAL, ir.ONIL, ir.OTYPE:
963 return x
964 }
965 x = ir.Copy(x)
966 ir.EditChildrenWithHidden(x, edit)
967
968
969 switch x.Op() {
970 case ir.OCONV:
971 x := x.(*ir.ConvExpr)
972 if x.X.Op() == ir.OLITERAL {
973 if x, ok := truncate(x.X, x.Type()); ok {
974 return x
975 }
976 valid = false
977 return x
978 }
979 case ir.OADDSTR:
980 return addStr(x.(*ir.AddStringExpr))
981 }
982 return x
983 }
984 n = edit(n)
985 return n, valid
986 }
987
988
989
990
991 func truncate(c ir.Node, t *types.Type) (ir.Node, bool) {
992 ct := c.Type()
993 cv := c.Val()
994 if ct.Kind() != t.Kind() {
995 switch {
996 default:
997
998
999
1000
1001
1002 return nil, false
1003
1004 case ct.IsInteger() && t.IsInteger():
1005
1006 bits := t.Size() * 8
1007 cv = constant.BinaryOp(cv, token.AND, constant.MakeUint64(1<<bits-1))
1008 if t.IsSigned() && constant.Compare(cv, token.GEQ, constant.MakeUint64(1<<(bits-1))) {
1009 cv = constant.BinaryOp(cv, token.OR, constant.MakeInt64(-1<<(bits-1)))
1010 }
1011 }
1012 }
1013 c = ir.NewConstExpr(cv, c)
1014 c.SetType(t)
1015 return c, true
1016 }
1017
1018 func addStr(n *ir.AddStringExpr) ir.Node {
1019
1020 s := n.List
1021 need := 0
1022 for i := 0; i < len(s); i++ {
1023 if i == 0 || !ir.IsConst(s[i-1], constant.String) || !ir.IsConst(s[i], constant.String) {
1024
1025 need++
1026 }
1027 }
1028 if need == len(s) {
1029 return n
1030 }
1031 if need == 1 {
1032 var strs []string
1033 for _, c := range s {
1034 strs = append(strs, ir.StringVal(c))
1035 }
1036 return ir.NewConstExpr(constant.MakeString(strings.Join(strs, "")), n)
1037 }
1038 newList := make([]ir.Node, 0, need)
1039 for i := 0; i < len(s); i++ {
1040 if ir.IsConst(s[i], constant.String) && i+1 < len(s) && ir.IsConst(s[i+1], constant.String) {
1041
1042 var strs []string
1043 i2 := i
1044 for i2 < len(s) && ir.IsConst(s[i2], constant.String) {
1045 strs = append(strs, ir.StringVal(s[i2]))
1046 i2++
1047 }
1048
1049 newList = append(newList, ir.NewConstExpr(constant.MakeString(strings.Join(strs, "")), s[i]))
1050 i = i2 - 1
1051 } else {
1052 newList = append(newList, s[i])
1053 }
1054 }
1055
1056 nn := ir.Copy(n).(*ir.AddStringExpr)
1057 nn.List = newList
1058 return nn
1059 }
1060
1061 const wrapGlobalMapInitSizeThreshold = 20
1062
1063
1064
1065
1066
1067
1068
1069 func tryWrapGlobalInit(n ir.Node) *ir.Func {
1070
1071
1072
1073 if n.Op() != ir.OAS {
1074 return nil
1075 }
1076 as := n.(*ir.AssignStmt)
1077 if ir.IsBlank(as.X) || as.X.Op() != ir.ONAME {
1078 return nil
1079 }
1080 nm := as.X.(*ir.Name)
1081 if !nm.Type().IsMap() {
1082 return nil
1083 }
1084
1085
1086 rsiz := 0
1087 ir.Any(as.Y, func(n ir.Node) bool {
1088 rsiz++
1089 return false
1090 })
1091 if base.Debug.WrapGlobalMapDbg > 0 {
1092 fmt.Fprintf(os.Stderr, "=-= mapassign %s %v rhs size %d\n",
1093 base.Ctxt.Pkgpath, n, rsiz)
1094 }
1095
1096
1097 if rsiz < wrapGlobalMapInitSizeThreshold && base.Debug.WrapGlobalMapCtl != 2 {
1098 if base.Debug.WrapGlobalMapDbg > 1 {
1099 fmt.Fprintf(os.Stderr, "=-= skipping %v size too small at %d\n",
1100 nm, rsiz)
1101 }
1102 return nil
1103 }
1104
1105
1106 if AnySideEffects(as.Y) {
1107 if base.Debug.WrapGlobalMapDbg > 0 {
1108 fmt.Fprintf(os.Stderr, "=-= rejected %v due to side effects\n", nm)
1109 }
1110 return nil
1111 }
1112
1113 if base.Debug.WrapGlobalMapDbg > 1 {
1114 fmt.Fprintf(os.Stderr, "=-= committed for: %+v\n", n)
1115 }
1116
1117
1118
1119
1120
1121
1122
1123
1124 minitsym := typecheck.LookupNum("map.init.", mapinitgen)
1125 mapinitgen++
1126
1127 fn := ir.NewFunc(n.Pos(), n.Pos(), minitsym, types.NewSignature(nil, nil, nil))
1128 fn.SetInlinabilityChecked(true)
1129 typecheck.DeclFunc(fn)
1130 if base.Debug.WrapGlobalMapDbg > 0 {
1131 fmt.Fprintf(os.Stderr, "=-= generated func is %v\n", fn)
1132 }
1133
1134
1135
1136
1137
1138
1139 fn.Body = []ir.Node{as}
1140 typecheck.FinishFuncBody()
1141
1142 if base.Debug.WrapGlobalMapDbg > 1 {
1143 fmt.Fprintf(os.Stderr, "=-= mapvar is %v\n", nm)
1144 fmt.Fprintf(os.Stderr, "=-= newfunc is %+v\n", fn)
1145 }
1146
1147 recordFuncForVar(nm, fn)
1148
1149 return fn
1150 }
1151
1152
1153
1154 var mapinitgen int
1155
1156
1157
1158
1159
1160
1161 func AddKeepRelocations() {
1162 if varToMapInit == nil {
1163 return
1164 }
1165 for k, v := range varToMapInit {
1166
1167 fs := v.Linksym()
1168 if fs == nil {
1169 base.Fatalf("bad: func %v has no linksym", v)
1170 }
1171 vs := k.Linksym()
1172 if vs == nil {
1173 base.Fatalf("bad: mapvar %v has no linksym", k)
1174 }
1175 r := obj.Addrel(vs)
1176 r.Sym = fs
1177 r.Type = objabi.R_KEEP
1178 if base.Debug.WrapGlobalMapDbg > 1 {
1179 fmt.Fprintf(os.Stderr, "=-= add R_KEEP relo from %s to %s\n",
1180 vs.Name, fs.Name)
1181 }
1182 }
1183 varToMapInit = nil
1184 }
1185
1186
1187
1188
1189
1190 func OutlineMapInits(fn *ir.Func) {
1191 if base.Debug.WrapGlobalMapCtl == 1 {
1192 return
1193 }
1194
1195 outlined := 0
1196 for i, stmt := range fn.Body {
1197
1198
1199 if wrapperFn := tryWrapGlobalInit(stmt); wrapperFn != nil {
1200 ir.WithFunc(fn, func() {
1201 fn.Body[i] = typecheck.Call(stmt.Pos(), wrapperFn.Nname, nil, false)
1202 })
1203 outlined++
1204 }
1205 }
1206
1207 if base.Debug.WrapGlobalMapDbg > 1 {
1208 fmt.Fprintf(os.Stderr, "=-= outlined %v map initializations\n", outlined)
1209 }
1210 }
1211
View as plain text