From: David Crawshaw Date: Thu, 31 Mar 2016 14:02:10 +0000 (-0400) Subject: cmd/compile, etc: use name for type pkgPath X-Git-Tag: go1.7beta1~699 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=f120936dfffa3ac935730699587e6957f2d5ea61;p=gostls13.git cmd/compile, etc: use name for type pkgPath 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 TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index 5df49b56d6..8411d2d0ac 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -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 diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index 2bd50b4665..70a75f9324 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -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) } diff --git a/src/cmd/internal/obj/data.go b/src/cmd/internal/obj/data.go index 546ff37269..d7f0840bc1 100644 --- a/src/cmd/internal/obj/data.go +++ b/src/cmd/internal/obj/data.go @@ -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] diff --git a/src/reflect/type.go b/src/reflect/type.go index c7ed402be2..3c7affcd7f 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -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") } diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go index 2410b1954a..adfd660847 100644 --- a/src/runtime/heapdump.go +++ b/src/runtime/heapdump.go @@ -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))) diff --git a/src/runtime/iface.go b/src/runtime/iface.go index 700bdc2f48..84f0ee8f0c 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -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 { diff --git a/src/runtime/type.go b/src/runtime/type.go index 86131d3ff3..711753bab5 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -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 -}