Skip to content

Commit 7e327cc

Browse files
committed
src/{reflect,runtime}: break circular dependency from runtime to reflect
Use go:linkname, move code around.
1 parent b1c8377 commit 7e327cc

File tree

4 files changed

+162
-140
lines changed

4 files changed

+162
-140
lines changed

src/reflect/hashmap.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package reflect
2+
3+
import (
4+
"unsafe"
5+
)
6+
7+
//go:linkname hash32 runtime.hash32
8+
func hash32(ptr unsafe.Pointer, n, seed uintptr) uint32
9+
10+
//go:linkname hashmapStringHash runtime.hashmapStringHash
11+
func hashmapStringHash(s string, seed uintptr) uint32
12+
13+
func hashmapFloat32Hash(ptr unsafe.Pointer, seed uintptr) uint32 {
14+
f := *(*uint32)(ptr)
15+
if f == 0x80000000 {
16+
// convert -0 to 0 for hashing
17+
f = 0
18+
}
19+
return hash32(unsafe.Pointer(&f), 4, seed)
20+
}
21+
22+
func hashmapFloat64Hash(ptr unsafe.Pointer, seed uintptr) uint32 {
23+
f := *(*uint64)(ptr)
24+
if f == 0x8000000000000000 {
25+
// convert -0 to 0 for hashing
26+
f = 0
27+
}
28+
return hash32(unsafe.Pointer(&f), 8, seed)
29+
}
30+
31+
func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
32+
x := ValueOf(itf)
33+
if x.RawType() == nil {
34+
return 0 // nil interface
35+
}
36+
37+
value := (*_interface)(unsafe.Pointer(&itf)).value
38+
ptr := value
39+
if x.RawType().Size() <= unsafe.Sizeof(uintptr(0)) {
40+
// Value fits in pointer, so it's directly stored in the pointer.
41+
ptr = unsafe.Pointer(&value)
42+
}
43+
44+
switch x.RawType().Kind() {
45+
case Int, Int8, Int16, Int32, Int64:
46+
return hash32(ptr, x.RawType().Size(), seed)
47+
case Bool, Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
48+
return hash32(ptr, x.RawType().Size(), seed)
49+
case Float32:
50+
// It should be possible to just has the contents. However, NaN != NaN
51+
// so if you're using lots of NaNs as map keys (you shouldn't) then hash
52+
// time may become exponential. To fix that, it would be better to
53+
// return a random number instead:
54+
// https://research.swtch.com/randhash
55+
return hashmapFloat32Hash(ptr, seed)
56+
case Float64:
57+
return hashmapFloat64Hash(ptr, seed)
58+
case Complex64:
59+
rptr, iptr := ptr, unsafe.Add(ptr, 4)
60+
return hashmapFloat32Hash(rptr, seed) ^ hashmapFloat32Hash(iptr, seed)
61+
case Complex128:
62+
rptr, iptr := ptr, unsafe.Add(ptr, 8)
63+
return hashmapFloat64Hash(rptr, seed) ^ hashmapFloat64Hash(iptr, seed)
64+
case String:
65+
return hashmapStringHash(x.String(), seed)
66+
case Chan, Ptr, UnsafePointer:
67+
// It might seem better to just return the pointer, but that won't
68+
// result in an evenly distributed hashmap. Instead, hash the pointer
69+
// like most other types.
70+
return hash32(ptr, x.RawType().Size(), seed)
71+
case Array:
72+
var hash uint32
73+
for i := 0; i < x.Len(); i++ {
74+
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Index(i)), seed)
75+
}
76+
return hash
77+
case Struct:
78+
var hash uint32
79+
for i := 0; i < x.NumField(); i++ {
80+
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Field(i)), seed)
81+
}
82+
return hash
83+
default:
84+
runtimePanic("comparing un-comparable type")
85+
return 0 // unreachable
86+
}
87+
}

