Add integer key support to hashmap
Этот коммит содержится в:
родитель
8f7db8661b
коммит
c912091f8b
3 изменённых файлов: 93 добавлений и 42 удалений
33
compiler.go
33
compiler.go
|
@ -1100,12 +1100,20 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
||||||
mapType := instr.Map.Type().Underlying().(*types.Map)
|
mapType := instr.Map.Type().Underlying().(*types.Map)
|
||||||
switch keyType := mapType.Key().Underlying().(type) {
|
switch keyType := mapType.Key().Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
if keyType.Kind() == types.String {
|
|
||||||
valueAlloca := c.builder.CreateAlloca(value.Type(), "hashmap.value")
|
valueAlloca := c.builder.CreateAlloca(value.Type(), "hashmap.value")
|
||||||
c.builder.CreateStore(value, valueAlloca)
|
c.builder.CreateStore(value, valueAlloca)
|
||||||
valuePtr := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "hashmap.valueptr")
|
valuePtr := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "hashmap.valueptr")
|
||||||
|
if keyType.Kind() == types.String {
|
||||||
params := []llvm.Value{m, key, valuePtr}
|
params := []llvm.Value{m, key, valuePtr}
|
||||||
fn := c.mod.NamedFunction("runtime.hashmapSet")
|
fn := c.mod.NamedFunction("runtime.hashmapStringSet")
|
||||||
|
c.builder.CreateCall(fn, params, "")
|
||||||
|
return nil
|
||||||
|
} else if keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 {
|
||||||
|
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
|
||||||
|
c.builder.CreateStore(key, keyAlloca)
|
||||||
|
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
||||||
|
params := []llvm.Value{m, keyPtr, valuePtr}
|
||||||
|
fn := c.mod.NamedFunction("runtime.hashmapBinarySet")
|
||||||
c.builder.CreateCall(fn, params, "")
|
c.builder.CreateCall(fn, params, "")
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
|
@ -1588,15 +1596,23 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
case *types.Map:
|
case *types.Map:
|
||||||
switch keyType := xType.Key().Underlying().(type) {
|
switch keyType := xType.Key().Underlying().(type) {
|
||||||
case *types.Basic:
|
case *types.Basic:
|
||||||
if keyType.Kind() == types.String {
|
|
||||||
llvmValueType, err := c.getLLVMType(expr.Type())
|
llvmValueType, err := c.getLLVMType(expr.Type())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return llvm.Value{}, err
|
return llvm.Value{}, err
|
||||||
}
|
}
|
||||||
mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "hashmap.value")
|
mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "hashmap.value")
|
||||||
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "hashmap.valueptr")
|
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "hashmap.valueptr")
|
||||||
|
if keyType.Kind() == types.String {
|
||||||
params := []llvm.Value{value, index, mapValuePtr}
|
params := []llvm.Value{value, index, mapValuePtr}
|
||||||
fn := c.mod.NamedFunction("runtime.hashmapGet")
|
fn := c.mod.NamedFunction("runtime.hashmapStringGet")
|
||||||
|
c.builder.CreateCall(fn, params, "")
|
||||||
|
return c.builder.CreateLoad(mapValueAlloca, ""), nil
|
||||||
|
} else if keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 {
|
||||||
|
keyAlloca := c.builder.CreateAlloca(index.Type(), "hashmap.key")
|
||||||
|
c.builder.CreateStore(index, keyAlloca)
|
||||||
|
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
||||||
|
params := []llvm.Value{value, keyPtr, mapValuePtr}
|
||||||
|
fn := c.mod.NamedFunction("runtime.hashmapBinaryGet")
|
||||||
c.builder.CreateCall(fn, params, "")
|
c.builder.CreateCall(fn, params, "")
|
||||||
return c.builder.CreateLoad(mapValueAlloca, ""), nil
|
return c.builder.CreateLoad(mapValueAlloca, ""), nil
|
||||||
} else {
|
} else {
|
||||||
|
@ -1658,9 +1674,6 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return llvm.Value{}, err
|
return llvm.Value{}, err
|
||||||
}
|
}
|
||||||
switch keyType := mapType.Key().Underlying().(type) {
|
|
||||||
case *types.Basic:
|
|
||||||
if keyType.Kind() == types.String {
|
|
||||||
keySize := c.targetData.TypeAllocSize(llvmKeyType)
|
keySize := c.targetData.TypeAllocSize(llvmKeyType)
|
||||||
valueSize := c.targetData.TypeAllocSize(llvmValueType)
|
valueSize := c.targetData.TypeAllocSize(llvmValueType)
|
||||||
hashmapMake := c.mod.NamedFunction("runtime.hashmapMake")
|
hashmapMake := c.mod.NamedFunction("runtime.hashmapMake")
|
||||||
|
@ -1668,12 +1681,6 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
llvmValueSize := llvm.ConstInt(llvm.Int8Type(), valueSize, false)
|
llvmValueSize := llvm.ConstInt(llvm.Int8Type(), valueSize, false)
|
||||||
hashmap := c.builder.CreateCall(hashmapMake, []llvm.Value{llvmKeySize, llvmValueSize}, "")
|
hashmap := c.builder.CreateCall(hashmapMake, []llvm.Value{llvmKeySize, llvmValueSize}, "")
|
||||||
return hashmap, nil
|
return hashmap, nil
|
||||||
} else {
|
|
||||||
return llvm.Value{}, errors.New("todo: map key type: " + keyType.String())
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return llvm.Value{}, errors.New("todo: map key type: " + keyType.String())
|
|
||||||
}
|
|
||||||
case *ssa.Phi:
|
case *ssa.Phi:
|
||||||
t, err := c.getLLVMType(expr.Type())
|
t, err := c.getLLVMType(expr.Type())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -30,13 +30,14 @@ type hashmapBucket struct {
|
||||||
// allocated but as they're of variable size they can't be shown here.
|
// allocated but as they're of variable size they can't be shown here.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get FNV-1a hash of this string.
|
// Get FNV-1a hash of this key.
|
||||||
//
|
//
|
||||||
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
|
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
|
||||||
func stringhash(s *string) uint32 {
|
func hashmapHash(ptr unsafe.Pointer, n uintptr) uint32 {
|
||||||
var result uint32 = 2166136261 // FNV offset basis
|
var result uint32 = 2166136261 // FNV offset basis
|
||||||
for i := 0; i < len(*s); i++ {
|
for i := uintptr(0); i < n; i++ {
|
||||||
result ^= uint32((*s)[i])
|
c := *(*uint8)(unsafe.Pointer(uintptr(ptr) + i))
|
||||||
|
result ^= uint32(c) // XOR with byte
|
||||||
result *= 16777619 // FNV prime
|
result *= 16777619 // FNV prime
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
@ -65,8 +66,7 @@ func hashmapMake(keySize, valueSize uint8) *hashmap {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set a specified key to a given value. Grow the map if necessary.
|
// Set a specified key to a given value. Grow the map if necessary.
|
||||||
func hashmapSet(m *hashmap, key string, value unsafe.Pointer) {
|
func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) {
|
||||||
hash := stringhash(&key)
|
|
||||||
numBuckets := uintptr(1) << m.bucketBits
|
numBuckets := uintptr(1) << m.bucketBits
|
||||||
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
||||||
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||||
|
@ -76,13 +76,13 @@ func hashmapSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||||
tophash := hashmapTopHash(hash)
|
tophash := hashmapTopHash(hash)
|
||||||
|
|
||||||
// See whether the key already exists somewhere.
|
// See whether the key already exists somewhere.
|
||||||
var emptySlotKey *string
|
var emptySlotKey unsafe.Pointer
|
||||||
var emptySlotValue unsafe.Pointer
|
var emptySlotValue unsafe.Pointer
|
||||||
var emptySlotTophash *byte
|
var emptySlotTophash *byte
|
||||||
for bucket != nil {
|
for bucket != nil {
|
||||||
for i := uintptr(0); i < 8; i++ {
|
for i := uintptr(0); i < 8; i++ {
|
||||||
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(i)
|
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(i)
|
||||||
slotKey := (*string)(unsafe.Pointer(bucketAddr + slotKeyOffset))
|
slotKey := unsafe.Pointer(bucketAddr + slotKeyOffset)
|
||||||
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(i)
|
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(i)
|
||||||
slotValue := unsafe.Pointer(bucketAddr + slotValueOffset)
|
slotValue := unsafe.Pointer(bucketAddr + slotValueOffset)
|
||||||
if bucket.tophash[i] == 0 && emptySlotKey == nil {
|
if bucket.tophash[i] == 0 && emptySlotKey == nil {
|
||||||
|
@ -94,7 +94,7 @@ func hashmapSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||||
}
|
}
|
||||||
if bucket.tophash[i] == tophash {
|
if bucket.tophash[i] == tophash {
|
||||||
// Could be an existing value that's the same.
|
// Could be an existing value that's the same.
|
||||||
if key == *slotKey {
|
if keyEqual(key, slotKey, uintptr(m.keySize)) {
|
||||||
// found same key, replace it
|
// found same key, replace it
|
||||||
memcpy(slotValue, value, uintptr(m.valueSize))
|
memcpy(slotValue, value, uintptr(m.valueSize))
|
||||||
return
|
return
|
||||||
|
@ -105,7 +105,7 @@ func hashmapSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||||
}
|
}
|
||||||
if emptySlotKey != nil {
|
if emptySlotKey != nil {
|
||||||
m.count++
|
m.count++
|
||||||
*emptySlotKey = key
|
memcpy(emptySlotKey, key, uintptr(m.keySize))
|
||||||
memcpy(emptySlotValue, value, uintptr(m.valueSize))
|
memcpy(emptySlotValue, value, uintptr(m.valueSize))
|
||||||
*emptySlotTophash = tophash
|
*emptySlotTophash = tophash
|
||||||
return
|
return
|
||||||
|
@ -114,8 +114,7 @@ func hashmapSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the value of a specified key, or zero the value if not found.
|
// Get the value of a specified key, or zero the value if not found.
|
||||||
func hashmapGet(m *hashmap, key string, value unsafe.Pointer) {
|
func hashmapGet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) {
|
||||||
hash := stringhash(&key)
|
|
||||||
numBuckets := uintptr(1) << m.bucketBits
|
numBuckets := uintptr(1) << m.bucketBits
|
||||||
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
||||||
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||||
|
@ -132,12 +131,12 @@ func hashmapGet(m *hashmap, key string, value unsafe.Pointer) {
|
||||||
for bucket != nil {
|
for bucket != nil {
|
||||||
for i := uintptr(0); i < 8; i++ {
|
for i := uintptr(0); i < 8; i++ {
|
||||||
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(i)
|
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(i)
|
||||||
slotKey := (*string)(unsafe.Pointer(bucketAddr + slotKeyOffset))
|
slotKey := unsafe.Pointer(bucketAddr + slotKeyOffset)
|
||||||
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(i)
|
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(i)
|
||||||
slotValue := unsafe.Pointer(bucketAddr + slotValueOffset)
|
slotValue := unsafe.Pointer(bucketAddr + slotValueOffset)
|
||||||
if bucket.tophash[i] == tophash {
|
if bucket.tophash[i] == tophash {
|
||||||
// This could be the key we're looking for.
|
// This could be the key we're looking for.
|
||||||
if key == *slotKey {
|
if keyEqual(key, slotKey, uintptr(m.keySize)) {
|
||||||
// Found the key, copy it.
|
// Found the key, copy it.
|
||||||
memcpy(value, slotValue, uintptr(m.valueSize))
|
memcpy(value, slotValue, uintptr(m.valueSize))
|
||||||
return
|
return
|
||||||
|
@ -150,3 +149,36 @@ func hashmapGet(m *hashmap, key string, value unsafe.Pointer) {
|
||||||
// Did not find the key.
|
// Did not find the key.
|
||||||
memzero(value, uintptr(m.valueSize))
|
memzero(value, uintptr(m.valueSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hashmap with plain binary data keys (not containing strings etc.).
|
||||||
|
|
||||||
|
func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
|
||||||
|
hash := hashmapHash(key, uintptr(m.keySize))
|
||||||
|
hashmapSet(m, key, value, hash, memequal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer) {
|
||||||
|
hash := hashmapHash(key, uintptr(m.keySize))
|
||||||
|
hashmapGet(m, key, value, hash, memequal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hashmap with string keys (a common case).
|
||||||
|
|
||||||
|
func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool {
|
||||||
|
return *(*string)(x) == *(*string)(y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashmapStringHash(s string) uint32 {
|
||||||
|
_s := (*_string)(unsafe.Pointer(&s))
|
||||||
|
return hashmapHash(unsafe.Pointer(_s.ptr), uintptr(_s.length))
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||||
|
hash := hashmapStringHash(key)
|
||||||
|
hashmapSet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer) {
|
||||||
|
hash := hashmapStringHash(key)
|
||||||
|
hashmapGet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual)
|
||||||
|
}
|
||||||
|
|
|
@ -35,6 +35,18 @@ func memzero(ptr unsafe.Pointer, size uintptr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Compare two same-size buffers for equality.
|
||||||
|
func memequal(x, y unsafe.Pointer, n uintptr) bool {
|
||||||
|
for i := uintptr(0); i < n; i++ {
|
||||||
|
cx := *(*uint8)(unsafe.Pointer(uintptr(x) + i))
|
||||||
|
cy := *(*uint8)(unsafe.Pointer(uintptr(y) + i))
|
||||||
|
if cx != cy {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func _panic(message interface{}) {
|
func _panic(message interface{}) {
|
||||||
printstring("panic: ")
|
printstring("panic: ")
|
||||||
printitf(message)
|
printitf(message)
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче