internal/runtime/maps,runtime/: take key by value in memHash{32,64}AES; replace strHashAes by call to memHashAESdiff --git a/src/internal/runtime/maps/memhash_386.s b/src/internal/runtime/maps/memhash_386.s
index 75b1383..2a0d82f 100644
--- a/src/internal/runtime/maps/memhash_386.s
+++ b/src/internal/runtime/maps/memhash_386.s
@@ -5,19 +5,20 @@
#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
+// func memHash64AES(k uint64, h uintptr) uintptr
TEXT ·memHash64AES(SB),NOSPLIT,$0-12
- MOVL p+0(FP), AX // ptr to data
- MOVQ (AX), X0 // data
+ MOVQ k+0(FP), X0 // data
PINSRD $2, h+4(FP), X0 // seed
AESENC ·aeskeysched+0(SB), X0
AESENC ·aeskeysched+16(SB), X0
@@ -25,24 +26,18 @@
MOVL X0, ret+8(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
+ // CX: hash seed
+ // DX: address to put return value
+ MOVL p+0(FP), AX
+ MOVL s+8(FP), BX
+ MOVL h+4(FP), CX
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
+ MOVL CX, 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
MOVO X0, X1 // save unscrambled seed
diff --git a/src/internal/runtime/maps/memhash_aes.go b/src/internal/runtime/maps/memhash_aes.go
index 41e90d3..8893cdd 100644
--- a/src/internal/runtime/maps/memhash_aes.go
+++ b/src/internal/runtime/maps/memhash_aes.go
@@ -19,23 +19,20 @@
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)))
}
diff --git a/src/internal/runtime/maps/memhash_aes_asm.go b/src/internal/runtime/maps/memhash_aes_asm.go
index ae3dabd..0c8a5413 100644
--- a/src/internal/runtime/maps/memhash_aes_asm.go
+++ b/src/internal/runtime/maps/memhash_aes_asm.go
@@ -18,10 +18,7 @@
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
diff --git a/src/internal/runtime/maps/memhash_aes_simd.go b/src/internal/runtime/maps/memhash_aes_simd.go
index 4cd71b2..bef533e 100644
--- a/src/internal/runtime/maps/memhash_aes_simd.go
+++ b/src/internal/runtime/maps/memhash_aes_simd.go
@@ -13,9 +13,9 @@
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 @@
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 @@
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
diff --git a/src/internal/runtime/maps/memhash_amd64.s b/src/internal/runtime/maps/memhash_amd64.s
index c649211..c922afa 100644
--- a/src/internal/runtime/maps/memhash_amd64.s
+++ b/src/internal/runtime/maps/memhash_amd64.s
@@ -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
diff --git a/src/internal/runtime/maps/memhash_arm64.s b/src/internal/runtime/maps/memhash_arm64.s
index 3a5ae09..eb5864b 100644
--- a/src/internal/runtime/maps/memhash_arm64.s
+++ b/src/internal/runtime/maps/memhash_arm64.s
@@ -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 @@
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 @@
// 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
diff --git a/src/internal/runtime/maps/memhash_noaes.go b/src/internal/runtime/maps/memhash_noaes.go
index c9a297f..3bdf64e 100644
--- a/src/internal/runtime/maps/memhash_noaes.go
+++ b/src/internal/runtime/maps/memhash_noaes.go
@@ -14,30 +14,30 @@
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)))
}
diff --git a/src/internal/runtime/maps/memhash_nosimd_amd64.s b/src/internal/runtime/maps/memhash_nosimd_amd64.s
index 3471779..ad3790c 100644
--- a/src/internal/runtime/maps/memhash_nosimd_amd64.s
+++ b/src/internal/runtime/maps/memhash_nosimd_amd64.s
@@ -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
diff --git a/src/internal/runtime/maps/runtime_alg.go b/src/internal/runtime/maps/runtime_alg.go
index 9fc3225..1c80e7b 100644
--- a/src/internal/runtime/maps/runtime_alg.go
+++ b/src/internal/runtime/maps/runtime_alg.go
@@ -38,8 +38,7 @@
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 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)
diff --git a/src/internal/runtime/maps/runtime_fast32.go b/src/internal/runtime/maps/runtime_fast32.go
index dce1676..d80489e 100644
--- a/src/internal/runtime/maps/runtime_fast32.go
+++ b/src/internal/runtime/maps/runtime_fast32.go
@@ -63,18 +63,6 @@
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 @@
// 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 @@
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 @@
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)((unsafe.Pointer)(&key)), m.seed)
} else {
- hash = memHash32Fallback(unsafe.Pointer(&k), m.seed)
+ hash = memHash32Fallback(*(*uint32)((unsafe.Pointer)(&key)), m.seed)
}
// Set writing after calling Hasher, since Hasher may panic, in which
diff --git a/src/internal/runtime/maps/runtime_fast64.go b/src/internal/runtime/maps/runtime_fast64.go
index 82b8e98..8a2c53d 100644
--- a/src/internal/runtime/maps/runtime_fast64.go
+++ b/src/internal/runtime/maps/runtime_fast64.go
@@ -63,15 +63,12 @@
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 @@
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 @@
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)((unsafe.Pointer)(&key)), m.seed)
} else {
- hash = memHash64Fallback(unsafe.Pointer(&k), m.seed)
+ hash = memHash64Fallback(*(*uint64)((unsafe.Pointer)(&key)), m.seed)
}
// Set writing after calling Hasher, since Hasher may panic, in which
diff --git a/src/internal/runtime/maps/runtime_faststr.go b/src/internal/runtime/maps/runtime_faststr.go
index 4d6839f..85cdccd 100644
--- a/src/internal/runtime/maps/runtime_faststr.go
+++ b/src/internal/runtime/maps/runtime_faststr.go
@@ -64,11 +64,13 @@
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 @@
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 @@
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 @@
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.
diff --git a/src/internal/runtime/maps/runtime_hash32.go b/src/internal/runtime/maps/runtime_hash32.go
index 2244c93..72ab13d 100644
--- a/src/internal/runtime/maps/runtime_hash32.go
+++ b/src/internal/runtime/maps/runtime_hash32.go
@@ -11,27 +11,25 @@
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)
diff --git a/src/internal/runtime/maps/runtime_hash64.go b/src/internal/runtime/maps/runtime_hash64.go
index 733cecb..0e3a35a 100644
--- a/src/internal/runtime/maps/runtime_hash64.go
+++ b/src/internal/runtime/maps/runtime_hash64.go
@@ -64,13 +64,13 @@
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]))
}
diff --git a/src/runtime/alg.go b/src/runtime/alg.go
index e8fd6f6..ff1ec3e 100644
--- a/src/runtime/alg.go
+++ b/src/runtime/alg.go
@@ -84,12 +84,12 @@
//go:nosplit
func memhash64(p unsafe.Pointer, seed uintptr) uintptr {
- return maps.MemHash64(p, seed)
+ return maps.MemHash64(*(*uint64)(p), seed)
}
//go:nosplit
func memhash32(p unsafe.Pointer, seed uintptr) uintptr {
- return maps.MemHash32(p, seed)
+ return maps.MemHash32(*(*uint32)(p), seed)
}
// strhash should be an internal detail,
@@ -104,9 +104,10 @@
// 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
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
For CL 753740, I moved memhash functions to the maps package (done in CL 770581). This allows us to freely change the API of these hashing functions to take values directly (since we now have compatibility wrappers in runtime that we can update as needed)
With this change, all things that we disscussed in [issue 77892](https://github.com/golang/go/issues/77892) are solved:
With that, my work on hasing fucntions are done. The next thing that I can work on is implementing `memhash{32,64}` for arm64 once the appropriate intrinsics become avalaible =).
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Thank you for starting CI!
I forgot to update asm declaration for `GOARCH=386`.
Interestingly, the way I tested it before, it passed silently:
```
GOARCH=386 ../bin/go test runtime -run=Map
ok runtime 12.667s
```
But running
```
GOARCH=386 ../bin/go test internal/runtime/maps/
```
Correctly tells about the issue:
```
# internal/runtime/maps
# [internal/runtime/maps]
internal/runtime/maps/memhash_386.s:26:1: [386] memHash64AES: invalid offset ret+8(FP); expected ret+12(FP)
```
So my earlier test path didn’t catch the assembly mismatch.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Code-Review | +2 |
MOVL CX, X0 // 32 bits of per-table hash seedThis could load directly from h+4(FP), I think.
hash = memHash32AES(*(*uint32)((unsafe.Pointer)(&key)), m.seed)Can we just do `uint32(uintptr(key))`?
hash = memHash64AES(*(*uint64)((unsafe.Pointer)(&key)), m.seed)same for both of these.
return maps.MemHash64(*(*uint64)(p), seed)Do we need to use readUnaligned here?
I guess the other possibility would be to require alignment for use of memhash64. Which I think would be in cmd/compile/internal/reflectdata/alg.go:genHashSig, to do what genEqSig does.
Might be good to have a test with a key like:
```
type Key struct {
_ [1]byte
k [8]byte
_ [7]byte
}
```
That would cause unaligned memhash64 calls, I think.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
This could load directly from h+4(FP), I think.
Done
hash = memHash32AES(*(*uint32)((unsafe.Pointer)(&key)), m.seed)Can we just do `uint32(uintptr(key))`?
Done
hash = memHash64AES(*(*uint64)((unsafe.Pointer)(&key)), m.seed)same for both of these.
Done
Do we need to use readUnaligned here?
I guess the other possibility would be to require alignment for use of memhash64. Which I think would be in cmd/compile/internal/reflectdata/alg.go:genHashSig, to do what genEqSig does.Might be good to have a test with a key like:
```
type Key struct {
_ [1]byte
k [8]byte
_ [7]byte
}
```
That would cause unaligned memhash64 calls, I think.
Do we need to use readUnaligned here?
Yes - you are spot on, thanks!
For most arches (everything except arm64,amd64,386), AES hashing is not implemented, so before my changes it would redirect to the fallback, which already used readUnaligned. I missed that.
> I guess the other possibility would be to require alignment for use of memhash64.
I don't think that we should enforce the alignment here, especially since the `memcobine` pass optimizes `readUnalgined` to single mov.
> Might be good to have a test with a key
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
The CI failure looks like a flaky result. It failed only on single builder (`gotip-windows-amd64`) in http test, without any clear error message.
@alexande...@gmail.com could you please restart the CI?
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Code-Review | +2 |
Thanks.
internal/runtime/maps,runtime/: take key by value in memHash{32,64}AES; replace strHashAes by call to memHashAESWould be good to explain the reason for the change in the CL desciption.
MOVL h+4(FP), X0 // seedComment is not aligned well. Maybe use only spaces, not tab, for aligning.
hash = memHash32AES((uint32)((uintptr)(key)), m.seed)Parenthese around the type are not needed. `uint32(uintptr(key))`
Also other places.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |