compiler: allow structs in map keys

Этот коммит содержится в:
Ayke van Laethem 2018-10-20 17:21:13 +02:00
родитель c0c1ccb381
коммит 77d6d6c417
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED

Просмотреть файл

@ -10,76 +10,68 @@ import (
) )
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) (llvm.Value, error) {
switch keyType := keyType.Underlying().(type) { llvmValueType, err := c.getLLVMType(valueType)
case *types.Basic: if err != nil {
llvmValueType, err := c.getLLVMType(valueType) return llvm.Value{}, err
if err != nil { }
return llvm.Value{}, err mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "hashmap.value")
} mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "hashmap.valueptr")
mapValueAlloca := c.builder.CreateAlloca(llvmValueType, "hashmap.value") if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
mapValuePtr := c.builder.CreateBitCast(mapValueAlloca, c.i8ptrType, "hashmap.valueptr") // key is a string
if keyType.Info()&types.IsString != 0 { params := []llvm.Value{m, key, mapValuePtr}
params := []llvm.Value{m, key, mapValuePtr} c.createRuntimeCall("hashmapStringGet", params, "")
c.createRuntimeCall("hashmapStringGet", params, "") return c.builder.CreateLoad(mapValueAlloca, ""), nil
return c.builder.CreateLoad(mapValueAlloca, ""), nil } else if hashmapIsBinaryKey(keyType) {
} else if keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 { // 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, "") c.createRuntimeCall("hashmapBinaryGet", params, "")
return c.builder.CreateLoad(mapValueAlloca, ""), nil return c.builder.CreateLoad(mapValueAlloca, ""), nil
} else { } 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 llvm.Value{}, errors.New("todo: map lookup key type: " + keyType.String())
} }
} }
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 {
switch keyType := keyType.Underlying().(type) { valueAlloca := c.builder.CreateAlloca(value.Type(), "hashmap.value")
case *types.Basic: c.builder.CreateStore(value, valueAlloca)
valueAlloca := c.builder.CreateAlloca(value.Type(), "hashmap.value") valuePtr := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "hashmap.valueptr")
c.builder.CreateStore(value, valueAlloca) keyType = keyType.Underlying()
valuePtr := c.builder.CreateBitCast(valueAlloca, c.i8ptrType, "hashmap.valueptr") if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
if keyType.Info()&types.IsString != 0 { // key is a string
params := []llvm.Value{m, key, valuePtr} params := []llvm.Value{m, key, valuePtr}
c.createRuntimeCall("hashmapStringSet", params, "") c.createRuntimeCall("hashmapStringSet", params, "")
return nil return nil
} else if keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 { } else if hashmapIsBinaryKey(keyType) {
keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key") // key can be compared with runtime.memequal
c.builder.CreateStore(key, keyAlloca) keyAlloca := c.builder.CreateAlloca(key.Type(), "hashmap.key")
keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr") c.builder.CreateStore(key, keyAlloca)
params := []llvm.Value{m, keyPtr, valuePtr} keyPtr := c.builder.CreateBitCast(keyAlloca, c.i8ptrType, "hashmap.keyptr")
c.createRuntimeCall("hashmapBinarySet", params, "") params := []llvm.Value{m, keyPtr, valuePtr}
return nil c.createRuntimeCall("hashmapBinarySet", params, "")
} else { return nil
return errors.New("todo: map update key type: " + keyType.String()) } else {
}
default:
return errors.New("todo: map update key type: " + keyType.String()) return errors.New("todo: map update key type: " + keyType.String())
} }
} }
func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value) error { func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value) error {
switch keyType := keyType.Underlying().(type) { keyType = keyType.Underlying()
case *types.Basic: if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
if keyType.Info()&types.IsString != 0 { // key is a string
params := []llvm.Value{m, key} params := []llvm.Value{m, key}
c.createRuntimeCall("hashmapStringDelete", params, "") c.createRuntimeCall("hashmapStringDelete", params, "")
return nil return nil
} else if keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 { } else if hashmapIsBinaryKey(keyType) {
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} params := []llvm.Value{m, keyPtr}
c.createRuntimeCall("hashmapBinaryDelete", params, "") c.createRuntimeCall("hashmapBinaryDelete", params, "")
return nil return nil
} else { } else {
return errors.New("todo: map lookup key type: " + keyType.String())
}
default:
return errors.New("todo: map delete key type: " + keyType.String()) return errors.New("todo: map delete key type: " + keyType.String())
} }
} }
@ -105,3 +97,22 @@ func hashmapTopHash(hash uint32) uint8 {
} }
return tophash return tophash
} }
// Returns true if this key type does not contain strings, interfaces etc., so
// can be compared with runtime.memequal.
func hashmapIsBinaryKey(keyType types.Type) bool {
switch keyType := keyType.(type) {
case *types.Basic:
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
case *types.Struct:
for i := 0; i < keyType.NumFields(); i++ {
fieldType := keyType.Field(i).Type().Underlying()
if !hashmapIsBinaryKey(fieldType) {
return false
}
}
return true
default:
return false
}
}