compiler: support comma-ok in map lookup
Этот коммит содержится в:
родитель
da89464a63
коммит
7c2a6169b0
5 изменённых файлов: 33 добавлений и 15 удалений
|
@ -2242,9 +2242,6 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
case *ssa.Lookup:
|
case *ssa.Lookup:
|
||||||
if expr.CommaOk {
|
|
||||||
return llvm.Value{}, errors.New("todo: lookup with comma-ok")
|
|
||||||
}
|
|
||||||
value, err := c.parseExpr(frame, expr.X)
|
value, err := c.parseExpr(frame, expr.X)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return llvm.Value{}, nil
|
return llvm.Value{}, nil
|
||||||
|
@ -2273,7 +2270,11 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
bufPtr := c.builder.CreateGEP(buf, []llvm.Value{index}, "")
|
bufPtr := c.builder.CreateGEP(buf, []llvm.Value{index}, "")
|
||||||
return c.builder.CreateLoad(bufPtr, ""), nil
|
return c.builder.CreateLoad(bufPtr, ""), nil
|
||||||
case *types.Map:
|
case *types.Map:
|
||||||
return c.emitMapLookup(xType.Key(), expr.Type(), value, index)
|
valueType := expr.Type()
|
||||||
|
if expr.CommaOk {
|
||||||
|
valueType = valueType.(*types.Tuple).At(0).Type()
|
||||||
|
}
|
||||||
|
return c.emitMapLookup(xType.Key(), valueType, value, index, expr.CommaOk)
|
||||||
default:
|
default:
|
||||||
panic("unknown lookup type: " + expr.String())
|
panic("unknown lookup type: " + expr.String())
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,29 +9,37 @@ import (
|
||||||
"github.com/aykevl/go-llvm"
|
"github.com/aykevl/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value) (llvm.Value, error) {
|
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool) (llvm.Value, error) {
|
||||||
llvmValueType, err := c.getLLVMType(valueType)
|
llvmValueType, err := c.getLLVMType(valueType)
|
||||||
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")
|
||||||
|
var commaOkValue llvm.Value
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// key is a string
|
// key is a string
|
||||||
params := []llvm.Value{m, key, mapValuePtr}
|
params := []llvm.Value{m, key, mapValuePtr}
|
||||||
c.createRuntimeCall("hashmapStringGet", params, "")
|
commaOkValue = c.createRuntimeCall("hashmapStringGet", params, "")
|
||||||
return c.builder.CreateLoad(mapValueAlloca, ""), nil
|
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
// key can be compared with runtime.memequal
|
// key can be compared with runtime.memequal
|
||||||
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
|
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
|
||||||
c.builder.CreateStore(key, keyAlloca)
|
c.builder.CreateStore(key, keyAlloca)
|
||||||
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
|
||||||
params := []llvm.Value{m, keyPtr, mapValuePtr}
|
params := []llvm.Value{m, keyPtr, mapValuePtr}
|
||||||
c.createRuntimeCall("hashmapBinaryGet", params, "")
|
commaOkValue = c.createRuntimeCall("hashmapBinaryGet", params, "")
|
||||||
return c.builder.CreateLoad(mapValueAlloca, ""), nil
|
|
||||||
} else {
|
} else {
|
||||||
return llvm.Value{}, errors.New("todo: map lookup key type: " + keyType.String())
|
return llvm.Value{}, errors.New("todo: map lookup key type: " + keyType.String())
|
||||||
}
|
}
|
||||||
|
mapValue := c.builder.CreateLoad(mapValueAlloca, "")
|
||||||
|
if commaOk {
|
||||||
|
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{llvmValueType, c.ctx.Int1Type()}, false))
|
||||||
|
tuple = c.builder.CreateInsertValue(tuple, mapValue, 0, "")
|
||||||
|
tuple = c.builder.CreateInsertValue(tuple, commaOkValue, 1, "")
|
||||||
|
return tuple, nil
|
||||||
|
} else {
|
||||||
|
return mapValue, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value) error {
|
func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value) error {
|
||||||
|
|
|
@ -122,7 +122,7 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
||||||
|
|
||||||
// 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.
|
||||||
//go:nobounds
|
//go:nobounds
|
||||||
func hashmapGet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) {
|
func hashmapGet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) bool {
|
||||||
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
|
||||||
|
@ -147,7 +147,7 @@ func hashmapGet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
||||||
if keyEqual(key, slotKey, uintptr(m.keySize)) {
|
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 true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,6 +156,7 @@ func hashmapGet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
||||||
|
|
||||||
// Did not find the key.
|
// Did not find the key.
|
||||||
memzero(value, uintptr(m.valueSize))
|
memzero(value, uintptr(m.valueSize))
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete a given key from the map. No-op when the key does not exist in the
|
// Delete a given key from the map. No-op when the key does not exist in the
|
||||||
|
@ -239,9 +240,9 @@ func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
|
||||||
hashmapSet(m, key, value, hash, memequal)
|
hashmapSet(m, key, value, hash, memequal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer) {
|
func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer) bool {
|
||||||
hash := hashmapHash(key, uintptr(m.keySize))
|
hash := hashmapHash(key, uintptr(m.keySize))
|
||||||
hashmapGet(m, key, value, hash, memequal)
|
return hashmapGet(m, key, value, hash, memequal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
|
func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
|
||||||
|
@ -265,9 +266,9 @@ func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) {
|
||||||
hashmapSet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual)
|
hashmapSet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer) {
|
func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer) bool {
|
||||||
hash := hashmapStringHash(key)
|
hash := hashmapStringHash(key)
|
||||||
hashmapGet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual)
|
return hashmapGet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual)
|
||||||
}
|
}
|
||||||
|
|
||||||
func hashmapStringDelete(m *hashmap, key string) {
|
func hashmapStringDelete(m *hashmap, key string) {
|
||||||
|
|
6
testdata/map.go
предоставленный
6
testdata/map.go
предоставленный
|
@ -24,6 +24,8 @@ func main() {
|
||||||
readMap(testmap2, "ten")
|
readMap(testmap2, "ten")
|
||||||
delete(testmap2, "six")
|
delete(testmap2, "six")
|
||||||
readMap(testmap2, "seven")
|
readMap(testmap2, "seven")
|
||||||
|
lookup(testmap2, "eight")
|
||||||
|
lookup(testmap2, "nokey")
|
||||||
}
|
}
|
||||||
|
|
||||||
func readMap(m map[string]int, key string) {
|
func readMap(m map[string]int, key string) {
|
||||||
|
@ -33,3 +35,7 @@ func readMap(m map[string]int, key string) {
|
||||||
println(" ", k, "=", v)
|
println(" ", k, "=", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
func lookup(m map[string]int, key string) {
|
||||||
|
value, ok := m[key]
|
||||||
|
println("lookup with comma-ok:", key, value, ok)
|
||||||
|
}
|
||||||
|
|
2
testdata/map.txt
предоставленный
2
testdata/map.txt
предоставленный
|
@ -46,3 +46,5 @@ map read: seven = 7
|
||||||
ten = 10
|
ten = 10
|
||||||
eleven = 11
|
eleven = 11
|
||||||
twelve = 12
|
twelve = 12
|
||||||
|
lookup with comma-ok: eight 8 true
|
||||||
|
lookup with comma-ok: nokey 0 false
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче