#ifndef EJIT_COMMON_H
#define EJIT_COMMON_H

#include <ejit/ejit.h>
#include <stdbool.h>

#define VEC_TYPE struct ejit_arg
#define VEC_NAME args
#include "vec.h"

#define VEC_TYPE int64_t
#define VEC_NAME gprs
#include "vec.h"

#define VEC_TYPE double
#define VEC_NAME fprs
#include "vec.h"

#define VEC_TYPE size_t
#define VEC_NAME labels
#include "vec.h"

enum ejit_opcode {
	EJIT_OP_MOVI,
	EJIT_OP_MOVI_F,
	EJIT_OP_MOVI_D,
	EJIT_OP_MOVR,
	EJIT_OP_MOVR_F,
	EJIT_OP_MOVR_D,

	EJIT_OP_LDI8,
	EJIT_OP_LDI16,
	EJIT_OP_LDI32,
	EJIT_OP_LDI64,
	EJIT_OP_LDIU8,
	EJIT_OP_LDIU16,
	EJIT_OP_LDIU32,
	EJIT_OP_LDIU64,
	EJIT_OP_LDIF,
	EJIT_OP_LDID,

	EJIT_OP_LDXI8,
	EJIT_OP_LDXI16,
	EJIT_OP_LDXI32,
	EJIT_OP_LDXI64,
	EJIT_OP_LDXIU8,
	EJIT_OP_LDXIU16,
	EJIT_OP_LDXIU32,
	EJIT_OP_LDXIU64,
	EJIT_OP_LDXIF,
	EJIT_OP_LDXID,

	EJIT_OP_LDXR8,
	EJIT_OP_LDXR16,
	EJIT_OP_LDXR32,
	EJIT_OP_LDXR64,
	EJIT_OP_LDXRU8,
	EJIT_OP_LDXRU16,
	EJIT_OP_LDXRU32,
	EJIT_OP_LDXRU64,
	EJIT_OP_LDXRF,
	EJIT_OP_LDXRD,

	EJIT_OP_STI8,
	EJIT_OP_STI16,
	EJIT_OP_STI32,
	EJIT_OP_STI64,
	EJIT_OP_STIF,
	EJIT_OP_STID,

	EJIT_OP_STXI8,
	EJIT_OP_STXI16,
	EJIT_OP_STXI32,
	EJIT_OP_STXI64,
	EJIT_OP_STXIF,
	EJIT_OP_STXID,

	EJIT_OP_STXR8,
	EJIT_OP_STXR16,
	EJIT_OP_STXR32,
	EJIT_OP_STXR64,
	EJIT_OP_STXRF,
	EJIT_OP_STXRD,

	EJIT_OP_EXTR8,
	EJIT_OP_EXTR16,
	EJIT_OP_EXTR32,
	EJIT_OP_EXTRU8,
	EJIT_OP_EXTRU16,
	EJIT_OP_EXTRU32,
	EJIT_OP_EXTRF,
	EJIT_OP_EXTRD,

	EJIT_OP_ADDR,
	EJIT_OP_ADDR_F,
	EJIT_OP_ADDR_D,
	EJIT_OP_ADDI,

	EJIT_OP_ABSR_F,
	EJIT_OP_ABSR_D,

	EJIT_OP_SUBR,
	EJIT_OP_SUBR_F,
	EJIT_OP_SUBR_D,
	EJIT_OP_SUBI,

	EJIT_OP_MULR,
	EJIT_OP_MULR_F,
	EJIT_OP_MULR_D,

	EJIT_OP_DIVR,
	EJIT_OP_DIVR_U,
	EJIT_OP_DIVR_F,
	EJIT_OP_DIVR_D,

	EJIT_OP_REMR,
	EJIT_OP_REMR_U,

	EJIT_OP_COMR,
	EJIT_OP_NEGR,
	EJIT_OP_NEGR_F,
	EJIT_OP_NEGR_D,

	EJIT_OP_LSHI,
	EJIT_OP_LSHR,
	EJIT_OP_RSHI,
	EJIT_OP_RSHI_U,
	EJIT_OP_RSHR,
	EJIT_OP_RSHR_U,

	EJIT_OP_ANDR,
	EJIT_OP_ANDI,

	EJIT_OP_ORR,
	EJIT_OP_ORI,

	EJIT_OP_XORR,
	EJIT_OP_XORI,

	EJIT_OP_TRUNCR_D_32,
	EJIT_OP_TRUNCR_D_64,
	EJIT_OP_TRUNCR_F_32,
	EJIT_OP_TRUNCR_F_64,

	EJIT_OP_SQRTR_F,
	EJIT_OP_SQRTR_D,

	EJIT_OP_EQR,
	EJIT_OP_NER,
	EJIT_OP_GTR,
	EJIT_OP_GTR_U,
	EJIT_OP_GER,
	EJIT_OP_GER_U,

	EJIT_OP_EQR_F,
	EJIT_OP_NER_F,
	EJIT_OP_GTR_F,
	EJIT_OP_GER_F,
	EJIT_OP_EQR_D,
	EJIT_OP_NER_D,
	EJIT_OP_GTR_D,
	EJIT_OP_GER_D,

	EJIT_OP_BNER,
	EJIT_OP_BNEI,
	EJIT_OP_BNER_F,
	EJIT_OP_BNER_D,

	EJIT_OP_BEQR,
	EJIT_OP_BEQI,
	EJIT_OP_BEQR_F,
	EJIT_OP_BEQR_D,

	EJIT_OP_BGER,
	EJIT_OP_BGER_U,
	EJIT_OP_BGEI,
	EJIT_OP_BGEI_U,
	EJIT_OP_BGER_F,
	EJIT_OP_BGER_D,

	EJIT_OP_BLEI,
	EJIT_OP_BLEI_U,

	EJIT_OP_BGTR,
	EJIT_OP_BGTR_U,
	EJIT_OP_BGTI,
	EJIT_OP_BGTI_U,
	EJIT_OP_BGTR_F,
	EJIT_OP_BGTR_D,

	EJIT_OP_BLTI,
	EJIT_OP_BLTI_U,

	EJIT_OP_JMP,
	EJIT_OP_JMPR,

	EJIT_OP_BMCI,
	EJIT_OP_BMCR,
	EJIT_OP_BMSI,
	EJIT_OP_BMSR,

	EJIT_OP_PARAM,
	EJIT_OP_PARAM_F,

	EJIT_OP_ARG,
	EJIT_OP_ARG_I,
	EJIT_OP_ARG_F,
	EJIT_OP_ARG_FI,

	EJIT_OP_ESCAPEI_I,
	EJIT_OP_ESCAPEI_L,
	EJIT_OP_ESCAPEI_F,
	EJIT_OP_ESCAPEI_D,

	EJIT_OP_CALLI_I,
	EJIT_OP_CALLI_L,
	EJIT_OP_CALLI_F,
	EJIT_OP_CALLI_D,

	EJIT_OP_RETR,
	EJIT_OP_RETI,
	EJIT_OP_RETR_F,
	EJIT_OP_RETR_D,
	EJIT_OP_RETI_F,
	EJIT_OP_RETI_D,

	EJIT_OP_RETVAL,
	EJIT_OP_RETVAL_F,
	EJIT_OP_RETVAL_D,

	EJIT_OP_START,
	EJIT_OP_END,

	EJIT_OPCODE_COUNT,
};

struct ejit_insn {
	union {
		enum ejit_opcode op;
		void *addr;
	};

	size_t r0;
	size_t r1;
	union {
		size_t r2;
		void *p;
		int64_t o;
		double d;
		float f;
	};
};

#define VEC_TYPE struct ejit_insn
#define VEC_NAME insns
#include "vec.h"

#define VEC_TYPE enum ejit_type
#define VEC_NAME types
#include "vec.h"

struct fpr_stat {
	struct ejit_fpr f;
	size_t prio, fno, start, end;
};

#define VEC_NAME fpr_stats
#define VEC_TYPE struct fpr_stat
#include "vec.h"

struct gpr_stat {
	struct ejit_gpr r;
	size_t prio, rno, start, end;
};

#define VEC_NAME gpr_stats
#define VEC_TYPE struct gpr_stat
#include "vec.h"

struct ejit_func {
	struct types sign;
	struct insns insns;
	struct labels labels;
	enum ejit_type rtype;

	struct gpr_stats gpr;
	struct fpr_stats fpr;
	bool use_64;

	void *arena;
	void *direct_call;
	size_t size;
	size_t prio;
	size_t max_args;
};

union interp_ret {
	int64_t i;
	double f;
};

union interp_ret ejit_run(struct ejit_func *f, size_t argc,
                     struct ejit_arg args[argc],
		     bool run,
		     void ***labels_wb);

bool ejit_compile(struct ejit_func *f, bool use_64, bool im_scawed);

#endif /* EJIT_COMMON_H */