aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2025-04-09 22:20:01 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2025-04-09 22:20:01 +0300
commitbe5c83ba8e57bc67beee41bc2c7227e6b8ebd9d5 (patch)
tree2a0d208fed58629d710ac5f908f1cc49000ecf75
parent057131cb20fb1c46e90adecfb4a16eb62f100580 (diff)
downloadejit-be5c83ba8e57bc67beee41bc2c7227e6b8ebd9d5.tar.gz
ejit-be5c83ba8e57bc67beee41bc2c7227e6b8ebd9d5.zip
add callr_i/l/f/d
-rw-r--r--include/ejit/ejit.h33
-rw-r--r--src/common.h6
-rw-r--r--src/compile/compile.c73
-rw-r--r--src/ejit.c142
-rw-r--r--src/interp.c41
-rw-r--r--tests/calli.c41
-rw-r--r--tests/callr_i.c42
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);
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 <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);
+}