aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorNicholas Noll <nbnoll@eml.cc>2020-04-17 18:31:34 -0700
committerNicholas Noll <nbnoll@eml.cc>2020-04-17 18:31:34 -0700
commitb7b3dece056794d7b840b71c25ad5ca5846cf231 (patch)
tree2871c90ccbce6c861c1835d4a79fe4672b4e1229 /src
parentdca72268ae6bcbd411aa8660604451226f76b4eb (diff)
feat: added co-routines
Diffstat (limited to 'src')
-rw-r--r--src/coro.c56
-rw-r--r--src/coro_unix_x64.s110
2 files changed, 166 insertions, 0 deletions
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 <u.h>
+
+// -----------------------------------------------------------------------
+// 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