1
0
mirror of https://github.com/golang/go synced 2024-11-19 03:44:40 -07:00

cmd/compile: shrink gc.Type in half

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 <mdempsky@google.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
Josh Bleecher Snyder 2016-04-01 20:11:30 -07:00
parent a25e368f44
commit f38f43d029
15 changed files with 491 additions and 226 deletions

View File

@ -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")
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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
}
}
}

View File

@ -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))

View File

@ -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 {

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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.

View File

@ -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 }

View File

@ -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 {

View File

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

View File

@ -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