1
0
mirror of https://github.com/golang/go synced 2024-11-19 16:54:44 -07:00

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 <iant@golang.org>
This commit is contained in:
David Crawshaw 2016-03-28 21:51:10 -04:00
parent 3c8d6af8e0
commit 95df0c6ab9
9 changed files with 130 additions and 85 deletions

View File

@ -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

View File

@ -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)
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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)),

View File

@ -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()

View File

@ -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

View File

@ -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 {

View File

@ -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
}
}