...

Source file src/pkg/cmd/compile/internal/gc/sinit.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 gc
     6	
     7	import (
     8		"cmd/compile/internal/types"
     9		"fmt"
    10	)
    11	
    12	type InitEntry struct {
    13		Xoffset int64 // struct, array only
    14		Expr    *Node // bytes of run-time computed expressions
    15	}
    16	
    17	type InitPlan struct {
    18		E []InitEntry
    19	}
    20	
    21	// An InitSchedule is used to decompose assignment statements into
    22	// static and dynamic initialization parts. Static initializations are
    23	// handled by populating variables' linker symbol data, while dynamic
    24	// initializations are accumulated to be executed in order.
    25	type InitSchedule struct {
    26		// out is the ordered list of dynamic initialization
    27		// statements.
    28		out []*Node
    29	
    30		initplans map[*Node]*InitPlan
    31		inittemps map[*Node]*Node
    32	}
    33	
    34	func (s *InitSchedule) append(n *Node) {
    35		s.out = append(s.out, n)
    36	}
    37	
    38	// staticInit adds an initialization statement n to the schedule.
    39	func (s *InitSchedule) staticInit(n *Node) {
    40		if !s.tryStaticInit(n) {
    41			if Debug['%'] != 0 {
    42				Dump("nonstatic", n)
    43			}
    44			s.append(n)
    45		}
    46	}
    47	
    48	// tryStaticInit attempts to statically execute an initialization
    49	// statement and reports whether it succeeded.
    50	func (s *InitSchedule) tryStaticInit(n *Node) bool {
    51		// Only worry about simple "l = r" assignments. Multiple
    52		// variable/expression OAS2 assignments have already been
    53		// replaced by multiple simple OAS assignments, and the other
    54		// OAS2* assignments mostly necessitate dynamic execution
    55		// anyway.
    56		if n.Op != OAS {
    57			return false
    58		}
    59		if n.Left.isBlank() && candiscard(n.Right) {
    60			return true
    61		}
    62		lno := setlineno(n)
    63		defer func() { lineno = lno }()
    64		return s.staticassign(n.Left, n.Right)
    65	}
    66	
    67	// like staticassign but we are copying an already
    68	// initialized value r.
    69	func (s *InitSchedule) staticcopy(l *Node, r *Node) bool {
    70		if r.Op != ONAME {
    71			return false
    72		}
    73		if r.Class() == PFUNC {
    74			gdata(l, r, Widthptr)
    75			return true
    76		}
    77		if r.Class() != PEXTERN || r.Sym.Pkg != localpkg {
    78			return false
    79		}
    80		if r.Name.Defn == nil { // probably zeroed but perhaps supplied externally and of unknown value
    81			return false
    82		}
    83		if r.Name.Defn.Op != OAS {
    84			return false
    85		}
    86		orig := r
    87		r = r.Name.Defn.Right
    88	
    89		for r.Op == OCONVNOP && !types.Identical(r.Type, l.Type) {
    90			r = r.Left
    91		}
    92	
    93		switch r.Op {
    94		case ONAME:
    95			if s.staticcopy(l, r) {
    96				return true
    97			}
    98			// We may have skipped past one or more OCONVNOPs, so
    99			// use conv to ensure r is assignable to l (#13263).
   100			s.append(nod(OAS, l, conv(r, l.Type)))
   101			return true
   102	
   103		case OLITERAL:
   104			if isZero(r) {
   105				return true
   106			}
   107			gdata(l, r, int(l.Type.Width))
   108			return true
   109	
   110		case OADDR:
   111			switch r.Left.Op {
   112			case ONAME:
   113				gdata(l, r, int(l.Type.Width))
   114				return true
   115			}
   116	
   117		case OPTRLIT:
   118			switch r.Left.Op {
   119			case OARRAYLIT, OSLICELIT, OSTRUCTLIT, OMAPLIT:
   120				// copy pointer
   121				gdata(l, nod(OADDR, s.inittemps[r], nil), int(l.Type.Width))
   122				return true
   123			}
   124	
   125		case OSLICELIT:
   126			// copy slice
   127			a := s.inittemps[r]
   128	
   129			n := l.copy()
   130			n.Xoffset = l.Xoffset + int64(array_array)
   131			gdata(n, nod(OADDR, a, nil), Widthptr)
   132			n.Xoffset = l.Xoffset + int64(array_nel)
   133			gdata(n, r.Right, Widthptr)
   134			n.Xoffset = l.Xoffset + int64(array_cap)
   135			gdata(n, r.Right, Widthptr)
   136			return true
   137	
   138		case OARRAYLIT, OSTRUCTLIT:
   139			p := s.initplans[r]
   140	
   141			n := l.copy()
   142			for i := range p.E {
   143				e := &p.E[i]
   144				n.Xoffset = l.Xoffset + e.Xoffset
   145				n.Type = e.Expr.Type
   146				if e.Expr.Op == OLITERAL {
   147					gdata(n, e.Expr, int(n.Type.Width))
   148					continue
   149				}
   150				ll := n.sepcopy()
   151				if s.staticcopy(ll, e.Expr) {
   152					continue
   153				}
   154				// Requires computation, but we're
   155				// copying someone else's computation.
   156				rr := orig.sepcopy()
   157				rr.Type = ll.Type
   158				rr.Xoffset += e.Xoffset
   159				setlineno(rr)
   160				s.append(nod(OAS, ll, rr))
   161			}
   162	
   163			return true
   164		}
   165	
   166		return false
   167	}
   168	
   169	func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
   170		for r.Op == OCONVNOP {
   171			r = r.Left
   172		}
   173	
   174		switch r.Op {
   175		case ONAME:
   176			return s.staticcopy(l, r)
   177	
   178		case OLITERAL:
   179			if isZero(r) {
   180				return true
   181			}
   182			gdata(l, r, int(l.Type.Width))
   183			return true
   184	
   185		case OADDR:
   186			var nam Node
   187			if stataddr(&nam, r.Left) {
   188				n := *r
   189				n.Left = &nam
   190				gdata(l, &n, int(l.Type.Width))
   191				return true
   192			}
   193			fallthrough
   194	
   195		case OPTRLIT:
   196			switch r.Left.Op {
   197			case OARRAYLIT, OSLICELIT, OMAPLIT, OSTRUCTLIT:
   198				// Init pointer.
   199				a := staticname(r.Left.Type)
   200	
   201				s.inittemps[r] = a
   202				gdata(l, nod(OADDR, a, nil), int(l.Type.Width))
   203	
   204				// Init underlying literal.
   205				if !s.staticassign(a, r.Left) {
   206					s.append(nod(OAS, a, r.Left))
   207				}
   208				return true
   209			}
   210			//dump("not static ptrlit", r);
   211	
   212		case OSTR2BYTES:
   213			if l.Class() == PEXTERN && r.Left.Op == OLITERAL {
   214				sval := r.Left.Val().U.(string)
   215				slicebytes(l, sval, len(sval))
   216				return true
   217			}
   218	
   219		case OSLICELIT:
   220			s.initplan(r)
   221			// Init slice.
   222			bound := r.Right.Int64()
   223			ta := types.NewArray(r.Type.Elem(), bound)
   224			a := staticname(ta)
   225			s.inittemps[r] = a
   226			n := l.copy()
   227			n.Xoffset = l.Xoffset + int64(array_array)
   228			gdata(n, nod(OADDR, a, nil), Widthptr)
   229			n.Xoffset = l.Xoffset + int64(array_nel)
   230			gdata(n, r.Right, Widthptr)
   231			n.Xoffset = l.Xoffset + int64(array_cap)
   232			gdata(n, r.Right, Widthptr)
   233	
   234			// Fall through to init underlying array.
   235			l = a
   236			fallthrough
   237	
   238		case OARRAYLIT, OSTRUCTLIT:
   239			s.initplan(r)
   240	
   241			p := s.initplans[r]
   242			n := l.copy()
   243			for i := range p.E {
   244				e := &p.E[i]
   245				n.Xoffset = l.Xoffset + e.Xoffset
   246				n.Type = e.Expr.Type
   247				if e.Expr.Op == OLITERAL {
   248					gdata(n, e.Expr, int(n.Type.Width))
   249					continue
   250				}
   251				setlineno(e.Expr)
   252				a := n.sepcopy()
   253				if !s.staticassign(a, e.Expr) {
   254					s.append(nod(OAS, a, e.Expr))
   255				}
   256			}
   257	
   258			return true
   259	
   260		case OMAPLIT:
   261			break
   262	
   263		case OCLOSURE:
   264			if hasemptycvars(r) {
   265				if Debug_closure > 0 {
   266					Warnl(r.Pos, "closure converted to global")
   267				}
   268				// Closures with no captured variables are globals,
   269				// so the assignment can be done at link time.
   270				gdata(l, r.Func.Closure.Func.Nname, Widthptr)
   271				return true
   272			}
   273			closuredebugruntimecheck(r)
   274	
   275		case OCONVIFACE:
   276			// This logic is mirrored in isStaticCompositeLiteral.
   277			// If you change something here, change it there, and vice versa.
   278	
   279			// Determine the underlying concrete type and value we are converting from.
   280			val := r
   281			for val.Op == OCONVIFACE {
   282				val = val.Left
   283			}
   284			if val.Type.IsInterface() {
   285				// val is an interface type.
   286				// If val is nil, we can statically initialize l;
   287				// both words are zero and so there no work to do, so report success.
   288				// If val is non-nil, we have no concrete type to record,
   289				// and we won't be able to statically initialize its value, so report failure.
   290				return Isconst(val, CTNIL)
   291			}
   292	
   293			var itab *Node
   294			if l.Type.IsEmptyInterface() {
   295				itab = typename(val.Type)
   296			} else {
   297				itab = itabname(val.Type, l.Type)
   298			}
   299	
   300			// Create a copy of l to modify while we emit data.
   301			n := l.copy()
   302	
   303			// Emit itab, advance offset.
   304			gdata(n, itab, Widthptr)
   305			n.Xoffset += int64(Widthptr)
   306	
   307			// Emit data.
   308			if isdirectiface(val.Type) {
   309				if Isconst(val, CTNIL) {
   310					// Nil is zero, nothing to do.
   311					return true
   312				}
   313				// Copy val directly into n.
   314				n.Type = val.Type
   315				setlineno(val)
   316				a := n.sepcopy()
   317				if !s.staticassign(a, val) {
   318					s.append(nod(OAS, a, val))
   319				}
   320			} else {
   321				// Construct temp to hold val, write pointer to temp into n.
   322				a := staticname(val.Type)
   323				s.inittemps[val] = a
   324				if !s.staticassign(a, val) {
   325					s.append(nod(OAS, a, val))
   326				}
   327				ptr := nod(OADDR, a, nil)
   328				n.Type = types.NewPtr(val.Type)
   329				gdata(n, ptr, Widthptr)
   330			}
   331	
   332			return true
   333		}
   334	
   335		//dump("not static", r);
   336		return false
   337	}
   338	
   339	// initContext is the context in which static data is populated.
   340	// It is either in an init function or in any other function.
   341	// Static data populated in an init function will be written either
   342	// zero times (as a readonly, static data symbol) or
   343	// one time (during init function execution).
   344	// Either way, there is no opportunity for races or further modification,
   345	// so the data can be written to a (possibly readonly) data symbol.
   346	// Static data populated in any other function needs to be local to
   347	// that function to allow multiple instances of that function
   348	// to execute concurrently without clobbering each others' data.
   349	type initContext uint8
   350	
   351	const (
   352		inInitFunction initContext = iota
   353		inNonInitFunction
   354	)
   355	
   356	func (c initContext) String() string {
   357		if c == inInitFunction {
   358			return "inInitFunction"
   359		}
   360		return "inNonInitFunction"
   361	}
   362	
   363	// from here down is the walk analysis
   364	// of composite literals.
   365	// most of the work is to generate
   366	// data statements for the constant
   367	// part of the composite literal.
   368	
   369	var statuniqgen int // name generator for static temps
   370	
   371	// staticname returns a name backed by a static data symbol.
   372	// Callers should call n.Name.SetReadonly(true) on the
   373	// returned node for readonly nodes.
   374	func staticname(t *types.Type) *Node {
   375		// Don't use lookupN; it interns the resulting string, but these are all unique.
   376		n := newname(lookup(fmt.Sprintf(".stmp_%d", statuniqgen)))
   377		statuniqgen++
   378		addvar(n, t, PEXTERN)
   379		return n
   380	}
   381	
   382	func isLiteral(n *Node) bool {
   383		// Treat nils as zeros rather than literals.
   384		return n.Op == OLITERAL && n.Val().Ctype() != CTNIL
   385	}
   386	
   387	func (n *Node) isSimpleName() bool {
   388		return n.Op == ONAME && n.Addable() && n.Class() != PAUTOHEAP && n.Class() != PEXTERN
   389	}
   390	
   391	func litas(l *Node, r *Node, init *Nodes) {
   392		a := nod(OAS, l, r)
   393		a = typecheck(a, ctxStmt)
   394		a = walkexpr(a, init)
   395		init.Append(a)
   396	}
   397	
   398	// initGenType is a bitmap indicating the types of generation that will occur for a static value.
   399	type initGenType uint8
   400	
   401	const (
   402		initDynamic initGenType = 1 << iota // contains some dynamic values, for which init code will be generated
   403		initConst                           // contains some constant values, which may be written into data symbols
   404	)
   405	
   406	// getdyn calculates the initGenType for n.
   407	// If top is false, getdyn is recursing.
   408	func getdyn(n *Node, top bool) initGenType {
   409		switch n.Op {
   410		default:
   411			if isLiteral(n) {
   412				return initConst
   413			}
   414			return initDynamic
   415	
   416		case OSLICELIT:
   417			if !top {
   418				return initDynamic
   419			}
   420			if n.Right.Int64()/4 > int64(n.List.Len()) {
   421				// <25% of entries have explicit values.
   422				// Very rough estimation, it takes 4 bytes of instructions
   423				// to initialize 1 byte of result. So don't use a static
   424				// initializer if the dynamic initialization code would be
   425				// smaller than the static value.
   426				// See issue 23780.
   427				return initDynamic
   428			}
   429	
   430		case OARRAYLIT, OSTRUCTLIT:
   431		}
   432	
   433		var mode initGenType
   434		for _, n1 := range n.List.Slice() {
   435			switch n1.Op {
   436			case OKEY:
   437				n1 = n1.Right
   438			case OSTRUCTKEY:
   439				n1 = n1.Left
   440			}
   441			mode |= getdyn(n1, false)
   442			if mode == initDynamic|initConst {
   443				break
   444			}
   445		}
   446		return mode
   447	}
   448	
   449	// isStaticCompositeLiteral reports whether n is a compile-time constant.
   450	func isStaticCompositeLiteral(n *Node) bool {
   451		switch n.Op {
   452		case OSLICELIT:
   453			return false
   454		case OARRAYLIT:
   455			for _, r := range n.List.Slice() {
   456				if r.Op == OKEY {
   457					r = r.Right
   458				}
   459				if !isStaticCompositeLiteral(r) {
   460					return false
   461				}
   462			}
   463			return true
   464		case OSTRUCTLIT:
   465			for _, r := range n.List.Slice() {
   466				if r.Op != OSTRUCTKEY {
   467					Fatalf("isStaticCompositeLiteral: rhs not OSTRUCTKEY: %v", r)
   468				}
   469				if !isStaticCompositeLiteral(r.Left) {
   470					return false
   471				}
   472			}
   473			return true
   474		case OLITERAL:
   475			return true
   476		case OCONVIFACE:
   477			// See staticassign's OCONVIFACE case for comments.
   478			val := n
   479			for val.Op == OCONVIFACE {
   480				val = val.Left
   481			}
   482			if val.Type.IsInterface() {
   483				return Isconst(val, CTNIL)
   484			}
   485			if isdirectiface(val.Type) && Isconst(val, CTNIL) {
   486				return true
   487			}
   488			return isStaticCompositeLiteral(val)
   489		}
   490		return false
   491	}
   492	
   493	// initKind is a kind of static initialization: static, dynamic, or local.
   494	// Static initialization represents literals and
   495	// literal components of composite literals.
   496	// Dynamic initialization represents non-literals and
   497	// non-literal components of composite literals.
   498	// LocalCode initializion represents initialization
   499	// that occurs purely in generated code local to the function of use.
   500	// Initialization code is sometimes generated in passes,
   501	// first static then dynamic.
   502	type initKind uint8
   503	
   504	const (
   505		initKindStatic initKind = iota + 1
   506		initKindDynamic
   507		initKindLocalCode
   508	)
   509	
   510	// fixedlit handles struct, array, and slice literals.
   511	// TODO: expand documentation.
   512	func fixedlit(ctxt initContext, kind initKind, n *Node, var_ *Node, init *Nodes) {
   513		var splitnode func(*Node) (a *Node, value *Node)
   514		switch n.Op {
   515		case OARRAYLIT, OSLICELIT:
   516			var k int64
   517			splitnode = func(r *Node) (*Node, *Node) {
   518				if r.Op == OKEY {
   519					k = indexconst(r.Left)
   520					if k < 0 {
   521						Fatalf("fixedlit: invalid index %v", r.Left)
   522					}
   523					r = r.Right
   524				}
   525				a := nod(OINDEX, var_, nodintconst(k))
   526				k++
   527				return a, r
   528			}
   529		case OSTRUCTLIT:
   530			splitnode = func(r *Node) (*Node, *Node) {
   531				if r.Op != OSTRUCTKEY {
   532					Fatalf("fixedlit: rhs not OSTRUCTKEY: %v", r)
   533				}
   534				if r.Sym.IsBlank() {
   535					return nblank, r.Left
   536				}
   537				setlineno(r)
   538				return nodSym(ODOT, var_, r.Sym), r.Left
   539			}
   540		default:
   541			Fatalf("fixedlit bad op: %v", n.Op)
   542		}
   543	
   544		for _, r := range n.List.Slice() {
   545			a, value := splitnode(r)
   546	
   547			switch value.Op {
   548			case OSLICELIT:
   549				if (kind == initKindStatic && ctxt == inNonInitFunction) || (kind == initKindDynamic && ctxt == inInitFunction) {
   550					slicelit(ctxt, value, a, init)
   551					continue
   552				}
   553	
   554			case OARRAYLIT, OSTRUCTLIT:
   555				fixedlit(ctxt, kind, value, a, init)
   556				continue
   557			}
   558	
   559			islit := isLiteral(value)
   560			if (kind == initKindStatic && !islit) || (kind == initKindDynamic && islit) {
   561				continue
   562			}
   563	
   564			// build list of assignments: var[index] = expr
   565			setlineno(a)
   566			a = nod(OAS, a, value)
   567			a = typecheck(a, ctxStmt)
   568			switch kind {
   569			case initKindStatic:
   570				genAsStatic(a)
   571			case initKindDynamic, initKindLocalCode:
   572				a = orderStmtInPlace(a, map[string][]*Node{})
   573				a = walkstmt(a)
   574				init.Append(a)
   575			default:
   576				Fatalf("fixedlit: bad kind %d", kind)
   577			}
   578	
   579		}
   580	}
   581	
   582	func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
   583		// make an array type corresponding the number of elements we have
   584		t := types.NewArray(n.Type.Elem(), n.Right.Int64())
   585		dowidth(t)
   586	
   587		if ctxt == inNonInitFunction {
   588			// put everything into static array
   589			vstat := staticname(t)
   590	
   591			fixedlit(ctxt, initKindStatic, n, vstat, init)
   592			fixedlit(ctxt, initKindDynamic, n, vstat, init)
   593	
   594			// copy static to slice
   595			var_ = typecheck(var_, ctxExpr|ctxAssign)
   596			var nam Node
   597			if !stataddr(&nam, var_) || nam.Class() != PEXTERN {
   598				Fatalf("slicelit: %v", var_)
   599			}
   600	
   601			var v Node
   602			v.Type = types.Types[TINT]
   603			setintconst(&v, t.NumElem())
   604	
   605			nam.Xoffset += int64(array_array)
   606			gdata(&nam, nod(OADDR, vstat, nil), Widthptr)
   607			nam.Xoffset += int64(array_nel) - int64(array_array)
   608			gdata(&nam, &v, Widthptr)
   609			nam.Xoffset += int64(array_cap) - int64(array_nel)
   610			gdata(&nam, &v, Widthptr)
   611	
   612			return
   613		}
   614	
   615		// recipe for var = []t{...}
   616		// 1. make a static array
   617		//	var vstat [...]t
   618		// 2. assign (data statements) the constant part
   619		//	vstat = constpart{}
   620		// 3. make an auto pointer to array and allocate heap to it
   621		//	var vauto *[...]t = new([...]t)
   622		// 4. copy the static array to the auto array
   623		//	*vauto = vstat
   624		// 5. for each dynamic part assign to the array
   625		//	vauto[i] = dynamic part
   626		// 6. assign slice of allocated heap to var
   627		//	var = vauto[:]
   628		//
   629		// an optimization is done if there is no constant part
   630		//	3. var vauto *[...]t = new([...]t)
   631		//	5. vauto[i] = dynamic part
   632		//	6. var = vauto[:]
   633	
   634		// if the literal contains constants,
   635		// make static initialized array (1),(2)
   636		var vstat *Node
   637	
   638		mode := getdyn(n, true)
   639		if mode&initConst != 0 {
   640			vstat = staticname(t)
   641			if ctxt == inInitFunction {
   642				vstat.Name.SetReadonly(true)
   643			}
   644			fixedlit(ctxt, initKindStatic, n, vstat, init)
   645		}
   646	
   647		// make new auto *array (3 declare)
   648		vauto := temp(types.NewPtr(t))
   649	
   650		// set auto to point at new temp or heap (3 assign)
   651		var a *Node
   652		if x := prealloc[n]; x != nil {
   653			// temp allocated during order.go for dddarg
   654			if !types.Identical(t, x.Type) {
   655				panic("dotdotdot base type does not match order's assigned type")
   656			}
   657	
   658			if vstat == nil {
   659				a = nod(OAS, x, nil)
   660				a = typecheck(a, ctxStmt)
   661				init.Append(a) // zero new temp
   662			} else {
   663				// Declare that we're about to initialize all of x.
   664				// (Which happens at the *vauto = vstat below.)
   665				init.Append(nod(OVARDEF, x, nil))
   666			}
   667	
   668			a = nod(OADDR, x, nil)
   669		} else if n.Esc == EscNone {
   670			a = temp(t)
   671			if vstat == nil {
   672				a = nod(OAS, temp(t), nil)
   673				a = typecheck(a, ctxStmt)
   674				init.Append(a) // zero new temp
   675				a = a.Left
   676			} else {
   677				init.Append(nod(OVARDEF, a, nil))
   678			}
   679	
   680			a = nod(OADDR, a, nil)
   681		} else {
   682			a = nod(ONEW, nil, nil)
   683			a.List.Set1(typenod(t))
   684		}
   685	
   686		a = nod(OAS, vauto, a)
   687		a = typecheck(a, ctxStmt)
   688		a = walkexpr(a, init)
   689		init.Append(a)
   690	
   691		if vstat != nil {
   692			// copy static to heap (4)
   693			a = nod(ODEREF, vauto, nil)
   694	
   695			a = nod(OAS, a, vstat)
   696			a = typecheck(a, ctxStmt)
   697			a = walkexpr(a, init)
   698			init.Append(a)
   699		}
   700	
   701		// put dynamics into array (5)
   702		var index int64
   703		for _, value := range n.List.Slice() {
   704			if value.Op == OKEY {
   705				index = indexconst(value.Left)
   706				if index < 0 {
   707					Fatalf("slicelit: invalid index %v", value.Left)
   708				}
   709				value = value.Right
   710			}
   711			a := nod(OINDEX, vauto, nodintconst(index))
   712			a.SetBounded(true)
   713			index++
   714	
   715			// TODO need to check bounds?
   716	
   717			switch value.Op {
   718			case OSLICELIT:
   719				break
   720	
   721			case OARRAYLIT, OSTRUCTLIT:
   722				k := initKindDynamic
   723				if vstat == nil {
   724					// Generate both static and dynamic initializations.
   725					// See issue #31987.
   726					k = initKindLocalCode
   727				}
   728				fixedlit(ctxt, k, value, a, init)
   729				continue
   730			}
   731	
   732			if vstat != nil && isLiteral(value) { // already set by copy from static value
   733				continue
   734			}
   735	
   736			// build list of vauto[c] = expr
   737			setlineno(value)
   738			a = nod(OAS, a, value)
   739	
   740			a = typecheck(a, ctxStmt)
   741			a = orderStmtInPlace(a, map[string][]*Node{})
   742			a = walkstmt(a)
   743			init.Append(a)
   744		}
   745	
   746		// make slice out of heap (6)
   747		a = nod(OAS, var_, nod(OSLICE, vauto, nil))
   748	
   749		a = typecheck(a, ctxStmt)
   750		a = orderStmtInPlace(a, map[string][]*Node{})
   751		a = walkstmt(a)
   752		init.Append(a)
   753	}
   754	
   755	func maplit(n *Node, m *Node, init *Nodes) {
   756		// make the map var
   757		a := nod(OMAKE, nil, nil)
   758		a.Esc = n.Esc
   759		a.List.Set2(typenod(n.Type), nodintconst(int64(n.List.Len())))
   760		litas(m, a, init)
   761	
   762		entries := n.List.Slice()
   763	
   764		// The order pass already removed any dynamic (runtime-computed) entries.
   765		// All remaining entries are static. Double-check that.
   766		for _, r := range entries {
   767			if !isStaticCompositeLiteral(r.Left) || !isStaticCompositeLiteral(r.Right) {
   768				Fatalf("maplit: entry is not a literal: %v", r)
   769			}
   770		}
   771	
   772		if len(entries) > 25 {
   773			// For a large number of entries, put them in an array and loop.
   774	
   775			// build types [count]Tindex and [count]Tvalue
   776			tk := types.NewArray(n.Type.Key(), int64(len(entries)))
   777			te := types.NewArray(n.Type.Elem(), int64(len(entries)))
   778	
   779			// TODO(josharian): suppress alg generation for these types?
   780			dowidth(tk)
   781			dowidth(te)
   782	
   783			// make and initialize static arrays
   784			vstatk := staticname(tk)
   785			vstatk.Name.SetReadonly(true)
   786			vstate := staticname(te)
   787			vstate.Name.SetReadonly(true)
   788	
   789			datak := nod(OARRAYLIT, nil, nil)
   790			datae := nod(OARRAYLIT, nil, nil)
   791			for _, r := range entries {
   792				datak.List.Append(r.Left)
   793				datae.List.Append(r.Right)
   794			}
   795			fixedlit(inInitFunction, initKindStatic, datak, vstatk, init)
   796			fixedlit(inInitFunction, initKindStatic, datae, vstate, init)
   797	
   798			// loop adding structure elements to map
   799			// for i = 0; i < len(vstatk); i++ {
   800			//	map[vstatk[i]] = vstate[i]
   801			// }
   802			i := temp(types.Types[TINT])
   803			rhs := nod(OINDEX, vstate, i)
   804			rhs.SetBounded(true)
   805	
   806			kidx := nod(OINDEX, vstatk, i)
   807			kidx.SetBounded(true)
   808			lhs := nod(OINDEX, m, kidx)
   809	
   810			zero := nod(OAS, i, nodintconst(0))
   811			cond := nod(OLT, i, nodintconst(tk.NumElem()))
   812			incr := nod(OAS, i, nod(OADD, i, nodintconst(1)))
   813			body := nod(OAS, lhs, rhs)
   814	
   815			loop := nod(OFOR, cond, incr)
   816			loop.Nbody.Set1(body)
   817			loop.Ninit.Set1(zero)
   818	
   819			loop = typecheck(loop, ctxStmt)
   820			loop = walkstmt(loop)
   821			init.Append(loop)
   822			return
   823		}
   824		// For a small number of entries, just add them directly.
   825	
   826		// Build list of var[c] = expr.
   827		// Use temporaries so that mapassign1 can have addressable key, elem.
   828		// TODO(josharian): avoid map key temporaries for mapfast_* assignments with literal keys.
   829		tmpkey := temp(m.Type.Key())
   830		tmpelem := temp(m.Type.Elem())
   831	
   832		for _, r := range entries {
   833			index, elem := r.Left, r.Right
   834	
   835			setlineno(index)
   836			a := nod(OAS, tmpkey, index)
   837			a = typecheck(a, ctxStmt)
   838			a = walkstmt(a)
   839			init.Append(a)
   840	
   841			setlineno(elem)
   842			a = nod(OAS, tmpelem, elem)
   843			a = typecheck(a, ctxStmt)
   844			a = walkstmt(a)
   845			init.Append(a)
   846	
   847			setlineno(tmpelem)
   848			a = nod(OAS, nod(OINDEX, m, tmpkey), tmpelem)
   849			a = typecheck(a, ctxStmt)
   850			a = walkstmt(a)
   851			init.Append(a)
   852		}
   853	
   854		a = nod(OVARKILL, tmpkey, nil)
   855		a = typecheck(a, ctxStmt)
   856		init.Append(a)
   857		a = nod(OVARKILL, tmpelem, nil)
   858		a = typecheck(a, ctxStmt)
   859		init.Append(a)
   860	}
   861	
   862	func anylit(n *Node, var_ *Node, init *Nodes) {
   863		t := n.Type
   864		switch n.Op {
   865		default:
   866			Fatalf("anylit: not lit, op=%v node=%v", n.Op, n)
   867	
   868		case ONAME:
   869			a := nod(OAS, var_, n)
   870			a = typecheck(a, ctxStmt)
   871			init.Append(a)
   872	
   873		case OPTRLIT:
   874			if !t.IsPtr() {
   875				Fatalf("anylit: not ptr")
   876			}
   877	
   878			var r *Node
   879			if n.Right != nil {
   880				// n.Right is stack temporary used as backing store.
   881				init.Append(nod(OAS, n.Right, nil)) // zero backing store, just in case (#18410)
   882				r = nod(OADDR, n.Right, nil)
   883				r = typecheck(r, ctxExpr)
   884			} else {
   885				r = nod(ONEW, nil, nil)
   886				r.SetTypecheck(1)
   887				r.Type = t
   888				r.Esc = n.Esc
   889			}
   890	
   891			r = walkexpr(r, init)
   892			a := nod(OAS, var_, r)
   893	
   894			a = typecheck(a, ctxStmt)
   895			init.Append(a)
   896	
   897			var_ = nod(ODEREF, var_, nil)
   898			var_ = typecheck(var_, ctxExpr|ctxAssign)
   899			anylit(n.Left, var_, init)
   900	
   901		case OSTRUCTLIT, OARRAYLIT:
   902			if !t.IsStruct() && !t.IsArray() {
   903				Fatalf("anylit: not struct/array")
   904			}
   905	
   906			if var_.isSimpleName() && n.List.Len() > 4 {
   907				// lay out static data
   908				vstat := staticname(t)
   909				vstat.Name.SetReadonly(true)
   910	
   911				ctxt := inInitFunction
   912				if n.Op == OARRAYLIT {
   913					ctxt = inNonInitFunction
   914				}
   915				fixedlit(ctxt, initKindStatic, n, vstat, init)
   916	
   917				// copy static to var
   918				a := nod(OAS, var_, vstat)
   919	
   920				a = typecheck(a, ctxStmt)
   921				a = walkexpr(a, init)
   922				init.Append(a)
   923	
   924				// add expressions to automatic
   925				fixedlit(inInitFunction, initKindDynamic, n, var_, init)
   926				break
   927			}
   928	
   929			var components int64
   930			if n.Op == OARRAYLIT {
   931				components = t.NumElem()
   932			} else {
   933				components = int64(t.NumFields())
   934			}
   935			// initialization of an array or struct with unspecified components (missing fields or arrays)
   936			if var_.isSimpleName() || int64(n.List.Len()) < components {
   937				a := nod(OAS, var_, nil)
   938				a = typecheck(a, ctxStmt)
   939				a = walkexpr(a, init)
   940				init.Append(a)
   941			}
   942	
   943			fixedlit(inInitFunction, initKindLocalCode, n, var_, init)
   944	
   945		case OSLICELIT:
   946			slicelit(inInitFunction, n, var_, init)
   947	
   948		case OMAPLIT:
   949			if !t.IsMap() {
   950				Fatalf("anylit: not map")
   951			}
   952			maplit(n, var_, init)
   953		}
   954	}
   955	
   956	func oaslit(n *Node, init *Nodes) bool {
   957		if n.Left == nil || n.Right == nil {
   958			// not a special composite literal assignment
   959			return false
   960		}
   961		if n.Left.Type == nil || n.Right.Type == nil {
   962			// not a special composite literal assignment
   963			return false
   964		}
   965		if !n.Left.isSimpleName() {
   966			// not a special composite literal assignment
   967			return false
   968		}
   969		if !types.Identical(n.Left.Type, n.Right.Type) {
   970			// not a special composite literal assignment
   971			return false
   972		}
   973	
   974		switch n.Right.Op {
   975		default:
   976			// not a special composite literal assignment
   977			return false
   978	
   979		case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT:
   980			if vmatch1(n.Left, n.Right) {
   981				// not a special composite literal assignment
   982				return false
   983			}
   984			anylit(n.Right, n.Left, init)
   985		}
   986	
   987		n.Op = OEMPTY
   988		n.Right = nil
   989		return true
   990	}
   991	
   992	func getlit(lit *Node) int {
   993		if smallintconst(lit) {
   994			return int(lit.Int64())
   995		}
   996		return -1
   997	}
   998	
   999	// stataddr sets nam to the static address of n and reports whether it succeeded.
  1000	func stataddr(nam *Node, n *Node) bool {
  1001		if n == nil {
  1002			return false
  1003		}
  1004	
  1005		switch n.Op {
  1006		case ONAME:
  1007			*nam = *n
  1008			return n.Addable()
  1009	
  1010		case ODOT:
  1011			if !stataddr(nam, n.Left) {
  1012				break
  1013			}
  1014			nam.Xoffset += n.Xoffset
  1015			nam.Type = n.Type
  1016			return true
  1017	
  1018		case OINDEX:
  1019			if n.Left.Type.IsSlice() {
  1020				break
  1021			}
  1022			if !stataddr(nam, n.Left) {
  1023				break
  1024			}
  1025			l := getlit(n.Right)
  1026			if l < 0 {
  1027				break
  1028			}
  1029	
  1030			// Check for overflow.
  1031			if n.Type.Width != 0 && thearch.MAXWIDTH/n.Type.Width <= int64(l) {
  1032				break
  1033			}
  1034			nam.Xoffset += int64(l) * n.Type.Width
  1035			nam.Type = n.Type
  1036			return true
  1037		}
  1038	
  1039		return false
  1040	}
  1041	
  1042	func (s *InitSchedule) initplan(n *Node) {
  1043		if s.initplans[n] != nil {
  1044			return
  1045		}
  1046		p := new(InitPlan)
  1047		s.initplans[n] = p
  1048		switch n.Op {
  1049		default:
  1050			Fatalf("initplan")
  1051	
  1052		case OARRAYLIT, OSLICELIT:
  1053			var k int64
  1054			for _, a := range n.List.Slice() {
  1055				if a.Op == OKEY {
  1056					k = indexconst(a.Left)
  1057					if k < 0 {
  1058						Fatalf("initplan arraylit: invalid index %v", a.Left)
  1059					}
  1060					a = a.Right
  1061				}
  1062				s.addvalue(p, k*n.Type.Elem().Width, a)
  1063				k++
  1064			}
  1065	
  1066		case OSTRUCTLIT:
  1067			for _, a := range n.List.Slice() {
  1068				if a.Op != OSTRUCTKEY {
  1069					Fatalf("initplan structlit")
  1070				}
  1071				if a.Sym.IsBlank() {
  1072					continue
  1073				}
  1074				s.addvalue(p, a.Xoffset, a.Left)
  1075			}
  1076	
  1077		case OMAPLIT:
  1078			for _, a := range n.List.Slice() {
  1079				if a.Op != OKEY {
  1080					Fatalf("initplan maplit")
  1081				}
  1082				s.addvalue(p, -1, a.Right)
  1083			}
  1084		}
  1085	}
  1086	
  1087	func (s *InitSchedule) addvalue(p *InitPlan, xoffset int64, n *Node) {
  1088		// special case: zero can be dropped entirely
  1089		if isZero(n) {
  1090			return
  1091		}
  1092	
  1093		// special case: inline struct and array (not slice) literals
  1094		if isvaluelit(n) {
  1095			s.initplan(n)
  1096			q := s.initplans[n]
  1097			for _, qe := range q.E {
  1098				// qe is a copy; we are not modifying entries in q.E
  1099				qe.Xoffset += xoffset
  1100				p.E = append(p.E, qe)
  1101			}
  1102			return
  1103		}
  1104	
  1105		// add to plan
  1106		p.E = append(p.E, InitEntry{Xoffset: xoffset, Expr: n})
  1107	}
  1108	
  1109	func isZero(n *Node) bool {
  1110		switch n.Op {
  1111		case OLITERAL:
  1112			switch u := n.Val().U.(type) {
  1113			default:
  1114				Dump("unexpected literal", n)
  1115				Fatalf("isZero")
  1116			case *NilVal:
  1117				return true
  1118			case string:
  1119				return u == ""
  1120			case bool:
  1121				return !u
  1122			case *Mpint:
  1123				return u.CmpInt64(0) == 0
  1124			case *Mpflt:
  1125				return u.CmpFloat64(0) == 0
  1126			case *Mpcplx:
  1127				return u.Real.CmpFloat64(0) == 0 && u.Imag.CmpFloat64(0) == 0
  1128			}
  1129	
  1130		case OARRAYLIT:
  1131			for _, n1 := range n.List.Slice() {
  1132				if n1.Op == OKEY {
  1133					n1 = n1.Right
  1134				}
  1135				if !isZero(n1) {
  1136					return false
  1137				}
  1138			}
  1139			return true
  1140	
  1141		case OSTRUCTLIT:
  1142			for _, n1 := range n.List.Slice() {
  1143				if !isZero(n1.Left) {
  1144					return false
  1145				}
  1146			}
  1147			return true
  1148		}
  1149	
  1150		return false
  1151	}
  1152	
  1153	func isvaluelit(n *Node) bool {
  1154		return n.Op == OARRAYLIT || n.Op == OSTRUCTLIT
  1155	}
  1156	
  1157	func genAsStatic(as *Node) {
  1158		if as.Left.Type == nil {
  1159			Fatalf("genAsStatic as.Left not typechecked")
  1160		}
  1161	
  1162		var nam Node
  1163		if !stataddr(&nam, as.Left) || (nam.Class() != PEXTERN && as.Left != nblank) {
  1164			Fatalf("genAsStatic: lhs %v", as.Left)
  1165		}
  1166	
  1167		switch {
  1168		case as.Right.Op == OLITERAL:
  1169		case as.Right.Op == ONAME && as.Right.Class() == PFUNC:
  1170		default:
  1171			Fatalf("genAsStatic: rhs %v", as.Right)
  1172		}
  1173	
  1174		gdata(&nam, as.Right, int(as.Right.Type.Width))
  1175	}
  1176	

View as plain text