cmd/compile,runtime,reflect: move embedded bit from offset to name

Previously we stole a bit from the field offset to encode whether
a struct field was embedded.

Instead, encode that bit in the name field, where we already have
some unused bits to play with. The bit associates naturally with
the name in any case.

This leaves a full uintptr to specify field offsets. This will make
the fix for #52740 cleaner.

Change-Id: I0bfb85564dc26e8c18101bc8b432f332176d7836
Reviewed-on: https://go-review.googlesource.com/c/go/+/412138
Reviewed-by: Cherry Mui <cherryyz@google.com>
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
This commit is contained in:
Keith Randall 2022-06-14 13:38:02 -07:00 committed by Keith Randall
parent cb9bf93078
commit e1e66a03a6
13 changed files with 104 additions and 82 deletions

View file

@ -412,7 +412,7 @@ func dimportpath(p *types.Pkg) {
}
s := base.Ctxt.Lookup("type..importpath." + p.Prefix + ".")
ot := dnameData(s, 0, p.Path, "", nil, false)
ot := dnameData(s, 0, p.Path, "", nil, false, false)
objw.Global(s, int32(ot), obj.DUPOK|obj.RODATA)
s.Set(obj.AttrContentAddressable, true)
p.Pathsym = s
@ -461,12 +461,12 @@ func dnameField(lsym *obj.LSym, ot int, spkg *types.Pkg, ft *types.Field) int {
if !types.IsExported(ft.Sym.Name) && ft.Sym.Pkg != spkg {
base.Fatalf("package mismatch for %v", ft.Sym)
}
nsym := dname(ft.Sym.Name, ft.Note, nil, types.IsExported(ft.Sym.Name))
nsym := dname(ft.Sym.Name, ft.Note, nil, types.IsExported(ft.Sym.Name), ft.Embedded != 0)
return objw.SymPtr(lsym, ot, nsym, 0)
}
// dnameData writes the contents of a reflect.name into s at offset ot.
func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported bool) int {
func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported, embedded bool) int {
if len(name) >= 1<<29 {
base.Fatalf("name too long: %d %s...", len(name), name[:1024])
}
@ -491,6 +491,9 @@ func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported b
if pkg != nil {
bits |= 1 << 2
}
if embedded {
bits |= 1 << 3
}
b := make([]byte, l)
b[0] = bits
copy(b[1:], nameLen[:nameLenLen])
@ -513,7 +516,7 @@ func dnameData(s *obj.LSym, ot int, name, tag string, pkg *types.Pkg, exported b
var dnameCount int
// dname creates a reflect.name for a struct field or method.
func dname(name, tag string, pkg *types.Pkg, exported bool) *obj.LSym {
func dname(name, tag string, pkg *types.Pkg, exported, embedded 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
@ -538,11 +541,14 @@ func dname(name, tag string, pkg *types.Pkg, exported bool) *obj.LSym {
sname = fmt.Sprintf(`%s"".%d`, sname, dnameCount)
dnameCount++
}
if embedded {
sname += ".embedded"
}
s := base.Ctxt.Lookup(sname)
if len(s.P) > 0 {
return s
}
ot := dnameData(s, 0, name, tag, pkg, exported)
ot := dnameData(s, 0, name, tag, pkg, exported, embedded)
objw.Global(s, int32(ot), obj.DUPOK|obj.RODATA)
s.Set(obj.AttrContentAddressable, true)
return s
@ -610,7 +616,7 @@ func dextratypeData(lsym *obj.LSym, ot int, t *types.Type) int {
if !exported && a.name.Pkg != typePkg(t) {
pkg = a.name.Pkg
}
nsym := dname(a.name.Name, "", pkg, exported)
nsym := dname(a.name.Name, "", pkg, exported, false)
ot = objw.SymPtrOff(lsym, ot, nsym)
ot = dmethodptrOff(lsym, ot, writeType(a.mtype))
@ -775,7 +781,7 @@ func dcommontype(lsym *obj.LSym, t *types.Type) int {
}
ot = objw.SymPtr(lsym, ot, gcsym, 0) // gcdata
nsym := dname(p, "", nil, exported)
nsym := dname(p, "", nil, exported, false)
ot = objw.SymPtrOff(lsym, ot, nsym) // str
// ptrToThis
if sptr == nil {
@ -1074,7 +1080,7 @@ func writeType(t *types.Type) *obj.LSym {
if !exported && a.name.Pkg != tpkg {
pkg = a.name.Pkg
}
nsym := dname(a.name.Name, "", pkg, exported)
nsym := dname(a.name.Name, "", pkg, exported, false)
ot = objw.SymPtrOff(lsym, ot, nsym)
ot = objw.SymPtrOff(lsym, ot, writeType(a.type_))
@ -1180,14 +1186,7 @@ func writeType(t *types.Type) *obj.LSym {
// ../../../../runtime/type.go:/structField
ot = dnameField(lsym, ot, spkg, f)
ot = objw.SymPtr(lsym, ot, writeType(f.Type), 0)
offsetAnon := uint64(f.Offset) << 1
if offsetAnon>>1 != uint64(f.Offset) {
base.Fatalf("%v: bad field offset for %s", t, f.Sym.Name)
}
if f.Embedded != 0 {
offsetAnon |= 1
}
ot = objw.Uintptr(lsym, ot, offsetAnon)
ot = objw.Uintptr(lsym, ot, uint64(f.Offset))
}
}
@ -1356,7 +1355,7 @@ func WriteTabs() {
// name nameOff
// typ typeOff // pointer to symbol
// }
nsym := dname(p.Sym().Name, "", nil, true)
nsym := dname(p.Sym().Name, "", nil, true, false)
t := p.Type()
if p.Class != ir.PFUNC {
t = types.NewPtr(t)

View file

@ -132,6 +132,15 @@ func decodetypeName(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs
return string(data[1+nameLenLen : 1+nameLenLen+int(nameLen)])
}
func decodetypeNameEmbedded(ldr *loader.Loader, symIdx loader.Sym, relocs *loader.Relocs, off int) bool {
r := decodeRelocSym(ldr, symIdx, relocs, int32(off))
if r == 0 {
return false
}
data := ldr.Data(r)
return data[0]&(1<<3) != 0
}
func decodetypeFuncInType(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs, i int) loader.Sym {
uadd := commonsize(arch) + 4
if arch.PtrSize == 8 {
@ -204,12 +213,18 @@ func decodetypeStructFieldType(ldr *loader.Loader, arch *sys.Arch, symIdx loader
return decodeRelocSym(ldr, symIdx, &relocs, int32(off+arch.PtrSize))
}
func decodetypeStructFieldOffsAnon(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int64 {
func decodetypeStructFieldOffset(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) int64 {
off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i)
data := ldr.Data(symIdx)
return int64(decodeInuxi(arch, data[off+2*arch.PtrSize:], arch.PtrSize))
}
func decodetypeStructFieldEmbedded(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, i int) bool {
off := decodetypeStructFieldArrayOff(ldr, arch, symIdx, i)
relocs := ldr.Relocs(symIdx)
return decodetypeNameEmbedded(ldr, symIdx, &relocs, off)
}
// decodetypeStr returns the contents of an rtype's str field (a nameOff).
func decodetypeStr(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym) string {
relocs := ldr.Relocs(symIdx)

View file

@ -682,9 +682,9 @@ func (d *dwctxt) newtype(gotype loader.Sym) *dwarf.DWDie {
}
fld := d.newdie(die, dwarf.DW_ABRV_STRUCTFIELD, f)
d.newrefattr(fld, dwarf.DW_AT_type, d.defgotype(s))
offsetAnon := decodetypeStructFieldOffsAnon(d.ldr, d.arch, gotype, i)
newmemberoffsetattr(fld, int32(offsetAnon>>1))
if offsetAnon&1 != 0 { // is embedded field
offset := decodetypeStructFieldOffset(d.ldr, d.arch, gotype, i)
newmemberoffsetattr(fld, int32(offset))
if decodetypeStructFieldEmbedded(d.ldr, d.arch, gotype, i) {
newattr(fld, dwarf.DW_AT_go_embedded_field, dwarf.DW_CLS_FLAG, 1, 0)
}
}

View file

@ -36,7 +36,7 @@ func Field(v Value, i int) Value {
// In the former case, we want v.ptr + offset.
// In the latter case, we must have field.offset = 0,
// so v.ptr + field.offset is still the correct address.
ptr := add(v.ptr, field.offset(), "same as non-reflect &v.field")
ptr := add(v.ptr, field.offset, "same as non-reflect &v.field")
return Value{typ, ptr, fl}
}

View file

@ -269,17 +269,13 @@ type sliceType struct {
// Struct field
type structField struct {
name name // name is always non-empty
typ *rtype // type of field
offsetEmbed uintptr // byte offset of field<<1 | isEmbedded
}
func (f *structField) offset() uintptr {
return f.offsetEmbed >> 1
name name // name is always non-empty
typ *rtype // type of field
offset uintptr // byte offset of field
}
func (f *structField) embedded() bool {
return f.offsetEmbed&1 != 0
return f.name.embedded()
}
// structType represents a struct type.
@ -328,6 +324,10 @@ func (n name) hasTag() bool {
return (*n.bytes)&(1<<1) != 0
}
func (n name) embedded() bool {
return (*n.bytes)&(1<<3) != 0
}
// readVarint parses a varint as encoded by encoding/binary.
// It returns the number of encoded bytes and the encoded value.
func (n name) readVarint(off int) (int, int) {
@ -947,7 +947,10 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
if cmpTags && tf.name.tag() != vf.name.tag() {
return false
}
if tf.offsetEmbed != vf.offsetEmbed {
if tf.offset != vf.offset {
return false
}
if tf.embedded() != vf.embedded() {
return false
}
}

View file

@ -237,7 +237,7 @@ func (a *abiSeq) regAssign(t *rtype, offset uintptr) bool {
st := (*structType)(unsafe.Pointer(t))
for i := range st.fields {
f := &st.fields[i]
if !a.regAssign(f.typ, offset+f.offset()) {
if !a.regAssign(f.typ, offset+f.offset) {
return false
}
}

View file

@ -140,7 +140,7 @@ func IsExported(t Type) bool {
}
func ResolveReflectName(s string) {
resolveReflectName(newName(s, "", false))
resolveReflectName(newName(s, "", false, false))
}
type Buffer struct {

View file

@ -433,17 +433,13 @@ type sliceType struct {
// Struct field
type structField struct {
name name // name is always non-empty
typ *rtype // type of field
offsetEmbed uintptr // byte offset of field<<1 | isEmbedded
}
func (f *structField) offset() uintptr {
return f.offsetEmbed >> 1
name name // name is always non-empty
typ *rtype // type of field
offset uintptr // byte offset of field
}
func (f *structField) embedded() bool {
return f.offsetEmbed&1 != 0
return f.name.embedded()
}
// structType represents a struct type.
@ -460,6 +456,7 @@ type structType struct {
// 1<<0 the name is exported
// 1<<1 tag data follows the name
// 1<<2 pkgPath nameOff follows the name and tag
// 1<<3 the name is of an embedded (a.k.a. anonymous) field
//
// Following that, there is a varint-encoded length of the name,
// followed by the name itself.
@ -496,6 +493,10 @@ func (n name) hasTag() bool {
return (*n.bytes)&(1<<1) != 0
}
func (n name) embedded() bool {
return (*n.bytes)&(1<<3) != 0
}
// readVarint parses a varint as encoded by encoding/binary.
// It returns the number of encoded bytes and the encoded value.
func (n name) readVarint(off int) (int, int) {
@ -565,7 +566,7 @@ func (n name) pkgPath() string {
return pkgPathName.name()
}
func newName(n, tag string, exported bool) name {
func newName(n, tag string, exported, embedded bool) name {
if len(n) >= 1<<29 {
panic("reflect.nameFrom: name too long: " + n[:1024] + "...")
}
@ -586,6 +587,9 @@ func newName(n, tag string, exported bool) name {
l += tagLenLen + len(tag)
bits |= 1 << 1
}
if embedded {
bits |= 1 << 3
}
b := make([]byte, l)
b[0] = bits
@ -1256,7 +1260,7 @@ func (t *structType) Field(i int) (f StructField) {
if tag := p.name.tag(); tag != "" {
f.Tag = StructTag(tag)
}
f.Offset = p.offset()
f.Offset = p.offset
// NOTE(rsc): This is the only allocation in the interface
// presented by a reflect.Type. It would be nice to avoid,
@ -1472,7 +1476,7 @@ func (t *rtype) ptrTo() *rtype {
prototype := *(**ptrType)(unsafe.Pointer(&iptr))
pp := *prototype
pp.str = resolveReflectName(newName(s, "", false))
pp.str = resolveReflectName(newName(s, "", false, false))
pp.ptrToThis = 0
// For the type structures linked into the binary, the
@ -1739,7 +1743,10 @@ func haveIdenticalUnderlyingType(T, V *rtype, cmpTags bool) bool {
if cmpTags && tf.name.tag() != vf.name.tag() {
return false
}
if tf.offsetEmbed != vf.offsetEmbed {
if tf.offset != vf.offset {
return false
}
if tf.embedded() != vf.embedded() {
return false
}
}
@ -1891,7 +1898,7 @@ func ChanOf(dir ChanDir, t Type) Type {
ch := *prototype
ch.tflag = tflagRegularMemory
ch.dir = uintptr(dir)
ch.str = resolveReflectName(newName(s, "", false))
ch.str = resolveReflectName(newName(s, "", false, false))
ch.hash = fnv1(typ.hash, 'c', byte(dir))
ch.elem = typ
@ -1934,7 +1941,7 @@ func MapOf(key, elem Type) Type {
// in ../cmd/compile/internal/reflectdata/reflect.go:writeType.
var imap any = (map[unsafe.Pointer]unsafe.Pointer)(nil)
mt := **(**mapType)(unsafe.Pointer(&imap))
mt.str = resolveReflectName(newName(s, "", false))
mt.str = resolveReflectName(newName(s, "", false, false))
mt.tflag = 0
mt.hash = fnv1(etyp.hash, 'm', byte(ktyp.hash>>24), byte(ktyp.hash>>16), byte(ktyp.hash>>8), byte(ktyp.hash))
mt.key = ktyp
@ -2113,7 +2120,7 @@ func FuncOf(in, out []Type, variadic bool) Type {
}
// Populate the remaining fields of ft and store in cache.
ft.str = resolveReflectName(newName(str, "", false))
ft.str = resolveReflectName(newName(str, "", false, false))
ft.ptrToThis = 0
return addToCache(&ft.rtype)
}
@ -2290,7 +2297,7 @@ func bucketOf(ktyp, etyp *rtype) *rtype {
gcdata: gcdata,
}
s := "bucket(" + ktyp.String() + "," + etyp.String() + ")"
b.str = resolveReflectName(newName(s, "", false))
b.str = resolveReflectName(newName(s, "", false, false))
return b
}
@ -2369,7 +2376,7 @@ func SliceOf(t Type) Type {
prototype := *(**sliceType)(unsafe.Pointer(&islice))
slice := *prototype
slice.tflag = 0
slice.str = resolveReflectName(newName(s, "", false))
slice.str = resolveReflectName(newName(s, "", false, false))
slice.hash = fnv1(typ.hash, '[')
slice.elem = typ
slice.ptrToThis = 0
@ -2632,7 +2639,7 @@ func StructOf(fields []StructField) Type {
typalign = ft.align
}
size = offset + ft.size
f.offsetEmbed |= offset << 1
f.offset = offset
if ft.size == 0 {
lastzero = size
@ -2698,7 +2705,7 @@ func StructOf(fields []StructField) Type {
*typ = *prototype
typ.fields = fs
if pkgpath != "" {
typ.pkgPath = newName(pkgpath, "", false)
typ.pkgPath = newName(pkgpath, "", false, false)
}
// Look in cache.
@ -2742,7 +2749,7 @@ func StructOf(fields []StructField) Type {
}
}
typ.str = resolveReflectName(newName(str, "", false))
typ.str = resolveReflectName(newName(str, "", false, false))
typ.tflag = 0 // TODO: set tflagRegularMemory
typ.hash = hash
typ.size = size
@ -2774,14 +2781,14 @@ func StructOf(fields []StructField) Type {
continue
}
// Pad to start of this field with zeros.
if ft.offset() > off {
n := (ft.offset() - off) / goarch.PtrSize
if ft.offset > off {
n := (ft.offset - off) / goarch.PtrSize
prog = append(prog, 0x01, 0x00) // emit a 0 bit
if n > 1 {
prog = append(prog, 0x81) // repeat previous bit
prog = appendVarint(prog, n-1) // n-1 times
}
off = ft.offset()
off = ft.offset
}
prog = appendGCProg(prog, ft.typ)
@ -2803,8 +2810,8 @@ func StructOf(fields []StructField) Type {
if comparable {
typ.equal = func(p, q unsafe.Pointer) bool {
for _, ft := range typ.fields {
pi := add(p, ft.offset(), "&x.field safe")
qi := add(q, ft.offset(), "&x.field safe")
pi := add(p, ft.offset, "&x.field safe")
qi := add(q, ft.offset, "&x.field safe")
if !ft.typ.equal(pi, qi) {
return false
}
@ -2841,16 +2848,11 @@ func runtimeStructField(field StructField) (structField, string) {
}
}
offsetEmbed := uintptr(0)
if field.Anonymous {
offsetEmbed |= 1
}
resolveReflectType(field.Type.common()) // install in runtime
f := structField{
name: newName(field.Name, string(field.Tag), field.IsExported()),
typ: field.Type.common(),
offsetEmbed: offsetEmbed,
name: newName(field.Name, string(field.Tag), field.IsExported(), field.Anonymous),
typ: field.Type.common(),
offset: 0,
}
return f, field.PkgPath
}
@ -2874,7 +2876,7 @@ func typeptrdata(t *rtype) uintptr {
return 0
}
f := st.fields[field]
return f.offset() + f.typ.ptrdata
return f.offset + f.typ.ptrdata
default:
panic("reflect.typeptrdata: unexpected type, " + t.String())
@ -2917,7 +2919,7 @@ func ArrayOf(length int, elem Type) Type {
prototype := *(**arrayType)(unsafe.Pointer(&iarray))
array := *prototype
array.tflag = typ.tflag & tflagRegularMemory
array.str = resolveReflectName(newName(s, "", false))
array.str = resolveReflectName(newName(s, "", false, false))
array.hash = fnv1(typ.hash, '[')
for n := uint32(length); n > 0; n >>= 8 {
array.hash = fnv1(array.hash, byte(n))
@ -3097,7 +3099,7 @@ func funcLayout(t *funcType, rcvr *rtype) (frametype *rtype, framePool *sync.Poo
} else {
s = "funcargs(" + t.String() + ")"
}
x.str = resolveReflectName(newName(s, "", false))
x.str = resolveReflectName(newName(s, "", false, false))
// cache result for future callers
framePool = &sync.Pool{New: func() any {
@ -3165,7 +3167,7 @@ func addTypeBits(bv *bitVector, offset uintptr, t *rtype) {
tt := (*structType)(unsafe.Pointer(t))
for i := range tt.fields {
f := &tt.fields[i]
addTypeBits(bv, offset+f.offset(), f.typ)
addTypeBits(bv, offset+f.offset, f.typ)
}
}
}

View file

@ -1287,7 +1287,7 @@ func (v Value) Field(i int) Value {
// In the former case, we want v.ptr + offset.
// In the latter case, we must have field.offset = 0,
// so v.ptr + field.offset is still the correct address.
ptr := add(v.ptr, field.offset(), "same as non-reflect &v.field")
ptr := add(v.ptr, field.offset, "same as non-reflect &v.field")
return Value{typ, ptr, fl}
}

View file

@ -182,7 +182,7 @@ func typehash(t *_type, p unsafe.Pointer, h uintptr) uintptr {
if f.name.isBlank() {
continue
}
h = typehash(f.typ, add(p, f.offset()), h)
h = typehash(f.typ, add(p, f.offset), h)
}
return h
default:

View file

@ -536,7 +536,7 @@ func cgoCheckArg(t *_type, p unsafe.Pointer, indir, top bool, msg string) {
if f.typ.ptrdata == 0 {
continue
}
cgoCheckArg(f.typ, add(p, f.offset()), true, top, msg)
cgoCheckArg(f.typ, add(p, f.offset), true, top, msg)
}
case kindPtr, kindUnsafePointer:
if indir {

View file

@ -174,7 +174,7 @@ func (p *abiDesc) tryRegAssignArg(t *_type, offset uintptr) bool {
st := (*structtype)(unsafe.Pointer(t))
for i := range st.fields {
f := &st.fields[i]
if !p.tryRegAssignArg(f.typ, offset+f.offset()) {
if !p.tryRegAssignArg(f.typ, offset+f.offset) {
return false
}
}

View file

@ -414,13 +414,9 @@ type ptrtype struct {
}
type structfield struct {
name name
typ *_type
offsetAnon uintptr
}
func (f *structfield) offset() uintptr {
return f.offsetAnon >> 1
name name
typ *_type
offset uintptr
}
type structtype struct {
@ -443,6 +439,10 @@ func (n name) isExported() bool {
return (*n.bytes)&(1<<0) != 0
}
func (n name) isEmbedded() bool {
return (*n.bytes)&(1<<3) != 0
}
func (n name) readvarint(off int) (int, int) {
v := 0
for i := 0; ; i++ {
@ -703,7 +703,10 @@ func typesEqual(t, v *_type, seen map[_typePair]struct{}) bool {
if tf.name.tag() != vf.name.tag() {
return false
}
if tf.offsetAnon != vf.offsetAnon {
if tf.offset != vf.offset {
return false
}
if tf.name.isEmbedded() != vf.name.isEmbedded() {
return false
}
}