compiler: refactor map operations to use the builder object
Этот коммит содержится в:
родитель
ce84f77c8d
коммит
2d9f3605b9
2 изменённых файлов: 60 добавлений и 54 удалений
|
@ -1107,7 +1107,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) {
|
||||||
key := frame.getValue(instr.Key)
|
key := frame.getValue(instr.Key)
|
||||||
value := frame.getValue(instr.Value)
|
value := frame.getValue(instr.Value)
|
||||||
mapType := instr.Map.Type().Underlying().(*types.Map)
|
mapType := instr.Map.Type().Underlying().(*types.Map)
|
||||||
c.emitMapUpdate(mapType.Key(), m, key, value, instr.Pos())
|
frame.createMapUpdate(mapType.Key(), m, key, value, instr.Pos())
|
||||||
case *ssa.Panic:
|
case *ssa.Panic:
|
||||||
value := frame.getValue(instr.X)
|
value := frame.getValue(instr.X)
|
||||||
c.createRuntimeCall("_panic", []llvm.Value{value}, "")
|
c.createRuntimeCall("_panic", []llvm.Value{value}, "")
|
||||||
|
@ -1219,7 +1219,7 @@ func (c *Compiler) parseBuiltin(frame *Frame, args []ssa.Value, callName string,
|
||||||
case "delete":
|
case "delete":
|
||||||
m := frame.getValue(args[0])
|
m := frame.getValue(args[0])
|
||||||
key := frame.getValue(args[1])
|
key := frame.getValue(args[1])
|
||||||
return llvm.Value{}, c.emitMapDelete(args[1].Type(), m, key, pos)
|
return llvm.Value{}, frame.createMapDelete(args[1].Type(), m, key, pos)
|
||||||
case "imag":
|
case "imag":
|
||||||
cplx := frame.getValue(args[0])
|
cplx := frame.getValue(args[0])
|
||||||
return c.builder.CreateExtractValue(cplx, 1, "imag"), nil
|
return c.builder.CreateExtractValue(cplx, 1, "imag"), nil
|
||||||
|
@ -1619,7 +1619,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
if expr.CommaOk {
|
if expr.CommaOk {
|
||||||
valueType = valueType.(*types.Tuple).At(0).Type()
|
valueType = valueType.(*types.Tuple).At(0).Type()
|
||||||
}
|
}
|
||||||
return c.emitMapLookup(xType.Key(), valueType, value, index, expr.CommaOk, expr.Pos())
|
return frame.createMapLookup(xType.Key(), valueType, value, index, expr.CommaOk, expr.Pos())
|
||||||
default:
|
default:
|
||||||
panic("unknown lookup type: " + expr.String())
|
panic("unknown lookup type: " + expr.String())
|
||||||
}
|
}
|
||||||
|
@ -1631,7 +1631,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
|
||||||
val := frame.getValue(expr.X)
|
val := frame.getValue(expr.X)
|
||||||
return frame.createMakeInterface(val, expr.X.Type(), expr.Pos()), nil
|
return frame.createMakeInterface(val, expr.X.Type(), expr.Pos()), nil
|
||||||
case *ssa.MakeMap:
|
case *ssa.MakeMap:
|
||||||
return c.emitMakeMap(frame, expr)
|
return frame.createMakeMap(expr)
|
||||||
case *ssa.MakeSlice:
|
case *ssa.MakeSlice:
|
||||||
sliceLen := frame.getValue(expr.Len)
|
sliceLen := frame.getValue(expr.Len)
|
||||||
sliceCap := frame.getValue(expr.Cap)
|
sliceCap := frame.getValue(expr.Cap)
|
||||||
|
|
106
compiler/map.go
106
compiler/map.go
|
@ -10,56 +10,58 @@ import (
|
||||||
"tinygo.org/x/go-llvm"
|
"tinygo.org/x/go-llvm"
|
||||||
)
|
)
|
||||||
|
|
||||||
// emitMakeMap creates a new map object (runtime.hashmap) by allocating and
|
// createMakeMap creates a new map object (runtime.hashmap) by allocating and
|
||||||
// initializing an appropriately sized object.
|
// initializing an appropriately sized object.
|
||||||
func (c *Compiler) emitMakeMap(frame *Frame, expr *ssa.MakeMap) (llvm.Value, error) {
|
func (b *builder) createMakeMap(expr *ssa.MakeMap) (llvm.Value, error) {
|
||||||
mapType := expr.Type().Underlying().(*types.Map)
|
mapType := expr.Type().Underlying().(*types.Map)
|
||||||
keyType := mapType.Key().Underlying()
|
keyType := mapType.Key().Underlying()
|
||||||
llvmValueType := c.getLLVMType(mapType.Elem().Underlying())
|
llvmValueType := b.getLLVMType(mapType.Elem().Underlying())
|
||||||
var llvmKeyType llvm.Type
|
var llvmKeyType llvm.Type
|
||||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||||
// String keys.
|
// String keys.
|
||||||
llvmKeyType = c.getLLVMType(keyType)
|
llvmKeyType = b.getLLVMType(keyType)
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
// Trivially comparable keys.
|
// Trivially comparable keys.
|
||||||
llvmKeyType = c.getLLVMType(keyType)
|
llvmKeyType = b.getLLVMType(keyType)
|
||||||
} else {
|
} else {
|
||||||
// All other keys. Implemented as map[interface{}]valueType for ease of
|
// All other keys. Implemented as map[interface{}]valueType for ease of
|
||||||
// implementation.
|
// implementation.
|
||||||
llvmKeyType = c.getLLVMRuntimeType("_interface")
|
llvmKeyType = b.getLLVMRuntimeType("_interface")
|
||||||
}
|
}
|
||||||
keySize := c.targetData.TypeAllocSize(llvmKeyType)
|
keySize := b.targetData.TypeAllocSize(llvmKeyType)
|
||||||
valueSize := c.targetData.TypeAllocSize(llvmValueType)
|
valueSize := b.targetData.TypeAllocSize(llvmValueType)
|
||||||
llvmKeySize := llvm.ConstInt(c.ctx.Int8Type(), keySize, false)
|
llvmKeySize := llvm.ConstInt(b.ctx.Int8Type(), keySize, false)
|
||||||
llvmValueSize := llvm.ConstInt(c.ctx.Int8Type(), valueSize, false)
|
llvmValueSize := llvm.ConstInt(b.ctx.Int8Type(), valueSize, false)
|
||||||
sizeHint := llvm.ConstInt(c.uintptrType, 8, false)
|
sizeHint := llvm.ConstInt(b.uintptrType, 8, false)
|
||||||
if expr.Reserve != nil {
|
if expr.Reserve != nil {
|
||||||
sizeHint = frame.getValue(expr.Reserve)
|
sizeHint = b.getValue(expr.Reserve)
|
||||||
var err error
|
var err error
|
||||||
sizeHint, err = frame.createConvert(expr.Reserve.Type(), types.Typ[types.Uintptr], sizeHint, expr.Pos())
|
sizeHint, err = b.createConvert(expr.Reserve.Type(), types.Typ[types.Uintptr], sizeHint, expr.Pos())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return llvm.Value{}, err
|
return llvm.Value{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hashmap := c.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
|
hashmap := b.createRuntimeCall("hashmapMake", []llvm.Value{llvmKeySize, llvmValueSize, sizeHint}, "")
|
||||||
return hashmap, nil
|
return hashmap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
// createMapLookup returns the value in a map. It calls a runtime function
|
||||||
llvmValueType := c.getLLVMType(valueType)
|
// depending on the map key type to load the map value and its comma-ok value.
|
||||||
|
func (b *builder) createMapLookup(keyType, valueType types.Type, m, key llvm.Value, commaOk bool, pos token.Pos) (llvm.Value, error) {
|
||||||
|
llvmValueType := b.getLLVMType(valueType)
|
||||||
|
|
||||||
// Allocate the memory for the resulting type. Do not zero this memory: it
|
// Allocate the memory for the resulting type. Do not zero this memory: it
|
||||||
// will be zeroed by the hashmap get implementation if the key is not
|
// will be zeroed by the hashmap get implementation if the key is not
|
||||||
// present in the map.
|
// present in the map.
|
||||||
mapValueAlloca, mapValuePtr, mapValueAllocaSize := c.createTemporaryAlloca(llvmValueType, "hashmap.value")
|
mapValueAlloca, mapValuePtr, mapValueAllocaSize := b.createTemporaryAlloca(llvmValueType, "hashmap.value")
|
||||||
|
|
||||||
// We need the map size (with type uintptr) to pass to the hashmap*Get
|
// We need the map size (with type uintptr) to pass to the hashmap*Get
|
||||||
// functions. This is necessary because those *Get functions are valid on
|
// functions. This is necessary because those *Get functions are valid on
|
||||||
// nil maps, and they'll need to zero the value pointer by that number of
|
// nil maps, and they'll need to zero the value pointer by that number of
|
||||||
// bytes.
|
// bytes.
|
||||||
mapValueSize := mapValueAllocaSize
|
mapValueSize := mapValueAllocaSize
|
||||||
if mapValueSize.Type().IntTypeWidth() > c.uintptrType.IntTypeWidth() {
|
if mapValueSize.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() {
|
||||||
mapValueSize = llvm.ConstTrunc(mapValueSize, c.uintptrType)
|
mapValueSize = llvm.ConstTrunc(mapValueSize, b.uintptrType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the lookup. How it is done depends on the key type.
|
// Do the lookup. How it is done depends on the key type.
|
||||||
|
@ -68,84 +70,88 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
|
||||||
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, mapValueSize}
|
params := []llvm.Value{m, key, mapValuePtr, mapValueSize}
|
||||||
commaOkValue = c.createRuntimeCall("hashmapStringGet", params, "")
|
commaOkValue = b.createRuntimeCall("hashmapStringGet", params, "")
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
// key can be compared with runtime.memequal
|
// key can be compared with runtime.memequal
|
||||||
// Store the key in an alloca, in the entry block to avoid dynamic stack
|
// Store the key in an alloca, in the entry block to avoid dynamic stack
|
||||||
// growth.
|
// growth.
|
||||||
mapKeyAlloca, mapKeyPtr, mapKeySize := c.createTemporaryAlloca(key.Type(), "hashmap.key")
|
mapKeyAlloca, mapKeyPtr, mapKeySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
c.builder.CreateStore(key, mapKeyAlloca)
|
b.CreateStore(key, mapKeyAlloca)
|
||||||
// Fetch the value from the hashmap.
|
// Fetch the value from the hashmap.
|
||||||
params := []llvm.Value{m, mapKeyPtr, mapValuePtr, mapValueSize}
|
params := []llvm.Value{m, mapKeyPtr, mapValuePtr, mapValueSize}
|
||||||
commaOkValue = c.createRuntimeCall("hashmapBinaryGet", params, "")
|
commaOkValue = b.createRuntimeCall("hashmapBinaryGet", params, "")
|
||||||
c.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
b.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
||||||
} else {
|
} else {
|
||||||
// Not trivially comparable using memcmp. Make it an interface instead.
|
// Not trivially comparable using memcmp. Make it an interface instead.
|
||||||
itfKey := key
|
itfKey := key
|
||||||
if _, ok := keyType.(*types.Interface); !ok {
|
if _, ok := keyType.(*types.Interface); !ok {
|
||||||
// Not already an interface, so convert it to an interface now.
|
// Not already an interface, so convert it to an interface now.
|
||||||
itfKey = c.parseMakeInterface(key, keyType, pos)
|
itfKey = b.createMakeInterface(key, keyType, pos)
|
||||||
}
|
}
|
||||||
params := []llvm.Value{m, itfKey, mapValuePtr, mapValueSize}
|
params := []llvm.Value{m, itfKey, mapValuePtr, mapValueSize}
|
||||||
commaOkValue = c.createRuntimeCall("hashmapInterfaceGet", params, "")
|
commaOkValue = b.createRuntimeCall("hashmapInterfaceGet", params, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the resulting value from the hashmap. The value is set to the zero
|
// Load the resulting value from the hashmap. The value is set to the zero
|
||||||
// value if the key doesn't exist in the hashmap.
|
// value if the key doesn't exist in the hashmap.
|
||||||
mapValue := c.builder.CreateLoad(mapValueAlloca, "")
|
mapValue := b.CreateLoad(mapValueAlloca, "")
|
||||||
c.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize)
|
b.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize)
|
||||||
|
|
||||||
if commaOk {
|
if commaOk {
|
||||||
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{llvmValueType, c.ctx.Int1Type()}, false))
|
tuple := llvm.Undef(b.ctx.StructType([]llvm.Type{llvmValueType, b.ctx.Int1Type()}, false))
|
||||||
tuple = c.builder.CreateInsertValue(tuple, mapValue, 0, "")
|
tuple = b.CreateInsertValue(tuple, mapValue, 0, "")
|
||||||
tuple = c.builder.CreateInsertValue(tuple, commaOkValue, 1, "")
|
tuple = b.CreateInsertValue(tuple, commaOkValue, 1, "")
|
||||||
return tuple, nil
|
return tuple, nil
|
||||||
} else {
|
} else {
|
||||||
return mapValue, nil
|
return mapValue, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) emitMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
// createMapUpdate updates a map key to a given value, by creating an
|
||||||
valueAlloca, valuePtr, valueSize := c.createTemporaryAlloca(value.Type(), "hashmap.value")
|
// appropriate runtime call.
|
||||||
c.builder.CreateStore(value, valueAlloca)
|
func (b *builder) createMapUpdate(keyType types.Type, m, key, value llvm.Value, pos token.Pos) {
|
||||||
|
valueAlloca, valuePtr, valueSize := b.createTemporaryAlloca(value.Type(), "hashmap.value")
|
||||||
|
b.CreateStore(value, valueAlloca)
|
||||||
keyType = keyType.Underlying()
|
keyType = keyType.Underlying()
|
||||||
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, valuePtr}
|
params := []llvm.Value{m, key, valuePtr}
|
||||||
c.createRuntimeCall("hashmapStringSet", params, "")
|
b.createRuntimeCall("hashmapStringSet", params, "")
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
// key can be compared with runtime.memequal
|
// key can be compared with runtime.memequal
|
||||||
keyAlloca, keyPtr, keySize := c.createTemporaryAlloca(key.Type(), "hashmap.key")
|
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
c.builder.CreateStore(key, keyAlloca)
|
b.CreateStore(key, keyAlloca)
|
||||||
params := []llvm.Value{m, keyPtr, valuePtr}
|
params := []llvm.Value{m, keyPtr, valuePtr}
|
||||||
c.createRuntimeCall("hashmapBinarySet", params, "")
|
b.createRuntimeCall("hashmapBinarySet", params, "")
|
||||||
c.emitLifetimeEnd(keyPtr, keySize)
|
b.emitLifetimeEnd(keyPtr, keySize)
|
||||||
} else {
|
} else {
|
||||||
// Key is not trivially comparable, so compare it as an interface instead.
|
// Key is not trivially comparable, so compare it as an interface instead.
|
||||||
itfKey := key
|
itfKey := key
|
||||||
if _, ok := keyType.(*types.Interface); !ok {
|
if _, ok := keyType.(*types.Interface); !ok {
|
||||||
// Not already an interface, so convert it to an interface first.
|
// Not already an interface, so convert it to an interface first.
|
||||||
itfKey = c.parseMakeInterface(key, keyType, pos)
|
itfKey = b.createMakeInterface(key, keyType, pos)
|
||||||
}
|
}
|
||||||
params := []llvm.Value{m, itfKey, valuePtr}
|
params := []llvm.Value{m, itfKey, valuePtr}
|
||||||
c.createRuntimeCall("hashmapInterfaceSet", params, "")
|
b.createRuntimeCall("hashmapInterfaceSet", params, "")
|
||||||
}
|
}
|
||||||
c.emitLifetimeEnd(valuePtr, valueSize)
|
b.emitLifetimeEnd(valuePtr, valueSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error {
|
// createMapDelete deletes a key from a map by calling the appropriate runtime
|
||||||
|
// function. It is the implementation of the Go delete() builtin.
|
||||||
|
func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos token.Pos) error {
|
||||||
keyType = keyType.Underlying()
|
keyType = keyType.Underlying()
|
||||||
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}
|
params := []llvm.Value{m, key}
|
||||||
c.createRuntimeCall("hashmapStringDelete", params, "")
|
b.createRuntimeCall("hashmapStringDelete", params, "")
|
||||||
return nil
|
return nil
|
||||||
} else if hashmapIsBinaryKey(keyType) {
|
} else if hashmapIsBinaryKey(keyType) {
|
||||||
keyAlloca, keyPtr, keySize := c.createTemporaryAlloca(key.Type(), "hashmap.key")
|
keyAlloca, keyPtr, keySize := b.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||||
c.builder.CreateStore(key, keyAlloca)
|
b.CreateStore(key, keyAlloca)
|
||||||
params := []llvm.Value{m, keyPtr}
|
params := []llvm.Value{m, keyPtr}
|
||||||
c.createRuntimeCall("hashmapBinaryDelete", params, "")
|
b.createRuntimeCall("hashmapBinaryDelete", params, "")
|
||||||
c.emitLifetimeEnd(keyPtr, keySize)
|
b.emitLifetimeEnd(keyPtr, keySize)
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
// Key is not trivially comparable, so compare it as an interface
|
// Key is not trivially comparable, so compare it as an interface
|
||||||
|
@ -153,10 +159,10 @@ func (c *Compiler) emitMapDelete(keyType types.Type, m, key llvm.Value, pos toke
|
||||||
itfKey := key
|
itfKey := key
|
||||||
if _, ok := keyType.(*types.Interface); !ok {
|
if _, ok := keyType.(*types.Interface); !ok {
|
||||||
// Not already an interface, so convert it to an interface first.
|
// Not already an interface, so convert it to an interface first.
|
||||||
itfKey = c.parseMakeInterface(key, keyType, pos)
|
itfKey = b.createMakeInterface(key, keyType, pos)
|
||||||
}
|
}
|
||||||
params := []llvm.Value{m, itfKey}
|
params := []llvm.Value{m, itfKey}
|
||||||
c.createRuntimeCall("hashmapInterfaceDelete", params, "")
|
b.createRuntimeCall("hashmapInterfaceDelete", params, "")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче