diff options
Diffstat (limited to 'src/cmd/compile/internal/gc/sinit.go')
-rw-r--r-- | src/cmd/compile/internal/gc/sinit.go | 1172 |
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) + } +} |