summaryrefslogtreecommitdiffstats
path: root/execute.c
diff options
context:
space:
mode:
authorStephen Dolan <mu@netsoc.tcd.ie>2012-09-18 17:44:43 +0100
committerStephen Dolan <mu@netsoc.tcd.ie>2012-09-18 17:44:43 +0100
commita4eea165bbab6d13f89b59707e835d58b7014a66 (patch)
treeb99ee5dde8540f8dbe5de3d87b99e04ac4dd2673 /execute.c
parent25cbab056b1f73e96b636c88779a92400d92dc15 (diff)
Move everything around - delete old Haskell code, clean up build.
Diffstat (limited to 'execute.c')
-rw-r--r--execute.c502
1 files changed, 502 insertions, 0 deletions
diff --git a/execute.c b/execute.c
new file mode 100644
index 00000000..6b0948c5
--- /dev/null
+++ b/execute.c
@@ -0,0 +1,502 @@
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "opcode.h"
+#include "bytecode.h"
+#include "compile.h"
+
+#include "forkable_stack.h"
+#include "frame_layout.h"
+
+#include "jv.h"
+
+
+typedef struct {
+ jv value;
+ int pathidx;
+} stackval;
+
+
+jv* pathbuf;
+int pathsize; // number of allocated elements
+
+int path_push(stackval sv, jv val) {
+ int pos = sv.pathidx;
+ assert(pos <= pathsize);
+ assert(pos >= 0);
+ if (pos == pathsize) {
+ int oldpathsize = pathsize;
+ pathsize = oldpathsize ? oldpathsize * 2 : 100;
+ pathbuf = realloc(pathbuf, sizeof(pathbuf[0]) * pathsize);
+ for (int i=oldpathsize; i<pathsize; i++) {
+ pathbuf[i] = jv_invalid();
+ }
+ }
+ jv_free(pathbuf[pos]);
+ pathbuf[pos] = val;
+ return pos + 1;
+}
+
+stackval stackval_replace(stackval value, jv newjs) {
+ jv_free(value.value);
+ stackval s = {newjs, value.pathidx};
+ return s;
+}
+
+
+// Probably all uses of this function are bugs
+stackval stackval_root(jv v) {
+ stackval s = {v, 0};
+ return s;
+}
+
+struct forkable_stack data_stk;
+typedef struct {
+ FORKABLE_STACK_HEADER;
+ stackval sv;
+} data_stk_elem;
+
+void stack_push(stackval val) {
+ assert(jv_is_valid(val.value));
+ data_stk_elem* s = forkable_stack_push(&data_stk, sizeof(data_stk_elem));
+ s->sv = val;
+}
+
+stackval stack_pop() {
+ data_stk_elem* s = forkable_stack_peek(&data_stk);
+ stackval sv = s->sv;
+ if (!forkable_stack_pop_will_free(&data_stk)) {
+ sv.value = jv_copy(sv.value);
+ }
+ forkable_stack_pop(&data_stk);
+ assert(jv_is_valid(sv.value));
+ return sv;
+}
+
+struct forkable_stack frame_stk;
+
+
+struct forkpoint {
+ FORKABLE_STACK_HEADER;
+ struct forkable_stack_state saved_data_stack;
+ struct forkable_stack_state saved_call_stack;
+};
+
+struct forkable_stack fork_stk;
+
+void stack_save(){
+ struct forkpoint* fork = forkable_stack_push(&fork_stk, sizeof(struct forkpoint));
+ forkable_stack_save(&data_stk, &fork->saved_data_stack);
+ forkable_stack_save(&frame_stk, &fork->saved_call_stack);
+}
+
+void stack_switch() {
+ struct forkpoint* fork = forkable_stack_peek(&fork_stk);
+ forkable_stack_switch(&data_stk, &fork->saved_data_stack);
+ forkable_stack_switch(&frame_stk, &fork->saved_call_stack);
+}
+
+int stack_restore(){
+ while (!forkable_stack_empty(&data_stk) &&
+ forkable_stack_pop_will_free(&data_stk)) {
+ jv_free(stack_pop().value);
+ }
+ while (!forkable_stack_empty(&frame_stk) &&
+ forkable_stack_pop_will_free(&frame_stk)) {
+ frame_pop(&frame_stk);
+ }
+ if (forkable_stack_empty(&fork_stk)) {
+ return 0;
+ } else {
+ struct forkpoint* fork = forkable_stack_peek(&fork_stk);
+ forkable_stack_restore(&data_stk, &fork->saved_data_stack);
+ forkable_stack_restore(&frame_stk, &fork->saved_call_stack);
+ forkable_stack_pop(&fork_stk);
+ return 1;
+ }
+}
+
+static struct closure make_closure(struct forkable_stack* stk, frame_ptr fr, uint16_t* pc) {
+ uint16_t level = *pc++;
+ uint16_t idx = *pc++;
+ fr = frame_get_level(stk, fr, level);
+ if (idx & ARG_NEWCLOSURE) {
+ int subfn_idx = idx & ~ARG_NEWCLOSURE;
+ assert(subfn_idx < frame_self(fr)->bc->nsubfunctions);
+ struct closure cl = {frame_self(fr)->bc->subfunctions[subfn_idx],
+ forkable_stack_to_idx(stk, fr)};
+ return cl;
+ } else {
+ return *frame_closure_arg(fr, idx);
+ }
+}
+
+void print_error(jv value) {
+ assert(!jv_is_valid(value));
+ jv msg = jv_invalid_get_msg(value);
+ if (jv_get_kind(msg) == JV_KIND_STRING) {
+ fprintf(stderr, "jq: error: %s\n", jv_string_value(msg));
+ }
+ jv_free(msg);
+}
+#define ON_BACKTRACK(op) ((op)+NUM_OPCODES)
+
+jv jq_next() {
+ jv cfunc_input[MAX_CFUNCTION_ARGS];
+ jv cfunc_output[MAX_CFUNCTION_ARGS];
+
+ assert(!forkable_stack_empty(&frame_stk));
+ uint16_t* pc = *frame_current_retaddr(&frame_stk);
+ frame_pop(&frame_stk);
+
+ assert(!forkable_stack_empty(&frame_stk));
+
+ int backtracking = 0;
+ while (1) {
+ uint16_t opcode = *pc;
+
+#if JQ_DEBUG
+ dump_operation(frame_current_bytecode(&frame_stk), pc);
+ printf("\t");
+ const struct opcode_description* opdesc = opcode_describe(opcode);
+ data_stk_elem* param;
+ for (int i=0; i<opdesc->stack_in; i++) {
+ if (i == 0) {
+ param = forkable_stack_peek(&data_stk);
+ } else {
+ printf(" | ");
+ param = forkable_stack_peek_next(&data_stk, param);
+ }
+ if (!param) break;
+ jv_dump(jv_copy(param->sv.value), 0);
+ printf("<%d>", jv_get_refcnt(param->sv.value));
+ }
+
+ if (backtracking) printf("\t<backtracking>");
+
+ printf("\n");
+#endif
+ if (backtracking) {
+ opcode = ON_BACKTRACK(opcode);
+ backtracking = 0;
+ }
+ pc++;
+
+ switch (opcode) {
+ default: assert(0 && "invalid instruction");
+
+ case LOADK: {
+ jv v = jv_array_get(jv_copy(frame_current_bytecode(&frame_stk)->constants), *pc++);
+ assert(jv_is_valid(v));
+ stack_push(stackval_replace(stack_pop(), v));
+ break;
+ }
+
+ case DUP: {
+ stackval v = stack_pop();
+ stackval v2 = v;
+ v2.value = jv_copy(v.value);
+ stack_push(v);
+ stack_push(v2);
+ break;
+ }
+
+ case SWAP: {
+ stackval a = stack_pop();
+ stackval b = stack_pop();
+ stack_push(a);
+ stack_push(b);
+ break;
+ }
+
+ case POP: {
+ jv_free(stack_pop().value);
+ break;
+ }
+
+ case APPEND: {
+ // FIXME paths
+ jv v = stack_pop().value;
+ jv array = stack_pop().value;
+ array = jv_array_append(array, v);
+ stack_push(stackval_root(array));
+ break;
+ }
+
+ case INSERT: {
+ stackval stktop = stack_pop();
+ jv v = stack_pop().value;
+ jv k = stack_pop().value;
+ stackval objv = stack_pop();
+ assert(jv_get_kind(k) == JV_KIND_STRING);
+ assert(jv_get_kind(objv.value) == JV_KIND_OBJECT);
+ objv.value = jv_object_set(objv.value, k, v);
+ stack_push(objv);
+ stack_push(stktop);
+ break;
+ }
+
+ // FIXME: loadv/storev may do too much copying/freeing
+ case LOADV: {
+ uint16_t level = *pc++;
+ uint16_t v = *pc++;
+ frame_ptr fp = frame_get_level(&frame_stk, frame_current(&frame_stk), level);
+ jv* var = frame_local_var(fp, v);
+ #if JQ_DEBUG
+ printf("V%d = ", v);
+ jv_dump(jv_copy(*var), 0);
+ printf("\n");
+ #endif
+ stack_push(stackval_replace(stack_pop(), jv_copy(*var)));
+ break;
+ }
+
+ case STOREV: {
+ uint16_t level = *pc++;
+ uint16_t v = *pc++;
+ frame_ptr fp = frame_get_level(&frame_stk, frame_current(&frame_stk), level);
+ jv* var = frame_local_var(fp, v);
+ stackval val = stack_pop();
+ #if JQ_DEBUG
+ printf("V%d = ", v);
+ jv_dump(jv_copy(val.value), 0);
+ printf("\n");
+ #endif
+ jv_free(*var);
+ *var = val.value;
+ break;
+ }
+
+ case ASSIGN: {
+ stackval replacement = stack_pop();
+ stackval path_end = stack_pop();
+ stackval path_start = stack_pop();
+ jv_free(path_end.value);
+ jv_free(path_start.value);
+
+ uint16_t level = *pc++;
+ uint16_t v = *pc++;
+ frame_ptr fp = frame_get_level(&frame_stk, frame_current(&frame_stk), level);
+ jv* var = frame_local_var(fp, v);
+ jv result = jv_insert(*var, replacement.value, pathbuf + path_start.pathidx, path_end.pathidx - path_start.pathidx);
+ if (jv_is_valid(result)) {
+ *var = result;
+ } else {
+ print_error(result);
+ *var = jv_null();
+ }
+ break;
+ }
+
+ case INDEX: {
+ stackval t = stack_pop();
+ jv k = stack_pop().value;
+ int pathidx = path_push(t, jv_copy(k));
+ jv v = jv_lookup(t.value, k);
+ if (jv_is_valid(v)) {
+ stackval sv;
+ sv.value = v;
+ sv.pathidx = pathidx;
+ stack_push(sv);
+ } else {
+ print_error(v);
+ goto do_backtrack;
+ }
+ break;
+ }
+
+
+ case JUMP: {
+ uint16_t offset = *pc++;
+ pc += offset;
+ break;
+ }
+
+ case JUMP_F: {
+ uint16_t offset = *pc++;
+ stackval t = stack_pop();
+ jv_kind kind = jv_get_kind(t.value);
+ if (kind == JV_KIND_FALSE || kind == JV_KIND_NULL) {
+ pc += offset;
+ }
+ stack_push(t); // FIXME do this better
+ break;
+ }
+
+ case EACH:
+ stack_push(stackval_root(jv_number(-1)));
+ // fallthrough
+ case ON_BACKTRACK(EACH): {
+ int idx = jv_number_value(stack_pop().value);
+ stackval container = stack_pop();
+
+ int keep_going;
+ jv key, value;
+ if (jv_get_kind(container.value) == JV_KIND_ARRAY) {
+ if (opcode == EACH) idx = 0;
+ else idx = idx + 1;
+ keep_going = idx < jv_array_length(jv_copy(container.value));
+ if (keep_going) {
+ key = jv_number(idx);
+ value = jv_array_get(jv_copy(container.value), idx);
+ }
+ } else if (jv_get_kind(container.value) == JV_KIND_OBJECT) {
+ if (opcode == EACH) idx = jv_object_iter(container.value);
+ else idx = jv_object_iter_next(container.value, idx);
+ keep_going = jv_object_iter_valid(container.value, idx);
+ if (keep_going) {
+ key = jv_object_iter_key(container.value, idx);
+ value = jv_object_iter_value(container.value, idx);
+ }
+ } else {
+ assert(opcode == EACH);
+ print_error(jv_invalid_with_msg(jv_string_fmt("Cannot iterate over %s",
+ jv_kind_name(jv_get_kind(container.value)))));
+ keep_going = 0;
+ }
+
+ if (!keep_going) {
+ jv_free(container.value);
+ goto do_backtrack;
+ } else {
+ stack_save();
+ stack_push(container);
+ stack_push(stackval_root(jv_number(idx)));
+ frame_push_backtrack(&frame_stk, pc - 1);
+ stack_switch();
+
+ stackval sv = {value,
+ path_push(container, key)};
+ stack_push(sv);
+ }
+ break;
+ }
+
+ do_backtrack:
+ case BACKTRACK: {
+ if (!stack_restore()) {
+ return jv_invalid();
+ }
+ pc = *frame_current_retaddr(&frame_stk);
+ frame_pop(&frame_stk);
+ backtracking = 1;
+ break;
+ }
+
+ case FORK: {
+ stack_save();
+ frame_push_backtrack(&frame_stk, pc - 1);
+ stack_switch();
+ pc++; // skip offset this time
+ break;
+ }
+
+ case ON_BACKTRACK(FORK): {
+ uint16_t offset = *pc++;
+ pc += offset;
+ break;
+ }
+
+ case YIELD: {
+ jv value = stack_pop().value;
+ frame_push_backtrack(&frame_stk, pc);
+ return value;
+ }
+
+ case CALL_BUILTIN_1_1: {
+ assert(*pc == 1); // no closure args allowed
+ pc++; // skip nclosures
+ pc++; // skip level
+ stackval top = stack_pop();
+ cfunc_input[0] = top.value;
+ struct cfunction* func = &frame_current_bytecode(&frame_stk)->globals->cfunctions[*pc++];
+ func->fptr(cfunc_input, cfunc_output);
+ top.value = cfunc_output[0];
+ if (jv_is_valid(top.value)) {
+ stack_push(top);
+ } else {
+ print_error(top.value);
+ goto do_backtrack;
+ }
+ break;
+ }
+
+ case CALL_BUILTIN_3_1: {
+ assert(*pc == 1); // no closure args allowed
+ pc++; // skip nclosures
+ pc++; // skip level
+ stackval top = stack_pop();
+ jv a = stack_pop().value;
+ jv b = stack_pop().value;
+ cfunc_input[0] = top.value;
+ cfunc_input[1] = a;
+ cfunc_input[2] = b;
+ struct cfunction* func = &frame_current_bytecode(&frame_stk)->globals->cfunctions[*pc++];
+ func->fptr(cfunc_input, cfunc_output);
+ top.value = cfunc_output[0];
+ if (jv_is_valid(top.value)) {
+ stack_push(top);
+ } else {
+ print_error(top.value);
+ goto do_backtrack;
+ }
+ break;
+ }
+
+ case CALL_1_1: {
+ uint16_t nclosures = *pc++;
+ frame_ptr new_frame = frame_push(&frame_stk,
+ make_closure(&frame_stk, frame_current(&frame_stk), pc),
+ pc + nclosures * 2);
+ pc += 2;
+ frame_ptr old_frame = forkable_stack_peek_next(&frame_stk, new_frame);
+ assert(nclosures - 1 == frame_self(new_frame)->bc->nclosures);
+ for (int i=0; i<nclosures-1; i++) {
+ *frame_closure_arg(new_frame, i) = make_closure(&frame_stk, old_frame, pc);
+ pc += 2;
+ }
+
+ pc = frame_current_bytecode(&frame_stk)->code;
+ break;
+ }
+
+ case RET: {
+ pc = *frame_current_retaddr(&frame_stk);
+ frame_pop(&frame_stk);
+ break;
+ }
+ }
+ }
+}
+
+
+void jq_init(struct bytecode* bc, jv input) {
+ forkable_stack_init(&data_stk, sizeof(stackval) * 1000); // FIXME: lower this number, see if it breaks
+ forkable_stack_init(&frame_stk, 10240); // FIXME: lower this number, see if it breaks
+ forkable_stack_init(&fork_stk, 10240); // FIXME: lower this number, see if it breaks
+
+ stack_push(stackval_root(input));
+ struct closure top = {bc, -1};
+ frame_push(&frame_stk, top, 0);
+ frame_push_backtrack(&frame_stk, bc->code);
+}
+
+void jq_teardown() {
+ while (stack_restore()) {}
+
+ assert(forkable_stack_empty(&fork_stk));
+ assert(forkable_stack_empty(&data_stk));
+ assert(forkable_stack_empty(&frame_stk));
+ forkable_stack_free(&fork_stk);
+ forkable_stack_free(&data_stk);
+ forkable_stack_free(&frame_stk);
+
+ for (int i=0; i<pathsize; i++) {
+ jv_free(pathbuf[i]);
+ }
+ free(pathbuf);
+ pathbuf = 0;
+ pathsize = 0;
+}