// 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) n.Sym.Linksym().Set(obj.AttrLocal, true) return n } // readonlystaticname returns a name backed by a (writable) static data symbol. func readonlystaticname(t *types.Type) *Node { n := staticname(t) n.MarkReadonly() n.Sym.Linksym().Set(obj.AttrContentAddressable, 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) } }