From d6535415fb3246ac5beda22bb6e557700df628ae Mon Sep 17 00:00:00 2001 From: Cherry Zhang Date: Tue, 8 Oct 2019 18:21:22 -0400 Subject: [PATCH] [dev.link] cmd/link: support reflect.Type.Method tracking in deadcode2 When reflect.Type.Method is called, all exported methods from a reachable type need to be conservatively live. When such a function is called, the compiler sets an attribute to the function, and the linker needs to check that attribute. Implement this in the index-based deadcode pass. Unify symbol flags and FuncInfo flags to make things simpler. In particular, the deadcode pass can check the reflectMethod attribute without reading in and decoding FuncInfo. Change-Id: Ibb21e172f2996e899c6efa5551a29d0eca62df67 Reviewed-on: https://go-review.googlesource.com/c/go/+/200099 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh --- src/cmd/internal/goobj/readnew.go | 4 +-- src/cmd/internal/goobj2/funcinfo.go | 13 +-------- src/cmd/internal/goobj2/objfile.go | 5 ++++ src/cmd/internal/obj/objfile2.go | 32 +++++++++++------------ src/cmd/link/internal/ld/deadcode2.go | 24 +++++++---------- src/cmd/link/internal/objfile/objfile2.go | 22 +++++++++++++--- 6 files changed, 51 insertions(+), 49 deletions(-) diff --git a/src/cmd/internal/goobj/readnew.go b/src/cmd/internal/goobj/readnew.go index b4b84692d5..f33bbf73b1 100644 --- a/src/cmd/internal/goobj/readnew.go +++ b/src/cmd/internal/goobj/readnew.go @@ -142,8 +142,8 @@ func (r *objReader) readNew() { Args: int64(info.Args), Frame: int64(info.Locals), NoSplit: info.NoSplit != 0, - Leaf: info.Flags&goobj2.FuncFlagLeaf != 0, - TopFrame: info.Flags&goobj2.FuncFlagTopFrame != 0, + Leaf: osym.Flag&goobj2.SymFlagLeaf != 0, + TopFrame: osym.Flag&goobj2.SymFlagTopFrame != 0, PCSP: Data{int64(pcdataBase + info.Pcsp), int64(info.Pcfile - info.Pcsp)}, PCFile: Data{int64(pcdataBase + info.Pcfile), int64(info.Pcline - info.Pcfile)}, PCLine: Data{int64(pcdataBase + info.Pcline), int64(info.Pcinline - info.Pcline)}, diff --git a/src/cmd/internal/goobj2/funcinfo.go b/src/cmd/internal/goobj2/funcinfo.go index 5938b5f920..4de9b93a03 100644 --- a/src/cmd/internal/goobj2/funcinfo.go +++ b/src/cmd/internal/goobj2/funcinfo.go @@ -15,7 +15,6 @@ import ( // TODO: make each pcdata a separate symbol? type FuncInfo struct { NoSplit uint8 - Flags uint8 Args uint32 Locals uint32 @@ -32,17 +31,8 @@ type FuncInfo struct { // TODO: InlTree } -const ( - FuncFlagLeaf = 1 << iota - FuncFlagCFunc - FuncFlagReflectMethod - FuncFlagShared // This is really silly - FuncFlagTopFrame -) - func (a *FuncInfo) Write(w *bytes.Buffer) { w.WriteByte(a.NoSplit) - w.WriteByte(a.Flags) var b [4]byte writeUint32 := func(x uint32) { @@ -77,8 +67,7 @@ func (a *FuncInfo) Write(w *bytes.Buffer) { func (a *FuncInfo) Read(b []byte) { a.NoSplit = b[0] - a.Flags = b[1] - b = b[2:] + b = b[1:] readUint32 := func() uint32 { x := binary.LittleEndian.Uint32(b) diff --git a/src/cmd/internal/goobj2/objfile.go b/src/cmd/internal/goobj2/objfile.go index b5cc0d7bf7..c92b9dd9af 100644 --- a/src/cmd/internal/goobj2/objfile.go +++ b/src/cmd/internal/goobj2/objfile.go @@ -191,6 +191,11 @@ const ( SymFlagDupok = 1 << iota SymFlagLocal SymFlagTypelink + SymFlagLeaf + SymFlagCFunc + SymFlagReflectMethod + SymFlagShared // This is really silly + SymFlagTopFrame ) func (s *Sym) Write(w *Writer) { diff --git a/src/cmd/internal/obj/objfile2.go b/src/cmd/internal/obj/objfile2.go index c51be0265b..39e2a4f224 100644 --- a/src/cmd/internal/obj/objfile2.go +++ b/src/cmd/internal/obj/objfile2.go @@ -228,6 +228,21 @@ func (w *writer) Sym(s *LSym) { if s.MakeTypelink() { flag |= goobj2.SymFlagTypelink } + if s.Leaf() { + flag |= goobj2.SymFlagLeaf + } + if s.CFunc() { + flag |= goobj2.SymFlagCFunc + } + if s.ReflectMethod() { + flag |= goobj2.SymFlagReflectMethod + } + if w.ctxt.Flag_shared { // This is really silly + flag |= goobj2.SymFlagShared + } + if s.TopFrame() { + flag |= goobj2.SymFlagTopFrame + } o := goobj2.Sym{ Name: s.Name, ABI: abi, @@ -299,25 +314,8 @@ func genFuncInfoSyms(ctxt *Link) { if s.NoSplit() { nosplit = 1 } - flags := uint8(0) - if s.Leaf() { - flags |= goobj2.FuncFlagLeaf - } - if s.CFunc() { - flags |= goobj2.FuncFlagCFunc - } - if s.ReflectMethod() { - flags |= goobj2.FuncFlagReflectMethod - } - if ctxt.Flag_shared { // This is really silly - flags |= goobj2.FuncFlagShared - } - if s.TopFrame() { - flags |= goobj2.FuncFlagTopFrame - } o := goobj2.FuncInfo{ NoSplit: nosplit, - Flags: flags, Args: uint32(s.Func.Args), Locals: uint32(s.Func.Locals), } diff --git a/src/cmd/link/internal/ld/deadcode2.go b/src/cmd/link/internal/ld/deadcode2.go index b1504e2e8a..a7a17d5097 100644 --- a/src/cmd/link/internal/ld/deadcode2.go +++ b/src/cmd/link/internal/ld/deadcode2.go @@ -18,9 +18,6 @@ import ( var _ = fmt.Print // TODO: -// - Live method tracking: -// The special handling of reflect.Type.Method has not -// been implemented. // - Shared object support: // It basically marks everything. We could consider using // a different mechanism to represent it. @@ -42,7 +39,7 @@ type deadcodePass2 struct { ifaceMethod map[methodsig]bool // methods declared in reached interfaces markableMethods []methodref2 // methods of reached types - reflectMethod bool // TODO: this is not set for now + reflectSeen bool // whether we have seen a reflect method call } func (d *deadcodePass2) init() { @@ -95,6 +92,8 @@ func (d *deadcodePass2) flood() { for !d.wq.empty() { symIdx := d.wq.pop() + d.reflectSeen = d.reflectSeen || d.loader.IsReflectMethod(symIdx) + name := d.loader.RawSymName(symIdx) if strings.HasPrefix(name, "type.") && name[5] != '.' { // TODO: use an attribute instead of checking name p := d.loader.Data(symIdx) @@ -168,23 +167,18 @@ func deadcode2(ctxt *Link) { callSym := loader.Lookup("reflect.Value.Call", sym.SymVerABIInternal) methSym := loader.Lookup("reflect.Value.Method", sym.SymVerABIInternal) - reflectSeen := false if ctxt.DynlinkingGo() { // Exported methods may satisfy interfaces we don't know // about yet when dynamically linking. - reflectSeen = true + d.reflectSeen = true } for { - if !reflectSeen { - if d.reflectMethod || (callSym != 0 && loader.Reachable.Has(callSym)) || (methSym != 0 && loader.Reachable.Has(methSym)) { - // Methods might be called via reflection. Give up on - // static analysis, mark all exported methods of - // all reachable types as reachable. - reflectSeen = true - } - } + // Methods might be called via reflection. Give up on + // static analysis, mark all exported methods of + // all reachable types as reachable. + d.reflectSeen = d.reflectSeen || (callSym != 0 && loader.Reachable.Has(callSym)) || (methSym != 0 && loader.Reachable.Has(methSym)) // Mark all methods that could satisfy a discovered // interface as reachable. We recheck old marked interfaces @@ -192,7 +186,7 @@ func deadcode2(ctxt *Link) { // in the last pass. rem := d.markableMethods[:0] for _, m := range d.markableMethods { - if (reflectSeen && m.isExported()) || d.ifaceMethod[m.m] { + if (d.reflectSeen && m.isExported()) || d.ifaceMethod[m.m] { d.markMethod(m) } else { rem = append(rem, m) diff --git a/src/cmd/link/internal/objfile/objfile2.go b/src/cmd/link/internal/objfile/objfile2.go index 4a91a97926..cc472954ab 100644 --- a/src/cmd/link/internal/objfile/objfile2.go +++ b/src/cmd/link/internal/objfile/objfile2.go @@ -266,6 +266,22 @@ func (l *Loader) SymType(i Sym) sym.SymKind { return sym.AbiSymKindToSymKind[objabi.SymKind(osym.Type)] } +// Returns the attributes of the i-th symbol. +func (l *Loader) SymAttr(i Sym) uint8 { + if l.extStart != 0 && i >= l.extStart { + return 0 + } + r, li := l.ToLocal(i) + osym := goobj2.Sym{} + osym.Read(r.Reader, r.SymOff(li)) + return osym.Flag +} + +// Returns whether the i-th symbol has ReflectMethod attribute set. +func (l *Loader) IsReflectMethod(i Sym) bool { + return l.SymAttr(i)&goobj2.SymFlagReflectMethod != 0 +} + // Returns the symbol content of the i-th symbol. i is global index. func (l *Loader) Data(i Sym) []byte { if l.extStart != 0 && i >= l.extStart { @@ -685,13 +701,13 @@ func loadObjFull(l *Loader, r *oReader) { if info.NoSplit != 0 { s.Attr |= sym.AttrNoSplit } - if info.Flags&goobj2.FuncFlagReflectMethod != 0 { + if osym.Flag&goobj2.SymFlagReflectMethod != 0 { s.Attr |= sym.AttrReflectMethod } - if info.Flags&goobj2.FuncFlagShared != 0 { + if osym.Flag&goobj2.SymFlagShared != 0 { s.Attr |= sym.AttrShared } - if info.Flags&goobj2.FuncFlagTopFrame != 0 { + if osym.Flag&goobj2.SymFlagTopFrame != 0 { s.Attr |= sym.AttrTopFrame } -- 2.48.1