From 07fb3a0cadb6c02e3c2a09429b45c9c9ea29b61c Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Thu, 27 Apr 2023 09:14:08 -0700 Subject: [PATCH] compiler,reflect: make field offsets varints Fixes #3686 --- compiler/interface.go | 8 ++++++-- src/reflect/type.go | 34 +++++++++++++++++++++++++++++----- src/runtime/interface.go | 3 +-- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/compiler/interface.go b/compiler/interface.go index c6818c34..c491646e 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -6,6 +6,7 @@ package compiler // interface-lowering.go for more details. import ( + "encoding/binary" "fmt" "go/token" "go/types" @@ -335,7 +336,11 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { if field.Embedded() { flags |= structFieldFlagIsEmbedded } - data := string(flags) + field.Name() + "\x00" + + var offsBytes [binary.MaxVarintLen32]byte + offLen := binary.PutUvarint(offsBytes[:], offset) + + data := string(flags) + string(offsBytes[:offLen]) + field.Name() + "\x00" if typ.Tag(i) != "" { if len(typ.Tag(i)) > 0xff { c.addError(field.Pos(), fmt.Sprintf("struct tag is %d bytes which is too long, max is 255", len(typ.Tag(i)))) @@ -352,7 +357,6 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value { fieldType := c.getTypeCode(field.Type()) fields = append(fields, llvm.ConstNamedStruct(structFieldType, []llvm.Value{ fieldType, - llvm.ConstInt(c.ctx.Int32Type(), uint64(offset), false), llvm.ConstGEP(dataGlobal.GlobalValueType(), dataGlobal, []llvm.Value{ llvm.ConstInt(c.ctx.Int32Type(), 0, false), llvm.ConstInt(c.ctx.Int32Type(), 0, false), diff --git a/src/reflect/type.go b/src/reflect/type.go index 6d8aec39..6a37dc7d 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -463,7 +463,6 @@ type structType struct { type structField struct { fieldType *rawType - offset uint32 data unsafe.Pointer // various bits of information, packed in a byte array } @@ -624,7 +623,7 @@ func (t *rawType) Field(i int) StructField { } } -func rawStructFieldFromPointer(descriptor *structType, fieldType *rawType, data unsafe.Pointer, flagsByte uint8, name string, offset uintptr) rawStructField { +func rawStructFieldFromPointer(descriptor *structType, fieldType *rawType, data unsafe.Pointer, flagsByte uint8, name string, offset uint32) rawStructField { // Read the field tag, if there is one. var tag string if flagsByte&structFieldFlagHasTag != 0 { @@ -651,7 +650,7 @@ func rawStructFieldFromPointer(descriptor *structType, fieldType *rawType, data Type: fieldType, Tag: StructTag(tag), Anonymous: flagsByte&structFieldFlagAnonymous != 0, - Offset: offset, + Offset: uintptr(offset), } } @@ -673,13 +672,14 @@ func (t *rawType) rawField(n int) rawStructField { // lookup faster), but by calculating it on-the-fly a bit of storage can be // saved. field := (*structField)(unsafe.Add(unsafe.Pointer(&descriptor.fields[0]), uintptr(n)*unsafe.Sizeof(structField{}))) - offset := uintptr(field.offset) data := field.data // Read some flags of this field, like whether the field is an embedded // field. See structFieldFlagAnonymous and similar flags. flagsByte := *(*byte)(data) data = unsafe.Add(data, 1) + offset, lenOffs := uvarint32(unsafe.Slice((*byte)(data), maxVarintLen32)) + data = unsafe.Add(data, lenOffs) name := readStringZ(data) data = unsafe.Add(data, len(name)) @@ -729,10 +729,12 @@ func (t *rawType) rawFieldByName(n string) (rawStructField, []int, bool) { flagsByte := *(*byte)(data) data = unsafe.Add(data, 1) + offset, lenOffs := uvarint32(unsafe.Slice((*byte)(data), maxVarintLen32)) + data = unsafe.Add(data, lenOffs) + name := readStringZ(data) data = unsafe.Add(data, len(name)) if name == n { - offset := uintptr(field.offset) found = append(found, result{ rawStructFieldFromPointer(descriptor, field.fieldType, data, flagsByte, name, offset), append(ll.index, int(i)), @@ -1284,3 +1286,25 @@ func StructOf([]StructField) Type { func MapOf(key, value Type) Type { panic("unimplemented: reflect.MapOf()") } + +const maxVarintLen32 = 5 + +// encoding/binary.Uvarint, specialized for uint32 +func uvarint32(buf []byte) (uint32, int) { + var x uint32 + var s uint + for i, b := range buf { + if i == maxVarintLen32 { + return 0, -(i + 1) // overflow + } + if b < 0x80 { + if i == maxVarintLen32-1 && b > 1 { + return 0, -(i + 1) // overflow + } + return x | uint32(b)<