2014-07-31 16:12:53 -06:00
|
|
|
// Copyright 2014 The Go Authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package runtime
|
|
|
|
|
|
|
|
import "unsafe"
|
|
|
|
|
|
|
|
const (
|
2014-08-29 14:20:48 -06:00
|
|
|
c0 = uintptr((8-ptrSize)/4*2860486313 + (ptrSize-4)/4*33054211828000289)
|
|
|
|
c1 = uintptr((8-ptrSize)/4*3267000013 + (ptrSize-4)/4*23344194077549503)
|
2014-07-31 16:12:53 -06:00
|
|
|
)
|
|
|
|
|
2014-08-29 22:40:56 -06:00
|
|
|
// type algorithms - known to compiler
|
2014-07-31 16:12:53 -06:00
|
|
|
const (
|
|
|
|
alg_MEM = iota
|
|
|
|
alg_MEM0
|
|
|
|
alg_MEM8
|
|
|
|
alg_MEM16
|
|
|
|
alg_MEM32
|
|
|
|
alg_MEM64
|
|
|
|
alg_MEM128
|
|
|
|
alg_NOEQ
|
|
|
|
alg_NOEQ0
|
|
|
|
alg_NOEQ8
|
|
|
|
alg_NOEQ16
|
|
|
|
alg_NOEQ32
|
|
|
|
alg_NOEQ64
|
|
|
|
alg_NOEQ128
|
|
|
|
alg_STRING
|
|
|
|
alg_INTER
|
|
|
|
alg_NILINTER
|
|
|
|
alg_SLICE
|
|
|
|
alg_FLOAT32
|
|
|
|
alg_FLOAT64
|
|
|
|
alg_CPLX64
|
|
|
|
alg_CPLX128
|
|
|
|
alg_max
|
|
|
|
)
|
|
|
|
|
2014-08-29 22:40:56 -06:00
|
|
|
type typeAlg struct {
|
|
|
|
// function for hashing objects of this type
|
|
|
|
// (ptr to object, size, seed) -> hash
|
|
|
|
hash func(unsafe.Pointer, uintptr, uintptr) uintptr
|
|
|
|
// function for comparing objects of this type
|
|
|
|
// (ptr to object A, ptr to object B, size) -> ==?
|
|
|
|
equal func(unsafe.Pointer, unsafe.Pointer, uintptr) bool
|
|
|
|
}
|
|
|
|
|
|
|
|
var algarray = [alg_max]typeAlg{
|
|
|
|
alg_MEM: {memhash, memequal},
|
|
|
|
alg_MEM0: {memhash, memequal0},
|
|
|
|
alg_MEM8: {memhash, memequal8},
|
|
|
|
alg_MEM16: {memhash, memequal16},
|
|
|
|
alg_MEM32: {memhash, memequal32},
|
|
|
|
alg_MEM64: {memhash, memequal64},
|
|
|
|
alg_MEM128: {memhash, memequal128},
|
|
|
|
alg_NOEQ: {nil, nil},
|
|
|
|
alg_NOEQ0: {nil, nil},
|
|
|
|
alg_NOEQ8: {nil, nil},
|
|
|
|
alg_NOEQ16: {nil, nil},
|
|
|
|
alg_NOEQ32: {nil, nil},
|
|
|
|
alg_NOEQ64: {nil, nil},
|
|
|
|
alg_NOEQ128: {nil, nil},
|
|
|
|
alg_STRING: {strhash, strequal},
|
|
|
|
alg_INTER: {interhash, interequal},
|
|
|
|
alg_NILINTER: {nilinterhash, nilinterequal},
|
|
|
|
alg_SLICE: {nil, nil},
|
|
|
|
alg_FLOAT32: {f32hash, f32equal},
|
|
|
|
alg_FLOAT64: {f64hash, f64equal},
|
|
|
|
alg_CPLX64: {c64hash, c64equal},
|
|
|
|
alg_CPLX128: {c128hash, c128equal},
|
|
|
|
}
|
|
|
|
|
2014-08-05 22:24:11 -06:00
|
|
|
const nacl = GOOS == "nacl"
|
|
|
|
|
2014-08-29 22:40:56 -06:00
|
|
|
var useAeshash bool
|
2014-07-31 16:12:53 -06:00
|
|
|
|
|
|
|
// in asm_*.s
|
2014-08-06 14:42:00 -06:00
|
|
|
func aeshash(p unsafe.Pointer, s, h uintptr) uintptr
|
cmd/cc, runtime: convert C compilers to use Go calling convention
To date, the C compilers and Go compilers differed only in how
values were returned from functions. This made it difficult to call
Go from C or C from Go if return values were involved. It also made
assembly called from Go and assembly called from C different.
This CL changes the C compiler to use the Go conventions, passing
results on the stack, after the arguments.
[Exception: this does not apply to C ... functions, because you can't
know where on the stack the arguments end.]
By doing this, the CL makes it possible to rewrite C functions into Go
one at a time, without worrying about which languages call that
function or which languages it calls.
This CL also updates all the assembly files in package runtime to use
the new conventions. Argument references of the form 40(SP) have
been rewritten to the form name+10(FP) instead, and there are now
Go func prototypes for every assembly function called from C or Go.
This means that 'go vet runtime' checks effectively every assembly
function, and go vet's output was used to automate the bulk of the
conversion.
Some functions, like seek and nsec on Plan 9, needed to be rewritten.
Many assembly routines called from C were reading arguments
incorrectly, using MOVL instead of MOVQ or vice versa, especially on
the less used systems like openbsd.
These were found by go vet and have been corrected too.
If we're lucky, this may reduce flakiness on those systems.
Tested on:
darwin/386
darwin/amd64
linux/arm
linux/386
linux/amd64
If this breaks another system, the bug is almost certainly in the
sys_$GOOS_$GOARCH.s file, since the rest of the CL is tested
by the combination of the above systems.
LGTM=dvyukov, iant
R=golang-codereviews, 0intro, dave, alex.brainman, dvyukov, iant
CC=golang-codereviews, josharian, r
https://golang.org/cl/135830043
2014-08-27 09:32:17 -06:00
|
|
|
func aeshash32(p unsafe.Pointer, s, h uintptr) uintptr
|
|
|
|
func aeshash64(p unsafe.Pointer, s, h uintptr) uintptr
|
|
|
|
func aeshashstr(p unsafe.Pointer, s, h uintptr) uintptr
|
2014-07-31 16:12:53 -06:00
|
|
|
|
2014-08-06 14:42:00 -06:00
|
|
|
func memhash(p unsafe.Pointer, s, h uintptr) uintptr {
|
2014-08-29 22:40:56 -06:00
|
|
|
if !nacl && useAeshash {
|
2014-07-31 16:12:53 -06:00
|
|
|
return aeshash(p, s, h)
|
|
|
|
}
|
|
|
|
|
|
|
|
h ^= c0
|
|
|
|
for s > 0 {
|
|
|
|
h = (h ^ uintptr(*(*byte)(p))) * c1
|
|
|
|
p = add(p, 1)
|
|
|
|
s--
|
|
|
|
}
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
|
2014-08-29 22:40:56 -06:00
|
|
|
func strhash(a unsafe.Pointer, s, h uintptr) uintptr {
|
|
|
|
return memhash((*stringStruct)(a).str, uintptr(len(*(*string)(a))), h)
|
2014-07-31 16:12:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: Because NaN != NaN, a map can contain any
|
|
|
|
// number of (mostly useless) entries keyed with NaNs.
|
|
|
|
// To avoid long hash chains, we assign a random number
|
|
|
|
// as the hash value for a NaN.
|
|
|
|
|
2014-08-29 22:40:56 -06:00
|
|
|
func f32hash(p unsafe.Pointer, s, h uintptr) uintptr {
|
|
|
|
f := *(*float32)(p)
|
2014-07-31 16:12:53 -06:00
|
|
|
switch {
|
|
|
|
case f == 0:
|
|
|
|
return c1 * (c0 ^ h) // +0, -0
|
|
|
|
case f != f:
|
2014-09-02 15:33:33 -06:00
|
|
|
return c1 * (c0 ^ h ^ uintptr(fastrand1())) // any kind of NaN
|
2014-07-31 16:12:53 -06:00
|
|
|
default:
|
2014-08-29 22:40:56 -06:00
|
|
|
return memhash(p, 4, h)
|
2014-07-31 16:12:53 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-29 22:40:56 -06:00
|
|
|
func f64hash(p unsafe.Pointer, s, h uintptr) uintptr {
|
|
|
|
f := *(*float64)(p)
|
2014-07-31 16:12:53 -06:00
|
|
|
switch {
|
|
|
|
case f == 0:
|
|
|
|
return c1 * (c0 ^ h) // +0, -0
|
|
|
|
case f != f:
|
2014-09-02 15:33:33 -06:00
|
|
|
return c1 * (c0 ^ h ^ uintptr(fastrand1())) // any kind of NaN
|
2014-07-31 16:12:53 -06:00
|
|
|
default:
|
2014-08-29 22:40:56 -06:00
|
|
|
return memhash(p, 8, h)
|
2014-07-31 16:12:53 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-29 22:40:56 -06:00
|
|
|
func c64hash(p unsafe.Pointer, s, h uintptr) uintptr {
|
|
|
|
x := (*[2]float32)(p)
|
|
|
|
return f32hash(unsafe.Pointer(&x[1]), 4, f32hash(unsafe.Pointer(&x[0]), 4, h))
|
2014-07-31 16:12:53 -06:00
|
|
|
}
|
|
|
|
|
2014-08-29 22:40:56 -06:00
|
|
|
func c128hash(p unsafe.Pointer, s, h uintptr) uintptr {
|
|
|
|
x := (*[2]float64)(p)
|
|
|
|
return f64hash(unsafe.Pointer(&x[1]), 8, f64hash(unsafe.Pointer(&x[0]), 8, h))
|
2014-07-31 16:12:53 -06:00
|
|
|
}
|
|
|
|
|
2014-08-29 22:40:56 -06:00
|
|
|
func interhash(p unsafe.Pointer, s, h uintptr) uintptr {
|
|
|
|
a := (*iface)(p)
|
2014-08-06 14:42:00 -06:00
|
|
|
tab := a.tab
|
2014-08-06 14:22:52 -06:00
|
|
|
if tab == nil {
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
t := tab._type
|
|
|
|
fn := goalg(t.alg).hash
|
2014-08-29 22:40:56 -06:00
|
|
|
if fn == nil {
|
2014-08-06 14:22:52 -06:00
|
|
|
panic(errorString("hash of unhashable type " + *t._string))
|
|
|
|
}
|
2014-08-18 19:13:11 -06:00
|
|
|
if isDirectIface(t) {
|
2014-08-06 14:42:00 -06:00
|
|
|
return c1 * fn(unsafe.Pointer(&a.data), uintptr(t.size), h^c0)
|
2014-08-06 14:22:52 -06:00
|
|
|
} else {
|
2014-08-06 14:42:00 -06:00
|
|
|
return c1 * fn(a.data, uintptr(t.size), h^c0)
|
2014-08-06 14:22:52 -06:00
|
|
|
}
|
2014-07-31 16:12:53 -06:00
|
|
|
}
|
|
|
|
|
2014-08-29 22:40:56 -06:00
|
|
|
func nilinterhash(p unsafe.Pointer, s, h uintptr) uintptr {
|
|
|
|
a := (*eface)(p)
|
2014-08-06 14:42:00 -06:00
|
|
|
t := a._type
|
2014-07-31 16:12:53 -06:00
|
|
|
if t == nil {
|
|
|
|
return h
|
|
|
|
}
|
|
|
|
fn := goalg(t.alg).hash
|
2014-08-29 22:40:56 -06:00
|
|
|
if fn == nil {
|
2014-07-31 16:12:53 -06:00
|
|
|
panic(errorString("hash of unhashable type " + *t._string))
|
|
|
|
}
|
2014-08-18 19:13:11 -06:00
|
|
|
if isDirectIface(t) {
|
2014-08-06 14:42:00 -06:00
|
|
|
return c1 * fn(unsafe.Pointer(&a.data), uintptr(t.size), h^c0)
|
2014-07-31 16:12:53 -06:00
|
|
|
} else {
|
2014-08-06 14:42:00 -06:00
|
|
|
return c1 * fn(a.data, uintptr(t.size), h^c0)
|
2014-07-31 16:12:53 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-07 15:52:55 -06:00
|
|
|
func memequal(p, q unsafe.Pointer, size uintptr) bool {
|
|
|
|
if p == q {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return memeq(p, q, size)
|
|
|
|
}
|
|
|
|
|
|
|
|
func memequal0(p, q unsafe.Pointer, size uintptr) bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
func memequal8(p, q unsafe.Pointer, size uintptr) bool {
|
|
|
|
return *(*int8)(p) == *(*int8)(q)
|
|
|
|
}
|
|
|
|
func memequal16(p, q unsafe.Pointer, size uintptr) bool {
|
|
|
|
return *(*int16)(p) == *(*int16)(q)
|
|
|
|
}
|
|
|
|
func memequal32(p, q unsafe.Pointer, size uintptr) bool {
|
|
|
|
return *(*int32)(p) == *(*int32)(q)
|
|
|
|
}
|
|
|
|
func memequal64(p, q unsafe.Pointer, size uintptr) bool {
|
|
|
|
return *(*int64)(p) == *(*int64)(q)
|
|
|
|
}
|
|
|
|
func memequal128(p, q unsafe.Pointer, size uintptr) bool {
|
|
|
|
return *(*[2]int64)(p) == *(*[2]int64)(q)
|
|
|
|
}
|
|
|
|
func f32equal(p, q unsafe.Pointer, size uintptr) bool {
|
|
|
|
return *(*float32)(p) == *(*float32)(q)
|
|
|
|
}
|
|
|
|
func f64equal(p, q unsafe.Pointer, size uintptr) bool {
|
|
|
|
return *(*float64)(p) == *(*float64)(q)
|
|
|
|
}
|
|
|
|
func c64equal(p, q unsafe.Pointer, size uintptr) bool {
|
|
|
|
return *(*complex64)(p) == *(*complex64)(q)
|
|
|
|
}
|
|
|
|
func c128equal(p, q unsafe.Pointer, size uintptr) bool {
|
|
|
|
return *(*complex128)(p) == *(*complex128)(q)
|
|
|
|
}
|
|
|
|
func strequal(p, q unsafe.Pointer, size uintptr) bool {
|
|
|
|
return *(*string)(p) == *(*string)(q)
|
|
|
|
}
|
|
|
|
func interequal(p, q unsafe.Pointer, size uintptr) bool {
|
|
|
|
return ifaceeq(*(*interface {
|
|
|
|
f()
|
|
|
|
})(p), *(*interface {
|
|
|
|
f()
|
|
|
|
})(q))
|
|
|
|
}
|
|
|
|
func nilinterequal(p, q unsafe.Pointer, size uintptr) bool {
|
|
|
|
return efaceeq(*(*interface{})(p), *(*interface{})(q))
|
|
|
|
}
|
|
|
|
func efaceeq(p, q interface{}) bool {
|
|
|
|
x := (*eface)(unsafe.Pointer(&p))
|
|
|
|
y := (*eface)(unsafe.Pointer(&q))
|
|
|
|
t := x._type
|
|
|
|
if t != y._type {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if t == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
eq := goalg(t.alg).equal
|
2014-08-29 22:40:56 -06:00
|
|
|
if eq == nil {
|
2014-08-07 15:52:55 -06:00
|
|
|
panic(errorString("comparing uncomparable type " + *t._string))
|
|
|
|
}
|
2014-08-18 19:13:11 -06:00
|
|
|
if isDirectIface(t) {
|
2014-08-07 15:52:55 -06:00
|
|
|
return eq(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data)), uintptr(t.size))
|
|
|
|
}
|
|
|
|
return eq(x.data, y.data, uintptr(t.size))
|
|
|
|
}
|
|
|
|
func ifaceeq(p, q interface {
|
|
|
|
f()
|
|
|
|
}) bool {
|
|
|
|
x := (*iface)(unsafe.Pointer(&p))
|
|
|
|
y := (*iface)(unsafe.Pointer(&q))
|
|
|
|
xtab := x.tab
|
|
|
|
if xtab != y.tab {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if xtab == nil {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
t := xtab._type
|
|
|
|
eq := goalg(t.alg).equal
|
2014-08-29 22:40:56 -06:00
|
|
|
if eq == nil {
|
2014-08-07 15:52:55 -06:00
|
|
|
panic(errorString("comparing uncomparable type " + *t._string))
|
|
|
|
}
|
2014-08-18 19:13:11 -06:00
|
|
|
if isDirectIface(t) {
|
2014-08-07 15:52:55 -06:00
|
|
|
return eq(noescape(unsafe.Pointer(&x.data)), noescape(unsafe.Pointer(&y.data)), uintptr(t.size))
|
|
|
|
}
|
|
|
|
return eq(x.data, y.data, uintptr(t.size))
|
|
|
|
}
|
|
|
|
|
2014-07-31 16:12:53 -06:00
|
|
|
// Testing adapters for hash quality tests (see hash_test.go)
|
|
|
|
func haveGoodHash() bool {
|
2014-08-29 22:40:56 -06:00
|
|
|
return useAeshash
|
2014-07-31 16:12:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func stringHash(s string, seed uintptr) uintptr {
|
2014-08-29 22:40:56 -06:00
|
|
|
return algarray[alg_STRING].hash(noescape(unsafe.Pointer(&s)), unsafe.Sizeof(s), seed)
|
2014-07-31 16:12:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func bytesHash(b []byte, seed uintptr) uintptr {
|
2014-08-29 22:40:56 -06:00
|
|
|
s := (*sliceStruct)(unsafe.Pointer(&b))
|
|
|
|
return algarray[alg_MEM].hash(s.array, uintptr(s.len), seed)
|
2014-07-31 16:12:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func int32Hash(i uint32, seed uintptr) uintptr {
|
2014-08-29 22:40:56 -06:00
|
|
|
return algarray[alg_MEM32].hash(noescape(unsafe.Pointer(&i)), 4, seed)
|
2014-07-31 16:12:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func int64Hash(i uint64, seed uintptr) uintptr {
|
2014-08-29 22:40:56 -06:00
|
|
|
return algarray[alg_MEM64].hash(noescape(unsafe.Pointer(&i)), 8, seed)
|
2014-07-31 16:12:53 -06:00
|
|
|
}
|
2014-08-07 13:33:20 -06:00
|
|
|
|
|
|
|
func efaceHash(i interface{}, seed uintptr) uintptr {
|
2014-08-29 22:40:56 -06:00
|
|
|
return algarray[alg_NILINTER].hash(noescape(unsafe.Pointer(&i)), unsafe.Sizeof(i), seed)
|
2014-08-07 13:33:20 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func ifaceHash(i interface {
|
|
|
|
F()
|
|
|
|
}, seed uintptr) uintptr {
|
2014-08-29 22:40:56 -06:00
|
|
|
return algarray[alg_INTER].hash(noescape(unsafe.Pointer(&i)), unsafe.Sizeof(i), seed)
|
2014-08-07 13:33:20 -06:00
|
|
|
}
|
2014-08-20 15:02:11 -06:00
|
|
|
|
|
|
|
// Testing adapter for memclr
|
|
|
|
func memclrBytes(b []byte) {
|
|
|
|
s := (*sliceStruct)(unsafe.Pointer(&b))
|
|
|
|
memclr(s.array, uintptr(s.len))
|
|
|
|
}
|
2014-08-29 22:40:56 -06:00
|
|
|
|
|
|
|
// TODO(dvyukov): remove when Type is converted to Go and contains *typeAlg.
|
|
|
|
func goalg(a unsafe.Pointer) *typeAlg {
|
|
|
|
return (*typeAlg)(a)
|
|
|
|
}
|
|
|
|
|
|
|
|
// used in asm_{386,amd64}.s
|
|
|
|
const hashRandomBytes = 32
|
|
|
|
|
|
|
|
var aeskeysched [hashRandomBytes]byte
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
if theGoos == "nacl" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Install aes hash algorithm if we have the instructions we need
|
|
|
|
if (cpuid_ecx&(1<<25)) != 0 && // aes (aesenc)
|
|
|
|
(cpuid_ecx&(1<<9)) != 0 && // sse3 (pshufb)
|
|
|
|
(cpuid_ecx&(1<<19)) != 0 { // sse4.1 (pinsr{d,q})
|
|
|
|
useAeshash = true
|
|
|
|
algarray[alg_MEM].hash = aeshash
|
|
|
|
algarray[alg_MEM8].hash = aeshash
|
|
|
|
algarray[alg_MEM16].hash = aeshash
|
|
|
|
algarray[alg_MEM32].hash = aeshash32
|
|
|
|
algarray[alg_MEM64].hash = aeshash64
|
|
|
|
algarray[alg_MEM128].hash = aeshash
|
|
|
|
algarray[alg_STRING].hash = aeshashstr
|
|
|
|
// Initialize with random data so hash collisions will be hard to engineer.
|
|
|
|
var rnd unsafe.Pointer
|
|
|
|
var n int32
|
|
|
|
get_random_data(&rnd, &n)
|
|
|
|
if n > hashRandomBytes {
|
|
|
|
n = hashRandomBytes
|
|
|
|
}
|
|
|
|
memmove(unsafe.Pointer(&aeskeysched[0]), rnd, uintptr(n))
|
|
|
|
if n < hashRandomBytes {
|
|
|
|
// Not very random, but better than nothing.
|
|
|
|
for t := nanotime(); n < hashRandomBytes; n++ {
|
|
|
|
aeskeysched[n] = byte(t >> uint(8*(n%8)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|