From c90b93c1b7f5576acb0da444f4719baf27cc95e8 Mon Sep 17 00:00:00 2001 From: visualfc Date: Thu, 12 Dec 2024 16:29:17 +0800 Subject: [PATCH] internal/lib/reflect: convertOp --- c/bitcast/_cast/cast.c | 12 + c/bitcast/bitcast.go | 6 + compiler/cl/_testgo/reflect/out.ll | 16 +- runtime/internal/clite/bitcast/_cast/cast.c | 12 + runtime/internal/clite/bitcast/bitcast.go | 6 + runtime/internal/lib/reflect/type.go | 11 +- runtime/internal/lib/reflect/value.go | 372 ++++++++++++++++++++ 7 files changed, 420 insertions(+), 15 deletions(-) diff --git a/c/bitcast/_cast/cast.c b/c/bitcast/_cast/cast.c index bcc90d64..2d7eeffc 100644 --- a/c/bitcast/_cast/cast.c +++ b/c/bitcast/_cast/cast.c @@ -15,3 +15,15 @@ float llgoToFloat32(long v) { k.v = v; return k.f; } + +long llgoFromFloat64(double v) { + castUnion k; + k.d = v; + return k.v; +} + +long llgoFromFloat32(float v) { + castUnion k; + k.f = v; + return k.v; +} diff --git a/c/bitcast/bitcast.go b/c/bitcast/bitcast.go index f6d12351..f988fa90 100644 --- a/c/bitcast/bitcast.go +++ b/c/bitcast/bitcast.go @@ -28,3 +28,9 @@ func ToFloat64(v uintptr) float64 //go:linkname ToFloat32 C.llgoToFloat32 func ToFloat32(v uintptr) float32 + +//go:linkname FromFloat64 C.llgoFromFloat64 +func FromFloat64(v float64) uintptr + +//go:linkname FromFloat32 C.llgoFromFloat32 +func FromFloat32(v float32) uintptr diff --git a/compiler/cl/_testgo/reflect/out.ll b/compiler/cl/_testgo/reflect/out.ll index c25ba795..9a6bbf60 100644 --- a/compiler/cl/_testgo/reflect/out.ll +++ b/compiler/cl/_testgo/reflect/out.ll @@ -90,7 +90,7 @@ _llgo_0: %13 = call %"github.com/goplus/llgo/runtime/internal/runtime.iface" @reflect.Value.Type(%reflect.Value %11) %14 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData"(%"github.com/goplus/llgo/runtime/internal/runtime.iface" %13) %15 = extractvalue %"github.com/goplus/llgo/runtime/internal/runtime.iface" %13, 0 - %16 = getelementptr ptr, ptr %15, i64 37 + %16 = getelementptr ptr, ptr %15, i64 31 %17 = load ptr, ptr %16, align 8 %18 = insertvalue { ptr, ptr } undef, ptr %17, 0 %19 = insertvalue { ptr, ptr } %18, ptr %14, 1 @@ -185,7 +185,7 @@ _llgo_0: %6 = call %"github.com/goplus/llgo/runtime/internal/runtime.iface" @reflect.Value.Type(%reflect.Value %4) %7 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData"(%"github.com/goplus/llgo/runtime/internal/runtime.iface" %6) %8 = extractvalue %"github.com/goplus/llgo/runtime/internal/runtime.iface" %6, 0 - %9 = getelementptr ptr, ptr %8, i64 37 + %9 = getelementptr ptr, ptr %8, i64 31 %10 = load ptr, ptr %9, align 8 %11 = insertvalue { ptr, ptr } undef, ptr %10, 0 %12 = insertvalue { ptr, ptr } %11, ptr %7, 1 @@ -286,7 +286,7 @@ _llgo_0: %16 = call %"github.com/goplus/llgo/runtime/internal/runtime.iface" @reflect.Value.Type(%reflect.Value %14) %17 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData"(%"github.com/goplus/llgo/runtime/internal/runtime.iface" %16) %18 = extractvalue %"github.com/goplus/llgo/runtime/internal/runtime.iface" %16, 0 - %19 = getelementptr ptr, ptr %18, i64 37 + %19 = getelementptr ptr, ptr %18, i64 31 %20 = load ptr, ptr %19, align 8 %21 = insertvalue { ptr, ptr } undef, ptr %20, 0 %22 = insertvalue { ptr, ptr } %21, ptr %17, 1 @@ -393,7 +393,7 @@ _llgo_0: %8 = call %"github.com/goplus/llgo/runtime/internal/runtime.iface" @reflect.Value.Type(%reflect.Value %6) %9 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData"(%"github.com/goplus/llgo/runtime/internal/runtime.iface" %8) %10 = extractvalue %"github.com/goplus/llgo/runtime/internal/runtime.iface" %8, 0 - %11 = getelementptr ptr, ptr %10, i64 37 + %11 = getelementptr ptr, ptr %10, i64 31 %12 = load ptr, ptr %11, align 8 %13 = insertvalue { ptr, ptr } undef, ptr %12, 0 %14 = insertvalue { ptr, ptr } %13, ptr %9, 1 @@ -827,7 +827,7 @@ _llgo_9: ; preds = %_llgo_8, %_llgo_7 %60 = call %"github.com/goplus/llgo/runtime/internal/runtime.iface" @reflect.Value.Type(%reflect.Value %11) %61 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData"(%"github.com/goplus/llgo/runtime/internal/runtime.iface" %60) %62 = extractvalue %"github.com/goplus/llgo/runtime/internal/runtime.iface" %60, 0 - %63 = getelementptr ptr, ptr %62, i64 20 + %63 = getelementptr ptr, ptr %62, i64 18 %64 = load ptr, ptr %63, align 8 %65 = insertvalue { ptr, ptr } undef, ptr %64, 0 %66 = insertvalue { ptr, ptr } %65, ptr %61, 1 @@ -839,7 +839,7 @@ _llgo_9: ; preds = %_llgo_8, %_llgo_7 %72 = call %"github.com/goplus/llgo/runtime/internal/runtime.iface" @reflect.Value.Type(%reflect.Value %11) %73 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData"(%"github.com/goplus/llgo/runtime/internal/runtime.iface" %72) %74 = extractvalue %"github.com/goplus/llgo/runtime/internal/runtime.iface" %72, 0 - %75 = getelementptr ptr, ptr %74, i64 11 + %75 = getelementptr ptr, ptr %74, i64 9 %76 = load ptr, ptr %75, align 8 %77 = insertvalue { ptr, ptr } undef, ptr %76, 0 %78 = insertvalue { ptr, ptr } %77, ptr %73, 1 @@ -1009,7 +1009,7 @@ _llgo_9: ; preds = %_llgo_8, %_llgo_7 %77 = call %"github.com/goplus/llgo/runtime/internal/runtime.iface" @reflect.Value.Type(%reflect.Value %10) %78 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData"(%"github.com/goplus/llgo/runtime/internal/runtime.iface" %77) %79 = extractvalue %"github.com/goplus/llgo/runtime/internal/runtime.iface" %77, 0 - %80 = getelementptr ptr, ptr %79, i64 20 + %80 = getelementptr ptr, ptr %79, i64 18 %81 = load ptr, ptr %80, align 8 %82 = insertvalue { ptr, ptr } undef, ptr %81, 0 %83 = insertvalue { ptr, ptr } %82, ptr %78, 1 @@ -1021,7 +1021,7 @@ _llgo_9: ; preds = %_llgo_8, %_llgo_7 %89 = call %"github.com/goplus/llgo/runtime/internal/runtime.iface" @reflect.Value.Type(%reflect.Value %10) %90 = call ptr @"github.com/goplus/llgo/runtime/internal/runtime.IfacePtrData"(%"github.com/goplus/llgo/runtime/internal/runtime.iface" %89) %91 = extractvalue %"github.com/goplus/llgo/runtime/internal/runtime.iface" %89, 0 - %92 = getelementptr ptr, ptr %91, i64 11 + %92 = getelementptr ptr, ptr %91, i64 9 %93 = load ptr, ptr %92, align 8 %94 = insertvalue { ptr, ptr } undef, ptr %93, 0 %95 = insertvalue { ptr, ptr } %94, ptr %90, 1 diff --git a/runtime/internal/clite/bitcast/_cast/cast.c b/runtime/internal/clite/bitcast/_cast/cast.c index bcc90d64..e948a2c6 100644 --- a/runtime/internal/clite/bitcast/_cast/cast.c +++ b/runtime/internal/clite/bitcast/_cast/cast.c @@ -15,3 +15,15 @@ float llgoToFloat32(long v) { k.v = v; return k.f; } + +long llgoFromFloat64(double v) { + castUnion k; + k.d = v; + return k.v; +} + +long llgoFromFloat32(float v) { + castUnion k; + k.f = v; + return k.v; +} \ No newline at end of file diff --git a/runtime/internal/clite/bitcast/bitcast.go b/runtime/internal/clite/bitcast/bitcast.go index f6d12351..f988fa90 100644 --- a/runtime/internal/clite/bitcast/bitcast.go +++ b/runtime/internal/clite/bitcast/bitcast.go @@ -28,3 +28,9 @@ func ToFloat64(v uintptr) float64 //go:linkname ToFloat32 C.llgoToFloat32 func ToFloat32(v uintptr) float32 + +//go:linkname FromFloat64 C.llgoFromFloat64 +func FromFloat64(v float64) uintptr + +//go:linkname FromFloat32 C.llgoFromFloat32 +func FromFloat32(v float32) uintptr diff --git a/runtime/internal/lib/reflect/type.go b/runtime/internal/lib/reflect/type.go index e0b1dfc5..4df7b4fb 100644 --- a/runtime/internal/lib/reflect/type.go +++ b/runtime/internal/lib/reflect/type.go @@ -924,13 +924,10 @@ func (t *rtype) AssignableTo(u Type) bool { } func (t *rtype) ConvertibleTo(u Type) bool { - /* - if u == nil { - panic("reflect: nil type passed to Type.ConvertibleTo") - } - return convertOp(u.common(), t.common()) != nil - */ - panic("todo: reflect.rtype.ConvertibleTo") + if u == nil { + panic("reflect: nil type passed to Type.ConvertibleTo") + } + return convertOp(u.common(), t.common()) != nil } func (t *rtype) Comparable() bool { diff --git a/runtime/internal/lib/reflect/value.go b/runtime/internal/lib/reflect/value.go index 5fa579b2..0a54d470 100644 --- a/runtime/internal/lib/reflect/value.go +++ b/runtime/internal/lib/reflect/value.go @@ -28,6 +28,7 @@ import ( "github.com/goplus/llgo/runtime/abi" "github.com/goplus/llgo/runtime/internal/clite/bitcast" "github.com/goplus/llgo/runtime/internal/ffi" + "github.com/goplus/llgo/runtime/internal/lib/internal/itoa" "github.com/goplus/llgo/runtime/internal/runtime" "github.com/goplus/llgo/runtime/internal/runtime/goarch" ) @@ -1856,6 +1857,43 @@ func (v Value) assignTo(context string, dst *abi.Type, target unsafe.Pointer) Va panic(context + ": value of type " + stringFor(v.typ()) + " is not assignable to type " + stringFor(dst)) } +// Convert returns the value v converted to type t. +// If the usual Go conversion rules do not allow conversion +// of the value v to type t, or if converting v to type t panics, Convert panics. +func (v Value) Convert(t Type) Value { + if v.flag&flagMethod != 0 { + v = makeMethodValue("Convert", v) + } + op := convertOp(t.common(), v.typ()) + if op == nil { + panic("reflect.Value.Convert: value of type " + stringFor(v.typ()) + " cannot be converted to type " + t.String()) + } + return op(v, t) +} + +// CanConvert reports whether the value v can be converted to type t. +// If v.CanConvert(t) returns true then v.Convert(t) will not panic. +func (v Value) CanConvert(t Type) bool { + vt := v.Type() + if !vt.ConvertibleTo(t) { + return false + } + // Converting from slice to array or to pointer-to-array can panic + // depending on the value. + switch { + case vt.Kind() == Slice && t.Kind() == Array: + if t.Len() > v.Len() { + return false + } + case vt.Kind() == Slice && t.Kind() == Pointer && t.Elem().Kind() == Array: + n := t.Elem().Len() + if n > v.Len() { + return false + } + } + return true +} + // memmove copies size bytes to dst from src. No write barriers are used. // //go:linkname memmove C.memmove @@ -2581,6 +2619,340 @@ func methodReceiver(op string, v Value, methodIndex int) (rcvrtype *abi.Type, t return } +// convertOp returns the function to convert a value of type src +// to a value of type dst. If the conversion is illegal, convertOp returns nil. +func convertOp(dst, src *abi.Type) func(Value, Type) Value { + switch Kind(src.Kind()) { + case Int, Int8, Int16, Int32, Int64: + switch Kind(dst.Kind()) { + case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return cvtInt + case Float32, Float64: + return cvtIntFloat + case String: + return cvtIntString + } + + case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + switch Kind(dst.Kind()) { + case Int, Int8, Int16, Int32, Int64, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return cvtUint + case Float32, Float64: + return cvtUintFloat + case String: + return cvtUintString + } + + case Float32, Float64: + switch Kind(dst.Kind()) { + case Int, Int8, Int16, Int32, Int64: + return cvtFloatInt + case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr: + return cvtFloatUint + case Float32, Float64: + return cvtFloat + } + + case Complex64, Complex128: + switch Kind(dst.Kind()) { + case Complex64, Complex128: + return cvtComplex + } + + case String: + if dst.Kind() == abi.Slice && pkgPathFor(dst.Elem()) == "" { + switch Kind(dst.Elem().Kind()) { + case Uint8: + return cvtStringBytes + case Int32: + return cvtStringRunes + } + } + + case Slice: + if dst.Kind() == abi.String && pkgPathFor(src.Elem()) == "" { + switch Kind(src.Elem().Kind()) { + case Uint8: + return cvtBytesString + case Int32: + return cvtRunesString + } + } + // "x is a slice, T is a pointer-to-array type, + // and the slice and array types have identical element types." + if dst.Kind() == abi.Pointer && dst.Elem().Kind() == abi.Array && src.Elem() == dst.Elem().Elem() { + return cvtSliceArrayPtr + } + // "x is a slice, T is an array type, + // and the slice and array types have identical element types." + if dst.Kind() == abi.Array && src.Elem() == dst.Elem() { + return cvtSliceArray + } + + case Chan: + if dst.Kind() == abi.Chan && specialChannelAssignability(dst, src) { + return cvtDirect + } + } + + // dst and src have same underlying type. + if haveIdenticalUnderlyingType(dst, src, false) { + return cvtDirect + } + + // dst and src are non-defined pointer types with same underlying base type. + if dst.Kind() == abi.Pointer && nameFor(dst) == "" && + src.Kind() == abi.Pointer && nameFor(src) == "" && + haveIdenticalUnderlyingType(elem(dst), elem(src), false) { + return cvtDirect + } + + if implements(dst, src) { + if src.Kind() == abi.Interface { + return cvtI2I + } + return cvtT2I + } + + return nil +} + +// _64bit = true on 64-bit systems, false on 32-bit systems +const is64bit = (1 << (^uintptr(0) >> 63) / 2) == 1 + +// makeInt returns a Value of type t equal to bits (possibly truncated), +// where t is a signed or unsigned int type. +func makeInt(f flag, bits uint64, t Type) Value { + typ := t.common() + var ptr unsafe.Pointer + switch typ.Size() { + case 1, 2, 4: + ptr = unsafe.Pointer(uintptr(bits)) + case 8: + if is64bit { + ptr = unsafe.Pointer(uintptr(bits)) + } else { + ptr = unsafe_New(typ) + *(*uint64)(ptr) = bits + f |= flagIndir + } + } + return Value{typ, ptr, f | flag(typ.Kind())} +} + +// makeFloat returns a Value of type t equal to v (possibly truncated to float32), +// where t is a float32 or float64 type. +func makeFloat(f flag, v float64, t Type) Value { + typ := t.common() + var ptr unsafe.Pointer + switch typ.Size() { + case 4: + ptr = unsafe.Pointer(bitcast.FromFloat32(float32(v))) + case 8: + if is64bit { + ptr = unsafe.Pointer(bitcast.FromFloat64(v)) + } else { + ptr = unsafe_New(typ) + *(*float64)(ptr) = v + f |= flagIndir + } + } + return Value{typ, ptr, f | flag(typ.Kind())} +} + +// makeFloat32 returns a Value of type t equal to v, where t is a float32 type. +func makeFloat32(f flag, ptr unsafe.Pointer, t Type) Value { + typ := t.common() + return Value{typ, ptr, f | flag(typ.Kind())} +} + +// makeComplex returns a Value of type t equal to v (possibly truncated to complex64), +// where t is a complex64 or complex128 type. +func makeComplex(f flag, v complex128, t Type) Value { + typ := t.common() + ptr := unsafe_New(typ) + switch typ.Size() { + case 8: + *(*complex64)(ptr) = complex64(v) + case 16: + *(*complex128)(ptr) = v + } + return Value{typ, ptr, f | flagIndir | flag(typ.Kind())} +} + +func makeString(f flag, v string, t Type) Value { + ret := New(t).Elem() + ret.SetString(v) + ret.flag = ret.flag&^flagAddr | f + return ret +} + +func makeBytes(f flag, v []byte, t Type) Value { + ret := New(t).Elem() + ret.SetBytes(v) + ret.flag = ret.flag&^flagAddr | f + return ret +} + +func makeRunes(f flag, v []rune, t Type) Value { + ret := New(t).Elem() + ret.setRunes(v) + ret.flag = ret.flag&^flagAddr | f + return ret +} + +// These conversion functions are returned by convertOp +// for classes of conversions. For example, the first function, cvtInt, +// takes any value v of signed int type and returns the value converted +// to type t, where t is any signed or unsigned int type. + +// convertOp: intXX -> [u]intXX +func cvtInt(v Value, t Type) Value { + return makeInt(v.flag.ro(), uint64(v.Int()), t) +} + +// convertOp: uintXX -> [u]intXX +func cvtUint(v Value, t Type) Value { + return makeInt(v.flag.ro(), v.Uint(), t) +} + +// convertOp: floatXX -> intXX +func cvtFloatInt(v Value, t Type) Value { + return makeInt(v.flag.ro(), uint64(int64(v.Float())), t) +} + +// convertOp: floatXX -> uintXX +func cvtFloatUint(v Value, t Type) Value { + return makeInt(v.flag.ro(), uint64(v.Float()), t) +} + +// convertOp: intXX -> floatXX +func cvtIntFloat(v Value, t Type) Value { + return makeFloat(v.flag.ro(), float64(v.Int()), t) +} + +// convertOp: uintXX -> floatXX +func cvtUintFloat(v Value, t Type) Value { + return makeFloat(v.flag.ro(), float64(v.Uint()), t) +} + +// convertOp: floatXX -> floatXX +func cvtFloat(v Value, t Type) Value { + if v.Type().Kind() == Float32 && t.Kind() == Float32 { + // Don't do any conversion if both types have underlying type float32. + // This avoids converting to float64 and back, which will + // convert a signaling NaN to a quiet NaN. See issue 36400. + return makeFloat32(v.flag.ro(), v.ptr, t) + } + return makeFloat(v.flag.ro(), v.Float(), t) +} + +// convertOp: complexXX -> complexXX +func cvtComplex(v Value, t Type) Value { + return makeComplex(v.flag.ro(), v.Complex(), t) +} + +// convertOp: intXX -> string +func cvtIntString(v Value, t Type) Value { + s := "\uFFFD" + if x := v.Int(); int64(rune(x)) == x { + s = string(rune(x)) + } + return makeString(v.flag.ro(), s, t) +} + +// convertOp: uintXX -> string +func cvtUintString(v Value, t Type) Value { + s := "\uFFFD" + if x := v.Uint(); uint64(rune(x)) == x { + s = string(rune(x)) + } + return makeString(v.flag.ro(), s, t) +} + +// convertOp: []byte -> string +func cvtBytesString(v Value, t Type) Value { + return makeString(v.flag.ro(), string(v.Bytes()), t) +} + +// convertOp: string -> []byte +func cvtStringBytes(v Value, t Type) Value { + return makeBytes(v.flag.ro(), []byte(v.String()), t) +} + +// convertOp: []rune -> string +func cvtRunesString(v Value, t Type) Value { + return makeString(v.flag.ro(), string(v.runes()), t) +} + +// convertOp: string -> []rune +func cvtStringRunes(v Value, t Type) Value { + return makeRunes(v.flag.ro(), []rune(v.String()), t) +} + +// convertOp: []T -> *[N]T +func cvtSliceArrayPtr(v Value, t Type) Value { + n := t.Elem().Len() + if n > v.Len() { + panic("reflect: cannot convert slice with length " + itoa.Itoa(v.Len()) + " to pointer to array with length " + itoa.Itoa(n)) + } + h := (*unsafeheaderSlice)(v.ptr) + return Value{t.common(), h.Data, v.flag&^(flagIndir|flagAddr|flagKindMask) | flag(Pointer)} +} + +// convertOp: []T -> [N]T +func cvtSliceArray(v Value, t Type) Value { + n := t.Len() + if n > v.Len() { + panic("reflect: cannot convert slice with length " + itoa.Itoa(v.Len()) + " to array with length " + itoa.Itoa(n)) + } + h := (*unsafeheaderSlice)(v.ptr) + typ := t.common() + ptr := h.Data + c := unsafe_New(typ) + typedmemmove(typ, c, ptr) + ptr = c + + return Value{typ, ptr, v.flag&^(flagAddr|flagKindMask) | flag(Array)} +} + +// convertOp: direct copy +func cvtDirect(v Value, typ Type) Value { + f := v.flag + t := typ.common() + ptr := v.ptr + if f&flagAddr != 0 { + // indirect, mutable word - make a copy + c := unsafe_New(t) + typedmemmove(t, c, ptr) + ptr = c + f &^= flagAddr + } + return Value{t, ptr, v.flag.ro() | f} // v.flag.ro()|f == f? +} + +// convertOp: concrete -> interface +func cvtT2I(v Value, typ Type) Value { + target := unsafe_New(typ.common()) + x := valueInterface(v, false) + if typ.NumMethod() == 0 { + *(*any)(target) = x + } else { + ifaceE2I(typ.common(), x, target) + } + return Value{typ.common(), target, v.flag.ro() | flagIndir | flag(Interface)} +} + +// convertOp: interface -> interface +func cvtI2I(v Value, typ Type) Value { + if v.IsNil() { + ret := Zero(typ) + ret.flag |= v.flag.ro() + return ret + } + return cvtT2I(v.Elem(), typ) +} + //go:linkname chancap github.com/goplus/llgo/runtime/internal/runtime.ChanCap func chancap(ch unsafe.Pointer) int