mirror of
https://github.com/golang/go
synced 2024-11-19 10:24:43 -07:00
cmd/link: when pruning methods also prune funcType
Remove method type information for pruned methods from any program that does not reflect on methods. This can be a significant saving: addr2line: -310KB (8.8%) A future update might want to consider a more aggressive variant of this: setting the Type and Func fields of reflect.Method to nil for unexported methods. That would shrink cmd/go by 2% and jujud by 2.6% but could be considered an API change. So this CL sticks to the uncontroversial change. For #6853. Change-Id: I5d186d9f822dc118ee89dc572c4912a3b3c72577 Reviewed-on: https://go-review.googlesource.com/20701 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
41f9f6f471
commit
d06b0db5bd
@ -504,18 +504,10 @@ func dextratypeData(s *Sym, ot int, t *Type) int {
|
|||||||
ot = dgostringptr(s, ot, a.name)
|
ot = dgostringptr(s, ot, a.name)
|
||||||
|
|
||||||
ot = dgopkgpath(s, ot, a.pkg)
|
ot = dgopkgpath(s, ot, a.pkg)
|
||||||
ot = dsymptr(s, ot, dtypesym(a.mtype), 0)
|
ot = dmethodptr(s, ot, dtypesym(a.mtype))
|
||||||
ot = dsymptr(s, ot, dtypesym(a.type_), 0)
|
ot = dmethodptr(s, ot, dtypesym(a.type_))
|
||||||
if a.isym != nil {
|
ot = dmethodptr(s, ot, a.isym)
|
||||||
ot = dmethodptr(s, ot, a.isym)
|
ot = dmethodptr(s, ot, a.tsym)
|
||||||
} else {
|
|
||||||
ot = duintptr(s, ot, 0)
|
|
||||||
}
|
|
||||||
if a.tsym != nil {
|
|
||||||
ot = dmethodptr(s, ot, a.tsym)
|
|
||||||
} else {
|
|
||||||
ot = duintptr(s, ot, 0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ot
|
return ot
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,12 @@ func deadcode(ctxt *Link) {
|
|||||||
methSym := Linkrlookup(ctxt, "reflect.Value.Method", 0)
|
methSym := Linkrlookup(ctxt, "reflect.Value.Method", 0)
|
||||||
reflectSeen := false
|
reflectSeen := false
|
||||||
|
|
||||||
|
if DynlinkingGo() {
|
||||||
|
// Exported methods may satisfy interfaces we don't know
|
||||||
|
// about yet when dynamically linking.
|
||||||
|
reflectSeen = true
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if !reflectSeen {
|
if !reflectSeen {
|
||||||
if d.reflectMethod || (callSym != nil && callSym.Attr.Reachable()) || (methSym != nil && methSym.Attr.Reachable()) {
|
if d.reflectMethod || (callSym != nil && callSym.Attr.Reachable()) || (methSym != nil && methSym.Attr.Reachable()) {
|
||||||
@ -80,6 +86,17 @@ func deadcode(ctxt *Link) {
|
|||||||
for _, m := range d.markableMethods {
|
for _, m := range d.markableMethods {
|
||||||
if (reflectSeen && m.isExported()) || d.ifaceMethod[m.m] {
|
if (reflectSeen && m.isExported()) || d.ifaceMethod[m.m] {
|
||||||
d.markMethod(m)
|
d.markMethod(m)
|
||||||
|
} else if reflectSeen {
|
||||||
|
// This ensures the Type and Func fields of
|
||||||
|
// reflect.Method are filled as they were in
|
||||||
|
// Go 1.
|
||||||
|
//
|
||||||
|
// An argument could be made for changing this
|
||||||
|
// and setting those fields to nil. Doing so
|
||||||
|
// would reduce the binary size of typical
|
||||||
|
// programs like cmd/go by ~2%.
|
||||||
|
d.markMethodType(m)
|
||||||
|
rem = append(rem, m)
|
||||||
} else {
|
} else {
|
||||||
rem = append(rem, m)
|
rem = append(rem, m)
|
||||||
}
|
}
|
||||||
@ -95,8 +112,9 @@ func deadcode(ctxt *Link) {
|
|||||||
|
|
||||||
// Remove all remaining unreached R_METHOD relocations.
|
// Remove all remaining unreached R_METHOD relocations.
|
||||||
for _, m := range d.markableMethods {
|
for _, m := range d.markableMethods {
|
||||||
d.cleanupReloc(m.r0)
|
for _, r := range m.r {
|
||||||
d.cleanupReloc(m.r1)
|
d.cleanupReloc(r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if Buildmode != BuildmodeShared {
|
if Buildmode != BuildmodeShared {
|
||||||
@ -153,15 +171,19 @@ var markextra = []string{
|
|||||||
}
|
}
|
||||||
|
|
||||||
// methodref holds the relocations from a receiver type symbol to its
|
// methodref holds the relocations from a receiver type symbol to its
|
||||||
// method. There are two relocations, one for the method type without
|
// method. There are four relocations, one for each of the fields in
|
||||||
// receiver, one with receiver
|
// the reflect.method struct: mtyp, typ, ifn, and tfn.
|
||||||
type methodref struct {
|
type methodref struct {
|
||||||
m methodsig
|
m methodsig
|
||||||
src *LSym // receiver type symbol
|
src *LSym // receiver type symbol
|
||||||
r0 *Reloc
|
r [4]*Reloc // R_METHOD relocations to fields of runtime.method
|
||||||
r1 *Reloc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) isExported() bool {
|
func (m methodref) isExported() bool {
|
||||||
for _, r := range m.m {
|
for _, r := range m.m {
|
||||||
return unicode.IsUpper(r)
|
return unicode.IsUpper(r)
|
||||||
@ -203,12 +225,18 @@ func (d *deadcodepass) mark(s, parent *LSym) {
|
|||||||
d.markQueue = append(d.markQueue, s)
|
d.markQueue = append(d.markQueue, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// markMethod marks a method as reachable and preps its R_METHOD relocations.
|
// markMethod marks a method as reachable.
|
||||||
func (d *deadcodepass) markMethod(m methodref) {
|
func (d *deadcodepass) markMethod(m methodref) {
|
||||||
d.mark(m.r0.Sym, m.src)
|
for _, r := range m.r {
|
||||||
d.mark(m.r1.Sym, m.src)
|
d.mark(r.Sym, m.src)
|
||||||
m.r0.Type = obj.R_ADDR
|
r.Type = obj.R_ADDR
|
||||||
m.r1.Type = obj.R_ADDR
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
// init marks all initial symbols as reachable.
|
||||||
@ -278,6 +306,7 @@ func (d *deadcodepass) flood() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mpos := 0 // 0-3, the R_METHOD relocs of runtime.uncommontype
|
||||||
var methods []methodref
|
var methods []methodref
|
||||||
for i := 0; i < len(s.R); i++ {
|
for i := 0; i < len(s.R); i++ {
|
||||||
r := &s.R[i]
|
r := &s.R[i]
|
||||||
@ -290,14 +319,17 @@ func (d *deadcodepass) flood() {
|
|||||||
}
|
}
|
||||||
// Collect rtype pointers to methods for
|
// Collect rtype pointers to methods for
|
||||||
// later processing in deadcode.
|
// later processing in deadcode.
|
||||||
if len(methods) > 0 {
|
if mpos == 0 {
|
||||||
mref := &methods[len(methods)-1]
|
m := methodref{src: s}
|
||||||
if mref.r1 == nil {
|
m.r[0] = r
|
||||||
mref.r1 = r
|
methods = append(methods, m)
|
||||||
continue
|
} else {
|
||||||
}
|
methods[len(methods)-1].r[mpos] = r
|
||||||
|
}
|
||||||
|
mpos++
|
||||||
|
if mpos == len(methodref{}.r) {
|
||||||
|
mpos = 0
|
||||||
}
|
}
|
||||||
methods = append(methods, methodref{src: s, r0: r})
|
|
||||||
}
|
}
|
||||||
if len(methods) > 0 {
|
if len(methods) > 0 {
|
||||||
// Decode runtime type information for type methods
|
// Decode runtime type information for type methods
|
||||||
@ -310,8 +342,8 @@ func (d *deadcodepass) flood() {
|
|||||||
for i, m := range methodsigs {
|
for i, m := range methodsigs {
|
||||||
name := string(m)
|
name := string(m)
|
||||||
name = name[:strings.Index(name, "(")]
|
name = name[:strings.Index(name, "(")]
|
||||||
if !strings.HasSuffix(methods[i].r0.Sym.Name, name) {
|
if !strings.HasSuffix(methods[i].ifn().Name, name) {
|
||||||
panic(fmt.Sprintf("%q relocation for %q does not match method %q", s.Name, methods[i].r0.Sym.Name, name))
|
panic(fmt.Sprintf("%q relocation for %q does not match method %q", s.Name, methods[i].ifn().Name, name))
|
||||||
}
|
}
|
||||||
methods[i].m = m
|
methods[i].m = m
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user