mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-09-10 14:39:31 +00:00
226 lines
5.0 KiB
Go
226 lines
5.0 KiB
Go
package main
|
|
|
|
import (
|
|
"reflect"
|
|
"runtime"
|
|
)
|
|
|
|
var xorshift32State uint32 = 1
|
|
|
|
func xorshift32(x uint32) uint32 {
|
|
// Algorithm "xor" from p. 4 of Marsaglia, "Xorshift RNGs"
|
|
x ^= x << 13
|
|
x ^= x >> 17
|
|
x ^= x << 5
|
|
return x
|
|
}
|
|
|
|
func randuint32() uint32 {
|
|
xorshift32State = xorshift32(xorshift32State)
|
|
return xorshift32State
|
|
}
|
|
|
|
func main() {
|
|
testNonPointerHeap()
|
|
testGlobalMapRoots()
|
|
testGlobalChannelRoots()
|
|
testReflectRoots()
|
|
testKeepAlive()
|
|
}
|
|
|
|
var scalarSlices [4][]byte
|
|
var randSeeds [4]uint32
|
|
|
|
func testNonPointerHeap() {
|
|
maxSliceSize := uint32(1024)
|
|
if ^uintptr(0) <= 0xffff {
|
|
// 16-bit and lower devices, such as AVR.
|
|
// Heap size is a real issue there, while it is still useful to run
|
|
// these tests. Therefore, lower the max slice size.
|
|
maxSliceSize = 64
|
|
}
|
|
// Allocate roughly 0.5MB of memory.
|
|
for i := 0; i < 1000; i++ {
|
|
// Pick a random index that the optimizer can't predict.
|
|
index := randuint32() % 4
|
|
|
|
// Check whether the contents of the previous allocation was correct.
|
|
rand := randSeeds[index]
|
|
for _, b := range scalarSlices[index] {
|
|
rand = xorshift32(rand)
|
|
if b != byte(rand) {
|
|
panic("memory was overwritten!")
|
|
}
|
|
}
|
|
|
|
// Allocate a randomly-sized slice, randomly sliced to be smaller.
|
|
sliceLen := randuint32() % maxSliceSize
|
|
slice := make([]byte, sliceLen)
|
|
cutLen := randuint32() % maxSliceSize
|
|
if cutLen < sliceLen {
|
|
slice = slice[cutLen:]
|
|
}
|
|
scalarSlices[index] = slice
|
|
|
|
// Fill the slice with a pattern that looks random but is easily
|
|
// calculated and verified.
|
|
rand = randuint32() + 1
|
|
randSeeds[index] = rand
|
|
for i := 0; i < len(slice); i++ {
|
|
rand = xorshift32(rand)
|
|
slice[i] = byte(rand)
|
|
}
|
|
}
|
|
println("ok")
|
|
}
|
|
|
|
func testKeepAlive() {
|
|
// There isn't much we can test, but at least we can test that
|
|
// runtime.KeepAlive compiles correctly.
|
|
var x int
|
|
runtime.KeepAlive(&x)
|
|
}
|
|
|
|
type globalMapObject struct {
|
|
marker int
|
|
data [64]byte
|
|
}
|
|
|
|
var globalMap = make(map[int]*globalMapObject)
|
|
var globalChannel chan *globalMapObject
|
|
var globalPointerSlice []*globalMapObject
|
|
var globalGCClobber any
|
|
|
|
type globalMapLargeKey struct {
|
|
object *globalMapObject
|
|
data [129]byte
|
|
}
|
|
|
|
type globalMapLargeValue struct {
|
|
object *globalMapObject
|
|
data [129]byte
|
|
}
|
|
|
|
var globalLargeKeyMap = make(map[globalMapLargeKey]int)
|
|
var globalLargeValueMap = make(map[int]globalMapLargeValue)
|
|
|
|
//go:noinline
|
|
func populateGlobalMaps() {
|
|
for i := 0; i < 32; i++ {
|
|
globalMap[i] = &globalMapObject{marker: 100 + i}
|
|
globalPointerSlice = append(globalPointerSlice, &globalMapObject{marker: 800 + i})
|
|
}
|
|
globalLargeKeyMap[globalMapLargeKey{
|
|
object: &globalMapObject{marker: 200},
|
|
}] = 1
|
|
globalLargeValueMap[0] = globalMapLargeValue{
|
|
object: &globalMapObject{marker: 300},
|
|
}
|
|
}
|
|
|
|
func testGlobalMapRoots() {
|
|
populateGlobalMaps()
|
|
|
|
runtime.GC()
|
|
for i := 0; i < 100; i++ {
|
|
globalGCClobber = new(globalMapObject)
|
|
}
|
|
runtime.GC()
|
|
|
|
for i := 0; i < 32; i++ {
|
|
if globalMap[i].marker != 100+i {
|
|
panic("global map value was collected")
|
|
}
|
|
}
|
|
for key := range globalLargeKeyMap {
|
|
if key.object.marker != 200 {
|
|
panic("indirect global map key was collected")
|
|
}
|
|
}
|
|
if globalLargeValueMap[0].object.marker != 300 {
|
|
panic("indirect global map value was collected")
|
|
}
|
|
for i, object := range globalPointerSlice {
|
|
if object.marker != 800+i {
|
|
panic("global slice value was collected")
|
|
}
|
|
}
|
|
}
|
|
|
|
//go:noinline
|
|
func populateGlobalChannel() {
|
|
globalChannel = make(chan *globalMapObject, 4)
|
|
globalChannel <- &globalMapObject{marker: 400}
|
|
}
|
|
|
|
func testGlobalChannelRoots() {
|
|
populateGlobalChannel()
|
|
|
|
runtime.GC()
|
|
for i := 0; i < 100; i++ {
|
|
globalGCClobber = new(globalMapObject)
|
|
}
|
|
runtime.GC()
|
|
|
|
if (<-globalChannel).marker != 400 {
|
|
panic("global channel value was collected")
|
|
}
|
|
}
|
|
|
|
type reflectRootObject struct {
|
|
marker int
|
|
child *reflectRootObject
|
|
data [128]byte
|
|
}
|
|
|
|
type reflectMapKey struct {
|
|
object *reflectRootObject
|
|
data [129]byte
|
|
}
|
|
|
|
type reflectMapValue struct {
|
|
object *reflectRootObject
|
|
data [129]byte
|
|
}
|
|
|
|
type reflectRootMap map[reflectMapKey]reflectMapValue
|
|
|
|
var globalReflectObject *reflectRootObject
|
|
var globalReflectMap reflectRootMap
|
|
|
|
//go:noinline
|
|
func populateReflectRoots() {
|
|
value := reflect.New(reflect.TypeOf(reflectRootObject{}))
|
|
globalReflectObject = value.Interface().(*reflectRootObject)
|
|
globalReflectObject.child = &reflectRootObject{marker: 500}
|
|
|
|
mapValue := reflect.MakeMapWithSize(reflect.TypeOf(globalReflectMap), 1)
|
|
mapValue.SetMapIndex(
|
|
reflect.ValueOf(reflectMapKey{object: &reflectRootObject{marker: 600}}),
|
|
reflect.ValueOf(reflectMapValue{object: &reflectRootObject{marker: 700}}),
|
|
)
|
|
globalReflectMap = mapValue.Interface().(reflectRootMap)
|
|
}
|
|
|
|
func testReflectRoots() {
|
|
populateReflectRoots()
|
|
|
|
runtime.GC()
|
|
for i := 0; i < 100; i++ {
|
|
globalGCClobber = new(reflectRootObject)
|
|
}
|
|
runtime.GC()
|
|
|
|
if globalReflectObject.child.marker != 500 {
|
|
panic("reflected object field was collected")
|
|
}
|
|
for key, value := range globalReflectMap {
|
|
if key.object.marker != 600 {
|
|
panic("reflected map key was collected")
|
|
}
|
|
if value.object.marker != 700 {
|
|
panic("reflected map value was collected")
|
|
}
|
|
}
|
|
}
|