| // 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) | |
| } | |
| } |