Skip to content

Commit 55cdc08

Browse files
committed
compiler: implement clear builtin for maps
1 parent 8b43b18 commit 55cdc08

File tree

7 files changed

+64
-0
lines changed

7 files changed

+64
-0
lines changed

compiler/compiler.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,10 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
16311631
}, "")
16321632
call.AddCallSiteAttribute(1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elementAlign)))
16331633

1634+
return llvm.Value{}, nil
1635+
case *types.Map:
1636+
m := argValues[0]
1637+
b.createMapClear(m)
16341638
return llvm.Value{}, nil
16351639
default:
16361640
return llvm.Value{}, b.makeError(pos, "unsupported type in clear builtin: "+typ.String())

compiler/map.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ func (b *builder) createMapDelete(keyType types.Type, m, key llvm.Value, pos tok
185185
}
186186
}
187187

188+
// Clear the given map.
189+
func (b *builder) createMapClear(m llvm.Value) {
190+
b.createRuntimeCall("hashmapClear", []llvm.Value{m}, "")
191+
}
192+
188193
// createMapIteratorNext lowers the *ssa.Next instruction for iterating over a
189194
// map. It returns a tuple of {bool, key, value} with the result of the
190195
// iteration.

compiler/testdata/go1.21.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,7 @@ func clearSlice(s []int) {
5959
func clearZeroSizedSlice(s []struct{}) {
6060
clear(s)
6161
}
62+
63+
func clearMap(m map[string]int) {
64+
clear(m)
65+
}

compiler/testdata/go1.21.ll

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,15 @@ entry:
147147
ret void
148148
}
149149

150+
; Function Attrs: nounwind
151+
define hidden void @main.clearMap(ptr dereferenceable_or_null(40) %m, ptr %context) unnamed_addr #2 {
152+
entry:
153+
call void @runtime.hashmapClear(ptr %m, ptr undef) #5
154+
ret void
155+
}
156+
157+
declare void @runtime.hashmapClear(ptr dereferenceable_or_null(40), ptr) #1
158+
150159
; Function Attrs: nocallback nofree nosync nounwind readnone speculatable willreturn
151160
declare i32 @llvm.smin.i32(i32, i32) #4
152161

src/runtime/hashmap.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,35 @@ func hashmapMakeUnsafePointer(keySize, valueSize uintptr, sizeHint uintptr, alg
9191
return (unsafe.Pointer)(hashmapMake(keySize, valueSize, sizeHint, alg))
9292
}
9393

94+
// Remove all entries from the map, without actually deallocating the space for
95+
// it. This is used for the clear builtin, and can be used to reuse a map (to
96+
// avoid extra heap allocations).
97+
func hashmapClear(m *hashmap) {
98+
if m == nil {
99+
// Nothing to do. According to the spec:
100+
// > If the map or slice is nil, clear is a no-op.
101+
return
102+
}
103+
104+
m.count = 0
105+
numBuckets := uintptr(1) << m.bucketBits
106+
bucketSize := hashmapBucketSize(m)
107+
for i := uintptr(0); i < numBuckets; i++ {
108+
bucket := hashmapBucketAddr(m, m.buckets, i)
109+
for bucket != nil {
110+
// Clear the tophash, to mark these keys/values as removed.
111+
bucket.tophash = [8]uint8{}
112+
113+
// Clear the keys and values in the bucket so that the GC won't pin
114+
// these allocations.
115+
memzero(unsafe.Add(unsafe.Pointer(bucket), unsafe.Sizeof(hashmapBucket{})), bucketSize-unsafe.Sizeof(hashmapBucket{}))
116+
117+
// Move on to the next bucket in the chain.
118+
bucket = bucket.next
119+
}
120+
}
121+
}
122+
94123
func hashmapKeyEqualAlg(alg hashmapAlgorithm) func(x, y unsafe.Pointer, n uintptr) bool {
95124
switch alg {
96125
case hashmapAlgorithmBinary:

testdata/go1.21.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,15 @@ func main() {
1515
s := []int{1, 2, 3, 4, 5}
1616
clear(s[:3])
1717
println("cleared s[:3]:", s[0], s[1], s[2], s[3], s[4])
18+
19+
// The clear builtin, for maps.
20+
m := map[int]string{
21+
1: "one",
22+
2: "two",
23+
3: "three",
24+
}
25+
clear(m)
26+
println("cleared map:", m[1], m[2], m[3], len(m))
27+
m[4] = "four"
28+
println("added to cleared map:", m[1], m[2], m[3], m[4], len(m))
1829
}

testdata/go1.21.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
min/max: -3 5
22
min/max: -3.000000e+000 +5.000000e+000
33
cleared s[:3]: 0 0 0 4 5
4+
cleared map: 0
5+
added to cleared map: four 1

0 commit comments

Comments
 (0)