reflect: implement support for array types

Этот коммит содержится в:
Ayke van Laethem 2019-08-16 22:08:44 +02:00 коммит произвёл Ron Evans
родитель bbc3046687
коммит c19c738f52
8 изменённых файлов: 184 добавлений и 20 удалений

Просмотреть файл

@ -54,6 +54,7 @@ func (c *Compiler) getTypeCode(typ types.Type) llvm.Value {
// element types. Store it directly in the typecode global to make
// reflect lowering simpler.
var references llvm.Value
var length int64
switch typ := typ.(type) {
case *types.Named:
references = c.getTypeCode(typ.Underlying())
@ -63,6 +64,9 @@ func (c *Compiler) getTypeCode(typ types.Type) llvm.Value {
references = c.getTypeCode(typ.Elem())
case *types.Slice:
references = c.getTypeCode(typ.Elem())
case *types.Array:
references = c.getTypeCode(typ.Elem())
length = typ.Len()
case *types.Struct:
// Take a pointer to the typecodeID of the first field (if it exists).
structGlobal := c.makeStructTypeFields(typ)
@ -72,6 +76,10 @@ func (c *Compiler) getTypeCode(typ types.Type) llvm.Value {
// Set the 'references' field of the runtime.typecodeID struct.
globalValue := c.getZeroValue(global.Type().ElementType())
globalValue = llvm.ConstInsertValue(globalValue, references, []uint32{0})
if length != 0 {
lengthValue := llvm.ConstInt(c.uintptrType, uint64(length), false)
globalValue = llvm.ConstInsertValue(globalValue, lengthValue, []uint32{1})
}
global.SetInitializer(globalValue)
global.SetLinkage(llvm.PrivateLinkage)
}

Просмотреть файл

@ -89,6 +89,11 @@ type typeCodeAssignmentState struct {
namedBasicTypes map[string]int
namedNonBasicTypes map[string]int
// Map of array types to their type code.
arrayTypes map[string]int
arrayTypesSidetable []byte
needsArrayTypesSidetable bool
// Map of struct types to their type code.
structTypes map[string]int
structTypesSidetable []byte
@ -137,11 +142,13 @@ func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
uintptrLen: c.uintptrType.IntTypeWidth(),
namedBasicTypes: make(map[string]int),
namedNonBasicTypes: make(map[string]int),
arrayTypes: make(map[string]int),
structTypes: make(map[string]int),
structNames: make(map[string]int),
needsNamedNonBasicTypesSidetable: len(getUses(c.mod.NamedGlobal("reflect.namedNonBasicTypesSidetable"))) != 0,
needsStructTypesSidetable: len(getUses(c.mod.NamedGlobal("reflect.structTypesSidetable"))) != 0,
needsStructNamesSidetable: len(getUses(c.mod.NamedGlobal("reflect.structNamesSidetable"))) != 0,
needsArrayTypesSidetable: len(getUses(c.mod.NamedGlobal("reflect.arrayTypesSidetable"))) != 0,
}
for _, t := range typeSlice {
num := state.getTypeCodeNum(t.typecode)
@ -161,6 +168,11 @@ func (c *Compiler) assignTypeCodes(typeSlice typeInfoSlice) {
global.SetLinkage(llvm.InternalLinkage)
global.SetUnnamedAddr(true)
}
if state.needsArrayTypesSidetable {
global := c.replaceGlobalIntWithArray("reflect.arrayTypesSidetable", state.arrayTypesSidetable)
global.SetLinkage(llvm.InternalLinkage)
global.SetUnnamedAddr(true)
}
if state.needsStructTypesSidetable {
global := c.replaceGlobalIntWithArray("reflect.structTypesSidetable", state.structTypesSidetable)
global.SetLinkage(llvm.InternalLinkage)
@ -268,6 +280,10 @@ func (state *typeCodeAssignmentState) getNonBasicTypeCode(class string, typecode
// Prefix-style type kinds. The upper bits contain the element type.
sub := llvm.ConstExtractValue(typecode.Initializer(), []uint32{0})
return state.getTypeCodeNum(sub)
case "array":
// An array is basically a pair of (typecode, length) stored in a
// sidetable.
return big.NewInt(int64(state.getArrayTypeNum(typecode)))
case "struct":
// More complicated type kind. The upper bits contain the index to the
// struct type in the struct types sidetable.
@ -312,6 +328,43 @@ func (state *typeCodeAssignmentState) getBasicNamedTypeNum(name string) int {
return num
}
// getArrayTypeNum returns the array type number, which is an index into the
// reflect.arrayTypesSidetable or a unique number for this type if this table is
// not used.
func (state *typeCodeAssignmentState) getArrayTypeNum(typecode llvm.Value) int {
name := typecode.Name()
if num, ok := state.arrayTypes[name]; ok {
// This array type already has an entry in the sidetable. Don't store
// it twice.
return num
}
if !state.needsArrayTypesSidetable {
// We don't need array sidetables, so we can just assign monotonically
// increasing numbers to each array type.
num := len(state.arrayTypes)
state.arrayTypes[name] = num
return num
}
elemTypeCode := llvm.ConstExtractValue(typecode.Initializer(), []uint32{0})
elemTypeNum := state.getTypeCodeNum(elemTypeCode)
if elemTypeNum.BitLen() > state.uintptrLen || !elemTypeNum.IsUint64() {
// TODO: make this a regular error
panic("array element type has a type code that is too big")
}
// The array side table is a sequence of {element type, array length}.
arrayLength := llvm.ConstExtractValue(typecode.Initializer(), []uint32{1}).ZExtValue()
buf := makeVarint(elemTypeNum.Uint64())
buf = append(buf, makeVarint(arrayLength)...)
index := len(state.arrayTypesSidetable)
state.arrayTypes[name] = index
state.arrayTypesSidetable = append(state.arrayTypesSidetable, buf...)
return index
}
// getStructTypeNum returns the struct type number, which is an index into
// reflect.structTypesSidetable or an unique number for every struct if this
// sidetable is not needed in the to-be-compiled program.

Просмотреть файл

@ -16,6 +16,9 @@ var structTypesSidetable byte
//go:extern reflect.structNamesSidetable
var structNamesSidetable byte
//go:extern reflect.arrayTypesSidetable
var arrayTypesSidetable byte
// readStringSidetable reads a string from the given table (like
// structNamesSidetable) and returns this string. No heap allocation is
// necessary because it makes the string point directly to the raw bytes of the

Просмотреть файл

@ -142,11 +142,17 @@ func (t Type) Kind() Kind {
}
}
// Elem returns the element type for channel, slice and array types, the
// pointed-to value for pointer types, and the key type for map types.
func (t Type) Elem() Type {
switch t.Kind() {
case Chan, Ptr, Slice:
return t.stripPrefix()
default: // not implemented: Array, Map
case Array:
index := t.stripPrefix()
elem, _ := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&arrayTypesSidetable)) + uintptr(index)))
return Type(elem)
default: // not implemented: Map
panic("unimplemented: (reflect.Type).Elem()")
}
}
@ -254,8 +260,20 @@ func (t Type) Bits() int {
panic(TypeError{"Bits"})
}
// Len returns the number of elements in this array. It panics of the type kind
// is not Array.
func (t Type) Len() int {
panic("unimplemented: (reflect.Type).Len()")
if t.Kind() != Array {
panic(TypeError{"Len"})
}
// skip past the element type
arrayIdentifier := t.stripPrefix()
_, p := readVarint(unsafe.Pointer(uintptr(unsafe.Pointer(&arrayTypesSidetable)) + uintptr(arrayIdentifier)))
// Read the array length.
arrayLen, _ := readVarint(p)
return int(arrayLen)
}
// NumField returns the number of fields of a struct type. It panics for other
@ -301,6 +319,8 @@ func (t Type) Size() uintptr {
return unsafe.Sizeof(SliceHeader{})
case Interface:
return unsafe.Sizeof(interfaceHeader{})
case Array:
return t.Elem().Size() * uintptr(t.Len())
case Struct:
numField := t.NumField()
if numField == 0 {

Просмотреть файл

@ -302,6 +302,8 @@ func (v Value) Slice(i, j int) Value {
panic("unimplemented: (reflect.Value).Slice()")
}
// Len returns the length of this value for slices, strings, arrays, channels,
// and maps. For oter types, it panics.
func (v Value) Len() int {
t := v.Type()
switch t.Kind() {
@ -309,7 +311,9 @@ func (v Value) Len() int {
return int((*SliceHeader)(v.value).Len)
case String:
return int((*StringHeader)(v.value).Len)
default: // Array, Chan, Map
case Array:
return v.Type().Len()
default: // Chan, Map
panic("unimplemented: (reflect.Value).Len()")
}
}
@ -392,27 +396,21 @@ func (v Value) Field(i int) Value {
// afterwards, so load the value (from the correct offset) and return
// it.
ptr := unsafe.Pointer(uintptr(v.value) + structField.Offset)
loadedValue := uintptr(0)
shift := uintptr(0)
for i := uintptr(0); i < fieldSize; i++ {
loadedValue |= uintptr(*(*byte)(ptr)) << shift
shift += 8
ptr = unsafe.Pointer(uintptr(ptr) + 1)
}
value := unsafe.Pointer(loadValue(ptr, fieldSize))
return Value{
flags: 0,
typecode: structField.Type,
value: unsafe.Pointer(loadedValue),
value: value,
}
}
// The value was already stored directly in the interface and it still
// is. Cut out the part of the value that we need.
mask := ^uintptr(0) >> ((unsafe.Sizeof(uintptr(0)) - fieldSize) * 8)
value := maskAndShift(uintptr(v.value), structField.Offset, fieldSize)
return Value{
flags: flags,
typecode: structField.Type,
value: unsafe.Pointer((uintptr(v.value) >> (structField.Offset * 8)) & mask),
value: unsafe.Pointer(value),
}
}
@ -444,12 +442,75 @@ func (v Value) Index(i int) Value {
value: unsafe.Pointer(uintptr(*(*uint8)(unsafe.Pointer(s.Data + uintptr(i))))),
}
case Array:
panic("unimplemented: (reflect.Value).Index()")
// Extract an element from the array.
elemType := v.Type().Elem()
elemSize := elemType.Size()
size := v.Type().Size()
if size == 0 {
// The element size is 0 and/or the length of the array is 0.
return Value{
typecode: v.Type().Elem(),
flags: v.flags,
}
}
if elemSize > unsafe.Sizeof(uintptr(0)) {
// The resulting value doesn't fit in a pointer so must be
// indirect. Also, because size != 0 this implies that the array
// length must be != 0, and thus that the total size is at least
// elemSize.
addr := uintptr(v.value) + elemSize*uintptr(i) // pointer to new value
return Value{
typecode: v.Type().Elem(),
flags: v.flags,
value: unsafe.Pointer(addr),
}
}
if size > unsafe.Sizeof(uintptr(0)) {
// The element fits in a pointer, but the array does not.
// Load the value from the pointer.
addr := uintptr(v.value) + elemSize*uintptr(i) // pointer to new value
return Value{
typecode: v.Type().Elem(),
flags: v.flags,
value: unsafe.Pointer(loadValue(unsafe.Pointer(addr), elemSize)),
}
}
// The value fits in a pointer, so extract it with some shifting and
// masking.
offset := elemSize * uintptr(i)
value := maskAndShift(uintptr(v.value), offset, elemSize)
return Value{
typecode: v.Type().Elem(),
flags: v.flags,
value: unsafe.Pointer(value),
}
default:
panic(&ValueError{"Index"})
}
}
// loadValue loads a value that may or may not be word-aligned. The number of
// bytes given in size are loaded. The biggest possible size it can load is that
// of an uintptr.
func loadValue(ptr unsafe.Pointer, size uintptr) uintptr {
loadedValue := uintptr(0)
shift := uintptr(0)
for i := uintptr(0); i < size; i++ {
loadedValue |= uintptr(*(*byte)(ptr)) << shift
shift += 8
ptr = unsafe.Pointer(uintptr(ptr) + 1)
}
return loadedValue
}
// maskAndShift cuts out a part of a uintptr. Note that the offset may not be 0.
func maskAndShift(value, offset, size uintptr) uintptr {
mask := ^uintptr(0) >> ((unsafe.Sizeof(uintptr(0)) - size) * 8)
return (uintptr(value) >> (offset * 8)) & mask
}
func (v Value) MapKeys() []Value {
panic("unimplemented: (reflect.Value).MapKeys()")
}

Просмотреть файл

@ -49,10 +49,13 @@ type typecodeID struct {
// * basic types: null
// * named type: the underlying type
// * interface: null
// * chan/pointer/slice: the element type
// * struct: GEP of structField array (to typecode field)
// * array/func/map: TODO
// * chan/pointer/slice/array: the element type
// * struct: bitcast of global with structField array
// * func/map: TODO
references *typecodeID
// The array length, for array types.
length uintptr
}
// structField is used by the compiler to pass information to the interface

8
testdata/reflect.go предоставленный
Просмотреть файл

@ -91,7 +91,8 @@ func main() {
[]complex128{1, 1.128 + 0.4i},
myslice{5, 3, 11},
// array
[4]int{1, 2, 3, 4},
[3]int64{5, 8, 2},
[2]uint8{3, 5},
// functions
zeroFunc,
emptyFunc,
@ -287,7 +288,10 @@ func showValue(rv reflect.Value, indent string) {
case reflect.UnsafePointer:
println(indent+" pointer:", rv.Pointer() != 0)
case reflect.Array:
println(indent + " array")
println(indent+" array:", rt.Len(), rt.Elem().Kind().String(), int(rt.Size()))
for i := 0; i < rv.Len(); i++ {
showValue(rv.Index(i), indent+" ")
}
case reflect.Chan:
println(indent+" chan:", rt.Elem().Kind().String())
println(indent+" nil:", rv.IsNil())

14
testdata/reflect.txt предоставленный
Просмотреть файл

@ -203,7 +203,19 @@ reflect type: slice
reflect type: uint8 settable=true
uint: 11
reflect type: array
array
array: 3 int64 24
reflect type: int64
int: 5
reflect type: int64
int: 8
reflect type: int64
int: 2
reflect type: array
array: 2 uint8 2
reflect type: uint8
uint: 3
reflect type: uint8
uint: 5
reflect type: func
func
nil: true