Skip to content

Commit cdb0a71

Browse files
committed
wasm: fix C realloc and optimize it a bit
- Do not use make([]byte, ...) to allocate, instead call the allocator directly with a nil (undefined) layout. This makes sure the precise GC will scan the contents of the allocation, since C could very well put pointers in there. - Simplify the map to use the pointer as the key and the size as the value, instead of storing the slices directly in the map.
1 parent 073862e commit cdb0a71

File tree

2 files changed

+22
-12
lines changed

2 files changed

+22
-12
lines changed

compiler/map.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,9 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv
250250
func hashmapIsBinaryKey(keyType types.Type) bool {
251251
switch keyType := keyType.Underlying().(type) {
252252
case *types.Basic:
253+
// TODO: unsafe.Pointer is also a binary key, but to support that we
254+
// need to fix an issue with interp first (see
255+
// https://github.com/tinygo-org/tinygo/pull/4898).
253256
return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0
254257
case *types.Pointer:
255258
return true

src/runtime/arch_tinygowasm_malloc.go

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,21 @@ import "unsafe"
88
// code linked from other languages can allocate memory without colliding with
99
// our GC allocations.
1010

11-
var allocs = make(map[uintptr][]byte)
11+
// Map of allocations, where the key is the allocated pointer and the value is
12+
// the size of the allocation.
13+
// TODO: make this a map[unsafe.Pointer]uintptr, since that results in slightly
14+
// smaller binaries. But for that to work, unsafe.Pointer needs to be seen as a
15+
// binary key (which it is not at the moment).
16+
// See https://github.com/tinygo-org/tinygo/pull/4898 for details.
17+
var allocs = make(map[*byte]uintptr)
1218

1319
//export malloc
1420
func libc_malloc(size uintptr) unsafe.Pointer {
1521
if size == 0 {
1622
return nil
1723
}
18-
buf := make([]byte, size)
19-
ptr := unsafe.Pointer(&buf[0])
20-
allocs[uintptr(ptr)] = buf
24+
ptr := alloc(size, nil)
25+
allocs[(*byte)(ptr)] = size
2126
return ptr
2227
}
2328

@@ -26,8 +31,8 @@ func libc_free(ptr unsafe.Pointer) {
2631
if ptr == nil {
2732
return
2833
}
29-
if _, ok := allocs[uintptr(ptr)]; ok {
30-
delete(allocs, uintptr(ptr))
34+
if _, ok := allocs[(*byte)(ptr)]; ok {
35+
delete(allocs, (*byte)(ptr))
3136
} else {
3237
panic("free: invalid pointer")
3338
}
@@ -48,18 +53,20 @@ func libc_realloc(oldPtr unsafe.Pointer, size uintptr) unsafe.Pointer {
4853

4954
// It's hard to optimize this to expand the current buffer with our GC, but
5055
// it is theoretically possible. For now, just always allocate fresh.
51-
buf := make([]byte, size)
56+
// TODO: we could skip this if the new allocation is smaller than the old.
57+
ptr := alloc(size, nil)
5258

5359
if oldPtr != nil {
54-
if oldBuf, ok := allocs[uintptr(oldPtr)]; ok {
55-
copy(buf, oldBuf)
56-
delete(allocs, uintptr(oldPtr))
60+
if oldSize, ok := allocs[(*byte)(oldPtr)]; ok {
61+
oldBuf := unsafe.Slice((*byte)(oldPtr), oldSize)
62+
newBuf := unsafe.Slice((*byte)(ptr), size)
63+
copy(newBuf, oldBuf)
64+
delete(allocs, (*byte)(oldPtr))
5765
} else {
5866
panic("realloc: invalid pointer")
5967
}
6068
}
6169

62-
ptr := unsafe.Pointer(&buf[0])
63-
allocs[uintptr(ptr)] = buf
70+
allocs[(*byte)(ptr)] = size
6471
return ptr
6572
}

0 commit comments

Comments
 (0)