Commit 8df733bd authored by David Crawshaw's avatar David Crawshaw

cmd/compile: remove slices from rtype.funcType

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/19916Reviewed-by: default avatarRuss Cox <rsc@golang.org>
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent b050adee
......@@ -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:
......
......@@ -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
......
......@@ -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)
......
......@@ -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
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
......
......@@ -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) +
......
......@@ -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)
......
......@@ -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")
}
......
......@@ -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
......@@ -188,9 +211,8 @@ type slicetype struct {
type functype struct {
typ _type
dotdotdot bool
in []*_type
out []*_type
inCount uint16
outCount uint16
}
type ptrtype struct {
......
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