Skip to content

Instantly share code, notes, and snippets.

@n1xx1
Last active December 12, 2015 16:57
Show Gist options
  • Save n1xx1/696e8c66cec305159abd to your computer and use it in GitHub Desktop.
Save n1xx1/696e8c66cec305159abd to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
/** Useless macro boilerplate, I will eventually make it less hacky, I think **/
#define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__)))
#define EVAL5(...) __VA_ARGS__
#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__
// if called returns a macro that does nothing if called.
#define NOTHING(...) DO_NOTHING
#define DO_NOTHING(...)
#define EMPTY()
#define DEFER(id) id EMPTY()
#define OBSTRUCT(...) __VA_ARGS__ DEFER(EMPTY)()
#define EXPAND(...) __VA_ARGS__
// get the first of the list. returns _END if the list is empty
#define FIRST(...) FIRST_IMPL(__VA_ARGS__, _END)
#define FIRST_IMPL(_0, ...) _0
#define LOOPY_THING_LOOP(loopedMacro, loopedMacroArgs, counter, firstArg, ...) \
OBSTRUCT ( loopedMacro(counter, firstArg, EXPAND(loopedMacroArgs)) ) \
OBSTRUCT ( LOOPY_THING_LOOP_HELPER( CAT(LOOPY_THING_LOOP_CHECK, FIRST(__VA_ARGS__)) ) ) \
() ( loopedMacro, loopedMacroArgs, counter+1, __VA_ARGS__)
#define LOOPY_THING_LOOP_CHECK_END _0, NOTHING
#define LOOPY_THING_LOOP_HELPER_IMPL(_0, _1, ...) _1
#define LOOPY_THING_LOOP_HELPER(...) LOOPY_THING_LOOP_HELPER_IMPL(__VA_ARGS__, LOOPY_THING_LOOP_INDIRECT,)
#define LOOPY_THING_LOOP_INDIRECT() LOOPY_THING_LOOP
#define LOOPY_THING(loopedMacro, loopedMacroArgs, ...) LOOPY_THING_LOOP(loopedMacro, loopedMacroArgs, 0, __VA_ARGS__, _END)
/** End **/
/*asm volatile ("lea (%%rip),%0" : "=r"((dest).restoreIp));*/
// This macro will save all the registers and instruction
// pointer to the context, and set it's restored value to 0.
#define CONTEXT_SAVE(dest) \
do { \
(dest).restored = 0;\
EVAL(LOOPY_THING(REGISTER_SAVE, (dest), r8, r9, r10, r11, r12, r13, r14, r15, rbp, rsi, rdi, rsp)) \
(dest).restoreIp = &&done; \
done:; \
} while(0)
#define REGISTER_SAVE(counter, arg, dest) asm volatile ("movq %%" #arg ",%0" : "=m"((dest).restoreRegs[counter]));
// This macro will load all the registrers from the context
// and set it's restored value to 1. Then it will jump to
// the saved instruction pointer.
#define CONTEXT_LOAD(from) \
do { \
(from).restored = 1;\
EVAL(LOOPY_THING(REGISTER_LOAD, (from), r8, r9, r10, r11, r12, r13, r14, r15, rbp, rsi, rdi, rsp)) \
asm volatile ("pushq %0" : : "m"((from).restoreIp)); asm volatile ("ret"); \
} while(0)
#define REGISTER_LOAD(counter, arg, from) asm volatile ("movq %0,%%" #arg : : "m"((from).restoreRegs[counter]));
// This struct stores all the regs, the instruction pointer
// and a flag to test if we were just restored or we are in
// the normal control flow.
typedef struct {
void* restoreRegs[16];
void* restoreIp;
int restored;
} context_t;
// This is the coroutine type. Work in progress still.
typedef struct {
unsigned stackSize;
void* stackPointer;
context_t calledContext;
context_t calleeContext;
void* returned;
} coroutine_t;
// Called by `callee`, creates the coroutine stack and
// starts it by calling func. Doesn't support
// parameters but the C++ version will.
void coro_start(coroutine_t* coro, void (*func)(coroutine_t*));
// Called by `callee`, destroy the coro after it retuned
// null.
void coro_destroy(coroutine_t* coro);
// Called by `callee`, resumes the coro.
void coro_resume(coroutine_t* coro);
// Called by `called`, set the returned value and
// restores goes back to the normal control flow.
void coro_return(coroutine_t* coro, void* returned);
void coro_start(coroutine_t* coro, void (*func)(coroutine_t*)) {
coro->stackSize = 8 * 1024; // 4kb
coro->stackPointer = malloc(coro->stackSize);
CONTEXT_SAVE(coro->calleeContext);
if(!coro->calleeContext.restored) {
asm("movq %0,%%rsp" : : "r"(coro->stackPointer + coro->stackSize - 16));
func(coro);
coro_return(coro, 0);
}
}
void coro_destroy(coroutine_t* coro) {
if(coro->stackPointer) {
free(coro->stackPointer);
}
}
void coro_resume(coroutine_t* coro) {
CONTEXT_SAVE(coro->calleeContext);
if(coro->calleeContext.restored) return;
CONTEXT_LOAD(coro->calledContext);
}
void coro_return(coroutine_t* coro, void* returned) {
coro->returned = returned;
CONTEXT_SAVE(coro->calledContext);
if(coro->calledContext.restored) return;
CONTEXT_LOAD(coro->calleeContext);
}
void testCoro(coroutine_t* self) {
int a;
int b[100];
for(a = 0; a < 100; a++) {
b[a] = a % 12;
if(b[a] == 0) coro_return(self, &a);
}
}
int main() {
coroutine_t coro;
coro_start(&coro, testCoro);
while(coro.returned) {
printf("Coro said: %d\n", *((int*)coro.returned));
coro_resume(&coro);
}
coro_destroy(&coro);
printf("Coro ended.\n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment