aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2020-03-17 20:45:30 +0100
committerAyke van Laethem <[email protected]>2021-09-28 00:21:12 +0200
commit7b77437208a74e40026fec55d2fec595166aa814 (patch)
tree567e65a7eb9e99a3b467ff5a06c826e05d091202
parent4ddb10751abaf2f2dbbd8632b46377ec564d5b36 (diff)
downloadtinygo-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.go2
-rw-r--r--src/internal/task/task_stack_tinygoriscv.S69
-rw-r--r--src/internal/task/task_stack_tinygoriscv.go66
-rw-r--r--targets/riscv.json1
-rw-r--r--targets/riscv32.json2
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",