#include <assert.h>
#include <sys/mman.h>

#include <ejit/ejit.h>

#include "common.h"

static void emit_insn_i(struct ejit_func *f, enum ejit_opcode op, size_t r0,
                        size_t r1, int64_t o)
{
	struct ejit_insn i = {.op = op, .r0 = r0, .r1 = r1, .o = o};
	vec_append(&f->insns, &i);
}

static void emit_insn_r(struct ejit_func *f, enum ejit_opcode op, size_t r0,
                        size_t r1, size_t r2)
{
	struct ejit_insn i = {.op = op, .r0 = r0, .r1 = r1, .r2 = r2};
	vec_append(&f->insns, &i);
}

static void emit_insn_p(struct ejit_func *f, enum ejit_opcode op, size_t r0,
                        size_t r1, void *p)
{
	struct ejit_insn i = {.op = op, .r0 = r0, .r1 = r1, .p = p};
	vec_append(&f->insns, &i);
}

static void emit_insn_f(struct ejit_func *f, enum ejit_opcode op, size_t r0,
                        size_t r1, float d)
{
	struct ejit_insn i = {.op = op, .r0 = r0, .r1 = r1, .f = d};
	vec_append(&f->insns, &i);
}

static void emit_insn_d(struct ejit_func *f, enum ejit_opcode op, size_t r0,
                        size_t r1, double d)
{
	struct ejit_insn i = {.op = op, .r0 = r0, .r1 = r1, .d = d};
	vec_append(&f->insns, &i);
}

struct ejit_func *ejit_create_func(enum ejit_type rtype, size_t argc,
                                   const struct ejit_operand args[argc])
{
	struct ejit_func *f = malloc(sizeof(struct ejit_func));
	assert(f);

	f->rtype = rtype;

	f->insns = vec_create(sizeof(struct ejit_insn));
	f->labels = vec_create(sizeof(size_t));
	f->arena = NULL;
	f->size = 0;

	for (size_t i = 0; i < argc; ++i) {
		switch (args[i].kind) {
		case EJIT_OPERAND_GPR: {
			assert(ejit_int_type(args[i].type));
			emit_insn_r(f, PARAM, i, args[i].type, args[i].r);
			break;
		}

		case EJIT_OPERAND_FPR: {
			assert(ejit_float_type(args[i].type));
			emit_insn_r(f, PARAM_F, i, args[i].type, args[i].r);
			break;
		}
		default: abort();
		}
	}

	emit_insn_i(f, START, 0, 0, 0);
	return f;
}

void ejit_compile_func(struct ejit_func *f, size_t gpr, size_t fpr, bool use_64)
{
	ejit_select_compile_func(f, gpr, fpr, use_64, true);
}

void ejit_select_compile_func(struct ejit_func *f, size_t gpr, size_t fpr,
                              bool use_64, bool try_jit)
{
	/* emit a final end instruction in case user didn't do a return */
	emit_insn_i(f, END, 0, 0, 0);

	f->gpr = gpr;
	f->fpr = fpr;

	/* try to jit compile if possible */
	if (try_jit && ejit_compile(f, use_64))
		return;

	/* otherwise, convert opcodes to address labels */

	void **labels;
	/* just get labels, don't actually run anything yet */
	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];
		assert(addr);
		i.addr = addr;
		vect_at(struct ejit_insn, f->insns, ii) = i;
	}

	/* doesn't really matter what we put here as long as it isn't 0 */
	f->size = 1;
}

void ejit_destroy_func(struct ejit_func *f)
{
	if (f->arena)
		munmap(f->arena, f->size);

	vec_destroy(&f->insns);
	vec_destroy(&f->labels);
	free(f);
}

struct ejit_label ejit_label(struct ejit_func *f)
{
	size_t addr = vec_len(&f->insns);
	vec_append(&f->labels, &addr);
	return (struct ejit_label){.addr = addr};
}

