reflect: implement support for array types
Этот коммит содержится в:
родитель
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
предоставленный
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
предоставленный
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
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче