diff options
| author | Kimplul <kimi.h.kuparinen@gmail.com> | 2025-04-09 20:20:48 +0300 | 
|---|---|---|
| committer | Kimplul <kimi.h.kuparinen@gmail.com> | 2025-04-09 20:20:48 +0300 | 
| commit | 057131cb20fb1c46e90adecfb4a16eb62f100580 (patch) | |
| tree | 82081321522eaef1b049cce1fb4627a75a6d6664 | |
| parent | 6824dd4b1ee22184f0e600115db3998924ed39d6 (diff) | |
| download | ejit-057131cb20fb1c46e90adecfb4a16eb62f100580.tar.gz ejit-057131cb20fb1c46e90adecfb4a16eb62f100580.zip | |
add taili
| -rw-r--r-- | include/ejit/ejit.h | 3 | ||||
| -rw-r--r-- | src/common.h | 2 | ||||
| -rw-r--r-- | src/compile/compile.c | 35 | ||||
| -rw-r--r-- | src/ejit.c | 44 | ||||
| -rw-r--r-- | src/interp.c | 14 | ||||
| -rw-r--r-- | tests/taili.c | 39 | ||||
| -rw-r--r-- | tests/tailr.c | 41 | 
7 files changed, 177 insertions, 1 deletions
| 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, @@ -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 <ejit/ejit.h> +#include <assert.h> +#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 <ejit/ejit.h> +#include <assert.h> +#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); +} | 
