/* The minimum number OSR hits a static frame as a whole has to receive
 * (across all callsites and type tuples) before it is hot enough to further
 * consider. Make this at least as high as a the threshold for a fairly large
 * body, otherwise it's possible to have the loop body optimized later than
 * the looping frame itself, and thus a (good) specialization of the loop body
 * would not be available. */
#define MVM_SPESH_PLAN_SF_MIN_OSR   200

/* The minimum number of hits or OSR hits a given static frame and interned
 * callsite combination have to have before it is hot enough to further
 * consider. */
#define MVM_SPESH_PLAN_CS_MIN_OSR   200

/* The percentage of hits or OSR hits that a type tuple should receive, out of
 * the total callsite hits, to receive an "observed types" specialization. */
#define MVM_SPESH_PLAN_TT_OBS_PERCENT       25
#define MVM_SPESH_PLAN_TT_OBS_PERCENT_OSR   25

/* The plan of what specializations to produce. */
struct MVMSpeshPlan {
    /* List of planned specializations. */
    MVMSpeshPlanned *planned;

    /* Number of planned specializations. */
    MVMuint32 num_planned;

    /* The number of specialization plans space is allocated for. */
    MVMuint32 alloc_planned;
};

/* Kinds of specializations we might decide to produce. */
typedef enum {
    /* A certain specialization based only on callsite. */
    MVM_SPESH_PLANNED_CERTAIN,

    /* A specialization based on an exact observed argument type tuple. */
    MVM_SPESH_PLANNED_OBSERVED_TYPES,

    /* A specialization based on analysis of various argument types that
     * showed up. This may happen when one argument type is predcitable, but
     * others are not. */
    MVM_SPESH_PLANNED_DERIVED_TYPES
} MVMSpeshPlannedKind;

/* An planned specialization that should be produced. */
struct MVMSpeshPlanned {
    /* What kind of specialization we're planning. */
    MVMSpeshPlannedKind kind;

    /* The maximum stack depth this was seen at; used to sort the plan so we
     * can specialize deepest first, in hope of having callees specialized
     * ahead of callers. */
    MVMuint32 max_depth;

    /* The static frame with the code to specialize. */
    MVMStaticFrame *sf;

    /* The callsite statistics entry that this specialization was planned as
     * a result of (by extension, we find the callsite, if any). */
    MVMSpeshStatsByCallsite *cs_stats;

    /* The type tuple to produce the specialization for, if this is a type
     * based specialization. NULL for certain specializations. The memory
     * associated with this tuple will always have been allocated by the
     * planner, not shared with the statistics structure, even if this is a
     * specialization for an exactly observed type. */
    MVMSpeshStatsType *type_tuple;

    /* Type statistics, if any, that the plan was formed based upon. */
    MVMSpeshStatsByType **type_stats;

    /* Number of entries in the type_stats array. (For an observed type
     * specialization, this would be 1.) */
    MVMuint32 num_type_stats;
};

MVMSpeshPlan * MVM_spesh_plan(MVMThreadContext *tc, MVMObject *updated_static_frames, MVMuint64 *certain_specialization, MVMuint64 *observed_specialization, MVMuint64 *osr_specialization);
void MVM_spesh_plan_gc_mark(MVMThreadContext *tc, MVMSpeshPlan *plan, MVMGCWorklist *worklist);
void MVM_spesh_plan_gc_describe(MVMThreadContext *tc, MVMHeapSnapshotState *ss, MVMSpeshPlan *plan);
void MVM_spesh_plan_destroy(MVMThreadContext *tc, MVMSpeshPlan *plan);
MVMSpeshStatsType * MVM_spesh_plan_copy_type_tuple(MVMThreadContext *tc, MVMCallsite *cs, MVMSpeshStatsType *to_copy);
