Skip to content

Instantly share code, notes, and snippets.

@nelix
Forked from simonask/gist:143815
Created July 9, 2009 19:28
Show Gist options
  • Save nelix/143927 to your computer and use it in GitHub Desktop.
Save nelix/143927 to your computer and use it in GitHub Desktop.
#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