#include #include #include #include #include #include #include #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(¶ms, &operand); } jit_operand_t gp = jit_operand_gpr(JIT_OPERAND_ABI_INT64, JIT_V0); vec_append(¶ms, &gp); jit_load_args(j, f->params + 1, params.buf); /* reuse vector as argument vector */ vec_reset(¶ms); 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, ¶ms); break; case CALL: compile_call(j, i, ¶ms); 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(¶ms); 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); }