1
0
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:
Calvin Rose 2018-03-04 18:56:13 -05:00
parent 57f88ba28a
commit eee453d2c0
5 changed files with 82 additions and 18 deletions

View File

@ -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");
}

View File

@ -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 */

View File

@ -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)

View File

@ -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;

View File

@ -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.