1  
     2  
     3  
     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   
    27  	Expr    ir.Node 
    28  }
    29  
    30  type Plan struct {
    31  	E []Entry
    32  }
    33  
    34  
    35  
    36  
    37  
    38  type Schedule struct {
    39  	
    40  	
    41  	Out []ir.Node
    42  
    43  	Plans map[ir.Node]*Plan
    44  	Temps map[ir.Node]*ir.Name
    45  
    46  	
    47  	
    48  	
    49  	seenMutation bool
    50  }
    51  
    52  func (s *Schedule) append(n ir.Node) {
    53  	s.Out = append(s.Out, n)
    54  }
    55  
    56  
    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  
    67  
    68  
    69  var varToMapInit map[*ir.Name]*ir.Func
    70  
    71  
    72  
    73  
    74  var MapInitToVar map[*ir.Func]*ir.Name
    75  
    76  
    77  
    78  
    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  
    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  
    99  
   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  		
   112  		
   113  		
   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 
   132  	}
   133  
   134  	if !s.seenMutation {
   135  		s.seenMutation = mayModifyPkgVar(rhs)
   136  	}
   137  
   138  	if allBlank(lhs) && !AnySideEffects(rhs) {
   139  		return true 
   140  	}
   141  
   142  	
   143  	
   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  
   156  
   157  func (s *Schedule) staticcopy(l *ir.Name, loff int64, rn *ir.Name, typ *types.Type) bool {
   158  	if rn.Class == ir.PFUNC {
   159  		
   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  		
   168  		
   169  		return false
   170  	}
   171  	if rn.Defn.Op() != ir.OAS {
   172  		return false
   173  	}
   174  	if rn.Type().IsString() { 
   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  		
   184  		base.Fatalf("unexpected initializer: %v", rn.Defn)
   185  	}
   186  
   187  	
   188  	
   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  		
   210  		
   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  			
   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  		
   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  			
   268  			
   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  	
   283  	
   284  	
   285  	
   286  	
   287  	
   288  	disableGlobalAddrs := base.Ctxt.IsFIPS()
   289  
   290  	if r == nil {
   291  		
   292  		
   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  			
   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  			
   360  			a := StaticName(r.X.Type())
   361  
   362  			s.Temps[r] = a
   363  			staticdata.InitAddr(l, loff, a.Linksym())
   364  
   365  			
   366  			assign(base.Pos, a, 0, r.X)
   367  			return true
   368  		}
   369  		
   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  		
   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  		
   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  			
   429  			
   430  			
   431  			staticdata.InitAddr(l, loff, staticdata.FuncLinksym(r.Func.Nname))
   432  			return true
   433  		}
   434  		ir.ClosureDebugRuntimeCheck(r)
   435  
   436  	case ir.OCONVIFACE:
   437  		
   438  		
   439  
   440  		if disableGlobalAddrs {
   441  			return false
   442  		}
   443  
   444  		
   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  			
   453  			
   454  			
   455  			
   456  			
   457  			return val.Op() == ir.ONIL
   458  		}
   459  
   460  		if val.Type().HasShape() {
   461  			
   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  		
   475  
   476  		
   477  		staticdata.InitAddr(l, loff, itab.X.(*ir.LinksymOffsetExpr).Linksym)
   478  
   479  		
   480  		if types.IsDirectIface(val.Type()) {
   481  			if val.Op() == ir.ONIL {
   482  				
   483  				return true
   484  			}
   485  			
   486  			ir.SetPos(val)
   487  			assign(base.Pos, l, loff+int64(types.PtrSize), val)
   488  		} else {
   489  			
   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  	
   562  	if ir.IsZero(n) {
   563  		return
   564  	}
   565  
   566  	
   567  	if isvaluelit(n) {
   568  		s.initplan(n)
   569  		q := s.Plans[n]
   570  		for _, qe := range q.E {
   571  			
   572  			qe.Xoffset += xoffset
   573  			p.E = append(p.E, qe)
   574  		}
   575  		return
   576  	}
   577  
   578  	
   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  	
   588  	
   589  	
   590  	
   591  	
   592  	
   593  	
   594  	
   595  	
   596  	
   597  	
   598  	
   599  	
   600  	
   601  	
   602  	
   603  	
   604  	
   605  	
   606  	
   607  	
   608  	
   609  	
   610  	
   611  	
   612  	
   613  	
   614  	
   615  	
   616  	
   617  	
   618  	
   619  	
   620  	
   621  	
   622  	
   623  	
   624  	
   625  	
   626  	
   627  	
   628  	
   629  	
   630  	
   631  	
   632  	
   633  	
   634  	
   635  	
   636  	
   637  	
   638  	
   639  	
   640  	
   641  	
   642  	
   643  	
   644  	
   645  	
   646  	
   647  	
   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  	
   688  	for _, v := range as2init.Lhs {
   689  		if v.(*ir.Name).Addrtaken() {
   690  			return false
   691  		}
   692  	}
   693  	
   694  	for _, r := range as2init.Rhs {
   695  		if AnySideEffects(r) {
   696  			return false
   697  		}
   698  	}
   699  
   700  	
   701  	
   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  	
   720  	
   721  	if hasClosure {
   722  		return false
   723  	}
   724  
   725  	for name, c := range count {
   726  		if c > 1 {
   727  			
   728  			
   729  			
   730  			for i, n := range as2init.Lhs {
   731  				if n == name && !canRepeat(as2init.Rhs[i]) {
   732  					return false
   733  				}
   734  			}
   735  		}
   736  	}
   737  
   738  	
   739  	
   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  
   762  
   763  
   764  
   765  
   766  
   767  var statuniqgen int 
   768  
   769  
   770  func StaticName(t *types.Type) *ir.Name {
   771  	
   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  
   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  		
   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  	
   835  	default:
   836  		return true
   837  
   838  	
   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  	
   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  	
   901  	
   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  	
   909  	
   910  	case ir.OMAKESLICE, ir.OMAKESLICECOPY:
   911  		return true
   912  	}
   913  	return false
   914  }
   915  
   916  
   917  func AnySideEffects(n ir.Node) bool {
   918  	return ir.Any(n, isSideEffect)
   919  }
   920  
   921  
   922  
   923  func mayModifyPkgVar(n ir.Node) bool {
   924  	
   925  	
   926  	safeLHS := func(lhs ir.Node) bool {
   927  		outer := ir.OuterValue(lhs)
   928  		
   929  		
   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 
   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  
   971  
   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  		
  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  
  1044  
  1045  
  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  			
  1053  			
  1054  			
  1055  			
  1056  			
  1057  			return nil, false
  1058  
  1059  		case ct.IsInteger() && t.IsInteger():
  1060  			
  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  	
  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  			
  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  			
  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  
  1119  
  1120  
  1121  
  1122  
  1123  
  1124  func tryWrapGlobalInit(n ir.Node) *ir.Func {
  1125  	
  1126  	
  1127  	
  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  	
  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  	
  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  	
  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  	
  1173  	
  1174  	
  1175  	
  1176  	
  1177  	
  1178  	
  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) 
  1184  	typecheck.DeclFunc(fn)
  1185  	if base.Debug.WrapGlobalMapDbg > 0 {
  1186  		fmt.Fprintf(os.Stderr, "=-= generated func is %v\n", fn)
  1187  	}
  1188  
  1189  	
  1190  	
  1191  	
  1192  
  1193  	
  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  
  1208  
  1209  var mapinitgen int
  1210  
  1211  
  1212  
  1213  
  1214  
  1215  
  1216  func AddKeepRelocations() {
  1217  	if varToMapInit == nil {
  1218  		return
  1219  	}
  1220  	for k, v := range varToMapInit {
  1221  		
  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  
  1240  
  1241  
  1242  
  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  		
  1251  		
  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