internal/runtime/maps,runtime/: pass keys by value to MemHash{32,64} and StrHash.

This removes redundant loads and stores when hashing keys in specialized maps and avoids treating keys as address-taken, which previously caused unnecessary reloads inside the loop.

Also remove the separate strHashAES implementation and route string hashing through memHashAES directly, simplifying the AES hashing code paths.

Change-Id: I498ae9f437ad089df4298aa85f04a16c2ed6a5f5
Reviewed-on: https://go-review.googlesource.com/c/go/+/776281
Reviewed-by: Keith Randall <khr@golang.org>
Auto-Submit: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
ArsenySamoylov 2026-05-09 14:42:18 +03:00 committed by Gopher Robot
parent e26a373785
commit 05f75fb9e8
16 changed files with 137 additions and 181 deletions

View file

@ -5,43 +5,36 @@
#include "textflag.h"
// hash function using AES hardware instructions
// func memHash32AES(k uint32, h uintptr) uintptr
TEXT ·memHash32AES(SB),NOSPLIT,$0-12
MOVL p+0(FP), AX // ptr to data
MOVL h+4(FP), X0 // seed
PINSRD $1, (AX), X0 // data
MOVL h+4(FP), X0 // seed
PINSRD $1, k+0(FP), X0 // data
AESENC ·aeskeysched+0(SB), X0
AESENC ·aeskeysched+16(SB), X0
AESENC ·aeskeysched+32(SB), X0
MOVL X0, ret+8(FP)
RET
TEXT ·memHash64AES(SB),NOSPLIT,$0-12
MOVL p+0(FP), AX // ptr to data
MOVQ (AX), X0 // data
PINSRD $2, h+4(FP), X0 // seed
// func memHash64AES(k uint64, h uintptr) uintptr
TEXT ·memHash64AES(SB),NOSPLIT,$0-16
MOVQ k+0(FP), X0 // data
PINSRD $2, h+8(FP), X0 // seed
AESENC ·aeskeysched+0(SB), X0
AESENC ·aeskeysched+16(SB), X0
AESENC ·aeskeysched+32(SB), X0
MOVL X0, ret+8(FP)
MOVL X0, ret+12(FP)
RET
// func memHashAES(p unsafe.Pointer, h, size uintptr) uintptr
TEXT ·memHashAES(SB),NOSPLIT,$0-16
MOVL p+0(FP), AX // ptr to data
MOVL s+8(FP), BX // size
// AX: data
// BX: size
// DX: address to put return value
MOVL p+0(FP), AX
MOVL s+8(FP), BX
LEAL ret+12(FP), DX
JMP ·aeshashbody<>(SB)
TEXT ·strHashAES(SB),NOSPLIT,$0-12
MOVL p+0(FP), AX // ptr to string object
MOVL 4(AX), BX // length of string
MOVL (AX), AX // string data
LEAL ret+8(FP), DX
JMP ·aeshashbody<>(SB)
// AX: data
// BX: length
// DX: address to put return value
TEXT ·aeshashbody<>(SB),NOSPLIT,$0-0
MOVL h+4(FP), X0 // 32 bits of per-table hash seed
PINSRW $4, BX, X0 // 16 bits of length
PSHUFHW $0, X0, X0 // replace size with its low 2 bytes repeated 4 times

View file

