Commit f2772a49 authored by David Crawshaw's avatar David Crawshaw

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: default avatarIan Lance Taylor <iant@golang.org>
parent 1b9f168f
......@@ -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() {
......
......@@ -351,6 +351,6 @@ func decodetype_methods(s *LSym) []methodsig {
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
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,60 +591,41 @@ 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
return m
}
func (t *uncommonType) NumMethod() int {
if t == nil {
return 0
func (t *rtype) MethodByName(name string) (m Method, ok bool) {
if t.Kind() == Interface {
tt := (*interfaceType)(unsafe.Pointer(t))
return tt.MethodByName(name)
}
return len(t.methods)
}
func (t *uncommonType) MethodByName(name string) (m Method, ok bool) {
if t == nil {
return
ut := t.uncommon()
if ut == nil {
return Method{}, false
}
var p *method
for i := range t.methods {
p = &t.methods[i]
for i := range ut.methods {
p = &ut.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)
}
func (t *rtype) MethodByName(name string) (m Method, ok bool) {
if t.Kind() == Interface {
tt := (*interfaceType)(unsafe.Pointer(t))
return tt.MethodByName(name)
}
return t.uncommon().MethodByName(name)
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
}
......
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