Source file 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  func canWriteStatic(off int64, typ *types.Type) bool {
    35  	if off < 0 || off >= obj.MaxDataOffset {
    36  		return false
    37  	}
    38  	// Arrays and structs are checked one plan entry at a time.
    39  	return typ.IsArray() || typ.IsStruct() || typ.Size() <= obj.MaxDataOffset-off
    40  }
    41  
    42  // An Schedule is used to decompose assignment statements into
    43  // static and dynamic initialization parts. Static initializations are
    44  // handled by populating variables' linker symbol data, while dynamic
    45  // initializations are accumulated to be executed in order.
    46  type Schedule struct {
    47  	// Out is the ordered list of dynamic initialization
    48  	// statements.
    49  	Out []ir.Node
    50  
    51  	Plans map[ir.Node]*Plan
    52  	Temps map[ir.Node]*ir.Name
    53  
    54  	// seenMutation tracks whether we've seen an initialization
    55  	// expression that may have modified other package-scope variables
    56  	// within this package.
    57  	seenMutation bool
    58  }
    59  
    60  func (s *Schedule) append(n ir.Node) {
    61  	s.Out = append(s.Out, n)
    62  }
    63  
    64  // StaticInit adds an initialization statement n to the schedule.
    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  // varToMapInit holds book-keeping state for global map initialization;
    75  // it records the init function created by the compiler to host the
    76  // initialization code for the map in question.
    77  var varToMapInit map[*ir.Name]*ir.Func
    78  
    79  // MapInitToVar is the inverse of VarToMapInit; it maintains a mapping
    80  // from a compiler-generated init function to the map the function is
    81  // initializing.
    82  var MapInitToVar map[*ir.Func]*ir.Name
    83  
    84  // recordFuncForVar establishes a mapping between global map var "v" and
    85  // outlined init function "fn" (and vice versa); so that we can use
    86  // the mappings later on to update relocations.
    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  // allBlank reports whether every node in exprs is blank.
    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  // tryStaticInit attempts to statically execute an initialization
   107  // statement and reports whether it succeeded.
   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  		// Usually OAS2 has been rewritten to separate OASes by types2.
   120  		// What's left here is "var a, b = tmp1, tmp2" as a result from rewriting
   121  		// "var a, b = f()" that needs type conversion, which is not static.
   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 // outlined map init call; no mutations
   140  	}
   141  
   142  	if !s.seenMutation {
   143  		s.seenMutation = mayModifyPkgVar(rhs)
   144  	}
   145  
   146  	if allBlank(lhs) && !AnySideEffects(rhs) {
   147  		return true // discard
   148  	}
   149  
   150  	// Only worry about simple "l = r" assignments. The OAS2*
   151  	// assignments mostly necessitate dynamic execution anyway.
   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  // like staticassign but we are copying an already
   164  // initialized value r.
   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  		// TODO if roff != 0 { panic }
   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  		// No explicit initialization value. Probably zeroed but perhaps
   179  		// supplied externally and of unknown value.
   180  		return false
   181  	}
   182  	if rn.Defn.Op() != ir.OAS {
   183  		return false
   184  	}
   185  	if rn.Type().IsString() { // perhaps overwritten by cmd/link -X (#34675)
   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  		// types2.InitOrder doesn't include default initializers.
   195  		base.Fatalf("unexpected initializer: %v", rn.Defn)
   196  	}
   197  
   198  	// Variable may have been reassigned by a user-written function call
   199  	// that was invoked to initialize another global variable (#51913).
   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  		// We may have skipped past one or more OCONVNOPs, so
   221  		// use conv to ensure r is assignable to l (#13263).
   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 // e.g. local from new(expr)
   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  			// copy pointer
   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  		// copy slice
   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  			// Requires computation, but we're
   283  			// copying someone else's computation.
   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  	// If we're building for FIPS, avoid global data relocations
   298  	// by treating all address-of operations as non-static.
   299  	// See ../../../internal/obj/fips.go for more context.
   300  	// We do this even in non-PIE mode to avoid generating
   301  	// static temporaries that would go into SRODATAFIPS
   302  	// but need relocations. We can't handle that in the verification.
   303  	disableGlobalAddrs := base.Ctxt.IsFIPS()
   304  
   305  	if r == nil {
   306  		// No explicit initialization value. Either zero or supplied
   307  		// externally.
   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  			// Don't use NameOffsetExpr with blank (#43677).
   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  			// Init pointer.
   378  			a := StaticName(r.X.Type())
   379  
   380  			s.Temps[r] = a
   381  			staticdata.InitAddr(l, loff, a.Linksym())
   382  
   383  			// Init underlying literal.
   384  			assign(base.Pos, a, 0, r.X)
   385  			return true
   386  		}
   387  		//dump("not static ptrlit", r);
   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  		// Init slice.
   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  		// Fall through to init underlying array.
   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  			// Closures with no captured variables are globals,
   448  			// so the assignment can be done at link time.
   449  			// TODO if roff != 0 { panic }
   450  			staticdata.InitAddr(l, loff, staticdata.FuncLinksym(r.Func.Nname))
   451  			return true
   452  		}
   453  		ir.ClosureDebugRuntimeCheck(r)
   454  
   455  	case ir.OCONVIFACE:
   456  		// This logic is mirrored in isStaticCompositeLiteral.
   457  		// If you change something here, change it there, and vice versa.
   458  
   459  		if disableGlobalAddrs {
   460  			return false
   461  		}
   462  
   463  		// Determine the underlying concrete type and value we are converting from.
   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  			// val is an interface type.
   472  			// If val is nil, we can statically initialize l;
   473  			// both words are zero and so there no work to do, so report success.
   474  			// If val is non-nil, we have no concrete type to record,
   475  			// and we won't be able to statically initialize its value, so report failure.
   476  			return val.Op() == ir.ONIL
   477  		}
   478  
   479  		if val.Type().HasShape() {
   480  			// See comment in cmd/compile/internal/walk/convert.go:walkConvInterface
   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  		// Create a copy of l to modify while we emit data.
   494  
   495  		// Emit itab, advance offset.
   496  		staticdata.InitAddr(l, loff, itab.X.(*ir.LinksymOffsetExpr).Linksym)
   497  
   498  		// Emit data.
   499  		if types.IsDirectIface(val.Type()) {
   500  			if val.Op() == ir.ONIL {
   501  				// Nil is zero, nothing to do.
   502  				return true
   503  			}
   504  			// Copy val directly into n.
   505  			ir.SetPos(val)
   506  			assign(base.Pos, l, loff+int64(types.PtrSize), val)
   507  		} else {
   508  			// Construct temp to hold val, write pointer to temp into n.
   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  	// special case: zero can be dropped entirely
   581  	if ir.IsZero(n) {
   582  		return
   583  	}
   584  
   585  	// special case: inline struct and array (not slice) literals
   586  	if isvaluelit(n) {
   587  		s.initplan(n)
   588  		q := s.Plans[n]
   589  		for _, qe := range q.E {
   590  			// qe is a copy; we are not modifying entries in q.E
   591  			qe.Xoffset += xoffset
   592  			p.E = append(p.E, qe)
   593  		}
   594  		return
   595  	}
   596  
   597  	// add to plan
   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  	// Handle the special case of an inlined call of
   607  	// a function body with a single return statement,
   608  	// which turns into a single assignment plus a goto.
   609  	//
   610  	// For example code like this:
   611  	//
   612  	//	type T struct{ x int }
   613  	//	func F(x int) *T { return &T{x} }
   614  	//	var Global = F(400)
   615  	//
   616  	// turns into IR like this:
   617  	//
   618  	// 	INLCALL-init
   619  	// 	.   AS2-init
   620  	// 	.   .   DCL # x.go:18:13
   621  	// 	.   .   .   NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13
   622  	// 	.   AS2 Def tc(1) # x.go:18:13
   623  	// 	.   AS2-Lhs
   624  	// 	.   .   NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13
   625  	// 	.   AS2-Rhs
   626  	// 	.   .   LITERAL-400 int tc(1) # x.go:18:14
   627  	// 	.   INLMARK Index:1 # +x.go:18:13
   628  	// 	INLCALL PTR-*T tc(1) # x.go:18:13
   629  	// 	INLCALL-Body
   630  	// 	.   BLOCK tc(1) # x.go:18:13
   631  	// 	.   BLOCK-List
   632  	// 	.   .   DCL tc(1) # x.go:18:13
   633  	// 	.   .   .   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
   634  	// 	.   .   AS2 tc(1) # x.go:18:13
   635  	// 	.   .   AS2-Lhs
   636  	// 	.   .   .   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
   637  	// 	.   .   AS2-Rhs
   638  	// 	.   .   .   INLINED RETURN ARGUMENT HERE
   639  	// 	.   .   GOTO p..i1 tc(1) # x.go:18:13
   640  	// 	.   LABEL p..i1 # x.go:18:13
   641  	// 	INLCALL-ReturnVars
   642  	// 	.   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
   643  	//
   644  	// If the init values are side-effect-free and each either only
   645  	// appears once in the function body or is safely repeatable,
   646  	// then we inline the value expressions into the return argument
   647  	// and then call StaticAssign to handle that copy.
   648  	//
   649  	// This handles simple cases like
   650  	//
   651  	//	var myError = errors.New("mine")
   652  	//
   653  	// where errors.New is
   654  	//
   655  	//	func New(text string) error {
   656  	//		return &errorString{text}
   657  	//	}
   658  	//
   659  	// We could make things more sophisticated but this kind of initializer
   660  	// is the most important case for us to get right.
   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  	// Can't remove the parameter variables if an address is taken.
   688  	for _, v := range as2init.Lhs {
   689  		if v.(*ir.Name).Addrtaken() {
   690  			return false
   691  		}
   692  	}
   693  	// Can't move the computation of the args if they have side effects.
   694  	for _, r := range as2init.Rhs {
   695  		if AnySideEffects(r) {
   696  			return false
   697  		}
   698  	}
   699  
   700  	// Can only substitute arg for param if param is used
   701  	// at most once or is repeatable.
   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  	// If there's a closure, it has captured the param,
   720  	// so we can't substitute arg for param.
   721  	if hasClosure {
   722  		return false
   723  	}
   724  
   725  	for name, c := range count {
   726  		if c > 1 {
   727  			// Check whether corresponding initializer can be repeated.
   728  			// Something like 1 can be; make(chan int) or &T{} cannot,
   729  			// because they need to evaluate to the same result in each use.
   730  			for i, n := range as2init.Lhs {
   731  				if n == name && !canRepeat(as2init.Rhs[i]) {
   732  					return false
   733  				}
   734  			}
   735  		}
   736  	}
   737  
   738  	// Possible static init.
   739  	// Build tree with args substituted for params and try it.
   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  // from here down is the walk analysis
   762  // of composite literals.
   763  // most of the work is to generate
   764  // data statements for the constant
   765  // part of the composite literal.
   766  
   767  var statuniqgen int // name generator for static temps
   768  
   769  // StaticName returns a name backed by a (writable) static data symbol.
   770  func StaticName(t *types.Type) *ir.Name {
   771  	// Don't use LookupNum; it interns the resulting string, but these are all unique.
   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  // StaticLoc returns the static address of n, if n has one, or else nil.
   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  		// Check for overflow.
   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  	// Assume side effects unless we know otherwise.
   837  	default:
   838  		return true
   839  
   840  	// No side effects here (arguments are checked separately).
   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  	// Only possible side effect is division by zero.
   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  	// Only possible side effect is panic on invalid size,
   903  	// but many makechan and makemap use size zero, which is definitely OK.
   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  	// Only possible side effect is panic on invalid size.
   911  	// TODO(rsc): Merge with previous case (probably breaks toolstash -cmp).
   912  	case ir.OMAKESLICE, ir.OMAKESLICECOPY:
   913  		return true
   914  	}
   915  	return false
   916  }
   917  
   918  // AnySideEffects reports whether n contains any operations that could have observable side effects.
   919  func AnySideEffects(n ir.Node) bool {
   920  	return ir.Any(n, isSideEffect)
   921  }
   922  
   923  // mayModifyPkgVar reports whether expression n may modify any
   924  // package-scope variables declared within the current package.
   925  func mayModifyPkgVar(n ir.Node) bool {
   926  	// safeLHS reports whether the assigned-to variable lhs is either a
   927  	// local variable or a global from another package.
   928  	safeLHS := func(lhs ir.Node) bool {
   929  		outer := ir.OuterValue(lhs)
   930  		// "*p = ..." should be safe if p is a local variable.
   931  		// TODO: Should ir.OuterValue handle this?
   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 // could mutate a global array
   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  // canRepeat reports whether executing n multiple times has the same effect as
   973  // assigning n to a single variable and using that variable multiple times.
   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  		// TODO: handle more operations, see details discussion in go.dev/cl/466277.
  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  // truncate returns the result of force converting c to type t,
  1046  // truncating its value as needed, like a conversion of a variable.
  1047  // If the conversion is too difficult, truncate returns nil, false.
  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  			// Note: float -> float/integer and complex -> complex are valid but subtle.
  1055  			// For example a float32(float64 1e300) evaluates to +Inf at runtime
  1056  			// and the compiler doesn't have any concept of +Inf, so that would
  1057  			// have to be left for runtime code evaluation.
  1058  			// For now
  1059  			return nil, false
  1060  
  1061  		case ct.IsInteger() && t.IsInteger():
  1062  			// truncate or sign extend
  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  	// Merge adjacent constants in the argument list.
  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  			// Can't merge s[i] into s[i-1]; need a slot in the list.
  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  			// merge from i up to but not including i2
  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  // tryWrapGlobalInit returns a new outlined function to contain global
  1121  // initializer statement n, if possible and worthwhile. Otherwise, it
  1122  // returns nil.
  1123  //
  1124  // Currently, it outlines map assignment statements with large,
  1125  // side-effect-free RHS expressions.
  1126  func tryWrapGlobalInit(n ir.Node) *ir.Func {
  1127  	// Look for "X = ..." where X has map type.
  1128  	// FIXME: might also be worth trying to look for cases where
  1129  	// the LHS is of interface type but RHS is map type.
  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  	// Determine size of RHS.
  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  	// Reject smaller candidates if not in stress mode.
  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  	// Reject right hand sides with side effects.
  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  	// Create a new function that will (eventually) have this form:
  1175  	//
  1176  	//	func map.init.%d() {
  1177  	//		globmapvar = <map initialization>
  1178  	//	}
  1179  	//
  1180  	// Note: cmd/link expects the function name to contain "map.init".
  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) // suppress inlining (which would defeat the point)
  1186  	typecheck.DeclFunc(fn)
  1187  	if base.Debug.WrapGlobalMapDbg > 0 {
  1188  		fmt.Fprintf(os.Stderr, "=-= generated func is %v\n", fn)
  1189  	}
  1190  
  1191  	// NB: we're relying on this phase being run before inlining;
  1192  	// if for some reason we need to move it after inlining, we'll
  1193  	// need code here that relocates or duplicates inline temps.
  1194  
  1195  	// Insert assignment into function body; mark body finished.
  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  // mapinitgen is a counter used to uniquify compiler-generated
  1210  // map init functions.
  1211  var mapinitgen int
  1212  
  1213  // AddKeepRelocations adds a dummy "R_KEEP" relocation from each
  1214  // global map variable V to its associated outlined init function.
  1215  // These relocation ensure that if the map var itself is determined to
  1216  // be reachable at link time, we also mark the init function as
  1217  // reachable.
  1218  func AddKeepRelocations() {
  1219  	if varToMapInit == nil {
  1220  		return
  1221  	}
  1222  	for k, v := range varToMapInit {
  1223  		// Add R_KEEP relocation from map to init function.
  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  // OutlineMapInits replaces global map initializers with outlined
  1242  // calls to separate "map init" functions (where possible and
  1243  // profitable), to facilitate better dead-code elimination by the
  1244  // linker.
  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  		// Attempt to outline stmt. If successful, replace it with a call
  1253  		// to the returned wrapper function.
  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  // varInitGen is a counter used to uniquify compiler-generated functions for initializing variables.
  1268  var varInitGen int
  1269  
  1270  const varInitFuncPrefix = "init.part."
  1271  
  1272  // GenerateVarInitFunc create a new function that will (eventually) have this form:
  1273  //
  1274  //	func init.part.%d() {
  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) // suppress inlining; otherwise, we end up with giant init eventually.
  1286  	fn.SetWrapper(true)             // less disruptive on backtraces.
  1287  
  1288  	return fn
  1289  }
  1290  
  1291  // CanOptimize reports whether the given fn can be optimized for static assignments.
  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