riscv: switch to tasks-based scheduler
This is only supported for RV32 at the moment. RV64 can be added at a later time.
Этот коммит содержится в:
родитель
5d8f25a622
коммит
878b62bbe8
5 изменённых файлов: 139 добавлений и 1 удалений
|
@ -1,4 +1,4 @@
|
|||
// +build scheduler.tasks,arm,!cortexm,!avr,!xtensa
|
||||
// +build scheduler.tasks,arm,!cortexm,!avr,!xtensa,!tinygo.riscv
|
||||
|
||||
package task
|
||||
|
||||
|
|
69
src/internal/task/task_stack_tinygoriscv.S
Обычный файл
69
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
|
66
src/internal/task/task_stack_tinygoriscv.go
Обычный файл
66
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
|
||||
}
|
|
@ -16,6 +16,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"
|
||||
],
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
"inherits": ["riscv"],
|
||||
"llvm-target": "riscv32--none",
|
||||
"build-tags": ["tinygo.riscv32"],
|
||||
"scheduler": "tasks",
|
||||
"default-stack-size": 2048,
|
||||
"cflags": [
|
||||
"-march=rv32imac",
|
||||
"-mabi=ilp32"
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче