mirror of https://github.com/golang/go.git
cmd/compile: compute second method type at runtime
The type information for a method includes two variants: a func without the receiver, and a func with the receiver as the first parameter. The former is used as part of the dynamic interface checks, but the latter is only returned as a type in the reflect.Method struct. Instead of computing it at compile time, construct it at run time with reflect.FuncOf. Using cl/20701 as a baseline, cmd/go: -480KB, (4.4%) jujud: -5.6MB, (7.8%) For #6853. Change-Id: I1b8c73f3ab894735f53d00cb9c0b506d84d54e92 Reviewed-on: https://go-review.googlesource.com/20709 Run-TryBot: David Crawshaw <crawshaw@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
1b9f168f73
commit
f2772a4935
|
|
@ -505,7 +505,6 @@ func dextratypeData(s *Sym, ot int, t *Type) int {
|
|||
|
||||
ot = dgopkgpath(s, ot, a.pkg)
|
||||
ot = dmethodptr(s, ot, dtypesym(a.mtype))
|
||||
ot = dmethodptr(s, ot, dtypesym(a.type_))
|
||||
ot = dmethodptr(s, ot, a.isym)
|
||||
ot = dmethodptr(s, ot, a.tsym)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ func deadcode(ctxt *Link) {
|
|||
// and setting those fields to nil. Doing so
|
||||
// would reduce the binary size of typical
|
||||
// programs like cmd/go by ~2%.
|
||||
d.markMethodType(m)
|
||||
d.mark(m.mtyp(), m.src)
|
||||
rem = append(rem, m)
|
||||
} else {
|
||||
rem = append(rem, m)
|
||||
|
|
@ -171,18 +171,17 @@ var markextra = []string{
|
|||
}
|
||||
|
||||
// methodref holds the relocations from a receiver type symbol to its
|
||||
// method. There are four relocations, one for each of the fields in
|
||||
// the reflect.method struct: mtyp, typ, ifn, and tfn.
|
||||
// method. There are three relocations, one for each of the fields in
|
||||
// the reflect.method struct: mtyp, ifn, and tfn.
|
||||
type methodref struct {
|
||||
m methodsig
|
||||
src *LSym // receiver type symbol
|
||||
r [4]*Reloc // R_METHOD relocations to fields of runtime.method
|
||||
r [3]*Reloc // R_METHOD relocations to fields of runtime.method
|
||||
}
|
||||
|
||||
func (m methodref) mtyp() *LSym { return m.r[0].Sym }
|
||||
func (m methodref) typ() *LSym { return m.r[1].Sym }
|
||||
func (m methodref) ifn() *LSym { return m.r[2].Sym }
|
||||
func (m methodref) tfn() *LSym { return m.r[3].Sym }
|
||||
func (m methodref) ifn() *LSym { return m.r[1].Sym }
|
||||
func (m methodref) tfn() *LSym { return m.r[2].Sym }
|
||||
|
||||
func (m methodref) isExported() bool {
|
||||
for _, r := range m.m {
|
||||
|
|
@ -233,12 +232,6 @@ func (d *deadcodepass) markMethod(m methodref) {
|
|||
}
|
||||
}
|
||||
|
||||
// markMethodType marks just a method's types as reachable.
|
||||
func (d *deadcodepass) markMethodType(m methodref) {
|
||||
d.mark(m.mtyp(), m.src)
|
||||
d.mark(m.typ(), m.src)
|
||||
}
|
||||
|
||||
// init marks all initial symbols as reachable.
|
||||
// In a typical binary, this is INITENTRY.
|
||||
func (d *deadcodepass) init() {
|
||||
|
|
|
|||
|
|
@ -350,7 +350,7 @@ func decodetype_methods(s *LSym) []methodsig {
|
|||
if r.Sym != s {
|
||||
panic(fmt.Sprintf("method slice pointer in %q leads to a different symbol", s.Name))
|
||||
}
|
||||
off = int(r.Add) // array of reflect.method values
|
||||
sizeofMethod := 6 * Thearch.Ptrsize
|
||||
off = int(r.Add) // array of reflect.method values
|
||||
sizeofMethod := 5 * Thearch.Ptrsize // sizeof reflect.method in program
|
||||
return decode_methodsig(s, off, sizeofMethod, numMethods)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -291,7 +291,6 @@ type method struct {
|
|||
name *string // name of method
|
||||
pkgPath *string // nil for exported Names; otherwise import path
|
||||
mtyp *rtype // method type (without receiver)
|
||||
typ *rtype // .(*FuncType) underneath (with receiver)
|
||||
ifn unsafe.Pointer // fn used in interface call (one-word receiver)
|
||||
tfn unsafe.Pointer // fn used for normal method call
|
||||
}
|
||||
|
|
@ -561,11 +560,29 @@ func (t *rtype) pointers() bool { return t.kind&kindNoPointers == 0 }
|
|||
|
||||
func (t *rtype) common() *rtype { return t }
|
||||
|
||||
func (t *uncommonType) Method(i int) (m Method) {
|
||||
if t == nil || i < 0 || i >= len(t.methods) {
|
||||
func (t *rtype) NumMethod() int {
|
||||
if t.Kind() == Interface {
|
||||
tt := (*interfaceType)(unsafe.Pointer(t))
|
||||
return tt.NumMethod()
|
||||
}
|
||||
ut := t.uncommon()
|
||||
if ut == nil {
|
||||
return 0
|
||||
}
|
||||
return len(ut.methods)
|
||||
}
|
||||
|
||||
func (t *rtype) Method(i int) (m Method) {
|
||||
if t.Kind() == Interface {
|
||||
tt := (*interfaceType)(unsafe.Pointer(t))
|
||||
return tt.Method(i)
|
||||
}
|
||||
ut := t.uncommon()
|
||||
|
||||
if ut == nil || i < 0 || i >= len(ut.methods) {
|
||||
panic("reflect: Method index out of range")
|
||||
}
|
||||
p := &t.methods[i]
|
||||
p := &ut.methods[i]
|
||||
if p.name != nil {
|
||||
m.Name = *p.name
|
||||
}
|
||||
|
|
@ -574,52 +591,22 @@ func (t *uncommonType) Method(i int) (m Method) {
|
|||
m.PkgPath = *p.pkgPath
|
||||
fl |= flagStickyRO
|
||||
}
|
||||
mt := p.typ
|
||||
ft := (*funcType)(unsafe.Pointer(p.mtyp))
|
||||
in := make([]Type, 0, 1+len(ft.in()))
|
||||
in = append(in, t)
|
||||
for _, arg := range ft.in() {
|
||||
in = append(in, arg)
|
||||
}
|
||||
out := make([]Type, 0, len(ft.out()))
|
||||
for _, ret := range ft.out() {
|
||||
out = append(out, ret)
|
||||
}
|
||||
mt := FuncOf(in, out, p.mtyp.IsVariadic())
|
||||
m.Type = mt
|
||||
fn := unsafe.Pointer(&p.tfn)
|
||||
m.Func = Value{mt, fn, fl}
|
||||
m.Func = Value{mt.(*rtype), fn, fl}
|
||||
m.Index = i
|
||||
return
|
||||
}
|
||||
|
||||
func (t *uncommonType) NumMethod() int {
|
||||
if t == nil {
|
||||
return 0
|
||||
}
|
||||
return len(t.methods)
|
||||
}
|
||||
|
||||
func (t *uncommonType) MethodByName(name string) (m Method, ok bool) {
|
||||
if t == nil {
|
||||
return
|
||||
}
|
||||
var p *method
|
||||
for i := range t.methods {
|
||||
p = &t.methods[i]
|
||||
if p.name != nil && *p.name == name {
|
||||
return t.Method(i), true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(rsc): gc supplies these, but they are not
|
||||
// as efficient as they could be: they have commonType
|
||||
// as the receiver instead of *rtype.
|
||||
func (t *rtype) NumMethod() int {
|
||||
if t.Kind() == Interface {
|
||||
tt := (*interfaceType)(unsafe.Pointer(t))
|
||||
return tt.NumMethod()
|
||||
}
|
||||
return t.uncommon().NumMethod()
|
||||
}
|
||||
|
||||
func (t *rtype) Method(i int) (m Method) {
|
||||
if t.Kind() == Interface {
|
||||
tt := (*interfaceType)(unsafe.Pointer(t))
|
||||
return tt.Method(i)
|
||||
}
|
||||
return t.uncommon().Method(i)
|
||||
return m
|
||||
}
|
||||
|
||||
func (t *rtype) MethodByName(name string) (m Method, ok bool) {
|
||||
|
|
@ -627,7 +614,18 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) {
|
|||
tt := (*interfaceType)(unsafe.Pointer(t))
|
||||
return tt.MethodByName(name)
|
||||
}
|
||||
return t.uncommon().MethodByName(name)
|
||||
ut := t.uncommon()
|
||||
if ut == nil {
|
||||
return Method{}, false
|
||||
}
|
||||
var p *method
|
||||
for i := range ut.methods {
|
||||
p = &ut.methods[i]
|
||||
if p.name != nil && *p.name == name {
|
||||
return t.Method(i), true
|
||||
}
|
||||
}
|
||||
return Method{}, false
|
||||
}
|
||||
|
||||
func (t *rtype) PkgPath() string {
|
||||
|
|
|
|||
|
|
@ -155,7 +155,6 @@ type method struct {
|
|||
name *string
|
||||
pkgpath *string
|
||||
mtyp *_type
|
||||
typ *_type
|
||||
ifn unsafe.Pointer
|
||||
tfn unsafe.Pointer
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue