From b95a80ff5d9095ae0e532488fcd7d90c0164b215 Mon Sep 17 00:00:00 2001 From: Junior Rantila Date: Mon, 3 Feb 2025 03:52:33 +0100 Subject: [PATCH 1/3] Remove references to function arguments in naked functions Naked functions are not allowed to contain anything other than asm blocks. These void casts cause clang to error. --- coroutine.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/coroutine.c b/coroutine.c index 0ba96a2..fa88976 100644 --- a/coroutine.c +++ b/coroutine.c @@ -103,7 +103,6 @@ void __attribute__((naked)) coroutine_yield(void) void __attribute__((naked)) coroutine_sleep_read(int fd) { - (void) fd; // @arch asm( " pushq %rdi\n" @@ -121,7 +120,6 @@ void __attribute__((naked)) coroutine_sleep_read(int fd) void __attribute__((naked)) coroutine_sleep_write(int fd) { - (void) fd; // @arch asm( " pushq %rdi\n" @@ -140,7 +138,6 @@ void __attribute__((naked)) coroutine_sleep_write(int fd) void __attribute__((naked)) coroutine_restore_context(void *rsp) { // @arch - (void)rsp; asm( " movq %rdi, %rsp\n" " popq %r15\n" From bb90359ce5ad16a4385bc10b6feafc744bede16b Mon Sep 17 00:00:00 2001 From: Junior Rantila Date: Mon, 3 Feb 2025 03:54:43 +0100 Subject: [PATCH 2/3] Add support for x86_64-apple-darwin --- README.md | 1 + coroutine.c | 120 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 106 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 882c8b4..8471ca6 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Make sure you have all the corresponding compilers for the languages. ## Supported platforms - Linux x86_64 +- macOS x86_64 *More are planned in the future* diff --git a/coroutine.c b/coroutine.c index fa88976..a3bcad6 100644 --- a/coroutine.c +++ b/coroutine.c @@ -82,12 +82,49 @@ typedef enum { SM_WRITE, } Sleep_Mode; +static void* alloc_stack(void) +{ +#ifdef __APPLE__ // macOS does not support MAP_STACK and MAP_GROWSDOWN + char* addr = (char*)mmap(NULL, STACK_CAPACITY, PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); +#else + char* addr = (char*)mmap(NULL, STACK_CAPACITY, PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_STACK|MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0); +#endif + assert(addr != MAP_FAILED); + return addr; +} + +static void free_stack(void* addr) +{ + munmap(addr, STACK_CAPACITY); +} + +static void* setup_stack(void* stack, void (*f)(void*), void* arg) +{ + void **rsp = (void**)stack; + // @arch +#if __x86_64__ + *(--rsp) = coroutine_finish; + *(--rsp) = f; + *(--rsp) = arg; // push rdi + *(--rsp) = 0; // push rbx + *(--rsp) = 0; // push rbp + *(--rsp) = 0; // push r12 + *(--rsp) = 0; // push r13 + *(--rsp) = 0; // push r14 + *(--rsp) = 0; // push r15 +#else +#error unsupported cpu +#endif + return rsp; +} + // Linux x86_64 call convention // %rdi, %rsi, %rdx, %rcx, %r8, and %r9 void __attribute__((naked)) coroutine_yield(void) { // @arch +#if __x86_64__ && __linux__ asm( " pushq %rdi\n" " pushq %rbp\n" @@ -99,11 +136,27 @@ void __attribute__((naked)) coroutine_yield(void) " movq %rsp, %rdi\n" // rsp " movq $0, %rsi\n" // sm = SM_NONE " jmp coroutine_switch_context\n"); +#elif __x86_64__ && __APPLE__ + asm( + " pushq %rdi\n" + " pushq %rbp\n" + " pushq %rbx\n" + " pushq %r12\n" + " pushq %r13\n" + " pushq %r14\n" + " pushq %r15\n" + " movq %rsp, %rdi\n" // rsp + " movq $0, %rsi\n" // sm = SM_NONE + " jmp _coroutine_switch_context\n"); +#else +#error weird cpu/os combo +#endif } void __attribute__((naked)) coroutine_sleep_read(int fd) { // @arch +#if __x86_64__ && __linux__ asm( " pushq %rdi\n" " pushq %rbp\n" @@ -116,11 +169,28 @@ void __attribute__((naked)) coroutine_sleep_read(int fd) " movq %rsp, %rdi\n" // rsp " movq $1, %rsi\n" // sm = SM_READ " jmp coroutine_switch_context\n"); +#elif __x86_64__ && __APPLE__ + asm( + " pushq %rdi\n" + " pushq %rbp\n" + " pushq %rbx\n" + " pushq %r12\n" + " pushq %r13\n" + " pushq %r14\n" + " pushq %r15\n" + " movq %rdi, %rdx\n" // fd + " movq %rsp, %rdi\n" // rsp + " movq $1, %rsi\n" // sm = SM_READ + " jmp _coroutine_switch_context\n"); +#else +#error weird cpu/os combo +#endif } void __attribute__((naked)) coroutine_sleep_write(int fd) { // @arch +#if __x86_64__ && __linux__ asm( " pushq %rdi\n" " pushq %rbp\n" @@ -133,11 +203,39 @@ void __attribute__((naked)) coroutine_sleep_write(int fd) " movq %rsp, %rdi\n" // rsp " movq $2, %rsi\n" // sm = SM_WRITE " jmp coroutine_switch_context\n"); +#elif __x86_64__ && __APPLE__ + asm( + " pushq %rdi\n" + " pushq %rbp\n" + " pushq %rbx\n" + " pushq %r12\n" + " pushq %r13\n" + " pushq %r14\n" + " pushq %r15\n" + " movq %rdi, %rdx\n" // fd + " movq %rsp, %rdi\n" // rsp + " movq $2, %rsi\n" // sm = SM_WRITE + " jmp _coroutine_switch_context\n"); +#else +#error weird cpu/os combo +#endif } void __attribute__((naked)) coroutine_restore_context(void *rsp) { // @arch +#if __x86_64__ && __linux__ + asm( + " movq %rdi, %rsp\n" + " popq %r15\n" + " popq %r14\n" + " popq %r13\n" + " popq %r12\n" + " popq %rbx\n" + " popq %rbp\n" + " popq %rdi\n" + " ret\n"); +#elif __x86_64__ && __APPLE__ asm( " movq %rdi, %rsp\n" " popq %r15\n" @@ -148,6 +246,9 @@ void __attribute__((naked)) coroutine_restore_context(void *rsp) " popq %rbp\n" " popq %rdi\n" " ret\n"); +#else +#error unsupported cpu/os combo +#endif } void coroutine_switch_context(void *rsp, Sleep_Mode sm, int fd) @@ -207,7 +308,7 @@ void coroutine_finish(void) if (contexts.count == 0) return; if (active.items[current] == 0) { for (size_t i = 1; i < contexts.count; ++i) { - munmap(contexts.items[i].stack_base, STACK_CAPACITY); + free_stack(contexts.items[i].stack_base); } free(contexts.items); free(active.items); @@ -255,22 +356,11 @@ void coroutine_go(void (*f)(void*), void *arg) } else { da_append(&contexts, ((Context){0})); id = contexts.count-1; - contexts.items[id].stack_base = mmap(NULL, STACK_CAPACITY, PROT_WRITE|PROT_READ, MAP_PRIVATE|MAP_STACK|MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0); - assert(contexts.items[id].stack_base != MAP_FAILED); + contexts.items[id].stack_base = alloc_stack(); } - void **rsp = (void**)((char*)contexts.items[id].stack_base + STACK_CAPACITY); - // @arch - *(--rsp) = coroutine_finish; - *(--rsp) = f; - *(--rsp) = arg; // push rdi - *(--rsp) = 0; // push rbx - *(--rsp) = 0; // push rbp - *(--rsp) = 0; // push r12 - *(--rsp) = 0; // push r13 - *(--rsp) = 0; // push r14 - *(--rsp) = 0; // push r15 - contexts.items[id].rsp = rsp; + void *rsp = ((char*)contexts.items[id].stack_base + STACK_CAPACITY); + contexts.items[id].rsp = setup_stack(rsp, f, arg); da_append(&active, id); } From 2c9930a2076e32c4b41f910e2b23ec3ca039a76d Mon Sep 17 00:00:00 2001 From: Junior Rantila Date: Mon, 3 Feb 2025 07:06:12 +0100 Subject: [PATCH 3/3] Combine yield functions This patch takes advantage of the arguments to the yield functions being in the same order. --- coroutine.c | 87 +++++++++++------------------------------------------ 1 file changed, 18 insertions(+), 69 deletions(-) diff --git a/coroutine.c b/coroutine.c index a3bcad6..18585de 100644 --- a/coroutine.c +++ b/coroutine.c @@ -121,7 +121,7 @@ static void* setup_stack(void* stack, void (*f)(void*), void* arg) // Linux x86_64 call convention // %rdi, %rsi, %rdx, %rcx, %r8, and %r9 -void __attribute__((naked)) coroutine_yield(void) +static void __attribute__((naked)) yield(Sleep_Mode sm, int fd) { // @arch #if __x86_64__ && __linux__ @@ -133,8 +133,9 @@ void __attribute__((naked)) coroutine_yield(void) " pushq %r13\n" " pushq %r14\n" " pushq %r15\n" - " movq %rsp, %rdi\n" // rsp - " movq $0, %rsi\n" // sm = SM_NONE + " movq %rsp, %rdx\n" // rsp + // " movq %rsi, %rsi\n" // fd + // " movq %rdi, %rdi\n" // sm " jmp coroutine_switch_context\n"); #elif __x86_64__ && __APPLE__ asm( @@ -145,80 +146,28 @@ void __attribute__((naked)) coroutine_yield(void) " pushq %r13\n" " pushq %r14\n" " pushq %r15\n" - " movq %rsp, %rdi\n" // rsp - " movq $0, %rsi\n" // sm = SM_NONE + " movq %rsp, %rdx\n" // rsp + // " movq %rsi, %rsi\n" // fd + // " movq %rdi, %rdi\n" // sm " jmp _coroutine_switch_context\n"); #else -#error weird cpu/os combo +#error unsupported cpu/os combo #endif } -void __attribute__((naked)) coroutine_sleep_read(int fd) +void coroutine_yield(void) { - // @arch -#if __x86_64__ && __linux__ - asm( - " pushq %rdi\n" - " pushq %rbp\n" - " pushq %rbx\n" - " pushq %r12\n" - " pushq %r13\n" - " pushq %r14\n" - " pushq %r15\n" - " movq %rdi, %rdx\n" // fd - " movq %rsp, %rdi\n" // rsp - " movq $1, %rsi\n" // sm = SM_READ - " jmp coroutine_switch_context\n"); -#elif __x86_64__ && __APPLE__ - asm( - " pushq %rdi\n" - " pushq %rbp\n" - " pushq %rbx\n" - " pushq %r12\n" - " pushq %r13\n" - " pushq %r14\n" - " pushq %r15\n" - " movq %rdi, %rdx\n" // fd - " movq %rsp, %rdi\n" // rsp - " movq $1, %rsi\n" // sm = SM_READ - " jmp _coroutine_switch_context\n"); -#else -#error weird cpu/os combo -#endif + yield(SM_NONE, -1); } -void __attribute__((naked)) coroutine_sleep_write(int fd) +void coroutine_sleep_read(int fd) { - // @arch -#if __x86_64__ && __linux__ - asm( - " pushq %rdi\n" - " pushq %rbp\n" - " pushq %rbx\n" - " pushq %r12\n" - " pushq %r13\n" - " pushq %r14\n" - " pushq %r15\n" - " movq %rdi, %rdx\n" // fd - " movq %rsp, %rdi\n" // rsp - " movq $2, %rsi\n" // sm = SM_WRITE - " jmp coroutine_switch_context\n"); -#elif __x86_64__ && __APPLE__ - asm( - " pushq %rdi\n" - " pushq %rbp\n" - " pushq %rbx\n" - " pushq %r12\n" - " pushq %r13\n" - " pushq %r14\n" - " pushq %r15\n" - " movq %rdi, %rdx\n" // fd - " movq %rsp, %rdi\n" // rsp - " movq $2, %rsi\n" // sm = SM_WRITE - " jmp _coroutine_switch_context\n"); -#else -#error weird cpu/os combo -#endif + yield(SM_READ, fd); +} + +void coroutine_sleep_write(int fd) +{ + yield(SM_WRITE, fd); } void __attribute__((naked)) coroutine_restore_context(void *rsp) @@ -251,7 +200,7 @@ void __attribute__((naked)) coroutine_restore_context(void *rsp) #endif } -void coroutine_switch_context(void *rsp, Sleep_Mode sm, int fd) +void coroutine_switch_context(Sleep_Mode sm, int fd, void* rsp) { contexts.items[active.items[current]].rsp = rsp;