/* The MVMJitGraph is - for now - really a linked list of instructions.
 * It's likely I'll add complexity when it's needed */
struct MVMJitGraph {
    MVMSpeshGraph *sg;
    MVMJitNode    *first_node;
    MVMJitNode    *last_node;

    /* Number of instruction+bb+graph labels, but excluding the expression labels */
    MVMint32       num_labels;
    /* Offset for instruction labels */
    MVMint32       obj_label_ofs;

    /* Sequence number for expr trees */
    MVMuint16      expr_seq_nr;

    /* resultant JIT code is supports 'invokish' etc? */
    MVMuint8       no_trampoline;

    /* All labeled things */
    MVM_VECTOR_DECL(void*, obj_labels);
    MVM_VECTOR_DECL(MVMJitDeopt, deopts);
    MVM_VECTOR_DECL(MVMJitHandler, handlers);
    MVM_VECTOR_DECL(MVMJitInline, inlines);
    MVM_VECTOR_DECL(MVMJitNode*, label_nodes);
};

struct MVMJitDeopt {
    MVMint32 idx;
    MVMint32 label;
};

struct MVMJitHandler {
    MVMint32 start_label;
    MVMint32 end_label;
    MVMint32 goto_label;
};

struct MVMJitInline {
    MVMint32 start_label;
    MVMint32 end_label;
};

/* A label (no more than a number) */
struct MVMJitLabel {
    MVMint32    name;
};

struct MVMJitPrimitive {
    MVMSpeshIns * ins;
};

struct MVMJitGuard {
    MVMSpeshIns * ins;
    MVMuint32 deopt_idx;
};


#define MVM_JIT_INFO_INVOKISH 1
#define MVM_JIT_INFO_THROWISH 2

typedef enum {
    MVM_JIT_CONTROL_BREAKPOINT,
} MVMJitControlType;

struct MVMJitControl {
    MVMSpeshIns       *ins;
    MVMJitControlType type;
};

/* Special branch target for the exit */
#define MVM_JIT_BRANCH_EXIT -1


/* What does a branch need? a label to go to, an instruction to read */
struct MVMJitBranch {
    MVMint32     dest;
    MVMSpeshIns *ins;
};

typedef enum {
    MVM_JIT_INTERP_TC,
    MVM_JIT_INTERP_CU,
    MVM_JIT_INTERP_FRAME,
    MVM_JIT_INTERP_PARAMS,
    MVM_JIT_INTERP_CALLER,
} MVMJitInterpVar;

typedef enum {
    MVM_JIT_INTERP_VAR,
    MVM_JIT_REG_VAL,
    MVM_JIT_REG_VAL_F,
    MVM_JIT_REG_ADDR,
    MVM_JIT_STR_IDX,
    MVM_JIT_LITERAL,
    MVM_JIT_LITERAL_F,
    MVM_JIT_LITERAL_64,
    MVM_JIT_LITERAL_PTR,
    MVM_JIT_REG_STABLE,
    MVM_JIT_REG_OBJBODY,
    /* Take from register relative to cur_op. Usually code is JIT compiled by
       spesh which already known the indexes of the registers an op uses.
       Compilation of native calls however happens ahead of time when the code
       that will call the ncinvoke op may not even exist yet. In that case
       we need to do the same as interp.c and address registers relative to
       cur_op. */
    MVM_JIT_REG_DYNIDX,
    MVM_JIT_DATA_LABEL,
    /* The MVM_JIT_ARG_* types are used when the offset into the WORK array is
       not known yet, i.e. for ahead of time compiled native calls. */
    MVM_JIT_ARG_I64,
    MVM_JIT_ARG_I64_RW,
    MVM_JIT_ARG_DOUBLE,
    /* Pointers are passed as objects with CPointer representation, i.e. the
       actual pointer is part of the object's data. The MVM_JIT_ARG_PTR type
       unboxes the CPointer object and passes on the contained pointer */
    MVM_JIT_ARG_PTR,
    MVM_JIT_ARG_VMARRAY,
    /* The MVM_JIT_PARAM_* types are usd when actual JIT compilation is
       happening as part of spesh, i.e. the offset of the args buffer in WORK
       is already known. */
    MVM_JIT_PARAM_I64,
    MVM_JIT_PARAM_I64_RW,
    MVM_JIT_PARAM_DOUBLE,
    MVM_JIT_PARAM_PTR,
    MVM_JIT_PARAM_VMARRAY,
    MVM_JIT_PARAM_CARRAY,
    /* spesh slot value */
    MVM_JIT_SPESH_SLOT_VALUE,
    /* stack relative address */
    MVM_JIT_STACK_VALUE,
} MVMJitArgType;

struct MVMJitCallArg {
    MVMJitArgType type;
    union {
        MVMint64      lit_i64;
        MVMnum64      lit_n64;
        MVMJitInterpVar  ivar;
        MVMint16          reg;
        void             *ptr;
    } v;
};


