/* A dispatch program is set of operations that we execute in order to perform
 * a dispatch. A dispatch callsite goes through a number of states:
 *
 * 1. Unlinked. This means that we will unconditionally run the dispatch
 *    callback. This can set guards, do capture transformations, delegate,
 *    and eventually ends up at a dispatch terminal. This set of steps make
 *    up the dispatch program; we say that the dispatch callback "records" a
 *    dispatch program.
 * 2. Monomorphic. This means that we have a single dispatch program recorded
 *    and we try to play it back. The dispatch program is a series of linear
 *    steps.
 * 3. Polymorphic. This means that we have recorded multiple dispatch programs
 *    at this callsite. We store each program, but may also store a tree or
 *    other prefix-lifting form of the programs. (However, the first cut will
 *    probably just run them in order.)
 * 4. Megamorphic. This means that we have reached the maximum number of
 *    dispatch programs we're willing to record here. Now we either call a
 *    megamorphic handler callback if one is registered, or otherwise run
 *    the dispatch program but without building up any guards.
 *
 * These states are modelled as different callbacks in the inline cache at the
 * callsite.
 */

/* Kinds of outcome of a dispatch program. */
typedef enum {
    /* Indicates we failed to produce an outcome, probably due to an exception. */
    MVM_DISP_OUTCOME_FAILED,

    /* Indicates we expect the userspace dispatch we're running to specify another
     * dispatcher to delegate to. */
    MVM_DISP_OUTCOME_EXPECT_DELEGATE,

    /* Indicates we are going to invoke the resume callback of the specified
     * dispatcher. */
    MVM_DISP_OUTCOME_RESUME,

    /* Indicates we are going to fall back on the next, outer, resumption. */
    MVM_DISP_OUTCOME_NEXT_RESUMPTION,

    /* Return a value (produced by the dispatch program). */
    MVM_DISP_OUTCOME_VALUE,

    /* Invoke bytecode. */
    MVM_DISP_OUTCOME_BYTECODE,

    /* Invoke a C function. */
    MVM_DISP_OUTCOME_CFUNCTION,

    /* Invoke a foreign C function. */
    MVM_DISP_OUTCOME_FOREIGNCODE
} MVMDispProgramOutcomeKind;

/* The outcome of a dispatch program. Used only in the record case; the run
 * just does the effect of that outcome. */
struct MVMDispProgramOutcome {
    /* The kind of outcome we have. */
    MVMDispProgramOutcomeKind kind;

    /* Data that goes with each outcome kind. */
    union {
        /* A value to return, along with the kind of value it is. Marked. */
        struct {
            MVMRegister result_value;
            MVMuint8 result_kind;
        };
        /* A invocation of either bytecode or a C function. */
        struct {
            union {
                /* The code object to invoke. Marked. */
                MVMCode *code;
                /* The C function to be invoked. */
                void (*c_func) (MVMThreadContext *tc, MVMArgs arg_info);

                /* The native call site to be invoked. */
                MVMNativeCall *site;
            };
            /* Arguments for an invocation (must point into otherwise marked
             * areas). */
            MVMArgs args;
        };
        /* A dispatcher delegation. */
        struct {
            MVMDispDefinition *delegate_disp;
            MVMObject *delegate_capture;
        };
        /* A dispatch resumption. */
        struct {
            MVMObject *resume_capture;
        };
    };
};

/* A value that is involved in the dispatch. It may come from the initial
 * arguments, it may be an inserted constant, or it may be from an attribute
 * read out of another value. */
typedef enum {
    /* A value from the initial capture. */
    MVMDispProgramRecordingCaptureValue,
    /* A value from the resume initialization state capture. */
    MVMDispProgramRecordingResumeInitCaptureValue,
    /* A literal constant value. */
    MVMDispProgramRecordingLiteralValue,
    /* A read of an attribute from a dependent value. */
    MVMDispProgramRecordingAttributeValue,
    /* A read of .HOW from a value. */
    MVMDispProgramRecordingHOWValue,
    /* An unbox of a value. */
    MVMDispProgramRecordingUnboxValue,
    /* A read of a key from a lookup table. */
    MVMDispProgramRecordingLookupValue,
    /* The resume state for this dispatcher. */
    MVMDispProgramRecordingResumeStateValue
} MVMDispProgramRecordingValueSource;
struct MVMDispProgramRecordingValue {
    /* The source of the value, which determines which part of the union below
     * that we shall look at. */
    MVMDispProgramRecordingValueSource source;

    /* Details (depends on source). */
    union {
        struct {
            /* The index within the initial arguments capture. */
            MVMuint32 index;
        } capture;
        struct {
            /* The index within the resume initialization arguments capture. */
            MVMuint32 index;
            /* The nesting level of the resume initialization. */
            MVMuint32 resumption_level;
        } resume_capture;
        struct {
            /* The literal value and its kind. */
            MVMRegister value;
            MVMCallsiteFlags kind;
        } literal;
        struct {
            /* The value that we'll read from. */
            MVMuint32 from_value;
            /* The offset of that object we'll read from. */
            MVMuint32 offset;
            /* The kind of value we'll read. */
            MVMCallsiteFlags kind;
        } attribute;
        struct {
            /* The value that we'll read the HOW of. */
            MVMuint32 from_value;
        } how;
        struct {
            /* The value that we'll unbox. */
            MVMuint32 from_value;
            /* The kind of value we'll unbox. */
            MVMCallsiteFlags kind;
        } unbox;
        struct {
            /* The value index of the lookup hash. */
            MVMuint32 lookup_index;
            /* The value index of the lookup key. */
            MVMuint32 key_index;
        } lookup;
        struct {
            /* The index of the resumption that this is the state of. */
            MVMuint32 index;
        } resumption;
    };

    /* The tracking object, if any. */
    MVMObject *tracked;

    /* Basic guards that have been applied. When we compile the guard program,
     * we'll often simplify this; for example, if the incoming argument was a
     * literal string then we'd drop the literal value guard, or if it has a
     * literal guard then the type and concreteness guards are implicitly
     * covered anyway. */
    MVMuint8 guard_type;
    MVMuint8 guard_concreteness;
    MVMuint8 guard_literal;
    MVMuint8 guard_hll;

    /* A list of objects that this value must *not* be. */
    MVM_VECTOR_DECL(MVMObject *, not_literal_guards);
};

/* A derived capture. */
typedef enum {
    MVMDispProgramRecordingInitial,
    MVMDispProgramRecordingResumeInitial,
    MVMDispProgramRecordingDrop,
    MVMDispProgramRecordingInsert
} MVMDispProgramRecordingTransformation;
struct MVMDispProgramRecordingCapture {
    /* The capture object we produced and handed back. */
    MVMObject *capture;

    /* The kind of transformation that it did. */
    MVMDispProgramRecordingTransformation transformation;

    /* The index involved in the insert or drop. */
    MVMuint32 index;

    /* For inserts, the index of the value that was involved. */
    MVMuint32 value_index;

    /* Tree of captures further derived from the this one. */
    MVM_VECTOR_DECL(MVMDispProgramRecordingCapture, captures);
};

/* Resume initialization arguments set up for future resumptions. */
struct MVMDispProgramRecordingResumeInit {
    /* The resumable dispatcher that, upon resumption, wants these args to
     * figure out how to resume. */
    MVMDispDefinition *disp;

    /* The capture (which must appear in the capture tree). */
    MVMObject *capture;
};

/* When we are recording a dispatch program for a resumption of an existing
 * dispatch, we need to keep some state about that. However, a resumption may
 * be exhausted of candidates and then defer to another resumption that is a
 * part of the same dispatch (for example, in Raku, method dispatch to a
 * wrapped method will resume through the wrappers, then back to walking
 * the MRO). Thus we need to keep a list of the dispatch resumptions we go
 * through in the recording, so we can account for this in the dispatch
 * program. */
