aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2021-04-11 13:50:44 +0200
committerRon Evans <[email protected]>2021-04-12 14:49:26 +0200
commite587b1d1b4031a7bed960d94122a5d243229f090 (patch)
tree3f234181c5578a4a0737b11327178222095bea7c
parent57271d7eaa50b122706610fc5c406e0380965998 (diff)
downloadtinygo-e587b1d1b4031a7bed960d94122a5d243229f090.tar.gz
tinygo-e587b1d1b4031a7bed960d94122a5d243229f090.zip
reflect: implement New function
This is very important for some use cases, for example for Vecty.
-rw-r--r--compiler/compiler.go2
-rw-r--r--compiler/interface.go34
-rw-r--r--compiler/testdata/interface.ll17
-rw-r--r--src/reflect/type.go3
-rw-r--r--src/reflect/value.go11
-rw-r--r--src/runtime/interface.go5
-rw-r--r--testdata/reflect.go15
-rw-r--r--testdata/reflect.txt25
8 files changed, 87 insertions, 25 deletions
diff --git a/compiler/compiler.go b/compiler/compiler.go
index 0afa20daf..c6e577fd6 100644
--- a/compiler/compiler.go
+++ b/compiler/compiler.go
@@ -23,7 +23,7 @@ import (
// Version of the compiler pacakge. Must be incremented each time the compiler
// package changes in a way that affects the generated LLVM module.
// This version is independent of the TinyGo version number.
-const Version = 8 // last change: don't use runtime.typecodeID in func lowering
+const Version = 9 // last change: implement reflect.New()
func init() {
llvm.InitializeAllTargets()
diff --git a/compiler/interface.go b/compiler/interface.go
index 8b3988a12..eda7e3653 100644
--- a/compiler/interface.go
+++ b/compiler/interface.go
@@ -46,6 +46,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
var references llvm.Value
var length int64
var methodSet llvm.Value
+ var ptrTo llvm.Value
switch typ := typ.(type) {
case *types.Named:
references = c.getTypeCode(typ.Underlying())
@@ -69,22 +70,25 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
if _, ok := typ.Underlying().(*types.Interface); !ok {
methodSet = c.getTypeMethodSet(typ)
}
- if !references.IsNil() || length != 0 || !methodSet.IsNil() {
- // Set the 'references' field of the runtime.typecodeID struct.
- globalValue := llvm.ConstNull(global.Type().ElementType())
- if !references.IsNil() {
- 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})
- }
- if !methodSet.IsNil() {
- globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2})
- }
- global.SetInitializer(globalValue)
- global.SetLinkage(llvm.LinkOnceODRLinkage)
+ if _, ok := typ.Underlying().(*types.Pointer); !ok {
+ ptrTo = c.getTypeCode(types.NewPointer(typ))
+ }
+ globalValue := llvm.ConstNull(global.Type().ElementType())
+ if !references.IsNil() {
+ 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})
+ }
+ if !methodSet.IsNil() {
+ globalValue = llvm.ConstInsertValue(globalValue, methodSet, []uint32{2})
+ }
+ if !ptrTo.IsNil() {
+ globalValue = llvm.ConstInsertValue(globalValue, ptrTo, []uint32{3})
}
+ global.SetInitializer(globalValue)
+ global.SetLinkage(llvm.LinkOnceODRLinkage)
global.SetGlobalConstant(true)
}
return global
diff --git a/compiler/testdata/interface.ll b/compiler/testdata/interface.ll
index e5aa7eef1..1414db8c3 100644
--- a/compiler/testdata/interface.ll
+++ b/compiler/testdata/interface.ll
@@ -3,20 +3,21 @@ source_filename = "interface.go"
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
target triple = "i686--linux"
-%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* }
+%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID* }
%runtime.interfaceMethodInfo = type { i8*, i32 }
%runtime._interface = type { i32, i8* }
%runtime._string = type { i8*, i32 }
-@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID zeroinitializer
-@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null }
-@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null }
-@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null }
-@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
+@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:int" }
+@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
+@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
+@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:named:error" }
+@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }
@"func Error() string" = external constant i8
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"]
-@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null }
-@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
+@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
+@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
+@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }
@"func String() string" = external constant i8
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"]
@"reflect/types.typeid:basic:int" = external constant i8
diff --git a/src/reflect/type.go b/src/reflect/type.go
index 67396c777..2e7096759 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -322,6 +322,9 @@ func TypeOf(i interface{}) Type {
}
func PtrTo(t Type) Type {
+ if t.Kind() == Ptr {
+ panic("reflect: cannot make **T type")
+ }
ptrType := t.(rawType)<<5 | 5 // 0b0101 == 5
if ptrType>>5 != t {
panic("reflect: PtrTo type does not fit")
diff --git a/src/reflect/value.go b/src/reflect/value.go
index 28308f751..08d828d80 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -724,8 +724,14 @@ func Zero(typ Type) Value {
panic("unimplemented: reflect.Zero()")
}
+// New is the reflect equivalent of the new(T) keyword, returning a pointer to a
+// new value of the given type.
func New(typ Type) Value {
- panic("unimplemented: reflect.New()")
+ return Value{
+ typecode: PtrTo(typ).(rawType),
+ value: alloc(typ.Size()),
+ flags: valueFlagExported,
+ }
}
type funcHeader struct {
@@ -756,6 +762,9 @@ func (e *ValueError) Error() string {
// llvm.memcpy.p0i8.p0i8.i32().
func memcpy(dst, src unsafe.Pointer, size uintptr)
+//go:linkname alloc runtime.alloc
+func alloc(size uintptr) unsafe.Pointer
+
// Copy copies the contents of src into dst until either
// dst has been filled or src has been exhausted.
func Copy(dst, src Value) int {
diff --git a/src/runtime/interface.go b/src/runtime/interface.go
index 650883634..10faa4eca 100644
--- a/src/runtime/interface.go
+++ b/src/runtime/interface.go
@@ -112,6 +112,11 @@ type typecodeID struct {
length uintptr
methodSet *interfaceMethodInfo // nil or a GEP of an array
+
+ // The type that's a pointer to this type, nil if it is already a pointer.
+ // Keeping the type struct alive here is important so that values from
+ // reflect.New (which uses reflect.PtrTo) can be used in type asserts etc.
+ ptrTo *typecodeID
}
// structField is used by the compiler to pass information to the interface
diff --git a/testdata/reflect.go b/testdata/reflect.go
index b02dd6c80..cd71d984a 100644
--- a/testdata/reflect.go
+++ b/testdata/reflect.go
@@ -124,6 +124,21 @@ func main() {
showValue(reflect.ValueOf(v), "")
}
+ // Test reflect.New().
+ newInt8 := reflect.New(reflect.TypeOf(int8(0)))
+ newInt8.Elem().SetInt(5)
+ newInt16 := reflect.New(reflect.TypeOf(int16(0)))
+ newInt16.Elem().SetInt(-800)
+ newInt32 := reflect.New(reflect.TypeOf(int32(0)))
+ newInt32.Elem().SetInt(1e8)
+ newInt64 := reflect.New(reflect.TypeOf(int64(0)))
+ newInt64.Elem().SetInt(-1e12)
+ newComplex128 := reflect.New(reflect.TypeOf(0 + 0i))
+ newComplex128.Elem().SetComplex(-8 - 20e5i)
+ for _, val := range []reflect.Value{newInt8, newInt16, newInt32, newInt64, newComplex128} {
+ showValue(val, "")
+ }
+
// test sizes
println("\nsizes:")
for _, tc := range []struct {
diff --git a/testdata/reflect.txt b/testdata/reflect.txt
index af9e7231f..4f04afe98 100644
--- a/testdata/reflect.txt
+++ b/testdata/reflect.txt
@@ -321,6 +321,31 @@ reflect type: ptr
embedded: false
reflect type: int addrable=true
int: 42
+reflect type: ptr
+ pointer: true int8
+ nil: false
+ reflect type: int8 settable=true addrable=true
+ int: 5
+reflect type: ptr
+ pointer: true int16
+ nil: false
+ reflect type: int16 settable=true addrable=true
+ int: -800
+reflect type: ptr
+ pointer: true int32
+ nil: false
+ reflect type: int32 settable=true addrable=true
+ int: 100000000
+reflect type: ptr
+ pointer: true int64
+ nil: false
+ reflect type: int64 settable=true addrable=true
+ int: -1000000000000
+reflect type: ptr
+ pointer: true complex128
+ nil: false
+ reflect type: complex128 settable=true addrable=true
+ complex: (-8.000000e+000-2.000000e+006i)
sizes:
int8 1 8