From f38f43d029de16f21f9102226d5c24684fb0ea25 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Fri, 1 Apr 2016 20:11:30 -0700 Subject: [PATCH] cmd/compile: shrink gc.Type in half MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many of Type's fields are etype-specific. This CL organizes them into their own auxiliary types, duplicating a few fields as necessary, and adds an Extra field to hold them. It also sorts the remaining fields for better struct packing. It also improves documentation for most fields. This reduces the size of Type at the cost of some extra allocations. There's no CPU impact; memory impact below. It also makes the natural structure of Type clearer. Passes toolstash -cmp on all architectures. Ideas for future work in this vein: (1) Width and Align probably only need to be stored for Struct and Array types. The refactoring to accomplish this would hopefully also eliminate TFUNCARGS and TCHANARGS entirely. (2) Maplineno is sparsely used and could probably better be stored in a separate map[*Type]int32, with mapqueue updated to store both a Node and a line number. (3) The Printed field may be removable once the old (non-binary) importer/exported has been removed. (4) StructType's fields field could be changed from *[]*Field to []*Field, which would remove a common allocation. (5) I believe that Type.Nod can be moved to ForwardType. Separate CL. name old alloc/op new alloc/op delta Template 57.9MB ± 0% 55.9MB ± 0% -3.43% (p=0.000 n=50+50) Unicode 38.3MB ± 0% 37.8MB ± 0% -1.39% (p=0.000 n=50+50) GoTypes 185MB ± 0% 180MB ± 0% -2.56% (p=0.000 n=50+50) Compiler 824MB ± 0% 806MB ± 0% -2.19% (p=0.000 n=50+50) name old allocs/op new allocs/op delta Template 486k ± 0% 497k ± 0% +2.25% (p=0.000 n=50+50) Unicode 377k ± 0% 379k ± 0% +0.55% (p=0.000 n=50+50) GoTypes 1.39M ± 0% 1.42M ± 0% +1.63% (p=0.000 n=50+50) Compiler 5.52M ± 0% 5.57M ± 0% +0.84% (p=0.000 n=47+50) Change-Id: I828488eeb74902b013d5ae4cf844de0b6c0dfc87 Reviewed-on: https://go-review.googlesource.com/21611 Reviewed-by: Matthew Dempsky Reviewed-by: Brad Fitzpatrick --- src/cmd/compile/internal/gc/align.go | 12 +- src/cmd/compile/internal/gc/bexport.go | 4 +- src/cmd/compile/internal/gc/bimport.go | 30 +- src/cmd/compile/internal/gc/dcl.go | 10 +- src/cmd/compile/internal/gc/export.go | 2 +- src/cmd/compile/internal/gc/fmt.go | 17 +- src/cmd/compile/internal/gc/pgen.go | 2 +- src/cmd/compile/internal/gc/pgen_test.go | 20 +- src/cmd/compile/internal/gc/reflect.go | 90 ++-- src/cmd/compile/internal/gc/sizeof_test.go | 16 +- src/cmd/compile/internal/gc/ssa.go | 2 +- src/cmd/compile/internal/gc/type.go | 487 ++++++++++++++++----- src/cmd/compile/internal/gc/typecheck.go | 17 +- src/cmd/compile/internal/gc/universe.go | 6 +- src/cmd/compile/internal/gc/walk.go | 2 +- 15 files changed, 491 insertions(+), 226 deletions(-) diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go index 9d5c3a550c4..e43ed7b2257 100644 --- a/src/cmd/compile/internal/gc/align.go +++ b/src/cmd/compile/internal/gc/align.go @@ -198,11 +198,11 @@ func dowidth(t *Type) { // make fake type to check later to // trigger channel argument check. - t1 := typWrapper(TCHANARGS, t) + t1 := typChanArgs(t) checkwidth(t1) case TCHANARGS: - t1 := t.Wrapped() + t1 := t.ChanArgs() dowidth(t1) // just in case if t1.Elem().Width >= 1<<16 { Yyerror("channel element type too large (>64kB)") @@ -271,18 +271,18 @@ func dowidth(t *Type) { // make fake type to check later to // trigger function argument computation. case TFUNC: - t1 := typWrapper(TFUNCARGS, t) + t1 := typFuncArgs(t) checkwidth(t1) w = int64(Widthptr) // width of func type is pointer // function is 3 cated structures; // compute their widths as side-effect. case TFUNCARGS: - t1 := t.Wrapped() + t1 := t.FuncArgs() w = widstruct(t1, t1.Recvs(), 0, 0) w = widstruct(t1, t1.Params(), w, Widthreg) w = widstruct(t1, t1.Results(), w, Widthreg) - t1.Argwid = w + t1.Extra.(*FuncType).Argwid = w if w%int64(Widthreg) != 0 { Warn("bad type %v %d\n", t1, w) } @@ -386,7 +386,7 @@ func Argsize(t *Type) int { } } - w = (w + int64(Widthptr) - 1) &^ (int64(Widthptr) - 1) + w = Rnd(w, int64(Widthptr)) if int64(int(w)) != w { Fatalf("argsize too big") } diff --git a/src/cmd/compile/internal/gc/bexport.go b/src/cmd/compile/internal/gc/bexport.go index f88afd24884..8dcf97b31de 100644 --- a/src/cmd/compile/internal/gc/bexport.go +++ b/src/cmd/compile/internal/gc/bexport.go @@ -602,7 +602,7 @@ func (p *exporter) typ(t *Type) { case TDDDFIELD: // see p.param use of TDDDFIELD p.tag(dddTag) - p.typ(t.Wrapped()) + p.typ(t.DDDField()) case TSTRUCT: p.tag(structTag) @@ -768,7 +768,7 @@ func (p *exporter) param(q *Field, n int, numbered bool) { t := q.Type if q.Isddd { // create a fake type to encode ... just for the p.typ call - t = typWrapper(TDDDFIELD, t.Elem()) + t = typDDDField(t.Elem()) } p.typ(t) if n > 0 { diff --git a/src/cmd/compile/internal/gc/bimport.go b/src/cmd/compile/internal/gc/bimport.go index 8c53372b802..7ad4d9dbb01 100644 --- a/src/cmd/compile/internal/gc/bimport.go +++ b/src/cmd/compile/internal/gc/bimport.go @@ -359,16 +359,20 @@ func (p *importer) typ() *Type { case arrayTag, sliceTag: t = p.newtyp(TARRAY) + var bound int64 if i == arrayTag { - t.SetNumElem(p.int64()) - } else { - t.SetNumElem(sliceBound) + bound = p.int64() + } + elem := p.typ() + if i == arrayTag { + t.Extra = &ArrayType{Elem: elem, Bound: bound} + } else { + t.Extra = SliceType{Elem: elem} } - t.Type = p.typ() case dddTag: t = p.newtyp(TDDDFIELD) - t.Type = p.typ() + t.Extra = DDDFieldType{T: p.typ()} case structTag: t = p.newtyp(TSTRUCT) @@ -376,7 +380,7 @@ func (p *importer) typ() *Type { case pointerTag: t = p.newtyp(Tptr) - t.Type = p.typ() + t.Extra = PtrType{Elem: p.typ()} case signatureTag: t = p.newtyp(TFUNC) @@ -393,13 +397,15 @@ func (p *importer) typ() *Type { case mapTag: t = p.newtyp(TMAP) - t.Down = p.typ() // key - t.Type = p.typ() // val + mt := t.MapType() + mt.Key = p.typ() + mt.Val = p.typ() case chanTag: t = p.newtyp(TCHAN) - t.Chan = ChanDir(p.int()) - t.Type = p.typ() + ct := t.ChanType() + ct.Dir = ChanDir(p.int()) + ct.Elem = p.typ() default: Fatalf("importer: unexpected type (tag = %d)", i) @@ -444,7 +450,7 @@ func (p *importer) field() *Node { // anonymous field - typ must be T or *T and T must be a type name s := typ.Sym if s == nil && typ.IsPtr() { - s = typ.Type.Sym // deref + s = typ.Elem().Sym // deref } pkg := importpkg if sym != nil { @@ -531,7 +537,7 @@ func (p *importer) param(named bool) *Node { isddd := false if typ.Etype == TDDDFIELD { // TDDDFIELD indicates wrapped ... slice type - typ = typSlice(typ.Wrapped()) + typ = typSlice(typ.DDDField()) isddd = true } diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index fb81545a467..c652c659622 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -743,8 +743,8 @@ func checkembeddedtype(t *Type) { if t.IsPtr() { Yyerror("embedded type cannot be a pointer") - } else if t.Etype == TFORW && t.Embedlineno == 0 { - t.Embedlineno = lineno + } else if t.Etype == TFORW && t.ForwardType().Embedlineno == 0 { + t.ForwardType().Embedlineno = lineno } } @@ -855,7 +855,7 @@ func tostruct0(t *Type, l []*Node) { func tofunargs(l []*Node) *Type { t := typ(TSTRUCT) - t.Funarg = true + t.StructType().Funarg = true fields := make([]*Field, len(l)) for i, n := range l { @@ -1061,11 +1061,11 @@ func functype0(t *Type, this *Node, in, out []*Node) { t.Broke = true } - t.Outnamed = false + t.FuncType().Outnamed = false if len(out) > 0 && out[0].Left != nil && out[0].Left.Orig != nil { s := out[0].Left.Orig.Sym if s != nil && (s.Name[0] != '~' || s.Name[1] != 'r') { // ~r%d is the name invented for an unnamed result - t.Outnamed = true + t.FuncType().Outnamed = true } } } diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index 6de7da0667b..17311cf6afb 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -592,7 +592,7 @@ func dumpasmhdr() { case OTYPE: t := n.Type - if !t.IsStruct() || t.Map != nil || t.IsFuncArgStruct() { + if !t.IsStruct() || t.StructType().Map != nil || t.IsFuncArgStruct() { break } fmt.Fprintf(b, "#define %s__size %d\n", t.Sym.Name, int(t.Width)) diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 27ccdfbdcf6..5c5503619ff 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -671,19 +671,20 @@ func typefmt(t *Type, flag FmtFlag) string { return buf.String() case TSTRUCT: - if t.Map != nil { + if m := t.StructType().Map; m != nil { + mt := m.MapType() // Format the bucket struct for map[x]y as map.bucket[x]y. // This avoids a recursive print that generates very long names. - if t.Map.Bucket == t { - return "map.bucket[" + t.Map.Key().String() + "]" + t.Map.Val().String() + if mt.Bucket == t { + return "map.bucket[" + m.Key().String() + "]" + m.Val().String() } - if t.Map.Hmap == t { - return "map.hdr[" + t.Map.Key().String() + "]" + t.Map.Val().String() + if mt.Hmap == t { + return "map.hdr[" + m.Key().String() + "]" + m.Val().String() } - if t.Map.Hiter == t { - return "map.iter[" + t.Map.Key().String() + "]" + t.Map.Val().String() + if mt.Hiter == t { + return "map.iter[" + m.Key().String() + "]" + m.Val().String() } Yyerror("unknown internal map type") @@ -735,7 +736,7 @@ func typefmt(t *Type, flag FmtFlag) string { if fmtmode == FExp { Fatalf("cannot use TDDDFIELD with old exporter") } - return fmt.Sprintf("%v <%v> %v", Econv(t.Etype), t.Sym, t.Wrapped()) + return fmt.Sprintf("%v <%v> %v", Econv(t.Etype), t.Sym, t.DDDField()) } if fmtmode == FExp { diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index efe10a419ca..63f7bf825e7 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -375,7 +375,7 @@ func compile(fn *Node) { // set up domain for labels clearlabels() - if Curfn.Type.Outnamed { + if Curfn.Type.FuncType().Outnamed { // add clearing of the output parameters for _, t := range Curfn.Type.Results().Fields().Slice() { if t.Nname != nil { diff --git a/src/cmd/compile/internal/gc/pgen_test.go b/src/cmd/compile/internal/gc/pgen_test.go index fcb8bfa0c23..44dc1db12e1 100644 --- a/src/cmd/compile/internal/gc/pgen_test.go +++ b/src/cmd/compile/internal/gc/pgen_test.go @@ -10,6 +10,14 @@ import ( "testing" ) +func typeWithoutPointers() *Type { + return &Type{Etype: TSTRUCT, Extra: &StructType{Haspointers: 1}} // haspointers -> false +} + +func typeWithPointers() *Type { + return &Type{Etype: TSTRUCT, Extra: &StructType{Haspointers: 2}} // haspointers -> true +} + // Test all code paths for cmpstackvarlt. func TestCmpstackvar(t *testing.T) { testdata := []struct { @@ -62,13 +70,13 @@ func TestCmpstackvar(t *testing.T) { false, }, { - Node{Class: PAUTO, Type: &Type{Haspointers: 1}}, // haspointers -> false - Node{Class: PAUTO, Type: &Type{Haspointers: 2}}, // haspointers -> true + Node{Class: PAUTO, Type: typeWithoutPointers()}, + Node{Class: PAUTO, Type: typeWithPointers()}, false, }, { - Node{Class: PAUTO, Type: &Type{Haspointers: 2}}, // haspointers -> true - Node{Class: PAUTO, Type: &Type{Haspointers: 1}}, // haspointers -> false + Node{Class: PAUTO, Type: typeWithPointers()}, + Node{Class: PAUTO, Type: typeWithoutPointers()}, true, }, { @@ -127,7 +135,7 @@ func TestStackvarSort(t *testing.T) { {Class: PFUNC, Xoffset: 10, Type: &Type{}, Name: &Name{}, Sym: &Sym{}}, {Class: PFUNC, Xoffset: 20, Type: &Type{}, Name: &Name{}, Sym: &Sym{}}, {Class: PAUTO, Used: true, Type: &Type{}, Name: &Name{}, Sym: &Sym{}}, - {Class: PAUTO, Type: &Type{Haspointers: 1}, Name: &Name{}, Sym: &Sym{}}, // haspointers -> false + {Class: PAUTO, Type: typeWithoutPointers(), Name: &Name{}, Sym: &Sym{}}, {Class: PAUTO, Type: &Type{}, Name: &Name{}, Sym: &Sym{}}, {Class: PAUTO, Type: &Type{}, Name: &Name{Needzero: true}, Sym: &Sym{}}, {Class: PAUTO, Type: &Type{Width: 1}, Name: &Name{}, Sym: &Sym{}}, @@ -148,7 +156,7 @@ func TestStackvarSort(t *testing.T) { {Class: PAUTO, Type: &Type{}, Name: &Name{}, Sym: &Sym{}}, {Class: PAUTO, Type: &Type{}, Name: &Name{}, Sym: &Sym{Name: "abc"}}, {Class: PAUTO, Type: &Type{}, Name: &Name{}, Sym: &Sym{Name: "xyz"}}, - {Class: PAUTO, Type: &Type{Haspointers: 1}, Name: &Name{}, Sym: &Sym{}}, // haspointers -> false + {Class: PAUTO, Type: typeWithoutPointers(), Name: &Name{}, Sym: &Sym{}}, } // haspointers updates Type.Haspointers as a side effect, so // exercise this function on all inputs so that reflect.DeepEqual diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index c069b357874..df9ef27b7a1 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -86,8 +86,8 @@ func makefield(name string, t *Type) *Field { } func mapbucket(t *Type) *Type { - if t.Bucket != nil { - return t.Bucket + if t.MapType().Bucket != nil { + return t.MapType().Bucket } bucket := typ(TSTRUCT) @@ -157,17 +157,17 @@ func mapbucket(t *Type) *Type { Yyerror("bad math in mapbucket for %v", t) } - t.Bucket = bucket + t.MapType().Bucket = bucket - bucket.Map = t + bucket.StructType().Map = t return bucket } // Builds a type representing a Hmap structure for the given map type. // Make sure this stays in sync with ../../../../runtime/hashmap.go! func hmap(t *Type) *Type { - if t.Hmap != nil { - return t.Hmap + if t.MapType().Hmap != nil { + return t.MapType().Hmap } bucket := mapbucket(t) @@ -186,14 +186,14 @@ func hmap(t *Type) *Type { h.Local = t.Local h.SetFields(field[:]) dowidth(h) - t.Hmap = h - h.Map = t + t.MapType().Hmap = h + h.StructType().Map = t return h } func hiter(t *Type) *Type { - if t.Hiter != nil { - return t.Hiter + if t.MapType().Hiter != nil { + return t.MapType().Hiter } // build a struct: @@ -234,8 +234,8 @@ func hiter(t *Type) *Type { if i.Width != int64(12*Widthptr) { Yyerror("hash_iter size not correct %d %d", i.Width, 12*Widthptr) } - t.Hiter = i - i.Map = t + t.MapType().Hiter = i + i.StructType().Map = t return i } @@ -664,67 +664,47 @@ var kinds = []int{ } func haspointers(t *Type) bool { - if t.Haspointers != 0 { - return t.Haspointers-1 != 0 - } - - var ret bool switch t.Etype { - case TINT, - TUINT, - TINT8, - TUINT8, - TINT16, - TUINT16, - TINT32, - TUINT32, - TINT64, - TUINT64, - TUINTPTR, - TFLOAT32, - TFLOAT64, - TCOMPLEX64, - TCOMPLEX128, - TBOOL: - ret = false + case TINT, TUINT, TINT8, TUINT8, TINT16, TUINT16, TINT32, TUINT32, TINT64, + TUINT64, TUINTPTR, TFLOAT32, TFLOAT64, TCOMPLEX64, TCOMPLEX128, TBOOL: + return false case TARRAY: if t.IsSlice() { - ret = true - break + return true } - if t.NumElem() == 0 { // empty array - ret = false - break + at := t.Extra.(*ArrayType) + if at.Haspointers != 0 { + return at.Haspointers-1 != 0 } - ret = haspointers(t.Elem()) + ret := false + if t.NumElem() != 0 { // non-empty array + ret = haspointers(t.Elem()) + } + + at.Haspointers = 1 + uint8(obj.Bool2int(ret)) + return ret case TSTRUCT: - ret = false + st := t.StructType() + if st.Haspointers != 0 { + return st.Haspointers-1 != 0 + } + + ret := false for _, t1 := range t.Fields().Slice() { if haspointers(t1.Type) { ret = true break } } - - case TSTRING, - TPTR32, - TPTR64, - TUNSAFEPTR, - TINTER, - TCHAN, - TMAP, - TFUNC: - fallthrough - default: - ret = true + st.Haspointers = 1 + uint8(obj.Bool2int(ret)) + return ret } - t.Haspointers = 1 + uint8(obj.Bool2int(ret)) - return ret + return true } // typeptrdata returns the length in bytes of the prefix of t diff --git a/src/cmd/compile/internal/gc/sizeof_test.go b/src/cmd/compile/internal/gc/sizeof_test.go index 11c0f419da5..8b0dfe538eb 100644 --- a/src/cmd/compile/internal/gc/sizeof_test.go +++ b/src/cmd/compile/internal/gc/sizeof_test.go @@ -27,7 +27,21 @@ func TestSizeof(t *testing.T) { {Name{}, 52, 80}, {Node{}, 92, 144}, {Sym{}, 60, 112}, - {Type{}, 116, 184}, + {Type{}, 56, 88}, + {MapType{}, 20, 40}, + {ForwardType{}, 16, 32}, + {FuncType{}, 28, 48}, + {StructType{}, 12, 24}, + {InterType{}, 4, 8}, + {ChanType{}, 8, 16}, + {ArrayType{}, 16, 24}, + {InterMethType{}, 4, 8}, + {DDDFieldType{}, 4, 8}, + {FuncArgsType{}, 4, 8}, + {ChanArgsType{}, 4, 8}, + {PtrType{}, 4, 8}, + {SliceType{}, 4, 8}, + {DDDArrayType{}, 4, 8}, } for _, tt := range tests { diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index 1c2e528384f..127a7c46986 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -4218,7 +4218,7 @@ func (e *ssaExport) SplitInterface(name ssa.LocalSlot) (ssa.LocalSlot, ssa.Local func (e *ssaExport) SplitSlice(name ssa.LocalSlot) (ssa.LocalSlot, ssa.LocalSlot, ssa.LocalSlot) { n := name.N.(*Node) - ptrType := Ptrto(n.Type.Type) + ptrType := Ptrto(n.Type.Elem()) lenType := Types[TINT] if n.Class == PAUTO && !n.Addrtaken { // Split this slice up into three separate variables. diff --git a/src/cmd/compile/internal/gc/type.go b/src/cmd/compile/internal/gc/type.go index e04cfcda638..3d2f01ef7d9 100644 --- a/src/cmd/compile/internal/gc/type.go +++ b/src/cmd/compile/internal/gc/type.go @@ -122,55 +122,174 @@ var ( // A Type represents a Go type. type Type struct { - Etype EType - Noalg bool - Chan ChanDir - Trecur uint8 // to detect loops - Printed bool - Funarg bool // TSTRUCT only: whether this struct represents function parameters - Local bool // created in this file - Deferwidth bool - Broke bool // broken type definition. - Align uint8 - Haspointers uint8 // 0 unknown, 1 no, 2 yes - Outnamed bool // on TFUNC + // Extra contains extra etype-specific fields. + // As an optimization, those etype-specific structs which contain exactly + // one pointer-shaped field are stored as values rather than pointers when possible. + // + // TMAP: *MapType + // TFORW: *ForwardType + // TFUNC: *FuncType + // TINTERMETHOD: InterMethType + // TSTRUCT: *StructType + // TINTER: *InterType + // TDDDFIELD: DDDFieldType + // TFUNCARGS: FuncArgsType + // TCHANARGS: ChanArgsType + // TCHAN: *ChanType + // TPTR32, TPTR64: PtrType + // TARRAY: *ArrayType, SliceType, or DDDArrayType + Extra interface{} - Nod *Node // canonical OTYPE node - Orig *Type // original type (type literal or predefined type) + // Width is the width of this Type in bytes. + Width int64 methods Fields allMethods Fields - Sym *Sym - Vargen int32 // unique name for OTYPE/ONAME - Lineno int32 + Nod *Node // canonical OTYPE node + Orig *Type // original type (type literal or predefined type) - nname *Node + Sym *Sym // symbol containing name, for named types + Vargen int32 // unique name for OTYPE/ONAME + Lineno int32 // line at which this type was declared, implicitly or explicitly + + Maplineno int32 // first use of this type as a map key + + Etype EType // kind of type + Noalg bool // suppress hash and eq algorithm generation + Trecur uint8 // to detect loops + Printed bool // prevent duplicate export printing + Local bool // created in this file + Deferwidth bool + Broke bool // broken type definition. + Align uint8 // the required alignment of this type, in bytes +} + +// MapType contains Type fields specific to maps. +type MapType struct { + Key *Type // Key type + Val *Type // Val (elem) type + + Bucket *Type // internal struct type representing a hash bucket + Hmap *Type // internal struct type representing the Hmap (map header object) + Hiter *Type // internal struct type representing hash iterator state +} + +// MapType returns t's extra map-specific fields. +func (t *Type) MapType() *MapType { + t.wantEtype(TMAP) + return t.Extra.(*MapType) +} + +// ForwardType contains Type fields specific to forward types. +type ForwardType struct { + Copyto []*Node // where to copy the eventual value to + Embedlineno int32 // first use of this type as an embedded type +} + +// ForwardType returns t's extra forward-type-specific fields. +func (t *Type) ForwardType() *ForwardType { + t.wantEtype(TFORW) + return t.Extra.(*ForwardType) +} + +// FuncType contains Type fields specific to func types. +type FuncType struct { + Receiver *Type // function receiver + Results *Type // function results + Params *Type // function params + + Nname *Node + + // Argwid is the total width of the function receiver, params, and results. + // It gets calculated via a temporary TFUNCARGS type. + // Note that TFUNC's Width is Widthptr. Argwid int64 - // most nodes - Type *Type // element type for TARRAY, TCHAN, TMAP, TPTRxx - Width int64 + Outnamed bool +} - // TSTRUCT +// FuncType returns t's extra func-specific fields. +func (t *Type) FuncType() *FuncType { + t.wantEtype(TFUNC) + return t.Extra.(*FuncType) +} + +// InterMethType contains Type fields specific to interface method psuedo-types. +type InterMethType struct { + Nname *Node +} + +// StructType contains Type fields specific to struct types. +type StructType struct { fields Fields - Down *Type // key type in TMAP; next struct in Funarg TSTRUCT + // Maps have three associated internal structs (see struct MapType). + // Map links such structs back to their map type. + Map *Type - // TARRAY - Bound int64 // negative is slice + Funarg bool // whether this struct represents function parameters + Haspointers uint8 // 0 unknown, 1 no, 2 yes +} - // TMAP - Bucket *Type // internal type representing a hash bucket - Hmap *Type // internal type representing a Hmap (map header object) - Hiter *Type // internal type representing hash iterator state - Map *Type // link from the above 3 internal types back to the map type. +// StructType returns t's extra struct-specific fields. +func (t *Type) StructType() *StructType { + t.wantEtype(TSTRUCT) + return t.Extra.(*StructType) +} - Maplineno int32 // first use of TFORW as map key - Embedlineno int32 // first use of TFORW as embedded type +// InterType contains Type fields specific to interface types. +type InterType struct { + fields Fields +} - // for TFORW, where to copy the eventual value to - Copyto []*Node +// PtrType contains Type fields specific to pointer types. +type PtrType struct { + Elem *Type // element type +} + +// DDDFieldType contains Type fields specific to TDDDFIELD types. +type DDDFieldType struct { + T *Type // reference to a slice type for ... args +} + +// ChanArgsType contains Type fields specific to TCHANARGS types. +type ChanArgsType struct { + T *Type // reference to a chan type whose elements need a width check +} + +// // FuncArgsType contains Type fields specific to TFUNCARGS types. +type FuncArgsType struct { + T *Type // reference to a func type whose elements need a width check +} + +// ChanType contains Type fields specific to channel types. +type ChanType struct { + Elem *Type // element type + Dir ChanDir // channel direction +} + +// ChanType returns t's extra channel-specific fields. +func (t *Type) ChanType() *ChanType { + t.wantEtype(TCHAN) + return t.Extra.(*ChanType) +} + +// ArrayType contains Type fields specific to array types with known lengths. +type ArrayType struct { + Elem *Type // element type + Bound int64 // number of elements; always >= 0; do not use with sliceBound or dddBound + Haspointers uint8 // 0 unknown, 1 no, 2 yes +} + +// SliceType contains Type fields specific to slice types. +type SliceType struct { + Elem *Type // element type +} + +// DDDArrayType contains Type fields specific to ddd array types. +type DDDArrayType struct { + Elem *Type // element type } // A Field represents a field in a struct or a method in an interface or @@ -252,38 +371,61 @@ func typ(et EType) *Type { Lineno: lineno, } t.Orig = t + // TODO(josharian): lazily initialize some of these? + switch t.Etype { + case TMAP: + t.Extra = new(MapType) + case TFORW: + t.Extra = new(ForwardType) + case TFUNC: + t.Extra = new(FuncType) + case TINTERMETH: + t.Extra = InterMethType{} + case TSTRUCT: + t.Extra = new(StructType) + case TINTER: + t.Extra = new(InterType) + case TPTR32, TPTR64: + t.Extra = PtrType{} + case TCHANARGS: + t.Extra = ChanArgsType{} + case TFUNCARGS: + t.Extra = FuncArgsType{} + case TDDDFIELD: + t.Extra = DDDFieldType{} + case TCHAN: + t.Extra = new(ChanType) + } return t } // typArray returns a new fixed-length array Type. func typArray(elem *Type, bound int64) *Type { t := typ(TARRAY) - t.Type = elem - t.Bound = bound + t.Extra = &ArrayType{Elem: elem, Bound: bound} return t } // typSlice returns a new slice Type. func typSlice(elem *Type) *Type { t := typ(TARRAY) - t.Type = elem - t.Bound = sliceBound + t.Extra = SliceType{Elem: elem} return t } // typDDDArray returns a new [...]T array Type. func typDDDArray(elem *Type) *Type { t := typ(TARRAY) - t.Type = elem - t.Bound = dddBound + t.Extra = DDDArrayType{Elem: elem} return t } // typChan returns a new chan Type with direction dir. func typChan(elem *Type, dir ChanDir) *Type { t := typ(TCHAN) - t.Type = elem - t.Chan = dir + ct := t.ChanType() + ct.Elem = elem + ct.Dir = dir return t } @@ -294,29 +436,39 @@ func typMap(k, v *Type) *Type { } t := typ(TMAP) - t.Down = k - t.Type = v + mt := t.MapType() + mt.Key = k + mt.Val = v return t } // typPtr returns a new pointer type pointing to t. func typPtr(elem *Type) *Type { t := typ(Tptr) - t.Type = elem + t.Extra = PtrType{Elem: elem} t.Width = int64(Widthptr) t.Align = uint8(Widthptr) return t } -// typWrapper returns a new wrapper psuedo-type. -func typWrapper(et EType, wrapped *Type) *Type { - switch et { - case TCHANARGS, TFUNCARGS, TDDDFIELD: - default: - Fatalf("typWrapper bad etype %s", et) - } - t := typ(et) - t.Type = wrapped +// typDDDField returns a new TDDDFIELD type for slice type s. +func typDDDField(s *Type) *Type { + t := typ(TDDDFIELD) + t.Extra = DDDFieldType{T: s} + return t +} + +// typChanArgs returns a new TCHANARGS type for channel type c. +func typChanArgs(c *Type) *Type { + t := typ(TCHANARGS) + t.Extra = ChanArgsType{T: c} + return t +} + +// typFuncArgs returns a new TFUNCARGS type for func type f. +func typFuncArgs(f *Type) *Type { + t := typ(TFUNCARGS) + t.Extra = FuncArgsType{T: f} return t } @@ -362,20 +514,43 @@ func substAny(t *Type, types *[]*Type) *Type { t = (*types)[0] *types = (*types)[1:] - case TPTR32, TPTR64, TCHAN, TARRAY: - elem := substAny(t.Type, types) - if elem != t.Type { + case TPTR32, TPTR64: + elem := substAny(t.Elem(), types) + if elem != t.Elem() { t = t.Copy() - t.Type = elem + t.Extra = PtrType{Elem: elem} + } + + case TARRAY: + elem := substAny(t.Elem(), types) + if elem != t.Elem() { + t = t.Copy() + switch x := t.Extra.(type) { + case *ArrayType: + x.Elem = elem + case SliceType: + t.Extra = SliceType{Elem: elem} + case DDDArrayType: + t.Extra = DDDArrayType{Elem: elem} + default: + Fatalf("substAny bad array elem type %T %v", x, t) + } + } + + case TCHAN: + elem := substAny(t.Elem(), types) + if elem != t.Elem() { + t = t.Copy() + t.Extra.(*ChanType).Elem = elem } case TMAP: - key := substAny(t.Down, types) - val := substAny(t.Type, types) - if key != t.Down || val != t.Type { + key := substAny(t.Key(), types) + val := substAny(t.Val(), types) + if key != t.Key() || val != t.Val() { t = t.Copy() - t.Down = key - t.Type = val + t.Extra.(*MapType).Key = key + t.Extra.(*MapType).Val = val } case TFUNC: @@ -426,6 +601,32 @@ func (t *Type) Copy() *Type { return nil } nt := *t + // copy any *T Extra fields, to avoid aliasing + switch t.Etype { + case TMAP: + x := *t.Extra.(*MapType) + nt.Extra = &x + case TFORW: + x := *t.Extra.(*ForwardType) + nt.Extra = &x + case TFUNC: + x := *t.Extra.(*FuncType) + nt.Extra = &x + case TSTRUCT: + x := *t.Extra.(*StructType) + nt.Extra = &x + case TINTER: + x := *t.Extra.(*InterType) + nt.Extra = &x + case TCHAN: + x := *t.Extra.(*ChanType) + nt.Extra = &x + case TARRAY: + if arr, ok := t.Extra.(*ArrayType); ok { + x := *arr + nt.Extra = &x + } + } // TODO(mdempsky): Find out why this is necessary and explain. if t.Orig == t { nt.Orig = &nt @@ -483,17 +684,17 @@ func (t *Type) wantEtype2(et1, et2 EType) { func (t *Type) RecvsP() **Type { t.wantEtype(TFUNC) - return &t.Type + return &t.Extra.(*FuncType).Receiver } func (t *Type) ParamsP() **Type { t.wantEtype(TFUNC) - return &t.Type.Down.Down + return &t.Extra.(*FuncType).Params } func (t *Type) ResultsP() **Type { t.wantEtype(TFUNC) - return &t.Type.Down + return &t.Extra.(*FuncType).Results } func (t *Type) Recvs() *Type { return *t.RecvsP() } @@ -524,51 +725,82 @@ var paramsResults = [2]func(*Type) *Type{ // Key returns the key type of map type t. func (t *Type) Key() *Type { t.wantEtype(TMAP) - return t.Down + return t.Extra.(*MapType).Key } // Val returns the value type of map type t. func (t *Type) Val() *Type { t.wantEtype(TMAP) - return t.Type + return t.Extra.(*MapType).Val } // Elem returns the type of elements of t. // Usable with pointers, channels, arrays, and slices. func (t *Type) Elem() *Type { switch t.Etype { - case TPTR32, TPTR64, TCHAN, TARRAY: - default: - Fatalf("Type.Elem %s", t.Etype) + case TPTR32, TPTR64: + return t.Extra.(PtrType).Elem + case TARRAY: + switch t := t.Extra.(type) { + case *ArrayType: + return t.Elem + case SliceType: + return t.Elem + case DDDArrayType: + return t.Elem + } + case TCHAN: + return t.Extra.(*ChanType).Elem } - return t.Type + Fatalf("Type.Elem %s", t.Etype) + return nil } -// Wrapped returns the type that pseudo-type t wraps. -func (t *Type) Wrapped() *Type { - switch t.Etype { - case TCHANARGS, TFUNCARGS, TDDDFIELD: - default: - Fatalf("Type.Wrapped %s", t.Etype) - } - return t.Type +// DDDField returns the slice ... type for TDDDFIELD type t. +func (t *Type) DDDField() *Type { + t.wantEtype(TDDDFIELD) + return t.Extra.(DDDFieldType).T +} + +// ChanArgs returns the channel type for TCHANARGS type t. +func (t *Type) ChanArgs() *Type { + t.wantEtype(TCHANARGS) + return t.Extra.(ChanArgsType).T +} + +// FuncArgs returns the channel type for TFUNCARGS type t. +func (t *Type) FuncArgs() *Type { + t.wantEtype(TFUNCARGS) + return t.Extra.(FuncArgsType).T } // Nname returns the associated function's nname. func (t *Type) Nname() *Node { - t.wantEtype2(TFUNC, TINTERMETH) - return t.nname + switch t.Etype { + case TFUNC: + return t.Extra.(*FuncType).Nname + case TINTERMETH: + return t.Extra.(InterMethType).Nname + } + Fatalf("Type.Nname %v %v", t.Etype, t) + return nil } // Nname sets the associated function's nname. func (t *Type) SetNname(n *Node) { - t.wantEtype2(TFUNC, TINTERMETH) - t.nname = n + switch t.Etype { + case TFUNC: + t.Extra.(*FuncType).Nname = n + case TINTERMETH: + t.Extra = InterMethType{Nname: n} + default: + Fatalf("Type.SetNname %v %v", t.Etype, t) + } } // IsFuncArgStruct reports whether t is a struct representing function parameters. func (t *Type) IsFuncArgStruct() bool { - return t.Etype == TSTRUCT && t.Funarg + return t.Etype == TSTRUCT && t.Extra.(*StructType).Funarg } func (t *Type) Methods() *Fields { @@ -582,10 +814,14 @@ func (t *Type) AllMethods() *Fields { } func (t *Type) Fields() *Fields { - if t.Etype != TSTRUCT && t.Etype != TINTER { - Fatalf("Fields: type %v does not have fields", t) + switch t.Etype { + case TSTRUCT: + return &t.Extra.(*StructType).fields + case TINTER: + return &t.Extra.(*InterType).fields } - return &t.fields + Fatalf("Fields: type %v does not have fields", t) + return nil } // Field returns the i'th field/method of struct/interface type t. @@ -608,15 +844,15 @@ func (t *Type) isDDDArray() bool { if t.Etype != TARRAY { return false } - t.checkBound() - return t.Bound == dddBound + _, ok := t.Extra.(DDDArrayType) + return ok } // ArgWidth returns the total aligned argument size for a function. // It includes the receiver, parameters, and results. func (t *Type) ArgWidth() int64 { t.wantEtype(TFUNC) - return t.Argwid + return t.Extra.(*FuncType).Argwid } func (t *Type) Size() int64 { @@ -764,20 +1000,20 @@ func (t *Type) cmp(x *Type) ssa.Cmp { // by the general code after the switch. case TSTRUCT: - if t.Map == nil { - if x.Map != nil { + if t.StructType().Map == nil { + if x.StructType().Map != nil { return ssa.CMPlt // nil < non-nil } // to the fallthrough - } else if x.Map == nil { + } else if x.StructType().Map == nil { return ssa.CMPgt // nil > non-nil - } else if t.Map.Bucket == t { + } else if t.StructType().Map.MapType().Bucket == t { // Both have non-nil Map // Special case for Maps which include a recursive type where the recursion is not broken with a named type - if x.Map.Bucket != x { + if x.StructType().Map.MapType().Bucket != x { return ssa.CMPlt // bucket maps are least } - return t.Map.cmp(x.Map) + return t.StructType().Map.cmp(x.StructType().Map) } // If t != t.Map.Bucket, fall through to general case fallthrough @@ -910,21 +1146,22 @@ func (t *Type) IsChan() bool { return t.Etype == TCHAN } -// checkBound enforces that Bound has an acceptable value. -func (t *Type) checkBound() { - if t.Bound != sliceBound && t.Bound < 0 && t.Bound != dddBound { - Fatalf("bad TARRAY bounds %d %s", t.Bound, t) - } -} - +// TODO: Remove noinline when issue 15084 is resolved. +//go:noinline func (t *Type) IsSlice() bool { - t.checkBound() - return t.Etype == TARRAY && t.Bound == sliceBound + if t.Etype != TARRAY { + return false + } + _, ok := t.Extra.(SliceType) + return ok } func (t *Type) IsArray() bool { - t.checkBound() - return t.Etype == TARRAY && t.Bound >= 0 + if t.Etype != TARRAY { + return false + } + _, ok := t.Extra.(*ArrayType) + return ok } func (t *Type) IsStruct() bool { @@ -961,24 +1198,48 @@ func (t *Type) FieldOff(i int) int64 { func (t *Type) NumElem() int64 { t.wantEtype(TARRAY) - t.checkBound() - return t.Bound + switch t := t.Extra.(type) { + case *ArrayType: + return t.Bound + case SliceType: + return sliceBound + case DDDArrayType: + return dddBound + } + Fatalf("NumElem on non-array %T %v", t.Extra, t) + return 0 } // SetNumElem sets the number of elements in an array type. // It should not be used if at all possible. // Create a new array/slice/dddArray with typX instead. -// TODO(josharian): figure out how to get rid of this. +// The only allowed uses are: +// * array -> slice as a hack to suppress extra error output +// * ddd array -> array +// TODO(josharian): figure out how to get rid of this entirely. func (t *Type) SetNumElem(n int64) { t.wantEtype(TARRAY) - t.Bound = n + switch { + case n >= 0: + if !t.isDDDArray() { + Fatalf("SetNumElem non-ddd -> array %v", t) + } + t.Extra = &ArrayType{Elem: t.Elem(), Bound: n} + case n == sliceBound: + if !t.IsArray() { + Fatalf("SetNumElem non-array -> slice %v", t) + } + t.Extra = SliceType{Elem: t.Elem()} + default: + Fatalf("SetNumElem %d %v", n, t) + } } // ChanDir returns the direction of a channel type t. // The direction will be one of Crecv, Csend, or Cboth. func (t *Type) ChanDir() ChanDir { t.wantEtype(TCHAN) - return t.Chan + return t.Extra.(*ChanType).Dir } func (t *Type) IsMemory() bool { return false } diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index db74a0d246c..ab7d257aacc 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -2103,7 +2103,7 @@ OpSwitch: return n } - if Curfn.Type.Outnamed && n.List.Len() == 0 { + if Curfn.Type.FuncType().Outnamed && n.List.Len() == 0 { break OpSwitch } typecheckaste(ORETURN, nil, false, Curfn.Type.Results(), n.List, func() string { return "return argument" }) @@ -2161,12 +2161,8 @@ OpSwitch: t := n.Type if t != nil && !t.IsFuncArgStruct() && n.Op != OTYPE { switch t.Etype { - case TFUNC, // might have TANY; wait until its called - TANY, - TFORW, - TIDEAL, - TNIL, - TBLANK: + case TFUNC, // might have TANY; wait until it's called + TANY, TFORW, TIDEAL, TNIL, TBLANK: break default: @@ -3522,13 +3518,13 @@ var mapqueue []*Node func copytype(n *Node, t *Type) { if t.Etype == TFORW { // This type isn't computed yet; when it is, update n. - t.Copyto = append(t.Copyto, n) + t.ForwardType().Copyto = append(t.ForwardType().Copyto, n) return } maplineno := n.Type.Maplineno - embedlineno := n.Type.Embedlineno - l := n.Type.Copyto + embedlineno := n.Type.ForwardType().Embedlineno + l := n.Type.ForwardType().Copyto // TODO(mdempsky): Fix Type rekinding. *n.Type = *t @@ -3544,7 +3540,6 @@ func copytype(n *Node, t *Type) { t.Nod = nil t.Printed = false t.Deferwidth = false - t.Copyto = nil // Update nodes waiting on this type. for _, n := range l { diff --git a/src/cmd/compile/internal/gc/universe.go b/src/cmd/compile/internal/gc/universe.go index c2ba9c9a930..3330fbbab2a 100644 --- a/src/cmd/compile/internal/gc/universe.go +++ b/src/cmd/compile/internal/gc/universe.go @@ -359,16 +359,16 @@ func lexinit1() { // t = interface { Error() string } rcvr := typ(TSTRUCT) - rcvr.Funarg = true + rcvr.StructType().Funarg = true field := newField() field.Type = Ptrto(typ(TSTRUCT)) rcvr.SetFields([]*Field{field}) in := typ(TSTRUCT) - in.Funarg = true + in.StructType().Funarg = true out := typ(TSTRUCT) - out.Funarg = true + out.StructType().Funarg = true field = newField() field.Type = Types[TSTRING] out.SetFields([]*Field{field}) diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 392dae0fa90..ff8ddea7f65 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -287,7 +287,7 @@ func walkstmt(n *Node) *Node { if n.List.Len() == 0 { break } - if (Curfn.Type.Outnamed && n.List.Len() > 1) || paramoutheap(Curfn) { + if (Curfn.Type.FuncType().Outnamed && n.List.Len() > 1) || paramoutheap(Curfn) { // assign to the function out parameters, // so that reorder3 can fix up conflicts var rl []*Node