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  		staticdata.InitConst(l, loff, r, int(typ.Size()))
   336  		return true
   337  
   338  	case ir.OADDR:
   339  		if disableGlobalAddrs {
   340  			return false
   341  		}
   342  		r := r.(*ir.AddrExpr)
   343  		if name, offset, ok := StaticLoc(r.X); ok && name.Class == ir.PEXTERN {
   344  			staticdata.InitAddrOffset(l, loff, name.Linksym(), offset)
   345  			return true
   346  		}
   347  		fallthrough
   348  
   349  	case ir.OPTRLIT:
   350  		if disableGlobalAddrs {
   351  			return false
   352  		}
   353  		r := r.(*ir.AddrExpr)
   354  		switch r.X.Op() {
   355  		case ir.OARRAYLIT, ir.OSLICELIT, ir.OMAPLIT, ir.OSTRUCTLIT:
   356  			// Init pointer.
   357  			a := StaticName(r.X.Type())
   358  
   359  			s.Temps[r] = a
   360  			staticdata.InitAddr(l, loff, a.Linksym())
   361  
   362  			// Init underlying literal.
   363  			assign(base.Pos, a, 0, r.X)
   364  			return true
   365  		}
   366  		//dump("not static ptrlit", r);
   367  
   368  	case ir.OSTR2BYTES:
   369  		if disableGlobalAddrs {
   370  			return false
   371  		}
   372  		r := r.(*ir.ConvExpr)
   373  		if l.Class == ir.PEXTERN && r.X.Op() == ir.OLITERAL {
   374  			sval := ir.StringVal(r.X)
   375  			staticdata.InitSliceBytes(l, loff, sval)
   376  			return true
   377  		}
   378  
   379  	case ir.OSLICELIT:
   380  		if disableGlobalAddrs {
   381  			return false
   382  		}
   383  		r := r.(*ir.CompLitExpr)
   384  		s.initplan(r)
   385  		// Init slice.
   386  		ta := types.NewArray(r.Type().Elem(), r.Len)
   387  		ta.SetNoalg(true)
   388  		a := StaticName(ta)
   389  		s.Temps[r] = a
   390  		staticdata.InitSlice(l, loff, a.Linksym(), r.Len)
   391  		// Fall through to init underlying array.
   392  		l = a
   393  		loff = 0
   394  		fallthrough
   395  
   396  	case ir.OARRAYLIT, ir.OSTRUCTLIT:
   397  		r := r.(*ir.CompLitExpr)
   398  		s.initplan(r)
   399  
   400  		p := s.Plans[r]
   401  		for i := range p.E {
   402  			e := &p.E[i]
   403  			if e.Expr.Op() == ir.OLITERAL && !disableGlobalAddrs || e.Expr.Op() == ir.ONIL {
   404  				staticdata.InitConst(l, loff+e.Xoffset, e.Expr, int(e.Expr.Type().Size()))
   405  				continue
   406  			}
   407  			ir.SetPos(e.Expr)
   408  			assign(base.Pos, l, loff+e.Xoffset, e.Expr)
   409  		}
   410  
   411  		return true
   412  
   413  	case ir.OMAPLIT:
   414  		break
   415  
   416  	case ir.OCLOSURE:
   417  		if disableGlobalAddrs {
   418  			return false
   419  		}
   420  		r := r.(*ir.ClosureExpr)
   421  		if !r.Func.IsClosure() {
   422  			if base.Debug.Closure > 0 {
   423  				base.WarnfAt(r.Pos(), "closure converted to global")
   424  			}
   425  			// Closures with no captured variables are globals,
   426  			// so the assignment can be done at link time.
   427  			// TODO if roff != 0 { panic }
   428  			staticdata.InitAddr(l, loff, staticdata.FuncLinksym(r.Func.Nname))
   429  			return true
   430  		}
   431  		ir.ClosureDebugRuntimeCheck(r)
   432  
   433  	case ir.OCONVIFACE:
   434  		// This logic is mirrored in isStaticCompositeLiteral.
   435  		// If you change something here, change it there, and vice versa.
   436  
   437  		if disableGlobalAddrs {
   438  			return false
   439  		}
   440  
   441  		// Determine the underlying concrete type and value we are converting from.
   442  		r := r.(*ir.ConvExpr)
   443  		val := ir.Node(r)
   444  		for val.Op() == ir.OCONVIFACE {
   445  			val = val.(*ir.ConvExpr).X
   446  		}
   447  
   448  		if val.Type().IsInterface() {
   449  			// val is an interface type.
   450  			// If val is nil, we can statically initialize l;
   451  			// both words are zero and so there no work to do, so report success.
   452  			// If val is non-nil, we have no concrete type to record,
   453  			// and we won't be able to statically initialize its value, so report failure.
   454  			return val.Op() == ir.ONIL
   455  		}
   456  
   457  		if val.Type().HasShape() {
   458  			// See comment in cmd/compile/internal/walk/convert.go:walkConvInterface
   459  			return false
   460  		}
   461  
   462  		reflectdata.MarkTypeUsedInInterface(val.Type(), l.Linksym())
   463  
   464  		var itab *ir.AddrExpr
   465  		if typ.IsEmptyInterface() {
   466  			itab = reflectdata.TypePtrAt(base.Pos, val.Type())
   467  		} else {
   468  			itab = reflectdata.ITabAddrAt(base.Pos, val.Type(), typ)
   469  		}
   470  
   471  		// Create a copy of l to modify while we emit data.
   472  
   473  		// Emit itab, advance offset.
   474  		staticdata.InitAddr(l, loff, itab.X.(*ir.LinksymOffsetExpr).Linksym)
   475  
   476  		// Emit data.
   477  		if types.IsDirectIface(val.Type()) {
   478  			if val.Op() == ir.ONIL {
   479  				// Nil is zero, nothing to do.
   480  				return true
   481  			}
   482  			// Copy val directly into n.
   483  			ir.SetPos(val)
   484  			assign(base.Pos, l, loff+int64(types.PtrSize), val)
   485  		} else {
   486  			// Construct temp to hold val, write pointer to temp into n.
   487  			a := StaticName(val.Type())
   488  			s.Temps[val] = a
   489  			assign(base.Pos, a, 0, val)
   490  			staticdata.InitAddr(l, loff+int64(types.PtrSize), a.Linksym())
   491  		}
   492  
   493  		return true
   494  
   495  	case ir.OINLCALL:
   496  		if disableGlobalAddrs {
   497  			return false
   498  		}
   499  		r := r.(*ir.InlinedCallExpr)
   500  		return s.staticAssignInlinedCall(l, loff, r, typ)
   501  	}
   502  
   503  	if base.Flag.Percent != 0 {
   504  		ir.Dump("not static", r)
   505  	}
   506  	return false
   507  }
   508  
   509  func (s *Schedule) initplan(n ir.Node) {
   510  	if s.Plans[n] != nil {
   511  		return
   512  	}
   513  	p := new(Plan)
   514  	s.Plans[n] = p
   515  	switch n.Op() {
   516  	default:
   517  		base.Fatalf("initplan")
   518  
   519  	case ir.OARRAYLIT, ir.OSLICELIT:
   520  		n := n.(*ir.CompLitExpr)
   521  		var k int64
   522  		for _, a := range n.List {
   523  			if a.Op() == ir.OKEY {
   524  				kv := a.(*ir.KeyExpr)
   525  				k = typecheck.IndexConst(kv.Key)
   526  				a = kv.Value
   527  			}
   528  			s.addvalue(p, k*n.Type().Elem().Size(), a)
   529  			k++
   530  		}
   531  
   532  	case ir.OSTRUCTLIT:
   533  		n := n.(*ir.CompLitExpr)
   534  		for _, a := range n.List {
   535  			if a.Op() != ir.OSTRUCTKEY {
   536  				base.Fatalf("initplan structlit")
   537  			}
   538  			a := a.(*ir.StructKeyExpr)
   539  			if a.Sym().IsBlank() {
   540  				continue
   541  			}
   542  			s.addvalue(p, a.Field.Offset, a.Value)
   543  		}
   544  
   545  	case ir.OMAPLIT:
   546  		n := n.(*ir.CompLitExpr)
   547  		for _, a := range n.List {
   548  			if a.Op() != ir.OKEY {
   549  				base.Fatalf("initplan maplit")
   550  			}
   551  			a := a.(*ir.KeyExpr)
   552  			s.addvalue(p, -1, a.Value)
   553  		}
   554  	}
   555  }
   556  
   557  func (s *Schedule) addvalue(p *Plan, xoffset int64, n ir.Node) {
   558  	// special case: zero can be dropped entirely
   559  	if ir.IsZero(n) {
   560  		return
   561  	}
   562  
   563  	// special case: inline struct and array (not slice) literals
   564  	if isvaluelit(n) {
   565  		s.initplan(n)
   566  		q := s.Plans[n]
   567  		for _, qe := range q.E {
   568  			// qe is a copy; we are not modifying entries in q.E
   569  			qe.Xoffset += xoffset
   570  			p.E = append(p.E, qe)
   571  		}
   572  		return
   573  	}
   574  
   575  	// add to plan
   576  	p.E = append(p.E, Entry{Xoffset: xoffset, Expr: n})
   577  }
   578  
   579  func (s *Schedule) staticAssignInlinedCall(l *ir.Name, loff int64, call *ir.InlinedCallExpr, typ *types.Type) bool {
   580  	if base.Debug.InlStaticInit == 0 {
   581  		return false
   582  	}
   583  
   584  	// Handle the special case of an inlined call of
   585  	// a function body with a single return statement,
   586  	// which turns into a single assignment plus a goto.
   587  	//
   588  	// For example code like this:
   589  	//
   590  	//	type T struct{ x int }
   591  	//	func F(x int) *T { return &T{x} }
   592  	//	var Global = F(400)
   593  	//
   594  	// turns into IR like this:
   595  	//
   596  	// 	INLCALL-init
   597  	// 	.   AS2-init
   598  	// 	.   .   DCL # x.go:18:13
   599  	// 	.   .   .   NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13
   600  	// 	.   AS2 Def tc(1) # x.go:18:13
   601  	// 	.   AS2-Lhs
   602  	// 	.   .   NAME-p.x Class:PAUTO Offset:0 InlFormal OnStack Used int tc(1) # x.go:14:9,x.go:18:13
   603  	// 	.   AS2-Rhs
   604  	// 	.   .   LITERAL-400 int tc(1) # x.go:18:14
   605  	// 	.   INLMARK Index:1 # +x.go:18:13
   606  	// 	INLCALL PTR-*T tc(1) # x.go:18:13
   607  	// 	INLCALL-Body
   608  	// 	.   BLOCK tc(1) # x.go:18:13
   609  	// 	.   BLOCK-List
   610  	// 	.   .   DCL tc(1) # x.go:18:13
   611  	// 	.   .   .   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
   612  	// 	.   .   AS2 tc(1) # x.go:18:13
   613  	// 	.   .   AS2-Lhs
   614  	// 	.   .   .   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
   615  	// 	.   .   AS2-Rhs
   616  	// 	.   .   .   INLINED RETURN ARGUMENT HERE
   617  	// 	.   .   GOTO p..i1 tc(1) # x.go:18:13
   618  	// 	.   LABEL p..i1 # x.go:18:13
   619  	// 	INLCALL-ReturnVars
   620  	// 	.   NAME-p.~R0 Class:PAUTO Offset:0 OnStack Used PTR-*T tc(1) # x.go:18:13
   621  	//
   622  	// In non-unified IR, the tree is slightly different:
   623  	//  - if there are no arguments to the inlined function,
   624  	//    the INLCALL-init omits the AS2.
   625  	//  - the DCL inside BLOCK is on the AS2's init list,
   626  	//    not its own statement in the top level of the BLOCK.
   627  	//
   628  	// If the init values are side-effect-free and each either only
   629  	// appears once in the function body or is safely repeatable,
   630  	// then we inline the value expressions into the return argument
   631  	// and then call StaticAssign to handle that copy.
   632  	//
   633  	// This handles simple cases like
   634  	//
   635  	//	var myError = errors.New("mine")
   636  	//
   637  	// where errors.New is
   638  	//
   639  	//	func New(text string) error {
   640  	//		return &errorString{text}
   641  	//	}
   642  	//
   643  	// We could make things more sophisticated but this kind of initializer
   644  	// is the most important case for us to get right.
   645  
   646  	init := call.Init()
   647  	var as2init *ir.AssignListStmt
   648  	if len(init) == 2 && init[0].Op() == ir.OAS2 && init[1].Op() == ir.OINLMARK {
   649  		as2init = init[0].(*ir.AssignListStmt)
   650  	} else if len(init) == 1 && init[0].Op() == ir.OINLMARK {
   651  		as2init = new(ir.AssignListStmt)
   652  	} else {
   653  		return false
   654  	}
   655  	if len(call.Body) != 2 || call.Body[0].Op() != ir.OBLOCK || call.Body[1].Op() != ir.OLABEL {
   656  		return false
   657  	}
   658  	label := call.Body[1].(*ir.LabelStmt).Label
   659  	block := call.Body[0].(*ir.BlockStmt)
   660  	list := block.List
   661  	var dcl *ir.Decl
   662  	if len(list) == 3 && list[0].Op() == ir.ODCL {
   663  		dcl = list[0].(*ir.Decl)
   664  		list = list[1:]
   665  	}
   666  	if len(list) != 2 ||
   667  		list[0].Op() != ir.OAS2 ||
   668  		list[1].Op() != ir.OGOTO ||
   669  		list[1].(*ir.BranchStmt).Label != label {
   670  		return false
   671  	}
   672  	as2body := list[0].(*ir.AssignListStmt)
   673  	if dcl == nil {
   674  		ainit := as2body.Init()
   675  		if len(ainit) != 1 || ainit[0].Op() != ir.ODCL {
   676  			return false
   677  		}
   678  		dcl = ainit[0].(*ir.Decl)
   679  	}
   680  	if len(as2body.Lhs) != 1 || as2body.Lhs[0] != dcl.X {
   681  		return false
   682  	}
   683  
   684  	// Can't remove the parameter variables if an address is taken.
   685  	for _, v := range as2init.Lhs {
   686  		if v.(*ir.Name).Addrtaken() {
   687  			return false
   688  		}
   689  	}
   690  	// Can't move the computation of the args if they have side effects.
   691  	for _, r := range as2init.Rhs {
   692  		if AnySideEffects(r) {
   693  			return false
   694  		}
   695  	}
   696  
   697  	// Can only substitute arg for param if param is used
   698  	// at most once or is repeatable.
   699  	count := make(map[*ir.Name]int)
   700  	for _, x := range as2init.Lhs {
   701  		count[x.(*ir.Name)] = 0
   702  	}
   703  
   704  	hasClosure := false
   705  	ir.Visit(as2body.Rhs[0], func(n ir.Node) {
   706  		if name, ok := n.(*ir.Name); ok {
   707  			if c, ok := count[name]; ok {
   708  				count[name] = c + 1
   709  			}
   710  		}
   711  		if clo, ok := n.(*ir.ClosureExpr); ok {
   712  			hasClosure = hasClosure || clo.Func.IsClosure()
   713  		}
   714  	})
   715  
   716  	// If there's a closure, it has captured the param,
   717  	// so we can't substitute arg for param.
   718  	if hasClosure {
   719  		return false
   720  	}
   721  
   722  	for name, c := range count {
   723  		if c > 1 {
   724  			// Check whether corresponding initializer can be repeated.
   725  			// Something like 1 can be; make(chan int) or &T{} cannot,
   726  			// because they need to evaluate to the same result in each use.
   727  			for i, n := range as2init.Lhs {
   728  				if n == name && !canRepeat(as2init.Rhs[i]) {
   729  					return false
   730  				}
   731  			}
   732  		}
   733  	}
   734  
   735  	// Possible static init.
   736  	// Build tree with args substituted for params and try it.
   737  	args := make(map[*ir.Name]ir.Node)
   738  	for i, v := range as2init.Lhs {
   739  		if ir.IsBlank(v) {
   740  			continue
   741  		}
   742  		args[v.(*ir.Name)] = as2init.Rhs[i]
   743  	}
   744  	r, ok := subst(as2body.Rhs[0], args)
   745  	if !ok {
   746  		return false
   747  	}
   748  	ok = s.StaticAssign(l, loff, r, typ)
   749  
   750  	if ok && base.Flag.Percent != 0 {
   751  		ir.Dump("static inlined-LEFT", l)
   752  		ir.Dump("static inlined-ORIG", call)
   753  		ir.Dump("static inlined-RIGHT", r)
   754  	}
   755  	return ok
   756  }
   757  
   758  // from here down is the walk analysis
   759  // of composite literals.
   760  // most of the work is to generate
   761  // data statements for the constant
   762  // part of the composite literal.
   763  
   764  var statuniqgen int // name generator for static temps
   765  
   766  // StaticName returns a name backed by a (writable) static data symbol.
   767  func StaticName(t *types.Type) *ir.Name {
   768  	// Don't use LookupNum; it interns the resulting string, but these are all unique.
   769  	sym := typecheck.Lookup(fmt.Sprintf("%s%d", obj.StaticNamePrefix, statuniqgen))
   770  	if sym.Name == ".stmp_0" && sym.Pkg.Path == "crypto/internal/fips/check" {
   771  		panic("bad")
   772  	}
   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