struct MVMDispProgramRecordingResumption {
    /* Details of the dispatcher we're resuming. */
    MVMDispProgramResumption *resumption;

    /* The resume initialization state capture. */
    MVMDispProgramRecordingCapture initial_resume_capture;

    /* Temporary storage for a resume init state capture, if it has to be
     * formed. */
    MVMRegister *initial_resume_args;

    /* Pointer to where the mutable resume state lives for the dispatcher. */
    MVMObject **resume_state_ptr;

    /* The index of the value in the dispatch program that corresponds to the
     * new dispatch state; -1 if there isn't any. */
    MVMint32 new_resume_state_value;

    /* The total number of dispatch values read at the end of this resumption
     * (before we defer to a next resumption). This is used so we can emit the
     * guards grouped by the resumption, so we only are considering a single
     * active resumption at a time. */
    MVMuint32 num_values;

    /* The total number of new resumption initialization arguments set up at
     * the end of this resumption. */
    MVMuint32 num_resume_inits;

    /* Did the resumption look for a further resumption to take place and not
     * find one? Used so we can produce a guard against that in the dispatch
     * program, so it won't falsely match. */
    MVMuint32 no_next_resumption;
};

/* Recording state of a dispatch program, updated as we move through the record
 * phase. */
typedef enum {
    MVMDispProgramRecordingResumeNone,
    MVMDispProgramRecordingResumeTopmost,
    MVMDispProgramRecordingResumeCaller
} MVMDispProgramRecordingResumeKind;
typedef enum {
    MVMDispProgramRecordingBindControlNone,
    MVMDispProgramRecordingBindControlAll,
    MVMDispProgramRecordingBindControlFailure
} MVMDispProgramRecordingBindControlKind;
struct MVMDispProgramRecording {
    /* The initial argument capture, top of the tree of captures. */
    MVMDispProgramRecordingCapture initial_capture;

    /* The kind of resume we're doing, if any. */
    MVMDispProgramRecordingResumeKind resume_kind;

    /* The inline cache size when we started recording this dispatch. */
    MVMuint32 inline_cache_size;

    /* If we are doing a resume, the list of resumable dispatches we have
     * worked through  */
    MVM_VECTOR_DECL(MVMDispProgramRecordingResumption, resumptions);

    /* The values that we have encountered while recording, and maybe added
     * guards against. */
    MVM_VECTOR_DECL(MVMDispProgramRecordingValue, values);

    /* Any resume init args that we have saved (for new resumable dispatches
     * we set up). */
    MVM_VECTOR_DECL(MVMDispProgramRecordingResumeInit, resume_inits);

    /* The index of the value that is the outcome of the dispatch. For a value
     * outcome, it's the value we'll produce. For the invocations, it's the
     * code or C function value. */
    MVMuint32 outcome_value;

    /* For an invocation outcome, this is the capture that we shall invoke the
     * arguments with. Will be located within the capture tree somewhere. */
    MVMObject *outcome_capture;

    /* If it's an invocation outcome, do we want bind failure (and maybe also
     * success) mapped to a resumption, and if so, what flag should we pass? */
    MVMuint32 bind_failure_resumption_flag;
    MVMuint32 bind_success_resumption_flag;
    MVMDispProgramRecordingBindControlKind map_bind_outcome_to_resumption;

    /* Flag indicating if we should install the dispatch program at the
     * callsite or just keep it around until unwind (if resumptions need
     * us to). */
    MVMuint8 do_not_install;
};

/* A "compiled" dispatch program, which is what we interpret at a callsite
 * (at least, until specialization, where more exciting things happen). */
struct MVMDispProgram {
    /* Non-GC-managed constants used in the dispatch. */
    MVMDispProgramConstant *constants;

    /* GC-managed constants (objects, strings, and STables) used in the
     * dispatch. */
    MVMCollectable **gc_constants;

