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 if disableGlobalAddrs && r.Type().IsString() {
336 return false
337 }
338 staticdata.InitConst(l, loff, r, int(typ.Size()))
339 return true
340
341 case ir.OADDR:
342 if disableGlobalAddrs {
343 return false
344 }
345 r := r.(*ir.AddrExpr)
346 if name, offset, ok := StaticLoc(r.X); ok && name.Class == ir.PEXTERN {
347 staticdata.InitAddrOffset(l, loff, name.Linksym(), offset)
348 return true
349 }
350 fallthrough
351
352 case ir.OPTRLIT:
353 if disableGlobalAddrs {
354 return false
355 }
356 r := r.(*ir.AddrExpr)
357 switch r.X.Op() {
358 case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT, ir.OSTRUCTLIT:
359
360 a := StaticName(r.X.Type())
361
362 s.Temps[r] = a
363 staticdata.InitAddr(l, loff, a.Linksym())
364
365
366 assign(base.Pos, a, 0, r.X)
367 return true
368 }
369
370
371 case ir.OSTR2BYTES:
372 if disableGlobalAddrs {
373 return false
374 }
375 r := r.(*ir.ConvExpr)
376 if l.Class == ir.PEXTERN && r.X.Op() == ir.OLITERAL {
377 sval := ir.StringVal(r.X)
378 staticdata.InitSliceBytes(l, loff, sval)
379 return true
380 }
381
382 case ir.OSLICELIT:
383 if disableGlobalAddrs {
384 return false
385 }
386 r := r.(*ir.CompLitExpr)
387 s.initplan(r)
388
389 ta := types.NewArray(r.Type().Elem(), r.Len)
390 ta.SetNoalg(true)
391 a := StaticName(ta)
392 s.Temps[r] = a
393 staticdata.InitSlice(l, loff, a.Linksym(), r.Len)
394
395 l = a
396 loff = 0
397 fallthrough
398
399 case ir.OARRAYLIT, ir.OSTRUCTLIT:
400 r := r.(*ir.CompLitExpr)
401 s.initplan(r)
402
403 p := s.Plans[r]
404 for i := range p.E {
405 e := &p.E[i]
406 if e.Expr.Op() == ir.OLITERAL && !disableGlobalAddrs || e.Expr.Op() == ir.ONIL {
407 staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(e.Expr.Type().Size()))
408 continue
409 }
410 ir.SetPos(e.Expr)
411 assign(base.Pos, l, loff+e.Xoffset, e.Expr)
412 }
413
414 return true
415
416 case ir.OMAPLIT:
417 break
418
419 case ir.OCLOSURE:
420 if disableGlobalAddrs {
421 return false
422 }
423 r := r.(*ir.ClosureExpr)
424 if !r.Func.IsClosure() {
425 if base.Debug.Closure > 0 {
426 base.WarnfAt(r.Pos(), "closure converted to global")
427 }
428
429
430
431 staticdata.InitAddr(l, loff, staticdata.FuncLinksym(r.Func.Nname))
432 return true
433 }
434 ir.ClosureDebugRuntimeCheck(r)
435
436 case ir.OCONVIFACE:
437
438
439
440 if disableGlobalAddrs {
441 return false
442 }
443
444
445 r := r.(*ir.ConvExpr)
446 val := ir.Node(r)
447 for val.Op() == ir.OCONVIFACE {
448 val = val.(*ir.ConvExpr).X
449 }
450
451 if val.Type().IsInterface() {
452
453
454
455
456
457 return val.Op() == ir.ONIL
458 }
459
460 if val.Type().HasShape() {
461
462 return false
463 }
464
465 reflectdata.MarkTypeUsedInInterface(val.Type(), l.Linksym())
466
467 var itab *ir.AddrExpr
468 if typ.IsEmptyInterface() {
469 itab = reflectdata.TypePtrAt(base.Pos, val.Type())
470 } else {
471 itab = reflectdata.ITabAddrAt(base.Pos, val.Type(), typ)
472 }
473
474
475
476
477 staticdata.InitAddr(l, loff, itab.X.(*ir.LinksymOffsetExpr).Linksym)
478
479
480 if types.IsDirectIface(val.Type()) {
481 if val.Op() == ir.ONIL {
482
483 return true
484 }
485
486 ir.SetPos(val)
487 assign(base.Pos, l, loff+int64(types.PtrSize), val)
488 } else {
489
490 a := StaticName(val.Type())
491 s.Temps[val] = a
492 assign(base.Pos, a, 0, val)
493 staticdata.InitAddr(l, loff+int64(types.PtrSize), a.Linksym())
494 }
495
496 return true
497
498 case ir.OINLCALL:
499 if disableGlobalAddrs {
500 return false
501 }
502 r := r.(*ir.InlinedCallExpr)
503 return s.staticAssignInlinedCall(l, loff, r, typ)
504 }
505
506 if base.Flag.Percent != 0 {
507 ir.Dump("not static", r)
508 }
509 return false
510 }
511
512 func (s *Schedule) initplan(n ir.Node) {
513 if s.Plans[n] != nil {
514 return
515 }
516 p := new(Plan)
517 s.Plans[n] = p
518 switch n.Op() {
519 default:
520 base.Fatalf("initplan")
521
522 case ir.OARRAYLIT, ir.OSLICELIT:
523 n := n.(*ir.CompLitExpr)
524 var k int64
525 for _, a := range n.List {
526 if a.Op() == ir.OKEY {
527 kv := a.(*ir.KeyExpr)
528 k = typecheck.IndexConst(kv.Key)
529 a = kv.Value
530 }
531 s.addvalue(p, k*n.Type().Elem().Size(), a)
532 k++
533 }
534
535 case ir.OSTRUCTLIT:
536 n := n.(*ir.CompLitExpr)
537 for _, a := range n.List {
538 if a.Op() != ir.OSTRUCTKEY {
539 base.Fatalf("initplan structlit")
540 }
541 a := a.(*ir.StructKeyExpr)
542 if a.Sym().IsBlank() {
543 continue
544 }
545 s.addvalue(p, a.Field.Offset, a.Value)
546 }
547
548 case ir.OMAPLIT:
549 n := n.(*ir.CompLitExpr)
550 for _, a := range n.List {
551 if a.Op() != ir.OKEY {
552 base.Fatalf("initplan maplit")
553 }
554 a := a.(*ir.KeyExpr)
555 s.addvalue(p, -1, a.Value)
556 }
557 }
558 }
559
560 func (s *Schedule) addvalue(p *Plan, xoffset int64, n ir.Node) {
561
562 if ir.IsZero(n) {
563 return
564 }
565
566
567 if isvaluelit(n) {
568 s.initplan(n)
569 q := s.Plans[n]
570 for _, qe := range q.E {
571
572 qe.Xoffset += xoffset
573 p.E = append(p.E, qe)
574 }
575 return
576 }
577
578
579 p.E = append(p.E, Entry{Xoffset: xoffset, Expr: n})
580 }
581
582 func (s *Schedule) staticAssignInlinedCall(l *ir.Name, loff int64, call *ir.InlinedCallExpr, typ *types.Type) bool {
583 if base.Debug.InlStaticInit == 0 {
584 return false
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
647
648
649 init := call.Init()
650 var as2init *ir.AssignListStmt
651 if len(init) == 2 && init[0].Op() == ir.OAS2 && init[1].Op() == ir.OINLMARK {
652 as2init = init[0].(*ir.AssignListStmt)
653 } else if len(init) == 1 && init[0].Op() == ir.OINLMARK {
654 as2init = new(ir.AssignListStmt)
655 } else {
656 return false
657 }
658 if len(call.Body) != 2 || call.Body[0].Op() != ir.OBLOCK || call.Body[1].Op() != ir.OLABEL {
659 return false
660 }
661 label := call.Body[1].(*ir.LabelStmt).Label
662 block := call.Body[0].(*ir.BlockStmt)
663 list := block.List
664 var dcl *ir.Decl
665 if len(list) == 3 && list[0].Op() == ir.ODCL {
666 dcl = list[0].(*ir.Decl)
667 list = list[1:]
668 }
669 if len(list) != 2 ||
670 list[0].Op() != ir.OAS2 ||
671 list[1].Op() != ir.OGOTO ||
672 list[1].(*ir.BranchStmt).Label != label {
673 return false
674 }
675 as2body := list[0].(*ir.AssignListStmt)
676 if dcl == nil {
677 ainit := as2body.Init()
678 if len(ainit) != 1 || ainit[0].Op() != ir.ODCL {
679 return false
680 }
681 dcl = ainit[0].(*ir.Decl)
682 }
683 if len(as2body.Lhs) != 1 || as2body.Lhs[0] != dcl.X {
684 return false
685 }
686
687
688 for _, v := range as2init.Lhs {
689 if v.(*ir.Name).Addrtaken() {
690 return false
691 }
692 }
693
694 for _, r := range as2init.Rhs {
695 if AnySideEffects(r) {
696 return false
697 }
698 }
699
700
701
702 count := make(map[*ir.Name]int)
703 for _, x := range as2init.Lhs {
704 count[x.(*ir.Name)] = 0
705 }
706
707 hasClosure := false
708 ir.Visit(as2body.Rhs[0], func(n ir.Node) {
709 if name, ok := n.(*ir.Name); ok {
710 if c, ok := count[name]; ok {
711 count[name] = c + 1
712 }
713 }
714 if clo, ok := n.(*ir.ClosureExpr); ok {
715 hasClosure = hasClosure || clo.Func.IsClosure()
716 }
717 })
718
719
720
721 if hasClosure {
722 return false
723 }
724
725 for name, c := range count {
726 if c > 1 {
727
728
729
730 for i, n := range as2init.Lhs {
731 if n == name && !canRepeat(as2init.Rhs[i]) {
732 return false
733 }
734 }
735 }
736 }
737
738
739
740 args := make(map[*ir.Name]ir.Node)
741 for i, v := range as2init.Lhs {
742 if ir.IsBlank(v) {
743 continue
744 }
745 args[v.(*ir.Name)] = as2init.Rhs[i]
746 }
747 r, ok := subst(as2body.Rhs[0], args)
748 if !ok {
749 return false
750 }
751 ok = s.StaticAssign(l, loff, r, typ)
752
753 if ok && base.Flag.Percent != 0 {
754 ir.Dump("static inlined-LEFT", l)
755 ir.Dump("static inlined-ORIG", call)
756 ir.Dump("static inlined-RIGHT", r)
757 }
758 return ok
759 }
760
761
762
763
764
765
766
767 var statuniqgen int
768
769
770 func StaticName(t *types.Type) *ir.Name {
771
772 sym := typecheck.Lookup(fmt.Sprintf("%s%d", obj.StaticNamePrefix, statuniqgen))
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