diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/hash/maphash/maphash_runtime.go | 10 | ||||
| -rw-r--r-- | src/hash/maphash/smhasher_test.go | 4 | ||||
| -rw-r--r-- | src/internal/runtime/maps/map.go | 6 | ||||
| -rw-r--r-- | src/internal/runtime/maps/table.go | 3 | ||||
| -rw-r--r-- | src/runtime/alg.go | 33 | ||||
| -rw-r--r-- | src/runtime/hash32.go | 2 | ||||
| -rw-r--r-- | src/runtime/hash64.go | 2 |
7 files changed, 38 insertions, 22 deletions
diff --git a/src/hash/maphash/maphash_runtime.go b/src/hash/maphash/maphash_runtime.go index 36ac638071..5ae23a0218 100644 --- a/src/hash/maphash/maphash_runtime.go +++ b/src/hash/maphash/maphash_runtime.go @@ -8,7 +8,7 @@ package maphash import ( "internal/abi" - "internal/goarch" + "internal/runtime/maps" "unsafe" ) @@ -29,10 +29,10 @@ func rthash(buf []byte, seed uint64) uint64 { // The runtime hasher only works on uintptr. For 64-bit // architectures, we use the hasher directly. Otherwise, // we use two parallel hashers on the lower and upper 32 bits. - if goarch.PtrSize == 8 { + if maps.Use64BitHash { return uint64(runtime_memhash(unsafe.Pointer(&buf[0]), uintptr(seed), uintptr(len))) } - lo := runtime_memhash(unsafe.Pointer(&buf[0]), uintptr(seed), uintptr(len)) + lo := runtime_memhash(unsafe.Pointer(&buf[0]), uintptr(uint32(seed)), uintptr(len)) hi := runtime_memhash(unsafe.Pointer(&buf[0]), uintptr(seed>>32), uintptr(len)) return uint64(hi)<<32 | uint64(lo) } @@ -51,10 +51,10 @@ func comparableHash[T comparable](v T, seed Seed) uint64 { var m map[T]struct{} mTyp := abi.TypeOf(m) hasher := (*abi.MapType)(unsafe.Pointer(mTyp)).Hasher - if goarch.PtrSize == 8 { + if maps.Use64BitHash { return uint64(hasher(abi.NoEscape(unsafe.Pointer(&v)), uintptr(s))) } - lo := hasher(abi.NoEscape(unsafe.Pointer(&v)), uintptr(s)) + lo := hasher(abi.NoEscape(unsafe.Pointer(&v)), uintptr(uint32(s))) hi := hasher(abi.NoEscape(unsafe.Pointer(&v)), uintptr(s>>32)) return uint64(hi)<<32 | uint64(lo) } diff --git a/src/hash/maphash/smhasher_test.go b/src/hash/maphash/smhasher_test.go index b9621b4c4f..86ff7c2cdb 100644 --- a/src/hash/maphash/smhasher_test.go +++ b/src/hash/maphash/smhasher_test.go @@ -8,6 +8,7 @@ package maphash import ( "fmt" + "internal/runtime/maps" "internal/testenv" "math" "math/rand" @@ -15,7 +16,6 @@ import ( "slices" "strings" "testing" - "unsafe" ) // Smhasher is a torture test for hash functions. @@ -486,7 +486,7 @@ func text(t *testing.T, h *hashSet, prefix, suffix string) { // Make sure different seed values generate different hashes. func TestSmhasherSeed(t *testing.T) { - if unsafe.Sizeof(uintptr(0)) == 4 { + if !maps.Use64BitHash { t.Skip("32-bit platforms don't have ideal seed-input distributions (see issue 33988)") } t.Parallel() diff --git a/src/internal/runtime/maps/map.go b/src/internal/runtime/maps/map.go index 865a3f36c2..515558a94f 100644 --- a/src/internal/runtime/maps/map.go +++ b/src/internal/runtime/maps/map.go @@ -245,8 +245,12 @@ type Map struct { clearSeq uint64 } +// Use 64-bit hash on 64-bit systems, except on Wasm, where we use +// 32-bit hash (see runtime/hash32.go). +const Use64BitHash = goarch.PtrSize == 8 && goarch.IsWasm == 0 + func depthToShift(depth uint8) uint8 { - if goarch.PtrSize == 4 { + if !Use64BitHash { return 32 - depth } return 64 - depth diff --git a/src/internal/runtime/maps/table.go b/src/internal/runtime/maps/table.go index d9bcf23dfa..fbce099655 100644 --- a/src/internal/runtime/maps/table.go +++ b/src/internal/runtime/maps/table.go @@ -6,7 +6,6 @@ package maps import ( "internal/abi" - "internal/goarch" "internal/runtime/math" "unsafe" ) @@ -1170,7 +1169,7 @@ func (t *table) rehash(typ *abi.MapType, m *Map) { // Bitmask for the last selection bit at this depth. func localDepthMask(localDepth uint8) uintptr { - if goarch.PtrSize == 4 { + if !Use64BitHash { return uintptr(1) << (32 - localDepth) } return uintptr(1) << (64 - localDepth) diff --git a/src/runtime/alg.go b/src/runtime/alg.go index b956f9d05a..c5951dc20b 100644 --- a/src/runtime/alg.go +++ b/src/runtime/alg.go @@ -14,10 +14,23 @@ import ( ) const ( - c0 = uintptr((8-goarch.PtrSize)/4*2860486313 + (goarch.PtrSize-4)/4*33054211828000289) - c1 = uintptr((8-goarch.PtrSize)/4*3267000013 + (goarch.PtrSize-4)/4*23344194077549503) + // We use 32-bit hash on Wasm, see hash32.go. + hashSize = (1-goarch.IsWasm)*goarch.PtrSize + goarch.IsWasm*4 + c0 = uintptr((8-hashSize)/4*2860486313 + (hashSize-4)/4*33054211828000289) + c1 = uintptr((8-hashSize)/4*3267000013 + (hashSize-4)/4*23344194077549503) ) +func trimHash(h uintptr) uintptr { + if goarch.IsWasm != 0 { + // On Wasm, we use 32-bit hash, despite that uintptr is 64-bit. + // memhash* always returns a uintptr with high 32-bit being 0 + // (see hash32.go). We trim the hash in other places where we + // compute the hash manually, e.g. in interhash. + return uintptr(uint32(h)) + } + return h +} + func memhash0(p unsafe.Pointer, h uintptr) uintptr { return h } @@ -100,9 +113,9 @@ func f32hash(p unsafe.Pointer, h uintptr) uintptr { f := *(*float32)(p) switch { case f == 0: - return c1 * (c0 ^ h) // +0, -0 + return trimHash(c1 * (c0 ^ h)) // +0, -0 case f != f: - return c1 * (c0 ^ h ^ uintptr(rand())) // any kind of NaN + return trimHash(c1 * (c0 ^ h ^ uintptr(rand()))) // any kind of NaN default: return memhash(p, h, 4) } @@ -112,9 +125,9 @@ func f64hash(p unsafe.Pointer, h uintptr) uintptr { f := *(*float64)(p) switch { case f == 0: - return c1 * (c0 ^ h) // +0, -0 + return trimHash(c1 * (c0 ^ h)) // +0, -0 case f != f: - return c1 * (c0 ^ h ^ uintptr(rand())) // any kind of NaN + return trimHash(c1 * (c0 ^ h ^ uintptr(rand()))) // any kind of NaN default: return memhash(p, h, 8) } @@ -145,9 +158,9 @@ func interhash(p unsafe.Pointer, h uintptr) uintptr { panic(errorString("hash of unhashable type " + toRType(t).string())) } if t.IsDirectIface() { - return c1 * typehash(t, unsafe.Pointer(&a.data), h^c0) + return trimHash(c1 * typehash(t, unsafe.Pointer(&a.data), h^c0)) } else { - return c1 * typehash(t, a.data, h^c0) + return trimHash(c1 * typehash(t, a.data, h^c0)) } } @@ -172,9 +185,9 @@ func nilinterhash(p unsafe.Pointer, h uintptr) uintptr { panic(errorString("hash of unhashable type " + toRType(t).string())) } if t.IsDirectIface() { - return c1 * typehash(t, unsafe.Pointer(&a.data), h^c0) + return trimHash(c1 * typehash(t, unsafe.Pointer(&a.data), h^c0)) } else { - return c1 * typehash(t, a.data, h^c0) + return trimHash(c1 * typehash(t, a.data, h^c0)) } } diff --git a/src/runtime/hash32.go b/src/runtime/hash32.go index 0616c7dd05..8589893127 100644 --- a/src/runtime/hash32.go +++ b/src/runtime/hash32.go @@ -5,7 +5,7 @@ // Hashing algorithm inspired by // wyhash: https://github.com/wangyi-fudan/wyhash/blob/ceb019b530e2c1c14d70b79bfa2bc49de7d95bc1/Modern%20Non-Cryptographic%20Hash%20Function%20and%20Pseudorandom%20Number%20Generator.pdf -//go:build 386 || arm || mips || mipsle +//go:build 386 || arm || mips || mipsle || wasm package runtime diff --git a/src/runtime/hash64.go b/src/runtime/hash64.go index 124bb7d77a..ac26e660c6 100644 --- a/src/runtime/hash64.go +++ b/src/runtime/hash64.go @@ -5,7 +5,7 @@ // Hashing algorithm inspired by // wyhash: https://github.com/wangyi-fudan/wyhash -//go:build amd64 || arm64 || loong64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x || wasm +//go:build amd64 || arm64 || loong64 || mips64 || mips64le || ppc64 || ppc64le || riscv64 || s390x package runtime |
