From 7d469179e6e3dafe16700b7fc1cf8683ad9453fa Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Mon, 28 Mar 2016 10:32:27 -0400 Subject: cmd/compile, etc: store method tables as offsets This CL introduces the typeOff type and a lookup method of the same name that can turn a typeOff offset into an *rtype. In a typical Go binary (built with buildmode=exe, pie, c-archive, or c-shared), there is one moduledata and all typeOff values are offsets relative to firstmoduledata.types. This makes computing the pointer cheap in typical programs. With buildmode=shared (and one day, buildmode=plugin) there are multiple modules whose relative offset is determined at runtime. We identify a type in the general case by the pair of the original *rtype that references it and its typeOff value. We determine the module from the original pointer, and then use the typeOff from there to compute the final *rtype. To ensure there is only one *rtype representing each type, the runtime initializes a typemap for each module, using any identical type from an earlier module when resolving that offset. This means that types computed from an offset match the type mapped by the pointer dynamic relocations. A series of followup CLs will replace other *rtype values with typeOff (and name/*string with nameOff). For types created at runtime by reflect, type offsets are treated as global IDs and reference into a reflect offset map kept by the runtime. darwin/amd64: cmd/go: -57KB (0.6%) jujud: -557KB (0.8%) linux/amd64 PIE: cmd/go: -361KB (3.0%) jujud: -3.5MB (4.2%) For #6853. Change-Id: Icf096fd884a0a0cb9f280f46f7a26c70a9006c96 Reviewed-on: https://go-review.googlesource.com/21285 Reviewed-by: Ian Lance Taylor Run-TryBot: David Crawshaw TryBot-Result: Gobot Gobot --- src/runtime/type.go | 307 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 303 insertions(+), 4 deletions(-) (limited to 'src/runtime/type.go') diff --git a/src/runtime/type.go b/src/runtime/type.go index fbf6f9973c..86131d3ff3 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -131,6 +131,92 @@ func (t *_type) name() string { return t._string[i+1:] } +// reflectOffs holds type offsets defined at run time by the reflect package. +// +// When a type is defined at run time, its *rtype data lives on the heap. +// There are a wide range of possible addresses the heap may use, that +// may not be representable as a 32-bit offset. Moreover the GC may +// one day start moving heap memory, in which case there is no stable +// offset that can be defined. +// +// To provide stable offsets, we add pin *rtype objects in a global map +// and treat the offset as an identifier. We use negative offsets that +// do not overlap with any compile-time module offsets. +// +// Entries are created by reflect.addReflectOff. +var reflectOffs struct { + lock mutex + next int32 + m map[int32]unsafe.Pointer + minv map[unsafe.Pointer]int32 +} + +func (t *_type) typeOff(off typeOff) *_type { + if off == 0 { + return nil + } + base := uintptr(unsafe.Pointer(t)) + var md *moduledata + for next := &firstmoduledata; next != nil; next = next.next { + if base >= next.types && base < next.etypes { + md = next + break + } + } + if md == nil { + lock(&reflectOffs.lock) + res := reflectOffs.m[int32(off)] + unlock(&reflectOffs.lock) + if res == nil { + println("runtime: typeOff", 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: type offset base pointer out of range") + } + return (*_type)(res) + } + if t := md.typemap[off]; t != nil { + return t + } + res := md.types + uintptr(off) + if res > md.etypes { + println("runtime: typeOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes)) + throw("runtime: type offset out of range") + } + return (*_type)(unsafe.Pointer(res)) +} + +func (t *_type) textOff(off textOff) unsafe.Pointer { + base := uintptr(unsafe.Pointer(t)) + var md *moduledata + for next := &firstmoduledata; next != nil; next = next.next { + if base >= next.types && base < next.etypes { + md = next + break + } + } + if md == nil { + lock(&reflectOffs.lock) + res := reflectOffs.m[int32(off)] + unlock(&reflectOffs.lock) + if res == nil { + println("runtime: textOff", 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: text offset base pointer out of range") + } + return res + } + res := md.text + uintptr(off) + if res > md.etext { + println("runtime: textOff", hex(off), "out of range", hex(md.text), "-", hex(md.etext)) + throw("runtime: text offset out of range") + } + return unsafe.Pointer(res) +} + func (t *functype) in() []*_type { // See funcType in reflect/type.go for details on data layout. uadd := uintptr(unsafe.Sizeof(functype{})) @@ -154,16 +240,20 @@ func (t *functype) dotdotdot() bool { return t.outCount&(1<<15) != 0 } +type typeOff int32 +type textOff int32 + type method struct { name name - mtyp *_type - ifn unsafe.Pointer - tfn unsafe.Pointer + mtyp typeOff + ifn textOff + tfn textOff } type uncommontype struct { pkgpath *string - mhdr []method + mcount uint16 // number of methods + moff uint16 // offset from this uncommontype to [mcount]method } type imethod struct { @@ -270,6 +360,18 @@ func (n *name) name() (s string) { return s } +func (n *name) tag() (s string) { + tl := n.tagLen() + if tl == 0 { + return "" + } + nl := n.nameLen() + hdr := (*stringStruct)(unsafe.Pointer(&s)) + hdr.str = unsafe.Pointer(n.data(3 + nl + 2)) + hdr.len = tl + return s +} + func (n *name) pkgPath() *string { if *n.data(0)&(1<<2) == 0 { return nil @@ -281,3 +383,200 @@ func (n *name) pkgPath() *string { off = int(round(uintptr(off), sys.PtrSize)) return *(**string)(unsafe.Pointer(n.data(off))) } + +// typelinksinit scans the types from extra modules and builds the +// moduledata typemap used to de-duplicate type pointers. +func typelinksinit() { + if firstmoduledata.next == nil { + return + } + typehash := make(map[uint32][]*_type) + + modules := []*moduledata{} + for md := &firstmoduledata; md != nil; md = md.next { + modules = append(modules, md) + } + prev, modules := modules[len(modules)-1], modules[:len(modules)-1] + for len(modules) > 0 { + // Collect types from the previous module into typehash. + collect: + for _, tl := range prev.typelinks { + var t *_type + if prev.typemap == nil { + t = (*_type)(unsafe.Pointer(prev.types + uintptr(tl))) + } else { + t = prev.typemap[typeOff(tl)] + } + // Add to typehash if not seen before. + tlist := typehash[t.hash] + for _, tcur := range tlist { + if tcur == t { + continue collect + } + } + typehash[t.hash] = append(tlist, t) + } + + // If any of this module's typelinks match a type from a + // prior module, prefer that prior type by adding the offset + // to this module's typemap. + md := modules[len(modules)-1] + md.typemap = make(map[typeOff]*_type, len(md.typelinks)) + for _, tl := range md.typelinks { + t := (*_type)(unsafe.Pointer(md.types + uintptr(tl))) + for _, candidate := range typehash[t.hash] { + if typesEqual(t, candidate) { + t = candidate + break + } + } + md.typemap[typeOff(tl)] = t + } + + prev, modules = md, modules[:len(modules)-1] + } +} + +// typesEqual reports whether two types are equal. +// +// Everywhere in the runtime and reflect packages, it is assumed that +// there is exactly one *_type per Go type, so that pointer equality +// can be used to test if types are equal. There is one place that +// breaks this assumption: buildmode=shared. In this case a type can +// appear as two different pieces of memory. This is hidden from the +// runtime and reflect package by the per-module typemap built in +// typelinksinit. It uses typesEqual to map types from later modules +// back into earlier ones. +// +// Only typelinksinit needs this function. +func typesEqual(t, v *_type) bool { + if t == v { + return true + } + kind := t.kind & kindMask + if kind != v.kind&kindMask { + return false + } + if t._string != v._string { + return false + } + ut := t.uncommon() + uv := v.uncommon() + if ut != nil || uv != nil { + if ut == nil || uv == nil { + return false + } + if !pkgPathEqual(ut.pkgpath, uv.pkgpath) { + return false + } + } + if kindBool <= kind && kind <= kindComplex128 { + return true + } + switch kind { + case kindString, kindUnsafePointer: + return true + case kindArray: + at := (*arraytype)(unsafe.Pointer(t)) + av := (*arraytype)(unsafe.Pointer(v)) + return typesEqual(at.elem, av.elem) && at.len == av.len + case kindChan: + ct := (*chantype)(unsafe.Pointer(t)) + cv := (*chantype)(unsafe.Pointer(v)) + return ct.dir == cv.dir && typesEqual(ct.elem, cv.elem) + case kindFunc: + ft := (*functype)(unsafe.Pointer(t)) + fv := (*functype)(unsafe.Pointer(v)) + if ft.outCount != fv.outCount || ft.inCount != fv.inCount { + return false + } + tin, vin := ft.in(), fv.in() + for i := 0; i < len(tin); i++ { + if !typesEqual(tin[i], vin[i]) { + return false + } + } + tout, vout := ft.out(), fv.out() + for i := 0; i < len(tout); i++ { + if !typesEqual(tout[i], vout[i]) { + return false + } + } + return true + case kindInterface: + it := (*interfacetype)(unsafe.Pointer(t)) + iv := (*interfacetype)(unsafe.Pointer(v)) + if !pkgPathEqual(it.pkgpath, iv.pkgpath) { + return false + } + if len(it.mhdr) != len(iv.mhdr) { + return false + } + for i := range it.mhdr { + tm := &it.mhdr[i] + vm := &iv.mhdr[i] + if tm.name.name() != vm.name.name() { + return false + } + if !pkgPathEqual(tm.name.pkgPath(), vm.name.pkgPath()) { + return false + } + if !typesEqual(tm._type, vm._type) { + return false + } + } + return true + case kindMap: + mt := (*maptype)(unsafe.Pointer(t)) + mv := (*maptype)(unsafe.Pointer(v)) + return typesEqual(mt.key, mv.key) && typesEqual(mt.elem, mv.elem) + case kindPtr: + pt := (*ptrtype)(unsafe.Pointer(t)) + pv := (*ptrtype)(unsafe.Pointer(v)) + return typesEqual(pt.elem, pv.elem) + case kindSlice: + st := (*slicetype)(unsafe.Pointer(t)) + sv := (*slicetype)(unsafe.Pointer(v)) + return typesEqual(st.elem, sv.elem) + case kindStruct: + st := (*structtype)(unsafe.Pointer(t)) + sv := (*structtype)(unsafe.Pointer(v)) + if len(st.fields) != len(sv.fields) { + return false + } + for i := range st.fields { + tf := &st.fields[i] + vf := &sv.fields[i] + if tf.name.name() != vf.name.name() { + return false + } + if !pkgPathEqual(tf.name.pkgPath(), vf.name.pkgPath()) { + return false + } + if !typesEqual(tf.typ, vf.typ) { + return false + } + if tf.name.tag() != vf.name.tag() { + return false + } + if tf.offset != vf.offset { + return false + } + } + return true + default: + println("runtime: impossible type kind", kind) + throw("runtime: impossible type kind") + return false + } +} + +func pkgPathEqual(p, q *string) bool { + if p == q { + return true + } + if p == nil || q == nil { + return false + } + return *p == *q +} -- cgit v1.3 From f120936dfffa3ac935730699587e6957f2d5ea61 Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Thu, 31 Mar 2016 10:02:10 -0400 Subject: 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 --- src/cmd/compile/internal/gc/go.go | 2 +- src/cmd/compile/internal/gc/reflect.go | 117 +++++++++++++++++++-------------- src/cmd/internal/obj/data.go | 13 +++- src/reflect/type.go | 64 +++++++++--------- src/runtime/heapdump.go | 5 +- src/runtime/iface.go | 8 +-- src/runtime/type.go | 72 ++++++++++++-------- 7 files changed, 168 insertions(+), 113 deletions(-) (limited to 'src/runtime/type.go') 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 -} -- cgit v1.3 From 95df0c6ab93f6a42bdc9fd45500fd4d56bfc9add Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Mon, 28 Mar 2016 21:51:10 -0400 Subject: cmd/compile, etc: use name offset in method tables Introduce and start using nameOff for two encoded names. This pair of changes is best done together because the linker's method decoder expects the method layouts to match. Precursor to converting all existing name and *string fields to nameOff. linux/amd64: cmd/go: -45KB (0.5%) jujud: -389KB (0.6%) linux/amd64 PIE: cmd/go: -170KB (1.4%) jujud: -1.5MB (1.8%) For #6853. Change-Id: Ia044423f010fb987ce070b94c46a16fc78666ff6 Reviewed-on: https://go-review.googlesource.com/21396 Reviewed-by: Ian Lance Taylor --- src/cmd/compile/internal/gc/reflect.go | 14 ++--- src/cmd/link/internal/ld/decodesym.go | 18 +++---- src/cmd/link/internal/ld/symtab.go | 2 +- src/reflect/export_test.go | 8 +-- src/reflect/type.go | 96 +++++++++++++++++++++------------- src/reflect/value.go | 8 +-- src/runtime/iface.go | 17 +++--- src/runtime/runtime1.go | 6 +++ src/runtime/type.go | 46 ++++++++++------ 9 files changed, 130 insertions(+), 85 deletions(-) (limited to 'src/runtime/type.go') diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index b8b9369f37..f782ce0974 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -70,7 +70,7 @@ const ( ) func structfieldSize() int { return 3 * Widthptr } // Sizeof(runtime.structfield{}) -func imethodSize() int { return 2 * Widthptr } // Sizeof(runtime.imethod{}) +func imethodSize() int { return 4 + 4 } // Sizeof(runtime.imethod{}) func uncommonSize(t *Type) int { // Sizeof(runtime.uncommontype{}) if t.Sym == nil && len(methods(t)) == 0 { return 0 @@ -647,13 +647,11 @@ func dextratypeData(s *Sym, ot int, t *Type) int { pkg = a.pkg } nsym := dname(a.name, "", pkg, exported) - ot = dsymptrLSym(lsym, ot, nsym, 0) + + ot = dsymptrOffLSym(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)) - if Widthptr == 8 { - ot = duintxxLSym(lsym, ot, 0, 4) // pad to reflect.method size - } } return ot } @@ -1226,6 +1224,7 @@ ok: dataAdd := imethodSize() * n ot = dextratype(s, ot, t, dataAdd) + lsym := Linksym(s) for _, a := range m { // ../../../../runtime/type.go:/imethod exported := exportname(a.name) @@ -1234,8 +1233,9 @@ ok: pkg = a.pkg } nsym := dname(a.name, "", pkg, exported) - ot = dsymptrLSym(Linksym(s), ot, nsym, 0) - ot = dsymptr(s, ot, dtypesym(a.type_), 0) + + ot = dsymptrOffLSym(lsym, ot, nsym, 0) + ot = dsymptrOffLSym(lsym, ot, Linksym(dtypesym(a.type_)), 0) } // ../../../../runtime/type.go:/mapType diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index 4725b91d01..5eb20c2fb2 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -262,8 +262,9 @@ const ( ) // decode_methodsig decodes an array of method signature information. -// Each element of the array is size bytes. The first word is a -// reflect.name for the name, the second word is a *rtype for the funcType. +// Each element of the array is size bytes. The first 4 bytes is a +// nameOff for the method name, and the next 4 bytes is a typeOff for +// the function type. // // Conveniently this is the layout of both runtime.method and runtime.imethod. func decode_methodsig(s *LSym, off, size, count int) []methodsig { @@ -271,7 +272,7 @@ func decode_methodsig(s *LSym, off, size, count int) []methodsig { var methods []methodsig for i := 0; i < count; i++ { buf.WriteString(decodetype_name(s, off)) - mtypSym := decode_reloc_sym(s, int32(off+SysArch.PtrSize)) + mtypSym := decode_reloc_sym(s, int32(off+4)) buf.WriteRune('(') inCount := decodetype_funcincount(mtypSym) @@ -311,7 +312,7 @@ func decodetype_ifacemethods(s *LSym) []methodsig { } off := int(r.Add) // array of reflect.imethod values numMethods := int(decodetype_ifacemethodcount(s)) - sizeofIMethod := 2 * SysArch.PtrSize + sizeofIMethod := 4 + 4 return decode_methodsig(s, off, sizeofIMethod, numMethods) } @@ -343,12 +344,7 @@ func decodetype_methods(s *LSym) []methodsig { mcount := int(decode_inuxi(s.P[off+SysArch.PtrSize:], 2)) moff := int(decode_inuxi(s.P[off+SysArch.PtrSize+2:], 2)) - off += moff // offset to array of reflect.method values - var sizeofMethod int // sizeof reflect.method in program - if SysArch.PtrSize == 4 { - sizeofMethod = 4 * SysArch.PtrSize - } else { - sizeofMethod = 3 * SysArch.PtrSize - } + off += moff // offset to array of reflect.method values + const sizeofMethod = 4 * 4 // sizeof reflect.method in program return decode_methodsig(s, off, sizeofMethod, mcount) } diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 96e8de5030..1f07a4eb77 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -427,7 +427,7 @@ func symtab() { if !DynlinkingGo() { s.Attr |= AttrHidden } - if UseRelro() && len(s.R) > 0 { + if UseRelro() { s.Type = obj.STYPERELRO s.Outer = symtyperel } else { diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go index 2769e0db40..f527434f0d 100644 --- a/src/reflect/export_test.go +++ b/src/reflect/export_test.go @@ -50,7 +50,8 @@ func TypeLinks() []string { for i, offs := range offset { rodata := sections[i] for _, off := range offs { - r = append(r, rtypeOff(rodata, off).string) + typ := (*rtype)(resolveTypeOff(unsafe.Pointer(rodata), off)) + r = append(r, typ.string) } } return r @@ -91,10 +92,11 @@ func FirstMethodNameBytes(t Type) *byte { panic("type has no methods") } m := ut.methods()[0] - if *m.name.data(0)&(1<<2) == 0 { + mname := t.(*rtype).nameOff(m.name) + if *mname.data(0)&(1<<2) == 0 { panic("method name does not have pkgPath *string") } - return m.name.bytes + return mname.bytes } type OtherPkgFields struct { diff --git a/src/reflect/type.go b/src/reflect/type.go index b8c778cc2b..0cae69a79c 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -288,7 +288,7 @@ type typeAlg struct { // Method on non-interface type type method struct { - name name // name of method + name nameOff // name of method mtyp typeOff // method type (without receiver) ifn textOff // fn used in interface call (one-word receiver) tfn textOff // fn used for normal method call @@ -347,8 +347,8 @@ type funcType struct { // imethod represents a method on an interface type type imethod struct { - name name // name of method - typ *rtype // .(*FuncType) underneath + name nameOff // name of method + typ typeOff // .(*FuncType) underneath } // interfaceType represents an interface type. @@ -424,19 +424,19 @@ type name struct { bytes *byte } -func (n *name) data(off int) *byte { +func (n name) data(off int) *byte { return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off))) } -func (n *name) isExported() bool { +func (n name) isExported() bool { return (*n.bytes)&(1<<0) != 0 } -func (n *name) nameLen() int { +func (n name) nameLen() int { return int(uint16(*n.data(1))<<8 | uint16(*n.data(2))) } -func (n *name) tagLen() int { +func (n name) tagLen() int { if *n.data(0)&(1<<1) == 0 { return 0 } @@ -444,7 +444,7 @@ func (n *name) tagLen() int { return int(uint16(*n.data(off))<<8 | uint16(*n.data(off + 1))) } -func (n *name) name() (s string) { +func (n name) name() (s string) { if n.bytes == nil { return "" } @@ -458,7 +458,7 @@ func (n *name) name() (s string) { return s } -func (n *name) tag() (s string) { +func (n name) tag() (s string) { tl := n.tagLen() if tl == 0 { return "" @@ -470,7 +470,7 @@ func (n *name) tag() (s string) { return s } -func (n *name) pkgPath() string { +func (n name) pkgPath() string { if n.bytes == nil || *n.data(0)&(1<<2) == 0 { return "" } @@ -480,7 +480,7 @@ func (n *name) pkgPath() string { } var nameOff int32 copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.data(off)))[:]) - pkgPathName := name{(*byte)(resolveTypeOff(unsafe.Pointer(n), nameOff))} + pkgPathName := name{(*byte)(resolveTypeOff(unsafe.Pointer(n.bytes), nameOff))} return pkgPathName.name() } @@ -605,6 +605,11 @@ func (t *uncommonType) PkgPath() string { return t.pkgPath.name() } +// resolveNameOff resolves a name offset from a base pointer. +// The (*rtype).nameOff method is a convenience wrapper for this function. +// Implemented in the runtime package. +func resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer + // resolveTypeOff resolves an *rtype offset from a base type. // The (*rtype).typeOff method is a convenience wrapper for this function. // Implemented in the runtime package. @@ -620,6 +625,12 @@ func resolveTextOff(rtype unsafe.Pointer, off int32) unsafe.Pointer // be resolved correctly. Implemented in the runtime package. func addReflectOff(ptr unsafe.Pointer) int32 +// resolveReflectType adds a name to the reflection lookup map in the runtime. +// It returns a new nameOff that can be used to refer to the pointer. +func resolveReflectName(n name) nameOff { + return nameOff(addReflectOff(unsafe.Pointer(n.bytes))) +} + // resolveReflectType adds a *rtype to the reflection lookup map in the runtime. // It returns a new typeOff that can be used to refer to the pointer. func resolveReflectType(t *rtype) typeOff { @@ -633,9 +644,17 @@ func resolveReflectText(ptr unsafe.Pointer) textOff { return textOff(addReflectOff(ptr)) } +type nameOff int32 // offset to a name type typeOff int32 // offset to an *rtype type textOff int32 // offset from top of text section +func (t *rtype) nameOff(off nameOff) name { + if off == 0 { + return name{} + } + return name{(*byte)(resolveNameOff(unsafe.Pointer(t), int32(off)))} +} + func (t *rtype) typeOff(off typeOff) *rtype { if off == 0 { return nil @@ -753,10 +772,11 @@ func (t *rtype) Method(i int) (m Method) { panic("reflect: Method index out of range") } p := ut.methods()[i] - m.Name = p.name.name() + pname := t.nameOff(p.name) + m.Name = pname.name() fl := flag(Func) - if !p.name.isExported() { - m.PkgPath = p.name.pkgPath() + if !pname.isExported() { + m.PkgPath = pname.pkgPath() if m.PkgPath == "" { m.PkgPath = ut.pkgPath.name() } @@ -796,7 +816,8 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) { utmethods := ut.methods() for i := 0; i < int(ut.mcount); i++ { p := utmethods[i] - if p.name.name() == name { + pname := t.nameOff(p.name) + if pname.name() == name { return t.Method(i), true } } @@ -1005,14 +1026,15 @@ func (t *interfaceType) Method(i int) (m Method) { return } p := &t.methods[i] - m.Name = p.name.name() - if !p.name.isExported() { - m.PkgPath = p.name.pkgPath() + pname := t.nameOff(p.name) + m.Name = pname.name() + if !pname.isExported() { + m.PkgPath = pname.pkgPath() if m.PkgPath == "" { m.PkgPath = t.pkgPath.name() } } - m.Type = toType(p.typ) + m.Type = toType(t.typeOff(p.typ)) m.Index = i return } @@ -1028,7 +1050,7 @@ func (t *interfaceType) MethodByName(name string) (m Method, ok bool) { var p *imethod for i := range t.methods { p = &t.methods[i] - if p.name.name() == name { + if t.nameOff(p.name).name() == name { return t.Method(i), true } } @@ -1468,7 +1490,7 @@ func implements(T, V *rtype) bool { for j := 0; j < len(v.methods); j++ { tm := &t.methods[i] vm := &v.methods[j] - if vm.name.name() == tm.name.name() && vm.typ == tm.typ { + if V.nameOff(vm.name).name() == t.nameOff(tm.name).name() && V.typeOff(vm.typ) == t.typeOff(tm.typ) { if i++; i >= len(t.methods) { return true } @@ -1486,7 +1508,7 @@ func implements(T, V *rtype) bool { for j := 0; j < int(v.mcount); j++ { tm := &t.methods[i] vm := vmethods[j] - if vm.name.name() == tm.name.name() && V.typeOff(vm.mtyp) == tm.typ { + if V.nameOff(vm.name).name() == t.nameOff(tm.name).name() && V.typeOff(vm.mtyp) == t.typeOff(tm.typ) { if i++; i >= len(t.methods) { return true } @@ -2327,12 +2349,13 @@ func StructOf(fields []StructField) Type { case Interface: ift := (*interfaceType)(unsafe.Pointer(ft)) for im, m := range ift.methods { - if m.name.pkgPath() != "" { + if ift.nameOff(m.name).pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } var ( + mtyp = ift.typeOff(m.typ) ifield = i imethod = im ifn Value @@ -2340,7 +2363,7 @@ func StructOf(fields []StructField) Type { ) if ft.kind&kindDirectIface != 0 { - tfn = MakeFunc(m.typ, func(in []Value) []Value { + tfn = MakeFunc(mtyp, func(in []Value) []Value { var args []Value var recv = in[0] if len(in) > 1 { @@ -2348,7 +2371,7 @@ func StructOf(fields []StructField) Type { } return recv.Field(ifield).Method(imethod).Call(args) }) - ifn = MakeFunc(m.typ, func(in []Value) []Value { + ifn = MakeFunc(mtyp, func(in []Value) []Value { var args []Value var recv = in[0] if len(in) > 1 { @@ -2357,7 +2380,7 @@ func StructOf(fields []StructField) Type { return recv.Field(ifield).Method(imethod).Call(args) }) } else { - tfn = MakeFunc(m.typ, func(in []Value) []Value { + tfn = MakeFunc(mtyp, func(in []Value) []Value { var args []Value var recv = in[0] if len(in) > 1 { @@ -2365,7 +2388,7 @@ func StructOf(fields []StructField) Type { } return recv.Field(ifield).Method(imethod).Call(args) }) - ifn = MakeFunc(m.typ, func(in []Value) []Value { + ifn = MakeFunc(mtyp, func(in []Value) []Value { var args []Value var recv = Indirect(in[0]) if len(in) > 1 { @@ -2376,8 +2399,8 @@ func StructOf(fields []StructField) Type { } methods = append(methods, method{ - name: m.name, - mtyp: resolveReflectType(m.typ), + name: resolveReflectName(ift.nameOff(m.name)), + mtyp: resolveReflectType(mtyp), ifn: resolveReflectText(unsafe.Pointer(&ifn)), tfn: resolveReflectText(unsafe.Pointer(&tfn)), }) @@ -2386,12 +2409,13 @@ 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() != "" { + mname := ptr.nameOff(m.name) + if mname.pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } methods = append(methods, method{ - name: m.name, + name: resolveReflectName(mname), mtyp: resolveReflectType(ptr.typeOff(m.mtyp)), ifn: resolveReflectText(ptr.textOff(m.ifn)), tfn: resolveReflectText(ptr.textOff(m.tfn)), @@ -2400,12 +2424,13 @@ func StructOf(fields []StructField) Type { } if unt := ptr.elem.uncommon(); unt != nil { for _, m := range unt.methods() { - if m.name.pkgPath() != "" { + mname := ptr.nameOff(m.name) + if mname.pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } methods = append(methods, method{ - name: m.name, + name: resolveReflectName(mname), mtyp: resolveReflectType(ptr.elem.typeOff(m.mtyp)), ifn: resolveReflectText(ptr.elem.textOff(m.ifn)), tfn: resolveReflectText(ptr.elem.textOff(m.tfn)), @@ -2415,12 +2440,13 @@ func StructOf(fields []StructField) Type { default: if unt := ft.uncommon(); unt != nil { for _, m := range unt.methods() { - if m.name.pkgPath() != "" { + mname := ft.nameOff(m.name) + if mname.pkgPath() != "" { // TODO(sbinet) panic("reflect: embedded interface with unexported method(s) not implemented") } methods = append(methods, method{ - name: m.name, + name: resolveReflectName(mname), mtyp: resolveReflectType(ft.typeOff(m.mtyp)), ifn: resolveReflectText(ft.textOff(m.ifn)), tfn: resolveReflectText(ft.textOff(m.tfn)), diff --git a/src/reflect/value.go b/src/reflect/value.go index d4d317436a..e6b846e5d1 100644 --- a/src/reflect/value.go +++ b/src/reflect/value.go @@ -553,7 +553,7 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn panic("reflect: internal error: invalid method index") } m := &tt.methods[i] - if !m.name.isExported() { + if !tt.nameOff(m.name).isExported() { panic("reflect: " + op + " of unexported method") } iface := (*nonEmptyInterface)(v.ptr) @@ -562,7 +562,7 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn } rcvrtype = iface.itab.typ fn = unsafe.Pointer(&iface.itab.fun[i]) - t = m.typ + t = tt.typeOff(m.typ) } else { rcvrtype = v.typ ut := v.typ.uncommon() @@ -570,7 +570,7 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype, t *rtype, fn panic("reflect: internal error: invalid method index") } m := ut.methods()[i] - if !m.name.isExported() { + if !v.typ.nameOff(m.name).isExported() { panic("reflect: " + op + " of unexported method") } ifn := v.typ.textOff(m.ifn) @@ -1684,7 +1684,7 @@ func (v Value) Type() Type { panic("reflect: internal error: invalid method index") } m := &tt.methods[i] - return m.typ + return v.typ.typeOff(m.typ) } // Method on concrete type. ut := v.typ.uncommon() diff --git a/src/runtime/iface.go b/src/runtime/iface.go index 84f0ee8f0c..8f179bac80 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -37,7 +37,8 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab { if canfail { return nil } - panic(&TypeAssertionError{"", typ._string, inter.typ._string, inter.mhdr[0].name.name()}) + name := inter.typ.nameOff(inter.mhdr[0].name) + panic(&TypeAssertionError{"", typ._string, inter.typ._string, name.name()}) } h := itabhash(inter, typ) @@ -98,20 +99,22 @@ func additab(m *itab, locked, canfail bool) { j := 0 for k := 0; k < ni; k++ { i := &inter.mhdr[k] - iname := i.name.name() - itype := i._type - ipkg := i.name.pkgPath() + itype := inter.typ.typeOff(i.ityp) + name := inter.typ.nameOff(i.name) + iname := name.name() + ipkg := name.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() + tname := typ.nameOff(t.name) + if typ.typeOff(t.mtyp) == itype && tname.name() == iname { + pkgPath := tname.pkgPath() if pkgPath == "" { pkgPath = x.pkgpath.name() } - if t.name.isExported() || pkgPath == ipkg { + if tname.isExported() || pkgPath == ipkg { if m != nil { ifn := typ.textOff(t.ifn) *(*unsafe.Pointer)(add(unsafe.Pointer(&m.fun[0]), uintptr(k)*sys.PtrSize)) = ifn diff --git a/src/runtime/runtime1.go b/src/runtime/runtime1.go index 02aeedaf75..9089383904 100644 --- a/src/runtime/runtime1.go +++ b/src/runtime/runtime1.go @@ -487,6 +487,12 @@ func reflect_typelinks() ([]unsafe.Pointer, [][]int32) { return sections, ret } +// reflect_resolveNameOff resolves a name offset from a base pointer. +//go:linkname reflect_resolveNameOff reflect.resolveNameOff +func reflect_resolveNameOff(ptrInModule unsafe.Pointer, off int32) unsafe.Pointer { + return unsafe.Pointer(resolveNameOff(ptrInModule, nameOff(off)).bytes) +} + // reflect_resolveTypeOff resolves an *rtype offset from a base type. //go:linkname reflect_resolveTypeOff reflect.resolveTypeOff func reflect_resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer { diff --git a/src/runtime/type.go b/src/runtime/type.go index 711753bab5..31f7ff81b8 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -161,11 +161,17 @@ func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name { } } 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)) + lock(&reflectOffs.lock) + res, found := reflectOffs.m[int32(off)] + unlock(&reflectOffs.lock) + if !found { + 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") } - throw("runtime: name offset base pointer out of range") + return name{(*byte)(res)} } res := md.types + uintptr(off) if res > md.etypes { @@ -175,6 +181,10 @@ func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name { return name{(*byte)(unsafe.Pointer(res))} } +func (t *_type) nameOff(off nameOff) name { + return resolveNameOff(unsafe.Pointer(t), off) +} + func (t *_type) typeOff(off typeOff) *_type { if off == 0 { return nil @@ -269,7 +279,7 @@ type typeOff int32 type textOff int32 type method struct { - name name + name nameOff mtyp typeOff ifn textOff tfn textOff @@ -282,8 +292,8 @@ type uncommontype struct { } type imethod struct { - name name - _type *_type + name nameOff + ityp typeOff } type interfacetype struct { @@ -354,19 +364,19 @@ type name struct { bytes *byte } -func (n *name) data(off int) *byte { +func (n name) data(off int) *byte { return (*byte)(add(unsafe.Pointer(n.bytes), uintptr(off))) } -func (n *name) isExported() bool { +func (n name) isExported() bool { return (*n.bytes)&(1<<0) != 0 } -func (n *name) nameLen() int { +func (n name) nameLen() int { return int(uint16(*n.data(1))<<8 | uint16(*n.data(2))) } -func (n *name) tagLen() int { +func (n name) tagLen() int { if *n.data(0)&(1<<1) == 0 { return 0 } @@ -374,7 +384,7 @@ func (n *name) tagLen() int { return int(uint16(*n.data(off))<<8 | uint16(*n.data(off + 1))) } -func (n *name) name() (s string) { +func (n name) name() (s string) { if n.bytes == nil { return "" } @@ -388,7 +398,7 @@ func (n *name) name() (s string) { return s } -func (n *name) tag() (s string) { +func (n name) tag() (s string) { tl := n.tagLen() if tl == 0 { return "" @@ -400,7 +410,7 @@ func (n *name) tag() (s string) { return s } -func (n *name) pkgPath() string { +func (n name) pkgPath() string { if n.bytes == nil || *n.data(0)&(1<<2) == 0 { return "" } @@ -545,13 +555,15 @@ func typesEqual(t, v *_type) bool { for i := range it.mhdr { tm := &it.mhdr[i] vm := &iv.mhdr[i] - if tm.name.name() != vm.name.name() { + tname := it.typ.nameOff(tm.name) + vname := iv.typ.nameOff(vm.name) + if tname.name() != vname.name() { return false } - if tm.name.pkgPath() != vm.name.pkgPath() { + if tname.pkgPath() != vname.pkgPath() { return false } - if !typesEqual(tm._type, vm._type) { + if !typesEqual(it.typ.typeOff(tm.ityp), iv.typ.typeOff(vm.ityp)) { return false } } -- cgit v1.3 From 1492e7db059ea7903110b0725d5ced3134558e73 Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Thu, 7 Apr 2016 16:29:16 -0400 Subject: cmd/compile, etc: use nameOff for rtype string linux/amd64: cmd/go: -8KB (basically nothing) linux/amd64 PIE: cmd/go: -191KB (1.6%) jujud: -1.5MB (1.9%) Updates #6853 Fixes #15064 Change-Id: I0adbb95685e28be92e8548741df0e11daa0a9b5f Reviewed-on: https://go-review.googlesource.com/21777 Reviewed-by: Ian Lance Taylor --- src/cmd/compile/internal/gc/reflect.go | 58 +++++++++------ src/cmd/link/internal/ld/data.go | 2 +- src/cmd/link/internal/ld/decodesym.go | 35 +++++---- src/reflect/all_test.go | 40 +++++++++- src/reflect/export_test.go | 8 +- src/reflect/type.go | 129 +++++++++++++++++---------------- src/runtime/alg.go | 8 +- src/runtime/error.go | 2 +- src/runtime/heapdump.go | 2 +- src/runtime/iface.go | 24 +++--- src/runtime/mbitmap.go | 10 +-- src/runtime/mfinal.go | 10 +-- src/runtime/mprof.go | 2 +- src/runtime/type.go | 46 ++++++++---- 14 files changed, 231 insertions(+), 145 deletions(-) (limited to 'src/runtime/type.go') diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index ac36f912b6..1643c2ce4b 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -788,14 +788,21 @@ func typeptrdata(t *Type) int64 { } } -// tflag is documented in ../../../../reflect/type.go. -const tflagUncommon = 1 - -// commonType -// ../../../../runtime/type.go:/commonType +// tflag is documented in reflect/type.go. +// +// tflag values must be kept in sync with copies in: +// cmd/compile/internal/gc/reflect.go +// cmd/link/internal/ld/decodesym.go +// reflect/type.go +// runtime/type.go +const ( + tflagUncommon = 1 << 0 + tflagExtraStar = 1 << 1 +) var dcommontype_algarray *Sym +// dcommontype dumps the contents of a reflect.rtype (runtime._type). func dcommontype(s *Sym, ot int, t *Type) int { if ot != 0 { Fatalf("dcommontype %d", ot) @@ -836,7 +843,8 @@ func dcommontype(s *Sym, ot int, t *Type) int { // kind uint8 // alg *typeAlg // gcdata *byte - // string *string + // str nameOff + // _ int32 // } ot = duintptr(s, ot, uint64(t.Width)) ot = duintptr(s, ot, uint64(ptrdata)) @@ -847,6 +855,26 @@ func dcommontype(s *Sym, ot int, t *Type) int { if uncommonSize(t) != 0 { tflag |= tflagUncommon } + + exported := false + p := Tconv(t, FmtLeft|FmtUnsigned) + // If we're writing out type T, + // we are very likely to write out type *T as well. + // Use the string "*T"[1:] for "T", so that the two + // share storage. This is a cheap way to reduce the + // amount of space taken up by reflect strings. + if !strings.HasPrefix(p, "*") { + p = "*" + p + tflag |= tflagExtraStar + if t.Sym != nil { + exported = exportname(t.Sym.Name) + } + } else { + if t.Elem() != nil && t.Elem().Sym != nil { + exported = exportname(t.Elem().Sym.Name) + } + } + ot = duint8(s, ot, tflag) // runtime (and common sense) expects alignment to be a power of two. @@ -882,21 +910,9 @@ func dcommontype(s *Sym, ot int, t *Type) int { } ot = dsymptr(s, ot, gcsym, 0) // gcdata - p := Tconv(t, FmtLeft|FmtUnsigned) - - // If we're writing out type T, - // we are very likely to write out type *T as well. - // Use the string "*T"[1:] for "T", so that the two - // share storage. This is a cheap way to reduce the - // amount of space taken up by reflect strings. - prefix := 0 - if !strings.HasPrefix(p, "*") { - p = "*" + p - prefix = 1 - } - _, symdata := stringsym(p) // string - ot = dsymptrLSym(Linksym(s), ot, symdata, prefix) - ot = duintxx(s, ot, uint64(len(p)-prefix), Widthint) + nsym := dname(p, "", nil, exported) + ot = dsymptrOffLSym(Linksym(s), ot, nsym, 0) + ot = duint32(s, ot, 0) return ot } diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 63caf9cf79..dbd5ad0b75 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -1832,7 +1832,7 @@ func dodataSect(symn int, syms []*LSym) (result []*LSym, maxAlign int32) { case obj.STYPELINK: // Sort typelinks by the rtype.string field so the reflect // package can binary search type links. - symsSort[i].name = string(decodetype_string(s.R[0].Sym)) + symsSort[i].name = string(decodetype_str(s.R[0].Sym)) } } diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index b1c55cf787..330aa6dc13 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -16,6 +16,18 @@ import ( // ../../runtime/type.go, or more specifically, with what // ../gc/reflect.c stuffs in these. +// tflag is documented in reflect/type.go. +// +// tflag values must be kept in sync with copies in: +// cmd/compile/internal/gc/reflect.go +// cmd/link/internal/ld/decodesym.go +// reflect/type.go +// runtime/type.go +const ( + tflagUncommon = 1 << 0 + tflagExtraStar = 1 << 1 +) + func decode_reloc(s *LSym, off int32) *Reloc { for i := range s.R { if s.R[i].Off == off { @@ -47,9 +59,9 @@ func decode_inuxi(p []byte, sz int) uint64 { } } -func commonsize() int { return 6*SysArch.PtrSize + 8 } // runtime._type -func structfieldSize() int { return 3 * SysArch.PtrSize } // runtime.structfield -func uncommonSize() int { return 2 * SysArch.PtrSize } // runtime.uncommontype +func commonsize() int { return 4*SysArch.PtrSize + 8 + 8 } // runtime._type +func structfieldSize() int { return 3 * SysArch.PtrSize } // runtime.structfield +func uncommonSize() int { return 2 * SysArch.PtrSize } // runtime.uncommontype // Type.commonType.kind func decodetype_kind(s *LSym) uint8 { @@ -73,7 +85,6 @@ func decodetype_ptrdata(s *LSym) int64 { // Type.commonType.tflag func decodetype_hasUncommon(s *LSym) bool { - const tflagUncommon = 1 // see ../../../../reflect/type.go:/^type.tflag return s.P[2*SysArch.PtrSize+4]&tflagUncommon != 0 } @@ -211,16 +222,13 @@ func decodetype_structfieldarrayoff(s *LSym, i int) int { return off } -// decodetype_string returns the contents of an rtype's string field. -func decodetype_string(s *LSym) []byte { - off := 4*SysArch.PtrSize + 8 - strlen := int64(decode_inuxi(s.P[off+SysArch.PtrSize:], SysArch.IntSize)) - - r := decode_reloc(s, int32(off)) - if r == nil { - return nil +// decodetype_str returns the contents of an rtype's str field (a nameOff). +func decodetype_str(s *LSym) string { + str := decodetype_name(s, 4*SysArch.PtrSize+8) + if s.P[2*SysArch.PtrSize+4]&tflagExtraStar != 0 { + return str[1:] } - return r.Sym.P[r.Add : r.Add+strlen] + return str } // decodetype_name decodes the name from a reflect.name. @@ -233,7 +241,6 @@ func decodetype_name(s *LSym, off int) string { data := r.Sym.P namelen := int(uint16(data[1]<<8) | uint16(data[2])) return string(data[3 : 3+namelen]) - } func decodetype_structfieldname(s *LSym, i int) string { diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 4dfae2743d..e88bc880e2 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -4175,12 +4175,12 @@ func TestStructOfExportRules(t *testing.T) { }, { field: StructField{Name: "", Type: TypeOf(ΦType{})}, - mustPanic: true, // TODO(sbinet): creating a struct with UTF-8 fields not supported + mustPanic: false, exported: true, }, { field: StructField{Name: "", Type: TypeOf(φType{})}, - mustPanic: true, // TODO(sbinet): creating a struct with UTF-8 fields not supported + mustPanic: false, exported: false, }, { @@ -5674,6 +5674,42 @@ func TestNames(t *testing.T) { } } +func TestExported(t *testing.T) { + type ΦExported struct{} + type φUnexported struct{} + type BigP *big + type P int + type p *P + type P2 p + type p3 p + + type exportTest struct { + v interface{} + want bool + } + exportTests := []exportTest{ + {D1{}, true}, + {(*D1)(nil), true}, + {big{}, false}, + {(*big)(nil), false}, + {(BigP)(nil), true}, + {(*BigP)(nil), true}, + {ΦExported{}, true}, + {φUnexported{}, false}, + {P(0), true}, + {(p)(nil), false}, + {(P2)(nil), true}, + {(p3)(nil), false}, + } + + for i, test := range exportTests { + typ := TypeOf(test.v) + if got := IsExported(typ); got != test.want { + t.Errorf("%d: %s exported=%v, want %v", i, typ.Name(), got, test.want) + } + } +} + type embed struct { EmbedWithUnexpMeth } diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go index f527434f0d..00189f3353 100644 --- a/src/reflect/export_test.go +++ b/src/reflect/export_test.go @@ -51,7 +51,7 @@ func TypeLinks() []string { rodata := sections[i] for _, off := range offs { typ := (*rtype)(resolveTypeOff(unsafe.Pointer(rodata), off)) - r = append(r, typ.string) + r = append(r, typ.String()) } } return r @@ -103,3 +103,9 @@ type OtherPkgFields struct { OtherExported int otherUnexported int } + +func IsExported(t Type) bool { + typ := t.(*rtype) + n := typ.nameOff(typ.str) + return n.isExported() +} diff --git a/src/reflect/type.go b/src/reflect/type.go index 0cae69a79c..b1758e6913 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -242,6 +242,11 @@ const ( // tflag is used by an rtype to signal what extra type information is // available in the memory directly following the rtype value. +// +// tflag values must be kept in sync with copies in: +// cmd/compile/internal/gc/reflect.go +// cmd/link/internal/ld/decodesym.go +// runtime/type.go type tflag uint8 const ( @@ -256,7 +261,13 @@ const ( // u uncommonType // } // u := &(*tUncommon)(unsafe.Pointer(t)).u - tflagUncommon tflag = 1 + tflagUncommon tflag = 1 << 0 + + // tflagExtraStar means the name in the str field has an + // extraneous '*' prefix. This is because for most types T in + // a program, the type *T also exists and reusing the str data + // saves binary size. + tflagExtraStar tflag = 1 << 1 ) // rtype is the common implementation of most values. @@ -273,7 +284,8 @@ type rtype struct { kind uint8 // enumeration for C alg *typeAlg // algorithm table gcdata *byte // garbage collection data - string string // string form; unnecessary but undeniably useful + str nameOff // string form + _ int32 // unused; keeps rtype always a multiple of ptrSize } // a copy of runtime.typeAlg @@ -420,6 +432,9 @@ type structType struct { // 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. +// +// If a name starts with "*", then the exported bit represents +// whether the pointed to type is exported. type name struct { bytes *byte } @@ -724,7 +739,13 @@ func (t *rtype) uncommon() *uncommonType { } } -func (t *rtype) String() string { return t.string } +func (t *rtype) String() string { + s := t.nameOff(t.str).name() + if t.tflag&tflagExtraStar != 0 { + return s[1:] + } + return s +} func (t *rtype) Size() uintptr { return t.size } @@ -833,33 +854,34 @@ func hasPrefix(s, prefix string) bool { } func (t *rtype) Name() string { - if hasPrefix(t.string, "map[") { + s := t.String() + if hasPrefix(s, "map[") { return "" } - if hasPrefix(t.string, "struct {") { + if hasPrefix(s, "struct {") { return "" } - if hasPrefix(t.string, "chan ") { + if hasPrefix(s, "chan ") { return "" } - if hasPrefix(t.string, "chan<-") { + if hasPrefix(s, "chan<-") { return "" } - if hasPrefix(t.string, "func(") { + if hasPrefix(s, "func(") { return "" } - switch t.string[0] { + switch s[0] { case '[', '*', '<': return "" } - i := len(t.string) - 1 + i := len(s) - 1 for i >= 0 { - if t.string[i] == '.' { + if s[i] == '.' { break } i-- } - return t.string[i+1:] + return s[i+1:] } func (t *rtype) ChanDir() ChanDir { @@ -1391,7 +1413,7 @@ func (t *rtype) ptrTo() *rtype { } // Look in known types. - s := "*" + t.string + s := "*" + t.String() for _, tt := range typesByString(s) { p = (*ptrType)(unsafe.Pointer(tt)) if p.elem == t { @@ -1408,7 +1430,7 @@ func (t *rtype) ptrTo() *rtype { prototype := *(**ptrType)(unsafe.Pointer(&iptr)) *p = *prototype - p.string = s + p.str = resolveReflectName(newName(s, "", "", false)) // For the type structures linked into the binary, the // compiler provides a good hash of the string. @@ -1645,7 +1667,7 @@ func haveIdenticalUnderlyingType(T, V *rtype) bool { // // and // -// t1.string < t2.string +// t1.String() < t2.String() // // Note that strings are not unique identifiers for types: // there can be more than one with a given string. @@ -1669,12 +1691,12 @@ func typesByString(s string) []*rtype { section := sections[offsI] // We are looking for the first index i where the string becomes >= s. - // This is a copy of sort.Search, with f(h) replaced by (*typ[h].string >= s). + // This is a copy of sort.Search, with f(h) replaced by (*typ[h].String() >= s). i, j := 0, len(offs) for i < j { h := i + (j-i)/2 // avoid overflow when computing h // i ≤ h < j - if !(rtypeOff(section, offs[h]).string >= s) { + if !(rtypeOff(section, offs[h]).String() >= s) { i = h + 1 // preserves f(i-1) == false } else { j = h // preserves f(j) == true @@ -1687,7 +1709,7 @@ func typesByString(s string) []*rtype { // to do a linear scan anyway. for j := i; j < len(offs); j++ { typ := rtypeOff(section, offs[j]) - if typ.string != s { + if typ.String() != s { break } ret = append(ret, typ) @@ -1783,11 +1805,11 @@ func ChanOf(dir ChanDir, t Type) Type { lookupCache.Unlock() panic("reflect.ChanOf: invalid dir") case SendDir: - s = "chan<- " + typ.string + s = "chan<- " + typ.String() case RecvDir: - s = "<-chan " + typ.string + s = "<-chan " + typ.String() case BothDir: - s = "chan " + typ.string + s = "chan " + typ.String() } for _, tt := range typesByString(s) { ch := (*chanType)(unsafe.Pointer(tt)) @@ -1802,7 +1824,7 @@ func ChanOf(dir ChanDir, t Type) Type { ch := new(chanType) *ch = *prototype ch.dir = uintptr(dir) - ch.string = s + ch.str = resolveReflectName(newName(s, "", "", false)) ch.hash = fnv1(typ.hash, 'c', byte(dir)) ch.elem = typ @@ -1832,7 +1854,7 @@ func MapOf(key, elem Type) Type { } // Look in known types. - s := "map[" + ktyp.string + "]" + etyp.string + s := "map[" + ktyp.String() + "]" + etyp.String() for _, tt := range typesByString(s) { mt := (*mapType)(unsafe.Pointer(tt)) if mt.key == ktyp && mt.elem == etyp { @@ -1844,7 +1866,7 @@ func MapOf(key, elem Type) Type { var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil) mt := new(mapType) *mt = **(**mapType)(unsafe.Pointer(&imap)) - mt.string = s + mt.str = resolveReflectName(newName(s, "", "", false)) mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash)) mt.key = ktyp mt.elem = etyp @@ -2002,7 +2024,7 @@ func FuncOf(in, out []Type, variadic bool) Type { } // Populate the remaining fields of ft and store in cache. - ft.string = str + ft.str = resolveReflectName(newName(str, "", "", false)) funcLookupCache.m[hash] = append(funcLookupCache.m[hash], &ft.rtype) return &ft.rtype @@ -2018,9 +2040,9 @@ func funcStr(ft *funcType) string { } if ft.IsVariadic() && i == int(ft.inCount)-1 { repr = append(repr, "..."...) - repr = append(repr, (*sliceType)(unsafe.Pointer(t)).elem.string...) + repr = append(repr, (*sliceType)(unsafe.Pointer(t)).elem.String()...) } else { - repr = append(repr, t.string...) + repr = append(repr, t.String()...) } } repr = append(repr, ')') @@ -2034,7 +2056,7 @@ func funcStr(ft *funcType) string { if i > 0 { repr = append(repr, ", "...) } - repr = append(repr, t.string...) + repr = append(repr, t.String()...) } if len(out) > 1 { repr = append(repr, ')') @@ -2199,8 +2221,8 @@ func bucketOf(ktyp, etyp *rtype) *rtype { b.ptrdata = ptrdata b.kind = kind b.gcdata = gcdata - s := "bucket(" + ktyp.string + "," + etyp.string + ")" - b.string = s + s := "bucket(" + ktyp.String() + "," + etyp.String() + ")" + b.str = resolveReflectName(newName(s, "", "", false)) return b } @@ -2216,7 +2238,7 @@ func SliceOf(t Type) Type { } // Look in known types. - s := "[]" + typ.string + s := "[]" + typ.String() for _, tt := range typesByString(s) { slice := (*sliceType)(unsafe.Pointer(tt)) if slice.elem == typ { @@ -2229,7 +2251,7 @@ func SliceOf(t Type) Type { prototype := *(**sliceType)(unsafe.Pointer(&islice)) slice := new(sliceType) *slice = *prototype - slice.string = s + slice.str = resolveReflectName(newName(s, "", "", false)) slice.hash = fnv1(typ.hash, '[') slice.elem = typ @@ -2337,11 +2359,11 @@ func StructOf(fields []StructField) Type { // Embedded ** and *interface{} are illegal elem := ft.Elem() if k := elem.Kind(); k == Ptr || k == Interface { - panic("reflect.StructOf: illegal anonymous field type " + ft.string) + panic("reflect.StructOf: illegal anonymous field type " + ft.String()) } name = elem.String() } else { - name = ft.string + name = ft.String() } // TODO(sbinet) check for syntactically impossible type names? @@ -2463,7 +2485,7 @@ func StructOf(fields []StructField) Type { hash = fnv1(hash, byte(ft.hash>>24), byte(ft.hash>>16), byte(ft.hash>>8), byte(ft.hash)) - repr = append(repr, (" " + ft.string)...) + repr = append(repr, (" " + ft.String())...) if f.name.tagLen() > 0 { hash = fnv1(hash, []byte(f.name.tag())...) repr = append(repr, (" " + strconv.Quote(f.name.tag()))...) @@ -2579,7 +2601,7 @@ func StructOf(fields []StructField) Type { } } - typ.string = str + typ.str = resolveReflectName(newName(str, "", "", false)) typ.hash = hash typ.size = size typ.align = typalign @@ -2691,11 +2713,11 @@ func StructOf(fields []StructField) Type { func runtimeStructField(field StructField) structField { exported := field.PkgPath == "" if field.Name == "" { - t := field.Type + t := field.Type.(*rtype) if t.Kind() == Ptr { - t = t.Elem() + t = t.Elem().(*rtype) } - exported = isExported(t.Name()) + exported = t.nameOff(t.str).isExported() } else if exported { b0 := field.Name[0] if ('a' <= b0 && b0 <= 'z') || b0 == '_' { @@ -2711,25 +2733,6 @@ func runtimeStructField(field StructField) structField { } } -func isExported(s string) bool { - if s == "" { - return false - } - // FIXME(sbinet): handle utf8/runes (see https://golang.org/issue/15064) - // TODO: turn rtype.string into a reflect.name type, and put the exported - // bit on there which can be checked here with field.Type.(*rtype).string.isExported() - // When done, remove the documented limitation of StructOf. - r := s[0] - switch { - case 'A' <= r && r <= 'Z': - return true - case r == '_' || 'a' <= r && r <= 'z': - return false - default: - panic("reflect.StructOf: creating a struct with UTF-8 fields is not supported yet") - } -} - // typeptrdata returns the length in bytes of the prefix of t // containing pointer data. Anything after this offset is scalar data. // keep in sync with ../cmd/compile/internal/gc/reflect.go @@ -2779,7 +2782,7 @@ func ArrayOf(count int, elem Type) Type { } // Look in known types. - s := "[" + strconv.Itoa(count) + "]" + typ.string + s := "[" + strconv.Itoa(count) + "]" + typ.String() for _, tt := range typesByString(s) { array := (*arrayType)(unsafe.Pointer(tt)) if array.elem == typ { @@ -2792,7 +2795,7 @@ func ArrayOf(count int, elem Type) Type { prototype := *(**arrayType)(unsafe.Pointer(&iarray)) array := new(arrayType) *array = *prototype - array.string = s + array.str = resolveReflectName(newName(s, "", "", false)) array.hash = fnv1(typ.hash, '[') for n := uint32(count); n > 0; n >>= 8 { array.hash = fnv1(array.hash, byte(n)) @@ -3046,11 +3049,11 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin var s string if rcvr != nil { - s = "methodargs(" + rcvr.string + ")(" + t.string + ")" + s = "methodargs(" + rcvr.String() + ")(" + t.String() + ")" } else { - s = "funcargs(" + t.string + ")" + s = "funcargs(" + t.String() + ")" } - x.string = s + x.str = resolveReflectName(newName(s, "", "", false)) // cache result for future callers if layoutCache.m == nil { diff --git a/src/runtime/alg.go b/src/runtime/alg.go index 7aacc8cf9b..66943495b5 100644 --- a/src/runtime/alg.go +++ b/src/runtime/alg.go @@ -146,7 +146,7 @@ func interhash(p unsafe.Pointer, h uintptr) uintptr { t := tab._type fn := t.alg.hash if fn == nil { - panic(errorString("hash of unhashable type " + t._string)) + panic(errorString("hash of unhashable type " + t.string())) } if isDirectIface(t) { return c1 * fn(unsafe.Pointer(&a.data), h^c0) @@ -163,7 +163,7 @@ func nilinterhash(p unsafe.Pointer, h uintptr) uintptr { } fn := t.alg.hash if fn == nil { - panic(errorString("hash of unhashable type " + t._string)) + panic(errorString("hash of unhashable type " + t.string())) } if isDirectIface(t) { return c1 * fn(unsafe.Pointer(&a.data), h^c0) @@ -221,7 +221,7 @@ func efaceeq(x, y eface) bool { } eq := t.alg.equal if eq == nil { - panic(errorString("comparing uncomparable type " + t._string)) + panic(errorString("comparing uncomparable type " + t.string())) } if isDirectIface(t) { return eq(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data))) @@ -239,7 +239,7 @@ func ifaceeq(x, y iface) bool { t := xtab._type eq := t.alg.equal if eq == nil { - panic(errorString("comparing uncomparable type " + t._string)) + panic(errorString("comparing uncomparable type " + t.string())) } if isDirectIface(t) { return eq(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data))) diff --git a/src/runtime/error.go b/src/runtime/error.go index 15f6bdf014..0238c5e592 100644 --- a/src/runtime/error.go +++ b/src/runtime/error.go @@ -67,7 +67,7 @@ type stringer interface { func typestring(x interface{}) string { e := efaceOf(&x) - return e._type._string + return e._type.string() } // For calling from C. diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go index adfd660847..1db29d7cb4 100644 --- a/src/runtime/heapdump.go +++ b/src/runtime/heapdump.go @@ -184,7 +184,7 @@ func dumptype(t *_type) { dumpint(uint64(uintptr(unsafe.Pointer(t)))) dumpint(uint64(t.size)) if x := t.uncommon(); x == nil || x.pkgpath.name() == "" { - dumpstr(t._string) + dumpstr(t.string()) } else { pkgpathstr := x.pkgpath.name() pkgpath := stringStructOf(&pkgpathstr) diff --git a/src/runtime/iface.go b/src/runtime/iface.go index 352ff77465..007c1ed174 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -38,7 +38,7 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab { return nil } name := inter.typ.nameOff(inter.mhdr[0].name) - panic(&TypeAssertionError{"", typ._string, inter.typ._string, name.name()}) + panic(&TypeAssertionError{"", typ.string(), inter.typ.string(), name.name()}) } h := itabhash(inter, typ) @@ -128,7 +128,7 @@ func additab(m *itab, locked, canfail bool) { if locked { unlock(&ifaceLock) } - panic(&TypeAssertionError{"", typ._string, inter.typ._string, iname}) + panic(&TypeAssertionError{"", typ.string(), inter.typ.string(), iname}) } m.bad = 1 break @@ -196,18 +196,18 @@ func convT2I(tab *itab, elem unsafe.Pointer, x unsafe.Pointer) (i iface) { func panicdottype(have, want, iface *_type) { haveString := "" if have != nil { - haveString = have._string + haveString = have.string() } - panic(&TypeAssertionError{iface._string, haveString, want._string, ""}) + panic(&TypeAssertionError{iface.string(), haveString, want.string(), ""}) } func assertI2T(t *_type, i iface, r unsafe.Pointer) { tab := i.tab if tab == nil { - panic(&TypeAssertionError{"", "", t._string, ""}) + panic(&TypeAssertionError{"", "", t.string(), ""}) } if tab._type != t { - panic(&TypeAssertionError{tab.inter.typ._string, tab._type._string, t._string, ""}) + panic(&TypeAssertionError{tab.inter.typ.string(), tab._type.string(), t.string(), ""}) } if r != nil { if isDirectIface(t) { @@ -238,10 +238,10 @@ func assertI2T2(t *_type, i iface, r unsafe.Pointer) bool { func assertE2T(t *_type, e eface, r unsafe.Pointer) { if e._type == nil { - panic(&TypeAssertionError{"", "", t._string, ""}) + panic(&TypeAssertionError{"", "", t.string(), ""}) } if e._type != t { - panic(&TypeAssertionError{"", e._type._string, t._string, ""}) + panic(&TypeAssertionError{"", e._type.string(), t.string(), ""}) } if r != nil { if isDirectIface(t) { @@ -285,7 +285,7 @@ func assertI2E(inter *interfacetype, i iface, r *eface) { tab := i.tab if tab == nil { // explicit conversions require non-nil interface value. - panic(&TypeAssertionError{"", "", inter.typ._string, ""}) + panic(&TypeAssertionError{"", "", inter.typ.string(), ""}) } r._type = tab._type r.data = i.data @@ -322,7 +322,7 @@ func assertI2I(inter *interfacetype, i iface, r *iface) { tab := i.tab if tab == nil { // explicit conversions require non-nil interface value. - panic(&TypeAssertionError{"", "", inter.typ._string, ""}) + panic(&TypeAssertionError{"", "", inter.typ.string(), ""}) } if tab.inter == inter { r.tab = tab @@ -361,7 +361,7 @@ func assertE2I(inter *interfacetype, e eface, r *iface) { t := e._type if t == nil { // explicit conversions require non-nil interface value. - panic(&TypeAssertionError{"", "", inter.typ._string, ""}) + panic(&TypeAssertionError{"", "", inter.typ.string(), ""}) } r.tab = getitab(inter, t, false) r.data = e.data @@ -402,7 +402,7 @@ func reflect_ifaceE2I(inter *interfacetype, e eface, dst *iface) { func assertE2E(inter *interfacetype, e eface, r *eface) { if e._type == nil { // explicit conversions require non-nil interface value. - panic(&TypeAssertionError{"", "", inter.typ._string, ""}) + panic(&TypeAssertionError{"", "", inter.typ.string(), ""}) } *r = e } diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index 685c29066b..f025ce1c68 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -461,11 +461,11 @@ func typeBitsBulkBarrier(typ *_type, p, size uintptr) { throw("runtime: typeBitsBulkBarrier without type") } if typ.size != size { - println("runtime: typeBitsBulkBarrier with type ", typ._string, " of size ", typ.size, " but memory size", size) + println("runtime: typeBitsBulkBarrier with type ", typ.string(), " of size ", typ.size, " but memory size", size) throw("runtime: invalid typeBitsBulkBarrier") } if typ.kind&kindGCProg != 0 { - println("runtime: typeBitsBulkBarrier with type ", typ._string, " with GC prog") + println("runtime: typeBitsBulkBarrier with type ", typ.string(), " with GC prog") throw("runtime: invalid typeBitsBulkBarrier") } if !writeBarrier.needed { @@ -916,7 +916,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { } if nw == 0 { // No pointers! Caller was supposed to check. - println("runtime: invalid type ", typ._string) + println("runtime: invalid type ", typ.string()) throw("heapBitsSetType: called with non-pointer type") return } @@ -1100,7 +1100,7 @@ Phase4: if doubleCheck { end := heapBitsForAddr(x + size) if typ.kind&kindGCProg == 0 && (hbitp != end.bitp || (w == nw+2) != (end.shift == 2)) { - println("ended at wrong bitmap byte for", typ._string, "x", dataSize/typ.size) + println("ended at wrong bitmap byte for", typ.string(), "x", dataSize/typ.size) print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n") print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n") h0 := heapBitsForAddr(x) @@ -1136,7 +1136,7 @@ Phase4: } } if have != want { - println("mismatch writing bits for", typ._string, "x", dataSize/typ.size) + println("mismatch writing bits for", typ.string(), "x", dataSize/typ.size) print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n") print("kindGCProg=", typ.kind&kindGCProg != 0, "\n") print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n") diff --git a/src/runtime/mfinal.go b/src/runtime/mfinal.go index f698e72709..e81650d842 100644 --- a/src/runtime/mfinal.go +++ b/src/runtime/mfinal.go @@ -274,7 +274,7 @@ func SetFinalizer(obj interface{}, finalizer interface{}) { throw("runtime.SetFinalizer: first argument is nil") } if etyp.kind&kindMask != kindPtr { - throw("runtime.SetFinalizer: first argument is " + etyp._string + ", not pointer") + throw("runtime.SetFinalizer: first argument is " + etyp.string() + ", not pointer") } ot := (*ptrtype)(unsafe.Pointer(etyp)) if ot.elem == nil { @@ -328,14 +328,14 @@ func SetFinalizer(obj interface{}, finalizer interface{}) { } if ftyp.kind&kindMask != kindFunc { - throw("runtime.SetFinalizer: second argument is " + ftyp._string + ", not a function") + throw("runtime.SetFinalizer: second argument is " + ftyp.string() + ", not a function") } ft := (*functype)(unsafe.Pointer(ftyp)) if ft.dotdotdot() { - throw("runtime.SetFinalizer: cannot pass " + etyp._string + " to finalizer " + ftyp._string + " because dotdotdot") + throw("runtime.SetFinalizer: cannot pass " + etyp.string() + " to finalizer " + ftyp.string() + " because dotdotdot") } if ft.dotdotdot() || ft.inCount != 1 { - throw("runtime.SetFinalizer: cannot pass " + etyp._string + " to finalizer " + ftyp._string) + throw("runtime.SetFinalizer: cannot pass " + etyp.string() + " to finalizer " + ftyp.string()) } fint := ft.in()[0] switch { @@ -358,7 +358,7 @@ func SetFinalizer(obj interface{}, finalizer interface{}) { goto okarg } } - throw("runtime.SetFinalizer: cannot pass " + etyp._string + " to finalizer " + ftyp._string) + throw("runtime.SetFinalizer: cannot pass " + etyp.string() + " to finalizer " + ftyp.string()) okarg: // compute size needed for return parameters nret := uintptr(0) diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go index f3b9b4bc78..c3e4e2cb87 100644 --- a/src/runtime/mprof.go +++ b/src/runtime/mprof.go @@ -624,7 +624,7 @@ func tracealloc(p unsafe.Pointer, size uintptr, typ *_type) { if typ == nil { print("tracealloc(", p, ", ", hex(size), ")\n") } else { - print("tracealloc(", p, ", ", hex(size), ", ", typ._string, ")\n") + print("tracealloc(", p, ", ", hex(size), ", ", typ.string(), ")\n") } if gp.m.curg == nil || gp == gp.m.curg { goroutineheader(gp) diff --git a/src/runtime/type.go b/src/runtime/type.go index 31f7ff81b8..0b28fa6d43 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -8,10 +8,18 @@ package runtime import "unsafe" -// tflag is documented in ../reflect/type.go. +// tflag is documented in reflect/type.go. +// +// tflag values must be kept in sync with copies in: +// cmd/compile/internal/gc/reflect.go +// cmd/link/internal/ld/decodesym.go +// reflect/type.go type tflag uint8 -const tflagUncommon tflag = 1 +const ( + tflagUncommon tflag = 1 << 0 + tflagExtraStar tflag = 1 << 1 +) // Needs to be in sync with ../cmd/compile/internal/ld/decodesym.go:/^func.commonsize, // ../cmd/compile/internal/gc/reflect.go:/^func.dcommontype and @@ -28,8 +36,17 @@ type _type struct { // gcdata stores the GC type data for the garbage collector. // If the KindGCProg bit is set in kind, gcdata is a GC program. // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. - gcdata *byte - _string string + gcdata *byte + str nameOff + _ int32 +} + +func (t *_type) string() string { + s := t.nameOff(t.str).name() + if t.tflag&tflagExtraStar != 0 { + return s[1:] + } + return s } func (t *_type) uncommon() *uncommontype { @@ -99,33 +116,34 @@ func hasPrefix(s, prefix string) bool { } func (t *_type) name() string { - if hasPrefix(t._string, "map[") { + s := t.string() + if hasPrefix(s, "map[") { return "" } - if hasPrefix(t._string, "struct {") { + if hasPrefix(s, "struct {") { return "" } - if hasPrefix(t._string, "chan ") { + if hasPrefix(s, "chan ") { return "" } - if hasPrefix(t._string, "chan<-") { + if hasPrefix(s, "chan<-") { return "" } - if hasPrefix(t._string, "func(") { + if hasPrefix(s, "func(") { return "" } - switch t._string[0] { + switch s[0] { case '[', '*', '<': return "" } - i := len(t._string) - 1 + i := len(s) - 1 for i >= 0 { - if t._string[i] == '.' { + if s[i] == '.' { break } i-- } - return t._string[i+1:] + return s[i+1:] } // reflectOffs holds type offsets defined at run time by the reflect package. @@ -497,7 +515,7 @@ func typesEqual(t, v *_type) bool { if kind != v.kind&kindMask { return false } - if t._string != v._string { + if t.string() != v.string() { return false } ut := t.uncommon() -- cgit v1.3 From c165988360457553ccbfa4a09919de3262a4438a Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Thu, 7 Apr 2016 21:37:45 -0400 Subject: cmd/compile, etc: use nameOff in uncommonType linux/amd64 PIE: cmd/go: -62KB (0.5%) jujud: -550KB (0.7%) For #6853. Change-Id: Ieb67982abce5832e24b997506f0ae7108f747108 Reviewed-on: https://go-review.googlesource.com/22371 Run-TryBot: David Crawshaw TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/cmd/compile/internal/gc/reflect.go | 15 ++++++--------- src/cmd/link/internal/ld/decodesym.go | 6 +++--- src/cmd/link/internal/ld/symtab.go | 5 +++++ src/reflect/type.go | 21 +++++++++------------ src/runtime/heapdump.go | 4 ++-- src/runtime/iface.go | 2 +- src/runtime/type.go | 6 ++++-- 7 files changed, 30 insertions(+), 29 deletions(-) (limited to 'src/runtime/type.go') diff --git a/src/cmd/compile/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go index 1643c2ce4b..3cd769fd2d 100644 --- a/src/cmd/compile/internal/gc/reflect.go +++ b/src/cmd/compile/internal/gc/reflect.go @@ -75,7 +75,7 @@ func uncommonSize(t *Type) int { // Sizeof(runtime.uncommontype{}) if t.Sym == nil && len(methods(t)) == 0 { return 0 } - return 2 * Widthptr + return 4 + 2 + 2 } func makefield(name string, t *Type) *Field { @@ -463,6 +463,9 @@ func dgopkgpathLSym(s *obj.LSym, ot int, pkg *Pkg) int { // 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 == nil { + return duintxxLSym(s, ot, 0, 4) + } 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 @@ -597,12 +600,9 @@ func dextratype(s *Sym, ot int, t *Type, dataAdd int) int { dtypesym(a.type_) } - ot = dgopkgpath(s, ot, typePkg(t)) + ot = dgopkgpathOffLSym(Linksym(s), ot, typePkg(t)) - dataAdd += Widthptr + 2 + 2 - if Widthptr == 8 { - dataAdd += 4 - } + dataAdd += 4 + 2 + 2 mcount := len(m) if mcount != int(uint16(mcount)) { Fatalf("too many methods on %s: %d", t, mcount) @@ -613,9 +613,6 @@ func dextratype(s *Sym, ot int, t *Type, dataAdd int) int { ot = duint16(s, ot, uint16(mcount)) ot = duint16(s, ot, uint16(dataAdd)) - if Widthptr == 8 { - ot = duint32(s, ot, 0) // align for following pointers - } return ot } diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index 330aa6dc13..3ec488bbe8 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -61,7 +61,7 @@ func decode_inuxi(p []byte, sz int) uint64 { func commonsize() int { return 4*SysArch.PtrSize + 8 + 8 } // runtime._type func structfieldSize() int { return 3 * SysArch.PtrSize } // runtime.structfield -func uncommonSize() int { return 2 * SysArch.PtrSize } // runtime.uncommontype +func uncommonSize() int { return 4 + 2 + 2 } // runtime.uncommontype // Type.commonType.kind func decodetype_kind(s *LSym) uint8 { @@ -361,8 +361,8 @@ func decodetype_methods(s *LSym) []methodsig { // just Sizeof(rtype) } - mcount := int(decode_inuxi(s.P[off+SysArch.PtrSize:], 2)) - moff := int(decode_inuxi(s.P[off+SysArch.PtrSize+2:], 2)) + mcount := int(decode_inuxi(s.P[off+4:], 2)) + moff := int(decode_inuxi(s.P[off+4+2:], 2)) off += moff // offset to array of reflect.method values const sizeofMethod = 4 * 4 // sizeof reflect.method in program return decode_methodsig(s, off, sizeofMethod, mcount) diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index acc238f698..94a6d0ab29 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -435,6 +435,11 @@ func symtab() { s.Outer = symtype } + case strings.HasPrefix(s.Name, "go.importpath.") && UseRelro(): + // Keep go.importpath symbols in the same section as types and + // names, as they can be referred to by a section offset. + s.Type = obj.STYPERELRO + case strings.HasPrefix(s.Name, "go.typelink."): ntypelinks++ s.Type = obj.STYPELINK diff --git a/src/reflect/type.go b/src/reflect/type.go index b1758e6913..ff6ff14c83 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -311,9 +311,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 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 + pkgPath nameOff // 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. @@ -613,13 +613,6 @@ func (t *uncommonType) methods() []method { return (*[1 << 16]method)(add(unsafe.Pointer(t), uintptr(t.moff)))[:t.mcount:t.mcount] } -func (t *uncommonType) PkgPath() string { - if t == nil { - return "" - } - return t.pkgPath.name() -} - // resolveNameOff resolves a name offset from a base pointer. // The (*rtype).nameOff method is a convenience wrapper for this function. // Implemented in the runtime package. @@ -799,7 +792,7 @@ func (t *rtype) Method(i int) (m Method) { if !pname.isExported() { m.PkgPath = pname.pkgPath() if m.PkgPath == "" { - m.PkgPath = ut.pkgPath.name() + m.PkgPath = t.nameOff(ut.pkgPath).name() } fl |= flagStickyRO } @@ -846,7 +839,11 @@ func (t *rtype) MethodByName(name string) (m Method, ok bool) { } func (t *rtype) PkgPath() string { - return t.uncommon().PkgPath() + ut := t.uncommon() + if ut == nil { + return "" + } + return t.nameOff(ut.pkgPath).name() } func hasPrefix(s, prefix string) bool { diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go index 1db29d7cb4..0afab09095 100644 --- a/src/runtime/heapdump.go +++ b/src/runtime/heapdump.go @@ -183,10 +183,10 @@ func dumptype(t *_type) { dumpint(tagType) dumpint(uint64(uintptr(unsafe.Pointer(t)))) dumpint(uint64(t.size)) - if x := t.uncommon(); x == nil || x.pkgpath.name() == "" { + if x := t.uncommon(); x == nil || t.nameOff(x.pkgpath).name() == "" { dumpstr(t.string()) } else { - pkgpathstr := x.pkgpath.name() + pkgpathstr := t.nameOff(x.pkgpath).name() pkgpath := stringStructOf(&pkgpathstr) namestr := t.name() name := stringStructOf(&namestr) diff --git a/src/runtime/iface.go b/src/runtime/iface.go index 007c1ed174..b57d1cc63c 100644 --- a/src/runtime/iface.go +++ b/src/runtime/iface.go @@ -112,7 +112,7 @@ func additab(m *itab, locked, canfail bool) { if typ.typeOff(t.mtyp) == itype && tname.name() == iname { pkgPath := tname.pkgPath() if pkgPath == "" { - pkgPath = x.pkgpath.name() + pkgPath = typ.nameOff(x.pkgpath).name() } if tname.isExported() || pkgPath == ipkg { if m != nil { diff --git a/src/runtime/type.go b/src/runtime/type.go index 0b28fa6d43..9e4c40553a 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -304,7 +304,7 @@ type method struct { } type uncommontype struct { - pkgpath name + pkgpath nameOff mcount uint16 // number of methods moff uint16 // offset from this uncommontype to [mcount]method } @@ -524,7 +524,9 @@ func typesEqual(t, v *_type) bool { if ut == nil || uv == nil { return false } - if ut.pkgpath.name() != uv.pkgpath.name() { + pkgpatht := t.nameOff(ut.pkgpath).name() + pkgpathv := v.nameOff(uv.pkgpath).name() + if pkgpatht != pkgpathv { return false } } -- cgit v1.3 From 217be5b35d8fb0f812ca59bf7dec3aa0fb850c46 Mon Sep 17 00:00:00 2001 From: David Crawshaw Date: Wed, 27 Apr 2016 12:49:27 -0400 Subject: reflect: unnamed interface types have no name Fixes #15468 Change-Id: I8723171f87774a98d5e80e7832ebb96dd1fbea74 Reviewed-on: https://go-review.googlesource.com/22524 Reviewed-by: Ian Lance Taylor Run-TryBot: David Crawshaw --- src/reflect/all_test.go | 25 +++++++++++++++---------- src/reflect/type.go | 3 +++ src/runtime/type.go | 3 +++ 3 files changed, 21 insertions(+), 10 deletions(-) (limited to 'src/runtime/type.go') diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index aff8ea253b..870ccbf521 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -5659,20 +5659,25 @@ type nameTest struct { } var nameTests = []nameTest{ - {int32(0), "int32"}, - {D1{}, "D1"}, - {[]D1{}, ""}, - {(chan D1)(nil), ""}, - {(func() D1)(nil), ""}, - {(<-chan D1)(nil), ""}, - {(chan<- D1)(nil), ""}, - {TheNameOfThisTypeIsExactly255BytesLongSoWhenTheCompilerPrependsTheReflectTestPackageNameAndExtraStarTheLinkerRuntimeAndReflectPackagesWillHaveToCorrectlyDecodeTheSecondLengthByte0123456789_0123456789_0123456789_0123456789_0123456789_012345678(0), "TheNameOfThisTypeIsExactly255BytesLongSoWhenTheCompilerPrependsTheReflectTestPackageNameAndExtraStarTheLinkerRuntimeAndReflectPackagesWillHaveToCorrectlyDecodeTheSecondLengthByte0123456789_0123456789_0123456789_0123456789_0123456789_012345678"}, + {(*int32)(nil), "int32"}, + {(*D1)(nil), "D1"}, + {(*[]D1)(nil), ""}, + {(*chan D1)(nil), ""}, + {(*func() D1)(nil), ""}, + {(*<-chan D1)(nil), ""}, + {(*chan<- D1)(nil), ""}, + {(*interface{})(nil), ""}, + {(*interface { + F() + })(nil), ""}, + {(*TheNameOfThisTypeIsExactly255BytesLongSoWhenTheCompilerPrependsTheReflectTestPackageNameAndExtraStarTheLinkerRuntimeAndReflectPackagesWillHaveToCorrectlyDecodeTheSecondLengthByte0123456789_0123456789_0123456789_0123456789_0123456789_012345678)(nil), "TheNameOfThisTypeIsExactly255BytesLongSoWhenTheCompilerPrependsTheReflectTestPackageNameAndExtraStarTheLinkerRuntimeAndReflectPackagesWillHaveToCorrectlyDecodeTheSecondLengthByte0123456789_0123456789_0123456789_0123456789_0123456789_012345678"}, } func TestNames(t *testing.T) { for _, test := range nameTests { - if got := TypeOf(test.v).Name(); got != test.want { - t.Errorf("%T Name()=%q, want %q", test.v, got, test.want) + typ := TypeOf(test.v).Elem() + if got := typ.Name(); got != test.want { + t.Errorf("%v Name()=%q, want %q", typ, got, test.want) } } } diff --git a/src/reflect/type.go b/src/reflect/type.go index ff6ff14c83..0213d56e83 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -867,6 +867,9 @@ func (t *rtype) Name() string { if hasPrefix(s, "func(") { return "" } + if hasPrefix(s, "interface {") { + return "" + } switch s[0] { case '[', '*', '<': return "" diff --git a/src/runtime/type.go b/src/runtime/type.go index 9e4c40553a..608c601abd 100644 --- a/src/runtime/type.go +++ b/src/runtime/type.go @@ -132,6 +132,9 @@ func (t *_type) name() string { if hasPrefix(s, "func(") { return "" } + if hasPrefix(s, "interface {") { + return "" + } switch s[0] { case '[', '*', '<': return "" -- cgit v1.3