summaryrefslogtreecommitdiffstats
path: root/src/cmd/compile/internal/gc/sinit.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/gc/sinit.go')
-rw-r--r--src/cmd/compile/internal/gc/sinit.go1172
1 files changed, 1172 insertions, 0 deletions
diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go
new file mode 100644
index 0000000..4d0837b
--- /dev/null
+++ b/src/cmd/compile/internal/gc/sinit.go
@@ -0,0 +1,1172 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gc
+
+import (
+ "cmd/compile/internal/types"
+ "cmd/internal/obj"
+ "fmt"
+)
+
+type InitEntry struct {
+ Xoffset int64 // struct, array only
+ Expr *Node // bytes of run-time computed expressions
+}
+
+type InitPlan struct {
+ E []InitEntry
+}
+
+// An InitSchedule is used to decompose assignment statements into
+// static and dynamic initialization parts. Static initializations are
+// handled by populating variables' linker symbol data, while dynamic
+// initializations are accumulated to be executed in order.
+type InitSchedule struct {
+ // out is the ordered list of dynamic initialization
+ // statements.
+ out []*Node
+
+ initplans map[*Node]*InitPlan
+ inittemps map[*Node]*Node
+}
+
+func (s *InitSchedule) append(n *Node) {
+ s.out = append(s.out, n)
+}
+
+// staticInit adds an initialization statement n to the schedule.
+func (s *InitSchedule) staticInit(n *Node) {
+ if !s.tryStaticInit(n) {
+ if Debug.P != 0 {
+ Dump("nonstatic", n)
+ }
+ s.append(n)
+ }
+}
+
+// tryStaticInit attempts to statically execute an initialization
+// statement and reports whether it succeeded.
+func (s *InitSchedule) tryStaticInit(n *Node) bool {
+ // Only worry about simple "l = r" assignments. Multiple
+ // variable/expression OAS2 assignments have already been
+ // replaced by multiple simple OAS assignments, and the other
+ // OAS2* assignments mostly necessitate dynamic execution
+ // anyway.
+ if n.Op != OAS {
+ return false
+ }
+ if n.Left.isBlank() && candiscard(n.Right) {
+ return true
+ }
+ lno := setlineno(n)
+ defer func() { lineno = lno }()
+ return s.staticassign(n.Left, n.Right)
+}
+
+// like staticassign but we are copying an already
+// initialized value r.
+func (s *InitSchedule) staticcopy(l *Node, r *Node) bool {
+ if r.Op != ONAME {
+ return false
+ }
+ if r.Class() == PFUNC {
+ pfuncsym(l, r)
+ return true
+ }
+ if r.Class() != PEXTERN || r.Sym.Pkg != localpkg {
+ return false
+ }
+ if r.Name.Defn == nil { // probably zeroed but perhaps supplied externally and of unknown value
+ return false
+ }
+ if r.Name.Defn.Op != OAS {
+ return false
+ }
+ if r.Type.IsString() { // perhaps overwritten by cmd/link -X (#34675)
+ return false
+ }
+ orig := r
+ r = r.Name.Defn.Right
+
+ for r.Op == OCONVNOP && !types.Identical(r.Type, l.Type) {
+ r = r.Left
+ }
+
+ switch r.Op {
+ case ONAME:
+ if s.staticcopy(l, r) {
+ return true
+ }
+ // We may have skipped past one or more OCONVNOPs, so
+ // use conv to ensure r is assignable to l (#13263).
+ s.append(nod(OAS, l, conv(r, l.Type)))
+ return true
+
+ case OLITERAL:
+ if isZero(r) {
+ return true
+ }
+ litsym(l, r, int(l.Type.Width))
+ return true
+
+ case OADDR:
+ if a := r.Left; a.Op == ONAME {
+ addrsym(l, a)
+ return true
+ }
+
+ case OPTRLIT:
+ switch r.Left.Op {
+ case OARRAYLIT, OSLICELIT, OSTRUCTLIT, OMAPLIT:
+ // copy pointer
+ addrsym(l, s.inittemps[r])
+ return true
+ }
+
+ case OSLICELIT:
+ // copy slice
+ a := s.inittemps[r]
+ slicesym(l, a, r.Right.Int64Val())
+ return true
+
+ case OARRAYLIT, OSTRUCTLIT:
+ p := s.initplans[r]
+
+ n := l.copy()
+ for i := range p.E {
+ e := &p.E[i]
+ n.Xoffset = l.Xoffset + e.Xoffset
+ n.Type = e.Expr.Type
+ if e.Expr.Op == OLITERAL {
+ litsym(n, e.Expr, int(n.Type.Width))
+ continue
+ }
+ ll := n.sepcopy()
+ if s.staticcopy(ll, e.Expr) {
+ continue
+ }
+ // Requires computation, but we're
+ // copying someone else's computation.
+ rr := orig.sepcopy()
+ rr.Type = ll.Type
+ rr.Xoffset += e.Xoffset
+ setlineno(rr)
+ s.append(nod(OAS, ll, rr))
+ }
+
+ return true
+ }
+
+ return false
+}
+
+func (s *InitSchedule) staticassign(l *Node, r *Node) bool {
+ for r.Op == OCONVNOP {
+ r = r.Left
+ }
+
+ switch r.Op {
+ case ONAME:
+ return s.staticcopy(l, r)
+
+ case OLITERAL:
+ if isZero(r) {
+ return true
+ }
+ litsym(l, r, int(l.Type.Width))
+ return true
+
+ case OADDR:
+ var nam Node
+ if stataddr(&nam, r.Left) {
+ addrsym(l, &nam)
+ return true
+ }
+ fallthrough
+
+ case OPTRLIT:
+ switch r.Left.Op {
+ case OARRAYLIT, OSLICELIT, OMAPLIT, OSTRUCTLIT:
+ // Init pointer.
+ a := staticname(r.Left.Type)
+
+ s.inittemps[r] = a
+ addrsym(l, a)
+
+ // Init underlying literal.
+ if !s.staticassign(a, r.Left) {
+ s.append(nod(OAS, a, r.Left))
+ }
+ return true
+ }
+ //dump("not static ptrlit", r);
+
+ case OSTR2BYTES:
+ if l.Class() == PEXTERN && r.Left.Op == OLITERAL {
+ sval := r.Left.StringVal()
+ slicebytes(l, sval)
+ return true
+ }
+
+ case OSLICELIT:
+ s.initplan(r)
+ // Init slice.
+ bound := r.Right.Int64Val()
+ ta := types.NewArray(r.Type.Elem(), bound)
+ ta.SetNoalg(true)
+ a := staticname(ta)
+ s.inittemps[r] = a
+ slicesym(l, a, bound)
+ // Fall through to init underlying array.
+ l = a
+ fallthrough
+
+ case OARRAYLIT, OSTRUCTLIT:
+ s.initplan(r)
+
+ p := s.initplans[r]
+ n := l.copy()
+ for i := range p.E {
+ e := &p.E[i]
+ n.Xoffset = l.Xoffset + e.Xoffset
+ n.Type = e.Expr.Type
+ if e.Expr.Op == OLITERAL {
+ litsym(n, e.Expr, int(n.Type.Width))
+ continue
+ }
+ setlineno(e.Expr)
+ a := n.sepcopy()
+ if !s.staticassign(a, e.Expr) {
+ s.append(nod(OAS, a, e.Expr))
+ }
+ }
+
+ return true
+
+ case OMAPLIT:
+ break
+
+ case OCLOSURE:
+ if hasemptycvars(r) {
+ if Debug_closure > 0 {
+ Warnl(r.Pos, "closure converted to global")
+ }
+ // Closures with no captured variables are globals,
+ // so the assignment can be done at link time.
+ pfuncsym(l, r.Func.Closure.Func.Nname)
+ return true
+ }
+ closuredebugruntimecheck(r)
+
+ case OCONVIFACE:
+ // This logic is mirrored in isStaticCompositeLiteral.
+ // If you change something here, change it there, and vice versa.
+
+ // Determine the underlying concrete type and value we are converting from.
+ val := r
+ for val.Op == OCONVIFACE {
+ val = val.Left
+ }
+ if val.Type.IsInterface() {
+ // val is an interface type.
+ // If val is nil, we can statically initialize l;
+ // both words are zero and so there no work to do, so report success.
+ // If val is non-nil, we have no concrete type to record,
+ // and we won't be able to statically initialize its value, so report failure.
+ return Isconst(val, CTNIL)
+ }
+
+ markTypeUsedInInterface(val.Type, l.Sym.Linksym())
+
+ var itab *Node
+ if l.Type.IsEmptyInterface() {
+ itab = typename(val.Type)
+ } else {
+ itab = itabname(val.Type, l.Type)
+ }
+
+ // Create a copy of l to modify while we emit data.
+ n := l.copy()
+
+ // Emit itab, advance offset.
+ addrsym(n, itab.Left) // itab is an OADDR node
+ n.Xoffset += int64(Widthptr)
+
+ // Emit data.
+ if isdirectiface(val.Type) {
+ if Isconst(val, CTNIL) {
+ // Nil is zero, nothing to do.
+ return true
+ }
+ // Copy val directly into n.
+ n.Type = val.Type
+ setlineno(val)
+ a := n.sepcopy()
+ if !s.staticassign(a, val) {
+ s.append(nod(OAS, a, val))
+ }
+ } else {
+ // Construct temp to hold val, write pointer to temp into n.
+ a := staticname(val.Type)
+ s.inittemps[val] = a
+ if !s.staticassign(a, val) {
+ s.append(nod(OAS, a, val))
+ }
+ addrsym(n, a)
+ }
+
+ return true
+ }
+
+ //dump("not static", r);
+ return false
+}
+
+// initContext is the context in which static data is populated.
+// It is either in an init function or in any other function.
+// Static data populated in an init function will be written either
+// zero times (as a readonly, static data symbol) or
+// one time (during init function execution).
+// Either way, there is no opportunity for races or further modification,
+// so the data can be written to a (possibly readonly) data symbol.
+// Static data populated in any other function needs to be local to
+// that function to allow multiple instances of that function
+// to execute concurrently without clobbering each others' data.
+type initContext uint8
+
+const (
+ inInitFunction initContext = iota
+ inNonInitFunction
+)
+
+func (c initContext) String() string {
+ if c == inInitFunction {
+ return "inInitFunction"
+ }
+ return "inNonInitFunction"
+}
+
+// from here down is the walk analysis
+// of composite literals.
+// most of the work is to generate
+// data statements for the constant
+// part of the composite literal.
+
+var statuniqgen int // name generator for static temps
+
+// staticname returns a name backed by a (writable) static data symbol.
+// Use readonlystaticname for read-only node.
+func staticname(t *types.Type) *Node {
+ // Don't use lookupN; it interns the resulting string, but these are all unique.
+ n := newname(lookup(fmt.Sprintf("%s%d", obj.StaticNamePref, statuniqgen)))
+ statuniqgen++
+ addvar(n, t, PEXTERN)
+ return n
+}
+
+// readonlystaticname returns a name backed by a read-only static data symbol.
+func readonlystaticname(t *types.Type) *Node {
+ n := staticname(t)
+ n.MarkReadonly()
+ n.Sym.Linksym().Set(obj.AttrContentAddressable, true)
+ n.Sym.Linksym().Set(obj.AttrLocal, true)
+ return n
+}
+
+func (n *Node) isSimpleName() bool {
+ return n.Op == ONAME && n.Class() != PAUTOHEAP && n.Class() != PEXTERN
+}
+
+func litas(l *Node, r *Node, init *Nodes) {
+ a := nod(OAS, l, r)
+ a = typecheck(a, ctxStmt)
+ a = walkexpr(a, init)
+ init.Append(a)
+}
+
+// initGenType is a bitmap indicating the types of generation that will occur for a static value.
+type initGenType uint8
+
+const (
+ initDynamic initGenType = 1 << iota // contains some dynamic values, for which init code will be generated
+ initConst // contains some constant values, which may be written into data symbols
+)
+
+// getdyn calculates the initGenType for n.
+// If top is false, getdyn is recursing.
+func getdyn(n *Node, top bool) initGenType {
+ switch n.Op {
+ default:
+ if n.isGoConst() {
+ return initConst
+ }
+ return initDynamic
+
+ case OSLICELIT:
+ if !top {
+ return initDynamic
+ }
+ if n.Right.Int64Val()/4 > int64(n.List.Len()) {
+ // <25% of entries have explicit values.
+ // Very rough estimation, it takes 4 bytes of instructions
+ // to initialize 1 byte of result. So don't use a static
+ // initializer if the dynamic initialization code would be
+ // smaller than the static value.
+ // See issue 23780.
+ return initDynamic
+ }
+
+ case OARRAYLIT, OSTRUCTLIT:
+ }
+
+ var mode initGenType
+ for _, n1 := range n.List.Slice() {
+ switch n1.Op {
+ case OKEY:
+ n1 = n1.Right
+ case OSTRUCTKEY:
+ n1 = n1.Left
+ }
+ mode |= getdyn(n1, false)
+ if mode == initDynamic|initConst {
+ break
+ }
+ }
+ return mode
+}
+
+// isStaticCompositeLiteral reports whether n is a compile-time constant.
+func isStaticCompositeLiteral(n *Node) bool {
+ switch n.Op {
+ case OSLICELIT:
+ return false
+ case OARRAYLIT:
+ for _, r := range n.List.Slice() {
+ if r.Op == OKEY {
+ r = r.Right
+ }
+ if !isStaticCompositeLiteral(r) {
+ return false
+ }
+ }
+ return true
+ case OSTRUCTLIT:
+ for _, r := range n.List.Slice() {
+ if r.Op != OSTRUCTKEY {
+ Fatalf("isStaticCompositeLiteral: rhs not OSTRUCTKEY: %v", r)
+ }
+ if !isStaticCompositeLiteral(r.Left) {
+ return false
+ }
+ }
+ return true
+ case OLITERAL:
+ return true
+ case OCONVIFACE:
+ // See staticassign's OCONVIFACE case for comments.
+ val := n
+ for val.Op == OCONVIFACE {
+ val = val.Left
+ }
+ if val.Type.IsInterface() {
+ return Isconst(val, CTNIL)
+ }
+ if isdirectiface(val.Type) && Isconst(val, CTNIL) {
+ return true
+ }
+ return isStaticCompositeLiteral(val)
+ }
+ return false
+}
+
+// initKind is a kind of static initialization: static, dynamic, or local.
+// Static initialization represents literals and
+// literal components of composite literals.
+// Dynamic initialization represents non-literals and
+// non-literal components of composite literals.
+// LocalCode initialization represents initialization
+// that occurs purely in generated code local to the function of use.
+// Initialization code is sometimes generated in passes,
+// first static then dynamic.
+type initKind uint8
+
+const (
+ initKindStatic initKind = iota + 1
+ initKindDynamic
+ initKindLocalCode
+)
+
+// fixedlit handles struct, array, and slice literals.
+// TODO: expand documentation.
+func fixedlit(ctxt initContext, kind initKind, n *Node, var_ *Node, init *Nodes) {
+ isBlank := var_ == nblank
+ var splitnode func(*Node) (a *Node, value *Node)
+ switch n.Op {
+ case OARRAYLIT, OSLICELIT:
+ var k int64
+ splitnode = func(r *Node) (*Node, *Node) {
+ if r.Op == OKEY {
+ k = indexconst(r.Left)
+ if k < 0 {
+ Fatalf("fixedlit: invalid index %v", r.Left)
+ }
+ r = r.Right
+ }
+ a := nod(OINDEX, var_, nodintconst(k))
+ k++
+ if isBlank {
+ a = nblank
+ }
+ return a, r
+ }
+ case OSTRUCTLIT:
+ splitnode = func(r *Node) (*Node, *Node) {
+ if r.Op != OSTRUCTKEY {
+ Fatalf("fixedlit: rhs not OSTRUCTKEY: %v", r)
+ }
+ if r.Sym.IsBlank() || isBlank {
+ return nblank, r.Left
+ }
+ setlineno(r)
+ return nodSym(ODOT, var_, r.Sym), r.Left
+ }
+ default:
+ Fatalf("fixedlit bad op: %v", n.Op)
+ }
+
+ for _, r := range n.List.Slice() {
+ a, value := splitnode(r)
+ if a == nblank && candiscard(value) {
+ continue
+ }
+
+ switch value.Op {
+ case OSLICELIT:
+ if (kind == initKindStatic && ctxt == inNonInitFunction) || (kind == initKindDynamic && ctxt == inInitFunction) {
+ slicelit(ctxt, value, a, init)
+ continue
+ }
+
+ case OARRAYLIT, OSTRUCTLIT:
+ fixedlit(ctxt, kind, value, a, init)
+ continue
+ }
+
+ islit := value.isGoConst()
+ if (kind == initKindStatic && !islit) || (kind == initKindDynamic && islit) {
+ continue
+ }
+
+ // build list of assignments: var[index] = expr
+ setlineno(a)
+ a = nod(OAS, a, value)
+ a = typecheck(a, ctxStmt)
+ switch kind {
+ case initKindStatic:
+ genAsStatic(a)
+ case initKindDynamic, initKindLocalCode:
+ a = orderStmtInPlace(a, map[string][]*Node{})
+ a = walkstmt(a)
+ init.Append(a)
+ default:
+ Fatalf("fixedlit: bad kind %d", kind)
+ }
+
+ }
+}
+
+func isSmallSliceLit(n *Node) bool {
+ if n.Op != OSLICELIT {
+ return false
+ }
+
+ r := n.Right
+
+ return smallintconst(r) && (n.Type.Elem().Width == 0 || r.Int64Val() <= smallArrayBytes/n.Type.Elem().Width)
+}
+
+func slicelit(ctxt initContext, n *Node, var_ *Node, init *Nodes) {
+ // make an array type corresponding the number of elements we have
+ t := types.NewArray(n.Type.Elem(), n.Right.Int64Val())
+ dowidth(t)
+
+ if ctxt == inNonInitFunction {
+ // put everything into static array
+ vstat := staticname(t)
+
+ fixedlit(ctxt, initKindStatic, n, vstat, init)
+ fixedlit(ctxt, initKindDynamic, n, vstat, init)
+
+ // copy static to slice
+ var_ = typecheck(var_, ctxExpr|ctxAssign)
+ var nam Node
+ if !stataddr(&nam, var_) || nam.Class() != PEXTERN {
+ Fatalf("slicelit: %v", var_)
+ }
+ slicesym(&nam, vstat, t.NumElem())
+ return
+ }
+
+ // recipe for var = []t{...}
+ // 1. make a static array
+ // var vstat [...]t
+ // 2. assign (data statements) the constant part
+ // vstat = constpart{}
+ // 3. make an auto pointer to array and allocate heap to it
+ // var vauto *[...]t = new([...]t)
+ // 4. copy the static array to the auto array
+ // *vauto = vstat
+ // 5. for each dynamic part assign to the array
+ // vauto[i] = dynamic part
+ // 6. assign slice of allocated heap to var
+ // var = vauto[:]
+ //
+ // an optimization is done if there is no constant part
+ // 3. var vauto *[...]t = new([...]t)
+ // 5. vauto[i] = dynamic part
+ // 6. var = vauto[:]
+
+ // if the literal contains constants,
+ // make static initialized array (1),(2)
+ var vstat *Node
+
+ mode := getdyn(n, true)
+ if mode&initConst != 0 && !isSmallSliceLit(n) {
+ if ctxt == inInitFunction {
+ vstat = readonlystaticname(t)
+ } else {
+ vstat = staticname(t)
+ }
+ fixedlit(ctxt, initKindStatic, n, vstat, init)
+ }
+
+ // make new auto *array (3 declare)
+ vauto := temp(types.NewPtr(t))
+
+ // set auto to point at new temp or heap (3 assign)
+ var a *Node
+ if x := prealloc[n]; x != nil {
+ // temp allocated during order.go for dddarg
+ if !types.Identical(t, x.Type) {
+ panic("dotdotdot base type does not match order's assigned type")
+ }
+
+ if vstat == nil {
+ a = nod(OAS, x, nil)
+ a = typecheck(a, ctxStmt)
+ init.Append(a) // zero new temp
+ } else {
+ // Declare that we're about to initialize all of x.
+ // (Which happens at the *vauto = vstat below.)
+ init.Append(nod(OVARDEF, x, nil))
+ }
+
+ a = nod(OADDR, x, nil)
+ } else if n.Esc == EscNone {
+ a = temp(t)
+ if vstat == nil {
+ a = nod(OAS, temp(t), nil)
+ a = typecheck(a, ctxStmt)
+ init.Append(a) // zero new temp
+ a = a.Left
+ } else {
+ init.Append(nod(OVARDEF, a, nil))
+ }
+
+ a = nod(OADDR, a, nil)
+ } else {
+ a = nod(ONEW, nil, nil)
+ a.List.Set1(typenod(t))
+ }
+
+ a = nod(OAS, vauto, a)
+ a = typecheck(a, ctxStmt)
+ a = walkexpr(a, init)
+ init.Append(a)
+
+ if vstat != nil {
+ // copy static to heap (4)
+ a = nod(ODEREF, vauto, nil)
+
+ a = nod(OAS, a, vstat)
+ a = typecheck(a, ctxStmt)
+ a = walkexpr(a, init)
+ init.Append(a)
+ }
+
+ // put dynamics into array (5)
+ var index int64
+ for _, value := range n.List.Slice() {
+ if value.Op == OKEY {
+ index = indexconst(value.Left)
+ if index < 0 {
+ Fatalf("slicelit: invalid index %v", value.Left)
+ }
+ value = value.Right
+ }
+ a := nod(OINDEX, vauto, nodintconst(index))
+ a.SetBounded(true)
+ index++
+
+ // TODO need to check bounds?
+
+ switch value.Op {
+ case OSLICELIT:
+ break
+
+ case OARRAYLIT, OSTRUCTLIT:
+ k := initKindDynamic
+ if vstat == nil {
+ // Generate both static and dynamic initializations.
+ // See issue #31987.
+ k = initKindLocalCode
+ }
+ fixedlit(ctxt, k, value, a, init)
+ continue
+ }
+
+ if vstat != nil && value.isGoConst() { // already set by copy from static value
+ continue
+ }
+
+ // build list of vauto[c] = expr
+ setlineno(value)
+ a = nod(OAS, a, value)
+
+ a = typecheck(a, ctxStmt)
+ a = orderStmtInPlace(a, map[string][]*Node{})
+ a = walkstmt(a)
+ init.Append(a)
+ }
+
+ // make slice out of heap (6)
+ a = nod(OAS, var_, nod(OSLICE, vauto, nil))
+
+ a = typecheck(a, ctxStmt)
+ a = orderStmtInPlace(a, map[string][]*Node{})
+ a = walkstmt(a)
+ init.Append(a)
+}
+
+func maplit(n *Node, m *Node, init *Nodes) {
+ // make the map var
+ a := nod(OMAKE, nil, nil)
+ a.Esc = n.Esc
+ a.List.Set2(typenod(n.Type), nodintconst(int64(n.List.Len())))
+ litas(m, a, init)
+
+ entries := n.List.Slice()
+
+ // The order pass already removed any dynamic (runtime-computed) entries.
+ // All remaining entries are static. Double-check that.
+ for _, r := range entries {
+ if !isStaticCompositeLiteral(r.Left) || !isStaticCompositeLiteral(r.Right) {
+ Fatalf("maplit: entry is not a literal: %v", r)
+ }
+ }
+
+ if len(entries) > 25 {
+ // For a large number of entries, put them in an array and loop.
+
+ // build types [count]Tindex and [count]Tvalue
+ tk := types.NewArray(n.Type.Key(), int64(len(entries)))
+ te := types.NewArray(n.Type.Elem(), int64(len(entries)))
+
+ tk.SetNoalg(true)
+ te.SetNoalg(true)
+
+ dowidth(tk)
+ dowidth(te)
+
+ // make and initialize static arrays
+ vstatk := readonlystaticname(tk)
+ vstate := readonlystaticname(te)
+
+ datak := nod(OARRAYLIT, nil, nil)
+ datae := nod(OARRAYLIT, nil, nil)
+ for _, r := range entries {
+ datak.List.Append(r.Left)
+ datae.List.Append(r.Right)
+ }
+ fixedlit(inInitFunction, initKindStatic, datak, vstatk, init)
+ fixedlit(inInitFunction, initKindStatic, datae, vstate, init)
+
+ // loop adding structure elements to map
+ // for i = 0; i < len(vstatk); i++ {
+ // map[vstatk[i]] = vstate[i]
+ // }
+ i := temp(types.Types[TINT])
+ rhs := nod(OINDEX, vstate, i)
+ rhs.SetBounded(true)
+
+ kidx := nod(OINDEX, vstatk, i)
+ kidx.SetBounded(true)
+ lhs := nod(OINDEX, m, kidx)
+
+ zero := nod(OAS, i, nodintconst(0))
+ cond := nod(OLT, i, nodintconst(tk.NumElem()))
+ incr := nod(OAS, i, nod(OADD, i, nodintconst(1)))
+ body := nod(OAS, lhs, rhs)
+
+ loop := nod(OFOR, cond, incr)
+ loop.Nbody.Set1(body)
+ loop.Ninit.Set1(zero)
+
+ loop = typecheck(loop, ctxStmt)
+ loop = walkstmt(loop)
+ init.Append(loop)
+ return
+ }
+ // For a small number of entries, just add them directly.
+
+ // Build list of var[c] = expr.
+ // Use temporaries so that mapassign1 can have addressable key, elem.
+ // TODO(josharian): avoid map key temporaries for mapfast_* assignments with literal keys.
+ tmpkey := temp(m.Type.Key())
+ tmpelem := temp(m.Type.Elem())
+
+ for _, r := range entries {
+ index, elem := r.Left, r.Right
+
+ setlineno(index)
+ a := nod(OAS, tmpkey, index)
+ a = typecheck(a, ctxStmt)
+ a = walkstmt(a)
+ init.Append(a)
+
+ setlineno(elem)
+ a = nod(OAS, tmpelem, elem)
+ a = typecheck(a, ctxStmt)
+ a = walkstmt(a)
+ init.Append(a)
+
+ setlineno(tmpelem)
+ a = nod(OAS, nod(OINDEX, m, tmpkey), tmpelem)
+ a = typecheck(a, ctxStmt)
+ a = walkstmt(a)
+ init.Append(a)
+ }
+
+ a = nod(OVARKILL, tmpkey, nil)
+ a = typecheck(a, ctxStmt)
+ init.Append(a)
+ a = nod(OVARKILL, tmpelem, nil)
+ a = typecheck(a, ctxStmt)
+ init.Append(a)
+}
+
+func anylit(n *Node, var_ *Node, init *Nodes) {
+ t := n.Type
+ switch n.Op {
+ default:
+ Fatalf("anylit: not lit, op=%v node=%v", n.Op, n)
+
+ case ONAME:
+ a := nod(OAS, var_, n)
+ a = typecheck(a, ctxStmt)
+ init.Append(a)
+
+ case OPTRLIT:
+ if !t.IsPtr() {
+ Fatalf("anylit: not ptr")
+ }
+
+ var r *Node
+ if n.Right != nil {
+ // n.Right is stack temporary used as backing store.
+ init.Append(nod(OAS, n.Right, nil)) // zero backing store, just in case (#18410)
+ r = nod(OADDR, n.Right, nil)
+ r = typecheck(r, ctxExpr)
+ } else {
+ r = nod(ONEW, nil, nil)
+ r.SetTypecheck(1)
+ r.Type = t
+ r.Esc = n.Esc
+ }
+
+ r = walkexpr(r, init)
+ a := nod(OAS, var_, r)
+
+ a = typecheck(a, ctxStmt)
+ init.Append(a)
+
+ var_ = nod(ODEREF, var_, nil)
+ var_ = typecheck(var_, ctxExpr|ctxAssign)
+ anylit(n.Left, var_, init)
+
+ case OSTRUCTLIT, OARRAYLIT:
+ if !t.IsStruct() && !t.IsArray() {
+ Fatalf("anylit: not struct/array")
+ }
+
+ if var_.isSimpleName() && n.List.Len() > 4 {
+ // lay out static data
+ vstat := readonlystaticname(t)
+
+ ctxt := inInitFunction
+ if n.Op == OARRAYLIT {
+ ctxt = inNonInitFunction
+ }
+ fixedlit(ctxt, initKindStatic, n, vstat, init)
+
+ // copy static to var
+ a := nod(OAS, var_, vstat)
+
+ a = typecheck(a, ctxStmt)
+ a = walkexpr(a, init)
+ init.Append(a)
+
+ // add expressions to automatic
+ fixedlit(inInitFunction, initKindDynamic, n, var_, init)
+ break
+ }
+
+ var components int64
+ if n.Op == OARRAYLIT {
+ components = t.NumElem()
+ } else {
+ components = int64(t.NumFields())
+ }
+ // initialization of an array or struct with unspecified components (missing fields or arrays)
+ if var_.isSimpleName() || int64(n.List.Len()) < components {
+ a := nod(OAS, var_, nil)
+ a = typecheck(a, ctxStmt)
+ a = walkexpr(a, init)
+ init.Append(a)
+ }
+
+ fixedlit(inInitFunction, initKindLocalCode, n, var_, init)
+
+ case OSLICELIT:
+ slicelit(inInitFunction, n, var_, init)
+
+ case OMAPLIT:
+ if !t.IsMap() {
+ Fatalf("anylit: not map")
+ }
+ maplit(n, var_, init)
+ }
+}
+
+func oaslit(n *Node, init *Nodes) bool {
+ if n.Left == nil || n.Right == nil {
+ // not a special composite literal assignment
+ return false
+ }
+ if n.Left.Type == nil || n.Right.Type == nil {
+ // not a special composite literal assignment
+ return false
+ }
+ if !n.Left.isSimpleName() {
+ // not a special composite literal assignment
+ return false
+ }
+ if !types.Identical(n.Left.Type, n.Right.Type) {
+ // not a special composite literal assignment
+ return false
+ }
+
+ switch n.Right.Op {
+ default:
+ // not a special composite literal assignment
+ return false
+
+ case OSTRUCTLIT, OARRAYLIT, OSLICELIT, OMAPLIT:
+ if vmatch1(n.Left, n.Right) {
+ // not a special composite literal assignment
+ return false
+ }
+ anylit(n.Right, n.Left, init)
+ }
+
+ n.Op = OEMPTY
+ n.Right = nil
+ return true
+}
+
+func getlit(lit *Node) int {
+ if smallintconst(lit) {
+ return int(lit.Int64Val())
+ }
+ return -1
+}
+
+// stataddr sets nam to the static address of n and reports whether it succeeded.
+func stataddr(nam *Node, n *Node) bool {
+ if n == nil {
+ return false
+ }
+
+ switch n.Op {
+ case ONAME:
+ *nam = *n
+ return true
+
+ case ODOT:
+ if !stataddr(nam, n.Left) {
+ break
+ }
+ nam.Xoffset += n.Xoffset
+ nam.Type = n.Type
+ return true
+
+ case OINDEX:
+ if n.Left.Type.IsSlice() {
+ break
+ }
+ if !stataddr(nam, n.Left) {
+ break
+ }
+ l := getlit(n.Right)
+ if l < 0 {
+ break
+ }
+
+ // Check for overflow.
+ if n.Type.Width != 0 && thearch.MAXWIDTH/n.Type.Width <= int64(l) {
+ break
+ }
+ nam.Xoffset += int64(l) * n.Type.Width
+ nam.Type = n.Type
+ return true
+ }
+
+ return false
+}
+
+func (s *InitSchedule) initplan(n *Node) {
+ if s.initplans[n] != nil {
+ return
+ }
+ p := new(InitPlan)
+ s.initplans[n] = p
+ switch n.Op {
+ default:
+ Fatalf("initplan")
+
+ case OARRAYLIT, OSLICELIT:
+ var k int64
+ for _, a := range n.List.Slice() {
+ if a.Op == OKEY {
+ k = indexconst(a.Left)
+ if k < 0 {
+ Fatalf("initplan arraylit: invalid index %v", a.Left)
+ }
+ a = a.Right
+ }
+ s.addvalue(p, k*n.Type.Elem().Width, a)
+ k++
+ }
+
+ case OSTRUCTLIT:
+ for _, a := range n.List.Slice() {
+ if a.Op != OSTRUCTKEY {
+ Fatalf("initplan structlit")
+ }
+ if a.Sym.IsBlank() {
+ continue
+ }
+ s.addvalue(p, a.Xoffset, a.Left)
+ }
+
+ case OMAPLIT:
+ for _, a := range n.List.Slice() {
+ if a.Op != OKEY {
+ Fatalf("initplan maplit")
+ }
+ s.addvalue(p, -1, a.Right)
+ }
+ }
+}
+
+func (s *InitSchedule) addvalue(p *InitPlan, xoffset int64, n *Node) {
+ // special case: zero can be dropped entirely
+ if isZero(n) {
+ return
+ }
+
+ // special case: inline struct and array (not slice) literals
+ if isvaluelit(n) {
+ s.initplan(n)
+ q := s.initplans[n]
+ for _, qe := range q.E {
+ // qe is a copy; we are not modifying entries in q.E
+ qe.Xoffset += xoffset
+ p.E = append(p.E, qe)
+ }
+ return
+ }
+
+ // add to plan
+ p.E = append(p.E, InitEntry{Xoffset: xoffset, Expr: n})
+}
+
+func isZero(n *Node) bool {
+ switch n.Op {
+ case OLITERAL:
+ switch u := n.Val().U.(type) {
+ default:
+ Dump("unexpected literal", n)
+ Fatalf("isZero")
+ case *NilVal:
+ return true
+ case string:
+ return u == ""
+ case bool:
+ return !u
+ case *Mpint:
+ return u.CmpInt64(0) == 0
+ case *Mpflt:
+ return u.CmpFloat64(0) == 0
+ case *Mpcplx:
+ return u.Real.CmpFloat64(0) == 0 && u.Imag.CmpFloat64(0) == 0
+ }
+
+ case OARRAYLIT:
+ for _, n1 := range n.List.Slice() {
+ if n1.Op == OKEY {
+ n1 = n1.Right
+ }
+ if !isZero(n1) {
+ return false
+ }
+ }
+ return true
+
+ case OSTRUCTLIT:
+ for _, n1 := range n.List.Slice() {
+ if !isZero(n1.Left) {
+ return false
+ }
+ }
+ return true
+ }
+
+ return false
+}
+
+func isvaluelit(n *Node) bool {
+ return n.Op == OARRAYLIT || n.Op == OSTRUCTLIT
+}
+
+func genAsStatic(as *Node) {
+ if as.Left.Type == nil {
+ Fatalf("genAsStatic as.Left not typechecked")
+ }
+
+ var nam Node
+ if !stataddr(&nam, as.Left) || (nam.Class() != PEXTERN && as.Left != nblank) {
+ Fatalf("genAsStatic: lhs %v", as.Left)
+ }
+
+ switch {
+ case as.Right.Op == OLITERAL:
+ litsym(&nam, as.Right, int(as.Right.Type.Width))
+ case as.Right.Op == ONAME && as.Right.Class() == PFUNC:
+ pfuncsym(&nam, as.Right)
+ default:
+ Fatalf("genAsStatic: rhs %v", as.Right)
+ }
+}