arm: automatically determine stack sizes

This is a big change that will determine the stack size for many
goroutines automatically. Functions that aren't recursive and don't call
function pointers can in many cases have an automatically determined
worst case stack size. This is useful, as the stack size is usually much
lower than the previous hardcoded default of 1024 bytes: somewhere
around 200-500 bytes is common.

A side effect of this change is that the default stack sizes (including
the stack size for other architectures such as AVR) can now be changed
in the config JSON file, making it tunable per application.
This commit is contained in:
Ayke van Laethem
2020-07-18 13:36:28 +02:00
committed by Ron Evans
parent a761f556ff
commit a21a039ac7
27 changed files with 416 additions and 77 deletions
+6
View File
@@ -1,9 +1,11 @@
.syntax unified
.cfi_sections .debug_frame
.section .text.HardFault_Handler
.global HardFault_Handler
.type HardFault_Handler, %function
HardFault_Handler:
.cfi_startproc
// Put the old stack pointer in the first argument, for easy debugging. This
// is especially useful on Cortex-M0, which supports far fewer debug
// facilities.
@@ -19,6 +21,7 @@ HardFault_Handler:
// Continue handling this error in Go.
bl handleHardFault
.cfi_endproc
.size HardFault_Handler, .-HardFault_Handler
// This is a convenience function for semihosting support.
@@ -27,5 +30,8 @@ HardFault_Handler:
.global SemihostingCall
.type SemihostingCall, %function
SemihostingCall:
.cfi_startproc
bkpt 0xab
bx lr
.cfi_endproc
.size SemihostingCall, .-SemihostingCall
+5
View File
@@ -18,3 +18,8 @@ type Task struct {
// state is the underlying running state of the task.
state state
}
// getGoroutineStackSize is a compiler intrinsic that returns the stack size for
// the given function and falls back to the default stack size. It is replaced
// with a load from a special section just before codegen.
func getGoroutineStackSize(fn uintptr) uintptr
+1 -1
View File
@@ -67,7 +67,7 @@ func createTask() *Task {
// start invokes a function in a new goroutine. Calls to this are inserted by the compiler.
// The created goroutine starts running immediately.
// This is implemented inside the compiler.
func start(fn uintptr, args unsafe.Pointer)
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr)
// Current returns the current active task.
// This is implemented inside the compiler.
+1 -1
View File
@@ -17,7 +17,7 @@ func Current() *Task {
}
//go:noinline
func start(fn uintptr, args unsafe.Pointer) {
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
// The compiler will error if this is reachable.
runtimePanic("scheduler is disabled")
}
+3 -3
View File
@@ -54,7 +54,7 @@ func (t *Task) Resume() {
}
// initialize the state and prepare to call the specified function with the specified argument bundle.
func (s *state) initialize(fn uintptr, args unsafe.Pointer) {
func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
// Create a stack.
stack := make([]uintptr, stackSize/unsafe.Sizeof(uintptr(0)))
@@ -67,9 +67,9 @@ func runqueuePushBack(*Task)
// start creates and starts a new goroutine with the given function and arguments.
// The new goroutine is scheduled to run later.
func start(fn uintptr, args unsafe.Pointer) {
func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
t := &Task{}
t.state.initialize(fn, args)
t.state.initialize(fn, args, stackSize)
runqueuePushBack(t)
}
-2
View File
@@ -4,8 +4,6 @@ package task
import "unsafe"
const stackSize = 256
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see scheduler_avr.S that relies on the
// exact layout of this struct.
-2
View File
@@ -4,8 +4,6 @@ package task
import "unsafe"
const stackSize = 1024
// calleeSavedRegs is the list of registers that must be saved and restored when
// switching between tasks. Also see scheduler_cortexm.S that relies on the
// exact layout of this struct.
+4 -7
View File
@@ -41,11 +41,7 @@ tinygo_getSystemStackPointer:
.cfi_endproc
.size tinygo_getSystemStackPointer, .-tinygo_getSystemStackPointer
// switchToScheduler and switchToTask are also in the same section, to make sure
// relative branches work.
.section .text.tinygo_swapTask
.section .text.tinygo_switchToScheduler
.global tinygo_switchToScheduler
.type tinygo_switchToScheduler, %function
tinygo_switchToScheduler:
@@ -62,6 +58,7 @@ tinygo_switchToScheduler:
.cfi_endproc
.size tinygo_switchToScheduler, .-tinygo_switchToScheduler
.section .text.tinygo_switchToTask
.global tinygo_switchToTask
.type tinygo_switchToTask, %function
tinygo_switchToTask:
@@ -72,11 +69,11 @@ tinygo_switchToTask:
// and then we can invoke swapTask.
msr PSP, r0
// Continue executing in the swapTask function, which swaps the stack
// pointer.
b.n tinygo_swapTask
.cfi_endproc
.size tinygo_switchToTask, .-tinygo_switchToTask
.section .text.tinygo_swapTask
.global tinygo_swapTask
.type tinygo_swapTask, %function
tinygo_swapTask: