-
-
Save nelix/143927 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdbool.h> | |
#include <stdlib.h> | |
#include <stdarg.h> | |
#include <string.h> | |
#define NOINLINE __attribute__((noinline)) | |
const size_t CONTINUATION_STACK_SIZE = 8192; | |
typedef unsigned char byte; | |
typedef struct Continuation { | |
struct { | |
void* rbx; | |
void* rbp; | |
void* rsp; | |
void* r12; | |
void* r13; | |
void* r14; | |
void* r15; | |
void* rip; | |
} reg; | |
byte* stack_hi; | |
byte* stack_lo; | |
bool running; | |
struct Continuation* return_to; | |
void* return_val; | |
void** args; | |
size_t num_args; | |
} Continuation; | |
NOINLINE void resume_cc(Continuation* cc) | |
{ | |
cc->running = true; | |
__asm__ __volatile__( | |
// restore registers | |
"movq %0, %%rdi\n" | |
"movq %1, %%rsp\n" | |
"movq %2, %%rbp\n" | |
// indicate to function being resumed that it was resumed | |
"movq $1, %%rax\n" | |
// resume! | |
"movq %3, %%r8\n" | |
"jmpq *%%r8\n" | |
: | |
: "r"(cc), "m"(cc->reg.rsp), "m"(cc->reg.rbp), "m"(cc->reg.rip) | |
: "%rax", "%rdi", "%rsi", "%rsp", "%r8" | |
); | |
} | |
NOINLINE bool save_cc(Continuation* cc) | |
{ | |
__asm__ __volatile__( | |
// rsp is rsp, since the stack size of this function is 0 | |
"movq %%rsp, %0\n" | |
// rbp stored on the stack | |
"movq (%%rsp), %%rax\n" | |
"movq %%rax, %1\n" | |
// read instruction pointer for calling function from the stack | |
"movq %%rbp, %%r8\n" | |
"movq 8(%%r8), %%rax\n" | |
"movq %%rax, %2\n" | |
// TODO: save other registers? | |
: "=m"(cc->reg.rsp), "=m"(cc->reg.rbp), "=m"(cc->reg.rip) | |
: | |
: "%rax" | |
); | |
return false; | |
} | |
NOINLINE void* exit_cc() { | |
register Continuation* cc; | |
__asm__ __volatile__( | |
"movq 16(%%rsp), %0\n" | |
"movq %%rax, %%rsi\n" | |
"movq %0, %%rdi\n" | |
"call _return_cc" | |
: "=r"(cc), "=m"(cc->return_val) | |
: | |
: "%rax", "%rdi" | |
); | |
} | |
void reset_stack_cc(Continuation* cc) { | |
(*(void**)(cc->stack_hi-0x8)) = cc; // hidden self-reference for exit_cc | |
(*(void**)(cc->stack_hi-0x10)) = NULL; // rbp for exit_cc | |
(*(void**)(cc->stack_hi-0x18)) = exit_cc; // natural return location | |
printf("reset cc stack: cc: 0x%llx, rsp: 0x%llx, rip: 0x%llx\n", cc, cc->stack_hi-0x16, exit_cc); | |
cc->reg.rsp = cc->stack_hi - 0x18; // for alignment | |
cc->reg.rbp = cc->reg.rsp; | |
// cc->rsp = cc->rbp = cc->stack_base; | |
} | |
void init_cc(Continuation* cc, void* func, size_t num_args) | |
{ | |
cc->reg.rip = func; | |
cc->stack_lo = (byte*)valloc(CONTINUATION_STACK_SIZE); | |
cc->stack_hi = cc->stack_lo + CONTINUATION_STACK_SIZE; | |
memset(cc->stack_lo, 0xcd, CONTINUATION_STACK_SIZE); | |
reset_stack_cc(cc); | |
cc->running = false; | |
cc->num_args = num_args; | |
cc->args = malloc(sizeof(void*)*num_args); | |
} | |
void* call_cc(Continuation* cc, Continuation* return_to, ...) { | |
cc->return_to = return_to; | |
if (save_cc(return_to)) { | |
// came back | |
return cc->return_val; | |
} | |
if (cc->running) { | |
printf("call_cc: resuming...\n"); | |
resume_cc(cc); | |
} | |
printf("call_cc: starting...\n"); | |
// reset stack | |
reset_stack_cc(cc); | |
va_list ap; | |
va_start(ap, return_to); | |
size_t i; | |
for (i = 0; i < cc->num_args; ++i) { | |
cc->args[i] = va_arg(ap, void*); | |
} | |
va_end(ap); | |
resume_cc(cc); | |
return NULL; // suppress warning | |
} | |
void yield_cc(Continuation* cc, void* data) { | |
if (save_cc(cc)) { | |
return; | |
} | |
cc->return_val = data; | |
resume_cc(cc->return_to); | |
} | |
void return_cc(Continuation* cc, void* data) { | |
cc->running = false; | |
cc->return_val = data; | |
resume_cc(cc->return_to); | |
} | |
void* continuation(Continuation* cc) { | |
int a = (int)cc->args[0]; | |
int b = (int)cc->args[1]; | |
int c = a + b; | |
printf("hello from continuation: %d\n", c); | |
yield_cc(cc, &c); | |
printf("continuation was resumed... %d\n", c); | |
printf("stack var c is at 0x%llx\n", &c); | |
return &c; | |
} | |
int main (int argc, char const *argv[]) | |
{ | |
Continuation return_here; | |
Continuation sub; | |
//printf("sub: 0x%llx\n", &sub); | |
init_cc(&sub, continuation, 2); | |
printf("before continuation\n"); | |
call_cc(&sub, &return_here, (void*)5, (void*)4); | |
printf("continuation yield\n"); | |
call_cc(&sub, &return_here); | |
printf("after resumed"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment