
The Cortex-M target isn't much changed, but much of the logic for the AVR stack switcher that was previously in assembly has now been moved to Go to make it more maintainable and in fact smaller in code size. Three functions (tinygo_getCurrentStackPointer, tinygo_switchToTask, tinygo_switchToScheduler) have been changed to one: tinygo_swapTask. This reduction in assembly code should make the code more maintainable and should make it easier to port stack switching to other architectures. I've also moved the assembly files to src/internal/task, which seems like a more appropriate location to me.
67 строки
1,8 КиБ
Go
67 строки
1,8 КиБ
Go
// +build scheduler.tasks,avr
|
|
|
|
package task
|
|
|
|
import "unsafe"
|
|
|
|
//go:extern tinygo_systemStack
|
|
var systemStack uintptr
|
|
|
|
// calleeSavedRegs is the list of registers that must be saved and restored when
|
|
// switching between tasks. Also see task_stack_avr.S that relies on the exact
|
|
// layout of this struct.
|
|
//
|
|
// https://gcc.gnu.org/wiki/avr-gcc#Call-Saved_Registers
|
|
type calleeSavedRegs struct {
|
|
r2r3 uintptr
|
|
r4r5 uintptr
|
|
r6r7 uintptr
|
|
r8r9 uintptr
|
|
r10r11 uintptr
|
|
r12r13 uintptr
|
|
r14r15 uintptr
|
|
r16r17 uintptr
|
|
r28r29 uintptr // Y register
|
|
|
|
pc uintptr
|
|
}
|
|
|
|
// archInit runs architecture-specific setup for the goroutine startup.
|
|
// Note: adding //go:noinline to work around an AVR backend bug.
|
|
//go:noinline
|
|
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
|
// Store the initial sp for the startTask function (implemented in assembly).
|
|
s.sp = uintptr(unsafe.Pointer(r)) - 1
|
|
|
|
// Initialize the registers.
|
|
// These will be popped off of the stack on the first resume of the goroutine.
|
|
|
|
// Start the function at tinygo_startTask.
|
|
startTask := uintptr(unsafe.Pointer(&startTask))
|
|
r.pc = startTask/2>>8 | startTask/2<<8
|
|
|
|
// Pass the function to call in r2:r3.
|
|
// This function is a compiler-generated wrapper which loads arguments out
|
|
// of a struct pointer. See createGoroutineStartWrapper (defined in
|
|
// compiler/goroutine.go) for more information.
|
|
r.r2r3 = fn
|
|
|
|
// Pass the pointer to the arguments struct in r4:45.
|
|
r.r4r5 = uintptr(args)
|
|
}
|
|
|
|
func (s *state) resume() {
|
|
swapTask(s.sp, &systemStack)
|
|
}
|
|
|
|
func (s *state) pause() {
|
|
newStack := systemStack
|
|
systemStack = 0
|
|
swapTask(newStack, &s.sp)
|
|
}
|
|
|
|
// SystemStack returns the system stack pointer when called from a task stack.
|
|
// When called from the system stack, it returns 0.
|
|
func SystemStack() uintptr {
|
|
return systemStack
|
|
}
|