void ejit_patch(struct ejit_func *f, struct ejit_reloc r, struct ejit_label l)
{
	struct ejit_insn i = vect_at(struct ejit_insn, f->insns, r.insn);
	/** @todo some assert that checks the opcode? */
	i.r0 = l.addr;
	vect_at(struct ejit_insn, f->insns, r.insn) = i;
}

void ejit_calli(struct ejit_func *s, struct ejit_func *f, size_t argc,
                const struct ejit_operand args[argc])
{
	for (size_t i = 0; i < argc; ++i) {
		switch (args[i].kind) {
		case EJIT_OPERAND_GPR: emit_insn_r(s, ARG, i, args[i].type,
			                           args[i].r); break;
		case EJIT_OPERAND_FPR: emit_insn_r(s, ARG_F, i, args[i].type,
			                           args[i].r); break;
		case EJIT_OPERAND_IMM: emit_insn_i(s, ARG_I, i, args[i].type,
			                           args[i].r); break;
		case EJIT_OPERAND_FLT: emit_insn_f(s, ARG_FI, i, args[i].type,
			                           args[i].d); break;
		default: abort();
		}
	}

	emit_insn_p(s, CALLI, 0, 0, f);
}

void ejit_escapei(struct ejit_func *s, ejit_escape_t f, size_t argc,
                  const struct ejit_operand args[argc])
{
	for (size_t i = 0; i < argc; ++i) {
		switch (args[i].kind) {
		case EJIT_OPERAND_GPR: emit_insn_r(s, ARG, i, args[i].type,
			                           args[i].r); break;
		case EJIT_OPERAND_FPR: emit_insn_r(s, ARG_F, i, args[i].type,
			                           args[i].r); break;
		case EJIT_OPERAND_IMM: emit_insn_r(s, ARG_I, i, args[i].type,
			                           args[i].r); break;
		case EJIT_OPERAND_FLT: emit_insn_f(s, ARG_FI, i, args[i].type,
			                           args[i].d); break;
		default: abort();
		}
	}

	emit_insn_p(s, ESCAPEI, 0, 0, f);
}

void ejit_escapei_f(struct ejit_func *s, ejit_escape_f_t f, size_t argc,
                    const struct ejit_operand args[argc])
{
	for (size_t i = 0; i < argc; ++i) {
		switch (args[i].kind) {
		case EJIT_OPERAND_GPR: emit_insn_r(s, ARG, i, args[i].type,
			                           args[i].r); break;
		case EJIT_OPERAND_FPR: emit_insn_r(s, ARG_F, i, args[i].type,
			                           args[i].r); break;
		case EJIT_OPERAND_IMM: emit_insn_r(s, ARG_I, i, args[i].type,
			                           args[i].r); break;
		case EJIT_OPERAND_FLT: emit_insn_f(s, ARG_FI, i, args[i].type,
			                           args[i].d); break;
		default: abort();
		}
	}

	emit_insn_p(s, ESCAPEI_F, 0, 0, f);
}

void ejit_retval(struct ejit_func *s, struct ejit_gpr r0)
{
	emit_insn_i(s, RETVAL, r0.r, 0, 0);
}

void ejit_retval_f(struct ejit_func *s, struct ejit_fpr r0)
{
	emit_insn_i(s, RETVAL_F, r0.f, 0, 0);
}

void ejit_retval_d(struct ejit_func *s, struct ejit_fpr r0)
{
	emit_insn_i(s, RETVAL_D, r0.f, 0, 0);
}

void ejit_sti_8(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_p(s, STI8, r0.r, 0, p);
}

void ejit_sti_16(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_p(s, STI16, r0.r, 0, p);
}

void ejit_sti_32(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_p(s, STI32, r0.r, 0, p);
}

void ejit_sti_64(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_p(s, STI64, r0.r, 0, p);
}

void ejit_sti_f(struct ejit_func *s, struct ejit_fpr r0, void *p)
{
	emit_insn_p(s, STIF, r0.f, 0, p);
}

