/* Internal header for the MoarVM JIT compiler. Probably best not to use it
 * outside the JIT */

/* Override dynasm state definitions, so that we can use our own compiler
 * with register allocation structures etc. */
#define Dst_DECL MVMJitCompiler *compiler
#define Dst_REF (compiler->dasm_handle)
#define Dst (compiler)
#include "dasm_proto.h"

#define MVM_JIT_MAX_GLOBALS 1

struct MVMJitCompiler {
    dasm_State *dasm_handle;
    MVMJitGraph   *graph;

    MVMint32    label_offset;

    /* For spilling values that don't fit into the register allocator */
    MVMint32    spills_base;
    MVMint32    spills_free[4];
    MVM_VECTOR_DECL(struct { MVMint8 reg_type; MVMint32 next; }, spills);

    void *dasm_globals[MVM_JIT_MAX_GLOBALS];
};

/* Declarations for architecture-specific codegen stuff */
MVMint32 MVM_jit_support(void);
const unsigned char * MVM_jit_actions(void);
void MVM_jit_emit_prologue(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg);
void MVM_jit_emit_epilogue(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg);
void MVM_jit_emit_primitive(MVMThreadContext *tc, MVMJitCompiler *compiler,
                            MVMJitGraph *jg, MVMJitPrimitive *prim);
void MVM_jit_emit_call_c(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg,
                         MVMJitCallC *call_spec);
void MVM_jit_emit_branch(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMint32 label);
void MVM_jit_emit_conditional_branch(MVMThreadContext *tc, MVMJitCompiler *compiler,
                                     MVMint32 cond, MVMint32 label, MVMuint8 test_type);
void MVM_jit_emit_block_branch(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg,
                               MVMJitBranch *branch_spec);
void MVM_jit_emit_all_bb_jumps(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMint32 name);
void MVM_jit_emit_label(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg,
                        MVMint32 label);
void MVM_jit_emit_guard(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg,
                        MVMJitGuard *guard);
void MVM_jit_emit_jumplist(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg,
                           MVMJitJumpList *jumplist);
void MVM_jit_emit_control(MVMThreadContext *tc, MVMJitCompiler *compiler,
                          MVMJitControl *ctrl, MVMJitTile *tile);
void MVM_jit_emit_data(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitData *data);

void MVM_jit_emit_load(MVMThreadContext *tc, MVMJitCompiler *compiler,
                       MVMint8 reg_dst, MVMJitStorageClass mem_cls, MVMint32 mem_src, MVMint32 size);
void MVM_jit_emit_store(MVMThreadContext *tc, MVMJitCompiler *compiler,
                        MVMJitStorageClass mem_cls, MVMint32 mem_pos, MVMint8 reg_src, MVMint32 size);
void MVM_jit_emit_copy(MVMThreadContext *tc, MVMJitCompiler *compiler,
                       MVMint8 dst_reg, MVMint8 src_num);
void MVM_jit_emit_marker(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMint32 num);
void MVM_jit_emit_deopt_check(MVMThreadContext *tc, MVMJitCompiler *compiler);
void MVM_jit_emit_runbytecode(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg, MVMJitRunByteCode *runcode);
void MVM_jit_emit_runccode(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg, MVMJitRunCCode *runcode);
void MVM_jit_emit_runnativecall(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg, MVMJitRunNativeCall *runcode);
void MVM_jit_emit_dispatch(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg, MVMJitDispatch *dispatch);
void MVM_jit_emit_istype(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMJitGraph *jg, MVMJitIsType *istype);

MVMuint32 MVM_jit_spill_memory_select(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMint8 reg_type);
void MVM_jit_spill_memory_release(MVMThreadContext *tc, MVMJitCompiler *compiler, MVMuint32 pos, MVMint8 reg_type);




/* Although we use these only symbolically, we need to assign a temporary value
 * in order to to distinguish between them */
#define MVM_JIT_ARCH_X64 1
#define MVM_JIT_PLATFORM_POSIX 1
#define MVM_JIT_PLATFORM_WIN32 2

#if MVM_JIT_ARCH == MVM_JIT_ARCH_X64
#define MVM_JIT_ARCH_H "jit/x64/arch.h"
#endif

/* Depends on values of MVM_JIT_PLATFORM, so need to be defined, but uses the
 * MVM_JIT_ARCH names literally, so these need to be undefined. */
#ifdef MVM_JIT_ARCH_H
#include MVM_JIT_ARCH_H
#endif

#undef MVM_JIT_ARCH_X64
#undef MVM_JIT_PLATFORM_POSIX
#undef MVM_JIT_PLATFORM_WIN32

enum {
    MVM_JIT_ARCH_GPR(MVM_JIT_REG),
    MVM_JIT_ARCH_FPR(MVM_JIT_REG)
};

extern const MVMBitmap MVM_JIT_AVAILABLE_REGISTERS;
extern const MVMBitmap MVM_JIT_RESERVED_REGISTERS;
extern const MVMBitmap MVM_JIT_SPARE_REGISTERS;
extern const MVMBitmap MVM_JIT_REGISTER_CLASS[];

/* We need max and min macros, they used to be in libtommath, but aren't anymore */
#ifndef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif

#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif

#define MVM_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))

const char * MVM_register_type(MVMint8 reg_type);
