diff --git a/compileopts/config.go b/compileopts/config.go index 499dcd27..62c9fa64 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -105,7 +105,7 @@ func (c *Config) GC() string { // that can be traced by the garbage collector. func (c *Config) NeedsStackObjects() bool { switch c.GC() { - case "conservative", "precise": + case "conservative", "custom", "precise": for _, tag := range c.BuildTags() { if tag == "tinygo.wasm" { return true diff --git a/compileopts/options.go b/compileopts/options.go index 884a722a..9c4cb10a 100644 --- a/compileopts/options.go +++ b/compileopts/options.go @@ -8,7 +8,7 @@ import ( ) var ( - validGCOptions = []string{"none", "leaking", "conservative", "precise"} + validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise"} validSchedulerOptions = []string{"none", "tasks", "asyncify"} validSerialOptions = []string{"none", "uart", "usb"} validPrintSizeOptions = []string{"none", "short", "full"} diff --git a/compileopts/options_test.go b/compileopts/options_test.go index e6c75d29..23ffec46 100644 --- a/compileopts/options_test.go +++ b/compileopts/options_test.go @@ -9,7 +9,7 @@ import ( func TestVerifyOptions(t *testing.T) { - expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, precise`) + expectedGCError := errors.New(`invalid gc option 'incorrect': valid values are none, leaking, conservative, custom, precise`) expectedSchedulerError := errors.New(`invalid scheduler option 'incorrect': valid values are none, tasks, asyncify`) expectedPrintSizeError := errors.New(`invalid size option 'incorrect': valid values are none, short, full`) expectedPanicStrategyError := errors.New(`invalid panic option 'incorrect': valid values are print, trap`) @@ -48,6 +48,12 @@ func TestVerifyOptions(t *testing.T) { GC: "conservative", }, }, + { + name: "GCOptionCustom", + opts: compileopts.Options{ + GC: "custom", + }, + }, { name: "InvalidSchedulerOption", opts: compileopts.Options{ diff --git a/src/internal/task/gc_stack_chain.go b/src/internal/task/gc_stack_chain.go index 0e2eba73..848bb15e 100644 --- a/src/internal/task/gc_stack_chain.go +++ b/src/internal/task/gc_stack_chain.go @@ -1,4 +1,4 @@ -//go:build (gc.conservative || gc.precise) && tinygo.wasm +//go:build (gc.conservative || gc.custom || gc.precise) && tinygo.wasm package task diff --git a/src/internal/task/gc_stack_noop.go b/src/internal/task/gc_stack_noop.go index 6bc1af6b..63aa1628 100644 --- a/src/internal/task/gc_stack_noop.go +++ b/src/internal/task/gc_stack_noop.go @@ -1,4 +1,4 @@ -//go:build !(gc.conservative || gc.precise) || !tinygo.wasm +//go:build !(gc.conservative || gc.custom || gc.precise) || !tinygo.wasm package task diff --git a/src/runtime/gc_custom.go b/src/runtime/gc_custom.go new file mode 100644 index 00000000..0d538619 --- /dev/null +++ b/src/runtime/gc_custom.go @@ -0,0 +1,55 @@ +//go:build gc.custom +// +build gc.custom + +package runtime + +// This GC strategy allows an external GC to be plugged in instead of the builtin +// implementations. +// +// The interface defined in this file is not stable and can be broken at anytime, even +// across minor versions. +// +// runtime.markStack() must be called at the beginning of any GC cycle. //go:linkname +// on a function without a body can be used to access this internal function. +// +// The custom implementation must provide the following functions in the runtime package +// using the go:linkname directive: +// +// - func initHeap() +// - func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer +// - func free(ptr unsafe.Pointer) +// - func markRoots(start, end uintptr) +// - func GC() +// +// +// In addition, if targeting wasi, the following functions should be exported for interoperability +// with wasi libraries that use them. Note, this requires the export directive, not go:linkname. +// +// - func malloc(size uintptr) unsafe.Pointer +// - func free(ptr unsafe.Pointer) +// - func calloc(nmemb, size uintptr) unsafe.Pointer +// - func realloc(oldPtr unsafe.Pointer, size uintptr) unsafe.Pointer + +import ( + "unsafe" +) + +// initHeap is called when the heap is first initialized at program start. +func initHeap() + +// alloc is called to allocate memory. layout is currently not used. +func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer + +// free is called to explicitly free a previously allocated pointer. +func free(ptr unsafe.Pointer) + +// markRoots is called with the start and end addresses to scan for references. +// It is currently only called with the top and bottom of the stack. +func markRoots(start, end uintptr) + +// GC is called to explicitly run garbage collection. +func GC() + +func setHeapEnd(newHeapEnd uintptr) { + // Heap is in custom GC so ignore for when called from wasm initialization. +} diff --git a/src/runtime/gc_stack_portable.go b/src/runtime/gc_stack_portable.go index ab921a99..f822d848 100644 --- a/src/runtime/gc_stack_portable.go +++ b/src/runtime/gc_stack_portable.go @@ -1,4 +1,4 @@ -//go:build (gc.conservative || gc.precise) && tinygo.wasm +//go:build (gc.conservative || gc.custom || gc.precise) && tinygo.wasm package runtime