|
x-engine-c v0.2.13
The C engine for x-lang
|
First-class continuation primitives (call/cc via stack copying). More...
#include "x-prim.h"#include "x-eval.h"#include "x-env.h"#include "x-heap.h"#include <setjmp.h>#include <string.h>#include "x-type/list.h"#include "x-type/prim.h"#include "x-type/ptr.h"#include "x-type/procedure.h"#include "x-type/symbol.h"Data Structures | |
| struct | x_callcc_cont_t |
Macros | |
| #define | X_CALLCC_NO_ASAN |
Functions | |
| static X_CALLCC_NO_ASAN void | x_callcc_copy (char *dst, const char *src, size_t n) |
| X_CALLCC_NO_ASAN void | x_callcc_init (void) |
| static X_CALLCC_NO_ASAN void | x_callcc_sp (void **pp_sp) |
| static void | x_callcc_restore (x_callcc_cont_t *cont) |
| static x_obj_t * | x_prim_cc_invoke (x_obj_t *p_base, x_obj_t *p_args) |
| static X_CALLCC_NO_ASAN x_obj_t * | x_prim_callcc (x_obj_t *p_base, x_obj_t *p_args) |
| x_obj_t * | x_prim_callcc_register (x_obj_t *p_base, x_obj_t *p_args) |
Variables | |
| static void(*volatile | x_callcc_sp_fn )(void **) = x_callcc_sp |
| static void(*volatile | x_callcc_restore_fn )(x_callcc_cont_t *) |
Continuation Data | |
Stack-copying continuations: capture the C call stack segment from a known base (set in main) to the current frame. Invocation restores the stack and longjmps back to the capture point. Interpreter state (env, save_stack, error_handler) is saved as a GC-visible x-lang list in the continuation procedure's closure env. | |
| static void * | g_stack_base = NULL |
First-class continuation primitives (call/cc via stack copying).
| #define X_CALLCC_NO_ASAN |
|
static |
Copy a captured stack segment. Under ASan this must be a plain byte loop in an exempt function – see the truce note above.
| X_CALLCC_NO_ASAN void x_callcc_init | ( | void | ) |
Establish the stack base address for continuation capture.
Must be called once at interpreter startup, from a frame close to the bottom of the C call stack (typically main). The address of a local variable is recorded as the upper bound of all future stack captures.
|
static |
Restore a captured continuation by replacing the current stack.
Recursively grows the C stack (via a 2 KiB pad per frame) until the stack pointer is below the lower bound of the captured segment, then copies the saved bytes back and longjmps to the capture point.
| cont | Continuation whose stack segment and jmp_buf to restore. |
|
static |
Store an address strictly below the caller's entire stack frame.
The capture in x_prim_callcc() must include EVERY slot of its own frame: locals the compiler spills below the address taken (gcc puts p_base/cont there) would otherwise be restored as garbage after longjmp – the crash class that only clang's register allocation hid. A callee's local is below the caller's whole frame by construction, so its address is a safe lower bound.
|
static |
Capture the current continuation and pass it to a procedure. x-lang: (call/cc proc)
Allocates a continuation struct with a copy of the C stack segment from the current frame up to g_stack_base, saves interpreter state in a GC-visible list, and constructs a variadic closure k that invokes cc-invoke when called. Then applies proc to k.
When k is later invoked, the saved stack is restored via x_callcc_restore() and control returns here through longjmp, yielding the value passed to k.
| p_base | Interpreter base context. |
| p_args | Unevaluated argument list: (call/cc proc). |
proc, or the value passed to the continuation. Register continuation primitives.
Binds: cc-invoke, call/cc.
| p_base | Interpreter base context. |
| p_args | Unused. |
p_base. Invoke a captured continuation, restoring its stack and interpreter state. x-lang: (cc-invoke ptr state args-list)
This is the internal entry point called by the closure k that call/cc hands to its procedure. args-list is the variadic argument collector: () for zero arguments, (val) for one.
| p_base | Interpreter base context. |
| p_args | Unevaluated argument list: (cc-invoke ptr state args-list). |
|
static |
Stack base pointer, set once at startup via x_callcc_init().
|
static |
Volatile call path for the recursion: sibling-call optimization would reuse the frame, so the pad would never advance and the descent would spin forever. A call through a volatile pointer cannot be folded.
|
static |
Volatile call path to x_callcc_sp(): inlining would hoist the anchor back into the caller's frame and re-create the under-capture; a call through a volatile pointer cannot be inlined or sibling-call folded.