void ejit_sti_d(struct ejit_func *s, struct ejit_fpr r0, void *p)
{
	emit_insn_p(s, STID, r0.f, 0, p);
}

void ejit_stxi_8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                 int64_t o)
{
	emit_insn_i(s, STXI8, r0.r, r1.r, o);
}

void ejit_stxi_16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  int64_t o)
{
	emit_insn_i(s, STXI16, r0.r, r1.r, o);
}

void ejit_stxi_32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  int64_t o)
{
	emit_insn_i(s, STXI32, r0.r, r1.r, o);
}

void ejit_stxi_64(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  int64_t o)
{
	emit_insn_i(s, STXI64, r0.r, r1.r, o);
}

void ejit_stxi_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_gpr r1,
                 int64_t o)
{
	emit_insn_i(s, STXIF, r0.f, r1.r, o);
}

void ejit_stxi_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_gpr r1,
                 int64_t o)
{
	emit_insn_i(s, STXID, r0.f, r1.r, o);
}

void ejit_stxr_8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_r(s, STXR8, r0.r, r1.r, r2.r);
}

void ejit_stxr_16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  struct ejit_gpr r2)
{
	emit_insn_r(s, STXR16, r0.r, r1.r, r2.r);
}

void ejit_stxr_32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  struct ejit_gpr r2)
{
	emit_insn_r(s, STXR32, r0.r, r1.r, r2.r);
}

void ejit_stxr_64(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  struct ejit_gpr r2)
{
	emit_insn_r(s, STXR64, r0.r, r1.r, r2.r);
}

void ejit_stxr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_r(s, STXRF, r0.f, r1.r, r2.r);
}

void ejit_stxr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_r(s, STXRD, r0.f, r1.r, r2.r);
}

void ejit_ldi_i8(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_p(s, LDI8, r0.r, 0, p);
}

void ejit_ldi_i16(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_p(s, LDI16, r0.r, 0, p);
}

void ejit_ldi_i32(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_p(s, LDI32, r0.r, 0, p);
}

void ejit_ldi_i64(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_p(s, LDI64, r0.r, 0, p);
}

void ejit_ldi_u8(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_p(s, LDIU8, r0.r, 0, p);
}

void ejit_ldi_u16(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_p(s, LDIU16, r0.r, 0, p);
}

void ejit_ldi_u32(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_p(s, LDIU32, r0.r, 0, p);
}

void ejit_ldi_u64(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_p(s, LDIU64, r0.r, 0, p);
}

void ejit_ldi_f(struct ejit_func *s, struct ejit_fpr r0, void *p)
{
	emit_insn_p(s, LDIF, r0.f, 0, p);
}

void ejit_ldi_d(struct ejit_func *s, struct ejit_fpr r0, void *p)
{
	emit_insn_p(s, LDID, r0.f, 0, p);
}

void ejit_ldxi_i8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  int64_t o)
{
	emit_insn_i(s, LDXI8, r0.r, r1.r, o);
}

void ejit_ldxi_i16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   int64_t o)
{
	emit_insn_i(s, LDXI16, r0.r, r1.r, o);
}

void ejit_ldxi_i32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   int64_t o)
{
	emit_insn_i(s, LDXI32, r0.r, r1.r, o);
}

void ejit_ldxi_i64(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   int64_t o)
{
	emit_insn_i(s, LDXI64, r0.r, r1.r, o);
}

void ejit_ldxi_u8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  int64_t o)
{
	emit_insn_i(s, LDXIU8, r0.r, r1.r, o);
}

void ejit_ldxi_u16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   int64_t o)
{
	emit_insn_i(s, LDXIU16, r0.r, r1.r, o);
}

void ejit_ldxi_u32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   int64_t o)
{
	emit_insn_i(s, LDXIU32, r0.r, r1.r, o);
}

