]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: remove slices from rtype.funcType
authorDavid Crawshaw <crawshaw@golang.org>
Tue, 23 Feb 2016 16:31:13 +0000 (11:31 -0500)
committerDavid Crawshaw <crawshaw@golang.org>
Wed, 9 Mar 2016 01:25:18 +0000 (01:25 +0000)
Alternative to golang.org/cl/19852. This memory layout doesn't have
an easy type representation, but it is noticeably smaller than the
current funcType, and saves significant extra space.

Some notes on the layout are in reflect/type.go:

// A *rtype for each in and out parameter is stored in an array that
// directly follows the funcType (and possibly its uncommonType). So
// a function type with one method, one input, and one output is:
//
// struct {
// funcType
// uncommonType
// [2]*rtype    // [0] is in, [1] is out
// uncommonTypeSliceContents
// }

There are three arbitrary limits introduced by this CL:

1. No more than 65535 function input parameters.
2. No more than 32767 function output parameters.
3. reflect.FuncOf is limited to 128 parameters.

I don't think these are limits in practice, but are worth noting.

Reduces godoc binary size by 2.4%, 330KB.

For #6853.

Change-Id: I225c0a0516ebdbe92d41dfdf43f716da42dfe347
Reviewed-on: https://go-review.googlesource.com/19916
Reviewed-by: Russ Cox <rsc@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/cmd/compile/internal/gc/reflect.go
src/cmd/link/internal/ld/decodesym.go
src/cmd/link/internal/ld/dwarf.go
src/reflect/type.go
src/reflect/value.go
src/runtime/mfinal.go
src/runtime/syscall_windows.go
src/runtime/type.go

index a18016fa78a0ab24a491e67a88bd5f675cbc4411..b234333d5e2ef915c4489f137752f0ca3b2d0b2d 100644 (file)
@@ -1059,49 +1059,34 @@ ok:
                        isddd = t1.Isddd
                        dtypesym(t1.Type)
                }
-
                for t1 := getoutargx(t).Type; t1 != nil; t1 = t1.Down {
                        dtypesym(t1.Type)
                }
 
                ot = dcommontype(s, ot, t)
-               ot = duint8(s, ot, uint8(obj.Bool2int(isddd)))
-
-               // two slice headers: in and out.
-               ot = int(Rnd(int64(ot), int64(Widthptr)))
-
-               ot = dsymptr(s, ot, s, ot+2*(Widthptr+2*Widthint)+uncommonSize(t))
-               n := t.Thistuple + t.Intuple
-               ot = duintxx(s, ot, uint64(n), Widthint)
-               ot = duintxx(s, ot, uint64(n), Widthint)
-               ot = dsymptr(s, ot, s, ot+1*(Widthptr+2*Widthint)+uncommonSize(t)+n*Widthptr)
-               ot = duintxx(s, ot, uint64(t.Outtuple), Widthint)
-               ot = duintxx(s, ot, uint64(t.Outtuple), Widthint)
-
-               dataAdd := 0
-               for t1 := getthisx(t).Type; t1 != nil; t1 = t1.Down {
-                       dataAdd += Widthptr
+               inCount := t.Thistuple + t.Intuple
+               outCount := t.Outtuple
+               if isddd {
+                       outCount |= 1 << 15
                }
-               for t1 := getinargx(t).Type; t1 != nil; t1 = t1.Down {
-                       dataAdd += Widthptr
-               }
-               for t1 := getoutargx(t).Type; t1 != nil; t1 = t1.Down {
-                       dataAdd += Widthptr
+               ot = duint16(s, ot, uint16(inCount))
+               ot = duint16(s, ot, uint16(outCount))
+               if Widthptr == 8 {
+                       ot += 4 // align for *rtype
                }
+
+               dataAdd := (inCount + outCount) * Widthptr
                ot = dextratype(s, ot, t, dataAdd)
 
-               // slice data
+               // Array of rtype pointers follows funcType.
                for t1 := getthisx(t).Type; t1 != nil; t1 = t1.Down {
                        ot = dsymptr(s, ot, dtypesym(t1.Type), 0)
-                       n++
                }
                for t1 := getinargx(t).Type; t1 != nil; t1 = t1.Down {
                        ot = dsymptr(s, ot, dtypesym(t1.Type), 0)
-                       n++
                }
                for t1 := getoutargx(t).Type; t1 != nil; t1 = t1.Down {
                        ot = dsymptr(s, ot, dtypesym(t1.Type), 0)
-                       n++
                }
 
        case TINTER:
index 89cf0b0564dbdca983461a889471fb0690803dd8..78da6848b56d2a9a28ba525ee327d2225533afec 100644 (file)
@@ -171,33 +171,32 @@ func decodetype_chanelem(s *LSym) *LSym {
 }
 
 // Type.FuncType.dotdotdot
-func decodetype_funcdotdotdot(s *LSym) int {
-       return int(s.P[commonsize()])
+func decodetype_funcdotdotdot(s *LSym) bool {
+       return uint16(decode_inuxi(s.P[commonsize()+2:], 2))&(1<<15) != 0
 }
 
-// Type.FuncType.in.length
+// Type.FuncType.inCount
 func decodetype_funcincount(s *LSym) int {
-       return int(decode_inuxi(s.P[commonsize()+2*Thearch.Ptrsize:], Thearch.Intsize))
+       return int(decode_inuxi(s.P[commonsize():], 2))
 }
 
 func decodetype_funcoutcount(s *LSym) int {
-       return int(decode_inuxi(s.P[commonsize()+3*Thearch.Ptrsize+2*Thearch.Intsize:], Thearch.Intsize))
+       return int(uint16(decode_inuxi(s.P[commonsize()+2:], 2)) & (1<<15 - 1))
 }
 
 func decodetype_funcintype(s *LSym, i int) *LSym {
-       r := decode_reloc(s, int32(commonsize())+int32(Thearch.Ptrsize))
-       if r == nil {
-               return nil
+       uadd := commonsize() + 4
+       if Thearch.Ptrsize == 8 {
+               uadd += 4
+       }
+       if decodetype_hasUncommon(s) {
+               uadd += uncommonSize()
        }
-       return decode_reloc_sym(r.Sym, int32(r.Add+int64(int32(i)*int32(Thearch.Ptrsize))))
+       return decode_reloc_sym(s, int32(uadd+i*Thearch.Ptrsize))
 }
 
 func decodetype_funcouttype(s *LSym, i int) *LSym {
-       r := decode_reloc(s, int32(commonsize())+2*int32(Thearch.Ptrsize)+2*int32(Thearch.Intsize))
-       if r == nil {
-               return nil
-       }
-       return decode_reloc_sym(r.Sym, int32(r.Add+int64(int32(i)*int32(Thearch.Ptrsize))))
+       return decodetype_funcintype(s, i+decodetype_funcincount(s))
 }
 
 // Type.StructType.fields.Slice::length
index 829d8dabf266cc40b0d167848dcf6ab4649ee213..81f7c306f2354ec72d0e108661fe04dc28f8c917 100644 (file)
@@ -984,7 +984,7 @@ func defgotype(gotype *LSym) *DWDie {
                        newrefattr(fld, DW_AT_type, defgotype(s))
                }
 
-               if decodetype_funcdotdotdot(gotype) != 0 {
+               if decodetype_funcdotdotdot(gotype) {
                        newdie(die, DW_ABRV_DOTDOTDOT, "...")
                }
                nfields = decodetype_funcoutcount(gotype)
index de0768a45f223178bc4bd65d3ebe19affb0db18e..44ab004274658c9de35bb070c32d172afa172325 100644 (file)
@@ -330,11 +330,20 @@ type chanType struct {
 }
 
 // funcType represents a function type.
+//
+// A *rtype for each in and out parameter is stored in an array that
+// directly follows the funcType (and possibly its uncommonType). So
+// a function type with one method, one input, and one output is:
+//
+//     struct {
+//             funcType
+//             uncommonType
+//             [2]*rtype    // [0] is in, [1] is out
+//     }
 type funcType struct {
-       rtype     `reflect:"func"`
-       dotdotdot bool     // last input parameter is ...
-       in        []*rtype // input parameter types
-       out       []*rtype // output parameter types
+       rtype    `reflect:"func"`
+       inCount  uint16
+       outCount uint16 // top bit is set if last input parameter is ...
 }
 
 // imethod represents a method on an interface type
@@ -672,7 +681,7 @@ func (t *rtype) IsVariadic() bool {
                panic("reflect: IsVariadic of non-func type")
        }
        tt := (*funcType)(unsafe.Pointer(t))
-       return tt.dotdotdot
+       return tt.outCount&(1<<15) != 0
 }
 
 func (t *rtype) Elem() Type {
@@ -733,7 +742,7 @@ func (t *rtype) In(i int) Type {
                panic("reflect: In of non-func type")
        }
        tt := (*funcType)(unsafe.Pointer(t))
-       return toType(tt.in[i])
+       return toType(tt.in()[i])
 }
 
 func (t *rtype) Key() Type {
@@ -765,7 +774,7 @@ func (t *rtype) NumIn() int {
                panic("reflect: NumIn of non-func type")
        }
        tt := (*funcType)(unsafe.Pointer(t))
-       return len(tt.in)
+       return int(tt.inCount)
 }
 
 func (t *rtype) NumOut() int {
@@ -773,7 +782,7 @@ func (t *rtype) NumOut() int {
                panic("reflect: NumOut of non-func type")
        }
        tt := (*funcType)(unsafe.Pointer(t))
-       return len(tt.out)
+       return len(tt.out())
 }
 
 func (t *rtype) Out(i int) Type {
@@ -781,7 +790,28 @@ func (t *rtype) Out(i int) Type {
                panic("reflect: Out of non-func type")
        }
        tt := (*funcType)(unsafe.Pointer(t))
-       return toType(tt.out[i])
+       return toType(tt.out()[i])
+}
+
+func (t *funcType) in() []*rtype {
+       uadd := uintptr(unsafe.Sizeof(*t))
+       if t.tflag&tflagUncommon != 0 {
+               uadd += unsafe.Sizeof(uncommonType{})
+       }
+       return (*[1 << 20]*rtype)(add(unsafe.Pointer(t), uadd))[:t.inCount]
+}
+
+func (t *funcType) out() []*rtype {
+       uadd := uintptr(unsafe.Sizeof(*t))
+       if t.tflag&tflagUncommon != 0 {
+               uadd += unsafe.Sizeof(uncommonType{})
+       }
+       outCount := t.outCount & (1<<15 - 1)
+       return (*[1 << 20]*rtype)(add(unsafe.Pointer(t), uadd))[t.inCount : t.inCount+outCount]
+}
+
+func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
+       return unsafe.Pointer(uintptr(p) + x)
 }
 
 func (d ChanDir) String() string {
@@ -1330,16 +1360,16 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool {
        case Func:
                t := (*funcType)(unsafe.Pointer(T))
                v := (*funcType)(unsafe.Pointer(V))
-               if t.dotdotdot != v.dotdotdot || len(t.in) != len(v.in) || len(t.out) != len(v.out) {
+               if t.outCount != v.outCount || t.inCount != v.inCount {
                        return false
                }
-               for i, typ := range t.in {
-                       if typ != v.in[i] {
+               for i := 0; i < t.NumIn(); i++ {
+                       if t.In(i) != v.In(i) {
                                return false
                        }
                }
-               for i, typ := range t.out {
-                       if typ != v.out[i] {
+               for i := 0; i < t.NumOut(); i++ {
+                       if t.Out(i) != v.Out(i) {
                                return false
                        }
                }
@@ -1617,6 +1647,31 @@ func MapOf(key, elem Type) Type {
        return cachePut(ckey, &mt.rtype)
 }
 
+type funcTypeFixed4 struct {
+       funcType
+       args [4]*rtype
+}
+type funcTypeFixed8 struct {
+       funcType
+       args [8]*rtype
+}
+type funcTypeFixed16 struct {
+       funcType
+       args [16]*rtype
+}
+type funcTypeFixed32 struct {
+       funcType
+       args [32]*rtype
+}
+type funcTypeFixed64 struct {
+       funcType
+       args [64]*rtype
+}
+type funcTypeFixed128 struct {
+       funcType
+       args [128]*rtype
+}
+
 // FuncOf returns the function type with the given argument and result types.
 // For example if k represents int and e represents string,
 // FuncOf([]Type{k}, []Type{e}, false) represents func(int) string.
@@ -1632,15 +1687,45 @@ func FuncOf(in, out []Type, variadic bool) Type {
        // Make a func type.
        var ifunc interface{} = (func())(nil)
        prototype := *(**funcType)(unsafe.Pointer(&ifunc))
-       ft := new(funcType)
+       n := len(in) + len(out)
+
+       var ft *funcType
+       var args []*rtype
+       switch {
+       case n <= 4:
+               fixed := new(funcTypeFixed4)
+               args = fixed.args[:0:len(fixed.args)]
+               ft = &fixed.funcType
+       case n <= 8:
+               fixed := new(funcTypeFixed8)
+               args = fixed.args[:0:len(fixed.args)]
+               ft = &fixed.funcType
+       case n <= 16:
+               fixed := new(funcTypeFixed16)
+               args = fixed.args[:0:len(fixed.args)]
+               ft = &fixed.funcType
+       case n <= 32:
+               fixed := new(funcTypeFixed32)
+               args = fixed.args[:0:len(fixed.args)]
+               ft = &fixed.funcType
+       case n <= 64:
+               fixed := new(funcTypeFixed64)
+               args = fixed.args[:0:len(fixed.args)]
+               ft = &fixed.funcType
+       case n <= 128:
+               fixed := new(funcTypeFixed128)
+               args = fixed.args[:0:len(fixed.args)]
+               ft = &fixed.funcType
+       default:
+               panic("reflect.FuncOf: too many arguments")
+       }
        *ft = *prototype
 
        // Build a hash and minimally populate ft.
        var hash uint32
-       var fin, fout []*rtype
        for _, in := range in {
                t := in.(*rtype)
-               fin = append(fin, t)
+               args = append(args, t)
                hash = fnv1(hash, byte(t.hash>>24), byte(t.hash>>16), byte(t.hash>>8), byte(t.hash))
        }
        if variadic {
@@ -1649,13 +1734,18 @@ func FuncOf(in, out []Type, variadic bool) Type {
        hash = fnv1(hash, '.')
        for _, out := range out {
                t := out.(*rtype)
-               fout = append(fout, t)
+               args = append(args, t)
                hash = fnv1(hash, byte(t.hash>>24), byte(t.hash>>16), byte(t.hash>>8), byte(t.hash))
        }
+       if len(args) > 50 {
+               panic("reflect.FuncOf does not support more than 50 arguments")
+       }
        ft.hash = hash
-       ft.in = fin
-       ft.out = fout
-       ft.dotdotdot = variadic
+       ft.inCount = uint16(len(in))
+       ft.outCount = uint16(len(out))
+       if variadic {
+               ft.outCount |= 1 << 15
+       }
 
        // Look in cache.
        funcLookupCache.RLock()
@@ -1699,11 +1789,11 @@ func FuncOf(in, out []Type, variadic bool) Type {
 func funcStr(ft *funcType) string {
        repr := make([]byte, 0, 64)
        repr = append(repr, "func("...)
-       for i, t := range ft.in {
+       for i, t := range ft.in() {
                if i > 0 {
                        repr = append(repr, ", "...)
                }
-               if ft.dotdotdot && i == len(ft.in)-1 {
+               if ft.IsVariadic() && i == int(ft.inCount)-1 {
                        repr = append(repr, "..."...)
                        repr = append(repr, (*sliceType)(unsafe.Pointer(t)).elem.string...)
                } else {
@@ -1711,18 +1801,19 @@ func funcStr(ft *funcType) string {
                }
        }
        repr = append(repr, ')')
-       if l := len(ft.out); l == 1 {
+       out := ft.out()
+       if len(out) == 1 {
                repr = append(repr, ' ')
-       } else if l > 1 {
+       } else if len(out) > 1 {
                repr = append(repr, " ("...)
        }
-       for i, t := range ft.out {
+       for i, t := range out {
                if i > 0 {
                        repr = append(repr, ", "...)
                }
                repr = append(repr, t.string...)
        }
-       if len(ft.out) > 1 {
+       if len(out) > 1 {
                repr = append(repr, ')')
        }
        return string(repr)
@@ -2175,7 +2266,7 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
                }
                offset += ptrSize
        }
-       for _, arg := range tt.in {
+       for _, arg := range tt.in() {
                offset += -offset & uintptr(arg.align-1)
                addTypeBits(ptrmap, offset, arg)
                offset += arg.size
@@ -2187,7 +2278,7 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
        }
        offset += -offset & (ptrSize - 1)
        retOffset = offset
-       for _, res := range tt.out {
+       for _, res := range tt.out() {
                offset += -offset & uintptr(res.align-1)
                addTypeBits(ptrmap, offset, res)
                offset += res.size
index 8af39b12ac0c175ff31a5d5f7daf4aaa7871d90c..95bfdb561cc16e9f6946d1d6158ede5e45b75977 100644 (file)
@@ -483,9 +483,8 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) {
        // Copy argument frame into Values.
        ptr := frame
        off := uintptr(0)
-       in := make([]Value, 0, len(ftyp.in))
-       for _, arg := range ftyp.in {
-               typ := arg
+       in := make([]Value, 0, int(ftyp.inCount))
+       for _, typ := range ftyp.in() {
                off += -off & uintptr(typ.align-1)
                addr := unsafe.Pointer(uintptr(ptr) + off)
                v := Value{typ, nil, flag(typ.Kind())}
@@ -506,18 +505,18 @@ func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) {
 
        // Call underlying function.
        out := f(in)
-       if len(out) != len(ftyp.out) {
+       numOut := ftyp.NumOut()
+       if len(out) != numOut {
                panic("reflect: wrong return count from function created by MakeFunc")
        }
 
        // Copy results back into argument frame.
-       if len(ftyp.out) > 0 {
+       if numOut > 0 {
                off += -off & (ptrSize - 1)
                if runtime.GOARCH == "amd64p32" {
                        off = align(off, 8)
                }
-               for i, arg := range ftyp.out {
-                       typ := arg
+               for i, typ := range ftyp.out() {
                        v := out[i]
                        if v.typ != typ {
                                panic("reflect: function created by MakeFunc using " + funcName(f) +
index 6d5378200e19ce97ff4ea7ec9a51c8dd524b8bb9..1238d4a0532753b7e90a788703473c7a8d8e9fe3 100644 (file)
@@ -331,10 +331,13 @@ func SetFinalizer(obj interface{}, finalizer interface{}) {
                throw("runtime.SetFinalizer: second argument is " + ftyp._string + ", not a function")
        }
        ft := (*functype)(unsafe.Pointer(ftyp))
-       if ft.dotdotdot || len(ft.in) != 1 {
+       if ft.dotdotdot() {
+               throw("runtime.SetFinalizer: cannot pass " + etyp._string + " to finalizer " + ftyp._string + " because dotdotdot")
+       }
+       if ft.dotdotdot() || ft.inCount != 1 {
                throw("runtime.SetFinalizer: cannot pass " + etyp._string + " to finalizer " + ftyp._string)
        }
-       fint := ft.in[0]
+       fint := ft.in()[0]
        switch {
        case fint == etyp:
                // ok - same type
@@ -359,8 +362,8 @@ func SetFinalizer(obj interface{}, finalizer interface{}) {
 okarg:
        // compute size needed for return parameters
        nret := uintptr(0)
-       for _, t := range ft.out {
-               nret = round(nret, uintptr(t.align)) + t.size
+       for _, t := range ft.out() {
+               nret = round(nret, uintptr(t.align)) + uintptr(t.size)
        }
        nret = round(nret, sys.PtrSize)
 
index 081516c70a179117731d6e4b865d03a460ea9126..ebfa32ff8cb272ca6958dd8a54cb6b5b214b4e07 100644 (file)
@@ -45,15 +45,15 @@ func compileCallback(fn eface, cleanstack bool) (code uintptr) {
                panic("compileCallback: not a function")
        }
        ft := (*functype)(unsafe.Pointer(fn._type))
-       if len(ft.out) != 1 {
+       if len(ft.out()) != 1 {
                panic("compileCallback: function must have one output parameter")
        }
        uintptrSize := unsafe.Sizeof(uintptr(0))
-       if ft.out[0].size != uintptrSize {
+       if ft.out()[0].size != uintptrSize {
                panic("compileCallback: output parameter size is wrong")
        }
        argsize := uintptr(0)
-       for _, t := range ft.in {
+       for _, t := range ft.in() {
                if t.size > uintptrSize {
                        panic("compileCallback: input parameter size is wrong")
                }
index 9c9b5fb8cc5688a5d6e4d80d1e945d9a9090ff0d..c504e2d29471e603df3130e751e17ca042dc5ab7 100644 (file)
@@ -128,6 +128,29 @@ func (t *_type) name() string {
        return t._string[i+1:]
 }
 
+func (t *functype) in() []*_type {
+       // See funcType in reflect/type.go for details on data layout.
+       uadd := uintptr(unsafe.Sizeof(functype{}))
+       if t.typ.tflag&tflagUncommon != 0 {
+               uadd += unsafe.Sizeof(uncommontype{})
+       }
+       return (*[1 << 20]*_type)(add(unsafe.Pointer(t), uadd))[:t.inCount]
+}
+
+func (t *functype) out() []*_type {
+       // See funcType in reflect/type.go for details on data layout.
+       uadd := uintptr(unsafe.Sizeof(functype{}))
+       if t.typ.tflag&tflagUncommon != 0 {
+               uadd += unsafe.Sizeof(uncommontype{})
+       }
+       outCount := t.outCount & (1<<15 - 1)
+       return (*[1 << 20]*_type)(add(unsafe.Pointer(t), uadd))[t.inCount : t.inCount+outCount]
+}
+
+func (t *functype) dotdotdot() bool {
+       return t.outCount&(1<<15) != 0
+}
+
 type method struct {
        name    *string
        pkgpath *string
@@ -187,10 +210,9 @@ type slicetype struct {
 }
 
 type functype struct {
-       typ       _type
-       dotdotdot bool
-       in        []*_type
-       out       []*_type
+       typ      _type
+       inCount  uint16
+       outCount uint16
 }
 
 type ptrtype struct {