@ -19,23 +19,20 @@ func MemHash(p unsafe.Pointer, h, s uintptr) uintptr {
return memHashFallback(p, h, s)
}
func MemHash32(p unsafe.Pointer, h uintptr) uintptr {
func MemHash32(k uint32, h uintptr) uintptr {
if UseAeshash {
return memHash32AES(p, h)
return memHash32AES(k, h)
}
return memHash32Fallback(p, h)
return memHash32Fallback(k, h)
}
func MemHash64(p unsafe.Pointer, h uintptr) uintptr {
func MemHash64(k uint64, h uintptr) uintptr {
if UseAeshash {
return memHash64AES(p, h)
return memHash64AES(k, h)
}
return memHash64Fallback(p, h)
return memHash64Fallback(k, h)
}
func StrHash(p unsafe.Pointer, h uintptr) uintptr {
if UseAeshash {
return strHashAES(p, h)
}
return strHashFallback(p, h)
func StrHash(s string, h uintptr) uintptr {
return MemHash(unsafe.Pointer(unsafe.StringData(s)), h, uintptr(len(s)))
}

View file

@ -18,10 +18,7 @@ const memHashUsesVAES = false
func memHashAES(p unsafe.Pointer, h, s uintptr) uintptr
//go:noescape
func memHash32AES(p unsafe.Pointer, h uintptr) uintptr
func memHash32AES(k uint32, h uintptr) uintptr
//go:noescape
func memHash64AES(p unsafe.Pointer, h uintptr) uintptr
//go:noescape
func strHashAES(p unsafe.Pointer, h uintptr) uintptr
func memHash64AES(k uint64, h uintptr) uintptr

View file

@ -13,9 +13,9 @@ import (
const memHashUsesVAES = true
func memHash32AES(p unsafe.Pointer, seed uintptr) uintptr {
func memHash32AES(k uint32, seed uintptr) uintptr {
var state archsimd.Uint64x2
state = state.SetElem(0, uint64(seed)).SetElem(1, uint64(*(*uint32)(p)))
state = state.SetElem(0, uint64(seed)).SetElem(1, uint64(k))
hash := state.
AsUint8x16().
@ -27,9 +27,9 @@ func memHash32AES(p unsafe.Pointer, seed uintptr) uintptr {
return uintptr(hash)
}
func memHash64AES(p unsafe.Pointer, seed uintptr) uintptr {
func memHash64AES(k uint64, seed uintptr) uintptr {
var state archsimd.Uint64x2
state = state.SetElem(0, uint64(seed)).SetElem(1, *(*uint64)(p))
state = state.SetElem(0, uint64(seed)).SetElem(1, k)
hash := state.
AsUint8x16().
@ -41,12 +41,9 @@ func memHash64AES(p unsafe.Pointer, seed uintptr) uintptr {
return uintptr(hash)
}
// TODO: Both strHashAES and memHashAES use aeshashbody that is quite large.
// So there is no point in rewriting them using simd intrinsics, since they won't be inlinable.
// TODO: memHashAES is quite large.
// So there is no point in rewriting it using simd intrinsics, since it won't be inlinable.
// Maybe in future we can do it for better maitanability.
//
//go:noescape
func memHashAES(p unsafe.Pointer, h, s uintptr) uintptr
//go:noescape
func strHashAES(p unsafe.Pointer, h uintptr) uintptr

View file

@ -7,24 +7,11 @@
// func memHashAES(p unsafe.Pointer, h, s uintptr) uintptr
// hash function using AES hardware instructions
TEXT ·memHashAES<ABIInternal>(SB),NOSPLIT,$0-32
// AX = ptr to data
// BX = seed
// CX = size
JMP ·aeshashbody<>(SB)
// AX: data
// BX: hash seed
// CX: length
// At return: AX = return value
// func strhashAES(p unsafe.Pointer, h uintptr) uintptr
TEXT ·strHashAES<ABIInternal>(SB),NOSPLIT,$0-24
// AX = ptr to string struct
// BX = seed
MOVQ 8(AX), CX // length of string
MOVQ (AX), AX // string data
JMP ·aeshashbody<>(SB)
// AX: data
// BX: hash seed
// CX: length
// At return: AX = return value
TEXT ·aeshashbody<>(SB),NOSPLIT,$0-0
// Fill an SSE register with our seeds.
MOVQ BX, X0 // 64 bits of per-table hash seed
PINSRW $4, CX, X0 // 16 bits of length

View file

@ -4,13 +4,13 @@
#include "textflag.h"
// func memHash32AES(p unsafe.Pointer, h uintptr) uintptr
// func memHash32AES(k uint32, h uintptr) uintptr
TEXT ·memHash32AES<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
MOVD $·aeskeysched+0(SB), R3
VEOR V0.B16, V0.B16, V0.B16
VLD1 (R3), [V2.B16]
VLD1 (R0), V0.S[2]
VMOV R0, V0.S[2]
VMOV R1, V0.D[0]
AESE V2.B16, V0.B16
@ -22,13 +22,13 @@ TEXT ·memHash32AES<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
VMOV V0.D[0], R0
RET
// func memHash64AES(p unsafe.Pointer, h uintptr) uintptr
// func memHash64AES(k uint64, h uintptr) uintptr
TEXT ·memHash64AES<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
MOVD $·aeskeysched+0(SB), R3
VEOR V0.B16, V0.B16, V0.B16
VLD1 (R3), [V2.B16]
VLD1 (R0), V0.D[1]
VMOV R0, V0.D[1]
VMOV R1, V0.D[0]
AESE V2.B16, V0.B16
@ -42,18 +42,10 @@ TEXT ·memHash64AES<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
// func memHashAES(p unsafe.Pointer, h, size uintptr) uintptr
TEXT ·memHashAES<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-32
B ·aeshashbody<>(SB)
// func strHashAES(p unsafe.Pointer, h uintptr) uintptr
TEXT ·strHashAES<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
LDP (R0), (R0, R2) // string data / length
B ·aeshashbody<>(SB)
// R0: data
// R1: seed data
// R2: length
// At return, R0 = return value
TEXT ·aeshashbody<>(SB),NOSPLIT|NOFRAME,$0
// R0: data
// R1: seed data
// R2: length
// At return, R0 = return value
VEOR V30.B16, V30.B16, V30.B16
VMOV R1, V30.D[0]
VMOV R2, V30.D[1] // load length into seed

View file

@ -14,30 +14,30 @@ import (
const memHashAESImplemented = false
const memHashUsesVAES = false
func memHash32AES(p unsafe.Pointer, h uintptr) uintptr {
func memHash32AES(k uint32, h uintptr) uintptr {
panic("memHash32AES not implemented")
}
func memHash64AES(p unsafe.Pointer, h uintptr) uintptr {
func memHash64AES(k uint64, h uintptr) uintptr {
panic("memHash64AES not implemented")
}
func strHashAES(p unsafe.Pointer, h uintptr) uintptr {
panic("strHashAES not implemented")
func memHashAES(p unsafe.Pointer, h, s uintptr) uintptr {
panic("memHashAES not implemented")
}
func MemHash(p unsafe.Pointer, h, s uintptr) uintptr {
return memHashFallback(p, h, s)
}
func MemHash32(p unsafe.Pointer, h uintptr) uintptr {
return memHash32Fallback(p, h)
func MemHash32(k uint32, h uintptr) uintptr {
return memHash32Fallback(k, h)
}
func MemHash64(p unsafe.Pointer, h uintptr) uintptr {
return memHash64Fallback(p, h)
func MemHash64(k uint64, h uintptr) uintptr {
return memHash64Fallback(k, h)
}
func StrHash(p unsafe.Pointer, h uintptr) uintptr {
return strHashFallback(p, h)
func StrHash(s string, h uintptr) uintptr {
return memHashFallback(unsafe.Pointer(unsafe.StringData(s)), h, uintptr(len(s)))
}

View file

@ -6,26 +6,26 @@
#include "textflag.h"
// func memHash32AES(p unsafe.Pointer, h uintptr) uintptr
// func memHash32AES(k uint32, h uintptr) uintptr
// ABIInternal for performance.
TEXT ·memHash32AES<ABIInternal>(SB),NOSPLIT,$0-24
// AX = ptr to data
// BX = seed
MOVQ BX, X0 // X0 = seed
PINSRD $2, (AX), X0 // data
MOVQ BX, X0 // X0 = seed
PINSRD $2, AX, X0 // data
AESENC ·aeskeysched+0(SB), X0
AESENC ·aeskeysched+16(SB), X0
AESENC ·aeskeysched+32(SB), X0
MOVQ X0, AX // return X0
RET
// func memHash64AES(p unsafe.Pointer, h uintptr) uintptr
// func memHash64AES(k uint64, h uintptr) uintptr
// ABIInternal for performance.
TEXT ·memHash64AES<ABIInternal>(SB),NOSPLIT,$0-24
// AX = ptr to data
// BX = seed
MOVQ BX, X0 // X0 = seed
PINSRQ $1, (AX), X0 // data
MOVQ BX, X0 // X0 = seed
PINSRQ $1, AX, X0 // data
AESENC ·aeskeysched+0(SB), X0
AESENC ·aeskeysched+16(SB), X0
AESENC ·aeskeysched+32(SB), X0

View file

@ -38,8 +38,7 @@ func AlgInit() {
cpu.X86.HasSSSE3 && // PSHUFB
cpu.X86.HasSSE41 { // PINSR{D,Q}
// In aeshashbody (that is used by memhash & strhash)
// we have global variables that should be properly aligned.
// In memHashAES we have global variables that should be properly aligned.
//
// See #12415
if !checkMasksAndShiftsAlignment() {
@ -71,15 +70,6 @@ func initAlgAES() {
}
}
func strHashFallback(a unsafe.Pointer, h uintptr) uintptr {
type stringStruct struct {
str unsafe.Pointer
len int
}
x := (*stringStruct)(a)
return memHashFallback(x.str, h, uintptr(x.len))
}
//go:nosplit
func add(p unsafe.Pointer, x uintptr) unsafe.Pointer {
return unsafe.Pointer(uintptr(p) + x)

View file

@ -63,18 +63,6 @@ func runtime_mapaccess2_fast32(typ *abi.MapType, m *Map, key uint32) (unsafe.Poi
return unsafe.Pointer(&zeroVal[0]), false
}
// Don't pass address of the key directly to the hashing function.
// Hashing functions are implemented in Go assembly and cannot be inlined,
// so compiler doesn't optimize redundant address taking/dereference.
//
// Taking &key makes compiler treat key as address-taken, which forces it to spill on the stack
// and reload it in the loop.
// This is suboptimal for performance.
//
// Note: Even when we pass k (local copy of key), the compiler still spills the key to the stack.
// However, from compiler's perspective, key is no longer address-taken and
// filled back in register before the loop.
k := key
var hash uintptr
// Explicitly inline MemHash32.
// MemHash32 cost is higher than the threshold for inlining.
@ -84,9 +72,9 @@ func runtime_mapaccess2_fast32(typ *abi.MapType, m *Map, key uint32) (unsafe.Poi
// Note: memHashAESImplemented is compile time constant. We use it to remove runtime UseAeshash check
// for architectures where we don't have AES hashing implementations.
if memHashAESImplemented && UseAeshash {
hash = memHash32AES(unsafe.Pointer(&k), m.seed)
hash = memHash32AES(key, m.seed)
} else {
hash = memHash32Fallback(unsafe.Pointer(&k), m.seed)
hash = memHash32Fallback(key, m.seed)
}
// Select table.
@ -209,15 +197,12 @@ func runtime_mapassign_fast32(typ *abi.MapType, m *Map, key uint32) unsafe.Point
fatal("concurrent map writes")
}
// See the related comment in runtime_mapaccess2_fast32
// for why we pass local copy of key.
k := key
var hash uintptr
// See the related comment in runtime_mapaccess2_fast32
if memHashAESImplemented && UseAeshash {
hash = memHash32AES(unsafe.Pointer(&k), m.seed)
hash = memHash32AES(key, m.seed)
} else {
hash = memHash32Fallback(unsafe.Pointer(&k), m.seed)
hash = memHash32Fallback(key, m.seed)
}
// Set writing after calling Hasher, since Hasher may panic, in which
@ -361,15 +346,12 @@ func runtime_mapassign_fast32ptr(typ *abi.MapType, m *Map, key unsafe.Pointer) u
fatal("concurrent map writes")
}
// See the related comment in runtime_mapaccess2_fast32
// for why we pass local copy of key.
k := key
var hash uintptr
// See the related comment in runtime_mapaccess2_fast32
if memHashAESImplemented && UseAeshash {
hash = memHash32AES(unsafe.Pointer(&k), m.seed)
hash = memHash32AES(uint32((uintptr)(key)), m.seed)
} else {
hash = memHash32Fallback(unsafe.Pointer(&k), m.seed)
hash = memHash32Fallback(uint32((uintptr)(key)), m.seed)
}
// Set writing after calling Hasher, since Hasher may panic, in which

View file

@ -63,15 +63,12 @@ func runtime_mapaccess2_fast64(typ *abi.MapType, m *Map, key uint64) (unsafe.Poi
return unsafe.Pointer(&zeroVal[0]), false
}
// See the related comment in runtime_mapaccess2_fast32
// for why we pass local copy of key.
k := key
var hash uintptr
// See the related comment in runtime_mapaccess2_fast32
if memHashAESImplemented && UseAeshash {
hash = memHash64AES(unsafe.Pointer(&k), m.seed)
hash = memHash64AES(key, m.seed)
} else {
hash = memHash64Fallback(unsafe.Pointer(&k), m.seed)
hash = memHash64Fallback(key, m.seed)
}
// Select table.
@ -195,15 +192,12 @@ func runtime_mapassign_fast64(typ *abi.MapType, m *Map, key uint64) unsafe.Point
fatal("concurrent map writes")
}
// See the related comment in runtime_mapaccess2_fast32
// for why we pass local copy of key.
k := key
var hash uintptr
// See the related comment in runtime_mapaccess2_fast32
if memHashAESImplemented && UseAeshash {
hash = memHash64AES(unsafe.Pointer(&k), m.seed)
hash = memHash64AES(key, m.seed)
} else {
hash = memHash64Fallback(unsafe.Pointer(&k), m.seed)
hash = memHash64Fallback(key, m.seed)
}
// Set writing after calling Hasher, since Hasher may panic, in which
@ -416,15 +410,12 @@ func runtime_mapassign_fast64ptr(typ *abi.MapType, m *Map, key unsafe.Pointer) u
fatal("concurrent map writes")
}
// See the related comment in runtime_mapaccess2_fast32
// for why we pass local copy of key.
k := key
var hash uintptr
// See the related comment in runtime_mapaccess2_fast32
if memHashAESImplemented && UseAeshash {
hash = memHash64AES(unsafe.Pointer(&k), m.seed)
hash = memHash64AES(uint64((uintptr)(key)), m.seed)
} else {
hash = memHash64Fallback(unsafe.Pointer(&k), m.seed)
hash = memHash64Fallback(uint64((uintptr)(key)), m.seed)
}
// Set writing after calling Hasher, since Hasher may panic, in which

View file

@ -64,11 +64,13 @@ func (m *Map) getWithoutKeySmallFastStr(typ *abi.MapType, key string) unsafe.Poi
dohash:
// This path will cost 1 hash and 1+ε comparisons.
var hash uintptr
// See the related comment in runtime_mapaccess2_fast32
// for why we pass local copy of key.
k := key
hash := StrHash(unsafe.Pointer(&k), m.seed)
if memHashAESImplemented && UseAeshash {
hash = memHashAES(unsafe.Pointer(unsafe.StringData(key)), m.seed, uintptr(len(key)))
} else {
hash = memHashFallback(unsafe.Pointer(unsafe.StringData(key)), m.seed, uintptr(len(key)))
}
h2 := uint8(h2(hash))
ctrls = *g.ctrls()
slotKey = g.key(typ, 0)
@ -94,26 +96,19 @@ func longStringQuickEqualityTest(a, b string) bool {
if len(a) != len(b) {
return false
}
x, y := stringPtr(a), stringPtr(b)
x, y := unsafe.Pointer(unsafe.StringData(a)), unsafe.Pointer(unsafe.StringData(b))
// Check first 8 bytes.
if *(*[8]byte)(x) != *(*[8]byte)(y) {
return false
}
// Check last 8 bytes.
x = unsafe.Pointer(uintptr(x) + uintptr(len(a)) - 8)
y = unsafe.Pointer(uintptr(y) + uintptr(len(a)) - 8)
x = add(x, uintptr(len(a)-8))
y = add(y, uintptr(len(a)-8))
if *(*[8]byte)(x) != *(*[8]byte)(y) {
return false
}
return true
}
func stringPtr(s string) unsafe.Pointer {
type stringStruct struct {
ptr unsafe.Pointer
len int
}
return (*stringStruct)(unsafe.Pointer(&s)).ptr
}
//go:linkname runtime_mapaccess1_faststr runtime.mapaccess1_faststr
func runtime_mapaccess1_faststr(typ *abi.MapType, m *Map, key string) unsafe.Pointer {
@ -146,10 +141,13 @@ func runtime_mapaccess2_faststr(typ *abi.MapType, m *Map, key string) (unsafe.Po
return elem, true
}
var hash uintptr
// See the related comment in runtime_mapaccess2_fast32
// for why we pass local copy of key.
k := key
hash := StrHash(unsafe.Pointer(&k), m.seed)
if memHashAESImplemented && UseAeshash {
hash = memHashAES(unsafe.Pointer(unsafe.StringData(key)), m.seed, uintptr(len(key)))
} else {
hash = memHashFallback(unsafe.Pointer(unsafe.StringData(key)), m.seed, uintptr(len(key)))
}
// Select table.
idx := m.directoryIndex(hash)
@ -273,10 +271,13 @@ func runtime_mapassign_faststr(typ *abi.MapType, m *Map, key string) unsafe.Poin
fatal("concurrent map writes")
}
var hash uintptr
// See the related comment in runtime_mapaccess2_fast32
// for why we pass local copy of key.
k := key
hash := StrHash(unsafe.Pointer(&k), m.seed)
if memHashAESImplemented && UseAeshash {
hash = memHashAES(unsafe.Pointer(unsafe.StringData(key)), m.seed, uintptr(len(key)))
} else {
hash = memHashFallback(unsafe.Pointer(unsafe.StringData(key)), m.seed, uintptr(len(key)))
}
// Set writing after calling Hasher, since Hasher may panic, in which
// case we have not actually done a write.

View file

@ -11,27 +11,25 @@ package maps
import "unsafe"
func memHash32Fallback(p unsafe.Pointer, seed uintptr) uintptr {
func memHash32Fallback(k uint32, seed uintptr) uintptr {
a, b := mix32(uint32(seed), uint32(4^hashkey[0]))
t := readUnaligned32(p)
a ^= t
b ^= t
a ^= k
b ^= k
a, b = mix32(a, b)
a, b = mix32(a, b)
return uintptr(a ^ b)
}
func memHash64Fallback(p unsafe.Pointer, seed uintptr) uintptr {
func memHash64Fallback(k uint64, seed uintptr) uintptr {
a, b := mix32(uint32(seed), uint32(8^hashkey[0]))
a ^= readUnaligned32(p)
b ^= readUnaligned32(add(p, 4))
a ^= uint32(k)
b ^= uint32(k >> 32)
a, b = mix32(a, b)
a, b = mix32(a, b)
return uintptr(a ^ b)
}
func memHashFallback(p unsafe.Pointer, seed, s uintptr) uintptr {
a, b := mix32(uint32(seed), uint32(s^hashkey[0]))
if s == 0 {
return uintptr(a ^ b)

View file

@ -64,13 +64,13 @@ func memHashFallback(p unsafe.Pointer, seed, s uintptr) uintptr {
return mix(m5^s, mix(a^hashkey[1], b^seed))
}
func memHash32Fallback(p unsafe.Pointer, seed uintptr) uintptr {
a := r4(p)
func memHash32Fallback(k uint32, seed uintptr) uintptr {
a := uintptr(k)
return mix(m5^4, mix(a^hashkey[1], a^seed^hashkey[0]))
}
func memHash64Fallback(p unsafe.Pointer, seed uintptr) uintptr {
a := r8(p)
func memHash64Fallback(k uint64, seed uintptr) uintptr {
a := uintptr(k)
return mix(m5^8, mix(a^hashkey[1], a^seed^hashkey[0]))
}

View file

@ -84,12 +84,12 @@ func memhash(p unsafe.Pointer, h, s uintptr) uintptr {
//go:nosplit
func memhash64(p unsafe.Pointer, seed uintptr) uintptr {
return maps.MemHash64(p, seed)
return maps.MemHash64(readUnaligned64(p), seed)
}
//go:nosplit
func memhash32(p unsafe.Pointer, seed uintptr) uintptr {
return maps.MemHash32(p, seed)
return maps.MemHash32(readUnaligned32(p), seed)
}
// strhash should be an internal detail,
@ -104,9 +104,10 @@ func memhash32(p unsafe.Pointer, seed uintptr) uintptr {
// Do not remove or change the type signature.
// See go.dev/issue/67401.
//
//go:nosplit
//go:linkname strhash
func strhash(p unsafe.Pointer, h uintptr) uintptr {
return maps.StrHash(p, h)
return maps.StrHash(*(*string)(p), h)
}
// NOTE: Because NaN != NaN, a map can contain any
@ -384,6 +385,14 @@ func ifaceHash(i interface {
return interhash(noescape(unsafe.Pointer(&i)), seed)
}
func readUnaligned32(p unsafe.Pointer) uint32 {
q := (*[4]byte)(p)
if goarch.BigEndian {
return byteorder.BEUint32(q[:])
}
return byteorder.LEUint32(q[:])
}
func readUnaligned64(p unsafe.Pointer) uint64 {
q := (*[8]byte)(p)
if goarch.BigEndian {

View file

@ -20,6 +20,17 @@ import (
"unsafe"
)
// Test that unalgined access to memhash32 doesn't cause a problem.
func TestMemHash32AlignAccess(t *testing.T) {
type Key struct {
_ [1]byte
k [4]byte
_ [3]byte
}
key := Key{}
sink = (uint64)(MemHash32(unsafe.Pointer(&key.k), 0))
}
func TestMemHash32Equality(t *testing.T) {
if *UseAeshash {
t.Skip("skipping since AES hash implementation is used")
@ -37,6 +48,17 @@ func TestMemHash32Equality(t *testing.T) {
}
}
// Test that unalgined access to memhash64 doesn't cause a problem.
func TestMemHash64AlignAccess(t *testing.T) {
type Key struct {
_ [1]byte
k [8]byte
_ [7]byte
}
key := Key{}
sink = (uint64)(MemHash64(unsafe.Pointer(&key.k), 0))
}
func TestMemHash64Equality(t *testing.T) {
if *UseAeshash {
t.Skip("skipping since AES hash implementation is used")