diff options
Diffstat (limited to 'src/interpret.c')
-rw-r--r-- | src/interpret.c | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/src/interpret.c b/src/interpret.c new file mode 100644 index 0000000..580303e --- /dev/null +++ b/src/interpret.c @@ -0,0 +1,407 @@ +#include <math.h> +#include <stdio.h> + +#include <posthaste/execute.h> +#include <posthaste/lower.h> +#include <posthaste/utils.h> +#include <posthaste/date.h> +#include <posthaste/vec.h> + +static int64_t load(struct loc l, size_t sp, struct vec *stack, + struct vec *globals) +{ + /* extra runtime branches is another reason this bytecode instruction + * set is slow to interpret. To get rid of this if (), one + * option would be to add LOAD/STORE_GLOBAL instructions that move data + * between the global array and the stack, but this would make the JIT + * slower */ + if (l.l) + return vect_at(int64_t, *stack, l.o + sp); + + return vect_at(int64_t, *globals, l.o); +} + +static void store(struct loc l, int64_t v, size_t sp, struct vec *stack, + struct vec *globals) +{ + if (l.l) { + vect_at(int64_t, *stack, l.o + sp) = v; + return; + } + + vect_at(int64_t, *globals, l.o) = v; +} + +static int64_t interpret_func(struct fn *f, struct vec *args, size_t sp, + struct vec *stack, struct vec *globals) +{ + /* move args to formal locations */ + size_t formal = 0; + foreach_vec(ai, *args) { + int64_t a = vect_at(int64_t, *args, ai); + vect_at(int64_t, *stack, sp + formal) = a; + formal += 1; + } + + vec_reset(args); + + /* load/store from location, selecting between global array and stack. Assumes + * parameter names, generally frowned upon. */ +#define get(l) \ + load(l, sp, stack, globals) + +#define put(l, v) \ + store(l, v, sp, stack, globals) + + /* use computed gotos to avoid extra overhead of loops/switch statements. + * Usually I'm not a huge fan of massive single functions and from a + * stylistic viewpoint maybe prefer breaking each instruction out into + * its own procedure, but this at least saves a bit of typing */ +#define LINK(x) [x] = &&CASE_##x + static void *labels[] = { + LINK(LABEL), + LINK(CALL), + LINK(MOVE), + LINK(ADD), + LINK(SUB), + LINK(MUL), + LINK(DIV), + LINK(ARG), + LINK(RETVAL), + LINK(PRINT_STRING), + LINK(PRINT_INT), + LINK(PRINT_BOOL), + LINK(PRINT_DATE), + LINK(PRINT_NEWLINE), + LINK(PRINT_SPACE), + LINK(DATE_ADD), + LINK(DATE_SUB), + LINK(DATE_DIFF), + LINK(STORE_DAY), + LINK(STORE_MONTH), + LINK(STORE_YEAR), + LINK(LOAD_DAY), + LINK(LOAD_MONTH), + LINK(LOAD_YEAR), + LINK(LOAD_WEEKDAY), + LINK(LOAD_WEEKNUM), + LINK(TODAY), + LINK(RET), + LINK(STOP), + LINK(CONST), + LINK(EQ), + LINK(LT), + LINK(NEG), + LINK(B), + LINK(BZ), + LINK(J), + }; +#undef LINK + +#define JUMP() i = vect_at(struct insn, insns, pc); goto *labels[i.k]; +#define DISPATCH() pc++; JUMP(); + +#define CASE(x) CASE_##x + + size_t pc = 0; + int64_t retval = 0; + struct vec insns = f->insns; + struct insn i = {0}; + /* jump to first instruction */ + JUMP(); + + CASE(STOP) : {return 0;} + CASE(RET) : {return get(i.i0);} + + CASE(LABEL) : {DISPATCH();} + CASE(RETVAL) : {put(i.o, retval); DISPATCH();} + + CASE(J) : {pc = i.v; JUMP();} + + CASE(B) : { + int64_t i0 = get(i.i0); + if (i0) { + pc = i.v; + JUMP(); + } + + DISPATCH(); + } + + CASE(BZ) : { + int64_t i0 = get(i.i0); + if (!i0) { + pc = i.v; + JUMP(); + } + + DISPATCH(); + } + + CASE(ARG) : { + int64_t a = get(i.i0); + vect_append(int64_t, *args, &a); + DISPATCH(); + } + + CASE(CALL) : { + struct fn *cf = find_fn(i.v); + assert(cf); + + retval = interpret_func(cf, args, sp + f->max_sp + 1, stack, + globals); + DISPATCH(); + } + + CASE(MOVE) : { + int64_t i0 = get(i.i0); + put(i.o, i0); + DISPATCH(); + } + + + CASE(ADD) : { + int64_t i0 = get(i.i0); + int64_t i1 = get(i.i1); + int64_t o = i0 + i1; + put(i.o, o); + DISPATCH(); + } + + CASE(SUB) : { + int64_t i0 = get(i.i0); + int64_t i1 = get(i.i1); + int64_t o = i0 - i1; + put(i.o, o); + DISPATCH(); + } + + CASE(MUL) : { + int64_t i0 = get(i.i0); + int64_t i1 = get(i.i1); + int64_t o = i0 * i1; + put(i.o, o); + DISPATCH(); + } + + CASE(DIV) : { + int64_t i0 = get(i.i0); + int64_t i1 = get(i.i1); + int64_t o = i0 / i1; + put(i.o, o); + DISPATCH(); + } + + CASE(CONST) : { + put(i.o, i.v); + DISPATCH(); + } + + CASE(PRINT_DATE) : { + int64_t i0 = get(i.i0); + char str[11] = {0}; + date_to_string(str, i0); + printf("%s", str); + DISPATCH(); + } + + CASE(PRINT_INT) : { + int64_t i0 = get(i.i0); + printf("%lli", (long long)i0); + DISPATCH(); + } + + CASE(PRINT_BOOL) : { + int64_t i0 = get(i.i0); + printf("%s", i0 ? "true" : "false"); + DISPATCH(); + } + + CASE(PRINT_STRING) : { + int64_t i0 = get(i.i0); + printf("%s", (const char *)i0); + DISPATCH(); + } + + CASE(PRINT_NEWLINE) : { + putchar('\n'); + DISPATCH(); + } + + CASE(PRINT_SPACE) : { + putchar(' '); + DISPATCH(); + } + + CASE(LOAD_DAY) : { + int64_t i0 = get(i.i0); + unsigned day = 0; + date_split((ph_date_t)i0, NULL, NULL, &day); + put(i.o, (int64_t)day); + DISPATCH(); + } + + CASE(LOAD_MONTH) : { + int64_t i0 = get(i.i0); + unsigned month = 0; + date_split((ph_date_t)i0, NULL, &month, NULL); + put(i.o, (int64_t)month); + DISPATCH(); + } + + CASE(LOAD_YEAR) : { + int64_t i0 = get(i.i0); + unsigned year = 0; + date_split((ph_date_t)i0, &year, NULL, NULL); + put(i.o, (int64_t)year); + DISPATCH(); + } + + CASE(LOAD_WEEKDAY) : { + int64_t i0 = get(i.i0); + struct tm time = tm_from_date((ph_date_t)i0); + put(i.o, (int64_t)time.tm_wday); + DISPATCH(); + } + + CASE(LOAD_WEEKNUM) : { + int64_t i0 = get(i.i0); + struct tm time = tm_from_date((ph_date_t)i0); + put(i.o, time.tm_yday / 7); + DISPATCH(); + } + + CASE(STORE_DAY) : { + int64_t i0 = get(i.i0); + int64_t i1 = get(i.i1); + + unsigned year = 0; + unsigned month = 0; + date_split((ph_date_t)i0, &year, &month, NULL); + ph_date_t date = date_from_numbers(year, month, i1); + put(i.o, (int64_t)date); + DISPATCH(); + } + + CASE(STORE_MONTH) : { + int64_t i0 = get(i.i0); + int64_t i1 = get(i.i1); + + unsigned year = 0; + unsigned day = 0; + date_split((ph_date_t)i0, &year, NULL, &day); + ph_date_t date = date_from_numbers(year, i1, day); + put(i.o, (int64_t)date); + DISPATCH(); + } + + CASE(STORE_YEAR) : { + int64_t i0 = get(i.i0); + int64_t i1 = get(i.i1); + + unsigned month = 0; + unsigned day = 0; + date_split((ph_date_t)i0, NULL, &month, &day); + ph_date_t date = date_from_numbers(i1, month, day); + put(i.o, (int64_t)date); + DISPATCH(); + } + + CASE(DATE_ADD) : { + int64_t i0 = get(i.i0); + int64_t i1 = get(i.i1); + + struct tm time = tm_from_date((ph_date_t)i0); + time.tm_mday += i1; + mktime(&time); + + ph_date_t date = date_from_tm(time); + put(i.o, (int64_t)date); + DISPATCH(); + } + + CASE(DATE_SUB) : { + int64_t i0 = get(i.i0); + int64_t i1 = get(i.i1); + + struct tm time = tm_from_date((ph_date_t)i0); + time.tm_mday -= i1; + mktime(&time); + + ph_date_t date = date_from_tm(time); + put(i.o, (int64_t)date); + DISPATCH(); + } + + CASE(DATE_DIFF) : { + int64_t i0 = get(i.i0); + int64_t i1 = get(i.i1); + + struct tm time0 = tm_from_date((ph_date_t)i0); + struct tm time1 = tm_from_date((ph_date_t)i1); + + /* close enough at least */ + time_t t0 = mktime(&time0); + time_t t1 = mktime(&time1); + double seconds = difftime(t0, t1); + int64_t days = round(seconds / 86400); + put(i.o, days); + DISPATCH(); + } + + CASE(TODAY) : { + ph_date_t date = current_date(); + put(i.o, (int64_t)date); + DISPATCH(); + } + + CASE(EQ) : { + int64_t i0 = get(i.i0); + int64_t i1 = get(i.i1); + int64_t b = i0 == i1; + put(i.o, b); + DISPATCH(); + } + + CASE(LT) : { + int64_t i0 = get(i.i0); + int64_t i1 = get(i.i1); + int64_t b = i0 < i1; + put(i.o, b); + DISPATCH(); + } + + CASE(NEG) : { + int64_t i0 = get(i.i0); + put(i.o, -i0); + DISPATCH(); + } + +#undef CASE +#undef JUMP +#undef DISPATCH +#undef get +#undef put +} + +void interpret() +{ + struct fn *f = find_fn(0); + assert(f); + + struct vec stack = vec_create(sizeof(int64_t)); + /* arbitrary amount of stack space, could potentially even be expanded + * if we run out but that's currently not implemented */ + vec_reserve(&stack, 65535); + + struct vec globals = vec_create(sizeof(int64_t)); + vec_reserve(&globals, num_globals()); + + struct vec args = vec_create(sizeof(int64_t)); + + interpret_func(f, &args, 0, &stack, &globals); + + vec_destroy(&args); + vec_destroy(&stack); + vec_destroy(&globals); +} |