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  // 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  	// 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  
    52  func (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.
    57  func (s *Schedule) StaticInit(n ir.Node) {
    58  	if !s.tryStaticInit(n) {
    59  		if base.Flag.Percent != 0 {
    60  			ir.Dump("StaticInit failed", n)
    61  		}
    62  		s.append(n)
    63  	}
    64  }
    65  
    66  // 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.
    69  var 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.
    74  var 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.
    79  func recordFuncForVar(v *ir.Name, fn *ir.Func) {
    80  	if varToMapInit == nil {
    81  		varToMapInit = make(map[*ir.Name]*ir.Func)
    82  		MapInitToVar = make(map[*ir.Func]*ir.Name)
    83  	}
    84  	varToMapInit[v] = fn
    85  	MapInitToVar[fn] = v
    86  }
    87  
    88  // allBlank reports whether every node in exprs is blank.
    89  func allBlank(exprs []ir.Node) bool {
    90  	for _, expr := range exprs {
    91  		if !ir.IsBlank(expr) {
    92  			return false
    93  		}
    94  	}
    95  	return true
    96  }
    97  
    98  // tryStaticInit attempts to statically execute an initialization
    99  // statement and reports whether it succeeded.
   100  func (s *Schedule) tryStaticInit(n ir.Node) bool {
   101  	var lhs []ir.Node
   102  	var rhs ir.Node
   103  
   104  	switch n.Op() {
   105  	default:
   106  		base.FatalfAt(n.Pos(), "unexpected initialization statement: %v", n)
   107  	case ir.OAS:
   108  		n := n.(*ir.AssignStmt)
   109  		lhs, rhs = []ir.Node{n.X}, n.Y
   110  	case ir.OAS2:
   111  		// 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.
   157  func (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  
   281  func (s *Schedule) StaticAssign(l *ir.Name, loff int64, r ir.Node, typ *types.Type) bool {
   282  	// If we're building for FIPS, avoid global data relocations
   283  	// by treating all address-of operations as non-static.
   284  	// See ../../../internal/obj/fips.go for more context.
   285  	// We do this even in non-PIE mode to avoid generating
   286  	// static temporaries that would go into SRODATAFIPS
   287  	// but need relocations. We can't handle that in the verification.
   288  	disableGlobalAddrs := base.Ctxt.IsFIPS()
   289  
   290  	if r == nil {
   291  		// No explicit initialization value. Either zero or supplied
   292  		// externally.
   293  		return true
   294  	}
   295  	for r.Op() == ir.OCONVNOP {
   296  		r = r.(*ir.ConvExpr).X
   297  	}
   298  
   299  	assign := func(pos src.XPos, a *ir.Name, aoff int64, v ir.Node) {
   300  		if s.StaticAssign(a, aoff, v, v.Type()) {
   301  			return
   302  		}
   303  		var lhs ir.Node
   304  		if ir.IsBlank(a) {
   305  			// Don't use NameOffsetExpr with blank (#43677).
   306  			lhs = ir.BlankNode
   307  		} else {
   308  			lhs = ir.NewNameOffsetExpr(pos, a, aoff, v.Type())
   309  		}
   310  		s.append(ir.NewAssignStmt(pos, lhs, v))
   311  	}
   312  
   313  	switch r.Op() {
   314  	case ir.ONAME:
   315  		if disableGlobalAddrs {
   316  			return false
   317  		}
   318  		r := r.(*ir.Name)
   319  		return s.staticcopy(l, loff, r, typ)
   320  
   321  	case ir.OMETHEXPR:
   322  		if disableGlobalAddrs {
   323  			return false
   324  		}
   325  		r := r.(*ir.SelectorExpr)
   326  		return s.staticcopy(l, loff, r.FuncName(), typ)
   327  
   328  	case ir.ONIL:
   329  		return true
   330  
   331  	case ir.OLITERAL:
   332  		if ir.IsZero(r) {
   333  			return true
   334  		}
   335  		if disableGlobalAddrs && r.Type().IsString() {
   336  			return false
   337  		}
   338  		staticdata.InitConst(l, loff, r, int(typ.Size()))
   339  		return true
   340  
   341  	case ir.OADDR:
   342  		if disableGlobalAddrs {
   343  			return false
   344  		}
   345  		r := r.(*ir.AddrExpr)
   346  		if name, offset, ok := StaticLoc(r.X); ok && name.Class == ir.PEXTERN {
   347  			staticdata.InitAddrOffset(l, loff, name.Linksym(), offset)
   348  			return true
   349  		}
   350  		fallthrough
   351  
   352  	case ir.OPTRLIT:
   353  		if disableGlobalAddrs {
   354  			return false
   355  		}
   356  		r := r.(*ir.AddrExpr)
   357  		switch r.X.Op() {
   358  		case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT, ir.OSTRUCTLIT:
   359  			// Init pointer.
   360  			a := StaticName(r.X.Type())
   361  
   362  			s.Temps[r] = a
   363  			staticdata.InitAddr(l, loff, a.Linksym())
   364  
   365  			// Init underlying literal.
   366  			assign(base.Pos, a, 0, r.X)
   367  			return true
   368  		}
   369  		//dump("not static ptrlit", r);
   370  
   371  	case ir.OSTR2BYTES:
   372  		if disableGlobalAddrs {
   373  			return false
   374  		}
   375  		r := r.(*ir.ConvExpr)
   376  		if l.Class == ir.PEXTERN && r.X.Op() == ir.OLITERAL {
   377  			sval := ir.StringVal(r.X)
   378  			staticdata.InitSliceBytes(l, loff, sval)
   379  			return true
   380  		}
   381  
   382  	case ir.OSLICELIT:
   383  		if disableGlobalAddrs {
   384  			return false
   385  		}
   386  		r := r.(*ir.CompLitExpr)
   387  		s.initplan(r)
   388  		// Init slice.
   389  		ta := types.NewArray(r.Type().Elem(), r.Len)
   390  		ta.SetNoalg(true)
   391  		a := StaticName(ta)
   392  		s.Temps[r] = a
   393  		staticdata.InitSlice(l, loff, a.Linksym(), r.Len)
   394  		// Fall through to init underlying array.
   395  		l = a
   396  		loff = 0
   397  		fallthrough
   398  
   399  	case ir.OARRAYLIT, ir.OSTRUCTLIT:
   400  		r := r.(*ir.CompLitExpr)
   401  		s.initplan(r)
   402  
   403  		p := s.Plans[r]
   404  		for i := range p.E {
   405  			e := &p.E[i]
   406  			if e.Expr.Op() == ir.OLITERAL && !disableGlobalAddrs || e.Expr.Op() == ir.ONIL {
   407  				staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(e.Expr.Type().Size()))
   408  				continue
   409  			}
   410  			ir.SetPos(e.Expr)
   411  			assign(base.Pos, l, loff+e.Xoffset, e.Expr)
   412  		}
   413  
   414  		return true
   415  
   416  	case ir.OMAPLIT:
   417  		break
   418  
   419  	case ir.OCLOSURE:
   420  		if disableGlobalAddrs {
   421  			return false
   422  		}
   423  		r := r.(*ir.ClosureExpr)
   424  		if !r.Func.IsClosure() {
   425  			if base.Debug.Closure > 0 {
   426  				base.WarnfAt(r.Pos(), "closure converted to global")
   427  			}
   428  			// Closures with no captured variables are globals,
   429  			// so the assignment can be done at link time.
   430  			// TODO if roff != 0 { panic }
   431  			staticdata.InitAddr(l, loff, staticdata.FuncLinksym(r.Func.Nname))
   432  			return true
   433  		}
   434  		ir.ClosureDebugRuntimeCheck(r)
   435  
   436  	case ir.OCONVIFACE:
   437  		// This logic is mirrored in isStaticCompositeLiteral.
   438  		// If you change something here, change it there, and vice versa.
   439  
   440  		if disableGlobalAddrs {
   441  			return false
   442  		}
   443  
   444  		// Determine the underlying concrete type and value we are converting from.
   445  		r := r.(*ir.ConvExpr)
   446  		val := ir.Node(r)
   447  		for val.Op() == ir.OCONVIFACE {
   448  			val = val.(*ir.ConvExpr).X
   449  		}
   450  
   451  		if val.Type().IsInterface() {
   452  			// val is an interface type.
   453  			// If val is nil, we can statically initialize l;
   454  			// both words are zero and so there no work to do, so report success.
   455  			// If val is non-nil, we have no concrete type to record,
   456  			// and we won't be able to statically initialize its value, so report failure.
   457  			return val.Op() == ir.ONIL
   458  		}
   459  
   460  		if val.Type().HasShape() {
   461  			// See comment in cmd/compile/internal/walk/convert.go:walkConvInterface
   462  			return false
   463  		}
   464  
   465  		reflectdata.MarkTypeUsedInInterface(val.Type(), l.Linksym())
   466  
   467  		var itab *ir.AddrExpr
   468  		if typ.IsEmptyInterface() {
   469  			itab = reflectdata.TypePtrAt(base.Pos, val.Type())
   470  		} else {
   471  			itab = reflectdata.ITabAddrAt(base.Pos, val.Type(), typ)
   472  		}
   473  
   474  		// Create a copy of l to modify while we emit data.
   475  
   476  		// Emit itab, advance offset.
   477  		staticdata.InitAddr(l, loff, itab.X.(*ir.LinksymOffsetExpr).Linksym)
   478  
   479  		// Emit data.
   480  		if types.IsDirectIface(val.Type()) {
   481  			if val.Op() == ir.ONIL {
   482  				// Nil is zero, nothing to do.
   483  				return true
   484  			}
   485  			// Copy val directly into n.
   486  			ir.SetPos(val)
   487  			assign(base.Pos, l, loff+int64(types.PtrSize), val)
   488  		} else {
   489  			// Construct temp to hold val, write pointer to temp into n.
   490  			a := StaticName(val.Type())
   491  			s.Temps[val] = a
   492  			assign(base.Pos, a, 0, val)
   493  			staticdata.InitAddr(l, loff+int64(types.PtrSize), a.Linksym())
   494  		}
   495  
   496  		return true
   497  
   498  	case ir.OINLCALL:
   499  		if disableGlobalAddrs {
   500  			return false
   501  		}
   502  		r := r.(*ir.InlinedCallExpr)
   503  		return s.staticAssignInlinedCall(l, loff, r, typ)
   504  	}
   505  
   506  	if base.Flag.Percent != 0 {
   507  		ir.Dump("not static", r)
   508  	}
   509  	return false
   510  }
   511  
   512  func (s *Schedule) initplan(n ir.Node) {
   513  	if s.Plans[n] != nil {
   514  		return
   515  	}
   516  	p := new(Plan)
   517  	s.Plans[n] = p
   518  	switch n.Op() {
   519  	default:
   520  		base.Fatalf("initplan")
   521  
   522  	case ir.OARRAYLIT, ir.OSLICELIT:
   523  		n := n.(*ir.CompLitExpr)
   524  		var k int64
   525  		for _, a := range n.List {
   526  			if a.Op() == ir.OKEY {
   527  				kv := a.(*ir.KeyExpr)
   528  				k = typecheck.IndexConst(kv.Key)
   529  				a = kv.Value
   530  			}
   531  			s.addvalue(p, k*n.Type().Elem().Size(), a)
   532  			k++
   533  		}
   534  
   535  	case ir.OSTRUCTLIT:
   536  		n := n.(*ir.CompLitExpr)
   537  		for _, a := range n.List {
   538  			if a.Op() != ir.OSTRUCTKEY {
   539  				base.Fatalf("initplan structlit")
   540  			}
   541  			a := a.(*ir.StructKeyExpr)
   542  			if a.Sym().IsBlank() {
   543  				continue
   544  			}
   545  			s.addvalue(p, a.Field.Offset, a.Value)
   546  		}
   547  
   548  	case ir.OMAPLIT:
   549  		n := n.(*ir.CompLitExpr)
   550  		for _, a := range n.List {
   551  			if a.Op() != ir.OKEY {
   552  				base.Fatalf("initplan maplit")
   553  			}
   554  			a := a.(*ir.KeyExpr)
   555  			s.addvalue(p, -1, a.Value)
   556  		}
   557  	}
   558  }
   559  
   560  func (s *Schedule) addvalue(p *Plan, xoffset int64, n ir.Node) {
   561  	// special case: zero can be dropped entirely
   562  	if ir.IsZero(n) {
   563  		return
   564  	}
   565  
   566  	// special case: inline struct and array (not slice) literals
   567  	if isvaluelit(n) {
   568  		s.initplan(n)
   569  		q := s.Plans[n]
   570  		for _, qe := range q.E {
   571  			// qe is a copy; we are not modifying entries in q.E
   572  			qe.Xoffset += xoffset
   573  			p.E = append(p.E, qe)
   574  		}
   575  		return
   576  	}
   577  
   578  	// add to plan
   579  	p.E = append(p.E, Entry{Xoffset: xoffset, Expr: n})
   580  }
   581  
   582  func (s *Schedule) staticAssignInlinedCall(l *ir.Name, loff int64, call *ir.InlinedCallExpr, typ *types.Type) bool {
   583  	if base.Debug.InlStaticInit == 0 {
   584  		return false
   585  	}
   586  
   587  	// Handle the special case of an inlined call of
   588  	// a function body with a single return statement,
   589  	// which turns into a single assignment plus a goto.
   590  	//
   591  	// For example code like this:
   592  	//
   593  	//	type T struct{ x int }
   594  	//	func F(x int) *T { return &T{x} }
   595  	//	var Global = F(400)
   596  	//
   597  	// turns into IR like this:
   598  	//
   599  	// 	INLCALL-init
   600  	// 	.   AS2-init
   601  	// 	.   .   DCL # x.go:18:13
   602  	// 	.   .   .   NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13
   603  	// 	.   AS2 Def tc(1) # x.go:18:13
   604  	// 	.   AS2-Lhs
   605  	// 	.   .   NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13
   606  	// 	.   AS2-Rhs
   607  	// 	.   .   LITERAL-400 int tc(1) # x.go:18:14
   608  	// 	.   INLMARK Index:1 # +x.go:18:13
   609  	// 	INLCALL PTR-*T tc(1) # x.go:18:13
   610  	// 	INLCALL-Body
   611  	// 	.   BLOCK tc(1) # x.go:18:13
   612  	// 	.   BLOCK-List
   613  	// 	.   .   DCL tc(1) # x.go:18:13
   614  	// 	.   .   .   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
   615  	// 	.   .   AS2 tc(1) # x.go:18:13
   616  	// 	.   .   AS2-Lhs
   617  	// 	.   .   .   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
   618  	// 	.   .   AS2-Rhs
   619  	// 	.   .   .   INLINED RETURN ARGUMENT HERE
   620  	// 	.   .   GOTO p..i1 tc(1) # x.go:18:13
   621  	// 	.   LABEL p..i1 # x.go:18:13
   622  	// 	INLCALL-ReturnVars
   623  	// 	.   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
   624  	//
   625  	// In non-unified IR, the tree is slightly different:
   626  	//  - if there are no arguments to the inlined function,
   627  	//    the INLCALL-init omits the AS2.
   628  	//  - the DCL inside BLOCK is on the AS2's init list,
   629  	//    not its own statement in the top level of the BLOCK.
   630  	//
   631  	// If the init values are side-effect-free and each either only
   632  	// appears once in the function body or is safely repeatable,
   633  	// then we inline the value expressions into the return argument
   634  	// and then call StaticAssign to handle that copy.
   635  	//
   636  	// This handles simple cases like
   637  	//
   638  	//	var myError = errors.New("mine")
   639  	//
   640  	// where errors.New is
   641  	//
   642  	//	func New(text string) error {
   643  	//		return &errorString{text}
   644  	//	}
   645  	//
   646  	// We could make things more sophisticated but this kind of initializer
   647  	// is the most important case for us to get right.
   648  
   649  	init := call.Init()
   650  	var as2init *ir.AssignListStmt
   651  	if len(init) == 2 && init[0].Op() == ir.OAS2 && init[1].Op() == ir.OINLMARK {
   652  		as2init = init[0].(*ir.AssignListStmt)
   653  	} else if len(init) == 1 && init[0].Op() == ir.OINLMARK {
   654  		as2init = new(ir.AssignListStmt)
   655  	} else {
   656  		return false
   657  	}
   658  	if len(call.Body) != 2 || call.Body[0].Op() != ir.OBLOCK || call.Body[1].Op() != ir.OLABEL {
   659  		return false
   660  	}
   661  	label := call.Body[1].(*ir.LabelStmt).Label
   662  	block := call.Body[0].(*ir.BlockStmt)
   663  	list := block.List
   664  	var dcl *ir.Decl
   665  	if len(list) == 3 && list[0].Op() == ir.ODCL {
   666  		dcl = list[0].(*ir.Decl)
   667  		list = list[1:]
   668  	}
   669  	if len(list) != 2 ||
   670  		list[0].Op() != ir.OAS2 ||
   671  		list[1].Op() != ir.OGOTO ||
   672  		list[1].(*ir.BranchStmt).Label != label {
   673  		return false
   674  	}
   675  	as2body := list[0].(*ir.AssignListStmt)
   676  	if dcl == nil {
   677  		ainit := as2body.Init()
   678  		if len(ainit) != 1 || ainit[0].Op() != ir.ODCL {
   679  			return false
   680  		}
   681  		dcl = ainit[0].(*ir.Decl)
   682  	}
   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  	return n
   783  }
   784  
   785  // StaticLoc returns the static address of n, if n has one, or else nil.
   786  func StaticLoc(n ir.Node) (name *ir.Name, offset int64, ok bool) {
   787  	if n == nil {
   788  		return nil, 0, false
   789  	}
   790  
   791  	switch n.Op() {
   792  	case ir.ONAME:
   793  		n := n.(*ir.Name)
   794  		return n, 0, true
   795  
   796  	case ir.OMETHEXPR:
   797  		n := n.(*ir.SelectorExpr)
   798  		return StaticLoc(n.FuncName())
   799  
   800  	case ir.ODOT:
   801  		n := n.(*ir.SelectorExpr)
   802  		if name, offset, ok = StaticLoc(n.X); !ok {
   803  			break
   804  		}
   805  		offset += n.Offset()
   806  		return name, offset, true
   807  
   808  	case ir.OINDEX:
   809  		n := n.(*ir.IndexExpr)
   810  		if n.X.Type().IsSlice() {
   811  			break
   812  		}
   813  		if name, offset, ok = StaticLoc(n.X); !ok {
   814  			break
   815  		}
   816  		l := getlit(n.Index)
   817  		if l < 0 {
   818  			break
   819  		}
   820  
   821  		// Check for overflow.
   822  		if n.Type().Size() != 0 && types.MaxWidth/n.Type().Size() <= int64(l) {
   823  			break
   824  		}
   825  		offset += int64(l) * n.Type().Size()
   826  		return name, offset, true
   827  	}
   828  
   829  	return nil, 0, false
   830  }
   831  
   832  func isSideEffect(n ir.Node) bool {
   833  	switch n.Op() {
   834  	// Assume side effects unless we know otherwise.
   835  	default:
   836  		return true
   837  
   838  	// No side effects here (arguments are checked separately).
   839  	case ir.ONAME,
   840  		ir.ONONAME,
   841  		ir.OTYPE,
   842  		ir.OLITERAL,
   843  		ir.ONIL,
   844  		ir.OADD,
   845  		ir.OSUB,
   846  		ir.OOR,
   847  		ir.OXOR,
   848  		ir.OADDSTR,
   849  		ir.OADDR,
   850  		ir.OANDAND,
   851  		ir.OBYTES2STR,
   852  		ir.ORUNES2STR,
   853  		ir.OSTR2BYTES,
   854  		ir.OSTR2RUNES,
   855  		ir.OCAP,
   856  		ir.OCOMPLIT,
   857  		ir.OMAPLIT,
   858  		ir.OSTRUCTLIT,
   859  		ir.OARRAYLIT,
   860  		ir.OSLICELIT,
   861  		ir.OPTRLIT,
   862  		ir.OCONV,
   863  		ir.OCONVIFACE,
   864  		ir.OCONVNOP,
   865  		ir.ODOT,
   866  		ir.OEQ,
   867  		ir.ONE,
   868  		ir.OLT,
   869  		ir.OLE,
   870  		ir.OGT,
   871  		ir.OGE,
   872  		ir.OKEY,
   873  		ir.OSTRUCTKEY,
   874  		ir.OLEN,
   875  		ir.OMUL,
   876  		ir.OLSH,
   877  		ir.ORSH,
   878  		ir.OAND,
   879  		ir.OANDNOT,
   880  		ir.ONEW,
   881  		ir.ONOT,
   882  		ir.OBITNOT,
   883  		ir.OPLUS,
   884  		ir.ONEG,
   885  		ir.OOROR,
   886  		ir.OPAREN,
   887  		ir.ORUNESTR,
   888  		ir.OREAL,
   889  		ir.OIMAG,
   890  		ir.OCOMPLEX:
   891  		return false
   892  
   893  	// Only possible side effect is division by zero.
   894  	case ir.ODIV, ir.OMOD:
   895  		n := n.(*ir.BinaryExpr)
   896  		if n.Y.Op() != ir.OLITERAL || constant.Sign(n.Y.Val()) == 0 {
   897  			return true
   898  		}
   899  
   900  	// Only possible side effect is panic on invalid size,
   901  	// but many makechan and makemap use size zero, which is definitely OK.
   902  	case ir.OMAKECHAN, ir.OMAKEMAP:
   903  		n := n.(*ir.MakeExpr)
   904  		if !ir.IsConst(n.Len, constant.Int) || constant.Sign(n.Len.Val()) != 0 {
   905  			return true
   906  		}
   907  
   908  	// Only possible side effect is panic on invalid size.
   909  	// TODO(rsc): Merge with previous case (probably breaks toolstash -cmp).
   910  	case ir.OMAKESLICE, ir.OMAKESLICECOPY:
   911  		return true
   912  	}
   913  	return false
   914  }
   915  
   916  // AnySideEffects reports whether n contains any operations that could have observable side effects.
   917  func AnySideEffects(n ir.Node) bool {
   918  	return ir.Any(n, isSideEffect)
   919  }
   920  
   921  // mayModifyPkgVar reports whether expression n may modify any
   922  // package-scope variables declared within the current package.
   923  func mayModifyPkgVar(n ir.Node) bool {
   924  	// safeLHS reports whether the assigned-to variable lhs is either a
   925  	// local variable or a global from another package.
   926  	safeLHS := func(lhs ir.Node) bool {
   927  		outer := ir.OuterValue(lhs)
   928  		// "*p = ..." should be safe if p is a local variable.
   929  		// TODO: Should ir.OuterValue handle this?
   930  		for outer.Op() == ir.ODEREF {
   931  			outer = outer.(*ir.StarExpr).X
   932  		}
   933  		v, ok := outer.(*ir.Name)
   934  		return ok && v.Op() == ir.ONAME && !(v.Class == ir.PEXTERN && v.Sym().Pkg == types.LocalPkg)
   935  	}
   936  
   937  	return ir.Any(n, func(n ir.Node) bool {
   938  		switch n.Op() {
   939  		case ir.OCALLFUNC, ir.OCALLINTER:
   940  			return !ir.IsFuncPCIntrinsic(n.(*ir.CallExpr))
   941  
   942  		case ir.OAPPEND, ir.OCLEAR, ir.OCOPY:
   943  			return true // could mutate a global array
   944  
   945  		case ir.OASOP:
   946  			n := n.(*ir.AssignOpStmt)
   947  			if !safeLHS(n.X) {
   948  				return true
   949  			}
   950  
   951  		case ir.OAS:
   952  			n := n.(*ir.AssignStmt)
   953  			if !safeLHS(n.X) {
   954  				return true
   955  			}
   956  
   957  		case ir.OAS2, ir.OAS2DOTTYPE, ir.OAS2FUNC, ir.OAS2MAPR, ir.OAS2RECV:
   958  			n := n.(*ir.AssignListStmt)
   959  			for _, lhs := range n.Lhs {
   960  				if !safeLHS(lhs) {
   961  					return true
   962  				}
   963  			}
   964  		}
   965  
   966  		return false
   967  	})
   968  }
   969  
   970  // canRepeat reports whether executing n multiple times has the same effect as
   971  // assigning n to a single variable and using that variable multiple times.
   972  func canRepeat(n ir.Node) bool {
   973  	bad := func(n ir.Node) bool {
   974  		if isSideEffect(n) {
   975  			return true
   976  		}
   977  		switch n.Op() {
   978  		case ir.OMAKECHAN,
   979  			ir.OMAKEMAP,
   980  			ir.OMAKESLICE,
   981  			ir.OMAKESLICECOPY,
   982  			ir.OMAPLIT,
   983  			ir.ONEW,
   984  			ir.OPTRLIT,
   985  			ir.OSLICELIT,
   986  			ir.OSTR2BYTES,
   987  			ir.OSTR2RUNES:
   988  			return true
   989  		}
   990  		return false
   991  	}
   992  	return !ir.Any(n, bad)
   993  }
   994  
   995  func getlit(lit ir.Node) int {
   996  	if ir.IsSmallIntConst(lit) {
   997  		return int(ir.Int64Val(lit))
   998  	}
   999  	return -1
  1000  }
  1001  
  1002  func isvaluelit(n ir.Node) bool {
  1003  	return n.Op() == ir.OARRAYLIT || n.Op() == ir.OSTRUCTLIT
  1004  }
  1005  
  1006  func subst(n ir.Node, m map[*ir.Name]ir.Node) (ir.Node, bool) {
  1007  	valid := true
  1008  	var edit func(ir.Node) ir.Node
  1009  	edit = func(x ir.Node) ir.Node {
  1010  		switch x.Op() {
  1011  		case ir.ONAME:
  1012  			x := x.(*ir.Name)
  1013  			if v, ok := m[x]; ok {
  1014  				return ir.DeepCopy(v.Pos(), v)
  1015  			}
  1016  			return x
  1017  		case ir.ONONAME, ir.OLITERAL, ir.ONIL, ir.OTYPE:
  1018  			return x
  1019  		}
  1020  		x = ir.Copy(x)
  1021  		ir.EditChildrenWithHidden(x, edit)
  1022  
  1023  		// TODO: handle more operations, see details discussion in go.dev/cl/466277.
  1024  		switch x.Op() {
  1025  		case ir.OCONV:
  1026  			x := x.(*ir.ConvExpr)
  1027  			if x.X.Op() == ir.OLITERAL {
  1028  				if x, ok := truncate(x.X, x.Type()); ok {
  1029  					return x
  1030  				}
  1031  				valid = false
  1032  				return x
  1033  			}
  1034  		case ir.OADDSTR:
  1035  			return addStr(x.(*ir.AddStringExpr))
  1036  		}
  1037  		return x
  1038  	}
  1039  	n = edit(n)
  1040  	return n, valid
  1041  }
  1042  
  1043  // truncate returns the result of force converting c to type t,
  1044  // truncating its value as needed, like a conversion of a variable.
  1045  // If the conversion is too difficult, truncate returns nil, false.
  1046  func truncate(c ir.Node, t *types.Type) (ir.Node, bool) {
  1047  	ct := c.Type()
  1048  	cv := c.Val()
  1049  	if ct.Kind() != t.Kind() {
  1050  		switch {
  1051  		default:
  1052  			// Note: float -> float/integer and complex -> complex are valid but subtle.
  1053  			// For example a float32(float64 1e300) evaluates to +Inf at runtime
  1054  			// and the compiler doesn't have any concept of +Inf, so that would
  1055  			// have to be left for runtime code evaluation.
  1056  			// For now
  1057  			return nil, false
  1058  
  1059  		case ct.IsInteger() && t.IsInteger():
  1060  			// truncate or sign extend
  1061  			bits := t.Size() * 8
  1062  			cv = constant.BinaryOp(cv, token.AND, constant.MakeUint64(1<<bits-1))
  1063  			if t.IsSigned() && constant.Compare(cv, token.GEQ, constant.MakeUint64(1<<(bits-1))) {
  1064  				cv = constant.BinaryOp(cv, token.OR, constant.MakeInt64(-1<<(bits-1)))
  1065  			}
  1066  		}
  1067  	}
  1068  	c = ir.NewConstExpr(cv, c)
  1069  	c.SetType(t)
  1070  	return c, true
  1071  }
  1072  
  1073  func addStr(n *ir.AddStringExpr) ir.Node {
  1074  	// Merge adjacent constants in the argument list.
  1075  	s := n.List
  1076  	need := 0
  1077  	for i := 0; i < len(s); i++ {
  1078  		if i == 0 || !ir.IsConst(s[i-1], constant.String) || !ir.IsConst(s[i], constant.String) {
  1079  			// Can't merge s[i] into s[i-1]; need a slot in the list.
  1080  			need++
  1081  		}
  1082  	}
  1083  	if need == len(s) {
  1084  		return n
  1085  	}
  1086  	if need == 1 {
  1087  		var strs []string
  1088  		for _, c := range s {
  1089  			strs = append(strs, ir.StringVal(c))
  1090  		}
  1091  		return ir.NewConstExpr(constant.MakeString(strings.Join(strs, "")), n)
  1092  	}
  1093  	newList := make([]ir.Node, 0, need)
  1094  	for i := 0; i < len(s); i++ {
  1095  		if ir.IsConst(s[i], constant.String) && i+1 < len(s) && ir.IsConst(s[i+1], constant.String) {
  1096  			// merge from i up to but not including i2
  1097  			var strs []string
  1098  			i2 := i
  1099  			for i2 < len(s) && ir.IsConst(s[i2], constant.String) {
  1100  				strs = append(strs, ir.StringVal(s[i2]))
  1101  				i2++
  1102  			}
  1103  
  1104  			newList = append(newList, ir.NewConstExpr(constant.MakeString(strings.Join(strs, "")), s[i]))
  1105  			i = i2 - 1
  1106  		} else {
  1107  			newList = append(newList, s[i])
  1108  		}
  1109  	}
  1110  
  1111  	nn := ir.Copy(n).(*ir.AddStringExpr)
  1112  	nn.List = newList
  1113  	return nn
  1114  }
  1115  
  1116  const wrapGlobalMapInitSizeThreshold = 20
  1117  
  1118  // tryWrapGlobalInit returns a new outlined function to contain global
  1119  // initializer statement n, if possible and worthwhile. Otherwise, it
  1120  // returns nil.
  1121  //
  1122  // Currently, it outlines map assignment statements with large,
  1123  // side-effect-free RHS expressions.
  1124  func tryWrapGlobalInit(n ir.Node) *ir.Func {
  1125  	// Look for "X = ..." where X has map type.
  1126  	// FIXME: might also be worth trying to look for cases where
  1127  	// the LHS is of interface type but RHS is map type.
  1128  	if n.Op() != ir.OAS {
  1129  		return nil
  1130  	}
  1131  	as := n.(*ir.AssignStmt)
  1132  	if ir.IsBlank(as.X) || as.X.Op() != ir.ONAME {
  1133  		return nil
  1134  	}
  1135  	nm := as.X.(*ir.Name)
  1136  	if !nm.Type().IsMap() {
  1137  		return nil
  1138  	}
  1139  
  1140  	// Determine size of RHS.
  1141  	rsiz := 0
  1142  	ir.Any(as.Y, func(n ir.Node) bool {
  1143  		rsiz++
  1144  		return false
  1145  	})
  1146  	if base.Debug.WrapGlobalMapDbg > 0 {
  1147  		fmt.Fprintf(os.Stderr, "=-= mapassign %s %v rhs size %d\n",
  1148  			base.Ctxt.Pkgpath, n, rsiz)
  1149  	}
  1150  
  1151  	// Reject smaller candidates if not in stress mode.
  1152  	if rsiz < wrapGlobalMapInitSizeThreshold && base.Debug.WrapGlobalMapCtl != 2 {
  1153  		if base.Debug.WrapGlobalMapDbg > 1 {
  1154  			fmt.Fprintf(os.Stderr, "=-= skipping %v size too small at %d\n",
  1155  				nm, rsiz)
  1156  		}
  1157  		return nil
  1158  	}
  1159  
  1160  	// Reject right hand sides with side effects.
  1161  	if AnySideEffects(as.Y) {
  1162  		if base.Debug.WrapGlobalMapDbg > 0 {
  1163  			fmt.Fprintf(os.Stderr, "=-= rejected %v due to side effects\n", nm)
  1164  		}
  1165  		return nil
  1166  	}
  1167  
  1168  	if base.Debug.WrapGlobalMapDbg > 1 {
  1169  		fmt.Fprintf(os.Stderr, "=-= committed for: %+v\n", n)
  1170  	}
  1171  
  1172  	// Create a new function that will (eventually) have this form:
  1173  	//
  1174  	//	func map.init.%d() {
  1175  	//		globmapvar = <map initialization>
  1176  	//	}
  1177  	//
  1178  	// Note: cmd/link expects the function name to contain "map.init".
  1179  	minitsym := typecheck.LookupNum("map.init.", mapinitgen)
  1180  	mapinitgen++
  1181  
  1182  	fn := ir.NewFunc(n.Pos(), n.Pos(), minitsym, types.NewSignature(nil, nil, nil))
  1183  	fn.SetInlinabilityChecked(true) // suppress inlining (which would defeat the point)
  1184  	typecheck.DeclFunc(fn)
  1185  	if base.Debug.WrapGlobalMapDbg > 0 {
  1186  		fmt.Fprintf(os.Stderr, "=-= generated func is %v\n", fn)
  1187  	}
  1188  
  1189  	// NB: we're relying on this phase being run before inlining;
  1190  	// if for some reason we need to move it after inlining, we'll
  1191  	// need code here that relocates or duplicates inline temps.
  1192  
  1193  	// Insert assignment into function body; mark body finished.
  1194  	fn.Body = []ir.Node{as}
  1195  	typecheck.FinishFuncBody()
  1196  
  1197  	if base.Debug.WrapGlobalMapDbg > 1 {
  1198  		fmt.Fprintf(os.Stderr, "=-= mapvar is %v\n", nm)
  1199  		fmt.Fprintf(os.Stderr, "=-= newfunc is %+v\n", fn)
  1200  	}
  1201  
  1202  	recordFuncForVar(nm, fn)
  1203  
  1204  	return fn
  1205  }
  1206  
  1207  // mapinitgen is a counter used to uniquify compiler-generated
  1208  // map init functions.
  1209  var mapinitgen int
  1210  
  1211  // AddKeepRelocations adds a dummy "R_KEEP" relocation from each
  1212  // global map variable V to its associated outlined init function.
  1213  // These relocation ensure that if the map var itself is determined to
  1214  // be reachable at link time, we also mark the init function as
  1215  // reachable.
  1216  func AddKeepRelocations() {
  1217  	if varToMapInit == nil {
  1218  		return
  1219  	}
  1220  	for k, v := range varToMapInit {
  1221  		// Add R_KEEP relocation from map to init function.
  1222  		fs := v.Linksym()
  1223  		if fs == nil {
  1224  			base.Fatalf("bad: func %v has no linksym", v)
  1225  		}
  1226  		vs := k.Linksym()
  1227  		if vs == nil {
  1228  			base.Fatalf("bad: mapvar %v has no linksym", k)
  1229  		}
  1230  		vs.AddRel(base.Ctxt, obj.Reloc{Type: objabi.R_KEEP, Sym: fs})
  1231  		if base.Debug.WrapGlobalMapDbg > 1 {
  1232  			fmt.Fprintf(os.Stderr, "=-= add R_KEEP relo from %s to %s\n",
  1233  				vs.Name, fs.Name)
  1234  		}
  1235  	}
  1236  	varToMapInit = nil
  1237  }
  1238  
  1239  // OutlineMapInits replaces global map initializers with outlined
  1240  // calls to separate "map init" functions (where possible and
  1241  // profitable), to facilitate better dead-code elimination by the
  1242  // linker.
  1243  func OutlineMapInits(fn *ir.Func) {
  1244  	if base.Debug.WrapGlobalMapCtl == 1 {
  1245  		return
  1246  	}
  1247  
  1248  	outlined := 0
  1249  	for i, stmt := range fn.Body {
  1250  		// Attempt to outline stmt. If successful, replace it with a call
  1251  		// to the returned wrapper function.
  1252  		if wrapperFn := tryWrapGlobalInit(stmt); wrapperFn != nil {
  1253  			ir.WithFunc(fn, func() {
  1254  				fn.Body[i] = typecheck.Call(stmt.Pos(), wrapperFn.Nname, nil, false)
  1255  			})
  1256  			outlined++
  1257  		}
  1258  	}
  1259  
  1260  	if base.Debug.WrapGlobalMapDbg > 1 {
  1261  		fmt.Fprintf(os.Stderr, "=-= outlined %v map initializations\n", outlined)
  1262  	}
  1263  }
  1264  

View as plain text