Implement package-global maps (of max 8 entries)
Этот коммит содержится в:
родитель
2b78b6d7e8
коммит
179cf74b01
4 изменённых файлов: 139 добавлений и 5 удалений
98
compiler.go
98
compiler.go
|
@ -686,6 +686,93 @@ func (c *Compiler) parseInitFunc(frame *Frame) error {
|
|||
// Ignore: CGo pointer conversion.
|
||||
case *ssa.FieldAddr, *ssa.IndexAddr:
|
||||
// Ignore: handled below with *ssa.Store.
|
||||
case *ssa.MakeMap:
|
||||
mapType := instr.Type().Underlying().(*types.Map)
|
||||
llvmKeyType, err := c.getLLVMType(mapType.Key().Underlying())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
llvmValueType, err := c.getLLVMType(mapType.Elem().Underlying())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: use the initial map size
|
||||
bucketType := llvm.StructType([]llvm.Type{
|
||||
llvm.ArrayType(llvm.Int8Type(), 8), // tophash
|
||||
c.i8ptrType, // next bucket
|
||||
llvm.ArrayType(llvmKeyType, 8), // key type
|
||||
llvm.ArrayType(llvmValueType, 8), // value type
|
||||
}, false)
|
||||
bucket := llvm.AddGlobal(c.mod, bucketType, ".hashmap.bucket")
|
||||
bucketValue, err := getZeroValue(bucketType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bucket.SetInitializer(bucketValue)
|
||||
zero := llvm.ConstInt(llvm.Int32Type(), 0, false)
|
||||
bucketPtr := llvm.ConstInBoundsGEP(bucket, []llvm.Value{zero})
|
||||
keySize := c.targetData.TypeAllocSize(llvmKeyType)
|
||||
valueSize := c.targetData.TypeAllocSize(llvmValueType)
|
||||
hashmapType := c.mod.GetTypeByName("runtime.hashmap")
|
||||
hashmap := llvm.ConstNamedStruct(hashmapType, []llvm.Value{
|
||||
llvm.ConstPointerNull(llvm.PointerType(hashmapType, 0)), // next
|
||||
llvm.ConstBitCast(bucketPtr, c.i8ptrType), // buckets
|
||||
llvm.ConstInt(c.lenType, 0, false), // count
|
||||
llvm.ConstInt(llvm.Int8Type(), keySize, false), // keySize
|
||||
llvm.ConstInt(llvm.Int8Type(), valueSize, false), // valueSize
|
||||
llvm.ConstInt(llvm.Int8Type(), 0, false), // bucketBits
|
||||
})
|
||||
allocs[instr] = hashmap
|
||||
case *ssa.MapUpdate:
|
||||
// Note: we're assuming here the Go SSA compiler knows what it's
|
||||
// doing and doesn't insert a key twice.
|
||||
|
||||
// Update hashmap.count.
|
||||
hashmap := allocs[instr.Map]
|
||||
count := llvm.ConstExtractValue(hashmap, []uint32{2}).ZExtValue()
|
||||
count++
|
||||
countValue := llvm.ConstInt(c.lenType, count + 1, false)
|
||||
hashmap = llvm.ConstInsertValue(hashmap, countValue, []uint32{2})
|
||||
allocs[instr.Map] = hashmap
|
||||
|
||||
// Select the bucket (chain).
|
||||
bucketPtr := llvm.ConstExtractValue(hashmap, []uint32{1})
|
||||
bucketGlobal := bucketPtr.Operand(0)
|
||||
|
||||
llvmKey, err := c.initParseValue(instr.Key, allocs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
llvmValue, err := c.initParseValue(instr.Value, allocs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Hash for the hashtable. This must be equal to what is
|
||||
// calculated in the runtime implementation.
|
||||
key := constant.StringVal(instr.Key.(*ssa.Const).Value)
|
||||
hash := stringhash(&key)
|
||||
|
||||
// Find an empty spot in the bucket.
|
||||
done := false
|
||||
for !done {
|
||||
bucket := bucketGlobal.Initializer()
|
||||
for i := uint32(0); i < 8; i++ {
|
||||
if llvm.ConstExtractValue(bucket, []uint32{0, i}).ZExtValue() != 0 {
|
||||
// already taken
|
||||
continue
|
||||
}
|
||||
tophashValue := llvm.ConstInt(llvm.Int8Type(), uint64(hashmapTopHash(hash)), false)
|
||||
bucket = llvm.ConstInsertValue(bucket, tophashValue, []uint32{0, i})
|
||||
bucket = llvm.ConstInsertValue(bucket, llvmKey, []uint32{2, i})
|
||||
bucket = llvm.ConstInsertValue(bucket, llvmValue, []uint32{3, i})
|
||||
bucketGlobal.SetInitializer(bucket)
|
||||
done = true
|
||||
}
|
||||
if !done {
|
||||
return errors.New("init: todo: allocate a new bucket")
|
||||
}
|
||||
}
|
||||
case *ssa.Slice:
|
||||
// Turn a just-allocated array into a slice.
|
||||
if instr.Low != nil || instr.High != nil || instr.Max != nil {
|
||||
|
@ -735,6 +822,17 @@ func (c *Compiler) parseInitFunc(frame *Frame) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, ok := instr.Val.Type().Underlying().(*types.Map); ok {
|
||||
// Store pointer to map instead of the map itself. It is
|
||||
// a reference type, so every access goes through a
|
||||
// pointer to the real value. Note that this has to be a
|
||||
// pointer in some cases as the global value can be
|
||||
// replaced with a different map.
|
||||
hashmap := llvm.AddGlobal(c.mod, val.Type(), ".hashmap")
|
||||
hashmap.SetInitializer(val)
|
||||
zero := llvm.ConstInt(llvm.Int32Type(), 0, false)
|
||||
val = llvm.ConstInBoundsGEP(hashmap, []llvm.Value{zero})
|
||||
}
|
||||
llvmAddr := c.ir.GetGlobal(addr).llvmGlobal
|
||||
llvmAddr.SetInitializer(val)
|
||||
default:
|
||||
|
|
|
@ -14,6 +14,8 @@ type Stringer interface {
|
|||
|
||||
const SIX = 6
|
||||
|
||||
var testmap = map[string]int{"data": 3}
|
||||
|
||||
func main() {
|
||||
println("Hello world from Go!")
|
||||
println("The answer is:", calculateAnswer())
|
||||
|
@ -25,6 +27,7 @@ func main() {
|
|||
|
||||
m := map[string]int{"answer": 42, "foo": 3}
|
||||
readMap(m, "answer")
|
||||
readMap(testmap, "data")
|
||||
|
||||
foo := []int{1, 2, 4, 5}
|
||||
println("len/cap foo:", len(foo), cap(foo))
|
||||
|
@ -50,6 +53,7 @@ func runFunc(f func(int), arg int) {
|
|||
}
|
||||
|
||||
func readMap(m map[string]int, key string) {
|
||||
println("map length:", len(m))
|
||||
println("map read:", key, "=", m[key])
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,16 @@ func stringhash(s *string) uint32 {
|
|||
return result
|
||||
}
|
||||
|
||||
// Get the topmost 8 bits of the hash, without using a special value (like 0).
|
||||
func hashmapTopHash(hash uint32) uint8 {
|
||||
tophash := uint8(hash >> 24)
|
||||
if tophash < 1 {
|
||||
// 0 means empty slot, so make it bigger.
|
||||
tophash += 1
|
||||
}
|
||||
return tophash
|
||||
}
|
||||
|
||||
// Create a new hashmap with the given keySize and valueSize.
|
||||
func hashmapMake(keySize, valueSize uint8) *hashmap {
|
||||
bucketBufSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(keySize)*8 + uintptr(valueSize)*8
|
||||
|
@ -63,11 +73,7 @@ func hashmapSet(m *hashmap, key string, value unsafe.Pointer) {
|
|||
bucketAddr := uintptr(m.buckets) + bucketSize*bucketNumber
|
||||
bucket := (*hashmapBucket)(unsafe.Pointer(bucketAddr))
|
||||
|
||||
tophash := uint8(hash >> 24)
|
||||
if tophash < 1 {
|
||||
// 0 means empty slot, so make it bigger.
|
||||
tophash += 1
|
||||
}
|
||||
tophash := hashmapTopHash(hash)
|
||||
|
||||
// See whether the key already exists somewhere.
|
||||
var emptySlotKey *string
|
||||
|
|
26
util.go
Обычный файл
26
util.go
Обычный файл
|
@ -0,0 +1,26 @@
|
|||
package main
|
||||
|
||||
// This file contains functions that are also used by the runtime. These must be
|
||||
// kept in sync.
|
||||
|
||||
// Get FNV-1a hash of this string.
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
|
||||
func stringhash(s *string) uint32 {
|
||||
var result uint32 = 2166136261 // FNV offset basis
|
||||
for i := 0; i < len(*s); i++ {
|
||||
result ^= uint32((*s)[i])
|
||||
result *= 16777619 // FNV prime
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Get the topmost 8 bits of the hash, without using a special value (like 0).
|
||||
func hashmapTopHash(hash uint32) uint8 {
|
||||
tophash := uint8(hash >> 24)
|
||||
if tophash < 1 {
|
||||
// 0 means empty slot, so make it bigger.
|
||||
tophash += 1
|
||||
}
|
||||
return tophash
|
||||
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче