Commit f120936d authored by David Crawshaw's avatar David Crawshaw

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 <crawshaw@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
parent 73e2ad20
......@@ -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
......
......@@ -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)
}
......
......@@ -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]
......
......@@ -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")
}
......
......@@ -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)))
......
......@@ -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 {
......
......@@ -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
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment