-
-
Save nelix/143924 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> | |
#define NOINLINE __attribute__((noinline)) | |
typedef unsigned char byte; | |
typedef struct Continuation { | |
void* rip; | |
void* rsp; | |
void* rbp; | |
byte* stack; | |
struct Continuation* return_to; | |
void** args; | |
size_t num_args; | |
} Continuation; | |
NOINLINE void resume(Continuation* cc) | |
{ | |
__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->rsp), "m"(cc->rbp), "m"(cc->rip) | |
: "%rax", "%rdi", "%rsi", "%rsp", "%r8" | |
); | |
} | |
NOINLINE bool save(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" | |
: "=m"(cc->rsp), "=m"(cc->rbp), "=m"(cc->rip) | |
: | |
: "%rax" | |
); | |
return false; | |
} | |
void init_continuation(Continuation* cc, void* func, size_t num_args) | |
{ | |
cc->rip = func; | |
cc->stack = (byte*)malloc(1024); | |
cc->rbp = cc->rsp = cc->stack; | |
cc->num_args = num_args; | |
cc->args = malloc(sizeof(void*)*num_args); | |
} | |
void start_continuation(Continuation* cc, Continuation* return_to, ...) { | |
cc->return_to = return_to; | |
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); | |
} | |
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); | |
if (!save(cc)) | |
{ | |
resume(cc->return_to); | |
} | |
else | |
{ | |
printf("continuation was resumed... %d\n", c); | |
printf("stack var c is at 0x%llx\n", &c); | |
} | |
resume(cc->return_to); | |
} | |
int main (int argc, char const *argv[]) | |
{ | |
Continuation return_here; | |
Continuation sub; | |
printf("return_here is at 0x%llx\n", &return_here); | |
printf("before\n"); | |
if (!save(&return_here)) { | |
printf("returning directly\n"); | |
init_continuation(&sub, continuation, 2); | |
start_continuation(&sub, &return_here, (void*)5, (void*)4); | |
printf("after resume -- should never be reached!\n"); | |
} else { | |
printf("returned from continuation!\n"); | |
if (!save(&return_here)) { | |
resume(&sub); | |
} else { | |
printf("returned from continuation for the second time\n"); | |
} | |
} | |
printf("exiting\n"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment