diff options
author | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-06-28 19:33:03 +0300 |
---|---|---|
committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2024-06-28 19:33:03 +0300 |
commit | 29718f2e84478b296c3198ae6d35cfd5d79efb14 (patch) | |
tree | 5261fe6e369f27f66a48c6f307870fc854c693c8 | |
parent | 2fc4f728643f78577c26e548619bc7db90a7c6ae (diff) | |
download | ejit-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.h | 10 | ||||
-rw-r--r-- | src/ejit.c | 38 | ||||
-rw-r--r-- | src/interp.c | 53 |
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); @@ -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}; } |