Skip to content

Commit f2772a4

Browse files
committed
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 golang#6853. Change-Id: I1b8c73f3ab894735f53d00cb9c0b506d84d54e92 Reviewed-on: https://go-review.googlesource.com/20709 Run-TryBot: David Crawshaw <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 1b9f168 commit f2772a4

5 files changed

Lines changed: 52 additions & 63 deletions

File tree

src/cmd/compile/internal/gc/reflect.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,6 @@ func dextratypeData(s *Sym, ot int, t *Type) int {
505505

506506
ot = dgopkgpath(s, ot, a.pkg)
507507
ot = dmethodptr(s, ot, dtypesym(a.mtype))
508-
ot = dmethodptr(s, ot, dtypesym(a.type_))
509508
ot = dmethodptr(s, ot, a.isym)
510509
ot = dmethodptr(s, ot, a.tsym)
511510
}

src/cmd/link/internal/ld/deadcode.go

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func deadcode(ctxt *Link) {
9595
// and setting those fields to nil. Doing so
9696
// would reduce the binary size of typical
9797
// programs like cmd/go by ~2%.
98-
d.markMethodType(m)
98+
d.mark(m.mtyp(), m.src)
9999
rem = append(rem, m)
100100
} else {
101101
rem = append(rem, m)
@@ -171,18 +171,17 @@ var markextra = []string{
171171
}
172172

173173
// methodref holds the relocations from a receiver type symbol to its
174-
// method. There are four relocations, one for each of the fields in
175-
// the reflect.method struct: mtyp, typ, ifn, and tfn.
174+
// method. There are three relocations, one for each of the fields in
175+
// the reflect.method struct: mtyp, ifn, and tfn.
176176
type methodref struct {
177177
m methodsig
178178
src *LSym // receiver type symbol
179-
r [4]*Reloc // R_METHOD relocations to fields of runtime.method
179+
r [3]*Reloc // R_METHOD relocations to fields of runtime.method
180180
}
181181

182182
func (m methodref) mtyp() *LSym { return m.r[0].Sym }
183-
func (m methodref) typ() *LSym { return m.r[1].Sym }
184-
func (m methodref) ifn() *LSym { return m.r[2].Sym }
185-
func (m methodref) tfn() *LSym { return m.r[3].Sym }
183+
func (m methodref) ifn() *LSym { return m.r[1].Sym }
184+
func (m methodref) tfn() *LSym { return m.r[2].Sym }
186185

187186
func (m methodref) isExported() bool {
188187
for _, r := range m.m {
@@ -233,12 +232,6 @@ func (d *deadcodepass) markMethod(m methodref) {
233232
}
234233
}
235234

236-
// markMethodType marks just a method's types as reachable.
237-
func (d *deadcodepass) markMethodType(m methodref) {
238-
d.mark(m.mtyp(), m.src)
239-
d.mark(m.typ(), m.src)
240-
}
241-
242235
// init marks all initial symbols as reachable.
243236
// In a typical binary, this is INITENTRY.
244237
func (d *deadcodepass) init() {

src/cmd/link/internal/ld/decodesym.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ func decodetype_methods(s *LSym) []methodsig {
350350
if r.Sym != s {
351351
panic(fmt.Sprintf("method slice pointer in %q leads to a different symbol", s.Name))
352352
}
353-
off = int(r.Add) // array of reflect.method values
354-
sizeofMethod := 6 * Thearch.Ptrsize
353+
off = int(r.Add) // array of reflect.method values
354+
sizeofMethod := 5 * Thearch.Ptrsize // sizeof reflect.method in program
355355
return decode_methodsig(s, off, sizeofMethod, numMethods)
356356
}

src/reflect/type.go

Lines changed: 44 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,6 @@ type method struct {
291291
name *string // name of method
292292
pkgPath *string // nil for exported Names; otherwise import path
293293
mtyp *rtype // method type (without receiver)
294-
typ *rtype // .(*FuncType) underneath (with receiver)
295294
ifn unsafe.Pointer // fn used in interface call (one-word receiver)
296295
tfn unsafe.Pointer // fn used for normal method call
297296
}
@@ -561,11 +560,29 @@ func (t *rtype) pointers() bool { return t.kind&kindNoPointers == 0 }
561560

562561
func (t *rtype) common() *rtype { return t }
563562

564-
func (t *uncommonType) Method(i int) (m Method) {
565-
if t == nil || i < 0 || i >= len(t.methods) {
563+
func (t *rtype) NumMethod() int {
564+
if t.Kind() == Interface {
565+
tt := (*interfaceType)(unsafe.Pointer(t))
566+
return tt.NumMethod()
567+
}
568+
ut := t.uncommon()
569+
if ut == nil {
570+
return 0
571+
}
572+
return len(ut.methods)
573+
}
574+
575+
func (t *rtype) Method(i int) (m Method) {
576+
if t.Kind() == Interface {
577+
tt := (*interfaceType)(unsafe.Pointer(t))
578+
return tt.Method(i)
579+
}
580+
ut := t.uncommon()
581+
582+
if ut == nil || i < 0 || i >= len(ut.methods) {
566583
panic("reflect: Method index out of range")
567584
}
568-
p := &t.methods[i]
585+
p := &ut.methods[i]
569586
if p.name != nil {
570587
m.Name = *p.name
571588
}
@@ -574,60 +591,41 @@ func (t *uncommonType) Method(i int) (m Method) {
574591
m.PkgPath = *p.pkgPath
575592
fl |= flagStickyRO
576593
}
577-
mt := p.typ
594+
ft := (*funcType)(unsafe.Pointer(p.mtyp))
595+
in := make([]Type, 0, 1+len(ft.in()))
596+
in = append(in, t)
597+
for _, arg := range ft.in() {
598+
in = append(in, arg)
599+
}
600+
out := make([]Type, 0, len(ft.out()))
601+
for _, ret := range ft.out() {
602+
out = append(out, ret)
603+
}
604+
mt := FuncOf(in, out, p.mtyp.IsVariadic())
578605
m.Type = mt
579606
fn := unsafe.Pointer(&p.tfn)
580-
m.Func = Value{mt, fn, fl}
607+
m.Func = Value{mt.(*rtype), fn, fl}
581608
m.Index = i
582-
return
609+
return m
583610
}
584611

585-
func (t *uncommonType) NumMethod() int {
586-
if t == nil {
587-
return 0
612+
func (t *rtype) MethodByName(name string) (m Method, ok bool) {
613+
if t.Kind() == Interface {
614+
tt := (*interfaceType)(unsafe.Pointer(t))
615+
return tt.MethodByName(name)
588616
}
589-
return len(t.methods)
590-
}
591-
592-
func (t *uncommonType) MethodByName(name string) (m Method, ok bool) {
593-
if t == nil {
594-
return
617+
ut := t.uncommon()
618+
if ut == nil {
619+
return Method{}, false
595620
}
596621
var p *method
597-
for i := range t.methods {
598-
p = &t.methods[i]
622+
for i := range ut.methods {
623+
p = &ut.methods[i]
599624
if p.name != nil && *p.name == name {
600625
return t.Method(i), true
601626
}
602627
}
603-
return
604-
}
605-
606-
// TODO(rsc): gc supplies these, but they are not
607-
// as efficient as they could be: they have commonType
608-
// as the receiver instead of *rtype.
609-
func (t *rtype) NumMethod() int {
610-
if t.Kind() == Interface {
611-
tt := (*interfaceType)(unsafe.Pointer(t))
612-
return tt.NumMethod()
613-
}
614-
return t.uncommon().NumMethod()
615-
}
616-
617-
func (t *rtype) Method(i int) (m Method) {
618-
if t.Kind() == Interface {
619-
tt := (*interfaceType)(unsafe.Pointer(t))
620-
return tt.Method(i)
621-
}
622-
return t.uncommon().Method(i)
623-
}
624-
625-
func (t *rtype) MethodByName(name string) (m Method, ok bool) {
626-
if t.Kind() == Interface {
627-
tt := (*interfaceType)(unsafe.Pointer(t))
628-
return tt.MethodByName(name)
629-
}
630-
return t.uncommon().MethodByName(name)
628+
return Method{}, false
631629
}
632630

633631
func (t *rtype) PkgPath() string {

src/runtime/type.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ type method struct {
155155
name *string
156156
pkgpath *string
157157
mtyp *_type
158-
typ *_type
159158
ifn unsafe.Pointer
160159
tfn unsafe.Pointer
161160
}

0 commit comments

Comments
 (0)