/* A GC sync point is a point where we can check if we're being signalled
 * to stop to do a GC run. This is placed at points where it is safe to
 * do such a thing, and hopefully so that it happens often enough; note
 * that every call down to the allocator is also a sync point, so this
 * really only means we need to do this enough to make sure tight native
 * loops trigger it. */
/* Don't use a MVM_load(&tc->gc_status) here for performance, it's okay
 * if the interrupt is delayed a bit. */
#define GC_SYNC_POINT(tc) \
    if (tc->gc_status) { \
        MVM_gc_enter_from_interrupt(tc); \
    }

/* Different views of a register. */
union MVMRegister {
    MVMObject         *o;
    MVMString *s;
    MVMint8            i8;
    MVMuint8           u8;
    MVMint16           i16;
    MVMuint16          u16;
    MVMint32           i32;
    MVMuint32          u32;
    MVMint64           i64;
    MVMuint64          u64;
    MVMnum32           n32;
    MVMnum64           n64;
};

/* Most operands an operation will have. */
#define MVM_MAX_OPERANDS 8

/* Kind of de-opt mark. */
#define MVM_DEOPT_MARK_ONE      1
#define MVM_DEOPT_MARK_ALL      2
#define MVM_DEOPT_MARK_OSR      4
#define MVM_DEOPT_MARK_ONE_PRE  8

/* Information about an opcode. */
struct MVMOpInfo {
    MVMuint16   opcode;
    const char *name;
    MVMuint16   num_operands;
    MVMuint8    pure : 1;
    MVMuint8    deopt_point : 4;
    MVMuint8    may_cause_deopt : 1;
    MVMuint8    logged : 1;
    MVMuint8    post_logged : 1;
    MVMuint8    no_inline : 1;
    MVMuint8    jittivity : 2;
    MVMuint8    uses_hll : 1;
    MVMuint8    specializable : 1;
    MVMuint8    uses_cache : 1;
    MVMuint8    operands[MVM_MAX_OPERANDS];
};

/* Operand read/write/literal flags. */
#define MVM_operand_literal     0
#define MVM_operand_read_reg    1
#define MVM_operand_write_reg   2
#define MVM_operand_read_lex    3
#define MVM_operand_write_lex   4
#define MVM_operand_rw_mask     7

/* Register data types. */
#define MVM_reg_int8            1
#define MVM_reg_int16           2
#define MVM_reg_int32           3
#define MVM_reg_int64           4
#define MVM_reg_num32           5
#define MVM_reg_num64           6
#define MVM_reg_str             7
#define MVM_reg_obj             8
#define MVM_reg_uint8           17
#define MVM_reg_uint16          18
#define MVM_reg_uint32          19
#define MVM_reg_uint64          20

/* Operand data types. */
#define MVM_operand_int8        (MVM_reg_int8 << 3)
#define MVM_operand_int16       (MVM_reg_int16 << 3)
#define MVM_operand_int32       (MVM_reg_int32 << 3)
#define MVM_operand_int64       (MVM_reg_int64 << 3)
#define MVM_operand_num32       (MVM_reg_num32 << 3)
#define MVM_operand_num64       (MVM_reg_num64 << 3)
#define MVM_operand_str         (MVM_reg_str << 3)
#define MVM_operand_obj         (MVM_reg_obj << 3)
#define MVM_operand_ins         (9 << 3)
#define MVM_operand_type_var    (10 << 3)
#define MVM_operand_coderef     (12 << 3)
#define MVM_operand_callsite    (13 << 3)
#define MVM_operand_spesh_slot  (16 << 3)
#define MVM_operand_uint8       (MVM_reg_uint8 << 3)
#define MVM_operand_uint16      (MVM_reg_uint16 << 3)
#define MVM_operand_uint32      (MVM_reg_uint32 << 3)
#define MVM_operand_uint64      (MVM_reg_uint64 << 3)
#define MVM_operand_type_mask   (31 << 3)

#ifdef MVM_BIGENDIAN
#define MVM_SWITCHENDIAN 1
#else
#define MVM_SWITCHENDIAN 2
#endif

struct MVMRunloopState {
    MVMuint8 **interp_cur_op;
    MVMuint8 **interp_bytecode_start;
    MVMRegister **interp_reg_base;
    MVMCompUnit **interp_cu;
};

/* Functions. */
void MVM_interp_run(MVMThreadContext *tc, void (*initial_invoke)(MVMThreadContext *, void *), void *invoke_data, MVMRunloopState *outer_runloop);
void MVM_interp_run_nested(MVMThreadContext *tc, void (*initial_invoke)(MVMThreadContext *, void *), void *invoke_data, MVMRegister *res);
MVM_PUBLIC void MVM_interp_enable_tracing(void);

MVM_STATIC_INLINE MVMint64 MVM_BC_get_I64(const MVMuint8 *cur_op, int offset) {
    const MVMuint8 *const where = cur_op + offset;
#ifdef MVM_CAN_UNALIGNED_INT64
    return *(MVMint64 *)where;
#else
    MVMint64 temp;
    memmove(&temp, where, sizeof(MVMint64));
    return temp;
#endif
}

MVM_STATIC_INLINE MVMnum64 MVM_BC_get_N64(const MVMuint8 *cur_op, int offset) {
#ifdef MVM_CAN_UNALIGNED_NUM64
    const MVMuint8 *const where = cur_op + offset;
    return *(MVMnum64 *)where;
#else
    MVMnum64 temp;
    memmove(&temp, cur_op + offset, sizeof(MVMnum64));
    return temp;
#endif
}
/* For MVM_reg_* types */
static char * MVM_reg_get_debug_name(MVMThreadContext *tc, MVMuint16 type) {
    switch (type) {
        case MVM_reg_int8:
            return "int8";
        case MVM_reg_int16:
            return "int16";
        case MVM_reg_int32:
            return "int32";
        case MVM_reg_int64:
            return "int64";
        case MVM_reg_num32:
            return "num32";
        case MVM_reg_num64:
            return "num64";
        case MVM_reg_str:
            return "str";
        case MVM_reg_obj:
            return "obj";
        case MVM_reg_uint8:
            return "uint8";
        case MVM_reg_uint16:
            return "uint16";
        case MVM_reg_uint32:
            return "uint32";
        case MVM_reg_uint64:
            return "uint64";
        default:
            return "unknown";
    }
}
