aboutsummaryrefslogtreecommitdiff
path: root/src/compile.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/compile.c')
-rw-r--r--src/compile.c652
1 files changed, 652 insertions, 0 deletions
diff --git a/src/compile.c b/src/compile.c
new file mode 100644
index 0000000..9bbdbf4
--- /dev/null
+++ b/src/compile.c
@@ -0,0 +1,652 @@
+#include <math.h>
+#include <sys/mman.h>
+
+#include <posthaste/compile.h>
+#include <posthaste/lower.h>
+#include <posthaste/date.h>
+
+#include <lightening/lightening.h>
+
+#define UNUSED(x) (void)x
+
+static void compile_fn(struct fn *f);
+
+static void *alloc_arena(size_t size)
+{
+ return mmap(NULL, size,
+ PROT_EXEC | PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+}
+
+static void free_arena(void *arena, size_t size)
+{
+ munmap(arena, size);
+}
+
+static jit_operand_t formal_at(size_t i)
+{
+ return jit_operand_mem(JIT_OPERAND_ABI_INT64, JIT_SP,
+ i * sizeof(int64_t));
+}
+
+static void get(jit_state_t *j, jit_gpr_t r, struct loc l)
+{
+ if (l.l) {
+ jit_ldxi_l(j, r, JIT_SP, l.o * sizeof(int64_t));
+ return;
+ }
+
+ jit_ldxi_l(j, r, JIT_V0, l.o * sizeof(int64_t));
+}
+
+static void put(jit_state_t *j, jit_gpr_t r, struct loc l)
+{
+ if (l.l) {
+ jit_stxi_l(j, l.o * sizeof(int64_t), JIT_SP, r);
+ return;
+ }
+
+ jit_stxi_l(j, l.o * sizeof(int64_t), JIT_V0, r);
+}
+
+static void compile_add(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ get(j, JIT_R1, i.i1);
+ jit_addr(j, JIT_R0, JIT_R0, JIT_R1);
+ put(j, JIT_R0, i.o);
+}
+
+static void compile_sub(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ get(j, JIT_R1, i.i1);
+ jit_subr(j, JIT_R0, JIT_R0, JIT_R1);
+ put(j, JIT_R0, i.o);
+}
+
+static void compile_mul(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ get(j, JIT_R1, i.i1);
+ jit_mulr(j, JIT_R0, JIT_R0, JIT_R1);
+ put(j, JIT_R0, i.o);
+}
+
+static void compile_div(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ get(j, JIT_R1, i.i1);
+ jit_divr(j, JIT_R0, JIT_R0, JIT_R1);
+ put(j, JIT_R0, i.o);
+}
+
+static void compile_move(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ put(j, JIT_R0, i.o);
+}
+
+static void compile_const(jit_state_t *j, struct insn i)
+{
+ jit_movi(j, JIT_R0, i.v);
+ put(j, JIT_R0, i.o);
+}
+
+static void compile_eq(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ get(j, JIT_R1, i.i1);
+ jit_reloc_t branch = jit_beqr(j, JIT_R0, JIT_R1);
+
+ jit_movi(j, JIT_R0, 0);
+ jit_reloc_t jump = jit_jmp(j);
+ jit_patch_there(j, branch, jit_address(j));
+
+ jit_movi(j, JIT_R0, 1);
+ jit_patch_there(j, jump, jit_address(j));
+
+ put(j, JIT_R0, i.o);
+}
+
+static void compile_lt(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ get(j, JIT_R1, i.i1);
+ jit_reloc_t branch = jit_bltr(j, JIT_R0, JIT_R1);
+
+ jit_movi(j, JIT_R0, 0);
+ jit_reloc_t jump = jit_jmp(j);
+ jit_patch_there(j, branch, jit_address(j));
+
+ jit_movi(j, JIT_R0, 1);
+ jit_patch_there(j, jump, jit_address(j));
+
+ put(j, JIT_R0, i.o);
+}
+
+static void print_date(int64_t date)
+{
+ char str[11];
+ date_to_string(str, (ph_date_t)date);
+ printf("%s", str);
+}
+
+static void compile_print_date(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ jit_calli_1(j, print_date,
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R0));
+}
+
+static void print_int(int64_t i)
+{
+ printf("%lli", (long long)i);
+}
+
+static void compile_print_int(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ jit_calli_1(j, print_int,
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R0));
+}
+
+static void print_string(const char *s)
+{
+ printf("%s", s);
+}
+
+static void compile_print_string(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ jit_calli_1(j, print_string,
+ jit_operand_gpr(JIT_OPERAND_ABI_POINTER, JIT_R0));
+}
+
+static void print_bool(int64_t b)
+{
+ printf("%s", b ? "true" : "false");
+}
+
+static void compile_print_bool(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ jit_calli_1(j, print_bool,
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R0));
+}
+
+static void compile_print_newline(jit_state_t *j, struct insn i)
+{
+ UNUSED(i);
+ jit_calli_1(j, putchar, jit_operand_imm(JIT_OPERAND_ABI_INT8, '\n'));
+}
+
+static void compile_print_space(jit_state_t *j, struct insn i)
+{
+ UNUSED(i);
+ jit_calli_1(j, putchar, jit_operand_imm(JIT_OPERAND_ABI_INT8, ' '));
+}
+
+static void compile_label(jit_state_t *j, size_t ii, struct vec *labels)
+{
+ vect_at(jit_addr_t, *labels, ii) = jit_address(j);
+}
+
+struct reloc_helper {
+ jit_reloc_t r;
+ size_t to;
+};
+
+static void compile_j(jit_state_t *j, struct insn i, struct vec *relocs)
+{
+ jit_reloc_t r = jit_jmp(j);
+ struct reloc_helper h = {.r = r, .to = i.v};
+ vect_append(struct reloc_helper, *relocs, &h);
+}
+
+static void compile_b(jit_state_t *j, struct insn i, struct vec *relocs)
+{
+ get(j, JIT_R0, i.i0);
+ jit_reloc_t r = jit_bnei(j, JIT_R0, 0);
+ struct reloc_helper h = {.r = r, .to = i.v};
+ vect_append(struct reloc_helper, *relocs, &h);
+}
+
+static void compile_bz(jit_state_t *j, struct insn i, struct vec *relocs)
+{
+ get(j, JIT_R0, i.i0);
+ jit_reloc_t r = jit_beqi(j, JIT_R0, 0);
+ struct reloc_helper h = {.r = r, .to = i.v};
+ vect_append(struct reloc_helper, *relocs, &h);
+}
+
+static void compile_arg(struct insn i, struct vec *params)
+{
+ jit_operand_t operand;
+ struct loc l = i.i0;
+ if (l.l) {
+ operand = jit_operand_mem(JIT_OPERAND_ABI_INT64, JIT_SP,
+ l.o * sizeof(int64_t));
+ }
+ else {
+ operand = jit_operand_mem(JIT_OPERAND_ABI_INT64, JIT_V0,
+ l.o * sizeof(int64_t));
+ }
+
+ vect_append(jit_operand_t, *params, &operand);
+}
+
+static void compile_call(jit_state_t *j, struct insn i, struct vec *params)
+{
+ jit_operand_t gp = jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_V0);
+ vect_append(jit_operand_t, *params, &gp);
+
+ struct fn *f = find_fn(i.v);
+ assert(f);
+
+ if (!f->arena)
+ compile_fn(f);
+
+ jit_calli(j, f->arena, vec_len(params), params->buf);
+ vec_reset(params);
+}
+
+static void compile_retval(jit_state_t *j, struct insn i)
+{
+ jit_retval_l(j, JIT_R0);
+ put(j, JIT_R0, i.o);
+}
+
+static void compile_neg(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ jit_negr(j, JIT_R0, JIT_R0);
+ put(j, JIT_R0, i.o);
+}
+
+static void compile_today(jit_state_t *j, struct insn i)
+{
+ jit_calli_0(j, current_date);
+ jit_retval_l(j, JIT_R0);
+ put(j, JIT_R0, i.o);
+}
+
+static ph_date_t date_add(int64_t i0, int64_t i1)
+{
+ struct tm time = tm_from_date((ph_date_t)i0);
+ time.tm_mday += i1;
+ mktime(&time);
+
+ return date_from_tm(time);
+}
+
+static void compile_date_add(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ get(j, JIT_R1, i.i1);
+ jit_calli_2(j, date_add,
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R0),
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R1));
+
+ jit_retval_l(j, JIT_R0);
+ put(j, JIT_R0, i.o);
+}
+
+static ph_date_t date_sub(int64_t i0, int64_t i1)
+{
+ struct tm time = tm_from_date((ph_date_t)i0);
+ time.tm_mday -= i1;
+ mktime(&time);
+
+ return date_from_tm(time);
+}
+
+static void compile_date_sub(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ get(j, JIT_R1, i.i1);
+ jit_calli_2(j, date_sub,
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R0),
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R1));
+
+ jit_retval_l(j, JIT_R0);
+ put(j, JIT_R0, i.o);
+}
+
+static int64_t date_diff(int64_t i0, int64_t i1)
+{
+ struct tm tm0 = tm_from_date((ph_date_t)i0);
+ struct tm tm1 = tm_from_date((ph_date_t)i1);
+
+ time_t t0 = mktime(&tm0);
+ time_t t1 = mktime(&tm1);
+
+ double seconds = difftime(t0, t1);
+ return round(seconds / 86400);
+}
+
+static void compile_date_diff(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ get(j, JIT_R1, i.i1);
+
+ jit_calli_2(j, date_diff,
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R0),
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R1));
+
+ jit_retval_l(j, JIT_R0);
+ put(j, JIT_R0, i.o);
+}
+
+static int64_t load_year(int64_t i0)
+{
+ unsigned year = 0;
+ date_split((ph_date_t)i0, &year, NULL, NULL);
+ return year;
+}
+
+static void compile_load_year(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ jit_calli_1(j, load_year,
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R0));
+
+ jit_retval_l(j, JIT_R0);
+ put(j, JIT_R0, i.o);
+}
+
+static int64_t load_month(int64_t i0)
+{
+ unsigned month = 0;
+ date_split((ph_date_t)i0, NULL, &month, NULL);
+ return month;
+}
+
+static void compile_load_month(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ jit_calli_1(j, load_month,
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R0));
+
+ jit_retval_l(j, JIT_R0);
+ put(j, JIT_R0, i.o);
+}
+
+static int64_t load_day(int64_t i0)
+{
+ unsigned day = 0;
+ date_split((ph_date_t)i0, NULL, NULL, &day);
+ return day;
+}
+
+static void compile_load_day(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ jit_calli_1(j, load_day,
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R0));
+
+ jit_retval_l(j, JIT_R0);
+ put(j, JIT_R0, i.o);
+}
+
+static int64_t load_weekday(int64_t i0)
+{
+ struct tm time = tm_from_date((ph_date_t)i0);
+
+ const char *day = "Sunday";
+ switch (time.tm_wday) {
+ case 0: day = "Sunday"; break;
+ case 1: day = "Monday"; break;
+ case 2: day = "Tuesday"; break;
+ case 3: day = "Wednesday"; break;
+ case 4: day = "Thursday"; break;
+ case 5: day = "Friday"; break;
+ case 6: day = "Saturday"; break;
+ }
+
+ return (int64_t)day;
+}
+
+static void compile_load_weekday(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ jit_calli_1(j, load_weekday,
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R0));
+
+ jit_retval_l(j, JIT_R0);
+ put(j, JIT_R0, i.o);
+}
+
+static int64_t load_weeknum(int64_t i0)
+{
+ struct tm time = tm_from_date((ph_date_t)i0);
+ return time.tm_yday / 7;
+}
+
+static void compile_load_weeknum(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ jit_calli_1(j, load_weeknum,
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R0));
+
+ jit_retval_l(j, JIT_R0);
+ put(j, JIT_R0, i.o);
+}
+
+static int64_t store_year(int64_t i0, int64_t i1)
+{
+ unsigned month = 0;
+ unsigned day = 0;
+ date_split((ph_date_t)i0, NULL, &month, &day);
+ return date_from_numbers(i1, month, day);
+}
+
+static void compile_store_year(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ get(j, JIT_R1, i.i1);
+ jit_calli_2(j, store_year,
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R0),
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R1));
+
+ jit_retval_l(j, JIT_R0);
+ put(j, JIT_R0, i.o);
+}
+
+static int64_t store_month(int64_t i0, int64_t i1)
+{
+ unsigned year = 0;
+ unsigned day = 0;
+ date_split((ph_date_t)i0, &year, NULL, &day);
+ return date_from_numbers(year, i1, day);
+}
+
+static void compile_store_month(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ get(j, JIT_R1, i.i1);
+ jit_calli_2(j, store_month,
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R0),
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R1));
+
+ jit_retval_l(j, JIT_R0);
+ put(j, JIT_R0, i.o);
+}
+
+static int64_t store_day(int64_t i0, int64_t i1)
+{
+ unsigned year = 0;
+ unsigned month = 0;
+ date_split((ph_date_t)i0, &year, &month, NULL);
+ return date_from_numbers(year, month, i1);
+}
+
+static void compile_store_day(jit_state_t *j, struct insn i)
+{
+ get(j, JIT_R0, i.i0);
+ get(j, JIT_R1, i.i1);
+ jit_calli_2(j, store_day,
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R0),
+ jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_R1));
+
+ jit_retval_l(j, JIT_R0);
+ put(j, JIT_R0, i.o);
+}
+
+static size_t compile_fn_body(struct fn *f, jit_state_t *j)
+{
+ jit_begin(j, f->arena, f->size);
+ size_t frame = jit_enter_jit_abi(j, 1, 0, 0);
+ /* easy off-by-one to miss, damn */
+ size_t stack = jit_align_stack(j, (f->max_sp + 1) * sizeof(int64_t));
+
+ /* move parameters to where they belong */
+ struct vec params = vec_create(sizeof(jit_operand_t));
+ for (size_t i = 0; i < f->params; ++i) {
+ jit_operand_t operand = formal_at(i);
+ vec_append(&params, &operand);
+ }
+
+ jit_operand_t gp = jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_V0);
+ vec_append(&params, &gp);
+ jit_load_args(j, f->params + 1, params.buf);
+
+ /* reuse vector as argument vector */
+ vec_reset(&params);
+
+ struct vec relocs = vec_create(sizeof(struct reloc_helper));
+ struct vec labels = vec_create(sizeof(jit_addr_t));
+ vec_reserve(&labels, vec_len(&f->insns));
+
+ foreach_vec(ii, f->insns) {
+ struct insn i = vect_at(struct insn, f->insns, ii);
+ switch (i.k) {
+ case LABEL: compile_label(j, ii, &labels); break;
+ case J: compile_j(j, i, &relocs); break;
+ case B: compile_b(j, i, &relocs); break;
+ case BZ: compile_bz(j, i, &relocs); break;
+ case ADD: compile_add(j, i); break;
+ case SUB: compile_sub(j, i); break;
+ case MUL: compile_mul(j, i); break;
+ case DIV: compile_div(j, i); break;
+ case MOVE: compile_move(j, i); break;
+ case CONST: compile_const(j, i); break;
+ case EQ: compile_eq(j, i); break;
+ case LT: compile_lt(j, i); break;
+
+ case PRINT_DATE: compile_print_date(j, i); break;
+ case PRINT_INT: compile_print_int(j, i); break;
+ case PRINT_STRING: compile_print_string(j, i); break;
+ case PRINT_BOOL: compile_print_bool(j, i); break;
+ case PRINT_NEWLINE: compile_print_newline(j, i); break;
+ case PRINT_SPACE: compile_print_space(j, i); break;
+
+ case ARG: compile_arg(i, &params); break;
+ case CALL: compile_call(j, i, &params); break;
+ case RETVAL: compile_retval(j, i); break;
+
+ case NEG: compile_neg(j, i); break;
+
+ case TODAY: compile_today(j, i); break;
+
+ case DATE_ADD: compile_date_add(j, i); break;
+ case DATE_SUB: compile_date_sub(j, i); break;
+ case DATE_DIFF: compile_date_diff(j, i); break;
+
+ case LOAD_YEAR: compile_load_year(j, i); break;
+ case LOAD_MONTH: compile_load_month(j, i); break;
+ case LOAD_DAY: compile_load_day(j, i); break;
+ case LOAD_WEEKDAY: compile_load_weekday(j, i); break;
+ case LOAD_WEEKNUM: compile_load_weeknum(j, i); break;
+
+ case STORE_YEAR: compile_store_year(j, i); break;
+ case STORE_MONTH: compile_store_month(j, i); break;
+ case STORE_DAY: compile_store_day(j, i); break;
+
+ case RET: {
+ get(j, JIT_R0, i.i0);
+ jit_shrink_stack(j, stack);
+ jit_leave_jit_abi(j, 1, 0, frame);
+ jit_retr(j, JIT_R0);
+ break;
+ }
+
+ case STOP: {
+ jit_shrink_stack(j, stack);
+ jit_leave_jit_abi(j, 1, 0, frame);
+ jit_ret(j);
+ break;
+ }
+ }
+ }
+
+ /* fix relocs */
+ foreach_vec(ri, relocs) {
+ struct reloc_helper h = vect_at(struct reloc_helper, relocs,
+ ri);
+ jit_addr_t a = vect_at(jit_addr_t, labels, h.to);
+ jit_reloc_t r = h.r;
+
+ assert(a);
+ jit_patch_there(j, r, a);
+ }
+
+ vec_destroy(&relocs);
+ vec_destroy(&labels);
+ vec_destroy(&params);
+
+ size_t size = 0;
+ void *p = jit_end(j, &size);
+ if (p)
+ return 0;
+
+ return size;
+}
+
+static void compile_fn(struct fn *f)
+{
+ init_jit();
+ jit_state_t *j = jit_new_state(NULL, NULL);
+ assert(j);
+
+ void *arena_base = NULL;
+ size_t arena_size = 4096;
+ while (1) {
+ arena_base = alloc_arena(arena_size);
+ assert(arena_base &&
+ "failed allocating executable arena, aborting");
+
+ f->arena = arena_base;
+ f->size = arena_size;
+
+ size_t required_size = compile_fn_body(f, j);
+ if (required_size == 0)
+ break;
+
+ free_arena(arena_base, arena_size);
+ /* give at least one page more than necessary to be on the safe
+ * side */
+ arena_size = required_size + 4096;
+ }
+
+ jit_destroy_state(j);
+}
+
+void compile()
+{
+ struct fn *f = find_fn(0);
+ compile_fn(f);
+}
+
+void execute_compilation()
+{
+ struct fn *f = find_fn(0);
+ void (*p)(int64_t *globals) = f->arena;
+
+ struct vec globals = vec_create(sizeof(int64_t));
+ vec_reserve(&globals, 65535);
+
+ p(globals.buf);
+
+ vec_destroy(&globals);
+}