void ejit_ldxi_u64(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   int64_t o)
{
	emit_insn_i(s, LDXIU64, r0.r, r1.r, o);
}

void ejit_ldxi_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_gpr r1,
                 int64_t o)
{
	emit_insn_i(s, LDXIF, r0.f, r1.r, o);
}

void ejit_ldxi_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_gpr r1,
                 int64_t o)
{
	emit_insn_i(s, LDXID, r0.f, r1.r, o);
}

void ejit_ldxr_i8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  struct ejit_gpr r2)
{
	emit_insn_r(s, LDXR8, r0.r, r1.r, r2.r);
}

void ejit_ldxr_i16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   struct ejit_gpr r2)
{
	emit_insn_r(s, LDXR16, r0.r, r1.r, r2.r);
}

void ejit_ldxr_i32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   struct ejit_gpr r2)
{
	emit_insn_r(s, LDXR32, r0.r, r1.r, r2.r);
}

void ejit_ldxr_i64(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   struct ejit_gpr r2)
{
	emit_insn_r(s, LDXR64, r0.r, r1.r, r2.r);
}

void ejit_ldxr_u8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  struct ejit_gpr r2)
{
	emit_insn_r(s, LDXRU8, r0.r, r1.r, r2.r);
}

void ejit_ldxr_u16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   struct ejit_gpr r2)
{
	emit_insn_r(s, LDXRU16, r0.r, r1.r, r2.r);
}

void ejit_ldxr_u32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   struct ejit_gpr r2)
{
	emit_insn_r(s, LDXRU32, r0.r, r1.r, r2.r);
}

void ejit_ldxr_u64(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   struct ejit_gpr r2)
{
	emit_insn_r(s, LDXIU64, r0.r, r1.r, r2.r);
}

void ejit_ldxr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_r(s, LDXRF, r0.f, r1.r, r2.r);
}

void ejit_ldxr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_r(s, LDXRD, r0.f, r1.r, r2.r);
}

void ejit_ret(struct ejit_func *s)
{
	emit_insn_i(s, END, 0, 0, 0);
}

void ejit_retr(struct ejit_func *s, struct ejit_gpr r0)
{
	emit_insn_r(s, RETR, r0.r, 0, 0);
}

void ejit_retr_f(struct ejit_func *s, struct ejit_fpr r0)
{
	emit_insn_r(s, RETR_F, r0.f, 0, 0);
}

void ejit_retr_d(struct ejit_func *s, struct ejit_fpr r0)
{
	emit_insn_r(s, RETR_D, r0.f, 0, 0);
}

void ejit_reti(struct ejit_func *s, int64_t i)
{
	emit_insn_i(s, RETI, 0, 0, i);
}

void ejit_reti_f(struct ejit_func *s, float f)
{
	emit_insn_f(s, RETI_F, 0, 0, f);
}

void ejit_reti_d(struct ejit_func *s, double f)
{
	emit_insn_d(s, RETI_F, 0, 0, f);
}

void ejit_extr_8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	emit_insn_i(s, EXTR8, r0.r, r1.r, 0);
}

void ejit_extr_16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	emit_insn_i(s, EXTR16, r0.r, r1.r, 0);
}

void ejit_extr_32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	emit_insn_i(s, EXTR32, r0.r, r1.r, 0);
}

void ejit_extr_u8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	emit_insn_i(s, EXTRU8, r0.r, r1.r, 0);
}

void ejit_extr_u16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	emit_insn_i(s, EXTRU16, r0.r, r1.r, 0);
}

void ejit_extr_u32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	emit_insn_i(s, EXTRU32, r0.r, r1.r, 0);
}

void ejit_extr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_gpr r1)
{
	emit_insn_i(s, EXTRF, r0.f, r1.r, 0);
}

void ejit_extr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_gpr r1)
{
	emit_insn_i(s, EXTRD, r0.f, r1.r, 0);
}

void ejit_addr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_r(s, ADDR, r0.r, r1.r, r2.r);
}

void ejit_addr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1,
                 struct ejit_fpr r2)
{
	emit_insn_r(s, ADDR_F, r0.f, r1.f, r2.f);
}

void ejit_addr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1,
                 struct ejit_fpr r2)
{
	emit_insn_r(s, ADDR_D, r0.f, r1.f, r2.f);
}

void ejit_addi(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               int64_t o)
{
	emit_insn_i(s, ADDI, r0.r, r1.r, o);
}

void ejit_absr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1)
{
	emit_insn_i(s, ABSR_F, r0.f, r1.f, 0);
}

void ejit_absr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1)
{
	emit_insn_i(s, ABSR_D, r0.f, r1.f, 0);
}

void ejit_subr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_r(s, SUBR, r0.r, r1.r, r2.r);
}

void ejit_subr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1,
                 struct ejit_fpr r2)
{
	emit_insn_r(s, SUBR_F, r0.f, r1.f, r2.f);
}

void ejit_subr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1,
                 struct ejit_fpr r2)
{
	emit_insn_r(s, SUBR_D, r0.f, r1.f, r2.f);
}

void ejit_subi(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               long o)
{
	emit_insn_i(s, SUBI, r0.r, r1.r, o);
}

void ejit_mulr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_r(s, MULR, r0.r, r1.r, r2.r);
}

void ejit_mulr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1,
                 struct ejit_fpr r2)
{
	emit_insn_r(s, MULR_F, r0.f, r1.f, r2.f);
}

void ejit_mulr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1,
                 struct ejit_fpr r2)
{
	emit_insn_r(s, MULR_D, r0.f, r1.f, r2.f);
}

void ejit_divr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_r(s, DIVR, r0.r, r1.r, r2.r);
}

void ejit_divr_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_r(s, DIVR_U, r0.r, r1.r, r2.r);
}

void ejit_divr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1,
                 struct ejit_fpr r2)
{
	emit_insn_r(s, DIVR_F, r0.f, r1.f, r2.f);
}

void ejit_divr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1,
                 struct ejit_fpr r2)
{
	emit_insn_r(s, DIVR_D, r0.f, r1.f, r2.f);
}

void ejit_remr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_r(s, REMR, r0.r, r1.r, r2.r);
}

void ejit_remr_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_r(s, REMR_U, r0.r, r1.r, r2.r);
}

void ejit_lshi(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               int64_t o)
{
	emit_insn_i(s, LSHI, r0.r, r1.r, o);
}

void ejit_lshr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_r(s, LSHR, r0.r, r1.r, r2.r);
}

void ejit_rshi(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               int64_t o)
{
	emit_insn_i(s, RSHI, r0.r, r1.r, o);
}

void ejit_rshi_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                 int64_t o)
{
	emit_insn_i(s, RSHI_U, r0.r, r1.r, o);
}

void ejit_rshr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_r(s, RSHR, r0.r, r1.r, r2.r);
}

void ejit_rshr_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_r(s, RSHR_U, r0.r, r1.r, r2.r);
}

void ejit_andr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_r(s, ANDR, r0.r, r1.r, r2.r);
}

void ejit_andi(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               int64_t o)
{
	emit_insn_i(s, ANDI, r0.r, r1.r, o);
}

void ejit_orr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              struct ejit_gpr r2)
{
	emit_insn_r(s, ORR, r0.r, r1.r, r2.r);
}

void ejit_ori(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              int64_t o)
{
	emit_insn_i(s, ORI, r0.r, r1.r, o);
}

void ejit_xorr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_r(s, XORR, r0.r, r1.r, r2.r);
}

void ejit_xori(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               int64_t o)
{
	emit_insn_i(s, XORI, r0.r, r1.r, o);
}

void ejit_comr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	emit_insn_i(s, COMR, r0.r, r1.r, 0);
}

void ejit_negr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	emit_insn_i(s, NEGR, r0.r, r1.r, 0);
}

