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