diff --git a/builder/build.go b/builder/build.go index ab5b2b44ee..0ece7ff5e2 100644 --- a/builder/build.go +++ b/builder/build.go @@ -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 diff --git a/builder/elfpatch.go b/builder/elfpatch.go index 6a407db6fc..fc13c97ea2 100644 --- a/builder/elfpatch.go +++ b/builder/elfpatch.go @@ -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() diff --git a/src/internal/task/task_asyncify.go b/src/internal/task/task_asyncify.go index 939008bcde..1cee9fc92e 100644 --- a/src/internal/task/task_asyncify.go +++ b/src/internal/task/task_asyncify.go @@ -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 -} diff --git a/src/internal/task/task_none.go b/src/internal/task/task_none.go index 19dbee6244..ccb0c3dfe1 100644 --- a/src/internal/task/task_none.go +++ b/src/internal/task/task_none.go @@ -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 } diff --git a/src/internal/task/task_stack.go b/src/internal/task/task_stack.go index 59af650313..0b48c68383 100644 --- a/src/internal/task/task_stack.go +++ b/src/internal/task/task_stack.go @@ -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 } diff --git a/src/internal/task/task_stack_386.S b/src/internal/task/task_stack_386.S index c82213e98f..eb4fb95cf4 100644 --- a/src/internal/task/task_stack_386.S +++ b/src/internal/task/task_stack_386.S @@ -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 diff --git a/src/internal/task/task_stack_386.go b/src/internal/task/task_stack_386.go index 10744757aa..8b698a662a 100644 --- a/src/internal/task/task_stack_386.go +++ b/src/internal/task/task_stack_386.go @@ -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 } diff --git a/src/internal/task/task_stack_amd64.S b/src/internal/task/task_stack_amd64.S index f9182d49ff..36287fc243 100644 --- a/src/internal/task/task_stack_amd64.S +++ b/src/internal/task/task_stack_amd64.S @@ -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 diff --git a/src/internal/task/task_stack_amd64.go b/src/internal/task/task_stack_amd64.go index b391893163..8e9e8d4c28 100644 --- a/src/internal/task/task_stack_amd64.go +++ b/src/internal/task/task_stack_amd64.go @@ -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 } diff --git a/src/internal/task/task_stack_amd64_windows.S b/src/internal/task/task_stack_amd64_windows.S index bdbcfca665..1545f275b7 100644 --- a/src/internal/task/task_stack_amd64_windows.S +++ b/src/internal/task/task_stack_amd64_windows.S @@ -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" diff --git a/src/internal/task/task_stack_amd64_windows.go b/src/internal/task/task_stack_amd64_windows.go index 41ddbe6c9f..890f595444 100644 --- a/src/internal/task/task_stack_amd64_windows.go +++ b/src/internal/task/task_stack_amd64_windows.go @@ -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 } diff --git a/src/internal/task/task_stack_arm.go b/src/internal/task/task_stack_arm.go index 18982e875f..a6da5f12ff 100644 --- a/src/internal/task/task_stack_arm.go +++ b/src/internal/task/task_stack_arm.go @@ -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 } diff --git a/src/internal/task/task_stack_arm64.go b/src/internal/task/task_stack_arm64.go index e4fd09b207..0d1d5e2776 100644 --- a/src/internal/task/task_stack_arm64.go +++ b/src/internal/task/task_stack_arm64.go @@ -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 } diff --git a/src/internal/task/task_stack_avr.S b/src/internal/task/task_stack_avr.S index 2cba528f2d..d4c5629adc 100644 --- a/src/internal/task/task_stack_avr.S +++ b/src/internal/task/task_stack_avr.S @@ -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 diff --git a/src/internal/task/task_stack_avr.go b/src/internal/task/task_stack_avr.go index 96b7034d9e..aa082ecc33 100644 --- a/src/internal/task/task_stack_avr.go +++ b/src/internal/task/task_stack_avr.go @@ -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 } diff --git a/src/internal/task/task_stack_baremetal.go b/src/internal/task/task_stack_baremetal.go new file mode 100644 index 0000000000..cd868f6547 --- /dev/null +++ b/src/internal/task/task_stack_baremetal.go @@ -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 +} diff --git a/src/internal/task/task_stack_cortexm.S b/src/internal/task/task_stack_cortexm.S index afb23eb27f..8923d372bf 100644 --- a/src/internal/task/task_stack_cortexm.S +++ b/src/internal/task/task_stack_cortexm.S @@ -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__) diff --git a/src/internal/task/task_stack_cortexm.go b/src/internal/task/task_stack_cortexm.go index f61688808d..d39367d7eb 100644 --- a/src/internal/task/task_stack_cortexm.go +++ b/src/internal/task/task_stack_cortexm.go @@ -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. diff --git a/src/internal/task/task_stack_esp32.go b/src/internal/task/task_stack_esp32.go index a766884477..475287c470 100644 --- a/src/internal/task/task_stack_esp32.go +++ b/src/internal/task/task_stack_esp32.go @@ -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 } diff --git a/src/internal/task/task_stack_esp8266.go b/src/internal/task/task_stack_esp8266.go index 4a01a6fffd..d8724975b0 100644 --- a/src/internal/task/task_stack_esp8266.go +++ b/src/internal/task/task_stack_esp8266.go @@ -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 } diff --git a/src/internal/task/task_stack_tinygoriscv.go b/src/internal/task/task_stack_tinygoriscv.go index 36ef742e29..6b94e4fbac 100644 --- a/src/internal/task/task_stack_tinygoriscv.go +++ b/src/internal/task/task_stack_tinygoriscv.go @@ -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 } diff --git a/src/runtime/gc_stack_raw.go b/src/runtime/gc_stack_raw.go index e8fb0dc134..f5deeed24c 100644 --- a/src/runtime/gc_stack_raw.go +++ b/src/runtime/gc_stack_raw.go @@ -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) diff --git a/src/runtime/scheduler.go b/src/runtime/scheduler.go index 196f6e3a78..f0b57a3feb 100644 --- a/src/runtime/scheduler.go +++ b/src/runtime/scheduler.go @@ -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") } diff --git a/src/runtime/scheduler_any.go b/src/runtime/scheduler_asyncify.go similarity index 90% rename from src/runtime/scheduler_any.go rename to src/runtime/scheduler_asyncify.go index d809791517..b3dd6a97f4 100644 --- a/src/runtime/scheduler_any.go +++ b/src/runtime/scheduler_asyncify.go @@ -1,5 +1,5 @@ -//go:build !scheduler.none -// +build !scheduler.none +//go:build scheduler.asyncify +// +build scheduler.asyncify package runtime diff --git a/src/runtime/scheduler_tasks.go b/src/runtime/scheduler_tasks.go index 67b475496e..b1a6759831 100644 --- a/src/runtime/scheduler_tasks.go +++ b/src/runtime/scheduler_tasks.go @@ -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 diff --git a/targets/arm.ld b/targets/arm.ld index e4155b9090..5cab4835d6 100644 --- a/targets/arm.ld +++ b/targets/arm.ld @@ -27,6 +27,7 @@ SECTIONS .stack (NOLOAD) : { . = ALIGN(4); + _stack_bottom = .; . += _stack_size; _stack_top = .; } >RAM diff --git a/targets/avr.ld b/targets/avr.ld index 720465ca4c..2997da2012 100644 --- a/targets/avr.ld +++ b/targets/avr.ld @@ -19,6 +19,7 @@ SECTIONS .stack (NOLOAD) : { + _stack_bottom = .; . += _stack_size; _stack_top = .; } >RAM diff --git a/targets/esp32.ld b/targets/esp32.ld index a8d161288e..6fa2f822ae 100644 --- a/targets/esp32.ld +++ b/targets/esp32.ld @@ -41,6 +41,7 @@ SECTIONS .stack (NOLOAD) : { . = ALIGN(16); + _stack_bottom = .; . += _stack_size; _stack_top = .; } >DRAM diff --git a/targets/esp32c3.ld b/targets/esp32c3.ld index b6be6e7acb..0a67b63679 100644 --- a/targets/esp32c3.ld +++ b/targets/esp32c3.ld @@ -70,6 +70,7 @@ SECTIONS .stack (NOLOAD) : { . = ALIGN(16); + _stack_bottom = .; . += _stack_size; _stack_top = .; } >DRAM diff --git a/targets/esp8266.ld b/targets/esp8266.ld index 9cbc42c143..8d35cc56e1 100644 --- a/targets/esp8266.ld +++ b/targets/esp8266.ld @@ -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 diff --git a/targets/maixbit.ld b/targets/maixbit.ld index 015a9beaf8..7f099329cc 100644 --- a/targets/maixbit.ld +++ b/targets/maixbit.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; diff --git a/targets/mimxrt1062-teensy40.ld b/targets/mimxrt1062-teensy40.ld index 70a717b72f..3148ee0dc1 100644 --- a/targets/mimxrt1062-teensy40.ld +++ b/targets/mimxrt1062-teensy40.ld @@ -59,6 +59,7 @@ SECTIONS .stack (NOLOAD) : { . = ALIGN(8); + _stack_bottom = .; . += _stack_size; _stack_top = .; diff --git a/targets/riscv.ld b/targets/riscv.ld index eecac6b476..cbc8dbe1c7 100644 --- a/targets/riscv.ld +++ b/targets/riscv.ld @@ -19,6 +19,7 @@ SECTIONS .stack (NOLOAD) : { . = ALIGN(16); + _stack_bottom = .; . += _stack_size; _stack_top = .; } >RAM