all: implement iterating over hashmaps
Still no support for hashmaps > 8 entries, but this kind of works.
Этот коммит содержится в:
родитель
5b4b6cfee2
коммит
152e12e4b0
3 изменённых файлов: 101 добавлений и 0 удалений
53
compiler.go
53
compiler.go
|
@ -2211,6 +2211,42 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
slice = c.builder.CreateInsertValue(slice, sliceLen, 1, "")
|
||||
slice = c.builder.CreateInsertValue(slice, sliceCap, 2, "")
|
||||
return slice, nil
|
||||
case *ssa.Next:
|
||||
if expr.IsString {
|
||||
return llvm.Value{}, errors.New("todo: next: string")
|
||||
} else { // map
|
||||
fn := c.mod.NamedFunction("runtime.hashmapNext")
|
||||
it, err := c.parseExpr(frame, expr.Iter)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
rangeMap := expr.Iter.(*ssa.Range).X
|
||||
m, err := c.parseExpr(frame, rangeMap)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
|
||||
llvmKeyType, err := c.getLLVMType(rangeMap.Type().(*types.Map).Key())
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
llvmValueType, err := c.getLLVMType(rangeMap.Type().(*types.Map).Elem())
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
|
||||
mapKeyAlloca := c.builder.CreateAlloca(llvmKeyType, "range.key")
|
||||
mapKeyPtr := c.builder.CreateBitCast(mapKeyAlloca, c.i8ptrType, "range.keyptr")
|
||||
mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "range.value")
|
||||
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "range.valueptr")
|
||||
ok := c.builder.CreateCall(fn, []llvm.Value{m, it, mapKeyPtr, mapValuePtr}, "range.next")
|
||||
|
||||
tuple := llvm.Undef(llvm.StructType([]llvm.Type{llvm.Int1Type(), llvmKeyType, llvmValueType}, false))
|
||||
tuple = c.builder.CreateInsertValue(tuple, ok, 0, "")
|
||||
tuple = c.builder.CreateInsertValue(tuple, c.builder.CreateLoad(mapKeyAlloca, ""), 1, "")
|
||||
tuple = c.builder.CreateInsertValue(tuple, c.builder.CreateLoad(mapValueAlloca, ""), 2, "")
|
||||
return tuple, nil
|
||||
}
|
||||
case *ssa.Phi:
|
||||
t, err := c.getLLVMType(expr.Type())
|
||||
if err != nil {
|
||||
|
@ -2219,6 +2255,23 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
phi := c.builder.CreatePHI(t, "")
|
||||
frame.phis = append(frame.phis, Phi{expr, phi})
|
||||
return phi, nil
|
||||
case *ssa.Range:
|
||||
switch typ := expr.X.Type().Underlying().(type) {
|
||||
case *types.Basic:
|
||||
// string
|
||||
return llvm.Value{}, errors.New("todo: range: string")
|
||||
case *types.Map:
|
||||
iteratorType := c.mod.GetTypeByName("runtime.hashmapIterator")
|
||||
it := c.builder.CreateAlloca(iteratorType, "range.it")
|
||||
zero, err := getZeroValue(iteratorType)
|
||||
if err != nil {
|
||||
return llvm.Value{}, nil
|
||||
}
|
||||
c.builder.CreateStore(zero, it)
|
||||
return it, nil
|
||||
default:
|
||||
panic("unknown type in range: " + typ.String())
|
||||
}
|
||||
case *ssa.Slice:
|
||||
if expr.Max != nil {
|
||||
return llvm.Value{}, errors.New("todo: full slice expressions (with max): " + expr.Type().String())
|
||||
|
|
|
@ -111,6 +111,9 @@ func testBound(f func() string) {
|
|||
func readMap(m map[string]int, key string) {
|
||||
println("map length:", len(m))
|
||||
println("map read:", key, "=", m[key])
|
||||
for k, v := range m {
|
||||
println(" ", k, "=", v)
|
||||
}
|
||||
}
|
||||
|
||||
func hello(n int) {
|
||||
|
|
|
@ -30,6 +30,12 @@ type hashmapBucket struct {
|
|||
// allocated but as they're of variable size they can't be shown here.
|
||||
}
|
||||
|
||||
type hashmapIterator struct {
|
||||
bucketNumber uintptr
|
||||
bucket *hashmapBucket
|
||||
bucketIndex uint8
|
||||
}
|
||||
|
||||
// Get FNV-1a hash of this key.
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash
|
||||
|
@ -152,6 +158,45 @@ func hashmapGet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
|||
memzero(value, uintptr(m.valueSize))
|
||||
}
|
||||
|
||||
// Iterate over a hashmap.
|
||||
//go:nobounds
|
||||
func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) bool {
|
||||
numBuckets := uintptr(1) << m.bucketBits
|
||||
for {
|
||||
if it.bucketIndex >= 8 {
|
||||
// end of bucket, move to the next in the chain
|
||||
it.bucketIndex = 0
|
||||
it.bucket = it.bucket.next
|
||||
}
|
||||
if it.bucket == nil {
|
||||
if it.bucketNumber >= numBuckets {
|
||||
// went through all buckets
|
||||
return false
|
||||
}
|
||||
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||
bucketAddr := uintptr(m.buckets) + bucketSize*it.bucketNumber
|
||||
it.bucket = (*hashmapBucket)(unsafe.Pointer(bucketAddr))
|
||||
it.bucketNumber++ // next bucket
|
||||
}
|
||||
if it.bucket.tophash[it.bucketIndex] == 0 {
|
||||
// slot is empty - move on
|
||||
it.bucketIndex++
|
||||
continue
|
||||
}
|
||||
|
||||
bucketAddr := uintptr(unsafe.Pointer(it.bucket))
|
||||
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(it.bucketIndex)
|
||||
slotKey := unsafe.Pointer(bucketAddr + slotKeyOffset)
|
||||
slotValueOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*uintptr(it.bucketIndex)
|
||||
slotValue := unsafe.Pointer(bucketAddr + slotValueOffset)
|
||||
memcpy(key, slotKey, uintptr(m.keySize))
|
||||
memcpy(value, slotValue, uintptr(m.valueSize))
|
||||
it.bucketIndex++
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Hashmap with plain binary data keys (not containing strings etc.).
|
||||
|
||||
func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче