Commit 517b6131 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: add and use new Fields type

Analogous to the Nodes type used as a more space efficient []*Node
representation.

Passes toolstash -cmp.

Change-Id: I8341e45304777d6e4200bd36dadc935b07ccf3ff
Reviewed-on: https://go-review.googlesource.com/20793Reviewed-by: 's avatarDavid Crawshaw <crawshaw@golang.org>
Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 857d0b48
...@@ -1269,9 +1269,7 @@ func addmethod(msym *Sym, t *Type, tpkg *Pkg, local, nointerface bool) { ...@@ -1269,9 +1269,7 @@ func addmethod(msym *Sym, t *Type, tpkg *Pkg, local, nointerface bool) {
n := Nod(ODCLFIELD, newname(msym), nil) n := Nod(ODCLFIELD, newname(msym), nil)
n.Type = t n.Type = t
var d *Field // last found
for f, it := IterMethods(pa); f != nil; f = it.Next() { for f, it := IterMethods(pa); f != nil; f = it.Next() {
d = f
if msym.Name != f.Sym.Name { if msym.Name != f.Sym.Name {
continue continue
} }
...@@ -1291,11 +1289,7 @@ func addmethod(msym *Sym, t *Type, tpkg *Pkg, local, nointerface bool) { ...@@ -1291,11 +1289,7 @@ func addmethod(msym *Sym, t *Type, tpkg *Pkg, local, nointerface bool) {
Fatalf("imported method name %v in wrong package %s\n", Sconv(f.Sym, FmtSign), tpkg.Name) Fatalf("imported method name %v in wrong package %s\n", Sconv(f.Sym, FmtSign), tpkg.Name)
} }
if d == nil { pa.Methods().Append(f)
pa.Method = f
} else {
d.Down = f
}
} }
func funccompile(n *Node) { func funccompile(n *Node) {
......
...@@ -602,24 +602,24 @@ func typefmt(t *Type, flag FmtFlag) string { ...@@ -602,24 +602,24 @@ func typefmt(t *Type, flag FmtFlag) string {
case TINTER: case TINTER:
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString("interface {") buf.WriteString("interface {")
for t1, it := IterFields(t); t1 != nil; t1 = it.Next() { for i, f := range t.Fields().Slice() {
if i != 0 {
buf.WriteString(";")
}
buf.WriteString(" ") buf.WriteString(" ")
switch { switch {
case t1.Sym == nil: case f.Sym == nil:
// Check first that a symbol is defined for this type. // Check first that a symbol is defined for this type.
// Wrong interface definitions may have types lacking a symbol. // Wrong interface definitions may have types lacking a symbol.
break break
case exportname(t1.Sym.Name): case exportname(f.Sym.Name):
buf.WriteString(Sconv(t1.Sym, FmtShort)) buf.WriteString(Sconv(f.Sym, FmtShort))
default: default:
buf.WriteString(Sconv(t1.Sym, FmtUnsigned)) buf.WriteString(Sconv(f.Sym, FmtUnsigned))
}
buf.WriteString(Tconv(t1.Type, FmtShort))
if t1.Down != nil {
buf.WriteString(";")
} }
buf.WriteString(Tconv(f.Type, FmtShort))
} }
if t.Fields != nil { if t.NumFields() != 0 {
buf.WriteString(" ") buf.WriteString(" ")
} }
buf.WriteString("}") buf.WriteString("}")
...@@ -679,32 +679,27 @@ func typefmt(t *Type, flag FmtFlag) string { ...@@ -679,32 +679,27 @@ func typefmt(t *Type, flag FmtFlag) string {
var buf bytes.Buffer var buf bytes.Buffer
if t.Funarg { if t.Funarg {
buf.WriteString("(") buf.WriteString("(")
var flag1 FmtFlag
if fmtmode == FTypeId || fmtmode == FErr { // no argument names on function signature, and no "noescape"/"nosplit" tags if fmtmode == FTypeId || fmtmode == FErr { // no argument names on function signature, and no "noescape"/"nosplit" tags
for t1, it := IterFields(t); t1 != nil; t1 = it.Next() { flag1 = FmtShort
buf.WriteString(Fldconv(t1, FmtShort)) }
if t1.Down != nil { for i, f := range t.Fields().Slice() {
buf.WriteString(", ") if i != 0 {
} buf.WriteString(", ")
}
} else {
for t1, it := IterFields(t); t1 != nil; t1 = it.Next() {
buf.WriteString(Fldconv(t1, 0))
if t1.Down != nil {
buf.WriteString(", ")
}
} }
buf.WriteString(Fldconv(f, flag1))
} }
buf.WriteString(")") buf.WriteString(")")
} else { } else {
buf.WriteString("struct {") buf.WriteString("struct {")
for t1, it := IterFields(t); t1 != nil; t1 = it.Next() { for i, f := range t.Fields().Slice() {
buf.WriteString(" ") if i != 0 {
buf.WriteString(Fldconv(t1, FmtLong))
if t1.Down != nil {
buf.WriteString(";") buf.WriteString(";")
} }
buf.WriteString(" ")
buf.WriteString(Fldconv(f, FmtLong))
} }
if t.Fields != nil { if t.NumFields() != 0 {
buf.WriteString(" ") buf.WriteString(" ")
} }
buf.WriteString("}") buf.WriteString("}")
......
...@@ -285,7 +285,7 @@ func methods(t *Type) []*Sig { ...@@ -285,7 +285,7 @@ func methods(t *Type) []*Sig {
// make list of methods for t, // make list of methods for t,
// generating code if necessary. // generating code if necessary.
var ms []*Sig var ms []*Sig
for f, it2 := IterAllMethods(mt); f != nil; f = it2.Next() { for _, f := range mt.AllMethods().Slice() {
if f.Type.Etype != TFUNC || f.Type.Thistuple == 0 { if f.Type.Etype != TFUNC || f.Type.Thistuple == 0 {
Fatalf("non-method on %v method %v %v\n", mt, f.Sym, f) Fatalf("non-method on %v method %v %v\n", mt, f.Sym, f)
} }
......
...@@ -1725,12 +1725,10 @@ func adddot(n *Node) *Node { ...@@ -1725,12 +1725,10 @@ func adddot(n *Node) *Node {
// the actual methods. // the actual methods.
type Symlink struct { type Symlink struct {
field *Field field *Field
link *Symlink
good bool
followptr bool followptr bool
} }
var slist *Symlink var slist []Symlink
func expand0(t *Type, followptr bool) { func expand0(t *Type, followptr bool) {
u := t u := t
...@@ -1740,17 +1738,12 @@ func expand0(t *Type, followptr bool) { ...@@ -1740,17 +1738,12 @@ func expand0(t *Type, followptr bool) {
} }
if u.Etype == TINTER { if u.Etype == TINTER {
var sl *Symlink
for f, it := IterFields(u); f != nil; f = it.Next() { for f, it := IterFields(u); f != nil; f = it.Next() {
if f.Sym.Flags&SymUniq != 0 { if f.Sym.Flags&SymUniq != 0 {
continue continue
} }
f.Sym.Flags |= SymUniq f.Sym.Flags |= SymUniq
sl = new(Symlink) slist = append(slist, Symlink{field: f, followptr: followptr})
sl.field = f
sl.link = slist
sl.followptr = followptr
slist = sl
} }
return return
...@@ -1758,17 +1751,12 @@ func expand0(t *Type, followptr bool) { ...@@ -1758,17 +1751,12 @@ func expand0(t *Type, followptr bool) {
u = methtype(t, 0) u = methtype(t, 0)
if u != nil { if u != nil {
var sl *Symlink
for f, it := IterMethods(u); f != nil; f = it.Next() { for f, it := IterMethods(u); f != nil; f = it.Next() {
if f.Sym.Flags&SymUniq != 0 { if f.Sym.Flags&SymUniq != 0 {
continue continue
} }
f.Sym.Flags |= SymUniq f.Sym.Flags |= SymUniq
sl = new(Symlink) slist = append(slist, Symlink{field: f, followptr: followptr})
sl.field = f
sl.link = slist
sl.followptr = followptr
slist = sl
} }
} }
} }
...@@ -1808,7 +1796,7 @@ out: ...@@ -1808,7 +1796,7 @@ out:
} }
func expandmeth(t *Type) { func expandmeth(t *Type) {
if t == nil || t.Xmethod != nil { if t == nil || t.AllMethods().Len() != 0 {
return return
} }
...@@ -1819,41 +1807,40 @@ func expandmeth(t *Type) { ...@@ -1819,41 +1807,40 @@ func expandmeth(t *Type) {
} }
// generate all reachable methods // generate all reachable methods
slist = nil slist = slist[:0]
expand1(t, true, false) expand1(t, true, false)
// check each method to be uniquely reachable // check each method to be uniquely reachable
for sl := slist; sl != nil; sl = sl.link { var ms []*Field
for i, sl := range slist {
slist[i].field = nil
sl.field.Sym.Flags &^= SymUniq sl.field.Sym.Flags &^= SymUniq
var f *Field var f *Field
if path, _ := dotpath(sl.field.Sym, t, &f, false); path == nil { if path, _ := dotpath(sl.field.Sym, t, &f, false); path == nil {
continue continue
} }
// dotpath may have dug out arbitrary fields, we only want methods. // dotpath may have dug out arbitrary fields, we only want methods.
if f.Type.Etype == TFUNC && f.Type.Thistuple > 0 { if f.Type.Etype != TFUNC || f.Type.Thistuple == 0 {
sl.good = true continue
sl.field = f
} }
// add it to the base type method list
f = f.Copy()
f.Embedded = 1 // needs a trampoline
if sl.followptr {
f.Embedded = 2
}
ms = append(ms, f)
} }
for f, it := IterMethods(t); f != nil; f = it.Next() { for f, it := IterMethods(t); f != nil; f = it.Next() {
f.Sym.Flags &^= SymUniq f.Sym.Flags &^= SymUniq
} }
t.Xmethod = t.Method ms = append(ms, t.Methods().Slice()...)
for sl := slist; sl != nil; sl = sl.link { t.AllMethods().Set(ms)
if sl.good {
// add it to the base type method list
f := sl.field.Copy()
f.Embedded = 1 // needs a trampoline
if sl.followptr {
f.Embedded = 2
}
f.Down = t.Xmethod
t.Xmethod = f
}
}
} }
// Given funarg struct list, return list of ODCLFIELD Node fn args. // Given funarg struct list, return list of ODCLFIELD Node fn args.
......
...@@ -123,8 +123,8 @@ type Type struct { ...@@ -123,8 +123,8 @@ type Type struct {
Intuple int Intuple int
Outnamed bool Outnamed bool
Method *Field methods Fields
Xmethod *Field allMethods Fields
Sym *Sym Sym *Sym
Vargen int32 // unique name for OTYPE/ONAME Vargen int32 // unique name for OTYPE/ONAME
...@@ -137,7 +137,7 @@ type Type struct { ...@@ -137,7 +137,7 @@ type Type struct {
Width int64 Width int64
// TSTRUCT // TSTRUCT
Fields *Field // first struct field fields Fields
Down *Type // key type in TMAP; next struct in Funarg TSTRUCT Down *Type // key type in TMAP; next struct in Funarg TSTRUCT
...@@ -172,7 +172,48 @@ type Field struct { ...@@ -172,7 +172,48 @@ type Field struct {
Type *Type // field type Type *Type // field type
Width int64 // TODO(mdempsky): Rename to offset. Width int64 // TODO(mdempsky): Rename to offset.
Note *string // literal string annotation Note *string // literal string annotation
Down *Field // next struct field }
// Fields is a pointer to a slice of *Field.
// This saves space in Types that do not have fields or methods
// compared to a simple slice of *Field.
type Fields struct {
s *[]*Field
}
// Len returns the number of entries in f.
func (f *Fields) Len() int {
if f.s == nil {
return 0
}
return len(*f.s)
}
// Slice returns the entries in f as a slice.
// Changes to the slice entries will be reflected in f.
func (f *Fields) Slice() []*Field {
if f.s == nil {
return nil
}
return *f.s
}
// Set sets f to a slice.
// This takes ownership of the slice.
func (f *Fields) Set(s []*Field) {
if len(s) != 0 {
f.s = &s
} else {
f.s = nil
}
}
// Append appends entries to f.
func (f *Fields) Append(s ...*Field) {
if f.s == nil {
f.s = new([]*Field)
}
*f.s = append(*f.s, s...)
} }
// typ returns a new Type of the specified kind. // typ returns a new Type of the specified kind.
...@@ -213,49 +254,38 @@ func (f *Field) Copy() *Field { ...@@ -213,49 +254,38 @@ func (f *Field) Copy() *Field {
// Iter provides an abstraction for iterating across struct fields and // Iter provides an abstraction for iterating across struct fields and
// interface methods. // interface methods.
type Iter struct { type Iter struct {
x *Field s []*Field
} }
// IterFields returns the first field or method in struct or interface type t // IterFields returns the first field or method in struct or interface type t
// and an Iter value to continue iterating across the rest. // and an Iter value to continue iterating across the rest.
func IterFields(t *Type) (*Field, Iter) { func IterFields(t *Type) (*Field, Iter) {
if t.Etype != TSTRUCT && t.Etype != TINTER { return t.Fields().Iter()
Fatalf("IterFields: type %v does not have fields", t)
}
return RawIter(t.Fields)
} }
// IterMethods returns the first method in type t's method set // IterMethods returns the first method in type t's method set
// and an Iter value to continue iterating across the rest. // and an Iter value to continue iterating across the rest.
// IterMethods does not include promoted methods. // IterMethods does not include promoted methods.
func IterMethods(t *Type) (*Field, Iter) { func IterMethods(t *Type) (*Field, Iter) {
// TODO(mdempsky): Validate t? return t.Methods().Iter()
return RawIter(t.Method)
}
// IterAllMethods returns the first (possibly promoted) method in type t's
// method set and an Iter value to continue iterating across the rest.
func IterAllMethods(t *Type) (*Field, Iter) {
// TODO(mdempsky): Validate t?
return RawIter(t.Xmethod)
} }
// RawIter returns field t and an Iter value to continue iterating across // Iter returns the first field in fs and an Iter value to continue iterating
// its successor fields. Most code should instead use one of the IterXXX // across its successor fields.
// functions above. // Deprecated: New code should use Slice instead.
func RawIter(t *Field) (*Field, Iter) { func (fs *Fields) Iter() (*Field, Iter) {
i := Iter{x: t} i := Iter{s: fs.Slice()}
f := i.Next() f := i.Next()
return f, i return f, i
} }
// Next returns the next field or method, if any. // Next returns the next field or method, if any.
func (i *Iter) Next() *Field { func (i *Iter) Next() *Field {
if i.x == nil { if len(i.s) == 0 {
return nil return nil
} }
f := i.x f := i.s[0]
i.x = f.Down i.s = i.s[1:]
return f return f
} }
...@@ -311,40 +341,37 @@ func (t *Type) Key() *Type { ...@@ -311,40 +341,37 @@ func (t *Type) Key() *Type {
return t.Down return t.Down
} }
func (t *Type) Methods() *Fields {
// TODO(mdempsky): Validate t?
return &t.methods
}
func (t *Type) AllMethods() *Fields {
// TODO(mdempsky): Validate t?
return &t.allMethods
}
func (t *Type) Fields() *Fields {
if t.Etype != TSTRUCT && t.Etype != TINTER {
Fatalf("Fields: type %v does not have fields", t)
}
return &t.fields
}
// Field returns the i'th field/method of struct/interface type t. // Field returns the i'th field/method of struct/interface type t.
func (t *Type) Field(i int) *Field { func (t *Type) Field(i int) *Field {
// TODO: store fields in a slice so we can return t.Fields().Slice()[i]
// look them up by index in constant time.
for f, it := IterFields(t); f != nil; f = it.Next() {
if i == 0 {
return f
}
i--
}
panic("not enough fields")
} }
// FieldSlice returns a slice of containing all fields/methods of // FieldSlice returns a slice of containing all fields/methods of
// struct/interface type t. // struct/interface type t.
func (t *Type) FieldSlice() []*Field { func (t *Type) FieldSlice() []*Field {
var s []*Field return t.Fields().Slice()
for f, it := IterFields(t); f != nil; f = it.Next() {
s = append(s, f)
}
return s
} }
// SetFields sets struct/interface type t's fields/methods to fields. // SetFields sets struct/interface type t's fields/methods to fields.
func (t *Type) SetFields(fields []*Field) { func (t *Type) SetFields(fields []*Field) {
if t.Etype != TSTRUCT && t.Etype != TINTER { t.Fields().Set(fields)
Fatalf("SetFields: type %v does not have fields", t)
}
var next *Field
for i := len(fields) - 1; i >= 0; i-- {
fields[i].Down = next
next = fields[i]
}
t.Fields = next
} }
func (t *Type) Size() int64 { func (t *Type) Size() int64 {
...@@ -649,11 +676,7 @@ func (t *Type) PtrTo() ssa.Type { ...@@ -649,11 +676,7 @@ func (t *Type) PtrTo() ssa.Type {
} }
func (t *Type) NumFields() int { func (t *Type) NumFields() int {
n := 0 return t.Fields().Len()
for f, it := IterFields(t); f != nil; f = it.Next() {
n++
}
return n
} }
func (t *Type) FieldType(i int) ssa.Type { func (t *Type) FieldType(i int) ssa.Type {
return t.Field(i).Type return t.Field(i).Type
......
...@@ -2362,9 +2362,9 @@ func twoarg(n *Node) bool { ...@@ -2362,9 +2362,9 @@ func twoarg(n *Node) bool {
return true return true
} }
func lookdot1(errnode *Node, s *Sym, t *Type, f *Field, dostrcmp int) *Field { func lookdot1(errnode *Node, s *Sym, t *Type, fs *Fields, dostrcmp int) *Field {
var r *Field var r *Field
for f, it := RawIter(f); f != nil; f = it.Next() { for _, f := range fs.Slice() {
if dostrcmp != 0 && f.Sym.Name == s.Name { if dostrcmp != 0 && f.Sym.Name == s.Name {
return f return f
} }
...@@ -2395,7 +2395,7 @@ func looktypedot(n *Node, t *Type, dostrcmp int) bool { ...@@ -2395,7 +2395,7 @@ func looktypedot(n *Node, t *Type, dostrcmp int) bool {
s := n.Right.Sym s := n.Right.Sym
if t.Etype == TINTER { if t.Etype == TINTER {
f1 := lookdot1(n, s, t, t.Fields, dostrcmp) f1 := lookdot1(n, s, t, t.Fields(), dostrcmp)
if f1 == nil { if f1 == nil {
return false return false
} }
...@@ -2415,7 +2415,7 @@ func looktypedot(n *Node, t *Type, dostrcmp int) bool { ...@@ -2415,7 +2415,7 @@ func looktypedot(n *Node, t *Type, dostrcmp int) bool {
} }
expandmeth(mt) expandmeth(mt)
f2 := lookdot1(n, s, mt, mt.Xmethod, dostrcmp) f2 := lookdot1(n, s, mt, mt.AllMethods(), dostrcmp)
if f2 == nil { if f2 == nil {
return false return false
} }
...@@ -2455,7 +2455,7 @@ func lookdot(n *Node, t *Type, dostrcmp int) *Field { ...@@ -2455,7 +2455,7 @@ func lookdot(n *Node, t *Type, dostrcmp int) *Field {
dowidth(t) dowidth(t)
var f1 *Field var f1 *Field
if t.Etype == TSTRUCT || t.Etype == TINTER { if t.Etype == TSTRUCT || t.Etype == TINTER {
f1 = lookdot1(n, s, t, t.Fields, dostrcmp) f1 = lookdot1(n, s, t, t.Fields(), dostrcmp)
} }
var f2 *Field var f2 *Field
...@@ -2464,7 +2464,7 @@ func lookdot(n *Node, t *Type, dostrcmp int) *Field { ...@@ -2464,7 +2464,7 @@ func lookdot(n *Node, t *Type, dostrcmp int) *Field {
if mt != nil { if mt != nil {
// Use f2->method, not f2->xmethod: adddot has // Use f2->method, not f2->xmethod: adddot has
// already inserted all the necessary embedded dots. // already inserted all the necessary embedded dots.
f2 = lookdot1(n, s, mt, mt.Method, dostrcmp) f2 = lookdot1(n, s, mt, mt.Methods(), dostrcmp)
} }
} }
...@@ -3103,7 +3103,7 @@ func typecheckcomplit(np **Node) { ...@@ -3103,7 +3103,7 @@ func typecheckcomplit(np **Node) {
} }
} }
f := lookdot1(nil, s, t, t.Fields, 0) f := lookdot1(nil, s, t, t.Fields(), 0)
if f == nil { if f == nil {
Yyerror("unknown %v field '%v' in struct literal", t, s) Yyerror("unknown %v field '%v' in struct literal", t, s)
continue continue
...@@ -3524,8 +3524,8 @@ func copytype(n *Node, t *Type) { ...@@ -3524,8 +3524,8 @@ func copytype(n *Node, t *Type) {
if n.Name != nil { if n.Name != nil {
t.Vargen = n.Name.Vargen t.Vargen = n.Name.Vargen
} }
t.Method = nil t.methods = Fields{}
t.Xmethod = nil t.allMethods = Fields{}
t.Nod = nil t.Nod = nil
t.Printed = false t.Printed = false
t.Deferwidth = false t.Deferwidth = false
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment