Last active
December 12, 2015 16:57
-
-
Save n1xx1/696e8c66cec305159abd 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 <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