aboutsummaryrefslogtreecommitdiff
path: root/src/lower.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lower.c')
-rw-r--r--src/lower.c40
1 files changed, 40 insertions, 0 deletions
diff --git a/src/lower.c b/src/lower.c
index 0a6c07d..7c5fdbd 100644
--- a/src/lower.c
+++ b/src/lower.c
@@ -6,6 +6,27 @@
#include <posthaste/scope.h>
#include <posthaste/utils.h>
+/* The bytecode is similar in construction to Lua's, as in there's two arrays, one
+ * for global values and one for local values. The local value array is referred
+ * to as the stack. In theory each function/procedure could get its own stack,
+ * but in this case there's just a single global stack, and each call moves a
+ * stack pointer up to signify entering a new function. Procedure/function
+ * formal parameters are the first things on the stack.
+ *
+ * All instructions are of the three-value kinds, i.e. an instruction has an
+ * opcode, an output location and two input locations, not all of which have to
+ * be used. There's a special null_loc() to signify an unused location.
+ * Locations are effectively just indexes into either the global or local
+ * array. I've also added a separate CONST instruction that pushes a constant
+ * value to a specified output location.
+ *
+ * Individual instructions are very wide, which decreases cache performance and
+ * makes this particular implementation fairly slow to interpret, but the extra
+ * width means it's a little bit easier to work due to not having to
+ * compress/decompress anything. In particular, I tried to make JIT compilation
+ * as easy as possible, at expense of interpretation speed.
+ */
+
/* globals are kind of ugly and could/should be made into parameters (or
* potentially even members of some all-encompassing state) but this works for
* this simple implementation, implementing the change would mosty just require
@@ -102,8 +123,16 @@ static void lower_add(struct fn *f, struct ast *n)
struct ast *l = add_l(n);
struct ast *r = add_r(n);
+ /* since variable definitions can't appear in expressions, there's no
+ * danger of some variable claiming a stack location above this point and
+ * it being overwritten by accident. If variable definitions were
+ * expressions, we would probably have to do an extra pass before
+ * lowering that pre-assigns locations to all variables */
f->sp += 1; lower(f, l);
f->sp += 1; lower(f, r);
+ /* technically we could reuse the stack location for l, but I found that
+ * reading the generated instruction sequences was a bit easier with
+ * separate output and input stack locations */
f->sp -= 2;
output_insn(f, ADD, n->l, l->l, r->l, 0);
@@ -194,6 +223,8 @@ static void lower_id(struct fn *f, struct ast *n)
struct ast *exists = file_scope_find(n->scope, n->id);
assert(exists);
+ /* using ast nodes/scope lookup as convenient way to store variable ->
+ * location mappings */
n->l = exists->l;
}
@@ -253,6 +284,8 @@ static void lower_proc_call(struct fn *f, struct ast *c)
{
size_t count = 0;
foreach_node(n, proc_call_args(c)) {
+ /* place each argument in its own stack location to await being
+ * used */
f->sp += 1; lower(f, n); count += 1;
}
@@ -429,6 +462,7 @@ static void lower_until(struct fn *f, struct ast *n)
static void patch_branch(struct fn *f, size_t branch, size_t off)
{
+ /* the patch destination must always be a label, required by the JIT */
assert((vect_at(struct insn, f->insns, off)).k == LABEL);
struct insn i = vect_at(struct insn, f->insns, branch);
@@ -510,6 +544,10 @@ static void lower_builtin_call(struct fn *f, struct ast *n)
static void lower(struct fn *f, struct ast *n)
{
+ /* technically it would be possible for some lowering to use stack space
+ * without calling lower(), probably causing difficult to debug runtime weirdness.
+ * Not currently an issue, but something that would need to be taken
+ * into account if this instruction set was extended at some point. */
if (f->max_sp < f->sp)
f->max_sp = f->sp;
@@ -552,6 +590,8 @@ static void lower(struct fn *f, struct ast *n)
case AST_DOT: break;
}
+ /* each ast node is assigned some location, regardless of if it actually
+ * needs one as a sanity check */
assert(loc_as_int(n->l) > 0);
}