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
5package staticinit
6
7import (
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
25type Entry struct {
26	Xoffset int64   // struct, array only
27	Expr    ir.Node // bytes of run-time computed expressions
28}
29
30type 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.
38type 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	// seenMutation tracks whether we've seen an initialization
47	// expression that may have modified other package-scope variables
48	// within this package.
49	seenMutation bool
50}
51
52func (s *Schedule) append(n ir.Node) {
53	s.Out = append(s.Out, n)
54}
55
56// StaticInit adds an initialization statement n to the schedule.
57func (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// varToMapInit holds book-keeping state for global map initialization;
67// it records the init function created by the compiler to host the
68// initialization code for the map in question.
69var varToMapInit map[*ir.Name]*ir.Func
70
71// MapInitToVar is the inverse of VarToMapInit; it maintains a mapping
72// from a compiler-generated init function to the map the function is
73// initializing.
74var MapInitToVar map[*ir.Func]*ir.Name
75
76// recordFuncForVar establishes a mapping between global map var "v" and
77// outlined init function "fn" (and vice versa); so that we can use
78// the mappings later on to update relocations.
79func 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// allBlank reports whether every node in exprs is blank.
89func 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// tryStaticInit attempts to statically execute an initialization
99// statement and reports whether it succeeded.
100func (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		// Usually OAS2 has been rewritten to separate OASes by types2.
112		// What's left here is "var a, b = tmp1, tmp2" as a result from rewriting
113		// "var a, b = f()" that needs type conversion, which is not static.
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 // outlined map init call; no mutations
132	}
133
134	if !s.seenMutation {
135		s.seenMutation = mayModifyPkgVar(rhs)
136	}
137
138	if allBlank(lhs) && !AnySideEffects(rhs) {
139		return true // discard
140	}
141
142	// Only worry about simple "l = r" assignments. The OAS2*
143	// assignments mostly necessitate dynamic execution anyway.
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// like staticassign but we are copying an already
156// initialized value r.
157func (s *Schedule) staticcopy(l *ir.Name, loff int64, rn *ir.Name, typ *types.Type) bool {
158	if rn.Class == ir.PFUNC {
159		// TODO if roff != 0 { panic }
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		// No explicit initialization value. Probably zeroed but perhaps
168		// supplied externally and of unknown value.
169		return false
170	}
171	if rn.Defn.Op() != ir.OAS {
172		return false
173	}
174	if rn.Type().IsString() { // perhaps overwritten by cmd/link -X (#34675)
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		// types2.InitOrder doesn't include default initializers.
184		base.Fatalf("unexpected initializer: %v", rn.Defn)
185	}
186
187	// Variable may have been reassigned by a user-written function call
188	// that was invoked to initialize another global variable (#51913).
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		// We may have skipped past one or more OCONVNOPs, so
210		// use conv to ensure r is assignable to l (#13263).
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			// copy pointer
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		// copy slice
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			// Requires computation, but we're
268			// copying someone else's computation.
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
281func (s *Schedule) StaticAssign(l *ir.Name, loff int64, r ir.Node, typ *types.Type) bool {
282	if r == nil {
283		// No explicit initialization value. Either zero or supplied
284		// externally.
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			// Don't use NameOffsetExpr with blank (#43677).
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			// Init pointer.
337			a := StaticName(r.X.Type())
338
339			s.Temps[r] = a
340			staticdata.InitAddr(l, loff, a.Linksym())
341
342			// Init underlying literal.
343			assign(base.Pos, a, 0, r.X)
344			return true
345		}
346		//dump("not static ptrlit", r);
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		// Init slice.
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		// Fall through to init underlying array.
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			// Issue 59680: if the closure we're looking at was produced
397			// by inlining, it could be marked as hidden, which we don't
398			// want (moving the func to a static init will effectively
399			// hide it from escape analysis). Mark as non-hidden here.
400			// so that it will participated in escape analysis.
401			r.Func.SetIsHiddenClosure(false)
402			// Closures with no captured variables are globals,
403			// so the assignment can be done at link time.
404			// TODO if roff != 0 { panic }
405			staticdata.InitAddr(l, loff, staticdata.FuncLinksym(r.Func.Nname))
406			return true
407		}
408		ir.ClosureDebugRuntimeCheck(r)
409
410	case ir.OCONVIFACE:
411		// This logic is mirrored in isStaticCompositeLiteral.
412		// If you change something here, change it there, and vice versa.
413
414		// Determine the underlying concrete type and value we are converting from.
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			// val is an interface type.
423			// If val is nil, we can statically initialize l;
424			// both words are zero and so there no work to do, so report success.
425			// If val is non-nil, we have no concrete type to record,
426			// and we won't be able to statically initialize its value, so report failure.
427			return val.Op() == ir.ONIL
428		}
429
430		if val.Type().HasShape() {
431			// See comment in cmd/compile/internal/walk/convert.go:walkConvInterface
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		// Create a copy of l to modify while we emit data.
445
446		// Emit itab, advance offset.
447		staticdata.InitAddr(l, loff, itab.X.(*ir.LinksymOffsetExpr).Linksym)
448
449		// Emit data.
450		if types.IsDirectIface(val.Type()) {
451			if val.Op() == ir.ONIL {
452				// Nil is zero, nothing to do.
453				return true
454			}
455			// Copy val directly into n.
456			ir.SetPos(val)
457			assign(base.Pos, l, loff+int64(types.PtrSize), val)
458		} else {
459			// Construct temp to hold val, write pointer to temp into n.
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
479func (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
530func (s *Schedule) addvalue(p *Plan, xoffset int64, n ir.Node) {
531	// special case: zero can be dropped entirely
532	if ir.IsZero(n) {
533		return
534	}
535
536	// special case: inline struct and array (not slice) literals
537	if isvaluelit(n) {
538		s.initplan(n)
539		q := s.Plans[n]
540		for _, qe := range q.E {
541			// qe is a copy; we are not modifying entries in q.E
542			qe.Xoffset += xoffset
543			p.E = append(p.E, qe)
544		}
545		return
546	}
547
548	// add to plan
549	p.E = append(p.E, Entry{Xoffset: xoffset, Expr: n})
550}
551
552func (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	// Handle the special case of an inlined call of
558	// a function body with a single return statement,
559	// which turns into a single assignment plus a goto.
560	//
561	// For example code like this:
562	//
563	//	type T struct{ x int }
564	//	func F(x int) *T { return &T{x} }
565	//	var Global = F(400)
566	//
567	// turns into IR like this:
568	//
569	// 	INLCALL-init
570	// 	.   AS2-init
571	// 	.   .   DCL # x.go:18:13
572	// 	.   .   .   NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13
573	// 	.   AS2 Def tc(1) # x.go:18:13
574	// 	.   AS2-Lhs
575	// 	.   .   NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13
576	// 	.   AS2-Rhs
577	// 	.   .   LITERAL-400 int tc(1) # x.go:18:14
578	// 	.   INLMARK Index:1 # +x.go:18:13
579	// 	INLCALL PTR-*T tc(1) # x.go:18:13
580	// 	INLCALL-Body
581	// 	.   BLOCK tc(1) # x.go:18:13
582	// 	.   BLOCK-List
583	// 	.   .   DCL tc(1) # x.go:18:13
584	// 	.   .   .   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
585	// 	.   .   AS2 tc(1) # x.go:18:13
586	// 	.   .   AS2-Lhs
587	// 	.   .   .   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
588	// 	.   .   AS2-Rhs
589	// 	.   .   .   INLINED RETURN ARGUMENT HERE
590	// 	.   .   GOTO p..i1 tc(1) # x.go:18:13
591	// 	.   LABEL p..i1 # x.go:18:13
592	// 	INLCALL-ReturnVars
593	// 	.   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
594	//
595	// In non-unified IR, the tree is slightly different:
596	//  - if there are no arguments to the inlined function,
597	//    the INLCALL-init omits the AS2.
598	//  - the DCL inside BLOCK is on the AS2's init list,
599	//    not its own statement in the top level of the BLOCK.
600	//
601	// If the init values are side-effect-free and each either only
602	// appears once in the function body or is safely repeatable,
603	// then we inline the value expressions into the return argument
604	// and then call StaticAssign to handle that copy.
605	//
606	// This handles simple cases like
607	//
608	//	var myError = errors.New("mine")
609	//
610	// where errors.New is
611	//
612	//	func New(text string) error {
613	//		return &errorString{text}
614	//	}
615	//
616	// We could make things more sophisticated but this kind of initializer
617	// is the most important case for us to get right.
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	// Can't remove the parameter variables if an address is taken.
658	for _, v := range as2init.Lhs {
659		if v.(*ir.Name).Addrtaken() {
660			return false
661		}
662	}
663	// Can't move the computation of the args if they have side effects.
664	for _, r := range as2init.Rhs {
665		if AnySideEffects(r) {
666			return false
667		}
668	}
669
670	// Can only substitute arg for param if param is used
671	// at most once or is repeatable.
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	// If there's a non-trivial closure, it has captured the param,
690	// so we can't substitute arg for param.
691	if hasNonTrivialClosure {
692		return false
693	}
694
695	for name, c := range count {
696		if c > 1 {
697			// Check whether corresponding initializer can be repeated.
698			// Something like 1 can be; make(chan int) or &T{} cannot,
699			// because they need to evaluate to the same result in each use.
700			for i, n := range as2init.Lhs {
701				if n == name && !canRepeat(as2init.Rhs[i]) {
702					return false
703				}
704			}
705		}
706	}
707
708	// Possible static init.
709	// Build tree with args substituted for params and try it.
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// from here down is the walk analysis
732// of composite literals.
733// most of the work is to generate
734// data statements for the constant
735// part of the composite literal.
736
737var statuniqgen int // name generator for static temps
738
739// StaticName returns a name backed by a (writable) static data symbol.
740// Use readonlystaticname for read-only node.
741func StaticName(t *types.Type) *ir.Name {
742	// Don't use LookupNum; it interns the resulting string, but these are all unique.
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// StaticLoc returns the static address of n, if n has one, or else nil.
757func 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		// Check for overflow.
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
803func isSideEffect(n ir.Node) bool {
804	switch n.Op() {
805	// Assume side effects unless we know otherwise.
806	default:
807		return true
808
809	// No side effects here (arguments are checked separately).
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	// Only possible side effect is division by zero.
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	// Only possible side effect is panic on invalid size,
872	// but many makechan and makemap use size zero, which is definitely OK.
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	// Only possible side effect is panic on invalid size.
880	// TODO(rsc): Merge with previous case (probably breaks toolstash -cmp).
881	case ir.OMAKESLICE, ir.OMAKESLICECOPY:
882		return true
883	}
884	return false
885}
886
887// AnySideEffects reports whether n contains any operations that could have observable side effects.
888func AnySideEffects(n ir.Node) bool {
889	return ir.Any(n, isSideEffect)
890}
891
892// mayModifyPkgVar reports whether expression n may modify any
893// package-scope variables declared within the current package.
894func mayModifyPkgVar(n ir.Node) bool {
895	// safeLHS reports whether the assigned-to variable lhs is either a
896	// local variable or a global from another package.
897	safeLHS := func(lhs ir.Node) bool {
898		outer := ir.OuterValue(lhs)
899		// "*p = ..." should be safe if p is a local variable.
900		// TODO: Should ir.OuterValue handle this?
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 // could mutate a global array
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// canRepeat reports whether executing n multiple times has the same effect as
942// assigning n to a single variable and using that variable multiple times.
943func 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
966func getlit(lit ir.Node) int {
967	if ir.IsSmallIntConst(lit) {
968		return int(ir.Int64Val(lit))
969	}
970	return -1
971}
972
973func isvaluelit(n ir.Node) bool {
974	return n.Op() == ir.OARRAYLIT || n.Op() == ir.OSTRUCTLIT
975}
976
977func 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		// TODO: handle more operations, see details discussion in go.dev/cl/466277.
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// truncate returns the result of force converting c to type t,
1015// truncating its value as needed, like a conversion of a variable.
1016// If the conversion is too difficult, truncate returns nil, false.
1017func 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			// Note: float -> float/integer and complex -> complex are valid but subtle.
1024			// For example a float32(float64 1e300) evaluates to +Inf at runtime
1025			// and the compiler doesn't have any concept of +Inf, so that would
1026			// have to be left for runtime code evaluation.
1027			// For now
1028			return nil, false
1029
1030		case ct.IsInteger() && t.IsInteger():
1031			// truncate or sign extend
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
1044func addStr(n *ir.AddStringExpr) ir.Node {
1045	// Merge adjacent constants in the argument list.
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			// Can't merge s[i] into s[i-1]; need a slot in the list.
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			// merge from i up to but not including i2
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
1087const wrapGlobalMapInitSizeThreshold = 20
1088
1089// tryWrapGlobalInit returns a new outlined function to contain global
1090// initializer statement n, if possible and worthwhile. Otherwise, it
1091// returns nil.
1092//
1093// Currently, it outlines map assignment statements with large,
1094// side-effect-free RHS expressions.
1095func tryWrapGlobalInit(n ir.Node) *ir.Func {
1096	// Look for "X = ..." where X has map type.
1097	// FIXME: might also be worth trying to look for cases where
1098	// the LHS is of interface type but RHS is map type.
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	// Determine size of RHS.
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	// Reject smaller candidates if not in stress mode.
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	// Reject right hand sides with side effects.
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	// Create a new function that will (eventually) have this form:
1144	//
1145	//	func map.init.%d() {
1146	//		globmapvar = <map initialization>
1147	//	}
1148	//
1149	// Note: cmd/link expects the function name to contain "map.init".
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) // suppress inlining (which would defeat the point)
1155	typecheck.DeclFunc(fn)
1156	if base.Debug.WrapGlobalMapDbg > 0 {
1157		fmt.Fprintf(os.Stderr, "=-= generated func is %v\n", fn)
1158	}
1159
1160	// NB: we're relying on this phase being run before inlining;
1161	// if for some reason we need to move it after inlining, we'll
1162	// need code here that relocates or duplicates inline temps.
1163
1164	// Insert assignment into function body; mark body finished.
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// mapinitgen is a counter used to uniquify compiler-generated
1179// map init functions.
1180var mapinitgen int
1181
1182// AddKeepRelocations adds a dummy "R_KEEP" relocation from each
1183// global map variable V to its associated outlined init function.
1184// These relocation ensure that if the map var itself is determined to
1185// be reachable at link time, we also mark the init function as
1186// reachable.
1187func AddKeepRelocations() {
1188	if varToMapInit == nil {
1189		return
1190	}
1191	for k, v := range varToMapInit {
1192		// Add R_KEEP relocation from map to init function.
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// OutlineMapInits replaces global map initializers with outlined
1213// calls to separate "map init" functions (where possible and
1214// profitable), to facilitate better dead-code elimination by the
1215// linker.
1216func 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		// Attempt to outline stmt. If successful, replace it with a call
1224		// to the returned wrapper function.
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