From 912c07167705613c6db70e542723c7ec2c06c7ea Mon Sep 17 00:00:00 2001 From: Kimplul Date: Sat, 15 Mar 2025 13:16:54 +0200 Subject: experiment with allocating regs on stack in interp + Avoids having to lug around an execution context, arguably simplified things but now there's no real way to detect when we run out memory for regs. --- include/ejit/ejit.h | 15 --------------- 1 file changed, 15 deletions(-) (limited to 'include') diff --git a/include/ejit/ejit.h b/include/ejit/ejit.h index 4ab2bbc..48c4dda 100644 --- a/include/ejit/ejit.h +++ b/include/ejit/ejit.h @@ -223,21 +223,6 @@ double ejit_run_func_d(struct ejit_func *f, size_t argc, struct ejit_arg ejit_run_func(struct ejit_func *f, size_t argc, struct ejit_arg args[argc]); - -/* currently unused, the idea is that an escape could get the interpreter state - * and pass it on to speed up entering/exiting the VM, could be useful for more - * dynamic languages */ -struct interp_state; - -long ejit_run_func_ctx_i(struct ejit_func *f, size_t argc, struct ejit_arg args[argc], struct interp_state *ctx); - -int64_t ejit_run_func_ctx_l(struct ejit_func *f, size_t argc, struct ejit_arg args[argc], struct interp_state *ctx); - -float ejit_run_func_ctx_f(struct ejit_func *f, size_t argc, struct ejit_arg args[argc], struct interp_state *ctx); - -double ejit_run_func_ctx_d(struct ejit_func *f, size_t argc, struct ejit_arg args[argc], struct interp_state *ctx); - - void ejit_destroy_func(struct ejit_func *s); #define EJIT_GPR(x) ((struct ejit_gpr){.r = (x)}) -- cgit v1.2.3 From 67a13eb82534996fbd6ba2fc0c36d3e1284bcd8e Mon Sep 17 00:00:00 2001 From: Kimplul Date: Wed, 26 Mar 2025 13:17:58 +0200 Subject: handle immediates a bit better + Passing floats as immediate values is not supported in lightening, but I might have a go at adding it since it seems like a useful feature at some point --- include/ejit/ejit.h | 4 +-- src/compile/compile.c | 25 +++++++++++++++ src/ejit.c | 3 ++ src/interp.c | 2 +- tests/escapei_immediate_10.c | 73 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 tests/escapei_immediate_10.c (limited to 'include') diff --git a/include/ejit/ejit.h b/include/ejit/ejit.h index 48c4dda..d4bb725 100644 --- a/include/ejit/ejit.h +++ b/include/ejit/ejit.h @@ -157,7 +157,7 @@ static inline struct ejit_arg ejit_build_arg_f(enum ejit_type type, double x) a.type = type; switch (type) { - case EJIT_FLOAT: a.f = x; break; + case EJIT_FLOAT: a.f = (float)x; break; case EJIT_DOUBLE: a.d = x; break; default: abort(); } @@ -245,7 +245,7 @@ void ejit_destroy_func(struct ejit_func *s); .type = (t)}) #define EJIT_OPERAND_FLT(x, t) \ ((struct ejit_operand){ .kind = EJIT_OPERAND_FLT, \ - .r = (double)(x), \ + .d = (double)(x), \ .type = (t)}) /* maybe slight hack, but increase width to interpeter register width */ diff --git a/src/compile/compile.c b/src/compile/compile.c index c979305..50f12dc 100644 --- a/src/compile/compile.c +++ b/src/compile/compile.c @@ -2140,6 +2140,31 @@ static size_t compile_fn_body(struct ejit_func *f, jit_state_t *j, void *arena, case JMP: compile_jmp(f, j, i, &relocs); break; + case ARG_I: { + jit_operand_t type = jit_operand_imm(JIT_OPERAND_ABI_WORD, i.r1); + jit_operand_t arg = jit_operand_imm(jit_abi_from(i.r1), i.o); + operands_append(&src, type); + operands_append(&src, arg); + operands_append(&direct, arg); + + jit_operand_t to[2] = { + jit_operand_mem(JIT_OPERAND_ABI_WORD, JIT_SP, + type_offset(i)), + jit_operand_mem(jit_abi_from(i.r1), JIT_SP, + arg_offset(i)) + }; + + operands_append(&dst, to[0]); + operands_append(&dst, to[1]); + break; + } + + case ARG_FI: { + assert(false && "immediate floats (currently?) not supported"); + abort(); + break; + } + case ARG: { size_t r2 = gpr_stats_at(&f->gpr, i.r2)->rno; jit_operand_t type = jit_operand_imm(JIT_OPERAND_ABI_WORD, i.r1); diff --git a/src/ejit.c b/src/ejit.c index 0a0e7a1..94c69e6 100644 --- a/src/ejit.c +++ b/src/ejit.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -161,6 +162,8 @@ static void emit_insn_af(struct ejit_func *f, enum ejit_opcode op, size_t idx, e static void emit_insn_ad(struct ejit_func *f, enum ejit_opcode op, size_t idx, enum ejit_type type, double d) { + fprintf(stderr, "warning: immediate floats are currently not supported.\n" + "Consider moving values into FPRs.\n"); struct ejit_insn i = {.op = op, .r0 = idx, .r1 = type, .d = d}; insns_append(&f->insns, i); } diff --git a/src/interp.c b/src/interp.c index 80a9edc..aa40c53 100644 --- a/src/interp.c +++ b/src/interp.c @@ -1009,7 +1009,7 @@ union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg pa if (i.r1 == EJIT_DOUBLE) a = ejit_build_arg_f(i.r1, i.d); else - a = ejit_build_arg_f(i.r1, i.f); + a = ejit_build_arg_f(i.r1, i.d); args[argc++] = a; DISPATCH(); diff --git a/tests/escapei_immediate_10.c b/tests/escapei_immediate_10.c new file mode 100644 index 0000000..381c79f --- /dev/null +++ b/tests/escapei_immediate_10.c @@ -0,0 +1,73 @@ +#include +#include +#include "do_jit.h" + +static int32_t func(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e, + int32_t f, int32_t g, int32_t h, int32_t i, int32_t j) +{ + assert(a == 0); + assert(b == 1); + assert(c == 2); + assert(d == 3); + assert(e == 4); + assert(f == 5); + assert(g == 6); + assert(h == 7); + assert(i == 8); + assert(j == 9); + return 42; +} + +static long escape_func(size_t argc, const struct ejit_arg args[argc]) +{ + assert(argc == 10); + assert(args[0].type == EJIT_INT32); + assert(args[1].type == EJIT_INT32); + assert(args[3].type == EJIT_INT32); + assert(args[4].type == EJIT_INT32); + assert(args[5].type == EJIT_INT32); + assert(args[6].type == EJIT_INT32); + assert(args[7].type == EJIT_INT32); + assert(args[8].type == EJIT_INT32); + assert(args[9].type == EJIT_INT32); + + int32_t a = args[0].i32; + int32_t b = args[1].i32; + int32_t c = args[2].i32; + int32_t d = args[3].i32; + int32_t e = args[4].i32; + int32_t f = args[5].i32; + int32_t g = args[6].i32; + int32_t h = args[7].i32; + int32_t i = args[8].i32; + int32_t j = args[9].i32; + return func(a, b, c, d, e, f, g, h, i, j); +} + +int main(int argc, char *argv[]) +{ + (void)argv; + bool do_jit = argc > 1; + struct ejit_func *f = ejit_create_func(EJIT_INT32, 0, NULL); + struct ejit_operand args[10] = { + EJIT_OPERAND_IMM(0, EJIT_INT32), + EJIT_OPERAND_IMM(1, EJIT_INT32), + EJIT_OPERAND_IMM(2, EJIT_INT32), + EJIT_OPERAND_IMM(3, EJIT_INT32), + EJIT_OPERAND_IMM(4, EJIT_INT32), + EJIT_OPERAND_IMM(5, EJIT_INT32), + EJIT_OPERAND_IMM(6, EJIT_INT32), + EJIT_OPERAND_IMM(7, EJIT_INT32), + EJIT_OPERAND_IMM(8, EJIT_INT32), + EJIT_OPERAND_IMM(9, EJIT_INT32), + }; + ejit_escapei_i(f, escape_func, 10, args); + ejit_retval(f, EJIT_GPR(0)); + ejit_retr(f, EJIT_GPR(0)); + + ejit_select_compile_func(f, 11, 0, false, do_jit, true); + + assert(ejit_run_func_i(f, 0, NULL) == 42); + + ejit_destroy_func(f); +} -- cgit v1.2.3 From 478c92b425eca53a0d884fb8f5dea8d769016858 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Tue, 1 Apr 2025 22:16:25 +0300 Subject: expose sqrt + Requires linking with libm in some cases, which is fine I suppose, but kind of annoying --- include/ejit/ejit.h | 3 +++ scripts/gen-tests | 2 +- src/common.h | 3 +++ src/compile/compile.c | 20 ++++++++++++++++++++ src/ejit.c | 10 ++++++++++ src/interp.c | 11 +++++++++++ tests/sqrtr_d.c | 23 +++++++++++++++++++++++ tests/sqrtr_f.c | 23 +++++++++++++++++++++++ 8 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 tests/sqrtr_d.c create mode 100644 tests/sqrtr_f.c (limited to 'include') diff --git a/include/ejit/ejit.h b/include/ejit/ejit.h index d4bb725..920fdc5 100644 --- a/include/ejit/ejit.h +++ b/include/ejit/ejit.h @@ -805,6 +805,9 @@ void ejit_truncr_d_32(struct ejit_func *s, struct ejit_gpr r0, void ejit_truncr_d_64(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr r1); +void ejit_sqrtr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1); +void ejit_sqrtr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1); + struct ejit_reloc ejit_bltr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1); struct ejit_reloc ejit_bner(struct ejit_func *s, struct ejit_gpr r0, diff --git a/scripts/gen-tests b/scripts/gen-tests index 5521006..deac247 100755 --- a/scripts/gen-tests +++ b/scripts/gen-tests @@ -11,5 +11,5 @@ do echo "${dep}:" >> tests.mk echo "-include ${dep}" >> tests.mk echo "${exe}: ${s} libejit.a" >> tests.mk - echo " \$(COMPILE_TEST) ${s} libejit.a -o ${exe}" >> tests.mk + echo " \$(COMPILE_TEST) ${s} libejit.a -o ${exe} -lm" >> tests.mk done diff --git a/src/common.h b/src/common.h index 6a3c754..c690f8f 100644 --- a/src/common.h +++ b/src/common.h @@ -142,6 +142,9 @@ enum ejit_opcode { EJIT_OP_TRUNCR_F_32, EJIT_OP_TRUNCR_F_64, + EJIT_OP_SQRTR_F, + EJIT_OP_SQRTR_D, + EJIT_OP_EQR, EJIT_OP_NER, EJIT_OP_GTR, diff --git a/src/compile/compile.c b/src/compile/compile.c index b90ee54..580b7fa 100644 --- a/src/compile/compile.c +++ b/src/compile/compile.c @@ -1326,6 +1326,23 @@ static void compile_truncr_f_32(struct ejit_func *f, jit_state_t *j, #endif } +static void compile_sqrtr_f(struct ejit_func *f, jit_state_t *j, + struct ejit_insn i) +{ + jit_fpr_t r0 = getfpr(f, i.r0, 0); + jit_fpr_t r1 = getloc_f(f, j, i.r1, 1); + jit_sqrtr_f(j, r0, r1); + putloc_d(f, j, i.r0, r0); +} + +static void compile_sqrtr_d(struct ejit_func *f, jit_state_t *j, + struct ejit_insn i) +{ + jit_fpr_t r0 = getfpr(f, i.r0, 0); + jit_fpr_t r1 = getloc_d(f, j, i.r1, 1); + jit_sqrtr_d(j, r0, r1); + putloc_d(f, j, i.r0, r0); +} static void compile_reg_cmp(struct ejit_func *f, jit_state_t *j, struct ejit_insn i, @@ -2084,6 +2101,9 @@ static size_t compile_fn_body(struct ejit_func *f, jit_state_t *j, void *arena, case EJIT_OP_TRUNCR_F_32: compile_truncr_f_32(f, j, i); break; case EJIT_OP_TRUNCR_F_64: compile_truncr_f_64(f, j, i); break; + case EJIT_OP_SQRTR_F: compile_sqrtr_f(f, j, i); break; + case EJIT_OP_SQRTR_D: compile_sqrtr_d(f, j, i); break; + case EJIT_OP_EQR: compile_eqr(f, j, i); break; case EJIT_OP_EQR_F: compile_eqr_f(f, j, i); break; case EJIT_OP_EQR_D: compile_eqr_d(f, j, i); break; diff --git a/src/ejit.c b/src/ejit.c index e7e2ff2..2224198 100644 --- a/src/ejit.c +++ b/src/ejit.c @@ -1371,6 +1371,16 @@ void ejit_truncr_f_64(struct ejit_func *s, struct ejit_gpr r0, emit_insn_orf(s, EJIT_OP_TRUNCR_F_64, r0, f1); } +void ejit_sqrtr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1) +{ + emit_insn_off(s, EJIT_OP_SQRTR_F, r0, r1); +} + +void ejit_sqrtr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1) +{ + emit_insn_off(s, EJIT_OP_SQRTR_D, r0, r1); +} + struct ejit_reloc ejit_bner(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1) { diff --git a/src/interp.c b/src/interp.c index b858f26..2d9b7c7 100644 --- a/src/interp.c +++ b/src/interp.c @@ -147,6 +147,9 @@ union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg pa [EJIT_OP_TRUNCR_F_32] = &&TRUNCR_F_32, [EJIT_OP_TRUNCR_F_64] = &&TRUNCR_F_64, + [EJIT_OP_SQRTR_F] = &&SQRTR_F, + [EJIT_OP_SQRTR_D] = &&SQRTR_D, + [EJIT_OP_BNER] = &&BNER, [EJIT_OP_BNEI] = &&BNEI, [EJIT_OP_BNER_F] = &&BNER_F, @@ -784,6 +787,14 @@ union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg pa gpr[i.r0] = (int64_t)fpr[i.r1].f; DISPATCH(); + DO(SQRTR_F); + fpr[i.r0].f = sqrt(fpr[i.r1].f); + DISPATCH(); + + DO(SQRTR_D); + fpr[i.r0].d = sqrt(fpr[i.r1].d); + DISPATCH(); + DO(BNER); if (gpr[i.r1] != gpr[i.r2]) JUMP(i.r0); diff --git a/tests/sqrtr_d.c b/tests/sqrtr_d.c new file mode 100644 index 0000000..06e7894 --- /dev/null +++ b/tests/sqrtr_d.c @@ -0,0 +1,23 @@ +#include +#include +#include "do_jit.h" + +int main(int argc, char *argv[]) +{ + (void)argv; + bool do_jit = argc > 1; + struct ejit_operand operands[1] = { + EJIT_OPERAND_FPR(0, EJIT_TYPE(double)), + }; + struct ejit_func *f = ejit_create_func(EJIT_TYPE(double), 1, operands); + + ejit_sqrtr_d(f, EJIT_FPR(0), EJIT_FPR(0)); + ejit_retr_d(f, EJIT_FPR(0)); + + ejit_select_compile_func(f, 0, 1, EJIT_USE64(double), do_jit, true); + + assert(erfd1(f, EJIT_ARG( 0.0, double)) == 0.0); + assert(erfd1(f, EJIT_ARG( 4.0, double)) == 2.0); + assert(erfd1(f, EJIT_ARG(-4.0, double)) + != erfd1(f, EJIT_ARG(-4.0, double))); // nan +} diff --git a/tests/sqrtr_f.c b/tests/sqrtr_f.c new file mode 100644 index 0000000..3baa00d --- /dev/null +++ b/tests/sqrtr_f.c @@ -0,0 +1,23 @@ +#include +#include +#include "do_jit.h" + +int main(int argc, char *argv[]) +{ + (void)argv; + bool do_jit = argc > 1; + struct ejit_operand operands[1] = { + EJIT_OPERAND_FPR(0, EJIT_TYPE(float)), + }; + struct ejit_func *f = ejit_create_func(EJIT_TYPE(float), 1, operands); + + ejit_sqrtr_f(f, EJIT_FPR(0), EJIT_FPR(0)); + ejit_retr_f(f, EJIT_FPR(0)); + + ejit_select_compile_func(f, 0, 1, EJIT_USE64(float), do_jit, true); + + assert(erff1(f, EJIT_ARG( 0.0, float)) == 0.0); + assert(erff1(f, EJIT_ARG( 4.0, float)) == 2.0); + assert(erff1(f, EJIT_ARG(-4.0, float)) + != erff1(f, EJIT_ARG(-4.0, float))); // nan +} -- cgit v1.2.3 From d8f9699debd435da5e1aef22c94c47154be4e2be Mon Sep 17 00:00:00 2001 From: Kimplul Date: Wed, 2 Apr 2025 21:13:03 +0300 Subject: fix big endian compilation + Code used some assumptions about type aliasing that might not hold for all systems --- include/ejit/ejit.h | 124 ++++++++++++++++++++++++++++++++----------- src/ejit.c | 6 ++- src/interp.c | 13 ++++- tests/escapei_10.c | 30 ++++------- tests/escapei_double.c | 6 +-- tests/escapei_float.c | 6 +-- tests/escapei_immediate_10.c | 30 ++++------- 7 files changed, 134 insertions(+), 81 deletions(-) (limited to 'include') diff --git a/include/ejit/ejit.h b/include/ejit/ejit.h index 920fdc5..2afdc61 100644 --- a/include/ejit/ejit.h +++ b/include/ejit/ejit.h @@ -72,29 +72,20 @@ static inline enum ejit_type ejit_signed_type(size_t w) struct ejit_arg { union { - int8_t i8; - uint8_t u8; + int8_t i8; int16_t i16; - uint16_t u16; int32_t i32; - uint32_t u32; int64_t i64; - uint64_t u64; - signed char c; - unsigned char uc; - signed short s; - unsigned short us; - signed int i; - unsigned int ui; - signed long l; - unsigned long ul; - signed long long ll; - unsigned long long ull; + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; float f; double d; void *p; + long l; }; enum ejit_type type; }; @@ -136,15 +127,15 @@ static inline struct ejit_arg ejit_build_arg(enum ejit_type type, uint64_t x) a.type = type; switch (type) { - case EJIT_INT8: a.u64 = (int8_t)x; break; - case EJIT_INT16: a.u64 = (int16_t)x; break; - case EJIT_INT32: a.u64 = (int32_t)x; break; - case EJIT_INT64: a.u64 = (int64_t)x; break; - case EJIT_UINT8: a.u64 = (uint8_t)x; break; - case EJIT_UINT16: a.u64 = (uint16_t)x; break; - case EJIT_UINT32: a.u64 = (uint32_t)x; break; - case EJIT_UINT64: a.u64 = (uint64_t)x; break; - case EJIT_POINTER: a.p = (void *)(uintptr_t)x; break; + case EJIT_INT8: a.i8 = (int8_t)x; break; + case EJIT_INT16: a.i16 = (int16_t)x; break; + case EJIT_INT32: a.i32 = (int32_t)x; break; + case EJIT_INT64: a.i64 = (int64_t)x; break; + case EJIT_UINT8: a.u8 = (uint8_t)x; break; + case EJIT_UINT16: a.u16 = (uint16_t)x; break; + case EJIT_UINT32: a.u32 = (uint32_t)x; break; + case EJIT_UINT64: a.u64 = (uint64_t)x; break; + case EJIT_POINTER: a.p = (void *)(uintptr_t)x; break; default: abort(); } @@ -251,17 +242,17 @@ void ejit_destroy_func(struct ejit_func *s); /* maybe slight hack, but increase width to interpeter register width */ static inline struct ejit_arg ejit_i8(int8_t a) { - return (struct ejit_arg){.i64 = a, .type = EJIT_INT8}; + return (struct ejit_arg){.i8 = a, .type = EJIT_INT8}; } static inline struct ejit_arg ejit_i16(int16_t a) { - return (struct ejit_arg){.i64 = a, .type = EJIT_INT16}; + return (struct ejit_arg){.i16 = a, .type = EJIT_INT16}; } static inline struct ejit_arg ejit_i32(int32_t a) { - return (struct ejit_arg){.i64 = a, .type = EJIT_INT32}; + return (struct ejit_arg){.i32 = a, .type = EJIT_INT32}; } static inline struct ejit_arg ejit_i64(int64_t a) @@ -271,22 +262,22 @@ static inline struct ejit_arg ejit_i64(int64_t a) static inline struct ejit_arg ejit_u8(uint8_t a) { - return (struct ejit_arg){.i64 = a, .type = EJIT_UINT8}; + return (struct ejit_arg){.u8 = a, .type = EJIT_UINT8}; } static inline struct ejit_arg ejit_u16(uint16_t a) { - return (struct ejit_arg){.i64 = a, .type = EJIT_UINT16}; + return (struct ejit_arg){.u16 = a, .type = EJIT_UINT16}; } static inline struct ejit_arg ejit_u32(uint32_t a) { - return (struct ejit_arg){.i64 = a, .type = EJIT_UINT32}; + return (struct ejit_arg){.u32 = a, .type = EJIT_UINT32}; } static inline struct ejit_arg ejit_u64(uint64_t a) { - return (struct ejit_arg){.i64 = a, .type = EJIT_UINT64}; + return (struct ejit_arg){.u64 = a, .type = EJIT_UINT64}; } static inline struct ejit_arg ejit_pointer(void *p) @@ -372,6 +363,77 @@ static inline struct ejit_arg ejit_pointer_arg(void *p, size_t w) #define EJIT_AUTO(x) \ EJIT_ARG(x, typeof(x)) +static inline int64_t ejit_signed_param(size_t argc, const struct ejit_arg args[argc], + size_t idx, enum ejit_type type) +{ + assert(idx < argc); + assert(args[idx].type == type); + switch (type) { + case EJIT_INT64: return args[idx].i64; + case EJIT_INT32: return args[idx].i32; + case EJIT_INT16: return args[idx].i16; + case EJIT_INT8: return args[idx].i8; + default: abort(); + } + + return 0; +} + +static inline uint64_t ejit_unsigned_param(size_t argc, const struct ejit_arg args[argc], + size_t idx, enum ejit_type type) +{ + assert(idx < argc); + assert(args[idx].type == type); + switch (type) { + case EJIT_UINT64: return args[idx].u64; + case EJIT_UINT32: return args[idx].u32; + case EJIT_UINT16: return args[idx].u16; + case EJIT_UINT8: return args[idx].u8; + default: abort(); + } +} + +static inline float ejit_float_param(size_t argc, const struct ejit_arg args[argc], + size_t idx, enum ejit_type type) +{ + assert(idx < argc); + assert(args[idx].type == type && type == EJIT_FLOAT); + return args[idx].f; +} + +static inline double ejit_double_param(size_t argc, const struct ejit_arg args[argc], + size_t idx, enum ejit_type type) +{ + assert(idx < argc); + assert(args[idx].type == type && type == EJIT_DOUBLE); + return args[idx].d; +} + +static inline void *ejit_pointer_param(size_t argc, const struct ejit_arg args[argc], + size_t idx, enum ejit_type type) +{ + assert(idx < argc); + assert(args[idx].type == type && type == EJIT_POINTER); + return args[idx].p; +} + +#define EJIT_PARAM(argc, args, idx, t) \ + _Generic((t)(0), \ + signed char : ejit_signed_param, \ + signed short : ejit_signed_param, \ + signed int : ejit_signed_param, \ + signed long : ejit_signed_param, \ + signed long long : ejit_signed_param, \ + unsigned char : ejit_unsigned_param, \ + unsigned short : ejit_unsigned_param, \ + unsigned int : ejit_unsigned_param, \ + unsigned long : ejit_unsigned_param, \ + unsigned long long: ejit_unsigned_param, \ + float : ejit_float_param, \ + double : ejit_double_param, \ + default : ejit_pointer_param \ + )(argc, args, idx, EJIT_TYPE(t)) + static inline bool ejit_use64(struct ejit_arg a) { if (a.type == EJIT_INT64 || a.type == EJIT_UINT64) diff --git a/src/ejit.c b/src/ejit.c index 2224198..0ee3986 100644 --- a/src/ejit.c +++ b/src/ejit.c @@ -1726,6 +1726,10 @@ struct ejit_arg ejit_run_func(struct ejit_func *f, size_t argc, struct ejit_arg }; case EJIT_UINT64: + return (struct ejit_arg){ + .u64 = ejit_run_func_l(f, argc, args), + .type = f->rtype + }; case EJIT_INT64: return (struct ejit_arg){ .i64 = ejit_run_func_l(f, argc, args), @@ -1734,7 +1738,7 @@ struct ejit_arg ejit_run_func(struct ejit_func *f, size_t argc, struct ejit_arg default: return (struct ejit_arg){ - .i64 = ejit_run_func_i(f, argc, args), + .l = ejit_run_func_i(f, argc, args), .type = f->rtype }; } diff --git a/src/interp.c b/src/interp.c index 2d9b7c7..049498a 100644 --- a/src/interp.c +++ b/src/interp.c @@ -984,7 +984,18 @@ union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg pa DISPATCH(); DO(PARAM); - gpr[i.r2] = params[i.r0].u64; + switch (i.r1) { + case EJIT_INT8: gpr[i.r2] = params[i.r0].i8; break; + case EJIT_INT16: gpr[i.r2] = params[i.r0].i16; break; + case EJIT_INT32: gpr[i.r2] = params[i.r0].i32; break; + case EJIT_INT64: gpr[i.r2] = params[i.r0].i64; break; + case EJIT_UINT8: gpr[i.r2] = params[i.r0].u8; break; + case EJIT_UINT16: gpr[i.r2] = params[i.r0].u16; break; + case EJIT_UINT32: gpr[i.r2] = params[i.r0].u32; break; + case EJIT_UINT64: gpr[i.r2] = params[i.r0].u64; break; + case EJIT_POINTER: gpr[i.r2] = (int64_t)params[i.r0].p; break; + default: abort(); + } DISPATCH(); DO(PARAM_F); diff --git a/tests/escapei_10.c b/tests/escapei_10.c index 4ae00b8..ec48df0 100644 --- a/tests/escapei_10.c +++ b/tests/escapei_10.c @@ -21,26 +21,16 @@ static int32_t func(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e, static long escape_func(size_t argc, const struct ejit_arg args[argc]) { assert(argc == 10); - assert(args[0].type == EJIT_INT32); - assert(args[1].type == EJIT_INT32); - assert(args[3].type == EJIT_INT32); - assert(args[4].type == EJIT_INT32); - assert(args[5].type == EJIT_INT32); - assert(args[6].type == EJIT_INT32); - assert(args[7].type == EJIT_INT32); - assert(args[8].type == EJIT_INT32); - assert(args[9].type == EJIT_INT32); - - int32_t a = args[0].i32; - int32_t b = args[1].i32; - int32_t c = args[2].i32; - int32_t d = args[3].i32; - int32_t e = args[4].i32; - int32_t f = args[5].i32; - int32_t g = args[6].i32; - int32_t h = args[7].i32; - int32_t i = args[8].i32; - int32_t j = args[9].i32; + int32_t a = EJIT_PARAM(argc, args, 0, int32_t); + int32_t b = EJIT_PARAM(argc, args, 1, int32_t); + int32_t c = EJIT_PARAM(argc, args, 2, int32_t); + int32_t d = EJIT_PARAM(argc, args, 3, int32_t); + int32_t e = EJIT_PARAM(argc, args, 4, int32_t); + int32_t f = EJIT_PARAM(argc, args, 5, int32_t); + int32_t g = EJIT_PARAM(argc, args, 6, int32_t); + int32_t h = EJIT_PARAM(argc, args, 7, int32_t); + int32_t i = EJIT_PARAM(argc, args, 8, int32_t); + int32_t j = EJIT_PARAM(argc, args, 9, int32_t); return func(a, b, c, d, e, f, g, h, i, j); } diff --git a/tests/escapei_double.c b/tests/escapei_double.c index 6ea9f90..736e978 100644 --- a/tests/escapei_double.c +++ b/tests/escapei_double.c @@ -9,10 +9,8 @@ static double func(int32_t a, double b) { static double escape_func(size_t argc, const struct ejit_arg args[argc]) { assert(argc == 2); - assert(args[0].type == EJIT_INT32); - assert(args[1].type == EJIT_DOUBLE); - int32_t a = args[0].i32; - double b = args[1].d; + int32_t a = EJIT_PARAM(argc, args, 0, int32_t); + double b = EJIT_PARAM(argc, args, 1, double); return func(a, b); } diff --git a/tests/escapei_float.c b/tests/escapei_float.c index 7a1b923..7cdc30d 100644 --- a/tests/escapei_float.c +++ b/tests/escapei_float.c @@ -9,10 +9,8 @@ static float func(int32_t a, float b) { static float escape_func(size_t argc, const struct ejit_arg args[argc]) { assert(argc == 2); - assert(args[0].type == EJIT_INT32); - assert(args[1].type == EJIT_FLOAT); - int32_t a = args[0].i32; - float b = args[1].f; + int32_t a = EJIT_PARAM(argc, args, 0, int32_t); + float b = EJIT_PARAM(argc, args, 1, float); return func(a, b); } diff --git a/tests/escapei_immediate_10.c b/tests/escapei_immediate_10.c index 381c79f..5517c35 100644 --- a/tests/escapei_immediate_10.c +++ b/tests/escapei_immediate_10.c @@ -21,26 +21,16 @@ static int32_t func(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e, static long escape_func(size_t argc, const struct ejit_arg args[argc]) { assert(argc == 10); - assert(args[0].type == EJIT_INT32); - assert(args[1].type == EJIT_INT32); - assert(args[3].type == EJIT_INT32); - assert(args[4].type == EJIT_INT32); - assert(args[5].type == EJIT_INT32); - assert(args[6].type == EJIT_INT32); - assert(args[7].type == EJIT_INT32); - assert(args[8].type == EJIT_INT32); - assert(args[9].type == EJIT_INT32); - - int32_t a = args[0].i32; - int32_t b = args[1].i32; - int32_t c = args[2].i32; - int32_t d = args[3].i32; - int32_t e = args[4].i32; - int32_t f = args[5].i32; - int32_t g = args[6].i32; - int32_t h = args[7].i32; - int32_t i = args[8].i32; - int32_t j = args[9].i32; + int32_t a = EJIT_PARAM(argc, args, 0, int32_t); + int32_t b = EJIT_PARAM(argc, args, 1, int32_t); + int32_t c = EJIT_PARAM(argc, args, 2, int32_t); + int32_t d = EJIT_PARAM(argc, args, 3, int32_t); + int32_t e = EJIT_PARAM(argc, args, 4, int32_t); + int32_t f = EJIT_PARAM(argc, args, 5, int32_t); + int32_t g = EJIT_PARAM(argc, args, 6, int32_t); + int32_t h = EJIT_PARAM(argc, args, 7, int32_t); + int32_t i = EJIT_PARAM(argc, args, 8, int32_t); + int32_t j = EJIT_PARAM(argc, args, 9, int32_t); return func(a, b, c, d, e, f, g, h, i, j); } -- cgit v1.2.3 From 5de12ad388b07e536c9c8ea04683e5e16472651b Mon Sep 17 00:00:00 2001 From: Kimplul Date: Wed, 2 Apr 2025 21:13:57 +0300 Subject: fix some warnings with clang --- include/ejit/ejit.h | 2 ++ src/compile/compile.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/ejit/ejit.h b/include/ejit/ejit.h index 2afdc61..b4b20c1 100644 --- a/include/ejit/ejit.h +++ b/include/ejit/ejit.h @@ -104,6 +104,7 @@ static inline bool ejit_int_type(enum ejit_type t) case EJIT_POINTER: return true; default: + break; } return false; @@ -116,6 +117,7 @@ static inline bool ejit_float_type(enum ejit_type t) case EJIT_DOUBLE: return true; default: + break; } return false; diff --git a/src/compile/compile.c b/src/compile/compile.c index 580b7fa..22da25a 100644 --- a/src/compile/compile.c +++ b/src/compile/compile.c @@ -1821,7 +1821,7 @@ static enum jit_operand_abi jit_abi_from(enum ejit_type t) case EJIT_POINTER: return JIT_OPERAND_ABI_POINTER; case EJIT_FLOAT: return JIT_OPERAND_ABI_FLOAT; case EJIT_DOUBLE: return JIT_OPERAND_ABI_DOUBLE; - default: + default: break; } abort(); @@ -1841,7 +1841,7 @@ static size_t arg_offsetof(enum ejit_type t) case EJIT_POINTER: return offsetof(struct ejit_arg, p); case EJIT_FLOAT: return offsetof(struct ejit_arg, f); case EJIT_DOUBLE: return offsetof(struct ejit_arg, d); - default: + default: break; }; abort(); -- cgit v1.2.3 From 42f89542550033a3f22700e6b0fd71627a252f96 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Sat, 5 Apr 2025 13:46:09 +0300 Subject: implement minr_d/maxr_d --- deps/lightening | 2 +- include/ejit/ejit.h | 6 ++++++ src/common.h | 6 ++++++ src/compile/compile.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/ejit.c | 26 ++++++++++++++++++++++++++ src/interp.c | 22 ++++++++++++++++++++++ tests/maxr_d.c | 28 ++++++++++++++++++++++++++++ tests/maxr_f.c | 28 ++++++++++++++++++++++++++++ tests/minr_d.c | 28 ++++++++++++++++++++++++++++ tests/minr_f.c | 28 ++++++++++++++++++++++++++++ 10 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 tests/maxr_d.c create mode 100644 tests/maxr_f.c create mode 100644 tests/minr_d.c create mode 100644 tests/minr_f.c (limited to 'include') diff --git a/deps/lightening b/deps/lightening index 6421af4..5a72993 160000 --- a/deps/lightening +++ b/deps/lightening @@ -1 +1 @@ -Subproject commit 6421af4db1570a6a5fc3a15f3bcb2601d854ed0e +Subproject commit 5a72993827bc2b1735a7611c0036640cdb01b93b diff --git a/include/ejit/ejit.h b/include/ejit/ejit.h index b4b20c1..965103c 100644 --- a/include/ejit/ejit.h +++ b/include/ejit/ejit.h @@ -872,6 +872,12 @@ void ejit_truncr_d_64(struct ejit_func *s, struct ejit_gpr r0, void ejit_sqrtr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1); void ejit_sqrtr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1); +void ejit_minr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1, struct ejit_fpr r2); +void ejit_minr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1, struct ejit_fpr r2); + +void ejit_maxr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1, struct ejit_fpr r2); +void ejit_maxr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1, struct ejit_fpr r2); + struct ejit_reloc ejit_bltr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1); struct ejit_reloc ejit_bner(struct ejit_func *s, struct ejit_gpr r0, diff --git a/src/common.h b/src/common.h index 661220b..69f1441 100644 --- a/src/common.h +++ b/src/common.h @@ -145,6 +145,12 @@ enum ejit_opcode { EJIT_OP_SQRTR_F, EJIT_OP_SQRTR_D, + EJIT_OP_MINR_F, + EJIT_OP_MINR_D, + + EJIT_OP_MAXR_F, + EJIT_OP_MAXR_D, + EJIT_OP_EQR, EJIT_OP_NER, EJIT_OP_GTR, diff --git a/src/compile/compile.c b/src/compile/compile.c index 38c368f..dcf662b 100644 --- a/src/compile/compile.c +++ b/src/compile/compile.c @@ -1977,6 +1977,46 @@ static void resolve_relocs(jit_state_t *j, struct relocs *relocs, struct addrs * } } +static void compile_maxr_f(struct ejit_func *f, jit_state_t *j, + struct ejit_insn i) +{ + jit_fpr_t r0 = getfpr(f, i.r0, 0); + jit_fpr_t r1 = getloc_f(f, j, i.r1, 1); + jit_fpr_t r2 = getloc_f(f, j, i.r2, 2); + jit_maxr_f(j, r0, r1, r2); + putloc_f(f, j, i.r0, r0); +} + +static void compile_maxr_d(struct ejit_func *f, jit_state_t *j, + struct ejit_insn i) +{ + jit_fpr_t r0 = getfpr(f, i.r0, 0); + jit_fpr_t r1 = getloc_d(f, j, i.r1, 1); + jit_fpr_t r2 = getloc_d(f, j, i.r2, 2); + jit_maxr_d(j, r0, r1, r2); + putloc_d(f, j, i.r0, r0); +} + +static void compile_minr_f(struct ejit_func *f, jit_state_t *j, + struct ejit_insn i) +{ + jit_fpr_t r0 = getfpr(f, i.r0, 0); + jit_fpr_t r1 = getloc_f(f, j, i.r1, 1); + jit_fpr_t r2 = getloc_f(f, j, i.r2, 2); + jit_minr_f(j, r0, r1, r2); + putloc_f(f, j, i.r0, r0); +} + +static void compile_minr_d(struct ejit_func *f, jit_state_t *j, + struct ejit_insn i) +{ + jit_fpr_t r0 = getfpr(f, i.r0, 0); + jit_fpr_t r1 = getloc_d(f, j, i.r1, 1); + jit_fpr_t r2 = getloc_d(f, j, i.r2, 2); + jit_minr_d(j, r0, r1, r2); + putloc_d(f, j, i.r0, r0); +} + static size_t compile_fn_body(struct ejit_func *f, jit_state_t *j, void *arena, size_t size) { @@ -2143,6 +2183,12 @@ static size_t compile_fn_body(struct ejit_func *f, jit_state_t *j, void *arena, case EJIT_OP_SQRTR_F: compile_sqrtr_f(f, j, i); break; case EJIT_OP_SQRTR_D: compile_sqrtr_d(f, j, i); break; + case EJIT_OP_MINR_F: compile_minr_f(f, j, i); break; + case EJIT_OP_MINR_D: compile_minr_d(f, j, i); break; + + case EJIT_OP_MAXR_F: compile_maxr_f(f, j, i); break; + case EJIT_OP_MAXR_D: compile_maxr_d(f, j, i); break; + case EJIT_OP_EQR: compile_eqr(f, j, i); break; case EJIT_OP_EQR_F: compile_eqr_f(f, j, i); break; case EJIT_OP_EQR_D: compile_eqr_d(f, j, i); break; diff --git a/src/ejit.c b/src/ejit.c index 75f6a6a..c997a01 100644 --- a/src/ejit.c +++ b/src/ejit.c @@ -901,31 +901,37 @@ void ejit_ret(struct ejit_func *s) void ejit_retr(struct ejit_func *s, struct ejit_gpr r0) { + assert(s->rtype != EJIT_FLOAT && s->rtype != EJIT_DOUBLE); emit_insn_oxr(s, EJIT_OP_RETR, r0); } void ejit_retr_f(struct ejit_func *s, struct ejit_fpr f0) { + assert(s->rtype == EJIT_FLOAT); emit_insn_oxf(s, EJIT_OP_RETR_F, f0); } void ejit_retr_d(struct ejit_func *s, struct ejit_fpr f0) { + assert(s->rtype == EJIT_DOUBLE); emit_insn_oxf(s, EJIT_OP_RETR_D, f0); } void ejit_reti(struct ejit_func *s, int64_t i) { + assert(s->rtype != EJIT_FLOAT && s->rtype != EJIT_DOUBLE); emit_insn_oi(s, EJIT_OP_RETI, i); } void ejit_reti_f(struct ejit_func *s, float f) { + assert(s->rtype == EJIT_FLOAT); emit_insn_oF(s, EJIT_OP_RETI_F, f); } void ejit_reti_d(struct ejit_func *s, double f) { + assert(s->rtype == EJIT_DOUBLE); emit_insn_oD(s, EJIT_OP_RETI_D, f); } @@ -1382,6 +1388,26 @@ void ejit_sqrtr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1) emit_insn_off(s, EJIT_OP_SQRTR_D, r0, r1); } +void ejit_minr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1, struct ejit_fpr r2) +{ + emit_insn_offf(s, EJIT_OP_MINR_F, r0, r1, r2); +} + +void ejit_minr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1, struct ejit_fpr r2) +{ + emit_insn_offf(s, EJIT_OP_MINR_D, r0, r1, r2); +} + +void ejit_maxr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1, struct ejit_fpr r2) +{ + emit_insn_offf(s, EJIT_OP_MAXR_F, r0, r1, r2); +} + +void ejit_maxr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1, struct ejit_fpr r2) +{ + emit_insn_offf(s, EJIT_OP_MAXR_D, r0, r1, r2); +} + struct ejit_reloc ejit_bner(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1) { diff --git a/src/interp.c b/src/interp.c index e7be77b..57dbfbe 100644 --- a/src/interp.c +++ b/src/interp.c @@ -150,6 +150,12 @@ union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg pa [EJIT_OP_SQRTR_F] = &&SQRTR_F, [EJIT_OP_SQRTR_D] = &&SQRTR_D, + [EJIT_OP_MINR_F] = &&MINR_F, + [EJIT_OP_MINR_D] = &&MINR_D, + + [EJIT_OP_MAXR_F] = &&MAXR_F, + [EJIT_OP_MAXR_D] = &&MAXR_D, + [EJIT_OP_BNER] = &&BNER, [EJIT_OP_BNEI] = &&BNEI, [EJIT_OP_BNER_F] = &&BNER_F, @@ -791,6 +797,22 @@ union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg pa fpr[i.r0].f = sqrt(fpr[i.r1].f); DISPATCH(); + DO(MINR_F); + fpr[i.r0].f = fminf(fpr[i.r1].f, fpr[i.r2].f); + DISPATCH(); + + DO(MINR_D); + fpr[i.r0].d = fmin(fpr[i.r1].d, fpr[i.r2].d); + DISPATCH(); + + DO(MAXR_F); + fpr[i.r0].f = fmaxf(fpr[i.r1].f, fpr[i.r2].f); + DISPATCH(); + + DO(MAXR_D); + fpr[i.r0].d = fmax(fpr[i.r1].d, fpr[i.r2].d); + DISPATCH(); + DO(SQRTR_D); fpr[i.r0].d = sqrt(fpr[i.r1].d); DISPATCH(); diff --git a/tests/maxr_d.c b/tests/maxr_d.c new file mode 100644 index 0000000..3e35665 --- /dev/null +++ b/tests/maxr_d.c @@ -0,0 +1,28 @@ +#include +#include +#include "do_jit.h" + +int main(int argc, char *argv[]) +{ + (void)argv; + bool do_jit = argc > 1; + struct ejit_operand operands[2] = { + EJIT_OPERAND_FPR(0, EJIT_TYPE(double)), + EJIT_OPERAND_FPR(1, EJIT_TYPE(double)) + }; + + struct ejit_func *f = ejit_create_func(EJIT_TYPE(double), 2, operands); + + ejit_maxr_d(f, EJIT_FPR(2), EJIT_FPR(0), EJIT_FPR(1)); + ejit_retr_d(f, EJIT_FPR(2)); + + ejit_select_compile_func(f, 0, 3, EJIT_USE64(double), do_jit, true); + + assert(erfd2(f, EJIT_ARG(42., double), EJIT_ARG(69., double) + ) == 69.); + + assert(erfd2(f, EJIT_ARG(-42., double), EJIT_ARG(-69., double) + ) == -42.); + + ejit_destroy_func(f); +} diff --git a/tests/maxr_f.c b/tests/maxr_f.c new file mode 100644 index 0000000..581f867 --- /dev/null +++ b/tests/maxr_f.c @@ -0,0 +1,28 @@ +#include +#include +#include "do_jit.h" + +int main(int argc, char *argv[]) +{ + (void)argv; + bool do_jit = argc > 1; + struct ejit_operand operands[2] = { + EJIT_OPERAND_FPR(0, EJIT_TYPE(float)), + EJIT_OPERAND_FPR(1, EJIT_TYPE(float)) + }; + + struct ejit_func *f = ejit_create_func(EJIT_TYPE(float), 2, operands); + + ejit_maxr_f(f, EJIT_FPR(2), EJIT_FPR(0), EJIT_FPR(1)); + ejit_retr_f(f, EJIT_FPR(2)); + + ejit_select_compile_func(f, 0, 3, EJIT_USE64(float), do_jit, true); + + assert(erff2(f, EJIT_ARG(42., float), EJIT_ARG(69., float) + ) == 69.); + + assert(erff2(f, EJIT_ARG(-42., float), EJIT_ARG(-69., float) + ) == -42.); + + ejit_destroy_func(f); +} diff --git a/tests/minr_d.c b/tests/minr_d.c new file mode 100644 index 0000000..d0fb7c8 --- /dev/null +++ b/tests/minr_d.c @@ -0,0 +1,28 @@ +#include +#include +#include "do_jit.h" + +int main(int argc, char *argv[]) +{ + (void)argv; + bool do_jit = argc > 1; + struct ejit_operand operands[2] = { + EJIT_OPERAND_FPR(0, EJIT_TYPE(double)), + EJIT_OPERAND_FPR(1, EJIT_TYPE(double)) + }; + + struct ejit_func *f = ejit_create_func(EJIT_TYPE(double), 2, operands); + + ejit_minr_d(f, EJIT_FPR(2), EJIT_FPR(0), EJIT_FPR(1)); + ejit_retr_d(f, EJIT_FPR(2)); + + ejit_select_compile_func(f, 0, 3, EJIT_USE64(double), do_jit, true); + + assert(erfd2(f, EJIT_ARG(42., double), EJIT_ARG(69., double) + ) == 42.); + + assert(erfd2(f, EJIT_ARG(-42., double), EJIT_ARG(-69., double) + ) == -69.); + + ejit_destroy_func(f); +} diff --git a/tests/minr_f.c b/tests/minr_f.c new file mode 100644 index 0000000..b02ec06 --- /dev/null +++ b/tests/minr_f.c @@ -0,0 +1,28 @@ +#include +#include +#include "do_jit.h" + +int main(int argc, char *argv[]) +{ + (void)argv; + bool do_jit = argc > 1; + struct ejit_operand operands[2] = { + EJIT_OPERAND_FPR(0, EJIT_TYPE(float)), + EJIT_OPERAND_FPR(1, EJIT_TYPE(float)) + }; + + struct ejit_func *f = ejit_create_func(EJIT_TYPE(float), 2, operands); + + ejit_minr_f(f, EJIT_FPR(2), EJIT_FPR(0), EJIT_FPR(1)); + ejit_retr_f(f, EJIT_FPR(2)); + + ejit_select_compile_func(f, 0, 3, EJIT_USE64(float), do_jit, true); + + assert(erff2(f, EJIT_ARG(42., float), EJIT_ARG(69., float) + ) == 42.); + + assert(erff2(f, EJIT_ARG(-42., float), EJIT_ARG(-69., float) + ) == -69.); + + ejit_destroy_func(f); +} -- cgit v1.2.3 From 6824dd4b1ee22184f0e600115db3998924ed39d6 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Wed, 9 Apr 2025 19:56:33 +0300 Subject: initial tail call stuff --- .gitignore | 1 + deps/lightening | 2 +- examples/fib.c | 4 +- examples/sum.c | 78 +++++++++++++++++++++++++++++++++++++ include/ejit/ejit.h | 12 ++---- scripts/makefile | 5 ++- src/common.h | 7 +--- src/compile/compile.c | 106 ++++++++++++++++++++++++++++++++++++++++++-------- src/ejit.c | 79 ++++++++++++++----------------------- src/interp.c | 58 +++++++++++++-------------- 10 files changed, 236 insertions(+), 116 deletions(-) create mode 100644 examples/sum.c (limited to 'include') diff --git a/.gitignore b/.gitignore index 93b2293..50343e4 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ test-* examples/matrix_mult examples/loop examples/fib +examples/sum examples/*.d diff --git a/deps/lightening b/deps/lightening index 8d59c87..3f4127c 160000 --- a/deps/lightening +++ b/deps/lightening @@ -1 +1 @@ -Subproject commit 8d59c872edc5a21e7dd78768b291d0c4d8136e48 +Subproject commit 3f4127c3ef16177be55cf6153a206ca4f8a4859f diff --git a/examples/fib.c b/examples/fib.c index 999546b..fcb0659 100644 --- a/examples/fib.c +++ b/examples/fib.c @@ -20,12 +20,12 @@ struct ejit_func *compile(bool try_jit, bool im_scawed) struct ejit_operand arg[1] = { EJIT_OPERAND_GPR(0, EJIT_INT32) }; - ejit_calli_i(f, f, 1, arg); + ejit_calli(f, f, 1, arg); ejit_retval(f, EJIT_GPR(1)); /* loc 1 contains temp result */ /* fib(n - 2) */ ejit_subi(f, EJIT_GPR(0), EJIT_GPR(0), 1); - ejit_calli_i(f, f, 1, arg); + ejit_calli(f, f, 1, arg); ejit_retval(f, EJIT_GPR(0)); /* loc 0 now contains second temp result */ ejit_addr(f, EJIT_GPR(0), EJIT_GPR(0), EJIT_GPR(1)); /* add results */ diff --git a/examples/sum.c b/examples/sum.c new file mode 100644 index 0000000..cc8f54b --- /dev/null +++ b/examples/sum.c @@ -0,0 +1,78 @@ +#include +#include + +#include "../include/ejit/ejit.h" + +struct ejit_func *compile(bool try_jit, bool im_scawed) +{ + struct ejit_operand params[2] = { + EJIT_OPERAND_GPR(0, EJIT_INT32), /* loc 0 contains s */ + EJIT_OPERAND_GPR(1, EJIT_INT32) /* loc 1 contains n */ + }; + struct ejit_func *f = ejit_create_func(EJIT_INT32, 2, params); + + /* n == 0, return s */ + struct ejit_reloc recurse = ejit_bnei(f, EJIT_GPR(1), 0); + ejit_retr(f, EJIT_GPR(0)); + ejit_patch(f, recurse, ejit_label(f)); + + /* s += n */ + ejit_addr(f, EJIT_GPR(0), EJIT_GPR(0), EJIT_GPR(1)); + + /* n -= 1 */ + ejit_subi(f, EJIT_GPR(1), EJIT_GPR(1), 1); + + struct ejit_operand args[2] = { + EJIT_OPERAND_GPR(0, EJIT_INT32), /* s */ + EJIT_OPERAND_GPR(1, EJIT_INT32) /* n */ + }; + + /* recurse */ + ejit_movi(f, EJIT_GPR(2), (uintptr_t)f); + ejit_tailr(f, EJIT_GPR(2), 2, args); + + ejit_select_compile_func(f, 3, 0, EJIT_USE64(uintptr_t), try_jit, im_scawed); + return f; +} + +int main(int argc, char *argv[]) +{ + if(argc != 4){ + fprintf(stderr, "Usage: %s compile_num loop_num jit\n", argv[0]); + return -1; + } + + int jit_level = strtoull(argv[3], 0, 0); + size_t compile_num = strtoull(argv[1], 0, 0); + struct ejit_func **info = calloc(compile_num, sizeof(struct ejit_func *)); + + clock_t t = clock(); + for(size_t i = 0; i < compile_num; ++i){ + info[i] = compile(jit_level > 0, jit_level > 1); + } + t = clock() - t; + + double compile_time_total = ((double)t) / CLOCKS_PER_SEC; + double compile_time_one = compile_time_total / compile_num; + printf("Compilation for n = %zu took %fs (1/%f).\n", + compile_num, compile_time_total, compile_time_one); + + size_t run_num = strtoull(argv[2], 0, 0); + t = clock(); + struct ejit_arg args[2] = { + (struct ejit_arg){.type = EJIT_INT32, .l = 0}, /* s */ + (struct ejit_arg){.type = EJIT_INT32, .l = run_num} /* n */ + }; + int32_t result = ejit_run_func_i(info[0], 2, args); + t = clock() - t; + + double run_time_total = ((double)t) / CLOCKS_PER_SEC; + printf("Running loop for n = %zu took %fs with res %ld\n", + run_num, run_time_total, (long)result); + + for(size_t i = 0; i < compile_num; ++i) + ejit_destroy_func(info[i]); + + free(info); + return 0; +} diff --git a/include/ejit/ejit.h b/include/ejit/ejit.h index 965103c..aa42eca 100644 --- a/include/ejit/ejit.h +++ b/include/ejit/ejit.h @@ -454,18 +454,12 @@ typedef double (*ejit_escape_d_t)(size_t argc, const struct ejit_arg args[argc]) struct ejit_label ejit_label(struct ejit_func *s); -void ejit_calli_i(struct ejit_func *s, struct ejit_func *f, size_t argc, - const struct ejit_operand args[argc]); +void ejit_tailr(struct ejit_func *s, struct ejit_gpr target, + size_t argc, const struct ejit_operand args[argc]); -void ejit_calli_l(struct ejit_func *s, struct ejit_func *f, size_t argc, +void ejit_calli(struct ejit_func *s, struct ejit_func *f, size_t argc, const struct ejit_operand args[argc]); -void ejit_calli_f(struct ejit_func *s, struct ejit_func *f, size_t argc, - const struct ejit_operand args[argc]); - -void ejit_calli_d(struct ejit_func *s, struct ejit_func *f, size_t argc, - const struct ejit_operand args[argc]); - void ejit_escapei_i(struct ejit_func *s, ejit_escape_i_t f, size_t argc, const struct ejit_operand args[argc]); diff --git a/scripts/makefile b/scripts/makefile index 5178359..dbb7a1c 100644 --- a/scripts/makefile +++ b/scripts/makefile @@ -65,7 +65,7 @@ COMPILE_EJIT = $(COMPILE) $(EJIT_FLAGS) libejit.a: $(EJIT_OBJS) $(CROSS_COMPILE)ar rcs libejit.a $(EJIT_OBJS) -examples: examples/loop examples/fib examples/matrix_mult +examples: examples/loop examples/fib examples/sum examples/matrix_mult examples/matrix_mult: examples/matrix_mult.c libejit.a $(COMPILE_EJIT) examples/matrix_mult.c libejit.a -o $@ -lm @@ -76,6 +76,9 @@ examples/loop: examples/loop.c libejit.a examples/fib: examples/fib.c libejit.a $(COMPILE_EJIT) examples/fib.c libejit.a -o $@ -lm +examples/sum: examples/sum.c libejit.a + $(COMPILE_EJIT) examples/sum.c libejit.a -o $@ -lm + # might lint some common things twice .PHONY: lint: $(TRISCV_LINTS) diff --git a/src/common.h b/src/common.h index 69f1441..3512717 100644 --- a/src/common.h +++ b/src/common.h @@ -218,10 +218,8 @@ enum ejit_opcode { EJIT_OP_ESCAPEI_F, EJIT_OP_ESCAPEI_D, - EJIT_OP_CALLI_I, - EJIT_OP_CALLI_L, - EJIT_OP_CALLI_F, - EJIT_OP_CALLI_D, + EJIT_OP_CALLI, + EJIT_OP_TAILR, EJIT_OP_RETR, EJIT_OP_RETI, @@ -308,7 +306,6 @@ union interp_ret { union interp_ret ejit_run(struct ejit_func *f, size_t argc, struct ejit_arg args[argc], - bool run, void ***labels_wb); bool ejit_compile(struct ejit_func *f, bool use_64, bool im_scawed); diff --git a/src/compile/compile.c b/src/compile/compile.c index dcf662b..54d79f2 100644 --- a/src/compile/compile.c +++ b/src/compile/compile.c @@ -22,22 +22,22 @@ struct reloc_helper { /* skip assertions since we know they must be valid due to type checking earlier */ static long checked_run_i(struct ejit_func *f, size_t argc, struct ejit_arg args[argc]) { - return ejit_run(f, argc, args, true, NULL).i; + return ejit_run(f, argc, args, NULL).i; } static int64_t checked_run_l(struct ejit_func *f, size_t argc, struct ejit_arg args[argc]) { - return ejit_run(f, argc, args, true, NULL).i; + return ejit_run(f, argc, args, NULL).i; } static float checked_run_f(struct ejit_func *f, size_t argc, struct ejit_arg args[argc]) { - return ejit_run(f, argc, args, true, NULL).f; + return ejit_run(f, argc, args, NULL).f; } static double checked_run_d(struct ejit_func *f, size_t argc, struct ejit_arg args[argc]) { - return ejit_run(f, argc, args, true, NULL).f; + return ejit_run(f, argc, args, NULL).f; } static void *alloc_arena(size_t size, bool im_scawed) @@ -47,6 +47,11 @@ static void *alloc_arena(size_t size, bool im_scawed) MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); } +static void assert_helper(const char *msg) +{ + assert(false && msg); +} + static void free_arena(void *arena, size_t size) { munmap(arena, size); @@ -2042,8 +2047,6 @@ static size_t compile_fn_body(struct ejit_func *f, jit_state_t *j, void *arena, struct addrs addrs = addrs_create(); addrs_reserve(&addrs, insns_len(&f->insns)); - void *call = NULL; - size_t label = 0; foreach_vec(ii, f->insns) { /* if we've hit a label, add it to our vector of label addresses */ @@ -2502,21 +2505,64 @@ static size_t compile_fn_body(struct ejit_func *f, jit_state_t *j, void *arena, break; } - case EJIT_OP_CALLI_L: -#if __WORDSIZE == 64 - call = checked_run_l; goto calli; -#else - assert(0 && "trying to compile calli_l on 32bit arch"); - break; + case EJIT_OP_TAILR: { + /* this is admittedly a slightly roundabout way of + * implementing tail calls and is arguably not the most + * performant way (if it works at all, heh) but for now + * I'm more interested in functionality than raw + * performance. Currently only supports two gpr + * registers, but should be fairly easy to extend with + * fprs as well */ + + assert(operands_len(&direct) <= 2); + jit_gpr_t r = getloc(f, j, i.r1, 0); + jit_ldxi(j, JIT_R0, r, offsetof(struct ejit_func, direct_call)); +#if defined(DEBUG) + jit_reloc_t assert_reloc = jit_bnei(j, JIT_R0, 0); /* null */ + jit_calli_1(j, assert_helper, + jit_operand_imm(JIT_OPERAND_ABI_POINTER, + (jit_imm_t)"trying to tail call interpreted function")); + jit_patch_here(j, assert_reloc); #endif + jit_operand_t regs[2] = { + jit_operand_gpr(JIT_OPERAND_ABI_WORD, JIT_R1), + jit_operand_gpr(JIT_OPERAND_ABI_WORD, JIT_R2) + }; + jit_move_operands(j, regs, direct.buf, operands_len(&direct)); + + /* with args safely in registers, reset stack/state + * while avoiding overwriting the call target */ + jit_gpr_t tmp = get_callr_temp(j); + jit_movr(j, tmp, JIT_R0); + + int frame_size = j->frame_size; + jit_shrink_stack(j, stack); + jit_leave_jit_abi(j, gprs, fprs, frame); + + /* now move args into place */ + jit_operand_t args[2] = {}; + foreach_vec(oi, direct) { + args[oi] = *operands_at(&direct, oi); + } - case EJIT_OP_CALLI_F: { call = checked_run_f; goto calli; } - case EJIT_OP_CALLI_D: { call = checked_run_d; goto calli; } - case EJIT_OP_CALLI_I: { call = checked_run_i; goto calli; -calli: + jit_locate_args(j, operands_len(&direct), args); + jit_move_operands(j, args, regs, operands_len(&direct)); + jit_jmpr(j, tmp); + j->frame_size = frame_size; + + operands_reset(&src); + operands_reset(&dst); + operands_reset(&direct); + break; + } + + case EJIT_OP_CALLI: { save_caller_save_regs(f, j); struct ejit_func *f = (struct ejit_func *)(uintptr_t)i.o; +#if __WORDSIZE != 64 + assert(f->rtype != EJIT_INT64 && f->rtype != EJIT_UINT64); +#endif if (f && f->direct_call) { jit_calli(j, f->direct_call, operands_len(&direct), direct.buf); restore_caller_save_regs(f, j); @@ -2535,6 +2581,16 @@ calli: * argument stack address */ jit_operand_gpr(JIT_OPERAND_ABI_POINTER, JIT_R0) }; + + void *call = NULL; + switch (f->rtype) { + case EJIT_INT64: + case EJIT_UINT64: call = checked_run_l; break; + case EJIT_FLOAT: call = checked_run_f; break; + case EJIT_DOUBLE: call = checked_run_d; break; + default: call = checked_run_i; break; + } + compile_imm_call(j, &src, &dst, call, 3, args); restore_caller_save_regs(f, j); @@ -2552,39 +2608,55 @@ calli: jit_gpr_t r = getloc(f, j, i.r1, 0); /* R0 won't get overwritten by jit_leave_jit_abi */ jit_movr(j, JIT_R0, r); + + /* keep track of frame size so we can continue + * generating code after 'leaving' the ABI. Bit of a + * hack, should maybe codify this better in the + * lightening API? */ + int frame_size = j->frame_size; jit_shrink_stack(j, stack); jit_leave_jit_abi(j, gprs, fprs, frame); jit_retr(j, JIT_R0); + j->frame_size = frame_size; break; } case EJIT_OP_RETR_F: { jit_fpr_t r = getloc_f(f, j, i.r1, 0); jit_movr_f(j, JIT_F0, r); + + int frame_size = j->frame_size; jit_shrink_stack(j, stack); jit_leave_jit_abi(j, gprs, fprs, frame); jit_retr_f(j, JIT_F0); + j->frame_size = frame_size; break; } case EJIT_OP_RETR_D: { jit_fpr_t r = getloc_d(f, j, i.r1, 0); jit_movr_d(j, JIT_F0, r); + + int frame_size = j->frame_size; jit_shrink_stack(j, stack); jit_leave_jit_abi(j, gprs, fprs, frame); jit_retr_d(j, JIT_F0); + j->frame_size = frame_size; break; } case EJIT_OP_RETI: { + int frame_size = j->frame_size; jit_shrink_stack(j, stack); jit_leave_jit_abi(j, gprs, fprs, frame); jit_reti(j, i.o); + j->frame_size = frame_size; break; } case EJIT_OP_END: { - /* 'void' return */ + /* 'void' return, must be last thing in function so no + * need to keep track of frame size */ jit_shrink_stack(j, stack); jit_leave_jit_abi(j, gprs, fprs, frame); jit_reti(j, 0); diff --git a/src/ejit.c b/src/ejit.c index 571a274..059d5d4 100644 --- a/src/ejit.c +++ b/src/ejit.c @@ -414,7 +414,7 @@ void ejit_select_compile_func(struct ejit_func *f, size_t gpr, size_t fpr, void **labels; /* just get labels, don't actually run anything yet */ - ejit_run(f, 0, NULL, false, &labels); + ejit_run(f, 0, NULL, &labels); foreach_vec(ii, f->insns) { struct ejit_insn i = *insns_at(&f->insns, ii); @@ -456,67 +456,48 @@ void ejit_patch(struct ejit_func *f, struct ejit_reloc r, struct ejit_label l) *insns_at(&f->insns, r.insn) = i; } -void ejit_calli_i(struct ejit_func *s, struct ejit_func *f, size_t argc, +void ejit_tailr(struct ejit_func *s, struct ejit_gpr target, size_t argc, const struct ejit_operand args[argc]) { s->max_args = argc > s->max_args ? argc : s->max_args; - check_operands(f, argc, args); + /** @todo check that gpr_args <= 2 and fpr_args <= 3 (?) */ + size_t gpr_args = 0, fpr_args = 0; for (size_t i = 0; i < argc; ++i) { switch (args[i].kind) { - case EJIT_OPERAND_GPR: emit_insn_ar(s, EJIT_OP_ARG, i, args[i].type, EJIT_GPR(args[i].r)); break; - case EJIT_OPERAND_FPR: emit_insn_af(s, EJIT_OP_ARG_F, i, args[i].type, EJIT_FPR(args[i].r)); break; - case EJIT_OPERAND_IMM: emit_insn_ai(s, EJIT_OP_ARG_I, i, args[i].type, args[i].r); break; - case EJIT_OPERAND_FLT: emit_insn_ad(s, EJIT_OP_ARG_FI, i, args[i].type, args[i].d); break; - default: abort(); - } - } - - emit_insn_op(s, EJIT_OP_CALLI_I, f); -} - -void ejit_calli_l(struct ejit_func *s, struct ejit_func *f, size_t argc, - const struct ejit_operand args[argc]) -{ - s->use_64 = true; - s->max_args = argc > s->max_args ? argc : s->max_args; - check_operands(f, argc, args); + case EJIT_OPERAND_GPR: + gpr_args++; + emit_insn_ar(s, EJIT_OP_ARG, i, args[i].type, EJIT_GPR(args[i].r)); + break; - for (size_t i = 0; i < argc; ++i) { - switch (args[i].kind) { - case EJIT_OPERAND_GPR: emit_insn_ar(s, EJIT_OP_ARG, i, args[i].type, EJIT_GPR(args[i].r)); break; - case EJIT_OPERAND_FPR: emit_insn_af(s, EJIT_OP_ARG_F, i, args[i].type, EJIT_FPR(args[i].r)); break; - case EJIT_OPERAND_IMM: emit_insn_ai(s, EJIT_OP_ARG_I, i, args[i].type, args[i].r); break; - case EJIT_OPERAND_FLT: emit_insn_ad(s, EJIT_OP_ARG_FI, i, args[i].type, args[i].d); break; - default: abort(); - } - } + case EJIT_OPERAND_FPR: + fpr_args++; + emit_insn_af(s, EJIT_OP_ARG_F, i, args[i].type, EJIT_FPR(args[i].r)); + break; - emit_insn_op(s, EJIT_OP_CALLI_L, f); -} + case EJIT_OPERAND_IMM: + gpr_args++; + emit_insn_ai(s, EJIT_OP_ARG_I, i, args[i].type, args[i].r); + break; -void ejit_calli_f(struct ejit_func *s, struct ejit_func *f, size_t argc, - const struct ejit_operand args[argc]) -{ - s->max_args = argc > s->max_args ? argc : s->max_args; - check_operands(f, argc, args); + case EJIT_OPERAND_FLT: + fpr_args++; + emit_insn_ad(s, EJIT_OP_ARG_FI, i, args[i].type, args[i].d); + break; - for (size_t i = 0; i < argc; ++i) { - switch (args[i].kind) { - case EJIT_OPERAND_GPR: emit_insn_ar(s, EJIT_OP_ARG, i, args[i].type, EJIT_GPR(args[i].r)); break; - case EJIT_OPERAND_FPR: emit_insn_af(s, EJIT_OP_ARG_F, i, args[i].type, EJIT_FPR(args[i].r)); break; - case EJIT_OPERAND_IMM: emit_insn_ai(s, EJIT_OP_ARG_I, i, args[i].type, args[i].r); break; - case EJIT_OPERAND_FLT: emit_insn_ad(s, EJIT_OP_ARG_FI, i, args[i].type, args[i].d); break; default: abort(); } } - emit_insn_op(s, EJIT_OP_CALLI_F, f); + assert(gpr_args <= 2 && fpr_args == 0 + && "only 2 gpr args and 0 fpr args supported in tail calls for now"); + emit_insn_oxr(s, EJIT_OP_TAILR, target); } -void ejit_calli_d(struct ejit_func *s, struct ejit_func *f, size_t argc, +void ejit_calli(struct ejit_func *s, struct ejit_func *f, size_t argc, const struct ejit_operand args[argc]) { + s->use_64 = f->rtype == EJIT_INT64 || f->rtype == EJIT_UINT64; s->max_args = argc > s->max_args ? argc : s->max_args; check_operands(f, argc, args); @@ -530,7 +511,7 @@ void ejit_calli_d(struct ejit_func *s, struct ejit_func *f, size_t argc, } } - emit_insn_op(s, EJIT_OP_CALLI_D, f); + emit_insn_op(s, EJIT_OP_CALLI, f); } void ejit_escapei_i(struct ejit_func *s, ejit_escape_i_t f, size_t argc, @@ -1712,7 +1693,7 @@ long ejit_run_func_i(struct ejit_func *f, size_t argc, #endif ); - return ejit_run(f, argc, args, true, NULL).i; + return ejit_run(f, argc, args, NULL).i; } int64_t ejit_run_func_l(struct ejit_func *f, size_t argc, @@ -1720,21 +1701,21 @@ int64_t ejit_run_func_l(struct ejit_func *f, size_t argc, { check_args(f, argc, args); assert(f->rtype == EJIT_INT64 || f->rtype == EJIT_UINT64); - return ejit_run(f, argc, args, true, NULL).i; + return ejit_run(f, argc, args, NULL).i; } float ejit_run_func_f(struct ejit_func *f, size_t argc, struct ejit_arg args[argc]) { check_args(f, argc, args); assert(f->rtype == EJIT_FLOAT); - return ejit_run(f, argc, args, true, NULL).f; + return ejit_run(f, argc, args, NULL).f; } double ejit_run_func_d(struct ejit_func *f, size_t argc, struct ejit_arg args[argc]) { check_args(f, argc, args); assert(f->rtype == EJIT_DOUBLE); - return ejit_run(f, argc, args, true, NULL).f; + return ejit_run(f, argc, args, NULL).f; } struct ejit_arg ejit_run_func(struct ejit_func *f, size_t argc, struct ejit_arg args[argc]) diff --git a/src/interp.c b/src/interp.c index 6ef414d..132ba4a 100644 --- a/src/interp.c +++ b/src/interp.c @@ -5,7 +5,7 @@ /* this is the body of a given ejit_interp function, it assumes there's an * external int64_t retval and double retval_f into which it places the value to * be returned. Included from src/interp.c */ -union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg params[paramc], bool run, void ***labels_wb) +union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg params[paramc], void ***labels_wb) { static void *labels[EJIT_OPCODE_COUNT] = { [EJIT_OP_MOVI] = &&MOVI, @@ -213,10 +213,9 @@ union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg pa [EJIT_OP_PARAM] = &&PARAM, [EJIT_OP_PARAM_F] = &&PARAM_F, - [EJIT_OP_CALLI_I] = &&CALLI_I, - [EJIT_OP_CALLI_L] = &&CALLI_L, - [EJIT_OP_CALLI_F] = &&CALLI_F, - [EJIT_OP_CALLI_D] = &&CALLI_D, + [EJIT_OP_CALLI] = &&CALLI, + [EJIT_OP_TAILR] = &&TAILR, + [EJIT_OP_ESCAPEI_I] = &&ESCAPEI_I, [EJIT_OP_ESCAPEI_F] = &&ESCAPEI_F, [EJIT_OP_ESCAPEI_L] = &&ESCAPEI_L, @@ -226,13 +225,12 @@ union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg pa [EJIT_OP_END] = &&END, }; - if (!run) { + if (labels_wb) { *labels_wb = labels; return (union interp_ret){.i = 0}; } assert(f->size && "trying to run a function that hasn't been compiled"); - if (f->extern_call) { if (f->rtype == EJIT_INT64 || f->rtype == EJIT_UINT64) return (union interp_ret){ @@ -254,8 +252,8 @@ union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg pa }; } - int64_t retval = 0; double retval_f = 0.0; - +top: + union interp_ret retval = {.i = 0}; union fpr { double d; float f; @@ -994,15 +992,15 @@ union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg pa DISPATCH(); DO(RETVAL); - gpr[i.r0] = retval; + gpr[i.r0] = retval.i; DISPATCH(); DO(RETVAL_F); - fpr[i.r0].f = retval_f; + fpr[i.r0].f = retval.f; DISPATCH(); DO(RETVAL_D); - fpr[i.r0].d = retval_f; + fpr[i.r0].d = retval.f; DISPATCH(); DO(PARAM); @@ -1058,51 +1056,47 @@ union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg pa args[argc++] = a; DISPATCH(); - DO(CALLI_I); - struct ejit_func *f = i.p; - retval = ejit_run(f, argc, args, true, NULL).i; - argc = 0; - DISPATCH(); + DO(TAILR); + f = (struct ejit_func *)gpr[i.r1]; - DO(CALLI_L); - struct ejit_func *f = i.p; - retval = ejit_run(f, argc, args, true, NULL).i; - argc = 0; - DISPATCH(); + /** @todo we could potentially just interpret the func as a fallback + * instead of aborting here, but this is good enough for now */ + assert(!f->direct_call && "trying to interpret compiled fun"); - DO(CALLI_F); - struct ejit_func *f = i.p; - retval_f = ejit_run(f, argc, args, true, NULL).f; - argc = 0; + paramc = argc; + for (size_t i = 0; i < argc; ++i) + params[i] = args[i]; + + goto top; DISPATCH(); - DO(CALLI_D); + DO(CALLI); struct ejit_func *f = i.p; - retval_f = ejit_run(f, argc, args, true, NULL).f; + retval = ejit_run(f, argc, args, NULL); argc = 0; DISPATCH(); DO(ESCAPEI_I); ejit_escape_i_t f = i.p; - retval = f(argc, args); + retval.i = f(argc, args); argc = 0; DISPATCH(); DO(ESCAPEI_L); ejit_escape_l_t f = i.p; - retval = f(argc, args); + retval.i = f(argc, args); argc = 0; DISPATCH(); DO(ESCAPEI_F); ejit_escape_f_t f = i.p; - retval_f = f(argc, args); + retval.f = f(argc, args); argc = 0; DISPATCH(); DO(ESCAPEI_D); ejit_escape_d_t f = i.p; - retval_f = f(argc, args); + retval.f = f(argc, args); argc = 0; DISPATCH(); -- cgit v1.2.3 From 057131cb20fb1c46e90adecfb4a16eb62f100580 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Wed, 9 Apr 2025 20:20:48 +0300 Subject: add taili --- include/ejit/ejit.h | 3 +++ src/common.h | 2 ++ src/compile/compile.c | 35 +++++++++++++++++++++++++++++++++++ src/ejit.c | 44 +++++++++++++++++++++++++++++++++++++++++++- src/interp.c | 14 ++++++++++++++ tests/taili.c | 39 +++++++++++++++++++++++++++++++++++++++ tests/tailr.c | 41 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 tests/taili.c create mode 100644 tests/tailr.c (limited to 'include') diff --git a/include/ejit/ejit.h b/include/ejit/ejit.h index aa42eca..5baaab6 100644 --- a/include/ejit/ejit.h +++ b/include/ejit/ejit.h @@ -457,6 +457,9 @@ struct ejit_label ejit_label(struct ejit_func *s); void ejit_tailr(struct ejit_func *s, struct ejit_gpr target, size_t argc, const struct ejit_operand args[argc]); +void ejit_taili(struct ejit_func *s, struct ejit_func *f, + size_t argc, const struct ejit_operand args[argc]); + void ejit_calli(struct ejit_func *s, struct ejit_func *f, size_t argc, const struct ejit_operand args[argc]); diff --git a/src/common.h b/src/common.h index 3512717..333c794 100644 --- a/src/common.h +++ b/src/common.h @@ -219,7 +219,9 @@ enum ejit_opcode { EJIT_OP_ESCAPEI_D, EJIT_OP_CALLI, + EJIT_OP_TAILR, + EJIT_OP_TAILI, EJIT_OP_RETR, EJIT_OP_RETI, diff --git a/src/compile/compile.c b/src/compile/compile.c index 54d79f2..60059d5 100644 --- a/src/compile/compile.c +++ b/src/compile/compile.c @@ -2505,6 +2505,40 @@ static size_t compile_fn_body(struct ejit_func *f, jit_state_t *j, void *arena, break; } + case EJIT_OP_TAILI: { + /* a bit of copy-paste between this and the next func, + * hmm */ + assert(operands_len(&direct) <= 2); + struct ejit_func *f = (struct ejit_func *)(uintptr_t)i.o; + assert(f->direct_call); + + jit_operand_t regs[2] = { + jit_operand_gpr(JIT_OPERAND_ABI_WORD, JIT_R1), + jit_operand_gpr(JIT_OPERAND_ABI_WORD, JIT_R2) + }; + jit_move_operands(j, regs, direct.buf, operands_len(&direct)); + + int frame_size = j->frame_size; + jit_shrink_stack(j, stack); + jit_leave_jit_abi(j, gprs, fprs, frame); + + /* now move args into place */ + jit_operand_t args[2] = {}; + foreach_vec(oi, direct) { + args[oi] = *operands_at(&direct, oi); + } + + jit_locate_args(j, operands_len(&direct), args); + jit_move_operands(j, args, regs, operands_len(&direct)); + jit_jmpi(j, f->direct_call); + j->frame_size = frame_size; + + operands_reset(&src); + operands_reset(&dst); + operands_reset(&direct); + break; + } + case EJIT_OP_TAILR: { /* this is admittedly a slightly roundabout way of * implementing tail calls and is arguably not the most @@ -2518,6 +2552,7 @@ static size_t compile_fn_body(struct ejit_func *f, jit_state_t *j, void *arena, jit_gpr_t r = getloc(f, j, i.r1, 0); jit_ldxi(j, JIT_R0, r, offsetof(struct ejit_func, direct_call)); #if defined(DEBUG) + /** @todo other checks? */ jit_reloc_t assert_reloc = jit_bnei(j, JIT_R0, 0); /* null */ jit_calli_1(j, assert_helper, jit_operand_imm(JIT_OPERAND_ABI_POINTER, diff --git a/src/ejit.c b/src/ejit.c index 059d5d4..0701b90 100644 --- a/src/ejit.c +++ b/src/ejit.c @@ -456,12 +456,54 @@ void ejit_patch(struct ejit_func *f, struct ejit_reloc r, struct ejit_label l) *insns_at(&f->insns, r.insn) = i; } +void ejit_taili(struct ejit_func *s, struct ejit_func *f, + size_t argc, const struct ejit_operand args[argc]) +{ + assert(s->rtype == f->rtype); + + s->max_args = argc > s->max_args ? argc : s->max_args; + check_operands(f, argc, args); + + size_t gpr_args = 0, fpr_args = 0; + for (size_t i = 0; i < argc; ++i) { + switch (args[i].kind) { + case EJIT_OPERAND_GPR: + gpr_args++; + emit_insn_ar(s, EJIT_OP_ARG, i, args[i].type, EJIT_GPR(args[i].r)); + break; + + case EJIT_OPERAND_FPR: + fpr_args++; + emit_insn_af(s, EJIT_OP_ARG_F, i, args[i].type, EJIT_FPR(args[i].r)); + break; + + case EJIT_OPERAND_IMM: + gpr_args++; + emit_insn_ai(s, EJIT_OP_ARG_I, i, args[i].type, args[i].r); + break; + + case EJIT_OPERAND_FLT: + fpr_args++; + emit_insn_ad(s, EJIT_OP_ARG_FI, i, args[i].type, args[i].d); + break; + + default: abort(); + } + } + + assert(gpr_args <= 2 && fpr_args == 0 + && "only 2 gpr args and 0 fpr args supported in tail calls for now"); + emit_insn_op(s, EJIT_OP_TAILI, f); +} + void ejit_tailr(struct ejit_func *s, struct ejit_gpr target, size_t argc, const struct ejit_operand args[argc]) { s->max_args = argc > s->max_args ? argc : s->max_args; - /** @todo check that gpr_args <= 2 and fpr_args <= 3 (?) */ + /* operands must match */ + check_operands(s, argc, args); + size_t gpr_args = 0, fpr_args = 0; for (size_t i = 0; i < argc; ++i) { switch (args[i].kind) { diff --git a/src/interp.c b/src/interp.c index 132ba4a..268bfb3 100644 --- a/src/interp.c +++ b/src/interp.c @@ -214,7 +214,9 @@ union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg pa [EJIT_OP_PARAM_F] = &&PARAM_F, [EJIT_OP_CALLI] = &&CALLI, + [EJIT_OP_TAILR] = &&TAILR, + [EJIT_OP_TAILI] = &&TAILI, [EJIT_OP_ESCAPEI_I] = &&ESCAPEI_I, [EJIT_OP_ESCAPEI_F] = &&ESCAPEI_F, @@ -1056,6 +1058,18 @@ top: args[argc++] = a; DISPATCH(); + DO(TAILI); + f = (struct ejit_func *)(uintptr_t)i.o; + + assert(!f->direct_call && "trying to interpret compiled fun"); + + paramc = argc; + for (size_t i = 0; i < argc; ++i) + params[i] = args[i]; + + goto top; + DISPATCH(); + DO(TAILR); f = (struct ejit_func *)gpr[i.r1]; diff --git a/tests/taili.c b/tests/taili.c new file mode 100644 index 0000000..cc09f59 --- /dev/null +++ b/tests/taili.c @@ -0,0 +1,39 @@ +#include +#include +#include "do_jit.h" + +int main(int argc, char *argv[]) +{ + (void)argv; + bool do_jit = argc > 1; + struct ejit_operand operands[2] = { + EJIT_OPERAND_GPR(0, EJIT_INT32), /* s */ + EJIT_OPERAND_GPR(1, EJIT_INT32) /* n */ + }; + + struct ejit_func *f = ejit_create_func(EJIT_INT32, 2, operands); + + /* n == 0, return s */ + struct ejit_reloc r = ejit_bnei(f, EJIT_GPR(1), 0); + ejit_retr(f, EJIT_GPR(0)); + ejit_patch(f, r, ejit_label(f)); + + /* s += n */ + ejit_addr(f, EJIT_GPR(0), EJIT_GPR(0), EJIT_GPR(1)); + + /* n -= 1 */ + ejit_subi(f, EJIT_GPR(1), EJIT_GPR(1), 1); + + struct ejit_operand args[2] = { + EJIT_OPERAND_GPR(0, EJIT_INT32), /* s */ + EJIT_OPERAND_GPR(1, EJIT_INT32) /* n */ + }; + ejit_taili(f, f, 2, args); + + ejit_select_compile_func(f, 2, 0, EJIT_USE64(uintptr_t), do_jit, true); + + /* arbitrary number but large enough to most likely cause a stack fault + * if the tail call leaks memory or something */ + assert((int32_t)erfi2(f, EJIT_ARG(0, int32_t), EJIT_ARG(1000000, int32_t)) == 1784293664); + ejit_destroy_func(f); +} diff --git a/tests/tailr.c b/tests/tailr.c new file mode 100644 index 0000000..69ad44b --- /dev/null +++ b/tests/tailr.c @@ -0,0 +1,41 @@ +#include +#include +#include "do_jit.h" + +int main(int argc, char *argv[]) +{ + (void)argv; + bool do_jit = argc > 1; + struct ejit_operand operands[2] = { + EJIT_OPERAND_GPR(0, EJIT_INT32), /* s */ + EJIT_OPERAND_GPR(1, EJIT_INT32) /* n */ + }; + + struct ejit_func *f = ejit_create_func(EJIT_INT32, 2, operands); + + /* n == 0, return s */ + struct ejit_reloc r = ejit_bnei(f, EJIT_GPR(1), 0); + ejit_retr(f, EJIT_GPR(0)); + ejit_patch(f, r, ejit_label(f)); + + /* s += n */ + ejit_addr(f, EJIT_GPR(0), EJIT_GPR(0), EJIT_GPR(1)); + + /* n -= 1 */ + ejit_subi(f, EJIT_GPR(1), EJIT_GPR(1), 1); + + struct ejit_operand args[2] = { + EJIT_OPERAND_GPR(0, EJIT_INT32), /* s */ + EJIT_OPERAND_GPR(1, EJIT_INT32) /* n */ + }; + + ejit_movi(f, EJIT_GPR(2), (uintptr_t)f); + ejit_tailr(f, EJIT_GPR(2), 2, args); + + ejit_select_compile_func(f, 3, 0, EJIT_USE64(uintptr_t), do_jit, true); + + /* arbitrary number but large enough to most likely cause a stack fault + * if the tail call leaks memory or something */ + assert((int32_t)erfi2(f, EJIT_ARG(0, int32_t), EJIT_ARG(1000000, int32_t)) == 1784293664); + ejit_destroy_func(f); +} -- cgit v1.2.3 From be5c83ba8e57bc67beee41bc2c7227e6b8ebd9d5 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Wed, 9 Apr 2025 22:20:01 +0300 Subject: add callr_i/l/f/d --- include/ejit/ejit.h | 33 ++++++++---- src/common.h | 6 ++- src/compile/compile.c | 73 +++++++++++++++++++++----- src/ejit.c | 142 ++++++++++++++++++++++++++++++++++---------------- src/interp.c | 41 +++++++++++---- tests/calli.c | 41 +++++++++++++++ tests/callr_i.c | 42 +++++++++++++++ 7 files changed, 298 insertions(+), 80 deletions(-) create mode 100644 tests/calli.c create mode 100644 tests/callr_i.c (limited to 'include') diff --git a/include/ejit/ejit.h b/include/ejit/ejit.h index 5baaab6..ab06d8f 100644 --- a/include/ejit/ejit.h +++ b/include/ejit/ejit.h @@ -460,20 +460,33 @@ void ejit_tailr(struct ejit_func *s, struct ejit_gpr target, void ejit_taili(struct ejit_func *s, struct ejit_func *f, size_t argc, const struct ejit_operand args[argc]); -void ejit_calli(struct ejit_func *s, struct ejit_func *f, size_t argc, - const struct ejit_operand args[argc]); +/* return type can be deduced */ +void ejit_calli(struct ejit_func *s, struct ejit_func *f, + size_t argc, const struct ejit_operand args[argc]); -void ejit_escapei_i(struct ejit_func *s, ejit_escape_i_t f, size_t argc, - const struct ejit_operand args[argc]); +void ejit_callr_i(struct ejit_func *s, struct ejit_gpr target, + size_t argc, const struct ejit_operand args[argc]); -void ejit_escapei_l(struct ejit_func *s, ejit_escape_l_t f, size_t argc, - const struct ejit_operand args[argc]); +void ejit_callr_l(struct ejit_func *s, struct ejit_gpr target, + size_t argc, const struct ejit_operand args[argc]); -void ejit_escapei_f(struct ejit_func *s, ejit_escape_f_t f, size_t argc, - const struct ejit_operand args[argc]); +void ejit_callr_f(struct ejit_func *s, struct ejit_gpr target, + size_t argc, const struct ejit_operand args[argc]); -void ejit_escapei_d(struct ejit_func *s, ejit_escape_d_t f, size_t argc, - const struct ejit_operand args[argc]); +void ejit_callr_d(struct ejit_func *s, struct ejit_gpr target, + size_t argc, const struct ejit_operand args[argc]); + +void ejit_escapei_i(struct ejit_func *s, ejit_escape_i_t f, + size_t argc, const struct ejit_operand args[argc]); + +void ejit_escapei_l(struct ejit_func *s, ejit_escape_l_t f, + size_t argc, const struct ejit_operand args[argc]); + +void ejit_escapei_f(struct ejit_func *s, ejit_escape_f_t f, + size_t argc, const struct ejit_operand args[argc]); + +void ejit_escapei_d(struct ejit_func *s, ejit_escape_d_t f, + size_t argc, const struct ejit_operand args[argc]); void ejit_ret(struct ejit_func *s); void ejit_retr(struct ejit_func *s, struct ejit_gpr r0); diff --git a/src/common.h b/src/common.h index 333c794..dc970f0 100644 --- a/src/common.h +++ b/src/common.h @@ -218,8 +218,12 @@ enum ejit_opcode { EJIT_OP_ESCAPEI_F, EJIT_OP_ESCAPEI_D, - EJIT_OP_CALLI, + EJIT_OP_CALLR_I, + EJIT_OP_CALLR_L, + EJIT_OP_CALLR_F, + EJIT_OP_CALLR_D, + EJIT_OP_CALLI, EJIT_OP_TAILR, EJIT_OP_TAILI, diff --git a/src/compile/compile.c b/src/compile/compile.c index 60059d5..bfcb12d 100644 --- a/src/compile/compile.c +++ b/src/compile/compile.c @@ -2476,17 +2476,10 @@ static size_t compile_fn_body(struct ejit_func *f, jit_state_t *j, void *arena, break; } + case EJIT_OP_ESCAPEI_I: case EJIT_OP_ESCAPEI_L: -#if __WORDSIZE == 64 - /* fallthrough */ -#else - assert(0 && "trying to compile escapei_l on 32bit arch"); - break; -#endif - - case EJIT_OP_ESCAPEI_D: case EJIT_OP_ESCAPEI_F: - case EJIT_OP_ESCAPEI_I: { + case EJIT_OP_ESCAPEI_D: { save_caller_save_regs(f, j); jit_operand_t args[2] = { @@ -2550,15 +2543,25 @@ static size_t compile_fn_body(struct ejit_func *f, jit_state_t *j, void *arena, assert(operands_len(&direct) <= 2); jit_gpr_t r = getloc(f, j, i.r1, 0); - jit_ldxi(j, JIT_R0, r, offsetof(struct ejit_func, direct_call)); + #if defined(DEBUG) - /** @todo other checks? */ - jit_reloc_t assert_reloc = jit_bnei(j, JIT_R0, 0); /* null */ + jit_ldxi(j, JIT_R1, r, offsetof(struct ejit_func, rtype)); + jit_reloc_t rtype_reloc = jit_beqi(j, JIT_R1, f->rtype); + jit_calli_1(j, assert_helper, + jit_operand_imm(JIT_OPERAND_ABI_POINTER, + (jit_imm_t)"trying to tail call different rtype")); + + jit_patch_here(j, rtype_reloc); + + jit_ldxi(j, JIT_R1, r, offsetof(struct ejit_func, direct_call)); + jit_reloc_t direct_reloc = jit_bnei(j, JIT_R1, 0); /* null */ jit_calli_1(j, assert_helper, jit_operand_imm(JIT_OPERAND_ABI_POINTER, (jit_imm_t)"trying to tail call interpreted function")); - jit_patch_here(j, assert_reloc); + jit_patch_here(j, direct_reloc); #endif + + jit_ldxi(j, JIT_R0, r, offsetof(struct ejit_func, direct_call)); jit_operand_t regs[2] = { jit_operand_gpr(JIT_OPERAND_ABI_WORD, JIT_R1), jit_operand_gpr(JIT_OPERAND_ABI_WORD, JIT_R2) @@ -2591,6 +2594,50 @@ static size_t compile_fn_body(struct ejit_func *f, jit_state_t *j, void *arena, break; } + case EJIT_OP_CALLR_I: + case EJIT_OP_CALLR_L: + case EJIT_OP_CALLR_F: + case EJIT_OP_CALLR_D: { + save_caller_save_regs(f, j); + + jit_gpr_t target = getgpr(f, i.r1, 0); + + /* check if there's a direct call avaiable */ + jit_ldxi(j, JIT_R1, target, offsetof(struct ejit_func, direct_call)); + jit_reloc_t direct_reloc = jit_beqi(j, JIT_R0, 0); + /* we can do a jit -> jit call */ + jit_callr(j, JIT_R1, operands_len(&direct), direct.buf); + jit_reloc_t out_reloc = jit_jmp(j); + + jit_patch_here(j, direct_reloc); + + /* we must do a jit -> bytecode call */ + jit_operand_t args[3] = { + jit_operand_gpr(JIT_OPERAND_ABI_POINTER, JIT_R1), + jit_operand_imm(JIT_OPERAND_ABI_WORD, operands_len(&src) / 2), + /* compile_imm_call populate JIT_R0 with the + * argument stack address */ + jit_operand_gpr(JIT_OPERAND_ABI_POINTER, JIT_R0) + }; + void *call = NULL; + switch (i.op) { + case EJIT_OP_CALLR_I: call = ejit_run_func_i; break; + case EJIT_OP_CALLR_L: call = ejit_run_func_l; break; + case EJIT_OP_CALLR_F: call = ejit_run_func_f; break; + case EJIT_OP_CALLR_D: call = ejit_run_func_d; break; + default: abort(); + } + + compile_imm_call(j, &src, &dst, call, 3, args); + jit_patch_here(j, out_reloc); + restore_caller_save_regs(f, j); + + operands_reset(&src); + operands_reset(&dst); + operands_reset(&direct); + break; + } + case EJIT_OP_CALLI: { save_caller_save_regs(f, j); diff --git a/src/ejit.c b/src/ejit.c index 0701b90..e8ff99e 100644 --- a/src/ejit.c +++ b/src/ejit.c @@ -545,10 +545,22 @@ void ejit_calli(struct ejit_func *s, struct ejit_func *f, size_t argc, for (size_t i = 0; i < argc; ++i) { switch (args[i].kind) { - case EJIT_OPERAND_GPR: emit_insn_ar(s, EJIT_OP_ARG, i, args[i].type, EJIT_GPR(args[i].r)); break; - case EJIT_OPERAND_FPR: emit_insn_af(s, EJIT_OP_ARG_F, i, args[i].type, EJIT_FPR(args[i].r)); break; - case EJIT_OPERAND_IMM: emit_insn_ai(s, EJIT_OP_ARG_I, i, args[i].type, args[i].r); break; - case EJIT_OPERAND_FLT: emit_insn_ad(s, EJIT_OP_ARG_FI, i, args[i].type, args[i].d); break; + case EJIT_OPERAND_GPR: + emit_insn_ar(s, EJIT_OP_ARG, i, args[i].type, EJIT_GPR(args[i].r)); + break; + + case EJIT_OPERAND_FPR: + emit_insn_af(s, EJIT_OP_ARG_F, i, args[i].type, EJIT_FPR(args[i].r)); + break; + + case EJIT_OPERAND_IMM: + emit_insn_ai(s, EJIT_OP_ARG_I, i, args[i].type, args[i].r); + break; + + case EJIT_OPERAND_FLT: + emit_insn_ad(s, EJIT_OP_ARG_FI, i, args[i].type, args[i].d); + break; + default: abort(); } } @@ -556,73 +568,113 @@ void ejit_calli(struct ejit_func *s, struct ejit_func *f, size_t argc, emit_insn_op(s, EJIT_OP_CALLI, f); } -void ejit_escapei_i(struct ejit_func *s, ejit_escape_i_t f, size_t argc, - const struct ejit_operand args[argc]) +static void ejit_callr(struct ejit_func *s, enum ejit_opcode op, struct ejit_gpr target, + size_t argc, const struct ejit_operand args[argc]) { + s->use_64 = op == EJIT_OP_CALLR_L; s->max_args = argc > s->max_args ? argc : s->max_args; + for (size_t i = 0; i < argc; ++i) { switch (args[i].kind) { - case EJIT_OPERAND_GPR: emit_insn_ar(s, EJIT_OP_ARG, i, args[i].type, EJIT_GPR(args[i].r)); break; - case EJIT_OPERAND_FPR: emit_insn_af(s, EJIT_OP_ARG_F, i, args[i].type, EJIT_FPR(args[i].r)); break; - case EJIT_OPERAND_IMM: emit_insn_ai(s, EJIT_OP_ARG_I, i, args[i].type, args[i].r); break; - case EJIT_OPERAND_FLT: emit_insn_ad(s, EJIT_OP_ARG_FI, i, args[i].type, args[i].d); break; + case EJIT_OPERAND_GPR: + emit_insn_ar(s, EJIT_OP_ARG, i, args[i].type, EJIT_GPR(args[i].r)); + break; + + case EJIT_OPERAND_FPR: + emit_insn_af(s, EJIT_OP_ARG_F, i, args[i].type, EJIT_FPR(args[i].r)); + break; + + case EJIT_OPERAND_IMM: + emit_insn_ai(s, EJIT_OP_ARG_I, i, args[i].type, args[i].r); + break; + + case EJIT_OPERAND_FLT: + emit_insn_ad(s, EJIT_OP_ARG_FI, i, args[i].type, args[i].d); + break; + default: abort(); } } - emit_insn_op(s, EJIT_OP_ESCAPEI_I, f); + emit_insn_oxr(s, op, target); } -void ejit_escapei_l(struct ejit_func *s, ejit_escape_l_t f, size_t argc, - const struct ejit_operand args[argc]) +void ejit_callr_i(struct ejit_func *s, struct ejit_gpr target, + size_t argc, const struct ejit_operand args[argc]) { - s->use_64 = true; - s->max_args = argc > s->max_args ? argc : s->max_args; - for (size_t i = 0; i < argc; ++i) { - switch (args[i].kind) { - case EJIT_OPERAND_GPR: emit_insn_ar(s, EJIT_OP_ARG, i, args[i].type, EJIT_GPR(args[i].r)); break; - case EJIT_OPERAND_FPR: emit_insn_af(s, EJIT_OP_ARG_F, i, args[i].type, EJIT_FPR(args[i].r)); break; - case EJIT_OPERAND_IMM: emit_insn_ai(s, EJIT_OP_ARG_I, i, args[i].type, args[i].r); break; - case EJIT_OPERAND_FLT: emit_insn_ad(s, EJIT_OP_ARG_FI, i, args[i].type, args[i].d); break; - default: abort(); - } - } + ejit_callr(s, EJIT_OP_CALLR_I, target, argc, args); +} + +void ejit_callr_l(struct ejit_func *s, struct ejit_gpr target, + size_t argc, const struct ejit_operand args[argc]) +{ + ejit_callr(s, EJIT_OP_CALLR_L, target, argc, args); +} - emit_insn_op(s, EJIT_OP_ESCAPEI_L, f); +void ejit_callr_f(struct ejit_func *s, struct ejit_gpr target, + size_t argc, const struct ejit_operand args[argc]) +{ + ejit_callr(s, EJIT_OP_CALLR_F, target, argc, args); } -void ejit_escapei_f(struct ejit_func *s, ejit_escape_f_t f, size_t argc, - const struct ejit_operand args[argc]) +void ejit_callr_d(struct ejit_func *s, struct ejit_gpr target, + size_t argc, const struct ejit_operand args[argc]) { + ejit_callr(s, EJIT_OP_CALLR_D, target, argc, args); +} + +static void ejit_escapei(struct ejit_func *s, enum ejit_opcode op, void *f, + size_t argc, const struct ejit_operand args[argc]) +{ + s->use_64 = op == EJIT_OP_ESCAPEI_L; s->max_args = argc > s->max_args ? argc : s->max_args; for (size_t i = 0; i < argc; ++i) { switch (args[i].kind) { - case EJIT_OPERAND_GPR: emit_insn_ar(s, EJIT_OP_ARG, i, args[i].type, EJIT_GPR(args[i].r)); break; - case EJIT_OPERAND_FPR: emit_insn_af(s, EJIT_OP_ARG_F, i, args[i].type, EJIT_FPR(args[i].r)); break; - case EJIT_OPERAND_IMM: emit_insn_ai(s, EJIT_OP_ARG_I, i, args[i].type, args[i].r); break; - case EJIT_OPERAND_FLT: emit_insn_ad(s, EJIT_OP_ARG_FI, i, args[i].type, args[i].d); break; + case EJIT_OPERAND_GPR: + emit_insn_ar(s, EJIT_OP_ARG, i, args[i].type, EJIT_GPR(args[i].r)); + break; + + case EJIT_OPERAND_FPR: + emit_insn_af(s, EJIT_OP_ARG_F, i, args[i].type, EJIT_FPR(args[i].r)); + break; + + case EJIT_OPERAND_IMM: + emit_insn_ai(s, EJIT_OP_ARG_I, i, args[i].type, args[i].r); + break; + + case EJIT_OPERAND_FLT: + emit_insn_ad(s, EJIT_OP_ARG_FI, i, args[i].type, args[i].d); + break; + default: abort(); } } - emit_insn_op(s, EJIT_OP_ESCAPEI_F, f); + emit_insn_op(s, op, f); } -void ejit_escapei_d(struct ejit_func *s, ejit_escape_d_t f, size_t argc, - const struct ejit_operand args[argc]) +void ejit_escapei_i(struct ejit_func *s, ejit_escape_i_t f, + size_t argc, const struct ejit_operand args[argc]) { - s->max_args = argc > s->max_args ? argc : s->max_args; - for (size_t i = 0; i < argc; ++i) { - switch (args[i].kind) { - case EJIT_OPERAND_GPR: emit_insn_ar(s, EJIT_OP_ARG, i, args[i].type, EJIT_GPR(args[i].r)); break; - case EJIT_OPERAND_FPR: emit_insn_af(s, EJIT_OP_ARG_F, i, args[i].type, EJIT_FPR(args[i].r)); break; - case EJIT_OPERAND_IMM: emit_insn_ai(s, EJIT_OP_ARG_I, i, args[i].type, args[i].r); break; - case EJIT_OPERAND_FLT: emit_insn_ad(s, EJIT_OP_ARG_FI, i, args[i].type, args[i].d); break; - default: abort(); - } - } + ejit_escapei(s, EJIT_OP_ESCAPEI_I, f, argc, args); +} + +void ejit_escapei_l(struct ejit_func *s, ejit_escape_l_t f, + size_t argc, const struct ejit_operand args[argc]) +{ + ejit_escapei(s, EJIT_OP_ESCAPEI_L, f, argc, args); +} - emit_insn_op(s, EJIT_OP_ESCAPEI_D, f); +void ejit_escapei_f(struct ejit_func *s, ejit_escape_f_t f, + size_t argc, const struct ejit_operand args[argc]) +{ + ejit_escapei(s, EJIT_OP_ESCAPEI_F, f, argc, args); +} + +void ejit_escapei_d(struct ejit_func *s, ejit_escape_d_t f, + size_t argc, const struct ejit_operand args[argc]) +{ + ejit_escapei(s, EJIT_OP_ESCAPEI_D, f, argc, args); } void ejit_retval(struct ejit_func *s, struct ejit_gpr r0) diff --git a/src/interp.c b/src/interp.c index 268bfb3..6f94f98 100644 --- a/src/interp.c +++ b/src/interp.c @@ -213,14 +213,18 @@ union interp_ret ejit_run(struct ejit_func *f, size_t paramc, struct ejit_arg pa [EJIT_OP_PARAM] = &&PARAM, [EJIT_OP_PARAM_F] = &&PARAM_F, + [EJIT_OP_CALLR_I] = &&CALLR_I, + [EJIT_OP_CALLR_L] = &&CALLR_L, + [EJIT_OP_CALLR_F] = &&CALLR_F, + [EJIT_OP_CALLR_D] = &&CALLR_D, [EJIT_OP_CALLI] = &&CALLI, [EJIT_OP_TAILR] = &&TAILR, [EJIT_OP_TAILI] = &&TAILI, [EJIT_OP_ESCAPEI_I] = &&ESCAPEI_I, - [EJIT_OP_ESCAPEI_F] = &&ESCAPEI_F, [EJIT_OP_ESCAPEI_L] = &&ESCAPEI_L, + [EJIT_OP_ESCAPEI_F] = &&ESCAPEI_F, [EJIT_OP_ESCAPEI_D] = &&ESCAPEI_D, [EJIT_OP_START] = &&START, @@ -1084,33 +1088,48 @@ top: goto top; DISPATCH(); + DO(CALLR_I); + retval = ejit_run((struct ejit_func *)gpr[i.r1], argc, args, NULL); + argc = 0; + DISPATCH(); + + DO(CALLR_L); + retval = ejit_run((struct ejit_func *)gpr[i.r1], argc, args, NULL); + argc = 0; + DISPATCH(); + + DO(CALLR_F); + retval = ejit_run((struct ejit_func *)gpr[i.r1], argc, args, NULL); + argc = 0; + DISPATCH(); + + DO(CALLR_D); + retval = ejit_run((struct ejit_func *)gpr[i.r1], argc, args, NULL); + argc = 0; + DISPATCH(); + DO(CALLI); - struct ejit_func *f = i.p; - retval = ejit_run(f, argc, args, NULL); + retval = ejit_run((struct ejit_func *)i.p, argc, args, NULL); argc = 0; DISPATCH(); DO(ESCAPEI_I); - ejit_escape_i_t f = i.p; - retval.i = f(argc, args); + retval.i = ((ejit_escape_i_t)i.p)(argc, args); argc = 0; DISPATCH(); DO(ESCAPEI_L); - ejit_escape_l_t f = i.p; - retval.i = f(argc, args); + retval.i = ((ejit_escape_l_t)i.p)(argc, args); argc = 0; DISPATCH(); DO(ESCAPEI_F); - ejit_escape_f_t f = i.p; - retval.f = f(argc, args); + retval.f = ((ejit_escape_f_t)i.p)(argc, args); argc = 0; DISPATCH(); DO(ESCAPEI_D); - ejit_escape_d_t f = i.p; - retval.f = f(argc, args); + retval.f = ((ejit_escape_d_t)i.p)(argc, args); argc = 0; DISPATCH(); diff --git a/tests/calli.c b/tests/calli.c new file mode 100644 index 0000000..991e97d --- /dev/null +++ b/tests/calli.c @@ -0,0 +1,41 @@ +#include +#include +#include "do_jit.h" + +struct ejit_func *compile(bool do_jit) +{ + struct ejit_operand operands[2] = { + EJIT_OPERAND_GPR(0, EJIT_TYPE(long)), + EJIT_OPERAND_GPR(1, EJIT_TYPE(long)) + }; + struct ejit_func *f = ejit_create_func(EJIT_TYPE(long), 2, operands); + ejit_addr(f, EJIT_GPR(0), EJIT_GPR(0), EJIT_GPR(1)); + ejit_retr(f, EJIT_GPR(0)); + ejit_select_compile_func(f, 2, 0, EJIT_USE64(long), do_jit, true); + return f; +} + +int main(int argc, char *argv[]) +{ + (void)argv; + bool do_jit = argc > 1; + struct ejit_func *target = compile(do_jit); + + struct ejit_operand operands[2] = { + EJIT_OPERAND_GPR(0, EJIT_TYPE(long)), + EJIT_OPERAND_GPR(1, EJIT_TYPE(long)) + }; + + struct ejit_func *f = ejit_create_func(EJIT_TYPE(long), 2, operands); + ejit_calli(f, target, 2, operands); + ejit_retval(f, EJIT_GPR(0)); + ejit_retr(f, EJIT_GPR(0)); + ejit_select_compile_func(f, 2, 0, EJIT_USE64(long), do_jit, true); + + assert(erfi2(f, + EJIT_ARG(42, long), + EJIT_ARG(69, long)) == 111); + + ejit_destroy_func(target); + ejit_destroy_func(f); +} diff --git a/tests/callr_i.c b/tests/callr_i.c new file mode 100644 index 0000000..00b5374 --- /dev/null +++ b/tests/callr_i.c @@ -0,0 +1,42 @@ +#include +#include +#include "do_jit.h" + +struct ejit_func *compile(bool do_jit) +{ + struct ejit_operand operands[2] = { + EJIT_OPERAND_GPR(0, EJIT_TYPE(long)), + EJIT_OPERAND_GPR(1, EJIT_TYPE(long)) + }; + struct ejit_func *f = ejit_create_func(EJIT_TYPE(long), 2, operands); + ejit_addr(f, EJIT_GPR(0), EJIT_GPR(0), EJIT_GPR(1)); + ejit_retr(f, EJIT_GPR(0)); + ejit_select_compile_func(f, 2, 0, EJIT_USE64(long), do_jit, true); + return f; +} + +int main(int argc, char *argv[]) +{ + (void)argv; + bool do_jit = argc > 1; + struct ejit_func *target = compile(do_jit); + + struct ejit_operand operands[2] = { + EJIT_OPERAND_GPR(0, EJIT_TYPE(long)), + EJIT_OPERAND_GPR(1, EJIT_TYPE(long)) + }; + + struct ejit_func *f = ejit_create_func(EJIT_TYPE(long), 2, operands); + ejit_movi(f, EJIT_GPR(2), (uintptr_t)target); + ejit_callr_i(f, EJIT_GPR(2), 2, operands); + ejit_retval(f, EJIT_GPR(0)); + ejit_retr(f, EJIT_GPR(0)); + ejit_select_compile_func(f, 3, 0, EJIT_USE64(long), do_jit, true); + + assert(erfi2(f, + EJIT_ARG(42, long), + EJIT_ARG(69, long)) == 111); + + ejit_destroy_func(target); + ejit_destroy_func(f); +} -- cgit v1.2.3