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