diff options
Diffstat (limited to 'src/lower.c')
-rw-r--r-- | src/lower.c | 40 |
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); } |