From b611cbc4ed6463d34f952528afab11dcc703367a Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 13 Jun 2025 12:45:01 +0200 Subject: [PATCH 1/2] compiler: treat unsafe.Pointer inside map keys like any other pointer Pointers are pointers, but unsafe.Pointer was not treated as such and forced a map lookup using reflect. This commit fixes that. --- compiler/map.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/map.go b/compiler/map.go index 71fb3f0da8..81bcebf29d 100644 --- a/compiler/map.go +++ b/compiler/map.go @@ -250,6 +250,9 @@ func (b *builder) createMapIteratorNext(rangeVal ssa.Value, llvmRangeVal, it llv func hashmapIsBinaryKey(keyType types.Type) bool { switch keyType := keyType.(type) { case *types.Basic: + if keyType.Kind() == types.UnsafePointer { + return true + } return keyType.Info()&(types.IsBoolean|types.IsInteger) != 0 case *types.Pointer: return true From 63c39af630f4a764ca5c4e756d408ca9545bda02 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 21 May 2025 12:33:44 +0200 Subject: [PATCH 2/2] 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. --- src/runtime/arch_tinygowasm_malloc.go | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/runtime/arch_tinygowasm_malloc.go b/src/runtime/arch_tinygowasm_malloc.go index 239f7c73eb..cc88a0be53 100644 --- a/src/runtime/arch_tinygowasm_malloc.go +++ b/src/runtime/arch_tinygowasm_malloc.go @@ -8,16 +8,17 @@ import "unsafe" // code linked from other languages can allocate memory without colliding with // our GC allocations. -var allocs = make(map[uintptr][]byte) +// Map of allocations, where the key is the allocated pointer and the value is +// the size of the allocation. +var allocs = make(map[unsafe.Pointer]uintptr) //export malloc func libc_malloc(size uintptr) unsafe.Pointer { if size == 0 { return nil } - buf := make([]byte, size) - ptr := unsafe.Pointer(&buf[0]) - allocs[uintptr(ptr)] = buf + ptr := alloc(size, nil) + allocs[ptr] = size return ptr } @@ -26,8 +27,8 @@ func libc_free(ptr unsafe.Pointer) { if ptr == nil { return } - if _, ok := allocs[uintptr(ptr)]; ok { - delete(allocs, uintptr(ptr)) + if _, ok := allocs[ptr]; ok { + delete(allocs, ptr) } else { panic("free: invalid pointer") } @@ -48,18 +49,20 @@ func libc_realloc(oldPtr unsafe.Pointer, size uintptr) unsafe.Pointer { // It's hard to optimize this to expand the current buffer with our GC, but // it is theoretically possible. For now, just always allocate fresh. - buf := make([]byte, size) + // TODO: we could skip this if the new allocation is smaller than the old. + ptr := alloc(size, nil) if oldPtr != nil { - if oldBuf, ok := allocs[uintptr(oldPtr)]; ok { - copy(buf, oldBuf) - delete(allocs, uintptr(oldPtr)) + if oldSize, ok := allocs[oldPtr]; ok { + oldBuf := unsafe.Slice((*byte)(oldPtr), oldSize) + newBuf := unsafe.Slice((*byte)(ptr), size) + copy(newBuf, oldBuf) + delete(allocs, oldPtr) } else { panic("realloc: invalid pointer") } } - ptr := unsafe.Pointer(&buf[0]) - allocs[uintptr(ptr)] = buf + allocs[ptr] = size return ptr }