]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/staticinit/sched.go
dd370a305c38fd4a32bead1108179599b9889a3f
[gostls13.git] / src / cmd / compile / internal / staticinit / sched.go
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
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   // struct, array only
27         Expr    ir.Node // bytes of run-time computed expressions
28 }
29
30 type Plan struct {
31         E []Entry
32 }
33
34 // An Schedule is used to decompose assignment statements into
35 // static and dynamic initialization parts. Static initializations are
36 // handled by populating variables' linker symbol data, while dynamic
37 // initializations are accumulated to be executed in order.
38 type Schedule struct {
39         // Out is the ordered list of dynamic initialization
40         // statements.
41         Out []ir.Node
42
43         Plans map[ir.Node]*Plan
44         Temps map[ir.Node]*ir.Name
45 }
46
47 func (s *Schedule) append(n ir.Node) {
48         s.Out = append(s.Out, n)
49 }
50
51 // StaticInit adds an initialization statement n to the schedule.
52 func (s *Schedule) StaticInit(n ir.Node) {
53         if !s.tryStaticInit(n) {
54                 if base.Flag.Percent != 0 {
55                         ir.Dump("StaticInit failed", n)
56                 }
57                 s.append(n)
58         }
59 }
60
61 // varToMapInit holds book-keeping state for global map initialization;
62 // it records the init function created by the compiler to host the
63 // initialization code for the map in question.
64 var varToMapInit map[*ir.Name]*ir.Func
65
66 // MapInitToVar is the inverse of VarToMapInit; it maintains a mapping
67 // from a compiler-generated init function to the map the function is
68 // initializing.
69 var MapInitToVar map[*ir.Func]*ir.Name
70
71 // recordFuncForVar establishes a mapping between global map var "v" and
72 // outlined init function "fn" (and vice versa); so that we can use
73 // the mappings later on to update relocations.
74 func recordFuncForVar(v *ir.Name, fn *ir.Func) {
75         if varToMapInit == nil {
76                 varToMapInit = make(map[*ir.Name]*ir.Func)
77                 MapInitToVar = make(map[*ir.Func]*ir.Name)
78         }
79         varToMapInit[v] = fn
80         MapInitToVar[fn] = v
81 }
82
83 // tryStaticInit attempts to statically execute an initialization
84 // statement and reports whether it succeeded.
85 func (s *Schedule) tryStaticInit(nn ir.Node) bool {
86         // Only worry about simple "l = r" assignments. Multiple
87         // variable/expression OAS2 assignments have already been
88         // replaced by multiple simple OAS assignments, and the other
89         // OAS2* assignments mostly necessitate dynamic execution
90         // anyway.
91         if nn.Op() != ir.OAS {
92                 return false
93         }
94         n := nn.(*ir.AssignStmt)
95         if ir.IsBlank(n.X) && !AnySideEffects(n.Y) {
96                 // Discard.
97                 return true
98         }
99         lno := ir.SetPos(n)
100         defer func() { base.Pos = lno }()
101         nam := n.X.(*ir.Name)
102         return s.StaticAssign(nam, 0, n.Y, nam.Type())
103 }
104
105 // like staticassign but we are copying an already
106 // initialized value r.
107 func (s *Schedule) staticcopy(l *ir.Name, loff int64, rn *ir.Name, typ *types.Type) bool {
108         if rn.Class == ir.PFUNC {
109                 // TODO if roff != 0 { panic }
110                 staticdata.InitAddr(l, loff, staticdata.FuncLinksym(rn))
111                 return true
112         }
113         if rn.Class != ir.PEXTERN || rn.Sym().Pkg != types.LocalPkg {
114                 return false
115         }
116         if rn.Defn == nil {
117                 // No explicit initialization value. Probably zeroed but perhaps
118                 // supplied externally and of unknown value.
119                 return false
120         }
121         if rn.Defn.Op() != ir.OAS {
122                 return false
123         }
124         if rn.Type().IsString() { // perhaps overwritten by cmd/link -X (#34675)
125                 return false
126         }
127         if rn.Embed != nil {
128                 return false
129         }
130         orig := rn
131         r := rn.Defn.(*ir.AssignStmt).Y
132         if r == nil {
133                 // types2.InitOrder doesn't include default initializers.
134                 base.Fatalf("unexpected initializer: %v", rn.Defn)
135         }
136
137         for r.Op() == ir.OCONVNOP && !types.Identical(r.Type(), typ) {
138                 r = r.(*ir.ConvExpr).X
139         }
140
141         switch r.Op() {
142         case ir.OMETHEXPR:
143                 r = r.(*ir.SelectorExpr).FuncName()
144                 fallthrough
145         case ir.ONAME:
146                 r := r.(*ir.Name)
147                 if s.staticcopy(l, loff, r, typ) {
148                         return true
149                 }
150                 // We may have skipped past one or more OCONVNOPs, so
151                 // use conv to ensure r is assignable to l (#13263).
152                 dst := ir.Node(l)
153                 if loff != 0 || !types.Identical(typ, l.Type()) {
154                         dst = ir.NewNameOffsetExpr(base.Pos, l, loff, typ)
155                 }
156                 s.append(ir.NewAssignStmt(base.Pos, dst, typecheck.Conv(r, typ)))
157                 return true
158
159         case ir.ONIL:
160                 return true
161
162         case ir.OLITERAL:
163                 if ir.IsZero(r) {
164                         return true
165                 }
166                 staticdata.InitConst(l, loff, r, int(typ.Size()))
167                 return true
168
169         case ir.OADDR:
170                 r := r.(*ir.AddrExpr)
171                 if a, ok := r.X.(*ir.Name); ok && a.Op() == ir.ONAME {
172                         staticdata.InitAddr(l, loff, staticdata.GlobalLinksym(a))
173                         return true
174                 }
175
176         case ir.OPTRLIT:
177                 r := r.(*ir.AddrExpr)
178                 switch r.X.Op() {
179                 case ir.OARRAYLIT, ir.OSLICELIT, ir.OSTRUCTLIT, ir.OMAPLIT:
180                         // copy pointer
181                         staticdata.InitAddr(l, loff, staticdata.GlobalLinksym(s.Temps[r]))
182                         return true
183                 }
184
185         case ir.OSLICELIT:
186                 r := r.(*ir.CompLitExpr)
187                 // copy slice
188                 staticdata.InitSlice(l, loff, staticdata.GlobalLinksym(s.Temps[r]), r.Len)
189                 return true
190
191         case ir.OARRAYLIT, ir.OSTRUCTLIT:
192                 r := r.(*ir.CompLitExpr)
193                 p := s.Plans[r]
194                 for i := range p.E {
195                         e := &p.E[i]
196                         typ := e.Expr.Type()
197                         if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL {
198                                 staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(typ.Size()))
199                                 continue
200                         }
201                         x := e.Expr
202                         if x.Op() == ir.OMETHEXPR {
203                                 x = x.(*ir.SelectorExpr).FuncName()
204                         }
205                         if x.Op() == ir.ONAME && s.staticcopy(l, loff+e.Xoffset, x.(*ir.Name), typ) {
206                                 continue
207                         }
208                         // Requires computation, but we're
209                         // copying someone else's computation.
210                         ll := ir.NewNameOffsetExpr(base.Pos, l, loff+e.Xoffset, typ)
211                         rr := ir.NewNameOffsetExpr(base.Pos, orig, e.Xoffset, typ)
212                         ir.SetPos(rr)
213                         s.append(ir.NewAssignStmt(base.Pos, ll, rr))
214                 }
215
216                 return true
217         }
218
219         return false
220 }
221
222 func (s *Schedule) StaticAssign(l *ir.Name, loff int64, r ir.Node, typ *types.Type) bool {
223         if r == nil {
224                 // No explicit initialization value. Either zero or supplied
225                 // externally.
226                 return true
227         }
228         for r.Op() == ir.OCONVNOP {
229                 r = r.(*ir.ConvExpr).X
230         }
231
232         assign := func(pos src.XPos, a *ir.Name, aoff int64, v ir.Node) {
233                 if s.StaticAssign(a, aoff, v, v.Type()) {
234                         return
235                 }
236                 var lhs ir.Node
237                 if ir.IsBlank(a) {
238                         // Don't use NameOffsetExpr with blank (#43677).
239                         lhs = ir.BlankNode
240                 } else {
241                         lhs = ir.NewNameOffsetExpr(pos, a, aoff, v.Type())
242                 }
243                 s.append(ir.NewAssignStmt(pos, lhs, v))
244         }
245
246         switch r.Op() {
247         case ir.ONAME:
248                 r := r.(*ir.Name)
249                 return s.staticcopy(l, loff, r, typ)
250
251         case ir.OMETHEXPR:
252                 r := r.(*ir.SelectorExpr)
253                 return s.staticcopy(l, loff, r.FuncName(), typ)
254
255         case ir.ONIL:
256                 return true
257
258         case ir.OLITERAL:
259                 if ir.IsZero(r) {
260                         return true
261                 }
262                 staticdata.InitConst(l, loff, r, int(typ.Size()))
263                 return true
264
265         case ir.OADDR:
266                 r := r.(*ir.AddrExpr)
267                 if name, offset, ok := StaticLoc(r.X); ok && name.Class == ir.PEXTERN {
268                         staticdata.InitAddrOffset(l, loff, name.Linksym(), offset)
269                         return true
270                 }
271                 fallthrough
272
273         case ir.OPTRLIT:
274                 r := r.(*ir.AddrExpr)
275                 switch r.X.Op() {
276                 case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT, ir.OSTRUCTLIT:
277                         // Init pointer.
278                         a := StaticName(r.X.Type())
279
280                         s.Temps[r] = a
281                         staticdata.InitAddr(l, loff, a.Linksym())
282
283                         // Init underlying literal.
284                         assign(base.Pos, a, 0, r.X)
285                         return true
286                 }
287                 //dump("not static ptrlit", r);
288
289         case ir.OSTR2BYTES:
290                 r := r.(*ir.ConvExpr)
291                 if l.Class == ir.PEXTERN && r.X.Op() == ir.OLITERAL {
292                         sval := ir.StringVal(r.X)
293                         staticdata.InitSliceBytes(l, loff, sval)
294                         return true
295                 }
296
297         case ir.OSLICELIT:
298                 r := r.(*ir.CompLitExpr)
299                 s.initplan(r)
300                 // Init slice.
301                 ta := types.NewArray(r.Type().Elem(), r.Len)
302                 ta.SetNoalg(true)
303                 a := StaticName(ta)
304                 s.Temps[r] = a
305                 staticdata.InitSlice(l, loff, a.Linksym(), r.Len)
306                 // Fall through to init underlying array.
307                 l = a
308                 loff = 0
309                 fallthrough
310
311         case ir.OARRAYLIT, ir.OSTRUCTLIT:
312                 r := r.(*ir.CompLitExpr)
313                 s.initplan(r)
314
315                 p := s.Plans[r]
316                 for i := range p.E {
317                         e := &p.E[i]
318                         if e.Expr.Op() == ir.OLITERAL || e.Expr.Op() == ir.ONIL {
319                                 staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(e.Expr.Type().Size()))
320                                 continue
321                         }
322                         ir.SetPos(e.Expr)
323                         assign(base.Pos, l, loff+e.Xoffset, e.Expr)
324                 }
325
326                 return true
327
328         case ir.OMAPLIT:
329                 break
330
331         case ir.OCLOSURE:
332                 r := r.(*ir.ClosureExpr)
333                 if ir.IsTrivialClosure(r) {
334                         if base.Debug.Closure > 0 {
335                                 base.WarnfAt(r.Pos(), "closure converted to global")
336                         }
337                         // Issue 59680: if the closure we're looking at was produced
338                         // by inlining, it could be marked as hidden, which we don't
339                         // want (moving the func to a static init will effectively
340                         // hide it from escape analysis). Mark as non-hidden here.
341                         // so that it will participated in escape analysis.
342                         r.Func.SetIsHiddenClosure(false)
343                         // Closures with no captured variables are globals,
344                         // so the assignment can be done at link time.
345                         // TODO if roff != 0 { panic }
346                         staticdata.InitAddr(l, loff, staticdata.FuncLinksym(r.Func.Nname))
347                         return true
348                 }
349                 ir.ClosureDebugRuntimeCheck(r)
350
351         case ir.OCONVIFACE:
352                 // This logic is mirrored in isStaticCompositeLiteral.
353                 // If you change something here, change it there, and vice versa.
354
355                 // Determine the underlying concrete type and value we are converting from.
356                 r := r.(*ir.ConvExpr)
357                 val := ir.Node(r)
358                 for val.Op() == ir.OCONVIFACE {
359                         val = val.(*ir.ConvExpr).X
360                 }
361
362                 if val.Type().IsInterface() {
363                         // val is an interface type.
364                         // If val is nil, we can statically initialize l;
365                         // both words are zero and so there no work to do, so report success.
366                         // If val is non-nil, we have no concrete type to record,
367                         // and we won't be able to statically initialize its value, so report failure.
368                         return val.Op() == ir.ONIL
369                 }
370
371                 if val.Type().HasShape() {
372                         // See comment in cmd/compile/internal/walk/convert.go:walkConvInterface
373                         return false
374                 }
375
376                 reflectdata.MarkTypeUsedInInterface(val.Type(), l.Linksym())
377
378                 var itab *ir.AddrExpr
379                 if typ.IsEmptyInterface() {
380                         itab = reflectdata.TypePtrAt(base.Pos, val.Type())
381                 } else {
382                         itab = reflectdata.ITabAddrAt(base.Pos, val.Type(), typ)
383                 }
384
385                 // Create a copy of l to modify while we emit data.
386
387                 // Emit itab, advance offset.
388                 staticdata.InitAddr(l, loff, itab.X.(*ir.LinksymOffsetExpr).Linksym)
389
390                 // Emit data.
391                 if types.IsDirectIface(val.Type()) {
392                         if val.Op() == ir.ONIL {
393                                 // Nil is zero, nothing to do.
394                                 return true
395                         }
396                         // Copy val directly into n.
397                         ir.SetPos(val)
398                         assign(base.Pos, l, loff+int64(types.PtrSize), val)
399                 } else {
400                         // Construct temp to hold val, write pointer to temp into n.
401                         a := StaticName(val.Type())
402                         s.Temps[val] = a
403                         assign(base.Pos, a, 0, val)
404                         staticdata.InitAddr(l, loff+int64(types.PtrSize), a.Linksym())
405                 }
406
407                 return true
408
409         case ir.OINLCALL:
410                 r := r.(*ir.InlinedCallExpr)
411                 return s.staticAssignInlinedCall(l, loff, r, typ)
412         }
413
414         if base.Flag.Percent != 0 {
415                 ir.Dump("not static", r)
416         }
417         return false
418 }
419
420 func (s *Schedule) initplan(n ir.Node) {
421         if s.Plans[n] != nil {
422                 return
423         }
424         p := new(Plan)
425         s.Plans[n] = p
426         switch n.Op() {
427         default:
428                 base.Fatalf("initplan")
429
430         case ir.OARRAYLIT, ir.OSLICELIT:
431                 n := n.(*ir.CompLitExpr)
432                 var k int64
433                 for _, a := range n.List {
434                         if a.Op() == ir.OKEY {
435                                 kv := a.(*ir.KeyExpr)
436                                 k = typecheck.IndexConst(kv.Key)
437                                 if k < 0 {
438                                         base.Fatalf("initplan arraylit: invalid index %v", kv.Key)
439                                 }
440                                 a = kv.Value
441                         }
442                         s.addvalue(p, k*n.Type().Elem().Size(), a)
443                         k++
444                 }
445
446         case ir.OSTRUCTLIT:
447                 n := n.(*ir.CompLitExpr)
448                 for _, a := range n.List {
449                         if a.Op() != ir.OSTRUCTKEY {
450                                 base.Fatalf("initplan structlit")
451                         }
452                         a := a.(*ir.StructKeyExpr)
453                         if a.Sym().IsBlank() {
454                                 continue
455                         }
456                         s.addvalue(p, a.Field.Offset, a.Value)
457                 }
458
459         case ir.OMAPLIT:
460                 n := n.(*ir.CompLitExpr)
461                 for _, a := range n.List {
462                         if a.Op() != ir.OKEY {
463                                 base.Fatalf("initplan maplit")
464                         }
465                         a := a.(*ir.KeyExpr)
466                         s.addvalue(p, -1, a.Value)
467                 }
468         }
469 }
470
471 func (s *Schedule) addvalue(p *Plan, xoffset int64, n ir.Node) {
472         // special case: zero can be dropped entirely
473         if ir.IsZero(n) {
474                 return
475         }
476
477         // special case: inline struct and array (not slice) literals
478         if isvaluelit(n) {
479                 s.initplan(n)
480                 q := s.Plans[n]
481                 for _, qe := range q.E {
482                         // qe is a copy; we are not modifying entries in q.E
483                         qe.Xoffset += xoffset
484                         p.E = append(p.E, qe)
485                 }
486                 return
487         }
488
489         // add to plan
490         p.E = append(p.E, Entry{Xoffset: xoffset, Expr: n})
491 }
492
493 func (s *Schedule) staticAssignInlinedCall(l *ir.Name, loff int64, call *ir.InlinedCallExpr, typ *types.Type) bool {
494         if base.Debug.InlStaticInit == 0 {
495                 return false
496         }
497
498         // Handle the special case of an inlined call of
499         // a function body with a single return statement,
500         // which turns into a single assignment plus a goto.
501         //
502         // For example code like this:
503         //
504         //      type T struct{ x int }
505         //      func F(x int) *T { return &T{x} }
506         //      var Global = F(400)
507         //
508         // turns into IR like this:
509         //
510         //      INLCALL-init
511         //      .   AS2-init
512         //      .   .   DCL # x.go:18:13
513         //      .   .   .   NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13
514         //      .   AS2 Def tc(1) # x.go:18:13
515         //      .   AS2-Lhs
516         //      .   .   NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13
517         //      .   AS2-Rhs
518         //      .   .   LITERAL-400 int tc(1) # x.go:18:14
519         //      .   INLMARK Index:1 # +x.go:18:13
520         //      INLCALL PTR-*T tc(1) # x.go:18:13
521         //      INLCALL-Body
522         //      .   BLOCK tc(1) # x.go:18:13
523         //      .   BLOCK-List
524         //      .   .   DCL tc(1) # x.go:18:13
525         //      .   .   .   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
526         //      .   .   AS2 tc(1) # x.go:18:13
527         //      .   .   AS2-Lhs
528         //      .   .   .   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
529         //      .   .   AS2-Rhs
530         //      .   .   .   INLINED RETURN ARGUMENT HERE
531         //      .   .   GOTO p..i1 tc(1) # x.go:18:13
532         //      .   LABEL p..i1 # x.go:18:13
533         //      INLCALL-ReturnVars
534         //      .   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
535         //
536         // In non-unified IR, the tree is slightly different:
537         //  - if there are no arguments to the inlined function,
538         //    the INLCALL-init omits the AS2.
539         //  - the DCL inside BLOCK is on the AS2's init list,
540         //    not its own statement in the top level of the BLOCK.
541         //
542         // If the init values are side-effect-free and each either only
543         // appears once in the function body or is safely repeatable,
544         // then we inline the value expressions into the return argument
545         // and then call StaticAssign to handle that copy.
546         //
547         // This handles simple cases like
548         //
549         //      var myError = errors.New("mine")
550         //
551         // where errors.New is
552         //
553         //      func New(text string) error {
554         //              return &errorString{text}
555         //      }
556         //
557         // We could make things more sophisticated but this kind of initializer
558         // is the most important case for us to get right.
559
560         init := call.Init()
561         var as2init *ir.AssignListStmt
562         if len(init) == 2 && init[0].Op() == ir.OAS2 && init[1].Op() == ir.OINLMARK {
563                 as2init = init[0].(*ir.AssignListStmt)
564         } else if len(init) == 1 && init[0].Op() == ir.OINLMARK {
565                 as2init = new(ir.AssignListStmt)
566         } else {
567                 return false
568         }
569         if len(call.Body) != 2 || call.Body[0].Op() != ir.OBLOCK || call.Body[1].Op() != ir.OLABEL {
570                 return false
571         }
572         label := call.Body[1].(*ir.LabelStmt).Label
573         block := call.Body[0].(*ir.BlockStmt)
574         list := block.List
575         var dcl *ir.Decl
576         if len(list) == 3 && list[0].Op() == ir.ODCL {
577                 dcl = list[0].(*ir.Decl)
578                 list = list[1:]
579         }
580         if len(list) != 2 ||
581                 list[0].Op() != ir.OAS2 ||
582                 list[1].Op() != ir.OGOTO ||
583                 list[1].(*ir.BranchStmt).Label != label {
584                 return false
585         }
586         as2body := list[0].(*ir.AssignListStmt)
587         if dcl == nil {
588                 ainit := as2body.Init()
589                 if len(ainit) != 1 || ainit[0].Op() != ir.ODCL {
590                         return false
591                 }
592                 dcl = ainit[0].(*ir.Decl)
593         }
594         if len(as2body.Lhs) != 1 || as2body.Lhs[0] != dcl.X {
595                 return false
596         }
597
598         // Can't remove the parameter variables if an address is taken.
599         for _, v := range as2init.Lhs {
600                 if v.(*ir.Name).Addrtaken() {
601                         return false
602                 }
603         }
604         // Can't move the computation of the args if they have side effects.
605         for _, r := range as2init.Rhs {
606                 if AnySideEffects(r) {
607                         return false
608                 }
609         }
610
611         // Can only substitute arg for param if param is used
612         // at most once or is repeatable.
613         count := make(map[*ir.Name]int)
614         for _, x := range as2init.Lhs {
615                 count[x.(*ir.Name)] = 0
616         }
617
618         hasNonTrivialClosure := false
619         ir.Visit(as2body.Rhs[0], func(n ir.Node) {
620                 if name, ok := n.(*ir.Name); ok {
621                         if c, ok := count[name]; ok {
622                                 count[name] = c + 1
623                         }
624                 }
625                 if clo, ok := n.(*ir.ClosureExpr); ok {
626                         hasNonTrivialClosure = hasNonTrivialClosure || !ir.IsTrivialClosure(clo)
627                 }
628         })
629
630         // If there's a non-trivial closure, it has captured the param,
631         // so we can't substitute arg for param.
632         if hasNonTrivialClosure {
633                 return false
634         }
635
636         for name, c := range count {
637                 if c > 1 {
638                         // Check whether corresponding initializer can be repeated.
639                         // Something like 1 can be; make(chan int) or &T{} cannot,
640                         // because they need to evaluate to the same result in each use.
641                         for i, n := range as2init.Lhs {
642                                 if n == name && !canRepeat(as2init.Rhs[i]) {
643                                         return false
644                                 }
645                         }
646                 }
647         }
648
649         // Possible static init.
650         // Build tree with args substituted for params and try it.
651         args := make(map[*ir.Name]ir.Node)
652         for i, v := range as2init.Lhs {
653                 if ir.IsBlank(v) {
654                         continue
655                 }
656                 args[v.(*ir.Name)] = as2init.Rhs[i]
657         }
658         r, ok := subst(as2body.Rhs[0], args)
659         if !ok {
660                 return false
661         }
662         ok = s.StaticAssign(l, loff, r, typ)
663
664         if ok && base.Flag.Percent != 0 {
665                 ir.Dump("static inlined-LEFT", l)
666                 ir.Dump("static inlined-ORIG", call)
667                 ir.Dump("static inlined-RIGHT", r)
668         }
669         return ok
670 }
671
672 // from here down is the walk analysis
673 // of composite literals.
674 // most of the work is to generate
675 // data statements for the constant
676 // part of the composite literal.
677
678 var statuniqgen int // name generator for static temps
679
680 // StaticName returns a name backed by a (writable) static data symbol.
681 // Use readonlystaticname for read-only node.
682 func StaticName(t *types.Type) *ir.Name {
683         // Don't use LookupNum; it interns the resulting string, but these are all unique.
684         sym := typecheck.Lookup(fmt.Sprintf("%s%d", obj.StaticNamePref, statuniqgen))
685         statuniqgen++
686
687         n := ir.NewNameAt(base.Pos, sym, t)
688         sym.Def = n
689
690         n.Class = ir.PEXTERN
691         typecheck.Target.Externs = append(typecheck.Target.Externs, n)
692
693         n.Linksym().Set(obj.AttrStatic, true)
694         return n
695 }
696
697 // StaticLoc returns the static address of n, if n has one, or else nil.
698 func StaticLoc(n ir.Node) (name *ir.Name, offset int64, ok bool) {
699         if n == nil {
700                 return nil, 0, false
701         }
702
703         switch n.Op() {
704         case ir.ONAME:
705                 n := n.(*ir.Name)
706                 return n, 0, true
707
708         case ir.OMETHEXPR:
709                 n := n.(*ir.SelectorExpr)
710                 return StaticLoc(n.FuncName())
711
712         case ir.ODOT:
713                 n := n.(*ir.SelectorExpr)
714                 if name, offset, ok = StaticLoc(n.X); !ok {
715                         break
716                 }
717                 offset += n.Offset()
718                 return name, offset, true
719
720         case ir.OINDEX:
721                 n := n.(*ir.IndexExpr)
722                 if n.X.Type().IsSlice() {
723                         break
724                 }
725                 if name, offset, ok = StaticLoc(n.X); !ok {
726                         break
727                 }
728                 l := getlit(n.Index)
729                 if l < 0 {
730                         break
731                 }
732
733                 // Check for overflow.
734                 if n.Type().Size() != 0 && types.MaxWidth/n.Type().Size() <= int64(l) {
735                         break
736                 }
737                 offset += int64(l) * n.Type().Size()
738                 return name, offset, true
739         }
740
741         return nil, 0, false
742 }
743
744 func isSideEffect(n ir.Node) bool {
745         switch n.Op() {
746         // Assume side effects unless we know otherwise.
747         default:
748                 return true
749
750         // No side effects here (arguments are checked separately).
751         case ir.ONAME,
752                 ir.ONONAME,
753                 ir.OTYPE,
754                 ir.OLITERAL,
755                 ir.ONIL,
756                 ir.OADD,
757                 ir.OSUB,
758                 ir.OOR,
759                 ir.OXOR,
760                 ir.OADDSTR,
761                 ir.OADDR,
762                 ir.OANDAND,
763                 ir.OBYTES2STR,
764                 ir.ORUNES2STR,
765                 ir.OSTR2BYTES,
766                 ir.OSTR2RUNES,
767                 ir.OCAP,
768                 ir.OCOMPLIT,
769                 ir.OMAPLIT,
770                 ir.OSTRUCTLIT,
771                 ir.OARRAYLIT,
772                 ir.OSLICELIT,
773                 ir.OPTRLIT,
774                 ir.OCONV,
775                 ir.OCONVIFACE,
776                 ir.OCONVNOP,
777                 ir.ODOT,
778                 ir.OEQ,
779                 ir.ONE,
780                 ir.OLT,
781                 ir.OLE,
782                 ir.OGT,
783                 ir.OGE,
784                 ir.OKEY,
785                 ir.OSTRUCTKEY,
786                 ir.OLEN,
787                 ir.OMUL,
788                 ir.OLSH,
789                 ir.ORSH,
790                 ir.OAND,
791                 ir.OANDNOT,
792                 ir.ONEW,
793                 ir.ONOT,
794                 ir.OBITNOT,
795                 ir.OPLUS,
796                 ir.ONEG,
797                 ir.OOROR,
798                 ir.OPAREN,
799                 ir.ORUNESTR,
800                 ir.OREAL,
801                 ir.OIMAG,
802                 ir.OCOMPLEX:
803                 return false
804
805         // Only possible side effect is division by zero.
806         case ir.ODIV, ir.OMOD:
807                 n := n.(*ir.BinaryExpr)
808                 if n.Y.Op() != ir.OLITERAL || constant.Sign(n.Y.Val()) == 0 {
809                         return true
810                 }
811
812         // Only possible side effect is panic on invalid size,
813         // but many makechan and makemap use size zero, which is definitely OK.
814         case ir.OMAKECHAN, ir.OMAKEMAP:
815                 n := n.(*ir.MakeExpr)
816                 if !ir.IsConst(n.Len, constant.Int) || constant.Sign(n.Len.Val()) != 0 {
817                         return true
818                 }
819
820         // Only possible side effect is panic on invalid size.
821         // TODO(rsc): Merge with previous case (probably breaks toolstash -cmp).
822         case ir.OMAKESLICE, ir.OMAKESLICECOPY:
823                 return true
824         }
825         return false
826 }
827
828 // AnySideEffects reports whether n contains any operations that could have observable side effects.
829 func AnySideEffects(n ir.Node) bool {
830         return ir.Any(n, isSideEffect)
831 }
832
833 // canRepeat reports whether executing n multiple times has the same effect as
834 // assigning n to a single variable and using that variable multiple times.
835 func canRepeat(n ir.Node) bool {
836         bad := func(n ir.Node) bool {
837                 if isSideEffect(n) {
838                         return true
839                 }
840                 switch n.Op() {
841                 case ir.OMAKECHAN,
842                         ir.OMAKEMAP,
843                         ir.OMAKESLICE,
844                         ir.OMAKESLICECOPY,
845                         ir.OMAPLIT,
846                         ir.ONEW,
847                         ir.OPTRLIT,
848                         ir.OSLICELIT,
849                         ir.OSTR2BYTES,
850                         ir.OSTR2RUNES:
851                         return true
852                 }
853                 return false
854         }
855         return !ir.Any(n, bad)
856 }
857
858 func getlit(lit ir.Node) int {
859         if ir.IsSmallIntConst(lit) {
860                 return int(ir.Int64Val(lit))
861         }
862         return -1
863 }
864
865 func isvaluelit(n ir.Node) bool {
866         return n.Op() == ir.OARRAYLIT || n.Op() == ir.OSTRUCTLIT
867 }
868
869 func subst(n ir.Node, m map[*ir.Name]ir.Node) (ir.Node, bool) {
870         valid := true
871         var edit func(ir.Node) ir.Node
872         edit = func(x ir.Node) ir.Node {
873                 switch x.Op() {
874                 case ir.ONAME:
875                         x := x.(*ir.Name)
876                         if v, ok := m[x]; ok {
877                                 return ir.DeepCopy(v.Pos(), v)
878                         }
879                         return x
880                 case ir.ONONAME, ir.OLITERAL, ir.ONIL, ir.OTYPE:
881                         return x
882                 }
883                 x = ir.Copy(x)
884                 ir.EditChildrenWithHidden(x, edit)
885
886                 // TODO: handle more operations, see details discussion in go.dev/cl/466277.
887                 switch x.Op() {
888                 case ir.OCONV:
889                         x := x.(*ir.ConvExpr)
890                         if x.X.Op() == ir.OLITERAL {
891                                 if x, ok := truncate(x.X, x.Type()); ok {
892                                         return x
893                                 }
894                                 valid = false
895                                 return x
896                         }
897                 case ir.OADDSTR:
898                         return addStr(x.(*ir.AddStringExpr))
899                 }
900                 return x
901         }
902         n = edit(n)
903         return n, valid
904 }
905
906 // truncate returns the result of force converting c to type t,
907 // truncating its value as needed, like a conversion of a variable.
908 // If the conversion is too difficult, truncate returns nil, false.
909 func truncate(c ir.Node, t *types.Type) (ir.Node, bool) {
910         ct := c.Type()
911         cv := c.Val()
912         if ct.Kind() != t.Kind() {
913                 switch {
914                 default:
915                         // Note: float -> float/integer and complex -> complex are valid but subtle.
916                         // For example a float32(float64 1e300) evaluates to +Inf at runtime
917                         // and the compiler doesn't have any concept of +Inf, so that would
918                         // have to be left for runtime code evaluation.
919                         // For now
920                         return nil, false
921
922                 case ct.IsInteger() && t.IsInteger():
923                         // truncate or sign extend
924                         bits := t.Size() * 8
925                         cv = constant.BinaryOp(cv, token.AND, constant.MakeUint64(1<<bits-1))
926                         if t.IsSigned() && constant.Compare(cv, token.GEQ, constant.MakeUint64(1<<(bits-1))) {
927                                 cv = constant.BinaryOp(cv, token.OR, constant.MakeInt64(-1<<(bits-1)))
928                         }
929                 }
930         }
931         c = ir.NewConstExpr(cv, c)
932         c.SetType(t)
933         return c, true
934 }
935
936 func addStr(n *ir.AddStringExpr) ir.Node {
937         // Merge adjacent constants in the argument list.
938         s := n.List
939         need := 0
940         for i := 0; i < len(s); i++ {
941                 if i == 0 || !ir.IsConst(s[i-1], constant.String) || !ir.IsConst(s[i], constant.String) {
942                         // Can't merge s[i] into s[i-1]; need a slot in the list.
943                         need++
944                 }
945         }
946         if need == len(s) {
947                 return n
948         }
949         if need == 1 {
950                 var strs []string
951                 for _, c := range s {
952                         strs = append(strs, ir.StringVal(c))
953                 }
954                 return typecheck.OrigConst(n, constant.MakeString(strings.Join(strs, "")))
955         }
956         newList := make([]ir.Node, 0, need)
957         for i := 0; i < len(s); i++ {
958                 if ir.IsConst(s[i], constant.String) && i+1 < len(s) && ir.IsConst(s[i+1], constant.String) {
959                         // merge from i up to but not including i2
960                         var strs []string
961                         i2 := i
962                         for i2 < len(s) && ir.IsConst(s[i2], constant.String) {
963                                 strs = append(strs, ir.StringVal(s[i2]))
964                                 i2++
965                         }
966
967                         nl := ir.Copy(n).(*ir.AddStringExpr)
968                         nl.List = s[i:i2]
969                         newList = append(newList, typecheck.OrigConst(nl, constant.MakeString(strings.Join(strs, ""))))
970                         i = i2 - 1
971                 } else {
972                         newList = append(newList, s[i])
973                 }
974         }
975
976         nn := ir.Copy(n).(*ir.AddStringExpr)
977         nn.List = newList
978         return nn
979 }
980
981 const wrapGlobalMapInitSizeThreshold = 20
982
983 // tryWrapGlobalInit returns a new outlined function to contain global
984 // initializer statement n, if possible and worthwhile. Otherwise, it
985 // returns nil.
986 //
987 // Currently, it outlines map assignment statements with large,
988 // side-effect-free RHS expressions.
989 func tryWrapGlobalInit(n ir.Node) *ir.Func {
990         // Look for "X = ..." where X has map type.
991         // FIXME: might also be worth trying to look for cases where
992         // the LHS is of interface type but RHS is map type.
993         if n.Op() != ir.OAS {
994                 return nil
995         }
996         as := n.(*ir.AssignStmt)
997         if ir.IsBlank(as.X) || as.X.Op() != ir.ONAME {
998                 return nil
999         }
1000         nm := as.X.(*ir.Name)
1001         if !nm.Type().IsMap() {
1002                 return nil
1003         }
1004
1005         // Determine size of RHS.
1006         rsiz := 0
1007         ir.Any(as.Y, func(n ir.Node) bool {
1008                 rsiz++
1009                 return false
1010         })
1011         if base.Debug.WrapGlobalMapDbg > 0 {
1012                 fmt.Fprintf(os.Stderr, "=-= mapassign %s %v rhs size %d\n",
1013                         base.Ctxt.Pkgpath, n, rsiz)
1014         }
1015
1016         // Reject smaller candidates if not in stress mode.
1017         if rsiz < wrapGlobalMapInitSizeThreshold && base.Debug.WrapGlobalMapCtl != 2 {
1018                 if base.Debug.WrapGlobalMapDbg > 1 {
1019                         fmt.Fprintf(os.Stderr, "=-= skipping %v size too small at %d\n",
1020                                 nm, rsiz)
1021                 }
1022                 return nil
1023         }
1024
1025         // Reject right hand sides with side effects.
1026         if AnySideEffects(as.Y) {
1027                 if base.Debug.WrapGlobalMapDbg > 0 {
1028                         fmt.Fprintf(os.Stderr, "=-= rejected %v due to side effects\n", nm)
1029                 }
1030                 return nil
1031         }
1032
1033         if base.Debug.WrapGlobalMapDbg > 1 {
1034                 fmt.Fprintf(os.Stderr, "=-= committed for: %+v\n", n)
1035         }
1036
1037         // Create a new function that will (eventually) have this form:
1038         //
1039         //      func map.init.%d() {
1040         //              globmapvar = <map initialization>
1041         //      }
1042         //
1043         // Note: cmd/link expects the function name to contain "map.init".
1044         minitsym := typecheck.LookupNum("map.init.", mapinitgen)
1045         mapinitgen++
1046
1047         fn := ir.NewFunc(n.Pos(), n.Pos(), minitsym, types.NewSignature(nil, nil, nil))
1048         fn.SetInlinabilityChecked(true) // suppress inlining (which would defeat the point)
1049         typecheck.DeclFunc(fn)
1050         if base.Debug.WrapGlobalMapDbg > 0 {
1051                 fmt.Fprintf(os.Stderr, "=-= generated func is %v\n", fn)
1052         }
1053
1054         // NB: we're relying on this phase being run before inlining;
1055         // if for some reason we need to move it after inlining, we'll
1056         // need code here that relocates or duplicates inline temps.
1057
1058         // Insert assignment into function body; mark body finished.
1059         fn.Body = []ir.Node{as}
1060         typecheck.FinishFuncBody()
1061
1062         if base.Debug.WrapGlobalMapDbg > 1 {
1063                 fmt.Fprintf(os.Stderr, "=-= mapvar is %v\n", nm)
1064                 fmt.Fprintf(os.Stderr, "=-= newfunc is %+v\n", fn)
1065         }
1066
1067         recordFuncForVar(nm, fn)
1068
1069         return fn
1070 }
1071
1072 // mapinitgen is a counter used to uniquify compiler-generated
1073 // map init functions.
1074 var mapinitgen int
1075
1076 // AddKeepRelocations adds a dummy "R_KEEP" relocation from each
1077 // global map variable V to its associated outlined init function.
1078 // These relocation ensure that if the map var itself is determined to
1079 // be reachable at link time, we also mark the init function as
1080 // reachable.
1081 func AddKeepRelocations() {
1082         if varToMapInit == nil {
1083                 return
1084         }
1085         for k, v := range varToMapInit {
1086                 // Add R_KEEP relocation from map to init function.
1087                 fs := v.Linksym()
1088                 if fs == nil {
1089                         base.Fatalf("bad: func %v has no linksym", v)
1090                 }
1091                 vs := k.Linksym()
1092                 if vs == nil {
1093                         base.Fatalf("bad: mapvar %v has no linksym", k)
1094                 }
1095                 r := obj.Addrel(vs)
1096                 r.Sym = fs
1097                 r.Type = objabi.R_KEEP
1098                 if base.Debug.WrapGlobalMapDbg > 1 {
1099                         fmt.Fprintf(os.Stderr, "=-= add R_KEEP relo from %s to %s\n",
1100                                 vs.Name, fs.Name)
1101                 }
1102         }
1103         varToMapInit = nil
1104 }
1105
1106 // OutlineMapInits replaces global map initializers with outlined
1107 // calls to separate "map init" functions (where possible and
1108 // profitable), to facilitate better dead-code elimination by the
1109 // linker.
1110 func OutlineMapInits(fn *ir.Func) {
1111         if base.Debug.WrapGlobalMapCtl == 1 {
1112                 return
1113         }
1114
1115         outlined := 0
1116         for i, stmt := range fn.Body {
1117                 // Attempt to outline stmt. If successful, replace it with a call
1118                 // to the returned wrapper function.
1119                 if wrapperFn := tryWrapGlobalInit(stmt); wrapperFn != nil {
1120                         ir.WithFunc(fn, func() {
1121                                 fn.Body[i] = typecheck.Call(stmt.Pos(), wrapperFn.Nname, nil, false)
1122                         })
1123                         outlined++
1124                 }
1125         }
1126
1127         if base.Debug.WrapGlobalMapDbg > 1 {
1128                 fmt.Fprintf(os.Stderr, "=-= outlined %v map initializations\n", outlined)
1129         }
1130 }