From 8a98b46ef2b5673fcdf0a9a466857a5de044dfc5 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Sat, 20 Apr 2024 10:47:49 +0300 Subject: add lightening jit --- src/compile.c | 652 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 652 insertions(+) create mode 100644 src/compile.c (limited to 'src/compile.c') 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 +#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); +} -- cgit v1.2.3