compiler: implement clear builtin for maps

Этот коммит содержится в:
Ayke van Laethem 2023-07-07 15:47:49 +02:00 коммит произвёл Ron Evans
родитель a2f886a67a
коммит f1e25a18d2
7 изменённых файлов: 64 добавлений и 0 удалений

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

@ -1631,6 +1631,10 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
}, "")
call.AddCallSiteAttribute(1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elementAlign)))
return llvm.Value{}, nil
case *types.Map:
m := argValues[0]
b.createMapClear(m)
return llvm.Value{}, nil
default:
return llvm.Value{}, b.makeError(pos, "unsupported type in clear builtin: "+typ.String())

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

@ -185,6 +185,11 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok
}
}
// Clear the given map.
func (b *builder) createMapClear(m llvm.Value) {
b.createRuntimeCall("hashmapClear", []llvm.Value{m}, "")
}
// createMapIteratorNext lowers the *ssa.Next instruction for iterating over a
// map. It returns a tuple of {bool, key, value} with the result of the
// iteration.

4
compiler/testdata/go1.21.go предоставленный
Просмотреть файл

@ -59,3 +59,7 @@ func clearSlice(s []int) {
func clearZeroSizedSlice(s []struct{}) {
clear(s)
}
func clearMap(m map[string]int) {
clear(m)
}

9
compiler/testdata/go1.21.ll предоставленный
Просмотреть файл

@ -147,6 +147,15 @@ entry:
ret void
}
; Function Attrs: nounwind
define hidden void @main.clearMap(ptr dereferenceable_or_null(40) %m, ptr %context) unnamed_addr #2 {
entry:
call void @runtime.hashmapClear(ptr %m, ptr undef) #5
ret void
}
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
declare i32 @llvm.smin.i32(i32, i32) #4

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

@ -91,6 +91,35 @@ func hashmapMakeUnsafePointer(keySize, valueSize uintptr, sizeHint uintptr, alg
return (unsafe.Pointer)(hashmapMake(keySize, valueSize, sizeHint, alg))
}
// Remove all entries from the map, without actually deallocating the space for
// it. This is used for the clear builtin, and can be used to reuse a map (to
// avoid extra heap allocations).
func hashmapClear(m *hashmap) {
if m == nil {
// Nothing to do. According to the spec:
// > If the map or slice is nil, clear is a no-op.
return
}
m.count = 0
numBuckets := uintptr(1) << m.bucketBits
bucketSize := hashmapBucketSize(m)
for i := uintptr(0); i < numBuckets; i++ {
bucket := hashmapBucketAddr(m, m.buckets, i)
for bucket != nil {
// Clear the tophash, to mark these keys/values as removed.
bucket.tophash = [8]uint8{}
// Clear the keys and values in the bucket so that the GC won't pin
// these allocations.
memzero(unsafe.Add(unsafe.Pointer(bucket), unsafe.Sizeof(hashmapBucket{})), bucketSize-unsafe.Sizeof(hashmapBucket{}))
// Move on to the next bucket in the chain.
bucket = bucket.next
}
}
}
func hashmapKeyEqualAlg(alg hashmapAlgorithm) func(x, y unsafe.Pointer, n uintptr) bool {
switch alg {
case hashmapAlgorithmBinary:

11
testdata/go1.21.go предоставленный
Просмотреть файл

@ -15,4 +15,15 @@ func main() {
s := []int{1, 2, 3, 4, 5}
clear(s[:3])
println("cleared s[:3]:", s[0], s[1], s[2], s[3], s[4])
// The clear builtin, for maps.
m := map[int]string{
1: "one",
2: "two",
3: "three",
}
clear(m)
println("cleared map:", m[1], m[2], m[3], len(m))
m[4] = "four"
println("added to cleared map:", m[1], m[2], m[3], m[4], len(m))
}

2
testdata/go1.21.txt предоставленный
Просмотреть файл

@ -1,3 +1,5 @@
min/max: -3 5
min/max: -3.000000e+000 +5.000000e+000
cleared s[:3]: 0 0 0 4 5
cleared map: 0
added to cleared map: four 1