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