mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-09-11 15:09:32 +00:00
runtime: require explicit GC layouts
This commit is contained in:
@@ -2,7 +2,10 @@
|
||||
|
||||
package runtime
|
||||
|
||||
import "unsafe"
|
||||
import (
|
||||
"internal/gclayout"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// The below functions override the default allocator of wasi-libc. This ensures
|
||||
// code linked from other languages can allocate memory without colliding with
|
||||
@@ -21,7 +24,7 @@ func libc_malloc(size uintptr) unsafe.Pointer {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
ptr := alloc(size, nil)
|
||||
ptr := alloc(size, gclayout.NoPtrs.AsPtr())
|
||||
allocs[(*byte)(ptr)] = size
|
||||
return ptr
|
||||
}
|
||||
@@ -54,7 +57,7 @@ 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.
|
||||
// TODO: we could skip this if the new allocation is smaller than the old.
|
||||
ptr := alloc(size, nil)
|
||||
ptr := alloc(size, gclayout.NoPtrs.AsPtr())
|
||||
|
||||
if oldPtr != nil {
|
||||
if oldSize, ok := allocs[(*byte)(oldPtr)]; ok {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"internal/gclayout"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -11,7 +12,7 @@ import (
|
||||
func libc_malloc(size uintptr) unsafe.Pointer {
|
||||
// Note: this zeroes the returned buffer which is not necessary.
|
||||
// The same goes for bytealg.MakeNoZero.
|
||||
return alloc(size, nil)
|
||||
return alloc(size, gclayout.NoPtrs.AsPtr())
|
||||
}
|
||||
|
||||
//export calloc
|
||||
|
||||
+2
-2
@@ -137,11 +137,11 @@ type chanSelectState struct {
|
||||
value unsafe.Pointer
|
||||
}
|
||||
|
||||
func chanMake(elementSize uintptr, bufSize uintptr) *channel {
|
||||
func chanMake(elementSize uintptr, bufSize uintptr, elementLayout unsafe.Pointer) *channel {
|
||||
return &channel{
|
||||
elementSize: elementSize,
|
||||
bufCap: bufSize,
|
||||
buf: alloc(elementSize*bufSize, nil),
|
||||
buf: alloc(elementSize*bufSize, elementLayout),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ package runtime
|
||||
// Moss.
|
||||
|
||||
import (
|
||||
"internal/gclayout"
|
||||
"internal/reflectlite"
|
||||
"internal/task"
|
||||
"runtime/interrupt"
|
||||
@@ -501,7 +502,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
|
||||
|
||||
func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
|
||||
if ptr == nil {
|
||||
return alloc(size, nil)
|
||||
return alloc(size, gclayout.NoPtrs.AsPtr())
|
||||
}
|
||||
|
||||
// Find the first block of the original allocation.
|
||||
@@ -526,7 +527,7 @@ func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
|
||||
}
|
||||
|
||||
// Create a new allocation and copy the old data.
|
||||
newAlloc := alloc(size, nil)
|
||||
newAlloc := alloc(size, gclayout.NoPtrs.AsPtr())
|
||||
memcpy(newAlloc, ptr, oldSize)
|
||||
free(ptr)
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ package runtime
|
||||
// may be the only memory allocator possible.
|
||||
|
||||
import (
|
||||
"internal/gclayout"
|
||||
"internal/task"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
@@ -69,7 +70,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer {
|
||||
}
|
||||
|
||||
func realloc(ptr unsafe.Pointer, size uintptr) unsafe.Pointer {
|
||||
newAlloc := alloc(size, nil)
|
||||
newAlloc := alloc(size, gclayout.NoPtrs.AsPtr())
|
||||
if ptr == nil {
|
||||
return newAlloc
|
||||
}
|
||||
|
||||
@@ -55,7 +55,10 @@
|
||||
|
||||
package runtime
|
||||
|
||||
import "unsafe"
|
||||
import (
|
||||
"internal/gclayout"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const sizeFieldBits = 4 + (unsafe.Sizeof(uintptr(0)) / 4)
|
||||
|
||||
@@ -76,9 +79,7 @@ func (layout gcLayout) pointerFree() bool {
|
||||
// The length is rounded down to a multiple of the element size.
|
||||
func (layout gcLayout) scan(start, len uintptr) {
|
||||
switch {
|
||||
case layout == 0:
|
||||
// This is an unknown layout.
|
||||
// Scan conservatively.
|
||||
case layout == gcLayout(gclayout.Conservative):
|
||||
// NOTE: This is *NOT* equivalent to a slice of pointers on AVR.
|
||||
scanConservative(start, len)
|
||||
|
||||
|
||||
+27
-12
@@ -14,6 +14,7 @@ import (
|
||||
// The underlying hashmap structure for Go.
|
||||
type hashmap struct {
|
||||
buckets unsafe.Pointer // pointer to array of buckets
|
||||
typeInfo *hashmapTypeInfo
|
||||
seed uintptr
|
||||
count uintptr
|
||||
keySize uintptr
|
||||
@@ -26,6 +27,17 @@ type hashmap struct {
|
||||
keyHash func(key unsafe.Pointer, size, seed uintptr) uint32
|
||||
}
|
||||
|
||||
type hashmapTypeInfo struct {
|
||||
keyLayout unsafe.Pointer
|
||||
valueLayout unsafe.Pointer
|
||||
bucketLayout unsafe.Pointer
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func hashmapType(m *hashmap) *hashmapTypeInfo {
|
||||
return m.typeInfo
|
||||
}
|
||||
|
||||
const (
|
||||
hashmapMaxKeySize = 128
|
||||
hashmapMaxValueSize = 128
|
||||
@@ -113,7 +125,7 @@ func hashmapTopHash(hash uint32) uint8 {
|
||||
}
|
||||
|
||||
// Create a new hashmap with the given keySize and valueSize.
|
||||
func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) *hashmap {
|
||||
func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, typeInfo unsafe.Pointer, alg uint8) *hashmap {
|
||||
bucketBits := uint8(0)
|
||||
for hashmapHasSpaceToGrow(bucketBits) && hashmapOverLoadFactor(sizeHint, bucketBits) {
|
||||
bucketBits++
|
||||
@@ -132,13 +144,14 @@ func hashmapMake(keySize, valueSize uintptr, sizeHint uintptr, alg uint8) *hashm
|
||||
}
|
||||
|
||||
bucketBufSize := hashmapBucketHeaderSize + keySlotSize*8 + valueSlotSize*8
|
||||
buckets := alloc(bucketBufSize*(1<<bucketBits), nil)
|
||||
buckets := alloc(bucketBufSize*(1<<bucketBits), (*hashmapTypeInfo)(typeInfo).bucketLayout)
|
||||
|
||||
keyHash := hashmapKeyHashAlg(tinygo.HashmapAlgorithm(alg))
|
||||
keyEqual := hashmapKeyEqualAlg(tinygo.HashmapAlgorithm(alg))
|
||||
|
||||
return &hashmap{
|
||||
buckets: buckets,
|
||||
typeInfo: (*hashmapTypeInfo)(typeInfo),
|
||||
seed: uintptr(fastrand()),
|
||||
keySize: keySize,
|
||||
valueSize: valueSize,
|
||||
@@ -325,7 +338,7 @@ func hashmapSet(m *hashmap, key unsafe.Pointer, value unsafe.Pointer, hash uint3
|
||||
//go:inline
|
||||
func hashmapStoreKey(m *hashmap, slotKey, key unsafe.Pointer) {
|
||||
if m.flags&hashmapFlagIndirectKey != 0 {
|
||||
p := alloc(m.keySize, nil)
|
||||
p := alloc(m.keySize, hashmapType(m).keyLayout)
|
||||
memcpy(p, key, m.keySize)
|
||||
*(*unsafe.Pointer)(slotKey) = p
|
||||
} else {
|
||||
@@ -343,7 +356,7 @@ func hashmapStoreValue(m *hashmap, slotValue, value unsafe.Pointer) {
|
||||
p := *(*unsafe.Pointer)(slotValue)
|
||||
if p == nil {
|
||||
// First insert: allocate backing storage.
|
||||
p = alloc(m.valueSize, nil)
|
||||
p = alloc(m.valueSize, hashmapType(m).valueLayout)
|
||||
*(*unsafe.Pointer)(slotValue) = p
|
||||
}
|
||||
memcpy(p, value, m.valueSize)
|
||||
@@ -356,7 +369,7 @@ func hashmapStoreValue(m *hashmap, slotValue, value unsafe.Pointer) {
|
||||
// value into the bucket, and returns a pointer to this bucket.
|
||||
func hashmapInsertIntoNewBucket(m *hashmap, key, value unsafe.Pointer, tophash uint8) *hashmapBucket {
|
||||
bucketBufSize := hashmapBucketSize(m)
|
||||
bucketBuf := alloc(bucketBufSize, nil)
|
||||
bucketBuf := alloc(bucketBufSize, hashmapType(m).bucketLayout)
|
||||
bucket := (*hashmapBucket)(bucketBuf)
|
||||
|
||||
// Insert into the first slot, which is empty as it has just been allocated.
|
||||
@@ -392,13 +405,13 @@ func hashmapCopy(m *hashmap, sizeBits uint8) hashmap {
|
||||
n.bucketBits = sizeBits
|
||||
numBuckets := uintptr(1) << n.bucketBits
|
||||
bucketBufSize := hashmapBucketSize(m)
|
||||
n.buckets = alloc(bucketBufSize*numBuckets, nil)
|
||||
n.buckets = alloc(bucketBufSize*numBuckets, hashmapType(m).bucketLayout)
|
||||
|
||||
// use a hashmap iterator to go through the old map
|
||||
var it hashmapIterator
|
||||
|
||||
var key = alloc(m.keySize, nil)
|
||||
var value = alloc(m.valueSize, nil)
|
||||
var key = alloc(m.keySize, hashmapType(m).keyLayout)
|
||||
var value = alloc(m.valueSize, hashmapType(m).valueLayout)
|
||||
|
||||
for hashmapNext(m, &it, key, value) {
|
||||
h := n.keyHash(key, uintptr(n.keySize), n.seed)
|
||||
@@ -624,6 +637,7 @@ func hashmapGenericDelete(m *hashmap, key unsafe.Pointer) {
|
||||
// equal functions. This avoids the interface/reflection path for composite
|
||||
// key types like structs containing strings.
|
||||
func hashmapMakeGeneric(keySize, valueSize uintptr, sizeHint uintptr,
|
||||
typeInfo unsafe.Pointer,
|
||||
keyHash func(key unsafe.Pointer, size, seed uintptr) uint32,
|
||||
keyEqual func(x, y unsafe.Pointer, n uintptr) bool) *hashmap {
|
||||
bucketBits := uint8(0)
|
||||
@@ -644,10 +658,11 @@ func hashmapMakeGeneric(keySize, valueSize uintptr, sizeHint uintptr,
|
||||
}
|
||||
|
||||
bucketBufSize := hashmapBucketHeaderSize + keySlotSize*8 + valueSlotSize*8
|
||||
buckets := alloc(bucketBufSize*(1<<bucketBits), nil)
|
||||
buckets := alloc(bucketBufSize*(1<<bucketBits), (*hashmapTypeInfo)(typeInfo).bucketLayout)
|
||||
|
||||
return &hashmap{
|
||||
buckets: buckets,
|
||||
typeInfo: (*hashmapTypeInfo)(typeInfo),
|
||||
seed: uintptr(fastrand()),
|
||||
keySize: keySize,
|
||||
valueSize: valueSize,
|
||||
@@ -663,12 +678,12 @@ func hashmapMakeGeneric(keySize, valueSize uintptr, sizeHint uintptr,
|
||||
// hashmapMakeReflect creates a hashmap for reflect.MakeMapWithSize using
|
||||
// closures that reconstruct interface{} values from raw key bytes,
|
||||
// delegating to hashmapInterfaceHash for hashing and == for equality.
|
||||
func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, keyType unsafe.Pointer) *hashmap {
|
||||
func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, typeInfo, keyType unsafe.Pointer) *hashmap {
|
||||
t := (*reflectlite.RawType)(keyType)
|
||||
if t.Kind() == reflectlite.Interface {
|
||||
// Interface keys are already stored as interface values in the
|
||||
// bucket; use the existing interface hash/equal directly.
|
||||
return hashmapMakeGeneric(keySize, valueSize, sizeHint,
|
||||
return hashmapMakeGeneric(keySize, valueSize, sizeHint, typeInfo,
|
||||
hashmapInterfacePtrHash, hashmapInterfaceEqual)
|
||||
}
|
||||
keyHash := func(key unsafe.Pointer, size, seed uintptr) uint32 {
|
||||
@@ -677,7 +692,7 @@ func hashmapMakeReflect(keySize, valueSize, sizeHint uintptr, keyType unsafe.Poi
|
||||
keyEqual := func(x, y unsafe.Pointer, n uintptr) bool {
|
||||
return rawToInterface(t, x) == rawToInterface(t, y)
|
||||
}
|
||||
return hashmapMakeGeneric(keySize, valueSize, sizeHint, keyHash, keyEqual)
|
||||
return hashmapMakeGeneric(keySize, valueSize, sizeHint, typeInfo, keyHash, keyEqual)
|
||||
}
|
||||
|
||||
// rawToInterface reconstructs an interface{} from raw bytes at ptr.
|
||||
|
||||
Reference in New Issue
Block a user