src/reflect/interface.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package reflect
2+
3+
import "unsafe"
4+
5+
// Cause a runtime panic, which is (currently) always a string.
6+
//
7+
//go:linkname runtimePanic runtime.runtimePanic
8+
func runtimePanic(msg string)
9+
10+
type _interface struct {
11+
typecode unsafe.Pointer
12+
value unsafe.Pointer
13+
}
14+
15+
// Return true iff both interfaces are equal.
16+
func interfaceEqual(x, y interface{}) bool {
17+
return reflectValueEqual(ValueOf(x), ValueOf(y))
18+
}
19+
20+
func reflectValueEqual(x, y Value) bool {
21+
// Note: doing a x.Type() == y.Type() comparison would not work here as that
22+
// would introduce an infinite recursion: comparing two Type values
23+
// is done with this reflectValueEqual runtime call.
24+
if x.RawType() == nil || y.RawType() == nil {
25+
// One of them is nil.
26+
return x.RawType() == y.RawType()
27+
}
28+
29+
if x.RawType() != y.RawType() {
30+
// The type is not the same, which means the interfaces are definitely
31+
// not the same.
32+
return false
33+
}
34+
35+
switch x.RawType().Kind() {
36+
case Bool:
37+
return x.Bool() == y.Bool()
38+
case Int, Int8, Int16, Int32, Int64:
39+
return x.Int() == y.Int()
40+
case Uint, Uint8, Uint16, Uint32, Uint64, Uintptr:
41+
return x.Uint() == y.Uint()
42+
case Float32, Float64:
43+
return x.Float() == y.Float()
44+
case Complex64, Complex128:
45+
return x.Complex() == y.Complex()
46+
case String:
47+
return x.String() == y.String()
48+
case Chan, Ptr, UnsafePointer:
49+
return x.UnsafePointer() == y.UnsafePointer()
50+
case Array:
51+
for i := 0; i < x.Len(); i++ {
52+
if !reflectValueEqual(x.Index(i), y.Index(i)) {
53+
return false
54+
}
55+
}
56+
return true
57+
case Struct:
58+
for i := 0; i < x.NumField(); i++ {
59+
if !reflectValueEqual(x.Field(i), y.Field(i)) {
60+
return false
61+
}
62+
}
63+
return true
64+
case Interface:
65+
return reflectValueEqual(x.Elem(), y.Elem())
66+
default:
67+
runtimePanic("comparing un-comparable type")
68+
return false // unreachable
69+
}
70+
}

src/runtime/hashmap.go

Lines changed: 2 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package runtime
66
// https://golang.org/src/runtime/map.go
77

88
import (
9-
"reflect"
109
"tinygo"
1110
"unsafe"
1211
)
@@ -534,89 +533,8 @@ func hashmapStringDelete(m *hashmap, key string) {
534533

535534
// Hashmap with interface keys (for everything else).
536535

537-
// This is a method that is intentionally unexported in the reflect package. It
538-
// is identical to the Interface() method call, except it doesn't check whether
539-
// a field is exported and thus allows circumventing the type system.
540-
// The hash function needs it as it also needs to hash unexported struct fields.
541-
//
542-
//go:linkname valueInterfaceUnsafe reflect.valueInterfaceUnsafe
543-
func valueInterfaceUnsafe(v reflect.Value) interface{}
544-
545-
func hashmapFloat32Hash(ptr unsafe.Pointer, seed uintptr) uint32 {
546-
f := *(*uint32)(ptr)
547-
if f == 0x80000000 {
548-
// convert -0 to 0 for hashing
549-
f = 0
550-
}
551-
return hash32(unsafe.Pointer(&f), 4, seed)
552-
}
553-
554-
func hashmapFloat64Hash(ptr unsafe.Pointer, seed uintptr) uint32 {
555-
f := *(*uint64)(ptr)
556-
if f == 0x8000000000000000 {
557-
// convert -0 to 0 for hashing
558-
f = 0
559-
}
560-
return hash32(unsafe.Pointer(&f), 8, seed)
561-
}
562-
563-
func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32 {
564-
x := reflect.ValueOf(itf)
565-
if x.RawType() == nil {
566-
return 0 // nil interface
567-
}
568-
569-
value := (*_interface)(unsafe.Pointer(&itf)).value
570-
ptr := value
571-
if x.RawType().Size() <= unsafe.Sizeof(uintptr(0)) {
572-
// Value fits in pointer, so it's directly stored in the pointer.
573-
ptr = unsafe.Pointer(&value)
574-
}
575-
576-
switch x.RawType().Kind() {
577-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
578-
return hash32(ptr, x.RawType().Size(), seed)
579-
case reflect.Bool, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
580-
return hash32(ptr, x.RawType().Size(), seed)
581-
case reflect.Float32:
582-
// It should be possible to just has the contents. However, NaN != NaN
583-
// so if you're using lots of NaNs as map keys (you shouldn't) then hash
584-
// time may become exponential. To fix that, it would be better to
585-
// return a random number instead:
586-
// https://research.swtch.com/randhash
587-
return hashmapFloat32Hash(ptr, seed)
588-
case reflect.Float64:
589-
return hashmapFloat64Hash(ptr, seed)
590-
case reflect.Complex64:
591-
rptr, iptr := ptr, unsafe.Add(ptr, 4)
592-
return hashmapFloat32Hash(rptr, seed) ^ hashmapFloat32Hash(iptr, seed)
593-
case reflect.Complex128:
594-
rptr, iptr := ptr, unsafe.Add(ptr, 8)
595-
return hashmapFloat64Hash(rptr, seed) ^ hashmapFloat64Hash(iptr, seed)
596-
case reflect.String:
597-
return hashmapStringHash(x.String(), seed)
598-
case reflect.Chan, reflect.Ptr, reflect.UnsafePointer:
599-
// It might seem better to just return the pointer, but that won't
600-
// result in an evenly distributed hashmap. Instead, hash the pointer
601-
// like most other types.
602-
return hash32(ptr, x.RawType().Size(), seed)
603-
case reflect.Array:
604-
var hash uint32
605-
for i := 0; i < x.Len(); i++ {
606-
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Index(i)), seed)
607-
}
608-
return hash
609-
case reflect.Struct:
610-
var hash uint32
611-
for i := 0; i < x.NumField(); i++ {
612-
hash ^= hashmapInterfaceHash(valueInterfaceUnsafe(x.Field(i)), seed)
613-
}
614-
return hash
615-
default:
616-
runtimePanic("comparing un-comparable type")
617-
return 0 // unreachable
618-
}
619-
}
536+
//go:linkname hashmapInterfaceHash reflect.hashmapInterfaceHash
537+
func hashmapInterfaceHash(itf interface{}, seed uintptr) uint32
620538

621539
func hashmapInterfacePtrHash(iptr unsafe.Pointer, size uintptr, seed uintptr) uint32 {
622540
_i := *(*interface{})(iptr)

src/runtime/interface.go

Lines changed: 3 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package runtime
66
// anything (including non-pointers).
77

88
import (
9-
"reflect"
109
"unsafe"
1110
)
1211

@@ -26,61 +25,9 @@ func decomposeInterface(i _interface) (unsafe.Pointer, unsafe.Pointer) {
2625
}
2726

2827
// Return true iff both interfaces are equal.
29-
func interfaceEqual(x, y interface{}) bool {
30-
return reflectValueEqual(reflect.ValueOf(x), reflect.ValueOf(y))
31-
}
32-
33-
func reflectValueEqual(x, y reflect.Value) bool {
34-
// Note: doing a x.Type() == y.Type() comparison would not work here as that
35-
// would introduce an infinite recursion: comparing two reflect.Type values
36-
// is done with this reflectValueEqual runtime call.
37-
if x.RawType() == nil || y.RawType() == nil {
38-
// One of them is nil.
39-
return x.RawType() == y.RawType()
40-
}
41-
42-
if x.RawType() != y.RawType() {
43-
// The type is not the same, which means the interfaces are definitely
44-
// not the same.
45-
return false
46-
}
47-
48-
switch x.RawType().Kind() {
49-
case reflect.Bool:
50-
return x.Bool() == y.Bool()
51-
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
52-
return x.Int() == y.Int()
53-
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
54-
return x.Uint() == y.Uint()
55-
case reflect.Float32, reflect.Float64:
56-
return x.Float() == y.Float()
57-
case reflect.Complex64, reflect.Complex128:
58-
return x.Complex() == y.Complex()
59-
case reflect.String:
60-
return x.String() == y.String()
61-
case reflect.Chan, reflect.Ptr, reflect.UnsafePointer:
62-
return x.UnsafePointer() == y.UnsafePointer()
63-
case reflect.Array:
64-
for i := 0; i < x.Len(); i++ {
65-
if !reflectValueEqual(x.Index(i), y.Index(i)) {
66-
return false
67-
}
68-
}
69-
return true
70-
case reflect.Struct:
71-
for i := 0; i < x.NumField(); i++ {
72-
if !reflectValueEqual(x.Field(i), y.Field(i)) {
73-
return false
74-
}
75-
}
76-
return true
77-
case reflect.Interface:
78-
return reflectValueEqual(x.Elem(), y.Elem())
79-
default:
80-
runtimePanic("comparing un-comparable type")
81-
return false // unreachable
82-
}
83-
}
28+
//
29+
//go:linkname interfaceEqual reflect.interfaceEqual
30+
func interfaceEqual(x, y interface{}) bool
8431

8532
// interfaceTypeAssert is called when a type assert without comma-ok still
8633
// returns false.

0 commit comments

Comments
 (0)