mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 02:57:46 +00:00
bfa29f17da
Instead of putting tinygo_scanCurrentStack in scheduler_*.S files, put
them in dedicated files. The function tinygo_scanCurrentStack has
nothing to do with scheduling and so doesn't belong there. Additionally,
while scheduling code is made specific for the Cortex-M, the
tinygo_scanCurrentStack is generic to all ARM targets so this move
removes some duplication there.
Specifically:
* tinygo_scanCurrentStack is moved out of scheduler_cortexm.S as it
isn't really part of the scheduler. It is now gc_arm.S.
* Same for the AVR target.
* Same for the RISCV target.
* scheduler_gba.S is removed, using gc_arm.S instead as it only
contains tinygo_scanCurrentStack.
139 lines
4.7 KiB
ArmAsm
139 lines
4.7 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
|