diff options
author | Kimplul <kimi.h.kuparinen@gmail.com> | 2025-04-09 22:20:01 +0300 |
---|---|---|
committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2025-04-09 22:20:01 +0300 |
commit | be5c83ba8e57bc67beee41bc2c7227e6b8ebd9d5 (patch) | |
tree | 2a0d208fed58629d710ac5f908f1cc49000ecf75 | |
parent | 057131cb20fb1c46e90adecfb4a16eb62f100580 (diff) | |
download | ejit-be5c83ba8e57bc67beee41bc2c7227e6b8ebd9d5.tar.gz ejit-be5c83ba8e57bc67beee41bc2c7227e6b8ebd9d5.zip |
add callr_i/l/f/d
-rw-r--r-- | include/ejit/ejit.h | 33 | ||||
-rw-r--r-- | src/common.h | 6 | ||||
-rw-r--r-- | src/compile/compile.c | 73 | ||||
-rw-r--r-- | src/ejit.c | 142 | ||||
-rw-r--r-- | src/interp.c | 41 | ||||
-rw-r--r-- | tests/calli.c | 41 | ||||
-rw-r--r-- | tests/callr_i.c | 42 |
7 files changed, 298 insertions, 80 deletions
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); @@ -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 <ejit/ejit.h> +#include <assert.h> +#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 <ejit/ejit.h> +#include <assert.h> +#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); +} |