mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 19:43:44 +00:00
a21a039ac7
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.
170 lines
5.4 KiB
ArmAsm
170 lines
5.4 KiB
ArmAsm
// Only generate .debug_frame, don't generate .eh_frame.
|
|
.cfi_sections .debug_frame
|
|
|
|
.section .text.tinygo_startTask
|
|
.global tinygo_startTask
|
|
.type tinygo_startTask, %function
|
|
tinygo_startTask:
|
|
.cfi_startproc
|
|
// Small assembly stub for starting a goroutine. This is already run on the
|
|
// new stack, with the callee-saved registers already loaded.
|
|
// Most importantly, r4 contains the pc of the to-be-started function and r5
|
|
// contains the only argument it is given. Multiple arguments are packed
|
|
// into one by storing them in a new allocation.
|
|
|
|
// Indicate to the unwinder that there is nothing to unwind, this is the
|
|
// root frame. It avoids the following (bogus) error message in GDB:
|
|
// Backtrace stopped: previous frame identical to this frame (corrupt stack?)
|
|
.cfi_undefined lr
|
|
|
|
// Set the first argument of the goroutine start wrapper, which contains all
|
|
// the arguments.
|
|
mov r0, r5
|
|
|
|
// Branch to the "goroutine start" function. By using blx instead of bx,
|
|
// we'll return here instead of tail calling.
|
|
blx r4
|
|
|
|
// After return, exit this goroutine. This is a tail call.
|
|
bl tinygo_pause
|
|
.cfi_endproc
|
|
.size tinygo_startTask, .-tinygo_startTask
|
|
|
|
.section .text.tinygo_getSystemStackPointer
|
|
.global tinygo_getSystemStackPointer
|
|
.type tinygo_getSystemStackPointer, %function
|
|
tinygo_getSystemStackPointer:
|
|
.cfi_startproc
|
|
// The system stack pointer is always stored in the MSP register.
|
|
mrs r0, MSP
|
|
bx lr
|
|
.cfi_endproc
|
|
.size tinygo_getSystemStackPointer, .-tinygo_getSystemStackPointer
|
|
|
|
.section .text.tinygo_switchToScheduler
|
|
.global tinygo_switchToScheduler
|
|
.type tinygo_switchToScheduler, %function
|
|
tinygo_switchToScheduler:
|
|
.cfi_startproc
|
|
// r0 = sp *uintptr
|
|
|
|
// Currently on the task stack (SP=PSP). We need to store the position on
|
|
// the stack where the in-use registers will be stored.
|
|
mov r1, sp
|
|
subs r1, #36
|
|
str r1, [r0]
|
|
|
|
b tinygo_swapTask
|
|
.cfi_endproc
|
|
.size tinygo_switchToScheduler, .-tinygo_switchToScheduler
|
|
|
|
.section .text.tinygo_switchToTask
|
|
.global tinygo_switchToTask
|
|
.type tinygo_switchToTask, %function
|
|
tinygo_switchToTask:
|
|
.cfi_startproc
|
|
// r0 = sp uintptr
|
|
|
|
// Currently on the scheduler stack (SP=MSP). We'll have to update the PSP,
|
|
// and then we can invoke swapTask.
|
|
msr PSP, r0
|
|
|
|
b.n tinygo_swapTask
|
|
.cfi_endproc
|
|
.size tinygo_switchToTask, .-tinygo_switchToTask
|
|
|
|
.section .text.tinygo_swapTask
|
|
.global tinygo_swapTask
|
|
.type tinygo_swapTask, %function
|
|
tinygo_swapTask:
|
|
.cfi_startproc
|
|
// This function stores the current register state to the stack, switches to
|
|
// the other stack (MSP/PSP), and loads the register state from the other
|
|
// stack. Apart from saving and restoring all relevant callee-saved
|
|
// registers, it also ends with branching to the last program counter (saved
|
|
// as the lr register, to follow the ARM calling convention).
|
|
|
|
// On pre-Thumb2 CPUs (Cortex-M0 in particular), registers r8-r15 cannot be
|
|
// used directly. Only very few operations work on them, such as mov. That's
|
|
// why the higher register values are first stored in the temporary register
|
|
// r3 when loading/storing them.
|
|
// It is possible to reduce the swapTask by two instructions (~2 cycles) on
|
|
// Cortex-M0 by reordering the layout of the pushed registers from {r4-r11,
|
|
// lr} to {r8-r11, r4-r8, lr}. However, that also requires a change on the
|
|
// Go side (depending on thumb1/thumb2!) and so is not really worth the
|
|
// complexity.
|
|
|
|
// Store state to old task. It saves the lr instead of the pc, because that
|
|
// will be the pc after returning back to the old task (in a different
|
|
// invocation of swapTask).
|
|
#if defined(__thumb2__)
|
|
push {r4-r11, lr}
|
|
.cfi_def_cfa_offset 9*4
|
|
#else
|
|
mov r0, r8
|
|
mov r1, r9
|
|
mov r2, r10
|
|
mov r3, r11
|
|
push {r0-r3, lr}
|
|
.cfi_def_cfa_offset 5*4
|
|
push {r4-r7}
|
|
.cfi_def_cfa_offset 9*4
|
|
#endif
|
|
|
|
// Switch the stack. This could either switch from PSP to MSP, or from MSP
|
|
// to PSP. By using an XOR (eor), it will just switch to the other stack.
|
|
mrs r0, CONTROL // load CONTROL register
|
|
movs r3, #2
|
|
eors r0, r0, r3 // flip the SPSEL (active stack pointer) bit
|
|
msr CONTROL, r0 // store CONTROL register
|
|
isb // required to flush the pipeline
|
|
|
|
// Load state from new task and branch to the previous position in the
|
|
// program.
|
|
#if defined(__thumb2__)
|
|
pop {r4-r11, pc}
|
|
#else
|
|
pop {r4-r7}
|
|
.cfi_def_cfa_offset 5*9
|
|
pop {r0-r3}
|
|
.cfi_def_cfa_offset 1*9
|
|
mov r8, r0
|
|
mov r9, r1
|
|
mov r10, r2
|
|
mov r11, r3
|
|
pop {pc}
|
|
#endif
|
|
.cfi_endproc
|
|
.size tinygo_swapTask, .-tinygo_swapTask
|
|
|
|
.section .text.tinygo_scanCurrentStack
|
|
.global tinygo_scanCurrentStack
|
|
.type tinygo_scanCurrentStack, %function
|
|
tinygo_scanCurrentStack:
|
|
.cfi_startproc
|
|
// Save callee-saved registers onto the stack.
|
|
#if defined(__thumb2__)
|
|
push {r4-r11, lr}
|
|
.cfi_def_cfa_offset 9*4
|
|
#else
|
|
mov r0, r8
|
|
mov r1, r9
|
|
mov r2, r10
|
|
mov r3, r11
|
|
push {r0-r3, lr}
|
|
.cfi_def_cfa_offset 5*4
|
|
push {r4-r7}
|
|
.cfi_def_cfa_offset 4*4
|
|
#endif
|
|
|
|
// Scan the stack.
|
|
mov r0, sp
|
|
bl tinygo_scanstack
|
|
|
|
// Restore stack state and return.
|
|
add sp, #32
|
|
.cfi_def_cfa_offset 1*4
|
|
pop {pc}
|
|
.cfi_endproc
|
|
.size tinygo_scanCurrentStack, .-tinygo_scanCurrentStack
|