    /* The number of GC-managed constants (for more efficient marking of
     * them). */
    MVMuint32 num_gc_constants;

    /* The number of temporaries. Temporaries are used while evaluating
     * the dispatch program, and may also be used as an argument buffer
     * when we cannot just point into a tail of the original one. */
    MVMuint32 num_temporaries;

    /* The first of the temporaries that is used in order to store an
     * argument buffer. These live long enough that we need to mark
     * them using the callsite. Equals num_temporaries if there aren't
     * any. */
    MVMuint32 first_args_temporary;

    /* Ops we execute to evaluate the dispatch program. */
    MVMuint32 num_ops;
    MVMDispProgramOp *ops;

    /* Resumptions, if any, ordered innermost first. */
    MVMDispProgramResumption *resumptions;
    MVMuint32 num_resumptions;
};

/* Various kinds of constant we use during a dispatch program, to let us keep
 * the ops part more compact. */
union MVMDispProgramConstant {
    MVMCallsite *cs;
    MVMHLLConfig *hll;
    MVMint64 i64;
    MVMnum64 n64;
};

/* Opcodes we may execute in a dispatch program. */
typedef enum {
    /* Initialize a resume dispatch, meaning we're starting out considering
     * the innermost resumption. */
    MVMDispOpcodeStartResumption,
    /* Increment the resumption nesting level. */
    MVMDispOpcodeNextResumption,
    /* Resume the topmost dispatch, taking the resumption at the current
     * nesting lvel, so long as it is for the appropriate dispatcher. */
    MVMDispOpcodeResumeTopmost,
    /* Same, except we're looking to the caller for the dispatcher to
     * resume. */
    MVMDispOpcodeResumeCaller,
    /* Assert that the resumption callsite is as expected. */
    MVMDispOpcodeGuardResumeInitCallsite,
    /* Assert that there is no further resumptions beyond those already
     * considered in the topmost dispatch. */
    MVMDispOpcodeGuardNoResumptionTopmost,
    /* Same, except we're looking to the caller. */
    MVMDispOpcodeGuardNoResumptionCaller,
    /* Update the dispatch resume state to the specified temporary. */
    MVMDispOpcodeUpdateResumeState,
    /* Guard that the type of an incoming argument is as expected. */
    MVMDispOpcodeGuardArgType,
    /* Guard that the type of an incoming argument is as expected and also
     * we have a concrete object. */
    MVMDispOpcodeGuardArgTypeConc,
    /* Guard that the type of an incoming argument is as expected and also
     * we have a type object. */
    MVMDispOpcodeGuardArgTypeTypeObject,
    /* Guard that the incoming argument is concrete. */
    MVMDispOpcodeGuardArgConc,
    /* Guard that the incoming argument is a type object. */
    MVMDispOpcodeGuardArgTypeObject,
    /* Guard that the type of an incoming argument is an expected object
     * literal. */
    MVMDispOpcodeGuardArgLiteralObj,
    /* Guard that the type of an incoming argument is an expected string
     * literal. */
    MVMDispOpcodeGuardArgLiteralStr,
    /* Guard that the type of an incoming argument is an expected int
     * literal. */
    MVMDispOpcodeGuardArgLiteralInt,
    /* Guard that the type of an incoming argument is an expected num
     * literal. */
    MVMDispOpcodeGuardArgLiteralNum,
    /* Guard that the type of an incoming argument is not an unexpected
     * literal. */
    MVMDispOpcodeGuardArgNotLiteralObj,
    /* Guard that the HLL of an incoming argument is as expected. */
    MVMDispOpcodeGuardArgHLL,
    /* Guard that the type of a temporary is as expected. */
    MVMDispOpcodeGuardTempType,
    /* Guard that the type of a temporary is as expected and also
     * we have a concrete object. */
    MVMDispOpcodeGuardTempTypeConc,
    /* Guard that the type of a temporary is as expected and also
     * we have a type object. */
    MVMDispOpcodeGuardTempTypeTypeObject,
    /* Guard that the incoming argument is concrete. */
    MVMDispOpcodeGuardTempConc,
    /* Guard that the incoming argument is a type object. */
    MVMDispOpcodeGuardTempTypeObject,
    /* Guard that the type of a temporary is an expected object
     * literal. */
    MVMDispOpcodeGuardTempLiteralObj,
    /* Guard that the type of a temporary is an expected string
     * literal. */
    MVMDispOpcodeGuardTempLiteralStr,
    /* Guard that the type of a temporary is an expected int
     * literal. */
    MVMDispOpcodeGuardTempLiteralInt,
    /* Guard that the type of a temporary is an expected num
     * literal. */
    MVMDispOpcodeGuardTempLiteralNum,
    /* Guard that the type of a temporary is not an unexpected
     * literal. */
    MVMDispOpcodeGuardTempNotLiteralObj,
    /* Guard that the HLL of a temporary is as expected. */
    MVMDispOpcodeGuardTempHLL,
    /* Load a capture value into a temporary. */
    MVMDispOpcodeLoadCaptureValue,
    /* Load an resumption initialization state value into a temporary. */
    MVMDispOpcodeLoadResumeInitValue,
    /* Load the resumption state into a temporary. */
    MVMDispOpcodeLoadResumeState,
    /* Load a constant object or string into a temporary. */
    MVMDispOpcodeLoadConstantObjOrStr,
    /* Load a constant int into a temporary. */
    MVMDispOpcodeLoadConstantInt,
    /* Load a constant num into a temporary. */
    MVMDispOpcodeLoadConstantNum,
    /* Load an attribute object value into a temporary. */
    MVMDispOpcodeLoadAttributeObj,
    /* Load an attribute int value into a temporary. */
    MVMDispOpcodeLoadAttributeInt,
    /* Load an attribute num value into a temporary. */
    MVMDispOpcodeLoadAttributeNum,
    /* Load an attribute string value into a temporary. */
    MVMDispOpcodeLoadAttributeStr,
    /* Load an object's HOW into a temporary. */
    MVMDispOpcodeLoadHOW,
    /* Unbox an int from an object into a temporary. */
    MVMDispOpcodeUnboxInt,
    /* Unbox a num from an object into a temporary. */
    MVMDispOpcodeUnboxNum,
    /* Unbox a str from an object into a temporary. */
    MVMDispOpcodeUnboxStr,
    /* Do a lookup in a hash table and put the result into a temporary if it is
     * found. */
    MVMDispOpcodeLookup,
    /* Set one temp to the value of another. */
    MVMDispOpcodeSet,
    /* Set an object result outcome from a temporary. */
    MVMDispOpcodeResultValueObj,
    /* Set a string result outcome from a temporary. */
    MVMDispOpcodeResultValueStr,
    /* Set an integer result outcome from a temporary. */
    MVMDispOpcodeResultValueInt,
    /* Set a num result outcome from a temporary. */
    MVMDispOpcodeResultValueNum,
    /* For call outcomes, push a callstack entry used to indicate that
     * a bind failure should be mapped to a resumption. */
    MVMDispOpcodeBindFailureToResumption,
    /* For call outcomes, push a callstack entry used to indicate that
     * a bind success or failure should be mapped to a resumption. */
    MVMDispOpcodeBindCompletionToResumption,
    /* Set the args buffer to use in the call to be the tail of that of
     * the initial capture. (Actually it's really the map that we take
     * the tail of.) The argument is how many of the args to skip from
     * the start. This is used when the dispatch should use everything
     * except the first args. */
    MVMDispOpcodeUseArgsTail,
    /* When we have to really produce a new set of arguments, we use this
     * op. Before it, we use load instructions to build the start of the
     * argument list. We then pass in a number of arguments to copy from
     * the incoming argument list, which may be 0. The arguments are
     * sourced by looking at the trailing N arguments of the initial
     * capture. They are copied into the last N temporaries. The args
     * are set to come from the argument temporaries, using the identity
     * map. */
    MVMDispOpcodeCopyArgsTail,
    /* Set a bytecode object result, specifying invokee and callsite.
     * The args should already have been set up. */
    MVMDispOpcodeResultBytecode,
    /* Set a C function object result, specifying invokee and callsite.
     * The args should already have been set up. */
    MVMDispOpcodeResultCFunction,
    /* Set a foreign function object result, specifying invokee and callsite.
     * The args should already have been set up. */
    MVMDispOpcodeResultForeignCode
} MVMDispProgramOpcode;

/* An operation, with its operands, in a dispatch program. */
struct MVMDispProgramOp {
    /* The opcode. */
    MVMDispProgramOpcode code;

    /* Operands. */
    union {
        struct {
            /* The dispatcher we're resuming. */
            MVMDispDefinition *disp;
        } resume;
        struct {
            /* The callsite of the resumption initialization state. */
            MVMuint32 callsite_idx;
        } resume_init_callsite;
        struct {
            /* The argument index in the incoming capture. */
            MVMuint16 arg_idx;
            /* The thing to check it against (looked up in one of the constant
             * tables). */
            MVMuint32 checkee;
        } arg_guard;
        struct {
            /* The temporary to guard. */
            MVMuint32 temp;
            /* The thing to check it against (looked up in one of the constant
             * tables). */
            MVMuint32 checkee;
        } temp_guard;
        struct {
            /* The temporary to load into. */
            MVMuint32 temp;
            /* The index to load from. The thing we're indexing is part of
             * the instruction. For attribute loads, this is the offset into
             * the object to read (and we expect temp to start out containing
             * the object to read from, unlike any other load; this keeps our
             * union to 64 bits). */
            MVMuint32 idx;
        } load;
        struct {
            /* The flag to pass when we do a resume when there is a bind
             * failure. */
            MVMuint32 failure_flag;
            /* The flag to pass when we do a resume when there is a bind
             * success. */
            MVMuint32 success_flag;
        } bind_control_resumption;
        struct {
            /* The number of args to skip when we use the tail of the incoming
             * capture. */
            MVMuint32 skip_args;
            /* The callsite index. */
            MVMuint32 callsite_idx;
        } use_arg_tail;
        struct {
            /* The number of args to copy from the tail of the incoming callsite
             * to the tail of the args temporaries area. */
            MVMuint32 tail_args;
            /* The callsite index. */
            MVMuint32 callsite_idx;
        } copy_arg_tail;
        struct {
            /* The temporary holding the result. */
            MVMuint32 temp;
        } res_value;
        struct {
            /* The temporary holding the thing to invoke. */
            MVMuint32 temp_invokee;
        } res_code;
    };
};

/* Information about a possible dispatch resumption. */
struct MVMDispProgramResumption {
    /* The dispatcher that we can resume. */
    MVMDispDefinition *disp;

    /* The callsite of the resume init state. */
    MVMCallsite *init_callsite;

    /* Where the arguments come from (array matching size of init_callsite
     * that specifies where each value comes from), or NULL if it is
     * precisely identical to the initial arguments of the call. */
    MVMDispProgramResumptionInitValue *init_values;
};

/* The source of a value in the resumption initialization state. */
typedef enum {
    /* An argument from the initial arguments the call was given. */
    MVM_DISP_RESUME_INIT_ARG,
    /* A constant object or string. */
    MVM_DISP_RESUME_INIT_CONSTANT_OBJ,
    /* A constant integer. */
    MVM_DISP_RESUME_INIT_CONSTANT_INT,
    /* A constant number. */
    MVM_DISP_RESUME_INIT_CONSTANT_NUM,
    /* A value stored in a temporary created by the evaluation of this
     * dispatch program. */
    MVM_DISP_RESUME_INIT_TEMP
} MVMDispProgramResumptionInitSource;

/* Where a value that is used to resume a dispatch originates from. */
struct MVMDispProgramResumptionInitValue {
    /* The source of the resumption initialization value. */
    MVMuint16 source;
    /* The index (either argument index, constant table index, or temporary
     * index). */
    MVMuint16 index;
};

