compiler, runtime: implement delete builtin
Этот коммит содержится в:
родитель
7f60dd79ee
коммит
c0c1ccb381
6 изменённых файлов: 179 добавлений и 73 удалений
|
@ -1559,28 +1559,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
|
|||
return err
|
||||
}
|
||||
mapType := instr.Map.Type().Underlying().(*types.Map)
|
||||
switch keyType := mapType.Key().Underlying().(type) {
|
||||
case *types.Basic:
|
||||
valueAlloca := c.builder.CreateAlloca(value.Type(), "hashmap.value")
|
||||
c.builder.CreateStore(value, valueAlloca)
|
||||
valuePtr := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "hashmap.valueptr")
|
||||
if keyType.Info()&types.IsString != 0 {
|
||||
params := []llvm.Value{m, key, valuePtr}
|
||||
c.createRuntimeCall("hashmapStringSet", 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}
|
||||
c.createRuntimeCall("hashmapBinarySet", params, "")
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("todo: map update key type: " + keyType.String())
|
||||
}
|
||||
default:
|
||||
return errors.New("todo: map update key type: " + keyType.String())
|
||||
}
|
||||
return c.emitMapUpdate(mapType.Key(), m, key, value)
|
||||
case *ssa.Panic:
|
||||
value, err := c.parseExpr(frame, instr.X)
|
||||
if err != nil {
|
||||
|
@ -1718,6 +1697,16 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string)
|
|||
srcBuf = c.builder.CreateBitCast(srcBuf, c.i8ptrType, "copy.srcPtr")
|
||||
elemSize := llvm.ConstInt(c.uintptrType, c.targetData.TypeAllocSize(elemType), false)
|
||||
return c.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
|
||||
case "delete":
|
||||
m, err := c.parseExpr(frame, args[0])
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
key, err := c.parseExpr(frame, args[1])
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
return llvm.Value{}, c.emitMapDelete(args[1].Type(), m, key)
|
||||
case "len":
|
||||
value, err := c.parseExpr(frame, args[0])
|
||||
if err != nil {
|
||||
|
@ -2284,31 +2273,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
|||
bufPtr := c.builder.CreateGEP(buf, []llvm.Value{index}, "")
|
||||
return c.builder.CreateLoad(bufPtr, ""), nil
|
||||
case *types.Map:
|
||||
switch keyType := xType.Key().Underlying().(type) {
|
||||
case *types.Basic:
|
||||
llvmValueType, err := c.getLLVMType(expr.Type())
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "hashmap.value")
|
||||
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "hashmap.valueptr")
|
||||
if keyType.Info()&types.IsString != 0 {
|
||||
params := []llvm.Value{value, index, mapValuePtr}
|
||||
c.createRuntimeCall("hashmapStringGet", 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}
|
||||
c.createRuntimeCall("hashmapBinaryGet", params, "")
|
||||
return c.builder.CreateLoad(mapValueAlloca, ""), nil
|
||||
} else {
|
||||
return llvm.Value{}, errors.New("todo: map lookup key type: " + keyType.String())
|
||||
}
|
||||
default:
|
||||
return llvm.Value{}, errors.New("todo: map lookup key type: " + keyType.String())
|
||||
}
|
||||
return c.emitMapLookup(xType.Key(), expr.Type(), value, index)
|
||||
default:
|
||||
panic("unknown lookup type: " + expr.String())
|
||||
}
|
||||
|
|
107
compiler/map.go
Обычный файл
107
compiler/map.go
Обычный файл
|
@ -0,0 +1,107 @@
|
|||
package compiler
|
||||
|
||||
// This file emits the correct map intrinsics for map operations.
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/types"
|
||||
|
||||
"github.com/aykevl/go-llvm"
|
||||
)
|
||||
|
||||
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value) (llvm.Value, error) {
|
||||
switch keyType := keyType.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
llvmValueType, err := c.getLLVMType(valueType)
|
||||
if err != nil {
|
||||
return llvm.Value{}, err
|
||||
}
|
||||
mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "hashmap.value")
|
||||
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "hashmap.valueptr")
|
||||
if keyType.Info()&types.IsString != 0 {
|
||||
params := []llvm.Value{m, key, mapValuePtr}
|
||||
c.createRuntimeCall("hashmapStringGet", params, "")
|
||||
return c.builder.CreateLoad(mapValueAlloca, ""), 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, mapValuePtr}
|
||||
c.createRuntimeCall("hashmapBinaryGet", params, "")
|
||||
return c.builder.CreateLoad(mapValueAlloca, ""), nil
|
||||
} else {
|
||||
return llvm.Value{}, errors.New("todo: map lookup key type: " + keyType.String())
|
||||
}
|
||||
default:
|
||||
return llvm.Value{}, errors.New("todo: map lookup key type: " + keyType.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value) error {
|
||||
switch keyType := keyType.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
valueAlloca := c.builder.CreateAlloca(value.Type(), "hashmap.value")
|
||||
c.builder.CreateStore(value, valueAlloca)
|
||||
valuePtr := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "hashmap.valueptr")
|
||||
if keyType.Info()&types.IsString != 0 {
|
||||
params := []llvm.Value{m, key, valuePtr}
|
||||
c.createRuntimeCall("hashmapStringSet", 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}
|
||||
c.createRuntimeCall("hashmapBinarySet", params, "")
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("todo: map update key type: " + keyType.String())
|
||||
}
|
||||
default:
|
||||
return errors.New("todo: map update key type: " + keyType.String())
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value) error {
|
||||
switch keyType := keyType.Underlying().(type) {
|
||||
case *types.Basic:
|
||||
if keyType.Info()&types.IsString != 0 {
|
||||
params := []llvm.Value{m, key}
|
||||
c.createRuntimeCall("hashmapStringDelete", 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}
|
||||
c.createRuntimeCall("hashmapBinaryDelete", params, "")
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("todo: map lookup key type: " + keyType.String())
|
||||
}
|
||||
default:
|
||||
return errors.New("todo: map delete key type: " + keyType.String())
|
||||
}
|
||||
}
|
||||
|
||||
// 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 hashmapHash(data []byte) uint32 {
|
||||
var result uint32 = 2166136261 // FNV offset basis
|
||||
for _, c := range data {
|
||||
result ^= uint32(c)
|
||||
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
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package compiler
|
||||
|
||||
// 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 hashmapHash(data []byte) uint32 {
|
||||
var result uint32 = 2166136261 // FNV offset basis
|
||||
for _, c := range data {
|
||||
result ^= uint32(c)
|
||||
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
|
||||
}
|
|
@ -158,6 +158,41 @@ func hashmapGet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
|||
memzero(value, uintptr(m.valueSize))
|
||||
}
|
||||
|
||||
// Delete a given key from the map. No-op when the key does not exist in the
|
||||
// map.
|
||||
//go:nobounds
|
||||
func hashmapDelete(m *hashmap, key unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) {
|
||||
numBuckets := uintptr(1) << m.bucketBits
|
||||
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
||||
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||
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
|
||||
}
|
||||
|
||||
// Try to find the key.
|
||||
for bucket != nil {
|
||||
for i := uintptr(0); i < 8; i++ {
|
||||
slotKeyOffset := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*uintptr(i)
|
||||
slotKey := unsafe.Pointer(uintptr(unsafe.Pointer(bucket)) + slotKeyOffset)
|
||||
if bucket.tophash[i] == tophash {
|
||||
// This could be the key we're looking for.
|
||||
if keyEqual(key, slotKey, uintptr(m.keySize)) {
|
||||
// Found the key, delete it.
|
||||
bucket.tophash[i] = 0
|
||||
m.count--
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
bucket = bucket.next
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over a hashmap.
|
||||
//go:nobounds
|
||||
func hashmapNext(m *hashmap, it *hashmapIterator, key, value unsafe.Pointer) bool {
|
||||
|
@ -209,6 +244,11 @@ func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer) {
|
|||
hashmapGet(m, key, value, hash, memequal)
|
||||
}
|
||||
|
||||
func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
|
||||
hash := hashmapHash(key, uintptr(m.keySize))
|
||||
hashmapDelete(m, key, hash, memequal)
|
||||
}
|
||||
|
||||
// Hashmap with string keys (a common case).
|
||||
|
||||
func hashmapStringEqual(x, y unsafe.Pointer, n uintptr) bool {
|
||||
|
@ -229,3 +269,8 @@ func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer) {
|
|||
hash := hashmapStringHash(key)
|
||||
hashmapGet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual)
|
||||
}
|
||||
|
||||
func hashmapStringDelete(m *hashmap, key string) {
|
||||
hash := hashmapStringHash(key)
|
||||
hashmapDelete(m, unsafe.Pointer(&key), hash, hashmapStringEqual)
|
||||
}
|
||||
|
|
2
testdata/map.go
предоставленный
2
testdata/map.go
предоставленный
|
@ -22,6 +22,8 @@ func main() {
|
|||
readMap(testmap1, "data")
|
||||
readMap(testmap2, "three")
|
||||
readMap(testmap2, "ten")
|
||||
delete(testmap2, "six")
|
||||
readMap(testmap2, "seven")
|
||||
}
|
||||
|
||||
func readMap(m map[string]int, key string) {
|
||||
|
|
13
testdata/map.txt
предоставленный
13
testdata/map.txt
предоставленный
|
@ -33,3 +33,16 @@ map read: ten = 10
|
|||
ten = 10
|
||||
eleven = 11
|
||||
twelve = 12
|
||||
map length: 11
|
||||
map read: seven = 7
|
||||
one = 1
|
||||
two = 2
|
||||
three = 3
|
||||
four = 4
|
||||
five = 5
|
||||
seven = 7
|
||||
eight = 8
|
||||
nine = 9
|
||||
ten = 10
|
||||
eleven = 11
|
||||
twelve = 12
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче