diff options
author | Ayke van Laethem <[email protected]> | 2020-03-17 20:45:30 +0100 |
---|---|---|
committer | Ayke van Laethem <[email protected]> | 2021-09-28 00:21:12 +0200 |
commit | 7b77437208a74e40026fec55d2fec595166aa814 (patch) | |
tree | 567e65a7eb9e99a3b467ff5a06c826e05d091202 | |
parent | 4ddb10751abaf2f2dbbd8632b46377ec564d5b36 (diff) | |
download | tinygo-7b77437208a74e40026fec55d2fec595166aa814.tar.gz tinygo-7b77437208a74e40026fec55d2fec595166aa814.zip |
riscv: switch to tasks-based scheduler
This is only supported for RV32 at the moment. RV64 can be added at a
later time.
-rw-r--r-- | src/internal/task/task_stack_arm.go | 2 | ||||
-rw-r--r-- | src/internal/task/task_stack_tinygoriscv.S | 69 | ||||
-rw-r--r-- | src/internal/task/task_stack_tinygoriscv.go | 66 | ||||
-rw-r--r-- | targets/riscv.json | 1 | ||||
-rw-r--r-- | targets/riscv32.json | 2 |
5 files changed, 139 insertions, 1 deletions
diff --git a/src/internal/task/task_stack_arm.go b/src/internal/task/task_stack_arm.go index acd63ece8..5934f0284 100644 --- a/src/internal/task/task_stack_arm.go +++ b/src/internal/task/task_stack_arm.go @@ -1,4 +1,4 @@ -// +build scheduler.tasks,arm,!cortexm,!avr,!xtensa +// +build scheduler.tasks,arm,!cortexm,!avr,!xtensa,!tinygo.riscv package task diff --git a/src/internal/task/task_stack_tinygoriscv.S b/src/internal/task/task_stack_tinygoriscv.S new file mode 100644 index 000000000..0ac6f1231 --- /dev/null +++ b/src/internal/task/task_stack_tinygoriscv.S @@ -0,0 +1,69 @@ +.section .text.tinygo_startTask +.global tinygo_startTask +.type tinygo_startTask, %function +tinygo_startTask: + // Small assembly stub for starting a goroutine. This is already run on the + // new stack, with the callee-saved registers already loaded. + // Most importantly, s0 contains the pc of the to-be-started function and s1 + // contains the only argument it is given. Multiple arguments are packed + // into one by storing them in a new allocation. + + // Set the first argument of the goroutine start wrapper, which contains all + // the arguments. + mv a0, s1 + + // Branch to the "goroutine start" function. Use jalr to write the return + // address to ra so we'll return here after the goroutine exits. + jalr s0 + + // After return, exit this goroutine. This is a tail call. + tail tinygo_pause + +.section .text.tinygo_swapTask +.global tinygo_swapTask +.type tinygo_swapTask, %function +tinygo_swapTask: + // This function gets the following parameters: + // a0 = newStack uintptr + // a1 = oldStack *uintptr + + // Push all callee-saved registers. + addi sp, sp, -52 + sw ra, 48(sp) + sw s11, 44(sp) + sw s10, 40(sp) + sw s9, 36(sp) + sw s8, 32(sp) + sw s7, 28(sp) + sw s6, 24(sp) + sw s5, 20(sp) + sw s4, 16(sp) + sw s3, 12(sp) + sw s2, 8(sp) + sw s1, 4(sp) + sw s0, (sp) + + // Save the current stack pointer in oldStack. + sw sp, 0(a1) + + // Switch to the new stack pointer. + mv sp, a0 + + // Pop all saved registers from this new stack. + lw ra, 48(sp) + lw s11, 44(sp) + lw s10, 40(sp) + lw s9, 36(sp) + lw s8, 32(sp) + lw s7, 28(sp) + lw s6, 24(sp) + lw s5, 20(sp) + lw s4, 16(sp) + lw s3, 12(sp) + lw s2, 8(sp) + lw s1, 4(sp) + lw s0, (sp) + addi sp, sp, 52 + + // Return into the task. + ret diff --git a/src/internal/task/task_stack_tinygoriscv.go b/src/internal/task/task_stack_tinygoriscv.go new file mode 100644 index 000000000..6f20eebf1 --- /dev/null +++ b/src/internal/task/task_stack_tinygoriscv.go @@ -0,0 +1,66 @@ +// +build scheduler.tasks,tinygo.riscv + +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. +type calleeSavedRegs struct { + s0 uintptr // x8 (fp) + s1 uintptr // x9 + s2 uintptr // x18 + s3 uintptr // x19 + s4 uintptr // x20 + s5 uintptr // x21 + s6 uintptr // x22 + s7 uintptr // x23 + s8 uintptr // x24 + s9 uintptr // x25 + s10 uintptr // x26 + s11 uintptr // x27 + + pc uintptr +} + +// archInit runs architecture-specific setup for the goroutine startup. +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)) + + // Initialize the registers. + // These will be popped off of the stack on the first resume of the goroutine. + + // Start the function at tinygo_startTask (defined in src/internal/task/task_stack_riscv.S). + // This assembly code calls a function (passed in s0) with a single argument + // (passed in s1). After the function returns, it calls Pause(). + r.pc = uintptr(unsafe.Pointer(&startTask)) + + // Pass the function to call in s0. + // 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.s0 = fn + + // Pass the pointer to the arguments struct in s1. + r.s1 = 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 +} diff --git a/targets/riscv.json b/targets/riscv.json index 1ccf123a4..f4374776d 100644 --- a/targets/riscv.json +++ b/targets/riscv.json @@ -17,6 +17,7 @@ ], "extra-files": [ "src/device/riscv/start.S", + "src/internal/task/task_stack_tinygoriscv.S", "src/runtime/gc_riscv.S", "src/device/riscv/handleinterrupt.S" ], diff --git a/targets/riscv32.json b/targets/riscv32.json index e0cb609da..506ba6822 100644 --- a/targets/riscv32.json +++ b/targets/riscv32.json @@ -2,6 +2,8 @@ "inherits": ["riscv"], "llvm-target": "riscv32--none", "build-tags": ["tinygo.riscv32"], + "scheduler": "tasks", + "default-stack-size": 2048, "cflags": [ "--target=riscv32--none", "-march=rv32imac", |