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 || rhs.Op() == ir.OCONVIFACE {
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, typecheck.FieldOffset(n.Type(), a.Field), 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 n.Linksym().Align = int16(t.Alignment())
767
768 return n
769 }
770
771
772 func StaticLoc(n ir.Node) (name *ir.Name, offset int64, ok bool) {
773 if n == nil {
774 return nil, 0, false
775 }
776
777 switch n.Op() {
778 case ir.ONAME:
779 n := n.(*ir.Name)
780 return n, 0, true
781
782 case ir.OMETHEXPR:
783 n := n.(*ir.SelectorExpr)
784 return StaticLoc(n.FuncName())
785
786 case ir.ODOT:
787 n := n.(*ir.SelectorExpr)
788 if name, offset, ok = StaticLoc(n.X); !ok {
789 break
790 }
791 offset += n.Offset()
792 return name, offset, true
793
794 case ir.OINDEX:
795 n := n.(*ir.IndexExpr)
796 if n.X.Type().IsSlice() {
797 break
798 }
799 if name, offset, ok = StaticLoc(n.X); !ok {
800 break
801 }
802 l := getlit(n.Index)
803 if l < 0 {
804 break
805 }
806
807
808 if n.Type().Size() != 0 && types.MaxWidth/n.Type().Size() <= int64(l) {
809 break
810 }
811 offset += int64(l) * n.Type().Size()
812 return name, offset, true
813 }
814
815 return nil, 0, false
816 }
817
818 func isSideEffect(n ir.Node) bool {
819 switch n.Op() {
820
821 default:
822 return true
823
824
825 case ir.ONAME,
826 ir.ONONAME,
827 ir.OTYPE,
828 ir.OLITERAL,
829 ir.ONIL,
830 ir.OADD,
831 ir.OSUB,
832 ir.OOR,
833 ir.OXOR,
834 ir.OADDSTR,
835 ir.OADDR,
836 ir.OANDAND,
837 ir.OBYTES2STR,
838 ir.ORUNES2STR,
839 ir.OSTR2BYTES,
840 ir.OSTR2RUNES,
841 ir.OCAP,
842 ir.OCOMPLIT,
843 ir.OMAPLIT,
844 ir.OSTRUCTLIT,
845 ir.OARRAYLIT,
846 ir.OSLICELIT,
847 ir.OPTRLIT,
848 ir.OCONV,
849 ir.OCONVIFACE,
850 ir.OCONVNOP,
851 ir.ODOT,
852 ir.OEQ,
853 ir.ONE,
854 ir.OLT,
855 ir.OLE,
856 ir.OGT,
857 ir.OGE,
858 ir.OKEY,
859 ir.OSTRUCTKEY,
860 ir.OLEN,
861 ir.OMUL,
862 ir.OLSH,
863 ir.ORSH,
864 ir.OAND,
865 ir.OANDNOT,
866 ir.ONEW,
867 ir.ONOT,
868 ir.OBITNOT,
869 ir.OPLUS,
870 ir.ONEG,
871 ir.OOROR,
872 ir.OPAREN,
873 ir.ORUNESTR,
874 ir.OREAL,
875 ir.OIMAG,
876 ir.OCOMPLEX:
877 return false
878
879
880 case ir.ODIV, ir.OMOD:
881 n := n.(*ir.BinaryExpr)
882 if n.Y.Op() != ir.OLITERAL || constant.Sign(n.Y.Val()) == 0 {
883 return true
884 }
885
886
887
888 case ir.OMAKECHAN, ir.OMAKEMAP:
889 n := n.(*ir.MakeExpr)
890 if !ir.IsConst(n.Len, constant.Int) || constant.Sign(n.Len.Val()) != 0 {
891 return true
892 }
893
894
895
896 case ir.OMAKESLICE, ir.OMAKESLICECOPY:
897 return true
898 }
899 return false
900 }
901
902
903 func AnySideEffects(n ir.Node) bool {
904 return ir.Any(n, isSideEffect)
905 }
906
907
908
909 func mayModifyPkgVar(n ir.Node) bool {
910
911
912 safeLHS := func(lhs ir.Node) bool {
913 outer := ir.OuterValue(lhs)
914
915
916 for outer.Op() == ir.ODEREF {
917 outer = outer.(*ir.StarExpr).X
918 }
919 v, ok := outer.(*ir.Name)
920 return ok && v.Op() == ir.ONAME && !(v.Class == ir.PEXTERN && v.Sym().Pkg == types.LocalPkg)
921 }
922
923 return ir.Any(n, func(n ir.Node) bool {
924 switch n.Op() {
925 case ir.OCALLFUNC, ir.OCALLINTER:
926 return !ir.IsFuncPCIntrinsic(n.(*ir.CallExpr))
927
928 case ir.OAPPEND, ir.OCLEAR, ir.OCOPY:
929 return true
930
931 case ir.OASOP:
932 n := n.(*ir.AssignOpStmt)
933 if !safeLHS(n.X) {
934 return true
935 }
936
937 case ir.OAS:
938 n := n.(*ir.AssignStmt)
939 if !safeLHS(n.X) {
940 return true
941 }
942
943 case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
944 n := n.(*ir.AssignListStmt)
945 for _, lhs := range n.Lhs {
946 if !safeLHS(lhs) {
947 return true
948 }
949 }
950 }
951
952 return false
953 })
954 }
955
956
957
958 func canRepeat(n ir.Node) bool {
959 bad := func(n ir.Node) bool {
960 if isSideEffect(n) {
961 return true
962 }
963 switch n.Op() {
964 case ir.OMAKECHAN,
965 ir.OMAKEMAP,
966 ir.OMAKESLICE,
967 ir.OMAKESLICECOPY,
968 ir.OMAPLIT,
969 ir.ONEW,
970 ir.OPTRLIT,
971 ir.OSLICELIT,
972 ir.OSTR2BYTES,
973 ir.OSTR2RUNES:
974 return true
975 }
976 return false
977 }
978 return !ir.Any(n, bad)
979 }
980
981 func getlit(lit ir.Node) int {
982 if ir.IsSmallIntConst(lit) {
983 return int(ir.Int64Val(lit))
984 }
985 return -1
986 }
987
988 func isvaluelit(n ir.Node) bool {
989 return n.Op() == ir.OARRAYLIT || n.Op() == ir.OSTRUCTLIT
990 }
991
992 func subst(n ir.Node, m map[*ir.Name]ir.Node) (ir.Node, bool) {
993 valid := true
994 var edit func(ir.Node) ir.Node
995 edit = func(x ir.Node) ir.Node {
996 switch x.Op() {
997 case ir.ONAME:
998 x := x.(*ir.Name)
999 if v, ok := m[x]; ok {
1000 return ir.DeepCopy(v.Pos(), v)
1001 }
1002 return x
1003 case ir.ONONAME, ir.OLITERAL, ir.ONIL, ir.OTYPE:
1004 return x
1005 }
1006 x = ir.Copy(x)
1007 ir.EditChildrenWithHidden(x, edit)
1008
1009
1010 switch x.Op() {
1011 case ir.OCONV:
1012 x := x.(*ir.ConvExpr)
1013 if x.X.Op() == ir.OLITERAL {
1014 if x, ok := truncate(x.X, x.Type()); ok {
1015 return x
1016 }
1017 valid = false
1018 return x
1019 }
1020 case ir.OADDSTR:
1021 return addStr(x.(*ir.AddStringExpr))
1022 }
1023 return x
1024 }
1025 n = edit(n)
1026 return n, valid
1027 }
1028
1029
1030
1031
1032 func truncate(c ir.Node, t *types.Type) (ir.Node, bool) {
1033 ct := c.Type()
1034 cv := c.Val()
1035 if ct.Kind() != t.Kind() {
1036 switch {
1037 default:
1038
1039
1040
1041
1042
1043 return nil, false
1044
1045 case ct.IsInteger() && t.IsInteger():
1046
1047 bits := t.Size() * 8
1048 cv = constant.BinaryOp(cv, token.AND, constant.MakeUint64(1<<bits-1))
1049 if t.IsSigned() && constant.Compare(cv, token.GEQ, constant.MakeUint64(1<<(bits-1))) {
1050 cv = constant.BinaryOp(cv, token.OR, constant.MakeInt64(-1<<(bits-1)))
1051 }
1052 }
1053 }
1054 c = ir.NewConstExpr(cv, c)
1055 c.SetType(t)
1056 return c, true
1057 }
1058
1059 func addStr(n *ir.AddStringExpr) ir.Node {
1060
1061 s := n.List
1062 need := 0
1063 for i := 0; i < len(s); i++ {
1064 if i == 0 || !ir.IsConst(s[i-1], constant.String) || !ir.IsConst(s[i], constant.String) {
1065
1066 need++
1067 }
1068 }
1069 if need == len(s) {
1070 return n
1071 }
1072 if need == 1 {
1073 var strs []string
1074 for _, c := range s {
1075 strs = append(strs, ir.StringVal(c))
1076 }
1077 return ir.NewConstExpr(constant.MakeString(strings.Join(strs, "")), n)
1078 }
1079 newList := make([]ir.Node, 0, need)
1080 for i := 0; i < len(s); i++ {
1081 if ir.IsConst(s[i], constant.String) && i+1 < len(s) && ir.IsConst(s[i+1], constant.String) {
1082
1083 var strs []string
1084 i2 := i
1085 for i2 < len(s) && ir.IsConst(s[i2], constant.String) {
1086 strs = append(strs, ir.StringVal(s[i2]))
1087 i2++
1088 }
1089
1090 newList = append(newList, ir.NewConstExpr(constant.MakeString(strings.Join(strs, "")), s[i]))
1091 i = i2 - 1
1092 } else {
1093 newList = append(newList, s[i])
1094 }
1095 }
1096
1097 nn := ir.Copy(n).(*ir.AddStringExpr)
1098 nn.List = newList
1099 return nn
1100 }
1101
1102 const wrapGlobalMapInitSizeThreshold = 20
1103
1104
1105
1106
1107
1108
1109
1110 func tryWrapGlobalInit(n ir.Node) *ir.Func {
1111
1112
1113
1114 if n.Op() != ir.OAS {
1115 return nil
1116 }
1117 as := n.(*ir.AssignStmt)
1118 if ir.IsBlank(as.X) || as.X.Op() != ir.ONAME {
1119 return nil
1120 }
1121 nm := as.X.(*ir.Name)
1122 if !nm.Type().IsMap() {
1123 return nil
1124 }
1125
1126
1127 rsiz := 0
1128 ir.Any(as.Y, func(n ir.Node) bool {
1129 rsiz++
1130 return false
1131 })
1132 if base.Debug.WrapGlobalMapDbg > 0 {
1133 fmt.Fprintf(os.Stderr, "=-= mapassign %s %v rhs size %d\n",
1134 base.Ctxt.Pkgpath, n, rsiz)
1135 }
1136
1137
1138 if rsiz < wrapGlobalMapInitSizeThreshold && base.Debug.WrapGlobalMapCtl != 2 {
1139 if base.Debug.WrapGlobalMapDbg > 1 {
1140 fmt.Fprintf(os.Stderr, "=-= skipping %v size too small at %d\n",
1141 nm, rsiz)
1142 }
1143 return nil
1144 }
1145
1146
1147 if AnySideEffects(as.Y) {
1148 if base.Debug.WrapGlobalMapDbg > 0 {
1149 fmt.Fprintf(os.Stderr, "=-= rejected %v due to side effects\n", nm)
1150 }
1151 return nil
1152 }
1153
1154 if base.Debug.WrapGlobalMapDbg > 1 {
1155 fmt.Fprintf(os.Stderr, "=-= committed for: %+v\n", n)
1156 }
1157
1158
1159
1160
1161
1162
1163
1164
1165 minitsym := typecheck.LookupNum("map.init.", mapinitgen)
1166 mapinitgen++
1167
1168 fn := ir.NewFunc(n.Pos(), n.Pos(), minitsym, types.NewSignature(nil, nil, nil))
1169 fn.SetInlinabilityChecked(true)
1170 typecheck.DeclFunc(fn)
1171 if base.Debug.WrapGlobalMapDbg > 0 {
1172 fmt.Fprintf(os.Stderr, "=-= generated func is %v\n", fn)
1173 }
1174
1175
1176
1177
1178
1179
1180 fn.Body = []ir.Node{as}
1181 typecheck.FinishFuncBody()
1182
1183 if base.Debug.WrapGlobalMapDbg > 1 {
1184 fmt.Fprintf(os.Stderr, "=-= mapvar is %v\n", nm)
1185 fmt.Fprintf(os.Stderr, "=-= newfunc is %+v\n", fn)
1186 }
1187
1188 recordFuncForVar(nm, fn)
1189
1190 return fn
1191 }
1192
1193
1194
1195 var mapinitgen int
1196
1197
1198
1199
1200
1201
1202 func AddKeepRelocations() {
1203 if varToMapInit == nil {
1204 return
1205 }
1206 for k, v := range varToMapInit {
1207
1208 fs := v.Linksym()
1209 if fs == nil {
1210 base.Fatalf("bad: func %v has no linksym", v)
1211 }
1212 vs := k.Linksym()
1213 if vs == nil {
1214 base.Fatalf("bad: mapvar %v has no linksym", k)
1215 }
1216 vs.AddRel(base.Ctxt, obj.Reloc{Type: objabi.R_KEEP, Sym: fs})
1217 if base.Debug.WrapGlobalMapDbg > 1 {
1218 fmt.Fprintf(os.Stderr, "=-= add R_KEEP relo from %s to %s\n",
1219 vs.Name, fs.Name)
1220 }
1221 }
1222 varToMapInit = nil
1223 }
1224
1225
1226
1227
1228
1229 func OutlineMapInits(fn *ir.Func) {
1230 if base.Debug.WrapGlobalMapCtl == 1 {
1231 return
1232 }
1233
1234 outlined := 0
1235 for i, stmt := range fn.Body {
1236
1237
1238 if wrapperFn := tryWrapGlobalInit(stmt); wrapperFn != nil {
1239 ir.WithFunc(fn, func() {
1240 fn.Body[i] = typecheck.Call(stmt.Pos(), wrapperFn.Nname, nil, false)
1241 })
1242 outlined++
1243 }
1244 }
1245
1246 if base.Debug.WrapGlobalMapDbg > 1 {
1247 fmt.Fprintf(os.Stderr, "=-= outlined %v map initializations\n", outlined)
1248 }
1249 }
1250
View as plain text