void ejit_negr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1)
{
	emit_insn_i(s, NEGR_F, r0.f, r1.f, 0);
}

void ejit_negr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1)
{
	emit_insn_i(s, NEGR_D, r0.f, r1.f, 0);
}

void ejit_movi(struct ejit_func *s, struct ejit_gpr r0, int64_t o)
{
	emit_insn_i(s, MOVI, r0.r, 0, o);
}

void ejit_movi_f(struct ejit_func *s, struct ejit_fpr r0, float o)
{
	emit_insn_f(s, MOVI_F, r0.f, 0, o);
}

void ejit_movi_d(struct ejit_func *s, struct ejit_fpr r0, double o)
{
	emit_insn_d(s, MOVI_D, r0.f, 0, o);
}

void ejit_movr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	if (r0.r == r1.r)
		return;

	emit_insn_i(s, MOVR, r0.r, r1.r, 0);
}

void ejit_movr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1)
{
	if (r0.f == r1.f)
		return;

	emit_insn_i(s, MOVR_F, r0.f, r1.f, 0);
}

void ejit_movr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1)
{
	if (r0.f == r1.f)
		return;

	emit_insn_i(s, MOVR_D, r0.f, r1.f, 0);
}

void ejit_eqr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              struct ejit_gpr r2)
{
	emit_insn_r(s, EQR, r0.r, r1.r, r2.r);
}

void ejit_eqr_f(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr r1,
                struct ejit_fpr r2)
{
	emit_insn_r(s, EQR_F, r0.r, r1.f, r2.f);
}

void ejit_eqr_d(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr r1,
                struct ejit_fpr r2)
{
	emit_insn_r(s, EQR_D, r0.r, r1.f, r2.f);
}

void ejit_ner(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              struct ejit_gpr r2)
{
	emit_insn_r(s, NER, r0.r, r1.r, r2.r);
}

void ejit_ner_f(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr r1,
                struct ejit_fpr r2)
{
	emit_insn_r(s, NER_F, r0.r, r1.f, r2.f);
}

void ejit_ner_d(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr r1,
                struct ejit_fpr r2)
{
	emit_insn_r(s, NER_D, r0.r, r1.f, r2.f);
}

void ejit_gtr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              struct ejit_gpr r2)
{
	emit_insn_r(s, GTR, r0.r, r1.r, r2.r);
}

void ejit_gtr_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                struct ejit_gpr r2)
{
	emit_insn_r(s, GTR_U, r0.r, r1.r, r2.r);
}

void ejit_gtr_f(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr r1,
                struct ejit_fpr r2)
{
	emit_insn_r(s, GTR_F, r0.r, r1.f, r2.f);
}

void ejit_gtr_d(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr r1,
                struct ejit_fpr r2)
{
	emit_insn_r(s, GTR_D, r0.r, r1.f, r2.f);
}

void ejit_ltr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              struct ejit_gpr r2)
{
	emit_insn_r(s, GTR, r0.r, r2.r, r1.r);
}

void ejit_ltr_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                struct ejit_gpr r2)
{
	emit_insn_r(s, GTR_U, r0.r, r2.r, r1.r);
}

void ejit_ltr_f(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr r1,
                struct ejit_fpr r2)
{
	emit_insn_r(s, GTR_F, r0.r, r2.f, r1.f);
}

void ejit_ltr_d(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr r1,
                struct ejit_fpr r2)
{
	emit_insn_r(s, GTR_D, r0.r, r2.f, r1.f);
}

void ejit_ger(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              struct ejit_gpr r2)
{
	emit_insn_r(s, GER, r0.r, r1.r, r2.r);
}

void ejit_ger_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                struct ejit_gpr r2)
{
	emit_insn_r(s, GER_U, r0.r, r1.r, r2.r);
}

void ejit_ger_f(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr r1,
                struct ejit_fpr r2)
{
	emit_insn_r(s, GER_F, r0.r, r1.f, r2.f);
}

void ejit_ler(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              struct ejit_gpr r2)
{
	emit_insn_r(s, GER, r0.r, r2.r, r1.r);
}

void ejit_ler_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                struct ejit_gpr r2)
{
	emit_insn_r(s, GER_U, r0.r, r2.r, r1.r);
}

void ejit_ler_f(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr r1,
                struct ejit_fpr r2)
{
	emit_insn_r(s, GER_F, r0.r, r2.f, r1.f);
}

void ejit_ler_d(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr r1,
                struct ejit_fpr r2)
{
	emit_insn_r(s, GER_D, r0.r, r2.f, r1.f);
}

void ejit_truncr_d_32(struct ejit_func *s, struct ejit_gpr r0,
                      struct ejit_fpr r1)
{
	emit_insn_i(s, TRUNCR_D_32, r0.r, r1.f, 0);
}

void ejit_truncr_d_64(struct ejit_func *s, struct ejit_gpr r0,
                      struct ejit_fpr r1)
{
	emit_insn_i(s, TRUNCR_D_64, r0.r, r1.f, 0);
}

void ejit_truncr_f_32(struct ejit_func *s, struct ejit_gpr r0,
                      struct ejit_fpr r1)
{
	emit_insn_i(s, TRUNCR_F_32, r0.r, r1.f, 0);
}

void ejit_truncr_f_64(struct ejit_func *s, struct ejit_gpr r0,
                      struct ejit_fpr r1)
{
	emit_insn_i(s, TRUNCR_F_64, r0.r, r1.f, 0);
}

struct ejit_reloc ejit_bner(struct ejit_func *s, struct ejit_gpr r0,
                            struct ejit_gpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BNER, 0, r0.r, r1.r);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bnei(struct ejit_func *s, struct ejit_gpr r0, int64_t o)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, BNEI, 0, r0.r, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bner_f(struct ejit_func *s, struct ejit_fpr r0,
                              struct ejit_fpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BNER_F, 0, r0.f, r1.f);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bner_d(struct ejit_func *s, struct ejit_fpr r0,
                              struct ejit_fpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BNER_D, 0, r0.f, r1.f);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_beqr(struct ejit_func *s, struct ejit_gpr r0,
                            struct ejit_gpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BEQR, 0, r0.r, r1.r);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_beqi(struct ejit_func *s, struct ejit_gpr r0, int64_t o)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, BEQI, 0, r0.r, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_beqr_f(struct ejit_func *s, struct ejit_fpr r0,
                              struct ejit_fpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BEQR_F, 0, r0.f, r1.f);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_beqr_d(struct ejit_func *s, struct ejit_fpr r0,
                              struct ejit_fpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BEQR_D, 0, r0.f, r1.f);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bger(struct ejit_func *s, struct ejit_gpr r0,
                            struct ejit_gpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGER, 0, r0.r, r1.r);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bger_u(struct ejit_func *s, struct ejit_gpr r0,
                              struct ejit_gpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGER_U, 0, r0.r, r1.r);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bger_f(struct ejit_func *s, struct ejit_fpr r0,
                              struct ejit_fpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGER_F, 0, r0.f, r1.f);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bgei(struct ejit_func *s, struct ejit_gpr r0, int64_t o)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, BGEI, 0, r0.r, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bgei_u(struct ejit_func *s, struct ejit_gpr r0,
                              int64_t o)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, BGEI_U, 0, r0.r, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bler(struct ejit_func *s, struct ejit_gpr r0,
                            struct ejit_gpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGER, 0, r1.r, r0.r);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bler_u(struct ejit_func *s, struct ejit_gpr r0,
                              struct ejit_gpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGER_U, 0, r1.r, r0.r);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bler_f(struct ejit_func *s, struct ejit_fpr r0,
                              struct ejit_fpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGER_F, 0, r1.f, r0.f);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bler_d(struct ejit_func *s, struct ejit_fpr r0,
                              struct ejit_fpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGER_D, 0, r1.f, r0.f);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_blei(struct ejit_func *s, struct ejit_gpr r0, int64_t o)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, BLEI, 0, r0.r, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_blei_u(struct ejit_func *s, struct ejit_gpr r0,
                              int64_t o)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, BLEI_U, 0, r0.r, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bgti(struct ejit_func *s, struct ejit_gpr r0, int64_t o)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, BGTI, 0, r0.r, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bgti_u(struct ejit_func *s, struct ejit_gpr r0,
                              int64_t o)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, BGTI_U, 0, r0.r, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bgtr(struct ejit_func *s, struct ejit_gpr r0,
                            struct ejit_gpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGTR, 0, r0.r, r1.r);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bgtr_u(struct ejit_func *s, struct ejit_gpr r0,
                              struct ejit_gpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGTR_U, 0, r0.r, r1.r);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bgtr_f(struct ejit_func *s, struct ejit_fpr r0,
                              struct ejit_fpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGTR_F, 0, r0.f, r1.f);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bgtr_d(struct ejit_func *s, struct ejit_fpr r0,
                              struct ejit_fpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGTR_D, 0, r0.f, r1.f);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bltr(struct ejit_func *s, struct ejit_gpr r0,
                            struct ejit_gpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGTR, 0, r1.r, r0.r);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bltr_u(struct ejit_func *s, struct ejit_gpr r0,
                              struct ejit_gpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGTR_U, 0, r1.r, r0.r);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bltr_f(struct ejit_func *s, struct ejit_fpr r0,
                              struct ejit_fpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGTR_F, 0, r1.f, r0.f);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bltr_d(struct ejit_func *s, struct ejit_fpr r0,
                              struct ejit_fpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_r(s, BGTR_D, 0, r1.f, r0.f);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_blti(struct ejit_func *s, struct ejit_gpr r0, int64_t o)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, BLTI, 0, r0.r, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_blti_u(struct ejit_func *s, struct ejit_gpr r0,
                              int64_t o)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, BLTI_U, 0, r0.r, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_jmp(struct ejit_func *s)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, JMP, 0, 0, 0);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_jmpr(struct ejit_func *s, struct ejit_gpr r0)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, JMPR, 0, r0.r, 0);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bmci(struct ejit_func *s, struct ejit_gpr r0, int64_t o)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, BMCI, 0, r0.r, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bmcr(struct ejit_func *s, struct ejit_gpr r0,
                            struct ejit_gpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, BMCR, 0, r0.r, r1.r);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bmsi(struct ejit_func *s, struct ejit_gpr r0, int64_t o)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, BMSI, 0, r0.r, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bmsr(struct ejit_func *s, struct ejit_gpr r0,
                            struct ejit_gpr r1)
{
	size_t addr = vec_len(&s->insns);
	emit_insn_i(s, BMSR, 0, r0.r, r1.r);
	return (struct ejit_reloc){.insn = addr};
}

static struct interp_state create_interp_state()
{
	struct interp_state state;
	state.gprs = vec_create(sizeof(int64_t));
	state.fprs = 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;
}

int64_t ejit_run_func(struct ejit_func *f, size_t argc,
                      struct ejit_arg args[argc])
{
	assert(f->size && "trying to run a function that hasn't been compiled");
	assert(f->rtype == EJIT_VOID || ejit_int_type(f->rtype));
	if (f->arena)
		return (int64_t)((ejit_escape_t)f->arena)(argc, args);

	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])
{
	assert(f->size && "trying to run a function that hasn't been compiled");
	assert(ejit_float_type(f->rtype));
	if (f->arena)
		return ((ejit_escape_f_t)f->arena)(argc, args);

	struct interp_state state = create_interp_state();
	double r = ejit_interp(f, argc, args, &state, true, NULL).d;
	destroy_interp_state(state);
	return r;
}