typedef enum {
    MVM_JIT_RV_VOID,
    /* ptr and int are mostly the same, but they might not be on all
       platforms */
    MVM_JIT_RV_INT,
    MVM_JIT_RV_PTR,
    /* floats aren't */
    MVM_JIT_RV_NUM,
    /* dereference and store */
    MVM_JIT_RV_DEREF,
    /* store local at address */
    MVM_JIT_RV_ADDR,
    /* Store in register relative to cur_op. Usually code is JIT compiled by
       spesh which already known the indexes of the registers an op uses.
       Compilation of native calls however happens ahead of time when the code
       that will call the ncinvoke op may not even exist yet. In that case
       we need to do the same as interp.c and address registers relative to
       cur_op. */
    MVM_JIT_RV_DYNIDX,
    /* store pointer or vmnull */
    MVM_JIT_RV_DEREF_OR_VMNULL,
    /* store on stack with offset */
    MVM_JIT_RV_TO_STACK,
    /* Exactly like the RV_INT, except we negate the value afterwards */
    MVM_JIT_RV_INT_NEGATED,
} MVMJitRVMode;


struct MVMJitCallC {
    void       *func_ptr;
    MVMJitCallArg  *args;
    MVMuint16   num_args;
    MVMJitRVMode rv_mode;
    MVMint16      rv_type;
    MVMint16      rv_idx;
};

struct MVMJitInvoke {
    MVMint16      callsite_idx;
    MVMint16      arg_count;
    MVMSpeshIns **arg_ins;
    MVMReturnType return_type;
    MVMint16      return_register;
    MVMuint32     code_register_or_name;
    MVMint16      spesh_cand_or_sf_slot;
    MVMint8       is_fast;
    MVMuint32     resolve_offset;           /* Only for spesh resolve */
    MVMint32      reentry_label;
};

struct MVMJitRunByteCode {
    MVMCallsite  *callsite;
    MVMReturnType return_type;
    MVMint16      return_register;
    MVMint16      code_register;
    MVMint16      spesh_cand;
    MVMSpeshOperand *map;
    MVMint32      reentry_label;
};

struct MVMJitRunCCode {
    MVMCallsite  *callsite;
    MVMReturnType return_type;
    MVMint16      return_register;
    MVMint16      code_register;
    MVMSpeshOperand *map;
    MVMint32      reentry_label;
};

struct MVMJitRunNativeCall {
    void            *entry_point;
    MVMJitCallArg   *args;
    MVMuint16        num_args;
    MVMReturnType    return_type;
    MVMint16         return_register;
    MVMint16         rv_type;
    MVMSpeshOperand *map;
    MVMint32         reentry_label;
};

struct MVMJitDispatch {
    MVMint32      id;
    MVMCallsite  *callsite;
    MVMuint16     sf_slot;
    MVMuint32     ice_slot;
    MVMReturnType return_type;
    MVMint16      return_register;
    MVMSpeshOperand *map;
    MVMint32      reentry_label;
};

struct MVMJitIsType {
    MVMint16      return_register;
    MVMint16      obj_register;
    MVMint16      type_register;
    MVMuint16     sf_slot;
    MVMuint32     ice_slot;
    MVMint32      reentry_label;
};

struct MVMJitJumpList {
    MVMint64 num_labels;
    MVMint16 reg;
    /* labels of the goto's / jump instructions themselves */
    MVMint32 *in_labels;
    /* labels the goto's jump to */
    MVMint32 *out_labels;
};

struct MVMJitData {
    MVMint32 label;
    void     *data;
    size_t    size;
};

struct MVMJitStackSlot {
    MVMint16 slot;
};

/* Node types */
typedef enum {
    MVM_JIT_NODE_PRIMITIVE,
    MVM_JIT_NODE_CALL_C,
    MVM_JIT_NODE_BRANCH,
    MVM_JIT_NODE_LABEL,
    MVM_JIT_NODE_GUARD,
    MVM_JIT_NODE_JUMPLIST,
    MVM_JIT_NODE_CONTROL,
    MVM_JIT_NODE_DATA,
    MVM_JIT_NODE_EXPR_TREE,
    MVM_JIT_NODE_DEOPT_CHECK,
    MVM_JIT_NODE_RUNCCODE,
    MVM_JIT_NODE_RUNBYTECODE,
    MVM_JIT_NODE_RUNNATIVECALL,
    MVM_JIT_NODE_DISPATCH,
    MVM_JIT_NODE_ISTYPE,
} MVMJitNodeType;

struct MVMJitNode {
    MVMJitNode   * next; /* linked list */
    MVMJitNodeType type; /* tag */
    union {
        MVMJitPrimitive prim;
        MVMJitCallC     call;
        MVMJitBranch    branch;
        MVMJitLabel     label;
        MVMJitGuard     guard;
        MVMJitInvoke    invoke;
        MVMJitJumpList  jumplist;
        MVMJitControl   control;
        MVMJitData      data;
        MVMJitExprTree *tree;
        MVMJitStackSlot stack;
        MVMJitRunCCode   runccode;
        MVMJitRunByteCode runbytecode;
        MVMJitRunNativeCall runnativecall;
        MVMJitDispatch  dispatch;
        MVMJitIsType    istype;
    } u;
};

MVMJitGraph* MVM_jit_try_make_graph(MVMThreadContext *tc, MVMSpeshGraph *sg);
void MVM_jit_graph_destroy(MVMThreadContext *tc, MVMJitGraph *graph);
