Skip to content

Commit

Permalink
compiler: implement clear builtin for maps
Browse files Browse the repository at this point in the history
  • Loading branch information
aykevl authored and deadprogram committed Aug 4, 2023
1 parent a2f886a commit f1e25a1
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 0 deletions.
4 changes: 4 additions & 0 deletions compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
5 changes: 5 additions & 0 deletions compiler/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions compiler/testdata/go1.21.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,7 @@ func clearSlice(s []int) {
func clearZeroSizedSlice(s []struct{}) {
clear(s)
}

func clearMap(m map[string]int) {
clear(m)
}
9 changes: 9 additions & 0 deletions compiler/testdata/go1.21.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
29 changes: 29 additions & 0 deletions src/runtime/hashmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
11 changes: 11 additions & 0 deletions testdata/go1.21.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 2 additions & 0 deletions testdata/go1.21.txt
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit f1e25a1

Please sign in to comment.