mirror of
https://github.com/janet-lang/janet
synced 2025-03-29 19:46:56 +00:00
Update thoughts.md, and add dst_call and dst_call suspend.
dst_call_suspend migt be redundant (suspension of GC is easy enough).
This commit is contained in:
parent
57f88ba28a
commit
eee453d2c0
@ -825,7 +825,7 @@ recur:
|
||||
dstc_cerror(c, ast, "macro expansion recursed too deeply");
|
||||
return dstc_cslot(dst_wrap_nil());
|
||||
} else {
|
||||
status = dst_call(fn, &x, dst_tuple_length(tup) - 1, tup + 1);
|
||||
status = dst_call_suspend(fn, &x, dst_tuple_length(tup) - 1, tup + 1);
|
||||
if (status) {
|
||||
dstc_cerror(c, ast, "error in macro expansion");
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
|
||||
/* VM State */
|
||||
DstFiber *dst_vm_fiber = NULL;
|
||||
int dst_vm_stackn = 0;
|
||||
|
||||
/* Helper to ensure proper fiber is activated after returning */
|
||||
static int dst_update_fiber() {
|
||||
@ -63,6 +64,13 @@ static int dst_continue(Dst *returnreg) {
|
||||
* Values stored here should be used immediately */
|
||||
Dst retreg;
|
||||
|
||||
/* Increment the stackn */
|
||||
if (dst_vm_stackn >= DST_RECURSION_GUARD) {
|
||||
*returnreg = dst_cstringv("C stack recursed too deeply");
|
||||
return 1;
|
||||
}
|
||||
dst_vm_stackn++;
|
||||
|
||||
/* Use computed gotos for GCC and clang, otherwise use switch */
|
||||
#ifdef __GNUC__
|
||||
#define VM_START() {vm_next();
|
||||
@ -632,6 +640,7 @@ static void *op_lookup[255] = {
|
||||
if (NULL == nextfiber) {
|
||||
frame->pc = pc;
|
||||
*returnreg = retreg;
|
||||
dst_vm_stackn--;
|
||||
return 0;
|
||||
}
|
||||
status = nextfiber->status;
|
||||
@ -700,6 +709,7 @@ static void *op_lookup[255] = {
|
||||
dst_fiber_popframe(dst_vm_fiber);
|
||||
if (dst_update_fiber()) {
|
||||
*returnreg = retreg;
|
||||
dst_vm_stackn--;
|
||||
return 0;
|
||||
}
|
||||
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
|
||||
@ -713,6 +723,7 @@ static void *op_lookup[255] = {
|
||||
dst_fiber_popframe(dst_vm_fiber);
|
||||
if (dst_update_fiber()) {
|
||||
*returnreg = retreg;
|
||||
dst_vm_stackn--;
|
||||
return 0;
|
||||
}
|
||||
/* Fall through to normal return */
|
||||
@ -724,6 +735,7 @@ static void *op_lookup[255] = {
|
||||
dst_fiber_popframe(dst_vm_fiber);
|
||||
if (dst_update_fiber()) {
|
||||
*returnreg = retreg;
|
||||
dst_vm_stackn--;
|
||||
return 0;
|
||||
}
|
||||
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
|
||||
@ -740,6 +752,7 @@ static void *op_lookup[255] = {
|
||||
dst_vm_fiber->status = DST_FIBER_ERROR;
|
||||
if (dst_update_fiber()) {
|
||||
*returnreg = retreg;
|
||||
dst_vm_stackn--;
|
||||
return 1;
|
||||
}
|
||||
stack = dst_vm_fiber->data + dst_vm_fiber->frame;
|
||||
@ -788,14 +801,8 @@ int dst_run(Dst callee, Dst *returnreg) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Run from inside a cfunction. This should only be used for
|
||||
* short functions as it prevents re-entering the current fiber
|
||||
* and suspend garbage collection. */
|
||||
int dst_call(Dst callee, Dst *returnreg, int32_t argn, const Dst *argv) {
|
||||
int ret;
|
||||
int lock;
|
||||
DstFiber *oldfiber = dst_vm_fiber;
|
||||
lock = dst_vm_gc_suspend++;
|
||||
/* Helper for calling a function */
|
||||
static int dst_call_help(Dst callee, Dst *returnreg, int32_t argn, const Dst* argv) {
|
||||
dst_vm_fiber = dst_fiber(64);
|
||||
dst_fiber_pushn(dst_vm_fiber, argv, argn);
|
||||
if (dst_checktype(callee, DST_CFUNCTION)) {
|
||||
@ -805,19 +812,44 @@ int dst_call(Dst callee, Dst *returnreg, int32_t argn, const Dst *argv) {
|
||||
args.n = argn;
|
||||
args.v = dst_vm_fiber->data + dst_vm_fiber->frame;
|
||||
args.ret = returnreg;
|
||||
ret = dst_unwrap_cfunction(callee)(args);
|
||||
return dst_unwrap_cfunction(callee)(args);
|
||||
} else if (dst_checktype(callee, DST_FUNCTION)) {
|
||||
dst_fiber_funcframe(dst_vm_fiber, dst_unwrap_function(callee));
|
||||
ret = dst_continue(returnreg);
|
||||
return dst_continue(returnreg);
|
||||
} else {
|
||||
*returnreg = dst_cstringv("expected function");
|
||||
ret = 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Run from inside a cfunction. This should only be used for
|
||||
* short functions as it prevents re-entering the current fiber
|
||||
* and suspend garbage collection. Currently used in the compiler
|
||||
* for macro evaluation. */
|
||||
int dst_call_suspend(Dst callee, Dst *returnreg, int32_t argn, const Dst *argv) {
|
||||
int ret;
|
||||
int lock;
|
||||
DstFiber *oldfiber = dst_vm_fiber;
|
||||
lock = dst_vm_gc_suspend++;
|
||||
ret = dst_call_help(callee, returnreg, argn, argv);
|
||||
dst_vm_fiber = oldfiber;
|
||||
dst_vm_gc_suspend = lock;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Run from inside a cfunction. This will not suspend GC, so
|
||||
* the caller must be sure that no Dst*'s are left dangling in the calling function.
|
||||
* Such values can be locked with dst_gcroot and unlocked with dst_gcunroot. */
|
||||
int dst_call(Dst callee, Dst *returnreg, int32_t argn, const Dst *argv) {
|
||||
int ret;
|
||||
DstFiber *oldfiber = dst_vm_fiber;
|
||||
dst_gcroot(dst_wrap_fiber(oldfiber));
|
||||
ret = dst_call_help(callee, returnreg, argn, argv);
|
||||
dst_gcunroot(dst_wrap_fiber(oldfiber));
|
||||
dst_vm_fiber = oldfiber;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Setup functions */
|
||||
int dst_init() {
|
||||
/* Garbage collection */
|
||||
|
@ -193,6 +193,7 @@ int dst_init(void);
|
||||
void dst_deinit(void);
|
||||
int dst_run(Dst callee, Dst *returnreg);
|
||||
int dst_call(Dst callee, Dst *returnreg, int32_t argn, const Dst *argv);
|
||||
int dst_call_suspend(Dst callee, Dst *returnreg, int32_t argn, const Dst *argv);
|
||||
|
||||
/* C Function helpers */
|
||||
#define dst_throw(a, e) (*((a).ret) = dst_cstringv(e), 1)
|
||||
|
@ -37,6 +37,9 @@ extern const char *dst_type_names[16];
|
||||
/* The VM state. Rather than a struct that is passed
|
||||
* around, the vm state is global for simplicity. */
|
||||
|
||||
/* How many VM stacks have been entered */
|
||||
extern int dst_vm_stackn;
|
||||
|
||||
/* Garbage collection */
|
||||
extern void *dst_vm_blocks;
|
||||
extern uint32_t dst_vm_gc_interval;
|
||||
|
@ -1,25 +1,39 @@
|
||||
A collection of thoughts and todo tasks for the project.
|
||||
# Thoughts
|
||||
|
||||
- Track depth of C stack in vm. While the VM is stackless, C functions can create
|
||||
new VM stack frames as needed. We should provide a configurable hard limit on
|
||||
stack that will simply error out immediately. This would prevent a stack overflow.
|
||||
A collection of thoughts and todo tasks for the project.
|
||||
|
||||
- Allow entrances into the VM to track the size of the stack when they entered, and return
|
||||
when the stack is less that. This would make calling dst functions from C feasible (
|
||||
The programmer would still have to ensure no GC violations).
|
||||
|
||||
Instead, we can just keep allocating new Fibers when we call a dst function from C. A pool
|
||||
of fibers would mostly mitigate the overhead of allocation. (going with this).
|
||||
|
||||
We can now call into dst from C without suspending the entire garbage collector. A separate
|
||||
function does exactly that.
|
||||
|
||||
- Make unknown instruction in vm trap and put current fiber in a new state, 'debug'.
|
||||
This could allow implementation of a debugger. Since opcodes are encoded in one byte,
|
||||
we can use the most significant bit (0x80) to set breakpoints in code, assuming all valid
|
||||
opcodes are in the range [0, 127]. The debugger could simply set the MSB of the opcode for each
|
||||
instruction that was marked. This would allow debugging with 0 overhead.
|
||||
|
||||
|
||||
We could also add a debugger instruction, much like JavaScripts debugger; statement very easily.
|
||||
|
||||
Lastly, to make continuation after a breakpoint easier, stopping on the first instruction
|
||||
could be optional. This could be as simple as selecting the first 7 bits of the instructions
|
||||
instead of the usual 8 for the very instruction executed after entering the vm loop.
|
||||
|
||||
What exactly should happen on a trapped instruction is another issue. It would be preferable
|
||||
for the runtime to be able to handle a trap in dst, but allow nested fibers to not capture
|
||||
debugging signals unless needed.
|
||||
|
||||
Fiber's currently propagate all states to their direct parent, but perhaps each fiber
|
||||
could have a mask for different signals - error, debug, return. So a single fiber could
|
||||
say capture returns, error, but not debug. Possibly like try - catch in other languages, where
|
||||
we only catch certain kinds of errors. The default fiber would be to only mask debug, so a single fiber
|
||||
could wrap an entire running application for debugging.
|
||||
|
||||
- Remove the concept of 'Ast node'. While providing fine-grained source mapping is
|
||||
is reasonably useful, it complicates the implementation of macros and other source
|
||||
transforming operations. Instead, we can key collection types (which have the unique
|
||||
@ -34,7 +48,17 @@ A collection of thoughts and todo tasks for the project.
|
||||
which potentially duplicates a fair amount of data. Macros would be easier to write without
|
||||
needing to either unwrap ast values or sacrifice all source mapping.
|
||||
|
||||
- Serialization and deserialization of all datatypes. This would allow
|
||||
- Keep track of source file information in the compiler. The compiler could simply accept
|
||||
and extra argument, sourcefile, which woud append the appropriate metadata to all function
|
||||
definitions generated with this one form.
|
||||
|
||||
- Serialization and deserialization of all datatypes. This would allow loading of bytecode
|
||||
without needing the compiler present. However, loading C functions is currently problamatic.
|
||||
C functions could perhaps be wrapped in data structures that contain some meta information
|
||||
about them, say home module and types. This could also allow some automated type checking for
|
||||
C functions rather than writing it manually. Some slight overhead could perhaps be compensated
|
||||
for by adding optional ommission of typechecking later for C functions if it can be statically
|
||||
shown the types are sound.
|
||||
|
||||
- Better support for custom user datatypes. Tables and structs do work well for creating
|
||||
custom 'objects' and records, but lack ability to differentiate between object style
|
||||
@ -64,5 +88,9 @@ A collection of thoughts and todo tasks for the project.
|
||||
by symbol. The current compiler does not do full SSA optimization, so named values
|
||||
are always accessible in the stack when in scope.
|
||||
|
||||
- Create a pool for fibers. Whlie the general purpose allocator and GC can be made more efficient,
|
||||
Fiber's can be well pooled because the allocated stack is large and can be reused. The stack
|
||||
size parameter on dst_fiber could simply become the minimum memory allocated for the stack. (Do
|
||||
a linear search throught the pool).
|
||||
|
||||
- Implement multi-methods.
|
Loading…
x
Reference in New Issue
Block a user