Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1172 lines (1014 sloc)
26.2 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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) | |
} | |
} |