From 219922e95b8e49cfb94da9de0c48edb22a2e7054 Mon Sep 17 00:00:00 2001 From: Than McIntosh Date: Wed, 30 Oct 2019 12:31:55 -0400 Subject: [PATCH] [dev.link] cmd/link: add support to new deadcode for field tracking Fix up the new dead code pass to include support for populating the ctxt "Reachparent" map, which is needed to support field tracking. Since we don't have sym.Symbols created at the point where new dead code runs, keep track of reachability using global symbol indices, and then once loader.LoadFull is complete we can translate the index mappings into symbol mappings. The fieldtracking output is unfortunately different relative to master, due to differences in the order in which symbols are encountered in deadcode, but I have eyeballed the results to make sure they look reasonable. Change-Id: I48c7a4597f05c00f15af3bfd37fc15ab4d0017c2 Reviewed-on: https://go-review.googlesource.com/c/go/+/204342 Run-TryBot: Than McIntosh TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang --- src/cmd/link/internal/ld/deadcode2.go | 33 ++++++++++++++------------ src/cmd/link/internal/ld/lib.go | 16 +++++++++++++ src/cmd/link/internal/loader/loader.go | 5 ++++ 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/cmd/link/internal/ld/deadcode2.go b/src/cmd/link/internal/ld/deadcode2.go index a1f7d2f3a4..2fbc0a94d6 100644 --- a/src/cmd/link/internal/ld/deadcode2.go +++ b/src/cmd/link/internal/ld/deadcode2.go @@ -18,8 +18,6 @@ import ( var _ = fmt.Print // TODO: -// - Field tracking support: -// It needs to record from where the symbol is referenced. // - Debug output: // Emit messages about which symbols are kept or deleted. @@ -43,6 +41,9 @@ type deadcodePass2 struct { func (d *deadcodePass2) init() { d.ldr.InitReachable() d.ifaceMethod = make(map[methodsig]bool) + if d.ctxt.Reachparent != nil { + d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym()) + } if d.ctxt.BuildMode == BuildModeShared { // Mark all symbols defined in this library as reachable when @@ -51,7 +52,7 @@ func (d *deadcodePass2) init() { for i := 1; i < n; i++ { s := loader.Sym(i) if !d.ldr.IsDup(s) { - d.mark(s) + d.mark(s, 0) } } return @@ -82,7 +83,7 @@ func (d *deadcodePass2) init() { if exportsIdx != 0 { d.ReadRelocs(exportsIdx) for i := 0; i < len(d.rtmp); i++ { - d.mark(d.rtmp[i].Sym) + d.mark(d.rtmp[i].Sym, 0) } } } @@ -106,9 +107,9 @@ func (d *deadcodePass2) init() { for _, name := range names { // Mark symbol as an data/ABI0 symbol. - d.mark(d.ldr.Lookup(name, 0)) + d.mark(d.ldr.Lookup(name, 0), 0) // Also mark any Go functions (internal ABI). - d.mark(d.ldr.Lookup(name, sym.SymVerABIInternal)) + d.mark(d.ldr.Lookup(name, sym.SymVerABIInternal), 0) } } @@ -155,12 +156,11 @@ func (d *deadcodePass2) flood() { // do nothing for now as we still load all type symbols. continue } - d.mark(r.Sym) + d.mark(r.Sym, symIdx) } - auxSyms = d.ldr.ReadAuxSyms(symIdx, auxSyms) for i := 0; i < len(auxSyms); i++ { - d.mark(auxSyms[i]) + d.mark(auxSyms[i], symIdx) } // Some host object symbols have an outer object, which acts like a // "carrier" symbol, or it holds all the symbols for a particular @@ -168,8 +168,8 @@ func (d *deadcodePass2) flood() { // so we make sure we're pulling in all outer symbols, and their sub // symbols. This is not ideal, and these carrier/section symbols could // be removed. - d.mark(d.ldr.OuterSym(symIdx)) - d.mark(d.ldr.SubSym(symIdx)) + d.mark(d.ldr.OuterSym(symIdx), symIdx) + d.mark(d.ldr.SubSym(symIdx), symIdx) if len(methods) != 0 { // Decode runtime type information for type methods @@ -187,18 +187,21 @@ func (d *deadcodePass2) flood() { } } -func (d *deadcodePass2) mark(symIdx loader.Sym) { +func (d *deadcodePass2) mark(symIdx, parent loader.Sym) { if symIdx != 0 && !d.ldr.Reachable.Has(symIdx) { d.wq.push(symIdx) d.ldr.Reachable.Set(symIdx) + if d.ctxt.Reachparent != nil { + d.ldr.Reachparent[symIdx] = parent + } } } func (d *deadcodePass2) markMethod(m methodref2) { d.ReadRelocs(m.src) - d.mark(d.rtmp[m.r].Sym) - d.mark(d.rtmp[m.r+1].Sym) - d.mark(d.rtmp[m.r+2].Sym) + d.mark(d.rtmp[m.r].Sym, m.src) + d.mark(d.rtmp[m.r+1].Sym, m.src) + d.mark(d.rtmp[m.r+2].Sym, m.src) } func deadcode2(ctxt *Link) { diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index e46457d858..811dd0f9ef 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -2611,6 +2611,22 @@ func (ctxt *Link) loadlibfull() { setupdynexp(ctxt) + // Populate ctxt.Reachparent if appropriate. + if ctxt.Reachparent != nil { + for i := 0; i < len(ctxt.loader.Reachparent); i++ { + p := ctxt.loader.Reachparent[i] + if p == 0 { + continue + } + if p == loader.Sym(i) { + panic("self-cycle in reachparent") + } + sym := ctxt.loader.Syms[i] + psym := ctxt.loader.Syms[p] + ctxt.Reachparent[sym] = psym + } + } + // Drop the reference. ctxt.loader = nil ctxt.cgodata = nil diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go index 67c4c9719c..38b2c810e3 100644 --- a/src/cmd/link/internal/loader/loader.go +++ b/src/cmd/link/internal/loader/loader.go @@ -112,6 +112,11 @@ type Loader struct { Syms []*sym.Symbol // indexed symbols. XXX we still make sym.Symbol for now. Reachable bitmap // bitmap of reachable symbols, indexed by global index + + // Used to implement field tracking; created during deadcode if + // field tracking is enabled. Reachparent[K] contains the index of + // the symbol that triggered the marking of symbol K as live. + Reachparent []Sym } func NewLoader() *Loader { -- 2.48.1