mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 14:48:40 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 907d90105a |
+13
-3
@@ -1228,10 +1228,15 @@ func determineStackSizes(mod llvm.Module, executable string) ([]string, map[stri
|
||||
// Goroutines need to be started and finished and take up some stack space
|
||||
// that way. This can be measured by measuing the stack size of
|
||||
// tinygo_startTask.
|
||||
if numFuncs := len(functions["tinygo_startTask"]); numFuncs != 1 {
|
||||
return nil, nil, fmt.Errorf("expected exactly one definition of tinygo_startTask, got %d", numFuncs)
|
||||
var baseStackSize uint64
|
||||
var baseStackSizeType stacksize.SizeType
|
||||
var baseStackSizeFailedAt *stacksize.CallNode
|
||||
if len(gowrappers) != 0 {
|
||||
if numFuncs := len(functions["tinygo_startTask"]); numFuncs != 1 {
|
||||
return nil, nil, fmt.Errorf("expected exactly one definition of tinygo_startTask, got %d", numFuncs)
|
||||
}
|
||||
baseStackSize, baseStackSizeType, baseStackSizeFailedAt = functions["tinygo_startTask"][0].StackSize()
|
||||
}
|
||||
baseStackSize, baseStackSizeType, baseStackSizeFailedAt := functions["tinygo_startTask"][0].StackSize()
|
||||
|
||||
sizes := make(map[string]functionStackSize)
|
||||
|
||||
@@ -1303,6 +1308,11 @@ func modifyStackSizes(executable string, stackSizeLoads []string, stackSizes map
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if data == nil {
|
||||
// The .tinygo_stacksizes section doesn't exist, so assume this
|
||||
// modification isn't needed.
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(stackSizeLoads)*4 != len(data) {
|
||||
// Note: while AVR should use 2 byte stack sizes, even 64-bit platforms
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ func getElfSectionData(executable string, sectionName string) ([]byte, elf.FileH
|
||||
|
||||
section := elfFile.Section(sectionName)
|
||||
if section == nil {
|
||||
return nil, elf.FileHeader{}, fmt.Errorf("could not find %s section", sectionName)
|
||||
return nil, elf.FileHeader{}, nil
|
||||
}
|
||||
|
||||
data, err := section.Data()
|
||||
|
||||
@@ -99,9 +99,9 @@ func Pause() {
|
||||
//export tinygo_unwind
|
||||
func (*stackState) unwind()
|
||||
|
||||
// Resume the task until it pauses or completes.
|
||||
// Switch to this task until it pauses or completes.
|
||||
// This may only be called from the scheduler.
|
||||
func (t *Task) Resume() {
|
||||
func (t *Task) Switch() {
|
||||
// The current task must be saved and restored because this can nest on WASM with JS.
|
||||
prevTask := currentTask
|
||||
t.gcData.swap()
|
||||
@@ -121,9 +121,3 @@ func (t *Task) Resume() {
|
||||
|
||||
//export tinygo_rewind
|
||||
func (*state) rewind()
|
||||
|
||||
// OnSystemStack returns whether the caller is running on the system stack.
|
||||
func OnSystemStack() bool {
|
||||
// If there is not an active goroutine, then this must be running on the system stack.
|
||||
return Current() == nil
|
||||
}
|
||||
|
||||
@@ -28,12 +28,12 @@ func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||
|
||||
type state struct{}
|
||||
|
||||
func (t *Task) Resume() {
|
||||
func (t *Task) Switch() {
|
||||
runtimePanic("scheduler is disabled")
|
||||
}
|
||||
|
||||
// OnSystemStack returns whether the caller is running on the system stack.
|
||||
func OnSystemStack() bool {
|
||||
// MainTask returns whether the caller is running in the main goroutine.
|
||||
func MainTask() bool {
|
||||
// This scheduler does not do any stack switching.
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -27,8 +27,12 @@ type state struct {
|
||||
canaryPtr *uintptr
|
||||
}
|
||||
|
||||
// currentTask is the current running task, or nil if currently in the scheduler.
|
||||
var currentTask *Task
|
||||
// The task struct for the main goroutine.
|
||||
var mainTask Task
|
||||
|
||||
// currentTask is the current running task. The default value is the main
|
||||
// goroutine.
|
||||
var currentTask = &mainTask
|
||||
|
||||
// Current returns the current active task.
|
||||
func Current() *Task {
|
||||
@@ -36,29 +40,36 @@ func Current() *Task {
|
||||
}
|
||||
|
||||
// Pause suspends the current task and returns to the scheduler.
|
||||
// This function may only be called when running on a goroutine stack, not when running on the system stack or in an interrupt.
|
||||
// This function may only be called when running on a goroutine stack, not when in an interrupt.
|
||||
func Pause() {
|
||||
// Check whether the canary (the lowest address of the stack) is still
|
||||
// valid. If it is not, a stack overflow has occured.
|
||||
if *currentTask.state.canaryPtr != stackCanary {
|
||||
if currentTask.state.canaryPtr != nil && *currentTask.state.canaryPtr != stackCanary {
|
||||
runtimePanic("goroutine stack overflow")
|
||||
}
|
||||
currentTask.state.pause()
|
||||
scheduler()
|
||||
}
|
||||
|
||||
//go:linkname scheduler runtime.scheduler
|
||||
func scheduler()
|
||||
|
||||
//export tinygo_pause
|
||||
func pause() {
|
||||
Pause()
|
||||
}
|
||||
|
||||
// Resume the task until it pauses or completes.
|
||||
// Switch to the given task until it pauses or completes.
|
||||
// This may only be called from the scheduler.
|
||||
func (t *Task) Resume() {
|
||||
func (t *Task) Switch() {
|
||||
current := currentTask
|
||||
if current == t {
|
||||
// Nothing to switch to: we're already in this task.
|
||||
return
|
||||
}
|
||||
currentTask = t
|
||||
t.gcData.swap()
|
||||
t.state.resume()
|
||||
t.state.switchTo(current)
|
||||
t.gcData.swap()
|
||||
currentTask = nil
|
||||
}
|
||||
|
||||
// initialize the state and prepare to call the specified function with the specified argument bundle.
|
||||
@@ -103,8 +114,8 @@ func start(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||
runqueuePushBack(t)
|
||||
}
|
||||
|
||||
// OnSystemStack returns whether the caller is running on the system stack.
|
||||
func OnSystemStack() bool {
|
||||
// MainTask returns whether the caller is running in the main goroutine.
|
||||
func MainTask() bool {
|
||||
// If there is not an active goroutine, then this must be running on the system stack.
|
||||
return Current() == nil
|
||||
return currentTask == &mainTask
|
||||
}
|
||||
|
||||
@@ -20,11 +20,8 @@ tinygo_startTask:
|
||||
// Branch to the "goroutine start" function.
|
||||
calll *%ebx
|
||||
|
||||
// Rebalance the stack (to undo the above push).
|
||||
addl $4, %esp
|
||||
|
||||
// After return, exit this goroutine. This is a tail call.
|
||||
jmp tinygo_pause
|
||||
// After return, exit this goroutine.
|
||||
calll tinygo_pause
|
||||
.cfi_endproc
|
||||
|
||||
.global tinygo_swapTask
|
||||
|
||||
@@ -5,8 +5,6 @@ package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see task_stack_386.S that relies on the exact
|
||||
// layout of this struct.
|
||||
@@ -46,18 +44,12 @@ func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
r.esi = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
func (s *state) switchTo(current *Task) {
|
||||
swapTask(s.sp, ¤t.state.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
|
||||
return mainTask.state.sp
|
||||
}
|
||||
|
||||
@@ -28,11 +28,11 @@ tinygo_startTask:
|
||||
// Branch to the "goroutine start" function.
|
||||
callq *%r12
|
||||
|
||||
// After return, exit this goroutine. This is a tail call.
|
||||
// After return, exit this goroutine.
|
||||
#ifdef __MACH__
|
||||
jmp _tinygo_pause
|
||||
callq _tinygo_pause
|
||||
#else
|
||||
jmp tinygo_pause
|
||||
callq tinygo_pause
|
||||
#endif
|
||||
.cfi_endproc
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see task_stack_amd64.S that relies on the exact
|
||||
// layout of this struct.
|
||||
@@ -45,18 +43,12 @@ func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
r.r13 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
func (s *state) switchTo(current *Task) {
|
||||
swapTask(s.sp, ¤t.state.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
|
||||
return mainTask.state.sp
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ tinygo_startTask:
|
||||
// Branch to the "goroutine start" function.
|
||||
callq *%r12
|
||||
|
||||
// After return, exit this goroutine. This is a tail call.
|
||||
jmp tinygo_pause
|
||||
// After return, exit this goroutine.
|
||||
callq tinygo_pause
|
||||
|
||||
.global tinygo_swapTask
|
||||
.section .text.tinygo_swapTask,"ax"
|
||||
|
||||
@@ -8,8 +8,6 @@ package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see task_stack_amd64_windows.S that relies on
|
||||
// the exact layout of this struct.
|
||||
@@ -50,18 +48,12 @@ func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
r.r13 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
func (s *state) switchTo(current *Task) {
|
||||
swapTask(s.sp, ¤t.state.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
|
||||
return mainTask.state.sp
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see task_stack_arm.S that relies on the exact
|
||||
// layout of this struct.
|
||||
@@ -45,18 +43,12 @@ func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
r.r5 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
func (s *state) switchTo(current *Task) {
|
||||
swapTask(s.sp, ¤t.state.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
|
||||
return mainTask.state.sp
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see task_stack_arm64.S that relies on the exact
|
||||
// layout of this struct.
|
||||
@@ -48,18 +46,12 @@ func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
r.x20 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
func (s *state) switchTo(current *Task) {
|
||||
swapTask(s.sp, ¤t.state.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
|
||||
return mainTask.state.sp
|
||||
}
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
.section .bss.tinygo_systemStack
|
||||
.global tinygo_systemStack
|
||||
.type tinygo_systemStack, %object
|
||||
tinygo_systemStack:
|
||||
.short 0
|
||||
|
||||
.section .text.tinygo_startTask
|
||||
.global tinygo_startTask
|
||||
.type tinygo_startTask, %function
|
||||
|
||||
@@ -5,9 +5,6 @@ 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.
|
||||
@@ -51,18 +48,12 @@ func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
r.r4r5 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
func (s *state) switchTo(current *Task) {
|
||||
swapTask(s.sp, ¤t.state.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
|
||||
return mainTask.state.sp
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
//go:build scheduler.tasks && baremetal
|
||||
// +build scheduler.tasks,baremetal
|
||||
|
||||
package task
|
||||
|
||||
//go:extern _stack_bottom
|
||||
var _stack_bottom uintptr
|
||||
|
||||
func init() {
|
||||
mainTask.state.canaryPtr = &_stack_bottom
|
||||
*mainTask.state.canaryPtr = stackCanary
|
||||
}
|
||||
@@ -30,10 +30,10 @@ tinygo_startTask:
|
||||
.cfi_endproc
|
||||
.size tinygo_startTask, .-tinygo_startTask
|
||||
|
||||
.section .text.tinygo_switchToScheduler
|
||||
.global tinygo_switchToScheduler
|
||||
.type tinygo_switchToScheduler, %function
|
||||
tinygo_switchToScheduler:
|
||||
.section .text.tinygo_switchToMain
|
||||
.global tinygo_switchToMain
|
||||
.type tinygo_switchToMain, %function
|
||||
tinygo_switchToMain:
|
||||
.cfi_startproc
|
||||
// r0 = sp *uintptr
|
||||
|
||||
@@ -43,9 +43,9 @@ tinygo_switchToScheduler:
|
||||
subs r1, #36
|
||||
str r1, [r0]
|
||||
|
||||
b tinygo_swapTask
|
||||
b tinygo_swapTaskInternal
|
||||
.cfi_endproc
|
||||
.size tinygo_switchToScheduler, .-tinygo_switchToScheduler
|
||||
.size tinygo_switchToMain, .-tinygo_switchToMain
|
||||
|
||||
.section .text.tinygo_switchToTask
|
||||
.global tinygo_switchToTask
|
||||
@@ -55,17 +55,17 @@ tinygo_switchToTask:
|
||||
// r0 = sp uintptr
|
||||
|
||||
// Currently on the scheduler stack (SP=MSP). We'll have to update the PSP,
|
||||
// and then we can invoke swapTask.
|
||||
// and then we can invoke swapTaskInternal.
|
||||
msr PSP, r0
|
||||
|
||||
b.n tinygo_swapTask
|
||||
b.n tinygo_swapTaskInternal
|
||||
.cfi_endproc
|
||||
.size tinygo_switchToTask, .-tinygo_switchToTask
|
||||
|
||||
.section .text.tinygo_swapTask
|
||||
.global tinygo_swapTask
|
||||
.type tinygo_swapTask, %function
|
||||
tinygo_swapTask:
|
||||
.section .text.tinygo_swapTaskInternal
|
||||
.global tinygo_swapTaskInternal
|
||||
.type tinygo_swapTaskInternal, %function
|
||||
tinygo_swapTaskInternal:
|
||||
.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
|
||||
@@ -77,15 +77,15 @@ tinygo_swapTask:
|
||||
// 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.
|
||||
// It is possible to reduce the swapTaskInternal 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).
|
||||
// invocation of swapTaskInternal).
|
||||
#if defined(__thumb2__)
|
||||
push {r4-r11, lr}
|
||||
.cfi_def_cfa_offset 9*4
|
||||
@@ -108,6 +108,63 @@ tinygo_swapTask:
|
||||
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_swapTaskInternal, .-tinygo_swapTaskInternal
|
||||
|
||||
|
||||
.section .text.tinygo_swapTask
|
||||
.global tinygo_swapTask
|
||||
.type tinygo_swapTask, %function
|
||||
tinygo_swapTask:
|
||||
.cfi_startproc
|
||||
// This function is like tinygo_swapTaskInternal but only switches the stack
|
||||
// pointer without touching the SPSEL bit (to switch between MSP and PSP).
|
||||
// Therefore, it is used to switch between two tasks that are not the main
|
||||
// goroutine.
|
||||
|
||||
// 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
|
||||
|
||||
// Save the current stack pointer in oldStack.
|
||||
#if defined(__thumb2__)
|
||||
str sp, [r1]
|
||||
#else
|
||||
mov r2, sp
|
||||
str r2, [r1]
|
||||
#endif
|
||||
|
||||
// Switch to the new stack pointer.
|
||||
mov sp, r0
|
||||
|
||||
// Load state from new task and branch to the previous position in the
|
||||
// program.
|
||||
#if defined(__thumb2__)
|
||||
|
||||
@@ -52,19 +52,23 @@ func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
r.r5 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
switchToTask(s.sp)
|
||||
func (s *state) switchTo(current *Task) {
|
||||
// This code is probably more complex than it needs to be.
|
||||
// TODO: simplify this, ideally to a single function call.
|
||||
if s == &mainTask.state {
|
||||
switchToMain(¤t.state.sp)
|
||||
} else if current == &mainTask {
|
||||
switchToTask(s.sp)
|
||||
} else {
|
||||
swapTask(s.sp, ¤t.state.sp)
|
||||
}
|
||||
}
|
||||
|
||||
//export tinygo_switchToTask
|
||||
func switchToTask(uintptr)
|
||||
|
||||
//export tinygo_switchToScheduler
|
||||
func switchToScheduler(*uintptr)
|
||||
|
||||
func (s *state) pause() {
|
||||
switchToScheduler(&s.sp)
|
||||
}
|
||||
//export tinygo_switchToMain
|
||||
func switchToMain(*uintptr)
|
||||
|
||||
// SystemStack returns the system stack pointer. On Cortex-M, it is always
|
||||
// available.
|
||||
|
||||
@@ -16,8 +16,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see task_stack_esp8266.S that relies on the
|
||||
// exact layout of this struct.
|
||||
@@ -60,18 +58,12 @@ func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
r.a2 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
func (s *state) switchTo(current *Task) {
|
||||
swapTask(s.sp, ¤t.state.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
|
||||
return mainTask.state.sp
|
||||
}
|
||||
|
||||
@@ -17,8 +17,6 @@ package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see task_stack_esp8266.S that relies on the
|
||||
// exact layout of this struct.
|
||||
@@ -54,18 +52,12 @@ func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
r.a13 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
func (s *state) switchTo(current *Task) {
|
||||
swapTask(s.sp, ¤t.state.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
|
||||
return mainTask.state.sp
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ package task
|
||||
|
||||
import "unsafe"
|
||||
|
||||
var systemStack uintptr
|
||||
|
||||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
||||
// switching between tasks. Also see scheduler_riscv.S that relies on the
|
||||
// exact layout of this struct.
|
||||
@@ -50,18 +48,12 @@ func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) {
|
||||
r.s1 = uintptr(args)
|
||||
}
|
||||
|
||||
func (s *state) resume() {
|
||||
swapTask(s.sp, &systemStack)
|
||||
}
|
||||
|
||||
func (s *state) pause() {
|
||||
newStack := systemStack
|
||||
systemStack = 0
|
||||
swapTask(newStack, &s.sp)
|
||||
func (s *state) switchTo(current *Task) {
|
||||
swapTask(s.sp, ¤t.state.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
|
||||
return mainTask.state.sp
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ func markStack() {
|
||||
// Scan the current stack, and all current registers.
|
||||
scanCurrentStack()
|
||||
|
||||
if !task.OnSystemStack() {
|
||||
if !task.MainTask() {
|
||||
// Mark system stack.
|
||||
markRoots(getSystemStackPointer(), stackTop)
|
||||
}
|
||||
@@ -28,7 +28,7 @@ func scanstack(sp uintptr) {
|
||||
// Mark current stack.
|
||||
// This function is called by scanCurrentStack, after pushing all registers onto the stack.
|
||||
// Callee-saved registers have been pushed onto stack by tinygo_localscan, so this will scan them too.
|
||||
if task.OnSystemStack() {
|
||||
if task.MainTask() {
|
||||
// This is the system stack.
|
||||
// Scan all words on the stack.
|
||||
markRoots(sp, stackTop)
|
||||
|
||||
@@ -112,9 +112,7 @@ func addSleepTask(t *task.Task, duration timeUnit) {
|
||||
*q = t
|
||||
}
|
||||
|
||||
// Run the scheduler until all tasks have finished.
|
||||
func scheduler() {
|
||||
// Main scheduler loop.
|
||||
var now timeUnit
|
||||
for !schedulerDone {
|
||||
scheduleLog("")
|
||||
@@ -164,7 +162,11 @@ func scheduler() {
|
||||
|
||||
// Run the given task.
|
||||
scheduleLogTask(" run:", t)
|
||||
t.Resume()
|
||||
t.Switch()
|
||||
if GOARCH != "wasm" {
|
||||
// Don't return when using Asyncify.
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,7 +183,7 @@ func minSched() {
|
||||
}
|
||||
|
||||
scheduleLogTask(" run:", t)
|
||||
t.Resume()
|
||||
t.Switch()
|
||||
}
|
||||
scheduleLog("stop nested scheduler")
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//go:build !scheduler.none
|
||||
// +build !scheduler.none
|
||||
//go:build scheduler.asyncify
|
||||
// +build scheduler.asyncify
|
||||
|
||||
package runtime
|
||||
|
||||
@@ -16,3 +16,27 @@ func getSystemStackPointer() uintptr {
|
||||
}
|
||||
return sp
|
||||
}
|
||||
|
||||
// Pause the current task for a given time.
|
||||
//
|
||||
//go:linkname sleep time.Sleep
|
||||
func sleep(duration int64) {
|
||||
if duration <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
addSleepTask(task.Current(), nanosecondsToTicks(duration))
|
||||
task.Pause()
|
||||
}
|
||||
|
||||
// run is called by the program entry point to execute the go program.
|
||||
// The main goroutine runs on the system stack, so initAll and callMain can be
|
||||
// called directly.
|
||||
func run() {
|
||||
initHeap()
|
||||
initAll()
|
||||
callMain()
|
||||
schedulerDone = true
|
||||
}
|
||||
|
||||
const hasScheduler = true
|
||||
|
||||
@@ -27,6 +27,7 @@ SECTIONS
|
||||
.stack (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_stack_bottom = .;
|
||||
. += _stack_size;
|
||||
_stack_top = .;
|
||||
} >RAM
|
||||
|
||||
@@ -19,6 +19,7 @@ SECTIONS
|
||||
|
||||
.stack (NOLOAD) :
|
||||
{
|
||||
_stack_bottom = .;
|
||||
. += _stack_size;
|
||||
_stack_top = .;
|
||||
} >RAM
|
||||
|
||||
@@ -41,6 +41,7 @@ SECTIONS
|
||||
.stack (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(16);
|
||||
_stack_bottom = .;
|
||||
. += _stack_size;
|
||||
_stack_top = .;
|
||||
} >DRAM
|
||||
|
||||
@@ -70,6 +70,7 @@ SECTIONS
|
||||
.stack (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(16);
|
||||
_stack_bottom = .;
|
||||
. += _stack_size;
|
||||
_stack_top = .;
|
||||
} >DRAM
|
||||
|
||||
@@ -67,6 +67,11 @@ _heap_end = ORIGIN(DRAM) + LENGTH(DRAM);
|
||||
*/
|
||||
_stack_top = 0x40000000;
|
||||
|
||||
/* This is the stack bottom according to this forum post:
|
||||
* https://bbs.espressif.com/viewtopic.php?t=8879#p19038
|
||||
*/
|
||||
_stack_bottom = 0x3fffeb2c;
|
||||
|
||||
/* Functions normally provided by a libc.
|
||||
* Source:
|
||||
* https://github.com/espressif/ESP8266_NONOS_SDK/blob/master/ld/eagle.rom.addr.v6.ld
|
||||
|
||||
@@ -57,6 +57,7 @@ SECTIONS
|
||||
}
|
||||
|
||||
PROVIDE(_stack_top = ORIGIN(RAM) + LENGTH(RAM));
|
||||
_stack_bottom = _stack_top - _stack_size;
|
||||
|
||||
/* For the memory allocator. */
|
||||
_heap_start = _ebss;
|
||||
|
||||
@@ -59,6 +59,7 @@ SECTIONS
|
||||
.stack (NOLOAD) : {
|
||||
|
||||
. = ALIGN(8);
|
||||
_stack_bottom = .;
|
||||
. += _stack_size;
|
||||
_stack_top = .;
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ SECTIONS
|
||||
.stack (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(16);
|
||||
_stack_bottom = .;
|
||||
. += _stack_size;
|
||||
_stack_top = .;
|
||||
} >RAM
|
||||
|
||||
Reference in New Issue
Block a user