]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.link] cmd/link: support reflect.Type.Method tracking in deadcode2
authorCherry Zhang <cherryyz@google.com>
Tue, 8 Oct 2019 22:21:22 +0000 (18:21 -0400)
committerCherry Zhang <cherryyz@google.com>
Mon, 14 Oct 2019 15:55:52 +0000 (15:55 +0000)
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 <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
src/cmd/internal/goobj/readnew.go
src/cmd/internal/goobj2/funcinfo.go
src/cmd/internal/goobj2/objfile.go
src/cmd/internal/obj/objfile2.go
src/cmd/link/internal/ld/deadcode2.go
src/cmd/link/internal/objfile/objfile2.go

index b4b84692d5ea97bc03644cf4cb1020cde1538341..f33bbf73b133b28dcf1c68ca2dce2692c26132c9 100644 (file)
@@ -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)},
index 5938b5f920e703e7f7ce450f9b6610e089ba0a41..4de9b93a03dd4a8151e5148e09696544a1590b10 100644 (file)
@@ -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)
index b5cc0d7bf7b2712cb2bc47502cad5d69f2cae310..c92b9dd9af3a06e4ff8e3e4b6e1bc872f1649322 100644 (file)
@@ -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) {
index c51be0265bd89518888020408feb5af5614ac09c..39e2a4f2248dd69d66cf189aba3aa9faeed84429 100644 (file)
@@ -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),
                }
index b1504e2e8a42562467c2b2c347f85ba22458e4c4..a7a17d5097c2128c2fd7b34d13bc8021b72cd9c2 100644 (file)
@@ -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)
index 4a91a979262b78d919e9719c28e52f997ded128f..cc472954ab71d5c10c94924d8600987386fe8179 100644 (file)
@@ -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
                }