diff options
| author | Cherry Zhang <cherryyz@google.com> | 2020-09-21 20:44:53 -0400 |
|---|---|---|
| committer | Cherry Zhang <cherryyz@google.com> | 2020-09-29 16:30:26 +0000 |
| commit | 39dde09126be02f5f8c38ddf7590ae8f9825fcaa (patch) | |
| tree | f8c66e4386c0226c376d7287dd639f0287b79feb /src/cmd/link/internal/ld/deadcode.go | |
| parent | 39a426d35615da2ef594cd72ea5de54a543305e1 (diff) | |
| download | go-39dde09126be02f5f8c38ddf7590ae8f9825fcaa.tar.xz | |
cmd/link: retain only used interface methods
Currently, in the linker's deadcode pass, when an interface type
is live, the linker thinks all its methods are live, and uses
them to match methods on concrete types. The interface method may
never be used, though.
This CL changes it to only keep used interface methods, for
matching concrete type methods. To do that, when an interface
method is used, the compiler generates a mark relocation. The
linker uses the marker relocations to mark used interface
methods, and only the used ones.
binary size before after
cmd/compile 18887400 18812200
cmd/go 13470652 13470492
Change-Id: I3cfd9df4a53783330ba87735853f2a0ec3c42802
Reviewed-on: https://go-review.googlesource.com/c/go/+/256798
Trust: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
Diffstat (limited to 'src/cmd/link/internal/ld/deadcode.go')
| -rw-r--r-- | src/cmd/link/internal/ld/deadcode.go | 53 |
1 files changed, 24 insertions, 29 deletions
diff --git a/src/cmd/link/internal/ld/deadcode.go b/src/cmd/link/internal/ld/deadcode.go index 816a23b9a7..74d61fa495 100644 --- a/src/cmd/link/internal/ld/deadcode.go +++ b/src/cmd/link/internal/ld/deadcode.go @@ -106,25 +106,16 @@ func (d *deadcodePass) flood() { if isgotype { usedInIface = d.ldr.AttrUsedInIface(symIdx) - p := d.ldr.Data(symIdx) - if len(p) != 0 && decodetypeKind(d.ctxt.Arch, p)&kindMask == kindInterface { - for _, sig := range d.decodeIfaceMethods(d.ldr, d.ctxt.Arch, symIdx, &relocs) { - if d.ctxt.Debugvlog > 1 { - d.ctxt.Logf("reached iface method: %v\n", sig) - } - d.ifaceMethod[sig] = true - } - } } methods = methods[:0] for i := 0; i < relocs.Count(); i++ { r := relocs.At(i) t := r.Type() - if t == objabi.R_WEAKADDROFF { + switch t { + case objabi.R_WEAKADDROFF: continue - } - if t == objabi.R_METHODOFF { + case objabi.R_METHODOFF: if i+2 >= relocs.Count() { panic("expect three consecutive R_METHODOFF relocs") } @@ -146,14 +137,12 @@ func (d *deadcodePass) flood() { } i += 2 continue - } - if t == objabi.R_USETYPE { + case objabi.R_USETYPE: // type symbol used for DWARF. we need to load the symbol but it may not // be otherwise reachable in the program. // do nothing for now as we still load all type symbols. continue - } - if t == objabi.R_USEIFACE { + case objabi.R_USEIFACE: // R_USEIFACE is a marker relocation that tells the linker the type is // converted to an interface, i.e. should have UsedInIface set. See the // comment below for why we need to unset the Reachable bit and re-mark it. @@ -166,6 +155,18 @@ func (d *deadcodePass) flood() { } } continue + case objabi.R_USEIFACEMETHOD: + // R_USEIFACEMETHOD is a marker relocation that marks an interface + // method as used. + rs := r.Sym() + if d.ldr.SymType(rs) != sym.SDYNIMPORT { // don't decode DYNIMPORT symbol (we'll mark all exported methods anyway) + m := d.decodeIfaceMethod(d.ldr, d.ctxt.Arch, rs, r.Add()) + if d.ctxt.Debugvlog > 1 { + d.ctxt.Logf("reached iface method: %v\n", m) + } + d.ifaceMethod[m] = true + } + continue } rs := r.Sym() if isgotype && usedInIface && d.ldr.IsGoType(rs) && !d.ldr.AttrUsedInIface(rs) { @@ -378,23 +379,17 @@ func (d *deadcodePass) decodeMethodSig(ldr *loader.Loader, arch *sys.Arch, symId return methods } -func (d *deadcodePass) decodeIfaceMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig { +// Decode the method of interface type symbol symIdx at offset off. +func (d *deadcodePass) decodeIfaceMethod(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, off int64) methodsig { p := ldr.Data(symIdx) if decodetypeKind(arch, p)&kindMask != kindInterface { panic(fmt.Sprintf("symbol %q is not an interface", ldr.SymName(symIdx))) } - rel := decodeReloc(ldr, symIdx, relocs, int32(commonsize(arch)+arch.PtrSize)) - s := rel.Sym() - if s == 0 { - return nil - } - if s != symIdx { - panic(fmt.Sprintf("imethod slice pointer in %q leads to a different symbol", ldr.SymName(symIdx))) - } - off := int(rel.Add()) // array of reflect.imethod values - numMethods := int(decodetypeIfaceMethodCount(arch, p)) - sizeofIMethod := 4 + 4 - return d.decodeMethodSig(ldr, arch, symIdx, relocs, off, sizeofIMethod, numMethods) + relocs := ldr.Relocs(symIdx) + var m methodsig + m.name = decodetypeName(ldr, symIdx, &relocs, int(off)) + m.typ = decodeRelocSym(ldr, symIdx, &relocs, int32(off+4)) + return m } func (d *deadcodePass) decodetypeMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig { |
