compiler,runtime: support operations on nil map
The index expression and delete keyword are valid on nil maps, so the runtime must be modified to support this.
Этот коммит содержится в:
родитель
53688c86c8
коммит
4dfc289ae5
4 изменённых файлов: 37 добавлений и 12 удалений
|
@ -51,14 +51,23 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
|
|||
// 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
|
||||
// present in the map.
|
||||
mapValueAlloca, mapValuePtr, mapValueSize := c.createTemporaryAlloca(llvmValueType, "hashmap.value")
|
||||
mapValueAlloca, mapValuePtr, mapValueAllocaSize := c.createTemporaryAlloca(llvmValueType, "hashmap.value")
|
||||
|
||||
// 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
|
||||
// nil maps, and they'll need to zero the value pointer by that number of
|
||||
// bytes.
|
||||
mapValueSize := mapValueAllocaSize
|
||||
if mapValueSize.Type().IntTypeWidth() > c.uintptrType.IntTypeWidth() {
|
||||
mapValueSize = llvm.ConstTrunc(mapValueSize, c.uintptrType)
|
||||
}
|
||||
|
||||
// Do the lookup. How it is done depends on the key type.
|
||||
var commaOkValue llvm.Value
|
||||
keyType = keyType.Underlying()
|
||||
if t, ok := keyType.(*types.Basic); ok && t.Info()&types.IsString != 0 {
|
||||
// key is a string
|
||||
params := []llvm.Value{m, key, mapValuePtr}
|
||||
params := []llvm.Value{m, key, mapValuePtr, mapValueSize}
|
||||
commaOkValue = c.createRuntimeCall("hashmapStringGet", params, "")
|
||||
} else if hashmapIsBinaryKey(keyType) {
|
||||
// key can be compared with runtime.memequal
|
||||
|
@ -67,7 +76,7 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
|
|||
mapKeyAlloca, mapKeyPtr, mapKeySize := c.createTemporaryAlloca(key.Type(), "hashmap.key")
|
||||
c.builder.CreateStore(key, mapKeyAlloca)
|
||||
// Fetch the value from the hashmap.
|
||||
params := []llvm.Value{m, mapKeyPtr, mapValuePtr}
|
||||
params := []llvm.Value{m, mapKeyPtr, mapValuePtr, mapValueSize}
|
||||
commaOkValue = c.createRuntimeCall("hashmapBinaryGet", params, "")
|
||||
c.emitLifetimeEnd(mapKeyPtr, mapKeySize)
|
||||
} else {
|
||||
|
@ -77,14 +86,14 @@ func (c *Compiler) emitMapLookup(keyType, valueType types.Type, m, key llvm.Valu
|
|||
// Not already an interface, so convert it to an interface now.
|
||||
itfKey = c.parseMakeInterface(key, keyType, pos)
|
||||
}
|
||||
params := []llvm.Value{m, itfKey, mapValuePtr}
|
||||
params := []llvm.Value{m, itfKey, mapValuePtr, mapValueSize}
|
||||
commaOkValue = c.createRuntimeCall("hashmapInterfaceGet", params, "")
|
||||
}
|
||||
|
||||
// Load the resulting value from the hashmap. The value is set to the zero
|
||||
// value if the key doesn't exist in the hashmap.
|
||||
mapValue := c.builder.CreateLoad(mapValueAlloca, "")
|
||||
c.emitLifetimeEnd(mapValuePtr, mapValueSize)
|
||||
c.emitLifetimeEnd(mapValuePtr, mapValueAllocaSize)
|
||||
|
||||
if commaOk {
|
||||
tuple := llvm.Undef(c.ctx.StructType([]llvm.Type{llvmValueType, c.ctx.Int1Type()}, false))
|
||||
|
|
|
@ -166,7 +166,14 @@ func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash u
|
|||
|
||||
// Get the value of a specified key, or zero the value if not found.
|
||||
//go:nobounds
|
||||
func hashmapGet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) bool {
|
||||
func hashmapGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) bool {
|
||||
if m == nil {
|
||||
// Getting a value out of a nil map is valid. From the spec:
|
||||
// > if the map is nil or does not contain such an entry, a[x] is the
|
||||
// > zero value for the element type of M
|
||||
memzero(value, uintptr(valueSize))
|
||||
return false
|
||||
}
|
||||
numBuckets := uintptr(1) << m.bucketBits
|
||||
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
||||
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||
|
@ -207,6 +214,12 @@ func hashmapGet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
|||
// map.
|
||||
//go:nobounds
|
||||
func hashmapDelete(m *hashmap, key unsafe.Pointer, hash uint32, keyEqual func(x, y unsafe.Pointer, n uintptr) bool) {
|
||||
if m == nil {
|
||||
// The delete builtin is defined even when the map is nil. From the spec:
|
||||
// > If the map m is nil or the element m[k] does not exist, delete is a
|
||||
// > no-op.
|
||||
return
|
||||
}
|
||||
numBuckets := uintptr(1) << m.bucketBits
|
||||
bucketNumber := (uintptr(hash) & (numBuckets - 1))
|
||||
bucketSize := unsafe.Sizeof(hashmapBucket{}) + uintptr(m.keySize)*8 + uintptr(m.valueSize)*8
|
||||
|
@ -284,9 +297,9 @@ func hashmapBinarySet(m *hashmap, key, value unsafe.Pointer) {
|
|||
hashmapSet(m, key, value, hash, memequal)
|
||||
}
|
||||
|
||||
func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer) bool {
|
||||
func hashmapBinaryGet(m *hashmap, key, value unsafe.Pointer, valueSize uintptr) bool {
|
||||
hash := hashmapHash(key, uintptr(m.keySize))
|
||||
return hashmapGet(m, key, value, hash, memequal)
|
||||
return hashmapGet(m, key, value, valueSize, hash, memequal)
|
||||
}
|
||||
|
||||
func hashmapBinaryDelete(m *hashmap, key unsafe.Pointer) {
|
||||
|
@ -310,9 +323,9 @@ func hashmapStringSet(m *hashmap, key string, value unsafe.Pointer) {
|
|||
hashmapSet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual)
|
||||
}
|
||||
|
||||
func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer) bool {
|
||||
func hashmapStringGet(m *hashmap, key string, value unsafe.Pointer, valueSize uintptr) bool {
|
||||
hash := hashmapStringHash(key)
|
||||
return hashmapGet(m, unsafe.Pointer(&key), value, hash, hashmapStringEqual)
|
||||
return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash, hashmapStringEqual)
|
||||
}
|
||||
|
||||
func hashmapStringDelete(m *hashmap, key string) {
|
||||
|
@ -381,9 +394,9 @@ func hashmapInterfaceSet(m *hashmap, key interface{}, value unsafe.Pointer) {
|
|||
hashmapSet(m, unsafe.Pointer(&key), value, hash, hashmapInterfaceEqual)
|
||||
}
|
||||
|
||||
func hashmapInterfaceGet(m *hashmap, key interface{}, value unsafe.Pointer) bool {
|
||||
func hashmapInterfaceGet(m *hashmap, key interface{}, value unsafe.Pointer, valueSize uintptr) bool {
|
||||
hash := hashmapInterfaceHash(key)
|
||||
return hashmapGet(m, unsafe.Pointer(&key), value, hash, hashmapInterfaceEqual)
|
||||
return hashmapGet(m, unsafe.Pointer(&key), value, valueSize, hash, hashmapInterfaceEqual)
|
||||
}
|
||||
|
||||
func hashmapInterfaceDelete(m *hashmap, key interface{}) {
|
||||
|
|
2
testdata/map.go
предоставленный
2
testdata/map.go
предоставленный
|
@ -44,6 +44,8 @@ func main() {
|
|||
var nilmap map[string]int
|
||||
println(m == nil, m != nil, len(m))
|
||||
println(nilmap == nil, nilmap != nil, len(nilmap))
|
||||
delete(nilmap, "foo")
|
||||
println("nilmap:", nilmap["bar"])
|
||||
println(testmapIntInt[2])
|
||||
testmapIntInt[2] = 42
|
||||
println(testmapIntInt[2])
|
||||
|
|
1
testdata/map.txt
предоставленный
1
testdata/map.txt
предоставленный
|
@ -50,6 +50,7 @@ lookup with comma-ok: eight 8 true
|
|||
lookup with comma-ok: nokey 0 false
|
||||
false true 2
|
||||
true false 0
|
||||
nilmap: 0
|
||||
4
|
||||
42
|
||||
4321
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче