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

#include <ejit/ejit.h>

#include "common.h"

static void check_operands(struct ejit_func *f, size_t argc, const struct ejit_operand args[argc])
{
	assert(argc == types_len(&f->sign));
	for (size_t i = 0; i < types_len(&f->sign); ++i) {
		assert(args[i].type == *types_at(&f->sign, i));
	}
}

static void check_args(struct ejit_func *f, size_t argc, const struct ejit_arg args[argc])
{
	assert(argc == types_len(&f->sign));
	for (size_t i = 0; i < types_len(&f->sign); ++i) {
		assert(args[i].type == *types_at(&f->sign, i));
	}
}

static struct gpr_stat zero_gpr_stat()
{
	return (struct gpr_stat){
		.r = EJIT_GPR(0),
		.prio = 0,
		.rno = 0
	};
}

static struct fpr_stat zero_fpr_stat()
{
	return (struct fpr_stat){
		.f = EJIT_FPR(0),
		.prio = 0,
		.fno = 0
	};
}

static void update_gpr(struct ejit_func *f, struct ejit_gpr r0)
{
	while (gpr_stats_len(&f->gpr) <= r0.r)
		gpr_stats_append(&f->gpr, zero_gpr_stat());

	struct gpr_stat *gpr = gpr_stats_at(&f->gpr, r0.r);
	/* presumably first time we see this gpr */
	/** @todo are branches faster than a memory write? */
	if (gpr->prio == 0) {
		gpr->start = insns_len(&f->insns);
		gpr->r = r0;
	}

	gpr->end = insns_len(&f->insns);
	gpr->prio += f->prio;
}

static void update_fpr(struct ejit_func *f, struct ejit_fpr f0)
{
	while (fpr_stats_len(&f->fpr) <= f0.f)
		fpr_stats_append(&f->fpr, zero_fpr_stat());

	struct fpr_stat *fpr = fpr_stats_at(&f->fpr, f0.f);
	if (fpr->prio == 0) {
		fpr->start = insns_len(&f->insns);
		fpr->f = f0;
	}

	fpr->end = insns_len(&f->insns);
	fpr->prio += f->prio;
}

static void update_64(struct ejit_func *f, int64_t o)
{
	if (f->use_64)
		return;

	int32_t x = o >> 32;
	f->use_64 = x != -1 && x != 0;
}

static void emit_insn_o(struct ejit_func *f, enum ejit_opcode op)
{
	struct ejit_insn i = {.op = op};
	insns_append(&f->insns, i);
}

static void emit_insn_oi(struct ejit_func *f, enum ejit_opcode op, int64_t o)
{
	update_64(f, o);
	struct ejit_insn i = {.op = op, .o = o};
	insns_append(&f->insns, i);
}

static void emit_insn_oF(struct ejit_func *f, enum ejit_opcode op, float o)
{
	struct ejit_insn i = {.op = op, .f = o};
	insns_append(&f->insns, i);
}

static void emit_insn_oD(struct ejit_func *f, enum ejit_opcode op, double o)
{
	struct ejit_insn i = {.op = op, .d = o};
	insns_append(&f->insns, i);
}

static void emit_insn_of(struct ejit_func *f, enum ejit_opcode op, struct ejit_fpr f0)
{
	update_fpr(f, f0);
	struct ejit_insn i = {.op = op, .r0 = f0.f};
	insns_append(&f->insns, i);
}

static void emit_insn_or(struct ejit_func *f, enum ejit_opcode op, struct ejit_gpr r0)
{
	update_gpr(f, r0);
	struct ejit_insn i = {.op = op, .r0 = r0.r};
	insns_append(&f->insns, i);
}

static void emit_insn_ori(struct ejit_func *f, enum ejit_opcode op, struct ejit_gpr r0, int64_t o)
{
	update_gpr(f, r0);
	update_64(f, o);
	struct ejit_insn i = {.op = op, .r0 = r0.r, .o = o};
	insns_append(&f->insns, i);
}

static void emit_insn_oxf(struct ejit_func *f, enum ejit_opcode op, struct ejit_fpr f1)
{
	update_fpr(f, f1);
	struct ejit_insn i = {.op = op, .r1 = f1.f};
	insns_append(&f->insns, i);
}

static void emit_insn_orri(struct ejit_func *f, enum ejit_opcode op, struct ejit_gpr r0,
                        struct ejit_gpr r1, int64_t o)
{
	update_gpr(f, r0);
	update_gpr(f, r1);
	update_64(f, o);
	struct ejit_insn i = {.op = op, .r0 = r0.r, .r1 = r1.r, .o = o};
	insns_append(&f->insns, i);
}

static void emit_insn_ar(struct ejit_func *f, enum ejit_opcode op, size_t idx, enum ejit_type type, struct ejit_gpr r0)
{
	update_gpr(f, r0);
	struct ejit_insn i = {.op = op, .r0 = idx, .r1 = type, .r2 = r0.r};
	insns_append(&f->insns, i);
}

static void emit_insn_ai(struct ejit_func *f, enum ejit_opcode op, size_t idx, enum ejit_type type, int64_t o)
{
	update_64(f, o);
	struct ejit_insn i = {.op = op, .r0 = idx, .r1 = type, .o = o};
	insns_append(&f->insns, i);
}

static void emit_insn_af(struct ejit_func *f, enum ejit_opcode op, size_t idx, enum ejit_type type, struct ejit_fpr f0)
{
	update_fpr(f, f0);
	struct ejit_insn i = {.op = op, .r0 = idx, .r1 = type, .r2 = f0.f};
	insns_append(&f->insns, i);
}

static void emit_insn_ad(struct ejit_func *f, enum ejit_opcode op, size_t idx, enum ejit_type type, double d)
{
	fprintf(stderr, "warning: immediate floats are currently not supported.\n"
			"Consider moving values into FPRs.\n");
	struct ejit_insn i = {.op = op, .r0 = idx, .r1 = type, .d = d};
	insns_append(&f->insns, i);
}

static void emit_insn_orr(struct ejit_func *f, enum ejit_opcode op, struct ejit_gpr r0,
                        struct ejit_gpr r1)
{
	update_gpr(f, r0);
	update_gpr(f, r1);
	struct ejit_insn i = {.op = op, .r0 = r0.r, .r1 = r1.r};
	insns_append(&f->insns, i);
}

static void emit_insn_orrr(struct ejit_func *f, enum ejit_opcode op, struct ejit_gpr r0,
                        struct ejit_gpr r1, struct ejit_gpr r2)
{
	update_gpr(f, r0);
	update_gpr(f, r1);
	update_gpr(f, r2);
	struct ejit_insn i = {.op = op, .r0 = r0.r, .r1 = r1.r, .r2 = r2.r};
	insns_append(&f->insns, i);
}

static void emit_insn_off(struct ejit_func *f, enum ejit_opcode op, struct ejit_fpr f0,
                        struct ejit_fpr f1)
{
	update_fpr(f, f0);
	update_fpr(f, f1);
	struct ejit_insn i = {.op = op, .r0 = f0.f, .r1 = f1.f};
	insns_append(&f->insns, i);
}

static void emit_insn_offf(struct ejit_func *f, enum ejit_opcode op, struct ejit_fpr f0,
                        struct ejit_fpr f1, struct ejit_fpr f2)
{
	update_fpr(f, f0);
	update_fpr(f, f1);
	update_fpr(f, f2);
	struct ejit_insn i = {.op = op, .r0 = f0.f, .r1 = f1.f, .r2 = f2.f};
	insns_append(&f->insns, i);
}

static void emit_insn_orf(struct ejit_func *f, enum ejit_opcode op, struct ejit_gpr r0,
                        struct ejit_fpr f1)
{
	update_gpr(f, r0);
	update_fpr(f, f1);
	struct ejit_insn i = {.op = op, .r0 = r0.r, .r1 = f1.f};
	insns_append(&f->insns, i);
}

static void emit_insn_oxr(struct ejit_func *f, enum ejit_opcode op,
                        struct ejit_gpr r1)
{
	update_gpr(f, r1);
	struct ejit_insn i = {.op = op, .r1 = r1.r};
	insns_append(&f->insns, i);
}

static void emit_insn_oxrr(struct ejit_func *f, enum ejit_opcode op,
                        struct ejit_gpr r1, struct ejit_gpr r2)
{
	update_gpr(f, r1);
	update_gpr(f, r2);
	struct ejit_insn i = {.op = op, .r1 = r1.r, .r2 = r2.r};
	insns_append(&f->insns, i);
}

static void emit_insn_oxri(struct ejit_func *f, enum ejit_opcode op,
                        struct ejit_gpr r1, int64_t o)
{
	update_gpr(f, r1);
	update_64(f, o);
	struct ejit_insn i = {.op = op, .r1 = r1.r, .o = o};
	insns_append(&f->insns, i);
}

static void emit_insn_ofr(struct ejit_func *f, enum ejit_opcode op, struct ejit_fpr f0,
                        struct ejit_gpr r1)
{
	update_fpr(f, f0);
	update_gpr(f, r1);
	struct ejit_insn i = {.op = op, .r0 = f0.f, .r1 = r1.r};
	insns_append(&f->insns, i);
}


static void emit_insn_ofrr(struct ejit_func *f, enum ejit_opcode op, struct ejit_fpr f0,
                        struct ejit_gpr r1, struct ejit_gpr r2)
{
	update_fpr(f, f0);
	update_gpr(f, r1);
	update_gpr(f, r2);
	struct ejit_insn i = {.op = op, .r0 = f0.f, .r1 = r1.r, .r2 = r2.r};
	insns_append(&f->insns, i);
}

static void emit_insn_ofri(struct ejit_func *f, enum ejit_opcode op, struct ejit_fpr f0,
                        struct ejit_gpr r1, int64_t o)
{
	update_fpr(f, f0);
	update_gpr(f, r1);
	update_64(f, o);
	struct ejit_insn i = {.op = op, .r0 = f0.f, .r1 = r1.r, .o = o};
	insns_append(&f->insns, i);
}

static void emit_insn_orff(struct ejit_func *f, enum ejit_opcode op, struct ejit_gpr r0,
                        struct ejit_fpr f1, struct ejit_fpr f2)
{
	update_gpr(f, r0);
	update_fpr(f, f1);
	update_fpr(f, f2);
	struct ejit_insn i = {.op = op, .r0 = r0.r, .r1 = f1.f, .r2 = f2.f};
	insns_append(&f->insns, i);
}

static void emit_insn_oxff(struct ejit_func *f, enum ejit_opcode op,
                        struct ejit_fpr f1, struct ejit_fpr f2)
{
	update_fpr(f, f1);
	update_fpr(f, f2);
	struct ejit_insn i = {.op = op, .r1 = f1.f, .r2 = f2.f};
	insns_append(&f->insns, i);
}

static void emit_insn_op(struct ejit_func *f, enum ejit_opcode op, void *p)
{
	struct ejit_insn i = {.op = op, .p = p};
	insns_append(&f->insns, i);
}

static void emit_insn_orp(struct ejit_func *f, enum ejit_opcode op, struct ejit_gpr r0, void *p)
{
	update_gpr(f, r0);
	struct ejit_insn i = {.op = op, .r0 = r0.r, .p = p};
	insns_append(&f->insns, i);
}

static void emit_insn_ofp(struct ejit_func *f, enum ejit_opcode op, struct ejit_fpr f0, void *p)
{
	update_fpr(f, f0);
	struct ejit_insn i = {.op = op, .r0 = f0.f, .p = p};
	insns_append(&f->insns, i);
}

static void emit_insn_ofF(struct ejit_func *f, enum ejit_opcode op, struct ejit_fpr f0, float d)
{
	update_fpr(f, f0);
	struct ejit_insn i = {.op = op, .r0 = f0.f, .f = d};
	insns_append(&f->insns, i);
}

static void emit_insn_ofD(struct ejit_func *f, enum ejit_opcode op, struct ejit_fpr f0, double d)
{
	update_fpr(f, f0);
	struct ejit_insn i = {.op = op, .r0 = f0.f, .d = d};
	insns_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->sign = types_create();
	f->insns = insns_create();
	f->labels = labels_create();
	f->gpr = gpr_stats_create();
	f->fpr = fpr_stats_create();
	f->arena = NULL;
	f->direct_call = NULL;
	f->extern_call = NULL;
	f->size = 0;
	f->prio = 1;
	f->use_64 = false;
	f->max_args = 0;

	for (size_t i = 0; i < argc; ++i) {
		types_append(&f->sign, args[i].type);

		switch (args[i].kind) {
		case EJIT_OPERAND_GPR: {
			assert(ejit_int_type(args[i].type));
			emit_insn_ar(f, EJIT_OP_PARAM, i, args[i].type, EJIT_GPR(args[i].r));
			break;
		}

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

	emit_insn_o(f, EJIT_OP_START);
	return f;
}

void ejit_compile_func_unsafe(struct ejit_func *f)
{
	ejit_select_compile_func(f,
			gpr_stats_len(&f->gpr),
			fpr_stats_len(&f->fpr),
			f->use_64,
			true,
			false);
}

void ejit_compile_func(struct ejit_func *f)
{
	/* might make sense to use flags instead of bools... */
	ejit_select_compile_func(f,
			gpr_stats_len(&f->gpr),
			fpr_stats_len(&f->fpr),
			f->use_64,
			true,
			true);
}

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

	/* user can get some sanity checking done by passing these explicitly */
	assert(gpr >= gpr_stats_len(&f->gpr));
	assert(fpr >= fpr_stats_len(&f->fpr));
	assert(!f->use_64 || (use_64 == f->use_64));

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

	/* otherwise, convert opcodes to address labels */

	void **labels;
	/* just get labels, don't actually run anything yet */
	ejit_run(f, 0, NULL, false, &labels);

	foreach_vec(ii, f->insns) {
		struct ejit_insn i = *insns_at(&f->insns, ii);
		void *addr = labels[i.op];
		assert(addr);
		i.addr = addr;
		*insns_at(&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);

	types_destroy(&f->sign);
	insns_destroy(&f->insns);
	labels_destroy(&f->labels);
	gpr_stats_destroy(&f->gpr);
	fpr_stats_destroy(&f->fpr);
	free(f);
}

struct ejit_label ejit_label(struct ejit_func *f)
{
	size_t addr = insns_len(&f->insns);
	labels_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 = *insns_at(&f->insns, r.insn);
	/** @todo some assert that checks the opcode? */
	i.r0 = l.addr;
	*insns_at(&f->insns, r.insn) = i;
}

void ejit_calli_i(struct ejit_func *s, struct ejit_func *f, size_t argc,
                const struct ejit_operand args[argc])
{
	f->max_args = argc > f->max_args ? argc : f->max_args;
	check_operands(f, argc, 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();
		}
	}

	emit_insn_op(s, EJIT_OP_CALLI_I, f);
}

void ejit_calli_l(struct ejit_func *s, struct ejit_func *f, size_t argc,
                const struct ejit_operand args[argc])
{
	s->use_64 = true;
	f->max_args = argc > f->max_args ? argc : f->max_args;
	check_operands(f, argc, 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();
		}
	}

	emit_insn_op(s, EJIT_OP_CALLI_L, f);
}

void ejit_calli_f(struct ejit_func *s, struct ejit_func *f, size_t argc,
                const struct ejit_operand args[argc])
{
	s->max_args = argc > s->max_args ? argc : s->max_args;
	check_operands(f, argc, 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();
		}
	}

	emit_insn_op(s, EJIT_OP_CALLI_F, f);
}

void ejit_calli_d(struct ejit_func *s, struct ejit_func *f, size_t argc,
                const struct ejit_operand args[argc])
{
	s->max_args = argc > s->max_args ? argc : s->max_args;
	check_operands(f, argc, 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();
		}
	}

	emit_insn_op(s, EJIT_OP_CALLI_D, f);
}

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();
		}
	}

	emit_insn_op(s, EJIT_OP_ESCAPEI_I, f);
}

void ejit_escapei_l(struct ejit_func *s, ejit_escape_l_t f, 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();
		}
	}

	emit_insn_op(s, EJIT_OP_ESCAPEI_L, f);
}

void ejit_escapei_f(struct ejit_func *s, ejit_escape_f_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();
		}
	}

	emit_insn_op(s, EJIT_OP_ESCAPEI_F, f);
}

void ejit_escapei_d(struct ejit_func *s, ejit_escape_d_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();
		}
	}

	emit_insn_op(s, EJIT_OP_ESCAPEI_D, f);
}

void ejit_retval(struct ejit_func *s, struct ejit_gpr r0)
{
	emit_insn_or(s, EJIT_OP_RETVAL, r0);
}

void ejit_retval_f(struct ejit_func *s, struct ejit_fpr f0)
{
	emit_insn_of(s, EJIT_OP_RETVAL_F, f0);
}

void ejit_retval_d(struct ejit_func *s, struct ejit_fpr f0)
{
	emit_insn_of(s, EJIT_OP_RETVAL_D, f0);
}

void ejit_sti_8(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_orp(s, EJIT_OP_STI8, r0, p);
}

void ejit_sti_16(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_orp(s, EJIT_OP_STI16, r0, p);
}

void ejit_sti_32(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_orp(s, EJIT_OP_STI32, r0, p);
}

void ejit_sti_64(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	s->use_64 = true;
	emit_insn_orp(s, EJIT_OP_STI64, r0, p);
}

void ejit_sti_f(struct ejit_func *s, struct ejit_fpr f0, void *p)
{
	emit_insn_ofp(s, EJIT_OP_STIF, f0, p);
}

void ejit_sti_d(struct ejit_func *s, struct ejit_fpr f0, void *p)
{
	emit_insn_ofp(s, EJIT_OP_STID, f0, p);
}

void ejit_stxi_8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                 int64_t o)
{
	emit_insn_orri(s, EJIT_OP_STXI8, r0, r1, o);
}

void ejit_stxi_16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  int64_t o)
{
	emit_insn_orri(s, EJIT_OP_STXI16, r0, r1, o);
}

void ejit_stxi_32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  int64_t o)
{
	emit_insn_orri(s, EJIT_OP_STXI32, r0, r1, o);
}

void ejit_stxi_64(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  int64_t o)
{
	s->use_64 = true;
	emit_insn_orri(s, EJIT_OP_STXI64, r0, r1, o);
}

void ejit_stxi_f(struct ejit_func *s, struct ejit_fpr f0, struct ejit_gpr r1,
                 int64_t o)
{
	emit_insn_ofri(s, EJIT_OP_STXIF, f0, r1, o);
}

void ejit_stxi_d(struct ejit_func *s, struct ejit_fpr f0, struct ejit_gpr r1,
                 int64_t o)
{
	emit_insn_ofri(s, EJIT_OP_STXID, f0, r1, o);
}

void ejit_stxr_8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_STXR8, r0, r1, r2);
}

void ejit_stxr_16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_STXR16, r0, r1, r2);
}

void ejit_stxr_32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_STXR32, r0, r1, r2);
}

void ejit_stxr_64(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  struct ejit_gpr r2)
{
	s->use_64 = true;
	emit_insn_orrr(s, EJIT_OP_STXR64, r0, r1, r2);
}

void ejit_stxr_f(struct ejit_func *s, struct ejit_fpr f0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_ofrr(s, EJIT_OP_STXRF, f0, r1, r2);
}

void ejit_stxr_d(struct ejit_func *s, struct ejit_fpr f0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_ofrr(s, EJIT_OP_STXRD, f0, r1, r2);
}

void ejit_ldi_i8(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_orp(s, EJIT_OP_LDI8, r0, p);
}

void ejit_ldi_i16(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_orp(s, EJIT_OP_LDI16, r0, p);
}

void ejit_ldi_i32(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_orp(s, EJIT_OP_LDI32, r0, p);
}

void ejit_ldi_i64(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_orp(s, EJIT_OP_LDI64, r0, p);
}

void ejit_ldi_u8(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_orp(s, EJIT_OP_LDIU8, r0, p);
}

void ejit_ldi_u16(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	emit_insn_orp(s, EJIT_OP_LDIU16, r0, p);
}

void ejit_ldi_u32(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	s->use_64 = true;
	emit_insn_orp(s, EJIT_OP_LDIU32, r0, p);
}

void ejit_ldi_u64(struct ejit_func *s, struct ejit_gpr r0, void *p)
{
	s->use_64 = true;
	emit_insn_orp(s, EJIT_OP_LDIU64, r0, p);
}

void ejit_ldi_f(struct ejit_func *s, struct ejit_fpr f0, void *p)
{
	emit_insn_ofp(s, EJIT_OP_LDIF, f0, p);
}

void ejit_ldi_d(struct ejit_func *s, struct ejit_fpr f0, void *p)
{
	emit_insn_ofp(s, EJIT_OP_LDID, f0, p);
}

void ejit_ldxi_i8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  int64_t o)
{
	emit_insn_orri(s, EJIT_OP_LDXI8, r0, r1, o);
}

void ejit_ldxi_i16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   int64_t o)
{
	emit_insn_orri(s, EJIT_OP_LDXI16, r0, r1, o);
}

void ejit_ldxi_i32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   int64_t o)
{
	emit_insn_orri(s, EJIT_OP_LDXI32, r0, r1, o);
}

void ejit_ldxi_i64(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   int64_t o)
{
	emit_insn_orri(s, EJIT_OP_LDXI64, r0, r1, o);
}

void ejit_ldxi_u8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  int64_t o)
{
	emit_insn_orri(s, EJIT_OP_LDXIU8, r0, r1, o);
}

void ejit_ldxi_u16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   int64_t o)
{
	emit_insn_orri(s, EJIT_OP_LDXIU16, r0, r1, o);
}

void ejit_ldxi_u32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   int64_t o)
{
	emit_insn_orri(s, EJIT_OP_LDXIU32, r0, r1, o);
}

void ejit_ldxi_u64(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   int64_t o)
{
	emit_insn_orri(s, EJIT_OP_LDXIU64, r0, r1, o);
}

void ejit_ldxi_f(struct ejit_func *s, struct ejit_fpr f0, struct ejit_gpr r1,
                 int64_t o)
{
	emit_insn_ofri(s, EJIT_OP_LDXIF, f0, r1, o);
}

void ejit_ldxi_d(struct ejit_func *s, struct ejit_fpr f0, struct ejit_gpr r1,
                 int64_t o)
{
	emit_insn_ofri(s, EJIT_OP_LDXID, f0, r1, o);
}

void ejit_ldxr_i8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_LDXR8, r0, r1, r2);
}

void ejit_ldxr_i16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_LDXR16, r0, r1, r2);
}

void ejit_ldxr_i32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_LDXR32, r0, r1, r2);
}

void ejit_ldxr_i64(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_LDXR64, r0, r1, r2);
}

void ejit_ldxr_u8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                  struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_LDXRU8, r0, r1, r2);
}

void ejit_ldxr_u16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_LDXRU16, r0, r1, r2);
}

void ejit_ldxr_u32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_LDXRU32, r0, r1, r2);
}

void ejit_ldxr_u64(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                   struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_LDXIU64, r0, r1, r2);
}

void ejit_ldxr_f(struct ejit_func *s, struct ejit_fpr f0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_ofrr(s, EJIT_OP_LDXRF, f0, r1, r2);
}

void ejit_ldxr_d(struct ejit_func *s, struct ejit_fpr f0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_ofrr(s, EJIT_OP_LDXRD, f0, r1, r2);
}

void ejit_ret(struct ejit_func *s)
{
	emit_insn_o(s, EJIT_OP_END);
}

void ejit_retr(struct ejit_func *s, struct ejit_gpr r0)
{
	emit_insn_oxr(s, EJIT_OP_RETR, r0);
}

void ejit_retr_f(struct ejit_func *s, struct ejit_fpr f0)
{
	emit_insn_oxf(s, EJIT_OP_RETR_F, f0);
}

void ejit_retr_d(struct ejit_func *s, struct ejit_fpr f0)
{
	emit_insn_oxf(s, EJIT_OP_RETR_D, f0);
}

void ejit_reti(struct ejit_func *s, int64_t i)
{
	emit_insn_oi(s, EJIT_OP_RETI, i);
}

void ejit_reti_f(struct ejit_func *s, float f)
{
	emit_insn_oF(s, EJIT_OP_RETI_F, f);
}

void ejit_reti_d(struct ejit_func *s, double f)
{
	emit_insn_oD(s, EJIT_OP_RETI_D, f);
}

void ejit_extr_8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	emit_insn_orr(s, EJIT_OP_EXTR8, r0, r1);
}

void ejit_extr_16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	emit_insn_orr(s, EJIT_OP_EXTR16, r0, r1);
}

void ejit_extr_32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	s->use_64 = true;
	emit_insn_orr(s, EJIT_OP_EXTR32, r0, r1);
}

void ejit_extr_u8(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	emit_insn_orr(s, EJIT_OP_EXTRU8, r0, r1);
}

void ejit_extr_u16(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	emit_insn_orr(s, EJIT_OP_EXTRU16, r0, r1);
}

void ejit_extr_u32(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	s->use_64 = true;
	emit_insn_orr(s, EJIT_OP_EXTRU32, r0, r1);
}

void ejit_extr_f(struct ejit_func *s, struct ejit_fpr f0, struct ejit_gpr r1)
{
	emit_insn_ofr(s, EJIT_OP_EXTRF, f0, r1);
}

void ejit_extr_d(struct ejit_func *s, struct ejit_fpr f0, struct ejit_gpr r1)
{
	emit_insn_ofr(s, EJIT_OP_EXTRD, f0, r1);
}

void ejit_addr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_ADDR, r0, r1, r2);
}

void ejit_addr_f(struct ejit_func *s, struct ejit_fpr f0, struct ejit_fpr f1,
                 struct ejit_fpr f2)
{
	emit_insn_offf(s, EJIT_OP_ADDR_F, f0, f1, f2);
}

void ejit_addr_d(struct ejit_func *s, struct ejit_fpr f0, struct ejit_fpr f1,
                 struct ejit_fpr f2)
{
	emit_insn_offf(s, EJIT_OP_ADDR_D, f0, f1, f2);
}

void ejit_addi(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               int64_t o)
{
	emit_insn_orri(s, EJIT_OP_ADDI, r0, r1, o);
}

void ejit_absr_f(struct ejit_func *s, struct ejit_fpr f0, struct ejit_fpr f1)
{
	emit_insn_off(s, EJIT_OP_ABSR_F, f0, f1);
}

void ejit_absr_d(struct ejit_func *s, struct ejit_fpr f0, struct ejit_fpr f1)
{
	emit_insn_off(s, EJIT_OP_ABSR_D, f0, f1);
}

void ejit_subr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_SUBR, r0, r1, r2);
}

void ejit_subr_f(struct ejit_func *s, struct ejit_fpr f0, struct ejit_fpr f1,
                 struct ejit_fpr f2)
{
	emit_insn_offf(s, EJIT_OP_SUBR_F, f0, f1, f2);
}

void ejit_subr_d(struct ejit_func *s, struct ejit_fpr f0, struct ejit_fpr f1,
                 struct ejit_fpr f2)
{
	emit_insn_offf(s, EJIT_OP_SUBR_D, f0, f1, f2);
}

void ejit_subi(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               int64_t o)
{
	emit_insn_orri(s, EJIT_OP_SUBI, r0, r1, o);
}

void ejit_mulr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_MULR, r0, r1, r2);
}

void ejit_mulr_f(struct ejit_func *s, struct ejit_fpr f0, struct ejit_fpr f1,
                 struct ejit_fpr f2)
{
	emit_insn_offf(s, EJIT_OP_MULR_F, f0, f1, f2);
}

void ejit_mulr_d(struct ejit_func *s, struct ejit_fpr f0, struct ejit_fpr f1,
                 struct ejit_fpr f2)
{
	emit_insn_offf(s, EJIT_OP_MULR_D, f0, f1, f2);
}

void ejit_divr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_DIVR, r0, r1, r2);
}

void ejit_divr_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_DIVR_U, r0, r1, r2);
}

void ejit_divr_f(struct ejit_func *s, struct ejit_fpr f0, struct ejit_fpr f1,
                 struct ejit_fpr f2)
{
	emit_insn_offf(s, EJIT_OP_DIVR_F, f0, f1, f2);
}

void ejit_divr_d(struct ejit_func *s, struct ejit_fpr f0, struct ejit_fpr f1,
                 struct ejit_fpr f2)
{
	emit_insn_offf(s, EJIT_OP_DIVR_D, f0, f1, f2);
}

void ejit_remr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_REMR, r0, r1, r2);
}

void ejit_remr_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_REMR_U, r0, r1, r2);
}

void ejit_lshi(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               int64_t o)
{
	emit_insn_orri(s, EJIT_OP_LSHI, r0, r1, o);
}

void ejit_lshr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_LSHR, r0, r1, r2);
}

void ejit_rshi(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               int64_t o)
{
	emit_insn_orri(s, EJIT_OP_RSHI, r0, r1, o);
}

void ejit_rshi_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                 int64_t o)
{
	emit_insn_orri(s, EJIT_OP_RSHI_U, r0, r1, o);
}

void ejit_rshr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_RSHR, r0, r1, r2);
}

void ejit_rshr_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                 struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_RSHR_U, r0, r1, r2);
}

void ejit_andr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_ANDR, r0, r1, r2);
}

void ejit_andi(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               int64_t o)
{
	emit_insn_orri(s, EJIT_OP_ANDI, r0, r1, o);
}

void ejit_orr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_ORR, r0, r1, r2);
}

void ejit_ori(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              int64_t o)
{
	emit_insn_orri(s, EJIT_OP_ORI, r0, r1, o);
}

void ejit_xorr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_XORR, r0, r1, r2);
}

void ejit_xori(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
               int64_t o)
{
	emit_insn_orri(s, EJIT_OP_XORI, r0, r1, o);
}

void ejit_comr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	emit_insn_orr(s, EJIT_OP_COMR, r0, r1);
}

void ejit_negr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1)
{
	emit_insn_orr(s, EJIT_OP_NEGR, r0, r1);
}

void ejit_negr_f(struct ejit_func *s, struct ejit_fpr f0, struct ejit_fpr f1)
{
	emit_insn_off(s, EJIT_OP_NEGR_F, f0, f1);
}

void ejit_negr_d(struct ejit_func *s, struct ejit_fpr f0, struct ejit_fpr f1)
{
	emit_insn_off(s, EJIT_OP_NEGR_D, f0, f1);
}

void ejit_movi(struct ejit_func *s, struct ejit_gpr r0, int64_t o)
{
	emit_insn_ori(s, EJIT_OP_MOVI, r0, o);
}

void ejit_movi_f(struct ejit_func *s, struct ejit_fpr f0, float o)
{
	emit_insn_ofF(s, EJIT_OP_MOVI_F, f0, o);
}

void ejit_movi_d(struct ejit_func *s, struct ejit_fpr f0, double o)
{
	emit_insn_ofD(s, EJIT_OP_MOVI_D, f0, o);
}

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

	emit_insn_orr(s, EJIT_OP_MOVR, r0, r1);
}

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

	emit_insn_off(s, EJIT_OP_MOVR_F, f0, f1);
}

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

	emit_insn_off(s, EJIT_OP_MOVR_D, f0, f1);
}

void ejit_eqr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_EQR, r0, r1, r2);
}

void ejit_eqr_f(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr f1,
                struct ejit_fpr f2)
{
	emit_insn_orff(s, EJIT_OP_EQR_F, r0, f1, f2);
}

void ejit_eqr_d(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr f1,
                struct ejit_fpr f2)
{
	emit_insn_orff(s, EJIT_OP_EQR_D, r0, f1, f2);
}

void ejit_ner(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_NER, r0, r1, r2);
}

void ejit_ner_f(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr f1,
                struct ejit_fpr f2)
{
	emit_insn_orff(s, EJIT_OP_NER_F, r0, f1, f2);
}

void ejit_ner_d(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr f1,
                struct ejit_fpr f2)
{
	emit_insn_orff(s, EJIT_OP_NER_D, r0, f1, f2);
}

void ejit_gtr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_GTR, r0, r1, r2);
}

void ejit_gtr_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_GTR_U, r0, r1, r2);
}

void ejit_gtr_f(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr f1,
                struct ejit_fpr f2)
{
	emit_insn_orff(s, EJIT_OP_GTR_F, r0, f1, f2);
}

void ejit_gtr_d(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr f1,
                struct ejit_fpr f2)
{
	emit_insn_orff(s, EJIT_OP_GTR_D, r0, f1, f2);
}

void ejit_ltr(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_GTR, r0, r2, r1);
}

void ejit_ltr_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_GTR_U, r0, r2, r1);
}

void ejit_ltr_f(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr f1,
                struct ejit_fpr f2)
{
	emit_insn_orff(s, EJIT_OP_GTR_F, r0, f2, f1);
}

void ejit_ltr_d(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr f1,
                struct ejit_fpr f2)
{
	emit_insn_orff(s, EJIT_OP_GTR_D, r0, f2, f1);
}

void ejit_ger(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_GER, r0, r1, r2);
}

void ejit_ger_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_GER_U, r0, r1, r2);
}

void ejit_ger_f(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr f1,
                struct ejit_fpr f2)
{
	emit_insn_orff(s, EJIT_OP_GER_F, r0, f1, f2);
}

void ejit_ger_d(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr f1,
                struct ejit_fpr f2)
{
	emit_insn_orff(s, EJIT_OP_GER_D, r0, f1, f2);
}

void ejit_ler(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
              struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_GER, r0, r2, r1);
}

void ejit_ler_u(struct ejit_func *s, struct ejit_gpr r0, struct ejit_gpr r1,
                struct ejit_gpr r2)
{
	emit_insn_orrr(s, EJIT_OP_GER_U, r0, r2, r1);
}

void ejit_ler_f(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr f1,
                struct ejit_fpr f2)
{
	emit_insn_orff(s, EJIT_OP_GER_F, r0, f2, f1);
}

void ejit_ler_d(struct ejit_func *s, struct ejit_gpr r0, struct ejit_fpr f1,
                struct ejit_fpr f2)
{
	emit_insn_orff(s, EJIT_OP_GER_D, r0, f2, f1);
}

void ejit_truncr_d_32(struct ejit_func *s, struct ejit_gpr r0,
                      struct ejit_fpr f1)
{
	emit_insn_orf(s, EJIT_OP_TRUNCR_D_32, r0, f1);
}

void ejit_truncr_d_64(struct ejit_func *s, struct ejit_gpr r0,
                      struct ejit_fpr f1)
{
	s->use_64 = true;
	emit_insn_orf(s, EJIT_OP_TRUNCR_D_64, r0, f1);
}

void ejit_truncr_f_32(struct ejit_func *s, struct ejit_gpr r0,
                      struct ejit_fpr f1)
{
	emit_insn_orf(s, EJIT_OP_TRUNCR_F_32, r0, f1);
}

void ejit_truncr_f_64(struct ejit_func *s, struct ejit_gpr r0,
                      struct ejit_fpr f1)
{
	s->use_64 = true;
	emit_insn_orf(s, EJIT_OP_TRUNCR_F_64, r0, f1);
}

void ejit_sqrtr_f(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1)
{
	emit_insn_off(s, EJIT_OP_SQRTR_F, r0, r1);
}

void ejit_sqrtr_d(struct ejit_func *s, struct ejit_fpr r0, struct ejit_fpr r1)
{
	emit_insn_off(s, EJIT_OP_SQRTR_D, r0, r1);
}

struct ejit_reloc ejit_bner(struct ejit_func *s, struct ejit_gpr r0,
                            struct ejit_gpr r1)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_oxrr(s, EJIT_OP_BNER, r0, r1);
	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 = insns_len(&s->insns);
	emit_insn_oxri(s, EJIT_OP_BNEI, r0, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bner_f(struct ejit_func *s, struct ejit_fpr f0,
                              struct ejit_fpr f1)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_oxff(s, EJIT_OP_BNER_F, f0, f1);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bner_d(struct ejit_func *s, struct ejit_fpr f0,
                              struct ejit_fpr f1)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_oxff(s, EJIT_OP_BNER_D, f0, f1);
	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 = insns_len(&s->insns);
	emit_insn_oxrr(s, EJIT_OP_BEQR, r0, r1);
	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 = insns_len(&s->insns);
	emit_insn_oxri(s, EJIT_OP_BEQI, r0, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_beqr_f(struct ejit_func *s, struct ejit_fpr f0,
                              struct ejit_fpr f1)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_oxff(s, EJIT_OP_BEQR_F, f0, f1);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_beqr_d(struct ejit_func *s, struct ejit_fpr f0,
                              struct ejit_fpr f1)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_oxff(s, EJIT_OP_BEQR_D, f0, f1);
	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 = insns_len(&s->insns);
	emit_insn_oxrr(s, EJIT_OP_BGER, r0, r1);
	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 = insns_len(&s->insns);
	emit_insn_oxrr(s, EJIT_OP_BGER_U, r0, r1);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bger_f(struct ejit_func *s, struct ejit_fpr f0,
                              struct ejit_fpr f1)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_oxff(s, EJIT_OP_BGER_F, f0, f1);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bger_d(struct ejit_func *s, struct ejit_fpr f0,
                              struct ejit_fpr f1)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_oxff(s, EJIT_OP_BGER_D, f0, f1);
	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 = insns_len(&s->insns);
	emit_insn_oxri(s, EJIT_OP_BGEI, r0, 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 = insns_len(&s->insns);
	emit_insn_oxri(s, EJIT_OP_BGEI_U, r0, 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 = insns_len(&s->insns);
	emit_insn_oxrr(s, EJIT_OP_BGER, r1, r0);
	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 = insns_len(&s->insns);
	emit_insn_oxrr(s, EJIT_OP_BGER_U, r1, r0);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bler_f(struct ejit_func *s, struct ejit_fpr f0,
                              struct ejit_fpr f1)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_oxff(s, EJIT_OP_BGER_F, f1, f0);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bler_d(struct ejit_func *s, struct ejit_fpr f0,
                              struct ejit_fpr f1)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_oxff(s, EJIT_OP_BGER_D, f1, f0);
	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 = insns_len(&s->insns);
	emit_insn_oxri(s, EJIT_OP_BLEI, r0, 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 = insns_len(&s->insns);
	emit_insn_oxri(s, EJIT_OP_BLEI_U, r0, 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 = insns_len(&s->insns);
	emit_insn_oxri(s, EJIT_OP_BGTI, r0, 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 = insns_len(&s->insns);
	emit_insn_oxri(s, EJIT_OP_BGTI_U, r0, 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 = insns_len(&s->insns);
	emit_insn_oxrr(s, EJIT_OP_BGTR, r0, r1);
	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 = insns_len(&s->insns);
	emit_insn_oxrr(s, EJIT_OP_BGTR_U, r0, r1);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bgtr_f(struct ejit_func *s, struct ejit_fpr f0,
                              struct ejit_fpr f1)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_oxff(s, EJIT_OP_BGTR_F, f0, f1);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bgtr_d(struct ejit_func *s, struct ejit_fpr f0,
                              struct ejit_fpr f1)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_oxff(s, EJIT_OP_BGTR_D, f0, f1);
	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 = insns_len(&s->insns);
	emit_insn_oxrr(s, EJIT_OP_BGTR, r1, r0);
	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 = insns_len(&s->insns);
	emit_insn_oxrr(s, EJIT_OP_BGTR_U, r1, r0);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bltr_f(struct ejit_func *s, struct ejit_fpr f0,
                              struct ejit_fpr f1)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_oxff(s, EJIT_OP_BGTR_F, f1, f0);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_bltr_d(struct ejit_func *s, struct ejit_fpr f0,
                              struct ejit_fpr f1)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_oxff(s, EJIT_OP_BGTR_D, f1, f0);
	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 = insns_len(&s->insns);
	emit_insn_oxri(s, EJIT_OP_BLTI, r0, 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 = insns_len(&s->insns);
	emit_insn_oxri(s, EJIT_OP_BLTI_U, r0, o);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_jmp(struct ejit_func *s)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_o(s, EJIT_OP_JMP);
	return (struct ejit_reloc){.insn = addr};
}

struct ejit_reloc ejit_jmpr(struct ejit_func *s, struct ejit_gpr r0)
{
	size_t addr = insns_len(&s->insns);
	emit_insn_oxr(s, EJIT_OP_JMPR, r0);
	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 = insns_len(&s->insns);
	emit_insn_oxri(s, EJIT_OP_BMCI, r0, 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 = insns_len(&s->insns);
	emit_insn_oxrr(s, EJIT_OP_BMCR, r0, r1);
	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 = insns_len(&s->insns);
	emit_insn_oxri(s, EJIT_OP_BMSI, r0, 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 = insns_len(&s->insns);
	emit_insn_oxrr(s, EJIT_OP_BMSR, r0, r1);
	return (struct ejit_reloc){.insn = addr};
}

long ejit_run_func_i(struct ejit_func *f, size_t argc,
                      struct ejit_arg args[argc])
{
	check_args(f, argc, args);
	assert((f->rtype == EJIT_VOID || ejit_int_type(f->rtype))
#if __WORDSIZE != 64
		&& f->rtype != EJIT_INT64 && f->rtype != EJIT_UINT64
#endif
		);

	return ejit_run(f, argc, args, true, NULL).i;
}

int64_t ejit_run_func_l(struct ejit_func *f, size_t argc,
                      struct ejit_arg args[argc])
{
	check_args(f, argc, args);
	assert(f->rtype == EJIT_INT64 || f->rtype == EJIT_UINT64);
	return ejit_run(f, argc, args, true, NULL).i;
}

float ejit_run_func_f(struct ejit_func *f, size_t argc, struct ejit_arg args[argc])
{
	check_args(f, argc, args);
	assert(f->rtype == EJIT_FLOAT);
	return ejit_run(f, argc, args, true, NULL).f;
}

double ejit_run_func_d(struct ejit_func *f, size_t argc, struct ejit_arg args[argc])
{
	check_args(f, argc, args);
	assert(f->rtype == EJIT_DOUBLE);
	return ejit_run(f, argc, args, true, NULL).f;
}

struct ejit_arg ejit_run_func(struct ejit_func *f, size_t argc, struct ejit_arg args[argc])
{
	switch (f->rtype) {
	case EJIT_DOUBLE:
		return (struct ejit_arg){
			.d = ejit_run_func_d(f, argc, args),
			.type = EJIT_DOUBLE
		};

	case EJIT_FLOAT:
		return (struct ejit_arg){
			.f = ejit_run_func_f(f, argc, args),
			.type = EJIT_FLOAT
		};

	case EJIT_UINT64:
		return (struct ejit_arg){
			.u64 = ejit_run_func_l(f, argc, args),
			.type = f->rtype
		};
	case EJIT_INT64:
		return (struct ejit_arg){
			.i64 = ejit_run_func_l(f, argc, args),
			.type = f->rtype
		};

	default:
		return (struct ejit_arg){
			.l = ejit_run_func_i(f, argc, args),
			.type = f->rtype
		};
	}
}

size_t ejit_get_prio(struct ejit_func *s)
{
	return s->prio;
}

void ejit_set_prio(struct ejit_func *s, size_t prio)
{
	s->prio = prio;
}

void ejit_inc_prio(struct ejit_func *s, size_t prio)
{
	s->prio += prio;
}

void ejit_dec_prio(struct ejit_func *s, size_t prio)
{
	s->prio -= prio;
}