diff options
| author | David Crawshaw <crawshaw@golang.org> | 2016-03-14 15:08:22 -0400 |
|---|---|---|
| committer | David Crawshaw <crawshaw@golang.org> | 2016-03-15 19:41:17 +0000 |
| commit | d06b0db5bd4c898bd162e16ab603081ab62a527c (patch) | |
| tree | f54d7b900bf3ec7cfcdd25a38384c6958fa3e3c4 /src/cmd/link/internal/ld/deadcode.go | |
| parent | 41f9f6f47118d8c2546a1534240e0eca6baa1829 (diff) | |
| download | go-d06b0db5bd4c898bd162e16ab603081ab62a527c.tar.xz | |
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>
Diffstat (limited to 'src/cmd/link/internal/ld/deadcode.go')
| -rw-r--r-- | src/cmd/link/internal/ld/deadcode.go | 74 |
1 files changed, 53 insertions, 21 deletions
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index a2286eb872..9367375102 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -62,6 +62,12 @@ func deadcode(ctxt *Link) { methSym := Linkrlookup(ctxt, "reflect.Value.Method", 0) reflectSeen := false + if DynlinkingGo() { + // Exported methods may satisfy interfaces we don't know + // about yet when dynamically linking. + reflectSeen = true + } + for { if !reflectSeen { 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 { if (reflectSeen && m.isExported()) || d.ifaceMethod[m.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 { rem = append(rem, m) } @@ -95,8 +112,9 @@ func deadcode(ctxt *Link) { // Remove all remaining unreached R_METHOD relocations. for _, m := range d.markableMethods { - d.cleanupReloc(m.r0) - d.cleanupReloc(m.r1) + for _, r := range m.r { + d.cleanupReloc(r) + } } if Buildmode != BuildmodeShared { @@ -153,15 +171,19 @@ var markextra = []string{ } // methodref holds the relocations from a receiver type symbol to its -// method. There are two relocations, one for the method type without -// receiver, one with receiver +// method. There are four relocations, one for each of the fields in +// the reflect.method struct: mtyp, typ, ifn, and tfn. type methodref struct { m methodsig - src *LSym // receiver type symbol - r0 *Reloc - r1 *Reloc + src *LSym // receiver type symbol + r [4]*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) isExported() bool { for _, r := range m.m { return unicode.IsUpper(r) @@ -203,12 +225,18 @@ func (d *deadcodepass) mark(s, parent *LSym) { 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) { - d.mark(m.r0.Sym, m.src) - d.mark(m.r1.Sym, m.src) - m.r0.Type = obj.R_ADDR - m.r1.Type = obj.R_ADDR + for _, r := range m.r { + d.mark(r.Sym, m.src) + r.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. @@ -278,6 +306,7 @@ func (d *deadcodepass) flood() { } } + mpos := 0 // 0-3, the R_METHOD relocs of runtime.uncommontype var methods []methodref for i := 0; i < len(s.R); i++ { r := &s.R[i] @@ -290,14 +319,17 @@ func (d *deadcodepass) flood() { } // Collect rtype pointers to methods for // later processing in deadcode. - if len(methods) > 0 { - mref := &methods[len(methods)-1] - if mref.r1 == nil { - mref.r1 = r - continue - } + if mpos == 0 { + m := methodref{src: s} + m.r[0] = r + methods = append(methods, m) + } 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 { // Decode runtime type information for type methods @@ -310,8 +342,8 @@ func (d *deadcodepass) flood() { for i, m := range methodsigs { name := string(m) name = name[:strings.Index(name, "(")] - if !strings.HasSuffix(methods[i].r0.Sym.Name, name) { - panic(fmt.Sprintf("%q relocation for %q does not match method %q", s.Name, 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].ifn().Name, name)) } methods[i].m = m } |
