From b7b3dece056794d7b840b71c25ad5ca5846cf231 Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Fri, 17 Apr 2020 18:31:34 -0700 Subject: feat: added co-routines --- src/coro.c | 56 ++++++++++++++++++++++++++ src/coro_unix_x64.s | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 src/coro.c create mode 100644 src/coro_unix_x64.s diff --git a/src/coro.c b/src/coro.c new file mode 100644 index 0000000..4e651d3 --- /dev/null +++ b/src/coro.c @@ -0,0 +1,56 @@ +#include + +// ----------------------------------------------------------------------- +// Assembly routines. + +extern void _newcoro(coro *co, uintptr (*func)(coro*, uintptr), void *stk); +extern uintptr _coroyield(coro *co, uintptr arg); + +struct coro +{ + void* sp; + void* bp; + uintptr size; + void* user; +}; + +coro* +coro·New(uintptr stk, uintptr (*func)(coro*, uintptr)) +{ + if (!func) return nil; + + if (stk == 0) stk = 8192; + + byte *block = malloc(stk); + coro *co = (coro*)&block[stk - sizeof(coro)]; + co->bp = block; + co->size = stk; + + _newcoro(co, func, co); + return co; +} + +error +coro·Free(coro *co) +{ + enum + { + GOOD, + NIL, + EMPTY, + LOST, + }; + + if (!co) return NIL; + if (!co->bp) return LOST; + if (co->size == 0) return EMPTY; + + free(co->bp); + return GOOD; +} + +uintptr +coro·Yield(coro *c, uintptr arg) +{ + return _coroyield(c, arg); +} diff --git a/src/coro_unix_x64.s b/src/coro_unix_x64.s new file mode 100644 index 0000000..98710c2 --- /dev/null +++ b/src/coro_unix_x64.s @@ -0,0 +1,110 @@ +; Nicholas Noll 2019 +; +; =================================================================== +%use altreg + + bits 64 + default rel + global _newcoro + global _coroyield + +; =================================================================== + section .text +; ------------------------------------------------------------------- + +%assign L.coro -8 +%assign L.func -16 + +coroinit: + mov R7, [RBP + L.coro] + mov R6, R0 + call [RBP + L.func] + +rerun: + mov R7, [RBP + L.coro] + mov R6, R0 + call _coroyield + jmp rerun + +; ------------------------------------------------------------------- +; # Register Mapping +; +; R0 R1 R2 R3 R4 R5 R6 R7 R8 ... +; RAX RCX RDX RBX RSP RBP RSI RDI R8 ... +; +; # Sys V calling convention +; func(R7, R6, R2, R1, R8, R9, Z0-7): R0 +; +; # Stack layout of an in-flight coro +; ^coro +; ^func +; ^bp (base pointer of stack) +; ....... STACK ......... +; Saved Clobbers +; +; ### +; Stack layout of an init coro +; Stores the func pointer to init +; Stores the clobber registers. +; +; L.coro [8] +; L.func [7] +; coroinit [6] +; RBP [5] +; R3 [4] +; R12 [3] +; R13 [2] +; R14 [1] +; R15 [0] + +%define WORDS 8 +%define SAVES 9 + +; func sys.newCoro(co ^coro, fn func, bp ^stack) +_newcoro: + lea R0, [coroinit] + lea R1, [R2 - SAVES*WORDS] + + mov [R1 + 8*WORDS], R7 + mov [R1 + 7*WORDS], R6 + mov [R1 + 6*WORDS], R0 + mov [R1 + 5*WORDS], R2 + + xor R0, R0 + + ; Blank out the clobbers + mov [R1 + 4*WORDS], R0 ; R3 + mov [R1 + 3*WORDS], R0 ; R12 + mov [R1 + 2*WORDS], R0 ; R13 + mov [R1 + 1*WORDS], R0 ; R14 + mov [R1 + 0*WORDS], R0 ; R15 + + mov [R7], R1 + ret + +%macro pushclobs 0 + push RBP + push R3 + push R12 + push R13 + push R14 + push R15 +%endmacro + +%macro popclobs 0 + pop R15 + pop R14 + pop R13 + pop R12 + pop R3 + pop RBP +%endmacro + +; func coro.yield(co ^coro, ret uintptr) +_coroyield: + pushclobs + mov R0, R6 ; Move return value into return register. + xchg RSP, [R7] ; Atomically swap the stack pointer with the yieldee. + popclobs + + ret -- cgit v1.2.1