/* Functions called during the recording. */
void MVM_disp_program_run_dispatch(MVMThreadContext *tc, MVMDispDefinition *disp,
        MVMArgs arg_info, MVMDispInlineCacheEntry **ic_entry_ptr,
        MVMDispInlineCacheEntry *ic_entry, MVMStaticFrame *update_sf);
MVMint64 MVM_disp_program_record_get_inline_cache_size(MVMThreadContext *tc);
void MVM_disp_program_record_do_not_install(MVMThreadContext *tc);
MVMHLLConfig * MVM_disp_program_record_get_hll(MVMThreadContext *tc);
MVMObject * MVM_disp_program_record_track_arg(MVMThreadContext *tc, MVMObject *capture,
        MVMuint32 index);
MVMObject * MVM_disp_program_record_track_attr(MVMThreadContext *tc, MVMObject *tracked,
        MVMObject *class_handle, MVMString *name);
MVMObject * MVM_disp_program_record_track_unbox_int(MVMThreadContext *tc, MVMObject *tracked);
MVMObject * MVM_disp_program_record_track_unbox_num(MVMThreadContext *tc, MVMObject *tracked);
MVMObject * MVM_disp_program_record_track_unbox_str(MVMThreadContext *tc, MVMObject *tracked);
MVMObject * MVM_disp_program_record_track_how(MVMThreadContext *tc, MVMObject *tracked);
void MVM_disp_program_record_guard_type(MVMThreadContext *tc, MVMObject *tracked);
void MVM_disp_program_record_guard_concreteness(MVMThreadContext *tc, MVMObject *tracked);
void MVM_disp_program_record_guard_literal(MVMThreadContext *tc, MVMObject *tracked);
void MVM_disp_program_record_guard_not_literal_obj(MVMThreadContext *tc,
       MVMObject *tracked, MVMObject *object);
void MVM_disp_program_record_guard_hll(MVMThreadContext *tc, MVMObject *tracked);
MVMObject * MVM_disp_program_record_index_lookup_table(MVMThreadContext *tc,
       MVMObject *lookup_hash, MVMObject *tracked_key);
MVMObject * MVM_disp_program_record_index_tracked_lookup_table(MVMThreadContext *tc,
       MVMObject *tracked_lookup_hash, MVMObject *tracked_key);
MVMObject * MVM_disp_program_record_capture_drop_arg(MVMThreadContext *tc, MVMObject *capture,
        MVMuint32 index);
MVMObject * MVM_disp_program_record_capture_drop_args(MVMThreadContext *tc, MVMObject *capture,
        MVMuint32 index, MVMuint32 count);
MVMObject * MVM_disp_program_record_capture_insert_constant_arg(MVMThreadContext *tc,
        MVMObject *capture, MVMuint32 index, MVMCallsiteFlags kind, MVMRegister value);
MVMint64 MVM_disp_program_record_capture_is_arg_literal(MVMThreadContext *tc,
        MVMObject *capture, MVMuint32 index);
MVMObject * MVM_disp_program_record_capture_insert_arg(MVMThreadContext *tc,
        MVMObject *capture, MVMuint32 index, MVMObject *tracked);
MVMObject * MVM_disp_program_record_capture_replace_arg(MVMThreadContext *tc,
        MVMObject *capture, MVMuint32 idx, MVMObject *tracked);
MVMObject * MVM_disp_program_record_capture_replace_literal_arg(MVMThreadContext *tc,
        MVMObject *capture, MVMuint32 idx, MVMCallsiteFlags kind, MVMRegister value);
void MVM_disp_program_record_set_resume_init_args(MVMThreadContext *tc, MVMObject *capture);
MVMObject * MVM_disp_program_record_get_resume_init_args(MVMThreadContext *tc);
void MVM_disp_program_record_set_resume_state(MVMThreadContext *tc, MVMObject *tracked);
void MVM_disp_program_record_set_resume_state_literal(MVMThreadContext *tc, MVMObject *new_state);
MVMObject * MVM_disp_program_record_get_resume_state(MVMThreadContext *tc);
MVMObject * MVM_disp_program_record_track_resume_state(MVMThreadContext *tc);
void MVM_disp_program_record_resume(MVMThreadContext *tc, MVMObject *capture);
void MVM_disp_program_record_resume_caller(MVMThreadContext *tc, MVMObject *capture);
void MVM_disp_program_record_delegate(MVMThreadContext *tc, MVMString *dispatcher_id,
        MVMObject *capture);
MVMint32 MVM_disp_program_record_next_resumption(MVMThreadContext *tc, MVMObject *with_args);
void MVM_disp_program_record_resume_on_bind_failure(MVMThreadContext *tc, MVMuint32 flag);
void MVM_disp_program_record_resume_after_bind(MVMThreadContext *tc, MVMuint32 failure_flag,
        MVMuint32 success_flag);
void MVM_disp_program_record_result_constant(MVMThreadContext *tc, MVMCallsiteFlags kind,
        MVMRegister value);
void MVM_disp_program_record_result_tracked_value(MVMThreadContext *tc, MVMObject *tracked);
void MVM_disp_program_record_code_constant(MVMThreadContext *tc, MVMCode *result, MVMObject *capture);
void MVM_disp_program_record_c_code_constant(MVMThreadContext *tc, MVMCFunction *result,
        MVMObject *capture);
void MVM_disp_program_record_foreign_code_constant(MVMThreadContext *tc,
        MVMNativeCall *result, MVMObject *capture);
void MVM_disp_program_record_tracked_code(MVMThreadContext *tc, MVMObject *tracked,
        MVMObject *capture);
void MVM_disp_program_record_tracked_c_code(MVMThreadContext *tc, MVMObject *tracked,
        MVMObject *capture);
MVMuint32 MVM_disp_program_record_end(MVMThreadContext *tc, MVMCallStackDispatchRecord* record);

/* Functions to run dispatch programs. */
MVMint64 MVM_disp_program_run(MVMThreadContext *tc, MVMDispProgram *dp,
        MVMCallStackDispatchRun *disp_run, MVMint32 spesh_cid,
        MVMuint32 bytecode_offset, MVMuint32 dp_index);

/* Memory management of dispatch programs. */
void MVM_disp_program_mark(MVMThreadContext *tc, MVMDispProgram *dp, MVMGCWorklist *worklist,
        MVMHeapSnapshotState *snapshot);
void MVM_disp_program_mark_recording(MVMThreadContext *tc, MVMDispProgramRecording *rec,
        MVMGCWorklist *worklist, MVMHeapSnapshotState *snapshot);
void MVM_disp_program_mark_run_temps(MVMThreadContext *tc, MVMDispProgram *dp,
        MVMCallsite *cs, MVMRegister *temps, MVMGCWorklist *worklist,
        MVMHeapSnapshotState *snapshot);
void MVM_disp_program_mark_record_temps(MVMThreadContext *tc, MVMDispProgram *dp,
        MVMRegister *temps, MVMGCWorklist *worklist, MVMHeapSnapshotState *snapshot);
void MVM_disp_program_mark_outcome(MVMThreadContext *tc, MVMDispProgramOutcome *outcome,
        MVMGCWorklist *worklist, MVMHeapSnapshotState *snapshot);
void MVM_disp_program_destroy(MVMThreadContext *tc, MVMDispProgram *dp);
void MVM_disp_program_recording_destroy(MVMThreadContext *tc, MVMDispProgramRecording *rec);

const char *MVM_disp_opcode_to_name(MVMDispProgramOpcode op);
