]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile, etc: use name for type pkgPath
authorDavid Crawshaw <crawshaw@golang.org>
Thu, 31 Mar 2016 14:02:10 +0000 (10:02 -0400)
committerDavid Crawshaw <crawshaw@golang.org>
Wed, 13 Apr 2016 20:48:26 +0000 (20:48 +0000)
By replacing the *string used to represent pkgPath with a
reflect.name everywhere, the embedded *string for package paths
inside the reflect.name can be replaced by an offset, nameOff.
This reduces the number of pointers in the type information.

This also moves all reflect.name types into the same section, making
it possible to use nameOff more widely in later CLs.

No significant binary size change for normal binaries, but:

linux/amd64 PIE:
cmd/go: -440KB (3.7%)
jujud:  -2.6MB (3.2%)

For #6853.

Change-Id: I3890b132a784a1090b1b72b32febfe0bea77eaee
Reviewed-on: https://go-review.googlesource.com/21395
Run-TryBot: David Crawshaw <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/cmd/compile/internal/gc/go.go
src/cmd/compile/internal/gc/reflect.go
src/cmd/internal/obj/data.go
src/reflect/type.go
src/runtime/heapdump.go
src/runtime/iface.go
src/runtime/type.go

index 5df49b56d6d7929284dd0843c2509931e2e05f23..8411d2d0ac04d361e053cf5b2c2098859a3aa50e 100644 (file)
@@ -20,7 +20,7 @@ const (
 type Pkg struct {
        Name     string // package name, e.g. "sys"
        Path     string // string literal used in import statement, e.g. "runtime/internal/sys"
-       Pathsym  *Sym
+       Pathsym  *obj.LSym
        Prefix   string // escaped path for use in symbol table
        Imported bool   // export data of this package was parsed
        Exported bool   // import line written in export data
index 2bd50b466595e1d8c0cf6f95ec19a2ab549c8983..70a75f93249d9545375e38b58fb085fbf5078807 100644 (file)
@@ -412,8 +412,6 @@ func imethods(t *Type) []*Sig {
        return methods
 }
 
-var dimportpath_gopkg *Pkg
-
 func dimportpath(p *Pkg) {
        if p.Pathsym != nil {
                return
@@ -426,27 +424,18 @@ func dimportpath(p *Pkg) {
                return
        }
 
-       if dimportpath_gopkg == nil {
-               dimportpath_gopkg = mkpkg("go")
-               dimportpath_gopkg.Name = "go"
-       }
-
-       nam := "importpath." + p.Prefix + "."
-
-       n := Nod(ONAME, nil, nil)
-       n.Sym = Pkglookup(nam, dimportpath_gopkg)
-
-       n.Class = PEXTERN
-       n.Xoffset = 0
-       p.Pathsym = n.Sym
-
+       var str string
        if p == localpkg {
                // Note: myimportpath != "", or else dgopkgpath won't call dimportpath.
-               gdatastring(n, myimportpath)
+               str = myimportpath
        } else {
-               gdatastring(n, p.Path)
+               str = p.Path
        }
-       ggloblsym(n.Sym, int32(Types[TSTRING].Width), obj.DUPOK|obj.RODATA)
+
+       s := obj.Linklookup(Ctxt, "go.importpath."+p.Prefix+".", 0)
+       ot := dnameData(s, 0, str, "", nil, false)
+       ggloblLSym(s, int32(ot), obj.DUPOK|obj.RODATA)
+       p.Pathsym = s
 }
 
 func dgopkgpath(s *Sym, ot int, pkg *Pkg) int {
@@ -469,7 +458,23 @@ func dgopkgpathLSym(s *obj.LSym, ot int, pkg *Pkg) int {
        }
 
        dimportpath(pkg)
-       return dsymptrLSym(s, ot, Linksym(pkg.Pathsym), 0)
+       return dsymptrLSym(s, ot, pkg.Pathsym, 0)
+}
+
+// dgopkgpathOffLSym writes an offset relocation in s at offset ot to the pkg path symbol.
+func dgopkgpathOffLSym(s *obj.LSym, ot int, pkg *Pkg) int {
+       if pkg == localpkg && myimportpath == "" {
+               // If we don't know the full import path of the package being compiled
+               // (i.e. -p was not passed on the compiler command line), emit a reference to
+               // go.importpath.""., which the linker will rewrite using the correct import path.
+               // Every package that imports this one directly defines the symbol.
+               // See also https://groups.google.com/forum/#!topic/golang-dev/myb9s53HxGQ.
+               ns := obj.Linklookup(Ctxt, `go.importpath."".`, 0)
+               return dsymptrOffLSym(s, ot, ns, 0)
+       }
+
+       dimportpath(pkg)
+       return dsymptrOffLSym(s, ot, pkg.Pathsym, 0)
 }
 
 // isExportedField reports whether a struct field is exported.
@@ -495,13 +500,12 @@ func dnameField(s *Sym, ot int, ft *Field) int {
        if ft.Note != nil {
                tag = *ft.Note
        }
-       return dname(s, ot, name, tag, nil, isExportedField(ft))
+       nsym := dname(name, tag, nil, isExportedField(ft))
+       return dsymptrLSym(Linksym(s), ot, nsym, 0)
 }
 
-var dnameCount int
-
-// dname dumps a reflect.name for a struct field or method.
-func dname(s *Sym, ot int, name, tag string, pkg *Pkg, exported bool) int {
+// dnameData writes the contents of a reflect.name into s at offset ot.
+func dnameData(s *obj.LSym, ot int, name, tag string, pkg *Pkg, exported bool) int {
        if len(name) > 1<<16-1 {
                Fatalf("name too long: %s", name)
        }
@@ -534,31 +538,46 @@ func dname(s *Sym, ot int, name, tag string, pkg *Pkg, exported bool) int {
                copy(tb[2:], tag)
        }
 
-       // Very few names require a pkgPath *string (only those
-       // defined in a different package than their type). So if
-       // there is no pkgPath, we treat the name contents as string
-       // data that duplicates across packages.
-       var bsym *obj.LSym
+       ot = int(s.WriteBytes(Ctxt, int64(ot), b))
+
+       if pkg != nil {
+               ot = dgopkgpathOffLSym(s, ot, pkg)
+       }
+
+       return ot
+}
+
+var dnameCount int
+
+// dname creates a reflect.name for a struct field or method.
+func dname(name, tag string, pkg *Pkg, exported bool) *obj.LSym {
+       // Write out data as "type.." to signal two things to the
+       // linker, first that when dynamically linking, the symbol
+       // should be moved to a relro section, and second that the
+       // contents should not be decoded as a type.
+       sname := "type..namedata."
        if pkg == nil {
-               _, bsym = stringsym(string(b))
+               // In the common case, share data with other packages.
+               if name == "" {
+                       if exported {
+                               sname += "-noname-exported." + tag
+                       } else {
+                               sname += "-noname-unexported." + tag
+                       }
+               } else {
+                       sname += name + "." + tag
+               }
        } else {
-               // Write out data as "type.." to signal two things to the
-               // linker, first that when dynamically linking, the symbol
-               // should be moved to a relro section, and second that the
-               // contents should not be decoded as a type.
-               bsymname := fmt.Sprintf(`type..methodname."".%d`, dnameCount)
+               sname = fmt.Sprintf(`%s"".%d`, sname, dnameCount)
                dnameCount++
-               bsym = obj.Linklookup(Ctxt, bsymname, 0)
-               bsym.P = b
-               boff := len(b)
-               boff = int(Rnd(int64(boff), int64(Widthptr)))
-               boff = dgopkgpathLSym(bsym, boff, pkg)
-               ggloblLSym(bsym, int32(boff), obj.RODATA|obj.LOCAL)
        }
-
-       ot = dsymptrLSym(Linksym(s), ot, bsym, 0)
-
-       return ot
+       s := obj.Linklookup(Ctxt, sname, 0)
+       if len(s.P) > 0 {
+               return s
+       }
+       ot := dnameData(s, 0, name, tag, pkg, exported)
+       ggloblLSym(s, int32(ot), obj.DUPOK|obj.RODATA)
+       return s
 }
 
 // dextratype dumps the fields of a runtime.uncommontype.
@@ -627,7 +646,8 @@ func dextratypeData(s *Sym, ot int, t *Type) int {
                if !exported && a.pkg != typePkg(t) {
                        pkg = a.pkg
                }
-               ot = dname(s, ot, a.name, "", pkg, exported)
+               nsym := dname(a.name, "", pkg, exported)
+               ot = dsymptrLSym(lsym, ot, nsym, 0)
                ot = dmethodptrOffLSym(lsym, ot, Linksym(dtypesym(a.mtype)))
                ot = dmethodptrOffLSym(lsym, ot, Linksym(a.isym))
                ot = dmethodptrOffLSym(lsym, ot, Linksym(a.tsym))
@@ -1213,7 +1233,8 @@ ok:
                        if !exported && a.pkg != tpkg {
                                pkg = a.pkg
                        }
-                       ot = dname(s, ot, a.name, "", pkg, exported)
+                       nsym := dname(a.name, "", pkg, exported)
+                       ot = dsymptrLSym(Linksym(s), ot, nsym, 0)
                        ot = dsymptr(s, ot, dtypesym(a.type_), 0)
                }
 
index 546ff372691fef1cba8d047744e1b3e940a843dd..d7f0840bc140e76f104871d07b40ed52adf6ce82 100644 (file)
@@ -75,7 +75,11 @@ func (s *LSym) prepwrite(ctxt *Link, off int64, siz int) {
        if s.Type == SBSS || s.Type == STLSBSS {
                ctxt.Diag("cannot supply data for BSS var")
        }
-       s.Grow(off + int64(siz))
+       l := off + int64(siz)
+       s.Grow(l)
+       if l > s.Size {
+               s.Size = l
+       }
 }
 
 // WriteFloat32 writes f into s at offset off.
@@ -150,6 +154,13 @@ func (s *LSym) WriteString(ctxt *Link, off int64, siz int, str string) {
        copy(s.P[off:off+int64(siz)], str)
 }
 
+// WriteBytes writes a slice of bytes into s at offset off.
+func (s *LSym) WriteBytes(ctxt *Link, off int64, b []byte) int64 {
+       s.prepwrite(ctxt, off, len(b))
+       copy(s.P[off:], b)
+       return off + int64(len(b))
+}
+
 func Addrel(s *LSym) *Reloc {
        s.R = append(s.R, Reloc{})
        return &s.R[len(s.R)-1]
index c7ed402be2661962c54b747ef595e40c651444b2..3c7affcd7f1f0f6098d07577288d8bb196bd533f 100644 (file)
@@ -299,9 +299,9 @@ type method struct {
 // Using a pointer to this struct reduces the overall size required
 // to describe an unnamed type with no methods.
 type uncommonType struct {
-       pkgPath *string // import path; nil for built-in types like int, string
-       mcount  uint16  // number of methods
-       moff    uint16  // offset from this uncommontype to [mcount]method
+       pkgPath name   // import path; empty for built-in types like int, string
+       mcount  uint16 // number of methods
+       moff    uint16 // offset from this uncommontype to [mcount]method
 }
 
 // ChanDir represents a channel type's direction.
@@ -354,7 +354,7 @@ type imethod struct {
 // interfaceType represents an interface type.
 type interfaceType struct {
        rtype   `reflect:"interface"`
-       pkgPath *string   // import path
+       pkgPath name      // import path
        methods []imethod // sorted by hash
 }
 
@@ -396,7 +396,7 @@ type structField struct {
 // structType represents a struct type.
 type structType struct {
        rtype   `reflect:"struct"`
-       pkgPath *string
+       pkgPath name
        fields  []structField // sorted by offset
 }
 
@@ -406,7 +406,7 @@ type structType struct {
 //
 //     1<<0 the name is exported
 //     1<<1 tag data follows the name
-//     1<<2 pkgPath *string follow the name and tag
+//     1<<2 pkgPath nameOff follows the name and tag
 //
 // The next two bytes are the data length:
 //
@@ -417,10 +417,9 @@ type structType struct {
 // If tag data follows then bytes 3+l and 3+l+1 are the tag length,
 // with the data following.
 //
-// If the import path follows, then ptrSize bytes at the end of
-// the data form a *string. The pointer is aligned to its width.
-// The import path is only set for concrete methods that are defined
-// in a different package than their type.
+// If the import path follows, then 4 bytes at the end of
+// the data form a nameOff. The import path is only set for concrete
+// methods that are defined in a different package than their type.
 type name struct {
        bytes *byte
 }
@@ -446,6 +445,9 @@ func (n *name) tagLen() int {
 }
 
 func (n *name) name() (s string) {
+       if n.bytes == nil {
+               return ""
+       }
        nl := n.nameLen()
        if nl == 0 {
                return ""
@@ -468,16 +470,18 @@ func (n *name) tag() (s string) {
        return s
 }
 
-func (n *name) pkgPath() *string {
-       if *n.data(0)&(1<<2) == 0 {
-               return nil
+func (n *name) pkgPath() string {
+       if n.bytes == nil || *n.data(0)&(1<<2) == 0 {
+               return ""
        }
        off := 3 + n.nameLen()
        if tl := n.tagLen(); tl > 0 {
                off += 2 + tl
        }
-       off = int(round(uintptr(off), ptrSize))
-       return *(**string)(unsafe.Pointer(n.data(off)))
+       var nameOff int32
+       copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off)))[:])
+       pkgPathName := name{(*byte)(resolveTypeOff(unsafe.Pointer(n), nameOff))}
+       return pkgPathName.name()
 }
 
 // round n up to a multiple of a.  a must be a power of 2.
@@ -595,10 +599,10 @@ func (t *uncommonType) methods() []method {
 }
 
 func (t *uncommonType) PkgPath() string {
-       if t == nil || t.pkgPath == nil {
+       if t == nil {
                return ""
        }
-       return *t.pkgPath
+       return t.pkgPath.name()
 }
 
 // resolveTypeOff resolves an *rtype offset from a base type.
@@ -752,11 +756,10 @@ func (t *rtype) Method(i int) (m Method) {
        m.Name = p.name.name()
        fl := flag(Func)
        if !p.name.isExported() {
-               pkgPath := p.name.pkgPath()
-               if pkgPath == nil {
-                       pkgPath = ut.pkgPath
+               m.PkgPath = p.name.pkgPath()
+               if m.PkgPath == "" {
+                       m.PkgPath = ut.pkgPath.name()
                }
-               m.PkgPath = *pkgPath
                fl |= flagStickyRO
        }
        if p.mtyp != 0 {
@@ -1004,11 +1007,10 @@ func (t *interfaceType) Method(i int) (m Method) {
        p := &t.methods[i]
        m.Name = p.name.name()
        if !p.name.isExported() {
-               pkgPath := p.name.pkgPath()
-               if pkgPath == nil {
-                       pkgPath = t.pkgPath
+               m.PkgPath = p.name.pkgPath()
+               if m.PkgPath == "" {
+                       m.PkgPath = t.pkgPath.name()
                }
-               m.PkgPath = *pkgPath
        }
        m.Type = toType(p.typ)
        m.Index = i
@@ -1146,9 +1148,9 @@ func (t *structType) Field(i int) (f StructField) {
                f.Name = t.Name()
                f.Anonymous = true
        }
-       if t.pkgPath != nil && !p.name.isExported() {
+       if !p.name.isExported() {
                // Fields never have an import path in their name.
-               f.PkgPath = *t.pkgPath
+               f.PkgPath = t.pkgPath.name()
        }
        if tag := p.name.tag(); tag != "" {
                f.Tag = StructTag(tag)
@@ -2325,7 +2327,7 @@ func StructOf(fields []StructField) Type {
                        case Interface:
                                ift := (*interfaceType)(unsafe.Pointer(ft))
                                for im, m := range ift.methods {
-                                       if m.name.pkgPath() != nil {
+                                       if m.name.pkgPath() != "" {
                                                // TODO(sbinet)
                                                panic("reflect: embedded interface with unexported method(s) not implemented")
                                        }
@@ -2384,7 +2386,7 @@ func StructOf(fields []StructField) Type {
                                ptr := (*ptrType)(unsafe.Pointer(ft))
                                if unt := ptr.uncommon(); unt != nil {
                                        for _, m := range unt.methods() {
-                                               if m.name.pkgPath() != nil {
+                                               if m.name.pkgPath() != "" {
                                                        // TODO(sbinet)
                                                        panic("reflect: embedded interface with unexported method(s) not implemented")
                                                }
@@ -2398,7 +2400,7 @@ func StructOf(fields []StructField) Type {
                                }
                                if unt := ptr.elem.uncommon(); unt != nil {
                                        for _, m := range unt.methods() {
-                                               if m.name.pkgPath() != nil {
+                                               if m.name.pkgPath() != "" {
                                                        // TODO(sbinet)
                                                        panic("reflect: embedded interface with unexported method(s) not implemented")
                                                }
@@ -2413,7 +2415,7 @@ func StructOf(fields []StructField) Type {
                        default:
                                if unt := ft.uncommon(); unt != nil {
                                        for _, m := range unt.methods() {
-                                               if m.name.pkgPath() != nil {
+                                               if m.name.pkgPath() != "" {
                                                        // TODO(sbinet)
                                                        panic("reflect: embedded interface with unexported method(s) not implemented")
                                                }
index 2410b1954abeef6dce33d0fc8ab8dbd7f67fbd56..adfd6608478a00979177ece55225e031460677db 100644 (file)
@@ -183,10 +183,11 @@ func dumptype(t *_type) {
        dumpint(tagType)
        dumpint(uint64(uintptr(unsafe.Pointer(t))))
        dumpint(uint64(t.size))
-       if x := t.uncommon(); x == nil || x.pkgpath == nil {
+       if x := t.uncommon(); x == nil || x.pkgpath.name() == "" {
                dumpstr(t._string)
        } else {
-               pkgpath := stringStructOf(x.pkgpath)
+               pkgpathstr := x.pkgpath.name()
+               pkgpath := stringStructOf(&pkgpathstr)
                namestr := t.name()
                name := stringStructOf(&namestr)
                dumpint(uint64(uintptr(pkgpath.len) + 1 + uintptr(name.len)))
index 700bdc2f48c11d043f62386db4c90893f49372ec..84f0ee8f0c89fbdbc04d6ee9d31ff948bf3a0ee8 100644 (file)
@@ -101,15 +101,15 @@ func additab(m *itab, locked, canfail bool) {
                iname := i.name.name()
                itype := i._type
                ipkg := i.name.pkgPath()
-               if ipkg == nil {
-                       ipkg = inter.pkgpath
+               if ipkg == "" {
+                       ipkg = inter.pkgpath.name()
                }
                for ; j < nt; j++ {
                        t := &xmhdr[j]
                        if typ.typeOff(t.mtyp) == itype && t.name.name() == iname {
                                pkgPath := t.name.pkgPath()
-                               if pkgPath == nil {
-                                       pkgPath = x.pkgpath
+                               if pkgPath == "" {
+                                       pkgPath = x.pkgpath.name()
                                }
                                if t.name.isExported() || pkgPath == ipkg {
                                        if m != nil {
index 86131d3ff3992a27dfb040eae8d8f466dbcc707b..711753bab54511f7a46f601bb072a8dcbe40bbf8 100644 (file)
@@ -6,10 +6,7 @@
 
 package runtime
 
-import (
-       "runtime/internal/sys"
-       "unsafe"
-)
+import "unsafe"
 
 // tflag is documented in ../reflect/type.go.
 type tflag uint8
@@ -151,6 +148,33 @@ var reflectOffs struct {
        minv map[unsafe.Pointer]int32
 }
 
+func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name {
+       if off == 0 {
+               return name{}
+       }
+       base := uintptr(ptrInModule)
+       var md *moduledata
+       for next := &firstmoduledata; next != nil; next = next.next {
+               if base >= next.types && base < next.etypes {
+                       md = next
+                       break
+               }
+       }
+       if md == nil {
+               println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:")
+               for next := &firstmoduledata; next != nil; next = next.next {
+                       println("\ttypes", hex(next.types), "etypes", hex(next.etypes))
+               }
+               throw("runtime: name offset base pointer out of range")
+       }
+       res := md.types + uintptr(off)
+       if res > md.etypes {
+               println("runtime: nameOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes))
+               throw("runtime: name offset out of range")
+       }
+       return name{(*byte)(unsafe.Pointer(res))}
+}
+
 func (t *_type) typeOff(off typeOff) *_type {
        if off == 0 {
                return nil
@@ -240,6 +264,7 @@ func (t *functype) dotdotdot() bool {
        return t.outCount&(1<<15) != 0
 }
 
+type nameOff int32
 type typeOff int32
 type textOff int32
 
@@ -251,7 +276,7 @@ type method struct {
 }
 
 type uncommontype struct {
-       pkgpath *string
+       pkgpath name
        mcount  uint16 // number of methods
        moff    uint16 // offset from this uncommontype to [mcount]method
 }
@@ -263,7 +288,7 @@ type imethod struct {
 
 type interfacetype struct {
        typ     _type
-       pkgpath *string
+       pkgpath name
        mhdr    []imethod
 }
 
@@ -319,7 +344,7 @@ type structfield struct {
 
 type structtype struct {
        typ     _type
-       pkgPath *string
+       pkgPath name
        fields  []structfield
 }
 
@@ -350,6 +375,9 @@ func (n *name) tagLen() int {
 }
 
 func (n *name) name() (s string) {
+       if n.bytes == nil {
+               return ""
+       }
        nl := n.nameLen()
        if nl == 0 {
                return ""
@@ -372,16 +400,18 @@ func (n *name) tag() (s string) {
        return s
 }
 
-func (n *name) pkgPath() *string {
-       if *n.data(0)&(1<<2) == 0 {
-               return nil
+func (n *name) pkgPath() string {
+       if n.bytes == nil || *n.data(0)&(1<<2) == 0 {
+               return ""
        }
        off := 3 + n.nameLen()
        if tl := n.tagLen(); tl > 0 {
                off += 2 + tl
        }
-       off = int(round(uintptr(off), sys.PtrSize))
-       return *(**string)(unsafe.Pointer(n.data(off)))
+       var nameOff nameOff
+       copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off)))[:])
+       pkgPathName := resolveNameOff(unsafe.Pointer(n.bytes), nameOff)
+       return pkgPathName.name()
 }
 
 // typelinksinit scans the types from extra modules and builds the
@@ -466,7 +496,7 @@ func typesEqual(t, v *_type) bool {
                if ut == nil || uv == nil {
                        return false
                }
-               if !pkgPathEqual(ut.pkgpath, uv.pkgpath) {
+               if ut.pkgpath.name() != uv.pkgpath.name() {
                        return false
                }
        }
@@ -506,7 +536,7 @@ func typesEqual(t, v *_type) bool {
        case kindInterface:
                it := (*interfacetype)(unsafe.Pointer(t))
                iv := (*interfacetype)(unsafe.Pointer(v))
-               if !pkgPathEqual(it.pkgpath, iv.pkgpath) {
+               if it.pkgpath.name() != iv.pkgpath.name() {
                        return false
                }
                if len(it.mhdr) != len(iv.mhdr) {
@@ -518,7 +548,7 @@ func typesEqual(t, v *_type) bool {
                        if tm.name.name() != vm.name.name() {
                                return false
                        }
-                       if !pkgPathEqual(tm.name.pkgPath(), vm.name.pkgPath()) {
+                       if tm.name.pkgPath() != vm.name.pkgPath() {
                                return false
                        }
                        if !typesEqual(tm._type, vm._type) {
@@ -550,7 +580,7 @@ func typesEqual(t, v *_type) bool {
                        if tf.name.name() != vf.name.name() {
                                return false
                        }
-                       if !pkgPathEqual(tf.name.pkgPath(), vf.name.pkgPath()) {
+                       if tf.name.pkgPath() != vf.name.pkgPath() {
                                return false
                        }
                        if !typesEqual(tf.typ, vf.typ) {
@@ -570,13 +600,3 @@ func typesEqual(t, v *_type) bool {
                return false
        }
 }
-
-func pkgPathEqual(p, q *string) bool {
-       if p == q {
-               return true
-       }
-       if p == nil || q == nil {
-               return false
-       }
-       return *p == *q
-}