aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2024-06-28 19:33:03 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2024-06-28 19:33:03 +0300
commit29718f2e84478b296c3198ae6d35cfd5d79efb14 (patch)
tree5261fe6e369f27f66a48c6f307870fc854c693c8
parent2fc4f728643f78577c26e548619bc7db90a7c6ae (diff)
downloadejit-29718f2e84478b296c3198ae6d35cfd5d79efb14.tar.gz
ejit-29718f2e84478b296c3198ae6d35cfd5d79efb14.zip
optimize interpreter a bit
+ Use a gpr/fpr/arg stack shared between different functions to minimize amount of memory allocations done at runtime while still allowing interleaving bytecode/machine code functions during call, just with a bit of extra setup in between
-rw-r--r--src/common.h10
-rw-r--r--src/ejit.c38
-rw-r--r--src/interp.c53
3 files changed, 76 insertions, 25 deletions
diff --git a/src/common.h b/src/common.h
index 1f52f76..1f2289d 100644
--- a/src/common.h
+++ b/src/common.h
@@ -136,7 +136,15 @@ union interp_ret {
double d;
};
-union interp_ret ejit_interp(struct ejit_func *f, size_t argc, struct ejit_arg args[argc], bool run, void ***labels_wb);
+struct interp_state {
+ struct vec gprs;
+ struct vec fprs;
+ struct vec args;
+};
+
+union interp_ret ejit_interp(struct ejit_func *f, size_t argc, struct ejit_arg args[argc], struct interp_state *state, bool run, void ***labels_wb);
+
+long ejit_run_interp(struct ejit_func *f, size_t argc, struct ejit_arg args[argc], struct interp_state *state);
bool ejit_compile(struct ejit_func *f);
diff --git a/src/ejit.c b/src/ejit.c
index 1fcae9d..b059c88 100644
--- a/src/ejit.c
+++ b/src/ejit.c
@@ -74,7 +74,7 @@ void ejit_select_compile_func(struct ejit_func *f, size_t gpr, size_t fpr, bool
void **labels;
/* just get labels, don't actually run anything yet */
- ejit_interp(f, 0, NULL, false, &labels);
+ ejit_interp(f, 0, NULL, NULL, false, &labels);
foreach_vec(ii, f->insns) {
struct ejit_insn i = vect_at(struct ejit_insn, f->insns, ii);
void *addr = labels[i.op];
@@ -292,6 +292,32 @@ struct ejit_reloc ejit_jmp(struct ejit_func *s)
return (struct ejit_reloc){.insn = addr};
}
+static struct interp_state create_interp_state()
+{
+ struct interp_state state;
+ state.gprs = vec_create(sizeof(long));
+ state.gprs = vec_create(sizeof(double));
+ state.args = vec_create(sizeof(struct ejit_arg));
+ return state;
+}
+
+static void destroy_interp_state(struct interp_state state)
+{
+ vec_destroy(&state.gprs);
+ vec_destroy(&state.fprs);
+ vec_destroy(&state.args);
+}
+
+long ejit_run_interp(struct ejit_func *f, size_t argc, struct ejit_arg args[argc], struct interp_state *state)
+{
+ assert(f->gpr && "trying to run a function that hasn't been compiled");
+ assert(f->rtype == EJIT_VOID || ejit_int_type(f->rtype));
+ if (f->arena)
+ return ((ejit_escape_t)f->arena)(argc, args);
+
+ return ejit_interp(f, argc, args, state, true, NULL).r;
+}
+
long ejit_run_func(struct ejit_func *f, size_t argc, struct ejit_arg args[argc])
{
assert(f->gpr && "trying to run a function that hasn't been compiled");
@@ -299,7 +325,10 @@ long ejit_run_func(struct ejit_func *f, size_t argc, struct ejit_arg args[argc])
if (f->arena)
return ((ejit_escape_t)f->arena)(argc, args);
- return ejit_interp(f, argc, args, true, NULL).r;
+ struct interp_state state = create_interp_state();
+ long r = ejit_interp(f, argc, args, &state, true, NULL).r;
+ destroy_interp_state(state);
+ return r;
}
double ejit_run_func_f(struct ejit_func *f, size_t argc, struct ejit_arg args[argc])
@@ -309,5 +338,8 @@ double ejit_run_func_f(struct ejit_func *f, size_t argc, struct ejit_arg args[ar
if (f->arena)
return ((ejit_escape_f_t)f->arena)(argc, args);
- return ejit_interp(f, argc, args, true, NULL).d;
+ struct interp_state state = create_interp_state();
+ double r = ejit_interp(f, argc, args, &state, true, NULL).d;
+ destroy_interp_state(state);
+ return r;
}
diff --git a/src/interp.c b/src/interp.c
index 25f723a..ed7e59c 100644
--- a/src/interp.c
+++ b/src/interp.c
@@ -2,7 +2,7 @@
#include "common.h"
-union interp_ret ejit_interp(struct ejit_func *f, size_t argc, struct ejit_arg args[argc], bool run, void ***labels_wb)
+union interp_ret ejit_interp(struct ejit_func *f, size_t argc, struct ejit_arg args[argc], struct interp_state *state, bool run, void ***labels_wb)
{
static void *labels[OPCODE_COUNT] = {
[MOVI] = &&MOVI,
@@ -61,10 +61,16 @@ union interp_ret ejit_interp(struct ejit_func *f, size_t argc, struct ejit_arg a
return (union interp_ret){.r = 0};
}
- long *gpr = malloc(f->gpr * sizeof(long));
- double *fpr = malloc(f->fpr * sizeof(double));
+ size_t prev_gprs = vec_len(&state->gprs);
+ size_t prev_fprs = vec_len(&state->fprs);
+ size_t prev_argc = vec_len(&state->args);
+
+ vec_reserve(&state->gprs, prev_gprs + f->gpr);
+ vec_reserve(&state->fprs, prev_fprs + f->fpr);
+
+ long *gpr = ((long *)state->gprs.buf) + prev_gprs;
+ double *fpr = ((double *)state->fprs.buf) + prev_fprs;
- struct vec call_args = vec_create(sizeof(struct ejit_arg));
struct ejit_insn *insns = f->insns.buf;
/* retval is kind of an unfortunate extra bit of state to keep track of,
@@ -190,40 +196,47 @@ union interp_ret ejit_interp(struct ejit_func *f, size_t argc, struct ejit_arg a
DO(ARG);
struct ejit_arg a = ejit_build_arg(i.r1, gpr[i.r2]);
- vec_append(&call_args, &a);
+ vec_append(&state->args, &a);
DISPATCH();
DO(ARG_I);
struct ejit_arg a = ejit_build_arg(i.r1, i.o);
- vec_append(&call_args, &a);
+ vec_append(&state->args, &a);
DISPATCH();
DO(ARG_F);
struct ejit_arg a = ejit_build_arg_f(i.r1, fpr[i.r2]);
- vec_append(&call_args, &a);
+ vec_append(&state->args, &a);
DISPATCH();
DO(ARG_FI);
struct ejit_arg a = ejit_build_arg_f(i.r1, i.d);
- vec_append(&call_args, &a);
+ vec_append(&state->args, &a);
DISPATCH();
DO(CALLI);
struct ejit_func *f = i.p;
- retval = ejit_run_func(f, vec_len(&call_args), call_args.buf);
- vec_reset(&call_args);
+ size_t argc = vec_len(&state->args) - prev_argc;
+ struct ejit_arg *args = ((struct ejit_arg *)state->args.buf) + prev_argc;
+
+ retval = ejit_run_interp(f, argc, args, state);
+
+ gpr = ((long *)state->gprs.buf) + prev_gprs;
+ fpr = ((double *)state->fprs.buf) + prev_fprs;
+ vec_shrink(&state->args, prev_argc);
DISPATCH();
DO(CALLI_F);
- struct ejit_func *f = i.p;
- retval_f = ejit_run_func_f(f, vec_len(&call_args), call_args.buf);
- vec_reset(&call_args);
DISPATCH();
DO(ESCAPEI);
ejit_escape_t f = i.p;
- retval = f(vec_len(&call_args), call_args.buf);
- vec_reset(&call_args);
+ size_t argc = vec_len(&state->args) - prev_argc;
+ struct ejit_arg *args = ((struct ejit_arg *)state->args.buf) + prev_argc;
+
+ retval = f(argc, args);
+
+ vec_shrink(&state->args, prev_argc);
DISPATCH();
/* dispatch is technically unnecessary for returns, but keep it for
@@ -253,14 +266,12 @@ union interp_ret ejit_interp(struct ejit_func *f, size_t argc, struct ejit_arg a
#undef DO
out_int:
- free(gpr);
- free(fpr);
- vec_destroy(&call_args);
+ vec_shrink(&state->gprs, prev_gprs);
+ vec_shrink(&state->fprs, prev_fprs);
return (union interp_ret){.r = retval};
out_float:
- free(gpr);
- free(fpr);
- vec_destroy(&call_args);
+ vec_shrink(&state->gprs, prev_gprs);
+ vec_shrink(&state->fprs, prev_fprs);
return (union interp_ret){.d = retval_f};
}